Upgrade to 3.9.6
This commit is contained in:
@@ -367,14 +367,14 @@ class App
|
||||
* @return \Kirby\Toolkit\Collection|null
|
||||
* @todo 5.0 Add return type declaration
|
||||
*/
|
||||
public function collection(string $name)
|
||||
public function collection(string $name, array $options = [])
|
||||
{
|
||||
return $this->collections()->get($name, [
|
||||
return $this->collections()->get($name, array_merge($options, [
|
||||
'kirby' => $this,
|
||||
'site' => $this->site(),
|
||||
'pages' => $this->site()->children(),
|
||||
'site' => $site = $this->site(),
|
||||
'pages' => $site->children(),
|
||||
'users' => $this->users()
|
||||
]);
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -580,7 +580,14 @@ class App
|
||||
$visitor = $this->visitor();
|
||||
|
||||
foreach ($visitor->acceptedLanguages() as $acceptedLang) {
|
||||
$closure = fn ($language) => $language->locale(LC_ALL) === $acceptedLang->locale();
|
||||
$closure = function ($language) use ($acceptedLang) {
|
||||
$languageLocale = $language->locale(LC_ALL);
|
||||
$acceptedLocale = $acceptedLang->locale();
|
||||
|
||||
return $languageLocale === $acceptedLocale ||
|
||||
$acceptedLocale === Str::substr($languageLocale, 0, 2);
|
||||
};
|
||||
|
||||
if ($language = $languages->filter($closure)?->first()) {
|
||||
return $language;
|
||||
}
|
||||
|
||||
@@ -279,18 +279,39 @@ class Auth
|
||||
|
||||
$id = $session->data()->get('kirby.userId');
|
||||
|
||||
// if no user is logged in, return immediately
|
||||
if (is_string($id) !== true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($user = $this->kirby->users()->find($id)) {
|
||||
// in case the session needs to be updated, do it now
|
||||
// for better performance
|
||||
$session->commit();
|
||||
return $user;
|
||||
// a user is logged in, ensure it exists
|
||||
$user = $this->kirby->users()->find($id);
|
||||
if ($user === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
if ($passwordTimestamp = $user->passwordTimestamp()) {
|
||||
$loginTimestamp = $session->data()->get('kirby.loginTimestamp');
|
||||
if (is_int($loginTimestamp) !== true) {
|
||||
// session that was created before Kirby
|
||||
// 3.5.8.3, 3.6.6.3, 3.7.5.2, 3.8.4.1 or 3.9.6
|
||||
// or when the user didn't have a password set
|
||||
$user->logout();
|
||||
return null;
|
||||
}
|
||||
|
||||
// invalidate the session if the password
|
||||
// changed since the login
|
||||
if ($loginTimestamp < $passwordTimestamp) {
|
||||
$user->logout();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// in case the session needs to be updated, do it now
|
||||
// for better performance
|
||||
$session->commit();
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -121,7 +121,10 @@ class Find
|
||||
'site' => $kirby->site(),
|
||||
'account' => static::user(),
|
||||
'page' => static::page(basename($path)),
|
||||
'file' => static::file(...explode('/files/', $path)),
|
||||
// regular expression to split the path at the last
|
||||
// occurrence of /files/ which separates parent path
|
||||
// and filename
|
||||
'file' => static::file(...preg_split('$.*\K(/files/)$', $path)),
|
||||
'user' => $kirby->user(basename($path)),
|
||||
default => throw new InvalidArgumentException('Invalid model type: ' . $modelType)
|
||||
};
|
||||
|
||||
@@ -441,6 +441,9 @@ class User extends ModelWithContent
|
||||
|
||||
$session->regenerateToken(); // privilege change
|
||||
$session->data()->set('kirby.userId', $this->id());
|
||||
if ($this->passwordTimestamp() !== null) {
|
||||
$session->data()->set('kirby.loginTimestamp', time());
|
||||
}
|
||||
$this->kirby()->auth()->setUser($this);
|
||||
|
||||
$kirby->trigger('user.login:after', ['user' => $this, 'session' => $session]);
|
||||
@@ -461,6 +464,7 @@ class User extends ModelWithContent
|
||||
|
||||
// remove the user from the session for future requests
|
||||
$session->data()->remove('kirby.userId');
|
||||
$session->data()->remove('kirby.loginTimestamp');
|
||||
|
||||
// clear the cached user object from the app state of the current request
|
||||
$this->kirby()->auth()->flush();
|
||||
@@ -607,6 +611,26 @@ class User extends ModelWithContent
|
||||
return $this->password = $this->readPassword();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the timestamp when the password
|
||||
* was last changed
|
||||
*/
|
||||
public function passwordTimestamp(): int|null
|
||||
{
|
||||
$file = $this->passwordFile();
|
||||
|
||||
// ensure we have the latest information
|
||||
// to prevent cache attacks
|
||||
clearstatcache();
|
||||
|
||||
// user does not have a password
|
||||
if (is_file($file) === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return filemtime($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Kirby\Cms\UserPermissions
|
||||
*/
|
||||
@@ -864,14 +888,29 @@ class User extends ModelWithContent
|
||||
throw new NotFoundException(['key' => 'user.password.undefined']);
|
||||
}
|
||||
|
||||
// `UserRules` enforces a minimum length of 8 characters,
|
||||
// so everything below that is a typo
|
||||
if (Str::length($password) < 8) {
|
||||
throw new InvalidArgumentException(['key' => 'user.password.invalid']);
|
||||
}
|
||||
|
||||
// too long passwords can cause DoS attacks
|
||||
if (Str::length($password) > 1000) {
|
||||
throw new InvalidArgumentException(['key' => 'user.password.excessive']);
|
||||
}
|
||||
|
||||
if (password_verify($password, $this->password()) !== true) {
|
||||
throw new InvalidArgumentException(['key' => 'user.password.wrong', 'httpCode' => 401]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the password file
|
||||
*/
|
||||
protected function passwordFile(): string
|
||||
{
|
||||
return $this->root() . '/.htpasswd';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,6 +118,13 @@ trait UserActions
|
||||
// update the users collection
|
||||
$user->kirby()->users()->set($user->id(), $user);
|
||||
|
||||
// keep the user logged in to the current browser
|
||||
// if they changed their own password
|
||||
// (regenerate the session token, update the login timestamp)
|
||||
if ($user->isLoggedIn() === true) {
|
||||
$user->loginPasswordless();
|
||||
}
|
||||
|
||||
return $user;
|
||||
});
|
||||
}
|
||||
@@ -323,7 +330,7 @@ trait UserActions
|
||||
*/
|
||||
protected function readPassword()
|
||||
{
|
||||
return F::read($this->root() . '/.htpasswd');
|
||||
return F::read($this->passwordFile());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -384,6 +391,6 @@ trait UserActions
|
||||
#[SensitiveParameter]
|
||||
string $password = null
|
||||
): bool {
|
||||
return F::write($this->root() . '/.htpasswd', $password);
|
||||
return F::write($this->passwordFile(), $password);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,12 +341,23 @@ class UserRules
|
||||
#[SensitiveParameter]
|
||||
string $password
|
||||
): bool {
|
||||
// too short passwords are ineffective
|
||||
if (Str::length($password ?? null) < 8) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'user.password.invalid',
|
||||
]);
|
||||
}
|
||||
|
||||
// too long passwords can cause DoS attacks
|
||||
// and are therefore blocked in the auth system
|
||||
// (blocked here as well to avoid passwords
|
||||
// that cannot be used to log in)
|
||||
if (Str::length($password ?? null) > 1000) {
|
||||
throw new InvalidArgumentException([
|
||||
'key' => 'user.password.excessive',
|
||||
]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user