nur-sery/nur_src/b/authnz/SimpleUserManager.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);
}
}