glasswork 1.0.1 → 1.2.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/CHANGELOG.md +48 -1
- package/README.md +37 -2
- package/dist/audit/audit-logger.d.ts +22 -0
- package/dist/audit/audit-logger.d.ts.map +1 -0
- package/dist/audit/audit-logger.js +44 -0
- package/dist/audit/audit-logger.js.map +1 -0
- package/dist/audit/context.d.ts +13 -0
- package/dist/audit/context.d.ts.map +1 -0
- package/dist/audit/context.js +66 -0
- package/dist/audit/context.js.map +1 -0
- package/dist/audit/index.d.ts +8 -0
- package/dist/audit/index.d.ts.map +1 -0
- package/dist/audit/index.js +7 -0
- package/dist/audit/index.js.map +1 -0
- package/dist/audit/types.d.ts +74 -0
- package/dist/audit/types.d.ts.map +1 -0
- package/dist/audit/types.js +2 -0
- package/dist/audit/types.js.map +1 -0
- package/dist/auth/abilities.d.ts +1 -1
- package/dist/auth/abilities.d.ts.map +1 -1
- package/dist/auth/abilities.js.map +1 -1
- package/dist/auth/better-auth-provider.d.ts +17 -6
- package/dist/auth/better-auth-provider.d.ts.map +1 -1
- package/dist/auth/better-auth-provider.js +16 -9
- package/dist/auth/better-auth-provider.js.map +1 -1
- package/dist/auth/index.d.ts +2 -2
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/middleware.d.ts +10 -8
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js +27 -11
- package/dist/auth/middleware.js.map +1 -1
- package/dist/auth/types.d.ts +13 -2
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/config/config-service.d.ts.map +1 -1
- package/dist/config/config-service.js.map +1 -1
- package/dist/config/providers.js.map +1 -1
- package/dist/core/bootstrap.d.ts.map +1 -1
- package/dist/core/bootstrap.js +16 -7
- package/dist/core/bootstrap.js.map +1 -1
- package/dist/core/module-graph.js +3 -0
- package/dist/core/module-graph.js.map +1 -1
- package/dist/core/request-scope.d.ts +12 -0
- package/dist/core/request-scope.d.ts.map +1 -0
- package/dist/core/request-scope.js +86 -0
- package/dist/core/request-scope.js.map +1 -0
- package/dist/core/route-factory.d.ts +1 -0
- package/dist/core/route-factory.d.ts.map +1 -1
- package/dist/core/types.d.ts +6 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/email/compiler/compiler.d.ts.map +1 -1
- package/dist/email/compiler/compiler.js +72 -20
- package/dist/email/compiler/compiler.js.map +1 -1
- package/dist/email/compiler/tokenizer.js +4 -2
- package/dist/email/compiler/tokenizer.js.map +1 -1
- package/dist/email/compiler/types.d.ts +2 -0
- package/dist/email/compiler/types.d.ts.map +1 -1
- package/dist/email/email-service.d.ts.map +1 -1
- package/dist/email/template-registry.d.ts.map +1 -1
- package/dist/email/templated-email-service.d.ts.map +1 -1
- package/dist/email/transports/mock-transport.d.ts.map +1 -1
- package/dist/email/transports/ses-transport.d.ts +8 -0
- package/dist/email/transports/ses-transport.d.ts.map +1 -1
- package/dist/email/transports/ses-transport.js +132 -25
- package/dist/email/transports/ses-transport.js.map +1 -1
- package/dist/email/webhooks/handler-factory.d.ts +1 -0
- package/dist/email/webhooks/handler-factory.d.ts.map +1 -1
- package/dist/email/webhooks/handler-factory.js +40 -38
- package/dist/email/webhooks/handler-factory.js.map +1 -1
- package/dist/email/webhooks/notification-parser.d.ts +1 -1
- package/dist/email/webhooks/notification-parser.js +1 -1
- package/dist/email/webhooks/signature-verification.d.ts +4 -2
- package/dist/email/webhooks/signature-verification.d.ts.map +1 -1
- package/dist/email/webhooks/signature-verification.js +63 -8
- package/dist/email/webhooks/signature-verification.js.map +1 -1
- package/dist/email/webhooks/subscription-handler.d.ts +4 -2
- package/dist/email/webhooks/subscription-handler.d.ts.map +1 -1
- package/dist/email/webhooks/subscription-handler.js +42 -6
- package/dist/email/webhooks/subscription-handler.js.map +1 -1
- package/dist/email/webhooks/types.d.ts +17 -2
- package/dist/email/webhooks/types.d.ts.map +1 -1
- package/dist/hono-context.d.ts +1 -1
- package/dist/hono-context.d.ts.map +1 -1
- package/dist/http/error-handler.js.map +1 -1
- package/dist/http/errors.d.ts.map +1 -1
- package/dist/http/errors.js.map +1 -1
- package/dist/http/route-authorization.d.ts +1 -2
- package/dist/http/route-authorization.d.ts.map +1 -1
- package/dist/http/route-authorization.js.map +1 -1
- package/dist/http/route-context.d.ts +4 -2
- package/dist/http/route-context.d.ts.map +1 -1
- package/dist/http/route-context.js +7 -2
- package/dist/http/route-context.js.map +1 -1
- package/dist/http/route-helpers.d.ts +3 -3
- package/dist/http/route-helpers.d.ts.map +1 -1
- package/dist/http/route-helpers.js.map +1 -1
- package/dist/http/route-openapi.d.ts +1 -1
- package/dist/http/route-openapi.d.ts.map +1 -1
- package/dist/http/route-openapi.js.map +1 -1
- package/dist/http/route-types.d.ts +26 -23
- package/dist/http/route-types.d.ts.map +1 -1
- package/dist/jobs/drivers/eventbridge-scheduler-driver.d.ts.map +1 -1
- package/dist/jobs/drivers/eventbridge-scheduler-driver.js.map +1 -1
- package/dist/jobs/drivers/mock-driver.d.ts.map +1 -1
- package/dist/jobs/drivers/mock-driver.js.map +1 -1
- package/dist/jobs/drivers/sqs-driver.d.ts.map +1 -1
- package/dist/jobs/drivers/sqs-driver.js.map +1 -1
- package/dist/jobs/errors.d.ts.map +1 -1
- package/dist/jobs/job-registry.d.ts.map +1 -1
- package/dist/jobs/job-registry.js.map +1 -1
- package/dist/jobs/job-service.d.ts.map +1 -1
- package/dist/jobs/job-service.js.map +1 -1
- package/dist/jobs/observability/client.d.ts +26 -0
- package/dist/jobs/observability/client.d.ts.map +1 -0
- package/dist/jobs/observability/client.js +286 -0
- package/dist/jobs/observability/client.js.map +1 -0
- package/dist/jobs/observability/dashboard.d.ts +19 -0
- package/dist/jobs/observability/dashboard.d.ts.map +1 -0
- package/dist/jobs/observability/dashboard.js +144 -0
- package/dist/jobs/observability/dashboard.js.map +1 -0
- package/dist/jobs/observability/emf.d.ts +24 -0
- package/dist/jobs/observability/emf.d.ts.map +1 -0
- package/dist/jobs/observability/emf.js +100 -0
- package/dist/jobs/observability/emf.js.map +1 -0
- package/dist/jobs/observability/index.d.ts +5 -0
- package/dist/jobs/observability/index.d.ts.map +1 -0
- package/dist/jobs/observability/index.js +4 -0
- package/dist/jobs/observability/index.js.map +1 -0
- package/dist/jobs/observability/types.d.ts +234 -0
- package/dist/jobs/observability/types.d.ts.map +1 -0
- package/dist/jobs/observability/types.js +2 -0
- package/dist/jobs/observability/types.js.map +1 -0
- package/dist/jobs/worker.d.ts +3 -1
- package/dist/jobs/worker.d.ts.map +1 -1
- package/dist/jobs/worker.js +2 -0
- package/dist/jobs/worker.js.map +1 -1
- package/dist/list-query/builder.d.ts +13 -3
- package/dist/list-query/builder.d.ts.map +1 -1
- package/dist/list-query/builder.js +45 -17
- package/dist/list-query/builder.js.map +1 -1
- package/dist/list-query/filter-mapping.d.ts +89 -0
- package/dist/list-query/filter-mapping.d.ts.map +1 -0
- package/dist/list-query/filter-mapping.js +254 -0
- package/dist/list-query/filter-mapping.js.map +1 -0
- package/dist/list-query/filter-value-schemas.d.ts +3 -3
- package/dist/list-query/filter-value-schemas.d.ts.map +1 -1
- package/dist/list-query/filter-value-schemas.js +13 -3
- package/dist/list-query/filter-value-schemas.js.map +1 -1
- package/dist/list-query/index.d.ts +4 -2
- package/dist/list-query/index.d.ts.map +1 -1
- package/dist/list-query/index.js +3 -1
- package/dist/list-query/index.js.map +1 -1
- package/dist/list-query/parse-filter-values.d.ts.map +1 -1
- package/dist/list-query/parse-filter-values.js +2 -2
- package/dist/list-query/parse-filter-values.js.map +1 -1
- package/dist/list-query/parser.d.ts +3 -2
- package/dist/list-query/parser.d.ts.map +1 -1
- package/dist/list-query/parser.js.map +1 -1
- package/dist/list-query/prisma-executor.d.ts +44 -29
- package/dist/list-query/prisma-executor.d.ts.map +1 -1
- package/dist/list-query/prisma-executor.js +48 -33
- package/dist/list-query/prisma-executor.js.map +1 -1
- package/dist/list-query/query-schema.d.ts +8 -13
- package/dist/list-query/query-schema.d.ts.map +1 -1
- package/dist/list-query/query-schema.js +11 -12
- package/dist/list-query/query-schema.js.map +1 -1
- package/dist/list-query/schema-helpers.d.ts +17 -17
- package/dist/list-query/schema-helpers.d.ts.map +1 -1
- package/dist/list-query/schema-helpers.js +11 -11
- package/dist/list-query/schema-helpers.js.map +1 -1
- package/dist/list-query/sort-mapping.d.ts +37 -0
- package/dist/list-query/sort-mapping.d.ts.map +1 -0
- package/dist/list-query/sort-mapping.js +88 -0
- package/dist/list-query/sort-mapping.js.map +1 -0
- package/dist/list-query/types.d.ts +3 -10
- package/dist/list-query/types.d.ts.map +1 -1
- package/dist/list-query/validator.d.ts.map +1 -1
- package/dist/list-query/validator.js +28 -10
- package/dist/list-query/validator.js.map +1 -1
- package/dist/middleware/rate-limit.d.ts.map +1 -1
- package/dist/middleware/rate-limit.js +60 -92
- package/dist/middleware/rate-limit.js.map +1 -1
- package/dist/observability/exception-tracking.d.ts.map +1 -1
- package/dist/observability/exception-tracking.js.map +1 -1
- package/dist/observability/index.d.ts +1 -1
- package/dist/observability/index.d.ts.map +1 -1
- package/dist/observability/index.js +1 -1
- package/dist/observability/index.js.map +1 -1
- package/dist/observability/pino-logger.d.ts +0 -28
- package/dist/observability/pino-logger.d.ts.map +1 -1
- package/dist/observability/pino-logger.js +21 -12
- package/dist/observability/pino-logger.js.map +1 -1
- package/dist/observability/request-context.d.ts +36 -1
- package/dist/observability/request-context.d.ts.map +1 -1
- package/dist/observability/request-context.js +43 -0
- package/dist/observability/request-context.js.map +1 -1
- package/dist/openapi/openapi.d.ts +1 -4
- package/dist/openapi/openapi.d.ts.map +1 -1
- package/dist/openapi/openapi.js +15 -35
- package/dist/openapi/openapi.js.map +1 -1
- package/dist/rls/configuration.d.ts +3 -0
- package/dist/rls/configuration.d.ts.map +1 -0
- package/dist/rls/configuration.js +13 -0
- package/dist/rls/configuration.js.map +1 -0
- package/dist/rls/context.d.ts +37 -0
- package/dist/rls/context.d.ts.map +1 -0
- package/dist/rls/context.js +56 -0
- package/dist/rls/context.js.map +1 -0
- package/dist/rls/extension.d.ts +7 -0
- package/dist/rls/extension.d.ts.map +1 -0
- package/dist/rls/extension.js +91 -0
- package/dist/rls/extension.js.map +1 -0
- package/dist/rls/generator.d.ts +17 -0
- package/dist/rls/generator.d.ts.map +1 -0
- package/dist/rls/generator.js +47 -0
- package/dist/rls/generator.js.map +1 -0
- package/dist/rls/index.d.ts +7 -0
- package/dist/rls/index.d.ts.map +1 -0
- package/dist/rls/index.js +7 -0
- package/dist/rls/index.js.map +1 -0
- package/dist/rls/middleware.d.ts +17 -0
- package/dist/rls/middleware.d.ts.map +1 -0
- package/dist/rls/middleware.js +51 -0
- package/dist/rls/middleware.js.map +1 -0
- package/dist/rls/testing.d.ts +24 -0
- package/dist/rls/testing.d.ts.map +1 -0
- package/dist/rls/testing.js +31 -0
- package/dist/rls/testing.js.map +1 -0
- package/dist/rls/types.d.ts +51 -0
- package/dist/rls/types.d.ts.map +1 -0
- package/dist/rls/types.js +16 -0
- package/dist/rls/types.js.map +1 -0
- package/dist/sse/index.d.ts +6 -0
- package/dist/sse/index.d.ts.map +1 -0
- package/dist/sse/index.js +18 -0
- package/dist/sse/index.js.map +1 -0
- package/dist/sse/sse-broadcaster.d.ts +38 -0
- package/dist/sse/sse-broadcaster.d.ts.map +1 -0
- package/dist/sse/sse-broadcaster.js +118 -0
- package/dist/sse/sse-broadcaster.js.map +1 -0
- package/dist/uploads/uploads.service.d.ts.map +1 -1
- package/dist/uploads/validators.d.ts.map +1 -1
- package/dist/utils/get-client-ip.d.ts.map +1 -1
- package/dist/utils/get-client-ip.js +22 -13
- package/dist/utils/get-client-ip.js.map +1 -1
- package/dist/utils/serialize-prisma-types.js.map +1 -1
- package/package.json +56 -35
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pino-logger.js","sourceRoot":"","sources":["../../src/observability/pino-logger.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAiCzD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,
|
|
1
|
+
{"version":3,"file":"pino-logger.js","sourceRoot":"","sources":["../../src/observability/pino-logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,8 +11,16 @@ export interface RequestContext {
|
|
|
11
11
|
method: string;
|
|
12
12
|
/** Request path */
|
|
13
13
|
path: string;
|
|
14
|
-
/** Optional user ID (set via setUser) */
|
|
14
|
+
/** Optional user ID (set via setUser or setRequestAuth) */
|
|
15
15
|
userId?: string;
|
|
16
|
+
/** Physical actor user ID (e.g. Superadmin during impersonation, otherwise same as userId) */
|
|
17
|
+
actorUserId?: string;
|
|
18
|
+
/** Effective target user ID when impersonating */
|
|
19
|
+
effectiveUserId?: string;
|
|
20
|
+
/** Whether the request is being executed via user impersonation */
|
|
21
|
+
isImpersonating?: boolean;
|
|
22
|
+
/** Organization or tenant ID associated with the authenticated user/request */
|
|
23
|
+
tenantId?: string;
|
|
16
24
|
/** Custom context data */
|
|
17
25
|
custom: Record<string, unknown>;
|
|
18
26
|
}
|
|
@@ -62,6 +70,14 @@ export declare function getRequestContext(): RequestContext | undefined;
|
|
|
62
70
|
* ```
|
|
63
71
|
*/
|
|
64
72
|
export declare function getRequestId(): string | undefined;
|
|
73
|
+
export interface RequestAuthContext {
|
|
74
|
+
/** Effective user ID (whose permissions and identity apply) */
|
|
75
|
+
userId: string;
|
|
76
|
+
/** Organization or tenant ID associated with the user/request */
|
|
77
|
+
tenantId?: string | null | undefined;
|
|
78
|
+
/** Impersonator user ID if this request is performed via impersonation */
|
|
79
|
+
impersonatedBy?: string | null | undefined;
|
|
80
|
+
}
|
|
65
81
|
/**
|
|
66
82
|
* Set user information in the current request context.
|
|
67
83
|
* Useful for correlating logs and errors with users after authentication.
|
|
@@ -78,6 +94,25 @@ export declare function getRequestId(): string | undefined;
|
|
|
78
94
|
* ```
|
|
79
95
|
*/
|
|
80
96
|
export declare function setRequestUser(userId: string): void;
|
|
97
|
+
/**
|
|
98
|
+
* Set full authentication and actor context for the current request.
|
|
99
|
+
* Automatically resolves whether impersonation is active:
|
|
100
|
+
* - If impersonatedBy is provided: actorUserId is the impersonator, effectiveUserId is the target user, isImpersonating is true.
|
|
101
|
+
* - Otherwise: actorUserId is the user, effectiveUserId is undefined, isImpersonating is false.
|
|
102
|
+
*
|
|
103
|
+
* @param auth - Authentication context including user ID, tenant ID, and optional impersonator ID
|
|
104
|
+
*/
|
|
105
|
+
export declare function setRequestAuth(auth: RequestAuthContext): void;
|
|
106
|
+
/**
|
|
107
|
+
* Get the current actor context for auditing and security correlation.
|
|
108
|
+
* Returns undefined if called outside a request lifecycle.
|
|
109
|
+
*/
|
|
110
|
+
export declare function getRequestActor(): {
|
|
111
|
+
actorUserId?: string;
|
|
112
|
+
effectiveUserId?: string;
|
|
113
|
+
isImpersonating: boolean;
|
|
114
|
+
tenantId?: string;
|
|
115
|
+
} | undefined;
|
|
81
116
|
/**
|
|
82
117
|
* Set custom context data for the current request.
|
|
83
118
|
* Useful for adding business-specific context to logs.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../../src/observability/request-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAE9C;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,
|
|
1
|
+
{"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../../src/observability/request-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAE9C;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8FAA8F;IAC9F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mEAAmE;IACnE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0BAA0B;IAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,mCAA0C,CAAC;AAE7E;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,SAAS,CAE9D;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,IAAI,MAAM,GAAG,SAAS,CAEjD;AAED,MAAM,WAAW,kBAAkB;IACjC,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACrC,0EAA0E;IAC1E,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CAC5C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAMnD;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,kBAAkB,GAAG,IAAI,CAiB7D;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAC3B;IACE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GACD,SAAS,CASZ;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAKxE;AAED;;;;;;GAMG;AACH,wBAAgB,8BAA8B,IAAI,iBAAiB,CAelE"}
|
|
@@ -68,8 +68,51 @@ export function setRequestUser(userId) {
|
|
|
68
68
|
const store = requestContextStorage.getStore();
|
|
69
69
|
if (store) {
|
|
70
70
|
store.userId = userId;
|
|
71
|
+
store.actorUserId = store.actorUserId ?? userId;
|
|
71
72
|
}
|
|
72
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Set full authentication and actor context for the current request.
|
|
76
|
+
* Automatically resolves whether impersonation is active:
|
|
77
|
+
* - If impersonatedBy is provided: actorUserId is the impersonator, effectiveUserId is the target user, isImpersonating is true.
|
|
78
|
+
* - Otherwise: actorUserId is the user, effectiveUserId is undefined, isImpersonating is false.
|
|
79
|
+
*
|
|
80
|
+
* @param auth - Authentication context including user ID, tenant ID, and optional impersonator ID
|
|
81
|
+
*/
|
|
82
|
+
export function setRequestAuth(auth) {
|
|
83
|
+
const store = requestContextStorage.getStore();
|
|
84
|
+
if (store) {
|
|
85
|
+
store.userId = auth.userId;
|
|
86
|
+
if (typeof auth.tenantId === 'string') {
|
|
87
|
+
store.tenantId = auth.tenantId;
|
|
88
|
+
}
|
|
89
|
+
if (auth.impersonatedBy) {
|
|
90
|
+
store.actorUserId = auth.impersonatedBy;
|
|
91
|
+
store.effectiveUserId = auth.userId;
|
|
92
|
+
store.isImpersonating = true;
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
store.actorUserId = auth.userId;
|
|
96
|
+
store.effectiveUserId = undefined;
|
|
97
|
+
store.isImpersonating = false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Get the current actor context for auditing and security correlation.
|
|
103
|
+
* Returns undefined if called outside a request lifecycle.
|
|
104
|
+
*/
|
|
105
|
+
export function getRequestActor() {
|
|
106
|
+
const store = requestContextStorage.getStore();
|
|
107
|
+
if (!store)
|
|
108
|
+
return undefined;
|
|
109
|
+
return {
|
|
110
|
+
actorUserId: store.actorUserId ?? store.userId,
|
|
111
|
+
effectiveUserId: store.effectiveUserId,
|
|
112
|
+
isImpersonating: Boolean(store.isImpersonating),
|
|
113
|
+
tenantId: store.tenantId,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
73
116
|
/**
|
|
74
117
|
* Set custom context data for the current request.
|
|
75
118
|
* Useful for adding business-specific context to logs.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-context.js","sourceRoot":"","sources":["../../src/observability/request-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"request-context.js","sourceRoot":"","sources":["../../src/observability/request-context.ts"],"names":[],"mappings":"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"}
|
|
@@ -44,10 +44,7 @@ export interface ConfigureOpenAPIResult {
|
|
|
44
44
|
*
|
|
45
45
|
* @example
|
|
46
46
|
* ```typescript
|
|
47
|
-
* //
|
|
48
|
-
* configureOpenAPI({ app, environment, openapi });
|
|
49
|
-
*
|
|
50
|
-
* // Explicit write after routes are registered (recommended)
|
|
47
|
+
* // Configure first, then explicitly write after routes are registered.
|
|
51
48
|
* const { writeSpec } = configureOpenAPI({ app, environment, openapi });
|
|
52
49
|
* // ... register all routes ...
|
|
53
50
|
* await writeSpec?.();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../../src/openapi/openapi.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../../src/openapi/openapi.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAqB,MAAM,MAAM,CAAC;AAGpD,OAAO,KAAK,EACV,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EACjB,MAAM,kBAAkB,CAAC;AAK1B,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,IAAI,CAAC;IACV,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,cAAc,CAAC;IACxB,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,UAAU,CAAC,EAAE,iBAAiB,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;;;;;;;;;;;;OAcG;IACH,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,sBAAsB,CA8DzF"}
|
package/dist/openapi/openapi.js
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import { writeFile } from 'node:fs/promises';
|
|
2
|
-
import {
|
|
3
|
-
import { openAPIRouteHandler } from 'hono-openapi';
|
|
2
|
+
import { generateSpecs, openAPIRouteHandler } from 'hono-openapi';
|
|
4
3
|
import { deepMerge } from '../utils/deep-merge.js';
|
|
5
4
|
import { createLogger } from '../utils/logger.js';
|
|
6
5
|
import { defaultOpenAPIComponents } from './defaults.js';
|
|
7
|
-
/** Default delay before writing OpenAPI spec (ms). Allows routes to register. */
|
|
8
|
-
const DEFAULT_WRITE_DELAY_MS = 1000;
|
|
9
6
|
/**
|
|
10
7
|
* Configure OpenAPI documentation for the application.
|
|
11
8
|
*
|
|
@@ -22,10 +19,7 @@ const DEFAULT_WRITE_DELAY_MS = 1000;
|
|
|
22
19
|
*
|
|
23
20
|
* @example
|
|
24
21
|
* ```typescript
|
|
25
|
-
* //
|
|
26
|
-
* configureOpenAPI({ app, environment, openapi });
|
|
27
|
-
*
|
|
28
|
-
* // Explicit write after routes are registered (recommended)
|
|
22
|
+
* // Configure first, then explicitly write after routes are registered.
|
|
29
23
|
* const { writeSpec } = configureOpenAPI({ app, environment, openapi });
|
|
30
24
|
* // ... register all routes ...
|
|
31
25
|
* await writeSpec?.();
|
|
@@ -54,40 +48,26 @@ export function configureOpenAPI(options) {
|
|
|
54
48
|
}
|
|
55
49
|
// Serve Swagger UI (development only by default)
|
|
56
50
|
if (shouldServeUI) {
|
|
57
|
-
|
|
51
|
+
let handlerPromise;
|
|
52
|
+
app.get('/api', async (c, next) => {
|
|
53
|
+
handlerPromise ??= import('@hono/swagger-ui')
|
|
54
|
+
.then(({ swaggerUI }) => swaggerUI({ url: '/api/openapi.json' }))
|
|
55
|
+
.catch((cause) => {
|
|
56
|
+
throw new Error('Unable to load Swagger UI. Install the optional peer with: npm install @hono/swagger-ui', { cause });
|
|
57
|
+
});
|
|
58
|
+
const handler = await handlerPromise;
|
|
59
|
+
return handler(c, next);
|
|
60
|
+
});
|
|
58
61
|
}
|
|
59
62
|
// Create writeSpec function if file writing is configured
|
|
60
63
|
let writeSpec;
|
|
61
64
|
if (openapi.writeToFile) {
|
|
62
65
|
const filePath = openapi.writeToFile;
|
|
63
66
|
writeSpec = async () => {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
// Use Hono's built-in request method to simulate the request
|
|
68
|
-
const response = await app.request('/api/openapi.json');
|
|
69
|
-
specContent = await response.text();
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
// If not serving specs, generate manually
|
|
73
|
-
// Mount a temporary internal endpoint
|
|
74
|
-
app.get('/api/openapi-internal.json', openAPIRouteHandler(app, { documentation: mergedDocumentation }));
|
|
75
|
-
const response = await app.request('/api/openapi-internal.json');
|
|
76
|
-
const spec = (await response.json());
|
|
77
|
-
specContent = JSON.stringify(spec, null, 2);
|
|
78
|
-
}
|
|
79
|
-
await writeFile(filePath, specContent, 'utf-8');
|
|
80
|
-
logger.info(`OpenAPI spec written to ${filePath}`);
|
|
81
|
-
}
|
|
82
|
-
catch (error) {
|
|
83
|
-
logger.error('Failed to write OpenAPI spec:', error);
|
|
84
|
-
}
|
|
67
|
+
const spec = await generateSpecs(app, { documentation: mergedDocumentation });
|
|
68
|
+
await writeFile(filePath, JSON.stringify(spec, null, 2), 'utf-8');
|
|
69
|
+
logger.info(`OpenAPI spec written to ${filePath}`);
|
|
85
70
|
};
|
|
86
|
-
// Auto-write with delay for backward compatibility
|
|
87
|
-
// NOTE: For more reliable writes, call writeSpec() explicitly after registering routes
|
|
88
|
-
setTimeout(() => {
|
|
89
|
-
writeSpec?.();
|
|
90
|
-
}, DEFAULT_WRITE_DELAY_MS);
|
|
91
71
|
}
|
|
92
72
|
return { writeSpec };
|
|
93
73
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openapi.js","sourceRoot":"","sources":["../../src/openapi/openapi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"openapi.js","sourceRoot":"","sources":["../../src/openapi/openapi.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configuration.d.ts","sourceRoot":"","sources":["../../src/rls/configuration.ts"],"names":[],"mappings":"AAIA,iEAAiE;AACjE,wBAAgB,wBAAwB,CAAC,eAAe,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAU/F"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { RLSConfigurationException } from './types.js';
|
|
2
|
+
const parameterName = /^[a-z_][a-z0-9_]*(?:\.[a-z_][a-z0-9_]*)+$/i;
|
|
3
|
+
/** PostgreSQL settings use qualified, case-insensitive names. */
|
|
4
|
+
export function validateSessionVariables(sessionVariable, bypassVariable) {
|
|
5
|
+
if (!parameterName.test(sessionVariable) ||
|
|
6
|
+
sessionVariable.toLowerCase() === bypassVariable?.toLowerCase()) {
|
|
7
|
+
throw new RLSConfigurationException('Use distinct, qualified session variables.');
|
|
8
|
+
}
|
|
9
|
+
if (bypassVariable !== undefined && !parameterName.test(bypassVariable)) {
|
|
10
|
+
throw new RLSConfigurationException('The bypass variable must be qualified.');
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=configuration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configuration.js","sourceRoot":"","sources":["../../src/rls/configuration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAEvD,MAAM,aAAa,GAAG,4CAA4C,CAAC;AAEnE,iEAAiE;AACjE,MAAM,UAAU,wBAAwB,CAAC,eAAuB,EAAE,cAAuB;IACvF,IACE,CAAC,aAAa,CAAC,IAAI,CAAC,eAAe,CAAC;QACpC,eAAe,CAAC,WAAW,EAAE,KAAK,cAAc,EAAE,WAAW,EAAE,EAC/D,CAAC;QACD,MAAM,IAAI,yBAAyB,CAAC,4CAA4C,CAAC,CAAC;IACpF,CAAC;IACD,IAAI,cAAc,KAAK,SAAS,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,yBAAyB,CAAC,wCAAwC,CAAC,CAAC;IAChF,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type TenantContext } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Run an asynchronous function within a specific tenant context.
|
|
4
|
+
*
|
|
5
|
+
* @param context - The TenantContext to bind.
|
|
6
|
+
* @param fn - The asynchronous callback function.
|
|
7
|
+
* @returns The resolved return value of `fn`.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const result = await runWithTenant({ tenantId: 'tenant_123' }, async () => {
|
|
12
|
+
* return await prisma.poll.findMany();
|
|
13
|
+
* });
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export declare function runWithTenant<T>(context: TenantContext | undefined, fn: () => T | PromiseLike<T>): Promise<T>;
|
|
17
|
+
/**
|
|
18
|
+
* Run trusted system code with an explicit bypass.
|
|
19
|
+
*
|
|
20
|
+
* Only tables with a matching bypass policy allow access across tenants.
|
|
21
|
+
*/
|
|
22
|
+
export declare function runWithBypass<T>(fn: () => T | PromiseLike<T>): Promise<T>;
|
|
23
|
+
/**
|
|
24
|
+
* Get the current active TenantContext, or undefined if outside a tenant execution boundary.
|
|
25
|
+
*/
|
|
26
|
+
export declare function getTenantContext(): Readonly<TenantContext> | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Get the current active tenant ID, or undefined if outside a tenant boundary.
|
|
29
|
+
*/
|
|
30
|
+
export declare function getTenantId(): string | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Get the current active tenant ID, throwing `MissingTenantContextException` if not present.
|
|
33
|
+
*
|
|
34
|
+
* @param modelName - Optional model name for descriptive error reporting.
|
|
35
|
+
*/
|
|
36
|
+
export declare function requireTenantId(modelName?: string): string;
|
|
37
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/rls/context.ts"],"names":[],"mappings":"AACA,OAAO,EAAiC,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAO/E;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAC7B,OAAO,EAAE,aAAa,GAAG,SAAS,EAClC,EAAE,EAAE,MAAM,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,GAC3B,OAAO,CAAC,CAAC,CAAC,CAEZ;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAEzE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,QAAQ,CAAC,aAAa,CAAC,GAAG,SAAS,CAEtE;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,MAAM,GAAG,SAAS,CAEhD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAM1D"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import { MissingTenantContextException } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* AsyncLocalStorage instance holding the active TenantContext for the current execution chain.
|
|
5
|
+
*/
|
|
6
|
+
const tenantContextStorage = new AsyncLocalStorage();
|
|
7
|
+
/**
|
|
8
|
+
* Run an asynchronous function within a specific tenant context.
|
|
9
|
+
*
|
|
10
|
+
* @param context - The TenantContext to bind.
|
|
11
|
+
* @param fn - The asynchronous callback function.
|
|
12
|
+
* @returns The resolved return value of `fn`.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* const result = await runWithTenant({ tenantId: 'tenant_123' }, async () => {
|
|
17
|
+
* return await prisma.poll.findMany();
|
|
18
|
+
* });
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export function runWithTenant(context, fn) {
|
|
22
|
+
return tenantContextStorage.run(context && Object.freeze({ ...context }), async () => await fn());
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Run trusted system code with an explicit bypass.
|
|
26
|
+
*
|
|
27
|
+
* Only tables with a matching bypass policy allow access across tenants.
|
|
28
|
+
*/
|
|
29
|
+
export function runWithBypass(fn) {
|
|
30
|
+
return runWithTenant({ bypass: true }, fn);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Get the current active TenantContext, or undefined if outside a tenant execution boundary.
|
|
34
|
+
*/
|
|
35
|
+
export function getTenantContext() {
|
|
36
|
+
return tenantContextStorage.getStore();
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Get the current active tenant ID, or undefined if outside a tenant boundary.
|
|
40
|
+
*/
|
|
41
|
+
export function getTenantId() {
|
|
42
|
+
return tenantContextStorage.getStore()?.tenantId;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Get the current active tenant ID, throwing `MissingTenantContextException` if not present.
|
|
46
|
+
*
|
|
47
|
+
* @param modelName - Optional model name for descriptive error reporting.
|
|
48
|
+
*/
|
|
49
|
+
export function requireTenantId(modelName) {
|
|
50
|
+
const context = getTenantContext();
|
|
51
|
+
if (!context?.tenantId) {
|
|
52
|
+
throw new MissingTenantContextException(modelName);
|
|
53
|
+
}
|
|
54
|
+
return context.tenantId;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/rls/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,6BAA6B,EAAsB,MAAM,YAAY,CAAC;AAE/E;;GAEG;AACH,MAAM,oBAAoB,GAAG,IAAI,iBAAiB,EAAuC,CAAC;AAE1F;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,aAAa,CAC3B,OAAkC,EAClC,EAA4B;IAE5B,OAAO,oBAAoB,CAAC,GAAG,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC,EAAE,KAAK,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACpG,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAI,EAA4B;IAC3D,OAAO,aAAa,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,oBAAoB,CAAC,QAAQ,EAAE,CAAC;AACzC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW;IACzB,OAAO,oBAAoB,CAAC,QAAQ,EAAE,EAAE,QAAQ,CAAC;AACnD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,SAAkB;IAChD,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;IACnC,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC;QACvB,MAAM,IAAI,6BAA6B,CAAC,SAAS,CAAC,CAAC;IACrD,CAAC;IACD,OAAO,OAAO,CAAC,QAAQ,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type RLSExtensionOptions, type RLSTransactionClient, type RLSTransactionOptions, type TenantContext } from './types.js';
|
|
2
|
+
/** Prisma extension for transaction-scoped PostgreSQL RLS. Apply this extension last. */
|
|
3
|
+
export declare function createRLSExtension(options?: RLSExtensionOptions): (client: any) => import("@prisma/client/extension").PrismaClientExtends<import("@prisma/client/runtime/client").InternalArgs<{}, {}, {}, {
|
|
4
|
+
$transaction<TClient, TResult>(this: TClient, fn: (tx: RLSTransactionClient<TClient>) => Promise<TResult>, transactionOptions?: RLSTransactionOptions): Promise<TResult>;
|
|
5
|
+
$withTenant<TClient, TResult>(this: TClient, context: TenantContext, fn: (tx: RLSTransactionClient<TClient>) => Promise<TResult>, transactionOptions?: RLSTransactionOptions): Promise<TResult>;
|
|
6
|
+
}> & import("@prisma/client/runtime/client").DefaultArgs>;
|
|
7
|
+
//# sourceMappingURL=extension.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.d.ts","sourceRoot":"","sources":["../../src/rls/extension.ts"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EACnB,MAAM,YAAY,CAAC;AAQpB,yFAAyF;AACzF,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,mBAAwB;iBA6E/C,OAAO,EAAE,OAAO,QACrB,OAAO,MACT,CAAC,EAAE,EAAE,oBAAoB,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,uBACtC,qBAAqB;gBAIhC,OAAO,EAAE,OAAO,QACpB,OAAO,WACJ,aAAa,MAClB,CAAC,EAAE,EAAE,oBAAoB,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,uBACtC,qBAAqB;0DAqBnD"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Prisma } from '@prisma/client/extension';
|
|
2
|
+
import { validateSessionVariables } from './configuration.js';
|
|
3
|
+
import { getTenantContext, runWithTenant } from './context.js';
|
|
4
|
+
import { MissingTenantContextException, RLSConfigurationException, } from './types.js';
|
|
5
|
+
/** Prisma extension for transaction-scoped PostgreSQL RLS. Apply this extension last. */
|
|
6
|
+
export function createRLSExtension(options = {}) {
|
|
7
|
+
const { sessionVariable = 'app.current_tenant_id', bypassVariable, models, missingContextBehavior = 'throw', } = options;
|
|
8
|
+
validateSessionVariables(sessionVariable, bypassVariable);
|
|
9
|
+
function resolveContext(model) {
|
|
10
|
+
const context = getTenantContext();
|
|
11
|
+
if (context?.bypass && !bypassVariable) {
|
|
12
|
+
throw new RLSConfigurationException('This RLS extension does not allow bypass.');
|
|
13
|
+
}
|
|
14
|
+
if (context?.tenantId || context?.bypass)
|
|
15
|
+
return context;
|
|
16
|
+
if (missingContextBehavior === 'throw' &&
|
|
17
|
+
(!models || (model !== undefined && models.includes(model)))) {
|
|
18
|
+
throw new MissingTenantContextException(model);
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
return Prisma.defineExtension((base) => {
|
|
23
|
+
function sessionConfig(context) {
|
|
24
|
+
const tenantId = context.tenantId ?? '';
|
|
25
|
+
// Set or clear both settings each time; values remain parameterized.
|
|
26
|
+
return bypassVariable
|
|
27
|
+
? [
|
|
28
|
+
'SELECT set_config($1, $2, true), set_config($3, $4, true)',
|
|
29
|
+
sessionVariable,
|
|
30
|
+
tenantId,
|
|
31
|
+
bypassVariable,
|
|
32
|
+
context.bypass ? 'true' : '',
|
|
33
|
+
]
|
|
34
|
+
: ['SELECT set_config($1, $2, true)', sessionVariable, tenantId];
|
|
35
|
+
}
|
|
36
|
+
async function transact(fn, transactionOptions) {
|
|
37
|
+
if (typeof fn !== 'function') {
|
|
38
|
+
throw new RLSConfigurationException('Use the callback form of $transaction for RLS.');
|
|
39
|
+
}
|
|
40
|
+
const context = resolveContext();
|
|
41
|
+
if (!context)
|
|
42
|
+
throw new MissingTenantContextException();
|
|
43
|
+
// A separate extension keeps Prisma's query callback on the transaction client.
|
|
44
|
+
// No private Prisma transaction fields or global client redirection are needed.
|
|
45
|
+
const transactional = base.$extends({
|
|
46
|
+
name: 'glasswork-rls-transaction',
|
|
47
|
+
query: {
|
|
48
|
+
async $allOperations({ model, args, query }) {
|
|
49
|
+
const active = resolveContext(model);
|
|
50
|
+
if (active?.tenantId !== context.tenantId || active?.bypass !== context.bypass) {
|
|
51
|
+
throw new RLSConfigurationException('The RLS context cannot change within a transaction.');
|
|
52
|
+
}
|
|
53
|
+
return query(args);
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
return transactional.$transaction(async (tx) => {
|
|
58
|
+
await tx.$executeRawUnsafe(...sessionConfig(context));
|
|
59
|
+
// Prisma preserves the model delegates and previously applied extensions.
|
|
60
|
+
return fn(tx);
|
|
61
|
+
}, transactionOptions);
|
|
62
|
+
}
|
|
63
|
+
return base.$extends({
|
|
64
|
+
name: 'glasswork-rls',
|
|
65
|
+
client: {
|
|
66
|
+
$transaction(fn, transactionOptions) {
|
|
67
|
+
return transact(fn, transactionOptions);
|
|
68
|
+
},
|
|
69
|
+
$withTenant(context, fn, transactionOptions) {
|
|
70
|
+
return runWithTenant(context, () => transact(fn, transactionOptions));
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
query: {
|
|
74
|
+
async $allOperations({ model, args, query }) {
|
|
75
|
+
const context = resolveContext(model);
|
|
76
|
+
if (!context)
|
|
77
|
+
return query(args);
|
|
78
|
+
// A batch binds the original PrismaPromise to the same connection.
|
|
79
|
+
// Global models can also load tenant relations: scope every query.
|
|
80
|
+
const operation = query(args);
|
|
81
|
+
const [, result] = await base.$transaction([
|
|
82
|
+
base.$executeRawUnsafe(...sessionConfig(context)),
|
|
83
|
+
operation,
|
|
84
|
+
]);
|
|
85
|
+
return result;
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=extension.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.js","sourceRoot":"","sources":["../../src/rls/extension.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { GenerateRLSPoliciesOptions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Generate PostgreSQL Row Level Security DDL statements for a list of tables.
|
|
4
|
+
*
|
|
5
|
+
* @param options - Configuration options for policy generation.
|
|
6
|
+
* @returns Formatted SQL migration string containing DDL statements.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const sql = generateRLSPolicies({
|
|
11
|
+
* models: ['Poll', 'MemberProfile'],
|
|
12
|
+
* tenantField: 'tenantId',
|
|
13
|
+
* });
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export declare function generateRLSPolicies(options: GenerateRLSPoliciesOptions): string;
|
|
17
|
+
//# sourceMappingURL=generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/rls/generator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAY7D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,MAAM,CA0C/E"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { validateSessionVariables } from './configuration.js';
|
|
2
|
+
/**
|
|
3
|
+
* Escape a PostgreSQL identifier (table name or column name) by wrapping in double quotes.
|
|
4
|
+
*/
|
|
5
|
+
function quoteIdentifier(identifier) {
|
|
6
|
+
if (!identifier.trim()) {
|
|
7
|
+
throw new Error('Identifier cannot be empty');
|
|
8
|
+
}
|
|
9
|
+
return `"${identifier.replaceAll('"', '""')}"`;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Generate PostgreSQL Row Level Security DDL statements for a list of tables.
|
|
13
|
+
*
|
|
14
|
+
* @param options - Configuration options for policy generation.
|
|
15
|
+
* @returns Formatted SQL migration string containing DDL statements.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const sql = generateRLSPolicies({
|
|
20
|
+
* models: ['Poll', 'MemberProfile'],
|
|
21
|
+
* tenantField: 'tenantId',
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export function generateRLSPolicies(options) {
|
|
26
|
+
const { models, tenantField = 'tenantId', sessionVariable = 'app.current_tenant_id', bypassVariable, forceRLS = true, } = options;
|
|
27
|
+
if (!models || models.length === 0) {
|
|
28
|
+
return '';
|
|
29
|
+
}
|
|
30
|
+
const quotedTenantField = quoteIdentifier(tenantField);
|
|
31
|
+
validateSessionVariables(sessionVariable, bypassVariable);
|
|
32
|
+
const policyStatements = models.map((model) => {
|
|
33
|
+
const quotedTable = quoteIdentifier(model);
|
|
34
|
+
const policyName = quoteIdentifier(`tenant_isolation_${model}`);
|
|
35
|
+
const condition = bypassVariable
|
|
36
|
+
? `current_setting('${bypassVariable}', true) = 'true' OR ${quotedTenantField} = NULLIF(current_setting('${sessionVariable}', true), '')`
|
|
37
|
+
: `${quotedTenantField} = NULLIF(current_setting('${sessionVariable}', true), '')`;
|
|
38
|
+
const lines = [`ALTER TABLE ${quotedTable} ENABLE ROW LEVEL SECURITY;`];
|
|
39
|
+
if (forceRLS) {
|
|
40
|
+
lines.push(`ALTER TABLE ${quotedTable} FORCE ROW LEVEL SECURITY;`);
|
|
41
|
+
}
|
|
42
|
+
lines.push(`DROP POLICY IF EXISTS ${policyName} ON ${quotedTable};`, `CREATE POLICY ${policyName} ON ${quotedTable}`, ' FOR ALL', ` USING (${condition})`, ` WITH CHECK (${condition});`);
|
|
43
|
+
return lines.join('\n');
|
|
44
|
+
});
|
|
45
|
+
return policyStatements.join('\n\n');
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generator.js","sourceRoot":"","sources":["../../src/rls/generator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAG9D;;GAEG;AACH,SAAS,eAAe,CAAC,UAAkB;IACzC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,IAAI,UAAU,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC;AACjD,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAmC;IACrE,MAAM,EACJ,MAAM,EACN,WAAW,GAAG,UAAU,EACxB,eAAe,GAAG,uBAAuB,EACzC,cAAc,EACd,QAAQ,GAAG,IAAI,GAChB,GAAG,OAAO,CAAC;IAEZ,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,iBAAiB,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC;IACvD,wBAAwB,CAAC,eAAe,EAAE,cAAc,CAAC,CAAC;IAE1D,MAAM,gBAAgB,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QAC5C,MAAM,WAAW,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;QAC3C,MAAM,UAAU,GAAG,eAAe,CAAC,oBAAoB,KAAK,EAAE,CAAC,CAAC;QAEhE,MAAM,SAAS,GAAG,cAAc;YAC9B,CAAC,CAAC,oBAAoB,cAAc,wBAAwB,iBAAiB,8BAA8B,eAAe,eAAe;YACzI,CAAC,CAAC,GAAG,iBAAiB,8BAA8B,eAAe,eAAe,CAAC;QAErF,MAAM,KAAK,GAAa,CAAC,eAAe,WAAW,6BAA6B,CAAC,CAAC;QAElF,IAAI,QAAQ,EAAE,CAAC;YACb,KAAK,CAAC,IAAI,CAAC,eAAe,WAAW,4BAA4B,CAAC,CAAC;QACrE,CAAC;QAED,KAAK,CAAC,IAAI,CACR,yBAAyB,UAAU,OAAO,WAAW,GAAG,EACxD,iBAAiB,UAAU,OAAO,WAAW,EAAE,EAC/C,WAAW,EACX,YAAY,SAAS,GAAG,EACxB,iBAAiB,SAAS,IAAI,CAC/B,CAAC;QAEF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;IAEH,OAAO,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { getTenantContext, getTenantId, requireTenantId, runWithBypass, runWithTenant, } from './context.js';
|
|
2
|
+
export { createRLSExtension } from './extension.js';
|
|
3
|
+
export { generateRLSPolicies } from './generator.js';
|
|
4
|
+
export { createRLSMiddleware } from './middleware.js';
|
|
5
|
+
export { withBypass, withTenant } from './testing.js';
|
|
6
|
+
export { type GenerateRLSPoliciesOptions, MissingTenantContextException, RLSConfigurationException, type RLSExtensionOptions, type RLSMiddlewareOptions, type RLSTransactionClient, type RLSTransactionOptions, type TenantContext, type TenantContextExtractor, } from './types.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rls/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,eAAe,EACf,aAAa,EACb,aAAa,GACd,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EACL,KAAK,0BAA0B,EAC/B,6BAA6B,EAC7B,yBAAyB,EACzB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAClB,KAAK,sBAAsB,GAC5B,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { getTenantContext, getTenantId, requireTenantId, runWithBypass, runWithTenant, } from './context.js';
|
|
2
|
+
export { createRLSExtension } from './extension.js';
|
|
3
|
+
export { generateRLSPolicies } from './generator.js';
|
|
4
|
+
export { createRLSMiddleware } from './middleware.js';
|
|
5
|
+
export { withBypass, withTenant } from './testing.js';
|
|
6
|
+
export { MissingTenantContextException, RLSConfigurationException, } from './types.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rls/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,eAAe,EACf,aAAa,EACb,aAAa,GACd,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAEL,6BAA6B,EAC7B,yBAAyB,GAO1B,MAAM,YAAY,CAAC"}
|