Skip to content
This repository was archived by the owner on Sep 19, 2022. It is now read-only.

Commit ebafb05

Browse files
author
Dominik Frantisek Bucik
committed
feat: 🎸 Added RestoreAcrs authproc filter, modify ACRs when MFA
1 parent 50848e9 commit ebafb05

File tree

2 files changed

+92
-0
lines changed

2 files changed

+92
-0
lines changed

lib/Auth/Process/RestoreAcrs.php

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SimpleSAML\Module\perun\Auth\Process;
6+
7+
use SimpleSAML\Auth\ProcessingFilter;
8+
use SimpleSAML\Logger;
9+
use SimpleSAML\Module\perun\Disco;
10+
11+
/**
12+
* Auth proc filter, which should be used if Proxy provides MFA, and wants to first pass the request to the upstream
13+
* IdP. The Perun Disco page, tries to add new ACRs to the request, if MFA has been requested, to prevent the IdP from
14+
* failing. As a result, we then get modified requested ACRs, which should be restored to the previous (original) state
15+
* using this authproc filter. It should be run on one of the first places of the IdP authproc chain.
16+
*/
17+
class RestoreAcrs extends ProcessingFilter
18+
{
19+
public const CONFIG_FILE_NAME = 'module_perun.php';
20+
21+
private const DEBUG_PREFIX = 'perun:RestoreAcrs';
22+
23+
public function __construct($config, $reserved)
24+
{
25+
parent::__construct($config, $reserved);
26+
}
27+
28+
public function process(&$request)
29+
{
30+
$this->restoreAcrs($request);
31+
}
32+
33+
public static function storeAcrs(array &$state, array $acrsToAdd)
34+
{
35+
if (!empty($acrsToAdd)
36+
&& !empty($state[Disco::SAML_REQUESTED_AUTHN_CONTEXT][Disco::STATE_AUTHN_CONTEXT_CLASS_REF])
37+
&& sizeof($state[Disco::SAML_REQUESTED_AUTHN_CONTEXT][Disco::STATE_AUTHN_CONTEXT_CLASS_REF]) <= 1
38+
&& in_array(
39+
Disco::MFA_PROFILE,
40+
$state[Disco::SAML_REQUESTED_AUTHN_CONTEXT][Disco::STATE_AUTHN_CONTEXT_CLASS_REF],
41+
true
42+
)
43+
) {
44+
Logger::debug(
45+
self::DEBUG_PREFIX . ': Modifying ACRs list to pass regular authentication to the IdP to fallback to proxy MFA'
46+
);
47+
$original = [];
48+
$new = [];
49+
foreach ($state[Disco::SAML_REQUESTED_AUTHN_CONTEXT][Disco::STATE_AUTHN_CONTEXT_CLASS_REF] as $acr) {
50+
$original[] = $acr;
51+
$new[] = $acr;
52+
}
53+
foreach ($acrsToAdd as $acr) {
54+
$new[] = $acr;
55+
}
56+
$new = array_unique($new);
57+
58+
unset($state[Disco::SAML_REQUESTED_AUTHN_CONTEXT][Disco::STATE_AUTHN_CONTEXT_CLASS_REF]);
59+
if (isset($state[Disco::SAML_REQUESTED_AUTHN_CONTEXT_ORIGINAL])) {
60+
unset($state[Disco::SAML_REQUESTED_AUTHN_CONTEXT_ORIGINAL]);
61+
}
62+
Logger::debug(self::DEBUG_PREFIX . ': original ACRs: ' . join(',', $original));
63+
$state[Disco::SAML_REQUESTED_AUTHN_CONTEXT_ORIGINAL] = $original;
64+
Logger::debug(self::DEBUG_PREFIX . ': ACRs after modification: ' . join(',', $new));
65+
$state[Disco::SAML_REQUESTED_AUTHN_CONTEXT][Disco::STATE_AUTHN_CONTEXT_CLASS_REF] = $new;
66+
}
67+
}
68+
69+
private function restoreAcrs(&$request)
70+
{
71+
if (!empty($request[Disco::SAML_REQUESTED_AUTHN_CONTEXT_ORIGINAL])) {
72+
unset($request[Disco::SAML_REQUESTED_AUTHN_CONTEXT][Disco::STATE_AUTHN_CONTEXT_CLASS_REF]);
73+
$handle = &$request[Disco::SAML_REQUESTED_AUTHN_CONTEXT][Disco::STATE_AUTHN_CONTEXT_CLASS_REF];
74+
$handle = $request[Disco::SAML_REQUESTED_AUTHN_CONTEXT_ORIGINAL];
75+
unset($request[Disco::SAML_REQUESTED_AUTHN_CONTEXT_ORIGINAL]);
76+
Logger::debug(self::DEBUG_PREFIX . ': ACRS restored: ' . join(',', $handle));
77+
}
78+
}
79+
}

lib/Disco.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ class Disco extends PowerIdPDisco
3030

3131
public const DEFAULT_THEME = 'perun';
3232

33+
public const MFA_PROFILE = 'https://refeds.org/profile/mfa';
34+
3335
// ROOT CONFIGURATION ENTRY
3436
public const WAYF = 'wayf_config';
3537

@@ -48,6 +50,8 @@ class Disco extends PowerIdPDisco
4850

4951
public const DISPLAY_SP = 'display_sp_name';
5052

53+
public const ADD_AUTHN_CONTEXT_CLASSES_FOR_MFA = 'add_authn_context_classes_for_mfa';
54+
5155
// CONFIGURATION ENTRIES IDP BLOCKS
5256
public const IDP_BLOCKS = 'idp_blocks_config';
5357

@@ -123,6 +127,8 @@ class Disco extends PowerIdPDisco
123127

124128
public const SAML_REQUESTED_AUTHN_CONTEXT = 'saml:RequestedAuthnContext';
125129

130+
public const SAML_REQUESTED_AUTHN_CONTEXT_ORIGINAL = 'saml:RequestedAuthnContext:original';
131+
126132
public const STATE_AUTHN_CONTEXT_CLASS_REF = 'AuthnContextClassRef';
127133

128134
public const SAML_SP_SSO = 'saml:sp:sso';
@@ -186,6 +192,7 @@ public function __construct(array $metadataSets, $instance)
186192
$this->originalAuthnContextClassRef = $state[self::SAML_REQUESTED_AUTHN_CONTEXT][self::AUTHN_CONTEXT_CLASS_REF];
187193

188194
$this->removeAuthContextClassRefWithPrefixes($state);
195+
$this->prepareAcrsForMfa($state);
189196
if (isset($state['IdPMetadata']['entityid'])) {
190197
$this->proxyIdpEntityId = $state['IdPMetadata']['entityid'];
191198
}
@@ -980,4 +987,10 @@ private function fillSpNameForSaml($t)
980987
$this->spName = $t->translate->getTranslation($this->originalsp[self::NAME]);
981988
}
982989
}
990+
991+
private function prepareAcrsForMfa(array &$state)
992+
{
993+
$contextsToAdd = $this->wayfConfiguration->getArray(self::ADD_AUTHN_CONTEXT_CLASSES_FOR_MFA, []);
994+
Module\perun\Auth\Process\RestoreAcrs::storeAcrs($state, $contextsToAdd);
995+
}
983996
}

0 commit comments

Comments
 (0)