Skip to content

Commit 4f18ae7

Browse files
committed
MenuHooks: fix saml login destination
1 parent 7b6001c commit 4f18ae7

1 file changed

Lines changed: 93 additions & 1 deletion

File tree

src/Hook/MenuHooks.php

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
namespace Drupal\oit\Hook;
44

55
use Drupal\Core\Hook\Attribute\Hook;
6+
use Drupal\Core\Routing\RedirectDestinationInterface;
7+
use Drupal\Core\Url;
68

79
/**
8-
* Hook implementations for responsive_menu performance tuning.
10+
* Hook implementations for menu performance tuning and link alterations.
911
*
1012
* The responsive_menu off-canvas and horizontal blocks force the entire menu
1113
* tree to expand and then run the generic "checkAccess" manipulator on every
@@ -30,6 +32,21 @@ class MenuHooks {
3032
'callable' => 'menu.default_tree_manipulators:checkNodeAccess',
3133
];
3234

35+
/**
36+
* The SAML login path that carries a destination back to the current page.
37+
*/
38+
protected const SAML_LOGIN_PATH = '/saml/login';
39+
40+
/**
41+
* Constructs a new MenuHooks object.
42+
*
43+
* @param \Drupal\Core\Routing\RedirectDestinationInterface $redirectDestination
44+
* The redirect destination helper.
45+
*/
46+
public function __construct(
47+
protected RedirectDestinationInterface $redirectDestination,
48+
) {}
49+
3350
/**
3451
* Prepends the node-access manipulator if it is not already present.
3552
*
@@ -61,4 +78,79 @@ public function horizontalManipulatorsAlter(array &$manipulators): void {
6178
$this->addNodeAccessManipulator($manipulators);
6279
}
6380

81+
/**
82+
* Implements hook_preprocess_menu().
83+
*
84+
* Adds a "destination" query parameter to the SAML login link so that users
85+
* are returned to the page they were on instead of the front page. samlauth
86+
* honours the parameter and rejects external values.
87+
*
88+
* @see \Drupal\samlauth\Controller\SamlController::getDestinationUrl()
89+
*/
90+
#[Hook('preprocess_menu')]
91+
public function preprocessMenu(array &$variables): void {
92+
if (($variables['menu_name'] ?? NULL) !== 'account') {
93+
return;
94+
}
95+
$destination = $this->redirectDestination->get();
96+
// Never send the user back to the login flow itself.
97+
if (!$destination || str_starts_with($destination, self::SAML_LOGIN_PATH)) {
98+
return;
99+
}
100+
$this->addLoginDestination($variables['items'], $destination);
101+
}
102+
103+
/**
104+
* Implements hook_block_build_alter().
105+
*
106+
* The account menu now renders a per-page destination, so its render cache
107+
* must vary by the current URL.
108+
*/
109+
#[Hook('block_build_alter')]
110+
public function blockBuildAlter(array &$build, $block): void {
111+
if (!str_starts_with($block->getPluginId(), 'system_menu_block:account')) {
112+
return;
113+
}
114+
$build['#cache']['contexts'][] = 'url.path';
115+
$build['#cache']['contexts'][] = 'url.query_args';
116+
}
117+
118+
/**
119+
* Recursively adds the destination query to any SAML login link.
120+
*
121+
* @param array $items
122+
* The menu items, passed by reference.
123+
* @param string $destination
124+
* The destination path to return to after login.
125+
*/
126+
protected function addLoginDestination(array &$items, string $destination): void {
127+
foreach ($items as &$item) {
128+
$url = $item['url'] ?? NULL;
129+
if ($url instanceof Url && $this->isSamlLoginUrl($url)) {
130+
$query = $url->getOption('query') ?: [];
131+
$query['destination'] = $destination;
132+
$url->setOption('query', $query);
133+
}
134+
if (!empty($item['below'])) {
135+
$this->addLoginDestination($item['below'], $destination);
136+
}
137+
}
138+
}
139+
140+
/**
141+
* Determines whether a URL points at the SAML login route.
142+
*
143+
* @param \Drupal\Core\Url $url
144+
* The menu link URL.
145+
*
146+
* @return bool
147+
* TRUE if the URL is the SAML login link.
148+
*/
149+
protected function isSamlLoginUrl(Url $url): bool {
150+
if ($url->isRouted()) {
151+
return $url->getRouteName() === 'samlauth.saml_controller_login';
152+
}
153+
return $url->isExternal() === FALSE && $url->toString() === self::SAML_LOGIN_PATH;
154+
}
155+
64156
}

0 commit comments

Comments
 (0)