Recherche d'utilisateurs
Ability to get all the users (or groups) from an ISV tenant using the REST API calls.
Introduction
La gestion d'un grand nombre d'utilisateurs ou de groupes peut s'avérer une tâche ardue. Les API REST fournies par l'ISV concilient performance et facilité d'utilisation, ce qui signifie qu'une recherche donnée pour un ensemble d'utilisateurs peut renvoyer au maximum 2500 utilisateurs à la fois.
Cet article a pour but de fournir un algorithme pour récupérer tous les utilisateurs (groupes) du locataire, et de montrer une implémentation de cet algorithme en javascript et nodejs.
Prérequis
Pour commencer, vous avez besoin d'un locataire ISV, d'un grand nombre d'utilisateurs (plus de 2500) et d'être familiarisé avec les appels de l'API REST.
L'obtention d'un jeton d'accès et les appels REST pour les recherches d'utilisateurs (opération GET sur /v2.0/Users ) exploitant les filtres basés sur le SCIM devraient être donnés.
Algorithme
L'algorithme que nous présentons ici s'appuie sur le fait que chaque utilisateur créé dans le registre ISV possède une date de création. En d'autres termes, nous savons que tous les utilisateurs existent dans une liste de commande sur la base de la date de création.
Un fait important de cette liste est qu'il peut y avoir des utilisateurs qui ont la même date de création, c'est-à -dire que tout utilisateur créé au cours de la même seconde a la même date.
Nous pouvons désormais effectuer une recherche ordonnée (par date de création) et effectuer des recherches simples pour différents groupes de données.
L'une des complications consiste à trouver le début et la fin des blocs de données. Nous définissons une fin initiale pour chaque morceau en demandant simplement 2000 utilisateurs.
Le premier départ est simple, nous partons de quelque part dans le passé, avant que notre locataire ISV n'existe.
Trouver le deuxième point de départ est plus compliqué, car nous devons trouver un nouveau point de départ de manière à ce qu'il n'y ait pas de chevauchement, en d'autres termes, nous avons besoin d'une date de création qui ne soit pas partagée par les utilisateurs à travers les pauses.
Nous avons donc l'algorithme suivant pour la recherche de chaque page :
- Définir une liste complète d'utilisateurs comme vide
- Effectuer une recherche d'utilisateurs, classée par date de création, à partir d'une date SD donnée (initialement une date dans le passé) et obtenir 2000 utilisateurs.
- Lorsque la recherche renvoie moins de 2000 utilisateurs, nous savons que nous avons atteint la fin de la recherche
2. 2.a Ajoutez les utilisateurs renvoyés à la liste complète des utilisateurs et vous avez terminé - La recherche a donné 2000 utilisateurs, ce qui signifie qu'il nous reste d'autres utilisateurs à rechercher.
Si nous regardons la fin de la liste, nous remarquons que les deux dernières entrées ont exactement le même horodatage. En fait, pour tout retour, nous avons les considérations ou possibilités suivantes :
- La fin a plusieurs utilisateurs avec la même date
- Il y a d'autres utilisateurs avec la même date que nous avons manqués (en d'autres termes, l'utilisateur suivant après Jane a également la même date)
En d'autres termes, nous ne pouvons pas nous contenter de déclarer qu'il faut commencer après la date de création de Jane, nous devons trouver une solution de rechange.
En regardant la fin de la liste ci-dessus, nous voulons commencer la recherche avec la date de création par jerry. Cela signifierait que nous aurions à nouveau Jerry et Jane, alors supprimez-les de la liste.
Voici une représentation graphique de cette situation :
L'algorithme est le suivant :
- Tant que le dernier et l'avant-dernier ont la même date de création, laissez tomber le dernier (nous pouvons en laisser tomber plusieurs)
- Maintenant que la dernière et l'avant-dernière ont des dates différentes, il faut le faire :
- Enregistrer la dernière date de création comme nouvelle date de début de la prochaine recherche
- Laissez tomber le dernier car il sera le premier dans la prochaine recherche.
Ensuite, nous ajouterons la liste restante à notre liste complète d'utilisateurs. Répétez la recherche avec la nouvelle date de début, en répétant le processus pour la page de données suivante.
Il est à noter que pour chaque page, nous obtenons 2000 utilisateurs, mais que nous en ajouterons toujours un peu moins à la liste globale, afin de trouver un "vrai" point de rupture.
Exemple de code
Les extraits de code suivants montrent la mise en œuvre de l'algorithme décrit en javascript (nodejs).
La première méthode est le point de départ qui définit les variables nécessaires et lance une boucle jusqu'à ce que nous atteignions la fin de notre liste.
Il convient de noter que la méthode autorise trois paramètres d'entrée :
filter est un filtre optionnel basé sur scim, qui peut être utilisé pour affiner la liste d'utilisateurs renvoyée, un exemple de filtre est : 'userName sw "a"'
stats est un objet qui contient des informations statistiques
grps est un drapeau indiquant si nous retournons également les groupes dont l'utilisateur est membre
// get all users by filter
async getAllUserbyFilter(filter, stats, grps=false) {
// the following object structure contains the information we need for each loop
// filter is the filter passed in
// more will be true until we read the last page (aka less then 2k users)
// masterlist is the overall result list
// block is the starting point for a search
let res = {
filter: filter,
more: true,
masterlist: [],
block: '2000-01-01T00:00:00Z'
}
// We keep searching and adding to the master list until more is false
// we pass our control structure, stats and the grps flag
while ( res.more ) {
await this.userpage(res, stats, grps);
}
// return the resulting list
return res.masterlist;
}
La méthode suivante permet d'obtenir une "page" et de définir les variables pour la page suivante.
async userpage(res, stats, grps=false) {
// increment and log our page count, also display a message to the screen showing we are working !
stats.page++;
log.debug('PAGE: ', stats.page);
console.log('Page: ', stats.page);
// we need a masterfilter
let mf;
// check if filter is empty
// build the masterfilter we will use for the actual search
// if an additional filter is provided, add it
// build a filter based on the current starting point, order by date, ascending, and return 2k users
if (res.filter == '') {
mf = 'meta.created gt \"' + res.block + '\"&sortBy=meta.created&sortOrder=ascending&count=2000';
} else {
mf = '((meta.created gt \"' + res.block + '\") and (' + res.filter + '))&sortBy=meta.created&sortOrder=ascending&count=2000';
}
// Make a call to the actual rest api to get a list of users based on the master filter, passing grps
let list = await this.getUserbyFilter(mf, grps);
// now also check make sure we have some groups or an error
if (list == null) {
console.log('Failure in searching!');
process.exit();
}
// We now check if the list returned is less then 2000!
if (list.totalResults < 2000) {
// we have no more searches to do, so set more to false
res.more = false;
} else {
// we have more searches to do so find a new starting point!
// remember that arrays are 0 based so the last element is length-1, etc.
// the following while loop simply compares last to last-1 and if they are the same pop it
while (list.Resources[list.Resources.length-1].meta.created == list.Resources[list.Resources.length-2].meta.created) {
list.Resources.pop();
//log.trace('>>>>',list.Resources[list.Resources.length-1].meta.created, ' == ', list.Resources[list.Resources.length-2].meta.created )
//log.trace('>>>>Length: ', list.Resources.length);
}
// we are now in the situation that the last and last-1 are different
// we need to pop one more and record the new starting block
res.block = list.Resources[list.Resources.length-1].meta.created;
list.Resources.pop();
}
// add the number of users in the remaining list to the total count and log the page count
stats.got += list.Resources.length;
log.debug('Page Count: ', list.Resources.length);
// combine with the master list and now log the overall count
res.masterlist = [...res.masterlist, ...list.Resources];
log.trace('masterlist size: ', res.masterlist.length);
}
Les extraits de code ci-dessus sont rédigés pour les utilisateurs, mais peuvent facilement être adaptés aux groupes.
Martin Schmidt, IBM Security
Updated about 1 month ago