@objectstack/plugin-security 7.3.0 → 7.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3,6 +3,9 @@ var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __esm = (fn, res) => function __init() {
7
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
+ };
6
9
  var __export = (target, all) => {
7
10
  for (var name in all)
8
11
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -17,6 +20,884 @@ var __copyProps = (to, from, except, desc) => {
17
20
  };
18
21
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
22
 
23
+ // src/translations/en.objects.generated.ts
24
+ var enObjects;
25
+ var init_en_objects_generated = __esm({
26
+ "src/translations/en.objects.generated.ts"() {
27
+ "use strict";
28
+ enObjects = {
29
+ sys_role: {
30
+ label: "Role",
31
+ pluralLabel: "Roles",
32
+ description: "Role definitions for RBAC access control",
33
+ fields: {
34
+ label: {
35
+ label: "Display Name"
36
+ },
37
+ name: {
38
+ label: "API Name",
39
+ help: "Unique machine name for the role (e.g. admin, editor, viewer)"
40
+ },
41
+ description: {
42
+ label: "Description"
43
+ },
44
+ permissions: {
45
+ label: "Permissions",
46
+ help: "JSON-serialized array of permission strings"
47
+ },
48
+ active: {
49
+ label: "Active"
50
+ },
51
+ is_default: {
52
+ label: "Default Role",
53
+ help: "Automatically assigned to new users"
54
+ },
55
+ id: {
56
+ label: "Role ID"
57
+ },
58
+ created_at: {
59
+ label: "Created At"
60
+ },
61
+ updated_at: {
62
+ label: "Updated At"
63
+ }
64
+ },
65
+ _views: {
66
+ active: {
67
+ label: "Active"
68
+ },
69
+ default_roles: {
70
+ label: "Default"
71
+ },
72
+ custom: {
73
+ label: "Custom"
74
+ },
75
+ all_roles: {
76
+ label: "All"
77
+ }
78
+ },
79
+ _actions: {
80
+ activate_role: {
81
+ label: "Activate Role",
82
+ successMessage: "Role activated"
83
+ },
84
+ deactivate_role: {
85
+ label: "Deactivate Role",
86
+ confirmText: "Deactivate this role? Users with the role keep their assignment but the role stops granting permissions until re-activated.",
87
+ successMessage: "Role deactivated"
88
+ },
89
+ set_default_role: {
90
+ label: "Set as Default",
91
+ confirmText: "Make this the default role for new users? Existing users are unaffected.",
92
+ successMessage: "Default role updated"
93
+ },
94
+ clone_role: {
95
+ label: "Clone Role",
96
+ successMessage: "Role cloned"
97
+ }
98
+ }
99
+ },
100
+ sys_permission_set: {
101
+ label: "Permission Set",
102
+ pluralLabel: "Permission Sets",
103
+ description: "Named permission groupings for fine-grained access control",
104
+ fields: {
105
+ label: {
106
+ label: "Display Name"
107
+ },
108
+ name: {
109
+ label: "API Name",
110
+ help: "Unique machine name for the permission set"
111
+ },
112
+ description: {
113
+ label: "Description"
114
+ },
115
+ object_permissions: {
116
+ label: "Object Permissions",
117
+ help: "JSON-serialized object-level CRUD permissions"
118
+ },
119
+ field_permissions: {
120
+ label: "Field Permissions",
121
+ help: "JSON-serialized field-level read/write permissions"
122
+ },
123
+ system_permissions: {
124
+ label: "System Permissions",
125
+ help: 'JSON-serialized array of system capability names (e.g. ["setup.access","studio.access","manage_users"])'
126
+ },
127
+ row_level_security: {
128
+ label: "Row-Level Security",
129
+ help: "JSON-serialized array of row-level security policies (USING/CHECK clauses)"
130
+ },
131
+ tab_permissions: {
132
+ label: "Tab Permissions",
133
+ help: "JSON-serialized map of app tab visibility (visible | hidden | default_on | default_off)"
134
+ },
135
+ active: {
136
+ label: "Active"
137
+ },
138
+ id: {
139
+ label: "Permission Set ID"
140
+ },
141
+ created_at: {
142
+ label: "Created At"
143
+ },
144
+ updated_at: {
145
+ label: "Updated At"
146
+ }
147
+ },
148
+ _views: {
149
+ active: {
150
+ label: "Active"
151
+ },
152
+ inactive: {
153
+ label: "Inactive"
154
+ },
155
+ all_permsets: {
156
+ label: "All"
157
+ }
158
+ },
159
+ _actions: {
160
+ activate_permission_set: {
161
+ label: "Activate",
162
+ successMessage: "Permission set activated"
163
+ },
164
+ deactivate_permission_set: {
165
+ label: "Deactivate",
166
+ confirmText: "Deactivate this permission set? Existing assignments stay in place but stop granting access until re-activated.",
167
+ successMessage: "Permission set deactivated"
168
+ },
169
+ clone_permission_set: {
170
+ label: "Clone",
171
+ successMessage: "Permission set cloned"
172
+ }
173
+ }
174
+ },
175
+ sys_user_permission_set: {
176
+ label: "User Permission Set",
177
+ pluralLabel: "User Permission Sets",
178
+ description: "Direct assignment of a permission set to a user (optionally scoped to an organization).",
179
+ fields: {
180
+ id: {
181
+ label: "Assignment ID",
182
+ help: "UUID of the assignment."
183
+ },
184
+ user_id: {
185
+ label: "User",
186
+ help: "Foreign key to sys_user."
187
+ },
188
+ permission_set_id: {
189
+ label: "Permission Set",
190
+ help: "Foreign key to sys_permission_set."
191
+ },
192
+ organization_id: {
193
+ label: "Organization",
194
+ help: "Optional organization scope. NULL = applies in every org context."
195
+ },
196
+ granted_by: {
197
+ label: "Granted By",
198
+ help: "User who granted this permission set."
199
+ },
200
+ created_at: {
201
+ label: "Created At"
202
+ },
203
+ updated_at: {
204
+ label: "Updated At"
205
+ }
206
+ }
207
+ },
208
+ sys_role_permission_set: {
209
+ label: "Role Permission Set",
210
+ pluralLabel: "Role Permission Sets",
211
+ description: "Binds a permission set to a role.",
212
+ fields: {
213
+ id: {
214
+ label: "Binding ID",
215
+ help: "UUID of the role-permission-set binding."
216
+ },
217
+ role_id: {
218
+ label: "Role",
219
+ help: "Foreign key to sys_role."
220
+ },
221
+ permission_set_id: {
222
+ label: "Permission Set",
223
+ help: "Foreign key to sys_permission_set."
224
+ },
225
+ created_at: {
226
+ label: "Created At"
227
+ },
228
+ updated_at: {
229
+ label: "Updated At"
230
+ }
231
+ }
232
+ }
233
+ };
234
+ }
235
+ });
236
+
237
+ // src/translations/zh-CN.objects.generated.ts
238
+ var zhCNObjects;
239
+ var init_zh_CN_objects_generated = __esm({
240
+ "src/translations/zh-CN.objects.generated.ts"() {
241
+ "use strict";
242
+ zhCNObjects = {
243
+ sys_role: {
244
+ label: "\u89D2\u8272",
245
+ pluralLabel: "\u89D2\u8272",
246
+ description: "\u7528\u4E8E RBAC \u8BBF\u95EE\u63A7\u5236\u7684\u89D2\u8272\u5B9A\u4E49",
247
+ fields: {
248
+ label: {
249
+ label: "\u663E\u793A\u540D\u79F0"
250
+ },
251
+ name: {
252
+ label: "API \u540D\u79F0",
253
+ help: "\u89D2\u8272\u7684\u552F\u4E00\u673A\u5668\u540D\u79F0\uFF08\u4F8B\u5982 admin\u3001editor\u3001viewer\uFF09"
254
+ },
255
+ description: {
256
+ label: "\u63CF\u8FF0"
257
+ },
258
+ permissions: {
259
+ label: "\u6743\u9650",
260
+ help: "\u6743\u9650\u5B57\u7B26\u4E32\u6570\u7EC4\u7684 JSON \u5E8F\u5217\u5316\u5185\u5BB9"
261
+ },
262
+ active: {
263
+ label: "\u542F\u7528"
264
+ },
265
+ is_default: {
266
+ label: "\u9ED8\u8BA4\u89D2\u8272",
267
+ help: "\u81EA\u52A8\u5206\u914D\u7ED9\u65B0\u7528\u6237"
268
+ },
269
+ id: {
270
+ label: "\u89D2\u8272 ID"
271
+ },
272
+ created_at: {
273
+ label: "\u521B\u5EFA\u65F6\u95F4"
274
+ },
275
+ updated_at: {
276
+ label: "\u66F4\u65B0\u65F6\u95F4"
277
+ }
278
+ },
279
+ _views: {
280
+ active: {
281
+ label: "\u542F\u7528"
282
+ },
283
+ default_roles: {
284
+ label: "\u9ED8\u8BA4"
285
+ },
286
+ custom: {
287
+ label: "\u81EA\u5B9A\u4E49"
288
+ },
289
+ all_roles: {
290
+ label: "\u5168\u90E8"
291
+ }
292
+ },
293
+ _actions: {
294
+ activate_role: {
295
+ label: "\u6FC0\u6D3B\u89D2\u8272",
296
+ successMessage: "\u89D2\u8272\u5DF2\u6FC0\u6D3B"
297
+ },
298
+ deactivate_role: {
299
+ label: "\u505C\u7528\u89D2\u8272",
300
+ confirmText: "\u786E\u5B9A\u8981\u505C\u7528\u6B64\u89D2\u8272\u5417\uFF1F\u62E5\u6709\u8BE5\u89D2\u8272\u7684\u7528\u6237\u4ECD\u4FDD\u7559\u5176\u5206\u914D\uFF0C\u4F46\u5728\u91CD\u65B0\u6FC0\u6D3B\u4E4B\u524D\u8BE5\u89D2\u8272\u5C06\u4E0D\u518D\u6388\u4E88\u6743\u9650\u3002",
301
+ successMessage: "\u89D2\u8272\u5DF2\u505C\u7528"
302
+ },
303
+ set_default_role: {
304
+ label: "\u8BBE\u4E3A\u9ED8\u8BA4",
305
+ confirmText: "\u5C06\u6B64\u89D2\u8272\u8BBE\u4E3A\u65B0\u7528\u6237\u7684\u9ED8\u8BA4\u89D2\u8272\u5417\uFF1F\u73B0\u6709\u7528\u6237\u4E0D\u53D7\u5F71\u54CD\u3002",
306
+ successMessage: "\u5DF2\u66F4\u65B0\u9ED8\u8BA4\u89D2\u8272"
307
+ },
308
+ clone_role: {
309
+ label: "\u514B\u9686\u89D2\u8272",
310
+ successMessage: "\u5DF2\u514B\u9686\u89D2\u8272"
311
+ }
312
+ }
313
+ },
314
+ sys_permission_set: {
315
+ label: "\u6743\u9650\u96C6",
316
+ pluralLabel: "\u6743\u9650\u96C6",
317
+ description: "\u7528\u4E8E\u7CBE\u7EC6\u5316\u8BBF\u95EE\u63A7\u5236\u7684\u547D\u540D\u6743\u9650\u5206\u7EC4",
318
+ fields: {
319
+ label: {
320
+ label: "\u663E\u793A\u540D\u79F0"
321
+ },
322
+ name: {
323
+ label: "API \u540D\u79F0",
324
+ help: "\u6743\u9650\u96C6\u7684\u552F\u4E00\u673A\u5668\u540D\u79F0"
325
+ },
326
+ description: {
327
+ label: "\u63CF\u8FF0"
328
+ },
329
+ object_permissions: {
330
+ label: "\u5BF9\u8C61\u6743\u9650",
331
+ help: "\u5BF9\u8C61\u7EA7 CRUD \u6743\u9650\u7684 JSON \u5E8F\u5217\u5316\u5185\u5BB9"
332
+ },
333
+ field_permissions: {
334
+ label: "\u5B57\u6BB5\u6743\u9650",
335
+ help: "\u5B57\u6BB5\u7EA7\u8BFB\u5199\u6743\u9650\u7684 JSON \u5E8F\u5217\u5316\u5185\u5BB9"
336
+ },
337
+ system_permissions: {
338
+ label: "\u7CFB\u7EDF\u6743\u9650",
339
+ help: '\u7CFB\u7EDF\u80FD\u529B\u540D\u79F0\u7684 JSON \u5E8F\u5217\u5316\u6570\u7EC4\uFF08\u4F8B\u5982 ["setup.access","studio.access","manage_users"]\uFF09'
340
+ },
341
+ row_level_security: {
342
+ label: "\u884C\u7EA7\u5B89\u5168",
343
+ help: "\u884C\u7EA7\u5B89\u5168\u7B56\u7565\u7684 JSON \u5E8F\u5217\u5316\u6570\u7EC4\uFF08USING/CHECK \u5B50\u53E5\uFF09"
344
+ },
345
+ tab_permissions: {
346
+ label: "\u6807\u7B7E\u9875\u6743\u9650",
347
+ help: "\u5E94\u7528\u6807\u7B7E\u9875\u53EF\u89C1\u6027\u7684 JSON \u5E8F\u5217\u5316\u6620\u5C04\uFF08visible | hidden | default_on | default_off\uFF09"
348
+ },
349
+ active: {
350
+ label: "\u542F\u7528"
351
+ },
352
+ id: {
353
+ label: "\u6743\u9650\u96C6 ID"
354
+ },
355
+ created_at: {
356
+ label: "\u521B\u5EFA\u65F6\u95F4"
357
+ },
358
+ updated_at: {
359
+ label: "\u66F4\u65B0\u65F6\u95F4"
360
+ }
361
+ },
362
+ _views: {
363
+ active: {
364
+ label: "\u542F\u7528"
365
+ },
366
+ inactive: {
367
+ label: "\u505C\u7528"
368
+ },
369
+ all_permsets: {
370
+ label: "\u5168\u90E8"
371
+ }
372
+ },
373
+ _actions: {
374
+ activate_permission_set: {
375
+ label: "\u6FC0\u6D3B",
376
+ successMessage: "\u6743\u9650\u96C6\u5DF2\u6FC0\u6D3B"
377
+ },
378
+ deactivate_permission_set: {
379
+ label: "\u505C\u7528",
380
+ confirmText: "\u786E\u5B9A\u8981\u505C\u7528\u6B64\u6743\u9650\u96C6\u5417\uFF1F\u73B0\u6709\u5206\u914D\u4ECD\u5C06\u4FDD\u7559\uFF0C\u4F46\u5728\u91CD\u65B0\u6FC0\u6D3B\u4E4B\u524D\u5C06\u4E0D\u518D\u6388\u4E88\u8BBF\u95EE\u6743\u9650\u3002",
381
+ successMessage: "\u6743\u9650\u96C6\u5DF2\u505C\u7528"
382
+ },
383
+ clone_permission_set: {
384
+ label: "\u514B\u9686",
385
+ successMessage: "\u5DF2\u514B\u9686\u6743\u9650\u96C6"
386
+ }
387
+ }
388
+ },
389
+ sys_user_permission_set: {
390
+ label: "\u7528\u6237\u6743\u9650\u96C6",
391
+ pluralLabel: "\u7528\u6237\u6743\u9650\u96C6",
392
+ description: "\u5C06\u6743\u9650\u96C6\u76F4\u63A5\u5206\u914D\u7ED9\u7528\u6237\uFF08\u53EF\u6309\u7EC4\u7EC7\u8303\u56F4\u9650\u5B9A\uFF09\u3002",
393
+ fields: {
394
+ id: {
395
+ label: "\u5206\u914D ID",
396
+ help: "\u8BE5\u5206\u914D\u8BB0\u5F55\u7684 UUID\u3002"
397
+ },
398
+ user_id: {
399
+ label: "\u7528\u6237",
400
+ help: "\u6307\u5411 sys_user \u7684\u5916\u952E\u3002"
401
+ },
402
+ permission_set_id: {
403
+ label: "\u6743\u9650\u96C6",
404
+ help: "\u6307\u5411 sys_permission_set \u7684\u5916\u952E\u3002"
405
+ },
406
+ organization_id: {
407
+ label: "\u7EC4\u7EC7",
408
+ help: "\u53EF\u9009\u7684\u7EC4\u7EC7\u8303\u56F4\u3002NULL = \u5728\u6240\u6709\u7EC4\u7EC7\u4E0A\u4E0B\u6587\u4E2D\u90FD\u751F\u6548\u3002"
409
+ },
410
+ granted_by: {
411
+ label: "\u6388\u6743\u4EBA",
412
+ help: "\u6388\u4E88\u8BE5\u6743\u9650\u96C6\u7684\u7528\u6237\u3002"
413
+ },
414
+ created_at: {
415
+ label: "\u521B\u5EFA\u65F6\u95F4"
416
+ },
417
+ updated_at: {
418
+ label: "\u66F4\u65B0\u65F6\u95F4"
419
+ }
420
+ }
421
+ },
422
+ sys_role_permission_set: {
423
+ label: "\u89D2\u8272\u6743\u9650\u96C6",
424
+ pluralLabel: "\u89D2\u8272\u6743\u9650\u96C6",
425
+ description: "\u5C06\u6743\u9650\u96C6\u7ED1\u5B9A\u5230\u89D2\u8272\u3002",
426
+ fields: {
427
+ id: {
428
+ label: "\u7ED1\u5B9A ID",
429
+ help: "\u89D2\u8272-\u6743\u9650\u96C6\u7ED1\u5B9A\u8BB0\u5F55\u7684 UUID\u3002"
430
+ },
431
+ role_id: {
432
+ label: "\u89D2\u8272",
433
+ help: "\u6307\u5411 sys_role \u7684\u5916\u952E\u3002"
434
+ },
435
+ permission_set_id: {
436
+ label: "\u6743\u9650\u96C6",
437
+ help: "\u6307\u5411 sys_permission_set \u7684\u5916\u952E\u3002"
438
+ },
439
+ created_at: {
440
+ label: "\u521B\u5EFA\u65F6\u95F4"
441
+ },
442
+ updated_at: {
443
+ label: "\u66F4\u65B0\u65F6\u95F4"
444
+ }
445
+ }
446
+ }
447
+ };
448
+ }
449
+ });
450
+
451
+ // src/translations/ja-JP.objects.generated.ts
452
+ var jaJPObjects;
453
+ var init_ja_JP_objects_generated = __esm({
454
+ "src/translations/ja-JP.objects.generated.ts"() {
455
+ "use strict";
456
+ jaJPObjects = {
457
+ sys_role: {
458
+ label: "\u30ED\u30FC\u30EB",
459
+ pluralLabel: "\u30ED\u30FC\u30EB",
460
+ description: "RBAC \u30A2\u30AF\u30BB\u30B9\u5236\u5FA1\u306E\u305F\u3081\u306E\u30ED\u30FC\u30EB\u5B9A\u7FA9",
461
+ fields: {
462
+ label: {
463
+ label: "\u8868\u793A\u540D"
464
+ },
465
+ name: {
466
+ label: "API \u540D",
467
+ help: "\u30ED\u30FC\u30EB\u306E\u4E00\u610F\u306E\u6A5F\u68B0\u540D\uFF08\u4F8B: admin\u3001editor\u3001viewer\uFF09"
468
+ },
469
+ description: {
470
+ label: "\u8AAC\u660E"
471
+ },
472
+ permissions: {
473
+ label: "\u6A29\u9650",
474
+ help: "\u6A29\u9650\u6587\u5B57\u5217\u306E JSON \u30B7\u30EA\u30A2\u30E9\u30A4\u30BA\u914D\u5217"
475
+ },
476
+ active: {
477
+ label: "\u6709\u52B9"
478
+ },
479
+ is_default: {
480
+ label: "\u30C7\u30D5\u30A9\u30EB\u30C8\u30ED\u30FC\u30EB",
481
+ help: "\u65B0\u898F\u30E6\u30FC\u30B6\u30FC\u306B\u81EA\u52D5\u7684\u306B\u5272\u308A\u5F53\u3066\u3089\u308C\u307E\u3059"
482
+ },
483
+ id: {
484
+ label: "\u30ED\u30FC\u30EB ID"
485
+ },
486
+ created_at: {
487
+ label: "\u4F5C\u6210\u65E5\u6642"
488
+ },
489
+ updated_at: {
490
+ label: "\u66F4\u65B0\u65E5\u6642"
491
+ }
492
+ },
493
+ _views: {
494
+ active: {
495
+ label: "\u6709\u52B9"
496
+ },
497
+ default_roles: {
498
+ label: "\u30C7\u30D5\u30A9\u30EB\u30C8"
499
+ },
500
+ custom: {
501
+ label: "\u30AB\u30B9\u30BF\u30E0"
502
+ },
503
+ all_roles: {
504
+ label: "\u3059\u3079\u3066"
505
+ }
506
+ },
507
+ _actions: {
508
+ activate_role: {
509
+ label: "\u30ED\u30FC\u30EB\u3092\u6709\u52B9\u5316",
510
+ successMessage: "\u30ED\u30FC\u30EB\u304C\u6709\u52B9\u5316\u3055\u308C\u307E\u3057\u305F"
511
+ },
512
+ deactivate_role: {
513
+ label: "\u30ED\u30FC\u30EB\u3092\u7121\u52B9\u5316",
514
+ confirmText: "\u3053\u306E\u30ED\u30FC\u30EB\u3092\u7121\u52B9\u5316\u3057\u307E\u3059\u304B\uFF1F\u3053\u306E\u30ED\u30FC\u30EB\u3092\u6301\u3064\u30E6\u30FC\u30B6\u30FC\u306E\u5272\u308A\u5F53\u3066\u306F\u7DAD\u6301\u3055\u308C\u307E\u3059\u304C\u3001\u518D\u5EA6\u6709\u52B9\u5316\u3059\u308B\u307E\u3067\u6A29\u9650\u306E\u4ED8\u4E0E\u306F\u505C\u6B62\u3055\u308C\u307E\u3059\u3002",
515
+ successMessage: "\u30ED\u30FC\u30EB\u304C\u7121\u52B9\u5316\u3055\u308C\u307E\u3057\u305F"
516
+ },
517
+ set_default_role: {
518
+ label: "\u30C7\u30D5\u30A9\u30EB\u30C8\u306B\u8A2D\u5B9A",
519
+ confirmText: "\u3053\u306E\u30ED\u30FC\u30EB\u3092\u65B0\u898F\u30E6\u30FC\u30B6\u30FC\u306E\u30C7\u30D5\u30A9\u30EB\u30C8\u30ED\u30FC\u30EB\u306B\u3057\u307E\u3059\u304B\uFF1F\u65E2\u5B58\u306E\u30E6\u30FC\u30B6\u30FC\u306B\u306F\u5F71\u97FF\u3057\u307E\u305B\u3093\u3002",
520
+ successMessage: "\u30C7\u30D5\u30A9\u30EB\u30C8\u30ED\u30FC\u30EB\u3092\u66F4\u65B0\u3057\u307E\u3057\u305F"
521
+ },
522
+ clone_role: {
523
+ label: "\u30ED\u30FC\u30EB\u3092\u8907\u88FD",
524
+ successMessage: "\u30ED\u30FC\u30EB\u3092\u8907\u88FD\u3057\u307E\u3057\u305F"
525
+ }
526
+ }
527
+ },
528
+ sys_permission_set: {
529
+ label: "\u6A29\u9650\u30BB\u30C3\u30C8",
530
+ pluralLabel: "\u6A29\u9650\u30BB\u30C3\u30C8",
531
+ description: "\u7D30\u304B\u3044\u30A2\u30AF\u30BB\u30B9\u5236\u5FA1\u306E\u305F\u3081\u306E\u6A29\u9650\u30B0\u30EB\u30FC\u30D7",
532
+ fields: {
533
+ label: {
534
+ label: "\u8868\u793A\u540D"
535
+ },
536
+ name: {
537
+ label: "API \u540D",
538
+ help: "\u6A29\u9650\u30BB\u30C3\u30C8\u306E\u4E00\u610F\u306E\u6A5F\u68B0\u540D"
539
+ },
540
+ description: {
541
+ label: "\u8AAC\u660E"
542
+ },
543
+ object_permissions: {
544
+ label: "\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u6A29\u9650",
545
+ help: "JSON \u30B7\u30EA\u30A2\u30E9\u30A4\u30BA\u3055\u308C\u305F\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u30EC\u30D9\u30EB\u306E CRUD \u6A29\u9650"
546
+ },
547
+ field_permissions: {
548
+ label: "\u30D5\u30A3\u30FC\u30EB\u30C9\u6A29\u9650",
549
+ help: "JSON \u30B7\u30EA\u30A2\u30E9\u30A4\u30BA\u3055\u308C\u305F\u30D5\u30A3\u30FC\u30EB\u30C9\u30EC\u30D9\u30EB\u306E\u8AAD\u307F\u53D6\u308A/\u66F8\u304D\u8FBC\u307F\u6A29\u9650"
550
+ },
551
+ system_permissions: {
552
+ label: "\u30B7\u30B9\u30C6\u30E0\u6A29\u9650",
553
+ help: '\u30B7\u30B9\u30C6\u30E0\u30B1\u30FC\u30D1\u30D3\u30EA\u30C6\u30A3\u540D\u306EJSON\u30B7\u30EA\u30A2\u30E9\u30A4\u30BA\u914D\u5217\uFF08\u4F8B: ["setup.access","studio.access","manage_users"]\uFF09'
554
+ },
555
+ row_level_security: {
556
+ label: "\u884C\u30EC\u30D9\u30EB\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3",
557
+ help: "\u884C\u30EC\u30D9\u30EB\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u30DD\u30EA\u30B7\u30FC\u306EJSON\u30B7\u30EA\u30A2\u30E9\u30A4\u30BA\u914D\u5217\uFF08USING/CHECK \u53E5\uFF09"
558
+ },
559
+ tab_permissions: {
560
+ label: "\u30BF\u30D6\u6A29\u9650",
561
+ help: "\u30A2\u30D7\u30EA\u306E\u30BF\u30D6\u8868\u793A\u306EJSON\u30B7\u30EA\u30A2\u30E9\u30A4\u30BA\u30DE\u30C3\u30D7\uFF08visible | hidden | default_on | default_off\uFF09"
562
+ },
563
+ active: {
564
+ label: "\u6709\u52B9"
565
+ },
566
+ id: {
567
+ label: "\u6A29\u9650\u30BB\u30C3\u30C8 ID"
568
+ },
569
+ created_at: {
570
+ label: "\u4F5C\u6210\u65E5\u6642"
571
+ },
572
+ updated_at: {
573
+ label: "\u66F4\u65B0\u65E5\u6642"
574
+ }
575
+ },
576
+ _views: {
577
+ active: {
578
+ label: "\u6709\u52B9"
579
+ },
580
+ inactive: {
581
+ label: "\u7121\u52B9"
582
+ },
583
+ all_permsets: {
584
+ label: "\u3059\u3079\u3066"
585
+ }
586
+ },
587
+ _actions: {
588
+ activate_permission_set: {
589
+ label: "\u6709\u52B9\u5316",
590
+ successMessage: "\u6A29\u9650\u30BB\u30C3\u30C8\u304C\u6709\u52B9\u5316\u3055\u308C\u307E\u3057\u305F"
591
+ },
592
+ deactivate_permission_set: {
593
+ label: "\u7121\u52B9\u5316",
594
+ confirmText: "\u3053\u306E\u6A29\u9650\u30BB\u30C3\u30C8\u3092\u7121\u52B9\u5316\u3057\u307E\u3059\u304B\uFF1F\u65E2\u5B58\u306E\u5272\u308A\u5F53\u3066\u306F\u7DAD\u6301\u3055\u308C\u307E\u3059\u304C\u3001\u518D\u5EA6\u6709\u52B9\u5316\u3059\u308B\u307E\u3067\u30A2\u30AF\u30BB\u30B9\u306E\u4ED8\u4E0E\u306F\u505C\u6B62\u3055\u308C\u307E\u3059\u3002",
595
+ successMessage: "\u6A29\u9650\u30BB\u30C3\u30C8\u304C\u7121\u52B9\u5316\u3055\u308C\u307E\u3057\u305F"
596
+ },
597
+ clone_permission_set: {
598
+ label: "\u8907\u88FD",
599
+ successMessage: "\u6A29\u9650\u30BB\u30C3\u30C8\u3092\u8907\u88FD\u3057\u307E\u3057\u305F"
600
+ }
601
+ }
602
+ },
603
+ sys_user_permission_set: {
604
+ label: "\u30E6\u30FC\u30B6\u30FC\u6A29\u9650\u30BB\u30C3\u30C8",
605
+ pluralLabel: "\u30E6\u30FC\u30B6\u30FC\u6A29\u9650\u30BB\u30C3\u30C8",
606
+ description: "\u30E6\u30FC\u30B6\u30FC\u3078\u306E\u6A29\u9650\u30BB\u30C3\u30C8\u306E\u76F4\u63A5\u5272\u308A\u5F53\u3066\uFF08\u7D44\u7E54\u30B9\u30B3\u30FC\u30D7\u53EF\u80FD\uFF09\u3002",
607
+ fields: {
608
+ id: {
609
+ label: "\u5272\u308A\u5F53\u3066 ID",
610
+ help: "\u5272\u308A\u5F53\u3066\u306E UUID\u3002"
611
+ },
612
+ user_id: {
613
+ label: "\u30E6\u30FC\u30B6\u30FC",
614
+ help: "sys_user \u3078\u306E\u5916\u90E8\u30AD\u30FC\u3002"
615
+ },
616
+ permission_set_id: {
617
+ label: "\u6A29\u9650\u30BB\u30C3\u30C8",
618
+ help: "sys_permission_set \u3078\u306E\u5916\u90E8\u30AD\u30FC\u3002"
619
+ },
620
+ organization_id: {
621
+ label: "\u7D44\u7E54",
622
+ help: "\u30AA\u30D7\u30B7\u30E7\u30F3\u306E\u7D44\u7E54\u30B9\u30B3\u30FC\u30D7\u3002NULL = \u3059\u3079\u3066\u306E\u7D44\u7E54\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u3067\u9069\u7528\u3002"
623
+ },
624
+ granted_by: {
625
+ label: "\u4ED8\u4E0E\u8005",
626
+ help: "\u3053\u306E\u6A29\u9650\u30BB\u30C3\u30C8\u3092\u4ED8\u4E0E\u3057\u305F\u30E6\u30FC\u30B6\u30FC\u3002"
627
+ },
628
+ created_at: {
629
+ label: "\u4F5C\u6210\u65E5\u6642"
630
+ },
631
+ updated_at: {
632
+ label: "\u66F4\u65B0\u65E5\u6642"
633
+ }
634
+ }
635
+ },
636
+ sys_role_permission_set: {
637
+ label: "\u30ED\u30FC\u30EB\u6A29\u9650\u30BB\u30C3\u30C8",
638
+ pluralLabel: "\u30ED\u30FC\u30EB\u6A29\u9650\u30BB\u30C3\u30C8",
639
+ description: "\u6A29\u9650\u30BB\u30C3\u30C8\u3092\u30ED\u30FC\u30EB\u306B\u30D0\u30A4\u30F3\u30C9\u3057\u307E\u3059\u3002",
640
+ fields: {
641
+ id: {
642
+ label: "\u30D0\u30A4\u30F3\u30C9 ID",
643
+ help: "\u30ED\u30FC\u30EB\u6A29\u9650\u30BB\u30C3\u30C8\u30D0\u30A4\u30F3\u30C9\u306E UUID\u3002"
644
+ },
645
+ role_id: {
646
+ label: "\u30ED\u30FC\u30EB",
647
+ help: "sys_role \u3078\u306E\u5916\u90E8\u30AD\u30FC\u3002"
648
+ },
649
+ permission_set_id: {
650
+ label: "\u6A29\u9650\u30BB\u30C3\u30C8",
651
+ help: "sys_permission_set \u3078\u306E\u5916\u90E8\u30AD\u30FC\u3002"
652
+ },
653
+ created_at: {
654
+ label: "\u4F5C\u6210\u65E5\u6642"
655
+ },
656
+ updated_at: {
657
+ label: "\u66F4\u65B0\u65E5\u6642"
658
+ }
659
+ }
660
+ }
661
+ };
662
+ }
663
+ });
664
+
665
+ // src/translations/es-ES.objects.generated.ts
666
+ var esESObjects;
667
+ var init_es_ES_objects_generated = __esm({
668
+ "src/translations/es-ES.objects.generated.ts"() {
669
+ "use strict";
670
+ esESObjects = {
671
+ sys_role: {
672
+ label: "Rol",
673
+ pluralLabel: "Roles",
674
+ description: "Definiciones de rol para el control de acceso RBAC",
675
+ fields: {
676
+ label: {
677
+ label: "Nombre visible"
678
+ },
679
+ name: {
680
+ label: "Nombre de API",
681
+ help: "Nombre t\xE9cnico \xFAnico del rol (p. ej. admin, editor, viewer)."
682
+ },
683
+ description: {
684
+ label: "Descripci\xF3n"
685
+ },
686
+ permissions: {
687
+ label: "Permisos",
688
+ help: "Matriz serializada en JSON de cadenas de permisos."
689
+ },
690
+ active: {
691
+ label: "Activo"
692
+ },
693
+ is_default: {
694
+ label: "Rol predeterminado",
695
+ help: "Se asigna autom\xE1ticamente a los nuevos usuarios."
696
+ },
697
+ id: {
698
+ label: "ID de rol"
699
+ },
700
+ created_at: {
701
+ label: "Creado el"
702
+ },
703
+ updated_at: {
704
+ label: "Actualizado el"
705
+ }
706
+ },
707
+ _views: {
708
+ active: {
709
+ label: "Activo"
710
+ },
711
+ default_roles: {
712
+ label: "Predeterminado"
713
+ },
714
+ custom: {
715
+ label: "Personalizado"
716
+ },
717
+ all_roles: {
718
+ label: "Todos"
719
+ }
720
+ },
721
+ _actions: {
722
+ activate_role: {
723
+ label: "Activar rol",
724
+ successMessage: "Rol activado"
725
+ },
726
+ deactivate_role: {
727
+ label: "Desactivar rol",
728
+ confirmText: "\xBFDesactivar este rol? Los usuarios con el rol conservan su asignaci\xF3n, pero el rol deja de otorgar permisos hasta que se vuelva a activar.",
729
+ successMessage: "Rol desactivado"
730
+ },
731
+ set_default_role: {
732
+ label: "Establecer como predeterminado",
733
+ confirmText: "\xBFConvertir este en el rol predeterminado para los nuevos usuarios? Los usuarios existentes no se ven afectados.",
734
+ successMessage: "Rol predeterminado actualizado"
735
+ },
736
+ clone_role: {
737
+ label: "Clonar rol",
738
+ successMessage: "Rol clonado"
739
+ }
740
+ }
741
+ },
742
+ sys_permission_set: {
743
+ label: "Conjunto de permisos",
744
+ pluralLabel: "Conjuntos de permisos",
745
+ description: "Agrupaciones de permisos con nombre para un control de acceso detallado",
746
+ fields: {
747
+ label: {
748
+ label: "Nombre visible"
749
+ },
750
+ name: {
751
+ label: "Nombre de API",
752
+ help: "Nombre t\xE9cnico \xFAnico del conjunto de permisos."
753
+ },
754
+ description: {
755
+ label: "Descripci\xF3n"
756
+ },
757
+ object_permissions: {
758
+ label: "Permisos de objeto",
759
+ help: "Permisos CRUD a nivel de objeto serializados en JSON."
760
+ },
761
+ field_permissions: {
762
+ label: "Permisos de campo",
763
+ help: "Permisos de lectura/escritura a nivel de campo serializados en JSON."
764
+ },
765
+ system_permissions: {
766
+ label: "Permisos del sistema",
767
+ help: 'Array serializado en JSON de nombres de capacidades del sistema (p. ej. ["setup.access","studio.access","manage_users"])'
768
+ },
769
+ row_level_security: {
770
+ label: "Seguridad a nivel de fila",
771
+ help: "Array serializado en JSON de pol\xEDticas de seguridad a nivel de fila (cl\xE1usulas USING/CHECK)"
772
+ },
773
+ tab_permissions: {
774
+ label: "Permisos de pesta\xF1as",
775
+ help: "Mapa serializado en JSON de la visibilidad de las pesta\xF1as de la app (visible | hidden | default_on | default_off)"
776
+ },
777
+ active: {
778
+ label: "Activo"
779
+ },
780
+ id: {
781
+ label: "ID de conjunto de permisos"
782
+ },
783
+ created_at: {
784
+ label: "Creado el"
785
+ },
786
+ updated_at: {
787
+ label: "Actualizado el"
788
+ }
789
+ },
790
+ _views: {
791
+ active: {
792
+ label: "Activo"
793
+ },
794
+ inactive: {
795
+ label: "Inactivo"
796
+ },
797
+ all_permsets: {
798
+ label: "Todos"
799
+ }
800
+ },
801
+ _actions: {
802
+ activate_permission_set: {
803
+ label: "Activar",
804
+ successMessage: "Conjunto de permisos activado"
805
+ },
806
+ deactivate_permission_set: {
807
+ label: "Desactivar",
808
+ confirmText: "\xBFDesactivar este conjunto de permisos? Las asignaciones existentes se mantienen, pero dejan de otorgar acceso hasta que se vuelva a activar.",
809
+ successMessage: "Conjunto de permisos desactivado"
810
+ },
811
+ clone_permission_set: {
812
+ label: "Clonar",
813
+ successMessage: "Conjunto de permisos clonado"
814
+ }
815
+ }
816
+ },
817
+ sys_user_permission_set: {
818
+ label: "Conjunto de permisos de usuario",
819
+ pluralLabel: "Conjuntos de permisos de usuario",
820
+ description: "Asignaci\xF3n directa de un conjunto de permisos a un usuario (opcionalmente con \xE1mbito de organizaci\xF3n).",
821
+ fields: {
822
+ id: {
823
+ label: "ID de asignaci\xF3n",
824
+ help: "UUID de la asignaci\xF3n."
825
+ },
826
+ user_id: {
827
+ label: "Usuario",
828
+ help: "Clave for\xE1nea a sys_user."
829
+ },
830
+ permission_set_id: {
831
+ label: "Conjunto de permisos",
832
+ help: "Clave for\xE1nea a sys_permission_set."
833
+ },
834
+ organization_id: {
835
+ label: "Organizaci\xF3n",
836
+ help: "\xC1mbito de organizaci\xF3n opcional. NULL = se aplica en cualquier contexto de organizaci\xF3n."
837
+ },
838
+ granted_by: {
839
+ label: "Concedido por",
840
+ help: "Usuario que concedi\xF3 este conjunto de permisos."
841
+ },
842
+ created_at: {
843
+ label: "Creado el"
844
+ },
845
+ updated_at: {
846
+ label: "Actualizado el"
847
+ }
848
+ }
849
+ },
850
+ sys_role_permission_set: {
851
+ label: "Conjunto de permisos de rol",
852
+ pluralLabel: "Conjuntos de permisos de rol",
853
+ description: "Vincula un conjunto de permisos a un rol.",
854
+ fields: {
855
+ id: {
856
+ label: "ID de vinculaci\xF3n",
857
+ help: "UUID de la vinculaci\xF3n rol-conjunto de permisos."
858
+ },
859
+ role_id: {
860
+ label: "Rol",
861
+ help: "Clave for\xE1nea a sys_role."
862
+ },
863
+ permission_set_id: {
864
+ label: "Conjunto de permisos",
865
+ help: "Clave for\xE1nea a sys_permission_set."
866
+ },
867
+ created_at: {
868
+ label: "Creado el"
869
+ },
870
+ updated_at: {
871
+ label: "Actualizado el"
872
+ }
873
+ }
874
+ }
875
+ };
876
+ }
877
+ });
878
+
879
+ // src/translations/index.ts
880
+ var translations_exports = {};
881
+ __export(translations_exports, {
882
+ SecurityTranslations: () => SecurityTranslations
883
+ });
884
+ var SecurityTranslations;
885
+ var init_translations = __esm({
886
+ "src/translations/index.ts"() {
887
+ "use strict";
888
+ init_en_objects_generated();
889
+ init_zh_CN_objects_generated();
890
+ init_ja_JP_objects_generated();
891
+ init_es_ES_objects_generated();
892
+ SecurityTranslations = {
893
+ en: { objects: enObjects },
894
+ "zh-CN": { objects: zhCNObjects },
895
+ "ja-JP": { objects: jaJPObjects },
896
+ "es-ES": { objects: esESObjects }
897
+ };
898
+ }
899
+ });
900
+
20
901
  // src/index.ts
21
902
  var index_exports = {};
22
903
  __export(index_exports, {
@@ -355,6 +1236,7 @@ function isPermissionDeniedError(e) {
355
1236
  }
356
1237
 
357
1238
  // src/bootstrap-platform-admin.ts
1239
+ var import_system = require("@objectstack/spec/system");
358
1240
  var SYSTEM_CTX = { isSystem: true };
359
1241
  async function tryFind(ql, object, where, limit = 100) {
360
1242
  try {
@@ -421,17 +1303,20 @@ async function bootstrapPlatformAdmin(ql, bootstrapPermissionSets, options = {})
421
1303
  ql,
422
1304
  "sys_user_permission_set",
423
1305
  { permission_set_id: adminPsId },
424
- 5
1306
+ 50
425
1307
  );
426
- if (existingAdminLinks.some((r) => !r.organization_id)) {
1308
+ if (existingAdminLinks.some((r) => !r.organization_id && r.user_id !== import_system.SystemUserId.SYSTEM)) {
427
1309
  return { seeded: seededCount, adminPromoted: false, reason: "already_have_admin" };
428
1310
  }
429
1311
  const allUsers = await tryFind(ql, "sys_user", {}, 50);
430
- if (allUsers.length === 0) {
431
- logger?.info?.("[security] no users yet \u2014 first sign-up will be promoted to platform admin");
1312
+ const humanUsers = allUsers.filter(
1313
+ (u) => u.id !== import_system.SystemUserId.SYSTEM && u.role !== "system"
1314
+ );
1315
+ if (humanUsers.length === 0) {
1316
+ logger?.info?.("[security] no human users yet \u2014 first sign-up will be promoted to platform admin");
432
1317
  return { seeded: seededCount, adminPromoted: false, reason: "no_users" };
433
1318
  }
434
- const sorted = [...allUsers].sort((a, b) => {
1319
+ const sorted = [...humanUsers].sort((a, b) => {
435
1320
  const ta = a.created_at ? new Date(a.created_at).getTime() : 0;
436
1321
  const tb = b.created_at ? new Date(b.created_at).getTime() : 0;
437
1322
  return ta - tb;
@@ -620,17 +1505,1024 @@ function extractMemberPairs(opCtx) {
620
1505
  return Array.from(out.values());
621
1506
  }
622
1507
 
1508
+ // src/objects/sys-role.object.ts
1509
+ var import_data = require("@objectstack/spec/data");
1510
+ var SysRole = import_data.ObjectSchema.create({
1511
+ name: "sys_role",
1512
+ label: "Role",
1513
+ pluralLabel: "Roles",
1514
+ icon: "shield",
1515
+ isSystem: true,
1516
+ managedBy: "config",
1517
+ // ADR-0010 §3.7 — RBAC primitive; tenants may add custom rows
1518
+ // (created via UI / API) but the schema itself is locked.
1519
+ protection: {
1520
+ lock: "no-overlay",
1521
+ reason: "RBAC schema is platform-defined \u2014 see ADR-0010.",
1522
+ docsUrl: "https://docs.objectstack.ai/adr/0010-metadata-protection"
1523
+ },
1524
+ description: "Role definitions for RBAC access control",
1525
+ displayNameField: "label",
1526
+ titleFormat: "{label}",
1527
+ compactLayout: ["label", "name", "active", "is_default"],
1528
+ // Custom actions — system roles drive RBAC and are edited rarely but
1529
+ // require the four high-frequency sysadmin affordances every IdP
1530
+ // (Salesforce, ServiceNow, Okta) ships: activate/deactivate (lifecycle
1531
+ // without losing assignments), mark default (auto-assign to new users),
1532
+ // and clone (template for new roles). All operations hit the generic
1533
+ // data CRUD endpoint exposed by `apiEnabled` — no custom server route
1534
+ // required because `managedBy: 'config'` allows direct mutation.
1535
+ actions: [
1536
+ {
1537
+ name: "activate_role",
1538
+ label: "Activate Role",
1539
+ icon: "circle-check",
1540
+ variant: "secondary",
1541
+ mode: "custom",
1542
+ locations: ["list_item", "record_header"],
1543
+ type: "api",
1544
+ method: "PATCH",
1545
+ target: "/api/v1/data/sys_role/{id}",
1546
+ bodyExtra: { active: true },
1547
+ successMessage: "Role activated",
1548
+ refreshAfter: true
1549
+ },
1550
+ {
1551
+ name: "deactivate_role",
1552
+ label: "Deactivate Role",
1553
+ icon: "circle-off",
1554
+ variant: "danger",
1555
+ mode: "custom",
1556
+ locations: ["list_item", "record_header"],
1557
+ type: "api",
1558
+ method: "PATCH",
1559
+ target: "/api/v1/data/sys_role/{id}",
1560
+ bodyExtra: { active: false },
1561
+ confirmText: "Deactivate this role? Users with the role keep their assignment but the role stops granting permissions until re-activated.",
1562
+ successMessage: "Role deactivated",
1563
+ refreshAfter: true
1564
+ },
1565
+ {
1566
+ name: "set_default_role",
1567
+ label: "Set as Default",
1568
+ icon: "star",
1569
+ variant: "secondary",
1570
+ mode: "custom",
1571
+ locations: ["list_item", "record_header"],
1572
+ type: "api",
1573
+ method: "PATCH",
1574
+ target: "/api/v1/data/sys_role/{id}",
1575
+ bodyExtra: { is_default: true },
1576
+ confirmText: "Make this the default role for new users? Existing users are unaffected.",
1577
+ successMessage: "Default role updated",
1578
+ refreshAfter: true
1579
+ },
1580
+ {
1581
+ // Clone — POST a new sys_role row pre-filled from the source. The
1582
+ // dialog asks only for the new API name / label so the operator
1583
+ // can rename atomically; permissions JSON is copied wholesale via
1584
+ // defaultFromRow.
1585
+ name: "clone_role",
1586
+ label: "Clone Role",
1587
+ icon: "copy",
1588
+ variant: "secondary",
1589
+ mode: "custom",
1590
+ locations: ["list_item", "record_header"],
1591
+ type: "api",
1592
+ method: "POST",
1593
+ target: "/api/v1/data/sys_role",
1594
+ bodyExtra: { is_default: false, active: true },
1595
+ successMessage: "Role cloned",
1596
+ refreshAfter: true,
1597
+ params: [
1598
+ { name: "label", label: "New Display Name", type: "text", required: true },
1599
+ { name: "name", label: "New API Name", type: "text", required: true, helpText: "Unique snake_case machine name" },
1600
+ { field: "description", defaultFromRow: true },
1601
+ { field: "permissions", defaultFromRow: true }
1602
+ ]
1603
+ }
1604
+ ],
1605
+ listViews: {
1606
+ active: {
1607
+ type: "grid",
1608
+ name: "active",
1609
+ label: "Active",
1610
+ data: { provider: "object", object: "sys_role" },
1611
+ columns: ["label", "name", "is_default", "updated_at"],
1612
+ filter: [{ field: "active", operator: "equals", value: true }],
1613
+ sort: [{ field: "label", order: "asc" }],
1614
+ pagination: { pageSize: 50 }
1615
+ },
1616
+ default_roles: {
1617
+ type: "grid",
1618
+ name: "default_roles",
1619
+ label: "Default",
1620
+ data: { provider: "object", object: "sys_role" },
1621
+ columns: ["label", "name", "description", "active"],
1622
+ filter: [{ field: "is_default", operator: "equals", value: true }],
1623
+ sort: [{ field: "label", order: "asc" }],
1624
+ pagination: { pageSize: 50 }
1625
+ },
1626
+ custom: {
1627
+ type: "grid",
1628
+ name: "custom",
1629
+ label: "Custom",
1630
+ data: { provider: "object", object: "sys_role" },
1631
+ columns: ["label", "name", "active", "updated_at"],
1632
+ filter: [{ field: "is_default", operator: "equals", value: false }],
1633
+ sort: [{ field: "label", order: "asc" }],
1634
+ pagination: { pageSize: 50 }
1635
+ },
1636
+ all_roles: {
1637
+ type: "grid",
1638
+ name: "all_roles",
1639
+ label: "All",
1640
+ data: { provider: "object", object: "sys_role" },
1641
+ columns: ["label", "name", "active", "is_default", "updated_at"],
1642
+ sort: [{ field: "label", order: "asc" }],
1643
+ pagination: { pageSize: 50 }
1644
+ }
1645
+ },
1646
+ fields: {
1647
+ // ── Identity ─────────────────────────────────────────────────
1648
+ label: import_data.Field.text({
1649
+ label: "Display Name",
1650
+ required: true,
1651
+ searchable: true,
1652
+ maxLength: 255,
1653
+ group: "Identity"
1654
+ }),
1655
+ name: import_data.Field.text({
1656
+ label: "API Name",
1657
+ required: true,
1658
+ searchable: true,
1659
+ maxLength: 100,
1660
+ description: "Unique machine name for the role (e.g. admin, editor, viewer)",
1661
+ group: "Identity"
1662
+ }),
1663
+ description: import_data.Field.textarea({
1664
+ label: "Description",
1665
+ required: false,
1666
+ group: "Identity"
1667
+ }),
1668
+ // ── Configuration ────────────────────────────────────────────
1669
+ permissions: import_data.Field.textarea({
1670
+ label: "Permissions",
1671
+ required: false,
1672
+ description: "JSON-serialized array of permission strings",
1673
+ group: "Configuration"
1674
+ }),
1675
+ // ── Status ───────────────────────────────────────────────────
1676
+ active: import_data.Field.boolean({
1677
+ label: "Active",
1678
+ defaultValue: true,
1679
+ group: "Status"
1680
+ }),
1681
+ is_default: import_data.Field.boolean({
1682
+ label: "Default Role",
1683
+ defaultValue: false,
1684
+ description: "Automatically assigned to new users",
1685
+ group: "Status"
1686
+ }),
1687
+ // ── System ───────────────────────────────────────────────────
1688
+ id: import_data.Field.text({
1689
+ label: "Role ID",
1690
+ required: true,
1691
+ readonly: true,
1692
+ group: "System"
1693
+ }),
1694
+ created_at: import_data.Field.datetime({
1695
+ label: "Created At",
1696
+ defaultValue: "NOW()",
1697
+ readonly: true,
1698
+ group: "System"
1699
+ }),
1700
+ updated_at: import_data.Field.datetime({
1701
+ label: "Updated At",
1702
+ defaultValue: "NOW()",
1703
+ readonly: true,
1704
+ group: "System"
1705
+ })
1706
+ },
1707
+ indexes: [
1708
+ { fields: ["name"], unique: true },
1709
+ { fields: ["active"] }
1710
+ ],
1711
+ enable: {
1712
+ trackHistory: true,
1713
+ searchable: true,
1714
+ apiEnabled: true,
1715
+ apiMethods: ["get", "list", "create", "update", "delete"],
1716
+ trash: true,
1717
+ mru: true
1718
+ }
1719
+ });
1720
+
1721
+ // src/objects/sys-permission-set.object.ts
1722
+ var import_data2 = require("@objectstack/spec/data");
1723
+ var SysPermissionSet = import_data2.ObjectSchema.create({
1724
+ name: "sys_permission_set",
1725
+ label: "Permission Set",
1726
+ pluralLabel: "Permission Sets",
1727
+ icon: "lock",
1728
+ isSystem: true,
1729
+ managedBy: "config",
1730
+ // ADR-0010 §3.7 — RBAC primitive; tenants may add custom rows
1731
+ // (created via UI / API) but the schema itself is locked.
1732
+ protection: {
1733
+ lock: "no-overlay",
1734
+ reason: "RBAC schema is platform-defined \u2014 see ADR-0010.",
1735
+ docsUrl: "https://docs.objectstack.ai/adr/0010-metadata-protection"
1736
+ },
1737
+ description: "Named permission groupings for fine-grained access control",
1738
+ displayNameField: "label",
1739
+ titleFormat: "{label}",
1740
+ compactLayout: ["label", "name", "active"],
1741
+ // Custom actions — permission sets are templates assigned to roles or
1742
+ // users (via sys_role_permission_set / sys_user_permission_set). The
1743
+ // sysadmin operations that don't live on the parent-detail tabs are
1744
+ // lifecycle (activate/deactivate without losing assignments) and
1745
+ // clone (build a new permset by tweaking an existing one). Both hit
1746
+ // the generic data CRUD endpoint — managedBy: 'config' permits it.
1747
+ actions: [
1748
+ {
1749
+ name: "activate_permission_set",
1750
+ label: "Activate",
1751
+ icon: "circle-check",
1752
+ variant: "secondary",
1753
+ mode: "custom",
1754
+ locations: ["list_item", "record_header"],
1755
+ type: "api",
1756
+ method: "PATCH",
1757
+ target: "/api/v1/data/sys_permission_set/{id}",
1758
+ bodyExtra: { active: true },
1759
+ successMessage: "Permission set activated",
1760
+ refreshAfter: true
1761
+ },
1762
+ {
1763
+ name: "deactivate_permission_set",
1764
+ label: "Deactivate",
1765
+ icon: "circle-off",
1766
+ variant: "danger",
1767
+ mode: "custom",
1768
+ locations: ["list_item", "record_header"],
1769
+ type: "api",
1770
+ method: "PATCH",
1771
+ target: "/api/v1/data/sys_permission_set/{id}",
1772
+ bodyExtra: { active: false },
1773
+ confirmText: "Deactivate this permission set? Existing assignments stay in place but stop granting access until re-activated.",
1774
+ successMessage: "Permission set deactivated",
1775
+ refreshAfter: true
1776
+ },
1777
+ {
1778
+ name: "clone_permission_set",
1779
+ label: "Clone",
1780
+ icon: "copy",
1781
+ variant: "secondary",
1782
+ mode: "custom",
1783
+ locations: ["list_item", "record_header"],
1784
+ type: "api",
1785
+ method: "POST",
1786
+ target: "/api/v1/data/sys_permission_set",
1787
+ bodyExtra: { active: true },
1788
+ successMessage: "Permission set cloned",
1789
+ refreshAfter: true,
1790
+ params: [
1791
+ { name: "label", label: "New Display Name", type: "text", required: true },
1792
+ { name: "name", label: "New API Name", type: "text", required: true, helpText: "Unique snake_case machine name" },
1793
+ { field: "description", defaultFromRow: true },
1794
+ { field: "object_permissions", defaultFromRow: true },
1795
+ { field: "field_permissions", defaultFromRow: true }
1796
+ ]
1797
+ }
1798
+ ],
1799
+ listViews: {
1800
+ active: {
1801
+ type: "grid",
1802
+ name: "active",
1803
+ label: "Active",
1804
+ data: { provider: "object", object: "sys_permission_set" },
1805
+ columns: ["label", "name", "description", "updated_at"],
1806
+ filter: [{ field: "active", operator: "equals", value: true }],
1807
+ sort: [{ field: "label", order: "asc" }],
1808
+ pagination: { pageSize: 50 }
1809
+ },
1810
+ inactive: {
1811
+ type: "grid",
1812
+ name: "inactive",
1813
+ label: "Inactive",
1814
+ data: { provider: "object", object: "sys_permission_set" },
1815
+ columns: ["label", "name", "updated_at"],
1816
+ filter: [{ field: "active", operator: "equals", value: false }],
1817
+ sort: [{ field: "label", order: "asc" }],
1818
+ pagination: { pageSize: 50 }
1819
+ },
1820
+ all_permsets: {
1821
+ type: "grid",
1822
+ name: "all_permsets",
1823
+ label: "All",
1824
+ data: { provider: "object", object: "sys_permission_set" },
1825
+ columns: ["label", "name", "active", "updated_at"],
1826
+ sort: [{ field: "label", order: "asc" }],
1827
+ pagination: { pageSize: 50 }
1828
+ }
1829
+ },
1830
+ fields: {
1831
+ // ── Identity ─────────────────────────────────────────────────
1832
+ label: import_data2.Field.text({
1833
+ label: "Display Name",
1834
+ required: true,
1835
+ searchable: true,
1836
+ maxLength: 255,
1837
+ group: "Identity"
1838
+ }),
1839
+ name: import_data2.Field.text({
1840
+ label: "API Name",
1841
+ required: true,
1842
+ searchable: true,
1843
+ maxLength: 100,
1844
+ description: "Unique machine name for the permission set",
1845
+ group: "Identity"
1846
+ }),
1847
+ description: import_data2.Field.textarea({
1848
+ label: "Description",
1849
+ required: false,
1850
+ group: "Identity"
1851
+ }),
1852
+ // ── Permissions ──────────────────────────────────────────────
1853
+ object_permissions: import_data2.Field.textarea({
1854
+ label: "Object Permissions",
1855
+ required: false,
1856
+ description: "JSON-serialized object-level CRUD permissions",
1857
+ group: "Permissions"
1858
+ }),
1859
+ field_permissions: import_data2.Field.textarea({
1860
+ label: "Field Permissions",
1861
+ required: false,
1862
+ description: "JSON-serialized field-level read/write permissions",
1863
+ group: "Permissions"
1864
+ }),
1865
+ system_permissions: import_data2.Field.textarea({
1866
+ label: "System Permissions",
1867
+ required: false,
1868
+ description: 'JSON-serialized array of system capability names (e.g. ["setup.access","studio.access","manage_users"])',
1869
+ group: "Permissions"
1870
+ }),
1871
+ row_level_security: import_data2.Field.textarea({
1872
+ label: "Row-Level Security",
1873
+ required: false,
1874
+ description: "JSON-serialized array of row-level security policies (USING/CHECK clauses)",
1875
+ group: "Permissions"
1876
+ }),
1877
+ tab_permissions: import_data2.Field.textarea({
1878
+ label: "Tab Permissions",
1879
+ required: false,
1880
+ description: "JSON-serialized map of app tab visibility (visible | hidden | default_on | default_off)",
1881
+ group: "Permissions"
1882
+ }),
1883
+ // ── Status ───────────────────────────────────────────────────
1884
+ active: import_data2.Field.boolean({
1885
+ label: "Active",
1886
+ defaultValue: true,
1887
+ group: "Status"
1888
+ }),
1889
+ // ── System ───────────────────────────────────────────────────
1890
+ id: import_data2.Field.text({
1891
+ label: "Permission Set ID",
1892
+ required: true,
1893
+ readonly: true,
1894
+ group: "System"
1895
+ }),
1896
+ created_at: import_data2.Field.datetime({
1897
+ label: "Created At",
1898
+ defaultValue: "NOW()",
1899
+ readonly: true,
1900
+ group: "System"
1901
+ }),
1902
+ updated_at: import_data2.Field.datetime({
1903
+ label: "Updated At",
1904
+ defaultValue: "NOW()",
1905
+ readonly: true,
1906
+ group: "System"
1907
+ })
1908
+ },
1909
+ indexes: [
1910
+ { fields: ["name"], unique: true },
1911
+ { fields: ["active"] }
1912
+ ],
1913
+ enable: {
1914
+ trackHistory: true,
1915
+ searchable: true,
1916
+ apiEnabled: true,
1917
+ apiMethods: ["get", "list", "create", "update", "delete"],
1918
+ trash: true,
1919
+ mru: true
1920
+ }
1921
+ });
1922
+
1923
+ // src/objects/sys-user-permission-set.object.ts
1924
+ var import_data3 = require("@objectstack/spec/data");
1925
+ var SysUserPermissionSet = import_data3.ObjectSchema.create({
1926
+ name: "sys_user_permission_set",
1927
+ label: "User Permission Set",
1928
+ pluralLabel: "User Permission Sets",
1929
+ icon: "user-check",
1930
+ isSystem: true,
1931
+ managedBy: "system",
1932
+ description: "Direct assignment of a permission set to a user (optionally scoped to an organization).",
1933
+ titleFormat: "{user_id} \u2192 {permission_set_id}",
1934
+ compactLayout: ["user_id", "permission_set_id", "organization_id"],
1935
+ fields: {
1936
+ id: import_data3.Field.text({
1937
+ label: "Assignment ID",
1938
+ required: true,
1939
+ readonly: true,
1940
+ description: "UUID of the assignment."
1941
+ }),
1942
+ user_id: import_data3.Field.lookup("sys_user", {
1943
+ label: "User",
1944
+ required: true,
1945
+ description: "Foreign key to sys_user."
1946
+ }),
1947
+ permission_set_id: import_data3.Field.lookup("sys_permission_set", {
1948
+ label: "Permission Set",
1949
+ required: true,
1950
+ description: "Foreign key to sys_permission_set."
1951
+ }),
1952
+ organization_id: import_data3.Field.lookup("sys_organization", {
1953
+ label: "Organization",
1954
+ required: false,
1955
+ description: "Optional organization scope. NULL = applies in every org context."
1956
+ }),
1957
+ granted_by: import_data3.Field.lookup("sys_user", {
1958
+ label: "Granted By",
1959
+ required: false,
1960
+ description: "User who granted this permission set."
1961
+ }),
1962
+ created_at: import_data3.Field.datetime({
1963
+ label: "Created At",
1964
+ defaultValue: "NOW()",
1965
+ readonly: true
1966
+ }),
1967
+ updated_at: import_data3.Field.datetime({
1968
+ label: "Updated At",
1969
+ defaultValue: "NOW()",
1970
+ readonly: true
1971
+ })
1972
+ },
1973
+ indexes: [
1974
+ { fields: ["user_id", "permission_set_id", "organization_id"], unique: true },
1975
+ { fields: ["user_id"] },
1976
+ { fields: ["organization_id"] },
1977
+ { fields: ["permission_set_id"] }
1978
+ ],
1979
+ enable: {
1980
+ trackHistory: true,
1981
+ searchable: true,
1982
+ apiEnabled: true,
1983
+ apiMethods: ["get", "list", "create", "update", "delete"],
1984
+ trash: true,
1985
+ mru: false
1986
+ }
1987
+ });
1988
+
1989
+ // src/objects/sys-role-permission-set.object.ts
1990
+ var import_data4 = require("@objectstack/spec/data");
1991
+ var SysRolePermissionSet = import_data4.ObjectSchema.create({
1992
+ name: "sys_role_permission_set",
1993
+ label: "Role Permission Set",
1994
+ pluralLabel: "Role Permission Sets",
1995
+ icon: "shield-plus",
1996
+ isSystem: true,
1997
+ managedBy: "system",
1998
+ description: "Binds a permission set to a role.",
1999
+ titleFormat: "{role_id} \u2192 {permission_set_id}",
2000
+ compactLayout: ["role_id", "permission_set_id"],
2001
+ fields: {
2002
+ id: import_data4.Field.text({
2003
+ label: "Binding ID",
2004
+ required: true,
2005
+ readonly: true,
2006
+ description: "UUID of the role-permission-set binding."
2007
+ }),
2008
+ role_id: import_data4.Field.lookup("sys_role", {
2009
+ label: "Role",
2010
+ required: true,
2011
+ description: "Foreign key to sys_role."
2012
+ }),
2013
+ permission_set_id: import_data4.Field.lookup("sys_permission_set", {
2014
+ label: "Permission Set",
2015
+ required: true,
2016
+ description: "Foreign key to sys_permission_set."
2017
+ }),
2018
+ created_at: import_data4.Field.datetime({
2019
+ label: "Created At",
2020
+ defaultValue: "NOW()",
2021
+ readonly: true
2022
+ }),
2023
+ updated_at: import_data4.Field.datetime({
2024
+ label: "Updated At",
2025
+ defaultValue: "NOW()",
2026
+ readonly: true
2027
+ })
2028
+ },
2029
+ indexes: [
2030
+ { fields: ["role_id", "permission_set_id"], unique: true },
2031
+ { fields: ["role_id"] },
2032
+ { fields: ["permission_set_id"] }
2033
+ ],
2034
+ enable: {
2035
+ trackHistory: true,
2036
+ searchable: true,
2037
+ apiEnabled: true,
2038
+ apiMethods: ["get", "list", "create", "update", "delete"],
2039
+ trash: true,
2040
+ mru: false
2041
+ }
2042
+ });
2043
+
2044
+ // src/objects/default-permission-sets.ts
2045
+ var import_security = require("@objectstack/spec/security");
2046
+ var BETTER_AUTH_MANAGED_OBJECTS = [
2047
+ "sys_user",
2048
+ "sys_account",
2049
+ "sys_session",
2050
+ "sys_organization",
2051
+ "sys_member",
2052
+ "sys_invitation",
2053
+ "sys_team",
2054
+ "sys_team_member",
2055
+ "sys_api_key",
2056
+ "sys_two_factor",
2057
+ "sys_verification",
2058
+ "sys_jwks",
2059
+ "sys_device_code",
2060
+ "sys_oauth_application",
2061
+ "sys_oauth_access_token",
2062
+ "sys_oauth_refresh_token",
2063
+ "sys_oauth_consent"
2064
+ ];
2065
+ var denyWritesOnManagedObjects = () => Object.fromEntries(
2066
+ BETTER_AUTH_MANAGED_OBJECTS.map((name) => [
2067
+ name,
2068
+ { allowRead: true, allowCreate: false, allowEdit: false, allowDelete: false }
2069
+ ])
2070
+ );
2071
+ var defaultPermissionSets = [
2072
+ import_security.PermissionSetSchema.parse({
2073
+ name: "admin_full_access",
2074
+ label: "Administrator \u2014 Full Access",
2075
+ isProfile: true,
2076
+ objects: {
2077
+ "*": {
2078
+ allowRead: true,
2079
+ allowCreate: true,
2080
+ allowEdit: true,
2081
+ allowDelete: true,
2082
+ viewAllRecords: true,
2083
+ modifyAllRecords: true
2084
+ }
2085
+ },
2086
+ systemPermissions: [
2087
+ "manage_users",
2088
+ "manage_metadata",
2089
+ "manage_platform_settings",
2090
+ "setup.access",
2091
+ "studio.access"
2092
+ ]
2093
+ }),
2094
+ // ── Organization Administrator ──────────────────────────────────────
2095
+ //
2096
+ // Third tier between platform admin (`admin_full_access`) and rank-and-file
2097
+ // member. Lives at the *organization* scope: full CRUD on business
2098
+ // objects within their org (governed by `tenant_isolation` RLS), plus
2099
+ // `setup.access` so the Setup app shell is reachable.
2100
+ //
2101
+ // **Deliberately withheld** vs `admin_full_access`:
2102
+ // - `studio.access` — schema-design surfaces are platform-level (a
2103
+ // tenant cannot mutate the shared metadata) and Studio is hidden.
2104
+ // - `manage_metadata` — same reasoning.
2105
+ // - `manage_platform_settings` — global settings manifests
2106
+ // (mail / storage / AI / knowledge) and platform-only Setup pages
2107
+ // (sharing rules, audit logs, OAuth apps, JWKS, …) require this
2108
+ // and are hidden / 403'd for org admins. Tenant-scoped manifests
2109
+ // (`branding`, `feature_flags`) keep using `setup.access` so org
2110
+ // admins CAN configure their own org's branding.
2111
+ //
2112
+ // **Anti-escalation**: writes to the global RBAC tables
2113
+ // (`sys_role`, `sys_permission_set`, `sys_role_permission_set`,
2114
+ // `sys_user_permission_set`, `sys_user_role`) are denied. Allowing
2115
+ // them would let an org admin bind `admin_full_access` (which has no
2116
+ // RLS) to themselves and break out of tenant isolation. Reads are
2117
+ // permitted so the Roles / Permission Sets nav entries still render.
2118
+ //
2119
+ // Auto-granted to every `sys_member` whose role contains `owner` or
2120
+ // `admin` by `plugin-security/src/auto-org-admin-grant.ts`.
2121
+ import_security.PermissionSetSchema.parse({
2122
+ name: "organization_admin",
2123
+ label: "Organization Administrator",
2124
+ isProfile: true,
2125
+ objects: {
2126
+ "*": {
2127
+ allowRead: true,
2128
+ allowCreate: true,
2129
+ allowEdit: true,
2130
+ allowDelete: true,
2131
+ viewAllRecords: true,
2132
+ modifyAllRecords: true
2133
+ },
2134
+ // Identity tables — go through better-auth endpoints (invite,
2135
+ // accept, remove-member, transfer, …) rather than raw CRUD.
2136
+ ...denyWritesOnManagedObjects(),
2137
+ // RBAC tables — read-only to prevent privilege escalation.
2138
+ sys_role: { allowRead: true, allowCreate: false, allowEdit: false, allowDelete: false },
2139
+ sys_permission_set: { allowRead: true, allowCreate: false, allowEdit: false, allowDelete: false },
2140
+ sys_role_permission_set: { allowRead: true, allowCreate: false, allowEdit: false, allowDelete: false },
2141
+ sys_user_permission_set: { allowRead: true, allowCreate: false, allowEdit: false, allowDelete: false },
2142
+ sys_user_role: { allowRead: true, allowCreate: false, allowEdit: false, allowDelete: false }
2143
+ },
2144
+ systemPermissions: ["manage_org_users", "setup.access"],
2145
+ rowLevelSecurity: [
2146
+ {
2147
+ name: "tenant_isolation",
2148
+ object: "*",
2149
+ operation: "all",
2150
+ using: "organization_id = current_user.organization_id"
2151
+ },
2152
+ // ── better-auth system tables that lack `organization_id` and would
2153
+ // otherwise be denied by the wildcard policy. Same self-only
2154
+ // carve-outs as `member_default` — an org admin does not get to
2155
+ // inspect cross-tenant identity rows.
2156
+ {
2157
+ name: "sys_organization_self",
2158
+ object: "sys_organization",
2159
+ operation: "all",
2160
+ using: "id = current_user.organization_id"
2161
+ },
2162
+ {
2163
+ name: "sys_user_self",
2164
+ object: "sys_user",
2165
+ operation: "select",
2166
+ using: "id = current_user.id"
2167
+ },
2168
+ {
2169
+ name: "sys_user_org_members",
2170
+ object: "sys_user",
2171
+ operation: "select",
2172
+ using: "id IN (current_user.org_user_ids)"
2173
+ },
2174
+ {
2175
+ name: "sys_session_self",
2176
+ object: "sys_session",
2177
+ operation: "all",
2178
+ using: "user_id = current_user.id"
2179
+ },
2180
+ {
2181
+ name: "sys_account_self",
2182
+ object: "sys_account",
2183
+ operation: "select",
2184
+ using: "user_id = current_user.id"
2185
+ },
2186
+ {
2187
+ name: "sys_team_member_self",
2188
+ object: "sys_team_member",
2189
+ operation: "select",
2190
+ using: "user_id = current_user.id"
2191
+ },
2192
+ {
2193
+ name: "sys_two_factor_self",
2194
+ object: "sys_two_factor",
2195
+ operation: "all",
2196
+ using: "user_id = current_user.id"
2197
+ },
2198
+ {
2199
+ name: "sys_user_preference_self",
2200
+ object: "sys_user_preference",
2201
+ operation: "all",
2202
+ using: "user_id = current_user.id"
2203
+ },
2204
+ {
2205
+ name: "sys_api_key_self",
2206
+ object: "sys_api_key",
2207
+ operation: "all",
2208
+ using: "user_id = current_user.id"
2209
+ },
2210
+ {
2211
+ name: "sys_device_code_self",
2212
+ object: "sys_device_code",
2213
+ operation: "all",
2214
+ using: "user_id = current_user.id"
2215
+ },
2216
+ {
2217
+ name: "sys_oauth_access_token_self",
2218
+ object: "sys_oauth_access_token",
2219
+ operation: "select",
2220
+ using: "user_id = current_user.id"
2221
+ },
2222
+ {
2223
+ name: "sys_oauth_refresh_token_self",
2224
+ object: "sys_oauth_refresh_token",
2225
+ operation: "select",
2226
+ using: "user_id = current_user.id"
2227
+ },
2228
+ {
2229
+ name: "sys_oauth_consent_self",
2230
+ object: "sys_oauth_consent",
2231
+ operation: "all",
2232
+ using: "user_id = current_user.id"
2233
+ },
2234
+ // OAuth applications a user has registered themselves (self-service
2235
+ // developer flow exposed in the Account app's Developer section).
2236
+ // `sys_oauth_application` has no `organization_id` so the wildcard
2237
+ // `tenant_isolation` policy would otherwise deny every row.
2238
+ {
2239
+ name: "sys_oauth_application_self",
2240
+ object: "sys_oauth_application",
2241
+ operation: "all",
2242
+ using: "user_id = current_user.id"
2243
+ },
2244
+ // Org-scoped visibility for organization-owned identity-adjacent
2245
+ // tables. Org admins may inspect their own org's invitations and
2246
+ // memberships (read; writes still flow through better-auth).
2247
+ {
2248
+ name: "sys_member_org",
2249
+ object: "sys_member",
2250
+ operation: "select",
2251
+ using: "organization_id = current_user.organization_id"
2252
+ },
2253
+ {
2254
+ name: "sys_invitation_org",
2255
+ object: "sys_invitation",
2256
+ operation: "select",
2257
+ using: "organization_id = current_user.organization_id"
2258
+ },
2259
+ {
2260
+ name: "sys_team_org",
2261
+ object: "sys_team",
2262
+ operation: "select",
2263
+ using: "organization_id = current_user.organization_id"
2264
+ }
2265
+ ]
2266
+ }),
2267
+ import_security.PermissionSetSchema.parse({
2268
+ name: "member_default",
2269
+ label: "Member \u2014 Standard Access",
2270
+ isProfile: true,
2271
+ objects: {
2272
+ "*": {
2273
+ allowRead: true,
2274
+ allowCreate: true,
2275
+ allowEdit: true,
2276
+ allowDelete: true
2277
+ },
2278
+ // Identity tables are managed by better-auth — no direct writes.
2279
+ ...denyWritesOnManagedObjects()
2280
+ },
2281
+ rowLevelSecurity: [
2282
+ {
2283
+ name: "tenant_isolation",
2284
+ object: "*",
2285
+ operation: "all",
2286
+ using: "organization_id = current_user.organization_id"
2287
+ },
2288
+ {
2289
+ name: "owner_only_writes",
2290
+ object: "*",
2291
+ operation: "update",
2292
+ using: "owner_id = current_user.id"
2293
+ },
2294
+ {
2295
+ name: "owner_only_deletes",
2296
+ object: "*",
2297
+ operation: "delete",
2298
+ using: "owner_id = current_user.id"
2299
+ },
2300
+ // ── better-auth system tables that lack `organization_id` and would
2301
+ // otherwise be left unprotected by the wildcard rule above. ────
2302
+ //
2303
+ // The security plugin's RLS injector treats wildcard policies that
2304
+ // target a missing field as `RLS_DENY_FILTER` (zero rows) unless a
2305
+ // per-object policy contributes an alternate match. Each `*_self`
2306
+ // policy below restores per-user visibility on a better-auth table
2307
+ // that has `user_id` but no `organization_id`. Tables without
2308
+ // `user_id` (`sys_verification`, `sys_jwks`, empty `sys_passkey`)
2309
+ // stay DENY for non-admins by design — only platform admins (via
2310
+ // `admin_full_access`, which has no RLS) should inspect them.
2311
+ {
2312
+ name: "sys_organization_self",
2313
+ object: "sys_organization",
2314
+ operation: "all",
2315
+ using: "id = current_user.organization_id"
2316
+ },
2317
+ {
2318
+ name: "sys_user_self",
2319
+ object: "sys_user",
2320
+ operation: "select",
2321
+ using: "id = current_user.id"
2322
+ },
2323
+ // Org collaborators: members can see other users in the same
2324
+ // organization. Without this, owner/assignee lookups, @-mention
2325
+ // suggestions, reviewer pickers and team-roster surfaces all
2326
+ // collapse to just the current user. `org_user_ids` is
2327
+ // pre-resolved by runtime/resolve-execution-context from
2328
+ // `sys_member` for the active organization. Sensitive credential
2329
+ // tables (`sys_account`, `sys_session`, `sys_api_key`, …) keep
2330
+ // their stricter self-only carve-outs above.
2331
+ {
2332
+ name: "sys_user_org_members",
2333
+ object: "sys_user",
2334
+ operation: "select",
2335
+ using: "id IN (current_user.org_user_ids)"
2336
+ },
2337
+ {
2338
+ name: "sys_session_self",
2339
+ object: "sys_session",
2340
+ operation: "all",
2341
+ using: "user_id = current_user.id"
2342
+ },
2343
+ {
2344
+ name: "sys_account_self",
2345
+ object: "sys_account",
2346
+ operation: "select",
2347
+ using: "user_id = current_user.id"
2348
+ },
2349
+ {
2350
+ name: "sys_team_member_self",
2351
+ object: "sys_team_member",
2352
+ operation: "select",
2353
+ using: "user_id = current_user.id"
2354
+ },
2355
+ {
2356
+ name: "sys_two_factor_self",
2357
+ object: "sys_two_factor",
2358
+ operation: "all",
2359
+ using: "user_id = current_user.id"
2360
+ },
2361
+ {
2362
+ name: "sys_user_preference_self",
2363
+ object: "sys_user_preference",
2364
+ operation: "all",
2365
+ using: "user_id = current_user.id"
2366
+ },
2367
+ {
2368
+ name: "sys_api_key_self",
2369
+ object: "sys_api_key",
2370
+ operation: "all",
2371
+ using: "user_id = current_user.id"
2372
+ },
2373
+ {
2374
+ name: "sys_device_code_self",
2375
+ object: "sys_device_code",
2376
+ operation: "all",
2377
+ using: "user_id = current_user.id"
2378
+ },
2379
+ {
2380
+ name: "sys_oauth_access_token_self",
2381
+ object: "sys_oauth_access_token",
2382
+ operation: "select",
2383
+ using: "user_id = current_user.id"
2384
+ },
2385
+ {
2386
+ name: "sys_oauth_refresh_token_self",
2387
+ object: "sys_oauth_refresh_token",
2388
+ operation: "select",
2389
+ using: "user_id = current_user.id"
2390
+ },
2391
+ {
2392
+ name: "sys_oauth_consent_self",
2393
+ object: "sys_oauth_consent",
2394
+ operation: "all",
2395
+ using: "user_id = current_user.id"
2396
+ },
2397
+ // OAuth applications a user has registered themselves (Account →
2398
+ // Developer → OAuth Applications). `sys_oauth_application` has no
2399
+ // `organization_id`, so without this carve-out the wildcard
2400
+ // `tenant_isolation` policy returns zero rows even for the owner.
2401
+ {
2402
+ name: "sys_oauth_application_self",
2403
+ object: "sys_oauth_application",
2404
+ operation: "all",
2405
+ using: "user_id = current_user.id"
2406
+ }
2407
+ ]
2408
+ }),
2409
+ import_security.PermissionSetSchema.parse({
2410
+ name: "viewer_readonly",
2411
+ label: "Viewer \u2014 Read-Only",
2412
+ isProfile: true,
2413
+ objects: {
2414
+ "*": {
2415
+ allowRead: true,
2416
+ allowCreate: false,
2417
+ allowEdit: false,
2418
+ allowDelete: false
2419
+ },
2420
+ // Belt-and-suspenders: explicit deny on managed objects even though
2421
+ // the wildcard already denies — keeps the policy readable when
2422
+ // future relaxations might widen the wildcard.
2423
+ ...denyWritesOnManagedObjects()
2424
+ },
2425
+ rowLevelSecurity: [
2426
+ {
2427
+ name: "tenant_isolation",
2428
+ object: "*",
2429
+ operation: "select",
2430
+ using: "organization_id = current_user.organization_id"
2431
+ },
2432
+ {
2433
+ name: "sys_organization_self",
2434
+ object: "sys_organization",
2435
+ operation: "select",
2436
+ using: "id = current_user.organization_id"
2437
+ },
2438
+ {
2439
+ name: "sys_user_self",
2440
+ object: "sys_user",
2441
+ operation: "select",
2442
+ using: "id = current_user.id"
2443
+ },
2444
+ // Org collaborators (read-only): see `sys_user_org_members` in
2445
+ // `member_default` for rationale.
2446
+ {
2447
+ name: "sys_user_org_members",
2448
+ object: "sys_user",
2449
+ operation: "select",
2450
+ using: "id IN (current_user.org_user_ids)"
2451
+ },
2452
+ {
2453
+ name: "sys_session_self",
2454
+ object: "sys_session",
2455
+ operation: "select",
2456
+ using: "user_id = current_user.id"
2457
+ },
2458
+ {
2459
+ name: "sys_account_self",
2460
+ object: "sys_account",
2461
+ operation: "select",
2462
+ using: "user_id = current_user.id"
2463
+ },
2464
+ {
2465
+ name: "sys_team_member_self",
2466
+ object: "sys_team_member",
2467
+ operation: "select",
2468
+ using: "user_id = current_user.id"
2469
+ },
2470
+ {
2471
+ name: "sys_two_factor_self",
2472
+ object: "sys_two_factor",
2473
+ operation: "select",
2474
+ using: "user_id = current_user.id"
2475
+ },
2476
+ {
2477
+ name: "sys_user_preference_self",
2478
+ object: "sys_user_preference",
2479
+ operation: "select",
2480
+ using: "user_id = current_user.id"
2481
+ },
2482
+ {
2483
+ name: "sys_api_key_self",
2484
+ object: "sys_api_key",
2485
+ operation: "select",
2486
+ using: "user_id = current_user.id"
2487
+ },
2488
+ {
2489
+ name: "sys_device_code_self",
2490
+ object: "sys_device_code",
2491
+ operation: "select",
2492
+ using: "user_id = current_user.id"
2493
+ },
2494
+ {
2495
+ name: "sys_oauth_access_token_self",
2496
+ object: "sys_oauth_access_token",
2497
+ operation: "select",
2498
+ using: "user_id = current_user.id"
2499
+ },
2500
+ {
2501
+ name: "sys_oauth_refresh_token_self",
2502
+ object: "sys_oauth_refresh_token",
2503
+ operation: "select",
2504
+ using: "user_id = current_user.id"
2505
+ },
2506
+ {
2507
+ name: "sys_oauth_consent_self",
2508
+ object: "sys_oauth_consent",
2509
+ operation: "select",
2510
+ using: "user_id = current_user.id"
2511
+ }
2512
+ ]
2513
+ })
2514
+ ];
2515
+
623
2516
  // src/manifest.ts
624
- var import_security = require("@objectstack/platform-objects/security");
625
2517
  var SECURITY_PLUGIN_ID = "com.objectstack.plugin-security";
626
2518
  var SECURITY_PLUGIN_VERSION = "1.0.0";
627
2519
  var securityObjects = [
628
- import_security.SysRole,
629
- import_security.SysPermissionSet,
630
- import_security.SysUserPermissionSet,
631
- import_security.SysRolePermissionSet
2520
+ SysRole,
2521
+ SysPermissionSet,
2522
+ SysUserPermissionSet,
2523
+ SysRolePermissionSet
632
2524
  ];
633
- var securityDefaultPermissionSets = import_security.defaultPermissionSets;
2525
+ var securityDefaultPermissionSets = defaultPermissionSets;
634
2526
  var securityPluginManifestHeader = {
635
2527
  id: SECURITY_PLUGIN_ID,
636
2528
  namespace: "sys",
@@ -696,8 +2588,36 @@ var SecurityPlugin = class {
696
2588
  // Permission sets ride along on the manifest so the metadata service
697
2589
  // can resolve them by name when SecurityPlugin middleware queries
698
2590
  // `metadata.list('permissions')`.
699
- permissions: this.bootstrapPermissionSets
2591
+ permissions: this.bootstrapPermissionSets,
2592
+ // ADR-0029 D7 — contribute the RBAC entries into the Setup app's
2593
+ // `group_access_control` slot. This plugin owns these objects (K2), so it
2594
+ // ships their menu too; when the plugin is absent the entries don't appear.
2595
+ navigationContributions: [
2596
+ {
2597
+ app: "setup",
2598
+ group: "group_access_control",
2599
+ priority: 100,
2600
+ items: [
2601
+ { id: "nav_roles", type: "object", label: "Roles", objectName: "sys_role", icon: "shield-check" },
2602
+ { id: "nav_permission_sets", type: "object", label: "Permission Sets", objectName: "sys_permission_set", icon: "lock" }
2603
+ ]
2604
+ }
2605
+ ]
700
2606
  });
2607
+ if (typeof ctx.hook === "function") {
2608
+ ctx.hook("kernel:ready", async () => {
2609
+ try {
2610
+ const i18n = ctx.getService("i18n");
2611
+ if (i18n && typeof i18n.loadTranslations === "function") {
2612
+ const { SecurityTranslations: SecurityTranslations2 } = await Promise.resolve().then(() => (init_translations(), translations_exports));
2613
+ for (const [locale, data] of Object.entries(SecurityTranslations2)) {
2614
+ i18n.loadTranslations(locale, data);
2615
+ }
2616
+ }
2617
+ } catch {
2618
+ }
2619
+ });
2620
+ }
701
2621
  ctx.logger.info("Security Plugin initialized", {
702
2622
  defaultPermissionSets: this.bootstrapPermissionSets.map((p) => p.name)
703
2623
  });