redis sync
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
Timo Hocker 2022-08-08 15:52:56 +02:00
parent 122bd7b574
commit fd26975559
No known key found for this signature in database
GPG Key ID: 3B86485AC71C835C
17 changed files with 1860 additions and 1403 deletions

View File

@ -1,5 +1,9 @@
# Changelog
## 3.3.0
- Verification Keys can now be synchronized through redis
## 3.2.0
- Logout function

View File

@ -1,6 +1,6 @@
# auth-server-helper
version: 3.2.x
version: 3.3.x
customizable and simple authentication
@ -196,6 +196,14 @@ const export = keystore.export_verification_data();
keystore.import_verification_data(export);
```
These keys can also be live synchronized with redis to allow sessions to be shared between servers
```js
const {keystore} = require('@sapphirecode/auth-server-helper');
keystore.sync_redis('redis://localhost');
```
### Exporting and importing blacklist entries across server instances
```js

View File

@ -1,13 +1,11 @@
{
"spec_dir": "test",
"spec_dir": "dist/test",
"spec_files": [
"spec/*.js",
"spec/*.ts"
"spec/*.js"
],
"helpers": [
"helpers/*.js",
"helpers/*.ts"
"helpers/*.js"
],
"stopSpecOnExpectationFailure": false,
"random": false

View File

@ -231,12 +231,12 @@ interface CreateHandlerOptions {
type ProcessRequestOptions = Omit<CreateHandlerOptions, 'parse_body'>
// eslint-disable-next-line max-lines-per-function
function process_request (
async function process_request (
request: AuthRequest,
token: RegExpExecArray | null,
default_handler: AuthRequestHandler,
options?: ProcessRequestOptions
): Promise<void> | void {
): Promise<void> {
if (token === null)
return default_handler (request);
@ -259,7 +259,7 @@ function process_request (
request.is_bearer = true;
request.token = token?.groups?.token;
const token_data = auth.verify (request.token as string);
const token_data = await auth.verify (request.token as string);
if (!token_data.valid)
return default_handler (request);

View File

@ -41,7 +41,7 @@ interface SignatureOptions
}
class Authority {
public verify (key: string): VerificationResult {
public async verify (key: string): Promise<VerificationResult> {
logger ('verifying token');
const result: VerificationResult = {
authorized: false,
@ -49,7 +49,7 @@ class Authority {
type: 'none',
id: ''
};
const data = verify_signature_get_info (
const data = await verify_signature_get_info (
key,
(info) => {
try {

View File

@ -17,7 +17,8 @@ const logger = debug ('gateway');
type AnyFunc = (...args: unknown[]) => unknown;
type Gateway = (
req: IncomingMessage,
res: ServerResponse, next: AnyFunc
res: ServerResponse,
next: AnyFunc
) => unknown;
interface RefreshSettings extends AccessSettings {
@ -78,7 +79,7 @@ class GatewayClass {
return auth.groups?.data;
}
public try_access (req: IncomingMessage): boolean {
public async try_access (req: IncomingMessage): Promise<boolean> {
logger ('authenticating incoming request');
let auth = this.get_header_auth (req);
if (auth === null)
@ -88,7 +89,7 @@ class GatewayClass {
return false;
}
const ver = authority.verify (auth);
const ver = await authority.verify (auth);
logger ('setting connection info');
const con = req.connection as unknown as Record<string, unknown>;
@ -119,20 +120,20 @@ class GatewayClass {
return false;
}
const ver = authority.verify (refresh);
const ver = await authority.verify (refresh);
if (ver.type === 'refresh_token' && ver.valid) {
logger ('refresh token valid, generating new tokens');
const auth_request = new AuthRequest (
req,
res,
''
, this._options.cookie,
'',
this._options.cookie,
this._options.refresh_cookie
);
const refresh_result = await auth_request.allow_access ({
...this._options.refresh_settings,
data: ver.data,
leave_open: true
data: ver.data,
leave_open: true
});
logger ('setting connection info');
@ -155,7 +156,7 @@ class GatewayClass {
res: ServerResponse
): Promise<boolean> {
logger ('trying to authenticate http request');
if (this.try_access (req)) {
if (await this.try_access (req)) {
logger ('authenticated via access_token');
return true;
}
@ -183,7 +184,7 @@ class GatewayClass {
return this.redirect (res);
}
public logout (req: IncomingMessage): void {
public async logout (req: IncomingMessage): Promise<void> {
const l = logger.extend ('logout');
l ('invalidating all submitted tokens');
const auth_strings = [
@ -191,10 +192,13 @@ class GatewayClass {
extract_cookie (this._options.cookie?.name, req.headers.cookie),
extract_cookie (this._options.refresh_cookie?.name, req.headers.cookie)
];
const tokens = auth_strings
.filter ((v) => v !== null)
.map ((v) => authority.verify (v as string))
.filter ((v) => v.valid);
const tokens = (
await Promise.all (
auth_strings
.filter ((v) => v !== null)
.map ((v) => authority.verify (v as string))
)
).filter ((v) => v.valid);
l ('found %d tokens: %O', tokens.length, tokens);
@ -210,10 +214,4 @@ export default function create_gateway (options: GatewayOptions): Gateway {
return g.process_request.bind (g);
}
export {
AnyFunc,
Gateway,
GatewayOptions,
GatewayClass,
RefreshSettings
};
export { AnyFunc, Gateway, GatewayOptions, GatewayClass, RefreshSettings };

16
lib/Key.ts Normal file
View File

@ -0,0 +1,16 @@
export interface Key {
key: string;
valid_until: number;
}
export interface LabelledKey extends Key {
index: string;
}
export interface KeyPair {
private_key?: Key;
public_key: Key;
}
export type KeyStoreData = Record<string, KeyPair>;
export type KeyStoreExport = LabelledKey[];

View File

@ -8,49 +8,18 @@
import { generate_keypair, random_hex } from '@sapphirecode/crypto-helper';
import { to_b58 } from '@sapphirecode/encoding-helper';
import { debug } from './debug';
import { KeyStoreData, KeyStoreExport } from './Key';
import { redis } from './Redis';
const logger = debug ('keystore');
const renew_interval = 3600;
interface Key {
key: string;
valid_until: number;
}
interface LabelledKey extends Key {
index: string;
}
interface KeyPair {
private_key?: Key;
public_key: Key;
}
type KeyStoreData = Record<string, KeyPair>;
type KeyStoreExport = LabelledKey[];
async function create_key (valid_for: number) {
logger ('generating new key');
const time = (new Date)
.getTime ();
const pair = await generate_keypair ();
return {
private_key: {
key: pair.private_key,
valid_until: time + (renew_interval * 1000)
},
public_key: {
key: pair.public_key,
valid_until: time + (valid_for * 1000)
}
};
}
class KeyStore {
private _keys: KeyStoreData = {};
private _interval: NodeJS.Timeout;
private _instance: string;
private _sync_redis = false;
public get instance_id (): string {
return this._instance;
@ -69,6 +38,27 @@ class KeyStore {
.toFixed (0);
}
private async create_key (index: string, valid_for: number): Promise<void> {
const log = logger.extend ('create_key');
log ('generating new key');
const time = (new Date)
.getTime ();
const pair = await generate_keypair ();
const result = {
private_key: {
key: pair.private_key,
valid_until: time + (renew_interval * 1000)
},
public_key: {
key: pair.public_key,
valid_until: time + (valid_for * 1000)
}
};
if (this._sync_redis)
await redis.set_key ({ ...result.public_key, index });
this._keys[index] = result;
}
private garbage_collect (): void {
const time = (new Date)
.getTime ();
@ -128,19 +118,26 @@ class KeyStore {
}
logger ('key does not exist, creating a new one');
this._keys[index] = await create_key (valid_for);
await this.create_key (index, valid_for);
return this._keys[index].private_key?.key as string;
}
public get_key (iat: number, instance?: string): string {
public async get_key (iat: number, instance?: string): Promise<string> {
logger ('querying public key from %s for timestamp %d', instance, iat);
const index = this.get_index (iat, instance);
if (typeof this._keys[index] === 'undefined')
let key = null;
if (typeof this._keys[index] === 'undefined') {
if (this._sync_redis)
key = await redis.get_key (index);
}
else { key = this._keys[index].public_key; }
if (key === null)
throw new Error ('key could not be found');
const key = this._keys[index];
return key.public_key.key;
return key.key;
}
public export_verification_data (): KeyStoreExport {
@ -172,9 +169,16 @@ class KeyStore {
logger ('resetting keystore');
this._instance = to_b58 (random_hex (16), 'hex');
this._keys = {};
this._sync_redis = false;
redis.disconnect ();
}
public sync_redis (url: string): void {
redis.connect (url);
this._sync_redis = true;
}
}
const ks: KeyStore = (new KeyStore);
export default ks;
export { KeyStore, Key, LabelledKey, KeyStoreExport };
export { KeyStore };

86
lib/Redis.ts Normal file
View File

@ -0,0 +1,86 @@
/*
* 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>, August 2022
*/
import IORedis from 'ioredis';
import { debug } from './debug';
import { LabelledKey } from './Key';
const logger = debug ('redis');
export class Redis {
private _redis: IORedis | null = null;
public connect (url: string): void {
const log = logger.extend ('connect');
log ('connecting to redis instance %s', url);
if (this._redis !== null) {
log ('disconnecting existing redis client');
this.disconnect ();
}
this._redis = new IORedis (url);
this._redis.on ('connect', () => {
log ('connected');
});
this._redis.on ('ready', () => {
log ('ready');
});
this._redis.on ('error', (err) => {
log ('error %o', err);
});
this._redis.on ('reconnecting', () => {
log ('reconnecting');
});
this._redis.on ('end', () => {
log ('connection ended');
});
}
public disconnect (): void {
const log = logger.extend ('disconnect');
log ('disconnecting redis client');
if (this._redis === null) {
log ('redis is inactive, skipping');
return;
}
this._redis.quit ();
this._redis = null;
log ('done');
}
public async set_key (key: LabelledKey): Promise<void> {
const log = logger.extend ('set_key');
log ('trying to set key %s to redis', key.index);
if (this._redis === null) {
log ('redis is inactive, skipping');
return;
}
const valid_for = (key.valid_until - (new Date)
.getTime ()) / 1000;
log ('key is valid for %d seconds', valid_for);
await this._redis.setex (key.index, valid_for, JSON.stringify (key));
log ('saved key');
}
public async get_key (index: string): Promise<LabelledKey | null> {
const log = logger.extend ('get_key');
log ('trying to get key %s from redis', index);
if (this._redis === null) {
log ('redis is inactive, skipping');
return null;
}
const res = await this._redis.get (index);
if (res === null) {
log ('key not found in redis');
return null;
}
log ('key found');
return JSON.parse (res);
}
}
export const redis = new Redis;

View File

@ -30,10 +30,11 @@ import create_gateway, {
AnyFunc,
RefreshSettings
} from './Gateway';
import keystore, {
KeyStore, KeyStoreExport,
import keystore, { KeyStore } from './KeyStore';
import {
KeyStoreExport,
LabelledKey, Key
} from './KeyStore';
} from './Key';
import {
CookieSettings,
SameSiteValue

View File

@ -1,6 +1,6 @@
{
"name": "@sapphirecode/auth-server-helper",
"version": "3.2.1",
"version": "3.3.0",
"main": "dist/index.js",
"author": {
"name": "Timo Hocker",
@ -15,22 +15,23 @@
"license": "MIT",
"devDependencies": {
"@sapphirecode/eslint-config-ts": "^1.1.27",
"@stryker-mutator/core": "^4.3.1",
"@stryker-mutator/jasmine-runner": "^4.3.1",
"@stryker-mutator/core": "^6.1.2",
"@stryker-mutator/jasmine-runner": "^6.1.2",
"@types/debug": "^4.1.7",
"@types/jasmine": "^3.6.2",
"@types/node": "^10.0.0",
"eslint": "^7.14.0",
"jasmine": "^3.6.3",
"jasmine-ts": "^0.3.3",
"@types/jasmine": "^4.0.3",
"@types/node": "^18.6.4",
"eslint": "^8.21.0",
"jasmine": "^4.3.0",
"nyc": "^15.1.0",
"ts-node": "^9.1.1",
"ts-node": "^10.9.1",
"typescript": "^4.1.2"
},
"scripts": {
"lint": "eslint . --ext .js,.jsx,.ts,.tsx,.vue,.mjs",
"test": "nyc jasmine-ts --config=\"jasmine.json\"",
"pretest": "yarn compile",
"test": "nyc jasmine --config=\"jasmine.json\"",
"mutate": "stryker run",
"precompile": "rm -rf dist",
"compile": "tsc"
},
"files": [
@ -45,10 +46,11 @@
"middleware"
],
"dependencies": {
"@sapphirecode/crypto-helper": "^1.3.0",
"@sapphirecode/crypto-helper": "^2.0.0",
"@sapphirecode/encoding-helper": "^1.1.0",
"@sapphirecode/utilities": "^1.8.8",
"debug": "^4.3.3"
"debug": "^4.3.3",
"ioredis": "^5.2.2"
},
"engines": {
"node": ">=10.0.0"

View File

@ -38,8 +38,8 @@ function check_headers (resp: Response): CheckHeaderResult {
return { data, at, rt };
}
function check_token (token: string|null, type: string): void {
const v = auth.verify (token || '');
async function check_token (token: string|null, type: string): Promise<void> {
const v = await auth.verify (token || '');
expect (v.valid)
.toEqual (true);
expect (v.authorized)
@ -164,11 +164,11 @@ describe ('auth handler', () => {
expect (resp1.headers['set-cookie'])
.toContain (build_cookie ({ name: 'mint_cookies' }, res1.rt as string));
check_token (res1.at as string, 'access_token');
await check_token (res1.at as string, 'access_token');
expect (res1.data.expires_in)
.toEqual (expires_seconds);
check_token (res1.rt as string, 'refresh_token');
await check_token (res1.rt as string, 'refresh_token');
expect (res1.data.refresh_expires_in)
.toEqual (refresh_expires_seconds);
@ -185,12 +185,12 @@ describe ('auth handler', () => {
expect (resp2.headers['set-cookie'])
.toContain (build_cookie ({ name: 'mint_cookies' }, res2.rt as string));
check_token (res2.at as string, 'access_token');
await check_token (res2.at as string, 'access_token');
expect (res2.data.expires_in)
.toEqual (expires_seconds);
expect (res2.at).not.toEqual (res1.at);
check_token (res2.rt as string, 'refresh_token');
await check_token (res2.rt as string, 'refresh_token');
expect (res2.data.refresh_expires_in)
.toEqual (refresh_expires_seconds);
expect (res2.rt).not.toEqual (res1.rt);
@ -217,11 +217,11 @@ describe ('auth handler', () => {
expect (resp1.headers['set-cookie'])
.toContain (build_cookie ({ name: 'mint_cookies' }, res1.rt as string));
check_token (res1.at as string, 'access_token');
await check_token (res1.at as string, 'access_token');
expect (res1.data.expires_in)
.toEqual (expires_seconds);
check_token (res1.rt as string, 'refresh_token');
await check_token (res1.rt as string, 'refresh_token');
expect (res1.data.refresh_expires_in)
.toEqual (refresh_expires_seconds);
});
@ -242,11 +242,11 @@ describe ('auth handler', () => {
expect (resp1.headers['set-cookie'])
.toContain (build_cookie ({ name: 'mint_cookies' }, res1.rt as string));
check_token (res1.at as string, 'access_token');
await check_token (res1.at as string, 'access_token');
expect (res1.data.expires_in)
.toEqual (expires_seconds);
check_token (res1.rt as string, 'refresh_token');
await check_token (res1.rt as string, 'refresh_token');
expect (res1.data.refresh_expires_in)
.toEqual (refresh_expires_seconds);
});
@ -299,7 +299,7 @@ describe ('auth handler', () => {
.toEqual ('bearer');
expect (res1.data.expires_in)
.toEqual (part_expires_seconds);
check_token (res1.data.part_token as string, 'part_token');
await check_token (res1.data.part_token as string, 'part_token');
const resp2 = await get (
{ authorization: `Bearer ${res1.data.part_token}` },
@ -315,12 +315,12 @@ describe ('auth handler', () => {
expect (resp2.headers['set-cookie'])
.toContain (build_cookie ({ name: 'mint_cookies' }, res2.rt as string));
check_token (res2.at as string, 'access_token');
await check_token (res2.at as string, 'access_token');
expect (res2.data.expires_in)
.toEqual (expires_seconds);
expect (res2.at).not.toEqual (res1.at);
check_token (res2.rt as string, 'refresh_token');
await check_token (res2.rt as string, 'refresh_token');
expect (res2.data.refresh_expires_in)
.toEqual (refresh_expires_seconds);
expect (res2.rt).not.toEqual (res1.rt);
@ -336,7 +336,7 @@ describe ('auth handler', () => {
'cookie_jar',
(resp1.headers['set-cookie'] || []).join ('\n')
);
check_token (signature, 'access_token');
await check_token (signature, 'access_token');
});
it ('should handle any authorization type', async () => {
@ -363,7 +363,7 @@ describe ('auth handler', () => {
(resp1.headers['set-cookie'] || []).join ('\n')
);
expect (signature).not.toEqual ('');
check_token (signature, 'access_token');
await check_token (signature, 'access_token');
});
it ('should disallow access and refresh cookies with the same name', () => {

View File

@ -27,7 +27,7 @@ describe ('authority', () => {
const token = await auth.sign ('access_token', 60);
jasmine.clock ()
.tick (30000);
const res = auth.verify (token.signature);
const res = await auth.verify (token.signature);
expect (res.authorized)
.toBeTrue ();
expect (res.valid)
@ -46,7 +46,7 @@ describe ('authority', () => {
const token = await auth.sign ('refresh_token', 600);
jasmine.clock ()
.tick (30000);
const res = auth.verify (token.signature);
const res = await auth.verify (token.signature);
expect (res.authorized)
.toBeFalse ();
expect (res.valid)
@ -65,7 +65,7 @@ describe ('authority', () => {
const token = await auth.sign ('part_token', 60, { next_module: '2fa' });
jasmine.clock ()
.tick (30000);
const res = auth.verify (token.signature);
const res = await auth.verify (token.signature);
expect (res.authorized)
.toBeFalse ();
expect (res.valid)
@ -85,7 +85,7 @@ describe ('authority', () => {
token.signature = modify_signature (token.signature);
jasmine.clock ()
.tick (30000);
const res = auth.verify (token.signature);
const res = await auth.verify (token.signature);
expect (res.authorized)
.toBeFalse ();
expect (res.valid)
@ -105,7 +105,7 @@ describe ('authority', () => {
jasmine.clock ()
.tick (30000);
bl.add_signature (token.id);
const res = auth.verify (token.signature);
const res = await auth.verify (token.signature);
expect (res.authorized)
.toBeFalse ();
expect (res.valid)
@ -125,7 +125,7 @@ describe ('authority', () => {
token.signature = modify_signature (token.signature);
jasmine.clock ()
.tick (30000);
const res = auth.verify (token.signature);
const res = await auth.verify (token.signature);
expect (res.authorized)
.toBeFalse ();
expect (res.valid)
@ -145,7 +145,7 @@ describe ('authority', () => {
jasmine.clock ()
.tick (30000);
bl.add_signature (token.id);
const res = auth.verify (token.signature);
const res = await auth.verify (token.signature);
expect (res.authorized)
.toBeFalse ();
expect (res.valid)

View File

@ -27,7 +27,7 @@ describe ('key store', () => {
.getTime () / 1000;
const duration = 10 * frame;
const key = await ks.get_sign_key (iat, duration);
const sign = ks.get_key (iat);
const sign = await ks.get_key (iat);
expect (typeof key)
.toEqual ('string');
expect (typeof sign)
@ -39,7 +39,7 @@ describe ('key store', () => {
const key = await ks.get_sign_key (keys[0].iat, 1);
expect (key)
.toEqual (keys[0].key);
const sign = ks.get_key (keys[0].iat);
const sign = await ks.get_key (keys[0].iat);
expect (sign)
.toEqual (keys[0].sign);
});
@ -48,7 +48,7 @@ describe ('key store', () => {
const key = await ks.get_sign_key (keys[0].iat + (frame / 2), 1);
expect (key)
.toEqual (keys[0].key);
const sign = ks.get_key (keys[0].iat + (frame / 2));
const sign = await ks.get_key (keys[0].iat + (frame / 2));
expect (sign)
.toEqual (keys[0].sign);
});
@ -60,7 +60,7 @@ describe ('key store', () => {
.getTime () / 1000;
const duration = 10 * frame;
const key = await ks.get_sign_key (iat, duration);
const sign = ks.get_key (iat);
const sign = await ks.get_key (iat);
expect (typeof key)
.toEqual ('string');
expect (key).not.toEqual (keys[0].key);
@ -69,32 +69,32 @@ describe ('key store', () => {
});
it ('should return both keys, but not the first sign key', async () => {
const sign = ks.get_key (keys[0].iat);
const sign = await ks.get_key (keys[0].iat);
expect (sign)
.toEqual (keys[0].sign);
await expectAsync (ks.get_sign_key (keys[0].iat, 1))
.toBeRejectedWithError ('cannot access already expired keys');
const k2 = await ks.get_sign_key (keys[1].iat, 1);
const s2 = ks.get_key (keys[1].iat);
const s2 = await ks.get_key (keys[1].iat);
expect (k2)
.toEqual (keys[1].key);
expect (s2)
.toEqual (keys[1].sign);
});
it ('should throw on non existing key', () => {
expect (() => ks.get_key (keys[1].iat + frame))
.toThrowError ('key could not be found');
it ('should throw on non existing key', async () => {
await expectAsync (ks.get_key (keys[1].iat + frame))
.toBeRejectedWithError ('key could not be found');
});
it ('should delete a key after it expires', () => {
it ('should delete a key after it expires', async () => {
// go to 10 frames + 1ms after key creation
jasmine.clock ()
.tick ((frame * 9e3) + 1);
// eslint-disable-next-line dot-notation
ks['garbage_collect'] ();
expect (() => ks.get_key (keys[0].iat))
.toThrowError ('key could not be found');
await expectAsync (ks.get_key (keys[0].iat))
.toBeRejectedWithError ('key could not be found');
});
it (
@ -102,7 +102,7 @@ describe ('key store', () => {
async () => {
await expectAsync (ks.get_sign_key (keys[1].iat, 1))
.toBeRejectedWithError ('cannot access already expired keys');
const sign = ks.get_key (keys[1].iat);
const sign = await ks.get_key (keys[1].iat);
expect (sign)
.toEqual (keys[1].sign);
}
@ -129,12 +129,12 @@ describe ('key store', () => {
jasmine.clock ()
.tick (step * 1000);
const key2 = await ks.get_sign_key (iat + step, duration2);
const sign = ks.get_key (iat);
const sign = await ks.get_key (iat);
expect (key1)
.toEqual (key2);
jasmine.clock ()
.tick (5000 * frame);
const signv = ks.get_key (iat + step);
const signv = await ks.get_key (iat + step);
expect (signv)
.toEqual (sign);
});
@ -151,7 +151,7 @@ describe ('key store', () => {
.getTime () / 1000;
const sign = await ks.get_sign_key (iat, frame);
const ver = ks.get_key (iat);
const ver = await ks.get_key (iat);
const exp = ks.export_verification_data ();
// eslint-disable-next-line dot-notation
expect (Object.keys (ks['_keys']))
@ -165,12 +165,12 @@ describe ('key store', () => {
.toEqual (exp.map ((v) => v.index));
const sign2 = await ks2.get_sign_key (iat, frame);
const ver2 = ks2.get_key (iat);
const ver2 = await ks2.get_key (iat);
expect (sign).not.toEqual (sign2);
expect (ver).not.toEqual (ver2);
await expectAsync (ks2.get_sign_key (iat, 60, ks.instance_id))
.toBeRejectedWithError ('cannot access already expired keys');
expect (ks2.get_key (iat, ks.instance_id))
expect (await ks2.get_key (iat, ks.instance_id))
.toEqual (ver);
});

62
test/spec/Redis.ts Normal file
View File

@ -0,0 +1,62 @@
/*
* 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>, August 2022
*/
import ks from '../../lib/KeyStore';
import { redis } from '../../lib/Redis';
import { clock_finalize, clock_setup } from '../Helper';
const frame = 3600;
const redis_url = process.env.TEST_REDIS_URL || 'redis://localhost';
describe ('redis', () => {
beforeAll (() => {
ks.reset_instance ();
ks.sync_redis (redis_url);
clock_setup ();
});
afterAll (() => clock_finalize ());
it ('should write and read all keys', async () => {
const iat1 = (new Date)
.getTime () / 1000;
await ks.get_sign_key (iat1, frame);
const k1 = await ks.get_key (iat1);
jasmine.clock ()
.tick (frame * 1000);
const iat2 = (new Date)
.getTime () / 1000;
await ks.get_sign_key (iat2, frame);
const k2 = await ks.get_key (iat2);
// eslint-disable-next-line dot-notation
const index1 = ks['get_index'] (iat1);
// eslint-disable-next-line dot-notation
const index2 = ks['get_index'] (iat2);
// eslint-disable-next-line dot-notation
expect (JSON.parse (await redis['_redis']?.get (index1) as string).key)
.toEqual (k1);
// eslint-disable-next-line dot-notation
expect (JSON.parse (await redis['_redis']?.get (index2) as string).key)
.toEqual (k2);
const old_instance = ks.instance_id;
ks.reset_instance ();
expectAsync (ks.get_key (iat1, old_instance))
.toBeRejectedWithError ('key could not be found');
expectAsync (ks.get_key (iat1, old_instance))
.toBeRejectedWithError ('key could not be found');
ks.sync_redis (redis_url);
expect (await ks.get_key (iat1, old_instance))
.toEqual (k1);
expect (await ks.get_key (iat2, old_instance))
.toEqual (k2);
});
});

View File

@ -2,13 +2,15 @@
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./lib",
"outDir": "./dist",
"strict": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"sourceMap": true
},
"exclude": ["test/**/*.ts"]
"include": [
"lib/**/*.ts",
"test/**/*.ts"
]
}

2822
yarn.lock

File diff suppressed because it is too large Load Diff