From e4720652d7119d0590f06cd7eee82b0e959154f0 Mon Sep 17 00:00:00 2001 From: Peter Lieverdink Date: Thu, 12 Sep 2024 10:10:41 +1000 Subject: [PATCH 01/11] chore: Ensure the config specifies we are using Ansible. --- config/openid_connect.client.entraid.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/openid_connect.client.entraid.yml b/config/openid_connect.client.entraid.yml index 7585a2f15..173a64b97 100644 --- a/config/openid_connect.client.entraid.yml +++ b/config/openid_connect.client.entraid.yml @@ -8,8 +8,8 @@ id: entraid label: 'Entra ID (UNITE ID)' plugin: windows_aad settings: - client_id: entraid_client_id - client_secret: entraid_client_secret + client_id: ansible_managed_entraid_client_id + client_secret: ansible_managed_entraid_client_secret authorization_endpoint_wa: '' token_endpoint_wa: '' userinfo_endpoint_wa: '' From c9a649e33e6122d46303d1417a6a870fa5598125 Mon Sep 17 00:00:00 2001 From: Peter Lieverdink Date: Fri, 13 Sep 2024 14:07:25 +1000 Subject: [PATCH 02/11] feat: Keep the OIDC login block for entra id. Refs: OPS-10834 --- ...mon_design_subtheme_openidconnectlogin.yml | 33 +++++++++++++++++++ config/openid_connect.client.entraid.yml | 4 +-- 2 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 config/block.block.common_design_subtheme_openidconnectlogin.yml diff --git a/config/block.block.common_design_subtheme_openidconnectlogin.yml b/config/block.block.common_design_subtheme_openidconnectlogin.yml new file mode 100644 index 000000000..67bd9fb4f --- /dev/null +++ b/config/block.block.common_design_subtheme_openidconnectlogin.yml @@ -0,0 +1,33 @@ +uuid: 767a48e1-f879-460c-9fc8-9b2b9c3e72d0 +langcode: en +status: true +dependencies: + module: + - openid_connect + - system + - user + theme: + - common_design_subtheme +id: common_design_subtheme_openidconnectlogin +theme: common_design_subtheme +region: content +weight: -3 +provider: null +plugin: openid_connect_login +settings: + id: openid_connect_login + label: 'Unite ID Login' + label_display: visible + provider: openid_connect +visibility: + request_path: + id: request_path + negate: false + pages: /user/login + user_role: + id: user_role + negate: false + context_mapping: + user: '@user.current_user_context:current_user' + roles: + anonymous: anonymous diff --git a/config/openid_connect.client.entraid.yml b/config/openid_connect.client.entraid.yml index 173a64b97..7585a2f15 100644 --- a/config/openid_connect.client.entraid.yml +++ b/config/openid_connect.client.entraid.yml @@ -8,8 +8,8 @@ id: entraid label: 'Entra ID (UNITE ID)' plugin: windows_aad settings: - client_id: ansible_managed_entraid_client_id - client_secret: ansible_managed_entraid_client_secret + client_id: entraid_client_id + client_secret: entraid_client_secret authorization_endpoint_wa: '' token_endpoint_wa: '' userinfo_endpoint_wa: '' From ed69b47e5c0f94423471f30e8830d408b11c23ef Mon Sep 17 00:00:00 2001 From: Peter Lieverdink Date: Tue, 24 Sep 2024 10:15:04 +1000 Subject: [PATCH 03/11] fix: Remove the OIDC login block, as it breaks things when the module is not enabled! --- ...mon_design_subtheme_openidconnectlogin.yml | 33 ------------------- 1 file changed, 33 deletions(-) delete mode 100644 config/block.block.common_design_subtheme_openidconnectlogin.yml diff --git a/config/block.block.common_design_subtheme_openidconnectlogin.yml b/config/block.block.common_design_subtheme_openidconnectlogin.yml deleted file mode 100644 index 67bd9fb4f..000000000 --- a/config/block.block.common_design_subtheme_openidconnectlogin.yml +++ /dev/null @@ -1,33 +0,0 @@ -uuid: 767a48e1-f879-460c-9fc8-9b2b9c3e72d0 -langcode: en -status: true -dependencies: - module: - - openid_connect - - system - - user - theme: - - common_design_subtheme -id: common_design_subtheme_openidconnectlogin -theme: common_design_subtheme -region: content -weight: -3 -provider: null -plugin: openid_connect_login -settings: - id: openid_connect_login - label: 'Unite ID Login' - label_display: visible - provider: openid_connect -visibility: - request_path: - id: request_path - negate: false - pages: /user/login - user_role: - id: user_role - negate: false - context_mapping: - user: '@user.current_user_context:current_user' - roles: - anonymous: anonymous From 95f182d54c2dfb2777ff5b6aecf0bdfdac3bbf4a Mon Sep 17 00:00:00 2001 From: Peter Lieverdink Date: Tue, 24 Sep 2024 10:19:18 +1000 Subject: [PATCH 04/11] feat: Provide an Entra ID login redirect, so editors can easily add it to page content. Refs: OPS-10834 --- config/core.extension.yml | 1 + .../custom/reliefweb_entraid/README.md | 6 +++ .../reliefweb_entraid.info.yml | 7 +++ .../reliefweb_entraid.module | 6 +++ .../reliefweb_entraid.routing.yml | 7 +++ .../src/Controller/AuthController.php | 53 +++++++++++++++++++ 6 files changed, 80 insertions(+) create mode 100644 html/modules/custom/reliefweb_entraid/README.md create mode 100755 html/modules/custom/reliefweb_entraid/reliefweb_entraid.info.yml create mode 100755 html/modules/custom/reliefweb_entraid/reliefweb_entraid.module create mode 100644 html/modules/custom/reliefweb_entraid/reliefweb_entraid.routing.yml create mode 100644 html/modules/custom/reliefweb_entraid/src/Controller/AuthController.php diff --git a/config/core.extension.yml b/config/core.extension.yml index e34122ade..a84ddd3f4 100644 --- a/config/core.extension.yml +++ b/config/core.extension.yml @@ -76,6 +76,7 @@ module: reliefweb_disaster_map: 0 reliefweb_dsr: 0 reliefweb_entities: 0 + reliefweb_entraid: 0 reliefweb_fields: 0 reliefweb_files: 0 reliefweb_form: 0 diff --git a/html/modules/custom/reliefweb_entraid/README.md b/html/modules/custom/reliefweb_entraid/README.md new file mode 100644 index 000000000..80749267f --- /dev/null +++ b/html/modules/custom/reliefweb_entraid/README.md @@ -0,0 +1,6 @@ +Reliefweb Entra ID +============ + +This module provides user authentication tweaks for Entra ID + +* Provide a `/user/login/entraid` callback to redirect to the Entra ID login workflow. diff --git a/html/modules/custom/reliefweb_entraid/reliefweb_entraid.info.yml b/html/modules/custom/reliefweb_entraid/reliefweb_entraid.info.yml new file mode 100755 index 000000000..8e62e5156 --- /dev/null +++ b/html/modules/custom/reliefweb_entraid/reliefweb_entraid.info.yml @@ -0,0 +1,7 @@ +name: 'Relieweb Entra ID' +description: Tweaks for Entra ID and/or Azure B2C. +type: module +core_version_requirement: ^9 || ^10 +package: reliefweb +dependencies: + - openid_connect_windows_aad:openid_connect_windows_aad diff --git a/html/modules/custom/reliefweb_entraid/reliefweb_entraid.module b/html/modules/custom/reliefweb_entraid/reliefweb_entraid.module new file mode 100755 index 000000000..f4a9da63c --- /dev/null +++ b/html/modules/custom/reliefweb_entraid/reliefweb_entraid.module @@ -0,0 +1,6 @@ +entityTypeManager = $container->get('entity_type.manager'); + $this->claims = $container->get('openid_connect.claims'); + $this->session = $container->get('openid_connect.session'); + + $client = $this->entityTypeManager->getStorage('openid_connect_client')->loadByProperties(['id' => 'entraid'])['entraid']; + $plugin = $client->getPlugin(); + $scopes = $this->claims->getScopes($plugin); + $this->session->saveOp('login'); + $response = $plugin->authorize($scopes); + + return $response->send(); + } + +} From 62960d8cef228601b1d4b82d12148b3ea429b08a Mon Sep 17 00:00:00 2001 From: Peter Lieverdink Date: Tue, 24 Sep 2024 10:20:27 +1000 Subject: [PATCH 05/11] chore: Relabel the entraid OIDC client as Unite ID for UX good. Refs: OPS-10834 --- config/openid_connect.client.entraid.yml | 2 +- .../custom/reliefweb_entraid/reliefweb_entraid.routing.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/openid_connect.client.entraid.yml b/config/openid_connect.client.entraid.yml index 7585a2f15..c85519eeb 100644 --- a/config/openid_connect.client.entraid.yml +++ b/config/openid_connect.client.entraid.yml @@ -5,7 +5,7 @@ dependencies: module: - openid_connect_windows_aad id: entraid -label: 'Entra ID (UNITE ID)' +label: 'Unite ID' plugin: windows_aad settings: client_id: entraid_client_id diff --git a/html/modules/custom/reliefweb_entraid/reliefweb_entraid.routing.yml b/html/modules/custom/reliefweb_entraid/reliefweb_entraid.routing.yml index c31314992..93872e877 100644 --- a/html/modules/custom/reliefweb_entraid/reliefweb_entraid.routing.yml +++ b/html/modules/custom/reliefweb_entraid/reliefweb_entraid.routing.yml @@ -2,6 +2,6 @@ reliefweb_entraid.login: path: '/user/login/entraid' defaults: _controller: '\Drupal\reliefweb_entraid\Controller\AuthController::redirectLogin' - _title: 'Login new account' + _title: 'Login with Unite ID' requirements: _user_is_logged_in: 'FALSE' From f5632eb6d69562ad8831bb562a3287f650f562f3 Mon Sep 17 00:00:00 2001 From: Peter Lieverdink Date: Tue, 24 Sep 2024 11:21:23 +1000 Subject: [PATCH 06/11] tests: This will probably fail because unconfigured, but let's try eh? --- .../ReliefwebEntraidTestLogin.php | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 html/modules/custom/reliefweb_entraid/tests/src/ExistingSite/ReliefwebEntraidTestLogin.php diff --git a/html/modules/custom/reliefweb_entraid/tests/src/ExistingSite/ReliefwebEntraidTestLogin.php b/html/modules/custom/reliefweb_entraid/tests/src/ExistingSite/ReliefwebEntraidTestLogin.php new file mode 100644 index 000000000..f00b10c8d --- /dev/null +++ b/html/modules/custom/reliefweb_entraid/tests/src/ExistingSite/ReliefwebEntraidTestLogin.php @@ -0,0 +1,23 @@ +drupalGet('user/login/entraid'); + $this->assertSession()->statusCodeEquals(301); + } + +} From 028e56c0bc82919c606e1fc11262a9e45b7b0d39 Mon Sep 17 00:00:00 2001 From: orakili Date: Tue, 24 Sep 2024 13:45:26 +0000 Subject: [PATCH 07/11] chore: return a 404 for the entraid login redirect if not configure properly Refs: OPS-10384 --- .../src/Controller/AuthController.php | 85 ++++++++++++++----- 1 file changed, 63 insertions(+), 22 deletions(-) diff --git a/html/modules/custom/reliefweb_entraid/src/Controller/AuthController.php b/html/modules/custom/reliefweb_entraid/src/Controller/AuthController.php index 96f19eed8..58c184f27 100644 --- a/html/modules/custom/reliefweb_entraid/src/Controller/AuthController.php +++ b/html/modules/custom/reliefweb_entraid/src/Controller/AuthController.php @@ -2,52 +2,93 @@ namespace Drupal\reliefweb_entraid\Controller; +use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Controller\ControllerBase; +use Drupal\Core\DependencyInjection\ContainerInjectionInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Http\Exception\CacheableNotFoundHttpException; +use Drupal\openid_connect\OpenIDConnectClaims; +use Drupal\openid_connect\OpenIDConnectSessionInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Returns responses for OpenID Connect Windows AAD module routes. */ -class AuthController extends ControllerBase { - - /** - * The entity type manager. - * - * @var \Drupal\Core\Entity\EntityTypeManagerInterface - */ - protected $entityTypeManager; +class AuthController extends ControllerBase implements ContainerInjectionInterface { /** * The OpenID Connect claims. * * @var \Drupal\openid_connect\OpenIDConnectClaims */ - protected $claims; + protected $openIdConnectClaims; /** * The OpenID Connect session service. * * @var \Drupal\openid_connect\OpenIDConnectSessionInterface */ - protected $session; + protected $openIdConnectSession; + + /** + * Constructs a new AuthController object. + * + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager. + * @param \Drupal\openid_connect\OpenIDConnectClaims $open_id_connect_claims + * The OpenID Connect claims. + * @param \Drupal\openid_connect\OpenIDConnectSessionInterface $open_id_connect_session + * The OpenID Connect session service. + */ + public function __construct( + EntityTypeManagerInterface $entity_type_manager, + OpenIDConnectClaims $open_id_connect_claims, + OpenIDConnectSessionInterface $open_id_connect_session, + ) { + $this->entityTypeManager = $entity_type_manager; + $this->openIdConnectClaims = $open_id_connect_claims; + $this->openIdConnectSession = $open_id_connect_session; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('entity_type.manager'), + $container->get('openid_connect.claims'), + $container->get('openid_connect.session') + ); + } /** - * Redirect the user login callback. + * Redirect to the user login callback. */ public function redirectLogin() { - // @codingStandardsIgnoreLine - $container = \Drupal::getContainer(); + try { + $client_entities = $this->entityTypeManager() + ->getStorage('openid_connect_client') + ->loadByProperties(['id' => 'entraid']); - $this->entityTypeManager = $container->get('entity_type.manager'); - $this->claims = $container->get('openid_connect.claims'); - $this->session = $container->get('openid_connect.session'); + if (!isset($client_entities['entraid'])) { + throw new \Exception(); + } - $client = $this->entityTypeManager->getStorage('openid_connect_client')->loadByProperties(['id' => 'entraid'])['entraid']; - $plugin = $client->getPlugin(); - $scopes = $this->claims->getScopes($plugin); - $this->session->saveOp('login'); - $response = $plugin->authorize($scopes); + $client = $client_entities['entraid']; + $plugin = $client->getPlugin(); + $scopes = $this->openIdConnectClaims->getScopes($plugin); + $this->openIdConnectSession->saveOp('login'); + $response = $plugin->authorize($scopes); - return $response->send(); + return $response->send(); + } + catch (\Exception $exception) { + print_r([$exception->getMessage()]); + $config = $this->config('openid_connect.client.entraid'); + $cacheable_metadata = new CacheableMetadata(); + $cacheable_metadata->addCacheableDependency($config); + throw new CacheableNotFoundHttpException($cacheable_metadata); + } } } From 571d6a29ecbf75c8fa07e258eeb4490dd0559d00 Mon Sep 17 00:00:00 2001 From: orakili Date: Tue, 24 Sep 2024 13:46:21 +0000 Subject: [PATCH 08/11] chore: update reliefweb entraid tests to check 404 and redirection Refs: OPS-10834 --- .../ReliefwebEntraidLoginTest.php | 82 +++++++++++++++++++ .../ReliefwebEntraidTestLogin.php | 23 ------ 2 files changed, 82 insertions(+), 23 deletions(-) create mode 100644 html/modules/custom/reliefweb_entraid/tests/src/ExistingSite/ReliefwebEntraidLoginTest.php delete mode 100644 html/modules/custom/reliefweb_entraid/tests/src/ExistingSite/ReliefwebEntraidTestLogin.php diff --git a/html/modules/custom/reliefweb_entraid/tests/src/ExistingSite/ReliefwebEntraidLoginTest.php b/html/modules/custom/reliefweb_entraid/tests/src/ExistingSite/ReliefwebEntraidLoginTest.php new file mode 100644 index 000000000..05e2546fb --- /dev/null +++ b/html/modules/custom/reliefweb_entraid/tests/src/ExistingSite/ReliefwebEntraidLoginTest.php @@ -0,0 +1,82 @@ +entraIdConfigData = $this->container + ->get('config.factory') + ->getEditable('openid_connect.client.entraid') + ->get(); + } + + /** + * {@inheritdoc} + */ + protected function tearDown(): void { + // Restore the original config data. + $this->container + ->get('config.factory') + ->getEditable('openid_connect.client.entraid') + ->setData($this->entraIdConfigData) + ->save(); + + parent::tearDown(); + } + + /** + * @covers ::redirectLogin() + */ + public function testRedirectLogin() { + // Get the EntraID configuration. + $entraid_config = $this->container + ->get('config.factory') + ->getEditable('openid_connect.client.entraid'); + + // Empty the enpoints to test the redirection when the config is not set. + $data = $entraid_config->get(); + $data['settings']['authorization_endpoint_wa'] = ''; + $data['settings']['token_endpoint_wa'] = ''; + $data['settings']['iss_allowed_domains'] = ''; + $entraid_config->setData($data)->save(); + + // The incomplete config will results in an exception and 404 response + // will be returned. + $this->drupalGet('/user/login/entraid'); + $this->assertSession()->statusCodeEquals(404); + + // Set the endpoints. The don't exists and will, on purpose return a 503. + $data = $entraid_config->get(); + $data['settings']['authorization_endpoint_wa'] = 'http://test.test/common/oauth2/v2.0/authorize'; + $data['settings']['token_endpoint_wa'] = 'http://test.test/common/oauth2/v2.0/token'; + $data['settings']['iss_allowed_domains'] = 'http://test.test/{tenantid}/v2.0'; + $entraid_config->setData($data)->save(); + + // If the redirection works, a 503 will be returned because the EntraID + // endpoints do not exist. + $this->drupalGet('/user/login/entraid'); + $this->assertSession()->statusCodeEquals(503); + } + +} diff --git a/html/modules/custom/reliefweb_entraid/tests/src/ExistingSite/ReliefwebEntraidTestLogin.php b/html/modules/custom/reliefweb_entraid/tests/src/ExistingSite/ReliefwebEntraidTestLogin.php deleted file mode 100644 index f00b10c8d..000000000 --- a/html/modules/custom/reliefweb_entraid/tests/src/ExistingSite/ReliefwebEntraidTestLogin.php +++ /dev/null @@ -1,23 +0,0 @@ -drupalGet('user/login/entraid'); - $this->assertSession()->statusCodeEquals(301); - } - -} From 3be7304daee1d4048af6417fb36fd490510aad80 Mon Sep 17 00:00:00 2001 From: orakili Date: Tue, 24 Sep 2024 13:47:00 +0000 Subject: [PATCH 09/11] chore: style all login links Refs: OPS-10834 --- .../components/rw-document/rw-document.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/html/themes/custom/common_design_subtheme/components/rw-document/rw-document.css b/html/themes/custom/common_design_subtheme/components/rw-document/rw-document.css index 70ae76c46..a17aaef51 100644 --- a/html/themes/custom/common_design_subtheme/components/rw-document/rw-document.css +++ b/html/themes/custom/common_design_subtheme/components/rw-document/rw-document.css @@ -126,7 +126,7 @@ /* RW-599: Style direct links to log in with HID on book pages to look like * a button like it was on the RW7 site. */ -.rw-document.rw-article--book .rw-article__content a[href*="/user/login/hid"] { +.rw-document.rw-article--book .rw-article__content a[href^="/user/login/"] { display: inline-block; margin: 1rem 0 0 0; padding: 12px 18px; @@ -136,8 +136,8 @@ background: var(--cd-reliefweb-brand-blue--dark); font-weight: bold; } -.rw-document.rw-article--book .rw-article__content a[href*="/user/login/hid"]:hover, -.rw-document.rw-article--book .rw-article__content a[href*="/user/login/hid"]:focus, -.rw-document.rw-article--book .rw-article__content a[href*="/user/login/hid"]:active { +.rw-document.rw-article--book .rw-article__content a[href^="/user/login/"]:hover, +.rw-document.rw-article--book .rw-article__content a[href^="/user/login/"]:focus, +.rw-document.rw-article--book .rw-article__content a[href^="/user/login/"]:active { background: var(--cd-reliefweb-brand-red--dark); } From 06629bc247c27f6849465ef16c510c2fe6ccb9ae Mon Sep 17 00:00:00 2001 From: orakili Date: Tue, 24 Sep 2024 13:49:40 +0000 Subject: [PATCH 10/11] chore: remove debug code... Refs: OPS-10834 --- .../custom/reliefweb_entraid/src/Controller/AuthController.php | 1 - 1 file changed, 1 deletion(-) diff --git a/html/modules/custom/reliefweb_entraid/src/Controller/AuthController.php b/html/modules/custom/reliefweb_entraid/src/Controller/AuthController.php index 58c184f27..a72972c4e 100644 --- a/html/modules/custom/reliefweb_entraid/src/Controller/AuthController.php +++ b/html/modules/custom/reliefweb_entraid/src/Controller/AuthController.php @@ -83,7 +83,6 @@ public function redirectLogin() { return $response->send(); } catch (\Exception $exception) { - print_r([$exception->getMessage()]); $config = $this->config('openid_connect.client.entraid'); $cacheable_metadata = new CacheableMetadata(); $cacheable_metadata->addCacheableDependency($config); From 9dd9c12c0877059a608ca3d598c562cfb128937b Mon Sep 17 00:00:00 2001 From: orakili Date: Tue, 24 Sep 2024 21:56:21 +0000 Subject: [PATCH 11/11] fix: entraid tests Refs: OPS-10834 --- .../ExistingSite/ReliefwebEntraidLoginTest.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/html/modules/custom/reliefweb_entraid/tests/src/ExistingSite/ReliefwebEntraidLoginTest.php b/html/modules/custom/reliefweb_entraid/tests/src/ExistingSite/ReliefwebEntraidLoginTest.php index 05e2546fb..4ba5e1e9c 100644 --- a/html/modules/custom/reliefweb_entraid/tests/src/ExistingSite/ReliefwebEntraidLoginTest.php +++ b/html/modules/custom/reliefweb_entraid/tests/src/ExistingSite/ReliefwebEntraidLoginTest.php @@ -66,17 +66,18 @@ public function testRedirectLogin() { $this->drupalGet('/user/login/entraid'); $this->assertSession()->statusCodeEquals(404); - // Set the endpoints. The don't exists and will, on purpose return a 503. + // Set the endpoints. We just point at the robots.txt as we know it exists + // and so, if the reponse status code in 200, then the redirection worked. $data = $entraid_config->get(); - $data['settings']['authorization_endpoint_wa'] = 'http://test.test/common/oauth2/v2.0/authorize'; - $data['settings']['token_endpoint_wa'] = 'http://test.test/common/oauth2/v2.0/token'; - $data['settings']['iss_allowed_domains'] = 'http://test.test/{tenantid}/v2.0'; + $data['settings']['authorization_endpoint_wa'] = 'http://localhost/robots.txt'; + $data['settings']['token_endpoint_wa'] = 'http://localhost/robots.txt'; + $data['settings']['iss_allowed_domains'] = 'http://localhost/robots.txt'; $entraid_config->setData($data)->save(); - // If the redirection works, a 503 will be returned because the EntraID - // endpoints do not exist. + // If the redirection works, a 200 will be returned. $this->drupalGet('/user/login/entraid'); - $this->assertSession()->statusCodeEquals(503); + $this->assertSession()->statusCodeEquals(200); + $this->assertStringContainsString('Disallow:', $this->getSession()->getPage()->getContent()); } }