Skip to content
Snippets Groups Projects
Forked from an inaccessible project.
user.ts 9.87 KiB
/**
 * @file Ce fichier contient la classe de l'API du LDAP qui gère les opérations sur les groupes. Elle est destinée à être exportée pour être utilisée par les resolvers.
 * @author hawkspar
 */

import { ldapConfig, userData, categories } from '../internal/config';
import {Basics} from '../internal/basics';
import {Tools} from '../internal/tools';
import ldapEscape from 'ldap-escape';

//------------------------------------------------------------------------------------------------------------------------
// Classes à exporter TBT
//------------------------------------------------------------------------------------------------------------------------

export {userData};

export class User {
    /**
     * @memberof LDAP
     * @class User
     * @classdesc Cette classe est une des deux classes exportables permettant de faire des opérations sur les utilisateurs.
     * @summary Constructeur vide.
     */
    constructor() {}
     
    /**
     * @memberof LDAP
     * @summary Fonction qui renvoit les infos de base relatives à un utilisateur particulier.
     * @desc Cette fonction utilise {@link Tools.peek} avec l'interface {@link userData}.
     * @arg {string} uid - Identifiant de l'utilisateur, supposé valide.
     * @return {Promise(userData)} Informations recueillies au format {@link userData}.
     * @static
     * @async
     */
    static async peek(uid: string) : Promise<userData> {
        try { 
            let data : userData = await Tools.peek<userData>("user", uid, userData);
            // Reconstruction des groupes dont un utilisateur est membre
            for (let cat of categories) { 
                let dn = ldapConfig.user.uid + "=" + ldapEscape.filter("${txt}", { txt: uid }) + "," + ldapConfig.dn.user;
                data[cat] = await Basics.searchSingle("group", ldapConfig.group.gid, null, ldapConfig.group[cat] + "=" + dn);
            }
            return data;
        }
        catch(err) {
            throw "Error while peeking a user.";
        }
    }
    
    /**
     * @memberof LDAP
     * @summary Fonction qui retrouve les uid des paxs validant les critères de recherche. Utiliser {@link User.peek} au cas par cas après pour obtenir les vraies infos.
     * @desc Cette fonction utilise {@link Tools.search}.
     * @arg {userData} data - Dictionnaire contenant les données nécessaires à la recherche. Les valeurs sont celles entrées par l'utilisateur et sont par hypothèse
     * comme des sous-parties compactes des valeurs renvoyées. Tous les champs ci-dessous peuvent être indifféremment des listes (par exemple pour chercher un membre
     * de plusieurs groupes) ou des éléments isolés. Si un champ n'est pas pertinent, le mettre à '' ou undefined.
     * @return {Promise(string[])} gids des profils qui "match" les critères proposés.
     * @static
     * @async
     */
    static async search(data: userData) : Promise<string[]> {
        try {
            return Tools.search("user", data);
        }
        catch(err) {
            throw "Erreur lors de la recherche approximative d'un utilisateur.";
        }
    }
    
    /**
     * @memberof LDAP
     * @summary Fonction qui créé un nouvel utilisateur dans le LDAP.
     * @desc Appelle {@link LDAP.add} bien sûr, mais aussi {@link Tools.add} pour gérer les groupes du nouvel utilisateur.
     * @arg {userData} data - Dictionnaire des informations utilisateurs. Des erreurs peuvent apparaître si tous les champs ne sont pas remplis.
     * Cette application permet de rejoindre des groupes en masse pour toute catégorie, à la fois façon Sigma et CAS.
     * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon
     * @async
     * @static
     */
    static async create(data: userData): Promise<boolean> {
        // Calcul d'un dictionnaire d'ajout
        let vals = {};

        // 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; } );
        }
        catch(err) {
            throw "Erreur lors de la génération d'un hruid pour un nouvel utilisateur.";
        }

        let uid : string = vals[ldapConfig.user.uid];

        // Génère une erreur si un champ n'est pas rempli
        for (let key_att of ["givenName","lastName","nickname","gender","photo","phone","adress","mail","birthdate","nationality"]) {
            // Ecriture de toutes les valeurs uniques
            if (data[key_att] != undefined) vals[ldapConfig.user[key_att]]=data[key_att];
        }

        // Appel à la fonction de base
        if (!await Basics.add("user", vals)) throw "Erreur de l'ajout de la feuille à l'arbre utilisateur.";
        
        // Certains champs nécessitent de petits calculs
        let vals3={};

        // ldapConfiguration du mot de passe utilisateur
        // Le préfixe {CRYPT} signifie que le mdp est hashé dans OpenLDAP voir : https://www.openldap.org/doc/admin24/security.html 
        vals3[ldapConfig.user['password']] = "{CRYPT}"+data['password'];
        
        // Ecriture d'un surnom s'il y a lieu
        if ((data['nickname']!=undefined) && (data['nickname']!='')) vals3[ldapConfig.user['nickname']]=data['nickname'];
        try {
            // Génération id aléatoire unique
            vals3[ldapConfig.user['id']]= await Tools.generateId(ldapConfig.user['id'], "user");
        }
        catch(err) {
            throw "Erreur lors de la génération d'un id numérique pour un nouvel utilisateur.";
        }

        // Code root
        vals3[ldapConfig.user['cleanFullName']]=data['fullName'].replace(':', ';').toLowerCase().normalize('UFD');

        // Inscription des valeurs calculées
        if (!await Basics.change("user", uid, "add", vals3)) throw "Erreur lors de l'ajout des valeurs calculées à la feuille du nouvel utilisateur.";

        ["posixAccount", "shadowAccount", "brUser"].forEach(cst => {
            let val3={};
            vals3[ldapConfig.user['class']]=cst;
            Basics.change("user", uid, "add", vals3).then(res => {
                if (!res) throw "Erreur lors de l'ajout d'une valeur constante à la feuille du nouvel utilisateur.";
            });
        });

        function DFS(uid, gid, cat) {
            let to_visit = [gid];
            let tmp = ldapConfig.user[cat];
            while (to_visit.length > 0) {
                let cur_gid = to_visit.pop();
                Basics.change("user", uid, "add", { tmp: cur_gid});
                if (cat == "admins") var rel = "childs";
                to_visit.concat(Basics.searchSingle("group", ldapConfig.group[rel], cur_gid));
            }
        }

        // Ajout dans les groupes à la catégorie voulue
        for (let cat of categories) {
            for (let gid of data[cat]) Tools.add(uid, gid, cat).then(res => {
                if (!res) throw "Erreur de l'ajout d'un membre au nouveau groupe.";
            });
        }
        return true;
    }

    //------------------------------------------------------------------------------------------------------------------------
    // Fonctions de suppression TBT
    //------------------------------------------------------------------------------------------------------------------------
    
    /**
     * @memberof LDAP
     * @summary Fonction qui supprime un utilisateur du LDAP.
     * @desc Cette fonction commence par gérer les groupes du membre puis le supprime entièrement.
     * Appelle {@link LDAP.clear} bien sûr, mais aussi {@link Tools.remove} pour gérer les groupes de l'utilisateur sortant.
     * @arg {string} uid - uid de la victime
     * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon
     * @async
     * @static
     */
    static async delete(uid: string): Promise<boolean> {
        try {
            // Gestion des groupes d'abord
            let profil = await User.peek(uid);
            for (let cat of categories) {
                profil[ldapConfig.user[cat]].forEach(async function (gid: string) {
                    // Enlever de la liste des membres
                    let lm = await Tools.get(gid, "group", cat);
                    if (lm.includes(uid)) {
                        // Supprime tous les membres
                        if (!await Basics.change("group", gid, "del", ldapConfig.group[cat])) {
                            throw "Erreur lors de la suppression de tous les membres du groupe.";
                        }
                        // Les rajoute un par un, sauf pour le supprimé
                        lm.forEach(id => {
                            if (id!=uid) {
                                Tools.add(id, gid, cat).then(res => {
                                    if (!res) throw "Erreur lors du ré-ajout d'un autre membre";
                                });
                            }
                        });
                    }
                });
            }
        }
        catch(err) {
            throw "Erreur lors de l'obtention des informations de l'utilisateur à supprimer.";
        }
        // Elimination
        if (!Basics.clear("user", uid)) throw "Erreur lors de la suppression de l'utilisateur.";
        return true;
    }

    /**
     * @memberof LDAP
     * @summary Fonction qui édite un utilisateur existant dans le LDAP.
     * @desc Appelle simplement {@link Tools.edit}. Sans effet sur les groupes de l'utilisateur concerné.
     * @arg {userData} data - Dictionnaire des informations utilisateurs
     * @return {Promise(boolean)} `true` si la modification s'est bien déroulée, false sinon
     * @async
     * @static
     */
    static async edit(data : userData) : Promise<boolean> {
        try {
            return Tools.edit("user",data);
        }
        catch(err) {
            throw "Erreur lors de la modification d'un utilisateur.";
        }
    }
}