Skip to content

Commit 670d80a

Browse files
committed
Add configurable sso registration bypass
#539
1 parent 68a97be commit 670d80a

24 files changed

Lines changed: 533 additions & 8 deletions

ci/qa/phpstan-baseline.neon

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,16 @@ parameters:
315315
count: 1
316316
path: ../../src/Surfnet/Stepup/Configuration/Event/SsoOn2faOptionChangedEvent.php
317317

318+
-
319+
message: "#^Method Surfnet\\\\Stepup\\\\Configuration\\\\Event\\\\SsoRegistrationBypassOptionChangedEvent\\:\\:deserialize\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#"
320+
count: 1
321+
path: ../../src/Surfnet/Stepup/Configuration/Event/SsoRegistrationBypassOptionChangedEvent.php
322+
323+
-
324+
message: "#^Method Surfnet\\\\Stepup\\\\Configuration\\\\Event\\\\SsoRegistrationBypassOptionChangedEvent\\:\\:serialize\\(\\) return type has no value type specified in iterable type array\\.$#"
325+
count: 1
326+
path: ../../src/Surfnet/Stepup/Configuration/Event/SsoRegistrationBypassOptionChangedEvent.php
327+
318328
-
319329
message: "#^Method Surfnet\\\\Stepup\\\\Configuration\\\\Event\\\\UseRaLocationsOptionChangedEvent\\:\\:deserialize\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#"
320330
count: 1
@@ -1930,6 +1940,11 @@ parameters:
19301940
count: 1
19311941
path: ../../src/Surfnet/StepupMiddleware/ApiBundle/Configuration/Projector/InstitutionConfigurationOptionsProjector.php
19321942

1943+
-
1944+
message: "#^Cannot access property \\$ssoRegistrationBypassOption on Surfnet\\\\StepupMiddleware\\\\ApiBundle\\\\Configuration\\\\Entity\\\\InstitutionConfigurationOptions\\|null\\.$#"
1945+
count: 1
1946+
path: ../../src/Surfnet/StepupMiddleware/ApiBundle/Configuration/Projector/InstitutionConfigurationOptionsProjector.php
1947+
19331948
-
19341949
message: "#^Cannot access property \\$useRaLocationsOption on Surfnet\\\\StepupMiddleware\\\\ApiBundle\\\\Configuration\\\\Entity\\\\InstitutionConfigurationOptions\\|null\\.$#"
19351950
count: 1
@@ -1942,7 +1957,7 @@ parameters:
19421957

19431958
-
19441959
message: "#^Parameter \\#1 \\$institutionConfigurationOptions of method Surfnet\\\\StepupMiddleware\\\\ApiBundle\\\\Configuration\\\\Repository\\\\InstitutionConfigurationOptionsRepository\\:\\:save\\(\\) expects Surfnet\\\\StepupMiddleware\\\\ApiBundle\\\\Configuration\\\\Entity\\\\InstitutionConfigurationOptions, Surfnet\\\\StepupMiddleware\\\\ApiBundle\\\\Configuration\\\\Entity\\\\InstitutionConfigurationOptions\\|null given\\.$#"
1945-
count: 7
1960+
count: 8
19461961
path: ../../src/Surfnet/StepupMiddleware/ApiBundle/Configuration/Projector/InstitutionConfigurationOptionsProjector.php
19471962

19481963
-
@@ -3545,6 +3560,11 @@ parameters:
35453560
count: 1
35463561
path: ../../src/Surfnet/StepupMiddleware/ManagementBundle/Controller/InstitutionConfigurationController.php
35473562

3563+
-
3564+
message: "#^Cannot access offset 'sso_registration…' on mixed\\.$#"
3565+
count: 1
3566+
path: ../../src/Surfnet/StepupMiddleware/ManagementBundle/Controller/InstitutionConfigurationController.php
3567+
35483568
-
35493569
message: "#^Cannot access offset 'use_ra' on mixed\\.$#"
35503570
count: 1
@@ -3605,6 +3625,11 @@ parameters:
36053625
count: 1
36063626
path: ../../src/Surfnet/StepupMiddleware/ManagementBundle/Controller/InstitutionConfigurationController.php
36073627

3628+
-
3629+
message: "#^Property Surfnet\\\\StepupMiddleware\\\\CommandHandlingBundle\\\\Configuration\\\\Command\\\\ReconfigureInstitutionConfigurationOptionsCommand\\:\\:\\$ssoRegistrationBypassOption \\(bool\\|null\\) does not accept mixed\\.$#"
3630+
count: 1
3631+
path: ../../src/Surfnet/StepupMiddleware/ManagementBundle/Controller/InstitutionConfigurationController.php
3632+
36083633
-
36093634
message: "#^Property Surfnet\\\\StepupMiddleware\\\\CommandHandlingBundle\\\\Configuration\\\\Command\\\\ReconfigureInstitutionConfigurationOptionsCommand\\:\\:\\$useRaLocationsOption \\(bool\\) does not accept mixed\\.$#"
36103635
count: 1

config/packages/doctrine.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ doctrine:
103103
stepup_sso_on_2fa_option:
104104
class: Surfnet\StepupMiddleware\ApiBundle\Doctrine\Type\SsoOn2faOptionType
105105
commented: false
106+
stepup_sso_registration_bypass_option:
107+
class: Surfnet\StepupMiddleware\ApiBundle\Doctrine\Type\SsoRegistrationBypassOptionType
108+
commented: false
106109
stepup_institution_role:
107110
class: Surfnet\StepupMiddleware\ApiBundle\Doctrine\Type\InstitutionRoleType
108111
commented: false

config/packages/monolog.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ when@dev: &override
2424
main_syslog:
2525
type: stream
2626
path: php://stderr
27-
level: error
27+
level: debug
2828
channels: ["!event", "!doctrine", "!deprecation", "!console"]
2929
console:
3030
type: console
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
/**
4+
* Copyright 2025 SURFnet bv
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
declare(strict_types=1);
20+
21+
namespace Surfnet\Migrations;
22+
23+
use Doctrine\DBAL\Schema\Schema;
24+
use Doctrine\Migrations\AbstractMigration;
25+
use Surfnet\Stepup\MigrationsFactory\ConfigurationAwareMigrationInterface;
26+
use Surfnet\Stepup\MigrationsFactory\ConfigurationAwareMigrationTrait;
27+
28+
final class Version20250501121457 extends AbstractMigration implements ConfigurationAwareMigrationInterface
29+
{
30+
use ConfigurationAwareMigrationTrait;
31+
32+
public function up(Schema $schema): void
33+
{
34+
$this->abortIf(
35+
$this->connection->getDatabasePlatform()->getName() !== 'mysql',
36+
'Migration can only be executed safely on \'mysql\'.',
37+
);
38+
// Create the new sso_on_2fa option, note the name conversion 'error' made by doctrine.
39+
$this->addSql('ALTER TABLE institution_configuration_options ADD sso_registration_bypass_option INT DEFAULT \'0\' NOT NULL');
40+
// Create the institution_configuration gateway schema
41+
$gatewaySchema = $this->getGatewaySchema();
42+
$this->addSql(
43+
sprintf(
44+
'ALTER TABLE %s.institution_configuration ADD sso_registration_bypass TINYINT(1) NOT NULL',
45+
$gatewaySchema,
46+
),
47+
);
48+
}
49+
50+
public function down(Schema $schema): void
51+
{
52+
$this->abortIf(
53+
$this->connection->getDatabasePlatform()->getName() !== 'mysql',
54+
'Migration can only be executed safely on \'mysql\'.',
55+
);
56+
// Down the Middleware schema change
57+
$this->addSql('ALTER TABLE institution_configuration_options DROP sso_registration_bypass_option');
58+
// Gateway schema change (remove the institution_configuration)
59+
$gatewaySchema = $this->getGatewaySchema();
60+
$this->addSql(sprintf('ALTER TABLE %s.institution_configuration DROP sso_registration_bypass', $gatewaySchema));
61+
}
62+
}

src/Surfnet/Stepup/Configuration/Event/NewInstitutionConfigurationCreatedEvent.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
use Surfnet\Stepup\Configuration\Value\SelfVetOption;
2727
use Surfnet\Stepup\Configuration\Value\ShowRaaContactInformationOption;
2828
use Surfnet\Stepup\Configuration\Value\SsoOn2faOption;
29+
use Surfnet\Stepup\Configuration\Value\SsoRegistrationBypassOption;
2930
use Surfnet\Stepup\Configuration\Value\UseRaLocationsOption;
3031
use Surfnet\Stepup\Configuration\Value\VerifyEmailOption;
3132

@@ -35,6 +36,10 @@
3536
*/
3637
class NewInstitutionConfigurationCreatedEvent implements SerializableInterface
3738
{
39+
40+
/**
41+
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
42+
*/
3843
public function __construct(
3944
public InstitutionConfigurationId $institutionConfigurationId,
4045
public Institution $institution,
@@ -43,6 +48,7 @@ public function __construct(
4348
public VerifyEmailOption $verifyEmailOption,
4449
public NumberOfTokensPerIdentityOption $numberOfTokensPerIdentityOption,
4550
public SsoOn2faOption $ssoOn2faOption,
51+
public SsoRegistrationBypassOption $ssoRegistrationBypassOption,
4652
public SelfVetOption $selfVetOption,
4753
public SelfAssertedTokensOption $selfAssertedTokensOption
4854
) {
@@ -60,6 +66,10 @@ public static function deserialize(array $data): self
6066
if (!isset($data['sso_on_2fa_option'])) {
6167
$data['sso_on_2fa_option'] = false;
6268
}
69+
// If sso registration bypass option is not yet present, default to false
70+
if (!isset($data['sso_registration_bypass_option'])) {
71+
$data['sso_registration_bypass_option'] = false;
72+
}
6373
// If self vet option is not yet present, default to false
6474
if (!isset($data['self_vet_option'])) {
6575
$data['self_vet_option'] = false;
@@ -76,6 +86,7 @@ public static function deserialize(array $data): self
7686
new VerifyEmailOption($data['verify_email_option']),
7787
new NumberOfTokensPerIdentityOption($data['number_of_tokens_per_identity_option']),
7888
new SsoOn2faOption($data['sso_on_2fa_option']),
89+
new SsoRegistrationBypassOption($data['sso_registration_bypass_option']),
7990
new SelfVetOption($data['self_vet_option']),
8091
new SelfAssertedTokensOption($data['self_asserted_tokens_option']),
8192
);
@@ -91,6 +102,7 @@ public function serialize(): array
91102
'verify_email_option' => $this->verifyEmailOption->isEnabled(),
92103
'number_of_tokens_per_identity_option' => $this->numberOfTokensPerIdentityOption->getNumberOfTokensPerIdentity(),
93104
'sso_on_2fa_option' => $this->ssoOn2faOption->isEnabled(),
105+
'sso_registration_bypass_option' => $this->ssoRegistrationBypassOption->isEnabled(),
94106
'self_vet_option' => $this->selfVetOption->isEnabled(),
95107
'self_asserted_tokens_option' => $this->selfAssertedTokensOption->isEnabled(),
96108
];
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* Copyright 2025 SURFnet B.V.
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\Stepup\Configuration\Event;
22+
23+
use Broadway\Serializer\Serializable as SerializableInterface;
24+
use Surfnet\Stepup\Configuration\Value\Institution;
25+
use Surfnet\Stepup\Configuration\Value\InstitutionConfigurationId;
26+
use Surfnet\Stepup\Configuration\Value\SsoRegistrationBypassOption;
27+
28+
final class SsoRegistrationBypassOptionChangedEvent implements SerializableInterface
29+
{
30+
public function __construct(
31+
public InstitutionConfigurationId $institutionConfigurationId,
32+
public Institution $institution,
33+
public SsoRegistrationBypassOption $ssoRegistrationBypassOption,
34+
) {
35+
}
36+
37+
public static function deserialize(array $data): self
38+
{
39+
return new self(
40+
new InstitutionConfigurationId($data['institution_configuration_id']),
41+
new Institution($data['institution']),
42+
new SsoRegistrationBypassOption($data['sso_registration_bypass_option']),
43+
);
44+
}
45+
46+
public function serialize(): array
47+
{
48+
return [
49+
'institution_configuration_id' => $this->institutionConfigurationId->getInstitutionConfigurationId(),
50+
'institution' => $this->institution->getInstitution(),
51+
'sso_registration_bypass_option' => $this->ssoRegistrationBypassOption->isEnabled(),
52+
];
53+
}
54+
}

src/Surfnet/Stepup/Configuration/InstitutionConfiguration.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
use Surfnet\Stepup\Configuration\Event\SelfVetOptionChangedEvent;
3636
use Surfnet\Stepup\Configuration\Event\ShowRaaContactInformationOptionChangedEvent;
3737
use Surfnet\Stepup\Configuration\Event\SsoOn2faOptionChangedEvent;
38+
use Surfnet\Stepup\Configuration\Event\SsoRegistrationBypassOptionChangedEvent;
3839
use Surfnet\Stepup\Configuration\Event\UseRaaOptionChangedEvent;
3940
use Surfnet\Stepup\Configuration\Event\UseRaLocationsOptionChangedEvent;
4041
use Surfnet\Stepup\Configuration\Event\UseRaOptionChangedEvent;
@@ -54,6 +55,7 @@
5455
use Surfnet\Stepup\Configuration\Value\SelfVetOption;
5556
use Surfnet\Stepup\Configuration\Value\ShowRaaContactInformationOption;
5657
use Surfnet\Stepup\Configuration\Value\SsoOn2faOption;
58+
use Surfnet\Stepup\Configuration\Value\SsoRegistrationBypassOption;
5759
use Surfnet\Stepup\Configuration\Value\UseRaLocationsOption;
5860
use Surfnet\Stepup\Configuration\Value\VerifyEmailOption;
5961
use Surfnet\Stepup\Exception\DomainException;
@@ -72,6 +74,7 @@
7274
* @SuppressWarnings(PHPMD.TooManyMethods) AggregateRoot
7375
* @SuppressWarnings(PHPMD.TooManyPublicMethods) AggregateRoot
7476
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity) AggregateRoot
77+
* @SuppressWarnings(PHPMD.TooManyFields) AggregateRoot
7578
*/
7679
class InstitutionConfiguration extends EventSourcedAggregateRoot implements InstitutionConfigurationInterface
7780
{
@@ -93,6 +96,8 @@ class InstitutionConfiguration extends EventSourcedAggregateRoot implements Inst
9396

9497
private ?SsoOn2faOption $ssoOn2faOption = null;
9598

99+
private ?SsoRegistrationBypassOption $ssoRegistrationBypassOption = null;
100+
96101
private ?SelfAssertedTokensOption $selfAssertedTokensOption = null;
97102

98103
private ?InstitutionAuthorizationOption $useRaOption = null;
@@ -119,6 +124,7 @@ public static function create(
119124
VerifyEmailOption::getDefault(),
120125
NumberOfTokensPerIdentityOption::getDefault(),
121126
SsoOn2faOption::getDefault(),
127+
SsoRegistrationBypassOption::getDefault(),
122128
SelfVetOption::getDefault(),
123129
SelfAssertedTokensOption::getDefault(),
124130
),
@@ -171,6 +177,7 @@ public function rebuild(): self
171177
VerifyEmailOption::getDefault(),
172178
NumberOfTokensPerIdentityOption::getDefault(),
173179
SsoOn2faOption::getDefault(),
180+
SsoRegistrationBypassOption::getDefault(),
174181
SelfVetOption::getDefault(),
175182
SelfAssertedTokensOption::getDefault(),
176183
),
@@ -320,6 +327,22 @@ public function configureSsoOn2faOption(SsoOn2faOption $ssoOn2faOption): void
320327
);
321328
}
322329

330+
public function configureSsoRegistrationBypassOption(SsoRegistrationBypassOption $ssoRegistrationBypassOption): void
331+
{
332+
if ($this->ssoRegistrationBypassOption instanceof SsoRegistrationBypassOption
333+
&& $this->ssoRegistrationBypassOption->equals($ssoRegistrationBypassOption)) {
334+
return;
335+
}
336+
337+
$this->apply(
338+
new SsoRegistrationBypassOptionChangedEvent(
339+
$this->institutionConfigurationId,
340+
$this->institution,
341+
$ssoRegistrationBypassOption
342+
)
343+
);
344+
}
345+
323346
public function updateUseRaOption(InstitutionAuthorizationOption $useRaOption): void
324347
{
325348
if ($this->useRaOption instanceof \Surfnet\Stepup\Configuration\Value\InstitutionAuthorizationOption
@@ -526,6 +549,7 @@ protected function applyNewInstitutionConfigurationCreatedEvent(NewInstitutionCo
526549
$this->verifyEmailOption = $event->verifyEmailOption;
527550
$this->selfVetOption = $event->selfVetOption;
528551
$this->ssoOn2faOption = $event->ssoOn2faOption;
552+
$this->ssoRegistrationBypassOption = $event->ssoRegistrationBypassOption;
529553
$this->selfAssertedTokensOption = $event->selfAssertedTokensOption;
530554
$this->numberOfTokensPerIdentityOption = $event->numberOfTokensPerIdentityOption;
531555
$this->raLocations = new RaLocationList([]);
@@ -592,6 +616,12 @@ protected function applySsoOn2faOptionChangedEvent(
592616
$this->ssoOn2faOption = $event->ssoOn2faOption;
593617
}
594618

619+
protected function applySsoRegistrationBypassOptionChangedEvent(
620+
SsoRegistrationBypassOptionChangedEvent $event,
621+
): void {
622+
$this->ssoRegistrationBypassOption = $event->ssoRegistrationBypassOption;
623+
}
624+
595625
protected function applyNumberOfTokensPerIdentityOptionChangedEvent(
596626
NumberOfTokensPerIdentityOptionChangedEvent $event,
597627
): void {
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* Copyright 2025 SURFnet B.V.
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\Stepup\Configuration\Value;
22+
23+
use JsonSerializable;
24+
25+
final readonly class SsoRegistrationBypassOption implements JsonSerializable
26+
{
27+
public static function getDefault(): self
28+
{
29+
return new self(false);
30+
}
31+
32+
public function __construct(
33+
private bool $ssoRegistrationBypass
34+
) {
35+
}
36+
37+
public function equals(SsoRegistrationBypassOption $other): bool
38+
{
39+
return $this->ssoRegistrationBypass === $other->isEnabled();
40+
}
41+
42+
public function isEnabled(): bool
43+
{
44+
return $this->ssoRegistrationBypass;
45+
}
46+
47+
public function jsonSerialize(): bool
48+
{
49+
return $this->ssoRegistrationBypass;
50+
}
51+
}

0 commit comments

Comments
 (0)