@develit-io/backend-sdk 9.3.0 → 9.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -447,11 +447,14 @@ interface CommandItem<TAuditAction = string> {
447
447
  interface IdempotencyContextVariables {
448
448
  key: string;
449
449
  }
450
- interface UserContextVariables {
451
- email: string;
452
- role: string;
453
- organizationId: string;
454
- }
450
+ type IdentityContextVariables = {
451
+ user: {
452
+ id: string;
453
+ email: string;
454
+ role: string;
455
+ rawUserMetaData: string | null;
456
+ };
457
+ };
455
458
 
456
459
  /**
457
460
  * Utility type to infer possible relation includes (`with`) for a given table.
@@ -844,4 +847,4 @@ type AsyncMethod<TArgs extends unknown[] = unknown[], TResult = unknown> = (...a
844
847
  declare function cloudflareQueue<TArgs extends unknown[] = unknown[], TResult = unknown>(options: WithRetryCounterOptions): (target: unknown, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<AsyncMethod<TArgs, TResult>>) => void;
845
848
 
846
849
  export { DatabaseTransaction, ENVIRONMENT, RPCResponse, USER_ROLES, action, asNonEmpty, bankAccount, bankAccountMetadataSchema, base, bicSchema, buildMultiFilterConditions, buildRangeFilterConditions, buildSearchConditions, calculateExponentialBackoff, cloudflareQueue, composeWranglerBase, createAuditLogWriter, createInternalError, defineCommand, derivePortFromId, develitWorker, durableObjectNamespaceIdFromName, first, firstOrError, getD1Credentials, getD1DatabaseIdFromWrangler, getDrizzleD1Config, getSecret, handleAction, ibanSchema, isInternalError, nullToOptional, optionalToNull, paginationQuerySchema, paginationSchema, resolveColumn, service, useFetch, useResult, useResultSync, uuidv4, workflowInstanceStatusSchema };
847
- export type { ActionExecution, ActionHandlerOptions, AuditLogWriter, BankAccountMetadata, BaseEvent, BuildSearchOptions, Command, CommandLogPayload, DevelitWorkerMethods, Environment, GatewayResponse, IRPCResponse, IdempotencyContextVariables, IncludeRelation, InferResultType, InternalError, InternalErrorResponseStatus, Project, RequestLog, ResponseLog, UserContextVariables, UserRole, ValidatedInput, WorkflowInstanceStatus };
850
+ export type { ActionExecution, ActionHandlerOptions, AuditLogWriter, BankAccountMetadata, BaseEvent, BuildSearchOptions, Command, CommandLogPayload, DevelitWorkerMethods, Environment, GatewayResponse, IRPCResponse, IdempotencyContextVariables, IdentityContextVariables, IncludeRelation, InferResultType, InternalError, InternalErrorResponseStatus, Project, RequestLog, ResponseLog, UserRole, ValidatedInput, WorkflowInstanceStatus };
package/dist/index.d.ts CHANGED
@@ -447,11 +447,14 @@ interface CommandItem<TAuditAction = string> {
447
447
  interface IdempotencyContextVariables {
448
448
  key: string;
449
449
  }
450
- interface UserContextVariables {
451
- email: string;
452
- role: string;
453
- organizationId: string;
454
- }
450
+ type IdentityContextVariables = {
451
+ user: {
452
+ id: string;
453
+ email: string;
454
+ role: string;
455
+ rawUserMetaData: string | null;
456
+ };
457
+ };
455
458
 
456
459
  /**
457
460
  * Utility type to infer possible relation includes (`with`) for a given table.
@@ -844,4 +847,4 @@ type AsyncMethod<TArgs extends unknown[] = unknown[], TResult = unknown> = (...a
844
847
  declare function cloudflareQueue<TArgs extends unknown[] = unknown[], TResult = unknown>(options: WithRetryCounterOptions): (target: unknown, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<AsyncMethod<TArgs, TResult>>) => void;
845
848
 
846
849
  export { DatabaseTransaction, ENVIRONMENT, RPCResponse, USER_ROLES, action, asNonEmpty, bankAccount, bankAccountMetadataSchema, base, bicSchema, buildMultiFilterConditions, buildRangeFilterConditions, buildSearchConditions, calculateExponentialBackoff, cloudflareQueue, composeWranglerBase, createAuditLogWriter, createInternalError, defineCommand, derivePortFromId, develitWorker, durableObjectNamespaceIdFromName, first, firstOrError, getD1Credentials, getD1DatabaseIdFromWrangler, getDrizzleD1Config, getSecret, handleAction, ibanSchema, isInternalError, nullToOptional, optionalToNull, paginationQuerySchema, paginationSchema, resolveColumn, service, useFetch, useResult, useResultSync, uuidv4, workflowInstanceStatusSchema };
847
- export type { ActionExecution, ActionHandlerOptions, AuditLogWriter, BankAccountMetadata, BaseEvent, BuildSearchOptions, Command, CommandLogPayload, DevelitWorkerMethods, Environment, GatewayResponse, IRPCResponse, IdempotencyContextVariables, IncludeRelation, InferResultType, InternalError, InternalErrorResponseStatus, Project, RequestLog, ResponseLog, UserContextVariables, UserRole, ValidatedInput, WorkflowInstanceStatus };
850
+ export type { ActionExecution, ActionHandlerOptions, AuditLogWriter, BankAccountMetadata, BaseEvent, BuildSearchOptions, Command, CommandLogPayload, DevelitWorkerMethods, Environment, GatewayResponse, IRPCResponse, IdempotencyContextVariables, IdentityContextVariables, IncludeRelation, InferResultType, InternalError, InternalErrorResponseStatus, Project, RequestLog, ResponseLog, UserRole, ValidatedInput, WorkflowInstanceStatus };
@@ -1,5 +1,16 @@
1
1
  import { MiddlewareHandler } from 'hono/types';
2
2
 
3
+ interface AccessMiddlewareOptions {
4
+ errorMessage?: string;
5
+ }
6
+ interface AccessRequest<TScope extends string = string> {
7
+ scope: TScope;
8
+ resourceId?: string;
9
+ resourcePath?: string;
10
+ }
11
+ type AccessRequestResolver<TScope extends string = string> = AccessRequest<TScope>[] | ((context: unknown) => AccessRequest<TScope>[]);
12
+ declare const access: <TScope extends string = string>(accessRequests: AccessRequestResolver<TScope>, options?: AccessMiddlewareOptions) => MiddlewareHandler;
13
+
3
14
  declare const idempotency: () => MiddlewareHandler;
4
15
 
5
16
  declare const jwt: () => MiddlewareHandler;
@@ -10,4 +21,4 @@ declare const logger: () => MiddlewareHandler;
10
21
 
11
22
  declare const signature: () => MiddlewareHandler;
12
23
 
13
- export { idempotency, ip, jwt, logger, signature };
24
+ export { access, idempotency, ip, jwt, logger, signature };
@@ -1,5 +1,16 @@
1
1
  import { MiddlewareHandler } from 'hono/types';
2
2
 
3
+ interface AccessMiddlewareOptions {
4
+ errorMessage?: string;
5
+ }
6
+ interface AccessRequest<TScope extends string = string> {
7
+ scope: TScope;
8
+ resourceId?: string;
9
+ resourcePath?: string;
10
+ }
11
+ type AccessRequestResolver<TScope extends string = string> = AccessRequest<TScope>[] | ((context: unknown) => AccessRequest<TScope>[]);
12
+ declare const access: <TScope extends string = string>(accessRequests: AccessRequestResolver<TScope>, options?: AccessMiddlewareOptions) => MiddlewareHandler;
13
+
3
14
  declare const idempotency: () => MiddlewareHandler;
4
15
 
5
16
  declare const jwt: () => MiddlewareHandler;
@@ -10,4 +21,4 @@ declare const logger: () => MiddlewareHandler;
10
21
 
11
22
  declare const signature: () => MiddlewareHandler;
12
23
 
13
- export { idempotency, ip, jwt, logger, signature };
24
+ export { access, idempotency, ip, jwt, logger, signature };
@@ -33,6 +33,26 @@ const getRequestUserAgent = (request) => {
33
33
  return request.header("user-agent") || "UNKNOWN";
34
34
  };
35
35
 
36
+ const access = (accessRequests, options = {}) => {
37
+ return createMiddleware(async (context, next) => {
38
+ if (!context.env.MIDDLEWARE_ACCESS_DISABLED) {
39
+ const identity = context.get("identity");
40
+ const requests = typeof accessRequests === "function" ? accessRequests(context) : accessRequests;
41
+ const { data: verifyData, error: verifyError } = await context.env.RBAC_SERVICE.verifyAccess({
42
+ userId: identity.user.id,
43
+ accessRequests: requests,
44
+ jwt: identity
45
+ });
46
+ if (verifyError || !verifyData?.isVerified) {
47
+ throw new HTTPException(404, {
48
+ message: options.errorMessage || "Forbidden"
49
+ });
50
+ }
51
+ }
52
+ await next();
53
+ });
54
+ };
55
+
36
56
  const idempotency = () => {
37
57
  return createMiddleware(async (context, next) => {
38
58
  if (!context.env.MIDDLEWARE_IDEMPOTENCY_DISABLED) {
@@ -94,17 +114,13 @@ const jwt = () => {
94
114
  }
95
115
  const rawUserMetaDataString = data.payload.user.rawUserMetaData;
96
116
  const rawUserMetaData = rawUserMetaDataString ? JSON.parse(rawUserMetaDataString) : null;
97
- const organizationId = rawUserMetaData?.organizationId ?? null;
98
- if (!organizationId) {
117
+ const identityId = rawUserMetaData.organizationId ?? rawUserMetaData.exchangeOfficeId;
118
+ if (!identityId) {
99
119
  throw new HTTPException(422, {
100
120
  message: "User data integrity check failed."
101
121
  });
102
122
  }
103
- context.set("user", {
104
- email: data.payload.user.email,
105
- role: data.payload.user.role,
106
- organizationId
107
- });
123
+ context.set("identity", data.payload);
108
124
  }
109
125
  await next();
110
126
  });
@@ -114,14 +130,17 @@ const ip = () => {
114
130
  return createMiddleware(async (context, next) => {
115
131
  if (!context.env.MIDDLEWARE_IP_DISABLED) {
116
132
  const requestIp = getRequestIpAddress(context.req);
117
- const user = context.get("user");
118
- if (!user.organizationId) {
133
+ const identityContext = context.get("identity");
134
+ const rawUserMetaDataString = identityContext.user.rawUserMetaData;
135
+ const rawUserMetaData = rawUserMetaDataString ? JSON.parse(rawUserMetaDataString) : null;
136
+ const organizationId = rawUserMetaData.organizationId;
137
+ if (!organizationId) {
119
138
  throw new HTTPException(401, {
120
139
  message: "Failed to retrieve request organization ID."
121
140
  });
122
141
  }
123
142
  const { data: organization, error } = await context.env.ORGANIZATION_SERVICE.getOrganization({
124
- organizationId: user.organizationId
143
+ organizationId
125
144
  });
126
145
  if (!organization || error) {
127
146
  throw new HTTPException(404, {
@@ -166,7 +185,15 @@ const logger = () => {
166
185
  const requestLog = await composeRequestLog(context.req);
167
186
  logRequest(requestLog);
168
187
  if (!context.env.MIDDLEWARE_LOGGER_AUDITLOG_DISABLED) {
169
- const userContext = context.get("user");
188
+ const identityContext = context.get("identity");
189
+ const rawUserMetaDataString = identityContext.user.rawUserMetaData;
190
+ const rawUserMetaData = rawUserMetaDataString ? JSON.parse(rawUserMetaDataString) : null;
191
+ const identityId = rawUserMetaData.organizationId ?? rawUserMetaData.exchangeOfficeId;
192
+ if (!identityId) {
193
+ throw new HTTPException(422, {
194
+ message: "User data integrity check failed."
195
+ });
196
+ }
170
197
  const requestId = uuidv4();
171
198
  context.set("auditLog", {
172
199
  requestId
@@ -176,8 +203,12 @@ const logger = () => {
176
203
  method: requestLog.method,
177
204
  path: requestLog.path,
178
205
  actor: {
179
- email: userContext?.email || "NOT_AUTHORIZED",
180
- organizationId: userContext?.organizationId || "NOT_AUTHORIZED"
206
+ email: identityContext.user.email || "NOT_AUTHORIZED",
207
+ ...rawUserMetaData.organizationId ? {
208
+ organizationId: rawUserMetaData.organizationId || "NOT_AUTHORIZED"
209
+ } : rawUserMetaData.organizationId ? {
210
+ exchangeOfficeId: rawUserMetaData.exchangeOfficeId || "NOT_AUTHORIZED"
211
+ } : null
181
212
  },
182
213
  metadata: {
183
214
  ip: getRequestIpAddress(context.req),
@@ -198,15 +229,27 @@ const logger = () => {
198
229
  );
199
230
  logResponse(responseLog);
200
231
  if (!context.env.MIDDLEWARE_LOGGER_AUDITLOG_DISABLED) {
201
- const userContext = context.get("user");
232
+ const identityContext = context.get("identity");
233
+ const rawUserMetaDataString = identityContext.user.rawUserMetaData;
234
+ const rawUserMetaData = rawUserMetaDataString ? JSON.parse(rawUserMetaDataString) : null;
235
+ const identityId = rawUserMetaData.organizationId ?? rawUserMetaData.exchangeOfficeId;
236
+ if (!identityId) {
237
+ throw new HTTPException(422, {
238
+ message: "User data integrity check failed."
239
+ });
240
+ }
202
241
  const auditLogContext = context.get("auditLog");
203
242
  await context.env.AUDITLOG_SERVICE.processLog({
204
243
  type: "RESPONSE",
205
244
  method: responseLog.method,
206
245
  path: responseLog.path,
207
246
  actor: {
208
- email: userContext?.email || "NOT_AUTHORIZED",
209
- organizationId: userContext?.organizationId || "NOT_AUTHORIZED"
247
+ email: identityContext.user.email || "NOT_AUTHORIZED",
248
+ ...rawUserMetaData.organizationId ? {
249
+ organizationId: rawUserMetaData.organizationId || "NOT_AUTHORIZED"
250
+ } : rawUserMetaData.organizationId ? {
251
+ exchangeOfficeId: rawUserMetaData.exchangeOfficeId || "NOT_AUTHORIZED"
252
+ } : null
210
253
  },
211
254
  metadata: {
212
255
  ip: getRequestIpAddress(context.req),
@@ -236,14 +279,17 @@ const signature = () => {
236
279
  });
237
280
  }
238
281
  const payload = JSON.stringify(await context.req.json().catch(() => null));
239
- const user = context.get("user");
240
- if (!user.organizationId) {
282
+ const identityContext = context.get("identity");
283
+ const rawUserMetaDataString = identityContext.user.rawUserMetaData;
284
+ const rawUserMetaData = rawUserMetaDataString ? JSON.parse(rawUserMetaDataString) : null;
285
+ const identityId = rawUserMetaData.organizationId ?? rawUserMetaData.exchangeOfficeId;
286
+ if (!identityId) {
241
287
  throw new HTTPException(401, {
242
- message: "Failed to retrieve request organization ID."
288
+ message: "Failed to retrieve request identity ID."
243
289
  });
244
290
  }
245
291
  const { data: organization, error } = await context.env.ORGANIZATION_SERVICE.getOrganization({
246
- organizationId: user.organizationId
292
+ organizationId: identityId
247
293
  });
248
294
  if (!organization || error) {
249
295
  throw new HTTPException(404, {
@@ -273,4 +319,4 @@ const signature = () => {
273
319
  });
274
320
  };
275
321
 
276
- export { idempotency, ip, jwt, logger, signature };
322
+ export { access, idempotency, ip, jwt, logger, signature };
@@ -1,11 +1,12 @@
1
- declare const createSignatureKeyPair: () => Promise<{
2
- publicKey: string;
3
- privateKey: string;
4
- }>;
5
1
  declare const signPayload: ({ payload, privateKey, }: {
6
2
  payload: string;
7
3
  privateKey: string;
8
4
  }) => Promise<string>;
5
+
6
+ declare const createSignatureKeyPair: () => Promise<{
7
+ publicKey: string;
8
+ privateKey: string;
9
+ }>;
9
10
  declare const algParams: {
10
11
  [key: string]: {
11
12
  name: string;
@@ -1,11 +1,12 @@
1
- declare const createSignatureKeyPair: () => Promise<{
2
- publicKey: string;
3
- privateKey: string;
4
- }>;
5
1
  declare const signPayload: ({ payload, privateKey, }: {
6
2
  payload: string;
7
3
  privateKey: string;
8
4
  }) => Promise<string>;
5
+
6
+ declare const createSignatureKeyPair: () => Promise<{
7
+ publicKey: string;
8
+ privateKey: string;
9
+ }>;
9
10
  declare const algParams: {
10
11
  [key: string]: {
11
12
  name: string;
@@ -1,21 +1,3 @@
1
- const createSignatureKeyPair = async () => {
2
- const { publicKey, privateKey } = await crypto.subtle.generateKey(
3
- {
4
- name: "RSASSA-PKCS1-v1_5",
5
- modulusLength: 4096,
6
- publicExponent: new Uint8Array([1, 0, 1]),
7
- hash: "SHA-256"
8
- },
9
- true,
10
- ["sign", "verify"]
11
- );
12
- const exportedPublicKey = await crypto.subtle.exportKey("spki", publicKey);
13
- const exportedPrivateKey = await crypto.subtle.exportKey("pkcs8", privateKey);
14
- return {
15
- publicKey: Buffer.from(exportedPublicKey).toString("base64"),
16
- privateKey: Buffer.from(exportedPrivateKey).toString("base64")
17
- };
18
- };
19
1
  const signPayload = async ({
20
2
  payload,
21
3
  privateKey
@@ -47,6 +29,25 @@ const signPayload = async ({
47
29
  );
48
30
  return base64Signature;
49
31
  };
32
+
33
+ const createSignatureKeyPair = async () => {
34
+ const { publicKey, privateKey } = await crypto.subtle.generateKey(
35
+ {
36
+ name: "RSASSA-PKCS1-v1_5",
37
+ modulusLength: 4096,
38
+ publicExponent: new Uint8Array([1, 0, 1]),
39
+ hash: "SHA-256"
40
+ },
41
+ true,
42
+ ["sign", "verify"]
43
+ );
44
+ const exportedPublicKey = await crypto.subtle.exportKey("spki", publicKey);
45
+ const exportedPrivateKey = await crypto.subtle.exportKey("pkcs8", privateKey);
46
+ return {
47
+ publicKey: Buffer.from(exportedPublicKey).toString("base64"),
48
+ privateKey: Buffer.from(exportedPrivateKey).toString("base64")
49
+ };
50
+ };
50
51
  const algParams = {
51
52
  RSA: {
52
53
  name: "RSASSA-PKCS1-v1_5",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@develit-io/backend-sdk",
3
- "version": "9.3.0",
3
+ "version": "9.4.0",
4
4
  "description": "Develit Backend SDK",
5
5
  "author": "Develit.io",
6
6
  "license": "ISC",