UNPKG

moleculer-iam

Version:

Centralized IAM module for moleculer. Including a certified OIDC provider and an Identity provider for user profile, credentials, and custom claims management. Custom claims could be defined/updated by declarative schema which contains claims validation a

228 lines 8.72 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.IDP_MemoryAdapter = void 0; const tslib_1 = require("tslib"); const _ = tslib_1.__importStar(require("lodash")); const adapter_1 = require("../adapter"); // tslint:disable-next-line:class-name class IDP_MemoryAdapter extends adapter_1.IDPAdapter { constructor(props, options) { super(props); this.props = props; this.displayName = "Memory"; /* metadata */ this.identityMetadataMap = new Map(); /* claims */ this.identityClaimsMap = new Map(); /* credentials */ this.identityCredentialsMap = new Map(); /* claims schema */ this.schemata = new Array(); this.migrationLocksMap = new Map(); } /* fetch */ // support only identity by id (sub), email, phone async find(args) { let foundId = ""; const argsId = args.id || args.claims && args.claims.sub; if (argsId) { const schema = await this.getClaimsSchema({ key: "sub", active: true }); for (const [id, claims] of this.identityClaimsMap.entries()) { if (claims.some(c => c.key === "sub" && c.value === argsId && c.schemaVersion === schema.version)) { foundId = id; break; } } } else if (args.claims && args.claims.email) { const schema = await this.getClaimsSchema({ key: "email", active: true }); for (const [id, claims] of this.identityClaimsMap.entries()) { if (claims.some(c => c.key === "email" && c.value === args.claims.email && c.schemaVersion === schema.version)) { foundId = id; break; } } } else if (args.claims && args.claims.phone_number) { const schema = await this.getClaimsSchema({ key: "phone_number", active: true }); for (const [id, claims] of this.identityClaimsMap.entries()) { if (claims.some(c => c.key === "phone_number" && c.value === args.claims.phone_number && c.schemaVersion === schema.version)) { foundId = id; break; } } } if (foundId) { // filter by metadata for the common test if (args.metadata && !(await this.filerMetadata([foundId], args.metadata)).includes(foundId)) { return; } return foundId; } } // filter by metadata poorly for the common test async filerMetadata(ids, condition) { if (Object.keys(condition).length === 0) return ids; const filteredIds = []; for (const id of ids) { const metadata = await this.getMetadata(id); if (metadata && _.isMatch(metadata, condition)) { filteredIds.push(id); } } return filteredIds; } // only support offset, limit, metadata async get(args) { let ids = [...this.identityMetadataMap.keys()]; // filter by metadata for the common test if (args.where && args.where.metadata) { ids = await this.filerMetadata(ids, args.where.metadata); } return ids .slice(args.offset || 0, typeof args.limit === "undefined" ? ids.length : args.limit); } async count(args) { return (await this.get(args)).length; } /* delete */ async delete(id) { if (!this.identityMetadataMap.has(id)) return false; this.identityMetadataMap.delete(id); this.identityClaimsMap.delete(id); this.identityCredentialsMap.delete(id); return true; } async createOrUpdateMetadata(id, metadata, transaction) { const old = this.identityMetadataMap.get(id); this.identityMetadataMap.set(id, _.defaultsDeep(metadata, old || {})); } async getMetadata(id) { return this.identityMetadataMap.get(id); } async createOrUpdateVersionedClaims(id, claims) { let oldClaims = this.identityClaimsMap.get(id); if (!oldClaims) { oldClaims = []; this.identityClaimsMap.set(id, oldClaims); } for (const claim of claims) { const oldClaim = oldClaims.find(c => c.key === claim.key && c.schemaVersion === claim.schemaVersion); if (oldClaim) { oldClaim.value = claim.value; } else { oldClaims.push(claim); } } } async onClaimsUpdated(id, updatedClaims, transaction) { // ... } async getVersionedClaims(id, claims) { const foundClaims = {}; const storedClaims = this.identityClaimsMap.get(id) || []; for (const { key, schemaVersion } of claims) { const foundClaim = storedClaims.find(claim => { if (key !== claim.key) return false; if (typeof schemaVersion !== "undefined" && schemaVersion !== claim.schemaVersion) return false; return true; }); if (foundClaim) foundClaims[key] = foundClaim.value; } return foundClaims; } async createOrUpdateCredentials(id, credentials, transaction) { const cred = this.identityCredentialsMap.get(id); if (cred && JSON.stringify(cred) === JSON.stringify(credentials)) return false; this.identityCredentialsMap.set(id, { ...cred, ...credentials }); return true; } async assertCredentials(id, credentials) { const cred = this.identityCredentialsMap.get(id); if (typeof cred === "undefined") return null; for (const [type, value] of Object.entries(credentials)) { const answer = cred[type]; if (typeof answer === "undefined") return null; if (answer !== value) return false; } return true; } async createClaimsSchema(schema, transaction) { this.schemata.push(schema); } async forceDeleteClaimsSchema(key) { this.schemata = this.schemata.filter(schema => schema.key !== key); } async getClaimsSchema(args) { const { key, version, active } = args; return this.schemata.find(sch => { if (key !== sch.key) return false; if (typeof version !== "undefined" && version !== sch.version) return false; if (typeof active !== "undefined" && active !== sch.active) return false; return true; }); } async setActiveClaimsSchema(args, transaction) { const { key, version } = args; this.schemata.forEach(sch => { if (key !== sch.key) return; sch.active = version === sch.version; }); } async getClaimsSchemata(args) { const { scope, key, version, active } = args; return this.schemata.filter(schema => { if (scope.length !== 0 && !scope.includes(schema.scope)) return false; if (typeof key !== "undefined" && key !== schema.key) return false; if (typeof version !== "undefined" && version !== schema.version) return false; if (typeof active !== "undefined" && active !== schema.active) return false; return true; }); } /* transaction and migration lock for distributed system */ async transaction() { const logger = this.logger; return { async commit() { logger.warn("Memory adapter has not implemented transaction: commit()"); }, async rollback() { logger.warn("Memory adapter has not implemented transaction: commit()"); }, }; } async acquireMigrationLock(key) { if (this.migrationLocksMap.size > 0) { await new Promise(resolve => setTimeout(resolve, 1000)); return this.acquireMigrationLock(key); } this.migrationLocksMap.set(key, true); } async touchMigrationLock(key, migratedIdentitiesNumber) { // ... this.logger.warn("Memory adapter has not implemented dead lock resolving strategy, migration is working for: ", key, migratedIdentitiesNumber); } async releaseMigrationLock(key) { this.migrationLocksMap.delete(key); } } exports.IDP_MemoryAdapter = IDP_MemoryAdapter; //# sourceMappingURL=adapter.js.map