Casuser auth backend: add user access filters feature

This commit is contained in:
Benjamin Renard 2024-02-21 10:13:27 +01:00
parent 82122089f9
commit 57aedc4869
Signed by: bn8
GPG key ID: 3E2E1CE1907115BC
8 changed files with 183 additions and 32 deletions

File diff suppressed because one or more lines are too long

View file

@ -1,7 +1,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"POT-Creation-Date: 2024-01-23 18:15+0000\n" "POT-Creation-Date: 2024-02-21 09:12+0000\n"
"PO-Revision-Date: \n" "PO-Revision-Date: \n"
"Last-Translator: Benjamin Renard <brenard@easter-eggs.com>\n" "Last-Translator: Benjamin Renard <brenard@easter-eggs.com>\n"
"Language-Team: \n" "Language-Team: \n"
@ -30,7 +30,7 @@ msgstr ""
msgid "Unknown" msgid "Unknown"
msgstr "Inconnu" msgstr "Inconnu"
#: Db.php:78 #: Db.php:93
msgid "Unable to connect to the database." msgid "Unable to connect to the database."
msgstr "Impossible de se connecter à la base de données." msgstr "Impossible de se connecter à la base de données."
@ -46,7 +46,7 @@ msgstr "Connection"
msgid "Authentication required" msgid "Authentication required"
msgstr "Authentication requise" msgstr "Authentication requise"
#: Auth/Http.php:124 Auth/Http.php:127 Url.php:185 #: Auth/Http.php:124 Auth/Http.php:127 Url.php:185 Auth.php:340 Auth.php:343
msgid "Access denied" msgid "Access denied"
msgstr "Accès interdit" msgstr "Accès interdit"
@ -54,6 +54,10 @@ msgstr "Accès interdit"
msgid "You must login to access this page." msgid "You must login to access this page."
msgstr "Vous devez vous connecter pour accéder à cette page." msgstr "Vous devez vous connecter pour accéder à cette page."
#: Auth/Casuser.php:88 Auth/Casuser.php:112
msgid "Configuration error in CAS auth backend."
msgstr "Erreur de configuration dans le backend d'authentification CAS."
#: Url.php:177 #: Url.php:177
msgid "Bad request" msgid "Bad request"
msgstr "Mauvaise requête" msgstr "Mauvaise requête"
@ -120,6 +124,14 @@ msgstr "Authentication requise mais impossible pour vous authentifier."
msgid "This request could not be processed correctly." msgid "This request could not be processed correctly."
msgstr "Cette requête n'a put être traitée correctement." msgstr "Cette requête n'a put être traitée correctement."
#: Auth.php:344
msgid "You do not have access to this application."
msgstr "Vous n'avez pas accès à cette application."
#: Auth.php:348
msgid "Details:"
msgstr "Détails :"
#: Cli.php:44 #: Cli.php:44
msgid "Create a new project using EesyPHP framework" msgid "Create a new project using EesyPHP framework"
msgstr "Créer un nouveau project utilisant le framework EesyPHP" msgstr "Créer un nouveau project utilisant le framework EesyPHP"
@ -261,13 +273,7 @@ msgstr ""
"Port d'écoute spécifié invalid. Il doit s'agir d'un entier positif entre 1 " "Port d'écoute spécifié invalid. Il doit s'agir d'un entier positif entre 1 "
"et 65535." "et 65535."
#: Cli.php:418 #: Cli.php:415
msgid "Can't invoke bash. Can't ask password prompt."
msgstr ""
"Impossible d'utiliser bash. Impossible de demander à l'utilisateur de saisir "
"un not de passe."
#: Cli.php:421
msgid "Please enter password:" msgid "Please enter password:"
msgstr "Merci de saisir le not de passe :" msgstr "Merci de saisir le not de passe :"
@ -300,19 +306,19 @@ msgstr "ns"
msgid "Less than 1%s" msgid "Less than 1%s"
msgstr "Moins de 1%s" msgstr "Moins de 1%s"
#: App.php:239 I18n.php:119 #: App.php:242 I18n.php:119
msgid "Hello world !" msgid "Hello world !"
msgstr "Bonjour tout le monde !" msgstr "Bonjour tout le monde !"
#: App.php:241 #: App.php:244
msgid "Hello world!" msgid "Hello world!"
msgstr "Salut tout le monde !" msgstr "Salut tout le monde !"
#: App.php:252 #: App.php:255
msgid "Disconnected" msgid "Disconnected"
msgstr "Déconnecté" msgstr "Déconnecté"
#: App.php:254 #: App.php:257
msgid "You are now disconnected." msgid "You are now disconnected."
msgstr "Vous êtes maintenant déconnecté." msgstr "Vous êtes maintenant déconnecté."
@ -606,12 +612,12 @@ msgstr ""
"\n" "\n"
"Mail originalement destiné à %s." "Mail originalement destiné à %s."
#: Email.php:139 #: Email.php:137
#, php-format #, php-format
msgid "<p><small>%s: %s</small></p>" msgid "<p><small>%s: %s</small></p>"
msgstr "<p><small>%s: %s</small></p>" msgstr "<p><small>%s: %s</small></p>"
#: Email.php:140 #: Email.php:138
#, php-format #, php-format
msgid "" msgid ""
"\n" "\n"
@ -657,6 +663,10 @@ msgstr "Erreur : %1"
msgid "Back" msgid "Back"
msgstr "Retour" msgstr "Retour"
#: templates/access_denied.tpl:12
msgid "Details: %1"
msgstr "Détails : %1"
#: templates/empty.tpl:74 #: templates/empty.tpl:74
msgid "Sign out" msgid "Sign out"
msgstr "Déconnexion" msgstr "Déconnexion"
@ -701,6 +711,11 @@ msgstr ""
"fichier <em>homepage.tpl</em> pour l'écraser. Vous pouvez également écraser " "fichier <em>homepage.tpl</em> pour l'écraser. Vous pouvez également écraser "
"le gestionnaire de l'URL racine de l'application web." "le gestionnaire de l'URL racine de l'application web."
#~ msgid "Can't invoke bash. Can't ask password prompt."
#~ msgstr ""
#~ "Impossible d'utiliser bash. Impossible de demander à l'utilisateur de "
#~ "saisir un not de passe."
#~ msgid "Confirmation" #~ msgid "Confirmation"
#~ msgstr "Confirmation" #~ msgstr "Confirmation"

View file

@ -1,7 +1,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"POT-Creation-Date: 2024-01-23 18:19+0000\n" "POT-Creation-Date: 2024-02-21 09:12+0000\n"
"PO-Revision-Date: 2024-01-23 18:19+0000\n" "PO-Revision-Date: 2024-02-21 09:12+0000\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
@ -22,7 +22,7 @@ msgstr ""
msgid "Unknown" msgid "Unknown"
msgstr "" msgstr ""
#: Db.php:78 #: Db.php:93
msgid "Unable to connect to the database." msgid "Unable to connect to the database."
msgstr "" msgstr ""
@ -38,7 +38,7 @@ msgstr ""
msgid "Authentication required" msgid "Authentication required"
msgstr "" msgstr ""
#: Auth/Http.php:124 Auth/Http.php:127 Url.php:185 #: Auth/Http.php:124 Auth/Http.php:127 Url.php:185 Auth.php:340 Auth.php:343
msgid "Access denied" msgid "Access denied"
msgstr "" msgstr ""
@ -46,6 +46,10 @@ msgstr ""
msgid "You must login to access this page." msgid "You must login to access this page."
msgstr "" msgstr ""
#: Auth/Casuser.php:88 Auth/Casuser.php:112
msgid "Configuration error in CAS auth backend."
msgstr ""
#: Url.php:177 #: Url.php:177
msgid "Bad request" msgid "Bad request"
msgstr "" msgstr ""
@ -104,6 +108,14 @@ msgstr ""
msgid "This request could not be processed correctly." msgid "This request could not be processed correctly."
msgstr "" msgstr ""
#: Auth.php:344
msgid "You do not have access to this application."
msgstr ""
#: Auth.php:348
msgid "Details:"
msgstr ""
#: Cli.php:44 #: Cli.php:44
msgid "Create a new project using EesyPHP framework" msgid "Create a new project using EesyPHP framework"
msgstr "" msgstr ""
@ -223,11 +235,7 @@ msgid ""
"65535." "65535."
msgstr "" msgstr ""
#: Cli.php:418 #: Cli.php:415
msgid "Can't invoke bash. Can't ask password prompt."
msgstr ""
#: Cli.php:421
msgid "Please enter password:" msgid "Please enter password:"
msgstr "" msgstr ""
@ -260,19 +268,19 @@ msgstr ""
msgid "Less than 1%s" msgid "Less than 1%s"
msgstr "" msgstr ""
#: App.php:239 I18n.php:119 #: App.php:242 I18n.php:119
msgid "Hello world !" msgid "Hello world !"
msgstr "" msgstr ""
#: App.php:241 #: App.php:244
msgid "Hello world!" msgid "Hello world!"
msgstr "" msgstr ""
#: App.php:252 #: App.php:255
msgid "Disconnected" msgid "Disconnected"
msgstr "" msgstr ""
#: App.php:254 #: App.php:257
msgid "You are now disconnected." msgid "You are now disconnected."
msgstr "" msgstr ""
@ -523,12 +531,12 @@ msgid ""
"Mail initially intended for %s." "Mail initially intended for %s."
msgstr "" msgstr ""
#: Email.php:139 #: Email.php:137
#, php-format #, php-format
msgid "<p><small>%s: %s</small></p>" msgid "<p><small>%s: %s</small></p>"
msgstr "" msgstr ""
#: Email.php:140 #: Email.php:138
#, php-format #, php-format
msgid "" msgid ""
"\n" "\n"
@ -569,6 +577,10 @@ msgstr ""
msgid "Back" msgid "Back"
msgstr "" msgstr ""
#: templates/access_denied.tpl:12
msgid "Details: %1"
msgstr ""
#: templates/empty.tpl:74 #: templates/empty.tpl:74
msgid "Sign out" msgid "Sign out"
msgstr "" msgstr ""

View file

@ -328,4 +328,27 @@ class Auth {
public static function user() { public static function user() {
return self :: $user; return self :: $user;
} }
/**
* Helper function to trigger access denied error
* @param string|null $details Reason or details about this error
* @return never
*/
public static function access_denied($details=null) {
if (Tpl::initialized()) {
Tpl :: assign("details", $details);
Tpl :: display("access_denied.tpl", I18n::_("Access denied"));
}
else {
printf("<h1>%s</h1>", I18n::_("Access denied"));
printf("<p>%s</p>", I18n::_("You do not have access to this application."));
if ($details)
printf(
"<p><small>%s%s</small></p>",
I18n::_("Details:"),
$details
);
}
exit();
}
} }

View file

@ -57,6 +57,16 @@ class Cas extends Method {
'default' => null, 'default' => null,
), ),
), ),
/*
* CAS user backend access filters:
* [
* attr => regex,
* attr => callable (will receive the username and the attr value as parameters),
* callable, (will receive the username and an associative array of all attrs values as
* parameters)
* ]
*/
'user_filters' => array(),
) )
); );
self :: $fake_authenticated_user = App :: get( self :: $fake_authenticated_user = App :: get(

View file

@ -3,10 +3,14 @@
namespace EesyPHP\Auth; namespace EesyPHP\Auth;
use EesyPHP\App; use EesyPHP\App;
use EesyPHP\Auth;
use EesyPHP\Auth\User; use EesyPHP\Auth\User;
use EesyPHP\Check;
use EesyPHP\Config; use EesyPHP\Config;
use EesyPHP\I18n;
use EesyPHP\Log; use EesyPHP\Log;
use function EesyPHP\cast; use function EesyPHP\cast;
use function EesyPHP\format_callable;
use function EesyPHP\vardump; use function EesyPHP\vardump;
use phpCAS; use phpCAS;
@ -37,6 +41,9 @@ class Casuser extends Backend {
return null; return null;
} }
// Check user filters and denied access if not match
self :: check_user_filters($username);
$info = array(); $info = array();
foreach(Config::get('auth.cas.user_attributes') as $name => $attr_config) { foreach(Config::get('auth.cas.user_attributes') as $name => $attr_config) {
$cas_name = Config::get("cas_name", null, 'string', false, $attr_config); $cas_name = Config::get("cas_name", null, 'string', false, $attr_config);
@ -52,4 +59,68 @@ class Casuser extends Backend {
return new User($username, '\\EesyPHP\\Auth\\Casuser', $info); return new User($username, '\\EesyPHP\\Auth\\Casuser', $info);
} }
/**
* Check authenticated user match with configured filters and denied access if not
* @param string $username
* @return void|never
*/
public static function check_user_filters($username) {
foreach(Config::get('auth.cas.user_filters', [], 'array') as $attr => $filter) {
if (is_callable($filter)) {
if (
!$filter(
$username,
is_string($attr)?phpCAS::getAttribute($attr):phpCAS::getAttributes()
)
)
{
Log::warning("get_user(%s): filter out by %s", $username, format_callable($filter));
Auth::access_denied();
}
}
else if (is_string($attr)) {
$regex_valid = Check :: regex($filter, true);
if ($regex_valid !== true) {
Log::error(
"Casuser auth backend: Invalid regex provided for attribute %s: %s (%s)",
$attr, $regex_valid, $filter
);
Log::fatal(I18n::_("Configuration error in CAS auth backend."));
}
$attr_values = self :: get_attr($attr, [], 'array');
if (!$attr_values) {
Log::warning(
"get_user(%s): filter out by attribute %s (not defined)",
$username, $attr
);
Auth::access_denied();
}
$match = false;
foreach ($attr_values as $attr_value) {
if (preg_match($filter, $attr_value)) {
$match = true;
break;
}
}
if (!$match) {
Log::warning(
"get_user(%s): filter out by attribute %s (not match with '%s')",
$username, $attr, $filter
);
Auth::access_denied();
}
}
else {
Log::error(
"Casuser auth backend: Invalid filter rule configured (%s => %s)",
vardump($attr), vardump($filter)
);
Log::fatal(I18n::_("Configuration error in CAS auth backend."));
}
}
}
} }

View file

@ -0,0 +1,20 @@
{extends file='Tpl:empty.tpl'}
{block name="pagetitle"}{/block}
{block name="content"}
<div class="p-5 mb-4 bg-light rounded-3">
<div class="container-fluid py-5">
<h1 class="display-5 fw-bold">{t domain=$CORE_TEXT_DOMAIN}Access denied{/t}</h1>
<p class="col-md-8 fs-4">
{t escape=off domain=$CORE_TEXT_DOMAIN}You do not have access to this application.{/t}
</p>
{if $details}
<p class="col-md-8 fs-6 fw-lighter fst-italic">
{t 1=$details domain=$CORE_TEXT_DOMAIN}Details: %1{/t}
</p>
{/if}
</div>
</div>
{/block}
{*
# vim: autoindent expandtab tabstop=2 shiftwidth=2 softtabstop=2
*}