Skip to content
Snippets Groups Projects
Verified Commit 132162d0 authored by Pavel Břoušek's avatar Pavel Břoušek
Browse files

feat: regular expression support for context options

parent 17939463
No related branches found
No related tags found
1 merge request!46utility class for proxy, configurable lists of AuthnContextClassRefs
...@@ -78,11 +78,13 @@ Add an instance of the auth proc filter with example configuration `authswitcher ...@@ -78,11 +78,13 @@ Add an instance of the auth proc filter with example configuration `authswitcher
'max_user_capability_attr' => 'maxUserCapability', 'max_user_capability_attr' => 'maxUserCapability',
'max_auth' => 'https://id.muni.cz/profile/maxAuth', 'max_auth' => 'https://id.muni.cz/profile/maxAuth',
//'password_contexts' => array_merge(AuthSwitcher::PASSWORD_CONTEXTS, [ //'password_contexts' => array_merge(AuthSwitcher::PASSWORD_CONTEXTS, [
// 'my-custom-authn-context-for-password' // 'my-custom-authn-context-for-password',
// '/^my-regex-.*/',
//]), //]),
//'mfa_contexts' => array_merge(AuthSwitcher::MFA_CONTEXTS, [ //'mfa_contexts' => array_merge(AuthSwitcher::MFA_CONTEXTS, [
// 'my-custom-authn-context-for-mfa' // 'my-custom-authn-context-for-mfa',
//]), //]),
//'contexts_regex' => true,
], ],
'configs' => [ 'configs' => [
'totp:Totp' => [ 'totp:Totp' => [
...@@ -123,6 +125,8 @@ Add an instance of the auth proc filter with example configuration `authswitcher ...@@ -123,6 +125,8 @@ Add an instance of the auth proc filter with example configuration `authswitcher
], ],
``` ```
You can override which AuthnContextClassRefs are treated as password authentication (`password_contexts`) and MFA authentication (`mfa_contexts`). It is recommended to keep the contexts supported by default, e.g. by merging arrays. If you set `contexts_regex` to `true` and a value in one of these options is a regular expression (wrapped in `/`), all contexts matching the expression are matched (but the regular expression is never used as a response).
## MFA tokens ## MFA tokens
This module expects that there will be a user attribute (`$attributes` aka `$state['Attributes']`) with This module expects that there will be a user attribute (`$attributes` aka `$state['Attributes']`) with
......
...@@ -97,10 +97,25 @@ class SwitchAuth extends \SimpleSAML\Auth\ProcessingFilter ...@@ -97,10 +97,25 @@ class SwitchAuth extends \SimpleSAML\Auth\ProcessingFilter
$this->sfa_len_attr = $config->getString('sfa_len_attr', $this->sfa_len_attr); $this->sfa_len_attr = $config->getString('sfa_len_attr', $this->sfa_len_attr);
$this->check_entropy = $config->getBoolean('check_entropy', $this->check_entropy); $this->check_entropy = $config->getBoolean('check_entropy', $this->check_entropy);
$this->contexts_regex = $config->getBoolean('contexts_regex', false);
$this->password_contexts = $config->getArray('password_contexts', AuthSwitcher::PASSWORD_CONTEXTS); $this->password_contexts = $config->getArray('password_contexts', AuthSwitcher::PASSWORD_CONTEXTS);
$this->mfa_contexts = $config->getArray('mfa_contexts', AuthSwitcher::MFA_CONTEXTS); $this->mfa_contexts = $config->getArray('mfa_contexts', AuthSwitcher::MFA_CONTEXTS);
if ($this->contexts_regex) {
$this->password_contexts_patterns = array_filter(self::is_regex, $this->password_contexts);
$this->password_contexts = array_diff($this->password_contexts, $this->password_contexts_patterns);
$this->mfa_contexts_patterns = array_filter(self::is_regex, $this->mfa_contexts);
$this->mfa_contexts = array_diff($this->mfa_contexts, $this->mfa_contexts_patterns);
} else {
$this->password_contexts_patterns = [];
$this->mfa_contexts_patterns = [];
}
$this->authnContextHelper = new AuthnContextHelper($this->password_contexts, $this->mfa_contexts); $this->authnContextHelper = new AuthnContextHelper(
$this->password_contexts,
$this->mfa_contexts,
$this->password_contexts_patterns,
$this->mfa_contexts_patterns
);
} }
/** /**
...@@ -191,6 +206,11 @@ class SwitchAuth extends \SimpleSAML\Auth\ProcessingFilter ...@@ -191,6 +206,11 @@ class SwitchAuth extends \SimpleSAML\Auth\ProcessingFilter
} }
} }
private static function is_regex($str)
{
return strlen($str) > 2 && substr($str, 0, 1) === '/' && substr($str, -1) === '/';
}
private function checkSfaEntropy($attributes) private function checkSfaEntropy($attributes)
{ {
if (!$this->sfa_len_attr || !$this->sfa_alphabet_attr || !in_array( if (!$this->sfa_len_attr || !$this->sfa_alphabet_attr || !in_array(
......
...@@ -14,17 +14,23 @@ use SimpleSAML\Module\saml\Error\NoAuthnContext; ...@@ -14,17 +14,23 @@ use SimpleSAML\Module\saml\Error\NoAuthnContext;
*/ */
class AuthnContextHelper class AuthnContextHelper
{ {
public function __construct($password_contexts, $mfa_contexts) public function __construct(
{ $password_contexts,
$mfa_contexts,
$password_contexts_patterns = [],
$mfa_contexts_patterns = []
) {
$this->password_contexts = $password_contexts; $this->password_contexts = $password_contexts;
$this->password_contexts_patterns = $password_contexts_patterns;
$this->mfa_contexts = $mfa_contexts; $this->mfa_contexts = $mfa_contexts;
$this->mfa_contexts_patterns = $mfa_contexts_patterns;
$this->supported_contexts = array_merge($this->mfa_contexts, $this->password_contexts); $this->supported_contexts = array_merge($this->mfa_contexts, $this->password_contexts);
$this->default_requested_contexts = array_merge($this->password_contexts, $this->mfa_contexts); $this->default_requested_contexts = array_merge($this->password_contexts, $this->mfa_contexts);
} }
public function MFAin($contexts) public function MFAin($contexts)
{ {
return !empty(array_intersect($this->mfa_contexts, $contexts)); return $this->contextsMatch($contexts, $this->mfa_contexts, $this->mfa_contexts_patterns);
} }
public function isMFAprefered($supportedRequestedContexts = []) public function isMFAprefered($supportedRequestedContexts = [])
...@@ -95,7 +101,27 @@ class AuthnContextHelper ...@@ -95,7 +101,27 @@ class AuthnContextHelper
public function SFAin($contexts) public function SFAin($contexts)
{ {
return !empty(array_intersect($this->password_contexts, $contexts)); return $this->contextsMatch($contexts, $this->password_contexts, $this->password_contexts_patterns);
}
private static function inPatterns($patterns, $contexts)
{
foreach ($patterns as $pattern) {
foreach ($contexts as $context) {
if (preg_match($pattern, $context)) {
return true;
}
}
}
return false;
}
private function contextsMatch($inputContexts, $matchedContexts, $matchedPatterns)
{
return !empty(array_intersect($matchedContexts, $inputContexts)) || self::inPatterns(
$matchedPatterns,
$inputContexts
);
} }
/** /**
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment