diff --git a/apps/policy-engine/Makefile b/apps/policy-engine/Makefile index 5602291de..f160115b6 100644 --- a/apps/policy-engine/Makefile +++ b/apps/policy-engine/Makefile @@ -74,14 +74,14 @@ policy-engine/db/create-migration: # project and resolve its path aliases before running the vanilla JavaScript # seed entry point. policy-engine/db/seed: - make policy-engine/build/script npx dotenv -e ${POLICY_ENGINE_PROJECT_DIR}/.env -- \ - node dist/out-tsc/${POLICY_ENGINE_PROJECT_DIR}/src/shared/module/persistence/seed.js + ts-node -r tsconfig-paths/register --project ${POLICY_ENGINE_PROJECT_DIR}/tsconfig.app.json ${POLICY_ENGINE_PROJECT_DIR}/src/shared/module/persistence/seed.ts # === Testing === policy-engine/test/type: + make policy-engine/db/generate-types npx tsc \ --project ${POLICY_ENGINE_PROJECT_DIR}/tsconfig.app.json \ --noEmit diff --git a/apps/policy-engine/src/app/__test__/e2e/admin.spec.ts b/apps/policy-engine/src/app/__test__/e2e/admin.spec.ts index f56975ed0..b3fa497b4 100644 --- a/apps/policy-engine/src/app/__test__/e2e/admin.spec.ts +++ b/apps/policy-engine/src/app/__test__/e2e/admin.spec.ts @@ -7,9 +7,9 @@ import { readFileSync, unlinkSync } from 'fs' import { mock } from 'jest-mock-extended' import request from 'supertest' import { AppModule } from '../../../app/app.module' +import { load } from '../../../policy-engine.config' import { Organization } from '../../../shared/types/entities.types' import { Criterion, Then, TimeWindow } from '../../../shared/types/policy.type' -import { load } from '../../app.config' import { EntityRepository } from '../../persistence/repository/entity.repository' const REQUEST_HEADER_ORG_ID = 'x-org-id' diff --git a/apps/policy-engine/src/keyring/core/keyring.service.ts b/apps/policy-engine/src/keyring/core/keyring.service.ts index 2d248c6a6..5c41f8539 100644 --- a/apps/policy-engine/src/keyring/core/keyring.service.ts +++ b/apps/policy-engine/src/keyring/core/keyring.service.ts @@ -11,7 +11,7 @@ const AUTH_TAG_LENGTH = 16 export class KeyringService implements OnApplicationBootstrap { private logger = new Logger(KeyringService.name) - private engineUid: string + private engineId: string private kek: Buffer @@ -25,34 +25,34 @@ export class KeyringService implements OnApplicationBootstrap { private keyringRepository: KeyringRepository, @Inject(ConfigService) configService: ConfigService ) { - this.engineUid = configService.get('engineUid', { infer: true }) - this.masterPassword = configService.get('masterPassword', { infer: true }) + this.engineId = configService.get('engine.id', { infer: true }) + this.masterPassword = configService.get('engine.masterPassword', { infer: true }) } async onApplicationBootstrap(): Promise { this.logger.log('Keyring Service boot') - let engine = await this.keyringRepository.getEngine(this.engineUid) + let engine = await this.keyringRepository.getEngine(this.engineId) // Derive the Key Encryption Key (KEK) from the master password using PBKDF2 - this.kek = this.deriveKek(this.masterPassword) + this.kek = this.deriveKeyEncryptionKey(this.masterPassword) if (!engine) { // New Engine, set it up engine = await this.firstTimeSetup() } - this.masterKey = this.decryptWithKey(engine.masterKey, this.kek) - this.adminApiKey = this.decryptWithKey(engine.adminApiKey, this.kek) + this.masterKey = this.decryptWithKey(this.kek, engine.masterKey) + this.adminApiKey = this.decryptWithKey(this.kek, engine.adminApiKey) } - private deriveKek(password: string): Buffer { + private deriveKeyEncryptionKey(password: string): Buffer { // Derive the Key Encryption Key (KEK) from the master password using PBKDF2 - const kek = crypto.pbkdf2Sync(password.normalize(), this.engineUid.normalize(), 1000000, 32, 'sha256') - this.logger.log('Derived KEK', { kek: kek.toString('hex') }) + const kek = crypto.pbkdf2Sync(password.normalize(), this.engineId.normalize(), 1000000, 32, 'sha256') + this.logger.log('Derived KEK', { kek: kek.toString('hex') }) // TODO: Sanitize return kek } - decryptWithKey(encryptedString: string, key: Buffer): Buffer { + decryptWithKey(key: Buffer, encryptedString: string): Buffer { const encryptedBuffer = Buffer.from(encryptedString, 'hex') // IV and AuthTag are prepend/appended, so slice them off const iv = encryptedBuffer.subarray(0, IV_LENGTH) @@ -68,7 +68,7 @@ export class KeyringService implements OnApplicationBootstrap { return decrypted } - encryptWithKey(data: Buffer, key: Buffer): string { + encryptWithKey(key: Buffer, data: Buffer): string { const iv = crypto.randomBytes(IV_LENGTH) const cipher = crypto.createCipheriv('aes-256-gcm', key, iv, { authTagLength: AUTH_TAG_LENGTH }) let encrypted = cipher.update(data) @@ -88,13 +88,14 @@ export class KeyringService implements OnApplicationBootstrap { const adminApiKeyBuffer = crypto.randomBytes(32) // Encrypt the Master Key (MK) with the Key Encryption Key (KEK) - const encryptedMk = this.encryptWithKey(mkBuffer, this.kek) - const encryptedApiKey = this.encryptWithKey(adminApiKeyBuffer, this.kek) + const encryptedMk = this.encryptWithKey(this.kek, mkBuffer) + const encryptedApiKey = this.encryptWithKey(this.kek, adminApiKeyBuffer) // Save the Result. - const engine = await this.keyringRepository.createEngine(this.engineUid, encryptedMk, encryptedApiKey) + const engine = await this.keyringRepository.createEngine(this.engineId, encryptedMk, encryptedApiKey) this.logger.log('Engine Initial Setup Complete') + // TODO: Print this to a console in a better way; may not even like this. this.logger.log('Admin API Key -- DO NOT LOSE THIS', adminApiKeyBuffer.toString('hex')) return engine } @@ -107,7 +108,7 @@ export class KeyringService implements OnApplicationBootstrap { deriveContentEncryptionKey(keyId: string) { // Derive a CEK from the MK+keyId using HKDF const cek = crypto.hkdfSync('sha256', this.masterKey, keyId, 'content', 32) - this.logger.log('Derived KEK', { cek: Buffer.from(cek).toString('hex') }) + this.logger.log('Derived KEK', { cek: Buffer.from(cek).toString('hex') }) // TODO: Sanitize return Buffer.from(cek) } } diff --git a/apps/policy-engine/src/keyring/persistence/repository/keyring.repository.ts b/apps/policy-engine/src/keyring/persistence/repository/keyring.repository.ts index 3745089d2..0da1091dd 100644 --- a/apps/policy-engine/src/keyring/persistence/repository/keyring.repository.ts +++ b/apps/policy-engine/src/keyring/persistence/repository/keyring.repository.ts @@ -1,28 +1,24 @@ -import { Injectable, Logger, OnModuleInit } from '@nestjs/common' +import { Injectable, Logger } from '@nestjs/common' import { PrismaService } from '../../../shared/module/persistence/service/prisma.service' @Injectable() -export class KeyringRepository implements OnModuleInit { +export class KeyringRepository { private logger = new Logger(KeyringRepository.name) constructor(private prismaService: PrismaService) {} - async onModuleInit() { - this.logger.log('KeyringRepository initialized') - } - - async getEngine(engineUid: string) { + async getEngine(engineId: string) { return this.prismaService.engine.findUnique({ where: { - uid: engineUid + id: engineId } }) } - async createEngine(engineUid: string, masterKey: string, adminApiKey: string) { + async createEngine(engineId: string, masterKey: string, adminApiKey: string) { return this.prismaService.engine.create({ data: { - uid: engineUid, + id: engineId, masterKey, adminApiKey } diff --git a/apps/policy-engine/src/policy-engine.config.ts b/apps/policy-engine/src/policy-engine.config.ts index 7a6eb8a4a..850cf9f4d 100644 --- a/apps/policy-engine/src/policy-engine.config.ts +++ b/apps/policy-engine/src/policy-engine.config.ts @@ -12,8 +12,10 @@ const ConfigSchema = z.object({ database: z.object({ url: z.string().startsWith('postgresql:') }), - engineUid: z.string().optional(), - masterPassword: z.string().optional() + engine: z.object({ + id: z.string(), + masterPassword: z.string() + }) }) export type Config = z.infer @@ -25,8 +27,10 @@ export const load = (): Config => { database: { url: process.env.POLICY_ENGINE_DATABASE_URL }, - engineUid: process.env.ENGINE_UID, - masterPassword: process.env.MASTER_PASSWORD + engine: { + id: process.env.ENGINE_UID, + masterPassword: process.env.MASTER_PASSWORD + } }) if (result.success) { diff --git a/apps/policy-engine/src/shared/module/persistence/schema/migrations/20240228152233_init/migration.sql b/apps/policy-engine/src/shared/module/persistence/schema/migrations/20240228183059_init/migration.sql similarity index 59% rename from apps/policy-engine/src/shared/module/persistence/schema/migrations/20240228152233_init/migration.sql rename to apps/policy-engine/src/shared/module/persistence/schema/migrations/20240228183059_init/migration.sql index 5494af3b1..4663d63d6 100644 --- a/apps/policy-engine/src/shared/module/persistence/schema/migrations/20240228152233_init/migration.sql +++ b/apps/policy-engine/src/shared/module/persistence/schema/migrations/20240228183059_init/migration.sql @@ -1,8 +1,8 @@ -- CreateTable CREATE TABLE "engine" ( - "uid" TEXT NOT NULL, + "id" TEXT NOT NULL, "master_key" TEXT NOT NULL, "admin_api_key" TEXT NOT NULL, - CONSTRAINT "engine_pkey" PRIMARY KEY ("uid") + CONSTRAINT "engine_pkey" PRIMARY KEY ("id") ); diff --git a/apps/policy-engine/src/shared/module/persistence/schema/schema.prisma b/apps/policy-engine/src/shared/module/persistence/schema/schema.prisma index f57046b13..73ed50081 100644 --- a/apps/policy-engine/src/shared/module/persistence/schema/schema.prisma +++ b/apps/policy-engine/src/shared/module/persistence/schema/schema.prisma @@ -13,7 +13,7 @@ datasource db { } model Engine { - uid String @id + id String @id masterKey String @map("master_key") adminApiKey String @map("admin_api_key") diff --git a/apps/policy-engine/src/shared/module/persistence/seed.ts b/apps/policy-engine/src/shared/module/persistence/seed.ts index cbedbfdeb..5f42eb050 100644 --- a/apps/policy-engine/src/shared/module/persistence/seed.ts +++ b/apps/policy-engine/src/shared/module/persistence/seed.ts @@ -5,7 +5,7 @@ import { Engine, PrismaClient } from '@prisma/client/policy-engine' const prisma = new PrismaClient() const engine: Engine = { - uid: '7d704a62-d15e-4382-a826-1eb41563043b', + id: '7d704a62-d15e-4382-a826-1eb41563043b', adminApiKey: 'admin-api-key-xxx', masterKey: 'master-key-xxx' }