Skip to content

Commit

Permalink
Shared configuration module (#184)
Browse files Browse the repository at this point in the history
* Add shared config module with better DevX

* Rollout the shared config module in the policy engine project

* Update README
  • Loading branch information
wcalderipe authored Mar 25, 2024
1 parent f9d815c commit 0f7b0fa
Show file tree
Hide file tree
Showing 29 changed files with 222 additions and 48 deletions.
6 changes: 3 additions & 3 deletions apps/policy-engine/src/cli/command/provision.command.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ConfigService } from '@nestjs/config'
import { ConfigService } from '@narval/config-module'
import { Command, CommandRunner } from 'nest-commander'
import { EngineService } from '../../engine/core/service/engine.service'
import { ProvisionService } from '../../engine/core/service/provision.service'
Expand All @@ -12,7 +12,7 @@ export class ProvisionCommand extends CommandRunner {
constructor(
private provisionService: ProvisionService,
private engineService: EngineService,
private configService: ConfigService<Config, true>
private configService: ConfigService<Config>
) {
super()
}
Expand All @@ -27,7 +27,7 @@ export class ProvisionCommand extends CommandRunner {
await this.provisionService.provision()

try {
const keyring = this.configService.get('keyring', { infer: true })
const keyring = this.configService.get('keyring')
const engine = await this.engineService.getEngineOrThrow()

console.log('Engine ID:', engine.id)
Expand Down
8 changes: 4 additions & 4 deletions apps/policy-engine/src/engine/__test__/e2e/tenant.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ConfigModule, ConfigService } from '@narval/config-module'
import { EncryptionModuleOptionProvider } from '@narval/encryption-module'
import { HttpStatus, INestApplication } from '@nestjs/common'
import { ConfigModule, ConfigService } from '@nestjs/config'
import { Test, TestingModule } from '@nestjs/testing'
import request from 'supertest'
import { v4 as uuid } from 'uuid'
Expand Down Expand Up @@ -28,7 +28,7 @@ describe('Tenant', () => {
let tenantRepository: TenantRepository
let tenantService: TenantService
let engineService: EngineService
let configService: ConfigService<Config, true>
let configService: ConfigService<Config>

const adminApiKey = 'test-admin-api-key'

Expand Down Expand Up @@ -71,12 +71,12 @@ describe('Tenant', () => {
tenantService = module.get<TenantService>(TenantService)
tenantRepository = module.get<TenantRepository>(TenantRepository)
testPrismaService = module.get<TestPrismaService>(TestPrismaService)
configService = module.get<ConfigService<Config, true>>(ConfigService)
configService = module.get<ConfigService<Config>>(ConfigService)

await testPrismaService.truncateAll()

await engineService.save({
id: configService.get('engine.id', { infer: true }),
id: configService.get('engine.id'),
masterKey: 'unsafe-test-master-key',
adminApiKey
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ConfigModule } from '@narval/config-module'
import { EncryptionException, EncryptionService } from '@narval/encryption-module'
import { ConfigModule } from '@nestjs/config'
import { Test } from '@nestjs/testing'
import { MockProxy, mock } from 'jest-mock-extended'
import { EngineService } from '../../../../../engine/core/service/engine.service'
Expand Down
6 changes: 3 additions & 3 deletions apps/policy-engine/src/engine/core/service/engine.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ConfigService } from '@narval/config-module'
import { Injectable } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import { Config } from '../../../policy-engine.config'
import { Engine } from '../../../shared/type/domain.type'
import { EngineRepository } from '../../persistence/repository/engine.repository'
Expand All @@ -8,7 +8,7 @@ import { EngineNotProvisionedException } from '../exception/engine-not-provision
@Injectable()
export class EngineService {
constructor(
private configService: ConfigService<Config, true>,
private configService: ConfigService<Config>,
private engineRepository: EngineRepository
) {}

Expand Down Expand Up @@ -37,6 +37,6 @@ export class EngineService {
}

private getId(): string {
return this.configService.get('engine.id', { infer: true })
return this.configService.get('engine.id')
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ConfigService } from '@narval/config-module'
import { EvaluationRequest, EvaluationResponse } from '@narval/policy-engine-shared'
import { HttpStatus, Injectable } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import { resolve } from 'path'
import { OpenPolicyAgentEngine } from '../../../open-policy-agent/core/open-policy-agent.engine'
import { Config } from '../../../policy-engine.config'
Expand All @@ -12,7 +12,7 @@ const UNSAFE_ENGINE_PRIVATE_KEY = '0x7cfef3303797cbc7515d9ce22ffe849c701b0f2812f
@Injectable()
export class EvaluationService {
constructor(
private configService: ConfigService<Config, true>,
private configService: ConfigService<Config>,
private tenantService: TenantService
) {}

Expand Down Expand Up @@ -44,7 +44,7 @@ export class EvaluationService {
entities: entityStore.data,
policies: policyStore.data,
privateKey: UNSAFE_ENGINE_PRIVATE_KEY,
resourcePath: resolve(this.configService.get('resourcePath', { infer: true }))
resourcePath: resolve(this.configService.get('resourcePath'))
}).load()

return engine.evaluate(evaluation)
Expand Down
13 changes: 7 additions & 6 deletions apps/policy-engine/src/engine/core/service/provision.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ConfigService } from '@narval/config-module'
import { generateKeyEncryptionKey, generateMasterKey } from '@narval/encryption-module'
import { Injectable, Logger } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import { randomBytes } from 'crypto'
import { Config } from '../../../policy-engine.config'
import { EngineService } from './engine.service'
Expand All @@ -10,7 +10,7 @@ export class ProvisionService {
private logger = new Logger(ProvisionService.name)

constructor(
private configService: ConfigService<Config, true>,
private configService: ConfigService<Config>,
private engineService: EngineService
) {}

Expand All @@ -21,11 +21,12 @@ export class ProvisionService {

const isFirstTime = engine === null

// IMPORTANT: The order of internal methods call matters.

if (isFirstTime) {
// IMPORTANT: The order of internal methods call matters.
await this.createEngine()
await this.maybeSetupEncryption()
} else {
this.logger.log('Skip engine provision')
}
}

Expand All @@ -46,7 +47,7 @@ export class ProvisionService {
return this.logger.log('Skip master key set up because it already exists')
}

const keyring = this.configService.get('keyring', { infer: true })
const keyring = this.configService.get('keyring')

if (keyring.type === 'raw') {
this.logger.log('Generate and save engine master key')
Expand All @@ -60,6 +61,6 @@ export class ProvisionService {
}

private getEngineId(): string {
return this.configService.get('engine.id', { infer: true })
return this.configService.get('engine.id')
}
}
2 changes: 1 addition & 1 deletion apps/policy-engine/src/engine/engine.module.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ConfigModule, ConfigService } from '@narval/config-module'
import { EncryptionModule } from '@narval/encryption-module'
import { HttpModule } from '@nestjs/axios'
import { Module, ValidationPipe } from '@nestjs/common'
import { ConfigModule, ConfigService } from '@nestjs/config'
import { APP_PIPE } from '@nestjs/core'
import { load } from '../policy-engine.config'
import { EncryptionModuleOptionFactory } from '../shared/factory/encryption-module-option.factory'
Expand Down
8 changes: 4 additions & 4 deletions apps/policy-engine/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ConfigService } from '@narval/config-module'
import { INestApplication, Logger, ValidationPipe } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import { NestFactory } from '@nestjs/core'
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'
import { lastValueFrom, map, of, switchMap } from 'rxjs'
Expand Down Expand Up @@ -48,7 +48,7 @@ const withGlobalPipes = (app: INestApplication): INestApplication => {
* @returns The modified Nest application instance.
*/
const withGlobalFilters =
(configService: ConfigService<Config, true>) =>
(configService: ConfigService<Config>) =>
(app: INestApplication): INestApplication => {
app.useGlobalFilters(new HttpExceptionFilter(configService), new ApplicationExceptionFilter(configService))

Expand All @@ -58,8 +58,8 @@ const withGlobalFilters =
async function bootstrap() {
const logger = new Logger('PolicyEngineBootstrap')
const application = await NestFactory.create(PolicyEngineModule, { bodyParser: true })
const configService = application.get(ConfigService)
const port = configService.get('PORT')
const configService = application.get(ConfigService<Config>)
const port = configService.get('port')

if (!port) {
throw new Error('Missing PORT environment variable')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ConfigModule, ConfigService } from '@narval/config-module'
import {
Action,
Criterion,
Expand All @@ -13,7 +14,7 @@ import {
toHex
} from '@narval/policy-engine-shared'
import { SigningAlg, buildSignerEip191, hash, secp256k1PrivateKeyToJwk, signJwt } from '@narval/signature'
import { ConfigModule, ConfigService, Path, PathValue } from '@nestjs/config'
import { Path, PathValue } from '@nestjs/config'
import { Test, TestingModule } from '@nestjs/testing'
import { Config, load } from '../../../../policy-engine.config'
import { OpenPolicyAgentException } from '../../exception/open-policy-agent.exception'
Expand Down Expand Up @@ -44,9 +45,9 @@ const getConfig = async <P extends Path<Config>>(propertyPath: P): Promise<PathV
imports: [ConfigModule.forRoot({ load: [load] })]
}).compile()

const service = module.get<ConfigService<Config, true>>(ConfigService)
const service = module.get<ConfigService<Config>>(ConfigService)

return service.get(propertyPath, { infer: true })
return service.get(propertyPath)
}

describe('OpenPolicyAgentEngine', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ConfigModule, ConfigService } from '@narval/config-module'
import {
ApprovalsCriterion,
Criterion,
Expand All @@ -10,7 +11,7 @@ import {
ValueOperators,
WalletAddressCriterion
} from '@narval/policy-engine-shared'
import { ConfigModule, ConfigService, Path, PathValue } from '@nestjs/config'
import { Path, PathValue } from '@nestjs/config'
import { Test, TestingModule } from '@nestjs/testing'
import { Config, load } from '../../../../../policy-engine.config'
import { getRegoRuleTemplatePath, transpile, transpileCriterion, transpileReason } from '../../rego-transpiler.util'
Expand All @@ -20,9 +21,9 @@ const getConfig = async <P extends Path<Config>>(propertyPath: P): Promise<PathV
imports: [ConfigModule.forRoot({ load: [load] })]
}).compile()

const service = module.get<ConfigService<Config, true>>(ConfigService)
const service = module.get<ConfigService<Config>>(ConfigService)

return service.get(propertyPath, { infer: true })
return service.get(propertyPath)
}

const getTemplatePath = async () => getRegoRuleTemplatePath(await getConfig('resourcePath'))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ConfigModule, ConfigService } from '@narval/config-module'
import { FIXTURE } from '@narval/policy-engine-shared'
import { ConfigModule, ConfigService, Path, PathValue } from '@nestjs/config'
import { Path, PathValue } from '@nestjs/config'
import { Test, TestingModule } from '@nestjs/testing'
import { loadPolicy } from '@open-policy-agent/opa-wasm'
import { existsSync } from 'fs'
Expand All @@ -22,9 +23,9 @@ const getConfig = async <P extends Path<Config>>(propertyPath: P): Promise<PathV
imports: [ConfigModule.forRoot({ load: [load] })]
}).compile()

const service = module.get<ConfigService<Config, true>>(ConfigService)
const service = module.get<ConfigService<Config>>(ConfigService)

return service.get(propertyPath, { infer: true })
return service.get(propertyPath)
}

const getTemplatePath = async () => getRegoRuleTemplatePath(await getConfig('resourcePath'))
Expand Down
9 changes: 7 additions & 2 deletions apps/policy-engine/src/policy-engine.module.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { ConfigModule, ConfigService } from '@narval/config-module'
import { EncryptionModule } from '@narval/encryption-module'
import { Module, OnApplicationBootstrap, ValidationPipe } from '@nestjs/common'
import { ConfigModule, ConfigService } from '@nestjs/config'
import { APP_PIPE } from '@nestjs/core'
import { BootstrapService } from './engine/core/service/bootstrap.service'
import { EngineService } from './engine/core/service/engine.service'
import { ProvisionService } from './engine/core/service/provision.service'
import { EngineModule } from './engine/engine.module'
import { load } from './policy-engine.config'
import { EncryptionModuleOptionFactory } from './shared/factory/encryption-module-option.factory'
Expand Down Expand Up @@ -32,9 +33,13 @@ import { EncryptionModuleOptionFactory } from './shared/factory/encryption-modul
]
})
export class PolicyEngineModule implements OnApplicationBootstrap {
constructor(private bootstrapService: BootstrapService) {}
constructor(
private provisionService: ProvisionService,
private bootstrapService: BootstrapService
) {}

async onApplicationBootstrap() {
await this.provisionService.provision()
await this.bootstrapService.boot()
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { RawAesKeyringNode } from '@aws-crypto/client-node'
import { ConfigService } from '@narval/config-module'
import {
EncryptionModuleOption,
decryptMasterKey,
Expand All @@ -7,7 +8,6 @@ import {
} from '@narval/encryption-module'
import { toBytes } from '@narval/policy-engine-shared'
import { Injectable, Logger } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import { EngineService } from '../../engine/core/service/engine.service'
import { Config } from '../../policy-engine.config'
import { ENCRYPTION_KEY_NAME, ENCRYPTION_KEY_NAMESPACE, ENCRYPTION_WRAPPING_SUITE } from '../../policy-engine.constant'
Expand All @@ -18,11 +18,11 @@ export class EncryptionModuleOptionFactory {

constructor(
private engineService: EngineService,
private configService: ConfigService<Config, true>
private configService: ConfigService<Config>
) {}

async create(): Promise<EncryptionModuleOption> {
const keyring = this.configService.get('keyring', { infer: true })
const keyring = this.configService.get('keyring')
const engine = await this.engineService.getEngine()

// NOTE: An undefined engine at boot time only happens during the
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ConfigService } from '@narval/config-module'
import { ArgumentsHost, HttpStatus, Logger } from '@nestjs/common'
import { HttpArgumentsHost } from '@nestjs/common/interfaces'
import { ConfigService } from '@nestjs/config'
import { Response } from 'express'
import { mock } from 'jest-mock-extended'
import { Config, Env } from '../../../../policy-engine.config'
Expand Down Expand Up @@ -41,7 +41,7 @@ describe(ApplicationExceptionFilter.name, () => {
}

const buildConfigServiceMock = (env: Env) =>
mock<ConfigService<Config, true>>({
mock<ConfigService<Config>>({
get: jest.fn().mockReturnValue(env)
})

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ConfigService } from '@narval/config-module'
import { ArgumentsHost, Catch, ExceptionFilter, LogLevel, Logger } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import { Response } from 'express'
import { Config, Env } from '../../policy-engine.config'
import { ApplicationException } from '../../shared/exception/application.exception'
Expand All @@ -8,7 +8,7 @@ import { ApplicationException } from '../../shared/exception/application.excepti
export class ApplicationExceptionFilter implements ExceptionFilter {
private logger = new Logger(ApplicationExceptionFilter.name)

constructor(private configService: ConfigService<Config, true>) {}
constructor(private configService: ConfigService<Config>) {}

catch(exception: ApplicationException, host: ArgumentsHost) {
const ctx = host.switchToHttp()
Expand Down
4 changes: 2 additions & 2 deletions apps/policy-engine/src/shared/filter/http-exception.filter.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { ConfigService } from '@narval/config-module'
import { ArgumentsHost, Catch, ExceptionFilter, HttpException, Logger } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import { Response } from 'express'
import { Config, Env } from '../../policy-engine.config'

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
private logger = new Logger(HttpExceptionFilter.name)

constructor(private configService: ConfigService<Config, true>) {}
constructor(private configService: ConfigService<Config>) {}

catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { ConfigService } from '@narval/config-module'
import { Inject, Injectable, Logger, OnApplicationShutdown, OnModuleDestroy, OnModuleInit } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import { PrismaClient } from '@prisma/client/policy-engine'
import { Config } from '../../../../policy-engine.config'

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy, OnApplicationShutdown {
private logger = new Logger(PrismaService.name)

constructor(@Inject(ConfigService) configService: ConfigService<Config, true>) {
const url = configService.get('database.url', { infer: true })
constructor(@Inject(ConfigService) configService: ConfigService<Config>) {
const url = configService.get('database.url')

super({
datasources: {
Expand Down
Loading

0 comments on commit 0f7b0fa

Please sign in to comment.