Added garbage collection for expired and defunct tokens in database.

This commit is contained in:
2019-12-10 15:57:06 +00:00
parent 6f53abf521
commit 3111185c10
6 changed files with 146 additions and 12 deletions

View File

@ -38,10 +38,11 @@ function authenticateLDAP (string $username, string $password) {
$commonName = ldap_get_entries($ds, $ldapSearchResults)[0]['cn'][0];
// Create JWT-payload
$jwtPayload = [
'iat' => time(), // Issued at: time when the token was generated
'iss' => $_SERVER['SERVER_NAME'], // Issuer
'sub' => $qualifiedUsername, // Subject (ie. username)
'name' => $commonName // Common name (as retrieved from AD)
'iat' => time(), // Issued at: time when the token was generated
'iss' => $_SERVER['SERVER_NAME'], // Issuer
'sub' => $qualifiedUsername, // Subject (ie. username)
'name' => $commonName, // Common name (as retrieved from AD)
'fp' => base64_encode(json_encode(get_browser(null, True))) // Fingerprint (based on `HTTP_USER_AGENT`)
];
$secureToken = JWT::encode($jwtPayload, base64_decode($settings->JWT['PrivateKey_base64']));
@ -121,8 +122,8 @@ function validateToken (string $secureToken) {
WHERE LOWER(User.Username) = :username
');
$pdoQuery->execute([
':username' => (string) strtolower($jwtPayload->sub)
]);
':username' => (string) strtolower($jwtPayload->sub)
]);
foreach($pdoQuery->fetchAll(PDO::FETCH_ASSOC) as $row) {
try {
$storedTokens[] = JWT::decode($row['Value'], base64_decode($settings->JWT['PrivateKey_base64']), $settings->JWT['Algorithm']);
@ -136,7 +137,9 @@ function validateToken (string $secureToken) {
if (!empty($storedTokens) && sizeof(array_filter($storedTokens, function ($value) use ($jwtPayload) {
return $value->iat === $jwtPayload->iat;
})) === 1) {
return [
purgeTokens($currentUserId, $settings->Session['Duration']);
return [
'status' => 'Success',
'name' => $jwtPayload->name,
'uid' => $currentUserId
@ -149,4 +152,54 @@ function validateToken (string $secureToken) {
}
}
function purgeTokens(int $userID, int $maximumTokenAge) {
global $settings, $pdoDB;
$defunctTokens = []; $expiredTokens = [];
$pdoQuery = $pdoDB->prepare('
SELECT SecureToken.Id, SecureToken.Value
FROM SecureToken
WHERE SecureToken.UserId = :userid
');
$pdoQuery->execute([
':userid' => (int) $userID
]);
foreach($pdoQuery->fetchAll(PDO::FETCH_ASSOC) as $row) {
try {
$token = JWT::decode($row['Value'], base64_decode($settings->JWT['PrivateKey_base64']), $settings->JWT['Algorithm']);
if ($token->iat < (time() - $maximumTokenAge)) {
$expiredTokens[] = $row['Id'];
}
} catch (Exception $e) {
$defunctTokens[] = $row['Id'];
}
}
try {
// Sadly, PDO does not support named parameters in constructions like 'IN ( :array )'
// instead, the supported syntax is unnamed placeholders like 'IN (?, ?, ?, ...)'
$pdoQuery = $pdoDB->prepare('
DELETE FROM SecureToken
WHERE SecureToken.Id IN (' . implode( ',', array_fill(0, count(array_merge($defunctTokens, $expiredTokens)), '?')) . ')
');
$pdoQuery->execute(array_merge($defunctTokens, $expiredTokens));
if ($settings->Debug['LogToFile']) {
file_put_contents('../purgeToken.log', (new DateTime())->format('Y-m-d\TH:i:s.u') . ' --- Garbage collection succeeded (' . $userID . ' => ' . $pdoQuery->rowCount() . ')' . PHP_EOL, FILE_APPEND);
}
return [
'status' => 'Success',
'amount' => $pdoQuery->rowCount()
];
} catch (Exception $e) {
if ($settings->Debug['LogToFile']) {
file_put_contents('../purgeToken.log', (new DateTime())->format('Y-m-d\TH:i:s.u') . ' --- Garbage collection failed (' . $userID . ' => ' . $e . ')' . PHP_EOL, FILE_APPEND);
}
return ['status' => 'Fail', 'reason' => $e];
}
}
?>