Skip to content

Commit

Permalink
WIP Move to SSP UI
Browse files Browse the repository at this point in the history
  • Loading branch information
Marko Ivančić committed Dec 1, 2024
1 parent c7d4699 commit bbbfa6f
Show file tree
Hide file tree
Showing 19 changed files with 388 additions and 57 deletions.
11 changes: 4 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ PHP version requirement changes in minor releases for SimpleSAMLphp.

### Upgrading?

If you are upgrading from a previous version, checkout the [upgrade guide](UPGRADE.md).
If you are upgrading from a previous version, make sure to check the [upgrade guide](UPGRADE.md).

## Installation

Expand Down Expand Up @@ -107,14 +107,12 @@ SimpleSAMLphp configuration file, `config/config.php`.
'oidc' => true,
],

This is required the enable the module on the _Federation_ tab in the admin web interface, which can be used in the
next two steps to finalize the installation.
Once the module is enabled, the database migrations must be run.

### Run database migrations

The module comes with some default SQL migrations which set up needed tables in the configured database. To run them,
open the _Federation_ tab from your _SimpleSAMLphp_ installation and select the option _OpenID Connect Installation_
inside the _Tools_ section. Once there, all you need to do is press the _Install_ button and the schema will be created.
go to `OIDC` > `Database Migrations`, and press the available button.

Alternatively, in case of automatic / scripted deployments, you can run the 'install.php' script from the command line:

Expand All @@ -124,8 +122,7 @@ Alternatively, in case of automatic / scripted deployments, you can run the 'ins

The module lets you manage (create, read, update and delete) approved RPs from the module user interface itself.

Once the database schema has been created, you can open the _Federation_ tab from your _SimpleSAMLphp_ installation
and select the option _OpenID Connect Client Registry_ inside the _Tools_ section.
Once the database schema has been created, you can go to `OIDC` > `Client Registry`.

Note that clients can be marked as confidential or public. If the client is not marked as confidential (it is public),
and is using Authorization Code flow, it will have to provide PKCE parameters during the flow.
Expand Down
7 changes: 6 additions & 1 deletion UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,12 @@ key `authproc.oidc`

## Low impact changes

Below are some internal changes that should not have impact for the OIDC OP implementors. However, if you are using
In an effort to move to SimpleSAMLphp way of working with user interface (UI), the client management UI was updated
to extend from the SimpleSAMLphp base template. In addition, we have also introduced some configuration overview pages
where you can take a quick view of some of the configuration values for the module. OIDC related pages are now available
from the main SimpleSAMLphp menu in Administration area.

Below are also some internal changes that should not have impact for the OIDC OP implementors. However, if you are using
this module as a library or extending from it, you will probably encounter breaking changes, since a lot of code
has been refactored:

Expand Down
Binary file modified docs/oidc.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion hooks/hook_adminmenu.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ function oidc_hook_adminmenu(Template &$template): void

$oidcMenuEntry = [
ModuleConfig::MODULE_NAME => [
'url' => $moduleConfig->getModuleUrl(RoutesEnum::AdminConfigProtocol->value),
'url' => $moduleConfig->getModuleUrl(RoutesEnum::AdminMigrations->value),
'name' => Translate::noop('OIDC'),
],
];
Expand Down
14 changes: 10 additions & 4 deletions hooks/hook_federationpage.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

use SimpleSAML\Locale\Translate;
use SimpleSAML\Module;
use SimpleSAML\Module\oidc\ModuleConfig;
use SimpleSAML\Module\oidc\Services\DatabaseMigration;
use SimpleSAML\XHTML\Template;

Expand All @@ -24,12 +25,17 @@
*/
function oidc_hook_federationpage(Template $template): void
{
$href = Module::getModuleURL('oidc/admin-clients/index.php');
$text = Translate::noop('OpenID Connect Registry');
$routes = new Module\oidc\Utils\Routes(
new ModuleConfig(),
new Module\oidc\Bridges\SspBridge(),
);

$href = $routes->urlAdminClients();
$text = Translate::noop('OIDC Client Registry');

if (! (new DatabaseMigration())->isMigrated()) {
$href = Module::getModuleURL('oidc/install.php');
$text = Translate::noop('OpenID Connect Installation');
$href = $routes->urlAdminMigrations();
$text = Translate::noop('OIDC Installation');
}

if (!is_array($template->data['links'])) {
Expand Down
2 changes: 1 addition & 1 deletion public/assets/css/src/default.css
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,6 @@ table.client-table {

.confirm-action {}

form.pure-form-stacked input {
form.pure-form-stacked .full-width {
width: 100%;
}
22 changes: 22 additions & 0 deletions public/assets/js/src/client-form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
(function () {
'use strict';

// Handle enabling and disabling input for 'allowed origins', based on client type radio input.
function toggleAllowedOrigins() {
if (radioOptionPublic.checked) {
inputAllowedOrigin.disabled = false; // Enable the input field
} else if (radioOptionConfidential.checked) {
inputAllowedOrigin.disabled = true; // Disable the input field
}
}

// Get references to the radio buttons and the input field
const radioOptionPublic = document.getElementById("radio-option-public");
const radioOptionConfidential = document.getElementById("radio-option-confidential");
const inputAllowedOrigin = document.getElementById("frm-allowed_origin");

radioOptionPublic.addEventListener("change", toggleAllowedOrigins);
radioOptionConfidential.addEventListener("change", toggleAllowedOrigins);

toggleAllowedOrigins();
})();
3 changes: 3 additions & 0 deletions routing/routes/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
$routes->add(RoutesEnum::AdminClientsShow->name, RoutesEnum::AdminClientsShow->value)
->controller([ClientController::class, 'show'])
->methods([HttpMethodsEnum::GET->value]);
$routes->add(RoutesEnum::AdminClientsEdit->name, RoutesEnum::AdminClientsEdit->value)
->controller([ClientController::class, 'edit'])
->methods([HttpMethodsEnum::GET->value, HttpMethodsEnum::POST->value]);
$routes->add(RoutesEnum::AdminClientsResetSecret->name, RoutesEnum::AdminClientsResetSecret->value)
->controller([ClientController::class, 'resetSecret'])
->methods([HttpMethodsEnum::POST->value]);
Expand Down
1 change: 1 addition & 0 deletions src/Codebooks/RoutesEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ enum RoutesEnum: string

case AdminClients = 'admin/clients';
case AdminClientsShow = 'admin/clients/show';
case AdminClientsEdit = 'admin/clients/edit';
case AdminClientsAdd = 'admin/clients/add';
case AdminClientsResetSecret = 'admin/clients/reset-secret';
case AdminClientsDelete = 'admin/clients/delete';
Expand Down
70 changes: 68 additions & 2 deletions src/Controllers/Admin/ClientController.php
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ public function add(): Response

$owner = $this->authorization->isAdmin() ? null : $this->authorization->getUserId();

Check warning on line 169 in src/Controllers/Admin/ClientController.php

View check run for this annotation

Codecov / codecov/patch

src/Controllers/Admin/ClientController.php#L169

Added line #L169 was not covered by tests

$client = $this->buildClientFromFormData(
$client = $this->buildClientEntityFromFormData(
$form,
$this->sspBridge->utils()->random()->generateID(),
$this->sspBridge->utils()->random()->generateID(),
Expand All @@ -179,6 +179,8 @@ public function add(): Response
$owner,
);

Check warning on line 180 in src/Controllers/Admin/ClientController.php

View check run for this annotation

Codecov / codecov/patch

src/Controllers/Admin/ClientController.php#L171-L180

Added lines #L171 - L180 were not covered by tests

// TODO mivanci Check if the entity identifier already exists.

$this->clientRepository->add($client);

Check warning on line 184 in src/Controllers/Admin/ClientController.php

View check run for this annotation

Codecov / codecov/patch

src/Controllers/Admin/ClientController.php#L184

Added line #L184 was not covered by tests

// Also persist allowed origins for this client.
Expand Down Expand Up @@ -209,11 +211,75 @@ public function add(): Response
);

Check warning on line 211 in src/Controllers/Admin/ClientController.php

View check run for this annotation

Codecov / codecov/patch

src/Controllers/Admin/ClientController.php#L200-L211

Added lines #L200 - L211 were not covered by tests
}

/**
* @throws \SimpleSAML\Error\ConfigurationError
* @throws \SimpleSAML\Module\oidc\Server\Exceptions\OidcServerException
* @throws \SimpleSAML\Error\Exception
* @throws \SimpleSAML\Module\oidc\Exceptions\OidcException
* @throws \JsonException
*/
public function edit(Request $request): Response

Check warning on line 221 in src/Controllers/Admin/ClientController.php

View check run for this annotation

Codecov / codecov/patch

src/Controllers/Admin/ClientController.php#L221

Added line #L221 was not covered by tests
{
$originalClient = $this->getClientFromRequest($request);
$clientAllowedOrigins = $this->allowedOriginRepository->get($originalClient->getIdentifier());
$form = $this->formFactory->build(ClientForm::class);

Check warning on line 225 in src/Controllers/Admin/ClientController.php

View check run for this annotation

Codecov / codecov/patch

src/Controllers/Admin/ClientController.php#L223-L225

Added lines #L223 - L225 were not covered by tests

$clientData = $originalClient->toArray();
$clientData['allowed_origin'] = $clientAllowedOrigins;
$form->setDefaults($clientData);

Check warning on line 229 in src/Controllers/Admin/ClientController.php

View check run for this annotation

Codecov / codecov/patch

src/Controllers/Admin/ClientController.php#L227-L229

Added lines #L227 - L229 were not covered by tests

if ($form->isSuccess()) {
$updatedAt = $this->helpers->dateTime()->getUtc();

Check warning on line 232 in src/Controllers/Admin/ClientController.php

View check run for this annotation

Codecov / codecov/patch

src/Controllers/Admin/ClientController.php#L231-L232

Added lines #L231 - L232 were not covered by tests

$updatedClient = $this->buildClientEntityFromFormData(
$form,
$originalClient->getIdentifier(),
$originalClient->getSecret(),
$originalClient->getRegistrationType(),
$updatedAt,
$originalClient->getCreatedAt(),
$originalClient->getExpiresAt(),
$originalClient->getOwner(),
);

Check warning on line 243 in src/Controllers/Admin/ClientController.php

View check run for this annotation

Codecov / codecov/patch

src/Controllers/Admin/ClientController.php#L234-L243

Added lines #L234 - L243 were not covered by tests

// TODO mivanci Check if the entity identifier already exists for other client.

$this->clientRepository->update($updatedClient);

Check warning on line 247 in src/Controllers/Admin/ClientController.php

View check run for this annotation

Codecov / codecov/patch

src/Controllers/Admin/ClientController.php#L247

Added line #L247 was not covered by tests

// Also persist allowed origins for this client.
is_array($allowedOrigins = $form->getValues('array')['allowed_origin'] ?? []) ||
throw new OidcException('Unexpected value for allowed origins.');

Check warning on line 251 in src/Controllers/Admin/ClientController.php

View check run for this annotation

Codecov / codecov/patch

src/Controllers/Admin/ClientController.php#L250-L251

Added lines #L250 - L251 were not covered by tests
/** @var string[] $allowedOrigins */
$this->allowedOriginRepository->set($originalClient->getIdentifier(), $allowedOrigins);

Check warning on line 253 in src/Controllers/Admin/ClientController.php

View check run for this annotation

Codecov / codecov/patch

src/Controllers/Admin/ClientController.php#L253

Added line #L253 was not covered by tests

$this->sessionMessagesService->addMessage(Translate::noop('Client has been updated.'));

Check warning on line 255 in src/Controllers/Admin/ClientController.php

View check run for this annotation

Codecov / codecov/patch

src/Controllers/Admin/ClientController.php#L255

Added line #L255 was not covered by tests

return $this->routes->getRedirectResponseToModuleUrl(
RoutesEnum::AdminClientsShow->value,
[ParametersEnum::ClientId->value => $originalClient->getIdentifier()],
);

Check warning on line 260 in src/Controllers/Admin/ClientController.php

View check run for this annotation

Codecov / codecov/patch

src/Controllers/Admin/ClientController.php#L257-L260

Added lines #L257 - L260 were not covered by tests
}

return $this->templateFactory->build(
'oidc:clients/edit.twig',
[
'originalClient' => $originalClient,
'form' => $form,
'actionRoute' => $this->routes->urlAdminClientsEdit($originalClient->getIdentifier()),
'regexUri' => ClientForm::REGEX_URI,
'regexAllowedOriginUrl' => ClientForm::REGEX_ALLOWED_ORIGIN_URL,
'regexHttpUri' => ClientForm::REGEX_HTTP_URI,
'regexHttpUriPath' => ClientForm::REGEX_HTTP_URI_PATH,
],
RoutesEnum::AdminClients->value,
);

Check warning on line 275 in src/Controllers/Admin/ClientController.php

View check run for this annotation

Codecov / codecov/patch

src/Controllers/Admin/ClientController.php#L263-L275

Added lines #L263 - L275 were not covered by tests
}

/**
* TODO mivanci Move to ClientEntityFactory::fromRegistrationData on dynamic client registration implementation.
* @throws \SimpleSAML\Module\oidc\Exceptions\OidcException
*/
protected function buildClientFromFormData(
protected function buildClientEntityFromFormData(

Check warning on line 282 in src/Controllers/Admin/ClientController.php

View check run for this annotation

Codecov / codecov/patch

src/Controllers/Admin/ClientController.php#L282

Added line #L282 was not covered by tests
Form $form,
string $identifier,
string $secret,
Expand Down
2 changes: 1 addition & 1 deletion src/Controllers/Client/EditController.php
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ public function __invoke(ServerRequest $request): Template|RedirectResponse
);
}

return $this->templateFactory->build('oidc:clients/edit.twig', [
return $this->templateFactory->build('oidc:clients/edit-old.twig', [
'form' => $form,
'regexUri' => ClientForm::REGEX_URI,
'regexAllowedOriginUrl' => ClientForm::REGEX_ALLOWED_ORIGIN_URL,
Expand Down
50 changes: 32 additions & 18 deletions src/Forms/ClientForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -341,11 +341,14 @@ protected function buildForm(): void
$this->addComponent($this->csrfProtection, Form::ProtectorId);

$this->addText('name', '{oidc:client:name}')
->setHtmlAttribute('class', 'full-width')
->setMaxLength(255)
->setRequired(Translate::noop('Name is required.'));

$this->addTextArea('description', '{oidc:client:description}', null, 5);
$this->addTextArea('description', '{oidc:client:description}', null, 3)
->setHtmlAttribute('class', 'full-width');
$this->addTextArea('redirect_uri', '{oidc:client:redirect_uri}', null, 5)
->setHtmlAttribute('class', 'full-width')
->setRequired(Translate::noop('At least one redirect URI is required.'));

$this->addCheckbox('is_enabled', '{oidc:client:is_enabled}');
Expand All @@ -354,38 +357,49 @@ protected function buildForm(): void

// TODO mivanci Source::getSource() move to SSP Bridge.
$this->addSelect('auth_source', '{oidc:client:auth_source}:')
->setHtmlAttribute('class', 'ui fluid dropdown clearable')
->setHtmlAttribute('class', 'full-width')
->setItems(Source::getSources(), false)
->setPrompt(Translate::noop('Pick an AuthSource'));
->setPrompt(Translate::noop('-'));

$scopes = $this->getScopes();

$this->addMultiSelect('scopes', '{oidc:client:scopes}')
->setHtmlAttribute('class', 'ui fluid dropdown')
->setItems($scopes)
$this->addMultiSelect('scopes', '{oidc:client:scopes}', $scopes, 10)
->setHtmlAttribute('class', 'full-width')
->setRequired(Translate::noop('At least one scope is required.'));

$this->addText('owner', '{oidc:client:owner}')
->setMaxLength(190);
$this->addTextArea('post_logout_redirect_uri', '{oidc:client:post_logout_redirect_uri}', null, 5);
$this->addTextArea('allowed_origin', '{oidc:client:allowed_origin}', null, 5);
$this->addTextArea('post_logout_redirect_uri', '{oidc:client:post_logout_redirect_uri}', null, 5)
->setHtmlAttribute('class', 'full-width');
$this->addTextArea('allowed_origin', '{oidc:client:allowed_origin}', null, 5)
->setHtmlAttribute('class', 'full-width');

$this->addText('backchannel_logout_uri', '{oidc:client:backchannel_logout_uri}');
$this->addText('backchannel_logout_uri', '{oidc:client:backchannel_logout_uri}')
->setHtmlAttribute('class', 'full-width');

$this->addText('entity_identifier', 'Entity Identifier');
$this->addText('entity_identifier', 'Entity Identifier')
->setHtmlAttribute('class', 'full-width');

$this->addMultiSelect('client_registration_types', 'Registration types')
->setHtmlAttribute('class', 'ui fluid dropdown')
->setItems($this->getClientRegistrationTypes());
$this->addMultiSelect(
'client_registration_types',
'Registration types',
$this->getClientRegistrationTypes(),
2,
)->setHtmlAttribute('class', 'full-width');

$this->addTextArea('federation_jwks', '{oidc:client:federation_jwks}', null, 5);
$this->addTextArea('federation_jwks', '{oidc:client:federation_jwks}', null, 5)
->setHtmlAttribute('class', 'full-width');

$this->addTextArea('jwks', '{oidc:client:jwks}', null, 5);
$this->addTextArea('jwks', '{oidc:client:jwks}', null, 5)
->setHtmlAttribute('class', 'full-width');

$this->addText('jwks_uri', 'JWKS URI');
$this->addText('signed_jwks_uri', 'Signed JWKS URI');
$this->addText('jwks_uri', 'JWKS URI')
->setHtmlAttribute('class', 'full-width');
$this->addText('signed_jwks_uri', 'Signed JWKS URI')
->setHtmlAttribute('class', 'full-width');

$this->addCheckbox('is_federated', '{oidc:client:is_federated}');
$this->addCheckbox('is_federated', '{oidc:client:is_federated}')
->setHtmlAttribute('class', 'full-width');
}

/**
Expand Down
6 changes: 6 additions & 0 deletions src/Utils/Routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ public function urlAdminClientsShow(string $clientId, array $parameters = []): s
return $this->getModuleUrl(RoutesEnum::AdminClientsShow->value, $parameters);

Check warning on line 75 in src/Utils/Routes.php

View check run for this annotation

Codecov / codecov/patch

src/Utils/Routes.php#L74-L75

Added lines #L74 - L75 were not covered by tests
}

public function urlAdminClientsEdit(string $clientId, array $parameters = []): string

Check warning on line 78 in src/Utils/Routes.php

View check run for this annotation

Codecov / codecov/patch

src/Utils/Routes.php#L78

Added line #L78 was not covered by tests
{
$parameters[ParametersEnum::ClientId->value] = $clientId;
return $this->getModuleUrl(RoutesEnum::AdminClientsEdit->value, $parameters);

Check warning on line 81 in src/Utils/Routes.php

View check run for this annotation

Codecov / codecov/patch

src/Utils/Routes.php#L80-L81

Added lines #L80 - L81 were not covered by tests
}

public function urlAdminClientsAdd(array $parameters = []): string

Check warning on line 84 in src/Utils/Routes.php

View check run for this annotation

Codecov / codecov/patch

src/Utils/Routes.php#L84

Added line #L84 was not covered by tests
{
return $this->getModuleUrl(RoutesEnum::AdminClientsAdd->value, $parameters);

Check warning on line 86 in src/Utils/Routes.php

View check run for this annotation

Codecov / codecov/patch

src/Utils/Routes.php#L86

Added line #L86 was not covered by tests
Expand Down
2 changes: 1 addition & 1 deletion templates/clients.twig
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
<a class="pure-button" href="{{ routes.urlAdminClientsShow(client.getIdentifier) }}">
<i class="fa fa-eye"></i>
</a>
<a class="pure-button" href="#">
<a class="pure-button" href="{{ routes.urlAdminClientsEdit(client.getIdentifier) }}">
<i class="fa fa-pen-to-square"></i>
</a>
<input type="hidden" name="secret" value="{{ client.secret }}">
Expand Down
15 changes: 15 additions & 0 deletions templates/clients/edit-old.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{% extends "@oidc/clients/_form.twig" %}

{% set pagetitle = 'Edit new OpenID Connect Client' | trans %}

{% block pre_breadcrump %}
<span class="divider">/</span>
<a class="section" href="./index.php">{{ 'OpenID Connect Client Registry'|trans }}</a>
{% endblock %}

{% block action %}
<div class="ui submit primary icon labeled button">
<i class="save icon"></i>
{{ '{oidc:save}'|trans }}
</div>
{% endblock %}
Loading

0 comments on commit bbbfa6f

Please sign in to comment.