Compare commits

..

2 commits

Author SHA1 Message Date
Benjamin Renard
8f9b361b8d
Casuser auth backend: add user access filters feature 2024-02-21 10:13:27 +01:00
Benjamin Renard
82122089f9
Check: add regex() 2024-02-21 10:12:09 +01:00
9 changed files with 198 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,15 @@ class Cas extends Method {
'default' => null, 'default' => null,
), ),
), ),
/*
* CAS user backend access filters:
* [
* attr => regex,
* attr => callable (will receive the attr value as parameter),
* callable, (will receive associative array of all attrs values as parameter)
* ]
*/
'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,59 @@ 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_value = self :: get_attr($attr);
if (!$attr_value) {
Log::warning(
"get_user(%s): filter out by attribute %s (not defined)",
$username, $attr
);
Auth::access_denied();
}
if (!preg_match($filter, self :: get_attr($attr))) {
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

@ -134,6 +134,31 @@ class Check {
return false; return false;
} }
/**
* Check value is a valid regex (PREG)
* @param mixed $value The value to check
* @param boolean $return_error Set to true to get an error message about the error
* @return ( $return_error is true ? string|true : boolean )
*/
public static function regex($value, $return_error=false) {
if (!is_string($value))
return $return_error?'PREG ERROR: not a string':false;
if (@preg_match($value, null) === false) { // @phpstan-ignore-line
if ($return_error)
return sprintf(
'PREG ERROR #%d%s',
preg_last_error(),
(
function_exists('preg_last_error_msg')?
sprintf(" (%s)", preg_last_error_msg()):
""
)
);
return false;
}
return true;
}
} }
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab # vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab

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
*}