auth-server-helper/lib/Blacklist.ts

151 lines
4.2 KiB
TypeScript
Raw Normal View History

/*
* Copyright (C) Sapphirecode - All Rights Reserved
* This file is part of Auth-Server-Helper which is released under MIT.
* See file 'LICENSE' for full license details.
* Created by Timo Hocker <timo@scode.ovh>, December 2020
*/
2022-01-05 12:32:04 +01:00
import { debug } from './debug';
2022-08-27 16:39:07 +02:00
import { redis_blacklist_store } from './RedisData/RedisBlacklistStore';
import { parse_token_id } from './token_id';
2022-01-05 12:32:04 +01:00
const logger = debug ('blacklist');
2020-12-12 15:53:47 +01:00
interface Signature {
token_id: string;
iat: number;
valid_until: Date;
}
interface ExportedSignature {
token_id: string;
2021-01-15 14:45:05 +01:00
iat: number;
2020-12-12 15:53:47 +01:00
}
class Blacklist {
private _signatures: Signature[];
private _interval: NodeJS.Timeout;
2020-12-12 15:53:47 +01:00
public constructor () {
this._signatures = [];
this._interval = setInterval (
this.garbage_collect.bind (this),
3600000
);
2020-12-12 15:53:47 +01:00
}
2022-08-27 16:39:07 +02:00
public async clear (
before: number = Number.POSITIVE_INFINITY
): Promise<void> {
2022-08-15 17:33:25 +02:00
logger.extend ('clear') ('clearing blacklist');
2020-12-12 15:53:47 +01:00
for (let i = this._signatures.length - 1; i >= 0; i--) {
2022-08-27 16:39:07 +02:00
if (this._signatures[i].iat < before) {
// eslint-disable-next-line no-await-in-loop
await this.remove_signature (i);
}
2020-12-12 15:53:47 +01:00
}
}
public async add_signature (token_id: string): Promise<void> {
logger.extend ('add_signature') ('blacklisting signature %s', token_id);
const parsed = parse_token_id (token_id);
this._signatures.push ({
iat: Date.now (),
token_id,
valid_until: parsed.valid_until
});
await redis_blacklist_store.add (token_id, parsed.valid_until);
2020-12-12 15:53:47 +01:00
}
2022-08-27 16:39:07 +02:00
public async remove_signature (signature: number | string): Promise<void> {
2022-08-15 17:33:25 +02:00
const log = logger.extend ('remove_signature');
log ('removing signature from blacklist %s', signature);
2022-08-27 16:39:07 +02:00
let key = '';
2022-08-15 17:33:25 +02:00
if (typeof signature === 'string') {
log ('received string, searching through signatures');
2022-08-27 16:39:07 +02:00
key = signature;
2022-08-15 17:33:25 +02:00
for (let i = this._signatures.length - 1; i >= 0; i--) {
if (this._signatures[i].token_id === signature) {
log ('removing sigature %s at %d', signature, i);
2022-08-15 17:33:25 +02:00
this._signatures.splice (i, 1);
}
2022-08-15 17:33:25 +02:00
}
}
else {
log (
'received index, removing signature %s at index %s',
this._signatures[signature].token_id,
signature
);
key = this._signatures[signature].token_id;
2022-08-15 17:33:25 +02:00
this._signatures.splice (signature, 1);
2020-12-12 15:53:47 +01:00
}
2022-08-27 16:39:07 +02:00
await redis_blacklist_store.remove (key);
2020-12-12 15:53:47 +01:00
}
2022-08-27 16:39:07 +02:00
public async is_valid (hash: string): Promise<boolean> {
2022-08-15 17:33:25 +02:00
const log = logger.extend ('is_valid');
log ('checking signature for blacklist entry %s', hash);
2020-12-12 15:53:47 +01:00
for (const sig of this._signatures) {
if (sig.token_id === hash) {
2022-08-15 17:33:25 +02:00
log ('found matching blacklist entry');
2020-12-12 15:53:47 +01:00
return false;
2022-01-05 12:32:04 +01:00
}
2020-12-12 15:53:47 +01:00
}
2022-08-27 16:39:07 +02:00
log ('signature is not blacklisted locally, checking redis');
if (await redis_blacklist_store.get (hash)) {
log ('signature is blacklisted in redis');
return false;
}
2022-08-15 17:33:25 +02:00
log ('signature is not blacklisted');
2020-12-12 15:53:47 +01:00
return true;
}
2021-01-09 12:20:14 +01:00
public export_blacklist (): ExportedSignature[] {
2022-08-15 17:33:25 +02:00
logger.extend ('export_blacklist') ('exporting blacklist');
return this._signatures.map ((v) => ({
iat: v.iat,
token_id: v.token_id
}));
2021-01-09 12:20:14 +01:00
}
public import_blacklist (data: ExportedSignature[]): void {
2022-08-15 17:33:25 +02:00
logger.extend ('import_blacklist') (
'importing %d blacklist entries',
data.length
);
for (const token of data) {
const parsed = parse_token_id (token.token_id);
this._signatures.push ({
token_id: token.token_id,
iat: token.iat,
valid_until: parsed.valid_until
});
}
2021-01-09 12:20:14 +01:00
}
2022-08-27 16:39:07 +02:00
public sync_redis (url: string): void {
redis_blacklist_store.connect (url);
}
private async garbage_collect (): Promise<void> {
const log = logger.extend ('garbage_collect');
const time = new Date;
log ('removing signatures expired before', time);
for (let i = this._signatures.length - 1; i >= 0; i--) {
if (this._signatures[i].valid_until < time) {
log ('signature %s expired', this._signatures[i].token_id);
await this.remove_signature (i);
}
}
}
2020-12-12 15:53:47 +01:00
}
const bl = (new Blacklist);
2021-01-05 17:06:35 +01:00
export { Blacklist };
2020-12-12 15:53:47 +01:00
export default bl;