/** * @file Ce fichier regroupe les différentes classes avec différents utilisateurs. Ces classes sont dédiées à être exportées directement pour être utilisées par le solver. * Le découpage par fichier est arbitraire mais permet de regrouper certaines classes proches. * @author hawkspar */ import {ldapConfig} from './config'; import {LDAP} from './basics'; import {Tools} from './utilities'; import {Group} from './group'; /** * @interface userData * @desc Interface avec toutes les données extractables pour un utilisateur. * @var {string} uid - Identifiant utilisateur * @var {string} givenName - Prénom * @var {string} lastName - Nom * @var {string} nickname - Surnom * @var {string} photo - Bytestring de la photo de l'utilisateur * @var {string} birthdate - Date d'anniversaire * TBA @var {string} nationality - Nationalité d'origine * @var {string} promotion - Année(s) de promo * @var {string} phone - Numéro(s) de téléphone * @var {string[]} adresses - Adresse(s) * @var {string[]} mails - Adresse(s) courriel * @var {string[]} groups - Un ou plusieurs groupes dont l'utilisateur est membre (inclus section sportive, binet, PA...) * @var {string} password - Mot de passe généré en amont * @var {string[]} ips - Adresse(s) ip * @var {string} directory - Adresse soft des données utilisateurs * @var {string} login - Astuce de root flemmard * @arg {string} readPerm - Permissions spéciales BR * @var {string} writePerm - Permissions spéciales BR * @var {string[]} forlifes - Alias BR (attention le filtre .fkz n'est plus fonctionnel) * @var {string[]} admins - Liste des gid dont l'utilisateur est admin ; supposé sous-liste de groups * TBA @var {string[]} likes - Liste des gid dont l'utilisateur est sympathisant */ export interface userData { "uid": string, "groups": string[], "groupsIsAdmin": string[], "password"?: string, "givenName"?: string, "lastName"?: string, "nickname"?: string, "promotion"?: string, "photo"?: string, "birthdate"?: string, //"nationality"?: string, "phone"?: string, "adress"?: string, "mail"?: string, "ips"?: string[], "directory"?: string, "login"?: string, "readPerm"?: string, "writePerm"?: string, "forlifes"?: string[] //"likes"?: string[] } //------------------------------------------------------------------------------------------------------------------------ // Classes à exporter TBT //------------------------------------------------------------------------------------------------------------------------ export class User { /** * @class Cette classe est une des deux classes exportables permettant de faire des opérations sur les utilisateurs. * @summary Constructeur vide. */ constructor() {} /** * @summary Fonction qui renvoit les infos de base relatives à un utilisateur particulier. * @desc Cette fonction utilise {@link Tools.genericPeek} avec l'interface {@link userData}. * @arg {string} uid - Identifiant de l'utilisateur * @return {Promise(userData)} Informations recueillies. * @static * @async */ static async peek(uid: string) : Promise<userData> { try { return Tools.genericPeek<userData>("us", uid); } catch(err) { throw "Error while peeking a user."; } } /** * @summary Fonction qui retrouve les uid des paxs validant les critères de recherche. Utiliser {@link peek} au cas par cas après pour obtenir les vraies infos. * @desc Cette fonction utilise {@link LDAP.search} mais avec un filtre généré à la volée. Accepte des champs exacts ou incomplets pour la plupart des champs * mais pas approximatifs et ne gère pas l'auto-complete. MEF Timeout pour des recherches trop vagues. Va crasher si un champ n'est pas dans ldapConfig. * Utiliser trouverGroupesParTypes pour chaque champ relié à groups. * @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 exempl 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.genericSearch("us", data); } catch(err) { throw "Erreur lors de la recherche approximative d'un utilisateur."; } } /** * @summary Fonction qui créé un nouvel utilisateur dans le LDAP. * @desc Appelle {@link LDAP.add} bien sûr, mais aussi {@link User.addGroupMember} et {@link Admin.addGroupAdmin} pour gérer les groupes du nouvel utilisateur. * @arg {fullUserData} data - Dictionnaire des informations utilisateurs. Des erreurs peuvent apparaître si tous les champs ne sont pas remplis. * @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(data['givenName'],data['lastName'],data['promotion']).then(id => { vals[ldapConfig.key_id]=id; } ); } catch(err) { throw "Erreur lors de la génération d'un hruid pour un nouvel utilisateur."; } let uid = vals[ldapConfig.key_id]; // Génère une erreur si un champ n'est pas rempli for (let key_att in data) { // Ecriture de toutes les valeurs uniques if (!Array.isArray(data[key_att])) { vals[ldapConfig.user[key_att]]=data[key_att]; } } // Appel à la fonction de base if (!await LDAP.add("us", vals)) { throw "Erreur de l'ajout de la feuille à l'arbre utilisateur."; } for (let key_att in data) { // Modifications multiples pour avoir plusieurs champs de même type ; boucle sur les attributs multiples if (Array.isArray(data[key_att])) { // On rajoute chaque valeur en entrée data[key_att].forEach(val => { let vals2 = {}; vals2[ldapConfig.user[key_att]]=val; LDAP.change("us", uid, "add", vals2).then(res => { if (!res) { throw "Erreur lors de l'ajout d'une valeur pour un champ à valeurs multiples à la feuille du nouvel utilisateur."; } }); }); } } // Certains champs nécessitent de petits calculs let vals3={}; // Création d'un nom complet lisible vals3[ldapConfig.user['fullName']]=data['givenName']+' '+data['lastName'].toUpperCase(); // 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'], "us"); } catch(err) { throw "Erreur lors de la génération d'un id numérique pour un nouvel utilisateur."; } // Stockage machine ; dépend du prénom vals3[ldapConfig.user['directory']] = '/hosting/users/' + data['givenName'][0]; // Code root vals3[ldapConfig.user['cleanFullName']]=data['fullName'].replace(':', ';').toLowerCase().normalize('UFD'); // Adressage root if (data['groups'].includes("on_platal")) { vals3[ldapConfig.user['login']] = "/bin/bash"; } else { vals3[ldapConfig.user['login']] = "/sbin/nologin"; } // Permissions BR vals3[ldapConfig.user['readPerm']] = 'br.*,public.*'; if (data['readPerm'].length>0) { vals3[ldapConfig.user['readPerm']] += ',' + data['readPerm']; } vals3[ldapConfig.user['writePerm']] = 'br.*,!br.blague-du-jour,public.*,!br.campagnekes'; if (data['writePerm'].length>0) { vals3[ldapConfig.user['readPerm']] += ',' + data['writePerm']; } // Valeur nécessaire ASKIP mais inutile vals3[ldapConfig.user['idNum']] ='5000'; // Inscription des valeurs calculées if (!await LDAP.change("us", uid, "add", vals3)) { throw "Erreur lors de l'ajout des valeurs calculées à la feuille du nouvel utilisateur."; } ["posixAccount", "shadowAccount", "inetOrgPerson", "brAccount"].forEach(cst => { let val3={}; vals3[ldapConfig.user['class']]=cst; LDAP.change("us", uid, "add", vals3).then(res => { if (!res) { throw "Erreur lors de l'ajout d'une valeur constante à la feuille du nouvel utilisateur."; } }); }); // Utilisation des fonctions adaptées pour assurer la cohérence de l'ensemble data['groupsIsMember'].forEach(gid => { Group.addMember(uid, gid).then(res => { if (!res) { throw "Erreur lors de l'ajout du nouvel utilisateur à un groupe."; } }); }); data['groupsIsAdmin'].forEach(gid => { Group.addAdmin(uid, gid).then(res => { if (!res) { throw "Erreur lors de l'ajout du nouvel utilisateur à un groupe en tant qu'admin."; } }); }); return true; } //------------------------------------------------------------------------------------------------------------------------ // Fonctions de suppression TBT //------------------------------------------------------------------------------------------------------------------------ /** * @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 Admin.delGroupMember} et {@link Admin.delGroupAdmin} 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); profil[ldapConfig.user['groups']].forEach(gid => { // Opérations effectuées par effet de bord if (Tools.isGroupAdmin(uid,gid)) { if (!Group.remAdmin(uid, gid)) { throw "Erreur lors de la suppression des droits d'admin de l'utilisateur."; } } if (!Group.remMember(uid, gid)) { throw "Erreur lors de la suppression de l'appartenance à un groupe de l'utilisateur."; } }); } catch(err) { throw "Erreur lors de l'obtention des informations de l'utilisateur à supprimer."; } // Elimination if (!LDAP.clear("us", uid)) { throw "Erreur lors de la suppression de l'utilisateur."; } return true; } /** * @summary Fonction qui édite un utilisateur existant dans le LDAP. Très similaire à {@link creerUtilisateur} * @desc Appelle simplement {@link genericEdit}. * @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 { let uid = data["uid"]; // Leave old groups let profil = User.peek(uid); profil["groups"].forEach(gid => { Group.remMember(uid, gid); }); profil["groupsIsAdmin"].forEach(gid => { Group.remAdmin(uid, gid); }); // Join new groups data["groups"].forEach(gid => { Group.addMember(uid, gid); }); data["groupsIsAdmin"].forEach(gid => { Group.addAdmin(uid, gid); }); // Edit all other fields return Tools.genericEdit("us",data); } catch(err) { throw "Erreur lors de la modification d'un utilisateur."; } } }