diff --git a/package-lock.json b/package-lock.json index d7ced642eeedb6670a9305b29877fbab913c38e7..d460047415b5ce13dc14a8bbd5ee78aa67186bbd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -94,7 +94,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, @@ -4814,8 +4814,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -4836,14 +4835,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4858,20 +4855,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -4988,8 +4982,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -5001,7 +4994,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5016,7 +5008,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5024,14 +5015,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -5050,7 +5039,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -5131,8 +5119,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -5144,7 +5131,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -5230,8 +5216,7 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -5267,7 +5252,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -5287,7 +5271,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -5331,14 +5314,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -10066,7 +10047,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true } } diff --git a/package.json b/package.json index 0f5d90a32080aa234be39b3dfe59ebec2c78b529..d5071064846dc4d5af25e77c7b8692af2399edc6 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "tslint": "tslint --project tsconfig.json", "tsfix": "tslint --project tsconfig.json --fix", "tsc": "tsc --project tsconfig.json", + "ldap_test": "node --require ts-node/register src/ldap/test.ts", "test": "mocha --require mocha-graphql-register --require ts-node/register test/**/*.test.ts --timeout 20000" }, "repository": { @@ -70,6 +71,7 @@ "@types/connect-flash": "0.0.34", "@types/graphql": "^14.0.7", "@types/knex": "^0.15.2", + "@types/ldapjs": "^1.0.3", "@types/mocha": "^5.2.6", "@types/node": "^11.9.5", "@types/passport": "^1.0.0", diff --git a/src/ldap/export/group.ts b/src/ldap/export/group.ts index 60134bddbef35d4986bd14ab5ca616a1bca0a198..f70b22f418476d853350daf1ebc7744ee54493c7 100644 --- a/src/ldap/export/group.ts +++ b/src/ldap/export/group.ts @@ -210,7 +210,7 @@ export class Group { // gid de base généré à partir du nom standardisé, pas à partir de l'entrée 'gid' ! try { - Tools.generateReadableId("group", data['name']).then(id => { + Tools.generateGid(data['name']).then(id => { vals[ldapConfig.group.gid] = id; vals[ldapConfig.group['name']] = id; }); diff --git a/src/ldap/export/user.ts b/src/ldap/export/user.ts index e6e22ad6fa596465b01ab80cf169c60876c3995e..5c221545fb04333cbe180105c10ff5e882c6acf2 100644 --- a/src/ldap/export/user.ts +++ b/src/ldap/export/user.ts @@ -119,7 +119,7 @@ export class User { // uid de base généré à partir de nom et prénom, plus potentiellement promo et un offset // MEF mélange de Promise et de fonction standard try { - Tools.generateUid("user",data['givenName'],data['lastName'],data['birthdate']).then(id => { vals[ldapConfig.user.uid]=id; } ); + Tools.generateUid(data['givenName'],data['lastName'],data['birthdate']).then(id => { vals[ldapConfig.user.uid]=id; } ); } catch(err) { throw "Erreur lors de la génération d'un hruid pour un nouvel utilisateur."; diff --git a/src/ldap/internal/basics.ts b/src/ldap/internal/basics.ts index 7dc90777e1daee1aaf0fa23054cb94705cb57ed3..4ca0bd2bf624c85fcf14f9d7937ff7219941c7dd 100644 --- a/src/ldap/internal/basics.ts +++ b/src/ldap/internal/basics.ts @@ -7,19 +7,18 @@ * @memberof LDAP */ -import ldap from 'ldapjs'; +// Import moche à cause mauvais typage ldapjs +var ldap:any = require('ldapjs'); // Toutes les entrées utilisateur sont escapées par sécurité import ldapEscape from 'ldap-escape'; // Fichier de ldapConfig du ldap import {ldapConfig, credentialsLdapConfig} from './config'; // Connection au serveur LDAP avec des temps de timeout arbitraires -var client = ldap.createClient({ url: ldapConfig.server}); +var client = ldap.createClient({ url: ldapConfig.server }); // Interface pratique pour que Typescript comprenne ce qu'est un dictionnaire simple -interface dic { - [Key: string]: string | string[]; -} +interface dic { [Key: string]: string | string[]; } //------------------------------------------------------------------------------------------------------------------------ // Fonctions de base agissant sur le LDAP @@ -36,48 +35,28 @@ export class Basics { /** * @memberof LDAP - * @summary Fonction qui sert à s'identifier sur le LDAP. - * @desc Assez important en terme de sécurité, de gestion de conflit, et de droit d'accès. Méthode ldapjs - * (voir [`Client API`](http://ldapjs.org/client.html) méthode bind). - * @arg {string} dn - Nom de domaine ; identifiant de l'utilisateur cherchant à se connecter - * @arg {string} password - Mot de passe de l'utilisateur cherchant à se connecter + * @summary Fonction qui sert à se déconnecter du LDAP, puis s'identifier sur le LDAP avec pleins pouvoirs. + * @desc Assez important en terme de sécurité, de gestion de conflit, et de droit d'accès. + * Fait appel à une méthode ldapjs (voir [`Client API`](http://ldapjs.org/client.html) méthode bind). * @returns {Promise(boolean)} `true` si l'opération s'est bien déroulée, `false` sinon. * @static - * @async */ - static async bind(dn: string, password: string) : Promise<boolean> { - // Escape DN as everywhere in this file, but password is taken as is - client.bind(dn, password, res => { - // Gestion erreur - try { res; } - catch(err) { throw "Erreur lors de la connection au LDAP.";} + static bind() : Promise<boolean> { + return new Promise<boolean>((resolve, reject) => { + // Se déconnecter dans le doute + client.unbind(); + // Escape DN as everywhere in this file, but password is taken as is + client.bind(credentialsLdapConfig.dn, credentialsLdapConfig.password, err => { + // Gestion erreur + if (err instanceof ldap.LDAPError) { + console.log("Erreur lors de la connection au LDAP : "+err.message); + resolve(false); + } + resolve(true); + }); }); - // End with a boolean - return true; } - /** - * @memberof LDAP - * @summary Fonction qui sert à s'identifier sur le LDAP avec plein pouvoirs. - * @desc Appelle {@link bind} avec un utilisateur tout puissant. - * @returns {Promise(boolean)} `true` si l'opération s'est bien déroulée, `false` sinon. - * @static - * @async - */ - static async adminBind() : Promise<boolean> { return Basics.bind(credentialsLdapConfig.dn, credentialsLdapConfig.password); } - - /** - * @memberof LDAP - * @summary Fonction qui sert à se déconnecter du LDAP. - * @desc Assez important en terme de sécurité, de gestion de conflit, et de droit d'accès. - * Fait appel à {@link Basics.bind} avec deux champs vides. - * @returns {Promise(boolean)} `true` si l'opération s'est bien déroulée, `false` sinon. - * @static - * @async - */ - static async unbind() : Promise<boolean> { return Basics.bind("", ""); } - - /** * @memberof LDAP * @callback entryHandler @@ -96,10 +75,9 @@ export class Basics { * @arg {entryHandler} handler - Wrapper pour gérer les requêtes simples ou multiples * @return {void} Utilise handler pour gérer ses résultats au fur et à mesure * @static - * @async */ - static search(domain: 'group'|'user', attributes: string[], id: string, filter: string, handler : (entry: any) => void) : Promise<void> { - let dn =""; + static search(domain: 'group'|'user', attributes: string[], id: string, filter: string, handler : (entry: any) => void) : Promise<boolean> { + let dn = ""; if (id != null) { if (domain == "group") dn += ldapConfig.group.gid; else dn += ldapConfig.user.uid; @@ -108,27 +86,33 @@ export class Basics { dn += ldapConfig.dn[domain]; console.log("Searching dn= " + dn + ", filter : " + filter); // Interrogation LDAP selon filter - let promise = new Promise<void>(function(resolve, reject) { - client.search(dn, { // Must be escaped in case of a malignious false id + return new Promise<boolean>(function(resolve, reject) { + client.search(dn, { // Must be escaped in case of a malignious false id "scope": "sub", - "filter": filter, // Must be escaped in case of a malignious search arg + "filter": filter, // Must be escaped in case of a malignious search arg "attributes": attributes }, (err, res) => { // Gestion erreur ; pb car pas simple true / autre en sortie - if (err) { - throw "Erreur lors de la recherche sur le LDAP."; + if (Basics.catch(err)) { + console.log("Erreur lors de la recherche sur le LDAP."); + resolve(false); } else { // Dès que la recherche renvoit une entrée, on stocke les attributs qui nous intéresse res.on('searchEntry', entry => handler(entry)); - // Si la recherche renvoie une erreur, on renvoit - res.on('error', resErr => { throw resErr; }); + // Si la recherche renvoie une erreur (client ou TCP seulement), on renvoit + res.on('error', err2 => { + console.log(err2); + resolve(false); + }); // Quand la recherche est finie on se déconnecte - res.on('end', _ => resolve()); + res.on('end', res2 => { + // Si la co avec le LDAP est tombée on relance + if (res2.status == 0) Basics.bind(); + resolve(true); + }); } }); }); - - return promise; } /** @@ -180,6 +164,35 @@ export class Basics { return vals; } + /** + * @memberof LDAP + * @summary Fonction intermédiaire qui factorise la gestion d'erreur pour toute la classe. + * @desc Permet de définir un comportement par défaut pour les méthodes de base add, change et clear pour différents types d'erreur. + * @arg {ldap.LDAPError} err - Erreur renvoyée + * @returns {Promise(boolean)} `true` si l'opération s'est bien déroulée, `false` sinon. + * @static + */ + static catch(err) : boolean { + if (err instanceof ldap.LDAPError) { + console.log("L'erreur suivante est survenue : " +err.message); + // TBC + if (err instanceof ldap.TimeLimitExceededError) { + Basics.bind(); + } + else if (err instanceof ldap.ProtocolError) { + Basics.bind(); + } + else if (err instanceof ldap.SizeLimitExceededError) { + Basics.bind(); + } + else if (err instanceof ldap.InsufficientAccessRightsError) { + Basics.bind(); + } + return true; + } + return false; + } + /** * @memberof LDAP * @summary Fonction qui permet de modifier un élément sur le LDAP. Gestion intelligente de l'appartenance à un binet. @@ -192,20 +205,26 @@ export class Basics { * @arg {string} mod[key] - Nouvelle valeur de l'attribut key. Une nouvelle valeur vide ("") est équivalent à la suppression de cet attribut. * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, `false` sinon. * @static - * @async */ - static async change(domain: 'group'|'user', id: string, op: "add"|"del"|"replace", mod: dic) : Promise<boolean> { - let dn = ""; - if (domain == 'group') dn += ldapConfig.group.gid; - else dn += ldapConfig.user.uid; - dn += '='+ldapEscape.dn("${txt}", { txt: id })+','+ldapConfig.dn[domain]; - // Modification LDAP selon dn fourni en argument (pourrait prendre une liste de Changes) - client.modify(ldapEscape.dn("${txt}", {txt: dn}), new ldap.Change({ - operation: op, - modification: mod, - // Gestion erreur - }), err => { throw "Erreur lors d'une opération de modification sur le LDAP."; }); - return true; + static change(domain: 'group'|'user', id: string, op: "add"|"del"|"replace", mod: dic) : Promise<boolean> { + return new Promise<boolean>((resolve, reject) => { + let dn = ""; + if (domain == 'group') dn += ldapConfig.group.gid; + else dn += ldapConfig.user.uid; + dn += '='+ldapEscape.dn("${txt}", { txt: id })+','+ldapConfig.dn[domain]; + // Modification LDAP selon dn fourni en argument (pourrait prendre une liste de Changes) + client.modify(ldapEscape.dn("${txt}", {txt: dn}), new ldap.Change({ + operation: op, + modification: mod, + // Gestion erreur + }), err => { + if (Basics.catch(err)) { + console.log("Erreur lors d'une opération élémentaire de modification dans le LDAP (Basics.change)"); + resolve(false); + } + resolve(true); + }); + }); } /** @@ -218,16 +237,22 @@ export class Basics { * @arg {Object} vals[key] - Nouvelle valeur pour le champ key * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon. * @static - * @async */ - static async add(domain: 'group'|'user', vals) : Promise<boolean> { - let dn = ""; - if (domain == "group") dn += ldapConfig.group.gid+"="+ldapEscape.dn("${txt}", { txt: vals[ldapConfig.group.gid] }); - else dn += ldapConfig.user.uid+"="+ldapEscape.dn("${txt}", { txt: vals[ldapConfig.user.uid] }); - dn += ldapConfig.dn[domain]; - // Ajout LDAP selon la ldapConfiguration en argument - client.add(ldapEscape.dn("${txt}", { txt: dn }), vals, err => { throw "Erreur lors d'une opération d'ajout sur le LDAP."; }); - return true; + static add(domain: 'group'|'user', vals) : Promise<boolean> { + return new Promise<boolean>((resolve, reject) => { + let dn = ""; + if (domain == "group") dn += ldapConfig.group.gid+"="+ldapEscape.dn("${txt}", { txt: vals[ldapConfig.group.gid] }); + else dn += ldapConfig.user.uid+"="+ldapEscape.dn("${txt}", { txt: vals[ldapConfig.user.uid] }); + dn += ldapConfig.dn[domain]; + // Ajout LDAP selon la ldapConfiguration en argument + client.add(dn, vals, err => { + if (Basics.catch(err)) { + console.log("Erreur lors d'une opération élémentaire d'ajout dans le LDAP (Basics.add)"); + resolve(false); + } + resolve(true); + }); + }); } /** @@ -239,21 +264,26 @@ export class Basics { * @arg {string} id - Identifiant unique de la cible, passé par ldapEscape dans cette fonction * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon * @static - * @async */ - static async clear(domain: 'group'|'user', id: string) : Promise<boolean> { - let dn = ""; - if (domain == "group") dn += ldapConfig.group.gid+"="+ldapEscape.dn("${txt}", { txt: id }); - else dn += ldapConfig.user.uid+"="+ldapEscape.dn("${txt}", { txt: id }); - dn += ldapConfig.dn[domain]; - // Suppression LDAP - client.del(ldapEscape.dn("${txt}", {txt: dn}), err => { throw "Erreur lors d'une opération de suppression sur le LDAP."; }); - return true; + static clear(domain: 'group'|'user', id: string) : Promise<boolean> { + return new Promise<boolean>((resolve, reject) => { + let dn = ""; + if (domain == "group") dn += ldapConfig.group.gid+"="+ldapEscape.dn("${txt}", { txt: id }); + else dn += ldapConfig.user.uid+"="+ldapEscape.dn("${txt}", { txt: id }); + dn += ldapConfig.dn[domain]; + // Suppression LDAP + client.del(dn, err => { + if (Basics.catch(err)) { + console.log("Erreur lors d'une opération élémentaire de suppression dans le LDAP (Basics.clear)"); + resolve(false); + } + resolve(true); + }); + }); } } // Bind -Basics.unbind(); -Basics.adminBind(); +Basics.bind(); -console.log("Binding with LDAP client completed successfully, looking good !"); \ No newline at end of file +console.info("Binding with LDAP client completed successfully, looking good !"); \ No newline at end of file diff --git a/src/ldap/internal/config.ts b/src/ldap/internal/config.ts index 2e25dc197f57effcd0d44b5a315c69e1e87faef9..b0b662db47ade1cd464035f71e7e6e9aaab6eca7 100644 --- a/src/ldap/internal/config.ts +++ b/src/ldap/internal/config.ts @@ -15,12 +15,12 @@ import dotenv from 'dotenv'; // Chargement de l'environnement let path_env = path.resolve(__dirname, '..', '..', '..', './.env'); -console.log(colors.red("Loading .env config file from "+path_env)); +console.info(colors.red("Loading .env config file from "+path_env)); dotenv.config({ path: path_env }); // Point central ; tous les champs de la BDD sont 'cachés' dans config.json et pas visibles directement let path_config = path.resolve(__dirname, '..', '..', '..', './ldap_config.json'); -console.log(colors.cyan("Loading LDAP config file from "+path_config)); +console.info(colors.cyan("Loading LDAP config file from "+path_config)); export const ldapConfig = JSON.parse(fs.readFileSync(path_config).toString()); // Override config server from environment @@ -32,7 +32,7 @@ else { // Gestion des super-identifiants let path_credentials = path.resolve(__dirname, '..', '..', '..', 'ldap_credentials.json'); -console.log(colors.green("Loading LDAP credentials from "+path_credentials)); +console.info(colors.green("Loading LDAP credentials from "+path_credentials)); export const credentialsLdapConfig = JSON.parse(fs.readFileSync(path_credentials).toString()); // Data formats and useful constants diff --git a/src/ldap/internal/tools.ts b/src/ldap/internal/tools.ts index 0846ad04c694c7d3f4d1657e5aca0836a063052d..4ade5e83d4ce53610ff16f5681d442e6792143f7 100644 --- a/src/ldap/internal/tools.ts +++ b/src/ldap/internal/tools.ts @@ -471,7 +471,6 @@ export class Tools { * @memberof LDAP * @summary Cette fonction génère un uid standard, puis le fait évoluer jusqu'à ce qu'il soit unique. * @desc Limité à un appel à {@link Tools.ensureUnique} avec les bons paramètres, et quelques opérations sur l'uid pour qu'il soit valide (escape, normalisation). - * @param {"group"|"user"} domain - Arbre à parcourir * @param {string} givenName - Prénom * @param {string} lastName - Nom * @param {string} promotion - Année de promotion @@ -479,12 +478,10 @@ export class Tools { * @static * @async */ - static async generateUid(domain : "group"|"user", givenName: string, lastName: string, promotion: string) : Promise<string> { + static async generateUid(givenName: string, lastName: string, promotion: string) : Promise<string> { try { - if (domain == "group") var att=ldapConfig.group.gid; - else var att=ldapConfig.user.uid; // normalize et lowerCase standardisent le format - return Tools.ensureUnique((givenName+'.'+lastName).toLowerCase().normalize('UFD'), att, "user", (id: string, n: number) => { + return Tools.ensureUnique((givenName+'.'+lastName).toLowerCase().normalize('UFD'), ldapConfig.user.uid, "user", (id: string, n: number) => { if (n=1) id+='.'+promotion; // Si prénom.nom existe déjà , on rajoute la promo else if (n=2) id+='.'+(n-1).toString(); // Puis si prénom.nom.promo existe déjà on passe à nom.prenom.promo .1 else if (n>2) id+=(n-1).toString(); // Ensuite on continue .123, .1234, etc... @@ -492,33 +489,30 @@ export class Tools { }); } catch(err) { - throw "Erreur lors de l'assurance de l'unicité d'un human readable unique identifier (hruid)."; + throw "Erreur lors de l'assurance de l'unicité d'un human readable unique identifier pour un utilisateur (hruid) (Tools.generateUid)."; } } /** * @memberof LDAP - * @summary Cette fonction génère un id lisible, puis le fait évoluer jusqu'à ce qu'il soit unique. - * @desc Limité à un appel à {@link Tools.ensureUnique} avec les bons paramètres, et quelques opérations sur l'uid pour qu'il soit valide (escape, normalisation). - * @param {"group"|"user"} domain - Arbre à parcourir + * @summary Cette fonction génère un gid lisible, puis le fait évoluer jusqu'à ce qu'il soit unique. + * @desc Limité à un appel à {@link Tools.ensureUnique} avec les bons paramètres, et quelques opérations sur le gid pour qu'il soit valide (escape, normalisation). * @param {string} name - Nom * @return {Promise(string)} Valeur unique dans le domaine spécifié de l'attribut spécifié * @static * @async */ - static async generateReadableId(domain : "group"|"user", name: string) : Promise<string> { + static async generateGid(name : string) : Promise<string> { try { - if (domain == "group") var att=ldapConfig.group.gid; - else var att=ldapConfig.user.uid; // normalize et lowerCase standardisent le format - return Tools.ensureUnique(name.toLowerCase().normalize('UFD'), att, domain, (id: string, n: number) => { + return Tools.ensureUnique(name.toLowerCase().normalize('UFD'), ldapConfig.group.gid, "group", (id: string, n: number) => { if (n=1) id+='.'+n.toString(); // Si nom existe déjà , on essaie nom.1 else if (n>1) id+=n.toString(); // Ensuite on continue .12, .123, etc... return id; }); } catch(err) { - throw "Erreur lors de l'assurance de l'unicité d'un human readable unique identifier (hruid)."; + throw "Erreur lors de l'assurance de l'unicité d'un human readable unique identifier pour un groupe (hrgid) (Tools.generateGid)."; } } diff --git a/src/ldap/test.js b/src/ldap/test.js deleted file mode 100644 index 832f1fee2566b21bbcba60cf0552242b64903ad0..0000000000000000000000000000000000000000 --- a/src/ldap/test.js +++ /dev/null @@ -1,6 +0,0 @@ -var Group = require("../../tsbuild/src/ldap/export/group").Group; -console.log(Group); - -Group.peek("faerix").then(dat => { console.log(dat); }); - -var User = require("../../tsbuild/src/ldap/export/group"); \ No newline at end of file diff --git a/src/ldap/test.ts b/src/ldap/test.ts new file mode 100644 index 0000000000000000000000000000000000000000..baa8af68202bff41e261c208b0f42dcc4c900d23 --- /dev/null +++ b/src/ldap/test.ts @@ -0,0 +1,14 @@ +import {Group} from './export/group'; + +async function test_creation() { + let dat = await Group.peek("faerix"); + console.log(dat); + dat.gid = "faerix2"; + Group.create(dat); +} + +import {User} from './export/user'; + +User.peek("oliver.facklam").then(dat => console.log(dat)); + +test_creation(); \ No newline at end of file