@astram/identity 0.12.0 → 0.14.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/features/authz/grant-admin.service.d.ts +1 -1
- package/dist/features/authz/grant-admin.service.d.ts.map +1 -1
- package/dist/features/authz/grant-admin.service.js +8 -0
- package/dist/features/authz/grant-ceiling.d.ts +11 -3
- package/dist/features/authz/grant-ceiling.d.ts.map +1 -1
- package/dist/features/authz/grant-ceiling.js +20 -3
- package/dist/features/authz/record-authorization.d.ts +9 -1
- package/dist/features/authz/record-authorization.d.ts.map +1 -1
- package/dist/features/authz/record-authorization.js +24 -3
- package/dist/features/company/company-hierarchy.service.d.ts +6 -0
- package/dist/features/company/company-hierarchy.service.d.ts.map +1 -1
- package/dist/features/company/company-hierarchy.service.js +19 -0
- package/dist/features/identity-feature.module.d.ts +7 -1
- package/dist/features/identity-feature.module.d.ts.map +1 -1
- package/dist/features/identity-feature.module.js +25 -5
- package/dist/features/membership/membership.service.d.ts +9 -0
- package/dist/features/membership/membership.service.d.ts.map +1 -1
- package/dist/features/membership/membership.service.js +40 -0
- package/dist/features/membership/posting.service.d.ts +18 -0
- package/dist/features/membership/posting.service.d.ts.map +1 -1
- package/dist/features/membership/posting.service.js +85 -27
- package/dist/features/org-bootstrap/org-bootstrap-error.d.ts +20 -0
- package/dist/features/org-bootstrap/org-bootstrap-error.d.ts.map +1 -0
- package/dist/features/org-bootstrap/org-bootstrap-error.js +24 -0
- package/dist/features/org-bootstrap/org-bootstrap-file.d.ts +58 -0
- package/dist/features/org-bootstrap/org-bootstrap-file.d.ts.map +1 -0
- package/dist/features/org-bootstrap/org-bootstrap-file.js +323 -0
- package/dist/features/org-bootstrap/org-bootstrap-plan.d.ts +79 -0
- package/dist/features/org-bootstrap/org-bootstrap-plan.d.ts.map +1 -0
- package/dist/features/org-bootstrap/org-bootstrap-plan.js +288 -0
- package/dist/features/org-bootstrap/org-bootstrap-runner.harness.d.ts +259 -0
- package/dist/features/org-bootstrap/org-bootstrap-runner.harness.d.ts.map +1 -0
- package/dist/features/org-bootstrap/org-bootstrap-runner.harness.js +604 -0
- package/dist/features/org-bootstrap/org-bootstrap-runner.mocks.d.ts +24 -0
- package/dist/features/org-bootstrap/org-bootstrap-runner.mocks.d.ts.map +1 -0
- package/dist/features/org-bootstrap/org-bootstrap-runner.mocks.js +39 -0
- package/dist/features/org-bootstrap/org-bootstrap.manifest.d.ts +19 -0
- package/dist/features/org-bootstrap/org-bootstrap.manifest.d.ts.map +1 -0
- package/dist/features/org-bootstrap/org-bootstrap.manifest.js +30 -0
- package/dist/features/org-bootstrap/org-bootstrap.tokens.d.ts +8 -0
- package/dist/features/org-bootstrap/org-bootstrap.tokens.d.ts.map +1 -0
- package/dist/features/org-bootstrap/org-bootstrap.tokens.js +10 -0
- package/dist/features/org-bootstrap/org-names.d.ts +12 -0
- package/dist/features/org-bootstrap/org-names.d.ts.map +1 -0
- package/dist/features/org-bootstrap/org-names.js +26 -0
- package/dist/features/org-bootstrap/org-state.reader.d.ts +15 -0
- package/dist/features/org-bootstrap/org-state.reader.d.ts.map +1 -0
- package/dist/features/org-bootstrap/org-state.reader.js +225 -0
- package/dist/features/org-bootstrap/root-password-source.d.ts +9 -0
- package/dist/features/org-bootstrap/root-password-source.d.ts.map +1 -0
- package/dist/features/org-bootstrap/root-password-source.js +57 -0
- package/dist/features/org-bootstrap/run-org-bootstrap.d.ts +42 -0
- package/dist/features/org-bootstrap/run-org-bootstrap.d.ts.map +1 -0
- package/dist/features/org-bootstrap/run-org-bootstrap.js +688 -0
- package/dist/features/org-bootstrap/set-password-links.d.ts +30 -0
- package/dist/features/org-bootstrap/set-password-links.d.ts.map +1 -0
- package/dist/features/org-bootstrap/set-password-links.js +55 -0
- package/dist/features/org-structure/org-catalog.service.d.ts +13 -0
- package/dist/features/org-structure/org-catalog.service.d.ts.map +1 -1
- package/dist/features/org-structure/org-catalog.service.js +40 -0
- package/dist/features/role/role-owner-transition.d.ts +17 -0
- package/dist/features/role/role-owner-transition.d.ts.map +1 -0
- package/dist/features/role/role-owner-transition.js +23 -0
- package/dist/features/role/role.controller.d.ts +1 -1
- package/dist/features/role/role.controller.d.ts.map +1 -1
- package/dist/features/role/role.controller.js +6 -4
- package/dist/features/role/role.service.d.ts +8 -2
- package/dist/features/role/role.service.d.ts.map +1 -1
- package/dist/features/role/role.service.js +13 -3
- package/dist/features/user/credential-provisioner.token.d.ts +8 -0
- package/dist/features/user/credential-provisioner.token.d.ts.map +1 -0
- package/dist/features/user/credential-provisioner.token.js +10 -0
- package/dist/features/user/user.controller.d.ts +1 -3
- package/dist/features/user/user.controller.d.ts.map +1 -1
- package/dist/features/user/user.controller.js +2 -2
- package/dist/features/user/user.service.d.ts +28 -5
- package/dist/features/user/user.service.d.ts.map +1 -1
- package/dist/features/user/user.service.js +45 -12
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/internal.d.ts +2 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +3 -1
- package/dist/role.object.d.ts +1 -0
- package/dist/role.object.d.ts.map +1 -1
- package/dist/role.object.js +8 -0
- package/migrations/1710000000101-AddRoleOwnerCompany.cjs +98 -0
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DbMembershipChecker = exports.DbScopeSubtreeResolver = exports.AlsSessionFactsReader = exports.FilesService = exports.IdentityFeatureModule = exports.buildIdentityAuthKeys = exports.identityControlPlaneKeys = exports.identityAuthActionKeys = exports.identityObjects = exports.identityMigrationsDir = exports.StoredFile = exports.ServiceAccount = exports.CompanyBranch = exports.CompanyDepartment = exports.MembershipPosting = exports.CompanyMembership = exports.Branch = exports.Department = exports.Role = exports.User = exports.Company = void 0;
|
|
3
|
+
exports.DbMembershipChecker = exports.DbScopeSubtreeResolver = exports.AlsSessionFactsReader = exports.FilesService = exports.OrgBootstrapError = exports.runOrgBootstrap = exports.IdentityFeatureModule = exports.buildIdentityAuthKeys = exports.identityControlPlaneKeys = exports.identityAuthActionKeys = exports.identityObjects = exports.identityMigrationsDir = exports.StoredFile = exports.ServiceAccount = exports.CompanyBranch = exports.CompanyDepartment = exports.MembershipPosting = exports.CompanyMembership = exports.Branch = exports.Department = exports.Role = exports.User = exports.Company = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* @astram/identity — the framework-owned identity + tenancy model.
|
|
6
6
|
* The 9 identity object classes (relocated from the consumer app in Sub-plan B) +
|
|
@@ -60,6 +60,14 @@ Object.defineProperty(exports, "buildIdentityAuthKeys", { enumerable: true, get:
|
|
|
60
60
|
*/
|
|
61
61
|
var identity_feature_module_js_1 = require("./features/identity-feature.module.js");
|
|
62
62
|
Object.defineProperty(exports, "IdentityFeatureModule", { enumerable: true, get: function () { return identity_feature_module_js_1.IdentityFeatureModule; } });
|
|
63
|
+
/**
|
|
64
|
+
* The org bootstrap (G-009): `runOrgBootstrap(app, { file, apply })` applies a declarative org file
|
|
65
|
+
* (roots, company tree, roles, users, postings) through the framework's own services, as the declared
|
|
66
|
+
* operation `shield:org-bootstrap`. Run at deploy time against the app's own `AppModule` context.
|
|
67
|
+
*/
|
|
68
|
+
var run_org_bootstrap_js_1 = require("./features/org-bootstrap/run-org-bootstrap.js");
|
|
69
|
+
Object.defineProperty(exports, "runOrgBootstrap", { enumerable: true, get: function () { return run_org_bootstrap_js_1.runOrgBootstrap; } });
|
|
70
|
+
Object.defineProperty(exports, "OrgBootstrapError", { enumerable: true, get: function () { return run_org_bootstrap_js_1.OrgBootstrapError; } });
|
|
63
71
|
/**
|
|
64
72
|
* Stored files (file-storage SPEC §5 I-K): `store` / `open` / `remove` on the engine pipe, provided
|
|
65
73
|
* and exported by `IdentityFeatureModule`. Apps call it from their own `@Classified` routes.
|
package/dist/internal.d.ts
CHANGED
|
@@ -8,4 +8,6 @@
|
|
|
8
8
|
* subpath everywhere else. Feature code never holds a pool: it uses the RecordEngine.
|
|
9
9
|
*/
|
|
10
10
|
export { createIdentityAuthzPool } from './features/authz/record-authorization.js';
|
|
11
|
+
export { transitionRoleOwner } from './features/role/role-owner-transition.js';
|
|
12
|
+
export type { RoleOwnerAudit, RoleOwnerQuery } from './features/role/role-owner-transition.js';
|
|
11
13
|
//# sourceMappingURL=internal.d.ts.map
|
package/dist/internal.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAC"}
|
|
1
|
+
{"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAC;AACnF,OAAO,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAC;AAC/E,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,0CAA0C,CAAC"}
|
package/dist/internal.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createIdentityAuthzPool = void 0;
|
|
3
|
+
exports.transitionRoleOwner = exports.createIdentityAuthzPool = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* @astram/identity/internal — composition-root surface (Phase 8 #5).
|
|
6
6
|
*
|
|
@@ -12,3 +12,5 @@ exports.createIdentityAuthzPool = void 0;
|
|
|
12
12
|
*/
|
|
13
13
|
var record_authorization_js_1 = require("./features/authz/record-authorization.js");
|
|
14
14
|
Object.defineProperty(exports, "createIdentityAuthzPool", { enumerable: true, get: function () { return record_authorization_js_1.createIdentityAuthzPool; } });
|
|
15
|
+
var role_owner_transition_js_1 = require("./features/role/role-owner-transition.js");
|
|
16
|
+
Object.defineProperty(exports, "transitionRoleOwner", { enumerable: true, get: function () { return role_owner_transition_js_1.transitionRoleOwner; } });
|
package/dist/role.object.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"role.object.d.ts","sourceRoot":"","sources":["../src/role.object.ts"],"names":[],"mappings":"AAGA,8BAA8B;AAC9B,qBAoBa,IAAI;IAEf,IAAI,EAAG,MAAM,CAAC;IAGd,IAAI,EAAG,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"role.object.d.ts","sourceRoot":"","sources":["../src/role.object.ts"],"names":[],"mappings":"AAGA,8BAA8B;AAC9B,qBAoBa,IAAI;IAEf,IAAI,EAAG,MAAM,CAAC;IAGd,IAAI,EAAG,MAAM,CAAC;IAMd,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAGjC,SAAS,EAAG,MAAM,CAAC;CACpB"}
|
package/dist/role.object.js
CHANGED
|
@@ -16,6 +16,10 @@ const schema_1 = require("./schema");
|
|
|
16
16
|
let Role = class Role {
|
|
17
17
|
uuid;
|
|
18
18
|
name;
|
|
19
|
+
// The company a role was created for (G-009 T14, SPEC R13); NULL = shared (root-created). Create-only
|
|
20
|
+
// and server-set (RoleService stamps it from the actor); NOT `trusted`, so the engine never
|
|
21
|
+
// derives it and root's UI-created roles stay unowned. Changed later only by `transitionRoleOwner`.
|
|
22
|
+
ownerCompanyUuid;
|
|
19
23
|
createdAt;
|
|
20
24
|
};
|
|
21
25
|
exports.Role = Role;
|
|
@@ -27,6 +31,10 @@ __decorate([
|
|
|
27
31
|
(0, engine_1.Field)({ column: 'r_name', type: 'string', required: true }),
|
|
28
32
|
__metadata("design:type", String)
|
|
29
33
|
], Role.prototype, "name", void 0);
|
|
34
|
+
__decorate([
|
|
35
|
+
(0, engine_1.Field)({ column: 'r_owner_company_uuid', type: 'uuid', immutable: true }),
|
|
36
|
+
__metadata("design:type", Object)
|
|
37
|
+
], Role.prototype, "ownerCompanyUuid", void 0);
|
|
30
38
|
__decorate([
|
|
31
39
|
(0, engine_1.Field)({ column: 'r_created_at', type: 'timestamp' }),
|
|
32
40
|
__metadata("design:type", String)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework identity migration — `roles.r_owner_company_uuid` (G-009 T14, AC-19; SPEC R13).
|
|
3
|
+
*
|
|
4
|
+
* WHY: `roles` was a global catalog with no owner, so a company delete could not say which roles
|
|
5
|
+
* were its own. The owner is the company (tenant node) a role was created for; NULL = shared
|
|
6
|
+
* (root-created, or unowned). It is create-only at the engine (immutable); the only later change
|
|
7
|
+
* is the audited `transitionRoleOwner` seam (company -> NULL on a company delete, NULL -> company
|
|
8
|
+
* for an owner claim).
|
|
9
|
+
*
|
|
10
|
+
* Steps (idempotent; a half-applied state is completed, nothing is dropped or overwritten):
|
|
11
|
+
* 1. ADD the nullable column when absent.
|
|
12
|
+
* 2. ADD `idx_roles_owner_company` when absent (the FK needs it; the delete's owned-roles read).
|
|
13
|
+
* 3. ADD `FK_roles_owner_company` -> `companies (c_uuid)` ON DELETE SET NULL when absent. The
|
|
14
|
+
* app clears owners itself on a company delete (audited); SET NULL is only the backstop.
|
|
15
|
+
* 4. BACK-FILL unowned roles bound ONLY inside ONE top-level tenant's subtree (postings ->
|
|
16
|
+
* memberships -> companies -> `scope_closure` ancestors with no parent) to that tenant. A role
|
|
17
|
+
* bound across several tenants, or bound nowhere, stays NULL. Only `IS NULL` rows are
|
|
18
|
+
* touched, so a re-run never overwrites an owner. No ids are hardcoded.
|
|
19
|
+
*
|
|
20
|
+
* `down` drops the FK, then the index, then the column, each with IF EXISTS (a second run, or a
|
|
21
|
+
* database that never had the column, does nothing).
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const { frameworkSchemasForMigration } = require('@astram/core');
|
|
25
|
+
|
|
26
|
+
const COLUMN = 'r_owner_company_uuid';
|
|
27
|
+
const INDEX = 'idx_roles_owner_company';
|
|
28
|
+
const FK = 'FK_roles_owner_company';
|
|
29
|
+
|
|
30
|
+
const present = async (query, sql, params) => {
|
|
31
|
+
const rows = await query(sql, params);
|
|
32
|
+
return Array.isArray(rows) && rows.length > 0;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
async function up(ctx) {
|
|
36
|
+
const { query } = ctx;
|
|
37
|
+
const schemas = frameworkSchemasForMigration(ctx, process.env);
|
|
38
|
+
const APP = schemas.app;
|
|
39
|
+
const AUTHZ = schemas.authz;
|
|
40
|
+
|
|
41
|
+
const hasColumn = await present(
|
|
42
|
+
query,
|
|
43
|
+
`SELECT 1 AS n FROM information_schema.COLUMNS
|
|
44
|
+
WHERE TABLE_SCHEMA = ? AND TABLE_NAME = 'roles' AND COLUMN_NAME = ? LIMIT 1`,
|
|
45
|
+
[APP, COLUMN],
|
|
46
|
+
);
|
|
47
|
+
if (!hasColumn) {
|
|
48
|
+
await query(`ALTER TABLE \`${APP}\`.\`roles\` ADD COLUMN ${COLUMN} VARCHAR(36) NULL`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const hasIndex = await present(
|
|
52
|
+
query,
|
|
53
|
+
`SELECT 1 AS n FROM information_schema.STATISTICS
|
|
54
|
+
WHERE TABLE_SCHEMA = ? AND TABLE_NAME = 'roles' AND INDEX_NAME = ? LIMIT 1`,
|
|
55
|
+
[APP, INDEX],
|
|
56
|
+
);
|
|
57
|
+
if (!hasIndex) {
|
|
58
|
+
await query(`CREATE INDEX ${INDEX} ON \`${APP}\`.\`roles\` (${COLUMN})`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const hasFk = await present(
|
|
62
|
+
query,
|
|
63
|
+
`SELECT 1 AS n FROM information_schema.REFERENTIAL_CONSTRAINTS
|
|
64
|
+
WHERE CONSTRAINT_SCHEMA = ? AND TABLE_NAME = 'roles' AND CONSTRAINT_NAME = ? LIMIT 1`,
|
|
65
|
+
[APP, FK],
|
|
66
|
+
);
|
|
67
|
+
if (!hasFk) {
|
|
68
|
+
await query(`ALTER TABLE \`${APP}\`.\`roles\`
|
|
69
|
+
ADD CONSTRAINT ${FK} FOREIGN KEY (${COLUMN})
|
|
70
|
+
REFERENCES \`${APP}\`.\`companies\` (c_uuid) ON DELETE SET NULL`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Back-fill: exactly one top-level tenant over every posting of the role, else leave NULL.
|
|
74
|
+
const tenantsOfRole = `SELECT COUNT(DISTINCT sc.ancestor_uuid)
|
|
75
|
+
FROM \`${APP}\`.\`membership_posting\` mp
|
|
76
|
+
JOIN \`${APP}\`.\`company_membership\` cm ON cm.cm_uuid = mp.mp_membership_uuid
|
|
77
|
+
JOIN \`${AUTHZ}\`.\`scope_closure\` sc ON sc.descendant_uuid = cm.cm_company_uuid
|
|
78
|
+
JOIN \`${APP}\`.\`companies\` top ON top.c_uuid = sc.ancestor_uuid AND top.c_parent_uuid IS NULL
|
|
79
|
+
WHERE mp.mp_role_uuid = r.r_uuid`;
|
|
80
|
+
await query(`UPDATE \`${APP}\`.\`roles\` r
|
|
81
|
+
SET r.${COLUMN} = (SELECT MIN(sc.ancestor_uuid)
|
|
82
|
+
FROM \`${APP}\`.\`membership_posting\` mp
|
|
83
|
+
JOIN \`${APP}\`.\`company_membership\` cm ON cm.cm_uuid = mp.mp_membership_uuid
|
|
84
|
+
JOIN \`${AUTHZ}\`.\`scope_closure\` sc ON sc.descendant_uuid = cm.cm_company_uuid
|
|
85
|
+
JOIN \`${APP}\`.\`companies\` top ON top.c_uuid = sc.ancestor_uuid AND top.c_parent_uuid IS NULL
|
|
86
|
+
WHERE mp.mp_role_uuid = r.r_uuid)
|
|
87
|
+
WHERE r.${COLUMN} IS NULL AND (${tenantsOfRole}) = 1`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function down(ctx) {
|
|
91
|
+
const { query } = ctx;
|
|
92
|
+
const APP = frameworkSchemasForMigration(ctx, process.env).app;
|
|
93
|
+
await query(`ALTER TABLE \`${APP}\`.\`roles\` DROP FOREIGN KEY IF EXISTS ${FK}`);
|
|
94
|
+
await query(`DROP INDEX IF EXISTS ${INDEX} ON \`${APP}\`.\`roles\``);
|
|
95
|
+
await query(`ALTER TABLE \`${APP}\`.\`roles\` DROP COLUMN IF EXISTS ${COLUMN}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = { up, down };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astram/identity",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Astram framework — the framework-owned identity + tenancy model. Hosts the identity @ObjectType classes (user, company, membership, posting), framework-authored identity migrations, and the identity resolver. Top of the dependency graph: depends on @astram/engine (for @ObjectType/@Field decorators) + @astram/guard / @astram/core (for the auth ports).",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"mariadb": "^3.5.4",
|
|
39
|
-
"@astram/core": "1.
|
|
40
|
-
"@astram/engine": "0.
|
|
41
|
-
"@astram/guard": "0.
|
|
39
|
+
"@astram/core": "1.8.0",
|
|
40
|
+
"@astram/engine": "0.10.0",
|
|
41
|
+
"@astram/guard": "0.17.0"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"@nestjs/common": "^11",
|