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

restorecommerce / identity-srv / 18836031862

27 Oct 2025 09:24AM UTC coverage: 52.139% (-3.9%) from 56.06%
18836031862

push

github

Arun-KumarH
fix: up node version, up kafka-client and other deps

624 of 1352 branches covered (46.15%)

Branch coverage included in aggregate %.

1314 of 2365 relevant lines covered (55.56%)

9.55 hits per line

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

56.64
/src/utils.ts
1
import * as _ from 'lodash-es';
2
import {
3
  AuthZAction, accessRequest, DecisionResponse, Operation, PolicySetRQResponse, ACSClientContext,
4
  ACSClientOptions
5
} from '@restorecommerce/acs-client';
6
import { createServiceConfig } from '@restorecommerce/service-config';
7
import { createClient, createChannel } from '@restorecommerce/grpc-client';
8
import { createLogger } from '@restorecommerce/logger';
9
import bcrypt from 'bcryptjs';
10
import {
11
  DeepPartial,
12
  FilterOp, FilterOp_Operator,
13
  Filter_Operation,
14
  Resource
15
} from '@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/resource_base.js';
16
import {
17
  UserServiceDefinition,
18
  UserServiceClient
19
} from '@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/user.js';
20
import {
21
  Response_Decision
22
} from '@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/access_control.js';
23
import { Subject } from '@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/auth.js';
24
import { OperationStatus, Status } from '@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/status.js';
25

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

46
export interface ACSResource {
47
  resource: string;
48
  id?: string | string[]; // for what is allowed operation id is not mandatory
49
  property?: string[];
50
}
51

52
export async function resolveSubject(subject: Subject) {
53
  if (subject) {
77✔
54
    const idsClient = getUserServiceClient();
15✔
55
    const resp = await idsClient?.findByToken({ token: subject.token });
15✔
56
    if (resp?.payload?.id) {
15!
57
      subject.id = resp.payload.id;
15✔
58
    }
59
  }
60
  return subject;
77✔
61
}
62

63
/**
64
 * reads metadata from DB and updates owner information in resource if action is UPDATE / DELETE
65
 */
66
export const createMetadata = async <T extends Resource>(
2✔
67
  resources: T | T[],
68
  urns: any,
69
  subject?: Subject
70
): Promise<T[]> => {
71
  if (!Array.isArray(resources)) {
×
72
    resources = [resources];
×
73
  }
74

75
  const orgOwnerAttributes = [];
×
76
  for (const resource of resources ?? []) {
×
77
    if (!resource.meta) {
×
78
      resource.meta = {};
×
79
      if (subject?.id) {
×
80
        orgOwnerAttributes.push(
×
81
          {
82
            id: urns.ownerIndicatoryEntity,
83
            value: urns.user,
84
            attributes: [{
85
              id: urns.ownerInstance,
86
              value: subject.id
87
            }]
88
          }
89
        );
90
      } else if (subject?.token) {
×
91
        // when no subjectID is provided find the subjectID using findByToken
92
        subject = await resolveSubject(subject);
×
93
        if (subject?.id) {
×
94
          orgOwnerAttributes.push(
×
95
            {
96
              id: urns.ownerIndicatoryEntity,
97
              value: urns.user,
98
              attributes: [{
99
                id: urns.ownerInstance,
100
                value: subject.id
101
              }]
102
            });
103
        }
104
      }
105
      resource.meta.owners = orgOwnerAttributes;
×
106
    }
107
  }
108

109
  return resources;
×
110
};
111

112
export async function checkAccessRequest(ctx: ACSClientContext, resource: ACSResource[], action: AuthZAction, operation: Operation.isAllowed, useCache?: boolean): Promise<DecisionResponse>;
113
export async function checkAccessRequest(ctx: ACSClientContext, resource: ACSResource[], action: AuthZAction, operation: Operation.whatIsAllowed, useCache?: boolean): Promise<PolicySetRQResponse>;
114

115
/**
116
 * Perform an access request using inputs from a GQL request
117
 *
118
 * @param subject Subject information
119
 * @param resources resources
120
 * @param action The action to perform
121
 * @param entity The entity type to check access against
122
 */
123
export async function checkAccessRequest(
124
  ctx: ACSClientContext,
125
  resource: ACSResource[],
126
  action: AuthZAction,
127
  operation: Operation,
128
  useCache = true
148✔
129
): Promise<DecisionResponse | PolicySetRQResponse> {
130
  const subject = ctx.subject as Subject;
148✔
131
  // resolve subject id using findByToken api and update subject with id
132
  if (!subject?.id && subject?.token) {
148✔
133
    await resolveSubject(subject);
8✔
134
  }
135

136
  let result: DecisionResponse | PolicySetRQResponse;
137
  try {
148✔
138
    result = await accessRequest(
148✔
139
      subject, resource, action, ctx,
140
      {
141
        operation,
142
        useCache,
143
        roleScopingEntityURN: cfg?.get('authorization:urns:roleScopingEntityURN')
144
      } as ACSClientOptions
145
    );
146
  } catch (err: any) {
147
    return {
×
148
      decision: Response_Decision.DENY,
149
      operation_status: {
150
        code: err.code || 500,
×
151
        message: err.details || err.message,
×
152
      }
153
    };
154
  }
155
  return result;
148✔
156
}
157

158
export const password = {
2✔
159
  hash: (pw: string): string => {
160
    const salt = bcrypt.genSaltSync(10);
37✔
161
    const hash = bcrypt.hashSync(pw, salt);
37✔
162
    return hash;
37✔
163
  },
164
  verify: (password_hash: string, pw: string) => {
165
    return bcrypt.compareSync(pw, password_hash);
10✔
166
  }
167
};
168

169
export const marshallProtobufAny = (msg: any): any => {
2✔
170
  return {
90✔
171
    type_url: 'identity.rendering.renderRequest',
172
    value: Buffer.from(JSON.stringify(msg))
173
  };
174
};
175

176
export const unmarshallProtobufAny = (msg: any, logger: any): any => {
2✔
177
  try {
×
178
    if (msg.value) {
×
179
      return JSON.parse(msg.value.toString());
×
180
    }
181
  } catch (error) {
182
    logger.error('Error unmarshalling JSON', msg);
×
183
  }
184
};
185

186
export const getDefaultFilter = (identifier: string): DeepPartial<FilterOp[]> => [{
42✔
187
  filters: [
188
    {
189
      field: 'name',
190
      operation: Filter_Operation.eq,
191
      value: identifier
192
    },
193
    {
194
      field: 'email',
195
      operation: Filter_Operation.eq,
196
      value: identifier
197
    }],
198
  operator: FilterOp_Operator.or
199
}];
200

201
export const getNameFilter = (userName: string) => [{
17✔
202
  filters: [{
203
    field: 'name',
204
    operation: Filter_Operation.eq,
205
    value: userName
206
  }]
207
}];
208

209
export const getLoginIdentifierFilter = (
2✔
210
  loginIdentifiers: any,
211
  value: string
212
): FilterOp[] => {
213
  if (typeof loginIdentifiers === 'string') {
15!
214
    return [{
×
215
      filters: [{
216
        field: loginIdentifiers,
217
        operation: Filter_Operation.eq,
218
        value
219
      }]
220
    }];
221
  } else if (Array.isArray(loginIdentifiers)) {
15!
222
    return [{
15✔
223
      filters: loginIdentifiers.map(
224
        field => ({
18✔
225
          field,
226
          operation: Filter_Operation.eq,
227
          value
228
        })
229
      ),
230
      operator: FilterOp_Operator.or
231
    }];
232
  }
233
};
234

235
export const returnOperationStatus = (code: number, message: string) => ({
19✔
236
  operation_status: {
237
    code: code ?? 500,
19!
238
    message
239
  } as OperationStatus
240
});
241

242
export const returnStatus = (
2✔
243
  code: number,
244
  message: string,
245
  id?: string
246
) => ({
173✔
247
  status: {
248
    id,
249
    code: code ?? 500,
173!
250
    message
251
  } as Status
252
});
253

254
export const returnCodeMessage = (
2✔
255
  code: number,
256
  message: string
257
): OperationStatus => ({
59✔
258
  code: code ?? 500,
59!
259
  message
260
});
261

262
interface CodeIdMsgObj {
263
  code: number;
264
  message: string;
265
  id?: string;
266
}
267

268
export const returnStatusArray = (
2✔
269
  codeIdMsgObj: CodeIdMsgObj[]
270
) => ({
×
271
  status: [...codeIdMsgObj]
272
});
273

274
/**
275
 * accessResponse returned from `acs-client` contains the filters for the list of
276
 * resources requested and it returns resource filter map, below api
277
 * returns applicable `Filters[]` for the specified resource, it iterates through
278
 * the ACS response and returns the applicable `Filters[]` for the resource.
279
 * @param accessResponse ACS response
280
 * @param enitity enitity name
281
 */
282
export const getACSFilters = (
2✔
283
  accessResponse: PolicySetRQResponse,
284
  resource: string
285
): FilterOp[] => accessResponse?.filters?.find(
34✔
286
  (e) => e?.resource === resource
×
287
    && e?.filters[0]?.filters?.length
288
)?.filters ?? [];
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