diff --git a/README.md b/README.md index 3c3e692a2f325f32e7555a81ace2d3bdba67ab3e..3bfde16e14ba5284e58b9b339dca24fc7ca30f6c 100755 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ Use this filter to read user mfa tokens from PrivacyIDEA server to state attribu 'tokens_Attr' => 'privacyIDEATokens', 'privacy_idea_username' => 'admin', 'privacy_idea_passwd' => 'secret', + //'privacy_idea_realm' => 'superadminrealm', // optional 'privacy_idea_domain' => 'https://mfa.id.muni.cz', 'tokens_type' => [ 'TOTP', @@ -45,6 +46,8 @@ Use this filter to read user mfa tokens from PrivacyIDEA server to state attribu ], 'user_attribute' => 'eduPersonPrincipalName', 'token_type_attr' => 'type', + //'connect_timeout' => 10, // optional, connect timeout in seconds + //'timeout' => 10, // optional, timeout in seconds ], ], ``` diff --git a/lib/Auth/Process/GetMfaTokensPrivacyIDEA.php b/lib/Auth/Process/GetMfaTokensPrivacyIDEA.php index 16a9228e2cdbd4079966771e5ed756a79ce35e95..8130aee9f6499d6cf79fd0e045efff571f261d28 100644 --- a/lib/Auth/Process/GetMfaTokensPrivacyIDEA.php +++ b/lib/Auth/Process/GetMfaTokensPrivacyIDEA.php @@ -16,12 +16,18 @@ class GetMfaTokensPrivacyIDEA extends \SimpleSAML\Auth\ProcessingFilter private const AS_PI_AUTH_TOKEN = 'auth_token'; private const AS_PI_AUTH_TOKEN_ISSUED_AT = 'auth_token_issued_at'; + private $connect_timeout = 0; + + private $timeout; + private $tokens_attr = 'mfaTokens'; private $privacy_idea_username; private $privacy_idea_passwd; + private $privacy_idea_realm; + private $privacy_idea_domain; private $tokens_type = ['TOTP', 'WebAuthn']; @@ -39,9 +45,12 @@ class GetMfaTokensPrivacyIDEA extends \SimpleSAML\Auth\ProcessingFilter parent::__construct($config, $reserved); $config = Configuration::loadFromArray($config['config']); + $this->connect_timeout = $config->getInteger('connect_timeout', $this->connect_timeout); + $this->timeout = $config->getInteger('timeout', $this->timeout); $this->tokens_attr = $config->getString('tokens_Attr', $this->tokens_attr); $this->privacy_idea_username = $config->getString('privacy_idea_username'); $this->privacy_idea_passwd = $config->getString('privacy_idea_passwd'); + $this->privacy_idea_realm = $config->getString('privacy_idea_realm', null); $this->privacy_idea_domain = $config->getString('privacy_idea_domain'); $this->tokens_type = $config->getArray('tokens_type', $this->tokens_type); $this->user_attribute = $config->getString('user_attribute', $this->user_attribute); @@ -58,7 +67,7 @@ class GetMfaTokensPrivacyIDEA extends \SimpleSAML\Auth\ProcessingFilter $state[Authswitcher::PRIVACY_IDEA_FAIL] = false; $state['Attributes'][$this->tokens_attr] = []; $admin_token = $this->getAdminToken(); - if (null === $admin_token) { + if (empty($admin_token)) { $state[AuthSwitcher::PRIVACY_IDEA_FAIL] = true; return; @@ -98,8 +107,15 @@ class GetMfaTokensPrivacyIDEA extends \SimpleSAML\Auth\ProcessingFilter 'username' => $this->privacy_idea_username, 'password' => $this->privacy_idea_passwd, ]; + if (null !== $this->privacy_idea_realm) { + $data['realm'] = $this->privacy_idea_realm; + } $ch = curl_init(); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->connect_timeout); + if (null !== $this->timeout) { + curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout); + } curl_setopt($ch, CURLOPT_URL, $this->privacy_idea_domain . '/auth'); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); $paramsJson = json_encode($data); @@ -118,9 +134,13 @@ class GetMfaTokensPrivacyIDEA extends \SimpleSAML\Auth\ProcessingFilter return $response['result']['value']['token']; } - private function getPrivacyIdeaTokensByType($state, $type, $admin_token) + private function getPrivacyIdeaTokensByType(&$state, $type, $admin_token) { $ch = curl_init(); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->connect_timeout); + if (null !== $this->timeout) { + curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout); + } curl_setopt($ch, CURLOPT_URL, $this->privacy_idea_domain . '/token/?user=' . $state['Attributes'][$this->user_attribute][0] . '&active=True&type=' . $type); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET'); diff --git a/lib/Auth/Process/SwitchAuth.php b/lib/Auth/Process/SwitchAuth.php index 1fdf924a521a9aada24de67e442f16a6ef1435d2..1e55fd25f130d0ee713eb3c091901c55f6f88cbe 100644 --- a/lib/Auth/Process/SwitchAuth.php +++ b/lib/Auth/Process/SwitchAuth.php @@ -122,22 +122,19 @@ class SwitchAuth extends \SimpleSAML\Auth\ProcessingFilter self::info('supported requested contexts: ' . json_encode($state[AuthSwitcher::SUPPORTED_REQUESTED_CONTEXTS])); - if ( - $this->mfa_preferred_privacyidea_fail && isset($state[AuthSwitcher::PRIVACY_IDEA_FAIL]) && - $state[AuthSwitcher::PRIVACY_IDEA_FAIL] && - AuthnContextHelper::isMFAprefered($state[Authswitcher::SUPPORTED_REQUESTED_CONTEXTS]) && - !AuthnContextHelper::MFAin([$upstreamContext]) - ) { - throw new Exception(self::DEBUG_PREFIX . 'MFA is preferred but connection to privacyidea failed.'); - } - - // switch to MFA if enforced or preferred but not already done if we handle the proxy mode - $performMFA = AuthnContextHelper::MFAin($usersCapabilities) && !AuthnContextHelper::MFAin([ + $shouldPerformMFA = !AuthnContextHelper::MFAin([ $upstreamContext, ]) && ($this->mfa_enforced || AuthnContextHelper::isMFAprefered( $state[AuthSwitcher::SUPPORTED_REQUESTED_CONTEXTS] )); + if ($this->mfa_preferred_privacyidea_fail && !empty($state[AuthSwitcher::PRIVACY_IDEA_FAIL]) && $shouldPerformMFA) { + throw new Exception(self::DEBUG_PREFIX . 'MFA should be performed but connection to privacyidea failed.'); + } + + // switch to MFA if enforced or preferred but not already done if we handle the proxy mode + $performMFA = AuthnContextHelper::MFAin($usersCapabilities) && $shouldPerformMFA; + $maxUserCapability = ''; if (in_array(AuthSwitcher::MFA, $usersCapabilities, true)) { $maxUserCapability = AuthSwitcher::MFA;