@happyvertical/smrt-users 0.30.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/AGENTS.md +85 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +7 -0
- package/README.md +459 -0
- package/dist/__smrt-register__.d.ts +2 -0
- package/dist/__smrt-register__.d.ts.map +1 -0
- package/dist/chunks/TerminalAuthService-DoAMQ_yn.js +5118 -0
- package/dist/chunks/TerminalAuthService-DoAMQ_yn.js.map +1 -0
- package/dist/chunks/index-DkoYIvIu.js +169 -0
- package/dist/chunks/index-DkoYIvIu.js.map +1 -0
- package/dist/collections/CliAuthRequestCollection.d.ts +19 -0
- package/dist/collections/CliAuthRequestCollection.d.ts.map +1 -0
- package/dist/collections/GroupCollection.d.ts +17 -0
- package/dist/collections/GroupCollection.d.ts.map +1 -0
- package/dist/collections/GroupMemberCollection.d.ts +43 -0
- package/dist/collections/GroupMemberCollection.d.ts.map +1 -0
- package/dist/collections/GroupRoleCollection.d.ts +33 -0
- package/dist/collections/GroupRoleCollection.d.ts.map +1 -0
- package/dist/collections/MagicLinkTokenCollection.d.ts +26 -0
- package/dist/collections/MagicLinkTokenCollection.d.ts.map +1 -0
- package/dist/collections/MembershipCollection.d.ts +38 -0
- package/dist/collections/MembershipCollection.d.ts.map +1 -0
- package/dist/collections/MembershipOverrideCollection.d.ts +55 -0
- package/dist/collections/MembershipOverrideCollection.d.ts.map +1 -0
- package/dist/collections/PermissionCollection.d.ts +34 -0
- package/dist/collections/PermissionCollection.d.ts.map +1 -0
- package/dist/collections/RoleCollection.d.ts +29 -0
- package/dist/collections/RoleCollection.d.ts.map +1 -0
- package/dist/collections/RolePermissionCollection.d.ts +33 -0
- package/dist/collections/RolePermissionCollection.d.ts.map +1 -0
- package/dist/collections/SessionCollection.d.ts +82 -0
- package/dist/collections/SessionCollection.d.ts.map +1 -0
- package/dist/collections/TenantCollection.d.ts +119 -0
- package/dist/collections/TenantCollection.d.ts.map +1 -0
- package/dist/collections/TenantPermissionOverrideCollection.d.ts +111 -0
- package/dist/collections/TenantPermissionOverrideCollection.d.ts.map +1 -0
- package/dist/collections/UserCollection.d.ts +116 -0
- package/dist/collections/UserCollection.d.ts.map +1 -0
- package/dist/collections/index.d.ts +19 -0
- package/dist/collections/index.d.ts.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1482 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.json +5216 -0
- package/dist/models/CliAuthRequest.d.ts +25 -0
- package/dist/models/CliAuthRequest.d.ts.map +1 -0
- package/dist/models/Group.d.ts +34 -0
- package/dist/models/Group.d.ts.map +1 -0
- package/dist/models/GroupMember.d.ts +29 -0
- package/dist/models/GroupMember.d.ts.map +1 -0
- package/dist/models/GroupRole.d.ts +29 -0
- package/dist/models/GroupRole.d.ts.map +1 -0
- package/dist/models/MagicLinkToken.d.ts +22 -0
- package/dist/models/MagicLinkToken.d.ts.map +1 -0
- package/dist/models/Membership.d.ts +48 -0
- package/dist/models/Membership.d.ts.map +1 -0
- package/dist/models/MembershipOverride.d.ts +50 -0
- package/dist/models/MembershipOverride.d.ts.map +1 -0
- package/dist/models/Permission.d.ts +79 -0
- package/dist/models/Permission.d.ts.map +1 -0
- package/dist/models/Role.d.ts +67 -0
- package/dist/models/Role.d.ts.map +1 -0
- package/dist/models/RolePermission.d.ts +29 -0
- package/dist/models/RolePermission.d.ts.map +1 -0
- package/dist/models/Session.d.ts +105 -0
- package/dist/models/Session.d.ts.map +1 -0
- package/dist/models/Tenant.d.ts +138 -0
- package/dist/models/Tenant.d.ts.map +1 -0
- package/dist/models/TenantPermissionOverride.d.ts +74 -0
- package/dist/models/TenantPermissionOverride.d.ts.map +1 -0
- package/dist/models/User.d.ts +72 -0
- package/dist/models/User.d.ts.map +1 -0
- package/dist/models/index.d.ts +19 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/playground.d.ts +2 -0
- package/dist/playground.d.ts.map +1 -0
- package/dist/playground.js +139 -0
- package/dist/playground.js.map +1 -0
- package/dist/services/MagicLinkService.d.ts +84 -0
- package/dist/services/MagicLinkService.d.ts.map +1 -0
- package/dist/services/OidcLoginService.d.ts +134 -0
- package/dist/services/OidcLoginService.d.ts.map +1 -0
- package/dist/services/PermissionCatalogService.d.ts +62 -0
- package/dist/services/PermissionCatalogService.d.ts.map +1 -0
- package/dist/services/PermissionResolver.d.ts +150 -0
- package/dist/services/PermissionResolver.d.ts.map +1 -0
- package/dist/services/PostgresPermissionPolicies.d.ts +29 -0
- package/dist/services/PostgresPermissionPolicies.d.ts.map +1 -0
- package/dist/services/SessionPermissionContext.d.ts +43 -0
- package/dist/services/SessionPermissionContext.d.ts.map +1 -0
- package/dist/services/SessionService.d.ts +139 -0
- package/dist/services/SessionService.d.ts.map +1 -0
- package/dist/services/TenantService.d.ts +135 -0
- package/dist/services/TenantService.d.ts.map +1 -0
- package/dist/services/TerminalAuthService.d.ts +189 -0
- package/dist/services/TerminalAuthService.d.ts.map +1 -0
- package/dist/services/index.d.ts +14 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/smrt-knowledge.json +2744 -0
- package/dist/svelte/components/InviteUserModal.svelte +351 -0
- package/dist/svelte/components/InviteUserModal.svelte.d.ts +17 -0
- package/dist/svelte/components/InviteUserModal.svelte.d.ts.map +1 -0
- package/dist/svelte/components/UserAvatar.svelte +105 -0
- package/dist/svelte/components/UserAvatar.svelte.d.ts +10 -0
- package/dist/svelte/components/UserAvatar.svelte.d.ts.map +1 -0
- package/dist/svelte/components/UserCard.svelte +179 -0
- package/dist/svelte/components/UserCard.svelte.d.ts +18 -0
- package/dist/svelte/components/UserCard.svelte.d.ts.map +1 -0
- package/dist/svelte/components/UserForm.svelte +194 -0
- package/dist/svelte/components/UserForm.svelte.d.ts +18 -0
- package/dist/svelte/components/UserForm.svelte.d.ts.map +1 -0
- package/dist/svelte/components/UserList.svelte +107 -0
- package/dist/svelte/components/UserList.svelte.d.ts +20 -0
- package/dist/svelte/components/UserList.svelte.d.ts.map +1 -0
- package/dist/svelte/components/UserMenu.svelte +326 -0
- package/dist/svelte/components/UserMenu.svelte.d.ts +33 -0
- package/dist/svelte/components/UserMenu.svelte.d.ts.map +1 -0
- package/dist/svelte/components/__tests__/InviteUserModal.test.js +54 -0
- package/dist/svelte/components/__tests__/UserAvatar.test.js +31 -0
- package/dist/svelte/components/__tests__/UserCard.test.js +39 -0
- package/dist/svelte/components/__tests__/UserForm.test.js +50 -0
- package/dist/svelte/components/__tests__/UserList.test.js +48 -0
- package/dist/svelte/components/__tests__/UserMenu.test.js +38 -0
- package/dist/svelte/i18n.d.ts +15 -0
- package/dist/svelte/i18n.d.ts.map +1 -0
- package/dist/svelte/i18n.js +15 -0
- package/dist/svelte/index.d.ts +23 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +27 -0
- package/dist/svelte/playground.d.ts +151 -0
- package/dist/svelte/playground.d.ts.map +1 -0
- package/dist/svelte/playground.js +134 -0
- package/dist/sveltekit/index.d.ts +379 -0
- package/dist/sveltekit/index.d.ts.map +1 -0
- package/dist/sveltekit/resource-list-handler.d.ts +127 -0
- package/dist/sveltekit/resource-list-handler.d.ts.map +1 -0
- package/dist/sveltekit/types.d.ts +31 -0
- package/dist/sveltekit/types.d.ts.map +1 -0
- package/dist/sveltekit.d.ts +2 -0
- package/dist/sveltekit.d.ts.map +1 -0
- package/dist/sveltekit.js +978 -0
- package/dist/sveltekit.js.map +1 -0
- package/dist/types/index.d.ts +61 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/ui.d.ts +10 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +75 -0
- package/dist/ui.js.map +1 -0
- package/package.json +97 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { SmrtClassOptions } from '@happyvertical/smrt-core';
|
|
2
|
+
import { OidcClaims, OidcIdentityResult } from '../collections/UserCollection.js';
|
|
3
|
+
export type OidcProviderKind = 'kanidm' | 'dex' | 'generic';
|
|
4
|
+
export type OidcTokenEndpointAuthMethod = 'client_secret_basic' | 'client_secret_post' | 'none';
|
|
5
|
+
export interface OidcProviderConfig {
|
|
6
|
+
/**
|
|
7
|
+
* Optional preset label for documentation and UI.
|
|
8
|
+
*
|
|
9
|
+
* Kanidm and Dex are standards-compliant OIDC providers, so the preset does
|
|
10
|
+
* not change protocol behavior; it lets apps name their intent clearly.
|
|
11
|
+
*/
|
|
12
|
+
kind?: OidcProviderKind;
|
|
13
|
+
/** OIDC issuer URL, e.g. https://id.example.com/dex */
|
|
14
|
+
issuer: string;
|
|
15
|
+
/** OAuth2/OIDC client ID registered with the provider */
|
|
16
|
+
clientId: string;
|
|
17
|
+
/** OAuth2/OIDC client secret for confidential clients */
|
|
18
|
+
clientSecret?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Redirect URI registered with the provider.
|
|
21
|
+
*
|
|
22
|
+
* SvelteKit helpers can derive this from the current request when omitted.
|
|
23
|
+
*/
|
|
24
|
+
redirectUri?: string;
|
|
25
|
+
/** Scopes to request. Defaults to openid profile email. */
|
|
26
|
+
scopes?: string[];
|
|
27
|
+
/** Override the discovery document URL when needed. */
|
|
28
|
+
discoveryUrl?: string;
|
|
29
|
+
/** Additional authorization request parameters. */
|
|
30
|
+
authorizationParams?: Record<string, string | number | boolean | undefined>;
|
|
31
|
+
/**
|
|
32
|
+
* Client authentication method for the token endpoint.
|
|
33
|
+
*
|
|
34
|
+
* Defaults to client_secret_basic when clientSecret exists, otherwise none.
|
|
35
|
+
*/
|
|
36
|
+
tokenEndpointAuthMethod?: OidcTokenEndpointAuthMethod;
|
|
37
|
+
}
|
|
38
|
+
export interface OidcProviderResolutionOptions {
|
|
39
|
+
defaultProvider?: string;
|
|
40
|
+
providers?: Record<string, OidcProviderConfig>;
|
|
41
|
+
}
|
|
42
|
+
export interface UsersOidcConfig extends OidcProviderResolutionOptions {
|
|
43
|
+
/** Seconds before login transaction cookies expire. */
|
|
44
|
+
transactionTtl?: number;
|
|
45
|
+
}
|
|
46
|
+
export interface OidcProviderResolution {
|
|
47
|
+
provider: OidcProviderConfig;
|
|
48
|
+
providerName: string;
|
|
49
|
+
}
|
|
50
|
+
export interface ResolvedOidcProviderConfig extends OidcProviderConfig {
|
|
51
|
+
redirectUri: string;
|
|
52
|
+
}
|
|
53
|
+
export interface OidcProviderMetadata {
|
|
54
|
+
authorization_endpoint: string;
|
|
55
|
+
end_session_endpoint?: string;
|
|
56
|
+
issuer: string;
|
|
57
|
+
jwks_uri: string;
|
|
58
|
+
token_endpoint: string;
|
|
59
|
+
userinfo_endpoint?: string;
|
|
60
|
+
[key: string]: unknown;
|
|
61
|
+
}
|
|
62
|
+
export interface OidcTransaction {
|
|
63
|
+
codeVerifier: string;
|
|
64
|
+
createdAt: number;
|
|
65
|
+
nonce: string;
|
|
66
|
+
provider: string;
|
|
67
|
+
returnTo?: string;
|
|
68
|
+
state: string;
|
|
69
|
+
}
|
|
70
|
+
export interface CreateAuthorizationUrlOptions {
|
|
71
|
+
authorizationParams?: Record<string, string | number | boolean | undefined>;
|
|
72
|
+
transaction?: OidcTransaction;
|
|
73
|
+
}
|
|
74
|
+
export interface OidcTokenSet {
|
|
75
|
+
accessToken?: string;
|
|
76
|
+
expiresAt?: Date;
|
|
77
|
+
idToken: string;
|
|
78
|
+
refreshToken?: string;
|
|
79
|
+
scope?: string;
|
|
80
|
+
tokenType?: string;
|
|
81
|
+
}
|
|
82
|
+
export interface OidcCallbackResult {
|
|
83
|
+
claims: OidcClaims;
|
|
84
|
+
tokens: OidcTokenSet;
|
|
85
|
+
}
|
|
86
|
+
export interface OidcLoginResult extends OidcIdentityResult {
|
|
87
|
+
claims: OidcClaims;
|
|
88
|
+
tokens: OidcTokenSet;
|
|
89
|
+
}
|
|
90
|
+
export interface OidcLoginServiceOptions extends SmrtClassOptions {
|
|
91
|
+
/** Provider key, e.g. kanidm or dex */
|
|
92
|
+
providerName: string;
|
|
93
|
+
/** Fully resolved provider config. redirectUri is required here. */
|
|
94
|
+
provider: ResolvedOidcProviderConfig;
|
|
95
|
+
/** Optional fetch override for tests or custom runtimes. */
|
|
96
|
+
fetch?: typeof fetch;
|
|
97
|
+
/** JWT clock tolerance passed to jose. Defaults to 15 seconds. */
|
|
98
|
+
clockTolerance?: number | string;
|
|
99
|
+
/** Provider metadata cache TTL in milliseconds. Defaults to 5 minutes. */
|
|
100
|
+
metadataCacheTtlMs?: number;
|
|
101
|
+
}
|
|
102
|
+
export declare class OidcLoginError extends Error {
|
|
103
|
+
constructor(message: string);
|
|
104
|
+
}
|
|
105
|
+
export declare function getUsersOidcConfig(): UsersOidcConfig;
|
|
106
|
+
export declare function encodeOidcTransaction(transaction: OidcTransaction): string;
|
|
107
|
+
export declare function decodeOidcTransaction(value: string): OidcTransaction;
|
|
108
|
+
export declare function resolveOidcProviderConfig(providerName?: string, options?: OidcProviderResolutionOptions): OidcProviderResolution;
|
|
109
|
+
export declare class OidcLoginService {
|
|
110
|
+
private readonly classOptions;
|
|
111
|
+
private readonly clockTolerance;
|
|
112
|
+
private readonly fetchImpl;
|
|
113
|
+
private readonly metadataCacheTtlMs;
|
|
114
|
+
private metadataFetchedAt;
|
|
115
|
+
private metadataPromise?;
|
|
116
|
+
private remoteJwks?;
|
|
117
|
+
readonly provider: ResolvedOidcProviderConfig;
|
|
118
|
+
readonly providerName: string;
|
|
119
|
+
constructor(options: OidcLoginServiceOptions);
|
|
120
|
+
createTransaction(returnTo?: string): OidcTransaction;
|
|
121
|
+
getMetadata(): Promise<OidcProviderMetadata>;
|
|
122
|
+
private fetchMetadata;
|
|
123
|
+
createAuthorizationUrl(options?: CreateAuthorizationUrlOptions): Promise<{
|
|
124
|
+
transaction: OidcTransaction;
|
|
125
|
+
url: URL;
|
|
126
|
+
}>;
|
|
127
|
+
exchangeCallback(callbackUrl: string | URL, transaction: OidcTransaction): Promise<OidcCallbackResult>;
|
|
128
|
+
completeLogin(callbackUrl: string | URL, transaction: OidcTransaction): Promise<OidcLoginResult>;
|
|
129
|
+
private validateCallback;
|
|
130
|
+
private exchangeCode;
|
|
131
|
+
private enrichClaimsFromUserInfo;
|
|
132
|
+
private verifyIdToken;
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=OidcLoginService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OidcLoginService.d.ts","sourceRoot":"","sources":["../../src/services/OidcLoginService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { SmrtClassOptions } from '@happyvertical/smrt-core';
|
|
2
|
+
export type PermissionCatalogSource = 'manifest' | 'config' | 'runtime';
|
|
3
|
+
export type PostgresPermissionAction = 'SELECT' | 'INSERT' | 'UPDATE' | 'DELETE';
|
|
4
|
+
export interface PostgresPermissionBinding {
|
|
5
|
+
action: Lowercase<PostgresPermissionAction> | PostgresPermissionAction;
|
|
6
|
+
permission?: string;
|
|
7
|
+
schemaName?: string;
|
|
8
|
+
tableName: string;
|
|
9
|
+
tenantField?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface PermissionDefinition {
|
|
12
|
+
category?: string;
|
|
13
|
+
className?: string;
|
|
14
|
+
collection?: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
name?: string;
|
|
17
|
+
postgres?: {
|
|
18
|
+
bindings?: PostgresPermissionBinding[];
|
|
19
|
+
};
|
|
20
|
+
qualifiedName?: string;
|
|
21
|
+
slug: string;
|
|
22
|
+
source?: PermissionCatalogSource;
|
|
23
|
+
}
|
|
24
|
+
export interface PermissionCatalog {
|
|
25
|
+
customPermissions: PermissionDefinition[];
|
|
26
|
+
manifestPermissions: PermissionDefinition[];
|
|
27
|
+
permissions: PermissionDefinition[];
|
|
28
|
+
runtimePermissions: PermissionDefinition[];
|
|
29
|
+
}
|
|
30
|
+
export interface PermissionCatalogSyncResult {
|
|
31
|
+
catalog: PermissionCatalog;
|
|
32
|
+
created: string[];
|
|
33
|
+
unchanged: string[];
|
|
34
|
+
updated: string[];
|
|
35
|
+
}
|
|
36
|
+
export interface UsersConfig extends Record<string, unknown> {
|
|
37
|
+
permissions?: {
|
|
38
|
+
custom?: PermissionDefinition[];
|
|
39
|
+
postgres?: {
|
|
40
|
+
bindings?: PostgresPermissionBinding[];
|
|
41
|
+
enabled?: boolean;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
declare global {
|
|
46
|
+
var __smrtUsersPermissionRegistrations: Map<number, PermissionDefinition[]> | undefined;
|
|
47
|
+
var __smrtUsersPermissionRegistrationCounter: number | undefined;
|
|
48
|
+
}
|
|
49
|
+
export declare function registerPermissionDefinitions(definitions: PermissionDefinition[]): () => void;
|
|
50
|
+
export declare class PermissionCatalogService {
|
|
51
|
+
private readonly options;
|
|
52
|
+
constructor(options?: SmrtClassOptions);
|
|
53
|
+
getUsersConfig(): UsersConfig;
|
|
54
|
+
getRuntimePermissionDefinitions(): PermissionDefinition[];
|
|
55
|
+
getCustomPermissionDefinitions(): PermissionDefinition[];
|
|
56
|
+
getCatalog(): PermissionCatalog;
|
|
57
|
+
syncPermissionCatalog(): Promise<PermissionCatalogSyncResult>;
|
|
58
|
+
private getManifestPermissionDefinitions;
|
|
59
|
+
static create(options?: SmrtClassOptions): PermissionCatalogService;
|
|
60
|
+
}
|
|
61
|
+
export declare function syncPermissionCatalog(options?: SmrtClassOptions): Promise<PermissionCatalogSyncResult>;
|
|
62
|
+
//# sourceMappingURL=PermissionCatalogService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PermissionCatalogService.d.ts","sourceRoot":"","sources":["../../src/services/PermissionCatalogService.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAIL,KAAK,gBAAgB,EACtB,MAAM,0BAA0B,CAAC;AAOlC,MAAM,MAAM,uBAAuB,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,CAAC;AAExE,MAAM,MAAM,wBAAwB,GAChC,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,QAAQ,CAAC;AAEb,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,SAAS,CAAC,wBAAwB,CAAC,GAAG,wBAAwB,CAAC;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE;QACT,QAAQ,CAAC,EAAE,yBAAyB,EAAE,CAAC;KACxC,CAAC;IACF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,uBAAuB,CAAC;CAClC;AAED,MAAM,WAAW,iBAAiB;IAChC,iBAAiB,EAAE,oBAAoB,EAAE,CAAC;IAC1C,mBAAmB,EAAE,oBAAoB,EAAE,CAAC;IAC5C,WAAW,EAAE,oBAAoB,EAAE,CAAC;IACpC,kBAAkB,EAAE,oBAAoB,EAAE,CAAC;CAC5C;AAED,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,iBAAiB,CAAC;IAC3B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,WAAY,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC1D,WAAW,CAAC,EAAE;QACZ,MAAM,CAAC,EAAE,oBAAoB,EAAE,CAAC;QAChC,QAAQ,CAAC,EAAE;YACT,QAAQ,CAAC,EAAE,yBAAyB,EAAE,CAAC;YACvC,OAAO,CAAC,EAAE,OAAO,CAAC;SACnB,CAAC;KACH,CAAC;CACH;AAED,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,kCAAkC,EAClC,GAAG,CAAC,MAAM,EAAE,oBAAoB,EAAE,CAAC,GACnC,SAAS,CAAC;IAEd,IAAI,wCAAwC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClE;AA4TD,wBAAgB,6BAA6B,CAC3C,WAAW,EAAE,oBAAoB,EAAE,GAClC,MAAM,IAAI,CASZ;AAED,qBAAa,wBAAwB;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,GAAE,gBAAqB;IAE3D,cAAc,IAAI,WAAW;IAI7B,+BAA+B,IAAI,oBAAoB,EAAE;IAIzD,8BAA8B,IAAI,oBAAoB,EAAE;IAIxD,UAAU,IAAI,iBAAiB;IA0BzB,qBAAqB,IAAI,OAAO,CAAC,2BAA2B,CAAC;IAoDnE,OAAO,CAAC,gCAAgC;IA8GxC,MAAM,CAAC,MAAM,CAAC,OAAO,GAAE,gBAAqB,GAAG,wBAAwB;CAGxE;AAED,wBAAsB,qBAAqB,CACzC,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,2BAA2B,CAAC,CAEtC"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { SmrtClassOptions } from '@happyvertical/smrt-core';
|
|
2
|
+
import { Membership } from '../models/Membership.js';
|
|
3
|
+
import { Tenant } from '../models/Tenant.js';
|
|
4
|
+
/**
|
|
5
|
+
* Permission resolution result
|
|
6
|
+
*/
|
|
7
|
+
export interface PermissionResolutionResult {
|
|
8
|
+
/** Set of granted permission slugs */
|
|
9
|
+
permissions: Set<string>;
|
|
10
|
+
/** Membership ID used for resolution */
|
|
11
|
+
membershipId: string | null;
|
|
12
|
+
/** Role ID from membership */
|
|
13
|
+
roleId: string | null;
|
|
14
|
+
/** Group IDs that contributed permissions */
|
|
15
|
+
groupIds: string[];
|
|
16
|
+
/** Permission IDs explicitly denied */
|
|
17
|
+
deniedPermissionIds: string[];
|
|
18
|
+
}
|
|
19
|
+
export interface PermissionResolutionOptions {
|
|
20
|
+
/**
|
|
21
|
+
* Active membership already resolved by the caller for this user/tenant.
|
|
22
|
+
* Passing this lets request-scoped session loaders avoid re-querying the
|
|
23
|
+
* same membership row before resolving permissions.
|
|
24
|
+
*/
|
|
25
|
+
membership?: Membership | null;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Tenant permission inheritance chain result
|
|
29
|
+
*/
|
|
30
|
+
export interface TenantPermissionInheritanceResult {
|
|
31
|
+
/** Effective tenant permissions (slugs) after inheritance resolution */
|
|
32
|
+
permissions: Set<string>;
|
|
33
|
+
/** Tenant IDs that contributed to the inheritance chain */
|
|
34
|
+
contributingTenantIds: string[];
|
|
35
|
+
/** Whether inheritance was active (at least one tenant in chain had inheritPermissions: true) */
|
|
36
|
+
inheritanceActive: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* PermissionResolver resolves the effective permissions for a user in a tenant.
|
|
40
|
+
*
|
|
41
|
+
* Resolution algorithm:
|
|
42
|
+
* 1. Resolve tenant hierarchy permissions (if hierarchical tenants are used)
|
|
43
|
+
* 2. Get user's membership in the tenant
|
|
44
|
+
* 3. Get base permissions from membership's role
|
|
45
|
+
* 4. Get user's groups in the tenant
|
|
46
|
+
* 5. Add permissions from group roles
|
|
47
|
+
* 6. Apply membership overrides (grant/deny)
|
|
48
|
+
*
|
|
49
|
+
* DENY overrides take precedence over GRANT overrides at every level.
|
|
50
|
+
*
|
|
51
|
+
* ## Hierarchical Tenant Permissions
|
|
52
|
+
*
|
|
53
|
+
* When tenants are organized hierarchically, permissions can cascade from
|
|
54
|
+
* parent tenants to child tenants. The cascade is controlled by:
|
|
55
|
+
* - Parent's `cascadePermissions`: If true, parent pushes permissions down
|
|
56
|
+
* - Child's `inheritPermissions`: If true, child accepts parent's permissions
|
|
57
|
+
*
|
|
58
|
+
* Child tenants can override inherited permissions using TenantPermissionOverride:
|
|
59
|
+
* - INHERIT: Use parent's value (default)
|
|
60
|
+
* - GRANT: Explicitly grant at this level
|
|
61
|
+
* - DENY: Explicitly block (even if parent grants)
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* const resolver = new PermissionResolver(options);
|
|
66
|
+
* await resolver.initialize();
|
|
67
|
+
*
|
|
68
|
+
* // Check single permission
|
|
69
|
+
* const canCreate = await resolver.hasPermission(userId, tenantId, 'articles.create');
|
|
70
|
+
*
|
|
71
|
+
* // Get all permissions
|
|
72
|
+
* const result = await resolver.resolvePermissions(userId, tenantId);
|
|
73
|
+
* console.log(result.permissions); // Set<string>
|
|
74
|
+
*
|
|
75
|
+
* // Resolve tenant-level permissions only (without user context)
|
|
76
|
+
* const tenantPerms = await resolver.resolveTenantPermissions(tenantId);
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export declare class PermissionResolver {
|
|
80
|
+
private options;
|
|
81
|
+
private membershipCollection;
|
|
82
|
+
private rolePermissionCollection;
|
|
83
|
+
private membershipOverrideCollection;
|
|
84
|
+
private groupMemberCollection;
|
|
85
|
+
private groupRoleCollection;
|
|
86
|
+
private permissionCollection;
|
|
87
|
+
private tenantCollection;
|
|
88
|
+
private tenantPermissionOverrideCollection;
|
|
89
|
+
constructor(options: SmrtClassOptions);
|
|
90
|
+
/**
|
|
91
|
+
* Initialize collections
|
|
92
|
+
*
|
|
93
|
+
* Note: The `as any` casts are required because `SmrtCollection.create()` is
|
|
94
|
+
* declared with a protected constructor and a static factory. TypeScript
|
|
95
|
+
* cannot infer that the static `create()` returns the concrete subclass type
|
|
96
|
+
* when invoked through a subclass reference, so the cast is needed to call
|
|
97
|
+
* `create(options)` on each collection. This is a known SMRT framework
|
|
98
|
+
* limitation around the protected-constructor + static-factory pattern.
|
|
99
|
+
*/
|
|
100
|
+
initialize(): Promise<void>;
|
|
101
|
+
/**
|
|
102
|
+
* Resolve effective permissions for a tenant, considering hierarchy inheritance.
|
|
103
|
+
*
|
|
104
|
+
* Algorithm:
|
|
105
|
+
* 1. Get the tenant and its ancestors (from root to immediate parent)
|
|
106
|
+
* 2. Batch fetch all permission overrides for the entire chain (single query)
|
|
107
|
+
* 3. Walk down the chain, building up permissions:
|
|
108
|
+
* - Start with root tenant's permissions
|
|
109
|
+
* - For each child: if parent.cascadePermissions && child.inheritPermissions:
|
|
110
|
+
* - Merge parent's permissions
|
|
111
|
+
* - Apply child's overrides (GRANT adds, DENY removes)
|
|
112
|
+
* 4. Return the final effective permission set
|
|
113
|
+
*/
|
|
114
|
+
resolveTenantPermissions(tenantId: string): Promise<TenantPermissionInheritanceResult>;
|
|
115
|
+
/**
|
|
116
|
+
* Get the inheritance chain for a tenant (for debugging/display purposes)
|
|
117
|
+
*/
|
|
118
|
+
getTenantInheritanceChain(tenantId: string): Promise<Array<{
|
|
119
|
+
tenant: Tenant;
|
|
120
|
+
inherits: boolean;
|
|
121
|
+
cascades: boolean;
|
|
122
|
+
}>>;
|
|
123
|
+
/**
|
|
124
|
+
* Resolve all effective permissions for a user in a tenant
|
|
125
|
+
*
|
|
126
|
+
* Algorithm:
|
|
127
|
+
* 1. Get membership and collect all permission IDs from all sources
|
|
128
|
+
* 2. Batch fetch all permissions in a single query
|
|
129
|
+
* 3. Apply permissions from role, groups, and overrides
|
|
130
|
+
* 4. DENY overrides take precedence over GRANT
|
|
131
|
+
*/
|
|
132
|
+
resolvePermissions(userId: string, tenantId: string, options?: PermissionResolutionOptions): Promise<PermissionResolutionResult>;
|
|
133
|
+
/**
|
|
134
|
+
* Check if a user has a specific permission in a tenant
|
|
135
|
+
*/
|
|
136
|
+
hasPermission(userId: string, tenantId: string, permissionSlug: string, options?: PermissionResolutionOptions): Promise<boolean>;
|
|
137
|
+
/**
|
|
138
|
+
* Check if a user has all of the specified permissions
|
|
139
|
+
*/
|
|
140
|
+
hasAllPermissions(userId: string, tenantId: string, permissionSlugs: string[], options?: PermissionResolutionOptions): Promise<boolean>;
|
|
141
|
+
/**
|
|
142
|
+
* Check if a user has any of the specified permissions
|
|
143
|
+
*/
|
|
144
|
+
hasAnyPermission(userId: string, tenantId: string, permissionSlugs: string[], options?: PermissionResolutionOptions): Promise<boolean>;
|
|
145
|
+
/**
|
|
146
|
+
* Static factory method
|
|
147
|
+
*/
|
|
148
|
+
static create(options: SmrtClassOptions): Promise<PermissionResolver>;
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=PermissionResolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PermissionResolver.d.ts","sourceRoot":"","sources":["../../src/services/PermissionResolver.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AASjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,sCAAsC;IACtC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACzB,wCAAwC;IACxC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,8BAA8B;IAC9B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,uCAAuC;IACvC,mBAAmB,EAAE,MAAM,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,2BAA2B;IAC1C;;;;OAIG;IACH,UAAU,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,iCAAiC;IAChD,wEAAwE;IACxE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACzB,2DAA2D;IAC3D,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,iGAAiG;IACjG,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,oBAAoB,CAAwB;IACpD,OAAO,CAAC,wBAAwB,CAA4B;IAC5D,OAAO,CAAC,4BAA4B,CAAgC;IACpE,OAAO,CAAC,qBAAqB,CAAyB;IACtD,OAAO,CAAC,mBAAmB,CAAuB;IAClD,OAAO,CAAC,oBAAoB,CAAwB;IACpD,OAAO,CAAC,gBAAgB,CAAoB;IAC5C,OAAO,CAAC,kCAAkC,CAAsC;gBAEpE,OAAO,EAAE,gBAAgB;IAIrC;;;;;;;;;OASG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IA+BjC;;;;;;;;;;;;OAYG;IACG,wBAAwB,CAC5B,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,iCAAiC,CAAC;IA6G7C;;OAEG;IACG,yBAAyB,CAC7B,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAmC3E;;;;;;;;OAQG;IACG,kBAAkB,CACtB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,0BAA0B,CAAC;IA6ItC;;OAEG;IACG,aAAa,CACjB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,OAAO,CAAC;IAKnB;;OAEG;IACG,iBAAiB,CACrB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,eAAe,EAAE,MAAM,EAAE,EACzB,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,OAAO,CAAC;IAKnB;;OAEG;IACG,gBAAgB,CACpB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,eAAe,EAAE,MAAM,EAAE,EACzB,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,OAAO,CAAC;IAKnB;;OAEG;WACU,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAK5E"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { SmrtClassOptions } from '@happyvertical/smrt-core';
|
|
2
|
+
import { PostgresPermissionAction, PostgresPermissionBinding } from './PermissionCatalogService.js';
|
|
3
|
+
export interface PostgresPermissionPolicyReportItem {
|
|
4
|
+
className?: string;
|
|
5
|
+
collection?: string;
|
|
6
|
+
qualifiedName?: string;
|
|
7
|
+
reason: string;
|
|
8
|
+
schemaName?: string;
|
|
9
|
+
tableName?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface PostgresPermissionPolicyTarget {
|
|
12
|
+
actions: Partial<Record<PostgresPermissionAction, string[]>>;
|
|
13
|
+
className?: string;
|
|
14
|
+
collection?: string;
|
|
15
|
+
qualifiedName?: string;
|
|
16
|
+
schemaName: string;
|
|
17
|
+
tableName: string;
|
|
18
|
+
tenantField: string;
|
|
19
|
+
}
|
|
20
|
+
export interface GeneratePostgresPermissionSqlResult {
|
|
21
|
+
bindings: PostgresPermissionBinding[];
|
|
22
|
+
skipped: PostgresPermissionPolicyReportItem[];
|
|
23
|
+
sql: string;
|
|
24
|
+
statements: string[];
|
|
25
|
+
targets: PostgresPermissionPolicyTarget[];
|
|
26
|
+
}
|
|
27
|
+
export declare function generatePostgresPermissionSql(options?: SmrtClassOptions): GeneratePostgresPermissionSqlResult;
|
|
28
|
+
export declare function applyPostgresPermissionPolicies(options?: SmrtClassOptions): Promise<GeneratePostgresPermissionSqlResult>;
|
|
29
|
+
//# sourceMappingURL=PostgresPermissionPolicies.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PostgresPermissionPolicies.d.ts","sourceRoot":"","sources":["../../src/services/PostgresPermissionPolicies.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAEL,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC/B,MAAM,+BAA+B,CAAC;AAmBvC,MAAM,WAAW,kCAAkC;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,8BAA8B;IAC7C,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,wBAAwB,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,mCAAmC;IAClD,QAAQ,EAAE,yBAAyB,EAAE,CAAC;IACtC,OAAO,EAAE,kCAAkC,EAAE,CAAC;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,EAAE,8BAA8B,EAAE,CAAC;CAC3C;AAuQD,wBAAgB,6BAA6B,CAC3C,OAAO,GAAE,gBAAqB,GAC7B,mCAAmC,CAgOrC;AAED,wBAAsB,+BAA+B,CACnD,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,mCAAmC,CAAC,CA0B9C"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { SmrtClassOptions } from '@happyvertical/smrt-core';
|
|
2
|
+
import { DatabaseInterface } from '@happyvertical/sql';
|
|
3
|
+
import { SessionContext, SessionService } from './SessionService.js';
|
|
4
|
+
interface QueryableDatabase extends DatabaseInterface {
|
|
5
|
+
beginTransaction?: () => Promise<TransactionDatabase>;
|
|
6
|
+
url: string;
|
|
7
|
+
}
|
|
8
|
+
interface TransactionDatabase extends QueryableDatabase {
|
|
9
|
+
commit: () => Promise<void>;
|
|
10
|
+
isActive: () => boolean;
|
|
11
|
+
rollback: () => Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
export interface SessionPermissionRuntimeContext {
|
|
14
|
+
database?: QueryableDatabase;
|
|
15
|
+
permissions: string[];
|
|
16
|
+
permissionSet: Set<string>;
|
|
17
|
+
membership: SessionContext['membership'];
|
|
18
|
+
postgresRls: boolean;
|
|
19
|
+
session: SessionContext | null;
|
|
20
|
+
sessionId: string | null;
|
|
21
|
+
superAdminBypass: boolean;
|
|
22
|
+
systemContext: boolean;
|
|
23
|
+
tenantId: string | null;
|
|
24
|
+
user: SessionContext['user'] | null;
|
|
25
|
+
userId: string | null;
|
|
26
|
+
}
|
|
27
|
+
export interface SessionPermissionRuntimeOptions extends SmrtClassOptions {
|
|
28
|
+
enterTenantContext?: boolean;
|
|
29
|
+
postgresRls?: boolean;
|
|
30
|
+
sessionId?: string | null;
|
|
31
|
+
sessionService?: SessionService;
|
|
32
|
+
superAdminBypass?: boolean;
|
|
33
|
+
systemContext?: boolean;
|
|
34
|
+
}
|
|
35
|
+
declare global {
|
|
36
|
+
var __smrtGetRequestPermissionContext: (() => SessionPermissionRuntimeContext | undefined) | undefined;
|
|
37
|
+
var __smrtGetRequestScopedDatabase: (() => QueryableDatabase | undefined) | undefined;
|
|
38
|
+
}
|
|
39
|
+
export declare function getCurrentSessionPermissionContext(): SessionPermissionRuntimeContext | undefined;
|
|
40
|
+
export declare function getRequestScopedDatabase(): QueryableDatabase | undefined;
|
|
41
|
+
export declare function withSessionPermissionContext<T>(options: SessionPermissionRuntimeOptions, fn: (context: SessionPermissionRuntimeContext) => Promise<T>): Promise<T>;
|
|
42
|
+
export {};
|
|
43
|
+
//# sourceMappingURL=SessionPermissionContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SessionPermissionContext.d.ts","sourceRoot":"","sources":["../../src/services/SessionPermissionContext.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EACL,KAAK,cAAc,EACnB,cAAc,EAEf,MAAM,qBAAqB,CAAC;AAE7B,UAAU,iBAAkB,SAAQ,iBAAiB;IACnD,gBAAgB,CAAC,EAAE,MAAM,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACtD,GAAG,EAAE,MAAM,CAAC;CACb;AAED,UAAU,mBAAoB,SAAQ,iBAAiB;IACrD,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,QAAQ,EAAE,MAAM,OAAO,CAAC;IACxB,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,UAAU,EAAE,cAAc,CAAC,YAAY,CAAC,CAAC;IACzC,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;IAC/B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,cAAc,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;IACpC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,+BAAgC,SAAQ,gBAAgB;IACvE,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,iCAAiC,EACjC,CAAC,MAAM,+BAA+B,GAAG,SAAS,CAAC,GACnD,SAAS,CAAC;IAEd,IAAI,8BAA8B,EAC9B,CAAC,MAAM,iBAAiB,GAAG,SAAS,CAAC,GACrC,SAAS,CAAC;CACf;AAKD,wBAAgB,kCAAkC,IAC9C,+BAA+B,GAC/B,SAAS,CAEZ;AAED,wBAAgB,wBAAwB,IAAI,iBAAiB,GAAG,SAAS,CAExE;AA2GD,wBAAsB,4BAA4B,CAAC,CAAC,EAClD,OAAO,EAAE,+BAA+B,EACxC,EAAE,EAAE,CAAC,OAAO,EAAE,+BAA+B,KAAK,OAAO,CAAC,CAAC,CAAC,GAC3D,OAAO,CAAC,CAAC,CAAC,CAoFZ"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { SmrtClassOptions } from '@happyvertical/smrt-core';
|
|
2
|
+
import { CreateSessionOptions } from '../collections/SessionCollection.js';
|
|
3
|
+
import { Membership } from '../models/Membership.js';
|
|
4
|
+
import { User } from '../models/User.js';
|
|
5
|
+
/**
|
|
6
|
+
* Session context with user and permissions
|
|
7
|
+
*/
|
|
8
|
+
export interface SessionContext {
|
|
9
|
+
/** The User record */
|
|
10
|
+
user: User;
|
|
11
|
+
/** Active membership for the current tenant, if one was resolved */
|
|
12
|
+
membership?: Membership | null;
|
|
13
|
+
/** Resolved permission slugs */
|
|
14
|
+
permissions: string[];
|
|
15
|
+
/** Tenant ID from session (if any) */
|
|
16
|
+
tenantId: string | null;
|
|
17
|
+
/** Session ID */
|
|
18
|
+
sessionId: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Options for SessionService
|
|
22
|
+
*/
|
|
23
|
+
export interface SessionServiceOptions extends SmrtClassOptions {
|
|
24
|
+
/** Default session TTL in seconds (default: 7 days) */
|
|
25
|
+
defaultTTL?: number;
|
|
26
|
+
/** Cookie name (default: 'sid') */
|
|
27
|
+
cookieName?: string;
|
|
28
|
+
/** Whether to auto-extend sessions on access (default: false) */
|
|
29
|
+
autoExtend?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* SessionService provides high-level session management that combines
|
|
33
|
+
* session storage with user and permission loading.
|
|
34
|
+
*
|
|
35
|
+
* This is the main service to use for session-based authentication.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* const sessionService = await SessionService.create({
|
|
40
|
+
* db: { type: 'sqlite', url: 'app.db' },
|
|
41
|
+
* defaultTTL: 7 * 24 * 60 * 60, // 7 days
|
|
42
|
+
* });
|
|
43
|
+
*
|
|
44
|
+
* // Create session after login
|
|
45
|
+
* const sessionId = await sessionService.createSession(userId, tenantId);
|
|
46
|
+
*
|
|
47
|
+
* // Load session context on each request
|
|
48
|
+
* const context = await sessionService.loadSessionContext(sessionId);
|
|
49
|
+
* if (context) {
|
|
50
|
+
* console.log('User:', context.user.email);
|
|
51
|
+
* console.log('Permissions:', context.permissions);
|
|
52
|
+
* }
|
|
53
|
+
*
|
|
54
|
+
* // Destroy session on logout
|
|
55
|
+
* await sessionService.destroySession(sessionId);
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export declare class SessionService {
|
|
59
|
+
private options;
|
|
60
|
+
private sessionCollection;
|
|
61
|
+
private userCollection;
|
|
62
|
+
private membershipCollection;
|
|
63
|
+
private permissionResolver;
|
|
64
|
+
private defaultTTL;
|
|
65
|
+
private autoExtend;
|
|
66
|
+
constructor(options: SessionServiceOptions);
|
|
67
|
+
/**
|
|
68
|
+
* Initialize collections
|
|
69
|
+
*/
|
|
70
|
+
initialize(): Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* Create a new session for a user
|
|
73
|
+
*
|
|
74
|
+
* @param userId - The user ID
|
|
75
|
+
* @param tenantId - Optional tenant context
|
|
76
|
+
* @param options - Additional session options
|
|
77
|
+
* @returns The session ID
|
|
78
|
+
*/
|
|
79
|
+
createSession(userId: string, tenantId?: string, options?: Partial<CreateSessionOptions>): Promise<string>;
|
|
80
|
+
/**
|
|
81
|
+
* Load full session context (user + permissions)
|
|
82
|
+
*
|
|
83
|
+
* Returns null if session is invalid or user doesn't exist
|
|
84
|
+
*/
|
|
85
|
+
loadSessionContext(sessionId: string): Promise<SessionContext | null>;
|
|
86
|
+
/**
|
|
87
|
+
* Get the initialized database connection backing this session service.
|
|
88
|
+
*/
|
|
89
|
+
getDatabase(): import('@happyvertical/sql').DatabaseInterface;
|
|
90
|
+
/**
|
|
91
|
+
* Refresh session (extend expiry, update lastAccessed)
|
|
92
|
+
*/
|
|
93
|
+
refreshSession(sessionId: string): Promise<boolean>;
|
|
94
|
+
/**
|
|
95
|
+
* Destroy a session (revoke it)
|
|
96
|
+
*/
|
|
97
|
+
destroySession(sessionId: string): Promise<boolean>;
|
|
98
|
+
/**
|
|
99
|
+
* Destroy all sessions for a user (logout from all devices)
|
|
100
|
+
*/
|
|
101
|
+
destroyAllUserSessions(userId: string): Promise<number>;
|
|
102
|
+
/**
|
|
103
|
+
* Switch tenant context for a session.
|
|
104
|
+
*
|
|
105
|
+
* A session's `tenantId` is the tenant-isolation key for every `@TenantScoped`
|
|
106
|
+
* query, so it must never be set to a tenant the session's user is not an
|
|
107
|
+
* active member of — otherwise a caller could read/write another tenant's data
|
|
108
|
+
* by feeding an arbitrary id here (e.g. straight from untrusted form data).
|
|
109
|
+
* Fail-closed (#1400): returns `false` without switching when the session is
|
|
110
|
+
* unknown or the user has no active membership in the target tenant. Passing
|
|
111
|
+
* `null` clears the tenant context and is always allowed.
|
|
112
|
+
*/
|
|
113
|
+
switchTenant(sessionId: string, tenantId: string | null): Promise<boolean>;
|
|
114
|
+
/**
|
|
115
|
+
* Get all active sessions for a user (for "manage sessions" UI)
|
|
116
|
+
*/
|
|
117
|
+
getUserSessions(userId: string): Promise<import('../index.js').Session[]>;
|
|
118
|
+
/**
|
|
119
|
+
* Clean up expired sessions (run periodically)
|
|
120
|
+
*/
|
|
121
|
+
cleanupExpiredSessions(): Promise<number>;
|
|
122
|
+
/**
|
|
123
|
+
* Check if a permission is granted for the session
|
|
124
|
+
*/
|
|
125
|
+
hasPermission(sessionId: string, permission: string): Promise<boolean>;
|
|
126
|
+
/**
|
|
127
|
+
* Get session data
|
|
128
|
+
*/
|
|
129
|
+
getSessionData<T>(sessionId: string, key: string): Promise<T | undefined>;
|
|
130
|
+
/**
|
|
131
|
+
* Set session data
|
|
132
|
+
*/
|
|
133
|
+
setSessionData(sessionId: string, key: string, value: unknown): Promise<boolean>;
|
|
134
|
+
/**
|
|
135
|
+
* Static factory method
|
|
136
|
+
*/
|
|
137
|
+
static create(options: SessionServiceOptions): Promise<SessionService>;
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=SessionService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SessionService.d.ts","sourceRoot":"","sources":["../../src/services/SessionService.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEjE,OAAO,EACL,KAAK,oBAAoB,EAE1B,MAAM,qCAAqC,CAAC;AAE7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAE1D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAG9C;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,sBAAsB;IACtB,IAAI,EAAE,IAAI,CAAC;IACX,oEAAoE;IACpE,UAAU,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC/B,gCAAgC;IAChC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,sCAAsC;IACtC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,iBAAiB;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAsB,SAAQ,gBAAgB;IAC7D,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mCAAmC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,iBAAiB,CAAqB;IAC9C,OAAO,CAAC,cAAc,CAAkB;IACxC,OAAO,CAAC,oBAAoB,CAAwB;IACpD,OAAO,CAAC,kBAAkB,CAAsB;IAChD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,UAAU,CAAU;gBAEhB,OAAO,EAAE,qBAAqB;IAM1C;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAWjC;;;;;;;OAOG;IACG,aAAa,CACjB,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,GACtC,OAAO,CAAC,MAAM,CAAC;IAalB;;;;OAIG;IACG,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IA6C3E;;OAEG;IACH,WAAW;IAIX;;OAEG;IACG,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIzD;;OAEG;IACG,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIzD;;OAEG;IACG,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAI7D;;;;;;;;;;OAUG;IACG,YAAY,CAChB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GAAG,IAAI,GACtB,OAAO,CAAC,OAAO,CAAC;IAiBnB;;OAEG;IACG,eAAe,CAAC,MAAM,EAAE,MAAM;IAIpC;;OAEG;IACG,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC;IAI/C;;OAEG;IACG,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAM5E;;OAEG;IACG,cAAc,CAAC,CAAC,EACpB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAIzB;;OAEG;IACG,cAAc,CAClB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,OAAO,CAAC;IAInB;;OAEG;WACU,MAAM,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,cAAc,CAAC;CAK7E"}
|