1
0
mirror of https://github.com/delight-im/PHP-Auth.git synced 2025-08-05 15:47:25 +02:00

Save all relevant user actions on 'Auth' interface to audit log

This commit is contained in:
Marco
2025-05-27 11:28:12 +02:00
parent ed7fb0b2eb
commit 288bc1d967

View File

@@ -234,6 +234,8 @@ final class Auth extends UserManager {
$newUserId = $this->createUserInternal(false, $email, $password, $username, $callback);
$this->logForAudit('register', $newUserId, [ 'email' => EmailAddress::mask($email), 'username' => $username ]);
$this->throttle([ 'createNewAccount', $this->getIpAddress() ], 1, (60 * 60 * 12), 5, false);
return $newUserId;
@@ -275,6 +277,8 @@ final class Auth extends UserManager {
$newUserId = $this->createUserInternal(true, $email, $password, $username, $callback);
$this->logForAudit('register.withUniqueUsername', $newUserId, [ 'email' => EmailAddress::mask($email), 'username' => $username ]);
$this->throttle([ 'createNewAccount', $this->getIpAddress() ], 1, (60 * 60 * 12), 5, false);
return $newUserId;
@@ -371,6 +375,9 @@ final class Auth extends UserManager {
if (!$validated) {
$this->throttle([ 'reconfirmPassword', $this->getIpAddress() ], 3, (60 * 60), 4, false);
}
else {
$this->logForAudit('password.reconfirm');
}
return $validated;
}
@@ -403,6 +410,8 @@ final class Auth extends UserManager {
);
}
$this->logForAudit('logout.local');
// remove all session variables maintained by this library
unset($_SESSION[self::SESSION_FIELD_LOGGED_IN]);
unset($_SESSION[self::SESSION_FIELD_USER_ID]);
@@ -455,6 +464,8 @@ final class Auth extends UserManager {
$previousRememberDirectiveExpiry - \time()
);
}
$this->logForAudit('logout.remote');
}
/**
@@ -470,6 +481,9 @@ final class Auth extends UserManager {
// schedule a forced logout in all sessions
$this->forceLogoutForUserById($this->getUserId());
$this->logForAudit('logout.remote', $this->getUserId());
// and immediately apply the logout locally
$this->logOut();
}
@@ -691,6 +705,11 @@ final class Auth extends UserManager {
if ($confirmationData['old_email'] === $confirmationData['new_email']) {
// the output should not contain any previous email address
$confirmationData['old_email'] = null;
$this->logForAudit('confirmation.email.verify', $confirmationData['user_id'], [ 'email' => EmailAddress::mask($confirmationData['new_email']) ]);
}
else {
$this->logForAudit('email.change.finish', $confirmationData['user_id'], [ 'old' => EmailAddress::mask($confirmationData['old_email']), 'new' => EmailAddress::mask($confirmationData['new_email']) ]);
}
return [
@@ -741,6 +760,8 @@ final class Auth extends UserManager {
[ 'id', 'email', 'username', 'status', 'roles_mask', 'force_logout' ]
);
$this->logForAudit('login.fromConfirmation', $userData['id']);
$this->finishSingleFactorOrThrow(
$userData['id'], $userData['email'], $userData['username'], $userData['status'], $userData['roles_mask'], $userData['force_logout'], true, $rememberDuration
);
@@ -782,6 +803,8 @@ final class Auth extends UserManager {
$newPassword = self::validatePassword($newPassword, true);
$this->updatePasswordInternal($this->getUserId(), $newPassword);
$this->logForAudit('password.change');
try {
$this->logOutEverywhereElse();
}
@@ -934,6 +957,12 @@ final class Auth extends UserManager {
throw new DatabaseError($e->getMessage());
}
$this->logForAudit('2fa.provide', $_SESSION[self::SESSION_FIELD_AWAITING_2FA_USER_ID], [
'totp' => ($mechanismUsed === self::TWO_FACTOR_MECHANISM_TOTP),
'sms' => ($mechanismUsed === self::TWO_FACTOR_MECHANISM_SMS),
'email' => ($mechanismUsed === self::TWO_FACTOR_MECHANISM_EMAIL),
]);
if (!empty($userData)) {
$this->onLoginSuccessful(
$_SESSION[self::SESSION_FIELD_AWAITING_2FA_USER_ID], $userData['email'], $userData['username'], $userData['status'], $userData['roles_mask'], $userData['force_logout'], false
@@ -1013,6 +1042,8 @@ final class Auth extends UserManager {
$this->throttle([ 'requestEmailChange', 'userId', $this->getUserId() ], 1, (60 * 60 * 24));
$this->throttle([ 'requestEmailChange', $this->getIpAddress() ], 1, (60 * 60 * 24), 3);
$this->logForAudit('email.change.start', null, [ 'old' => EmailAddress::mask($this->getEmail()), 'new' => EmailAddress::mask($newEmail) ]);
$this->createConfirmationRequest($this->getUserId(), $newEmail, $callback);
}
else {
@@ -1120,6 +1151,8 @@ final class Auth extends UserManager {
throw new DatabaseError($e->getMessage());
}
$this->logForAudit('username.change', null, [ 'old' => $this->getUsername(), 'new' => $newUsername ]);
// immediately update the username in the current session as well
$_SESSION[self::SESSION_FIELD_USERNAME] = $newUsername;
}
@@ -1171,6 +1204,8 @@ final class Auth extends UserManager {
$latestAttempt['email'],
$callback
);
$this->logForAudit('confirmation.email.resend', $latestAttempt['user_id'], [ 'email' => EmailAddress::mask($latestAttempt['email']) ]);
}
/**
@@ -1241,6 +1276,8 @@ final class Auth extends UserManager {
$this->throttle([ 'requestPasswordReset', $this->getIpAddress() ], 4, (60 * 60 * 24 * 7), 2);
$this->throttle([ 'requestPasswordReset', 'user', $userData['id'] ], 4, (60 * 60 * 24 * 7), 2);
$this->logForAudit('password.reset.start', $userData['id'], [ 'email' => EmailAddress::mask($email) ]);
$this->createPasswordResetRequest($userData['id'], $requestExpiresAfter, $callback);
}
else {
@@ -1315,6 +1352,8 @@ final class Auth extends UserManager {
$rememberDuration = null;
}
$this->logForAudit('login', $userData['id'], [ 'email' => EmailAddress::mask($email), 'username' => $username ]);
$this->finishSingleFactorOrThrow(
$userData['id'], $userData['email'], $userData['username'], $userData['status'], $userData['roles_mask'], $userData['force_logout'], false, $rememberDuration
);
@@ -1416,6 +1455,12 @@ final class Auth extends UserManager {
// remember the "remember me" duration that the user just requested to be kept after signing in
$_SESSION[self::SESSION_FIELD_AWAITING_2FA_REMEMBER_DURATION] = $rememberDuration;
$this->logForAudit('2fa.prompt', $userId, [
'totp' => $secondFactorRequiredException->hasTotpOption(),
'sms' => $secondFactorRequiredException->hasSmsOption(),
'email' => $secondFactorRequiredException->hasEmailOption(),
]);
// cancel/pause the login attempt for now
throw $secondFactorRequiredException;
}
@@ -1631,6 +1676,9 @@ final class Auth extends UserManager {
if ($resetData['expires'] >= \time()) {
$newPassword = self::validatePassword($newPassword, true);
$this->updatePasswordInternal($resetData['user'], $newPassword);
$this->logForAudit('password.reset.finish', $resetData['user']);
$this->forceLogoutForUserById($resetData['user']);
try {
@@ -1701,6 +1749,8 @@ final class Auth extends UserManager {
[ 'username', 'status', 'roles_mask', 'force_logout' ]
);
$this->logForAudit('login.fromPasswordReset', $idAndEmail['id']);
$this->finishSingleFactorOrThrow(
$idAndEmail['id'], $idAndEmail['email'], $userData['username'], $userData['status'], $userData['roles_mask'], $userData['force_logout'], true, $rememberDuration
);
@@ -1799,6 +1849,8 @@ final class Auth extends UserManager {
catch (Error $e) {
throw new DatabaseError($e->getMessage());
}
$this->logForAudit($enabled ? 'password.reset.enable' : 'password.reset.disable');
}
else {
throw new NotLoggedInException();
@@ -1850,6 +1902,8 @@ final class Auth extends UserManager {
$keyUriAndSecretString = $this->prepareTwoFactor(self::TWO_FACTOR_MECHANISM_TOTP, $serviceName, null);
$this->logForAudit('2fa.totp.enable.start');
return $keyUriAndSecretString;
}
@@ -1878,6 +1932,8 @@ final class Auth extends UserManager {
$this->prepareTwoFactor(self::TWO_FACTOR_MECHANISM_SMS, null, $phoneNumber);
$otpValue = $this->generateAndStoreRandomOneTimePassword($this->getUserId(), self::TWO_FACTOR_MECHANISM_SMS);
$this->logForAudit('2fa.sms.enable.start', null, [ 'phone' => PhoneNumber::mask($phoneNumber) ]);
return [ $phoneNumber, $otpValue ];
}
@@ -1898,6 +1954,8 @@ final class Auth extends UserManager {
$this->prepareTwoFactor(self::TWO_FACTOR_MECHANISM_EMAIL, null, null);
$otpValue = $this->generateAndStoreRandomOneTimePassword($this->getUserId(), self::TWO_FACTOR_MECHANISM_EMAIL);
$this->logForAudit('2fa.email.enable.start', null, [ 'email' => EmailAddress::mask($this->getEmail()) ]);
return [ $this->getEmail(), $otpValue ];
}
@@ -2040,6 +2098,8 @@ final class Auth extends UserManager {
public function enableTwoFactorViaTotp($otpValue) {
$recoveryCodes = $this->enableTwoFactor(self::TWO_FACTOR_MECHANISM_TOTP, $otpValue);
$this->logForAudit('2fa.totp.enable.finish');
return $recoveryCodes;
}
@@ -2062,6 +2122,8 @@ final class Auth extends UserManager {
public function enableTwoFactorViaSms($otpValue) {
$recoveryCodes = $this->enableTwoFactor(self::TWO_FACTOR_MECHANISM_SMS, $otpValue);
$this->logForAudit('2fa.sms.enable.finish');
return $recoveryCodes;
}
@@ -2084,6 +2146,8 @@ final class Auth extends UserManager {
public function enableTwoFactorViaEmail($otpValue) {
$recoveryCodes = $this->enableTwoFactor(self::TWO_FACTOR_MECHANISM_EMAIL, $otpValue);
$this->logForAudit('2fa.email.enable.finish');
return $recoveryCodes;
}
@@ -2642,6 +2706,10 @@ final class Auth extends UserManager {
catch (Error $e) {
throw new DatabaseError($e->getMessage());
}
if ($disabled > 0) {
$this->logForAudit('2fa.totp.disable');
}
}
else {
throw new NotLoggedInException();
@@ -2668,6 +2736,10 @@ final class Auth extends UserManager {
catch (Error $e) {
throw new DatabaseError($e->getMessage());
}
if ($disabled > 0) {
$this->logForAudit('2fa.sms.disable');
}
}
else {
throw new NotLoggedInException();
@@ -2694,6 +2766,10 @@ final class Auth extends UserManager {
catch (Error $e) {
throw new DatabaseError($e->getMessage());
}
if ($disabled > 0) {
$this->logForAudit('2fa.email.disable');
}
}
else {
throw new NotLoggedInException();
@@ -2720,6 +2796,10 @@ final class Auth extends UserManager {
catch (Error $e) {
throw new DatabaseError($e->getMessage());
}
if ($disabled > 0) {
$this->logForAudit('2fa.disable');
}
}
else {
throw new NotLoggedInException();