This commit is contained in:
parent
05f2e53a8f
commit
df8de9e0c8
@ -7,26 +7,50 @@
|
|||||||
|
|
||||||
import { create_salt } from '@sapphirecode/crypto-helper';
|
import { create_salt } from '@sapphirecode/crypto-helper';
|
||||||
|
|
||||||
|
interface Key {
|
||||||
|
key: string;
|
||||||
|
valid_until: number;
|
||||||
|
timeout: NodeJS.Timeout;
|
||||||
|
}
|
||||||
|
|
||||||
class KeyStore {
|
class KeyStore {
|
||||||
private _keys: Record<string, string> = {};
|
private _keys: Record<string, Key> = {};
|
||||||
|
|
||||||
|
private set_timeout (index: string, valid_for: number): NodeJS.Timeout {
|
||||||
|
return setTimeout (() => {
|
||||||
|
delete this._keys[index];
|
||||||
|
}, (valid_for + 5) * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
public get_key (iat: number, valid_for = 0): string {
|
public get_key (iat: number, valid_for = 0): string {
|
||||||
const key = Math.floor (iat / 60)
|
const index = Math.floor (iat / 60)
|
||||||
.toFixed (0);
|
.toFixed (0);
|
||||||
|
|
||||||
if (typeof this._keys[key] === 'string')
|
const valid_until = (new Date)
|
||||||
return this._keys[key];
|
.getTime () + (valid_for * 1000);
|
||||||
|
|
||||||
|
if (typeof this._keys[index] !== 'undefined') {
|
||||||
|
const key = this._keys[index];
|
||||||
|
if (valid_for !== 0 && key.valid_until < valid_until) {
|
||||||
|
clearTimeout (key.timeout);
|
||||||
|
key.timeout = this.set_timeout (index, valid_for);
|
||||||
|
key.valid_until = valid_until;
|
||||||
|
}
|
||||||
|
return key.key;
|
||||||
|
}
|
||||||
|
|
||||||
if (valid_for !== 0) {
|
if (valid_for !== 0) {
|
||||||
if ((iat + valid_for) * 1000 < (new Date)
|
if ((iat + 1) * 1000 < (new Date)
|
||||||
.getTime ())
|
.getTime ())
|
||||||
throw new Error ('cannot create already expired keys');
|
throw new Error ('cannot create already expired keys');
|
||||||
|
|
||||||
this._keys[key] = create_salt ();
|
this._keys[index] = {
|
||||||
setTimeout (() => {
|
key: create_salt (),
|
||||||
delete this._keys[key];
|
timeout: this.set_timeout (index, valid_for),
|
||||||
}, (valid_for + 5) * 1000);
|
valid_until
|
||||||
return this._keys[key];
|
};
|
||||||
|
|
||||||
|
return this._keys[index].key;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error ('key could not be found');
|
throw new Error ('key could not be found');
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
import ks from '../../lib/KeyStore';
|
import ks from '../../lib/KeyStore';
|
||||||
|
|
||||||
|
const frame = 60;
|
||||||
|
|
||||||
/* eslint-disable-next-line max-lines-per-function */
|
/* eslint-disable-next-line max-lines-per-function */
|
||||||
describe ('key store', () => {
|
describe ('key store', () => {
|
||||||
beforeAll (() => {
|
beforeAll (() => {
|
||||||
@ -23,7 +25,7 @@ describe ('key store', () => {
|
|||||||
it ('should generate a new key', () => {
|
it ('should generate a new key', () => {
|
||||||
const iat = (new Date)
|
const iat = (new Date)
|
||||||
.getTime () / 1000;
|
.getTime () / 1000;
|
||||||
const duration = 600;
|
const duration = 10 * frame;
|
||||||
const key = ks.get_key (iat, duration);
|
const key = ks.get_key (iat, duration);
|
||||||
expect (typeof key)
|
expect (typeof key)
|
||||||
.toEqual ('string');
|
.toEqual ('string');
|
||||||
@ -39,17 +41,17 @@ describe ('key store', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it ('should return the same key on a different time', () => {
|
it ('should return the same key on a different time', () => {
|
||||||
const key = ks.get_key (keys[0].iat + 30);
|
const key = ks.get_key (keys[0].iat + (frame / 2));
|
||||||
expect (key)
|
expect (key)
|
||||||
.toEqual (keys[0].key);
|
.toEqual (keys[0].key);
|
||||||
});
|
});
|
||||||
|
|
||||||
it ('should generate a new key after 60 seconds', () => {
|
it ('should generate a new key after time frame is over', () => {
|
||||||
jasmine.clock ()
|
jasmine.clock ()
|
||||||
.tick (60000);
|
.tick (frame * 1000);
|
||||||
const iat = (new Date)
|
const iat = (new Date)
|
||||||
.getTime () / 1000;
|
.getTime () / 1000;
|
||||||
const duration = 600;
|
const duration = 10 * frame;
|
||||||
const key = ks.get_key (iat, duration);
|
const key = ks.get_key (iat, duration);
|
||||||
expect (typeof key)
|
expect (typeof key)
|
||||||
.toEqual ('string');
|
.toEqual ('string');
|
||||||
@ -69,13 +71,13 @@ describe ('key store', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it ('should throw on non existing key', () => {
|
it ('should throw on non existing key', () => {
|
||||||
expect (() => ks.get_key (keys[1].iat + 60))
|
expect (() => ks.get_key (keys[1].iat + frame))
|
||||||
.toThrowError ('key could not be found');
|
.toThrowError ('key could not be found');
|
||||||
});
|
});
|
||||||
|
|
||||||
it ('should delete a key after it expires', () => {
|
it ('should delete a key after it expires', () => {
|
||||||
jasmine.clock ()
|
jasmine.clock ()
|
||||||
.tick (600000);
|
.tick (10000 * frame);
|
||||||
expect (() => ks.get_key (keys[0].iat))
|
expect (() => ks.get_key (keys[0].iat))
|
||||||
.toThrowError ('key could not be found');
|
.toThrowError ('key could not be found');
|
||||||
});
|
});
|
||||||
@ -88,12 +90,40 @@ describe ('key store', () => {
|
|||||||
|
|
||||||
it ('should reject key generation of expired keys', () => {
|
it ('should reject key generation of expired keys', () => {
|
||||||
const iat = ((new Date)
|
const iat = ((new Date)
|
||||||
.getTime () / 1000) - 10;
|
.getTime () / 1000) - 2;
|
||||||
const duration = 5;
|
const duration = 5;
|
||||||
expect (() => ks.get_key (iat, duration))
|
expect (() => ks.get_key (iat, duration))
|
||||||
.toThrowError ('cannot create already expired keys');
|
.toThrowError ('cannot create already expired keys');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it ('key should live as long as the longest created token', () => {
|
||||||
|
const base = new Date;
|
||||||
|
base.setSeconds (2, 0);
|
||||||
|
jasmine.clock ()
|
||||||
|
.mockDate (base);
|
||||||
|
jasmine.clock ()
|
||||||
|
.tick (24 * 60 * 60 * 1000);
|
||||||
|
const iat = (new Date)
|
||||||
|
.getTime () / 1000;
|
||||||
|
const duration1 = frame;
|
||||||
|
const duration2 = frame * 10;
|
||||||
|
|
||||||
|
const key1 = ks.get_key (iat, duration1);
|
||||||
|
const step = 0.9 * frame;
|
||||||
|
jasmine.clock ()
|
||||||
|
.tick (step * 1000);
|
||||||
|
const key2 = ks.get_key (iat + step, duration2);
|
||||||
|
expect (key1)
|
||||||
|
.toEqual (key2);
|
||||||
|
jasmine.clock ()
|
||||||
|
.tick (5000 * frame);
|
||||||
|
const keyv = ks.get_key (iat + step);
|
||||||
|
expect (keyv)
|
||||||
|
.toEqual (key1);
|
||||||
|
});
|
||||||
|
|
||||||
|
// required use case: insert keys for verification of old tokens
|
||||||
|
|
||||||
afterAll (() => {
|
afterAll (() => {
|
||||||
jasmine.clock ()
|
jasmine.clock ()
|
||||||
.tick (24 * 60 * 60 * 1000);
|
.tick (24 * 60 * 60 * 1000);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user