44
55namespace SimpleSAML \Module \oidc \Controllers \Admin ;
66
7+ use Nette \Forms \Form ;
78use SimpleSAML \Locale \Translate ;
89use SimpleSAML \Module \oidc \Admin \Authorization ;
910use SimpleSAML \Module \oidc \Bridges \SspBridge ;
1011use SimpleSAML \Module \oidc \Codebooks \ParametersEnum ;
12+ use SimpleSAML \Module \oidc \Codebooks \RegistrationTypeEnum ;
1113use SimpleSAML \Module \oidc \Codebooks \RoutesEnum ;
14+ use SimpleSAML \Module \oidc \Entities \ClientEntity ;
1215use SimpleSAML \Module \oidc \Entities \Interfaces \ClientEntityInterface ;
1316use SimpleSAML \Module \oidc \Exceptions \OidcException ;
17+ use SimpleSAML \Module \oidc \Factories \Entities \ClientEntityFactory ;
1418use SimpleSAML \Module \oidc \Factories \FormFactory ;
1519use SimpleSAML \Module \oidc \Factories \TemplateFactory ;
1620use SimpleSAML \Module \oidc \Forms \ClientForm ;
21+ use SimpleSAML \Module \oidc \Helpers ;
1722use SimpleSAML \Module \oidc \Repositories \AllowedOriginRepository ;
1823use SimpleSAML \Module \oidc \Repositories \ClientRepository ;
1924use SimpleSAML \Module \oidc \Services \AuthContextService ;
@@ -29,11 +34,13 @@ public function __construct(
2934 protected readonly TemplateFactory $ templateFactory ,
3035 protected readonly Authorization $ authorization ,
3136 protected readonly ClientRepository $ clientRepository ,
37+ protected readonly ClientEntityFactory $ clientEntityFactory ,
3238 protected readonly AllowedOriginRepository $ allowedOriginRepository ,
3339 protected readonly FormFactory $ formFactory ,
3440 protected readonly SspBridge $ sspBridge ,
3541 protected readonly SessionMessagesService $ sessionMessagesService ,
3642 protected readonly Routes $ routes ,
43+ protected readonly Helpers $ helpers ,
3744 protected readonly LoggerService $ logger ,
3845 ) {
3946 $ this ->authorization ->requireAdminOrUserWithPermission (AuthContextService::PERM_CLIENT );
@@ -146,15 +153,53 @@ public function delete(Request $request): Response
146153 );
147154 }
148155
156+ /**
157+ * @throws \SimpleSAML\Error\ConfigurationError
158+ * @throws \SimpleSAML\Error\Exception
159+ * @throws \SimpleSAML\Module\oidc\Exceptions\OidcException
160+ */
149161 public function add (): Response
150162 {
151163 $ form = $ this ->formFactory ->build (ClientForm::class);
152- $ form ->setAction ($ this ->routes ->urlAdminClientsAddPersist ());
164+
165+ if ($ form ->isSuccess ()) {
166+ $ createdAt = $ this ->helpers ->dateTime ()->getUtc ();
167+ $ updatedAt = $ createdAt ;
168+
169+ $ owner = $ this ->authorization ->isAdmin () ? null : $ this ->authorization ->getUserId ();
170+
171+ $ client = $ this ->buildClientFromFormData (
172+ $ form ,
173+ $ this ->sspBridge ->utils ()->random ()->generateID (),
174+ $ this ->sspBridge ->utils ()->random ()->generateID (),
175+ RegistrationTypeEnum::Manual,
176+ $ updatedAt ,
177+ $ createdAt ,
178+ null ,
179+ $ owner ,
180+ );
181+
182+ $ this ->clientRepository ->add ($ client );
183+
184+ // Also persist allowed origins for this client.
185+ is_array ($ allowedOrigins = $ form ->getValues ('array ' )['allowed_origin ' ] ?? []) ||
186+ throw new OidcException ('Unexpected value for allowed origins. ' );
187+ /** @var string[] $allowedOrigins */
188+ $ this ->allowedOriginRepository ->set ($ client ->getIdentifier (), $ allowedOrigins );
189+
190+ $ this ->sessionMessagesService ->addMessage (Translate::noop ('Client has been added. ' ));
191+
192+ return $ this ->routes ->getRedirectResponseToModuleUrl (
193+ RoutesEnum::AdminClientsShow->value ,
194+ [ParametersEnum::ClientId->value => $ client ->getIdentifier ()],
195+ );
196+ }
153197
154198 return $ this ->templateFactory ->build (
155199 'oidc:clients/new.twig ' ,
156200 [
157201 'form ' => $ form ,
202+ 'actionRoute ' => $ this ->routes ->urlAdminClientsAdd (),
158203 'regexUri ' => ClientForm::REGEX_URI ,
159204 'regexAllowedOriginUrl ' => ClientForm::REGEX_ALLOWED_ORIGIN_URL ,
160205 'regexHttpUri ' => ClientForm::REGEX_HTTP_URI ,
@@ -163,4 +208,77 @@ public function add(): Response
163208 RoutesEnum::AdminClients->value ,
164209 );
165210 }
211+
212+ /**
213+ * TODO mivanci Move to ClientEntityFactory::fromRegistrationData on dynamic client registration implementation.
214+ * @throws \SimpleSAML\Module\oidc\Exceptions\OidcException
215+ */
216+ protected function buildClientFromFormData (
217+ Form $ form ,
218+ string $ identifier ,
219+ string $ secret ,
220+ RegistrationTypeEnum $ registrationType ,
221+ \DateTimeImmutable $ updatedAt ,
222+ ?\DateTimeImmutable $ createdAt = null ,
223+ ?\DateTimeImmutable $ expiresAt = null ,
224+ ?string $ owner = null ,
225+ ): ClientEntityInterface {
226+ /** @var array $data */
227+ $ data = $ form ->getValues ('array ' );
228+
229+ if (
230+ !is_string ($ data [ClientEntity::KEY_NAME ]) ||
231+ !is_string ($ data [ClientEntity::KEY_DESCRIPTION ]) ||
232+ !is_array ($ data [ClientEntity::KEY_REDIRECT_URI ]) ||
233+ !is_array ($ data [ClientEntity::KEY_SCOPES ]) ||
234+ !is_array ($ data [ClientEntity::KEY_POST_LOGOUT_REDIRECT_URI ])
235+ ) {
236+ throw new OidcException ('Invalid Client Entity data ' );
237+ }
238+
239+ /** @var string[] $redirectUris */
240+ $ redirectUris = $ data [ClientEntity::KEY_REDIRECT_URI ];
241+ /** @var string[] $scopes */
242+ $ scopes = $ data [ClientEntity::KEY_SCOPES ];
243+ /** @var string[] $postLogoutRedirectUris */
244+ $ postLogoutRedirectUris = $ data [ClientEntity::KEY_POST_LOGOUT_REDIRECT_URI ];
245+ /** @var ?string[] $clientRegistrationTypes */
246+ $ clientRegistrationTypes = is_array ($ data [ClientEntity::KEY_CLIENT_REGISTRATION_TYPES ]) ?
247+ $ data [ClientEntity::KEY_CLIENT_REGISTRATION_TYPES ] : null ;
248+ /** @var ?array[] $federationJwks */
249+ $ federationJwks = is_array ($ data [ClientEntity::KEY_FEDERATION_JWKS ]) ?
250+ $ data [ClientEntity::KEY_FEDERATION_JWKS ] : null ;
251+ /** @var ?array[] $jwks */
252+ $ jwks = is_array ($ data [ClientEntity::KEY_JWKS ]) ? $ data [ClientEntity::KEY_JWKS ] : null ;
253+ $ jwksUri = empty ($ data [ClientEntity::KEY_JWKS_URI ]) ? null : (string )$ data [ClientEntity::KEY_JWKS_URI ];
254+ $ signedJwksUri = empty ($ data [ClientEntity::KEY_SIGNED_JWKS_URI ]) ?
255+ null : (string )$ data [ClientEntity::KEY_SIGNED_JWKS_URI ];
256+ $ isFederated = (bool )$ data [ClientEntity::KEY_IS_FEDERATED ];
257+
258+ return $ this ->clientEntityFactory ->fromData (
259+ $ identifier ,
260+ $ secret ,
261+ $ data ['name ' ],
262+ $ data ['description ' ],
263+ $ redirectUris ,
264+ $ scopes ,
265+ (bool ) $ data ['is_enabled ' ],
266+ (bool ) $ data ['is_confidential ' ],
267+ empty ($ data ['auth_source ' ]) ? null : (string )$ data ['auth_source ' ],
268+ $ owner ,
269+ $ postLogoutRedirectUris ,
270+ empty ($ data ['backchannel_logout_uri ' ]) ? null : (string )$ data ['backchannel_logout_uri ' ],
271+ empty ($ data ['entity_identifier ' ]) ? null : (string )$ data ['entity_identifier ' ],
272+ $ clientRegistrationTypes ,
273+ $ federationJwks ,
274+ $ jwks ,
275+ $ jwksUri ,
276+ $ signedJwksUri ,
277+ $ registrationType ,
278+ $ updatedAt ,
279+ $ createdAt ,
280+ $ expiresAt ,
281+ $ isFederated ,
282+ );
283+ }
166284}
0 commit comments