getCookieKey(), false); if ($value === false) return false; if (!preg_match('/^(cas|form|ext|):(.*)$/', $value, $ms)) return false; $authType = $ms[1]; $username = $ms[2]; return true; } protected function setCookie(string $username, string $authType): void { cookie::setd($this->getCookieKey(), "$authType:$username", $this->getCookieDuration()); } protected function resetCookie(): void { cookie::set($this->getCookieKey(), false); } ############################################################################# # Session /** @var int */ private $status = self::STATUS_NONE; function getStatus(): int { return $this->status; } /** * Vérifier que la session est valide. * * comme raccourci, si la session est valide, initialiser $username avec le * nom d'utilisateur *du cookie* */ function checkSession(?string &$username=null, ?string &$authType=null): bool { if (!$this->checkCookie($username, $authType)) return false; session::start(); $this->status = session::get(self::SESSION_KEY_STATUS, self::STATUS_NONE); return session::get(self::SESSION_KEY_COOKIE) === "$authType:$username"; } function isNewSession(): bool { return session::get(self::SESSION_KEY_COOKIE) === null; } function initSession(int $status): void { $this->checkCookie($username, $authType); session::start(); session::set(self::SESSION_KEY_COOKIE, "$authType:$username"); $this->status = $status; session::set(self::SESSION_KEY_STATUS, $status); session::set(self::SESSION_KEY_USERNAME, null); session::set(self::SESSION_KEY_USER, null); } function resetSession(int $status=self::STATUS_DISCONNECTED): void { if ($this->checkCookie()) { session::start(); session::destroy(true); $this->initSession($status); } elseif (session::started()) { session::destroy(true); $this->initSession($status); } } /** @var string */ private $auth; /** tester si l'utilisateur est connecté */ function _isAuth(): bool { if (!session::started() && session::started_once()) { # la session a déjà été démarrée, pas besoin de la démarrer de nouveau } else if (!$this->checkSession()) { return false; } return session::get(self::SESSION_KEY_USERNAME) !== null; } /** * obtenir le compte de l'utilisateur connecté * * @throws ValueException si aucun utilisateur n'est connecté */ function _getAuth(): string { if (!session::started() && session::started_once()) { # la session a déjà été démarrée, pas besoin de la démarrer de nouveau $username = session::get(self::SESSION_KEY_USERNAME); if ($username !== null) return $username; } elseif ($this->checkSession()) { $username = session::get(self::SESSION_KEY_USERNAME); if ($username !== null) return $username; } throw new ValueException("not authenticated"); } function isAuth(): bool { $auth = $this->auth; if ($auth !== null) return true; else return $this->_isAuth(); } function getAuth(): string { $auth = $this->auth; if ($auth !== null) return $auth; else return $this->_getAuth(); } ############################################################################# # Authz const USER_MANAGER_CLASS = SimpleUserManager::class; /** @var IUserManager */ private static $user_manager; protected function getUserManager(): IUserManager { if (self::$user_manager === null) { $class = static::USER_MANAGER_CLASS; self::$user_manager = new $class(); } return self::$user_manager; } function redirect(int $reason, string $destUrl, string $loginUrl): void { $params = ["d" => $destUrl]; switch ($reason) { case self::REASON_LOGIN: $params["a"] = 1; // autologin si possible break; case self::REASON_SESSION: $this->resetSession(self::STATUS_DISCONNECTED); break; case self::REASON_UNAUTHORIZED: session::set(self::SESSION_KEY_STATUS, self::STATUS_UNAUTHORIZED); break; default: throw IllegalAccessException::unexpected_state(); } page::redirect(page::bu($loginUrl, $params)); } function extLogin(string $username): bool { # l'utilisateur doit exister $user = $this->getUserManager()->getAuthzUser($username, null); # ce doit être un utilisateur valide if ($user !== null && $user->isValid()) { $this->setCookie($username, "ext"); $this->initSession(self::STATUS_INITIAL); session::set(self::SESSION_KEY_USERNAME, $username); $this->onAuthOk($username); session::set(self::SESSION_KEY_USER, $user); $this->onAuthzOk($user); return true; } return false; } function formLogin(string $username, string $password): bool { # l'utilisateur doit exister $user = $this->getUserManager()->getAuthzUser($username, null); if ($user !== null) { # ce doit être un utilisateur valide if ($user->isValid()) { $this->setCookie($username, "form"); # le cas échéant le mot de passe doit correspondre if (config::is_devel() && !$password) $password = null; if ($password === null || $user->validatePassword($password)) { # c'est bon $this->initSession(self::STATUS_INITIAL); session::set(self::SESSION_KEY_USERNAME, $username); $this->onAuthOk($username); session::set(self::SESSION_KEY_USER, $user); $this->onAuthzOk($user); return true; } } } return false; } function casLogin(string $username, ?array $overrides): bool { $this->setCookie($username, "cas"); $this->initSession(self::STATUS_INITIAL); session::set(self::SESSION_KEY_USERNAME, $username); $this->onAuthOk($username); # l'utilisateur doit exister $user = $this->getUserManager()->getAuthzUser($username, $overrides); # ce doit être un utilisteur valide if ($user !== null && $user->isValid()) { session::set(self::SESSION_KEY_USER, $user); $this->onAuthzOk($user); return true; } return false; } /** * après les avoir chargées le cas échéant, retourner les informations * d'autorisation de l'utilisateur spécifié ou une instance invalide s'il n'y * en a pas. mettre à jour la session le cas échéant * * prendre par défaut l'utilisateur connecté */ function _selectAuthz(?string $username=null): IAuthzUser { # ici, la session a peut-être déjà été fermée, mais si elle a été ouverte au # moins une fois, en tenir compte quand on accède aux informations if ($username === null) { if (!session::started_once() || !$this->isAuth()) return UnknownUser::with(); $username = $this->getAuth(); $user = session::get(self::SESSION_KEY_USER); } else if (session::started_once() && $this->isAuth()) { $user = session::get(self::SESSION_KEY_USER); } else { $user = null; } if ($user instanceof IAuthzUser && $user->getUsername() === $username) { return $user; } $user = $this->getUserManager()->getAuthzUser($username, null); if ($user === null) $user = UnknownUser::with($username); session::set(self::SESSION_KEY_USERNAME, $username); session::set(self::SESSION_KEY_USER, $user); return $user; } /** @var IAuthzUser */ private $authz; function setConnected(): void { $this->auth = $this->_getAuth(); $this->authz = $this->_selectAuthz(); } function selectAuthz(?string $username=null): IAuthzUser { $authz = null; if ($username === null) $authz = $this->authz; if ($authz === null) $authz = $this->_selectAuthz($username); return $authz; } /** obtenir les informations d'autorisation de l'utilisateur effectif */ function getUser(): IAuthzUser { #XXX le cas échéant, ajouter les informations de l'utilisateur effectif return $this->selectAuthz(); } function checkAuthz(?array $roles, ?array $perms): bool { $authz = $this->selectAuthz(); if ($roles !== null && !$authz->isRole($roles)) return false; if ($perms !== null && !$authz->isPerm($perms)) return false; # sinon, au moins un rôle ou une permission doivent être définis return $authz->isRole(IAuthzUser::ROLE_AUTHZ); } /** la connexion actuelle a-t-elle été forcée par un administrateur? */ function isSulogin(): bool { return boolval(session::get(self::SESSION_KEY_SULOGIN, false)); } /** marquer la connexion actuelle comme étant faite par un administrateur. */ function setSulogin(bool $sulogin=true): void { if ($sulogin) { $user = session::get(self::SESSION_KEY_SULOGIN, false); if ($user === false) $user = session::get(self::SESSION_KEY_USER); session::set(self::SESSION_KEY_SULOGIN, $user); } else { session::set(self::SESSION_KEY_SULOGIN, false); } } ############################################################################# # Méthodes surchargeables /** Traiter le cas où l'utilisateur s'est authentifié avec succès. */ function onAuthOk(string $username): void { } /** Traiter le cas où l'utilisateur a été autorisé avec succès. */ function onAuthzOk(IAuthzUser $authz): void { } ############################################################################# # Page login private $username = null; private $authType = null; function beforeSetup(AInitAuthzPage $page): void { # initialiser la session avant setup. ainsi, dans les fonction beforeSetup(), # setup() et afterSetup(), la session est disponible $username = P::get("u"); $password = P::get("p"); $destPage = F::get("d", $page->getMainUrl()); $page->_ensureFormLoginAndRedirect($username, $password, $destPage); $this->checkSession($this->username, $this->authType); } function print(AInitAuthzPage $page): void { page::no_cache(); $username = P::get("u"); $password = P::get("p"); ly::row(); vo::h1(["class" => "text-center", q($page->TITLE())]); ly::col(["sm" => 6, "sm-push" => 3]); $status = $this->getStatus(); switch ($status) { case authz::DISCONNECTED: msg::warning("Vous avez été déconnecté. Veuillez vous reconnecter"); break; case authz::UNAUTHORIZED: msg::error(["user" => [ "Connecté en tant que ", v::b($this->getAuth()), ", vous n'êtes pas autorisé à accéder à la page que vous avez demandé.", ]]); break; } ly::panel("Connexion par identifiant/mot de passe"); fo::start([ "type" => "basic", "action" => "", "method" => "post", ]); fo::text("Identifiant", "u", $username?: $this->username, [ "accesskey" => "q", "placeholder" => "Votre identifiant", ]); fo::password("Mot de passe", "p", $password, [ "placeholder" => "Votre mot de passe", ]); if ($username || $password) { msg::error("$username: Votre identifiant et/ou votre mot de passe sont incorrects"); } elseif ($username === "") { msg::error("Vous devez saisir votre identifiant"); } elseif ($password === "") { msg::error("Vous devez saisir votre mot de passe"); } fo::submit(["Connexion", "accesskey" => "r"]); if ($this->isAuth() && $this->authType === "form") { if ($status != authz::UNAUTHORIZED) { msg::warning(["user" => [ "Connecté en tant que ", v::b($this->getAuth()), ", vous n'êtes pas autorisé à accéder à cette application.", ]]); } fo::submit([ "Vous déconnecter", "accesskey" => "z", "formmethod" => "get", "formaction" => $page->getLogoutUrl(), ]); } fo::end(); ly::end(); } }