Add support for more settings

Signed-off-by: Marcus Eide <eide@spotify.com>
This commit is contained in:
Marcus Eide
2021-10-14 14:43:56 +02:00
parent 5ad1f523d5
commit 33b9694f5c
4 changed files with 44 additions and 66 deletions
+6
View File
@@ -35,6 +35,12 @@ export interface Config {
keyStore?: {
provider?: 'postgres' | 'memory' | 'firestore';
firestore?: {
/** The host to connect to */
host?: string;
/** The port to connect to */
port?: number;
/** Whether to use SSL when connecting. */
ssl?: boolean;
/** The Google Cloud Project ID */
projectId?: string;
/**
@@ -16,7 +16,10 @@
import { Firestore } from '@google-cloud/firestore';
import { FirestoreKeyStore } from './FirestoreKeyStore';
import {
FirestoreKeyStore,
FirestoreKeyStoreSettings,
} from './FirestoreKeyStore';
import { AnyJWK } from './types';
const data = jest.fn().mockReturnValue('data');
@@ -37,7 +40,6 @@ jest.mock('@google-cloud/firestore', () => ({
}));
describe('FirestoreKeyStore', () => {
const OLD_ENV = process.env;
const key = {
kid: '123',
use: 'sig',
@@ -45,72 +47,45 @@ describe('FirestoreKeyStore', () => {
alg: 'Base64',
} as AnyJWK;
const settings = {
projectId: 'my-project',
host: 'my-host',
port: 8080,
ssl: false,
keyFilename: 'cred.json',
};
const path = 'my-path';
const firestoreSettings = { ...settings, path } as FirestoreKeyStoreSettings;
beforeEach(() => {
jest.clearAllMocks();
});
afterAll(() => {
process.env = OLD_ENV;
});
it('can create an instance without settings', async () => {
const keyStore = await FirestoreKeyStore.create();
expect(keyStore).toBeInstanceOf(FirestoreKeyStore);
expect(Firestore).toHaveBeenCalledWith({});
});
it('can set the project id', async () => {
await FirestoreKeyStore.create({ projectId: 'my-project' });
it('can create an instance with settings', async () => {
await FirestoreKeyStore.create(firestoreSettings);
expect(Firestore).toHaveBeenCalledWith(
expect.objectContaining({
projectId: 'my-project',
}),
);
expect(Firestore).toHaveBeenCalledWith(settings);
});
it('can handle keyfile file', async () => {
await FirestoreKeyStore.create({ keyFilename: 'keyFile.json' });
expect(Firestore).toHaveBeenCalledWith(
expect.objectContaining({
keyFilename: 'keyFile.json',
}),
);
});
it('can use default google credentials', async () => {
process.env.GOOGLE_APPLICATION_CREDENTIALS = 'cred.json';
await FirestoreKeyStore.create();
expect(Firestore).toHaveBeenCalledWith(
expect.objectContaining({
keyFilename: 'cred.json',
}),
);
});
it('can uses a default path', async () => {
it('can use a default path', async () => {
const keyStore = await FirestoreKeyStore.create();
await keyStore.addKey(key);
expect(firestoreMock.collection).toBeCalledWith('sessions');
});
it('can set the path', async () => {
const keyStore = await FirestoreKeyStore.create({
path: 'my-path',
});
await keyStore.addKey(key);
expect(firestoreMock.collection).toBeCalledWith('my-path');
});
it('can add keys', async () => {
const keyStore = await FirestoreKeyStore.create();
const keyStore = await FirestoreKeyStore.create(firestoreSettings);
await keyStore.addKey(key);
expect(firestoreMock.collection).toBeCalledWith('sessions');
expect(firestoreMock.collection).toBeCalledWith(path);
expect(firestoreMock.doc).toBeCalledWith(key.kid);
expect(firestoreMock.set).toHaveBeenCalledWith({
kid: key.kid,
@@ -119,29 +94,29 @@ describe('FirestoreKeyStore', () => {
});
it('can delete a single key', async () => {
const keyStore = await FirestoreKeyStore.create();
const keyStore = await FirestoreKeyStore.create(firestoreSettings);
await keyStore.removeKeys(['123']);
expect(firestoreMock.collection).toBeCalledWith('sessions');
expect(firestoreMock.collection).toBeCalledWith(path);
expect(firestoreMock.doc).toBeCalledWith('123');
expect(firestoreMock.delete).toBeCalledTimes(1);
});
it('can delete a multiple keys', async () => {
const keyStore = await FirestoreKeyStore.create();
const keyStore = await FirestoreKeyStore.create(firestoreSettings);
await keyStore.removeKeys(['123', '456']);
expect(firestoreMock.collection).toBeCalledWith('sessions');
expect(firestoreMock.collection).toBeCalledWith(path);
expect(firestoreMock.doc).toBeCalledWith('123');
expect(firestoreMock.doc).toBeCalledWith('456');
expect(firestoreMock.delete).toBeCalledTimes(2);
});
it('can list keys', async () => {
const keyStore = await FirestoreKeyStore.create();
const keyStore = await FirestoreKeyStore.create(firestoreSettings);
const items = await keyStore.listKeys();
expect(firestoreMock.collection).toBeCalledWith('sessions');
expect(firestoreMock.collection).toBeCalledWith(path);
expect(firestoreMock.get).toBeCalledTimes(1);
expect(data).toBeCalledTimes(1);
expect(toDate).toBeCalledTimes(1);
@@ -18,19 +18,16 @@ import { Firestore, Settings } from '@google-cloud/firestore';
import { AnyJWK, KeyStore, StoredKey } from './types';
type FirestoreSettings = Settings & {
export type FirestoreKeyStoreSettings = Settings & {
path?: string;
};
export class FirestoreKeyStore implements KeyStore {
static async create(
settings?: FirestoreSettings,
settings?: FirestoreKeyStoreSettings,
): Promise<FirestoreKeyStore> {
const { projectId, keyFilename, path } = settings ?? {};
const database = new Firestore({
projectId,
keyFilename: keyFilename ?? process.env.GOOGLE_APPLICATION_CREDENTIALS,
});
const { path, ...firestoreSettings } = settings ?? {};
const database = new Firestore(firestoreSettings);
return new FirestoreKeyStore(database, path ?? 'sessions');
}
@@ -21,7 +21,10 @@ import { Config } from '@backstage/config';
import { DatabaseKeyStore } from './DatabaseKeyStore';
import { MemoryKeyStore } from './MemoryKeyStore';
import { FirestoreKeyStore } from './FirestoreKeyStore';
import {
FirestoreKeyStore,
FirestoreKeyStoreSettings,
} from './FirestoreKeyStore';
import { KeyStore } from './types';
type Options = {
@@ -44,7 +47,6 @@ export class KeyStores {
const ks = config.getOptionalConfig('auth.keyStore');
const provider = ks?.getOptionalString('provider') ?? 'postgres';
const providerConfig = ks?.getOptionalConfig(provider);
logger?.info(`Configuring "${provider}" as KeyStore provider`);
@@ -63,11 +65,9 @@ export class KeyStores {
}
if (provider === 'firestore') {
return await FirestoreKeyStore.create({
projectId: providerConfig?.getOptionalString('projectId'),
keyFilename: providerConfig?.getOptionalString('keyFilename'),
path: providerConfig?.getOptionalString('path'),
});
const settings = ks?.getOptional(provider) as FirestoreKeyStoreSettings;
return await FirestoreKeyStore.create(settings);
}
throw new Error(`Unknown KeyStore provider: ${provider}`);