générifier la méthode d'authentification

This commit is contained in:
Jephté Clain 2025-05-12 15:12:22 +04:00
parent 33a10a37c2
commit 39b2fa58be
11 changed files with 394 additions and 15 deletions

View File

@ -1,12 +1,22 @@
<?php <?php
namespace nur\b\authnz; namespace nur\b\authnz;
use nur\authz;
use nur\b\IllegalAccessException; use nur\b\IllegalAccessException;
use nur\b\ValueException; use nur\b\ValueException;
use nur\config; use nur\config;
use nur\cookie; use nur\cookie;
use nur\F;
use nur\msg;
use nur\P;
use nur\session; use nur\session;
use nur\v\fo;
use nur\v\ly;
use nur\v\page; use nur\v\page;
use nur\v\v;
use nur\v\vo;
use nur\v\vp\AInitAuthzPage;
use nur\v\vp\AInitPage;
/** /**
* Interface IAuthzManager: gestionnaire d'authentification et d'autorisation * Interface IAuthzManager: gestionnaire d'authentification et d'autorisation
@ -53,7 +63,7 @@ class AuthzManager {
function checkCookie(?string &$username=null, ?string &$authType=null): bool { function checkCookie(?string &$username=null, ?string &$authType=null): bool {
$value = cookie::get($this->getCookieKey(), false); $value = cookie::get($this->getCookieKey(), false);
if ($value === false) return false; if ($value === false) return false;
if (!preg_match('/^(cas|form|):(.*)$/', $value, $ms)) return false; if (!preg_match('/^(cas|form|ext|):(.*)$/', $value, $ms)) return false;
$authType = $ms[1]; $authType = $ms[1];
$username = $ms[2]; $username = $ms[2];
return true; return true;
@ -191,6 +201,22 @@ class AuthzManager {
page::redirect(page::bu($loginUrl, $params)); 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 { function formLogin(string $username, string $password): bool {
# l'utilisateur doit exister # l'utilisateur doit exister
$user = $this->getUserManager()->getAuthzUser($username, null); $user = $this->getUserManager()->getAuthzUser($username, null);
@ -202,7 +228,7 @@ class AuthzManager {
if (config::is_devel() && !$password) $password = null; if (config::is_devel() && !$password) $password = null;
if ($password === null || $user->validatePassword($password)) { if ($password === null || $user->validatePassword($password)) {
# c'est bon # c'est bon
$this->initSession(self::STATUS_INITIAL, null); $this->initSession(self::STATUS_INITIAL);
session::set(self::SESSION_KEY_USERNAME, $username); session::set(self::SESSION_KEY_USERNAME, $username);
$this->onAuthOk($username); $this->onAuthOk($username);
session::set(self::SESSION_KEY_USER, $user); session::set(self::SESSION_KEY_USER, $user);
@ -221,14 +247,11 @@ class AuthzManager {
$this->onAuthOk($username); $this->onAuthOk($username);
# l'utilisateur doit exister # l'utilisateur doit exister
$user = $this->getUserManager()->getAuthzUser($username, $overrides); $user = $this->getUserManager()->getAuthzUser($username, $overrides);
if ($user !== null) { # ce doit être un utilisteur valide
# ce doit être un utilisteur valide if ($user !== null && $user->isValid()) {
if ($user->isValid()) { session::set(self::SESSION_KEY_USER, $user);
# c'est bon $this->onAuthzOk($user);
session::set(self::SESSION_KEY_USER, $user); return true;
$this->onAuthzOk($user);
return true;
}
} }
return false; return false;
} }
@ -317,4 +340,83 @@ class AuthzManager {
/** Traiter le cas où l'utilisateur a été autorisé avec succès. */ /** Traiter le cas où l'utilisateur a été autorisé avec succès. */
function onAuthzOk(IAuthzUser $authz): void { 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();
}
} }

View File

@ -1,8 +1,18 @@
<?php <?php
namespace nur\b\authnz; namespace nur\b\authnz;
use nur\authz;
use nur\config; use nur\config;
use nur\F;
use nur\func; use nur\func;
use nur\msg;
use nur\v\fo;
use nur\v\icon;
use nur\v\ly;
use nur\v\page;
use nur\v\v;
use nur\v\vo;
use nur\v\vp\AInitAuthzPage;
/** /**
* Class CasAuthzManager: un utilisateur authentifié par CAS v3 * Class CasAuthzManager: un utilisateur authentifié par CAS v3
@ -28,4 +38,93 @@ class CasAuthzManager extends AuthzManager {
} }
return $this->userManager; return $this->userManager;
} }
private $destPage = null;
function beforeSetup(AInitAuthzPage $page): void {
# initialiser la session avant setup. ainsi, dans les fonction beforeSetup(),
# setup() et afterSetup(), la session est disponible
$this->destPage = F::get("d", $page->getMainUrl());
$this->checkSession($username, $authType);
if ($authType === "cas" && F::get("a")) {
# autologin
$casauthUrl = config::k("url")."/".$page->getCasauthUrl();
page::redirect(page::bu($page->getCasLoginUrl(), [
"service" => page::bu($casauthUrl, [
"r" => $page->getLoginUrl(),
"d" => $this->destPage,
])
]));
}
}
function print(AInitAuthzPage $page): void {
page::no_cache();
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 CAS");
if ($page->isDevauthAllowed()) {
fo::start([
"type" => "basic",
"action" => $page->getCasauthUrl(),
"method" => "get",
]);
fo::hidden("r", $page->getLoginUrl());
fo::hidden("d", $this->destPage);
} else {
fo::start([
"type" => "basic",
"action" => $page->getCasLoginUrl(),
"method" => "get",
]);
$casauthUrl = config::k("url")."/".$page->getCasauthUrl();
fo::hidden("service", page::bu($casauthUrl, [
"r" => $page->getLoginUrl(),
"d" => $this->destPage,
]));
}
#fo::p("Si vous avez un compte à l'université, vous pouvez vous connecter via CAS");
vo::p("Si vous avez un compte à l'université, vous pouvez vous connecter via CAS");
if ($this->isAuth()) {
if ($status != authz::UNAUTHORIZED) {
msg::warning(["user" => [
"Connecté en tant que ",
v::b(authz::get_auth()),
", vous n'êtes pas autorisé à accéder à cette application.",
]]);
}
fo::submit([
icon::logout("Vous déconnecter"),
"formaction" => $page->getLogoutUrl(),
"accesskey" => "z",
]);
fo::hidden("renew", "true");
fo::submit([
icon::login("Changer de compte"),
"accesskey" => "r",
]);
} else {
fo::submit(["Connexion par CAS", "accesskey" => "r"]);
}
fo::end();
ly::end();
}
} }

View File

@ -0,0 +1,78 @@
<?php
namespace nur\b\authnz;
use nur\authz;
use nur\config;
use nur\F;
use nur\msg;
use nur\v\fo;
use nur\v\ly;
use nur\v\page;
use nur\v\v;
use nur\v\vo;
use nur\v\vp\AInitAuthzPage;
class ExtAuthzManager extends AuthzManager {
const USER_MANAGER_CLASS = ExtUserManager::class;
private $destPage = null;
function beforeSetup(AInitAuthzPage $page): void {
# initialiser la session avant setup. ainsi, dans les fonction beforeSetup(),
# setup() et afterSetup(), la session est disponible
$this->destPage = F::get("d", $page->getMainUrl());
$this->checkSession($username, $authType);
if ($authType === "ext" && F::get("a")) {
# autologin
$extauthUrl = config::k("url")."/".$page->getExtauthUrl();
page::redirect(page::bu($extauthUrl, [
"d" => $this->destPage,
]));
}
}
function print(AInitAuthzPage $page): void {
page::no_cache();
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");
fo::start([
"type" => "basic",
"action" => $page->getExtauthUrl(),
"method" => "get",
]);
fo::hidden("d", $this->destPage);
vo::p("Si vous avez un compte à l'université, vous pouvez vous connecter");
if ($this->isAuth()) {
if ($status != authz::UNAUTHORIZED) {
msg::warning(["user" => [
"Connecté en tant que ",
v::b($this->getAuth()),
", vous n'êtes pas autorisé à accéder à cette application.",
]]);
}
} else {
fo::submit(["Connexion", "accesskey" => "r"]);
}
fo::end();
ly::end();
}
}

View File

@ -0,0 +1,8 @@
<?php
namespace nur\b\authnz;
class ExtUserManager extends SimpleUserManager {
function _getUser(string $username): ?array {
return ["username" => $username];
}
}

View File

@ -64,7 +64,7 @@ class AInitAuthzPage extends AInitPage implements IBasicPage {
} }
} }
protected function ensureAuthOrRedirect(bool $requireAuth, bool $requireAuthz, $requireRole, $requirePerm): void { protected function _ensureAuthOrRedirect(bool $requireAuth, bool $requireAuthz, $requireRole, $requirePerm): void {
$am = authz::manager(); $am = authz::manager();
$loginUrl = $this->getLoginUrl(); $loginUrl = $this->getLoginUrl();
$destUrl = page::self(true); $destUrl = page::self(true);
@ -88,7 +88,7 @@ class AInitAuthzPage extends AInitPage implements IBasicPage {
if ($am->isAuth()) $am->setConnected(); if ($am->isAuth()) $am->setConnected();
} }
protected function ensureFormLoginAndRedirect(?string $username, ?string $password, string $destUrl): void { function _ensureFormLoginAndRedirect(?string $username, ?string $password, string $destUrl): void {
if ($username === null && $password === null) return; if ($username === null && $password === null) return;
if (authz::manager()->formLogin($username, $password)) { if (authz::manager()->formLogin($username, $password)) {
page::redirect($destUrl); page::redirect($destUrl);

View File

@ -40,6 +40,11 @@ abstract class AInitPage extends AbstractPage {
} }
} }
/** obtenir l'url d'authentification externe. cet url doit être *relatif* */
function getExtauthUrl(): string {
return page::bu(config::k("extauth_page", "_extauth.php"));
}
function getLogoutUrl(): string { function getLogoutUrl(): string {
return page::bu(config::k("logout_page","_logout.php")); return page::bu(config::k("logout_page","_logout.php"));
} }

View File

@ -0,0 +1,68 @@
<?php
namespace nur\v\vp;
use nur\A;
use nur\authz;
use nur\config;
use nur\F;
use nur\v\base\AbstractPage;
use nur\v\page;
class AppExtauthPage extends AbstractPage {
/**
* @var bool faut-il afficher les variables au lieu de rediriger vers
* $ret_url?
*/
const DEBUG = false;
function isDebug(): bool {
if (!config::is_devel()) return false;
return static::DEBUG || F::get("D");
}
/** @var string nom de l'utilisateur connecté */
private $user;
function setup(): void {
$destUrl = null;
$user = false;
if ($user === false) $user = A::get($_SERVER, "REMOTE_USER", false);
if ($user === false) $user = A::get($_SERVER, "HTTP_REMOTE_USER", false);
if ($user === false) $user = A::get($_SERVER, "HTTP_X_REMOTE_USER", false);
if ($user) {
if (authz::manager()->extLogin($user)) {
$destUrl = F::get("d");
if ($destUrl && !$this->isDebug()) page::redirect($destUrl);
}
} else {
$user = "NONE";
}
$this->user = $user;
$this->destUrl = $destUrl;
}
private $destUrl;
function print(): void {
page::content_type("text/plain");
page::no_cache();
if ($this->isDebug()) {
echo "destUrl: $this->destUrl\n";
echo "--- \$_SERVER\n\n";
foreach ($_SERVER as $name => $value) {
echo "$name: $value\n";
}
echo "\n--- \$_REQUEST\n\n";
foreach ($_REQUEST as $name => $value) {
echo "$name: $value\n";
}
echo "\n--- \$_SESSION\n\n";
foreach ($_SESSION as $name => $value) {
echo "$name: ".var_export($value, true)."\n";
}
} else {
echo $this->user;
}
}
}

View File

@ -13,7 +13,7 @@ class BasicPage extends AInitAuthzPage {
function afterConfig(): void { function afterConfig(): void {
# initialiser la session avant setup. ainsi, dans les fonction beforeSetup(), # initialiser la session avant setup. ainsi, dans les fonction beforeSetup(),
# setup() et afterSetup(), la session est disponible # setup() et afterSetup(), la session est disponible
$this->ensureAuthOrRedirect(static::REQUIRE_AUTH, static::REQUIRE_AUTHZ, static::REQUIRE_ROLE, static::REQUIRE_PERM); $this->_ensureAuthOrRedirect(static::REQUIRE_AUTH, static::REQUIRE_AUTHZ, static::REQUIRE_ROLE, static::REQUIRE_PERM);
parent::afterConfig(); parent::afterConfig();
} }
} }

View File

@ -32,7 +32,7 @@ class NavigablePage extends AInitAuthzPage implements INavigablePage {
function afterConfig(): void { function afterConfig(): void {
# initialiser la session avant setup. ainsi, dans les fonction beforeSetup(), # initialiser la session avant setup. ainsi, dans les fonction beforeSetup(),
# setup() et afterSetup(), la session est disponible # setup() et afterSetup(), la session est disponible
$this->ensureAuthOrRedirect(static::REQUIRE_AUTH, static::REQUIRE_AUTHZ, static::REQUIRE_ROLE, static::REQUIRE_PERM); $this->_ensureAuthOrRedirect(static::REQUIRE_AUTH, static::REQUIRE_AUTHZ, static::REQUIRE_ROLE, static::REQUIRE_PERM);
parent::afterConfig(); parent::afterConfig();
} }

View File

@ -0,0 +1,19 @@
<?php
namespace nur\v\vp;
use nur\authz;
trait TAuthzLoginPage {
function TLoginPage_beforeSetup(): void {
authz::manager()->beforeSetup($this);
}
function beforeSetup(): void {
$this->TLoginPage_beforeSetup();
parent::beforeSetup();
}
function print(): void {
authz::manager()->print($this);
}
}

View File

@ -17,7 +17,7 @@ trait TFormLoginPage {
$username = P::get("u"); $username = P::get("u");
$password = P::get("p"); $password = P::get("p");
$destPage = F::get("d", $this->getMainUrl()); $destPage = F::get("d", $this->getMainUrl());
$this->ensureFormLoginAndRedirect($username, $password, $destPage); $this->_ensureFormLoginAndRedirect($username, $password, $destPage);
authz::manager()->checkSession($flcUsername, $flcAuthType); authz::manager()->checkSession($flcUsername, $flcAuthType);
$this->flcUsername = $flcUsername; $this->flcUsername = $flcUsername;