44
55namespace Rezzza \SecurityBundle \DependencyInjection \Security \Factory ;
66
7+ use Rezzza \SecurityBundle \Security \Firewall \AccountingFirmIdBadgeConfig ;
78use Symfony \Bundle \SecurityBundle \DependencyInjection \Security \Factory \AuthenticatorFactoryInterface ;
89use Symfony \Component \Config \Definition \Builder \NodeDefinition ;
10+ use Symfony \Component \Config \Definition \Exception \InvalidConfigurationException ;
911use Symfony \Component \DependencyInjection \ChildDefinition ;
1012use Symfony \Component \DependencyInjection \ContainerBuilder ;
13+ use Symfony \Component \DependencyInjection \Definition ;
1114use Symfony \Component \DependencyInjection \Reference ;
1215
16+ /**
17+ * @phpstan-type RequestSignatureConfig array{
18+ * algorithm: string,
19+ * secret: string,
20+ * ignore: bool,
21+ * parameter: string,
22+ * replay_protection: array{
23+ * enabled: bool,
24+ * lifetime: int|string,
25+ * parameter: string,
26+ * },
27+ * badges?: array{
28+ * accounting_firm_id?: array{
29+ * source: 'header'|'query'|'request'|'attribute',
30+ * name: string,
31+ * },
32+ * },
33+ * }
34+ */
1335class RequestSignatureFactory implements AuthenticatorFactoryInterface
1436{
37+ /**
38+ * @param RequestSignatureConfig $config
39+ */
1540 public function createAuthenticator (ContainerBuilder $ container , string $ firewallName , array $ config , string $ userProviderId ): array |string
1641 {
1742 $ signatureQueryParametersId = $ this ->createSignatureQueryParameters ($ container , $ firewallName , $ config );
1843 $ signatureConfigId = $ this ->createSignatureConfig ($ container , $ firewallName , $ config );
1944 $ replayProtectionId = $ this ->createReplayProtection ($ container , $ firewallName , $ config );
45+ $ accountingFirmIdBadgeConfigId = $ this ->createAccountingFirmIdBadgeConfig ($ container , $ firewallName , $ config );
2046
2147 $ listenerId = 'security.authentication.listener.request_signature. ' .$ firewallName ;
22- $ listener = $ container
48+ $ container
2349 ->setDefinition ($ listenerId , $ this ->createDefinition ('rezzza.security.request_signature.listener ' ))
2450 ->replaceArgument (1 , new Reference ($ signatureQueryParametersId ))
2551 ->replaceArgument (2 , $ config ['ignore ' ])
2652 ->replaceArgument (3 , new Reference ($ signatureConfigId ))
2753 ->replaceArgument (4 , new Reference ($ replayProtectionId ))
54+ ->replaceArgument (5 , null !== $ accountingFirmIdBadgeConfigId ? new Reference ($ accountingFirmIdBadgeConfigId ) : null )
2855 ;
2956
3057 return $ listenerId ;
@@ -45,6 +72,9 @@ public function getPriority(): int
4572 return 0 ;
4673 }
4774
75+ /**
76+ * @param RequestSignatureConfig $config
77+ */
4878 public function createSignatureConfig (ContainerBuilder $ container , string $ firewallName , array $ config ): string
4979 {
5080 $ signatureConfigId = 'rezzza.security.request_signature.signature_config. ' .$ firewallName ;
@@ -58,6 +88,9 @@ public function createSignatureConfig(ContainerBuilder $container, string $firew
5888 return $ signatureConfigId ;
5989 }
6090
91+ /**
92+ * @param RequestSignatureConfig $config
93+ */
6194 public function createSignatureQueryParameters (ContainerBuilder $ container , string $ firewallName , array $ config ): string
6295 {
6396 $ signatureQueryParametersId = 'rezzza.security.request_signature.signature_query_parameters. ' .$ firewallName ;
@@ -70,6 +103,9 @@ public function createSignatureQueryParameters(ContainerBuilder $container, stri
70103 return $ signatureQueryParametersId ;
71104 }
72105
106+ /**
107+ * @param RequestSignatureConfig $config
108+ */
73109 public function createReplayProtection (ContainerBuilder $ container , string $ firewallName , array $ config ): string
74110 {
75111 $ replayProtectionId = 'rezzza.security.request_signature.replay_protection. ' .$ firewallName ;
@@ -82,6 +118,25 @@ public function createReplayProtection(ContainerBuilder $container, string $fire
82118 return $ replayProtectionId ;
83119 }
84120
121+ /**
122+ * @param RequestSignatureConfig $config
123+ */
124+ public function createAccountingFirmIdBadgeConfig (ContainerBuilder $ container , string $ firewallName , array $ config ): ?string
125+ {
126+ if (!isset ($ config ['badges ' ]['accounting_firm_id ' ])) {
127+ return null ;
128+ }
129+
130+ $ accountingFirmIdBadgeConfigId = 'rezzza.security.request_signature.accounting_firm_id_badge_config. ' .$ firewallName ;
131+ $ container
132+ ->setDefinition ($ accountingFirmIdBadgeConfigId , new Definition (AccountingFirmIdBadgeConfig::class))
133+ ->addArgument ($ config ['badges ' ]['accounting_firm_id ' ]['source ' ])
134+ ->addArgument ($ config ['badges ' ]['accounting_firm_id ' ]['name ' ])
135+ ;
136+
137+ return $ accountingFirmIdBadgeConfigId ;
138+ }
139+
85140 public function addConfiguration (NodeDefinition $ node ): void
86141 {
87142 $ node ->children ()
@@ -97,6 +152,30 @@ public function addConfiguration(NodeDefinition $node): void
97152 ->scalarNode ('parameter ' )->defaultValue ('_signature_time ' )->cannotBeEmpty ()->end ()
98153 ->end ()
99154 ->end ()
155+ ->arrayNode ('badges ' )
156+ ->beforeNormalization ()
157+ ->always (static function (mixed $ v ): mixed {
158+ if (\is_array ($ v ) && [] === $ v ) {
159+ throw new InvalidConfigurationException ('At least one badge must be configured under "badges" when this section is defined. ' );
160+ }
161+
162+ return $ v ;
163+ })
164+ ->end ()
165+ ->children ()
166+ ->arrayNode ('accounting_firm_id ' )
167+ ->treatNullLike ([])
168+ ->addDefaultsIfNotSet ()
169+ ->children ()
170+ ->enumNode ('source ' )
171+ ->values (['header ' , 'query ' , 'request ' , 'attribute ' ])
172+ ->defaultValue ('header ' )
173+ ->end ()
174+ ->scalarNode ('name ' )->defaultValue ('X-Accounting-Firm-Id ' )->cannotBeEmpty ()->end ()
175+ ->end ()
176+ ->end ()
177+ ->end ()
178+ ->end ()
100179 ;
101180 }
102181
0 commit comments