Compare commits

...

2 Commits

Author SHA1 Message Date
e80e3f9a94
improve debug, redis storage structure
Some checks failed
continuous-integration/drone/push Build is failing
2022-08-15 17:33:25 +02:00
b7514941f0
Revert "refactoring redis for multiple value classes"
This reverts commit d5c136790ee7521dc5d884e23b108a22c122c49d.
2022-08-15 13:56:02 +02:00
14 changed files with 3533 additions and 3447 deletions

View File

@ -78,7 +78,7 @@ class AuthRequest {
this._cookie = cookie; this._cookie = cookie;
this._refresh_cookie = refresh_cookie; this._refresh_cookie = refresh_cookie;
this._is_successful = false; this._is_successful = false;
logger ('started processing new auth request'); logger.extend ('constructor') ('started processing new auth request');
} }
private default_header (set_content = true) { private default_header (set_content = true) {
@ -97,7 +97,8 @@ class AuthRequest {
data, data,
leave_open leave_open
}: AccessSettings): Promise<AccessResult> { }: AccessSettings): Promise<AccessResult> {
logger ('allowed access'); const log = logger.extend ('allow_access');
log ('allowed access');
this.default_header (typeof redirect_to !== 'string' && !leave_open); this.default_header (typeof redirect_to !== 'string' && !leave_open);
const at = await auth.sign ( const at = await auth.sign (
@ -119,7 +120,7 @@ class AuthRequest {
cookies.push (build_cookie (this._cookie, at.signature)); cookies.push (build_cookie (this._cookie, at.signature));
if (include_refresh_token) { if (include_refresh_token) {
logger ('including refresh token'); log ('including refresh token');
if (typeof refresh_token_expires_in !== 'number') if (typeof refresh_token_expires_in !== 'number')
throw new Error ('no expiry time defined for refresh tokens'); throw new Error ('no expiry time defined for refresh tokens');
const rt = await auth.sign ( const rt = await auth.sign (
@ -136,7 +137,7 @@ class AuthRequest {
} }
if (cookies.length > 0) { if (cookies.length > 0) {
logger ('sending %d cookies', cookies.length); log ('sending %d cookies', cookies.length);
this.response.setHeader ( this.response.setHeader (
'Set-Cookie', 'Set-Cookie',
cookies cookies
@ -146,7 +147,7 @@ class AuthRequest {
this._is_successful = true; this._is_successful = true;
if (typeof redirect_to === 'string') { if (typeof redirect_to === 'string') {
logger ('redirecting to %s', redirect_to); log ('redirecting to %s', redirect_to);
this.response.setHeader ('Location', redirect_to); this.response.setHeader ('Location', redirect_to);
this.response.statusCode = 302; this.response.statusCode = 302;
if (!leave_open) if (!leave_open)
@ -155,7 +156,7 @@ class AuthRequest {
} }
if (!leave_open) { if (!leave_open) {
logger ('finishing http request'); log ('finishing http request');
this.response.writeHead (200); this.response.writeHead (200);
this.response.end (JSON.stringify (res)); this.response.end (JSON.stringify (res));
} }
@ -169,7 +170,8 @@ class AuthRequest {
data?: Record<string, unknown>, data?: Record<string, unknown>,
leave_open = false leave_open = false
): Promise<string> { ): Promise<string> {
logger ('allowed part token'); const log = logger.extend ('allow_part');
log ('allowed part token');
this.default_header (); this.default_header ();
const pt = await auth.sign ( const pt = await auth.sign (
@ -185,7 +187,7 @@ class AuthRequest {
}; };
if (!leave_open) { if (!leave_open) {
logger ('finishing http request'); log ('finishing http request');
this.response.writeHead (200); this.response.writeHead (200);
this.response.end (JSON.stringify (res)); this.response.end (JSON.stringify (res));
} }
@ -195,11 +197,12 @@ class AuthRequest {
} }
public invalid (error_description?: string, leave_open = false): void { public invalid (error_description?: string, leave_open = false): void {
logger ('rejecting invalid request'); const log = logger.extend ('invalid');
log ('rejecting invalid request');
this.default_header (); this.default_header ();
this.response.statusCode = 400; this.response.statusCode = 400;
if (!leave_open) { if (!leave_open) {
logger ('finishing http request'); log ('finishing http request');
this.response.end (JSON.stringify ({ this.response.end (JSON.stringify ({
error: 'invalid_request', error: 'invalid_request',
error_description error_description
@ -208,11 +211,12 @@ class AuthRequest {
} }
public deny (leave_open = false): void { public deny (leave_open = false): void {
logger ('denied access'); const log = logger.extend ('deny');
log ('denied access');
this.default_header (); this.default_header ();
this.response.statusCode = 401; this.response.statusCode = 401;
if (!leave_open) { if (!leave_open) {
logger ('finishing http request'); log ('finishing http request');
this.response.end (JSON.stringify ({ error: 'invalid_client' })); this.response.end (JSON.stringify ({ error: 'invalid_client' }));
} }
} }
@ -230,18 +234,19 @@ interface CreateHandlerOptions {
type ProcessRequestOptions = Omit<CreateHandlerOptions, 'parse_body'> type ProcessRequestOptions = Omit<CreateHandlerOptions, 'parse_body'>
// eslint-disable-next-line max-lines-per-function // eslint-disable-next-line max-lines-per-function, max-statements
async function process_request ( async function process_request (
request: AuthRequest, request: AuthRequest,
token: RegExpExecArray | null, token: RegExpExecArray | null,
default_handler: AuthRequestHandler, default_handler: AuthRequestHandler,
options?: ProcessRequestOptions options?: ProcessRequestOptions
): Promise<void> { ): Promise<void> {
const log = logger.extend ('process_request');
if (token === null) if (token === null)
return default_handler (request); return default_handler (request);
if ((/Basic/ui).test (token?.groups?.type as string)) { if ((/Basic/ui).test (token?.groups?.type as string)) {
logger ('found basic login data'); log ('found basic login data');
request.is_basic = true; request.is_basic = true;
let login = token?.groups?.token as string; let login = token?.groups?.token as string;
@ -255,7 +260,7 @@ async function process_request (
} }
if ((/Bearer/ui).test (token?.groups?.type as string)) { if ((/Bearer/ui).test (token?.groups?.type as string)) {
logger ('found bearer login data'); log ('found bearer login data');
request.is_bearer = true; request.is_bearer = true;
request.token = token?.groups?.token; request.token = token?.groups?.token;
@ -264,7 +269,7 @@ async function process_request (
if (!token_data.valid) if (!token_data.valid)
return default_handler (request); return default_handler (request);
logger ('bearer token is valid'); log ('bearer token is valid');
request.token_data = token_data.data; request.token_data = token_data.data;
request.token_id = token_data.id; request.token_id = token_data.id;
@ -274,7 +279,7 @@ async function process_request (
&& typeof options.refresh !== 'undefined' && typeof options.refresh !== 'undefined'
&& token_data.type === 'refresh_token' && token_data.type === 'refresh_token'
) { ) {
logger ('found refresh token, emitting new access token'); log ('found refresh token, emitting new access token');
request.allow_access (options.refresh); request.allow_access (options.refresh);
return Promise.resolve (); return Promise.resolve ();
} }
@ -287,7 +292,7 @@ async function process_request (
&& Object.keys (options.modules) && Object.keys (options.modules)
.includes (token_data.next_module) .includes (token_data.next_module)
) { ) {
logger ('processing module %s', token_data.next_module); log ('processing module %s', token_data.next_module);
return options.modules[token_data.next_module] (request); return options.modules[token_data.next_module] (request);
} }
@ -295,7 +300,7 @@ async function process_request (
return Promise.resolve (); return Promise.resolve ();
} }
logger ('no matching login method, triggering default handler'); log ('no matching login method, triggering default handler');
return default_handler (request); return default_handler (request);
} }
@ -304,7 +309,7 @@ export default function create_auth_handler (
default_handler: AuthRequestHandler, default_handler: AuthRequestHandler,
options?: CreateHandlerOptions options?: CreateHandlerOptions
): AuthHandler { ): AuthHandler {
logger ('creating new auth handler'); logger.extend ('create_auth_handler') ('creating new auth handler');
if ( if (
typeof options?.cookie !== 'undefined' typeof options?.cookie !== 'undefined'
&& typeof options?.refresh_cookie !== 'undefined' && typeof options?.refresh_cookie !== 'undefined'

View File

@ -42,7 +42,8 @@ interface SignatureOptions
class Authority { class Authority {
public async verify (key: string): Promise<VerificationResult> { public async verify (key: string): Promise<VerificationResult> {
logger ('verifying token'); const log = logger.extend ('verify');
log ('verifying token');
const result: VerificationResult = { const result: VerificationResult = {
authorized: false, authorized: false,
valid: false, valid: false,
@ -63,7 +64,7 @@ class Authority {
); );
if (data === null) { if (data === null) {
logger ('token invalid'); log ('token invalid');
result.error = 'invalid signature'; result.error = 'invalid signature';
return result; return result;
} }
@ -71,10 +72,10 @@ class Authority {
result.id = data.id; result.id = data.id;
result.type = data.type; result.type = data.type;
logger ('parsing token %s %s', result.type, result.id); log ('parsing token %s %s', result.type, result.id);
if (!blacklist.is_valid (data.id)) { if (!blacklist.is_valid (data.id)) {
logger ('token is blacklisted'); log ('token is blacklisted');
result.error = 'blacklisted'; result.error = 'blacklisted';
return result; return result;
} }
@ -84,7 +85,7 @@ class Authority {
result.next_module = data.next_module; result.next_module = data.next_module;
result.data = data.obj; result.data = data.obj;
logger ( log (
'valid %s; targeting module %s', 'valid %s; targeting module %s',
result.type, result.type,
result.next_module result.next_module
@ -98,7 +99,8 @@ class Authority {
valid_for: number, valid_for: number,
options?: SignatureOptions options?: SignatureOptions
): Promise<SignatureResult> { ): Promise<SignatureResult> {
logger ('signing new %s', type); const log = logger.extend ('sign');
log ('signing new %s', type);
const time = Date.now (); const time = Date.now ();
const key = await keystore.get_sign_key (time / 1000, valid_for); const key = await keystore.get_sign_key (time / 1000, valid_for);
const attributes = { const attributes = {
@ -110,7 +112,7 @@ class Authority {
next_module: options?.next_module next_module: options?.next_module
}; };
const signature = sign_object (options?.data, key, attributes); const signature = sign_object (options?.data, key, attributes);
logger ('created token %s', attributes.id); log ('created token %s', attributes.id);
return { id: attributes.id, signature }; return { id: attributes.id, signature };
} }
} }

View File

@ -22,46 +22,58 @@ class Blacklist {
} }
public clear (before: number = Number.POSITIVE_INFINITY): void { public clear (before: number = Number.POSITIVE_INFINITY): void {
logger ('clearing blacklist'); logger.extend ('clear') ('clearing blacklist');
for (let i = this._signatures.length - 1; i >= 0; i--) { for (let i = this._signatures.length - 1; i >= 0; i--) {
if (this._signatures[i].iat < before) if (this._signatures[i].iat < before)
this._signatures.splice (i, 1); this.remove_signature (i);
} }
} }
public add_signature (hash: string): void { public add_signature (hash: string): void {
logger ('blacklisting signature %s', hash); logger.extend ('add_signature') ('blacklisting signature %s', hash);
this._signatures.push ({ iat: Date.now (), hash }); this._signatures.push ({ iat: Date.now (), hash });
} }
public remove_signature (hash: string): void { public remove_signature (signature: number | string): void {
logger ('removing signature from blacklist %s', hash); const log = logger.extend ('remove_signature');
for (let i = this._signatures.length - 1; i >= 0; i--) { log ('removing signature from blacklist %s', signature);
if (this._signatures[i].hash === hash) if (typeof signature === 'string') {
this._signatures.splice (i, 1); log ('received string, searching through signatures');
for (let i = this._signatures.length - 1; i >= 0; i--) {
if (this._signatures[i].hash === signature)
this._signatures.splice (i, 1);
}
}
else {
log ('received index, removing at index');
this._signatures.splice (signature, 1);
} }
} }
public is_valid (hash: string): boolean { public is_valid (hash: string): boolean {
logger ('checking signature for blacklist entry %s', hash); const log = logger.extend ('is_valid');
log ('checking signature for blacklist entry %s', hash);
for (const sig of this._signatures) { for (const sig of this._signatures) {
if (sig.hash === hash) { if (sig.hash === hash) {
logger ('found matching blacklist entry'); log ('found matching blacklist entry');
return false; return false;
} }
} }
logger ('signature is not blacklisted'); log ('signature is not blacklisted');
return true; return true;
} }
public export_blacklist (): Signature[] { public export_blacklist (): Signature[] {
logger ('exporting blacklist'); logger.extend ('export_blacklist') ('exporting blacklist');
return this._signatures; return this._signatures;
} }
public import_blacklist (data: Signature[]): void { public import_blacklist (data: Signature[]): void {
logger ('importing %d blacklist entries', data.length); logger.extend ('import_blacklist') (
'importing %d blacklist entries',
data.length
);
this._signatures.push (...data); this._signatures.push (...data);
} }
} }

View File

@ -38,7 +38,8 @@ class GatewayClass {
private _options: GatewayOptions; private _options: GatewayOptions;
public constructor (options: GatewayOptions = {}) { public constructor (options: GatewayOptions = {}) {
logger ('creating new gateway'); const log = logger.extend ('constructor');
log ('creating new gateway');
if ( if (
typeof options?.cookie !== 'undefined' typeof options?.cookie !== 'undefined'
&& typeof options?.refresh_cookie !== 'undefined' && typeof options?.refresh_cookie !== 'undefined'
@ -50,15 +51,16 @@ class GatewayClass {
} }
public deny (res: ServerResponse): void { public deny (res: ServerResponse): void {
logger ('denied http request'); logger.extend ('deny') ('denied http request');
res.statusCode = 403; res.statusCode = 403;
res.end (); res.end ();
} }
public redirect (res: ServerResponse): void { public redirect (res: ServerResponse): void {
logger ('redirecting http request to %s', this._options.redirect_url); const log = logger.extend ('redirect');
log ('redirecting http request to %s', this._options.redirect_url);
if (typeof this._options.redirect_url !== 'string') { if (typeof this._options.redirect_url !== 'string') {
logger ('no redirect url defined'); log ('no redirect url defined');
this.deny (res); this.deny (res);
return; return;
} }
@ -68,34 +70,36 @@ class GatewayClass {
} }
public get_header_auth (req: IncomingMessage): string | null { public get_header_auth (req: IncomingMessage): string | null {
logger ('extracting authorization header'); const log = logger.extend ('get_header_auth');
log ('extracting authorization header');
const auth_header = req.headers.authorization; const auth_header = req.headers.authorization;
const auth = (/(?<type>\w+) (?<data>.*)/u).exec (auth_header || ''); const auth = (/(?<type>\w+) (?<data>.*)/u).exec (auth_header || '');
if (auth === null) if (auth === null)
return null; return null;
if (auth.groups?.type !== 'Bearer') if (auth.groups?.type !== 'Bearer')
return null; return null;
logger ('found bearer token'); log ('found bearer token');
return auth.groups?.data; return auth.groups?.data;
} }
public async try_access (req: IncomingMessage): Promise<boolean> { public async try_access (req: IncomingMessage): Promise<boolean> {
logger ('authenticating incoming request'); const log = logger.extend ('try_access');
log ('authenticating incoming request');
let auth = this.get_header_auth (req); let auth = this.get_header_auth (req);
if (auth === null) if (auth === null)
auth = extract_cookie (this._options.cookie?.name, req.headers.cookie); auth = extract_cookie (this._options.cookie?.name, req.headers.cookie);
if (auth === null) { if (auth === null) {
logger ('found no auth token'); log ('found no auth token');
return false; return false;
} }
const ver = await authority.verify (auth); const ver = await authority.verify (auth);
logger ('setting connection info'); log ('setting connection info');
const con = req.connection as unknown as Record<string, unknown>; const con = req.connection as unknown as Record<string, unknown>;
con.auth = { token_id: ver.id, token_data: ver.data }; con.auth = { token_id: ver.id, token_data: ver.data };
logger ('token valid: %s', ver.authorized); log ('token valid: %s', ver.authorized);
return ver.authorized; return ver.authorized;
} }
@ -103,26 +107,27 @@ class GatewayClass {
req: IncomingMessage, req: IncomingMessage,
res: ServerResponse res: ServerResponse
): Promise<boolean> { ): Promise<boolean> {
const log = logger.extend ('try_refresh');
if ( if (
typeof this._options.refresh_cookie === 'undefined' typeof this._options.refresh_cookie === 'undefined'
|| typeof this._options.refresh_settings === 'undefined' || typeof this._options.refresh_settings === 'undefined'
) )
return false; return false;
logger ('trying to apply refresh token'); log ('trying to apply refresh token');
const refresh = extract_cookie ( const refresh = extract_cookie (
this._options.refresh_cookie.name, this._options.refresh_cookie.name,
req.headers.cookie req.headers.cookie
); );
if (refresh === null) { if (refresh === null) {
logger ('could not find refresh token'); log ('could not find refresh token');
return false; return false;
} }
const ver = await authority.verify (refresh); const ver = await authority.verify (refresh);
if (ver.type === 'refresh_token' && ver.valid) { if (ver.type === 'refresh_token' && ver.valid) {
logger ('refresh token valid, generating new tokens'); log ('refresh token valid, generating new tokens');
const auth_request = new AuthRequest ( const auth_request = new AuthRequest (
req, req,
res, res,
@ -136,18 +141,18 @@ class GatewayClass {
leave_open: true leave_open: true
}); });
logger ('setting connection info'); log ('setting connection info');
const con = req.connection as unknown as Record<string, unknown>; const con = req.connection as unknown as Record<string, unknown>;
con.auth = { con.auth = {
token_id: refresh_result.access_token_id, token_id: refresh_result.access_token_id,
token_data: ver.data token_data: ver.data
}; };
logger ('tokens refreshed'); log ('tokens refreshed');
return true; return true;
} }
logger ('refresh token invalid'); log ('refresh token invalid');
return false; return false;
} }
@ -155,17 +160,18 @@ class GatewayClass {
req: IncomingMessage, req: IncomingMessage,
res: ServerResponse res: ServerResponse
): Promise<boolean> { ): Promise<boolean> {
logger ('trying to authenticate http request'); const log = logger.extend ('authenticate');
log ('trying to authenticate http request');
if (await this.try_access (req)) { if (await this.try_access (req)) {
logger ('authenticated via access_token'); log ('authenticated via access_token');
return true; return true;
} }
if (await this.try_refresh (req, res)) { if (await this.try_refresh (req, res)) {
logger ('authenticated via refresh_token'); log ('authenticated via refresh_token');
return true; return true;
} }
logger ('could not verify session'); log ('could not verify session');
return false; return false;
} }
@ -174,19 +180,20 @@ class GatewayClass {
res: ServerResponse, res: ServerResponse,
next: AnyFunc next: AnyFunc
): Promise<unknown> { ): Promise<unknown> {
logger ('processing incoming http request'); const log = logger.extend ('process_request');
log ('processing incoming http request');
if (await this.authenticate (req, res)) { if (await this.authenticate (req, res)) {
logger ('authentification successful, calling next handler'); log ('authentification successful, calling next handler');
return next (); return next ();
} }
logger ('failed to authenticate, redirecting client'); log ('failed to authenticate, redirecting client');
return this.redirect (res); return this.redirect (res);
} }
public async logout (req: IncomingMessage): Promise<void> { public async logout (req: IncomingMessage): Promise<void> {
const l = logger.extend ('logout'); const log = logger.extend ('logout');
l ('invalidating all submitted tokens'); log ('invalidating all submitted tokens');
const auth_strings = [ const auth_strings = [
this.get_header_auth (req), this.get_header_auth (req),
extract_cookie (this._options.cookie?.name, req.headers.cookie), extract_cookie (this._options.cookie?.name, req.headers.cookie),
@ -200,12 +207,12 @@ class GatewayClass {
) )
).filter ((v) => v.valid); ).filter ((v) => v.valid);
l ('found %d tokens: %O', tokens.length, tokens); log ('found %d tokens: %O', tokens.length, tokens);
for (const token of tokens) for (const token of tokens)
blacklist.add_signature (token.id); blacklist.add_signature (token.id);
l ('complete'); log ('complete');
} }
} }

View File

@ -1,3 +1,10 @@
/*
* 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
*/
export interface Key { export interface Key {
key: string; key: string;
valid_until: number; valid_until: number;

View File

@ -10,6 +10,7 @@ import { to_b58 } from '@sapphirecode/encoding-helper';
import { debug } from './debug'; import { debug } from './debug';
import { KeyStoreData, KeyStoreExport } from './Key'; import { KeyStoreData, KeyStoreExport } from './Key';
import { redis } from './Redis'; import { redis } from './Redis';
import { redis_key_store } from './RedisData/RedisKeyStore';
const logger = debug ('keystore'); const logger = debug ('keystore');
@ -30,7 +31,10 @@ class KeyStore {
this.garbage_collect (); this.garbage_collect ();
}, renew_interval); }, renew_interval);
this._instance = to_b58 (random_hex (16), 'hex'); this._instance = to_b58 (random_hex (16), 'hex');
logger ('created keystore instance %s', this._instance); logger.extend ('constructor') (
'created keystore instance %s',
this._instance
);
} }
private get_index (iat: number, instance = this._instance): string { private get_index (iat: number, instance = this._instance): string {
@ -55,11 +59,12 @@ class KeyStore {
} }
}; };
if (this._sync_redis) if (this._sync_redis)
await redis.set_key ({ ...result.public_key, index }); await redis_key_store.set ({ ...result.public_key, index });
this._keys[index] = result; this._keys[index] = result;
} }
private garbage_collect (): void { private garbage_collect (): void {
const log = logger.extend ('garbage_collect');
const time = (new Date) const time = (new Date)
.getTime (); .getTime ();
const keys = Object.keys (this._keys); const keys = Object.keys (this._keys);
@ -68,12 +73,12 @@ class KeyStore {
if (typeof entry.private_key !== 'undefined' if (typeof entry.private_key !== 'undefined'
&& entry.private_key.valid_until < time && entry.private_key.valid_until < time
) { ) {
logger ('deleting expired private key'); log ('deleting expired private key');
delete entry.private_key; delete entry.private_key;
} }
if (entry.public_key.valid_until < time) { if (entry.public_key.valid_until < time) {
logger ('deleting expired key pair'); log ('deleting expired key pair');
delete this._keys[index]; delete this._keys[index];
} }
} }
@ -84,7 +89,8 @@ class KeyStore {
valid_for: number, valid_for: number,
instance?: string instance?: string
): Promise<string> { ): Promise<string> {
logger ( const log = logger.extend ('get_sign_key');
log (
'querying key from %s for timestamp %d, valid for %d', 'querying key from %s for timestamp %d, valid for %d',
instance, instance,
iat, iat,
@ -103,34 +109,35 @@ class KeyStore {
.getTime () + (valid_for * 1000); .getTime () + (valid_for * 1000);
if (typeof this._keys[index] !== 'undefined') { if (typeof this._keys[index] !== 'undefined') {
logger ('loading existing key'); log ('loading existing key');
const key = this._keys[index]; const key = this._keys[index];
if (typeof key.private_key === 'undefined') if (typeof key.private_key === 'undefined')
throw new Error ('cannot access already expired keys'); throw new Error ('cannot access already expired keys');
if (key.public_key.valid_until < valid_until) { if (key.public_key.valid_until < valid_until) {
logger ('updating key valid timespan to match new value'); log ('updating key valid timespan to match new value');
key.public_key.valid_until = valid_until; key.public_key.valid_until = valid_until;
} }
return key.private_key?.key as string; return key.private_key?.key as string;
} }
logger ('key does not exist, creating a new one'); log ('key does not exist, creating a new one');
await this.create_key (index, valid_for); await this.create_key (index, valid_for);
return this._keys[index].private_key?.key as string; return this._keys[index].private_key?.key as string;
} }
public async get_key (iat: number, instance?: string): Promise<string> { public async get_key (iat: number, instance?: string): Promise<string> {
logger ('querying public key from %s for timestamp %d', instance, iat); const log = logger.extend ('get_key');
log ('querying public key from %s for timestamp %d', instance, iat);
const index = this.get_index (iat, instance); const index = this.get_index (iat, instance);
let key = null; let key = null;
if (typeof this._keys[index] === 'undefined') { if (typeof this._keys[index] === 'undefined') {
if (this._sync_redis) if (this._sync_redis)
key = await redis.get_key (index); key = await redis_key_store.get (index);
} }
else { key = this._keys[index].public_key; } else { key = this._keys[index].public_key; }
@ -141,18 +148,23 @@ class KeyStore {
} }
public export_verification_data (): KeyStoreExport { public export_verification_data (): KeyStoreExport {
logger ('exporting public keys'); const log = logger.extend ('export_verification_data');
log ('exporting public keys');
log ('cleaning up before export');
this.garbage_collect (); this.garbage_collect ();
const out: KeyStoreExport = []; const out: KeyStoreExport = [];
for (const index of Object.keys (this._keys)) for (const index of Object.keys (this._keys)) {
log ('exporting key %s', index);
out.push ({ ...this._keys[index].public_key, index }); out.push ({ ...this._keys[index].public_key, index });
}
return out; return out;
} }
public import_verification_data (data: KeyStoreExport): void { public import_verification_data (data: KeyStoreExport): void {
logger ('importing %d public keys', data.length); const log = logger.extend ('import_verification_data');
log ('importing %d public keys', data.length);
for (const key of data) { for (const key of data) {
log ('importing key %s', key.index);
if (typeof this._keys[key.index] !== 'undefined') if (typeof this._keys[key.index] !== 'undefined')
throw new Error ('cannot import to the same instance'); throw new Error ('cannot import to the same instance');
this._keys[key.index] = { this._keys[key.index] = {
@ -162,11 +174,12 @@ class KeyStore {
} }
}; };
} }
log ('running garbage collector');
this.garbage_collect (); this.garbage_collect ();
} }
public reset_instance (): void { public reset_instance (): void {
logger ('resetting keystore'); logger.extend ('reset_instance') ('resetting keystore');
this._instance = to_b58 (random_hex (16), 'hex'); this._instance = to_b58 (random_hex (16), 'hex');
this._keys = {}; this._keys = {};
this._sync_redis = false; this._sync_redis = false;

View File

@ -7,13 +7,9 @@
import IORedis from 'ioredis'; import IORedis from 'ioredis';
import { debug } from './debug'; import { debug } from './debug';
import { LabelledKey } from './Key';
const logger = debug ('redis'); const logger = debug ('redis');
export type SyncClass = 'blacklist' | 'keystore'
export type SyncValue = LabelledKey | string;
export class Redis { export class Redis {
private _redis: IORedis | null = null; private _redis: IORedis | null = null;
@ -55,47 +51,15 @@ export class Redis {
log ('done'); log ('done');
} }
public async set ( public get redis (): IORedis {
sync_class: SyncClass, if (this._redis === null)
key: string, throw new Error ('redis is not connected');
value: SyncValue
): Promise<void> {
const log = logger.extend ('set');
log ('trying to set %s value %s to redis', sync_class, key);
if (this._redis === null) {
log ('redis is inactive, skipping');
return;
}
let valid_for = null;
if (sync_class === 'keystore') {
valid_for = Math.floor (
(key.valid_until - (new Date)
.getTime ()) / 1000
);
log ('key is valid for %d seconds', valid_for);
}
if (valid_for === null)
await this._redis.set (key, JSON.stringify (value));
else
await this._redis.setex (key, valid_for, JSON.stringify (value));
log ('saved value'); return this._redis;
} }
public async get_key (index: string): Promise<LabelledKey | null> { public get is_active (): boolean {
const log = logger.extend ('get_key'); return this._redis !== null;
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);
} }
} }

View File

@ -0,0 +1,36 @@
/*
* 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 { debug } from '../debug';
import { redis } from '../Redis';
const logger = debug ('RedisBlacklistStore');
export class RedisBlacklistStore {
public async add (key: string): Promise<void> {
const log = logger.extend ('set');
log ('trying to add key %s to redis blacklist', key);
if (!redis.is_active) {
log ('redis is inactive, skipping');
return;
}
await redis.redis.sadd ('blacklist', key);
log ('saved key');
}
public async get (key: string): Promise<boolean> {
const log = logger.extend ('get');
log ('trying to find key %s in redis blacklist', key);
if (!redis.is_active) {
log ('redis is inactive, skipping');
return false;
}
const res = await redis.redis.sismember ('blacklist', key) === 1;
log ('found key %s', res);
return res;
}
}

View File

@ -0,0 +1,52 @@
/*
* 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 { debug } from '../debug';
import { LabelledKey } from '../Key';
import { redis } from '../Redis';
const logger = debug ('RedisKeyStore');
export class RedisKeyStore {
public async set (value: LabelledKey): Promise<void> {
const log = logger.extend ('set');
log ('trying to set key %s to redis', value.index);
if (!redis.is_active) {
log ('redis is inactive, skipping');
return;
}
const valid_for = Math.floor (
(value.valid_until - (new Date)
.getTime ()) / 1000
);
log ('key is valid for %d seconds', valid_for);
await redis.redis.setex (
`keystore_${value.index}`,
valid_for,
JSON.stringify (value)
);
log ('saved key');
}
public async get (index: string): Promise<LabelledKey | null> {
const log = logger.extend ('get');
log ('trying to get key %s from redis', index);
if (!redis.is_active) {
log ('redis is inactive, skipping');
return null;
}
const res = await redis.redis.get (`keystore_${index}`);
if (res === null) {
log ('key not found in redis');
return null;
}
log ('key found');
return JSON.parse (res);
}
}
export const redis_key_store = new RedisKeyStore;

View File

@ -1,3 +1,10 @@
/*
* 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 { run_regex } from '@sapphirecode/utilities'; import { run_regex } from '@sapphirecode/utilities';
import { debug } from './debug'; import { debug } from './debug';
@ -54,7 +61,8 @@ function extract_cookie (
name: string|undefined, name: string|undefined,
header: string|undefined header: string|undefined
): string| null { ): string| null {
logger (`extracting cookie ${name}`); const log = logger.extend ('extract_cookie');
log (`extracting cookie ${name}`);
const cookie_regex = /(?:^|;)\s*(?<name>[^;=]+)=(?<value>[^;]+)/gu; const cookie_regex = /(?:^|;)\s*(?<name>[^;=]+)=(?<value>[^;]+)/gu;
@ -64,9 +72,9 @@ function extract_cookie (
cookie_regex, cookie_regex,
header, header,
(res: RegExpMatchArray) => { (res: RegExpMatchArray) => {
logger ('parsing cookie %s', res.groups?.name); log ('parsing cookie %s', res.groups?.name);
if (res.groups?.name === name) { if (res.groups?.name === name) {
logger ('found cookie'); log ('found cookie');
result = res.groups?.value as string; result = res.groups?.value as string;
} }
} }

View File

@ -1,3 +1,10 @@
/*
* 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 build_debug from 'debug'; import build_debug from 'debug';
function debug (scope: string): build_debug.Debugger { function debug (scope: string): build_debug.Debugger {

View File

@ -19,33 +19,46 @@ describe ('redis', () => {
clock_setup (); clock_setup ();
}); });
let iat1 = 0;
let iat2 = 0;
let k1 = '';
let k2 = '';
let i1 = '';
let i2 = '';
afterAll (() => clock_finalize ()); afterAll (() => clock_finalize ());
it ('should write and read all keys', async () => { it ('should generate two keys', async () => {
const iat1 = (new Date) iat1 = (new Date)
.getTime () / 1000; .getTime () / 1000;
await ks.get_sign_key (iat1, frame); await ks.get_sign_key (iat1, frame);
const k1 = await ks.get_key (iat1); k1 = await ks.get_key (iat1);
jasmine.clock () jasmine.clock ()
.tick (frame * 1000); .tick (frame * 1000);
const iat2 = (new Date) iat2 = (new Date)
.getTime () / 1000; .getTime () / 1000;
await ks.get_sign_key (iat2, frame); await ks.get_sign_key (iat2, frame);
const k2 = await ks.get_key (iat2); k2 = await ks.get_key (iat2);
// eslint-disable-next-line dot-notation // eslint-disable-next-line dot-notation
const index1 = ks['get_index'] (iat1); i1 = ks['get_index'] (iat1);
// eslint-disable-next-line dot-notation // eslint-disable-next-line dot-notation
const index2 = ks['get_index'] (iat2); i2 = ks['get_index'] (iat2);
});
it ('should have two keys in redis', async () => {
// eslint-disable-next-line dot-notation // eslint-disable-next-line dot-notation
expect (JSON.parse (await redis['_redis']?.get (index1) as string).key) expect (JSON.parse (await redis['_redis']
?.get (`keystore_${i1}`) as string).key)
.toEqual (k1); .toEqual (k1);
// eslint-disable-next-line dot-notation // eslint-disable-next-line dot-notation
expect (JSON.parse (await redis['_redis']?.get (index2) as string).key) expect (JSON.parse (await redis['_redis']
?.get (`keystore_${i2}`) as string).key)
.toEqual (k2); .toEqual (k2);
});
it ('should read two keys with a new instance', async () => {
const old_instance = ks.instance_id; const old_instance = ks.instance_id;
ks.reset_instance (); ks.reset_instance ();
expectAsync (ks.get_key (iat1, old_instance)) expectAsync (ks.get_key (iat1, old_instance))

166
yarn.lock
View File

@ -401,9 +401,9 @@
"@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/sourcemap-codec" "^1.4.10"
"@jridgewell/trace-mapping@^0.3.9": "@jridgewell/trace-mapping@^0.3.9":
version "0.3.14" version "0.3.15"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774"
integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ== integrity sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==
dependencies: dependencies:
"@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/resolve-uri" "^3.0.3"
"@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/sourcemap-codec" "^1.4.10"
@ -612,18 +612,18 @@
integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==
"@types/node@^18.6.4": "@types/node@^18.6.4":
version "18.6.4" version "18.7.4"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.6.4.tgz#fd26723a8a3f8f46729812a7f9b4fc2d1608ed39" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.4.tgz#95baa50846ae112a7376869d49fec23b2506c69d"
integrity sha512-I4BD3L+6AWiUobfxZ49DlU43gtI+FTHSv9pE2Zekg6KjMpre4ByusaljW3vYSLJrvQ1ck1hUaeVu8HVlY3vzHg== integrity sha512-RzRcw8c0B8LzryWOR4Wj7YOTFXvdYKwvrb6xQQyuDfnlTxwYXGCV5RZ/TEbq5L5kn+w3rliHAUyRcG1RtbmTFg==
"@typescript-eslint/eslint-plugin@^5.0.0": "@typescript-eslint/eslint-plugin@^5.0.0":
version "5.32.0" version "5.33.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.32.0.tgz#e27e38cffa4a61226327c874a7be965e9a861624" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.33.0.tgz#059798888720ec52ffa96c5f868e31a8f70fa3ec"
integrity sha512-CHLuz5Uz7bHP2WgVlvoZGhf0BvFakBJKAD/43Ty0emn4wXWv5k01ND0C0fHcl/Im8Td2y/7h44E9pca9qAu2ew== integrity sha512-jHvZNSW2WZ31OPJ3enhLrEKvAZNyAFWZ6rx9tUwaessTc4sx9KmgMNhVcqVAl1ETnT5rU5fpXTLmY9YvC1DCNg==
dependencies: dependencies:
"@typescript-eslint/scope-manager" "5.32.0" "@typescript-eslint/scope-manager" "5.33.0"
"@typescript-eslint/type-utils" "5.32.0" "@typescript-eslint/type-utils" "5.33.0"
"@typescript-eslint/utils" "5.32.0" "@typescript-eslint/utils" "5.33.0"
debug "^4.3.4" debug "^4.3.4"
functional-red-black-tree "^1.0.1" functional-red-black-tree "^1.0.1"
ignore "^5.2.0" ignore "^5.2.0"
@ -632,68 +632,68 @@
tsutils "^3.21.0" tsutils "^3.21.0"
"@typescript-eslint/parser@^5.0.0": "@typescript-eslint/parser@^5.0.0":
version "5.32.0" version "5.33.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.32.0.tgz#1de243443bc6186fb153b9e395b842e46877ca5d" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.33.0.tgz#26ec3235b74f0667414613727cb98f9b69dc5383"
integrity sha512-IxRtsehdGV9GFQ35IGm5oKKR2OGcazUoiNBxhRV160iF9FoyuXxjY+rIqs1gfnd+4eL98OjeGnMpE7RF/NBb3A== integrity sha512-cgM5cJrWmrDV2KpvlcSkelTBASAs1mgqq+IUGKJvFxWrapHpaRy5EXPQz9YaKF3nZ8KY18ILTiVpUtbIac86/w==
dependencies: dependencies:
"@typescript-eslint/scope-manager" "5.32.0" "@typescript-eslint/scope-manager" "5.33.0"
"@typescript-eslint/types" "5.32.0" "@typescript-eslint/types" "5.33.0"
"@typescript-eslint/typescript-estree" "5.32.0" "@typescript-eslint/typescript-estree" "5.33.0"
debug "^4.3.4" debug "^4.3.4"
"@typescript-eslint/scope-manager@5.32.0": "@typescript-eslint/scope-manager@5.33.0":
version "5.32.0" version "5.33.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.32.0.tgz#763386e963a8def470580cc36cf9228864190b95" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.33.0.tgz#509d7fa540a2c58f66bdcfcf278a3fa79002e18d"
integrity sha512-KyAE+tUON0D7tNz92p1uetRqVJiiAkeluvwvZOqBmW9z2XApmk5WSMV9FrzOroAcVxJZB3GfUwVKr98Dr/OjOg== integrity sha512-/Jta8yMNpXYpRDl8EwF/M8It2A9sFJTubDo0ATZefGXmOqlaBffEw0ZbkbQ7TNDK6q55NPHFshGBPAZvZkE8Pw==
dependencies: dependencies:
"@typescript-eslint/types" "5.32.0" "@typescript-eslint/types" "5.33.0"
"@typescript-eslint/visitor-keys" "5.32.0" "@typescript-eslint/visitor-keys" "5.33.0"
"@typescript-eslint/type-utils@5.32.0": "@typescript-eslint/type-utils@5.33.0":
version "5.32.0" version "5.33.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.32.0.tgz#45a14506fe3fb908600b4cef2f70778f7b5cdc79" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.33.0.tgz#92ad1fba973c078d23767ce2d8d5a601baaa9338"
integrity sha512-0gSsIhFDduBz3QcHJIp3qRCvVYbqzHg8D6bHFsDMrm0rURYDj+skBK2zmYebdCp+4nrd9VWd13egvhYFJj/wZg== integrity sha512-2zB8uEn7hEH2pBeyk3NpzX1p3lF9dKrEbnXq1F7YkpZ6hlyqb2yZujqgRGqXgRBTHWIUG3NGx/WeZk224UKlIA==
dependencies: dependencies:
"@typescript-eslint/utils" "5.32.0" "@typescript-eslint/utils" "5.33.0"
debug "^4.3.4" debug "^4.3.4"
tsutils "^3.21.0" tsutils "^3.21.0"
"@typescript-eslint/types@5.32.0": "@typescript-eslint/types@5.33.0":
version "5.32.0" version "5.33.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.32.0.tgz#484273021eeeae87ddb288f39586ef5efeb6dcd8" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.33.0.tgz#d41c584831805554b063791338b0220b613a275b"
integrity sha512-EBUKs68DOcT/EjGfzywp+f8wG9Zw6gj6BjWu7KV/IYllqKJFPlZlLSYw/PTvVyiRw50t6wVbgv4p9uE2h6sZrQ== integrity sha512-nIMt96JngB4MYFYXpZ/3ZNU4GWPNdBbcB5w2rDOCpXOVUkhtNlG2mmm8uXhubhidRZdwMaMBap7Uk8SZMU/ppw==
"@typescript-eslint/typescript-estree@5.32.0": "@typescript-eslint/typescript-estree@5.33.0":
version "5.32.0" version "5.33.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.32.0.tgz#282943f34babf07a4afa7b0ff347a8e7b6030d12" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.33.0.tgz#02d9c9ade6f4897c09e3508c27de53ad6bfa54cf"
integrity sha512-ZVAUkvPk3ITGtCLU5J4atCw9RTxK+SRc6hXqLtllC2sGSeMFWN+YwbiJR9CFrSFJ3w4SJfcWtDwNb/DmUIHdhg== integrity sha512-tqq3MRLlggkJKJUrzM6wltk8NckKyyorCSGMq4eVkyL5sDYzJJcMgZATqmF8fLdsWrW7OjjIZ1m9v81vKcaqwQ==
dependencies: dependencies:
"@typescript-eslint/types" "5.32.0" "@typescript-eslint/types" "5.33.0"
"@typescript-eslint/visitor-keys" "5.32.0" "@typescript-eslint/visitor-keys" "5.33.0"
debug "^4.3.4" debug "^4.3.4"
globby "^11.1.0" globby "^11.1.0"
is-glob "^4.0.3" is-glob "^4.0.3"
semver "^7.3.7" semver "^7.3.7"
tsutils "^3.21.0" tsutils "^3.21.0"
"@typescript-eslint/utils@5.32.0": "@typescript-eslint/utils@5.33.0":
version "5.32.0" version "5.33.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.32.0.tgz#eccb6b672b94516f1afc6508d05173c45924840c" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.33.0.tgz#46797461ce3146e21c095d79518cc0f8ec574038"
integrity sha512-W7lYIAI5Zlc5K082dGR27Fczjb3Q57ECcXefKU/f0ajM5ToM0P+N9NmJWip8GmGu/g6QISNT+K6KYB+iSHjXCQ== integrity sha512-JxOAnXt9oZjXLIiXb5ZIcZXiwVHCkqZgof0O8KPgz7C7y0HS42gi75PdPlqh1Tf109M0fyUw45Ao6JLo7S5AHw==
dependencies: dependencies:
"@types/json-schema" "^7.0.9" "@types/json-schema" "^7.0.9"
"@typescript-eslint/scope-manager" "5.32.0" "@typescript-eslint/scope-manager" "5.33.0"
"@typescript-eslint/types" "5.32.0" "@typescript-eslint/types" "5.33.0"
"@typescript-eslint/typescript-estree" "5.32.0" "@typescript-eslint/typescript-estree" "5.33.0"
eslint-scope "^5.1.1" eslint-scope "^5.1.1"
eslint-utils "^3.0.0" eslint-utils "^3.0.0"
"@typescript-eslint/visitor-keys@5.32.0": "@typescript-eslint/visitor-keys@5.33.0":
version "5.32.0" version "5.33.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.32.0.tgz#b9715d0b11fdb5dd10fd0c42ff13987470525394" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.33.0.tgz#fbcbb074e460c11046e067bc3384b5d66b555484"
integrity sha512-S54xOHZgfThiZ38/ZGTgB2rqx51CMJ5MCfVT2IplK4Q7hgzGfe0nLzLCcenDnc/cSjP568hdeKfeDcBgqNHD/g== integrity sha512-/XsqCzD4t+Y9p5wd9HZiptuGKBlaZO5showwqODii5C0nZawxWLF+Q6k5wYHBrQv96h6GYKyqqMHCSTqta8Kiw==
dependencies: dependencies:
"@typescript-eslint/types" "5.32.0" "@typescript-eslint/types" "5.33.0"
eslint-visitor-keys "^3.3.0" eslint-visitor-keys "^3.3.0"
acorn-jsx@^5.3.2: acorn-jsx@^5.3.2:
@ -939,9 +939,9 @@ camelcase@^5.0.0, camelcase@^5.3.1:
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
caniuse-lite@^1.0.30001370: caniuse-lite@^1.0.30001370:
version "1.0.30001374" version "1.0.30001376"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001374.tgz#3dab138e3f5485ba2e74bd13eca7fe1037ce6f57" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001376.tgz#af2450833e5a06873fbb030a9556ca9461a2736d"
integrity sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw== integrity sha512-I27WhtOQ3X3v3it9gNs/oTpoE5KpwmqKR5oKPA8M0G7uMXh9Ty81Q904HpKUrM30ei7zfcL5jE7AXefgbOfMig==
chalk@^2.0.0: chalk@^2.0.0:
version "2.4.2" version "2.4.2"
@ -1166,9 +1166,9 @@ eastasianwidth@^0.2.0:
integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
electron-to-chromium@^1.4.202: electron-to-chromium@^1.4.202:
version "1.4.211" version "1.4.219"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.211.tgz#afaa8b58313807501312d598d99b953568d60f91" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.219.tgz#a7a672304b6aa4f376918d3f63a47f2c3906009a"
integrity sha512-BZSbMpyFQU0KBJ1JG26XGeFI3i4op+qOYGxftmZXFZoHkhLgsSv4DHDJfl8ogII3hIuzGt51PaZ195OVu0yJ9A== integrity sha512-zoQJsXOUw0ZA0YxbjkmzBumAJRtr6je5JySuL/bAoFs0DuLiLJ+5FzRF7/ZayihxR2QcewlRZVm5QZdUhwjOgA==
emoji-regex@^8.0.0: emoji-regex@^8.0.0:
version "8.0.0" version "8.0.0"
@ -1259,12 +1259,11 @@ eslint-import-resolver-node@^0.3.6:
resolve "^1.20.0" resolve "^1.20.0"
eslint-module-utils@^2.7.3: eslint-module-utils@^2.7.3:
version "2.7.3" version "2.7.4"
resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz#4f3e41116aaf13a20792261e61d3a2e7e0583974"
integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ== integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==
dependencies: dependencies:
debug "^3.2.7" debug "^3.2.7"
find-up "^2.1.0"
eslint-plugin-es@^3.0.0: eslint-plugin-es@^3.0.0:
version "3.0.1" version "3.0.1"
@ -1364,9 +1363,9 @@ eslint-visitor-keys@^3.3.0:
integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
eslint@^8.21.0: eslint@^8.21.0:
version "8.21.0" version "8.22.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.21.0.tgz#1940a68d7e0573cef6f50037addee295ff9be9ef" resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.22.0.tgz#78fcb044196dfa7eef30a9d65944f6f980402c48"
integrity sha512-/XJ1+Qurf1T9G2M5IHrsjp+xrGT73RZf23xA1z5wB1ZzzEAWSZKvRwhWxTFp1rvkvCfwcvAUNAP31bhKTTGfDA== integrity sha512-ci4t0sz6vSRKdmkOGmprBo6fmI4PrphDFMy5JEq/fNS0gQkJM3rLmrqcp8ipMcdobH3KtUP40KniAE9W19S4wA==
dependencies: dependencies:
"@eslint/eslintrc" "^1.3.0" "@eslint/eslintrc" "^1.3.0"
"@humanwhocodes/config-array" "^0.10.4" "@humanwhocodes/config-array" "^0.10.4"
@ -1544,13 +1543,6 @@ find-cache-dir@^3.2.0:
make-dir "^3.0.2" make-dir "^3.0.2"
pkg-dir "^4.1.0" pkg-dir "^4.1.0"
find-up@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
integrity sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==
dependencies:
locate-path "^2.0.0"
find-up@^4.0.0, find-up@^4.1.0: find-up@^4.0.0, find-up@^4.1.0:
version "4.1.0" version "4.1.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
@ -2181,14 +2173,6 @@ levn@^0.4.1:
prelude-ls "^1.2.1" prelude-ls "^1.2.1"
type-check "~0.4.0" type-check "~0.4.0"
locate-path@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
integrity sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==
dependencies:
p-locate "^2.0.0"
path-exists "^3.0.0"
locate-path@^5.0.0: locate-path@^5.0.0:
version "5.0.0" version "5.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
@ -2504,13 +2488,6 @@ os-tmpdir@~1.0.2:
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==
p-limit@^1.1.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8"
integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==
dependencies:
p-try "^1.0.0"
p-limit@^2.2.0: p-limit@^2.2.0:
version "2.3.0" version "2.3.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
@ -2525,13 +2502,6 @@ p-limit@^3.0.2:
dependencies: dependencies:
yocto-queue "^0.1.0" yocto-queue "^0.1.0"
p-locate@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
integrity sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==
dependencies:
p-limit "^1.1.0"
p-locate@^4.1.0: p-locate@^4.1.0:
version "4.1.0" version "4.1.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
@ -2553,11 +2523,6 @@ p-map@^3.0.0:
dependencies: dependencies:
aggregate-error "^3.0.0" aggregate-error "^3.0.0"
p-try@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
integrity sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==
p-try@^2.0.0: p-try@^2.0.0:
version "2.2.0" version "2.2.0"
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
@ -2580,11 +2545,6 @@ parent-module@^1.0.0:
dependencies: dependencies:
callsites "^3.0.0" callsites "^3.0.0"
path-exists@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==
path-exists@^4.0.0: path-exists@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"