Mercurial > hg > isophonics-drupal-site
comparison core/modules/user/src/Controller/UserController.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | c2387f117808 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\user\Controller; | |
4 | |
5 use Drupal\Component\Utility\Crypt; | |
6 use Drupal\Component\Utility\Xss; | |
7 use Drupal\Core\Controller\ControllerBase; | |
8 use Drupal\Core\Datetime\DateFormatterInterface; | |
9 use Drupal\user\Form\UserPasswordResetForm; | |
10 use Drupal\user\UserDataInterface; | |
11 use Drupal\user\UserInterface; | |
12 use Drupal\user\UserStorageInterface; | |
13 use Psr\Log\LoggerInterface; | |
14 use Symfony\Component\DependencyInjection\ContainerInterface; | |
15 use Symfony\Component\HttpFoundation\Request; | |
16 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; | |
17 | |
18 /** | |
19 * Controller routines for user routes. | |
20 */ | |
21 class UserController extends ControllerBase { | |
22 | |
23 /** | |
24 * The date formatter service. | |
25 * | |
26 * @var \Drupal\Core\Datetime\DateFormatterInterface | |
27 */ | |
28 protected $dateFormatter; | |
29 | |
30 /** | |
31 * The user storage. | |
32 * | |
33 * @var \Drupal\user\UserStorageInterface | |
34 */ | |
35 protected $userStorage; | |
36 | |
37 /** | |
38 * The user data service. | |
39 * | |
40 * @var \Drupal\user\UserDataInterface | |
41 */ | |
42 protected $userData; | |
43 | |
44 /** | |
45 * A logger instance. | |
46 * | |
47 * @var \Psr\Log\LoggerInterface | |
48 */ | |
49 protected $logger; | |
50 | |
51 /** | |
52 * Constructs a UserController object. | |
53 * | |
54 * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter | |
55 * The date formatter service. | |
56 * @param \Drupal\user\UserStorageInterface $user_storage | |
57 * The user storage. | |
58 * @param \Drupal\user\UserDataInterface $user_data | |
59 * The user data service. | |
60 * @param \Psr\Log\LoggerInterface $logger | |
61 * A logger instance. | |
62 */ | |
63 public function __construct(DateFormatterInterface $date_formatter, UserStorageInterface $user_storage, UserDataInterface $user_data, LoggerInterface $logger) { | |
64 $this->dateFormatter = $date_formatter; | |
65 $this->userStorage = $user_storage; | |
66 $this->userData = $user_data; | |
67 $this->logger = $logger; | |
68 } | |
69 | |
70 /** | |
71 * {@inheritdoc} | |
72 */ | |
73 public static function create(ContainerInterface $container) { | |
74 return new static( | |
75 $container->get('date.formatter'), | |
76 $container->get('entity.manager')->getStorage('user'), | |
77 $container->get('user.data'), | |
78 $container->get('logger.factory')->get('user') | |
79 ); | |
80 } | |
81 | |
82 /** | |
83 * Redirects to the user password reset form. | |
84 * | |
85 * In order to never disclose a reset link via a referrer header this | |
86 * controller must always return a redirect response. | |
87 * | |
88 * @param \Symfony\Component\HttpFoundation\Request $request | |
89 * The request. | |
90 * @param int $uid | |
91 * User ID of the user requesting reset. | |
92 * @param int $timestamp | |
93 * The current timestamp. | |
94 * @param string $hash | |
95 * Login link hash. | |
96 * | |
97 * @return \Symfony\Component\HttpFoundation\RedirectResponse | |
98 * The redirect response. | |
99 */ | |
100 public function resetPass(Request $request, $uid, $timestamp, $hash) { | |
101 $account = $this->currentUser(); | |
102 // When processing the one-time login link, we have to make sure that a user | |
103 // isn't already logged in. | |
104 if ($account->isAuthenticated()) { | |
105 // The current user is already logged in. | |
106 if ($account->id() == $uid) { | |
107 user_logout(); | |
108 // We need to begin the redirect process again because logging out will | |
109 // destroy the session. | |
110 return $this->redirect( | |
111 'user.reset', | |
112 [ | |
113 'uid' => $uid, | |
114 'timestamp' => $timestamp, | |
115 'hash' => $hash, | |
116 ] | |
117 ); | |
118 } | |
119 // A different user is already logged in on the computer. | |
120 else { | |
121 /** @var \Drupal\user\UserInterface $reset_link_user */ | |
122 if ($reset_link_user = $this->userStorage->load($uid)) { | |
123 drupal_set_message($this->t('Another user (%other_user) is already logged into the site on this computer, but you tried to use a one-time link for user %resetting_user. Please <a href=":logout">log out</a> and try using the link again.', | |
124 ['%other_user' => $account->getUsername(), '%resetting_user' => $reset_link_user->getUsername(), ':logout' => $this->url('user.logout')]), 'warning'); | |
125 } | |
126 else { | |
127 // Invalid one-time link specifies an unknown user. | |
128 drupal_set_message($this->t('The one-time login link you clicked is invalid.'), 'error'); | |
129 } | |
130 return $this->redirect('<front>'); | |
131 } | |
132 } | |
133 | |
134 $session = $request->getSession(); | |
135 $session->set('pass_reset_hash', $hash); | |
136 $session->set('pass_reset_timeout', $timestamp); | |
137 return $this->redirect( | |
138 'user.reset.form', | |
139 ['uid' => $uid] | |
140 ); | |
141 } | |
142 | |
143 /** | |
144 * Returns the user password reset form. | |
145 * | |
146 * @param \Symfony\Component\HttpFoundation\Request $request | |
147 * The request. | |
148 * @param int $uid | |
149 * User ID of the user requesting reset. | |
150 * | |
151 * @return array|\Symfony\Component\HttpFoundation\RedirectResponse | |
152 * The form structure or a redirect response. | |
153 * | |
154 * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException | |
155 * If the pass_reset_timeout or pass_reset_hash are not available in the | |
156 * session. Or if $uid is for a blocked user or invalid user ID. | |
157 */ | |
158 public function getResetPassForm(Request $request, $uid) { | |
159 $session = $request->getSession(); | |
160 $timestamp = $session->get('pass_reset_timeout'); | |
161 $hash = $session->get('pass_reset_hash'); | |
162 // As soon as the session variables are used they are removed to prevent the | |
163 // hash and timestamp from being leaked unexpectedly. This could occur if | |
164 // the user does not click on the log in button on the form. | |
165 $session->remove('pass_reset_timeout'); | |
166 $session->remove('pass_reset_hash'); | |
167 if (!$hash || !$timestamp) { | |
168 throw new AccessDeniedHttpException(); | |
169 } | |
170 | |
171 /** @var \Drupal\user\UserInterface $user */ | |
172 $user = $this->userStorage->load($uid); | |
173 if ($user === NULL || !$user->isActive()) { | |
174 // Blocked or invalid user ID, so deny access. The parameters will be in | |
175 // the watchdog's URL for the administrator to check. | |
176 throw new AccessDeniedHttpException(); | |
177 } | |
178 | |
179 // Time out, in seconds, until login URL expires. | |
180 $timeout = $this->config('user.settings')->get('password_reset_timeout'); | |
181 | |
182 $expiration_date = $user->getLastLoginTime() ? $this->dateFormatter->format($timestamp + $timeout) : NULL; | |
183 return $this->formBuilder()->getForm(UserPasswordResetForm::class, $user, $expiration_date, $timestamp, $hash); | |
184 } | |
185 | |
186 /** | |
187 * Validates user, hash, and timestamp; logs the user in if correct. | |
188 * | |
189 * @param int $uid | |
190 * User ID of the user requesting reset. | |
191 * @param int $timestamp | |
192 * The current timestamp. | |
193 * @param string $hash | |
194 * Login link hash. | |
195 * | |
196 * @return \Symfony\Component\HttpFoundation\RedirectResponse | |
197 * Returns a redirect to the user edit form if the information is correct. | |
198 * If the information is incorrect redirects to 'user.pass' route with a | |
199 * message for the user. | |
200 * | |
201 * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException | |
202 * If $uid is for a blocked user or invalid user ID. | |
203 */ | |
204 public function resetPassLogin($uid, $timestamp, $hash) { | |
205 // The current user is not logged in, so check the parameters. | |
206 $current = REQUEST_TIME; | |
207 /** @var \Drupal\user\UserInterface $user */ | |
208 $user = $this->userStorage->load($uid); | |
209 | |
210 // Verify that the user exists and is active. | |
211 if ($user === NULL || !$user->isActive()) { | |
212 // Blocked or invalid user ID, so deny access. The parameters will be in | |
213 // the watchdog's URL for the administrator to check. | |
214 throw new AccessDeniedHttpException(); | |
215 } | |
216 | |
217 // Time out, in seconds, until login URL expires. | |
218 $timeout = $this->config('user.settings')->get('password_reset_timeout'); | |
219 // No time out for first time login. | |
220 if ($user->getLastLoginTime() && $current - $timestamp > $timeout) { | |
221 drupal_set_message($this->t('You have tried to use a one-time login link that has expired. Please request a new one using the form below.'), 'error'); | |
222 return $this->redirect('user.pass'); | |
223 } | |
224 elseif ($user->isAuthenticated() && ($timestamp >= $user->getLastLoginTime()) && ($timestamp <= $current) && Crypt::hashEquals($hash, user_pass_rehash($user, $timestamp))) { | |
225 user_login_finalize($user); | |
226 $this->logger->notice('User %name used one-time login link at time %timestamp.', ['%name' => $user->getDisplayName(), '%timestamp' => $timestamp]); | |
227 drupal_set_message($this->t('You have just used your one-time login link. It is no longer necessary to use this link to log in. Please change your password.')); | |
228 // Let the user's password be changed without the current password | |
229 // check. | |
230 $token = Crypt::randomBytesBase64(55); | |
231 $_SESSION['pass_reset_' . $user->id()] = $token; | |
232 return $this->redirect( | |
233 'entity.user.edit_form', | |
234 ['user' => $user->id()], | |
235 [ | |
236 'query' => ['pass-reset-token' => $token], | |
237 'absolute' => TRUE, | |
238 ] | |
239 ); | |
240 } | |
241 | |
242 drupal_set_message($this->t('You have tried to use a one-time login link that has either been used or is no longer valid. Please request a new one using the form below.'), 'error'); | |
243 return $this->redirect('user.pass'); | |
244 } | |
245 | |
246 /** | |
247 * Redirects users to their profile page. | |
248 * | |
249 * This controller assumes that it is only invoked for authenticated users. | |
250 * This is enforced for the 'user.page' route with the '_user_is_logged_in' | |
251 * requirement. | |
252 * | |
253 * @return \Symfony\Component\HttpFoundation\RedirectResponse | |
254 * Returns a redirect to the profile of the currently logged in user. | |
255 */ | |
256 public function userPage() { | |
257 return $this->redirect('entity.user.canonical', ['user' => $this->currentUser()->id()]); | |
258 } | |
259 | |
260 /** | |
261 * Route title callback. | |
262 * | |
263 * @param \Drupal\user\UserInterface $user | |
264 * The user account. | |
265 * | |
266 * @return string|array | |
267 * The user account name as a render array or an empty string if $user is | |
268 * NULL. | |
269 */ | |
270 public function userTitle(UserInterface $user = NULL) { | |
271 return $user ? ['#markup' => $user->getUsername(), '#allowed_tags' => Xss::getHtmlTagList()] : ''; | |
272 } | |
273 | |
274 /** | |
275 * Logs the current user out. | |
276 * | |
277 * @return \Symfony\Component\HttpFoundation\RedirectResponse | |
278 * A redirection to home page. | |
279 */ | |
280 public function logout() { | |
281 user_logout(); | |
282 return $this->redirect('<front>'); | |
283 } | |
284 | |
285 /** | |
286 * Confirms cancelling a user account via an email link. | |
287 * | |
288 * @param \Drupal\user\UserInterface $user | |
289 * The user account. | |
290 * @param int $timestamp | |
291 * The timestamp. | |
292 * @param string $hashed_pass | |
293 * The hashed password. | |
294 * | |
295 * @return \Symfony\Component\HttpFoundation\RedirectResponse | |
296 * A redirect response. | |
297 */ | |
298 public function confirmCancel(UserInterface $user, $timestamp = 0, $hashed_pass = '') { | |
299 // Time out in seconds until cancel URL expires; 24 hours = 86400 seconds. | |
300 $timeout = 86400; | |
301 $current = REQUEST_TIME; | |
302 | |
303 // Basic validation of arguments. | |
304 $account_data = $this->userData->get('user', $user->id()); | |
305 if (isset($account_data['cancel_method']) && !empty($timestamp) && !empty($hashed_pass)) { | |
306 // Validate expiration and hashed password/login. | |
307 if ($timestamp <= $current && $current - $timestamp < $timeout && $user->id() && $timestamp >= $user->getLastLoginTime() && Crypt::hashEquals($hashed_pass, user_pass_rehash($user, $timestamp))) { | |
308 $edit = [ | |
309 'user_cancel_notify' => isset($account_data['cancel_notify']) ? $account_data['cancel_notify'] : $this->config('user.settings')->get('notify.status_canceled'), | |
310 ]; | |
311 user_cancel($edit, $user->id(), $account_data['cancel_method']); | |
312 // Since user_cancel() is not invoked via Form API, batch processing | |
313 // needs to be invoked manually and should redirect to the front page | |
314 // after completion. | |
315 return batch_process(''); | |
316 } | |
317 else { | |
318 drupal_set_message(t('You have tried to use an account cancellation link that has expired. Please request a new one using the form below.'), 'error'); | |
319 return $this->redirect('entity.user.cancel_form', ['user' => $user->id()], ['absolute' => TRUE]); | |
320 } | |
321 } | |
322 throw new AccessDeniedHttpException(); | |
323 } | |
324 | |
325 } |