@astram/identity 0.11.0 → 0.12.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/cross-tenant-grant.object.d.ts.map +1 -1
- package/dist/cross-tenant-grant.object.js +3 -2
- package/dist/features/audit/directory-label-resolver.d.ts +4 -1
- package/dist/features/audit/directory-label-resolver.d.ts.map +1 -1
- package/dist/features/audit/directory-label-resolver.js +8 -5
- package/dist/features/audit/mariadb-audit-reader.d.ts +5 -1
- package/dist/features/audit/mariadb-audit-reader.d.ts.map +1 -1
- package/dist/features/audit/mariadb-audit-reader.js +10 -2
- package/dist/features/authz/access-bundle-reconcile.job.d.ts +4 -1
- package/dist/features/authz/access-bundle-reconcile.job.d.ts.map +1 -1
- package/dist/features/authz/access-bundle-reconcile.job.js +7 -4
- package/dist/features/authz/access-bundle-store.d.ts +4 -1
- package/dist/features/authz/access-bundle-store.d.ts.map +1 -1
- package/dist/features/authz/access-bundle-store.js +19 -16
- package/dist/features/authz/catalog-reconcile.job.d.ts +3 -1
- package/dist/features/authz/catalog-reconcile.job.d.ts.map +1 -1
- package/dist/features/authz/catalog-reconcile.job.js +3 -2
- package/dist/features/authz/db-permission-facts.adapter.d.ts +9 -3
- package/dist/features/authz/db-permission-facts.adapter.d.ts.map +1 -1
- package/dist/features/authz/db-permission-facts.adapter.js +22 -16
- package/dist/features/authz/grant-admin.service.d.ts +4 -1
- package/dist/features/authz/grant-admin.service.d.ts.map +1 -1
- package/dist/features/authz/grant-admin.service.js +32 -37
- package/dist/features/authz/grant-ceiling.d.ts +6 -1
- package/dist/features/authz/grant-ceiling.d.ts.map +1 -1
- package/dist/features/authz/grant-ceiling.js +19 -16
- package/dist/features/authz/mariadb-catalog-store.d.ts +4 -1
- package/dist/features/authz/mariadb-catalog-store.d.ts.map +1 -1
- package/dist/features/authz/mariadb-catalog-store.js +10 -7
- package/dist/features/authz/permission-version.bumper.d.ts +6 -1
- package/dist/features/authz/permission-version.bumper.d.ts.map +1 -1
- package/dist/features/authz/permission-version.bumper.js +13 -11
- package/dist/features/authz/record-authorization.d.ts +8 -2
- package/dist/features/authz/record-authorization.d.ts.map +1 -1
- package/dist/features/authz/record-authorization.js +8 -4
- package/dist/features/authz/role-binding.d.ts +3 -2
- package/dist/features/authz/role-binding.d.ts.map +1 -1
- package/dist/features/authz/role-binding.js +7 -8
- package/dist/features/company/company-hierarchy.service.d.ts +6 -1
- package/dist/features/company/company-hierarchy.service.d.ts.map +1 -1
- package/dist/features/company/company-hierarchy.service.js +17 -16
- package/dist/features/cross-tenant/cross-tenant-grant-read.service.d.ts +5 -1
- package/dist/features/cross-tenant/cross-tenant-grant-read.service.d.ts.map +1 -1
- package/dist/features/cross-tenant/cross-tenant-grant-read.service.js +18 -14
- package/dist/features/framework-schemas.token.d.ts +8 -0
- package/dist/features/framework-schemas.token.d.ts.map +1 -0
- package/dist/features/framework-schemas.token.js +10 -0
- package/dist/features/identity-feature.module.d.ts +29 -1
- package/dist/features/identity-feature.module.d.ts.map +1 -1
- package/dist/features/identity-feature.module.js +134 -33
- package/dist/features/membership/membership-checker.d.ts +7 -1
- package/dist/features/membership/membership-checker.d.ts.map +1 -1
- package/dist/features/membership/membership-checker.js +9 -4
- package/dist/features/migration-ledger-check.d.ts +15 -0
- package/dist/features/migration-ledger-check.d.ts.map +1 -0
- package/dist/features/migration-ledger-check.js +26 -0
- package/dist/features/scope/scope-closure.service.d.ts +8 -1
- package/dist/features/scope/scope-closure.service.d.ts.map +1 -1
- package/dist/features/scope/scope-closure.service.js +22 -19
- package/dist/features/scope/scope-subtree.resolver.d.ts +7 -1
- package/dist/features/scope/scope-subtree.resolver.d.ts.map +1 -1
- package/dist/features/scope/scope-subtree.resolver.js +9 -4
- package/dist/features/service-account/lock-service-accounts.d.ts +3 -1
- package/dist/features/service-account/lock-service-accounts.d.ts.map +1 -1
- package/dist/features/service-account/lock-service-accounts.js +4 -4
- package/dist/features/service-account/no-expiry-key-report.d.ts +9 -3
- package/dist/features/service-account/no-expiry-key-report.d.ts.map +1 -1
- package/dist/features/service-account/no-expiry-key-report.js +17 -10
- package/dist/features/service-account/service-account.store.d.ts +8 -1
- package/dist/features/service-account/service-account.store.d.ts.map +1 -1
- package/dist/features/service-account/service-account.store.js +19 -14
- package/dist/features/user/user.service.d.ts +7 -3
- package/dist/features/user/user.service.d.ts.map +1 -1
- package/dist/features/user/user.service.js +12 -4
- package/dist/record-share.object.d.ts.map +1 -1
- package/dist/record-share.object.js +3 -2
- package/dist/schema.d.ts +15 -2
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +17 -2
- package/migrations/1710000000001-CreateIdentityOrgModel.cjs +14 -4
- package/migrations/1710000000002-CreateMembershipAndPostings.cjs +14 -4
- package/migrations/1710000000003-CreateIdentityCredential.cjs +14 -4
- package/migrations/1710000000004-CreateSessionTables.cjs +14 -4
- package/migrations/1710000000005-CreateMfaAndTokenSurface.cjs +14 -4
- package/migrations/1710000000010-CreateAuditLog.cjs +14 -4
- package/migrations/1710000000011-CreateCrossTenantGrant.cjs +15 -4
- package/migrations/1710000000012-SeedCrossTenantGrantPerms.cjs +15 -4
- package/migrations/1710000000013-CreateSharedResource.cjs +14 -4
- package/migrations/1710000000018-CreateServiceAccount.cjs +13 -4
- package/migrations/1710000000019-AddSessionMustReenroll.cjs +14 -4
- package/migrations/1710000000020-CreateEmailChangeToken.cjs +15 -5
- package/migrations/1710000000021-AddUsersEmailUniqueIndex.cjs +24 -13
- package/migrations/1710000000022-CreateStoredFile.cjs +14 -4
- package/migrations/1710000000099-SeedDevIdentity.cjs +14 -6
- package/migrations/1710000000100-SeedDevTenantBAndRoot.cjs +14 -6
- package/package.json +4 -4
|
@@ -33,9 +33,15 @@
|
|
|
33
33
|
* `shield_authz`; the shared authz pool reaches it.
|
|
34
34
|
*/
|
|
35
35
|
import type { Pool } from 'mariadb';
|
|
36
|
+
import { type IdentitySchemaNames } from '../../schema.js';
|
|
36
37
|
export declare class DbScopeSubtreeResolver {
|
|
37
38
|
private readonly pool;
|
|
38
|
-
|
|
39
|
+
private readonly schemas;
|
|
40
|
+
/**
|
|
41
|
+
* @param schemas resolved framework names; the closure table lives in `schemas.authz`
|
|
42
|
+
* (Task 1.1 / ADR-0011). Absent ⇒ resolved from the namespace, once, here.
|
|
43
|
+
*/
|
|
44
|
+
constructor(pool: Pool, schemas?: IdentitySchemaNames);
|
|
39
45
|
/**
|
|
40
46
|
* Resolve the active-scope subtree for `nodeId` — the node itself plus every
|
|
41
47
|
* descendant — from `scope_closure`. Returns `[nodeId]` (self-node, non-widening)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scope-subtree.resolver.d.ts","sourceRoot":"","sources":["../../../src/features/scope/scope-subtree.resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"scope-subtree.resolver.d.ts","sourceRoot":"","sources":["../../../src/features/scope/scope-subtree.resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAA2B,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEpF,qBAAa,sBAAsB;IAM/B,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAN1B;;;OAGG;gBAEgB,IAAI,EAAE,IAAI,EACV,OAAO,GAAE,mBAA+C;IAG3E;;;;;OAKG;IACG,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC;CAQjE"}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DbScopeSubtreeResolver = void 0;
|
|
4
|
-
|
|
5
|
-
const AUTHZ = 'shield_authz';
|
|
4
|
+
const schema_js_1 = require("../../schema.js");
|
|
6
5
|
class DbScopeSubtreeResolver {
|
|
7
6
|
pool;
|
|
8
|
-
|
|
7
|
+
schemas;
|
|
8
|
+
/**
|
|
9
|
+
* @param schemas resolved framework names; the closure table lives in `schemas.authz`
|
|
10
|
+
* (Task 1.1 / ADR-0011). Absent ⇒ resolved from the namespace, once, here.
|
|
11
|
+
*/
|
|
12
|
+
constructor(pool, schemas = (0, schema_js_1.frameworkSchemasFromEnv)()) {
|
|
9
13
|
this.pool = pool;
|
|
14
|
+
this.schemas = schemas;
|
|
10
15
|
}
|
|
11
16
|
/**
|
|
12
17
|
* Resolve the active-scope subtree for `nodeId` — the node itself plus every
|
|
@@ -15,7 +20,7 @@ class DbScopeSubtreeResolver {
|
|
|
15
20
|
* store outage (the login path swallows it → self-node fallback per request).
|
|
16
21
|
*/
|
|
17
22
|
async resolveSubtree(nodeId) {
|
|
18
|
-
const rows = (await this.pool.query(`SELECT descendant_uuid FROM \`${
|
|
23
|
+
const rows = (await this.pool.query(`SELECT descendant_uuid FROM \`${this.schemas.authz}\`.\`scope_closure\` WHERE ancestor_uuid = ?`, [nodeId]));
|
|
19
24
|
if (rows.length === 0)
|
|
20
25
|
return [nodeId]; // pre-first-reconcile → self-node (non-widening).
|
|
21
26
|
return rows.map((r) => r.descendant_uuid);
|
|
@@ -18,12 +18,14 @@
|
|
|
18
18
|
* `columns` and `where.sql` are fixed SQL written by the caller (never client input); every value
|
|
19
19
|
* is `?`-bound.
|
|
20
20
|
*/
|
|
21
|
+
import type { IdentitySchemaNames } from '../../schema.js';
|
|
21
22
|
/** The connection surface: one transaction's `query`. */
|
|
22
23
|
export interface LockConn {
|
|
23
24
|
query(sql: string, params?: readonly unknown[]): Promise<unknown>;
|
|
24
25
|
}
|
|
26
|
+
/** @param schemas resolved framework names; `service_account` lives in `schemas.app`. */
|
|
25
27
|
export declare function lockServiceAccountsWhere<T>(conn: LockConn, columns: string, where: {
|
|
26
28
|
readonly sql: string;
|
|
27
29
|
readonly params: readonly unknown[];
|
|
28
|
-
}): Promise<T[]>;
|
|
30
|
+
}, schemas: IdentitySchemaNames): Promise<T[]>;
|
|
29
31
|
//# sourceMappingURL=lock-service-accounts.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lock-service-accounts.d.ts","sourceRoot":"","sources":["../../../src/features/service-account/lock-service-accounts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;
|
|
1
|
+
{"version":3,"file":"lock-service-accounts.d.ts","sourceRoot":"","sources":["../../../src/features/service-account/lock-service-accounts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAE3D,yDAAyD;AACzD,MAAM,WAAW,QAAQ;IACvB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACnE;AAED,yFAAyF;AACzF,wBAAsB,wBAAwB,CAAC,CAAC,EAC9C,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,MAAM,EACf,KAAK,EAAE;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAA;CAAE,EACpE,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,CAAC,EAAE,CAAC,CAed"}
|
|
@@ -21,14 +21,14 @@
|
|
|
21
21
|
*/
|
|
22
22
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
23
|
exports.lockServiceAccountsWhere = lockServiceAccountsWhere;
|
|
24
|
-
|
|
25
|
-
async function lockServiceAccountsWhere(conn, columns, where) {
|
|
26
|
-
const candidates = (await conn.query(`SELECT sa_id FROM \`${
|
|
24
|
+
/** @param schemas resolved framework names; `service_account` lives in `schemas.app`. */
|
|
25
|
+
async function lockServiceAccountsWhere(conn, columns, where, schemas) {
|
|
26
|
+
const candidates = (await conn.query(`SELECT sa_id FROM \`${schemas.app}\`.\`service_account\` WHERE ${where.sql}`, [...where.params]));
|
|
27
27
|
const ids = [...new Set(candidates.map((r) => r.sa_id))].sort();
|
|
28
28
|
if (ids.length === 0)
|
|
29
29
|
return [];
|
|
30
30
|
return (await conn.query(`SELECT ${columns}
|
|
31
|
-
FROM \`${
|
|
31
|
+
FROM \`${schemas.app}\`.\`service_account\`
|
|
32
32
|
WHERE sa_id IN (${ids.map(() => '?').join(', ')}) AND (${where.sql})
|
|
33
33
|
ORDER BY sa_id
|
|
34
34
|
FOR UPDATE`, [...ids, ...where.params]));
|
|
@@ -7,8 +7,12 @@
|
|
|
7
7
|
* lists each agent's CURRENT, unrevoked key with no expiry so an operator can rotate it with an
|
|
8
8
|
* expiry. It prints labels and company names only: never a secret, a hash or a raw id.
|
|
9
9
|
*/
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
import { type IdentitySchemaNames } from '../../schema.js';
|
|
11
|
+
/**
|
|
12
|
+
* Read-only: the current unrevoked key of each agent that has no expiry, with display names, over
|
|
13
|
+
* the resolved framework names (validated identifiers, never input).
|
|
14
|
+
*/
|
|
15
|
+
export declare function noExpiryKeyReportSql({ app, authz }: IdentitySchemaNames): string;
|
|
12
16
|
/** One non-expiring key, as printed (no ids, no secrets). */
|
|
13
17
|
export interface NonExpiringKey {
|
|
14
18
|
/** The agent's label; `null` when the agent row no longer exists. */
|
|
@@ -24,7 +28,7 @@ export interface ReportQueryable {
|
|
|
24
28
|
query(sql: string, params?: unknown[]): Promise<unknown>;
|
|
25
29
|
}
|
|
26
30
|
/** Run the report query and map the rows. Read-only. */
|
|
27
|
-
export declare function findNonExpiringKeys(db: ReportQueryable): Promise<NonExpiringKey[]>;
|
|
31
|
+
export declare function findNonExpiringKeys(db: ReportQueryable, schemas: IdentitySchemaNames): Promise<NonExpiringKey[]>;
|
|
28
32
|
/** Human-readable report text (one line per key, plus a summary line). */
|
|
29
33
|
export declare function formatNonExpiringKeyReport(keys: readonly NonExpiringKey[]): string;
|
|
30
34
|
/** The bin's behaviour, testable without a process: returns the exit code. */
|
|
@@ -35,5 +39,7 @@ export declare function runNonExpiringKeyReport(opts: {
|
|
|
35
39
|
};
|
|
36
40
|
readonly out: (text: string) => void;
|
|
37
41
|
readonly err: (text: string) => void;
|
|
42
|
+
/** Resolved framework names; absent ⇒ resolved from `SHIELD_DB_NAMESPACE` once per run. */
|
|
43
|
+
readonly schemas?: IdentitySchemaNames;
|
|
38
44
|
}): Promise<number>;
|
|
39
45
|
//# sourceMappingURL=no-expiry-key-report.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-expiry-key-report.d.ts","sourceRoot":"","sources":["../../../src/features/service-account/no-expiry-key-report.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,
|
|
1
|
+
{"version":3,"file":"no-expiry-key-report.d.ts","sourceRoot":"","sources":["../../../src/features/service-account/no-expiry-key-report.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAA2B,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEpF;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,mBAAmB,GAAG,MAAM,CAYhF;AAED,6DAA6D;AAC7D,MAAM,WAAW,cAAc;IAC7B,qEAAqE;IACrE,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED,wFAAwF;AACxF,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1D;AAMD,wDAAwD;AACxD,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,eAAe,EACnB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,cAAc,EAAE,CAAC,CAS3B;AAED,0EAA0E;AAC1E,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,SAAS,cAAc,EAAE,GAAG,MAAM,CAalF;AAcD,8EAA8E;AAC9E,wBAAsB,uBAAuB,CAAC,IAAI,EAAE;IAClD,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,eAAe,GAAG;QAAE,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAC;IAClE,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,2FAA2F;IAC3F,QAAQ,CAAC,OAAO,CAAC,EAAE,mBAAmB,CAAC;CACxC,GAAG,OAAO,CAAC,MAAM,CAAC,CAyBlB"}
|
|
@@ -9,27 +9,33 @@
|
|
|
9
9
|
* expiry. It prints labels and company names only: never a secret, a hash or a raw id.
|
|
10
10
|
*/
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.
|
|
12
|
+
exports.noExpiryKeyReportSql = noExpiryKeyReportSql;
|
|
13
13
|
exports.findNonExpiringKeys = findNonExpiringKeys;
|
|
14
14
|
exports.formatNonExpiringKeyReport = formatNonExpiringKeyReport;
|
|
15
15
|
exports.runNonExpiringKeyReport = runNonExpiringKeyReport;
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
const schema_js_1 = require("../../schema.js");
|
|
17
|
+
/**
|
|
18
|
+
* Read-only: the current unrevoked key of each agent that has no expiry, with display names, over
|
|
19
|
+
* the resolved framework names (validated identifiers, never input).
|
|
20
|
+
*/
|
|
21
|
+
function noExpiryKeyReportSql({ app, authz }) {
|
|
22
|
+
return `SELECT sa.sa_label AS label, c.c_name AS company,
|
|
18
23
|
sa.sa_status AS status, k.sak_version AS version, k.sak_created_at_ms AS issuedAtMs
|
|
19
|
-
FROM
|
|
20
|
-
LEFT JOIN
|
|
21
|
-
LEFT JOIN
|
|
24
|
+
FROM ${authz}.service_account_key k
|
|
25
|
+
LEFT JOIN ${app}.service_account sa ON sa.sa_id = k.sak_account_id
|
|
26
|
+
LEFT JOIN ${app}.companies c ON c.c_uuid = k.sak_tenant_id
|
|
22
27
|
WHERE k.sak_expires_at_ms IS NULL
|
|
23
28
|
AND k.sak_revoked_at_ms IS NULL
|
|
24
29
|
AND k.sak_version = (SELECT MAX(v.sak_version)
|
|
25
|
-
FROM
|
|
30
|
+
FROM ${authz}.service_account_key v
|
|
26
31
|
WHERE v.sak_account_id = k.sak_account_id)
|
|
27
32
|
ORDER BY k.sak_created_at_ms`;
|
|
33
|
+
}
|
|
28
34
|
const str = (v) => (v === null || v === undefined ? null : String(v));
|
|
29
35
|
const num = (v) => v === null || v === undefined || !Number.isFinite(Number(v)) ? null : Number(v);
|
|
30
36
|
/** Run the report query and map the rows. Read-only. */
|
|
31
|
-
async function findNonExpiringKeys(db) {
|
|
32
|
-
const rows = (await db.query(
|
|
37
|
+
async function findNonExpiringKeys(db, schemas) {
|
|
38
|
+
const rows = (await db.query(noExpiryKeyReportSql(schemas)));
|
|
33
39
|
return rows.map((r) => ({
|
|
34
40
|
label: str(r.label),
|
|
35
41
|
company: str(r.company),
|
|
@@ -75,8 +81,9 @@ async function runNonExpiringKeyReport(opts) {
|
|
|
75
81
|
}
|
|
76
82
|
let db;
|
|
77
83
|
try {
|
|
84
|
+
const schemas = opts.schemas ?? (0, schema_js_1.frameworkSchemasFromEnv)();
|
|
78
85
|
db = opts.openDb();
|
|
79
|
-
const keys = await findNonExpiringKeys(db);
|
|
86
|
+
const keys = await findNonExpiringKeys(db, schemas);
|
|
80
87
|
opts.out(formatNonExpiringKeyReport(keys));
|
|
81
88
|
return keys.length > 0 && opts.argv.includes('--fail-on-found') ? 1 : 0;
|
|
82
89
|
}
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* stamped `sa_tenant_id` (from the session) on create — never client input.
|
|
13
13
|
*/
|
|
14
14
|
import type { Pool } from 'mariadb';
|
|
15
|
+
import { type IdentitySchemaNames } from '../../schema.js';
|
|
15
16
|
/** The status of an account's current key (no hash, no secret). Epoch-ms; `null` = not set. */
|
|
16
17
|
export interface ServiceAccountKeyRow {
|
|
17
18
|
readonly accountId: string;
|
|
@@ -50,7 +51,13 @@ export interface ScopeNodeOption {
|
|
|
50
51
|
}
|
|
51
52
|
export declare class ServiceAccountStore {
|
|
52
53
|
private readonly pool;
|
|
53
|
-
|
|
54
|
+
private readonly schemas;
|
|
55
|
+
/**
|
|
56
|
+
* @param schemas resolved framework names: accounts in `schemas.app`; guard's key table
|
|
57
|
+
* (`1710000000017-CreateServiceAccountKey.cjs`, read-only here) in `schemas.authz`. Absent ⇒
|
|
58
|
+
* resolved from the namespace, once, here.
|
|
59
|
+
*/
|
|
60
|
+
constructor(pool: Pool, schemas?: IdentitySchemaNames);
|
|
54
61
|
/** Roles by id (unknown ids are simply absent — the caller supplies a fallback label). */
|
|
55
62
|
rolesByIds(ids: readonly string[]): Promise<RoleOption[]>;
|
|
56
63
|
/** Every role in the catalog (ROOT's assignable set). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service-account.store.d.ts","sourceRoot":"","sources":["../../../src/features/service-account/service-account.store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"service-account.store.d.ts","sourceRoot":"","sources":["../../../src/features/service-account/service-account.store.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ServiceAccountStore = void 0;
|
|
4
|
-
const
|
|
5
|
-
/** Guard's key table lives here (`1710000000017-CreateServiceAccountKey.cjs`); read-only here. */
|
|
6
|
-
const AUTHZ = 'shield_authz';
|
|
4
|
+
const schema_js_1 = require("../../schema.js");
|
|
7
5
|
/** Normalize a JSON-array column (driver returns array OR string) → string[]. Fail-safe to []. */
|
|
8
6
|
function jsonArray(v) {
|
|
9
7
|
const arr = Array.isArray(v)
|
|
@@ -53,8 +51,15 @@ function uniqueIds(ids) {
|
|
|
53
51
|
}
|
|
54
52
|
class ServiceAccountStore {
|
|
55
53
|
pool;
|
|
56
|
-
|
|
54
|
+
schemas;
|
|
55
|
+
/**
|
|
56
|
+
* @param schemas resolved framework names: accounts in `schemas.app`; guard's key table
|
|
57
|
+
* (`1710000000017-CreateServiceAccountKey.cjs`, read-only here) in `schemas.authz`. Absent ⇒
|
|
58
|
+
* resolved from the namespace, once, here.
|
|
59
|
+
*/
|
|
60
|
+
constructor(pool, schemas = (0, schema_js_1.frameworkSchemasFromEnv)()) {
|
|
57
61
|
this.pool = pool;
|
|
62
|
+
this.schemas = schemas;
|
|
58
63
|
}
|
|
59
64
|
// ── directory reads (names for the picker, the view labels and the ceiling messages) ─────
|
|
60
65
|
// Read-only lookups over the global role catalog + the company tree (`shield_app.roles`,
|
|
@@ -64,12 +69,12 @@ class ServiceAccountStore {
|
|
|
64
69
|
const u = uniqueIds(ids);
|
|
65
70
|
if (u.length === 0)
|
|
66
71
|
return [];
|
|
67
|
-
const rows = (await this.pool.query(`SELECT r_uuid AS id, r_name AS name FROM \`${
|
|
72
|
+
const rows = (await this.pool.query(`SELECT r_uuid AS id, r_name AS name FROM \`${this.schemas.app}\`.\`roles\` WHERE r_uuid IN (${u.map(() => '?').join(', ')}) ORDER BY r_name`, u));
|
|
68
73
|
return rows.map((r) => ({ id: r.id, name: r.name }));
|
|
69
74
|
}
|
|
70
75
|
/** Every role in the catalog (ROOT's assignable set). */
|
|
71
76
|
async allRoles() {
|
|
72
|
-
const rows = (await this.pool.query(`SELECT r_uuid AS id, r_name AS name FROM \`${
|
|
77
|
+
const rows = (await this.pool.query(`SELECT r_uuid AS id, r_name AS name FROM \`${this.schemas.app}\`.\`roles\` ORDER BY r_name`, []));
|
|
73
78
|
return rows.map((r) => ({ id: r.id, name: r.name }));
|
|
74
79
|
}
|
|
75
80
|
/** Companies (scope nodes) by id, with their parent handle. Unknown ids are absent. */
|
|
@@ -77,12 +82,12 @@ class ServiceAccountStore {
|
|
|
77
82
|
const u = uniqueIds(ids);
|
|
78
83
|
if (u.length === 0)
|
|
79
84
|
return [];
|
|
80
|
-
const rows = (await this.pool.query(`SELECT c_uuid AS id, c_name AS name, c_parent_uuid AS parentId FROM \`${
|
|
85
|
+
const rows = (await this.pool.query(`SELECT c_uuid AS id, c_name AS name, c_parent_uuid AS parentId FROM \`${this.schemas.app}\`.\`companies\` WHERE c_uuid IN (${u.map(() => '?').join(', ')}) ORDER BY c_name`, u));
|
|
81
86
|
return rows.map((r) => ({ id: r.id, name: r.name, parentId: r.parentId ?? null }));
|
|
82
87
|
}
|
|
83
88
|
/** Every company (ROOT's assignable scope — root reaches all). */
|
|
84
89
|
async allNodes() {
|
|
85
|
-
const rows = (await this.pool.query(`SELECT c_uuid AS id, c_name AS name, c_parent_uuid AS parentId FROM \`${
|
|
90
|
+
const rows = (await this.pool.query(`SELECT c_uuid AS id, c_name AS name, c_parent_uuid AS parentId FROM \`${this.schemas.app}\`.\`companies\` ORDER BY c_name`, []));
|
|
86
91
|
return rows.map((r) => ({ id: r.id, name: r.name, parentId: r.parentId ?? null }));
|
|
87
92
|
}
|
|
88
93
|
/**
|
|
@@ -97,9 +102,9 @@ class ServiceAccountStore {
|
|
|
97
102
|
return [];
|
|
98
103
|
const rows = (await this.pool.query(`SELECT k.sak_account_id, k.sak_version, k.sak_expires_at_ms, k.sak_revoked_at_ms,
|
|
99
104
|
k.sak_created_at_ms
|
|
100
|
-
FROM \`${
|
|
105
|
+
FROM \`${this.schemas.authz}\`.\`service_account_key\` k
|
|
101
106
|
JOIN (SELECT sak_account_id, MAX(sak_version) AS v
|
|
102
|
-
FROM \`${
|
|
107
|
+
FROM \`${this.schemas.authz}\`.\`service_account_key\`
|
|
103
108
|
WHERE sak_account_id IN (${u.map(() => '?').join(', ')})
|
|
104
109
|
GROUP BY sak_account_id) m
|
|
105
110
|
ON m.sak_account_id = k.sak_account_id AND m.v = k.sak_version`, u));
|
|
@@ -115,13 +120,13 @@ class ServiceAccountStore {
|
|
|
115
120
|
/** List the caller's own-tenant accounts (newest first). Root sees all. */
|
|
116
121
|
async list(scope) {
|
|
117
122
|
const t = tenantScope(scope);
|
|
118
|
-
const rows = (await this.pool.query(`SELECT ${COLS} FROM \`${
|
|
123
|
+
const rows = (await this.pool.query(`SELECT ${COLS} FROM \`${this.schemas.app}\`.\`service_account\` WHERE 1=1${t.sql} ORDER BY sa_created_at_ms DESC`, t.params));
|
|
119
124
|
return rows.map(toRow);
|
|
120
125
|
}
|
|
121
126
|
/** Fetch one account BY id, scoped to the caller's subtree. Cross-tenant/unknown → null (404). */
|
|
122
127
|
async getById(id, scope) {
|
|
123
128
|
const t = tenantScope(scope);
|
|
124
|
-
const rows = (await this.pool.query(`SELECT ${COLS} FROM \`${
|
|
129
|
+
const rows = (await this.pool.query(`SELECT ${COLS} FROM \`${this.schemas.app}\`.\`service_account\` WHERE sa_id = ?${t.sql} LIMIT 1`, [id, ...t.params]));
|
|
125
130
|
return rows[0] ? toRow(rows[0]) : null;
|
|
126
131
|
}
|
|
127
132
|
/**
|
|
@@ -130,7 +135,7 @@ class ServiceAccountStore {
|
|
|
130
135
|
* per create); the id is unique by construction.
|
|
131
136
|
*/
|
|
132
137
|
async insert(input) {
|
|
133
|
-
await this.pool.query(`INSERT INTO \`${
|
|
138
|
+
await this.pool.query(`INSERT INTO \`${this.schemas.app}\`.\`service_account\`
|
|
134
139
|
(sa_id, sa_tenant_id, sa_label, sa_scope_subtree, sa_role_ids, sa_status,
|
|
135
140
|
sa_created_by, sa_created_at_ms, sa_updated_at_ms)
|
|
136
141
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, NULL)`, [
|
|
@@ -180,7 +185,7 @@ class ServiceAccountStore {
|
|
|
180
185
|
const t = tenantScope(scope);
|
|
181
186
|
const guard = expectedUpdatedAtMs !== undefined ? ' AND sa_updated_at_ms <=> ?' : '';
|
|
182
187
|
const guardParams = expectedUpdatedAtMs !== undefined ? [expectedUpdatedAtMs] : [];
|
|
183
|
-
const res = (await this.pool.query(`UPDATE \`${
|
|
188
|
+
const res = (await this.pool.query(`UPDATE \`${this.schemas.app}\`.\`service_account\` SET ${sets.join(', ')} WHERE sa_id = ?${t.sql}${guard}`, [...params, id, ...t.params, ...guardParams]));
|
|
184
189
|
return res.affectedRows ?? 0;
|
|
185
190
|
}
|
|
186
191
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RecordsService, type RecordId } from '@astram/engine';
|
|
2
|
-
import type { Principal } from '@astram/core';
|
|
2
|
+
import type { FrameworkSchemas, Principal } from '@astram/core';
|
|
3
3
|
import { type EmailAddressChanger, type EmailSender, type ResetTokenStore, type SessionSweeper } from '@astram/guard';
|
|
4
4
|
import { type PostingVersionBump } from '../membership/posting-version-bump.js';
|
|
5
5
|
import { type ListParams } from '../shared/list-query.js';
|
|
@@ -38,9 +38,13 @@ export declare class UserService {
|
|
|
38
38
|
private readonly versionBump?;
|
|
39
39
|
private readonly emailAddresses?;
|
|
40
40
|
private readonly logger;
|
|
41
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* The engine's audit sink, used on the email helper's transaction (no pool of its own), on the
|
|
43
|
+
* audit database forRoot resolved (`IDENTITY_FRAMEWORK_SCHEMAS`); absent (a hand-built
|
|
44
|
+
* service) ⇒ the sink resolves `SHIELD_DB_NAMESPACE` itself.
|
|
45
|
+
*/
|
|
42
46
|
private readonly emailAudit;
|
|
43
|
-
constructor(records: RecordsService, ceiling: GrantCeiling, resetTokens?: (ResetTokenStore | null) | undefined, emailSender?: (EmailSender | null) | undefined, sessions?: (SessionSweeper | null) | undefined, versionBump?: (PostingVersionBump | null) | undefined, emailAddresses?: (EmailAddressChanger | null) | undefined);
|
|
47
|
+
constructor(records: RecordsService, ceiling: GrantCeiling, resetTokens?: (ResetTokenStore | null) | undefined, emailSender?: (EmailSender | null) | undefined, sessions?: (SessionSweeper | null) | undefined, versionBump?: (PostingVersionBump | null) | undefined, emailAddresses?: (EmailAddressChanger | null) | undefined, schemas?: FrameworkSchemas | null);
|
|
44
48
|
/**
|
|
45
49
|
* Create a user. With `companyUuid`, the user is created AND attached (one active membership) in
|
|
46
50
|
* ONE transaction — the onboarding path for a non-root admin, whose membership create is checked
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user.service.d.ts","sourceRoot":"","sources":["../../../src/features/user/user.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAoB,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACjF,OAAO,KAAK,EAAc,SAAS,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"user.service.d.ts","sourceRoot":"","sources":["../../../src/features/user/user.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAoB,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACjF,OAAO,KAAK,EAAc,gBAAgB,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC5E,OAAO,EAOL,KAAK,mBAAmB,EAExB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAsCzD,4EAA4E;AAC5E,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBACa,WAAW;IAUpB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAGxB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;IACY,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;IAMtE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;IAK1B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;IAI7B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IA7BlC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgC;IACvD;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAmB;gBAG3B,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,YAAY,EAGrB,WAAW,CAAC,GAAE,eAAe,GAAG,IAAI,aAAA,EACK,WAAW,CAAC,GAAE,WAAW,GAAG,IAAI,aAAA,EAMzE,QAAQ,CAAC,GAAE,cAAc,GAAG,IAAI,aAAA,EAKhC,WAAW,CAAC,GAAE,kBAAkB,GAAG,IAAI,aAAA,EAIvC,cAAc,CAAC,GAAE,mBAAmB,GAAG,IAAI,aAAA,EAI5D,OAAO,CAAC,EAAE,gBAAgB,GAAG,IAAI;IAKnC;;;;;;;;;;OAUG;IACG,UAAU,CACd,KAAK,EAAE,SAAS,EAChB,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,GACxF,OAAO,CAAC,QAAQ,CAAC;IAsBpB,uEAAuE;IACvE,SAAS,CAAC,MAAM,CAAC,EAAE,UAAU;IAI7B,0BAA0B;IAC1B,OAAO,CAAC,IAAI,EAAE,MAAM;IAIpB,mGAAmG;IAC7F,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAsCjF;;;;;;OAMG;YACW,WAAW;IAyBzB,uFAAuF;IACvF,OAAO,CAAC,gBAAgB;IA8BxB;;;;;;;;;OASG;YACW,iBAAiB;IAY/B,uDAAuD;IACjD,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO/D,6FAA6F;YAC/E,kBAAkB;CAkBjC"}
|
|
@@ -22,6 +22,7 @@ const list_query_js_1 = require("../shared/list-query.js");
|
|
|
22
22
|
const grant_ceiling_js_1 = require("../authz/grant-ceiling.js");
|
|
23
23
|
const identity_errors_js_1 = require("../shared/identity-errors.js");
|
|
24
24
|
const email_address_changer_token_js_1 = require("./email-address-changer.token.js");
|
|
25
|
+
const framework_schemas_token_js_1 = require("../framework-schemas.token.js");
|
|
25
26
|
/**
|
|
26
27
|
* KI #87: `users` has two unique keys besides the PK, `IDX_users_username` (migration 001) and
|
|
27
28
|
* `uq_users_email` (021, `utf8mb4_unicode_ci`: folds case, accents, width, trailing spaces). The
|
|
@@ -77,9 +78,13 @@ let UserService = UserService_1 = class UserService {
|
|
|
77
78
|
versionBump;
|
|
78
79
|
emailAddresses;
|
|
79
80
|
logger = new common_1.Logger(UserService_1.name);
|
|
80
|
-
/**
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
/**
|
|
82
|
+
* The engine's audit sink, used on the email helper's transaction (no pool of its own), on the
|
|
83
|
+
* audit database forRoot resolved (`IDENTITY_FRAMEWORK_SCHEMAS`); absent (a hand-built
|
|
84
|
+
* service) ⇒ the sink resolves `SHIELD_DB_NAMESPACE` itself.
|
|
85
|
+
*/
|
|
86
|
+
emailAudit;
|
|
87
|
+
constructor(records, ceiling, resetTokens, emailSender, sessions, versionBump, emailAddresses, schemas) {
|
|
83
88
|
this.records = records;
|
|
84
89
|
this.ceiling = ceiling;
|
|
85
90
|
this.resetTokens = resetTokens;
|
|
@@ -87,6 +92,7 @@ let UserService = UserService_1 = class UserService {
|
|
|
87
92
|
this.sessions = sessions;
|
|
88
93
|
this.versionBump = versionBump;
|
|
89
94
|
this.emailAddresses = emailAddresses;
|
|
95
|
+
this.emailAudit = new engine_1.MariaDbAuditSink(schemas ? { schema: schemas.audit } : {});
|
|
90
96
|
}
|
|
91
97
|
/**
|
|
92
98
|
* Create a user. With `companyUuid`, the user is created AND attached (one active membership) in
|
|
@@ -288,6 +294,8 @@ exports.UserService = UserService = UserService_1 = __decorate([
|
|
|
288
294
|
__param(5, (0, common_1.Inject)(posting_version_bump_js_1.POSTING_VERSION_BUMP)),
|
|
289
295
|
__param(6, (0, common_1.Optional)()),
|
|
290
296
|
__param(6, (0, common_1.Inject)(email_address_changer_token_js_1.IDENTITY_EMAIL_ADDRESS_CHANGER)),
|
|
297
|
+
__param(7, (0, common_1.Optional)()),
|
|
298
|
+
__param(7, (0, common_1.Inject)(framework_schemas_token_js_1.IDENTITY_FRAMEWORK_SCHEMAS)),
|
|
291
299
|
__metadata("design:paramtypes", [engine_1.RecordsService,
|
|
292
|
-
grant_ceiling_js_1.GrantCeiling, Object, Object, Object, Object, Object])
|
|
300
|
+
grant_ceiling_js_1.GrantCeiling, Object, Object, Object, Object, Object, Object])
|
|
293
301
|
], UserService);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record-share.object.d.ts","sourceRoot":"","sources":["../src/record-share.object.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"record-share.object.d.ts","sourceRoot":"","sources":["../src/record-share.object.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;GAiBG;AACH,qBASa,WAAW;IAEtB,IAAI,EAAG,MAAM,CAAC;IAGd,UAAU,EAAG,MAAM,CAAC;IAGpB,UAAU,EAAG,MAAM,CAAC;CACrB"}
|
|
@@ -12,8 +12,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.RecordShare = void 0;
|
|
13
13
|
const engine_1 = require("@astram/engine");
|
|
14
14
|
const guard_1 = require("@astram/guard");
|
|
15
|
-
/** The
|
|
16
|
-
|
|
15
|
+
/** The authz-database token `record_share` lives in (migration 1710000000014); the engine
|
|
16
|
+
* resolves it at registration (ADR-0037). */
|
|
17
|
+
const SCHEMA = engine_1.FRAMEWORK_SCHEMA.authz;
|
|
17
18
|
/**
|
|
18
19
|
* `record_share` — an AUTHORITY-ONLY @ObjectType, never CRUD'd through the RecordEngine pipe
|
|
19
20
|
* (list-scoping engine Phase 6, §9.2). Its sole purpose is to give `record_share` an entry in the
|
package/dist/schema.d.ts
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { type FrameworkSchemas } from '@astram/core';
|
|
2
|
+
/**
|
|
3
|
+
* The schema every identity table declares (single-source): the app-database token, resolved by
|
|
4
|
+
* the engine at registration to `<namespace>_app` (configurable-framework-db, ADR-0037). It is
|
|
5
|
+
* never a database name: SQL built outside the engine uses {@link IdentitySchemaNames}.
|
|
6
|
+
*/
|
|
7
|
+
export declare const SCHEMA: "{{framework_schema:app}}";
|
|
8
|
+
/** The resolved framework database names identity SQL needs (`FrameworkSchemas` fits). */
|
|
9
|
+
export type IdentitySchemaNames = Pick<FrameworkSchemas, 'app' | 'authz'>;
|
|
10
|
+
/**
|
|
11
|
+
* The framework database names from `SHIELD_DB_NAMESPACE` (unset ⇒ `shield_*`), for a class built
|
|
12
|
+
* without explicit names. Call at construction (boot) only, never per request; an invalid
|
|
13
|
+
* namespace throws (fail closed). `IdentityFeatureModule.forRoot` always passes its names.
|
|
14
|
+
*/
|
|
15
|
+
export declare function frameworkSchemasFromEnv(): FrameworkSchemas;
|
|
3
16
|
//# sourceMappingURL=schema.d.ts.map
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAG9E;;;;GAIG;AACH,eAAO,MAAM,MAAM,4BAAuB,CAAC;AAE3C,0FAA0F;AAC1F,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,gBAAgB,EAAE,KAAK,GAAG,OAAO,CAAC,CAAC;AAE1E;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,gBAAgB,CAE1D"}
|
package/dist/schema.js
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SCHEMA = void 0;
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
exports.frameworkSchemasFromEnv = frameworkSchemasFromEnv;
|
|
5
|
+
const core_1 = require("@astram/core");
|
|
6
|
+
const engine_1 = require("@astram/engine");
|
|
7
|
+
/**
|
|
8
|
+
* The schema every identity table declares (single-source): the app-database token, resolved by
|
|
9
|
+
* the engine at registration to `<namespace>_app` (configurable-framework-db, ADR-0037). It is
|
|
10
|
+
* never a database name: SQL built outside the engine uses {@link IdentitySchemaNames}.
|
|
11
|
+
*/
|
|
12
|
+
exports.SCHEMA = engine_1.FRAMEWORK_SCHEMA.app;
|
|
13
|
+
/**
|
|
14
|
+
* The framework database names from `SHIELD_DB_NAMESPACE` (unset ⇒ `shield_*`), for a class built
|
|
15
|
+
* without explicit names. Call at construction (boot) only, never per request; an invalid
|
|
16
|
+
* namespace throws (fail closed). `IdentityFeatureModule.forRoot` always passes its names.
|
|
17
|
+
*/
|
|
18
|
+
function frameworkSchemasFromEnv() {
|
|
19
|
+
return (0, core_1.resolveFrameworkSchemas)(process.env);
|
|
20
|
+
}
|
|
@@ -14,8 +14,14 @@
|
|
|
14
14
|
*/
|
|
15
15
|
const { Company, User, Role, Department, Branch } = require('@astram/identity');
|
|
16
16
|
const { columnForRole, reflectObjectType } = require('@astram/engine');
|
|
17
|
+
const { frameworkSchemasForMigration } = require('@astram/core');
|
|
18
|
+
|
|
19
|
+
// Database names come from `ctx.schemas` (SHIELD_DB_NAMESPACE, ADR-0037); under an older
|
|
20
|
+
// @astram/migrate without it, the default names apply only while the namespace is unset.
|
|
21
|
+
// replacesChecksums: every checksum this file had in a published release (git release tags), so
|
|
22
|
+
// an existing ledger row is accepted and updated, not reported as drift (ADR-0037).
|
|
23
|
+
const replacesChecksums = ['027818f28b12088392ba5f2194a842ef36815aa34b9df7669683c9cdf24c316f'];
|
|
17
24
|
|
|
18
|
-
const SCHEMA = 'shield_app';
|
|
19
25
|
const TS = 'TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP';
|
|
20
26
|
|
|
21
27
|
// Reflect the @ObjectType/@Field metadata off each decorated class into its registration
|
|
@@ -30,7 +36,9 @@ const BranchReg = reflectObjectType(Branch);
|
|
|
30
36
|
// Resolve a column by role from a reflected object registration (single source of truth).
|
|
31
37
|
const col = (reg, role) => columnForRole(reg, role);
|
|
32
38
|
|
|
33
|
-
async function up(
|
|
39
|
+
async function up(ctx) {
|
|
40
|
+
const { query } = ctx;
|
|
41
|
+
const SCHEMA = frameworkSchemasForMigration(ctx, process.env).app;
|
|
34
42
|
// companies (the tenant) — hierarchy + type + status.
|
|
35
43
|
await query(`CREATE TABLE IF NOT EXISTS \`${SCHEMA}\`.\`${CompanyReg.table}\` (
|
|
36
44
|
${col(CompanyReg, 'primaryKey')} VARCHAR(36) NOT NULL,
|
|
@@ -89,7 +97,9 @@ async function up({ query }) {
|
|
|
89
97
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`);
|
|
90
98
|
}
|
|
91
99
|
|
|
92
|
-
async function down(
|
|
100
|
+
async function down(ctx) {
|
|
101
|
+
const { query } = ctx;
|
|
102
|
+
const SCHEMA = frameworkSchemasForMigration(ctx, process.env).app;
|
|
93
103
|
// Reverse dependency order — membership tables (1710000000002) drop first via their
|
|
94
104
|
// own down(); here drop the catalogs + principal + tenant.
|
|
95
105
|
for (const t of [BranchReg.table, DepartmentReg.table, RoleReg.table, UserReg.table, CompanyReg.table]) {
|
|
@@ -97,4 +107,4 @@ async function down({ query }) {
|
|
|
97
107
|
}
|
|
98
108
|
}
|
|
99
109
|
|
|
100
|
-
module.exports = { up, down };
|
|
110
|
+
module.exports = { up, down, replacesChecksums };
|
|
@@ -15,8 +15,14 @@ const {
|
|
|
15
15
|
CompanyMembership, MembershipPosting, CompanyDepartment, CompanyBranch,
|
|
16
16
|
} = require('@astram/identity');
|
|
17
17
|
const { columnForRole, reflectObjectType } = require('@astram/engine');
|
|
18
|
+
const { frameworkSchemasForMigration } = require('@astram/core');
|
|
19
|
+
|
|
20
|
+
// Database names come from `ctx.schemas` (SHIELD_DB_NAMESPACE, ADR-0037); under an older
|
|
21
|
+
// @astram/migrate without it, the default names apply only while the namespace is unset.
|
|
22
|
+
// replacesChecksums: every checksum this file had in a published release (git release tags), so
|
|
23
|
+
// an existing ledger row is accepted and updated, not reported as drift (ADR-0037).
|
|
24
|
+
const replacesChecksums = ['a214c2b29d1b21093851d3644581b17757a78e2341b50c67c7b9b3846c5dfc9e'];
|
|
18
25
|
|
|
19
|
-
const SCHEMA = 'shield_app';
|
|
20
26
|
const TS = 'TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP';
|
|
21
27
|
|
|
22
28
|
// The decorated classes carry NO static `.table` — @ObjectType/@Field metadata is read
|
|
@@ -34,7 +40,9 @@ const CompanyDepartmentReg = reflectObjectType(CompanyDepartment);
|
|
|
34
40
|
const CompanyBranchReg = reflectObjectType(CompanyBranch);
|
|
35
41
|
const col = (reg, role) => columnForRole(reg, role);
|
|
36
42
|
|
|
37
|
-
async function up(
|
|
43
|
+
async function up(ctx) {
|
|
44
|
+
const { query } = ctx;
|
|
45
|
+
const SCHEMA = frameworkSchemasForMigration(ctx, process.env).app;
|
|
38
46
|
// company_membership — first-class membership + active-tenant pointer.
|
|
39
47
|
await query(`CREATE TABLE IF NOT EXISTS \`${SCHEMA}\`.\`${CompanyMembershipReg.table}\` (
|
|
40
48
|
${col(CompanyMembershipReg, 'primaryKey')} VARCHAR(36) NOT NULL,
|
|
@@ -102,10 +110,12 @@ async function up({ query }) {
|
|
|
102
110
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`);
|
|
103
111
|
}
|
|
104
112
|
|
|
105
|
-
async function down(
|
|
113
|
+
async function down(ctx) {
|
|
114
|
+
const { query } = ctx;
|
|
115
|
+
const SCHEMA = frameworkSchemasForMigration(ctx, process.env).app;
|
|
106
116
|
for (const t of [MembershipPostingReg.table, CompanyBranchReg.table, CompanyDepartmentReg.table, CompanyMembershipReg.table]) {
|
|
107
117
|
await query(`DROP TABLE IF EXISTS \`${SCHEMA}\`.\`${t}\``);
|
|
108
118
|
}
|
|
109
119
|
}
|
|
110
120
|
|
|
111
|
-
module.exports = { up, down };
|
|
121
|
+
module.exports = { up, down, replacesChecksums };
|
|
@@ -25,9 +25,17 @@
|
|
|
25
25
|
* Idempotent (CREATE TABLE IF NOT EXISTS).
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
|
-
const
|
|
28
|
+
const { frameworkSchemasForMigration } = require('@astram/core');
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
// Database names come from `ctx.schemas` (SHIELD_DB_NAMESPACE, ADR-0037); under an older
|
|
31
|
+
// @astram/migrate without it, the default names apply only while the namespace is unset.
|
|
32
|
+
// replacesChecksums: every checksum this file had in a published release (git release tags), so
|
|
33
|
+
// an existing ledger row is accepted and updated, not reported as drift (ADR-0037).
|
|
34
|
+
const replacesChecksums = ['440923cce54f25764ca53fd68d3e18d7069207b22c3f2afb5551469a0279a1aa'];
|
|
35
|
+
|
|
36
|
+
async function up(ctx) {
|
|
37
|
+
const { query } = ctx;
|
|
38
|
+
const SCHEMA = frameworkSchemasForMigration(ctx, process.env).identity;
|
|
31
39
|
// identity_credential — one row per login principal. Resolved by
|
|
32
40
|
// ic_identity_hint (username/email) at login. ic_password_hash is the
|
|
33
41
|
// Argon2id encoded hash the engine verifies against. Whether the principal
|
|
@@ -51,8 +59,10 @@ async function up({ query }) {
|
|
|
51
59
|
);
|
|
52
60
|
}
|
|
53
61
|
|
|
54
|
-
async function down(
|
|
62
|
+
async function down(ctx) {
|
|
63
|
+
const { query } = ctx;
|
|
64
|
+
const SCHEMA = frameworkSchemasForMigration(ctx, process.env).identity;
|
|
55
65
|
await query(`DROP TABLE IF EXISTS \`${SCHEMA}\`.\`identity_credential\``);
|
|
56
66
|
}
|
|
57
67
|
|
|
58
|
-
module.exports = { up, down };
|
|
68
|
+
module.exports = { up, down, replacesChecksums };
|