diff --git a/public/assets/css/src/default.css b/public/assets/css/src/default.css index 612e4f20..cc42a760 100644 --- a/public/assets/css/src/default.css +++ b/public/assets/css/src/default.css @@ -112,3 +112,5 @@ table.client-table { width: 25%; font-weight: bolder; } + +.confirm-action {} diff --git a/public/assets/js/src/default.js b/public/assets/js/src/default.js new file mode 100644 index 00000000..e6eb33fc --- /dev/null +++ b/public/assets/js/src/default.js @@ -0,0 +1,22 @@ + +(function() { + + // Attach `confirm-action` click event to all elements with the `confirm-action` class. + document.querySelectorAll('.confirm-action').forEach(button => { + button.addEventListener('click', function (event) { + // Get custom confirmation text + const confirmText = this.getAttribute('data-confirm-text') ?? 'Are you sure?'; + // Optional: Retrieve additional data + const itemId = this.getAttribute('data-confirm-id') ?? 'N/A'; + + if (!confirm(confirmText)) { + // Prevent the default action if the user cancels + event.preventDefault(); + } else { + // Optional: Handle confirmed action + console.log( + `Confirmed action "${confirmText}" for item with ID "${itemId}"`); + } + }); + }); +})(); diff --git a/routing/routes/routes.php b/routing/routes/routes.php index 3bf3469a..9c89c99c 100644 --- a/routing/routes/routes.php +++ b/routing/routes/routes.php @@ -43,6 +43,9 @@ ->controller([ClientController::class, 'index']); $routes->add(RoutesEnum::AdminClientsShow->name, RoutesEnum::AdminClientsShow->value) ->controller([ClientController::class, 'show']); + $routes->add(RoutesEnum::AdminClientsResetSecret->name, RoutesEnum::AdminClientsResetSecret->value) + ->controller([ClientController::class, 'resetSecret']) + ->methods([HttpMethodsEnum::POST->value]); /***************************************************************************************************************** * OpenID Connect diff --git a/src/Codebooks/ParametersEnum.php b/src/Codebooks/ParametersEnum.php new file mode 100644 index 00000000..bb8630ad --- /dev/null +++ b/src/Codebooks/ParametersEnum.php @@ -0,0 +1,10 @@ +authorization->requireAdminOrUserWithPermission(AuthContextService::PERM_CLIENT); } @@ -33,7 +41,7 @@ public function __construct( */ protected function getClientFromRequest(Request $request): ClientEntityInterface { - ($clientId = $request->query->getString('client_id')) + ($clientId = $request->query->getString(ParametersEnum::ClientId->value)) || throw new OidcException('Client ID not provided.'); $authedUserId = $this->authorization->isAdmin() ? null : $this->authorization->getUserId(); @@ -50,7 +58,6 @@ public function index(Request $request): Response $pagination = $this->clientRepository->findPaginated($page, $query, $authedUserId); - return $this->templateFactory->build( 'oidc:clients.twig', [ @@ -71,9 +78,8 @@ public function show(Request $request): Response $client = $this->getClientFromRequest($request); $allowedOrigins = $this->allowedOriginRepository->get($client->getIdentifier()); - // TODO mivanci rename *-ssp.twig templates after removing old ones. return $this->templateFactory->build( - 'oidc:clients/show-ssp.twig', + 'oidc:clients/show.twig', [ 'client' => $client, 'allowedOrigins' => $allowedOrigins, @@ -81,4 +87,30 @@ public function show(Request $request): Response RoutesEnum::AdminClients->value, ); } + + /** + * @throws \SimpleSAML\Module\oidc\Exceptions\OidcException + */ + public function resetSecret(Request $request): Response + { + $client = $this->getClientFromRequest($request); + + $oldSecret = $request->request->get('secret'); + + if ($oldSecret !== $client->getSecret()) { + throw new OidcException('Client secret does not match.'); + } + + $client->restoreSecret($this->sspBridge->utils()->random()->generateID()); + $authedUserId = $this->authorization->isAdmin() ? null : $this->authorization->getUserId(); + $this->clientRepository->update($client, $authedUserId); + + $message = Translate::noop('Client secret has been reset.'); + $this->sessionMessagesService->addMessage($message); + + return $this->routes->getRedirectResponseToModuleUrl( + RoutesEnum::AdminClientsShow->value, + [ParametersEnum::ClientId->value => $client->getIdentifier()], + ); + } } diff --git a/src/Controllers/Admin/ConfigController.php b/src/Controllers/Admin/ConfigController.php index f87fde1c..e5100a1c 100644 --- a/src/Controllers/Admin/ConfigController.php +++ b/src/Controllers/Admin/ConfigController.php @@ -11,8 +11,8 @@ use SimpleSAML\Module\oidc\ModuleConfig; use SimpleSAML\Module\oidc\Services\DatabaseMigration; use SimpleSAML\Module\oidc\Services\SessionMessagesService; +use SimpleSAML\Module\oidc\Utils\Routes; use SimpleSAML\OpenID\Federation; -use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Response; class ConfigController @@ -24,6 +24,7 @@ public function __construct( protected readonly DatabaseMigration $databaseMigration, protected readonly SessionMessagesService $sessionMessagesService, protected readonly Federation $federation, + protected readonly Routes $routes, ) { $this->authorization->requireAdmin(true); } @@ -44,14 +45,14 @@ public function runMigrations(): Response if ($this->databaseMigration->isMigrated()) { $message = Translate::noop('Database is already migrated.'); $this->sessionMessagesService->addMessage($message); - return new RedirectResponse($this->moduleConfig->getModuleUrl(RoutesEnum::AdminMigrations->value)); + return $this->routes->getRedirectResponseToModuleUrl(RoutesEnum::AdminMigrations->value); } $this->databaseMigration->migrate(); $message = Translate::noop('Database migrated successfully.'); $this->sessionMessagesService->addMessage($message); - return new RedirectResponse($this->moduleConfig->getModuleUrl(RoutesEnum::AdminMigrations->value)); + return $this->routes->getRedirectResponseToModuleUrl(RoutesEnum::AdminMigrations->value); } public function protocolSettings(): Response diff --git a/src/Controllers/Client/ShowController.php b/src/Controllers/Client/ShowController.php index c5e2efd1..ed96c22e 100644 --- a/src/Controllers/Client/ShowController.php +++ b/src/Controllers/Client/ShowController.php @@ -49,7 +49,7 @@ public function __invoke(ServerRequest $request): Template $client = $this->getClientFromRequest($request); $allowedOrigins = $this->allowedOriginRepository->get($client->getIdentifier()); - return $this->templateFactory->build('oidc:clients/show.twig', [ + return $this->templateFactory->build('oidc:clients/show-old.twig', [ 'client' => $client, 'allowedOrigins' => $allowedOrigins, ]); diff --git a/src/Utils/Routes.php b/src/Utils/Routes.php index a75d2f80..a5d18061 100644 --- a/src/Utils/Routes.php +++ b/src/Utils/Routes.php @@ -7,6 +7,7 @@ use SimpleSAML\Module\oidc\Bridges\SspBridge; use SimpleSAML\Module\oidc\Codebooks\RoutesEnum; use SimpleSAML\Module\oidc\ModuleConfig; +use Symfony\Component\HttpFoundation\RedirectResponse; class Routes { @@ -23,6 +24,19 @@ public function getModuleUrl(string $resource = '', array $parameters = []): str return $this->sspBridge->module()->getModuleUrl($resource, $parameters); } + public function getRedirectResponseToModuleUrl( + string $resource = '', + array $parameters = [], + int $status = 302, + array $headers = [], + ): RedirectResponse { + return new RedirectResponse( + $this->getModuleUrl($resource, $parameters), + $status, + $headers, + ); + } + /***************************************************************************************************************** * Admin area ****************************************************************************************************************/ @@ -60,6 +74,12 @@ public function urlAdminClientsShow(string $clientId, array $parameters = []): s return $this->getModuleUrl(RoutesEnum::AdminClientsShow->value, $parameters); } + public function urlAdminClientsResetSecret(string $clientId, array $parameters = []): string + { + $parameters['client_id'] = $clientId; + return $this->getModuleUrl(RoutesEnum::AdminClientsResetSecret->value, $parameters); + } + /***************************************************************************************************************** * OpenID Connect ****************************************************************************************************************/ diff --git a/templates/base.twig b/templates/base.twig index 4df64ea1..7fd9eafe 100644 --- a/templates/base.twig +++ b/templates/base.twig @@ -38,6 +38,11 @@ {% endblock content -%} -{% block postload %}{% endblock postload %} +{% block postload %} + + {{ parent() }} + + +{% endblock postload %} {% block oidcPostload %}{% endblock %} diff --git a/templates/clients.twig b/templates/clients.twig index c071e7a2..3c0753fe 100644 --- a/templates/clients.twig +++ b/templates/clients.twig @@ -67,7 +67,10 @@ - + diff --git a/templates/clients/show-old.twig b/templates/clients/show-old.twig new file mode 100644 index 00000000..3992a13f --- /dev/null +++ b/templates/clients/show-old.twig @@ -0,0 +1,231 @@ +{% extends "@oidc/oidc_base.twig" %} + +{% set pagetitle = 'Show OpenID Connect Client' | trans %} + +{% block pre_breadcrump %} + / + {{ 'OpenID Connect Client Registry'|trans }} +{% endblock %} + +{% block content %} +
{{ '{oidc:client:name}'|trans }} | ++ {{ client.name }} + | +
{{ '{oidc:client:description}'|trans }} | +{{ client.description }} | +
{{ '{oidc:client:state}'|trans }} | ++ + {{ (client.isEnabled ? '{oidc:client:is_enabled}' : '{oidc:client:deactivated}')|trans }} + + | +
{{ '{oidc:client:type}'|trans }} | +{{ (client.isConfidential ? '{oidc:client:confidential}' : '{oidc:client:public}')|trans }} | +
{{ '{oidc:client:identifier}'|trans }} | +{{ client.identifier }} + + | +
{{ '{oidc:client:secret}'|trans }} | ++ {{ client.secret }} + + | +
{{ '{oidc:client:auth_source}'|trans }} | +{{ client.authSourceId }} | +
{{ '{oidc:client:redirect_uri}'|trans }} | +
+
|
+
{{ '{oidc:client:scopes}'|trans }} | +
+
|
+
{{ '{oidc:client:backchannel_logout_uri}'|trans }} | +{{ client.backChannelLogoutUri }} | +
{{ '{oidc:client:post_logout_redirect_uri}'|trans }} | +
+
|
+
{{ '{oidc:client:allowed_origin}'|trans }} | +
+
|
+
{{ 'Signed JWKS URI'|trans }} | +{{ client.signedJwksUri }} | +
{{ 'JWKS URI'|trans }} | +{{ client.jwksUri }} | +
{{ 'JWKS'|trans }} | +
+ {% if client.jwks %}
+
+
+ {% endif %}
+
+
+
+ {{ client.jwks|json_encode(constant('JSON_PRETTY_PRINT')) }}
+
+ |
+
{{ '{oidc:client:owner}'|trans }} | +{{ client.owner }} | +
OpenID Federation related properties | +|
{{ 'Is federated'|trans }} | +{{ (client.isFederated ? 'Yes' : 'No')|trans }} | +
{{ 'Entity Identifier'|trans }} | +{{ client.entityIdentifier }} | +
{{ 'Client registration types'|trans }} | +
+
|
+
{{ 'Federation JWKS'|trans }} | +
+ {% if client.federationJwks %}
+
+
+ {% endif %}
+
+
+
+ {{ client.federationJwks|json_encode(constant('JSON_PRETTY_PRINT')) }}
+
+ |
+
+ | + | + +
---|
- {{ 'Name and description'|trans }} - | -
- {{ client.name }} - {{ client.description }} - |
-
- - | -- - | -
- - | -- - | -
- - | -- - | -
- - | -- - | -
- - | -- - | -
- - | -- - | -
{{ '{oidc:client:name}'|trans }} | -- {{ client.name }} - | -
{{ '{oidc:client:description}'|trans }} | -{{ client.description }} | -
{{ '{oidc:client:state}'|trans }} | -- - {{ (client.isEnabled ? '{oidc:client:is_enabled}' : '{oidc:client:deactivated}')|trans }} - - | -
{{ '{oidc:client:type}'|trans }} | -{{ (client.isConfidential ? '{oidc:client:confidential}' : '{oidc:client:public}')|trans }} | -
{{ '{oidc:client:identifier}'|trans }} | -{{ client.identifier }} - - | -
{{ '{oidc:client:secret}'|trans }} | -- {{ client.secret }} - - | -
{{ '{oidc:client:auth_source}'|trans }} | -{{ client.authSourceId }} | -
{{ '{oidc:client:redirect_uri}'|trans }} | -
-
|
-
{{ '{oidc:client:scopes}'|trans }} | -
-
|
-
{{ '{oidc:client:backchannel_logout_uri}'|trans }} | -{{ client.backChannelLogoutUri }} | -
{{ '{oidc:client:post_logout_redirect_uri}'|trans }} | -
-
|
-
{{ '{oidc:client:allowed_origin}'|trans }} | -
-
|
-
{{ 'Signed JWKS URI'|trans }} | -{{ client.signedJwksUri }} | -
{{ 'JWKS URI'|trans }} | -{{ client.jwksUri }} | -
{{ 'JWKS'|trans }} | -
- {% if client.jwks %}
-
-
- {% endif %}
-
-
-
- {{ client.jwks|json_encode(constant('JSON_PRETTY_PRINT')) }}
-
- |
-
{{ '{oidc:client:owner}'|trans }} | -{{ client.owner }} | -
OpenID Federation related properties | -|
{{ 'Is federated'|trans }} | -{{ (client.isFederated ? 'Yes' : 'No')|trans }} | -
{{ 'Entity Identifier'|trans }} | -{{ client.entityIdentifier }} | -
{{ 'Client registration types'|trans }} | -
-
|
-
{{ 'Federation JWKS'|trans }} | -
- {% if client.federationJwks %}
-
-
- {% endif %}
-
-
-
- {{ client.federationJwks|json_encode(constant('JSON_PRETTY_PRINT')) }}
-
- |
-
- | - |
---|