@objectstack/plugin-security 5.1.0 → 5.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/dist/index.d.mts +83 -1
- package/dist/index.d.ts +83 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
package/dist/index.d.mts
CHANGED
|
@@ -179,6 +179,14 @@ interface RLSUserContext {
|
|
|
179
179
|
*/
|
|
180
180
|
organization_id?: string;
|
|
181
181
|
roles?: string[];
|
|
182
|
+
/**
|
|
183
|
+
* IDs of all users that share the active organization with the
|
|
184
|
+
* current user (incl. self). Pre-resolved by the runtime so RLS can
|
|
185
|
+
* scope identity tables like `sys_user` via
|
|
186
|
+
* `id IN (current_user.org_user_ids)` without needing subquery
|
|
187
|
+
* support in the compiler.
|
|
188
|
+
*/
|
|
189
|
+
org_user_ids?: string[];
|
|
182
190
|
[key: string]: unknown;
|
|
183
191
|
}
|
|
184
192
|
/**
|
|
@@ -8552,4 +8560,78 @@ declare const securityPluginManifestHeader: {
|
|
|
8552
8560
|
description: string;
|
|
8553
8561
|
};
|
|
8554
8562
|
|
|
8555
|
-
|
|
8563
|
+
/**
|
|
8564
|
+
* ensureUserHasOrganization — auto-create a personal org for new users.
|
|
8565
|
+
*
|
|
8566
|
+
* In multi-tenant mode, every record visible through the default
|
|
8567
|
+
* `tenant_isolation` RLS policy must have an `organization_id`, and
|
|
8568
|
+
* every authenticated user must have an `activeOrganizationId` on their
|
|
8569
|
+
* session for that policy to evaluate to anything other than "deny
|
|
8570
|
+
* all". A user with zero `sys_member` rows, however, can sign in
|
|
8571
|
+
* successfully and reach the dashboard — the dashboard's
|
|
8572
|
+
* `RequireOrganization` guard has a single-tenant carve-out that lets
|
|
8573
|
+
* users with empty organization lists through, so they land on a UI
|
|
8574
|
+
* that simply hides every record. The standard remedy ("invite users
|
|
8575
|
+
* via an admin") doesn't apply to self-service signup.
|
|
8576
|
+
*
|
|
8577
|
+
* This helper, run right after a `sys_user` insert, ensures the new
|
|
8578
|
+
* user has at least one organization by creating a personal workspace
|
|
8579
|
+
* (named "<User>'s Workspace", slug `<username>-workspace`) and an
|
|
8580
|
+
* owner-role `sys_member` row. The user's session will pick this up as
|
|
8581
|
+
* their `activeOrganizationId` on the next sign-in / org-list refresh
|
|
8582
|
+
* (better-auth's `setActiveOrganization` runs lazily when the picker
|
|
8583
|
+
* sees exactly one membership).
|
|
8584
|
+
*
|
|
8585
|
+
* Idempotent: bails out if the user already has any `sys_member` row.
|
|
8586
|
+
* Slug collisions retry with a numeric suffix; a cap of 5 attempts
|
|
8587
|
+
* means a pathological username will fail loudly rather than loop.
|
|
8588
|
+
*/
|
|
8589
|
+
interface EnsureOptions {
|
|
8590
|
+
logger?: {
|
|
8591
|
+
info: (message: string, meta?: Record<string, any>) => void;
|
|
8592
|
+
warn: (message: string, meta?: Record<string, any>) => void;
|
|
8593
|
+
};
|
|
8594
|
+
/**
|
|
8595
|
+
* Optional hook called after a personal org is successfully created.
|
|
8596
|
+
* Used by SecurityPlugin to wire in `cloneTenantSeedData` so each
|
|
8597
|
+
* new workspace gets its own copy of demo data. Pulled in via DI
|
|
8598
|
+
* to keep this helper free of a hard import on the cloner (which
|
|
8599
|
+
* keeps the tenant-claim and ensure-org test surfaces narrow).
|
|
8600
|
+
*/
|
|
8601
|
+
cloneSeedData?: (ql: any, targetOrgId: string, opts: {
|
|
8602
|
+
logger?: EnsureOptions['logger'];
|
|
8603
|
+
}) => Promise<{
|
|
8604
|
+
object: string;
|
|
8605
|
+
count: number;
|
|
8606
|
+
}[]>;
|
|
8607
|
+
}
|
|
8608
|
+
/**
|
|
8609
|
+
* Ensure `user` has at least one `sys_member` row. Creates a personal
|
|
8610
|
+
* organization owned by them if not.
|
|
8611
|
+
*
|
|
8612
|
+
* Returns `{ created: true, organizationId }` when a new org was made,
|
|
8613
|
+
* or `{ created: false, reason }` when the user already has memberships
|
|
8614
|
+
* or the operation was skipped.
|
|
8615
|
+
*/
|
|
8616
|
+
declare function ensureUserHasOrganization(ql: any, user: {
|
|
8617
|
+
id: string;
|
|
8618
|
+
name?: string;
|
|
8619
|
+
email?: string;
|
|
8620
|
+
}, options?: EnsureOptions): Promise<{
|
|
8621
|
+
created: boolean;
|
|
8622
|
+
organizationId?: string;
|
|
8623
|
+
reason?: string;
|
|
8624
|
+
}>;
|
|
8625
|
+
|
|
8626
|
+
interface CloneOptions {
|
|
8627
|
+
logger?: {
|
|
8628
|
+
info: (message: string, meta?: Record<string, any>) => void;
|
|
8629
|
+
warn: (message: string, meta?: Record<string, any>) => void;
|
|
8630
|
+
};
|
|
8631
|
+
}
|
|
8632
|
+
declare function cloneTenantSeedData(ql: any, targetOrgId: string, options?: CloneOptions): Promise<{
|
|
8633
|
+
object: string;
|
|
8634
|
+
count: number;
|
|
8635
|
+
}[]>;
|
|
8636
|
+
|
|
8637
|
+
export { FieldMasker, PermissionDeniedError, PermissionEvaluator, RLSCompiler, RLS_DENY_FILTER, SECURITY_PLUGIN_ID, SECURITY_PLUGIN_VERSION, SecurityPlugin, cloneTenantSeedData, ensureUserHasOrganization, isPermissionDeniedError, securityDefaultPermissionSets, securityObjects, securityPluginManifestHeader };
|
package/dist/index.d.ts
CHANGED
|
@@ -179,6 +179,14 @@ interface RLSUserContext {
|
|
|
179
179
|
*/
|
|
180
180
|
organization_id?: string;
|
|
181
181
|
roles?: string[];
|
|
182
|
+
/**
|
|
183
|
+
* IDs of all users that share the active organization with the
|
|
184
|
+
* current user (incl. self). Pre-resolved by the runtime so RLS can
|
|
185
|
+
* scope identity tables like `sys_user` via
|
|
186
|
+
* `id IN (current_user.org_user_ids)` without needing subquery
|
|
187
|
+
* support in the compiler.
|
|
188
|
+
*/
|
|
189
|
+
org_user_ids?: string[];
|
|
182
190
|
[key: string]: unknown;
|
|
183
191
|
}
|
|
184
192
|
/**
|
|
@@ -8552,4 +8560,78 @@ declare const securityPluginManifestHeader: {
|
|
|
8552
8560
|
description: string;
|
|
8553
8561
|
};
|
|
8554
8562
|
|
|
8555
|
-
|
|
8563
|
+
/**
|
|
8564
|
+
* ensureUserHasOrganization — auto-create a personal org for new users.
|
|
8565
|
+
*
|
|
8566
|
+
* In multi-tenant mode, every record visible through the default
|
|
8567
|
+
* `tenant_isolation` RLS policy must have an `organization_id`, and
|
|
8568
|
+
* every authenticated user must have an `activeOrganizationId` on their
|
|
8569
|
+
* session for that policy to evaluate to anything other than "deny
|
|
8570
|
+
* all". A user with zero `sys_member` rows, however, can sign in
|
|
8571
|
+
* successfully and reach the dashboard — the dashboard's
|
|
8572
|
+
* `RequireOrganization` guard has a single-tenant carve-out that lets
|
|
8573
|
+
* users with empty organization lists through, so they land on a UI
|
|
8574
|
+
* that simply hides every record. The standard remedy ("invite users
|
|
8575
|
+
* via an admin") doesn't apply to self-service signup.
|
|
8576
|
+
*
|
|
8577
|
+
* This helper, run right after a `sys_user` insert, ensures the new
|
|
8578
|
+
* user has at least one organization by creating a personal workspace
|
|
8579
|
+
* (named "<User>'s Workspace", slug `<username>-workspace`) and an
|
|
8580
|
+
* owner-role `sys_member` row. The user's session will pick this up as
|
|
8581
|
+
* their `activeOrganizationId` on the next sign-in / org-list refresh
|
|
8582
|
+
* (better-auth's `setActiveOrganization` runs lazily when the picker
|
|
8583
|
+
* sees exactly one membership).
|
|
8584
|
+
*
|
|
8585
|
+
* Idempotent: bails out if the user already has any `sys_member` row.
|
|
8586
|
+
* Slug collisions retry with a numeric suffix; a cap of 5 attempts
|
|
8587
|
+
* means a pathological username will fail loudly rather than loop.
|
|
8588
|
+
*/
|
|
8589
|
+
interface EnsureOptions {
|
|
8590
|
+
logger?: {
|
|
8591
|
+
info: (message: string, meta?: Record<string, any>) => void;
|
|
8592
|
+
warn: (message: string, meta?: Record<string, any>) => void;
|
|
8593
|
+
};
|
|
8594
|
+
/**
|
|
8595
|
+
* Optional hook called after a personal org is successfully created.
|
|
8596
|
+
* Used by SecurityPlugin to wire in `cloneTenantSeedData` so each
|
|
8597
|
+
* new workspace gets its own copy of demo data. Pulled in via DI
|
|
8598
|
+
* to keep this helper free of a hard import on the cloner (which
|
|
8599
|
+
* keeps the tenant-claim and ensure-org test surfaces narrow).
|
|
8600
|
+
*/
|
|
8601
|
+
cloneSeedData?: (ql: any, targetOrgId: string, opts: {
|
|
8602
|
+
logger?: EnsureOptions['logger'];
|
|
8603
|
+
}) => Promise<{
|
|
8604
|
+
object: string;
|
|
8605
|
+
count: number;
|
|
8606
|
+
}[]>;
|
|
8607
|
+
}
|
|
8608
|
+
/**
|
|
8609
|
+
* Ensure `user` has at least one `sys_member` row. Creates a personal
|
|
8610
|
+
* organization owned by them if not.
|
|
8611
|
+
*
|
|
8612
|
+
* Returns `{ created: true, organizationId }` when a new org was made,
|
|
8613
|
+
* or `{ created: false, reason }` when the user already has memberships
|
|
8614
|
+
* or the operation was skipped.
|
|
8615
|
+
*/
|
|
8616
|
+
declare function ensureUserHasOrganization(ql: any, user: {
|
|
8617
|
+
id: string;
|
|
8618
|
+
name?: string;
|
|
8619
|
+
email?: string;
|
|
8620
|
+
}, options?: EnsureOptions): Promise<{
|
|
8621
|
+
created: boolean;
|
|
8622
|
+
organizationId?: string;
|
|
8623
|
+
reason?: string;
|
|
8624
|
+
}>;
|
|
8625
|
+
|
|
8626
|
+
interface CloneOptions {
|
|
8627
|
+
logger?: {
|
|
8628
|
+
info: (message: string, meta?: Record<string, any>) => void;
|
|
8629
|
+
warn: (message: string, meta?: Record<string, any>) => void;
|
|
8630
|
+
};
|
|
8631
|
+
}
|
|
8632
|
+
declare function cloneTenantSeedData(ql: any, targetOrgId: string, options?: CloneOptions): Promise<{
|
|
8633
|
+
object: string;
|
|
8634
|
+
count: number;
|
|
8635
|
+
}[]>;
|
|
8636
|
+
|
|
8637
|
+
export { FieldMasker, PermissionDeniedError, PermissionEvaluator, RLSCompiler, RLS_DENY_FILTER, SECURITY_PLUGIN_ID, SECURITY_PLUGIN_VERSION, SecurityPlugin, cloneTenantSeedData, ensureUserHasOrganization, isPermissionDeniedError, securityDefaultPermissionSets, securityObjects, securityPluginManifestHeader };
|
package/dist/index.js
CHANGED
|
@@ -28,6 +28,8 @@ __export(index_exports, {
|
|
|
28
28
|
SECURITY_PLUGIN_ID: () => SECURITY_PLUGIN_ID,
|
|
29
29
|
SECURITY_PLUGIN_VERSION: () => SECURITY_PLUGIN_VERSION,
|
|
30
30
|
SecurityPlugin: () => SecurityPlugin,
|
|
31
|
+
cloneTenantSeedData: () => cloneTenantSeedData,
|
|
32
|
+
ensureUserHasOrganization: () => ensureUserHasOrganization,
|
|
31
33
|
isPermissionDeniedError: () => isPermissionDeniedError,
|
|
32
34
|
securityDefaultPermissionSets: () => securityDefaultPermissionSets,
|
|
33
35
|
securityObjects: () => securityObjects,
|
|
@@ -179,7 +181,8 @@ var RLSCompiler = class {
|
|
|
179
181
|
const userCtx = {
|
|
180
182
|
id: executionContext?.userId,
|
|
181
183
|
organization_id: executionContext?.tenantId,
|
|
182
|
-
roles: executionContext?.roles
|
|
184
|
+
roles: executionContext?.roles,
|
|
185
|
+
org_user_ids: executionContext?.org_user_ids
|
|
183
186
|
};
|
|
184
187
|
const filters = [];
|
|
185
188
|
for (const policy of policies) {
|
|
@@ -1248,6 +1251,8 @@ var SecurityPlugin = class {
|
|
|
1248
1251
|
SECURITY_PLUGIN_ID,
|
|
1249
1252
|
SECURITY_PLUGIN_VERSION,
|
|
1250
1253
|
SecurityPlugin,
|
|
1254
|
+
cloneTenantSeedData,
|
|
1255
|
+
ensureUserHasOrganization,
|
|
1251
1256
|
isPermissionDeniedError,
|
|
1252
1257
|
securityDefaultPermissionSets,
|
|
1253
1258
|
securityObjects,
|