Skip to content

Commit c7d4699

Browse files
committed
WIP Move to SSP UI
1 parent 5eb4f81 commit c7d4699

File tree

11 files changed

+180
-18
lines changed

11 files changed

+180
-18
lines changed

public/assets/css/src/default.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,3 +114,7 @@ table.client-table {
114114
}
115115

116116
.confirm-action {}
117+
118+
form.pure-form-stacked input {
119+
width: 100%;
120+
}

routing/routes/routes.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,7 @@
4343
->controller([ClientController::class, 'index']);
4444
$routes->add(RoutesEnum::AdminClientsAdd->name, RoutesEnum::AdminClientsAdd->value)
4545
->controller([ClientController::class, 'add'])
46-
->methods([HttpMethodsEnum::GET->value]);
47-
$routes->add(RoutesEnum::AdminClientsAddPersist->name, RoutesEnum::AdminClientsAddPersist->value)
48-
->controller([ClientController::class, 'addPersist'])
49-
->methods([HttpMethodsEnum::POST->value]);
46+
->methods([HttpMethodsEnum::GET->value, HttpMethodsEnum::POST->value]);
5047
$routes->add(RoutesEnum::AdminClientsShow->name, RoutesEnum::AdminClientsShow->value)
5148
->controller([ClientController::class, 'show'])
5249
->methods([HttpMethodsEnum::GET->value]);

src/Codebooks/RoutesEnum.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ enum RoutesEnum: string
2020
case AdminClients = 'admin/clients';
2121
case AdminClientsShow = 'admin/clients/show';
2222
case AdminClientsAdd = 'admin/clients/add';
23-
case AdminClientsAddPersist = 'admin/clients/add/persist';
2423
case AdminClientsResetSecret = 'admin/clients/reset-secret';
2524
case AdminClientsDelete = 'admin/clients/delete';
2625

src/Controllers/Admin/ClientController.php

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,21 @@
44

55
namespace SimpleSAML\Module\oidc\Controllers\Admin;
66

7+
use Nette\Forms\Form;
78
use SimpleSAML\Locale\Translate;
89
use SimpleSAML\Module\oidc\Admin\Authorization;
910
use SimpleSAML\Module\oidc\Bridges\SspBridge;
1011
use SimpleSAML\Module\oidc\Codebooks\ParametersEnum;
12+
use SimpleSAML\Module\oidc\Codebooks\RegistrationTypeEnum;
1113
use SimpleSAML\Module\oidc\Codebooks\RoutesEnum;
14+
use SimpleSAML\Module\oidc\Entities\ClientEntity;
1215
use SimpleSAML\Module\oidc\Entities\Interfaces\ClientEntityInterface;
1316
use SimpleSAML\Module\oidc\Exceptions\OidcException;
17+
use SimpleSAML\Module\oidc\Factories\Entities\ClientEntityFactory;
1418
use SimpleSAML\Module\oidc\Factories\FormFactory;
1519
use SimpleSAML\Module\oidc\Factories\TemplateFactory;
1620
use SimpleSAML\Module\oidc\Forms\ClientForm;
21+
use SimpleSAML\Module\oidc\Helpers;
1722
use SimpleSAML\Module\oidc\Repositories\AllowedOriginRepository;
1823
use SimpleSAML\Module\oidc\Repositories\ClientRepository;
1924
use 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
}

src/Controllers/Client/CreateController.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ public function __invoke(): Template|RedirectResponse
9595
$jwks = is_array($client['jwks']) ? $client['jwks'] : null;
9696
$jwksUri = empty($client['jwks_uri']) ? null : (string)$client['jwks_uri'];
9797
$signedJwksUri = empty($client['signed_jwks_uri']) ? null : (string)$client['signed_jwks_uri'];
98+
9899
$registrationType = RegistrationTypeEnum::Manual;
99100
$createdAt = $this->helpers->dateTime()->getUtc();
100101
$updatedAt = $createdAt;

src/Controllers/Client/EditController.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ public function __invoke(ServerRequest $request): Template|RedirectResponse
101101
$jwks = is_array($data['jwks']) ? $data['jwks'] : null;
102102
$jwksUri = empty($data['jwks_uri']) ? null : (string)$data['jwks_uri'];
103103
$signedJwksUri = empty($data['signed_jwks_uri']) ? null : (string)$data['signed_jwks_uri'];
104+
104105
$registrationType = $client->getRegistrationType();
105106
$updatedAt = $this->helpers->dateTime()->getUtc();
106107
$createdAt = $client->getCreatedAt();

src/Forms/ClientForm.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
use Nette\Forms\Form;
2020
use SimpleSAML\Auth\Source;
21+
use SimpleSAML\Locale\Translate;
2122
use SimpleSAML\Module\oidc\Forms\Controls\CsrfProtection;
2223
use SimpleSAML\Module\oidc\ModuleConfig;
2324
use SimpleSAML\OpenID\Codebooks\ClientRegistrationTypesEnum;
@@ -341,11 +342,11 @@ protected function buildForm(): void
341342

342343
$this->addText('name', '{oidc:client:name}')
343344
->setMaxLength(255)
344-
->setRequired('Set a name');
345+
->setRequired(Translate::noop('Name is required.'));
345346

346347
$this->addTextArea('description', '{oidc:client:description}', null, 5);
347348
$this->addTextArea('redirect_uri', '{oidc:client:redirect_uri}', null, 5)
348-
->setRequired('Write one redirect URI at least');
349+
->setRequired(Translate::noop('At least one redirect URI is required.'));
349350

350351
$this->addCheckbox('is_enabled', '{oidc:client:is_enabled}');
351352

@@ -355,14 +356,14 @@ protected function buildForm(): void
355356
$this->addSelect('auth_source', '{oidc:client:auth_source}:')
356357
->setHtmlAttribute('class', 'ui fluid dropdown clearable')
357358
->setItems(Source::getSources(), false)
358-
->setPrompt('Pick an AuthSource');
359+
->setPrompt(Translate::noop('Pick an AuthSource'));
359360

360361
$scopes = $this->getScopes();
361362

362363
$this->addMultiSelect('scopes', '{oidc:client:scopes}')
363364
->setHtmlAttribute('class', 'ui fluid dropdown')
364365
->setItems($scopes)
365-
->setRequired('Select one scope at least');
366+
->setRequired(Translate::noop('At least one scope is required.'));
366367

367368
$this->addText('owner', '{oidc:client:owner}')
368369
->setMaxLength(190);

src/Utils/Routes.php

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,6 @@ public function urlAdminClientsAdd(array $parameters = []): string
8080
return $this->getModuleUrl(RoutesEnum::AdminClientsAdd->value, $parameters);
8181
}
8282

83-
public function urlAdminClientsAddPersist(array $parameters = []): string
84-
{
85-
return $this->getModuleUrl(RoutesEnum::AdminClientsAddPersist->value, $parameters);
86-
}
87-
8883
public function urlAdminClientsResetSecret(string $clientId, array $parameters = []): string
8984
{
9085
$parameters[ParametersEnum::ClientId->value] = $clientId;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
2+
{% if form.hasErrors %}
3+
<div class="message-box warning">
4+
<ul>
5+
{% for error in form.getErrors %}
6+
<li>{{ error | trans }}</li>
7+
{% endfor %}
8+
</ul>
9+
</div>
10+
{% endif %}
11+
12+
<p>{{ form.hasErrors ? 'yes' : 'no' }}</p>
13+
14+
<form method="post"
15+
action="{{ actionRoute }}"
16+
class="pure-form pure-form-stacked">
17+
18+
{{ form['_token_'].control | raw }}
19+
20+
<fieldset>
21+
<label for="frm-name">{{ 'Name'|trans }}</label>
22+
{{ form.name.label |raw }}
23+
{{ form.name.control | raw }}
24+
{% if form.name.hasErrors %}
25+
<span class="pure-form-message red-text">{{ form.name.getError }}</span>
26+
{% endif %}
27+
28+
29+
<br>
30+
<button type="submit" class="pure-button ">{{ (actionText|default('Submit'))|trans }}</button>
31+
</fieldset>
32+
</form>

templates/clients/new-old.twig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{% extends "@oidc/clients/_form.twig" %}
1+
{% extends "@oidc/clients/_form-old.twig" %}
22

33
{% set pagetitle = 'Create new OpenID Connect Client' | trans %}
44

0 commit comments

Comments
 (0)