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);
|
||
|
}
|
||
|
}
|