@tenxyte/core 0.1.5 → 0.9.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.
@@ -1,160 +1,207 @@
1
- import { TenxyteHttpClient } from '../http/client';
2
- import { decodeJwt, DecodedTenxyteToken } from '../utils/jwt';
3
-
4
- export interface Role {
5
- id: string;
6
- name: string;
7
- description?: string;
8
- is_default?: boolean;
9
- permissions?: string[];
10
- }
11
-
12
- export interface Permission {
13
- id: string;
14
- code: string;
15
- name: string;
16
- description?: string;
17
- }
18
-
19
- export class RbacModule {
20
- private cachedToken: string | null = null;
21
-
22
- constructor(private client: TenxyteHttpClient) { }
23
-
24
- /**
25
- * Cache a token to use for parameter-less synchronous checks.
26
- */
27
- setToken(token: string | null) {
28
- this.cachedToken = token;
29
- }
30
-
31
- private getDecodedToken(token?: string): DecodedTenxyteToken | null {
32
- const t = token || this.cachedToken;
33
- if (!t) return null;
34
- return decodeJwt(t);
35
- }
36
-
37
- // --- Synchronous Checks --- //
38
-
39
- hasRole(role: string, token?: string): boolean {
40
- const decoded = this.getDecodedToken(token);
41
- if (!decoded?.roles) return false;
42
- return decoded.roles.includes(role);
43
- }
44
-
45
- hasAnyRole(roles: string[], token?: string): boolean {
46
- const decoded = this.getDecodedToken(token);
47
- if (!decoded?.roles) return false;
48
- return roles.some(r => decoded.roles!.includes(r));
49
- }
50
-
51
- hasAllRoles(roles: string[], token?: string): boolean {
52
- const decoded = this.getDecodedToken(token);
53
- if (!decoded?.roles) return false;
54
- return roles.every(r => decoded.roles!.includes(r));
55
- }
56
-
57
- hasPermission(permission: string, token?: string): boolean {
58
- const decoded = this.getDecodedToken(token);
59
- if (!decoded?.permissions) return false;
60
- // Check exact match or wildcard, assuming backend handles wildcard expansion in JWT
61
- return decoded.permissions.includes(permission);
62
- }
63
-
64
- hasAnyPermission(permissions: string[], token?: string): boolean {
65
- const decoded = this.getDecodedToken(token);
66
- if (!decoded?.permissions) return false;
67
- return permissions.some(p => decoded.permissions!.includes(p));
68
- }
69
-
70
- hasAllPermissions(permissions: string[], token?: string): boolean {
71
- const decoded = this.getDecodedToken(token);
72
- if (!decoded?.permissions) return false;
73
- return permissions.every(p => decoded.permissions!.includes(p));
74
- }
75
-
76
- // --- Roles CRUD --- //
77
-
78
- async listRoles(): Promise<Role[]> {
79
- return this.client.get<Role[]>('/api/v1/auth/roles/');
80
- }
81
-
82
- async createRole(data: { name: string; description?: string; permission_codes?: string[]; is_default?: boolean }): Promise<Role> {
83
- return this.client.post<Role>('/api/v1/auth/roles/', data);
84
- }
85
-
86
- async getRole(roleId: string): Promise<Role> {
87
- return this.client.get<Role>(`/api/v1/auth/roles/${roleId}/`);
88
- }
89
-
90
- async updateRole(roleId: string, data: { name?: string; description?: string; permission_codes?: string[]; is_default?: boolean }): Promise<Role> {
91
- return this.client.put<Role>(`/api/v1/auth/roles/${roleId}/`, data);
92
- }
93
-
94
- async deleteRole(roleId: string): Promise<void> {
95
- return this.client.delete<void>(`/api/v1/auth/roles/${roleId}/`);
96
- }
97
-
98
- // --- Role Permissions Management --- //
99
-
100
- async getRolePermissions(roleId: string): Promise<Permission[]> {
101
- return this.client.get<Permission[]>(`/api/v1/auth/roles/${roleId}/permissions/`);
102
- }
103
-
104
- async addPermissionsToRole(roleId: string, permission_codes: string[]): Promise<void> {
105
- return this.client.post<void>(`/api/v1/auth/roles/${roleId}/permissions/`, { permission_codes });
106
- }
107
-
108
- async removePermissionsFromRole(roleId: string, permission_codes: string[]): Promise<void> {
109
- return this.client.delete<void>(`/api/v1/auth/roles/${roleId}/permissions/`, {
110
- // Note: DELETE request with body is supported via our fetch wrapper if enabled,
111
- // or we might need to rely on query strings. The schema specifies body or query.
112
- // Let's pass it in body via a custom config or URL params.
113
- body: { permission_codes }
114
- } as any);
115
- }
116
-
117
- // --- Permissions CRUD --- //
118
-
119
- async listPermissions(): Promise<Permission[]> {
120
- return this.client.get<Permission[]>('/api/v1/auth/permissions/');
121
- }
122
-
123
- async createPermission(data: { code: string; name: string; description?: string; parent_code?: string }): Promise<Permission> {
124
- return this.client.post<Permission>('/api/v1/auth/permissions/', data);
125
- }
126
-
127
- async getPermission(permissionId: string): Promise<Permission> {
128
- return this.client.get<Permission>(`/api/v1/auth/permissions/${permissionId}/`);
129
- }
130
-
131
- async updatePermission(permissionId: string, data: { name?: string; description?: string }): Promise<Permission> {
132
- return this.client.put<Permission>(`/api/v1/auth/permissions/${permissionId}/`, data);
133
- }
134
-
135
- async deletePermission(permissionId: string): Promise<void> {
136
- return this.client.delete<void>(`/api/v1/auth/permissions/${permissionId}/`);
137
- }
138
-
139
- // --- Direct Assignment (Users) --- //
140
-
141
- async assignRoleToUser(userId: string, roleCode: string): Promise<void> {
142
- return this.client.post<void>(`/api/v1/auth/users/${userId}/roles/`, { role_code: roleCode });
143
- }
144
-
145
- async removeRoleFromUser(userId: string, roleCode: string): Promise<void> {
146
- return this.client.delete<void>(`/api/v1/auth/users/${userId}/roles/`, {
147
- params: { role_code: roleCode }
148
- });
149
- }
150
-
151
- async assignPermissionsToUser(userId: string, permissionCodes: string[]): Promise<void> {
152
- return this.client.post<void>(`/api/v1/auth/users/${userId}/permissions/`, { permission_codes: permissionCodes });
153
- }
154
-
155
- async removePermissionsFromUser(userId: string, permissionCodes: string[]): Promise<void> {
156
- return this.client.delete<void>(`/api/v1/auth/users/${userId}/permissions/`, {
157
- body: { permission_codes: permissionCodes }
158
- } as any);
159
- }
160
- }
1
+ import { TenxyteHttpClient } from '../http/client';
2
+ import { decodeJwt, DecodedTenxyteToken } from '../utils/jwt';
3
+
4
+ export interface Role {
5
+ id: string;
6
+ name: string;
7
+ description?: string;
8
+ is_default?: boolean;
9
+ permissions?: string[];
10
+ }
11
+
12
+ export interface Permission {
13
+ id: string;
14
+ code: string;
15
+ name: string;
16
+ description?: string;
17
+ }
18
+
19
+ export class RbacModule {
20
+ private cachedToken: string | null = null;
21
+
22
+ constructor(private client: TenxyteHttpClient) { }
23
+
24
+ /**
25
+ * Cache a decoded JWT payload locally to perform parameter-less synchronous permission checks.
26
+ * Usually invoked automatically by the system upon login or token refresh.
27
+ * @param token - The raw JWT access token encoded string.
28
+ */
29
+ setToken(token: string | null) {
30
+ this.cachedToken = token;
31
+ }
32
+
33
+ private getDecodedToken(token?: string): DecodedTenxyteToken | null {
34
+ const t = token || this.cachedToken;
35
+ if (!t) return null;
36
+ return decodeJwt(t);
37
+ }
38
+
39
+ // --- Synchronous Checks --- //
40
+
41
+ /**
42
+ * Synchronously deeply inspects the cached (or provided) JWT to determine if the user has a specific Role.
43
+ * @param role - The exact code name of the Role.
44
+ * @param token - (Optional) Provide a specific token overriding the cached one.
45
+ */
46
+ hasRole(role: string, token?: string): boolean {
47
+ const decoded = this.getDecodedToken(token);
48
+ if (!decoded?.roles) return false;
49
+ return decoded.roles.includes(role);
50
+ }
51
+
52
+ /**
53
+ * Evaluates if the active session holds AT LEAST ONE of the listed Roles.
54
+ * @param roles - An array of Role codes.
55
+ */
56
+ hasAnyRole(roles: string[], token?: string): boolean {
57
+ const decoded = this.getDecodedToken(token);
58
+ if (!decoded?.roles) return false;
59
+ return roles.some(r => decoded.roles!.includes(r));
60
+ }
61
+
62
+ /**
63
+ * Evaluates if the active session holds ALL of the listed Roles concurrently.
64
+ * @param roles - An array of Role codes.
65
+ */
66
+ hasAllRoles(roles: string[], token?: string): boolean {
67
+ const decoded = this.getDecodedToken(token);
68
+ if (!decoded?.roles) return false;
69
+ return roles.every(r => decoded.roles!.includes(r));
70
+ }
71
+
72
+ /**
73
+ * Synchronously deeply inspects the cached (or provided) JWT to determine if the user has a specific granular Permission.
74
+ * @param permission - The exact code name of the Permission (e.g., 'invoices.read').
75
+ */
76
+ hasPermission(permission: string, token?: string): boolean {
77
+ const decoded = this.getDecodedToken(token);
78
+ if (!decoded?.permissions) return false;
79
+ return decoded.permissions.includes(permission);
80
+ }
81
+
82
+ /**
83
+ * Evaluates if the active session holds AT LEAST ONE of the listed Permissions.
84
+ */
85
+ hasAnyPermission(permissions: string[], token?: string): boolean {
86
+ const decoded = this.getDecodedToken(token);
87
+ if (!decoded?.permissions) return false;
88
+ return permissions.some(p => decoded.permissions!.includes(p));
89
+ }
90
+
91
+ /**
92
+ * Evaluates if the active session holds ALL of the listed Permissions concurrently.
93
+ */
94
+ hasAllPermissions(permissions: string[], token?: string): boolean {
95
+ const decoded = this.getDecodedToken(token);
96
+ if (!decoded?.permissions) return false;
97
+ return permissions.every(p => decoded.permissions!.includes(p));
98
+ }
99
+
100
+ // --- Roles CRUD --- //
101
+
102
+ /** Fetch all application global Roles structure */
103
+ async listRoles(): Promise<Role[]> {
104
+ return this.client.get<Role[]>('/api/v1/auth/roles/');
105
+ }
106
+
107
+ /** Create a new architectural Role inside Tenxyte */
108
+ async createRole(data: { name: string; description?: string; permission_codes?: string[]; is_default?: boolean }): Promise<Role> {
109
+ return this.client.post<Role>('/api/v1/auth/roles/', data);
110
+ }
111
+
112
+ /** Get detailed metadata defining a single bounded Role */
113
+ async getRole(roleId: string): Promise<Role> {
114
+ return this.client.get<Role>(`/api/v1/auth/roles/${roleId}/`);
115
+ }
116
+
117
+ /** Modify properties bounding a Role */
118
+ async updateRole(roleId: string, data: { name?: string; description?: string; permission_codes?: string[]; is_default?: boolean }): Promise<Role> {
119
+ return this.client.put<Role>(`/api/v1/auth/roles/${roleId}/`, data);
120
+ }
121
+
122
+ /** Unbind and destruct a Role from the global Tenant. (Dangerous, implies cascading permission unbindings) */
123
+ async deleteRole(roleId: string): Promise<void> {
124
+ return this.client.delete<void>(`/api/v1/auth/roles/${roleId}/`);
125
+ }
126
+
127
+ // --- Role Permissions Management --- //
128
+
129
+ async getRolePermissions(roleId: string): Promise<Permission[]> {
130
+ return this.client.get<Permission[]>(`/api/v1/auth/roles/${roleId}/permissions/`);
131
+ }
132
+
133
+ async addPermissionsToRole(roleId: string, permission_codes: string[]): Promise<void> {
134
+ return this.client.post<void>(`/api/v1/auth/roles/${roleId}/permissions/`, { permission_codes });
135
+ }
136
+
137
+ async removePermissionsFromRole(roleId: string, permission_codes: string[]): Promise<void> {
138
+ return this.client.delete<void>(`/api/v1/auth/roles/${roleId}/permissions/`, {
139
+ // Note: DELETE request with body is supported via our fetch wrapper if enabled,
140
+ // or we might need to rely on query strings. The schema specifies body or query.
141
+ // Let's pass it in body via a custom config or URL params.
142
+ body: { permission_codes }
143
+ } as any);
144
+ }
145
+
146
+ // --- Permissions CRUD --- //
147
+
148
+ /** Enumerates all available fine-grained Permissions inside this Tenant scope. */
149
+ async listPermissions(): Promise<Permission[]> {
150
+ return this.client.get<Permission[]>('/api/v1/auth/permissions/');
151
+ }
152
+
153
+ /** Bootstraps a new granular Permission flag (e.g. `billing.refund`). */
154
+ async createPermission(data: { code: string; name: string; description?: string; parent_code?: string }): Promise<Permission> {
155
+ return this.client.post<Permission>('/api/v1/auth/permissions/', data);
156
+ }
157
+
158
+ /** Retrieves an existing atomic Permission construct. */
159
+ async getPermission(permissionId: string): Promise<Permission> {
160
+ return this.client.get<Permission>(`/api/v1/auth/permissions/${permissionId}/`);
161
+ }
162
+
163
+ /** Edits the human readable description or structural dependencies of a Permission. */
164
+ async updatePermission(permissionId: string, data: { name?: string; description?: string }): Promise<Permission> {
165
+ return this.client.put<Permission>(`/api/v1/auth/permissions/${permissionId}/`, data);
166
+ }
167
+
168
+ /** Destroys an atomic Permission permanently. Any Roles referencing it will be stripped of this grant automatically. */
169
+ async deletePermission(permissionId: string): Promise<void> {
170
+ return this.client.delete<void>(`/api/v1/auth/permissions/${permissionId}/`);
171
+ }
172
+
173
+ // --- Direct Assignment (Users) --- //
174
+
175
+ /**
176
+ * Attach a given Role globally to a user entity.
177
+ * Use sparingly if B2B multi-tenancy contexts are preferred.
178
+ */
179
+ async assignRoleToUser(userId: string, roleCode: string): Promise<void> {
180
+ return this.client.post<void>(`/api/v1/auth/users/${userId}/roles/`, { role_code: roleCode });
181
+ }
182
+
183
+ /**
184
+ * Unbind a global Role from a user entity.
185
+ */
186
+ async removeRoleFromUser(userId: string, roleCode: string): Promise<void> {
187
+ return this.client.delete<void>(`/api/v1/auth/users/${userId}/roles/`, {
188
+ params: { role_code: roleCode }
189
+ });
190
+ }
191
+
192
+ /**
193
+ * Ad-Hoc directly attach specific granular Permissions to a single User, bypassing Role boundaries.
194
+ */
195
+ async assignPermissionsToUser(userId: string, permissionCodes: string[]): Promise<void> {
196
+ return this.client.post<void>(`/api/v1/auth/users/${userId}/permissions/`, { permission_codes: permissionCodes });
197
+ }
198
+
199
+ /**
200
+ * Ad-Hoc strip direct granular Permissions bindings from a specific User.
201
+ */
202
+ async removePermissionsFromUser(userId: string, permissionCodes: string[]): Promise<void> {
203
+ return this.client.delete<void>(`/api/v1/auth/users/${userId}/permissions/`, {
204
+ body: { permission_codes: permissionCodes }
205
+ } as any);
206
+ }
207
+ }