Skip to content

Commit 6eca10c

Browse files
authored
Merge pull request #336 from OpenConext/feature/use-entiltlement-saml-attributes-for-activation-flow
Use saml entitlement attribute for activation flow
2 parents a2ea132 + 6b4ed2b commit 6eca10c

17 files changed

+367
-184
lines changed

ci/qa/phpstan-baseline.php

-20
Original file line numberDiff line numberDiff line change
@@ -741,21 +741,6 @@
741741
'count' => 1,
742742
'path' => __DIR__ . '/../../src/Surfnet/StepupSelfService/SelfServiceBundle/Security/Factory/SamlFactory.php',
743743
];
744-
$ignoreErrors[] = [
745-
'message' => '#^Method Surfnet\\\\StepupSelfService\\\\SelfServiceBundle\\\\Service\\\\ActivationFlowService\\:\\:__construct\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#',
746-
'count' => 1,
747-
'path' => __DIR__ . '/../../src/Surfnet/StepupSelfService/SelfServiceBundle/Service/ActivationFlowService.php',
748-
];
749-
$ignoreErrors[] = [
750-
'message' => '#^Method Surfnet\\\\StepupSelfService\\\\SelfServiceBundle\\\\Service\\\\ActivationFlowService\\:\\:getPreference\\(\\) should return Surfnet\\\\StepupSelfService\\\\SelfServiceBundle\\\\Value\\\\ActivationFlowPreferenceInterface but returns mixed\\.$#',
751-
'count' => 1,
752-
'path' => __DIR__ . '/../../src/Surfnet/StepupSelfService/SelfServiceBundle/Service/ActivationFlowService.php',
753-
];
754-
$ignoreErrors[] = [
755-
'message' => '#^Parameter \\#1 \\$preference of class Surfnet\\\\StepupSelfService\\\\SelfServiceBundle\\\\Value\\\\ActivationFlowPreference constructor expects string, array\\|string given\\.$#',
756-
'count' => 1,
757-
'path' => __DIR__ . '/../../src/Surfnet/StepupSelfService/SelfServiceBundle/Service/ActivationFlowService.php',
758-
];
759744
$ignoreErrors[] = [
760745
'message' => '#^Parameter \\#2 \\$array of function array_key_exists expects array, array\\<string, int\\|string\\>\\|false given\\.$#',
761746
'count' => 1,
@@ -1126,11 +1111,6 @@
11261111
'count' => 1,
11271112
'path' => __DIR__ . '/../../src/Surfnet/StepupSelfService/SelfServiceBundle/Twig/Extensions/Extension/SecondFactorType.php',
11281113
];
1129-
$ignoreErrors[] = [
1130-
'message' => '#^Property Surfnet\\\\StepupSelfService\\\\SelfServiceBundle\\\\Value\\\\ActivationFlowPreference\\:\\:\\$allowedPreferences type has no value type specified in iterable type array\\.$#',
1131-
'count' => 1,
1132-
'path' => __DIR__ . '/../../src/Surfnet/StepupSelfService/SelfServiceBundle/Value/ActivationFlowPreference.php',
1133-
];
11341114
$ignoreErrors[] = [
11351115
'message' => '#^Cannot cast mixed to string\\.$#',
11361116
'count' => 2,

config/openconext/parameters.yaml.dist

+4
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ parameters:
7575

7676
preferred_activation_flow_name: activate
7777
preferred_activation_flow_options: [ra, self]
78+
activation_flow_attribute_name: urn:mace:dir:attribute-def:eduPersonEntitlement
79+
activation_flow_attributes:
80+
ra: urn:mace:surf.nl:surfsecureid:activation:ra
81+
self: urn:mace:surf.nl:surfsecureid:activation:self
7882

7983
# Self-asserted tokens: enable/disable recovery methods
8084
#

config/packages/security.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ security:
1717

1818
saml_based:
1919
custom_authenticators:
20-
- Surfnet\SamlBundle\Security\Authentication\SamlAuthenticator
20+
- Surfnet\StepupSelfService\SelfServiceBundle\Security\Authentication\SamlAuthenticator
2121
logout:
2222
path: /logout
2323

config/packages/surfnet_stepup_self_service_self_service.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,5 @@ surfnet_stepup_self_service_self_service:
1111
preferred_activation_flow:
1212
query_string_field_name: "%preferred_activation_flow_name%"
1313
options: "%preferred_activation_flow_options%"
14+
saml_attribute_field_name: "%activation_flow_attribute_name%"
15+
saml_attributes: "%activation_flow_attributes%"

config/services.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ services:
5757
- '@security.helper'
5858
- "%logout_redirect_url%"
5959

60+
Surfnet\StepupSelfService\SelfServiceBundle\Security\Authentication\SamlAuthenticator:
6061

6162
Surfnet\StepupSelfService\SelfServiceBundle\EventListener\AuthenticatedUserListener:
6263
Surfnet\StepupSelfService\SelfServiceBundle\EventListener\ExplicitSessionTimeoutListener:

src/Surfnet/StepupSelfService/SelfServiceBundle/Controller/EntryPointController.php

+4-4
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@
2020

2121
namespace Surfnet\StepupSelfService\SelfServiceBundle\Controller;
2222

23-
use Surfnet\StepupSelfService\SelfServiceBundle\Security\Authentication\AuthenticatedSessionStateHandler;
2423
use Surfnet\StepupSelfService\SelfServiceBundle\Service\ActivationFlowService;
2524
use Surfnet\StepupSelfService\SelfServiceBundle\Service\SecondFactorService;
2625
use Surfnet\StepupSelfService\SelfServiceBundle\Service\SelfAssertedTokens\RecoveryTokenService;
2726
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
2827
use Symfony\Component\HttpFoundation\RedirectResponse;
28+
use Symfony\Component\HttpFoundation\Request;
2929
use Symfony\Component\Routing\Attribute\Route;
3030

3131
class EntryPointController extends AbstractController
@@ -34,17 +34,17 @@ public function __construct(
3434
private readonly SecondFactorService $secondFactorService,
3535
private readonly RecoveryTokenService $recoveryTokenService,
3636
private readonly ActivationFlowService $activationFlowService,
37-
private readonly AuthenticatedSessionStateHandler $authStateHandler
3837
) {
3938
}
4039
#[Route(path: '/', name: 'ss_entry_point', methods:['GET'])]
41-
public function decideSecondFactorFlow() : RedirectResponse
40+
public function decideSecondFactorFlow(Request $request) : RedirectResponse
4241
{
4342
$identity = $this->getUser()->getIdentity();
4443
$hasSecondFactor = $this->secondFactorService->doSecondFactorsExistForIdentity($identity->id);
4544
$hasRecoveryToken = $this->recoveryTokenService->hasRecoveryToken($identity);
4645
// Check if we need to do a registration flow nudge
47-
$this->activationFlowService->process($this->authStateHandler->getCurrentRequestUri());
46+
// This is only used when already logged in
47+
$this->activationFlowService->processPreferenceFromUri($request->getUri());
4848

4949
return $hasSecondFactor || $hasRecoveryToken
5050
? $this->redirectToRoute('ss_second_factor_list')

src/Surfnet/StepupSelfService/SelfServiceBundle/DependencyInjection/Configuration.php

+22-3
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,29 @@ private function appendActivationFlow(NodeBuilder $childNodes): void
132132
->end()
133133
->arrayNode('options')
134134
->prototype('scalar')
135+
->isRequired()
136+
->info('The options describing the preferred activation flow. Example: ra, self')
137+
->end()
138+
->end()
139+
->scalarNode('saml_attribute_field_name')
135140
->isRequired()
136-
->info('The options describing the preferred activation flow. Example: ra, self')
141+
->info('The name of the entitlement attribute (in SAML assertion) that is read to determine the preferred activation flow')
137142
->end()
138-
->end()
139-
->end();
143+
->arrayNode('saml_attributes')
144+
->isRequired()
145+
->children()
146+
->scalarNode('ra')
147+
->isRequired()
148+
->info('The entitlement attribute name for the ra vetting flow')
149+
->end()
150+
->scalarNode('self')
151+
->isRequired()
152+
->info('The entitlement attribute name for the self vetting flow')
153+
->end()
154+
->end()
155+
->end()
156+
157+
->end()
158+
;
140159
}
141160
}

src/Surfnet/StepupSelfService/SelfServiceBundle/DependencyInjection/SurfnetStepupSelfServiceSelfServiceExtension.php

+4-2
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,9 @@ private function parseActivationFlowPreferenceConfiguration(
109109
ContainerBuilder $container
110110
): void {
111111
$container->getDefinition(ActivationFlowService::class)
112-
->replaceArgument(2, $preferenceConfig['query_string_field_name'])
113-
->replaceArgument(3, $preferenceConfig['options']);
112+
->replaceArgument(3, $preferenceConfig['query_string_field_name'])
113+
->replaceArgument(4, $preferenceConfig['options'])
114+
->replaceArgument(5, $preferenceConfig['saml_attribute_field_name'])
115+
->replaceArgument(6, $preferenceConfig['saml_attributes']);
114116
}
115117
}

src/Surfnet/StepupSelfService/SelfServiceBundle/EventListener/AuthenticatedUserListener.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public function updateLastInteractionMoment(RequestEvent $event): void
5656
}
5757
$this->logger->notice('Logged in user with a session within time limits detected, updating session state');
5858

59-
// see ExplicitSessionTimeoutHandler for the rationale
59+
// see ExplicitSessionTimeoutListener for the rationale
6060
if ($event->getRequest()->getMethod() === 'GET') {
6161
$this->sessionStateHandler->setCurrentRequestUri($event->getRequest()->getRequestUri());
6262
}

src/Surfnet/StepupSelfService/SelfServiceBundle/Resources/config/services.yaml

+4-1
Original file line numberDiff line numberDiff line change
@@ -192,10 +192,13 @@ services:
192192

193193
Surfnet\StepupSelfService\SelfServiceBundle\Service\ActivationFlowService:
194194
arguments:
195-
- '@request_stack'
195+
- '@Surfnet\StepupSelfService\SelfServiceBundle\Security\Authentication\AuthenticatedSessionStateHandler'
196+
- "@security.token_storage"
196197
- "@logger"
197198
- '' # See extension
198199
- [] # See extension
200+
- '' # See extension
201+
- [] # See extension
199202

200203

201204
Surfnet\StepupSelfService\SelfServiceBundle\Service\IdentityService:

src/Surfnet/StepupSelfService/SelfServiceBundle/Security/Authentication/AuthenticatedSessionStateHandler.php

+5
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
namespace Surfnet\StepupSelfService\SelfServiceBundle\Security\Authentication;
2222

2323
use Surfnet\StepupSelfService\SelfServiceBundle\Exception\LogicException;
24+
use Surfnet\StepupSelfService\SelfServiceBundle\Value\ActivationFlowPreferenceInterface;
2425
use Surfnet\StepupSelfService\SelfServiceBundle\Value\DateTime;
2526

2627
interface AuthenticatedSessionStateHandler
@@ -57,6 +58,10 @@ public function setCurrentRequestUri(string $uri): void;
5758

5859
public function getCurrentRequestUri(): string;
5960

61+
public function setRequestedActivationFlowPreference(ActivationFlowPreferenceInterface $preference): void;
62+
63+
public function getRequestedActivationFlowPreference(): ActivationFlowPreferenceInterface;
64+
6065
/**
6166
* Migrates the current session to a new session id while maintaining all
6267
* session attributes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
/**
6+
* Copyright 2024 SURFnet bv
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
21+
namespace Surfnet\StepupSelfService\SelfServiceBundle\Security\Authentication;
22+
23+
use Surfnet\SamlBundle\Security\Authentication\SamlAuthenticator as StepupSamlAuthenticator;
24+
use Surfnet\StepupSelfService\SelfServiceBundle\Service\ActivationFlowService;
25+
use Symfony\Component\HttpFoundation\Request;
26+
use Symfony\Component\HttpFoundation\Response;
27+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
28+
use Symfony\Component\Security\Core\Exception\AuthenticationException;
29+
use Symfony\Component\Security\Http\Authenticator\InteractiveAuthenticatorInterface;
30+
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
31+
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
32+
33+
class SamlAuthenticator implements InteractiveAuthenticatorInterface, AuthenticationEntryPointInterface
34+
{
35+
public function __construct(
36+
readonly private StepupSamlAuthenticator $authenticator,
37+
private readonly ActivationFlowService $activationFlowService,
38+
) {
39+
}
40+
41+
public function start(Request $request, ?AuthenticationException $authException = null): Response
42+
{
43+
// Check if we need to do a registration flow nudge
44+
// This is used for when we are not logged in yet because the authentication is done in the Stepup-SAML-bundle
45+
$this->activationFlowService->processPreferenceFromUri($request->getUri());
46+
return $this->authenticator->start($request, $authException);
47+
}
48+
49+
public function supports(Request $request): ?bool
50+
{
51+
return $this->authenticator->supports($request);
52+
}
53+
54+
public function authenticate(Request $request): Passport
55+
{
56+
return $this->authenticator->authenticate($request);
57+
}
58+
59+
public function createToken(Passport $passport, string $firewallName): TokenInterface
60+
{
61+
return $this->authenticator->createToken($passport, $firewallName);
62+
}
63+
64+
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
65+
{
66+
return $this->authenticator->onAuthenticationSuccess($request, $token, $firewallName);
67+
}
68+
69+
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
70+
{
71+
return $this->authenticator->onAuthenticationFailure($request, $exception);
72+
}
73+
74+
public function isInteractive(): bool
75+
{
76+
return $this->authenticator->isInteractive();
77+
}
78+
}

src/Surfnet/StepupSelfService/SelfServiceBundle/Security/Authentication/SamlInteractionProvider.php

-95
This file was deleted.

src/Surfnet/StepupSelfService/SelfServiceBundle/Security/Authentication/Session/SessionStorage.php

+18
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
use Surfnet\StepupSelfService\SelfServiceBundle\Exception\LogicException;
2424
use Surfnet\StepupSelfService\SelfServiceBundle\Security\Authentication\SamlAuthenticationStateHandler;
2525
use Surfnet\StepupSelfService\SelfServiceBundle\Security\Authentication\AuthenticatedSessionStateHandler;
26+
use Surfnet\StepupSelfService\SelfServiceBundle\Value\ActivationFlowPreference;
27+
use Surfnet\StepupSelfService\SelfServiceBundle\Value\ActivationFlowPreferenceInterface;
28+
use Surfnet\StepupSelfService\SelfServiceBundle\Value\ActivationFlowPreferenceNotExpressed;
2629
use Surfnet\StepupSelfService\SelfServiceBundle\Value\DateTime;
2730
use Symfony\Component\HttpFoundation\RequestStack;
2831

@@ -116,6 +119,21 @@ public function clearRequestId(): void
116119
$this->requestStack->getSession()->remove(self::SAML_SESSION_KEY . 'request_id');
117120
}
118121

122+
public function setRequestedActivationFlowPreference(ActivationFlowPreferenceInterface $preference): void
123+
{
124+
$this->requestStack->getSession()->set(self::SAML_SESSION_KEY . 'activation_preference', $preference->__toString());
125+
}
126+
127+
public function getRequestedActivationFlowPreference(): ActivationFlowPreferenceInterface
128+
{
129+
if ($this->requestStack->getSession()->has(self::SAML_SESSION_KEY . 'activation_preference')) {
130+
/** @var string $preference */
131+
$preference = $this->requestStack->getSession()->get(self::SAML_SESSION_KEY . 'activation_preference');
132+
return ActivationFlowPreference::fromString($preference);
133+
}
134+
return new ActivationFlowPreferenceNotExpressed();
135+
}
136+
119137
public function invalidate(): void
120138
{
121139
$this->requestStack->getSession()->invalidate();

0 commit comments

Comments
 (0)