Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: release #318

Merged
merged 3 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
### Build
FROM node:20.11.1-alpine3.19 as build
FROM node:22.2.0-alpine3.20 as build
ENV NO_UPDATE_NOTIFIER=true

USER node
Expand All @@ -17,7 +17,7 @@ RUN npm run build


### Deployment
FROM node:20.11.1-alpine3.19 as deployment
FROM node:22.2.0-alpine3.20 as deployment

ENV NO_UPDATE_NOTIFIER=true

Expand Down
2 changes: 1 addition & 1 deletion test/cfg/config_test.json → cfg/config_test.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"logger": {
"console": {
"handleExceptions": false,
"level": "silly",
"level": "crit",
"colorize": true,
"prettyPrint": true
}
Expand Down
4,567 changes: 963 additions & 3,604 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 3 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
"dependencies": {
"@restorecommerce/acs-client": "^1.6.6",
"@restorecommerce/chassis-srv": "^1.6.0",
"@restorecommerce/cluster-service": "^1.0.3",
"@restorecommerce/grpc-client": "^2.2.1",
"@restorecommerce/kafka-client": "^1.2.5",
"@restorecommerce/rc-grpc-clients": "^5.1.27",
Expand All @@ -37,7 +36,7 @@
"@commitlint/cli": "^19.2.2",
"@commitlint/config-conventional": "^19.2.2",
"@grpc/proto-loader": "^0.7.12",
"@restorecommerce/dev": "^0.0.9",
"@restorecommerce/dev": "^0.0.12",
"@restorecommerce/logger": "^1.2.10",
"@semantic-release-plus/docker": "^3.1.3",
"@types/lodash-es": "^4.17.12",
Expand All @@ -52,8 +51,8 @@
"coveralls": "^3.1.1",
"cross-env": "^7.0.3",
"cz-conventional-changelog": "^3.3.0",
"esbuild": "^0.20.2",
"eslint": "^8.57.0",
"esbuild": "^0.21.4",
"eslint": "^8.56.0",
"eslint-plugin-prefer-arrow-functions": "^3.3.2",
"husky": "^9.0.11",
"mocha": "^10.4.0",
Expand Down
223 changes: 117 additions & 106 deletions src/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,47 +4,48 @@ import { RedisClientType } from 'redis';
import { ResourcesAPIBase, ServiceBase } from '@restorecommerce/resource-base-interface';
import { ACSAuthZ, DecisionResponse, Operation, PolicySetRQResponse, ResolvedSubject } from '@restorecommerce/acs-client';
import { AuthZAction } from '@restorecommerce/acs-client';
import { checkAccessRequest, getACSFilters } from './utils.js';
import { checkAccessRequest, getACSFilters, resolveSubject } from './utils.js';
import * as uuid from 'uuid';
import { Response_Decision } from '@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/access_control.js';
import { ReadRequest, DeleteRequest, DeepPartial, DeleteResponse } from '@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/resource_base.js';
import { Filter_Operation } from '@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/filter.js';
import {
ReadRequest,
DeleteRequest,
DeepPartial,
DeleteResponse,
Resource,
ResourceListResponse,
ResourceList
} from '@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/resource_base.js';
import { Filter_Operation, Filter_ValueType } from '@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/filter.js';
import { Subject } from '@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/auth.js';

export class ResourceService extends ServiceBase<ResourceListResponse, ResourceList> {
private readonly urns: any;

export class ResourceService extends ServiceBase<any, any> {
authZ: ACSAuthZ;
redisClient: RedisClientType;
cfg: any;
resourceName: string;
constructor(
resourceName: string,
private readonly resourceName: string,
resourceEvents: any,
cfg: any,
logger: Logger,
resourceAPI: ResourcesAPIBase,
isEventsEnabled: boolean,
authZ: ACSAuthZ,
redisClientSubject: RedisClientType,
) {
super(resourceName, resourceEvents, logger, resourceAPI, isEventsEnabled);
this.authZ = authZ;
this.cfg = cfg;
this.resourceName = resourceName;
this.redisClient = redisClientSubject;
this.urns = cfg.get('authorization:urns');
}

async create(request: any, ctx: any) {
let data = request.items;
let subject = request.subject;
const subject = await resolveSubject(request.subject);
// update meta data for owners information
const acsResources = await this.createMetadata(data, AuthZAction.CREATE, subject);
request.items = await this.createMetadata(request.items, AuthZAction.CREATE, subject);
let acsResponse: DecisionResponse;
try {
ctx ??= {};
ctx.subject = subject;
ctx.resources = acsResources;
ctx.resources = request.items;
acsResponse = await checkAccessRequest(
ctx,
[{ resource: this.resourceName, id: acsResources.map((item: any) => item.id) }],
[{ resource: this.resourceName, id: request.items.map((item: any) => item.id) }],
AuthZAction.CREATE,
Operation.isAllowed
);
Expand All @@ -64,7 +65,7 @@ export class ResourceService extends ServiceBase<any, any> {
}

async read(request: ReadRequest, ctx: any): Promise<DeepPartial<any>> {
const subject = request.subject;
const subject = await resolveSubject(request.subject);
let acsResponse: PolicySetRQResponse;
try {
if (!ctx) { ctx = {}; };
Expand Down Expand Up @@ -102,7 +103,7 @@ export class ResourceService extends ServiceBase<any, any> {
}

async update(request: any, ctx: any) {
let subject = request.subject;
const subject = await resolveSubject(request.subject);
// update meta data for owner information
const acsResources = await this.createMetadata(request.items, AuthZAction.MODIFY, subject);
let acsResponse: DecisionResponse;
Expand Down Expand Up @@ -132,7 +133,7 @@ export class ResourceService extends ServiceBase<any, any> {
}

async upsert(request: any, ctx: any) {
let subject = request.subject;
const subject = await resolveSubject(request.subject);
const acsResources = await this.createMetadata(request.items, AuthZAction.MODIFY, subject);
let acsResponse: DecisionResponse;
try {
Expand Down Expand Up @@ -163,8 +164,8 @@ export class ResourceService extends ServiceBase<any, any> {
async delete(request: DeleteRequest, ctx: any): Promise<DeepPartial<DeleteResponse>> {
let resourceIDs = request.ids;
let resources = [];
let acsResources = [];
let subject = request.subject;
let acsResources = new Array<any>();
const subject = await resolveSubject(request.subject);
let action = AuthZAction.DELETE;
if (resourceIDs) {
if (Array.isArray(resourceIDs)) {
Expand All @@ -175,7 +176,7 @@ export class ResourceService extends ServiceBase<any, any> {
resources = [{ id: resourceIDs }];
}
Object.assign(resources, { id: resourceIDs });
acsResources = await this.createMetadata(resources, action, subject as ResolvedSubject);
acsResources = await this.createMetadata<any>(resources, action, subject as ResolvedSubject);
}
if (request.collection) {
action = AuthZAction.DROP;
Expand Down Expand Up @@ -208,97 +209,107 @@ export class ResourceService extends ServiceBase<any, any> {
}

/**
* reads meta data from DB and updates owner information in resource if action is UPDATE / DELETE
* @param reaources list of resources
* @param entity entity name
* @param action resource action
*/
async createMetadata(resources: any, action: string, subject?: ResolvedSubject): Promise<any> {
let orgOwnerAttributes = [];
if (resources && !Array.isArray(resources)) {
* reads meta data from DB and updates owners information in resource if action is UPDATE / DELETE
* @param reaources list of resources
* @param entity entity name
* @param action resource action
*/
async createMetadata<T extends Resource>(
resources: T | T[],
action: string,
subject?: Subject
): Promise<T[]> {

if (!Array.isArray(resources)) {
resources = [resources];
}
const urns = this.cfg.get('authorization:urns');
if (subject && subject.scope && (action === AuthZAction.CREATE || action === AuthZAction.MODIFY)) {
// add user and subject scope as default owner
orgOwnerAttributes.push(
{
id: urns?.ownerIndicatoryEntity,
value: urns?.organization,
attributes: [{
id: urns?.ownerInstance,
value: subject?.scope
}]
});
}

if (resources?.length > 0) {
for (let resource of resources) {
if (!resource.meta) {
resource.meta = {};
}
if (action === AuthZAction.MODIFY || action === AuthZAction.DELETE) {
let result = await super.read(ReadRequest.fromPartial({
filters: [{
filters: [{
field: 'id',
operation: Filter_Operation.eq,
value: resource?.id
const setDefaultMeta = (resource: T) => {
if (!resource.id?.length) {
resource.id = uuid.v4().replace(/-/g, '');
}

if (!resource.meta) {
resource.meta = {};
resource.meta.owners = [];

if (subject?.scope) {
resource.meta.owners.push(
{
id: this.urns.ownerIndicatoryEntity,
value: this.urns.organization,
attributes: [{
id: this.urns.ownerInstance,
value: subject.scope
}]
}]
}) as any, {});
// update owner info
if (result?.items?.length === 1) {
let item = result.items[0].payload;
resource.meta.owners = item?.meta?.owners;
} else if (result?.items?.length === 0) {
if (!resource?.id?.length) {
resource.id = uuid.v4().replace(/-/g, '');
}
let ownerAttributes;
if (!resource?.meta?.owners) {
ownerAttributes = _.cloneDeep(orgOwnerAttributes);
} else {
ownerAttributes = resource.meta.owners;
}
if (subject?.id) {
ownerAttributes.push(
{
id: urns?.ownerIndicatoryEntity,
value: urns?.user,
attributes: [{
id: urns?.ownerInstance,
value: subject?.id
}]
});
);
}

if (subject?.id) {
resource.meta.owners.push(
{
id: this.urns.ownerIndicatoryEntity,
value: this.urns.user,
attributes: [{
id: this.urns.ownerInstance,
value: subject.id
}]
}
resource.meta.owners = ownerAttributes;
}
} else if (action === AuthZAction.CREATE) {
if (!resource?.id?.length) {
resource.id = uuid.v4().replace(/-/g, '');
}
let ownerAttributes;
if (!resource?.meta?.owners) {
ownerAttributes = _.cloneDeep(orgOwnerAttributes);
} else {
ownerAttributes = resource.meta.owners;
}
if (subject?.id) {
ownerAttributes.push(
);
}
}
};

if (action === AuthZAction.MODIFY || action === AuthZAction.DELETE) {
const ids = [
...new Set(
resources.map(
r => r.id
).filter(
id => id
)
).values()
];
const filters = ReadRequest.fromPartial({
filters: [
{
filters: [
{
id: urns?.ownerIndicatoryEntity,
value: urns?.user,
attributes: [{
id: urns?.ownerInstance,
value: subject?.id
}]
});
field: 'id',
operation: Filter_Operation.in,
value: JSON.stringify(ids),
type: Filter_ValueType.ARRAY
}
]
}
resource.meta.owners = ownerAttributes;
],
limit: ids.length
});

const result_map = await super.read(filters, {}).then(
resp => new Map(
resp.items?.map(
item => [item.payload?.id, item?.payload]
)
)
);

for (let resource of resources) {
if (!resource.meta && result_map.has(resource?.id)) {
resource.meta = result_map.get(resource?.id).meta;
}
else {
setDefaultMeta(resource);
}
}
}
else if (action === AuthZAction.CREATE) {
for (let resource of resources) {
setDefaultMeta(resource);
}
}

return resources;
}
}
6 changes: 1 addition & 5 deletions src/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ import { createLogger } from '@restorecommerce/logger';
// cfg and logger
const cfg = createServiceConfig(process.cwd());
const loggerCfg = cfg.get('logger');
loggerCfg.esTransformer = (msg) => {
msg.fields = JSON.stringify(msg.fields);
return msg;
};
const logger = createLogger(loggerCfg);

const worker = new Worker();
Expand All @@ -22,4 +18,4 @@ process.on('SIGINT', () => {
logger.error('shutdown error', err);
process.exit(1);
});
});
});
Loading
Loading