@i4e/invest4edu-access-core 0.4.0 → 0.4.2
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/package.json +1 -1
- package/src/index.js +1 -1
- package/src/tenant-plugin.js +28 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@i4e/invest4edu-access-core",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "Shared access-control primitives (Track 3: tenant keystone, role capabilities, reportee tree, feature flags) for NeoFindesk backends.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
package/src/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Tenant isolation (D1 keystone) + role capabilities (G7).
|
|
4
4
|
*/
|
|
5
5
|
export { default as als, runWithTenant, runAsSystem, getTenantStore } from "./tenant-context.js";
|
|
6
|
-
export { default as tenantPlugin } from "./tenant-plugin.js";
|
|
6
|
+
export { default as tenantPlugin, setTenantMode, getTenantMode } from "./tenant-plugin.js";
|
|
7
7
|
export {
|
|
8
8
|
CAPABILITY_FLAGS,
|
|
9
9
|
DEFAULT_ROLE_CAPABILITIES,
|
package/src/tenant-plugin.js
CHANGED
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
* injects `account_id` from the request's ALS store (tenant-context.js), so a query cannot
|
|
6
6
|
* reach Mongo without the caller's tenant boundary — even if the controller forgot it.
|
|
7
7
|
*
|
|
8
|
-
* Modes (env `TENANT_ENFORCEMENT`, default `
|
|
9
|
-
* -
|
|
10
|
-
* -
|
|
8
|
+
* Modes (env `TENANT_ENFORCEMENT`, default `off`):
|
|
9
|
+
* - `off` (DEFAULT) → literal no-op; behaviour identical to pre-Track-3. Safe merge baseline.
|
|
10
|
+
* - `warn` → inject `account_id` when identity present; log identity-less reads.
|
|
11
|
+
* - `enforce` → inject when present; THROW on identity-less reads (fail-closed).
|
|
12
|
+
* Rollout ladder: off → warn (burn-in, watch `[tenant] identity-less`) → enforce.
|
|
11
13
|
*
|
|
12
14
|
* Bypass: `runAsSystem(fn)` or `.setOptions({ skipTenant: true })` for legitimately
|
|
13
15
|
* cross-tenant reads. NOT covered: aggregation pipelines and write ops (reads are C0 scope).
|
|
@@ -18,9 +20,31 @@
|
|
|
18
20
|
import als from "./tenant-context.js";
|
|
19
21
|
|
|
20
22
|
const READ_OPS = ["find", "findOne", "countDocuments"];
|
|
21
|
-
const
|
|
23
|
+
const VALID = new Set(["off", "warn", "enforce"]);
|
|
24
|
+
|
|
25
|
+
// Enforcement mode is resolvable from TWO places, so it can be flipped from the admin UI
|
|
26
|
+
// (DB) WITHOUT a restart, while the env var stays the infra-level emergency override.
|
|
27
|
+
// Precedence: env TENANT_ENFORCEMENT (if valid) > DB value (setTenantMode) > "off"
|
|
28
|
+
let dbMode = null;
|
|
29
|
+
|
|
30
|
+
/** Called by the backend from the `accessconfig` DB doc so a UI toggle takes effect (~refresh). */
|
|
31
|
+
export function setTenantMode(m) {
|
|
32
|
+
const v = m ? String(m).toLowerCase() : "";
|
|
33
|
+
dbMode = VALID.has(v) ? v : null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Effective mode. */
|
|
37
|
+
export function getTenantMode() {
|
|
38
|
+
const env = (process.env.TENANT_ENFORCEMENT || "").toLowerCase();
|
|
39
|
+
if (VALID.has(env)) return env; // explicit env wins (emergency / infra)
|
|
40
|
+
return dbMode || "off";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const mode = getTenantMode;
|
|
22
44
|
|
|
23
45
|
function tenantHook() {
|
|
46
|
+
if (mode() === "off") return; // literal no-op — identical to pre-Track-3
|
|
47
|
+
|
|
24
48
|
const opts = typeof this.getOptions === "function" ? this.getOptions() : this.options || {};
|
|
25
49
|
if (opts && opts.skipTenant) return;
|
|
26
50
|
|