Skip to content

Commit c6da2b7

Browse files
committed
Fix source selectors
1 parent db997a7 commit c6da2b7

4 files changed

Lines changed: 115 additions & 34 deletions

File tree

modules/core/src/Auth/Source/Selector/RequestedAuthnContextSelector.php renamed to modules/core/src/Auth/Source/RequestedAuthnContextSelector.php

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22

33
declare(strict_types=1);
44

5-
namespace SimpleSAML\Module\core\Auth\Source\Selector;
5+
namespace SimpleSAML\Module\core\Auth\Source;
66

77
use SAML2\Constants as C;
88
use SAML2\Exception\Protocol\NoAuthnContextException;
99
use SimpleSAML\Assert\Assert;
1010
use SimpleSAML\Error\Exception;
1111
use SimpleSAML\Logger;
12-
use SimpleSAML\Module\core\Auth\Source\AbstractSourceSelector;
1312

1413
use function array_key_exists;
1514
use function sprintf;
@@ -25,17 +24,17 @@ class RequestedAuthnContextSelector extends AbstractSourceSelector
2524
/**
2625
* The key of the AuthId field in the state.
2726
*/
28-
public const AUTHID = '\SimpleSAML\Module\core\Auth\Source\Selector\RequestedAuthnContextSelector.AuthId';
27+
public const AUTHID = '\SimpleSAML\Module\core\Auth\Source\RequestedAuthnContextSelector.AuthId';
2928

3029
/**
3130
* The string used to identify our states.
3231
*/
33-
public const STAGEID = '\SimpleSAML\Module\core\Auth\Source\Selector\RequestedAuthnContextSelector.StageId';
32+
public const STAGEID = '\SimpleSAML\Module\core\Auth\Source\RequestedAuthnContextSelector.StageId';
3433

3534
/**
3635
* The key where the sources is saved in the state.
3736
*/
38-
public const SOURCESID = '\SimpleSAML\Module\core\Auth\Source\Selector\RequestedAuthnContextSelector.SourceId';
37+
public const SOURCESID = '\SimpleSAML\Module\core\Auth\Source\RequestedAuthnContextSelector.SourceId';
3938

4039
/**
4140
* @var string The default authentication source to use when no RequestedAuthnContext is passed
@@ -74,20 +73,19 @@ public function __construct(array $info, array $config)
7473
parent::__construct($info, $config);
7574

7675
Assert::keyExists($config, 'contexts');
76+
Assert::keyExists($config['contexts'], 'default');
77+
Assert::stringNotEmpty($config['contexts']['default']);
78+
$this->defaultSource = $config['contexts']['default'];
79+
unset($config['contexts']['default']);
7780

7881
foreach ($config['contexts'] as $key => $context) {
79-
if ($key === 'default') {
80-
Assert::stringNotEmpty($config['contexts']['default']);
81-
$this->defaultSource = $config['contexts']['default'];
82+
Assert::natural($key);
83+
if (!array_key_exists('identifier', $context)) {
84+
throw new Exception(sprintf("Incomplete context '%d' due to missing `identifier` key.", $key));
85+
} elseif (!array_key_exists('source', $context)) {
86+
throw new Exception(sprintf("Incomplete context '%d' due to missing `source` key.", $key));
8287
} else {
83-
Assert::natural($key);
84-
if (!array_key_exists('identifier', $context)) {
85-
throw new Exception(sprintf("Incomplete context '%d' due to missing `identifier` key.", $key));
86-
} elseif (!array_key_exists('source', $context)) {
87-
throw new Exception(sprintf("Incomplete context '%d' due to missing `source` key.", $key));
88-
} else {
89-
$this->contexts[$key] = $context;
90-
}
88+
$this->contexts[$key] = $context;
9189
}
9290
}
9391
}
@@ -117,12 +115,12 @@ protected function selectAuthSource(array &$state): string
117115
* The set of supplied references MUST be evaluated as an ordered set, where the first element
118116
* is the most preferred authentication context class or declaration.
119117
*/
120-
$index = false;
121118
foreach ($requestedContexts['AuthnContextClassRef'] as $requestedContext) {
122119
switch ($comparison) {
123120
case 'exact':
124121
foreach ($this->contexts as $index => $context) {
125122
if ($context['identifier'] === $requestedContext) {
123+
$state['saml:AuthnContextClassRef'] = $context['identifier'];
126124
return $context['source'];
127125
}
128126
}

modules/core/src/Auth/Source/Selector/SourceIPSelector.php renamed to modules/core/src/Auth/Source/SourceIPSelector.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@
22

33
declare(strict_types=1);
44

5-
namespace SimpleSAML\Module\core\Auth\Source\Selector;
5+
namespace SimpleSAML\Module\core\Auth\Source;
66

77
use SimpleSAML\Assert\Assert;
88
use SimpleSAML\Error\Exception;
99
use SimpleSAML\Logger;
10-
use SimpleSAML\Module\core\Auth\Source\AbstractSourceSelector;
1110
use SimpleSAML\Utils;
1211

1312
use function array_key_exists;
@@ -24,17 +23,17 @@ class SourceIPSelector extends AbstractSourceSelector
2423
/**
2524
* The key of the AuthId field in the state.
2625
*/
27-
public const AUTHID = '\SimpleSAML\Module\core\Auth\Source\Selector\SourceIPSelector.AuthId';
26+
public const AUTHID = '\SimpleSAML\Module\core\Auth\Source\SourceIPSelector.AuthId';
2827

2928
/**
3029
* The string used to identify our states.
3130
*/
32-
public const STAGEID = '\SimpleSAML\Module\core\Auth\Source\Selector\SourceIPSelector.StageId';
31+
public const STAGEID = '\SimpleSAML\Module\core\Auth\Source\SourceIPSelector.StageId';
3332

3433
/**
3534
* The key where the sources is saved in the state.
3635
*/
37-
public const SOURCESID = '\SimpleSAML\Module\core\Auth\Source\Selector\SourceIPSelector.SourceId';
36+
public const SOURCESID = '\SimpleSAML\Module\core\Auth\Source\SourceIPSelector.SourceId';
3837

3938
/**
4039
* @param string The default authentication source to use when none of the zones match

tests/modules/core/src/Auth/Source/Selector/RequestedAuthnContextSelectorTest.php renamed to tests/modules/core/src/Auth/Source/RequestedAuthnContextSelectorTest.php

Lines changed: 93 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@
22

33
declare(strict_types=1);
44

5-
namespace SimpleSAML\Test\Module\core\Auth\Source\Selector;
5+
namespace SimpleSAML\Test\Module\core\Auth\Source;
66

77
use PHPUnit\Framework\TestCase;
88
use SAML2\Exception\Protocol\NoAuthnContextException;
99
use SimpleSAML\Assert\AssertionFailedException;
1010
use SimpleSAML\Auth;
1111
use SimpleSAML\Configuration;
1212
use SimpleSAML\Error\Exception;
13-
use SimpleSAML\Module\core\Auth\Source\Selector\RequestedAuthnContextSelector;
13+
use SimpleSAML\Module\core\Auth\Source\RequestedAuthnContextSelector;
1414

1515
/**
1616
* @covers \SimpleSAML\Module\core\Auth\Source\AbstractSourceSelector
17-
* @covers \SimpleSAML\Module\core\Auth\Source\Selector\RequestedAuthnContextSelector
17+
* @covers \SimpleSAML\Module\core\Auth\Source\RequestedAuthnContextSelector
1818
*/
1919
class RequestedAuthnContextSelectorTest extends TestCase
2020
{
@@ -78,8 +78,9 @@ public function setUp(): void
7878

7979

8080
/**
81+
* No RequestedAuthnContext
8182
*/
82-
public function testAuthentication(): void
83+
public function testAuthenticationVariant1(): void
8384
{
8485
$info = ['AuthId' => 'selector'];
8586
$config = $this->sourceConfig->getArray('selector');
@@ -94,24 +95,75 @@ public static function doAuthentication(Auth\Source $as, array $state): void
9495
{
9596
// Dummy
9697
}
98+
};
99+
100+
$state = ['saml:RequestedAuthnContext' => ['AuthnContextClassRef' => null]];
101+
$selector->authenticate($state);
102+
$this->assertArrayNotHasKey('saml:AuthnContextClassRef', $state);
103+
}
97104

105+
106+
/**
107+
* Specific RequestedAuthnContext
108+
*/
109+
public function testAuthenticationVariant2(): void
110+
{
111+
$info = ['AuthId' => 'selector'];
112+
$config = $this->sourceConfig->getArray('selector');
113+
114+
$selector = new class ($info, $config) extends RequestedAuthnContextSelector {
98115
/**
99-
* @param array &$state
116+
* @param \SimpleSAML\Auth\Source $as
117+
* @param array $state
100118
* @return void
101119
*/
102-
public function authenticate(array &$state): void
120+
public static function doAuthentication(Auth\Source $as, array $state): void
103121
{
104-
$state['finished'] = true;
122+
// Dummy
105123
}
106124
};
107125

108-
$state = ['saml:RequestedAuthnContext' => ['AuthnContextClassRef' => null]];
126+
$state = ['saml:RequestedAuthnContext' => ['AuthnContextClassRef' => ['urn:x-simplesamlphp:loa1']]];
109127
$selector->authenticate($state);
110-
$this->assertTrue($state['finished']);
128+
$this->assertArrayHasKey('saml:AuthnContextClassRef', $state);
129+
$this->assertEquals('urn:x-simplesamlphp:loa1', $state['saml:AuthnContextClassRef']);
111130
}
112131

113132

114133
/**
134+
* Specific RequestedAuthnContext with comparison=exact
135+
*/
136+
public function testAuthenticationVariant3(): void
137+
{
138+
$info = ['AuthId' => 'selector'];
139+
$config = $this->sourceConfig->getArray('selector');
140+
141+
$selector = new class ($info, $config) extends RequestedAuthnContextSelector {
142+
/**
143+
* @param \SimpleSAML\Auth\Source $as
144+
* @param array $state
145+
* @return void
146+
*/
147+
public static function doAuthentication(Auth\Source $as, array $state): void
148+
{
149+
// Dummy
150+
}
151+
};
152+
153+
$state = [
154+
'saml:RequestedAuthnContext' => [
155+
'AuthnContextClassRef' => ['urn:x-simplesamlphp:loa1'],
156+
'Comparison' => 'exact',
157+
],
158+
];
159+
$selector->authenticate($state);
160+
$this->assertArrayHasKey('saml:AuthnContextClassRef', $state);
161+
$this->assertEquals('urn:x-simplesamlphp:loa1', $state['saml:AuthnContextClassRef']);
162+
}
163+
164+
165+
/**
166+
* Missing source
115167
*/
116168
public function testIncompleteConfigurationThrowsExceptionVariant1(): void
117169
{
@@ -123,6 +175,7 @@ public function testIncompleteConfigurationThrowsExceptionVariant1(): void
123175
10 => [
124176
'identifier' => 'urn:x-simplesamlphp:loa1',
125177
],
178+
'default' => 'phpunit',
126179
],
127180
],
128181
]);
@@ -140,6 +193,7 @@ public function testIncompleteConfigurationThrowsExceptionVariant1(): void
140193

141194

142195
/**
196+
* Missing identifier
143197
*/
144198
public function testIncompleteConfigurationThrowsExceptionVariant2(): void
145199
{
@@ -151,6 +205,7 @@ public function testIncompleteConfigurationThrowsExceptionVariant2(): void
151205
10 => [
152206
'source' => 'loa1',
153207
],
208+
'default' => 'phpunit',
154209
],
155210
],
156211
]);
@@ -167,6 +222,35 @@ public function testIncompleteConfigurationThrowsExceptionVariant2(): void
167222
}
168223

169224

225+
/**
226+
* Missing default
227+
*/
228+
public function testIncompleteConfigurationThrowsExceptionVariant3(): void
229+
{
230+
$sourceConfig = Configuration::loadFromArray([
231+
'selector' => [
232+
'core:RequestedAuthnContextSelector',
233+
234+
'contexts' => [
235+
10 => [
236+
'source' => 'loa1',
237+
],
238+
],
239+
],
240+
]);
241+
242+
Configuration::setPreLoadedConfig($this->sourceConfig, 'authsources.php');
243+
244+
$info = ['AuthId' => 'selector'];
245+
$config = $sourceConfig->getArray('selector');
246+
247+
$this->expectException(AssertionFailedException::class);
248+
$this->expectExceptionMessage('Expected the key "default" to exist.');
249+
250+
new RequestedAuthnContextSelector($info, $config);
251+
}
252+
253+
170254
/**
171255
* @dataProvider provideRequestedAuthnContext
172256
* @param array $requestedAuthnContext The RequestedAuthnContext

tests/modules/core/src/Auth/Source/Selector/SourceIPSelectorTest.php renamed to tests/modules/core/src/Auth/Source/SourceIPSelectorTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@
22

33
declare(strict_types=1);
44

5-
namespace SimpleSAML\Test\Module\core\Auth\Source\Selector;
5+
namespace SimpleSAML\Test\Module\core\Auth\Source;
66

77
use PHPUnit\Framework\TestCase;
88
use SimpleSAML\Assert\AssertionFailedException;
99
use SimpleSAML\Auth;
1010
use SimpleSAML\Configuration;
1111
use SimpleSAML\Error\Exception;
12-
use SimpleSAML\Module\core\Auth\Source\Selector\SourceIPSelector;
12+
use SimpleSAML\Module\core\Auth\Source\SourceIPSelector;
1313

1414
/**
1515
* @covers \SimpleSAML\Module\core\Auth\Source\AbstractSourceSelector
16-
* @covers \SimpleSAML\Module\core\Auth\Source\Selector\SourceIPSelector
16+
* @covers \SimpleSAML\Module\core\Auth\Source\SourceIPSelector
1717
*/
1818
class SourceIPSelectorTest extends TestCase
1919
{

0 commit comments

Comments
 (0)