Agent de liaison JavaScript Plugins

Création de plugins JavaScript pour Bridge Agent

Cette documentation détaille le développement de plugins JavaScript pour l'agent IBM Security Verify Bridge. Les plugins JavaScript sont utilisés pour mettre en œuvre une logique personnalisée d'authentification des utilisateurs et de récupération des attributs pour des sources d'attributs autres que le LDAP principal.

Procédure de haut niveau

Assurez-vous d'avoir accès à un locataire IBM Security Verify (ISV) et suivez les étapes décrites dans les sections suivantes :

  1. Installer l'agent de pont
  2. Définir la configuration du plugin
  3. Voir les exemples de démarrage rapide

Architecture de haut niveau

909

À partir de la version 1.0.14, l'agent de pont prend en charge deux types différents de sources d'attributs :

  1. LDAP primaire
  2. LDAP non primaire

Par défaut, l'agent de pont effectue l'authentification de l'utilisateur à l'aide de l'adresse primary LDAP. L'authentification et la récupération des attributs sont intégrées et ne nécessitent pas de plugin JavaScript.

Pour les cas d'utilisation avancés, il peut être souhaitable d'utiliser les sources d'attributs non-primary LDAP. Elles permettent de mettre en œuvre une logique personnalisée de recherche d'attributs et d'authentification à l'aide du site JavaScript plugin. L'agent bridge prend en charge les sources d'attributs LDAP non primaires suivantes :

  1. LDAP
  2. Oracle Database
  3. PostgreSQL
  4. IBM Db2

Les liaisons avec ces sources d'attributs sont créées au moyen d'une configuration JavaScript ( jsconfig ). La logique de gestion de la récupération des attributs et de l'authentification des utilisateurs doit être mise en œuvre au moyen d'un plugin JavaScript ( jsplugin ).

Ce guide détaille le site création d'un jsconfig pour configurer une connexion à votre source d'attributs et le site l'élaboration d'un jsplugin pour mettre en œuvre la logique d'extraction d'attributs et d'authentification de l'utilisateur.

Terminologie

TermeAutres nomsDéfinition
Onprem AgentBridge Agent, ldap agent, agent, identity agentL'agent qui s'exécute surprem et s'interface avec un ou plusieurs sites onprem attribute sources pour effectuer l'authentification de l'utilisateur et la recherche d'attributs.
Bridge Serviceagentbridgesvc, Agent Bridge ServiceMicroservice ISV chargé de traiter les demandes de charge de travail interne et les réponses reçues de onprem agent.
Support Serviceagentbridgesupsvc, Agent Bridge Support ServiceMicroservice ISV responsable de la gestion des configurations des agents.
Data bindingjavascript bindingUne connexion à un site attribute source établie via des utilitaires golang et exposée via des fonctions javascript pour être traitée par un site javascript plugin.
Javascript pluginjspluginUn programme javascript écrit par un utilisateur d' IBM Security Verify Bridge. Il met en œuvre une logique de recherche d'attributs et d'authentification de l'utilisateur pour le compte de onprem agent par le biais de diverses bases de données spécifiques data bindings.
Plugin configurationjsconfigConfiguration requise par onprem agent pour établir une liaison de données avec attribute source au nom de javascript plugin.
Primary LDAPauth LDAPRépertoire LDAP principal pour lequel l'authentification par nom d'utilisateur et mot de passe est effectuée. Peut être remplacé par la propriété authenticationSource.
Attribute sourcedata source, attribute store, data store, databaseSource de données sur laquelle l'authentification peut être effectuée et à partir de laquelle les attributs de l'utilisateur sont récupérés. Il peut s'agir de : LDAP, Db2, PostgreSQL et OracleDB.
Authentication sourceauth sourceUne adresse attribute source par rapport à laquelle l'authentification doit être effectuée. Est le primary LDAP par défaut.

Installation de l'agent Bridge

Les plugins sont une nouvelle fonctionnalité de v1.0.14 de l'agent Bridge qui est fourni en tant que conteneur Docker. Voir Installation et configuration de Verify Bridge sur Docker.

Annuaires importants

Le site Bridge Agent recherche les fichiers de configuration et les plugins JavaScript dans les deux répertoires suivants :

RépertoireDescriptionType de fichier
/go/src/jsconfigCe répertoire contient les fichiers jsconfig. Ils définissent les détails de la liaison et identifient le jsplugin. N.B. La passerelle peut charger un maximum de 10 plugins JavaScript. Les 10 premières configurations de ce répertoire seront chargées par ordre alphabétique, les autres seront ignorées. .json
/go/src/jspluginsCe répertoire contient jsplugins, de petits programmes JavaScript qui gèrent l'authentification et la récupération des attributs de l'utilisateur.js
/certUtilisé pour MTLS avec les plugins LDAP. Le pont sera ici en
/cert/<clientCertLabel>_cert.pem /cert/<clientCertLabel>_key.pem
.pem

Placez vos fichiers de configuration et de plugin dans leurs répertoires respectifs. Les montages Docker bind peuvent être utilisés pour simplifier ce processus. Voir l'exemple docker-compose.yml pour l'utilisation des montages bind

# Example docker-compose.yml
version: "3"

services:
    verify-bridge:
         image: icr.io/isv-saas/verify-bridge:latest


         container_name: bridge_agent
         volumes:
                - ./jsconfig:/go/src/jsconfig:ro
                - ./jsplugins:/go/src/jsplugins:ro
                - ./cert/:/cert:ro

         environment:
                TRACE: "true"
                LICENSE_ACCEPT: "yes"
                TENANT_URI: "https://tenant.verify.ibm.com"
                CLIENT_ID: "12345678-1234-1234-1234-123456789012"
                CLIENT_SECRET: "ABCDEFGHIJ"


        restart: always

La section bind mount

- ./jsconfig:/go/src/jsconfig:ro
- ./jsplugins:/go/src/jsplugins:ro
- ./cert/:/cert:ro

Monte les répertoires locaux ./jsconfig, ./jsplugins, ./cert aux emplacements correspondants dans le conteneur. L'option ro monte ces répertoires en lecture seule. Il s'agit d'une bonne pratique de sécurité pour les montages bind, car elle garantit que les conteneurs en cours d'exécution ne peuvent pas apporter de modifications au système de fichiers de l'hôte.

Définir les configurations des plugins

Les configurations des plugins doivent être placées dans le répertoire /go/src/jsconfig et doivent être des documents JSON valides. Les propriétés suivantes sont disponibles :

Configurations Standard

PropriétéDéfinition
pluginNameLe nom du plugin. L'agent Bridge recherchera le plugin correspondant sur le site /go/src/jsplugins/<pluginName>.js.
pluginTypeLe type de liaison. Doit être l'un des suivants : ldap, db2, postgres ou oracle.
executionOrderL'ordre dans lequel le plugin doit être exécuté par rapport aux autres plugins. Exécuté du plus petit au plus grand, c'est-à-dire que 2 est exécuté avant 3.
hardFailDéfinissez true si vous voulez que le pont renvoie si le plugin ne s'exécute pas ou renvoie autre chose que SUCCESS.
isAuthenticationSourceDéfinir à true si ce plugin est destiné à être le authentication source. C I.e que l'authentification par nom d'utilisateur/mot de passe est effectuée par rapport à cette source d'attributs. En fixant cette valeur à true, le plugin se comportera automatiquement comme si hardFail était également vrai. Lorsque true, l'authentification n'est pas effectuée par rapport au LDAP primaire, mais la recherche d'attributs est toujours effectuée. Pour désactiver entièrement la recherche LDAP primaire (y compris la recherche d'attributs), vous devez remplacer disablePrimaryLdapLookup par true. NB Seul UN (1) plugin peut être une source d'authentification.
disablePrimaryLdapLookupDésactive le LDAP primaire. Ce paramètre doit être défini lorsque isAuthenticationSource est true et que vous souhaitez également désactiver la recherche d'attributs à partir du LDAP principal.
customCette section est facultative et peut inclure des propriétés personnalisées à transmettre à jsplugins au moment de l'exécution.

Configurations de la reliure

Les configurations de liaison sont utilisées pour établir une liaison avec la source d'attributs et définir les détails de la connexion de la liaison. La colonne LDAP or DB indique si la propriété est attendue pour les plugins LDAP ou DB ( db2, postgres, oracle ).

PropriétéLDAP ou DBDéfinitionExemple
connectionStringBase de donnéesLa chaîne de connexion utilisée pour établir une liaison avec cette source de données particulière. Utilisé uniquement pour db2, postgres, et oracle. Voir chaînes de connexion.Voir chaînes de connexion.
maxPoolSizeBOTHNombre maximal de connexions dans le pool de connexions.50
minPoolSizeBOTHNombre minimum de connexions dans le pool de connexions.10
agedTimeoutBOTHDélai en secondes avant qu'une connexion ne soit abandonnée.60
maxIdleTimeBOTHDurée maximale d'inactivité d'une connexion avant qu'elle ne soit rejetée.10
bindDnLDAPLe DN de liaison utilisé pour établir une liaison LDAP.cn=admin,dc=ibm,dc=com
bindPasswordLDAPLe mot de passe de liaison utilisé pour établir une liaison LDAP.pTms!REJECs@hzdNEGZ8
urisLDAPUn tableau d'URI de serveurs LDAP. Il est utilisé pour le basculement, si le premier serveur de la matrice tombe en panne, le suivant sera essayé, etc. N.B. Le protocole est important ici. Si vous souhaitez utiliser TLS, vous devez utiliser le protocole ldaps://. Les autres paramètres TLS seront ignorés si ce paramètre n'est pas défini. ldaps://localhost:636
filterLDAPLe filtre à utiliser lors de la recherche LDAP.(|(|(objectclass=ePerson)(objectclass=person))(objectclass=User))
userObjectClassesLDAPLes classes d'objets utilisateur.top,Person,organizationalPerson,inetOrgPerson
selectorLDAPSélecteurs utilisés lors de la recherche LDAP.objectClass,cn,sn,givenName,userPassword,streetAddress,seeAlso,mobile
baseDnLDAPLe DN de base utilisé lors d'une recherche LDAP standard. La logique du plugin peut remplacer cela si nécessaire.dc=ibm,dc=com
caCertLDAPLe certificat de l'autorité de certification utilisé pendant TLS pour vérifier l'authenticité du certificat présenté par le serveur LDAP pendant TLS.-----BEGIN CERTIFICATE-----\nMIIDazCCAlOgA...
insecureSkipVerifyLDAPIgnorer la vérification du certificat TLS. N.B ne doit être utilisé que dans des environnements de test et non en production.false
tlsMinVersionLDAPDéfinir la version TLS minimale autorisée. Voir la version minimale de tls.771
clientCertLabelLDAPUtilisé pendant le MTLS. Vous devez définir ici une étiquette pour le certificat et la clé du client à présenter au serveur LDAP. L'agent de liaison recherchera cette information à l'adresse suivante /cert/<clientCertLabel>_cert.pem /cert/<clientCertLabel>_key.pemldap.client

Chaînes de connexion

Les chaînes de connexion s'appliquent aux sources d'attributs de la base de données ( db2, oracle et postgres ). Les chaînes de connexion ont chacune leur propre format et peuvent être utilisées pour spécifier des détails de connexion tels que TLS, les délais d'attente et d'autres paramètres spécifiques à la base de données. Veuillez consulter la documentation de votre source de données pour obtenir une liste complète des options de chaîne de connexion disponibles. Voici des exemples de chaînes de connexion :

Oracle Database

oracle://system:[email protected]:1521/XE?CONNECTION TIMEOUT=5

Base de données Postgres

host=host.docker.internal port=8788 dbname=postgres user=postgres password=postgrespassword connect_timeout=5

Base de données Db2

HOSTNAME=host.docker.internal;PORT=50000;UID=db2inst1;PWD=db2_password;DATABASE=usersdb

Version TLS minimale

La propriété tlsMinVersion définit la version TLS minimale autorisée. Il doit s'agir d'un nombre entier. Le tableau suivant présente une correspondance entre ces nombres entiers et leur version TLS.

Version TLSEntier
v1.0769
v1.1770
v1.2 (par défaut)771
v1.3772

Utilisez un nombre entier pour définir la version minimale de TLS, par exemple "tlsMinVersion": 770. L'omission de cette propriété ou la définition d'une mauvaise valeur entraînera la définition par défaut de 771 (TLS version 1.2 ).

Exemple de configuration LDAP

Voici un exemple de LDAP jsconfig qui utilise MTLS.

{
  "pluginName": "ldap",
  "pluginType": "ldap",
  "executionOrder": 2,
  "hardFail": false,
  "authenticationSource": {
    "isAuthenticationSource": false,
    "disablePrimaryLDAPLookup": false
  },
  "bindingConfig": {
    "bindDn": "cn=admin,dc=ibm,dc=com",
    "bindPassword": "pass",
    "uris": ["ldaps://host.docker.internal:8636", "http://localhost:8389"],
    "maxPoolSize": 50,
    "agedTimeout": 60,
    "connectTimeout": 5,
    "filter": "(|(|(objectclass=ePerson)(objectclass=person))(objectclass=User))",
    "userObjectClasses": "top,Person,organizationalPerson,inetOrgPerson",
    "selector": "objectClass,cn,sn,givenName,userPassword,streetAddress,seeAlso,mobile",
    "baseDn": "dc=ibm,dc=com"
    "tlsConfig": {
      "caCert": "-----BEGIN CERTIFICATE-----\nMIIDazCCAlOgAwIB...\n-----END CERTIFICATE-----\n",
      "insecureSkipVerify": false,
      "tlsMinVersion": 771,
      "clientCertLabel": "ldap.client"
    }
  }
}

Exemple de configuration de la base de données

Voici un exemple de PostgreSQL jsconfig.

{
  "pluginName": "PgPlug",
  "pluginType": "postgres",
  "executionOrder": 1,
  "hardFail": true,
  "authenticationSource": {
    "isAuthenticationSource": true,
    "disablePrimaryLDAPLookup": true
  },
  "bindingConfig": {
    "connectionString": "host=host.docker.internal port=8788 dbname=postgres user=postgres password=postgrespassword connect_timeout=5",
    "maxPoolSize": 50,
    "minPoolSize": 10,
    "agedTimeout": 60,
    "maxIdleTime": 10
  },
  "custom": {
    "table": "users"
  }
}

Développer des plugins JavaScript

Les plugins JavaScript se composent de trois concepts importants :

  1. Objets d'entrée
  2. Fonctions de liaison
  3. outputData Objet

Les objets d'entrée fournissent des instructions au plugin, la charge de travail opérationnelle à traiter, l'objet de travail actuel (résultats des opérations précédentes) et une copie de la configuration du plugin. Elles contiennent toutes les données nécessaires pour effectuer le traitement de votre plugin.

Fonction de liaison Les fonctions de liaison sont les fonctions et les classes mises à disposition pour interagir avec la liaison de données. Ils peuvent être utilisés pour effectuer des requêtes et extraire des attributs de la source de données.

objet outputData
L'objet output est un objet spécial qui doit être créé par votre script. L'objet de sortie doit se conformer à une structure spécifique.

Objets d'entrée

Lorsque le script est exécuté, trois adresses input objects sont transmises au script :

Objet d'entréeDescription
requestParametersLa demande reçue de l'ISV.
workingObjectParamètres déjà récupérés du LDAP primaire ou des plugins précédents dans l'ordre d'exécution.
pluginConfigLa configuration du plugin telle qu'elle existe dans le répertoire ./jsconfig. Plus l'ajout de l'objet userAttributes.

N.B Ces objets d'entrée sont transmis au script du plugin sous forme de chaînes, ils doivent être analysés en JSON avant d'être utilisés :

let rp = JSON.parse(requestParameters);
let wo = JSON.parse(workingObject);
let pc = JSON.parse(pluginConfig);
requestParameters

Il s'agit de la charge utile de la demande récupérée auprès de l'ISV. Il possède les propriétés suivantes :
Les propriétés importantes sont les suivantes

PropriétéDescription
addressedToLe nom du module, qui sera toujours ldapauth.
enqueuedTimeHeure à laquelle l'opération a été mise en file d'attente.
opérationL'opération à effectuer par le plugin. Il peut s'agir de password-verify ou de password-change.
paramètresUn ensemble de paramètres correspondant à l'opération spécifique.
exemple de vérification de mot de passe
{
  "addressedTo": "ldapauth",
  "enqueuedTime": "2023-10-15 05:15:36.769",
  "id": "jobUuId",
  "operation": "password-verify",
  "parameters": {
       "password": "Passw0rd",
       "username": "Scott"
   }
}
exemple de changement de mot de passe
{
  "addressedTo": "ldapauth",
  "enqueuedTime": "2023-10-15 05:15:36.769",
  "id": "jobUuId",
  "operation": "password-change",
  "parameters": {
       "oldpassword": "OldPassw0rd",
       "newpassword": "NewPassw0rd",
       "username": "Scott"
   }
}

N.B Vous ne devez traiter que password-change pour votre source d'authentification.

workingObject

Le site workingObject contient des attributs qui ont déjà été récupérés lors de consultations précédentes dans la chaîne de consultation. Il a la structure suivante :

PropriétéDescription
groupesUn tableau des groupes ISV auxquels l'utilisateur appartient.
utilisateurUn objet dont les objets enfants correspondent aux attributs renvoyés pour l'utilisateur.
workingObject Exemple
{
  "groups": [],
  "user": {
    "dn": ["uid=scott,ou=Verify,dc=ibm,dc=com"],
    "mail": ["[email protected]"],
    "mobile": ["+61 2345 6789"],
    "sn": ["Administrator"],
    "uid": ["scott"]
  }
}

N.B. La valeur de l'attribut user doit être contenue dans un tableau, comme dans l'exemple ci-dessus.

pluginConfig

L'objet pluginConfig contient simplement une copie exacte de la configuration du plugin qui s'exécute ainsi que l'objet userAttributes.

Les userAttributes sont les attributs que ce plugin devra récupérer. Ils sont injectés dans la configuration lorsque le script est appelé.

pluginConfig Exemple
// pluginConfig value that will be passed to the PgPlug.js plugin
{
  "pluginName": "PgPlug",
  "pluginType": "postgres",
  "executionOrder": 1,
  "hardFail": true,
  "authenticationSource": {
    "isAuthenticationSource": true,
    "disablePrimaryLDAPLookup": true
  },
  "bindingConfig": {
    "connectionString": "host=host.docker.internal port=8788 dbname=postgres user=postgres password=postgrespassword connect_timeout=5",
    "maxPoolSize": 50,
    "minPoolSize": 10,
    "agedTimeout": 60,
    "maxIdleTime": 10
  },
  "userAttributes": [ // These attributes sourced from ISV
       "phone",
       "address",
       "sn"
  ]
  "custom": {
    "table": "users"
  }
}
pluginConfig userAttributes

userAttributes est la représentation sous forme de tableau de chaînes des attributs de l'utilisateur demandés par l'ISV.

let pc = JSON.parse(pluginConfig);
let requestedAttributes = pc.userAttributes; // Array of attributes requested for this user

Fonctions de liaison

Les fonctions de liaison sont des fonctions/classes fournies à votre script qui vous permettent d'interfacer avec votre source d'attributs via la liaison de connexion établie.

Il existe différentes fonctions de liaison pour databases et ldap :

Consignateur

Une fonction de liaison avec l'enregistreur est fournie. Pour enregistrer, ajoutez ce qui suit au début de votre script de plugin

importClass(logger);

La classe logger offre les fonctions de journalisation suivantes :

FonctionDescription
trace(message)Journal avec niveau trace.
debug(message)Log avec le niveau debug.
info(message)Journal avec informations sur le niveau.
warn(message)Journal avec niveau d'avertissement.
error(message)Journal avec erreur de niveau.
Exemple d'enregistreur
importClass(logger); // Import logger at the top of your plugin script

// Log with logging level info
logger.info(`Plugin script logging with level info`)
Niveau de journalisation

Le niveau de journalisation est donné à titre indicatif et apparaîtra dans la sortie standard de l'agent bridge.

time="2023-10-24T11:15:47Z" level=<log level> msg="log message" func=pkg.binding.logger.log

Fonctions de liaison avec la base de données

Pour accéder à database binding functions, placez l'importation suivante au début du script de votre plugin.

// Place this at the top of your Plugin.js file
importClass(database);

Ceci importera l'objet javascript database qui a les fonctions suivantes définies sur lui :

database.query()

database.query() accepte

database.query(serverName, query);

Paramètres:

  • serverName ( String ): Spécifie le nom du serveur interne de la base de données sur laquelle la requête sera effectuée. Elle peut être réglée sur la valeur serverName, qui est une variable prédéfinie contenant la valeur correcte.
  • query ( Object ): Un objet de la forme :
{
       "sql": "query string",
       "args": [
              ["argument 1", "argument 2"]
       ]
}

Les arguments seront remplacés par des marqueurs de paramètres.

Voici un exemple d'appel complet à database.query :

// Db2 style substitution using the `?` symbol
result = database.query(serverName, {
       "sql": "SELECT * FROM USERS WHERE ID = ? AND PASSWORD = ?",
       "args": [
              ["scott", "Password"]
       ]
});

Le symbole ? ci-dessus est un parameter marker est un substitut des valeurs de la propriété args.

N.B: les différents types de bases de données utilisent des marqueurs de paramètres différents :

Marqueurs de paramètres
Base de donnéesMarqueur de paramètre
Db2?
Oracle Database:1
PostgreSQL$1

Voici le même exemple, mais en utilisant le style PostgreSQL parameter markers :

// Postgres style substitution using the `$1` symbol
result = database.query(serverName, {
       "sql": "SELECT * FROM USERS WHERE ID = $1 AND PASSWORD = $1",
       "args": [
              ["scott", "Password"]
       ]
});

Objet du résultat

Lorsque la requête est renvoyée, elle renvoie l'un des éléments suivants :

  1. Objet du résultat
  2. Objet d'erreur
Objet du résultat
PropriétéDescription
rowsAffectedNombre de lignes modifiées par l'opération.
lignesUn tableau de Object. Chaque objet représente une ligne renvoyée par la requête. Chaque propriété de l'objet correspond à une colonne et à sa valeur.
{
  "rowsAffected": 0, // Rows changed
  "rows": [          // Array of objects representing each row. Each property represents a column and its value
    {
      "CREATED_ON": "2023-11-02T00:00:00Z",
      "EMAIL": "[email protected]",
      "ISLOCKED": false,
      "LAST_LOGIN": "2023-10-24T00:00:00Z",
      "PASSWORD": "scottpass",
      "PGATTR": "scottpsec1234",
      "USERNAME": "Scott",
      "USER_ID": 2
    }
  ]
}

Si aucune ligne n'est renvoyée, null sera renvoyé pour rows.

Exemple :

{"rowsAffected":0,"rows":null}
Objet d'erreur

Un objet d'erreur est renvoyé lorsque database.query échoue.

PropriétéDescription
ErreurLa chaîne d'erreur.

Exemple d'objet de résultat d'erreur :

{ "error": "the error string" }

Un objet d'erreur est renvoyé dans les cas où la requête n'a pas été exécutée correctement.

database.execute()

Exécute une requête SQL paramétrée avec des arguments optionnels et renvoie le nombre de lignes affectées. Cette fonction fonctionne de la même manière que la fonction database.query mais est utilisée pour INSERT, UPDATE, DELETE, DROP.

result = database.execute(serverName, query)

Pour une description de ces paramètres, voir les paramètres d'interrogation de la base de données.

Voir l'objet résultat pour une description des objets result et error.

Exemple
result = database.execute(serverName,
  {
    "sql": "DELETE FROM TEST_DB_BINDING WHERE ID = ?",
    "args": [
      ["test_key001"],
      ["test_key002"]
    ]
  }
);

Comme pour database.query, des substitutions sont possibles.

database.ping()

La fonction ping est une simple requête qui permet de vérifier si la base de données est vivante.

result = database.ping(serverName);

Un résultat sain renvoie les informations suivantes :

{"rowsAffected":0,"rows":null}

Dans le cas contraire, un objet d'erreur sera renvoyé.

Fonctions de liaison LDAP

Les fonctions de liaison LDAP sont fournies par deux classes d'aide :

ClasseDescription
ldapUserLookupHelperRechercher des attributs et procéder à l'authentification de l'utilisateur associé à un identifiant d'utilisateur.
ldapAttributeUtilCréer, modifier, supprimer et rechercher des attributs en fonction de leur DN.

Voir la section " quickstart" pour des exemples d'utilisation de ces classes par les jsplugins.

Aide à la recherche d'utilisateurs

Pour accéder aux fonctions de liaison LDAP, placez l'importation suivante en tête de votre script de plugin

importClass(ldapUserLookupHelper);

Cet import expose les classes suivantes :

ClassePrésentation
UserLookupHelperFournit des fonctions pour la recherche d'un utilisateur.
UtilisateurReprésente un utilisateur et fournit des fonctions pour les opérations de l'utilisateur.
Référence de la classe UserLookupHelper

Le UserLookupHelper fournit des classes pour aider à l'authentification et à la recherche d'attributs d'utilisateurs. Des exemples de démarrage rapide sont fournis.

Initialiser UserLookupHelper avec :

// Initialize new user lookup helper
let pc = JSON.parse(pluginConfig);
let configName = `${pc.pluginName}_config`
let userLookupHelper = new UserLookupHelper(configName);

Le paramètre configName est une chaîne de caractères qui doit être fournie sous la forme <pluginName>_config. L'exemple de code ci-dessus extrait l'adresse pluginName de l'objet pluginConfig et la formate de manière appropriée.

Méthodes de UserLookupHelper
MéthodeType de retourDescription
isReady()booléen ou erreurtrue lorsqu'il est prêt. Objet d'erreur sur l'erreur.
createUser(userName, dn, password, firstName, LastName )UtilisateurCrée un utilisateur LDAP. Renvoie un objet User.
getUser(userName)UtilisateurRenvoie un objet User avec l'objet User correspondant.
getUserByNativeId(nativeId)UtilisateurRenvoie un objet User. nativeId représente un nom complet (distinguished name).
deleteUser(userName)booléen, nullSupprime l'utilisateur avec le nom d'utilisateur userName. Renvoie true si la suppression a été effectuée avec succès. Peut renvoyer null en cas d'erreur.

Toutes les recherches susmentionnées (à l'exception de createUser et getUserByNativeId ) seront effectuées en utilisant la configuration de recherche LDAP définie dans la configuration de liaison LDAPuserIdentifier, baseDn, etc.).

getUserByNativeId utilise un DN complet pour rechercher l'utilisateur et contourne donc userIdentifier.

Référence de l'objet utilisateur

L'objet utilisateur représente un utilisateur. Il fournit des méthodes pour les opérations des utilisateurs telles que l'authentification par mot de passe et la recherche d'attributs.

Initialisation

Cet objet ne doit pas être initialisé manuellement, mais renvoyé par une fonction UserLookupHelper.

let ulh = new UserLookupHelper(configName);
user = ulh.getUser(`Scott`) // returned user is a User object for the user with id `Scott`
Méthodes d'utilisateur
MéthodeTypes de retourDescription
hasError()booléenRenvoie true si l'objet utilisateur présente une erreur. Cette erreur peut être déclenchée pendant la fonction de recherche de l'utilisateur ( getUser, getUserByNativeId, createUser ). N.B Vous devez vérifier cette valeur avant d'appeler toute autre méthode sur un objet User.
getError()chaîneRenvoie la valeur de l'objet Error (le message d'erreur).
authentifier(mot de passe)bool, nullRenvoie true si la chaîne password est correcte pour l'utilisateur.
getId()chaîne de caractères, nullRenvoie l' userId/username de l'utilisateur.
getNativeId()chaîne de caractères, nullRenvoie le nom distinctif complet (dn) de l'utilisateur.
getAttributeNames()TableauRenvoie un tableau des noms d'attributs de cet utilisateur. Remarque : cette opération ne renvoie pas les valeurs de ces attributs.
getAttribute(attrName)chaîne de caractères, nullRenvoie la valeur de l'attribut attrName. null si aucun attribut correspondant n'a été trouvé. Remarque: si l'attribut attrName renvoie un tableau, cette fonction renvoie le premier élément de ce tableau. Si vous souhaitez renvoyer ce tableau, utilisez la méthode getAttributes.
getAttributes(attrName)Tableau, nullRenvoie un tableau de valeurs correspondant au nom de l'attribut attrName. Si la valeur de attrName n'est pas un tableau, elle sera renvoyée sous la forme d'un tableau à un seul membre contenant cette valeur.
attributeExists(attrName)booléenRenvoie true si l'attribut utilisateur attrName existe.
Référence de la classe AttributeUtil

Les classes ldapAttributeUtil peuvent être utilisées pour effectuer des opérations personnalisées de recherche et de manipulation d'attributs. Un exemple de démarrage rapide est fourni.

Importer la classe d'aide avec :

// Add import to the top of your javascript plugin
importClass(ldapAttributeUtil);

Cet import expose les classes suivantes :

ClassePrésentation
LdapAttributeUtilUtilisé pour la création et la récupération d'attributs LDAP.
AttributReprésente un tableau de valeurs d'attributs correspondant à un ID d'attribut.
AttributsReprésente un tableau d'objets Attribut.
SearchResultReprésente une entrée de résultat de recherche.
NamingEnumerationClasse permettant d'énumérer les données d'un site type.
LdapAttributeResultReprésente la réponse à l'opération de l'attribut util.
Référence de LdapAttributeUtil

Le site LdapAttributeUtil est utilisé pour la création et l'extraction d'attributs LDAP. Initialiser LdapAttributeUtil en utilisant le nom de la configuration du plugin :

// Initialize attribute util
let pc = JSON.parse(pluginConfig);
let configName = `${pc.pluginName}_config`
let attrUtil = new LdapAttributeUtil(configName);
Méthodes de LdapAttributeUtil
MéthodeTypes de retourDescription
Init()LdapAttributeResultEffectue un contrôle de santé de la source d'attributs LDAP et renvoie un objet LdapAttributeResult. En cas d'échec du contrôle de santé, la propriété result de cet objet est un objet Error.
isReady()booléenEffectue un contrôle de santé sur la source d'attributs LDAP. Renvoie true lorsqu'il est en bonne santé.
addAttributeValue(dn, attributeName, attributeValue)LdapAttributeResultAjoute un attribut LDAP attributeName avec la valeur attributeValue à dn.
createSubContext(dn, attributes)LdapAttributeResultCrée le sous-contexte dn avec les attributs attributes. Pour plus d'informations sur la manière de spécifier les attributs d'entrée, voir le format des attributs.
getAttributeValue(dn, attributeNames)LdapAttributeResultRécupère les valeurs des attributs de dn. Le paramètre d'entrée attributeNames doit être un tableau codé en JSON de noms d'attributs à récupérer. Par exemple [``pommes'', 'banane''].
removeAttribute(dn, attributeName, attributeValue)LdapAttributeResultSupprime l'attribut attributeName avec la valeur attributeValue de dn.
search(dn, filter)LdapAttributeResultRechercher les entrées sur dn avec le filtre filter. Par exemple : (objectclass=*).
setAttributeValue(dn, attributeName, attributeValue)LdapAttributeResultDéfinit l'attribut attributeName de dn à la valeur attributeValue.
attributes format

Spécifiez les attributs sous la forme d'un objet JSON qui associe le nom de l'attribut à un tableau de ses valeurs. Exemple :

// createSubContext(dn, attributes)
attrUtil.createSubContext("ou=newOu1", {"objectClass": ["top", "organizationalUnit"]}
Référence de l'attribut

L'objet Attribut représente un tableau de valeurs d'attributs.

PropriétéTypeDescription
identificateurchaîneL'ID de l'attribut
attrTableauUn tableau de valeurs d'attributs
MéthodeTypes de retourDescription
constructor(id, attr)AttributCrée un objet Attribute avec l'ID id et un tableau de valeurs d'attributs attr.
isOrdered()booléenRetourne toujours true.
getID()chaîneRetourne la propriété id de l'attribut.
get(idx)chaîneRenvoie la valeur de l'attribut indexée par idx.
getAll()NamingEnumeration des valeurs d'attributsRenvoie une adresse NamingEnumeration des valeurs d'attribut que cet objet représente.
taille()entierRenvoie le nombre de valeurs que cet objet représente.
contains attrVal )booléenRenvoie true lorsque cet attribut contient la valeur attrVal.
Documentation de référence des attributs

L'objet Attributs représente un tableau d'objets Attributs.

PropriétéTypeDescription
attrsObjetObjet dont les propriétés correspondent à celles des objets Attribut.
MéthodeTypes de retourDescription
constructeur(attrs)AttributsPrend un tableau d 'objets JSON attr et renvoie un objet Attributes.
isCaseIgnored()booléenRetourne toujours false.
taille()entierRenvoie le nombre d'attributs représentés par l'objet.
get attrID )AttributRenvoie un attribut spécifique avec l'ID d'attribut attrID.
getIDs()NamingEnumeration de stringRenvoie les ID des attributs représentés par cet objet.
getAll()NamingEnumeration of AttributRenvoie les valeurs des attributs représentés par cet objet.
attr Format d'objet JSON
{
  "name": "attributeName", // The attribute name
  "attrs": [] // Array of attribute values
}
Référence NamingEnumeration

Le site NamingEnumeration propose des fonctions permettant de compter dans un tableau d'objets. Le tableau sous-jacent est accessible directement via la propriété results. next() renvoie l'élément suivant de l'énumération.

PropriétéTypeDescription
l'année en coursLe type dans lequel le résultat de next() est enveloppé.
wrapObjbooléenLorsque true est le résultat de next() enveloppé dans type.
résultatsTableauUn tableau de résultats à énumérer.
curIdxentierL'indice d'énumération actuel.
MéthodeTypes de retourDescription
constructor(results, wrapObj, type)NamingEnumerationAccepte un tableau de results. Définir wrapObj à true si results doit être enveloppé dans un type avant d'être renvoyé.
hasMore()booléenRenvoie true si l'énumération comporte plus d'éléments.
suivant()Résultat de recherche, Attribut, Chaîne, ObjetRenvoie l'élément suivant de l'énumération. Il est du type attendu.
Référence du SearchResult

Représente une entrée de résultat de recherche.

PropriétéTypeDescription
entréechaîneEntrée du résultat de la recherche.
entry.attributesAttributsAttributs retournés.
entry.namechaîneEntrées d'attributs renvoyées.
MéthodeTypes de retourDescription
getAttributes()AttributsRenvoie un objet Attributs qui contient les attributs retournés par la recherche.
getName()chaîneRenvoie le nom de l'entrée du résultat de la recherche.
Référence LdapAttributeResult

La classe LdapAttributeResult représente une réponse d'opération d'attribut. Propriétés définies pour cette classe :

PropriétéTypeDescription
cfgNamechaîneNom de la configuration du plugin. Il se présente sous la forme <pluginName>_config.
résultatObjetLe résultat de l'opération.
namingEumNamingEnumerationEnumération des résultats de la recherche sous forme d'objets SearchResult
attrsAttributsAttributs de SearchResult.

Méthodes définies pour la classe LdapAttributeResult :

MéthodeTypes de retourDescription
getAttributes()AttributsRenvoie les attributs demandés par LdapAttributeUtil.getAttributeValue().
getNamingEnumeration()NamingEnumeration de SearchResultRenvoie le résultat NamingEnumeration l'opération LdapAttributeUtil.search().
isSuccessful()booléenRenvoie true si l'opération s'est terminée par une erreur.
hasError()booléenRenvoie true si l'opération s'est terminée par une erreur.
getError()ErreurRenvoie l'erreur qui s'est produite.
getNamingException()ErreurAlias pour getError(). Renvoie l'erreur qui s'est produite.

outputData Référence de l'objet

Votre script de plugin doit se terminer par la définition d'un objet appelé outputData. L'objet outputData représente une charge utile de réponse à renvoyer à l'ISV. Il possède les propriétés suivantes :

PropriétéDéfinition
status.resultLe code de résultat.
status.messageUn message d'état facultatif.
parameters.groupsTableau d' objets de groupes ISV auxquels un utilisateur doit appartenir.
utilisateurUn objet dont les objets enfants représentent les paramètres renvoyés par cette opération. Remarque : la valeur de chaque attribut doit être un tableau.
Exemple
// Define this object at the end of your plugin script
outputData = {
  status: {
    result: "Success",
    message: "",
  },
  parameters: {
    groups: [
      {
        name: "AGroup1",
        id: "AGroup1Id1",
      },
      {
        name: "AGroup2",
        id: "AGroup2Id2",
      },
    ],
    user: {
      attribute1: ["value for attribute 1"],
      attribute2: ["value for attribute 2"],
    },
  },
};
Objet de groupe
PropriétéDescription
nomLe site displayName du groupe ISV.
identificateurLe site id du groupe ISV.

Vous pouvez obtenir des identifiants de groupe via le point d'accès ISV /v2.0/Groups.

Exemple :

curl --request GET \
  --url https://tenant.verify.ibm.com/v2.0/Groups \
  --header 'authorization: Bearer <token>'
// Response from /v2.0/Groups
{
  "displayName": "developer",
  "meta": {
    "created": "2023-03-15T04:52:32Z",
    "lastModified": "2023-03-15T05:06:57Z"
  },
  "id": "60500081MT",
  "urn:ietf:params:scim:schemas:extension:ibm:2.0:Group": {
    "totalMembers": 1,
    "groupType": "reserved",
    "description": ""
  }
}

La définition d'objets de groupe permet d'intégrer l'utilisateur renvoyé dans le groupe ISV correspondant.

Démarrage rapide

Cette section contient des exemples de scripts de plugins simplistes/minimaux. Pour utiliser ces exemples, vous devez d'abord définir une configuration de plugin pour pointer vers le script et établir correctement la liaison.

Échafaudage minimal LDAP

L'échafaudage suivant est un plugin LDAP incomplet. La logique d'authentification et de récupération des attributs peut être ajoutée dans la section TODO.

// LDAP minimal scaffold

// Import binding functions
importClass(logger);
importClass(ldapUserLookupHelper);

// Parse Input Objects
let rp = JSON.parse(requestParameters);
let wo = JSON.parse(workingObject);
let pc = JSON.parse(pluginConfig);

// Initialize the UserLookupHelper
let configName = `${pc.pluginName}_config`;
let userLookupHelper = new UserLookupHelper(configName);

// Define the output object to be returned
let output = {
  status: {
    result: "",
    message: "",
  },
  parameters: {
    groups: [],
    user: {},
  },
};

// Inserting main logic inside a function allows for `return` to be used in order to break execution
let process = () => {
  // TODO: Insert plugin logic here
};
process();

// End script by defining the outputData object
let outputData = output;

Exemple de recherche simple LDAP

// simpleLdapLookup.js - Simple

// Import binding functions
importClass(logger);
importClass(ldapUserLookupHelper);

// Parse Input Objects
let rp = JSON.parse(requestParameters);
let wo = JSON.parse(workingObject);
let pc = JSON.parse(pluginConfig);

// Initialize the UserLookupHelper
let configName = `${pc.pluginName}_config`;
let userLookupHelper = new UserLookupHelper(configName);

// Define the output object to be returned
let output = {
  status: {
    result: "",
    message: "",
  },
  parameters: {
    groups: [],
    user: {},
  },
};

// Inserting main logic inside a function allows for `return` to be used in order to break execution
let process = () => {
  // Get the requested user
  let userName = rp.parameters.username;

  // Get the requested attributes
  let requestedAttributes = pc.userAttributes;
  if (!Array.isArray(requestedAttributes)) {
    requestedAttributes = Array.from([requestedAttributes]);
  }

  // Get the user
  let user = userLookupHelper.getUser(userName);

  // Check if user has error
  if (user.hasError()) {
    // Get the error object
    let errorString = user.getError();

    // Log error
    logger.info(`Unable to get user: ${errorString}`);

    // Update the output object with the error
    output.status.result = "ERROR";
    output.status.message = errorString;

    // Return will break execution and cause the output object to return with the above error
    return;
  }

  // Get the requested attributes
  requestedAttributes.forEach((attrName) => {
    // Retrieve this attribute from the user
    output.parameters.user[attrName] = user.getAttribute(attrName);
  });

  // Set the status to successful
  output.status.result = "SUCCESS";
};
process();

// End script by defining the outputData object
let outputData = output;

Exemple d'authentification simple LDAP

Cet exemple ajoute l'authentification à l'exemple de recherche simple LDAP.

Veillez à identifier ce plugin comme authentication source dans la configuration du plugin:

// simpleLdapAuth_config.json
"authenticationSource": {
  "isAuthenticationSource": true,
  "disablePrimaryLDAPLookup": true
},
// simpleLdapAuth.js

// Import binding functions
importClass(logger);
importClass(ldapUserLookupHelper);

// Parse Input Objects
let rp = JSON.parse(requestParameters);
let wo = JSON.parse(workingObject);
let pc = JSON.parse(pluginConfig);

// Initialize the UserLookupHelper
let configName = `${pc.pluginName}_config`;
let userLookupHelper = new UserLookupHelper(configName);

// Define the output object to be returned
let output = {
  status: {
    result: "",
    message: "",
  },
  parameters: {
    groups: [],
    user: {},
  },
};

// Inserting main logic inside a function allows for `return` to be used in order to break execution
let process = () => {
  // Get the requested user
  let userName = rp.parameters.username;
  let password = rp.parameters.password;

  // Get the requested attributes
  let requestedAttributes = pc.userAttributes;
  if (!Array.isArray(requestedAttributes)) {
    requestedAttributes = Array.from([requestedAttributes]);
  }

  // Get the user
  let user = userLookupHelper.getUser(userName);

  // Check if user has error
  if (user.hasError()) {
    // Get the error object
    let errorString = user.getError();

    // Log error
    logger.info(`Unable to get user: ${errorString}`);

    // Update the output object with the error
    output.status.result = "ERROR";
    output.status.message = errorString;

    // Return will break execution and cause the output object to return with the above error
    return;
  }

  // Perform authentication
  if (user.authenticate(password) !== true) {
    output.status.result = "PASSWORD_NOT_VALID";
    output.status.message = "Invalid user password";
    return;
  }

  // Get the requested attributes
  requestedAttributes.forEach((attrName) => {
    // Retrieve this attribute from the user
    output.parameters.user[attrName] = user.getAttribute(attrName);
  });

  // Set the status to successful
  output.status.result = "SUCCESS";
};
process();

// End script by defining the outputData object
let outputData = output;

Notez que cet exemple ne diffère de l'exemple de recherche LDAP simple que par l'ajout de l'authentification :

// Perform authentication
if (user.authenticate(password) !== true) {
  output.status.result = "PASSWORD_NOT_VALID";
  output.status.message = "Invalid user password";
  return;
}

Exemple d'utilitaire d'attribut LDAP

Cet exemple montre comment utiliser la classe LdapAttributeUtil pour définir, récupérer et rechercher des attributs LDAP. La sortie est donnée par l'intermédiaire de l'enregistreur. L' objet outputData ne contient aucun attribut.

// LdapAttributeUtil Example

// Import binding functions
importClass(logger);
importClass(ldapUserLookupHelper);
importClass(ldapAttributeUtil);


// Parse Input Objects
let rp = JSON.parse(requestParameters);
let wo = JSON.parse(workingObject);
let pc = JSON.parse(pluginConfig);

// Initialize the UserLookupHelper
let configName = `${pc.pluginName}_config`;
let userLookupHelper = new UserLookupHelper(configName);
let attrUtil = new LdapAttributeUtil(configName)


// Define the output object to be returned
let output = {
  status: {
    result: "",
    message: "",
  },
  parameters: {
    groups: [],
    user: {},
  },
};

// Inserting main logic inside a function allows for `return` to be used in order to break execution
let process = () => {

  if (!attrUtil.isReady()) {
    output.status.result = "ERROR";
    output.status.message = "Attribute Utility failed healthcheck";
    return;
  }

  // Random integer for sub context
  let rand = Math.floor(Math.random() * 100);
  let newOu = `ou=newOu${rand}`;

  // Create a new sub context
  let resultCreateSubContextErr = attrUtil.createSubContext(newOu, {"objectClass": ["top", "organizationalUnit"]});
  if (resultCreateSubContextErr.hasError()) {
    let error = resultCreateSubContextErr.getError();
    output.status.result = "ERROR";
    output.status.message = error;
    return;
  }
  logger.info(`Created sub context entry ${newOu}`);

  // Create an attribute
  let attributeName = "description";
  let attributeValue = "1234";
  let attributeValue2 = "4567";
  let resultAddAttributeValue = attrUtil.addAttributeValue(newOu, attributeName, attributeValue);
  if (resultAddAttributeValue.hasError()) {
    let error = resultAddAttributeValue.getError();
    output.status.result = "ERROR";
    output.status.message = error;
    return;
  }
  logger.info(`Added attribute ${attributeName} with value ${attributeValue} to the entry ${newOu}`);

  // Validate that the entry has been written successfully by retrieving the value
  let resultGetAttributeValue = attrUtil.getAttributeValue(newOu, ['description']);
  if (resultGetAttributeValue.hasError()) {
    let error = resultGetAttributeValue.getError();
    output.status.result = "ERROR";
    output.status.message = error;
    return;
  }

  // Check value is correct
  let retrievedAttr = resultGetAttributeValue.getAttributes().get(attributeName).get(0);
  if (retrievedAttr !== attributeValue) {
    let error = `Retrieved attribute ${retrievedAttr}, expected ${attributeValue}`;
    logger.info(error);
    output.status.result = "ERROR";
    output.status.message = error;
    return;
  }
  logger.info(`Retrieved attribute ${retrievedAttr}, matches expected ${attributeValue}`);

  // Modify the new entries description value
  let resultSetAttributeValue = attrUtil.setAttributeValue(newOu, attributeName, attributeValue2);
  if (resultSetAttributeValue.hasError()) {
    let error = resultSetAttributeValue.getError();
    output.status.result = "ERROR";
    output.status.message = error;
    return;
  }
  logger.info(`Set entry ${newOu} attribute ${attributeName} to value ${attributeValue2}`);

  // Remove the description value
  let resultRemoveAttribute = attrUtil.removeAttribute(newOu, attributeName, attributeValue2);
  if (resultRemoveAttribute.hasError()) {
    let error = resultRemoveAttribute.getError();
    output.status.result = "ERROR";
    output.status.message = error;
    return;
  }
  logger.info(`Removed attribute ${attributeName} with value ${attributeValue2} from entry ${newOu}`);

  // Perform a search
  let resultSearch = attrUtil.search(newOu, "(objectclass=top)");
  if (resultSearch.hasError()) {
    let error = resultSearch.getError();
    output.status.result = "ERROR";
    output.status.message = error;
    return;
  }
  logger.info(`Removed attribute ${attributeName} with value ${attributeValue2} from entry ${newOu}`);

  // Enumerate through the search results
  let searchEnumerator = resultSearch.getNamingEnumeration();
  let i = 0;
  while (searchEnumerator.hasMore()) {
    let searchResult = searchEnumerator.next();
    logger.info(`Search results [${i}]: ${searchResult.getName()}`);
  }


  // Set the status to successful
  output.status.result = "SUCCESS";
};
process();

// End script by defining the outputData object
let outputData = output;