• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

restorecommerce / resource-srv / 9427919672

08 Jun 2024 10:01AM UTC coverage: 71.315% (+0.2%) from 71.115%
9427919672

push

github

vanthome
chore: upgrade deps

67 of 90 branches covered (74.44%)

Branch coverage included in aggregate %.

833 of 1172 relevant lines covered (71.08%)

3.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

51.74
/src/utils.ts
1
import {
1✔
2
  AuthZAction,
1✔
3
  accessRequest,
1✔
4
  DecisionResponse,
1✔
5
  Operation,
1✔
6
  PolicySetRQResponse,
1✔
7
  ResolvedSubject,
1✔
8
  HierarchicalScope
1✔
9
} from '@restorecommerce/acs-client';
1✔
10
import { createServiceConfig } from '@restorecommerce/service-config';
1✔
11
import {
1✔
12
  UserServiceClient as UserClient,
1✔
13
  UserResponse,
1✔
14
  UserServiceDefinition as UserServiceDefinition
1✔
15
} from '@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/user.js';
1✔
16
import { createChannel, createClient } from '@restorecommerce/grpc-client';
1✔
17
import { createLogger } from '@restorecommerce/logger';
1✔
18
import { Response_Decision } from '@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/access_control.js';
1✔
19
import { Subject } from '@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/auth.js';
1✔
20
import { FilterOp } from '@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/resource_base.js';
1✔
21
import {
1✔
22
  GraphServiceClient as GraphClient,
1✔
23
  GraphServiceDefinition,
1✔
24
  Options_Direction as Direction,
1✔
25
  TraversalRequest
1✔
26
} from '@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/graph.js';
1✔
27

1✔
28
// Create a ids client instance
1✔
29
let idsClientInstance: UserClient;
1✔
30
const cfg = createServiceConfig(process.cwd());
1✔
31
export const getUserServiceClient = async () => {
1✔
32
  if (!idsClientInstance) {
12✔
33
    // identity-srv client to resolve subject ID by token
1✔
34
    const grpcIDSConfig = cfg.get('client:user');
1✔
35
    const loggerCfg = cfg.get('logger');
1✔
36
    const logger = createLogger(loggerCfg);
1✔
37
    if (grpcIDSConfig) {
1✔
38
      idsClientInstance = createClient({
1✔
39
        ...grpcIDSConfig,
1✔
40
        logger
1✔
41
      }, UserServiceDefinition, createChannel(grpcIDSConfig.address));
1✔
42
    }
1✔
43
  }
1✔
44
  return idsClientInstance;
12✔
45
};
12✔
46

1✔
47
// Create a graph client instance for traversal requests
1✔
48
let graphClientInstance: GraphClient;
1✔
49
export const getGraphServiceClient = async () => {
1✔
50
  if (!graphClientInstance) {
2✔
51
    const cfg = createServiceConfig(process.cwd());
1✔
52
    const grpcGraphConfig = cfg.get('client:graph-srv');
1✔
53
    const loggerCfg = cfg.get('logger');
1✔
54
    const logger = createLogger(loggerCfg);
1✔
55
    if (grpcGraphConfig) {
1✔
56
      graphClientInstance = createClient({
1✔
57
        ...grpcGraphConfig,
1✔
58
        logger
1✔
59
      }, GraphServiceDefinition, createChannel(grpcGraphConfig.address));
1✔
60
    }
1✔
61
  }
1✔
62
  return graphClientInstance;
2✔
63
};
2✔
64

1✔
65
export interface Resource {
1✔
66
  resource: string;
1✔
67
  id?: string | string[]; // for what is allowed operation id is not mandatory
1✔
68
  property?: string[];
1✔
69
}
1✔
70

1✔
71
export interface Attribute {
1✔
72
  id: string;
1✔
73
  value: string;
1✔
74
  attributes: Attribute[];
1✔
75
}
1✔
76

1✔
77
export interface CtxResource {
1✔
78
  id: string;
1✔
79
  meta: {
1✔
80
    created?: Date;
1✔
81
    modified?: Date;
1✔
82
    modified_by?: string;
1✔
83
    owners: Attribute[]; // id and owner is mandatory in ctx resource other attributes are optional
1✔
84
  };
1✔
85
  [key: string]: any;
1✔
86
}
1✔
87

1✔
88
export interface GQLClientContext {
1✔
89
  // if subject is missing by default it will be treated as unauthenticated subject
1✔
90
  subject?: Subject;
1✔
91
  resources?: CtxResource[];
1✔
92
}
1✔
93

1✔
94
export async function checkAccessRequest(ctx: GQLClientContext, resource: Resource[], action: AuthZAction, operation: Operation.isAllowed, useCache?: boolean): Promise<DecisionResponse>;
1✔
95
export async function checkAccessRequest(ctx: GQLClientContext, resource: Resource[], action: AuthZAction, operation: Operation.whatIsAllowed, useCache?: boolean): Promise<PolicySetRQResponse>;
1✔
96

1✔
97
/**
1✔
98
 * Perform an access request using inputs from a GQL request
1✔
99
 *
1✔
100
 * @param subject Subject information
1✔
101
 * @param resources resources
1✔
102
 * @param action The action to perform
1✔
103
 * @param entity The entity type to check access against
1✔
104
 */
1✔
105
/* eslint-disable prefer-arrow-functions/prefer-arrow-functions */
1✔
106
export async function checkAccessRequest(ctx: GQLClientContext, resource: Resource[], action: AuthZAction,
1✔
107
  operation: Operation, useCache = true): Promise<DecisionResponse | PolicySetRQResponse> {
26✔
108
  let subject = ctx.subject as Subject;
26✔
109
  let dbSubject;
26✔
110
  // resolve subject id using findByToken api and update subject with id
26✔
111
  if (subject && subject.token) {
26✔
112
    const idsClient = await getUserServiceClient();
10✔
113
    if (idsClient) {
10✔
114
      dbSubject = await idsClient.findByToken({ token: subject.token });
10✔
115
      if (dbSubject && dbSubject.payload && dbSubject.payload.id) {
10✔
116
        subject.id = dbSubject.payload.id;
10✔
117
      }
10✔
118
    }
10✔
119
  }
10✔
120

26✔
121
  let result: DecisionResponse | PolicySetRQResponse;
26✔
122
  try {
26✔
123
    result = await accessRequest(
26✔
124
      subject,
26✔
125
      resource,
26✔
126
      action,
26✔
127
      ctx,
26✔
128
      {
26✔
129
        operation,
26✔
130
        roleScopingEntityURN: cfg?.get('authorization:urns:roleScopingEntityURN')
26✔
131
      });
26✔
132
  } catch (err: any) {
26!
133
    return {
×
134
      decision: Response_Decision.DENY,
×
135
      operation_status: {
×
136
        code: err.code ?? 500,
×
137
        message: err.details ?? err.message ?? 'Unknown Error!',
×
138
      }
×
139
    };
×
140
  }
×
141
  return result;
26✔
142
}
26✔
143

1✔
144
/**
1✔
145
 * accessResponse returned from `acs-client` contains the filters for the list of
1✔
146
 * resources requested and it returns resource filter map, below api
1✔
147
 * returns applicable `Filters[]` for the specified resource, it iterates through
1✔
148
 * the ACS response and returns the applicable `Filters[]` for the resource.
1✔
149
 * @param accessResponse ACS response
1✔
150
 * @param enitity enitity name
1✔
151
 */
1✔
152
export const getACSFilters = (accessResponse: PolicySetRQResponse, resource: string): FilterOp[] => {
1✔
153
  return accessResponse?.filters?.filter(
10!
154
    (e) => !e.resource && e.resource === resource
10✔
155
  ).flatMap(
10✔
156
    e => e.filters
10✔
157
  ) ?? [];
10✔
158
};
10✔
159

1✔
160
const setNestedChildOrgs = (hrScope: any, targetOrgID: string, subOrgs: any[]) => {
1✔
161
  if (!hrScope) {
×
162
    return;
×
163
  }
×
164

×
165
  if (!Array.isArray(hrScope)) {
×
166
    hrScope = [hrScope];
×
167
  }
×
168

×
169
  for (let subHrScope of hrScope) {
×
170
    if (subHrScope.id === targetOrgID) {
×
171
      if (subHrScope.children) {
×
172
        subHrScope.children.push(...subOrgs);
×
173
      }
×
174
      else {
×
175
        subHrScope.children = [...subOrgs];
×
176
      }
×
177
      return;
×
178
    }
×
179
    for (let item of subHrScope.children) {
×
180
      if (item.id === targetOrgID) {
×
181
        item.children.push(...subOrgs);
×
182
        return;
×
183
      } else {
×
184
        setNestedChildOrgs(item.children, targetOrgID, subOrgs);
×
185
      }
×
186
    }
×
187
  }
×
188
};
×
189

1✔
190
export const getSubTreeOrgs = async (
1✔
191
  orgID: string,
×
192
  role: string,
×
193
  cfg: any,
×
194
  graphClient: GraphClient,
×
195
): Promise<HierarchicalScope> => {
×
196
  const hrScope: HierarchicalScope = { role, id: orgID, children: [] };
×
197
  let traversalResponse: any = [];
×
198
  const hierarchicalResources = cfg.get('authorization:hierarchicalResources') ?? [];
×
199
  const orgTechUser = cfg.get('techUser');
×
200
  for (let hierarchicalResource of hierarchicalResources) {
×
201
    const { collection, edge } = hierarchicalResource;
×
202
    // search in inbound - org has parent org
×
203
    const traversalRequest: TraversalRequest = {
×
204
      subject: orgTechUser,
×
205
      vertices: { collection_name: collection, start_vertex_ids: [orgID] },
×
206
      opts: {
×
207
        direction: Direction.INBOUND,
×
208
        include_edges: [edge]
×
209
      }
×
210
    };
×
211
    const result = await graphClient.traversal(traversalRequest);
×
212
    for await (const partResp of result) {
×
213
      if ((partResp && partResp.data && partResp.data.value)) {
×
214
        traversalResponse.push(...JSON.parse(partResp.data.value.toString()));
×
215
      }
×
216
    }
×
217
  }
×
218

×
219
  console.log(JSON.stringify(traversalResponse, undefined, 2));
×
220

×
221
  for (let item of traversalResponse) {
×
222
    let targetID = item.id;
×
223
    const subOrgs = traversalResponse.filter((e: any) => e.parent_id === targetID);
×
224
    // find hrScopes id and then get the childer object
×
225
    const filteredSubOrgFields = [];
×
226
    for (let org of subOrgs) {
×
227
      filteredSubOrgFields.push({ id: org.id, role, children: [] });
×
228
    }
×
229
    // leaf node or no more children nodes
×
230
    if (filteredSubOrgFields.length === 0) {
×
231
      filteredSubOrgFields.push({ id: targetID, role, children: [] });
×
232
      targetID = item.parent_id;
×
233
    }
×
234
    else {
×
235
      // set sub orgs on target org
×
236
      setNestedChildOrgs(hrScope, targetID, filteredSubOrgFields);
×
237
    }
×
238
  }
×
239
  return hrScope;
×
240
};
×
241

1✔
242
export const createHRScope = async (
1✔
243
  user: UserResponse,
×
244
  token: string,
×
245
  graphClient: GraphClient,
×
246
  cache: any,
×
247
  cfg: any,
×
248
  logger: any,
×
249
): Promise<ResolvedSubject | undefined> => {
×
250
  const subject = user?.payload as ResolvedSubject;
×
251
  const roleScopingEntityURN = cfg.get('authorization:urns:roleScopingEntity');
×
252
  const roleScopingInstanceURN = cfg.get('authorization:urns:roleScopingInstance');
×
253
  if (subject?.role_associations && !subject?.hierarchical_scopes?.length) {
×
254
    // create HR scopes iterating through the user's assigned role scoping instances
×
255
    let userRoleAssocs = subject.role_associations;
×
256
    let assignedUserScopes = new Set<{ userScope: string | undefined; role: string | undefined }>();
×
257
    let tokenData;
×
258
    // verify the validity of subject tokens
×
259
    if (token && user?.payload?.tokens?.length! > 0) {
×
260
      for (let tokenInfo of user?.payload?.tokens ?? []) {
×
261
        if (tokenInfo.token === token) {
×
262
          tokenData = tokenInfo;
×
263
          const expiresIn = tokenInfo.expires_in;
×
264
          if (expiresIn && expiresIn != new Date(0) && expiresIn < new Date()) {
×
265
            logger.info(`Token name ${tokenInfo.name} has expired`);
×
266
            return undefined;
×
267
          }
×
268
        }
×
269
      }
×
270
    }
×
271

×
272
    const reducedUserRoleAssocs = tokenData?.scopes?.flatMap(
×
273
      (scope: string) => userRoleAssocs?.filter(
×
274
        attr => attr.id === scope
×
275
      )
×
276
    ) ?? userRoleAssocs;
×
277

×
278
    for (let roleObj of reducedUserRoleAssocs) {
×
279
      if (roleObj?.attributes?.length! > 0) {
×
280
        for (let roleAttribute of roleObj?.attributes!) {
×
281
          if (roleAttribute.id === roleScopingEntityURN) {
×
282
            for (let roleScopInstObj of roleAttribute.attributes!) {
×
283
              if (roleScopInstObj.id === roleScopingInstanceURN) {
×
284
                let obj = { userScope: roleScopInstObj.value, role: roleObj.role };
×
285
                assignedUserScopes.add(obj);
×
286
              }
×
287
            }
×
288
          }
×
289
        }
×
290
      }
×
291
    }
×
292
    let hrScopes: HierarchicalScope[] = [];
×
293
    let userScopesRoleArray = Array.from(assignedUserScopes);
×
294
    for (let obj of userScopesRoleArray) {
×
295
      try {
×
296
        let hrScope = await getSubTreeOrgs(obj.userScope, obj.role, cfg, graphClient);
×
297
        if (hrScope) {
×
298
          hrScopes.push(hrScope);
×
299
        }
×
300
      } catch (err) {
×
301
        logger.error('Error computing hierarchical scopes', err);
×
302
      }
×
303
    }
×
304
    subject.hierarchical_scopes = hrScopes;
×
305
  }
×
306
  return subject;
×
307
};
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc