UNPKG

@restorecommerce/acs-client

Version:

Access Control Service Client

191 lines 8.41 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ByPass = exports.DefaultMetaDataInjector = exports.DefaultSubjectResolver = exports.DefaultACSClientContextFactory = void 0; exports.DefaultResourceFactory = DefaultResourceFactory; exports.setByPass = setByPass; exports.access_controlled_service = access_controlled_service; exports.access_controlled_function = access_controlled_function; exports.resolves_subject = resolves_subject; exports.injects_meta_data = injects_meta_data; const nice_grpc_1 = require("nice-grpc"); const service_config_1 = require("@restorecommerce/service-config"); const logger_1 = require("@restorecommerce/logger"); const grpc_client_1 = require("@restorecommerce/grpc-client"); const user_1 = require("@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/user"); const access_control_1 = require("@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/access_control"); const authz_1 = require("./authz"); const cache_1 = require("./cache"); const resolver_1 = require("./resolver"); const config_1 = require("../config"); const utils_1 = require("../utils"); const crypto_1 = require("crypto"); const DefaultACSClientContextFactory = async (self, request, context) => ({ ...context, subject: request.subject, resources: [], }); exports.DefaultACSClientContextFactory = DefaultACSClientContextFactory; function DefaultResourceFactory(...resourceNames) { return async (self, request, context) => (resourceNames?.length ? resourceNames : [self.name])?.map(resourceName => ({ resource: resourceName, id: request.items?.map((item) => item.id) })); } const DefaultSubjectResolver = async (self, request, ...args) => { const subject = request?.subject; if (subject?.id) { delete subject.id; } if (subject?.token) { const user = await self.__userService.findByToken({ token: subject.token }); if (user?.payload?.id) { subject.id = user.payload.id; } } return request; }; exports.DefaultSubjectResolver = DefaultSubjectResolver; const DefaultMetaDataInjector = async (self, request, ...args) => { const urns = config_1.cfg.get('authorization:urns'); request.items?.forEach((item) => { if (!item.id?.length) { item.id = (0, crypto_1.randomUUID)().replace(/-/g, ''); item.meta ??= {}; item.meta.owners ??= [ request.subject?.scope ? { id: urns.ownerIndicatoryEntity, value: urns.organization, attributes: [{ id: urns.ownerInstance, value: request.subject.scope }], } : undefined, request.subject?.id ? { id: urns.ownerIndicatoryEntity, value: urns.user, attributes: [{ id: urns.ownerInstance, value: request.subject.id }], } : undefined, ].filter(i => i); } }); return request; }; exports.DefaultMetaDataInjector = DefaultMetaDataInjector; var ByPass; (function (ByPass) { ByPass["SUBJECT"] = "SUBJECT"; ByPass["META"] = "META"; ByPass["ACS"] = "ACS"; })(ByPass || (exports.ByPass = ByPass = {})); ; function setByPass(...args) { return { metadata: new nice_grpc_1.Metadata(args.map(arg => ['bypass', arg.toString()])) }; } function access_controlled_service(baseService) { return class extends baseService { __userService; __acsDatabaseProvider; constructor(...args) { super(...args); const cfg = args.find((arg) => (arg instanceof service_config_1.ServiceConfig)); const logger = args.find((arg) => (arg instanceof logger_1.Logger)); this.__acsDatabaseProvider = cfg.get('authorization:database') ?? 'arangoDB'; this.__userService = (0, grpc_client_1.createClient)({ ...cfg.get('client:user'), logger }, user_1.UserServiceDefinition, (0, grpc_client_1.createChannel)(cfg.get('client:user:address'))); (0, authz_1.initAuthZ)(cfg); (0, cache_1.initializeCache)(); } }; } function access_controlled_function(kwargs) { return function (target, propertyName, descriptor) { const method = descriptor.value; descriptor.value = async function () { const that = this; const request = arguments[0]; const context = arguments[1]; const args = [...arguments].slice(2); if (context?.byPassACS) { return await method.apply(this, arguments); } try { if (!that.__userService) { throw new Error('An @access_controlled_function must be member of an @access_controlled_service class'); } const acsContext = typeof (kwargs.context) === 'function' ? await kwargs.context(this, request, ...args) : kwargs.context; const resource = typeof (kwargs.resource) === 'function' ? await kwargs.resource(this, request, ...args) : kwargs.resource; const database = typeof (kwargs.database) === 'function' ? await kwargs.database(this, request, ...args) : kwargs.database; const subject = acsContext?.subject; if (subject) { subject.id = null; } if (subject?.token) { const user = await that.__userService.findByToken({ token: subject.token }); if (user?.payload?.id) { subject.id = user.payload.id; } } const acsResponse = await (0, resolver_1.accessRequest)(subject, resource ?? [], kwargs.action, acsContext, { operation: kwargs.operation, database: database ?? that.__acsDatabaseProvider ?? 'arangoDB', useCache: kwargs.useCache ?? false }); if (acsResponse?.decision !== access_control_1.Response_Decision.PERMIT) { return acsResponse; } if (request) { const arg = acsResponse?.custom_query_args?.find(arg => resource?.some(r => r.resource === arg.resource)); request.custom_queries = arg?.custom_queries; request.custom_arguments = arg?.custom_arguments; } const appResponse = await method.apply(this, arguments); const property = acsResponse.obligations?.filter((o) => resource.some((r) => r.resource === o.resource)).flatMap(o => o.property).flatMap(p => [p, new RegExp(p)]); // @ts-expect-error TS2339 return property?.length ? utils_1._.omitDeep(appResponse, property) : appResponse; } catch (err) { return { operation_status: { code: err.code ?? 500, message: err.details ?? err.message ?? err, } }; } }; }; } function resolves_subject(subjectResolver = (exports.DefaultSubjectResolver)) { return function (target, propertyName, descriptor) { const method = descriptor.value; descriptor.value = async function () { const args = [...arguments].slice(1); const request = await subjectResolver(this, arguments[0], ...args); return await method.apply(this, [request, ...args]); }; }; } ; function injects_meta_data(metaDataInjector = (exports.DefaultMetaDataInjector)) { return function (target, propertyName, descriptor) { const method = descriptor.value; descriptor.value = async function () { const args = [...arguments].slice(1); const request = await metaDataInjector(this, arguments[0], ...args); return await method.apply(this, [request, ...args]); }; }; } ; //# sourceMappingURL=decorators.js.map