@astram/identity 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth-facts-keys.d.ts +3 -0
- package/dist/auth-facts-keys.d.ts.map +1 -0
- package/dist/auth-facts-keys.js +24 -0
- package/dist/cross-tenant-grant.object.d.ts +28 -0
- package/dist/cross-tenant-grant.object.d.ts.map +1 -0
- package/dist/cross-tenant-grant.object.js +64 -0
- package/dist/features/access/access-admin.access.d.ts +3 -0
- package/dist/features/access/access-admin.access.d.ts.map +1 -0
- package/dist/features/access/access-admin.access.js +27 -0
- package/dist/features/access/company-admin.access.d.ts +3 -0
- package/dist/features/access/company-admin.access.d.ts.map +1 -0
- package/dist/features/access/company-admin.access.js +31 -0
- package/dist/features/access/index.d.ts +15 -0
- package/dist/features/access/index.d.ts.map +1 -0
- package/dist/features/access/index.js +26 -0
- package/dist/features/access/user-management.access.d.ts +3 -0
- package/dist/features/access/user-management.access.d.ts.map +1 -0
- package/dist/features/access/user-management.access.js +35 -0
- package/dist/features/audit/audit-read.service.d.ts +92 -0
- package/dist/features/audit/audit-read.service.d.ts.map +1 -0
- package/dist/features/audit/audit-read.service.js +97 -0
- package/dist/features/audit/audit.controller.d.ts +35 -0
- package/dist/features/audit/audit.controller.d.ts.map +1 -0
- package/dist/features/audit/audit.controller.js +73 -0
- package/dist/features/audit/directory-label-resolver.d.ts +14 -0
- package/dist/features/audit/directory-label-resolver.d.ts.map +1 -0
- package/dist/features/audit/directory-label-resolver.js +46 -0
- package/dist/features/audit/mariadb-audit-reader.d.ts +46 -0
- package/dist/features/audit/mariadb-audit-reader.d.ts.map +1 -0
- package/dist/features/audit/mariadb-audit-reader.js +116 -0
- package/dist/features/authz/access-bundle-reconcile.job.d.ts +35 -0
- package/dist/features/authz/access-bundle-reconcile.job.d.ts.map +1 -0
- package/dist/features/authz/access-bundle-reconcile.job.js +84 -0
- package/dist/features/authz/access-bundle-store.d.ts +65 -0
- package/dist/features/authz/access-bundle-store.d.ts.map +1 -0
- package/dist/features/authz/access-bundle-store.js +154 -0
- package/dist/features/authz/catalog-bootstrap.provider.d.ts +28 -0
- package/dist/features/authz/catalog-bootstrap.provider.d.ts.map +1 -0
- package/dist/features/authz/catalog-bootstrap.provider.js +85 -0
- package/dist/features/authz/catalog-reconcile.job.d.ts +23 -0
- package/dist/features/authz/catalog-reconcile.job.d.ts.map +1 -0
- package/dist/features/authz/catalog-reconcile.job.js +47 -0
- package/dist/features/authz/db-permission-facts.adapter.d.ts +60 -0
- package/dist/features/authz/db-permission-facts.adapter.d.ts.map +1 -0
- package/dist/features/authz/db-permission-facts.adapter.js +116 -0
- package/dist/features/authz/grant-admin.controller.d.ts +96 -0
- package/dist/features/authz/grant-admin.controller.d.ts.map +1 -0
- package/dist/features/authz/grant-admin.controller.js +209 -0
- package/dist/features/authz/grant-admin.service.d.ts +107 -0
- package/dist/features/authz/grant-admin.service.d.ts.map +1 -0
- package/dist/features/authz/grant-admin.service.js +297 -0
- package/dist/features/authz/mariadb-catalog-store.d.ts +16 -0
- package/dist/features/authz/mariadb-catalog-store.d.ts.map +1 -0
- package/dist/features/authz/mariadb-catalog-store.js +44 -0
- package/dist/features/authz/permission-version.bumper.d.ts +47 -0
- package/dist/features/authz/permission-version.bumper.d.ts.map +1 -0
- package/dist/features/authz/permission-version.bumper.js +68 -0
- package/dist/features/authz/record-authorization.d.ts +31 -0
- package/dist/features/authz/record-authorization.d.ts.map +1 -0
- package/dist/features/authz/record-authorization.js +59 -0
- package/dist/features/authz/session-facts.reader.d.ts +41 -0
- package/dist/features/authz/session-facts.reader.d.ts.map +1 -0
- package/dist/features/authz/session-facts.reader.js +27 -0
- package/dist/features/company/company.controller.d.ts +37 -0
- package/dist/features/company/company.controller.d.ts.map +1 -0
- package/dist/features/company/company.controller.js +100 -0
- package/dist/features/company/company.service.d.ts +25 -0
- package/dist/features/company/company.service.d.ts.map +1 -0
- package/dist/features/company/company.service.js +53 -0
- package/dist/features/cross-tenant/cross-tenant-grant-read.service.d.ts +126 -0
- package/dist/features/cross-tenant/cross-tenant-grant-read.service.d.ts.map +1 -0
- package/dist/features/cross-tenant/cross-tenant-grant-read.service.js +276 -0
- package/dist/features/cross-tenant/cross-tenant-grant.controller.d.ts +65 -0
- package/dist/features/cross-tenant/cross-tenant-grant.controller.d.ts.map +1 -0
- package/dist/features/cross-tenant/cross-tenant-grant.controller.js +250 -0
- package/dist/features/cross-tenant/grant-authorize.d.ts +42 -0
- package/dist/features/cross-tenant/grant-authorize.d.ts.map +1 -0
- package/dist/features/cross-tenant/grant-authorize.js +17 -0
- package/dist/features/identity-feature.module.d.ts +26 -0
- package/dist/features/identity-feature.module.d.ts.map +1 -0
- package/dist/features/identity-feature.module.js +247 -0
- package/dist/features/membership/membership.controller.d.ts +77 -0
- package/dist/features/membership/membership.controller.d.ts.map +1 -0
- package/dist/features/membership/membership.controller.js +174 -0
- package/dist/features/membership/membership.service.d.ts +73 -0
- package/dist/features/membership/membership.service.d.ts.map +1 -0
- package/dist/features/membership/membership.service.js +173 -0
- package/dist/features/membership/posting-version-bump.d.ts +10 -0
- package/dist/features/membership/posting-version-bump.d.ts.map +1 -0
- package/dist/features/membership/posting-version-bump.js +4 -0
- package/dist/features/membership/posting.service.d.ts +59 -0
- package/dist/features/membership/posting.service.d.ts.map +1 -0
- package/dist/features/membership/posting.service.js +181 -0
- package/dist/features/org-structure/org-catalog.controller.d.ts +14 -0
- package/dist/features/org-structure/org-catalog.controller.d.ts.map +1 -0
- package/dist/features/org-structure/org-catalog.controller.js +62 -0
- package/dist/features/org-structure/org-catalog.service.d.ts +19 -0
- package/dist/features/org-structure/org-catalog.service.d.ts.map +1 -0
- package/dist/features/org-structure/org-catalog.service.js +53 -0
- package/dist/features/role/role.controller.d.ts +37 -0
- package/dist/features/role/role.controller.d.ts.map +1 -0
- package/dist/features/role/role.controller.js +104 -0
- package/dist/features/role/role.service.d.ts +24 -0
- package/dist/features/role/role.service.d.ts.map +1 -0
- package/dist/features/role/role.service.js +53 -0
- package/dist/features/shared/http.d.ts +15 -0
- package/dist/features/shared/http.d.ts.map +1 -0
- package/dist/features/shared/http.js +25 -0
- package/dist/features/shared/list-query.d.ts +44 -0
- package/dist/features/shared/list-query.d.ts.map +1 -0
- package/dist/features/shared/list-query.js +53 -0
- package/dist/features/shared/named-option.d.ts +6 -0
- package/dist/features/shared/named-option.d.ts.map +1 -0
- package/dist/features/shared/named-option.js +2 -0
- package/dist/features/shared/record-resolver.d.ts +11 -0
- package/dist/features/shared/record-resolver.d.ts.map +1 -0
- package/dist/features/shared/record-resolver.js +32 -0
- package/dist/features/user/user.controller.d.ts +39 -0
- package/dist/features/user/user.controller.d.ts.map +1 -0
- package/dist/features/user/user.controller.js +104 -0
- package/dist/features/user/user.service.d.ts +26 -0
- package/dist/features/user/user.service.d.ts.map +1 -0
- package/dist/features/user/user.service.js +53 -0
- package/dist/index.d.ts +34 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +66 -33
- package/dist/objects.d.ts +13 -0
- package/dist/objects.d.ts.map +1 -0
- package/dist/objects.js +20 -0
- package/dist/shared-resource.object.d.ts +28 -0
- package/dist/shared-resource.object.d.ts.map +1 -0
- package/dist/shared-resource.object.js +63 -0
- package/migrations/1710000000010-CreateAuditLog.cjs +42 -0
- package/migrations/1710000000011-CreateCrossTenantGrant.cjs +66 -0
- package/migrations/1710000000012-SeedCrossTenantGrantPerms.cjs +86 -0
- package/migrations/1710000000013-CreateSharedResource.cjs +30 -0
- package/migrations/1710000000099-SeedDevIdentity.cjs +130 -0
- package/migrations/1710000000100-SeedDevTenantBAndRoot.cjs +212 -0
- package/package.json +4 -3
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
* the `identityObjects` array the engine registers. Physical names unchanged from the
|
|
5
5
|
* app's originals (rename is a later sub-plan).
|
|
6
6
|
*/
|
|
7
|
-
import type { ObjectTypeInput } from '@astram/engine';
|
|
8
7
|
export { Company } from './company.object.js';
|
|
9
8
|
export { User } from './user.object.js';
|
|
10
9
|
export { Role } from './role.object.js';
|
|
@@ -15,6 +14,38 @@ export { MembershipPosting } from './membership-posting.object.js';
|
|
|
15
14
|
export { CompanyDepartment } from './company-department.object.js';
|
|
16
15
|
export { CompanyBranch } from './company-branch.object.js';
|
|
17
16
|
export { identityMigrationsDir } from './migrations-dir.js';
|
|
18
|
-
/**
|
|
19
|
-
|
|
17
|
+
/**
|
|
18
|
+
* All identity objects, registered with the engine at boot (same set + order as before).
|
|
19
|
+
* Defined in the leaf module `./objects.js` (imported by BOTH this barrel and
|
|
20
|
+
* `./auth-facts-keys.ts`) so the auth-facts key projection has no module-init cycle.
|
|
21
|
+
*/
|
|
22
|
+
export { identityObjects } from './objects.js';
|
|
23
|
+
/**
|
|
24
|
+
* The framework identity permission-key universe, projected from `identityObjects` via
|
|
25
|
+
* `@astram/guard`'s registry bridge. Pass these into
|
|
26
|
+
* `ShieldModule.forRoot({ auth: true, identityActionKeys, identityControlPlaneKeys })`
|
|
27
|
+
* so guard's auth-facts adapter can expand `identity:*` grants + union root's
|
|
28
|
+
* control-plane keys WITHOUT guard importing this package.
|
|
29
|
+
*/
|
|
30
|
+
export { identityAuthActionKeys, identityControlPlaneKeys } from './auth-facts-keys.js';
|
|
31
|
+
/**
|
|
32
|
+
* The framework-owned identity FEATURE module (company/user/role CRUD). The consumer imports
|
|
33
|
+
* `IdentityFeatureModule.forRoot()` in one line alongside `ShieldModule.forRoot(...)` (ADR-0026);
|
|
34
|
+
* `forRoot()` self-wires the engine (`EngineModule.forRoot` over the app's APP_DB_* env), registers
|
|
35
|
+
* `identityObjects`, and mounts the CRUD controllers — no separate engine setup needed.
|
|
36
|
+
*/
|
|
37
|
+
export { IdentityFeatureModule } from './features/identity-feature.module.js';
|
|
38
|
+
/**
|
|
39
|
+
* The record-tier authorization wiring: the lean grant-read pool factory + the helper that
|
|
40
|
+
* assembles `RecordAuthorizationInputs` for `ShieldModule.forRoot({ recordAuthorization })`.
|
|
41
|
+
* Once wired, `decide()` is real (the framework's record-tier adapters run over the identity
|
|
42
|
+
* app schema) instead of the deny-everything stub.
|
|
43
|
+
*/
|
|
44
|
+
export { createIdentityAuthzPool, buildIdentityRecordAuthorization, } from './features/authz/record-authorization.js';
|
|
45
|
+
/**
|
|
46
|
+
* The ADR-0011 per-request session-facts reader (ALS hot path), re-exported so a consumer
|
|
47
|
+
* that wants to construct it directly (rather than via `buildIdentityRecordAuthorization`)
|
|
48
|
+
* can do so.
|
|
49
|
+
*/
|
|
50
|
+
export { AlsSessionFactsReader } from './features/authz/session-facts.reader.js';
|
|
20
51
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE5D;;;;GAIG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C;;;;;;GAMG;AACH,OAAO,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAExF;;;;;GAKG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAE9E;;;;;GAKG;AACH,OAAO,EACL,uBAAuB,EACvB,gCAAgC,GACjC,MAAM,0CAA0C,CAAC;AAElD;;;;GAIG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,37 +1,70 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.identityObjects = exports.identityMigrationsDir = exports.CompanyBranch = exports.CompanyDepartment = exports.MembershipPosting = exports.CompanyMembership = exports.Branch = exports.Department = exports.Role = exports.User = exports.Company = void 0;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
Object.defineProperty(exports, "CompanyDepartment", { enumerable: true, get: function () { return company_department_object_js_2.CompanyDepartment; } });
|
|
29
|
-
var company_branch_object_js_2 = require("./company-branch.object.js");
|
|
30
|
-
Object.defineProperty(exports, "CompanyBranch", { enumerable: true, get: function () { return company_branch_object_js_2.CompanyBranch; } });
|
|
3
|
+
exports.AlsSessionFactsReader = exports.buildIdentityRecordAuthorization = exports.createIdentityAuthzPool = exports.IdentityFeatureModule = exports.identityControlPlaneKeys = exports.identityAuthActionKeys = exports.identityObjects = exports.identityMigrationsDir = exports.CompanyBranch = exports.CompanyDepartment = exports.MembershipPosting = exports.CompanyMembership = exports.Branch = exports.Department = exports.Role = exports.User = exports.Company = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* @astram/identity — the framework-owned identity + tenancy model.
|
|
6
|
+
* The 9 identity object classes (relocated from the consumer app in Sub-plan B) +
|
|
7
|
+
* the `identityObjects` array the engine registers. Physical names unchanged from the
|
|
8
|
+
* app's originals (rename is a later sub-plan).
|
|
9
|
+
*/
|
|
10
|
+
var company_object_js_1 = require("./company.object.js");
|
|
11
|
+
Object.defineProperty(exports, "Company", { enumerable: true, get: function () { return company_object_js_1.Company; } });
|
|
12
|
+
var user_object_js_1 = require("./user.object.js");
|
|
13
|
+
Object.defineProperty(exports, "User", { enumerable: true, get: function () { return user_object_js_1.User; } });
|
|
14
|
+
var role_object_js_1 = require("./role.object.js");
|
|
15
|
+
Object.defineProperty(exports, "Role", { enumerable: true, get: function () { return role_object_js_1.Role; } });
|
|
16
|
+
var department_object_js_1 = require("./department.object.js");
|
|
17
|
+
Object.defineProperty(exports, "Department", { enumerable: true, get: function () { return department_object_js_1.Department; } });
|
|
18
|
+
var branch_object_js_1 = require("./branch.object.js");
|
|
19
|
+
Object.defineProperty(exports, "Branch", { enumerable: true, get: function () { return branch_object_js_1.Branch; } });
|
|
20
|
+
var company_membership_object_js_1 = require("./company-membership.object.js");
|
|
21
|
+
Object.defineProperty(exports, "CompanyMembership", { enumerable: true, get: function () { return company_membership_object_js_1.CompanyMembership; } });
|
|
22
|
+
var membership_posting_object_js_1 = require("./membership-posting.object.js");
|
|
23
|
+
Object.defineProperty(exports, "MembershipPosting", { enumerable: true, get: function () { return membership_posting_object_js_1.MembershipPosting; } });
|
|
24
|
+
var company_department_object_js_1 = require("./company-department.object.js");
|
|
25
|
+
Object.defineProperty(exports, "CompanyDepartment", { enumerable: true, get: function () { return company_department_object_js_1.CompanyDepartment; } });
|
|
26
|
+
var company_branch_object_js_1 = require("./company-branch.object.js");
|
|
27
|
+
Object.defineProperty(exports, "CompanyBranch", { enumerable: true, get: function () { return company_branch_object_js_1.CompanyBranch; } });
|
|
31
28
|
var migrations_dir_js_1 = require("./migrations-dir.js");
|
|
32
29
|
Object.defineProperty(exports, "identityMigrationsDir", { enumerable: true, get: function () { return migrations_dir_js_1.identityMigrationsDir; } });
|
|
33
|
-
/**
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
/**
|
|
31
|
+
* All identity objects, registered with the engine at boot (same set + order as before).
|
|
32
|
+
* Defined in the leaf module `./objects.js` (imported by BOTH this barrel and
|
|
33
|
+
* `./auth-facts-keys.ts`) so the auth-facts key projection has no module-init cycle.
|
|
34
|
+
*/
|
|
35
|
+
var objects_js_1 = require("./objects.js");
|
|
36
|
+
Object.defineProperty(exports, "identityObjects", { enumerable: true, get: function () { return objects_js_1.identityObjects; } });
|
|
37
|
+
/**
|
|
38
|
+
* The framework identity permission-key universe, projected from `identityObjects` via
|
|
39
|
+
* `@astram/guard`'s registry bridge. Pass these into
|
|
40
|
+
* `ShieldModule.forRoot({ auth: true, identityActionKeys, identityControlPlaneKeys })`
|
|
41
|
+
* so guard's auth-facts adapter can expand `identity:*` grants + union root's
|
|
42
|
+
* control-plane keys WITHOUT guard importing this package.
|
|
43
|
+
*/
|
|
44
|
+
var auth_facts_keys_js_1 = require("./auth-facts-keys.js");
|
|
45
|
+
Object.defineProperty(exports, "identityAuthActionKeys", { enumerable: true, get: function () { return auth_facts_keys_js_1.identityAuthActionKeys; } });
|
|
46
|
+
Object.defineProperty(exports, "identityControlPlaneKeys", { enumerable: true, get: function () { return auth_facts_keys_js_1.identityControlPlaneKeys; } });
|
|
47
|
+
/**
|
|
48
|
+
* The framework-owned identity FEATURE module (company/user/role CRUD). The consumer imports
|
|
49
|
+
* `IdentityFeatureModule.forRoot()` in one line alongside `ShieldModule.forRoot(...)` (ADR-0026);
|
|
50
|
+
* `forRoot()` self-wires the engine (`EngineModule.forRoot` over the app's APP_DB_* env), registers
|
|
51
|
+
* `identityObjects`, and mounts the CRUD controllers — no separate engine setup needed.
|
|
52
|
+
*/
|
|
53
|
+
var identity_feature_module_js_1 = require("./features/identity-feature.module.js");
|
|
54
|
+
Object.defineProperty(exports, "IdentityFeatureModule", { enumerable: true, get: function () { return identity_feature_module_js_1.IdentityFeatureModule; } });
|
|
55
|
+
/**
|
|
56
|
+
* The record-tier authorization wiring: the lean grant-read pool factory + the helper that
|
|
57
|
+
* assembles `RecordAuthorizationInputs` for `ShieldModule.forRoot({ recordAuthorization })`.
|
|
58
|
+
* Once wired, `decide()` is real (the framework's record-tier adapters run over the identity
|
|
59
|
+
* app schema) instead of the deny-everything stub.
|
|
60
|
+
*/
|
|
61
|
+
var record_authorization_js_1 = require("./features/authz/record-authorization.js");
|
|
62
|
+
Object.defineProperty(exports, "createIdentityAuthzPool", { enumerable: true, get: function () { return record_authorization_js_1.createIdentityAuthzPool; } });
|
|
63
|
+
Object.defineProperty(exports, "buildIdentityRecordAuthorization", { enumerable: true, get: function () { return record_authorization_js_1.buildIdentityRecordAuthorization; } });
|
|
64
|
+
/**
|
|
65
|
+
* The ADR-0011 per-request session-facts reader (ALS hot path), re-exported so a consumer
|
|
66
|
+
* that wants to construct it directly (rather than via `buildIdentityRecordAuthorization`)
|
|
67
|
+
* can do so.
|
|
68
|
+
*/
|
|
69
|
+
var session_facts_reader_js_1 = require("./features/authz/session-facts.reader.js");
|
|
70
|
+
Object.defineProperty(exports, "AlsSessionFactsReader", { enumerable: true, get: function () { return session_facts_reader_js_1.AlsSessionFactsReader; } });
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The framework identity object-type set — extracted here (rather than defined in
|
|
3
|
+
* `./index.ts`) so it can be imported WITHOUT a module-init cycle. `./auth-facts-keys.ts`
|
|
4
|
+
* builds the permission-key registry at module-eval time from this array; had it imported
|
|
5
|
+
* `identityObjects` from `./index.ts` (which re-exports `auth-facts-keys.ts`), the array
|
|
6
|
+
* could still be `undefined` when the bridge runs → a throw. Both `./index.ts` and
|
|
7
|
+
* `./auth-facts-keys.ts` import the array from THIS leaf module, which depends only on the
|
|
8
|
+
* object classes — so it is fully initialized before either consumer evaluates.
|
|
9
|
+
*/
|
|
10
|
+
import type { ObjectTypeInput } from '@astram/engine';
|
|
11
|
+
/** All identity objects, registered with the engine at boot (same set + order as before). */
|
|
12
|
+
export declare const identityObjects: readonly ObjectTypeInput[];
|
|
13
|
+
//# sourceMappingURL=objects.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"objects.d.ts","sourceRoot":"","sources":["../src/objects.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAatD,6FAA6F;AAC7F,eAAO,MAAM,eAAe,EAAE,SAAS,eAAe,EAIrD,CAAC"}
|
package/dist/objects.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.identityObjects = void 0;
|
|
4
|
+
const company_object_js_1 = require("./company.object.js");
|
|
5
|
+
const user_object_js_1 = require("./user.object.js");
|
|
6
|
+
const role_object_js_1 = require("./role.object.js");
|
|
7
|
+
const department_object_js_1 = require("./department.object.js");
|
|
8
|
+
const branch_object_js_1 = require("./branch.object.js");
|
|
9
|
+
const company_membership_object_js_1 = require("./company-membership.object.js");
|
|
10
|
+
const membership_posting_object_js_1 = require("./membership-posting.object.js");
|
|
11
|
+
const company_department_object_js_1 = require("./company-department.object.js");
|
|
12
|
+
const company_branch_object_js_1 = require("./company-branch.object.js");
|
|
13
|
+
const cross_tenant_grant_object_js_1 = require("./cross-tenant-grant.object.js");
|
|
14
|
+
const shared_resource_object_js_1 = require("./shared-resource.object.js");
|
|
15
|
+
/** All identity objects, registered with the engine at boot (same set + order as before). */
|
|
16
|
+
exports.identityObjects = [
|
|
17
|
+
company_object_js_1.Company, user_object_js_1.User, role_object_js_1.Role, department_object_js_1.Department, branch_object_js_1.Branch,
|
|
18
|
+
company_membership_object_js_1.CompanyMembership, membership_posting_object_js_1.MembershipPosting, company_department_object_js_1.CompanyDepartment, company_branch_object_js_1.CompanyBranch,
|
|
19
|
+
cross_tenant_grant_object_js_1.CrossTenantGrant, shared_resource_object_js_1.SharedResource,
|
|
20
|
+
];
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* shared_resource — a MINIMAL, framework-owned `tenantScoped` demo object type.
|
|
3
|
+
*
|
|
4
|
+
* WHY IT EXISTS: it is the first (and currently only) `tenantScoped`/offerable type in the
|
|
5
|
+
* framework. All nine identity types are `system` (non-offerable, grant-governed), so the
|
|
6
|
+
* cross-tenant grant flow would have NOTHING to offer without an offerable resource type.
|
|
7
|
+
* `shared_resource` fills that gap so a cross-tenant grant has a real, shareable target.
|
|
8
|
+
*
|
|
9
|
+
* ACCESS: `{ tenantScoped: true, plane: 'data' }` — tenant business content, not
|
|
10
|
+
* administration. `tenantScoped` (mutually exclusive with `system`) means the engine
|
|
11
|
+
* enforces per-tenant isolation via the server-stamped `sr_tenant_id` column below; the
|
|
12
|
+
* engine's registry REQUIRES exactly one field with `trusted: 'tenantId'` for a
|
|
13
|
+
* tenantScoped type (else boot fails), which `tenantId` satisfies. `plane: 'data'` denies
|
|
14
|
+
* the capped root by construction — correct for a shareable business resource, since the
|
|
15
|
+
* cross-tenant flow reaches it through per-tenant admins, never root.
|
|
16
|
+
*
|
|
17
|
+
* TENANT ISOLATION IS SERVER-STAMPED: `sr_tenant_id` is `trusted: 'tenantId'` — the engine
|
|
18
|
+
* OVERWRITES it from the session's tenant on every write; a client can never forge it.
|
|
19
|
+
*
|
|
20
|
+
* Keep this tiny — its only job is to be an offerable type. Not a real business feature.
|
|
21
|
+
*/
|
|
22
|
+
export declare class SharedResource {
|
|
23
|
+
uuid: string;
|
|
24
|
+
/** Server-stamped tenant boundary (engine overwrites from the session; never client). */
|
|
25
|
+
tenantId: string;
|
|
26
|
+
label: string;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=shared-resource.object.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared-resource.object.d.ts","sourceRoot":"","sources":["../src/shared-resource.object.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAOa,cAAc;IAEzB,IAAI,EAAG,MAAM,CAAC;IAEd,yFAAyF;IAEzF,QAAQ,EAAG,MAAM,CAAC;IAGlB,KAAK,EAAG,MAAM,CAAC;CAChB"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.SharedResource = void 0;
|
|
13
|
+
const engine_1 = require("@astram/engine");
|
|
14
|
+
const schema_1 = require("./schema");
|
|
15
|
+
/**
|
|
16
|
+
* shared_resource — a MINIMAL, framework-owned `tenantScoped` demo object type.
|
|
17
|
+
*
|
|
18
|
+
* WHY IT EXISTS: it is the first (and currently only) `tenantScoped`/offerable type in the
|
|
19
|
+
* framework. All nine identity types are `system` (non-offerable, grant-governed), so the
|
|
20
|
+
* cross-tenant grant flow would have NOTHING to offer without an offerable resource type.
|
|
21
|
+
* `shared_resource` fills that gap so a cross-tenant grant has a real, shareable target.
|
|
22
|
+
*
|
|
23
|
+
* ACCESS: `{ tenantScoped: true, plane: 'data' }` — tenant business content, not
|
|
24
|
+
* administration. `tenantScoped` (mutually exclusive with `system`) means the engine
|
|
25
|
+
* enforces per-tenant isolation via the server-stamped `sr_tenant_id` column below; the
|
|
26
|
+
* engine's registry REQUIRES exactly one field with `trusted: 'tenantId'` for a
|
|
27
|
+
* tenantScoped type (else boot fails), which `tenantId` satisfies. `plane: 'data'` denies
|
|
28
|
+
* the capped root by construction — correct for a shareable business resource, since the
|
|
29
|
+
* cross-tenant flow reaches it through per-tenant admins, never root.
|
|
30
|
+
*
|
|
31
|
+
* TENANT ISOLATION IS SERVER-STAMPED: `sr_tenant_id` is `trusted: 'tenantId'` — the engine
|
|
32
|
+
* OVERWRITES it from the session's tenant on every write; a client can never forge it.
|
|
33
|
+
*
|
|
34
|
+
* Keep this tiny — its only job is to be an offerable type. Not a real business feature.
|
|
35
|
+
*/
|
|
36
|
+
let SharedResource = class SharedResource {
|
|
37
|
+
uuid;
|
|
38
|
+
/** Server-stamped tenant boundary (engine overwrites from the session; never client). */
|
|
39
|
+
tenantId;
|
|
40
|
+
label;
|
|
41
|
+
};
|
|
42
|
+
exports.SharedResource = SharedResource;
|
|
43
|
+
__decorate([
|
|
44
|
+
(0, engine_1.Field)({ column: 'sr_uuid', type: 'uuid', role: 'primaryKey' }),
|
|
45
|
+
__metadata("design:type", String)
|
|
46
|
+
], SharedResource.prototype, "uuid", void 0);
|
|
47
|
+
__decorate([
|
|
48
|
+
(0, engine_1.Field)({ column: 'sr_tenant_id', type: 'uuid', trusted: 'tenantId' }),
|
|
49
|
+
__metadata("design:type", String)
|
|
50
|
+
], SharedResource.prototype, "tenantId", void 0);
|
|
51
|
+
__decorate([
|
|
52
|
+
(0, engine_1.Field)({ column: 'sr_label', type: 'string', required: true }),
|
|
53
|
+
__metadata("design:type", String)
|
|
54
|
+
], SharedResource.prototype, "label", void 0);
|
|
55
|
+
exports.SharedResource = SharedResource = __decorate([
|
|
56
|
+
(0, engine_1.ObjectType)({
|
|
57
|
+
typeKey: 'shared_resource',
|
|
58
|
+
table: 'shared_resource',
|
|
59
|
+
schema: schema_1.SCHEMA,
|
|
60
|
+
access: { tenantScoped: true, plane: 'data' },
|
|
61
|
+
}),
|
|
62
|
+
(0, engine_1.PrimaryKey)({ kind: 'uuid', column: 'sr_uuid' })
|
|
63
|
+
], SharedResource);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework audit migration — the `audit_log` table (ADR-0014's `shield_audit`
|
|
3
|
+
* security schema), the append-only sink the @astram/engine pipe's mandatory
|
|
4
|
+
* audit stage writes to (via MariaDbAuditSink). No existing framework migration
|
|
5
|
+
* creates `shield_audit` (guard's 1710000000006 creates `shield_authz`, a
|
|
6
|
+
* DIFFERENT schema) — this migration owns both the schema and the table.
|
|
7
|
+
*
|
|
8
|
+
* Columns mirror the MariaDbAuditSink INSERT exactly (al_* prefix): al_uuid,
|
|
9
|
+
* al_type, al_occurred_at_ms, al_actor_id, al_tenant_id, al_action_key,
|
|
10
|
+
* al_resource_json, al_decision_json, al_attributes_json. Append-only by
|
|
11
|
+
* convention — the engine only ever INSERTs; there is no update/delete path
|
|
12
|
+
* in the adapter. Ported from astram3's frozen
|
|
13
|
+
* server/migrations/1720000000002-CreateAuditLog.js. Idempotent.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const SCHEMA = 'shield_audit';
|
|
17
|
+
|
|
18
|
+
async function up({ query }) {
|
|
19
|
+
await query(`CREATE SCHEMA IF NOT EXISTS \`${SCHEMA}\` DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`);
|
|
20
|
+
|
|
21
|
+
await query(`CREATE TABLE IF NOT EXISTS \`${SCHEMA}\`.\`audit_log\` (
|
|
22
|
+
al_uuid VARCHAR(36) NOT NULL,
|
|
23
|
+
al_type VARCHAR(64) NOT NULL,
|
|
24
|
+
al_occurred_at_ms BIGINT NOT NULL,
|
|
25
|
+
al_actor_id VARCHAR(64) NOT NULL,
|
|
26
|
+
al_tenant_id VARCHAR(64) NOT NULL,
|
|
27
|
+
al_action_key VARCHAR(64) NULL,
|
|
28
|
+
al_resource_json JSON NULL,
|
|
29
|
+
al_decision_json JSON NULL,
|
|
30
|
+
al_attributes_json JSON NULL,
|
|
31
|
+
PRIMARY KEY (al_uuid),
|
|
32
|
+
INDEX idx_audit_actor (al_actor_id),
|
|
33
|
+
INDEX idx_audit_tenant (al_tenant_id),
|
|
34
|
+
INDEX idx_audit_time (al_occurred_at_ms)
|
|
35
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function down({ query }) {
|
|
39
|
+
await query(`DROP TABLE IF EXISTS \`${SCHEMA}\`.\`audit_log\``);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = { up, down };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework authz migration — the `shield_authz.cross_tenant_grant` table (ADR-0009). One row
|
|
3
|
+
* = one explicit, consented, scoped grant that lets a GRANTEE tenant's principal reach into
|
|
4
|
+
* the OWNING tenant's object type (or a single record) for a bounded set of actions, until an
|
|
5
|
+
* expiry. Guard's `CrossTenantGrantWriter` (offer/accept/revoke) and
|
|
6
|
+
* `MariaDbCrossTenantGrantAdapter` (findActiveGrant) READ + WRITE this table via
|
|
7
|
+
* `SHIELD_CROSS_TENANT_GRANT_WRITER` / `SHIELD_CROSS_TENANT_GRANT_PORT` — no existing framework
|
|
8
|
+
* migration created it (a framework-ownership gap, like `audit_log` before it). The schema
|
|
9
|
+
* `shield_authz` already exists (guard's 1710000000006-CreateAuthzSchema.cjs creates it) — this
|
|
10
|
+
* migration does NOT recreate the schema, only the table.
|
|
11
|
+
*
|
|
12
|
+
* • Lifecycle bound (who may offer/accept/revoke) lives in code (CrossTenantGrantWriter); this
|
|
13
|
+
* table is pure storage.
|
|
14
|
+
* • A grant is ACTIVE only when `ctg_status = 'accepted'` AND `ctg_revoked_at IS NULL` AND
|
|
15
|
+
* `ctg_expires_at_ms > <now-epoch-ms>` — exactly what the adapter's findActiveGrant filters on.
|
|
16
|
+
* • ⚠️ `ctg_expires_at_ms` is a BIGINT epoch-ms (NOT a TIMESTAMP): compared to a BOUND integer
|
|
17
|
+
* clock value, so expiry is timezone-immune (mirrors the reset-token store's
|
|
18
|
+
* `prt_expires_at_ms` precedent).
|
|
19
|
+
* • `ctg_allowed_actions` is a JSON array of action-key strings — the hard, non-wildcard,
|
|
20
|
+
* non-empty action ceiling enforced at offer time.
|
|
21
|
+
* • `ctg_offered_by` records the owning-tenant admin who offered (who-may-grant audit trail).
|
|
22
|
+
*
|
|
23
|
+
* Columns + `idx_ctg_lookup` ported from astram3's frozen
|
|
24
|
+
* server/migrations/1720000000040-CreateCrossTenantGrant.js; `idx_ctg_grantee` folded in from
|
|
25
|
+
* astram3's frozen server/migrations/1720000000070-AddCtgGranteeIndex.js (astram3 split the
|
|
26
|
+
* grantee-side index across a later migration — the framework creates the table + both indexes
|
|
27
|
+
* in one). Idempotent (`CREATE TABLE IF NOT EXISTS`). No hard-coded UUIDs. `down()` drops the
|
|
28
|
+
* table. Timestamp 1710000000011 sorts after audit (...010) and before the dev seed (...099).
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
const SCHEMA = 'shield_authz';
|
|
32
|
+
const TS = 'TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP';
|
|
33
|
+
|
|
34
|
+
async function up({ query }) {
|
|
35
|
+
await query(`CREATE TABLE IF NOT EXISTS \`${SCHEMA}\`.\`cross_tenant_grant\` (
|
|
36
|
+
ctg_uuid VARCHAR(36) NOT NULL, -- PK (grant uuid)
|
|
37
|
+
ctg_owning_tenant_uuid VARCHAR(36) NOT NULL, -- tenant whose data is reached into / who consents
|
|
38
|
+
ctg_grantee_tenant_uuid VARCHAR(36) NOT NULL, -- tenant reaching in
|
|
39
|
+
ctg_grantee_kind VARCHAR(16) NOT NULL, -- 'user' | 'role' (in the grantee tenant)
|
|
40
|
+
ctg_grantee_uuid VARCHAR(36) NOT NULL, -- the specific user/role uuid
|
|
41
|
+
ctg_type_key VARCHAR(64) NOT NULL, -- object type the grant covers
|
|
42
|
+
ctg_scope VARCHAR(16) NOT NULL, -- 'object_type' | 'record'
|
|
43
|
+
ctg_record_uuid VARCHAR(36) NULL, -- set iff scope='record'
|
|
44
|
+
ctg_allowed_actions JSON NOT NULL, -- hard ceiling: JSON array of action keys (non-wildcard, non-empty)
|
|
45
|
+
ctg_status VARCHAR(16) NOT NULL DEFAULT 'offered', -- 'offered' | 'accepted' | 'revoked' (activates only on 'accepted')
|
|
46
|
+
ctg_offered_by VARCHAR(36) NOT NULL, -- the owning-tenant admin who offered (who-may-grant audit)
|
|
47
|
+
ctg_expires_at_ms BIGINT NOT NULL, -- mandatory FUTURE expiry, epoch-ms (BIGINT — timezone-immune)
|
|
48
|
+
ctg_created_at ${TS},
|
|
49
|
+
ctg_accepted_at TIMESTAMP(0) NULL, -- stamped on accept (audit ts; not security-compared)
|
|
50
|
+
ctg_revoked_at TIMESTAMP(0) NULL, -- stamped on revoke, NULL sentinel = not revoked (either side)
|
|
51
|
+
PRIMARY KEY (ctg_uuid),
|
|
52
|
+
-- The findActiveGrant lookup path: (owning tenant, type, grantee, status) narrows to a
|
|
53
|
+
-- grantee's active grants for an object type fast.
|
|
54
|
+
INDEX idx_ctg_lookup (ctg_owning_tenant_uuid, ctg_type_key, ctg_grantee_uuid, ctg_status),
|
|
55
|
+
-- The grantee-side lookup path: "the offers sitting in MY tenant's inbox" —
|
|
56
|
+
-- WHERE ctg_grantee_tenant_uuid = ? AND ctg_status = 'offered' AND ctg_expires_at_ms > ?.
|
|
57
|
+
-- idx_ctg_lookup is owning-tenant-first and does not serve a grantee-tenant-first scan.
|
|
58
|
+
INDEX idx_ctg_grantee (ctg_grantee_tenant_uuid, ctg_status, ctg_expires_at_ms)
|
|
59
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function down({ query }) {
|
|
63
|
+
await query(`DROP TABLE IF EXISTS \`${SCHEMA}\`.\`cross_tenant_grant\``);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = { up, down };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework authz migration — grant-authority seed for cross-tenant grants. `cross_tenant_grant`'s
|
|
3
|
+
* three permission keys (`identity:cross_tenant_grant:{share,accept,revoke}` — module key is
|
|
4
|
+
* 'identity', see `@astram/guard`'s `identity/identity-actions.ts`'s `MODULE_KEY`) are the
|
|
5
|
+
* data-driven authority for the offer/accept/revoke lifecycle — `decide()` gates them exactly like
|
|
6
|
+
* every other object-type action, now that `cross_tenant_grant` is a registered object type
|
|
7
|
+
* (`packages/identity/src/cross-tenant-grant.object.ts` + `identityObjects`).
|
|
8
|
+
*
|
|
9
|
+
* WHAT THIS MIGRATION DOES vs WHAT IT DOESN'T:
|
|
10
|
+
* - It DOES NOT insert the 3 rows into `shield_authz.permission` (the catalog). The boot
|
|
11
|
+
* catalog reconciler auto-inserts an ACTIVE catalog row for every action the registry
|
|
12
|
+
* declares — and `cross_tenant_grant` is now registered, so the 3 keys appear in the catalog
|
|
13
|
+
* automatically the first time the app boots after this ships. No manual catalog seed needed.
|
|
14
|
+
* - It DOES grant the 3 new keys to every role that CURRENTLY holds coarse identity-admin
|
|
15
|
+
* authority, so an existing admin role doesn't lose the ability to offer/accept/revoke the
|
|
16
|
+
* moment this ships: a role counts as "identity admin" iff it holds any of
|
|
17
|
+
* `identity:company:update` | `identity:company:delete` | `identity:company:*` |
|
|
18
|
+
* `identity:*` | `identity` (mirrors astram3's deleted `DbGrantAuthorityResolver`'s
|
|
19
|
+
* `COMPANY_ADMIN_KEYS` set, matched by EXACT string — the same wildcards `expandGrantKeys`
|
|
20
|
+
* treats as coarse admin grants). This also ensures the framework's dev seed
|
|
21
|
+
* (`1710000000099-SeedDevIdentity.cjs`, which grants its role `identity:*`) gets the 3 keys
|
|
22
|
+
* automatically, so the seeded admin can offer/accept/revoke out of the box. Resolved by
|
|
23
|
+
* `rp_perm_key` STRING MATCH against `role_permission` — no hardcoded uuids, no assumption
|
|
24
|
+
* about which named roles exist on a given tenant.
|
|
25
|
+
*
|
|
26
|
+
* Idempotent: SELECT-before-INSERT via NOT EXISTS on the natural key (rp_role_uuid, rp_perm_key)
|
|
27
|
+
* — re-running inserts ZERO new rows. `down()` deletes only the 3 keys' grants (by key), leaving
|
|
28
|
+
* every other role_permission row untouched.
|
|
29
|
+
*
|
|
30
|
+
* Ported from astram3's frozen `server/migrations/1720000000080-SeedCrossTenantGrantPerms.js`.
|
|
31
|
+
* Timestamp 1710000000012 sorts strictly after the ctg DDL (...011) and before the dev seed
|
|
32
|
+
* (...099) — no dependency ordering concern (this migration only reads/writes `role_permission`,
|
|
33
|
+
* a table created by guard's 1710000000006-CreateAuthzSchema.cjs, and does not depend on the
|
|
34
|
+
* `cross_tenant_grant` object type's runtime registration — the catalog row for these keys is
|
|
35
|
+
* optional at grant-write time; `role_permission` has no FK to `permission`).
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
const AUTHZ = 'shield_authz';
|
|
39
|
+
const MODULE = 'identity';
|
|
40
|
+
const TYPE_KEY = 'cross_tenant_grant';
|
|
41
|
+
const NEW_ACTIONS = ['share', 'accept', 'revoke'];
|
|
42
|
+
const NEW_KEYS = NEW_ACTIONS.map((a) => `${MODULE}:${TYPE_KEY}:${a}`);
|
|
43
|
+
|
|
44
|
+
/** Coarse identity-admin keys — matched by exact string, mirroring astram3's deleted
|
|
45
|
+
* `DbGrantAuthorityResolver`'s `COMPANY_ADMIN_KEYS`. */
|
|
46
|
+
const OLD_ADMIN_KEYS = [
|
|
47
|
+
'identity:company:update',
|
|
48
|
+
'identity:company:delete',
|
|
49
|
+
'identity:company:*',
|
|
50
|
+
'identity:*',
|
|
51
|
+
'identity',
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
async function up({ query }) {
|
|
55
|
+
// Every DISTINCT role currently holding ANY of the coarse identity-admin keys.
|
|
56
|
+
const oldAdminKeyPlaceholders = OLD_ADMIN_KEYS.map(() => '?').join(', ');
|
|
57
|
+
const adminRoles = await query(
|
|
58
|
+
`SELECT DISTINCT rp_role_uuid AS role_uuid
|
|
59
|
+
FROM \`${AUTHZ}\`.\`role_permission\`
|
|
60
|
+
WHERE rp_perm_key IN (${oldAdminKeyPlaceholders})`,
|
|
61
|
+
[...OLD_ADMIN_KEYS],
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
for (const { role_uuid: roleUuid } of adminRoles) {
|
|
65
|
+
for (const key of NEW_KEYS) {
|
|
66
|
+
await query(
|
|
67
|
+
`INSERT INTO \`${AUTHZ}\`.\`role_permission\` (rp_uuid, rp_role_uuid, rp_perm_key)
|
|
68
|
+
SELECT UUID(), ?, ?
|
|
69
|
+
WHERE NOT EXISTS (
|
|
70
|
+
SELECT 1 FROM \`${AUTHZ}\`.\`role_permission\`
|
|
71
|
+
WHERE rp_role_uuid = ? AND rp_perm_key = ?)`,
|
|
72
|
+
[roleUuid, key, roleUuid, key],
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function down({ query }) {
|
|
79
|
+
const keyPlaceholders = NEW_KEYS.map(() => '?').join(', ');
|
|
80
|
+
await query(
|
|
81
|
+
`DELETE FROM \`${AUTHZ}\`.\`role_permission\` WHERE rp_perm_key IN (${keyPlaceholders})`,
|
|
82
|
+
[...NEW_KEYS],
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
module.exports = { up, down };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework migration — the `shield_app`.`shared_resource` table.
|
|
3
|
+
*
|
|
4
|
+
* `shared_resource` is the framework's minimal `tenantScoped`/offerable demo object type
|
|
5
|
+
* (see `src/shared-resource.object.ts`): the first offerable resource so cross-tenant
|
|
6
|
+
* grants have something to offer (all nine identity types are `system`/non-offerable).
|
|
7
|
+
*
|
|
8
|
+
* Columns EXACTLY mirror the @ObjectType's declared @Field columns (entity↔migration sync
|
|
9
|
+
* is absolute): sr_uuid (PK), sr_tenant_id (trusted:'tenantId', server-stamped), sr_label.
|
|
10
|
+
* The `shield_app` schema already exists (created by 1710000000001) — this migration owns
|
|
11
|
+
* only the table, not the schema. Idempotent (IF NOT EXISTS); down() drops the table.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const SCHEMA = 'shield_app';
|
|
15
|
+
|
|
16
|
+
async function up({ query }) {
|
|
17
|
+
await query(`CREATE TABLE IF NOT EXISTS \`${SCHEMA}\`.\`shared_resource\` (
|
|
18
|
+
sr_uuid VARCHAR(36) NOT NULL,
|
|
19
|
+
sr_tenant_id VARCHAR(36) NOT NULL,
|
|
20
|
+
sr_label VARCHAR(255) NOT NULL,
|
|
21
|
+
PRIMARY KEY (sr_uuid),
|
|
22
|
+
INDEX idx_sr_tenant (sr_tenant_id)
|
|
23
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function down({ query }) {
|
|
27
|
+
await query(`DROP TABLE IF EXISTS \`${SCHEMA}\`.\`shared_resource\``);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = { up, down };
|