207 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			207 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| namespace nur\b\authnz;
 | |
| 
 | |
| use nur\A;
 | |
| use nur\base;
 | |
| use nur\config;
 | |
| use nur\data\types\Metadata;
 | |
| use nur\txt;
 | |
| 
 | |
| /**
 | |
|  * Class UserManager: gestionnaire d'utilisateurs et de groupes
 | |
|  *
 | |
|  * cette implémentation lit la clé conf.authnz de la configuration
 | |
|  * - users: liste des utilisateurs
 | |
|  * - groups: liste des groupes
 | |
|  * - default_role: rôle principal par défaut
 | |
|  * - user_perms, user_role, user_groups: associations par utilisateur
 | |
|  * - group_perms, group_role: associations par groupe
 | |
|  */
 | |
| class SimpleUserManager implements IUserManager {
 | |
|   const USER_SCHEMA = [
 | |
|     "username" => ["string", null, "nom d'utilisateur"],
 | |
|     "password" => ["?string", null, "mot de passe"],
 | |
|     "mail" => ["?string", null, "mail de l'utilisateur"],
 | |
|     "role" => ["?string", null, "role principal de l'utilisateur"],
 | |
|     "perms" => ["?array", null, "permissions attribuées à l'utilisateur"],
 | |
|     "groups" => ["?array", null, "groupes dont l'utilisateur fait partie"],
 | |
|     "display_name" => ["?string", null, "nom d'affichage long"],
 | |
|     "short_name" => ["?string", null, "nom d'affichage court"],
 | |
|     "disabled" => ["bool", false, "ce compte est-il désactivé?"],
 | |
|   ];
 | |
|   
 | |
|   const GROUP_SCHEMA = [
 | |
|     "groupname" => ["string", null, "identifiant", "required" => true],
 | |
|     "lib" => ["?string", null, "libellé"],
 | |
|     "role" => ["?string", null, "rôle par défaut attribué aux membres de ce groupe"],
 | |
|     "perms" => ["?array", null, "permissions supplémentaires associées à ce groupe"],
 | |
|   ];
 | |
| 
 | |
|   const OVERRIDES_SCHEMA = [
 | |
|     "cas_attrs" => ["?array", null, "attributs CAS"],
 | |
|   ];
 | |
| 
 | |
|   private static $user_md;
 | |
|   protected static function user_md(): Metadata {
 | |
|     if (self::$user_md === null) self::$user_md = new Metadata(self::USER_SCHEMA);
 | |
|     return self::$user_md;
 | |
|   }
 | |
|   
 | |
|   function _getUser(string $username): ?array {
 | |
|     return config::k("users.$username");
 | |
|   }
 | |
| 
 | |
|   function getUser(string $username, ?array $overrides, ?bool &$found=null): array {
 | |
|     $user = $this->_getUser($username);
 | |
|     $found = $user !== null;
 | |
|     self::user_md()->ensureSchema($user, $username);
 | |
|     # support des attributs CAS
 | |
|     $cas_attrs = A::get($overrides, "cas_attrs");
 | |
|     if ($cas_attrs !== null) {
 | |
|       # nom et prénom
 | |
|       $sn = A::first(explode(",", A::get($cas_attrs, "sn")));
 | |
|       $givenName = A::get($cas_attrs, "givenname");
 | |
|       if ($sn !== null && $givenName !== null) {
 | |
|         $sn = txt::upperw($sn);
 | |
|         $givenName = txt::upperw($givenName);
 | |
|         $parts = preg_split('/[- ]+/', $givenName);
 | |
|         $gn = "";
 | |
|         foreach ($parts as $part) {
 | |
|           $gn .= mb_substr($part, 0, 1);
 | |
|         }
 | |
|         $gn = txt::upper($gn);
 | |
| 
 | |
|         $displayName = "$givenName $sn";
 | |
|         $shortName = "$gn.$sn";
 | |
|         A::replace_n($user, "display_name", $displayName);
 | |
|         A::replace_n($user, "short_name", $shortName);
 | |
|       }
 | |
|       # mail
 | |
|       A::replace_n($user, "mail", A::get($cas_attrs, "mail"));
 | |
|     }
 | |
|     return $user;
 | |
|   }
 | |
| 
 | |
|   private static $group_md;
 | |
|   protected static function group_md(): Metadata {
 | |
|     if (self::$group_md === null) self::$group_md = new Metadata(self::GROUP_SCHEMA);
 | |
|     return self::$group_md;
 | |
|   }
 | |
| 
 | |
|   function _getGroup(string $groupname): ?array {
 | |
|     return config::k("groups.$groupname");
 | |
|   }
 | |
| 
 | |
|   function getGroup(string $groupname, ?array $overrides, ?bool &$found=null): array {
 | |
|     $group = $this->_getGroup($groupname);
 | |
|     $found = $group !== null;
 | |
|     self::group_md()->ensureSchema($group, $groupname);
 | |
|     # support des attributs CAS
 | |
|     $cas_attrs = A::get($overrides, "cas_attrs");
 | |
|     if ($cas_attrs !== null) {
 | |
|     }
 | |
|     return $group;
 | |
|   }
 | |
| 
 | |
|   protected function getDefaultRole(): ?string {
 | |
|     return config::k("default_role");
 | |
|   }
 | |
| 
 | |
|   /** retourner la liste des permissions de l'utilisateur */
 | |
|   protected function getUserPerms(array $user): array {
 | |
|     $perms = $user["perms"];
 | |
|     A::merge($perms, config::k("user_perms.$user[username]"));
 | |
|     return $perms;
 | |
|   }
 | |
|   /** retourner le rôle de l'utilisateur */
 | |
|   protected function getUserRole(array $user): ?string {
 | |
|     $role = $user["role"];
 | |
|     base::update_n($role, config::k("user_role.$user[username]"));
 | |
|     return $role;
 | |
|   }
 | |
|   /** retourner les noms des groupes dont fait partie l'utilisateur */
 | |
|   protected function getUserGroupnames(array $user): array {
 | |
|     $groups = $user["groups"];
 | |
|     A::merge($groups, config::k("user_groups.$user[username]"));
 | |
|     return $groups;
 | |
|   }
 | |
| 
 | |
|   /** retourner le rôle par défaut attribué aux membres du groupe */
 | |
|   protected function getGroupRole(array $group): ?string {
 | |
|     $role = $group["role"];
 | |
|     base::update_n($role, config::k("group_role.$group[groupname]"));
 | |
|     return $role;
 | |
|   }
 | |
|   /** retourner les permissions supplémentaires attribuées aux membres du groupe */
 | |
|   protected function getGroupPerms(array $group): ?array {
 | |
|     $perms = $group["perms"];
 | |
|     A::merge($perms, config::k("group_perms.$group[groupname]"));
 | |
|     return $perms;
 | |
|   }
 | |
| 
 | |
|   /** retourner les permissions associées au rôle */
 | |
|   protected function getRolePerms(string $role): ?array {
 | |
|     return config::k("role_perms.$role");
 | |
|   }
 | |
| 
 | |
|   protected function mergePerms(array &$userPerms, ?array $perms): void {
 | |
|     if ($perms === null) return;
 | |
|     foreach ($perms as $perm) {
 | |
|       $userPerms[$perm] = true;
 | |
|     }
 | |
|   }
 | |
|   protected function mergeRolePerms(array &$userPerms, ?string $role): void {
 | |
|     if ($role === null) return;
 | |
|     $perms = $this->getRolePerms($role);
 | |
|     if ($perms === null) return;
 | |
|     foreach ($perms as $perm) {
 | |
|       $userPerms[$perm] = true;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   function getAuthzUser(string $username, ?array $overrides): ?IAuthzUser {
 | |
|     $user = $this->getUser($username, $overrides, $founduser);
 | |
| 
 | |
|     $role = null;
 | |
|     # permissions par défaut, ajoutées uniquement si l'utilisateur a déjà des
 | |
|     # permissions explicites
 | |
|     $defaultPerms = ["^$username" => true];
 | |
|     # tout d'abord calculer les permissions et rôles associées à l'utilisateur
 | |
|     $perms = array_fill_keys($this->getUserPerms($user), true);
 | |
|     $userRole = $this->getUserRole($user);
 | |
|     if ($role === null) $role = $userRole;
 | |
|     $this->mergeRolePerms($perms, $userRole);
 | |
|     # puis calculer les permissions et rôles associés aux groupes
 | |
|     $groupnames = $this->getUserGroupnames($user);
 | |
|     foreach ($groupnames as $groupname) {
 | |
|       # permission automatique basée sur le groupe
 | |
|       A::merge($defaultPerms, ["@$groupname" => true]);
 | |
| 
 | |
|       $group = $this->getGroup($groupname, $overrides, $foundgroup);
 | |
|       $this->mergePerms($perms, $this->getGroupPerms($group));
 | |
|       $groupRole = $this->getGroupRole($group);
 | |
|       if ($role === null) $role = $groupRole;
 | |
|       $this->mergeRolePerms($perms, $groupRole);
 | |
|       # rôle automatique du fait de l'appartenance au groupe
 | |
|       $this->mergeRolePerms($perms, "@$groupname");
 | |
|     }
 | |
| 
 | |
|     # si un role ou des permissions existent, considérer que l'utilisateur
 | |
|     # existe, même s'il n'avait pas été trouvé au départ
 | |
|     if (!$founduser && !$role && !$perms) return null;
 | |
| 
 | |
|     # permissions par défaut
 | |
|     A::merge($perms, $defaultPerms);
 | |
|     # rôle par défaut
 | |
|     if ($role === null) {
 | |
|       $role = $this->getDefaultRole();
 | |
|       $this->mergeRolePerms($perms, $role);
 | |
|     }
 | |
| 
 | |
|     $user["role"] = $role;
 | |
|     $user["perms"] = array_keys($perms);
 | |
|     $user["groups"] = $groupnames;
 | |
|     return new SimpleUser($user);
 | |
|   }
 | |
| }
 |