diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 8bdcc22..7fb5f8a 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -8,7 +8,7 @@ jobs: fail-fast: false steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 2 @@ -26,7 +26,7 @@ jobs: php-versions: [ '8.3' ] dependency-version: [ prefer-lowest, prefer-stable ] steps: - - uses: actions/checkout@master + - uses: actions/checkout@v4 - name: Setup PHP, with composer and extensions uses: shivammathur/setup-php@v2 with: @@ -56,9 +56,8 @@ jobs: strategy: matrix: php-versions: [ '8.3' ] - dependency-version: [ prefer-lowest, prefer-stable ] steps: - - uses: actions/checkout@master + - uses: actions/checkout@v4 - name: Setup PHP, with composer and extensions uses: shivammathur/setup-php@v2 with: @@ -76,6 +75,7 @@ jobs: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} restore-keys: ${{ runner.os }}-composer- + - name: Install Dependencies run: | composer install --no-interaction --no-progress @@ -89,9 +89,8 @@ jobs: strategy: matrix: php-versions: [ '8.3' ] - dependency-version: [ prefer-lowest, prefer-stable ] steps: - - uses: actions/checkout@master + - uses: actions/checkout@v4 - name: Setup PHP, with composer and extensions uses: shivammathur/setup-php@v2 with: @@ -112,9 +111,9 @@ jobs: - name: Install Dependencies run: | composer install --no-interaction --no-progress - - name: twigcs + - name: twig-cs-fixer run: | - composer coding-standards-check/twigcs + composer coding-standards-check/twig-cs-fixer php-code-analysis: name: PHP code analysis @@ -122,9 +121,8 @@ jobs: strategy: matrix: php-versions: [ '8.3' ] - dependency-version: [ prefer-lowest, prefer-stable ] steps: - - uses: actions/checkout@master + - uses: actions/checkout@v4 - name: Setup PHP, with composer and extensions uses: shivammathur/setup-php@v2 with: @@ -183,7 +181,7 @@ jobs: name: Markdown coding standards steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Get yarn cache directory path id: yarn-cache-dir-path run: echo "::set-output name=dir::$(yarn cache dir)" diff --git a/.gitignore b/.gitignore index f782610..52dd39d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ vendor composer.lock node_modules yarn.lock +*.cache diff --git a/.markdownlint.jsonc b/.markdownlint.jsonc new file mode 100644 index 0000000..a28c580 --- /dev/null +++ b/.markdownlint.jsonc @@ -0,0 +1,13 @@ +{ + "default": true, + // https://github.com/DavidAnson/markdownlint/blob/main/doc/md013.md + "line-length": { + "line_length": 120, + "code_blocks": false, + "tables": false + }, + // https://github.com/DavidAnson/markdownlint/blob/main/doc/md024.md + "no-duplicate-heading": { + "siblings_only": true + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md index fb4fbab..b5fc4b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -## [2.2.1] +## [2.3.0] 2025-12-11 + +* [PR-20](https://github.com/itk-dev/os2forms_nemlogin_openid_connect/pull/20) + Added support for Key. + +## [2.2.1] 2025-03-13 * Allowed `os2forms/os2forms` 4.0. * Updated GitHub action dependencies. @@ -32,7 +37,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). * Allowed multiple providers. -[Unreleased]: https://github.com/itk-dev/os2forms_nemlogin_openid_connect/compare/2.2.1...HEAD +[Unreleased]: https://github.com/itk-dev/os2forms_nemlogin_openid_connect/compare/2.3.0...HEAD +[2.3.0]: https://github.com/itk-dev/os2forms_nemlogin_openid_connect/compare/2.2.1...2.3.0 [2.2.1]: https://github.com/itk-dev/os2forms_nemlogin_openid_connect/compare/2.2.0...2.2.1 [2.2.0]: https://github.com/itk-dev/os2forms_nemlogin_openid_connect/compare/2.1.0...2.2.0 [2.1.0]: https://github.com/itk-dev/os2forms_nemlogin_openid_connect/compare/2.0.1...2.1.0 diff --git a/README.md b/README.md index 87273af..c0123eb 100644 --- a/README.md +++ b/README.md @@ -94,5 +94,37 @@ $settings['os2forms_nemlogin_openid_connect']['another-plugin-id']['local_test_u 'name' => 'User 87', ], ]; +``` + +## Coding standards + +Our coding are checked by GitHub Actions (cf. [.github/workflows/pr.yml](.github/workflows/pr.yml)). Use the commands +below to run the checks locally. + +### PHP + +```shell +docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.3-fpm composer install +# Fix (some) coding standards issues +docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.3-fpm composer coding-standards-apply +# Check that code adheres to the coding standards +docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.3-fpm composer coding-standards-check +``` + +### Markdown + +```shell +docker run --rm --volume $PWD:/md peterdavehello/markdownlint markdownlint --ignore vendor --ignore LICENSE.md '**/*.md' --fix +docker run --rm --volume $PWD:/md peterdavehello/markdownlint markdownlint --ignore vendor --ignore LICENSE.md '**/*.md' +``` + +## Code analysis + +We use [PHPStan](https://phpstan.org/) for static code analysis. + +Running statis code analysis on a standalone Drupal module is a bit tricky, so we use a helper script to run the +analysis: +```shell +docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.3-fpm ./scripts/code-analysis ``` diff --git a/composer.json b/composer.json index fd0cba9..f4bb594 100644 --- a/composer.json +++ b/composer.json @@ -1,63 +1,79 @@ { "name": "itk-dev/os2forms_nemlogin_openid_connect", - "type": "drupal-module", - "license": "MIT", "description": "os2forms_nemlogin_openid_connect", - "keywords": [], + "license": "MIT", + "type": "drupal-module", "homepage": "https://www.drupal.org/project/os2forms_nemlogin_openid_connect", - "minimum-stability": "dev", - "repositories": [ - { - "type": "composer", - "url": "https://packages.drupal.org/8" - } - ], "require": { + "drupal/webform": "^6.0", + "itk-dev/drupal_psr6_cache": "^1.1", "itk-dev/openid-connect": "^3.1", - "itk-dev/drupal_psr6_cache": "^1.0", - "os2forms/os2forms": "^3.17 || ^4.0", + "os2web/os2web_audit": "^1.0", + "os2web/os2web_key": "^1.0", "os2web/os2web_nemlogin": "^1.2", - "drupal/webform": "^6.0", "symfony/options-resolver": "^5.4 || ^6.0" }, "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", "drupal/coder": "^8.3", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.1", - "mglaman/drupal-check": "^1.4", + "ergebnis/composer-normalize": "^2.47", + "mglaman/drupal-check": "^1.5", "mglaman/phpstan-drupal": "~1.2.0", - "friendsoftwig/twigcs": "^6.0" + "vincentlanglet/twig-cs-fixer": "^3.5" + }, + "repositories": [ + { + "type": "composer", + "url": "https://packages.drupal.org/8" + } + ], + "minimum-stability": "dev", + "prefer-stable": true, + "config": { + "allow-plugins": { + "cweagans/composer-patches": true, + "dealerdirect/phpcodesniffer-composer-installer": true, + "ergebnis/composer-normalize": true, + "mglaman/composer-drupal-lenient": true, + "simplesamlphp/composer-module-installer": true, + "simplesamlphp/composer-xmlprovider-installer": true, + "zaporylie/composer-drupal-optimizations": true + } + }, + "extra": { + "drupal-lenient": { + "allowed-list": [ + "drupal/coc_forms_auto_export", + "drupal/webform_node_element" + ] + } }, "scripts": { + "code-analysis": [ + "@code-analysis/drupal-check" + ], "code-analysis/drupal-check": [ "drupal-check --deprecations --analysis --exclude-dir=vendor *.* src" ], - "code-analysis": [ - "@code-analysis/drupal-check" + "coding-standards-apply": [ + "@coding-standards-apply/phpcbf", + "@coding-standards-apply/twig-cs-fixer" ], - "coding-standards-check/phpcs": [ - "phpcs --standard=phpcs.xml.dist" + "coding-standards-apply/phpcbf": [ + "phpcbf --standard=phpcs.xml.dist" ], - "coding-standards-check/twigcs": [ - "twigcs templates" + "coding-standards-apply/twig-cs-fixer": [ + "twig-cs-fixer --fix" ], "coding-standards-check": [ "@coding-standards-check/phpcs", "@coding-standards-check/twigcs" ], - "coding-standards-apply/phpcbf": [ - "phpcbf --standard=phpcs.xml.dist" + "coding-standards-check/phpcs": [ + "phpcs --standard=phpcs.xml.dist" ], - "coding-standards-apply": [ - "@coding-standards-apply/phpcbf" + "coding-standards-check/twig-cs-fixer": [ + "twig-cs-fixer" ] - }, - "config": { - "allow-plugins": { - "dealerdirect/phpcodesniffer-composer-installer": true, - "zaporylie/composer-drupal-optimizations": true, - "cweagans/composer-patches": true, - "simplesamlphp/composer-module-installer": true, - "mglaman/composer-drupal-lenient": true - } } } diff --git a/os2forms_nemlogin_openid_connect.info.yml b/os2forms_nemlogin_openid_connect.info.yml index c90eae1..51db3c4 100644 --- a/os2forms_nemlogin_openid_connect.info.yml +++ b/os2forms_nemlogin_openid_connect.info.yml @@ -11,5 +11,6 @@ dependencies: - drupal:os2forms_forloeb - drupal:os2web_nemlogin - os2web:os2web_audit + - os2web_key:os2web_key configure: os2forms_nemlogin_openid_connect.admin.settings diff --git a/src/Controller/OpenIDConnectController.php b/src/Controller/OpenIDConnectController.php index c9974fa..b77088b 100644 --- a/src/Controller/OpenIDConnectController.php +++ b/src/Controller/OpenIDConnectController.php @@ -12,8 +12,11 @@ use Drupal\Core\Site\Settings; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\Url; +use Drupal\key\KeyRepositoryInterface; use Drupal\os2forms_nemlogin_openid_connect\Exception\AuthenticationException; use Drupal\os2forms_nemlogin_openid_connect\Plugin\os2web\NemloginAuthProvider\OpenIDConnect; +use Drupal\os2web_key\KeyHelper; +use Drupal\os2web_key\Plugin\KeyType\OidcKeyType; use Drupal\os2web_nemlogin\Service\AuthProviderService; use ItkDev\OpenIdConnect\Security\OpenIdConfigurationProvider; use Psr\Cache\CacheItemPoolInterface; @@ -77,8 +80,10 @@ public function __construct( private readonly SessionInterface $session, private readonly CacheItemPoolInterface $cacheItemPool, private readonly LanguageManagerInterface $languageManager, - LoggerInterface $logger, private readonly RendererInterface $renderer, + private readonly KeyRepositoryInterface $keyRepository, + private readonly KeyHelper $keyHelper, + LoggerInterface $logger, ) { $this->setLogger($logger); } @@ -93,8 +98,10 @@ public static function create(ContainerInterface $container): self { $container->get('session'), $container->get('drupal_psr6_cache.cache_item_pool'), $container->get('language_manager'), - $container->get('logger.channel.os2forms_nemlogin_openid_connect'), $container->get('renderer'), + $container->get('key.repository'), + $container->get(KeyHelper::class), + $container->get('logger.channel.os2forms_nemlogin_openid_connect'), ); } @@ -140,12 +147,35 @@ public function main(string $id) { private function getOpenIdConfigurationProvider(): OpenIdConfigurationProvider { $pluginConfiguration = $this->plugin->getConfiguration(); + if (OpenIDConnect::PROVIDER_TYPE_KEY === $pluginConfiguration[OpenIDConnect::PROVIDER_TYPE_KEY]) { + $discoveryUrl = $pluginConfiguration[OpenIDConnect::DISCOVERY_URL]; + $clientId = $pluginConfiguration[OpenIDConnect::CLIENT_ID]; + $clientSecret = $pluginConfiguration[OpenIDConnect::CLIENT_SECRET]; + } + else { + try { + $keyId = $pluginConfiguration[OpenIDConnect::KEY] ?? ''; + $key = $this->keyRepository->getKey($keyId); + if (NULL === $key) { + throw new \RuntimeException(sprintf('Cannot get key %s', $keyId)); + } + [ + OidcKeyType::DISCOVERY_URL => $discoveryUrl, + OidcKeyType::CLIENT_ID => $clientId, + OidcKeyType::CLIENT_SECRET => $clientSecret, + ] = $this->keyHelper->getOidcValues($key); + } + catch (\Exception $e) { + throw new AuthenticationException('Cannot get client id and secret', $e->getCode(), $e); + } + } + $providerOptions = [ 'redirectUri' => $this->getRedirectUri(), - 'openIDConnectMetadataUrl' => $pluginConfiguration['nemlogin_openid_connect_discovery_url'], + 'openIDConnectMetadataUrl' => $discoveryUrl, 'cacheItemPool' => $this->cacheItemPool, - 'clientId' => $pluginConfiguration['nemlogin_openid_connect_client_id'], - 'clientSecret' => $pluginConfiguration['nemlogin_openid_connect_client_secret'], + 'clientId' => $clientId, + 'clientSecret' => $clientSecret, 'localTestMode' => FALSE, 'allowHttp' => (bool) ($this->getSettings()['allow_http'] ?? FALSE), ]; @@ -273,7 +303,7 @@ public function endSession(string $id): Response { private function getPostLogoutRedirectUri(): string { try { $pluginConfiguration = $this->plugin->getConfiguration(); - $url = $pluginConfiguration['nemlogin_openid_connect_post_logout_redirect_uri'] ?? '/'; + $url = $pluginConfiguration[OpenIDConnect::POST_LOGOUT_REDIRECT_URI] ?? '/'; $options = [ 'absolute' => TRUE, 'path_processing' => FALSE, diff --git a/src/Form/SettingsForm.php b/src/Form/SettingsForm.php index 0189fc4..1ac0eb3 100644 --- a/src/Form/SettingsForm.php +++ b/src/Form/SettingsForm.php @@ -4,6 +4,7 @@ use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Link; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\os2forms_nemlogin_openid_connect\Helper\Settings; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -73,6 +74,41 @@ public function buildForm(array $form, FormStateInterface $form_state): array { '#value' => $this->t('Save settings'), ]; + try { + $providers = Yaml::parse($providers); + + $form['info'] = [ + '#type' => 'details', + '#open' => TRUE, + '#title' => $this->t('Provider details'), + '#weight' => 9999, + ]; + + $form['info']['table'] = [ + '#theme' => 'table', + '#header' => [ + ['data' => 'Id'], + ['data' => 'Label'], + ['data' => 'Redirect URI'], + ['data' => ''], + ], + '#rows' => array_map(fn ($id, $provider) => [ + 'data' => [ + ['data' => $id], + ['data' => $provider], + ['data' => Link::createFromRoute($provider, 'os2forms_nemlogin_openid_connect.openid_connect_authenticate', ['id' => $id])->toRenderable()], + ['data' => Link::createFromRoute($this->t('Edit'), 'os2web_nemlogin.auth_provider.' . $id)->toRenderable()], + ], + ], + array_keys($providers), + $providers + ), + ]; + } + catch (\Exception $e) { + // Ignore all exception. + } + return $form; } @@ -81,9 +117,8 @@ public function buildForm(array $form, FormStateInterface $form_state): array { * * @phpstan-param array $form */ - public function validateForm(array &$form, FormStateInterface $formState): void { - - $providers = $formState->getValue(self::PROVIDERS); + public function validateForm(array &$form, FormStateInterface $form_state): void { + $providers = $form_state->getValue(self::PROVIDERS); try { $values = Yaml::parse($providers); @@ -92,7 +127,7 @@ public function validateForm(array &$form, FormStateInterface $formState): void } foreach ($values as $name => $value) { if (!is_string($name)) { - $formState->setErrorByName( + $form_state->setErrorByName( self::PROVIDERS, $this->t('Name (@name) must be a string; found @type.', [ '@name' => $name, @@ -102,7 +137,7 @@ public function validateForm(array &$form, FormStateInterface $formState): void break; } if (!is_string($value)) { - $formState->setErrorByName( + $form_state->setErrorByName( self::PROVIDERS, $this->t('Value for “@name” must be a string; found @type.', [ '@name' => $name, @@ -114,7 +149,7 @@ public function validateForm(array &$form, FormStateInterface $formState): void } } catch (ParseException $exception) { - $formState->setErrorByName(self::PROVIDERS, $this->t('Invalid providers (@message)', ['@message' => $exception->getMessage()])); + $form_state->setErrorByName(self::PROVIDERS, $this->t('Invalid providers (@message)', ['@message' => $exception->getMessage()])); } } @@ -123,10 +158,9 @@ public function validateForm(array &$form, FormStateInterface $formState): void * * @phpstan-param array $form */ - public function submitForm(array &$form, FormStateInterface $formState): void { - + public function submitForm(array &$form, FormStateInterface $form_state): void { try { - $settings[self::PROVIDERS] = $formState->getValue(self::PROVIDERS); + $settings[self::PROVIDERS] = $form_state->getValue(self::PROVIDERS); $this->settings->setSettings($settings); $this->messenger()->addStatus($this->t('Settings saved')); diff --git a/src/Plugin/os2web/NemloginAuthProvider/OpenIDConnect.php b/src/Plugin/os2web/NemloginAuthProvider/OpenIDConnect.php index 5c9845a..6ecbaa7 100644 --- a/src/Plugin/os2web/NemloginAuthProvider/OpenIDConnect.php +++ b/src/Plugin/os2web/NemloginAuthProvider/OpenIDConnect.php @@ -32,10 +32,18 @@ class OpenIDConnect extends AuthProviderBase { use LoggerTrait; use LoggerAwareTrait; - /** - * Session name for storing OIDC user token. - */ - private const SESSION_TOKEN = 'os2forms_nemlogin_openid_connect.user_token'; + public const SESSION_TOKEN = 'os2forms_nemlogin_openid_connect.user_token'; + public const KEY = 'nemlogin_openid_connect_key'; + public const SECRET_PROVIDER = 'secret_provider'; + public const PROVIDER_TYPE_FORM = 'form'; + public const PROVIDER_TYPE_KEY = 'key'; + public const FETCH_ONCE = 'nemlogin_openid_connect_fetch_once'; + public const POST_LOGOUT_REDIRECT_URI = 'nemlogin_openid_connect_post_logout_redirect_uri'; + public const USER_CLAIMS = 'nemlogin_openid_connect_user_claims'; + public const DISCOVERY_URL = 'nemlogin_openid_connect_discovery_url'; + public const CLIENT_ID = 'nemlogin_openid_connect_client_id'; + public const CLIENT_SECRET = 'nemlogin_openid_connect_client_secret'; + /** * Fetch only mode flag. @@ -115,8 +123,8 @@ public static function create(ContainerInterface $container, array $configuratio * {@inheritdoc} */ public function isInitialized() { - $configuration = $configuration = $this->getConfiguration(); - if (!empty($configuration['nemlogin_openid_connect_discovery_url'])) { + $configuration = $this->getConfiguration(); + if (!empty($configuration[self::DISCOVERY_URL])) { return TRUE; } @@ -275,12 +283,14 @@ public function fetchValue($key) { */ public function defaultConfiguration() { return parent::defaultConfiguration() + [ - 'nemlogin_openid_connect_discovery_url' => '', - 'nemlogin_openid_connect_client_id' => '', - 'nemlogin_openid_connect_client_secret' => '', - 'nemlogin_openid_connect_fetch_once' => '', - 'nemlogin_openid_connect_post_logout_redirect_uri' => '', - 'nemlogin_openid_connect_user_claims' => '', + self::SECRET_PROVIDER => self::PROVIDER_TYPE_FORM, + self::DISCOVERY_URL => '', + self::CLIENT_ID => '', + self::CLIENT_SECRET => '', + self::KEY => '', + self::FETCH_ONCE => '', + self::POST_LOGOUT_REDIRECT_URI => '', + self::USER_CLAIMS => '', ]; } @@ -291,47 +301,99 @@ public function defaultConfiguration() { * @phpstan-return array */ public function buildConfigurationForm(array $form, FormStateInterface $form_state): array { - $form['nemlogin_openid_connect_discovery_url'] = [ + + $form[self::SECRET_PROVIDER] = [ + '#type' => 'select', + '#title' => $this->t('Provider'), + '#options' => [ + self::PROVIDER_TYPE_FORM => $this->t('Form'), + self::PROVIDER_TYPE_KEY => $this->t('Key'), + ], + '#default_value' => $this->configuration[self::SECRET_PROVIDER] ?? self::PROVIDER_TYPE_FORM, + ]; + + $form[self::DISCOVERY_URL] = [ '#type' => 'textfield', '#title' => $this->t('OpenID Connect Discovery url'), // Our urls are very long. '#maxlength' => 256, - '#required' => TRUE, - '#default_value' => $this->configuration['nemlogin_openid_connect_discovery_url'] ?? NULL, + '#default_value' => $this->configuration[self::DISCOVERY_URL] ?? NULL, '#description' => $this->t('OpenID Connect Discovery url (cf. https://swagger.io/docs/specification/authentication/openid-connect-discovery/)'), + '#states' => [ + 'visible' => [ + [':input[name="secret_provider"]' => ['value' => self::PROVIDER_TYPE_FORM]], + ], + 'required' => [ + [':input[name="secret_provider"]' => ['value' => self::PROVIDER_TYPE_FORM]], + ], + ], ]; - $form['nemlogin_openid_connect_client_id'] = [ + + $form[self::CLIENT_ID] = [ '#type' => 'textfield', '#title' => $this->t('Client id'), - '#required' => TRUE, - '#default_value' => $this->configuration['nemlogin_openid_connect_client_id'] ?? NULL, + '#default_value' => $this->configuration[self::CLIENT_ID] ?? NULL, + '#states' => [ + 'visible' => [ + [':input[name="secret_provider"]' => ['value' => self::PROVIDER_TYPE_FORM]], + ], + 'required' => [ + [':input[name="secret_provider"]' => ['value' => self::PROVIDER_TYPE_FORM]], + ], + ], ]; - $form['nemlogin_openid_connect_client_secret'] = [ + + $form[self::CLIENT_SECRET] = [ '#type' => 'textfield', '#title' => $this->t('Client secret'), - '#required' => TRUE, - '#default_value' => $this->configuration['nemlogin_openid_connect_client_secret'] ?? NULL, + '#default_value' => $this->configuration[self::CLIENT_SECRET] ?? NULL, + '#states' => [ + 'visible' => [ + [':input[name="secret_provider"]' => ['value' => self::PROVIDER_TYPE_FORM]], + ], + 'required' => [ + [':input[name="secret_provider"]' => ['value' => self::PROVIDER_TYPE_FORM]], + ], + ], ]; - $form['nemlogin_openid_connect_fetch_once'] = [ + + $form[self::KEY] = [ + '#type' => 'key_select', + '#key_filters' => [ + 'type' => 'os2web_key_oidc', + ], + '#title' => $this->t('Key'), + '#default_value' => $this->configuration[self::KEY] ?? NULL, + '#states' => [ + 'visible' => [ + [':input[name="secret_provider"]' => ['value' => self::PROVIDER_TYPE_KEY]], + ], + 'required' => [ + [':input[name="secret_provider"]' => ['value' => self::PROVIDER_TYPE_KEY]], + ], + ], + ]; + + $form[self::FETCH_ONCE] = [ '#type' => 'checkbox', '#title' => $this->t('Use fetch only mode.'), - '#default_value' => $this->configuration['nemlogin_openid_connect_fetch_once'] ?? FALSE, + '#default_value' => $this->configuration[self::FETCH_ONCE] ?? FALSE, '#description' => $this->t('User will be logged out immediately after login. User data will be removed from session after first retrieving'), ]; - $form['nemlogin_openid_connect_post_logout_redirect_uri'] = [ + $form[self::POST_LOGOUT_REDIRECT_URI] = [ '#type' => 'textfield', '#title' => $this->t('Post logout redirect url'), '#required' => TRUE, - '#default_value' => $this->configuration['nemlogin_openid_connect_post_logout_redirect_uri'] ?? NULL, + '#default_value' => $this->configuration[self::POST_LOGOUT_REDIRECT_URI] ?? NULL, '#description' => $this->t('Url to redirect to after logout. Can be an internal path, e.g. /node/87, or an external url, e.g. https://aarhus.dk'), ]; - $form['nemlogin_openid_connect_user_claims'] = [ + $form[self::USER_CLAIMS] = [ '#type' => 'textarea', '#title' => $this->t('User claims'), '#description' => $this->t('Describe user claims for use when comparing user values.
Each line must be on the form «claim»: «display name», e.g.

cpr: CPR-nummer
email: E-mailadresse
'), - '#default_value' => $this->configuration['nemlogin_openid_connect_user_claims'] ?? NULL, + '#default_value' => $this->configuration[self::USER_CLAIMS] ?? NULL, ]; return $form; @@ -343,27 +405,32 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta * @phpstan-param array $form */ public function validateConfigurationForm(array &$form, FormStateInterface $form_state): void { - $url = $form_state->getValue('nemlogin_openid_connect_discovery_url'); - if (!UrlHelper::isValid($url, TRUE)) { - $form_state->setErrorByName('nemlogin_openid_connect_discovery_url', $this->t('Url is not valid')); + if (self::PROVIDER_TYPE_FORM === $form_state->getValue(self::SECRET_PROVIDER)) { + + $discoveryUrl = $form_state->getValue(self::DISCOVERY_URL); + + if (!UrlHelper::isValid($discoveryUrl, TRUE)) { + $form_state->setErrorByName(self::DISCOVERY_URL, $this->t('Discovery url is not valid')); + } } - $url = $form_state->getValue('nemlogin_openid_connect_post_logout_redirect_uri'); + $redirectUrl = $form_state->getValue(self::POST_LOGOUT_REDIRECT_URI); + try { - UrlHelper::isExternal($url) ? Url::fromUri($url) : Url::fromUserInput($url); + UrlHelper::isExternal($redirectUrl) ? Url::fromUri($redirectUrl) : Url::fromUserInput($redirectUrl); } catch (\Exception $exception) { - $form_state->setErrorByName('nemlogin_openid_connect_post_logout_redirect_uri', $this->t('Post logout redirect url is not valid (@message)', ['@message' => $exception->getMessage()])); + $form_state->setErrorByName(self::POST_LOGOUT_REDIRECT_URI, $this->t('Post logout redirect url is not valid (@message)', ['@message' => $exception->getMessage()])); } - $claims = $form_state->getValue('nemlogin_openid_connect_user_claims'); + $claims = $form_state->getValue(self::USER_CLAIMS); try { $values = Yaml::parse($claims); foreach ($values as $name => $value) { if (!is_string($name)) { $form_state->setErrorByName( - 'nemlogin_openid_connect_user_claims', + self::USER_CLAIMS, $this->t('Name (@name) must be a string; found @type.', [ '@name' => $name, '@type' => gettype($name), @@ -373,7 +440,7 @@ public function validateConfigurationForm(array &$form, FormStateInterface $form } if (!is_string($value)) { $form_state->setErrorByName( - 'nemlogin_openid_connect_user_claims', + self::USER_CLAIMS, $this->t('Value for “@name” must be a string; found @type.', [ '@name' => $name, '@type' => gettype($value), @@ -384,7 +451,7 @@ public function validateConfigurationForm(array &$form, FormStateInterface $form } } catch (ParseException $exception) { - $form_state->setErrorByName('nemlogin_openid_connect_user_claims', $this->t('Invalid claims (@message)', ['@message' => $exception->getMessage()])); + $form_state->setErrorByName(self::USER_CLAIMS, $this->t('Invalid claims (@message)', ['@message' => $exception->getMessage()])); } } @@ -396,12 +463,14 @@ public function validateConfigurationForm(array &$form, FormStateInterface $form public function submitConfigurationForm(array &$form, FormStateInterface $form_state): void { $configuration = $this->getConfiguration(); - $configuration['nemlogin_openid_connect_discovery_url'] = $form_state->getValue('nemlogin_openid_connect_discovery_url'); - $configuration['nemlogin_openid_connect_client_id'] = $form_state->getValue('nemlogin_openid_connect_client_id'); - $configuration['nemlogin_openid_connect_client_secret'] = $form_state->getValue('nemlogin_openid_connect_client_secret'); - $configuration['nemlogin_openid_connect_fetch_once'] = $form_state->getValue('nemlogin_openid_connect_fetch_once'); - $configuration['nemlogin_openid_connect_post_logout_redirect_uri'] = $form_state->getValue('nemlogin_openid_connect_post_logout_redirect_uri'); - $configuration['nemlogin_openid_connect_user_claims'] = $form_state->getValue('nemlogin_openid_connect_user_claims'); + $configuration[self::SECRET_PROVIDER] = $form_state->getValue(self::SECRET_PROVIDER); + $configuration[self::DISCOVERY_URL] = $form_state->getValue(self::DISCOVERY_URL); + $configuration[self::CLIENT_ID] = $form_state->getValue(self::CLIENT_ID); + $configuration[self::CLIENT_SECRET] = $form_state->getValue(self::CLIENT_SECRET); + $configuration[self::KEY] = $form_state->getValue(self::KEY); + $configuration[self::FETCH_ONCE] = $form_state->getValue(self::FETCH_ONCE); + $configuration[self::POST_LOGOUT_REDIRECT_URI] = $form_state->getValue(self::POST_LOGOUT_REDIRECT_URI); + $configuration[self::USER_CLAIMS] = $form_state->getValue(self::USER_CLAIMS); $this->setConfiguration($configuration); }