Move the 'authenticate' hook deeper into the authentication flow so that password authentication addons are consulted whenever a user's password needs confirming.

This commit is contained in:
very-ape 2021-05-19 12:57:58 -07:00
parent 6434d018c8
commit 9ddaabc22d
3 changed files with 51 additions and 34 deletions

View File

@ -666,8 +666,13 @@ Here is a complete list of all hook callbacks with file locations (as of 24-Sep-
Hook::callAll('event_updated', $event['id']); Hook::callAll('event_updated', $event['id']);
Hook::callAll("event_created", $event['id']); Hook::callAll("event_created", $event['id']);
### src/Model/Register.php
Hook::callAll('authenticate', $addon_auth);
### src/Model/User.php ### src/Model/User.php
Hook::callAll('authenticate', $addon_auth);
Hook::callAll('register_account', $uid); Hook::callAll('register_account', $uid);
Hook::callAll('remove_user', $user); Hook::callAll('remove_user', $user);
@ -675,6 +680,22 @@ Here is a complete list of all hook callbacks with file locations (as of 24-Sep-
Hook::callAll('lockview_content', $item); Hook::callAll('lockview_content', $item);
### src/Module/Settings/Delegation.php
Hook::callAll('authenticate', $addon_auth);
### src/Module/Settings/TwoFactor/Index.php
Hook::callAll('authenticate', $addon_auth);
### src/Security/Authenticate.php
Hook::callAll('authenticate', $addon_auth);
### src/Security/ExAuth.php
Hook::callAll('authenticate', $addon_auth);
### src/Content/ContactBlock.php ### src/Content/ContactBlock.php
Hook::callAll('contact_block_end', $arr); Hook::callAll('contact_block_end', $arr);

View File

@ -544,6 +544,24 @@ class User
} }
return $user['uid']; return $user['uid'];
} else {
$addon_auth = [
'username' => $user['nickname'],
'password' => $password,
'authenticated' => 0,
'user_record' => null
];
/*
* An addon indicates successful login by setting 'authenticated' to non-zero value and returning a user record
* Addons should never set 'authenticated' except to indicate success - as hooks may be chained
* and later addons should not interfere with an earlier one that succeeded.
*/
Hook::callAll('authenticate', $addon_auth);
if ($addon_auth['authenticated'] && $addon_auth['user_record']) {
return $user['uid'];
}
} }
throw new HTTPException\ForbiddenException(DI::l10n()->t('Login failed')); throw new HTTPException\ForbiddenException(DI::l10n()->t('Login failed'));
@ -584,7 +602,7 @@ class User
if (is_int($user_info)) { if (is_int($user_info)) {
$user = DBA::selectFirst( $user = DBA::selectFirst(
'user', 'user',
['uid', 'password', 'legacy_password'], ['uid', 'nickname', 'password', 'legacy_password'],
[ [
'uid' => $user_info, 'uid' => $user_info,
'blocked' => 0, 'blocked' => 0,
@ -594,7 +612,7 @@ class User
] ]
); );
} else { } else {
$fields = ['uid', 'password', 'legacy_password']; $fields = ['uid', 'nickname', 'password', 'legacy_password'];
$condition = [ $condition = [
"(`email` = ? OR `username` = ? OR `nickname` = ?) "(`email` = ? OR `username` = ? OR `nickname` = ?)
AND NOT `blocked` AND NOT `account_expired` AND NOT `account_removed` AND `verified`", AND NOT `blocked` AND NOT `account_expired` AND NOT `account_removed` AND `verified`",

View File

@ -239,34 +239,12 @@ class Authentication
{ {
$record = null; $record = null;
$addon_auth = [
'username' => $username,
'password' => $password,
'authenticated' => 0,
'user_record' => null
];
/*
* An addon indicates successful login by setting 'authenticated' to non-zero value and returning a user record
* Addons should never set 'authenticated' except to indicate success - as hooks may be chained
* and later addons should not interfere with an earlier one that succeeded.
*/
Hook::callAll('authenticate', $addon_auth);
try { try {
if ($addon_auth['authenticated']) {
$record = $addon_auth['user_record'];
if (empty($record)) {
throw new Exception($this->l10n->t('Login failed.'));
}
} else {
$record = $this->dba->selectFirst( $record = $this->dba->selectFirst(
'user', 'user',
[], [],
['uid' => User::getIdFromPasswordAuthentication($username, $password)] ['uid' => User::getIdFromPasswordAuthentication($username, $password)]
); );
}
} catch (Exception $e) { } catch (Exception $e) {
$this->logger->warning('authenticate: failed login attempt', ['action' => 'login', 'username' => Strings::escapeTags($username), 'ip' => $_SERVER['REMOTE_ADDR']]); $this->logger->warning('authenticate: failed login attempt', ['action' => 'login', 'username' => Strings::escapeTags($username), 'ip' => $_SERVER['REMOTE_ADDR']]);
notice($this->l10n->t('Login failed. Please check your credentials.')); notice($this->l10n->t('Login failed. Please check your credentials.'));