UNPKG

unleash-server

Version:

Unleash is an enterprise ready feature flag service. It provides different strategies for handling feature flags.

113 lines 5.33 kB
import { ApiTokenType, SYSTEM_USER_AUDIT, } from '../types/index.js'; import { InvalidOperationError } from '../error/index.js'; import { decryptSecret, encryptSecret, } from '../features/edgetokens/edge-verification.js'; import { EdgeTokenStore } from '../features/edgetokens/edge-token-store.js'; import { createApiTokenService, createFakeApiTokenService, } from '../features/api-tokens/createApiTokenService.js'; import { FakeEdgeTokenStore } from '../features/edgetokens/fake-edge-token-store.js'; export const createTransactionalEdgeService = (db, config) => { const edgeTokenStore = new EdgeTokenStore(db, config.eventBus, config); const transactionalApiTokenService = createApiTokenService(db, config); return new EdgeService({ edgeTokenStore }, { apiTokenService: transactionalApiTokenService }, config); }; export const createFakeEdgeService = (config) => { const fakeEdgeTokenStore = new FakeEdgeTokenStore(); const fakeApiTokenService = createFakeApiTokenService(config); return new EdgeService({ edgeTokenStore: fakeEdgeTokenStore }, fakeApiTokenService, config); }; export default class EdgeService { constructor({ edgeTokenStore }, { apiTokenService }, { getLogger, edgeMasterKey, }) { this.logger = getLogger('lib/services/edge-service.ts'); this.apiTokenService = apiTokenService; this.edgeTokenStore = edgeTokenStore; this.edgeMasterKey = edgeMasterKey; } async getValidTokens(tokens) { // new behavior: use cached tokens when possible // use the db to fetch the missing ones // cache stores both missing and active so we don't hammer the db const validatedTokens = []; for (const token of tokens) { const found = await this.apiTokenService.getTokenWithCache(token); if (found) { validatedTokens.push({ token: token, type: found.type, projects: found.projects, environment: found.environment, }); } } return { tokens: validatedTokens }; } async notSeenBefore({ clientId, nonce, expiresAt, }) { try { await this.edgeTokenStore.registerNonce(clientId, nonce, expiresAt); return true; } catch (_e) { } return false; } async loadClient(clientId) { return this.edgeTokenStore.loadClient(clientId); } decryptedClientSecret(client) { if (this.edgeMasterKey === undefined) { throw new InvalidOperationError('You have to define an EDGE_MASTER_SECRET for this to be supported'); } return decryptSecret(Buffer.from(this.edgeMasterKey, 'base64'), client.secret_enc); } async saveClient(clientId, secret) { if (this.edgeMasterKey === undefined) { throw new InvalidOperationError('EDGE_MASTER_KEY was not defined'); } const masterSecretBuffer = Buffer.from(this.edgeMasterKey, 'base64'); if (masterSecretBuffer.length !== 32) { throw new InvalidOperationError('You must define a 32 byte secret in the EDGE_MASTER_SECRET environment variable'); } const secretEnc = encryptSecret(masterSecretBuffer, secret); await this.edgeTokenStore.saveClient(clientId, secretEnc); this.logger.info('Successfully set client secret'); } async getOrCreateTokens(clientId, tokenRequest) { if (!this.edgeMasterKey) { throw new InvalidOperationError('You must define a secret in the EDGE_MASTER_SECRET environment variable'); } const tokens = []; for (const tokenReq of tokenRequest.tokens) { const existing = await this.edgeTokenStore.getToken(clientId, tokenReq.environment, tokenReq.projects); if (existing !== undefined) { tokens.push({ projects: existing.projects, type: existing.type, token: existing.secret, environment: existing.environment, }); } else if (tokenReq.environment && tokenReq.projects) { const newToken = await this.apiTokenService.createApiTokenWithProjects({ tokenName: `enterprise_edge_${tokenReq.environment}_${truncate(tokenReq.projects, 3)}`, alias: `ee_${tokenReq.environment}`, type: ApiTokenType.BACKEND, environment: tokenReq.environment, projects: tokenReq.projects, }, SYSTEM_USER_AUDIT); await this.edgeTokenStore.saveToken(clientId, newToken); tokens.push({ projects: newToken.projects, type: newToken.type, token: newToken.secret, environment: newToken.environment, }); } } return { tokens }; } async deleteExpiredNonces() { await this.edgeTokenStore.cleanExpiredNonces(); } async deleteAllTokens() { await this.edgeTokenStore.deleteAll(); } } const truncate = (projects, max_length) => projects.length > max_length ? `[]` : projects.join('_'); //# sourceMappingURL=edge-service.js.map