Elgg  Version 6.3
PersistentLoginService.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg;
4 
6 use Elgg\Traits\TimeUsing;
7 
18 
19  use TimeUsing;
20 
21  protected array $cookie_config;
22 
23  protected string $cookie_token;
24 
29  public $_callable_elgg_set_cookie = 'elgg_set_cookie';
30 
40  public function __construct(
41  protected UsersRememberMeCookiesTable $cookie_table,
42  protected \ElggSession $session,
43  protected \Elgg\Security\Crypto $crypto,
45  \Elgg\Http\Request $request
46  ) {
47  $global_cookies_config = $config->getCookieConfig();
48 
49  $this->cookie_config = $global_cookies_config['remember_me'];
50  $this->cookie_token = $request->cookies->get($this->cookie_config['name'], '');
51  }
52 
60  public function makeLoginPersistent(\ElggUser $user): void {
61  $token = $this->generateToken();
62  $hash = $this->hashToken($token);
63 
64  $this->cookie_table->insertHash($user, $hash);
65  $this->setCookie($token);
66  $this->setSessionToken($token);
67  }
68 
74  public function removePersistentLogin(): void {
75  if ($this->cookie_token) {
76  $client_hash = $this->hashToken($this->cookie_token);
77  $this->cookie_table->deleteHash($client_hash);
78  }
79 
80  $this->setCookie('');
81  $this->setSessionToken('');
82  }
83 
92  public function handlePasswordChange(\ElggUser $subject, ?\ElggUser $modifier = null): void {
93  $this->cookie_table->deleteAllHashes($subject);
94  if (!$modifier || ($modifier->guid !== $subject->guid) || !$this->cookie_token) {
95  return;
96  }
97 
98  $this->makeLoginPersistent($subject);
99  }
100 
107  public function bootSession(): ?\ElggUser {
108  if (!$this->cookie_token) {
109  return null;
110  }
111 
112  // is this token good?
113  $user = $this->getUserFromToken($this->cookie_token);
114  if ($user) {
115  $this->setSessionToken($this->cookie_token);
116 
117  return $user;
118  }
119 
120  $this->setCookie('');
121  return null;
122  }
123 
131  public function getUserFromToken(string $token): ?\ElggUser {
132  if (empty($token)) {
133  return null;
134  }
135 
136  $hash = $this->hashToken($token);
137  if (empty($hash)) {
138  return null;
139  }
140 
141  $user_row = $this->cookie_table->getRowFromHash($hash);
142  if (empty($user_row)) {
143  return null;
144  }
145 
146  $user = get_user($user_row->guid);
147  return ($user instanceof \ElggUser) ? $user : null;
148  }
149 
157  public function updateTokenUsage(\ElggUser $user): ?bool {
158  if (!$this->cookie_token) {
159  return null;
160  }
161 
162  // update the database record
163  // not interested in number of updated rows, as an update in the same second won't update the row
164  $this->cookie_table->updateHash($user, $this->hashToken($this->cookie_token));
165 
166  // also update the cookie lifetime client-side
167  $this->setCookie($this->cookie_token);
168 
169  return true;
170  }
171 
179  public function removeExpiredTokens($time): bool {
180  $time = Values::normalizeTime($time);
181 
182  $expires = Values::normalizeTime($this->cookie_config['expire']);
183  $diff = $time->diff($expires);
184 
185  $time->sub($diff);
186  if ($time->getTimestamp() > time()) {
187  return false;
188  }
189 
190  return (bool) $this->cookie_table->deleteExpiredHashes($time->getTimestamp());
191  }
192 
200  protected function hashToken(string $token): string {
201  // note: with user passwords, you'd want legit password hashing, but since these are randomly
202  // generated and long tokens, rainbow tables aren't any help.
203  return md5($token);
204  }
205 
213  protected function setCookie(string $token): void {
214  $cookie = new \ElggCookie($this->cookie_config['name']);
215  foreach (['expire', 'path', 'domain', 'secure', 'httpOnly'] as $key) {
216  $cookie->$key = $this->cookie_config[strtolower($key)];
217  }
218 
219  $cookie->value = $token;
220  if (!$token) {
221  $cookie->expire = $this->getCurrentTime('-30 days')->getTimestamp();
222  }
223 
224  call_user_func($this->_callable_elgg_set_cookie, $cookie);
225  }
226 
234  protected function setSessionToken(string $token): void {
235  if ($token) {
236  $this->session->set('code', $token);
237  } else {
238  $this->session->remove('code');
239  }
240  }
241 
250  protected function generateToken(): string {
251  return 'z' . $this->crypto->getRandomString(31);
252  }
253 }
return[ 'admin/delete_admin_notices'=>['access'=> 'admin'], 'admin/menu/save'=>['access'=> 'admin'], 'admin/plugins/activate'=>['access'=> 'admin'], 'admin/plugins/activate_all'=>['access'=> 'admin'], 'admin/plugins/deactivate'=>['access'=> 'admin'], 'admin/plugins/deactivate_all'=>['access'=> 'admin'], 'admin/plugins/set_priority'=>['access'=> 'admin'], 'admin/security/security_txt'=>['access'=> 'admin'], 'admin/security/settings'=>['access'=> 'admin'], 'admin/security/regenerate_site_secret'=>['access'=> 'admin'], 'admin/site/cache/invalidate'=>['access'=> 'admin'], 'admin/site/flush_cache'=>['access'=> 'admin'], 'admin/site/icons'=>['access'=> 'admin'], 'admin/site/set_maintenance_mode'=>['access'=> 'admin'], 'admin/site/set_robots'=>['access'=> 'admin'], 'admin/site/theme'=>['access'=> 'admin'], 'admin/site/unlock_upgrade'=>['access'=> 'admin'], 'admin/site/settings'=>['access'=> 'admin'], 'admin/upgrade'=>['access'=> 'admin'], 'admin/upgrade/reset'=>['access'=> 'admin'], 'admin/user/ban'=>['access'=> 'admin'], 'admin/user/bulk/ban'=>['access'=> 'admin'], 'admin/user/bulk/delete'=>['access'=> 'admin'], 'admin/user/bulk/unban'=>['access'=> 'admin'], 'admin/user/bulk/validate'=>['access'=> 'admin'], 'admin/user/change_email'=>['access'=> 'admin'], 'admin/user/delete'=>['access'=> 'admin'], 'admin/user/login_as'=>['access'=> 'admin'], 'admin/user/logout_as'=>[], 'admin/user/makeadmin'=>['access'=> 'admin'], 'admin/user/resetpassword'=>['access'=> 'admin'], 'admin/user/removeadmin'=>['access'=> 'admin'], 'admin/user/unban'=>['access'=> 'admin'], 'admin/user/validate'=>['access'=> 'admin'], 'annotation/delete'=>[], 'avatar/upload'=>[], 'comment/save'=>[], 'diagnostics/download'=>['access'=> 'admin'], 'entity/chooserestoredestination'=>[], 'entity/delete'=>[], 'entity/mute'=>[], 'entity/restore'=>[], 'entity/subscribe'=>[], 'entity/trash'=>[], 'entity/unmute'=>[], 'entity/unsubscribe'=>[], 'login'=>['access'=> 'logged_out'], 'logout'=>[], 'notifications/mute'=>['access'=> 'public'], 'plugins/settings/remove'=>['access'=> 'admin'], 'plugins/settings/save'=>['access'=> 'admin'], 'plugins/usersettings/save'=>[], 'register'=>['access'=> 'logged_out', 'middleware'=>[\Elgg\Router\Middleware\RegistrationAllowedGatekeeper::class,],], 'river/delete'=>[], 'settings/notifications'=>[], 'settings/notifications/subscriptions'=>[], 'user/changepassword'=>['access'=> 'public'], 'user/requestnewpassword'=>['access'=> 'public'], 'useradd'=>['access'=> 'admin'], 'usersettings/save'=>[], 'widgets/add'=>[], 'widgets/delete'=>[], 'widgets/move'=>[], 'widgets/save'=>[],]
Definition: actions.php:73
if(! $annotation instanceof ElggAnnotation) $time
Definition: time.php:20
$user
Definition: ban.php:7
Elgg Session Management.
Definition: ElggSession.php:19
Manage the users_remember_me_cookies table.
\Elgg\PersistentLoginService
setCookie(string $token)
Store the token in the client cookie (or remove the cookie)
generateToken()
Generate a random token (base 64 URL)
getUserFromToken(string $token)
Get a user from a persistent cookie token.
bootSession()
Boot the persistent login session, possibly returning the user who should be silently logged in.
setSessionToken(string $token)
Store the token in the session (or remove it from the session)
removePersistentLogin()
Remove the persisted login token from client and server.
makeLoginPersistent(\ElggUser $user)
Make the user's login persistent.
updateTokenUsage(\ElggUser $user)
Update the timestamp linked to a persistent cookie code, this indicates that the code was used recent...
__construct(protected UsersRememberMeCookiesTable $cookie_table, protected \ElggSession $session, protected \Elgg\Security\Crypto $crypto, \Elgg\Config $config, \Elgg\Http\Request $request)
Constructor.
hashToken(string $token)
Create a hash from the token.
removeExpiredTokens($time)
Remove all persistent codes from the database which have expired based on the cookie config.
handlePasswordChange(\ElggUser $subject, ?\ElggUser $modifier=null)
Handle a password change.
Request container.
Definition: Request.php:12
Functions for use as event handlers or other situations where you need a globally accessible callable...
Definition: Values.php:13
$config
Advanced site settings, debugging section.
Definition: debugging.php:6
$subject
HTML body of an email.
Definition: body.php:11
get_user(int $guid)
Elgg users Functions to manage multiple or single users in an Elgg install.
Definition: users.php:16
$request
Definition: livesearch.php:12
if(isset($_COOKIE['elggperm'])) $session
Definition: login_as.php:29
if($container instanceof ElggGroup && $container->guid !=elgg_get_page_owner_guid()) $key
Definition: summary.php:44
if(parse_url(elgg_get_site_url(), PHP_URL_PATH) !=='/') if(file_exists(elgg_get_root_path() . 'robots.txt'))
Set robots.txt.
Definition: robots.php:10
$token
$expires