@restorecommerce/acs-client
Version:
Access Control Service Client
191 lines • 8.41 kB
JavaScript
;
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