@stratum-hq/control-plane 0.2.1 → 0.2.3
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/app.d.ts.map +1 -1
- package/dist/app.js +2 -0
- package/dist/app.js.map +1 -1
- package/dist/db/connection.d.ts.map +1 -1
- package/dist/db/connection.js +7 -2
- package/dist/db/connection.js.map +1 -1
- package/dist/db/migrate.d.ts.map +1 -1
- package/dist/db/migrate.js +3 -46
- package/dist/db/migrate.js.map +1 -1
- package/dist/routes/abac.d.ts +4 -0
- package/dist/routes/abac.d.ts.map +1 -0
- package/dist/routes/abac.js +34 -0
- package/dist/routes/abac.js.map +1 -0
- package/dist/routes/webhooks.d.ts.map +1 -1
- package/dist/routes/webhooks.js +58 -16
- package/dist/routes/webhooks.js.map +1 -1
- package/package.json +1 -1
package/dist/app.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AACA,OAAgB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AACA,OAAgB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AA2CnD,wBAAsB,QAAQ,IAAI,OAAO,CAAC,eAAe,CAAC,CA0EzD"}
|
package/dist/app.js
CHANGED
|
@@ -29,6 +29,7 @@ const maintenance_js_1 = require("./routes/maintenance.js");
|
|
|
29
29
|
const regions_js_1 = require("./routes/regions.js");
|
|
30
30
|
const roles_js_1 = require("./routes/roles.js");
|
|
31
31
|
const config_diff_js_1 = require("./routes/config-diff.js");
|
|
32
|
+
const abac_js_1 = require("./routes/abac.js");
|
|
32
33
|
const telemetry_js_1 = require("./middleware/telemetry.js");
|
|
33
34
|
const config_js_2 = require("./config.js");
|
|
34
35
|
const connection_js_1 = require("./db/connection.js");
|
|
@@ -108,6 +109,7 @@ async function buildApp() {
|
|
|
108
109
|
await app.register((0, roles_js_1.createRoleRoutes)(stratum), { prefix: "/api/v1/roles" });
|
|
109
110
|
await app.register((0, maintenance_js_1.createMaintenanceRoutes)(stratum), { prefix: "/api/v1/maintenance" });
|
|
110
111
|
await app.register((0, config_diff_js_1.createConfigDiffRoutes)(stratum), { prefix: "/api/v1/config" });
|
|
112
|
+
await app.register((0, abac_js_1.createAbacRoutes)(stratum), { prefix: "/api/v1/tenants/:tenantId/abac-policies" });
|
|
111
113
|
return app;
|
|
112
114
|
}
|
|
113
115
|
//# sourceMappingURL=app.js.map
|
package/dist/app.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/db/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAMpB,wBAAgB,OAAO,IAAI,EAAE,CAAC,IAAI,
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/db/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAMpB,wBAAgB,OAAO,IAAI,EAAE,CAAC,IAAI,CAiBjC;AAED,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAK/C"}
|
package/dist/db/connection.js
CHANGED
|
@@ -10,12 +10,17 @@ const { Pool } = pg_1.default;
|
|
|
10
10
|
let pool = null;
|
|
11
11
|
function getPool() {
|
|
12
12
|
if (!pool) {
|
|
13
|
+
// Note: pg.Pool has no built-in queue depth limit. Under heavy load,
|
|
14
|
+
// excess requests queue in memory until connectionTimeoutMillis elapses.
|
|
15
|
+
// Keep connectionTimeoutMillis short so queued requests fail fast rather
|
|
16
|
+
// than accumulating and exhausting the event loop.
|
|
13
17
|
pool = new Pool({
|
|
14
18
|
connectionString: process.env.DATABASE_URL ||
|
|
15
|
-
"postgres://
|
|
19
|
+
"postgres://stratum_app:stratum_dev@localhost:5432/stratum",
|
|
16
20
|
max: 20,
|
|
17
21
|
idleTimeoutMillis: 30000,
|
|
18
|
-
connectionTimeoutMillis:
|
|
22
|
+
connectionTimeoutMillis: 2000,
|
|
23
|
+
allowExitOnIdle: true,
|
|
19
24
|
});
|
|
20
25
|
}
|
|
21
26
|
return pool;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/db/connection.ts"],"names":[],"mappings":";;;;;AAMA,
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/db/connection.ts"],"names":[],"mappings":";;;;;AAMA,0BAiBC;AAED,8BAKC;AA9BD,4CAAoB;AAEpB,MAAM,EAAE,IAAI,EAAE,GAAG,YAAE,CAAC;AAEpB,IAAI,IAAI,GAAmB,IAAI,CAAC;AAEhC,SAAgB,OAAO;IACrB,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,qEAAqE;QACrE,yEAAyE;QACzE,yEAAyE;QACzE,mDAAmD;QACnD,IAAI,GAAG,IAAI,IAAI,CAAC;YACd,gBAAgB,EACd,OAAO,CAAC,GAAG,CAAC,YAAY;gBACxB,2DAA2D;YAC7D,GAAG,EAAE,EAAE;YACP,iBAAiB,EAAE,KAAK;YACxB,uBAAuB,EAAE,IAAI;YAC7B,eAAe,EAAE,IAAI;SACtB,CAAC,CAAC;IACL,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAEM,KAAK,UAAU,SAAS;IAC7B,IAAI,IAAI,EAAE,CAAC;QACT,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;QACjB,IAAI,GAAG,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
package/dist/db/migrate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/db/migrate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/db/migrate.ts"],"names":[],"mappings":"AAGA,iBAAe,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAKtC;AAED,OAAO,EAAE,OAAO,EAAE,CAAC"}
|
package/dist/db/migrate.js
CHANGED
|
@@ -1,55 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.migrate = migrate;
|
|
7
|
-
const
|
|
8
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
4
|
+
const lib_1 = require("@stratum-hq/lib");
|
|
9
5
|
const connection_js_1 = require("./connection.js");
|
|
10
6
|
async function migrate() {
|
|
11
7
|
const pool = (0, connection_js_1.getPool)();
|
|
12
|
-
|
|
13
|
-
await pool.
|
|
14
|
-
CREATE TABLE IF NOT EXISTS _migrations (
|
|
15
|
-
id SERIAL PRIMARY KEY,
|
|
16
|
-
name TEXT NOT NULL UNIQUE,
|
|
17
|
-
applied_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
18
|
-
)
|
|
19
|
-
`);
|
|
20
|
-
// Get applied migrations
|
|
21
|
-
const { rows: applied } = await pool.query("SELECT name FROM _migrations ORDER BY id");
|
|
22
|
-
const appliedSet = new Set(applied.map((r) => r.name));
|
|
23
|
-
// Get migration files
|
|
24
|
-
const migrationsDir = node_path_1.default.resolve(__dirname, "migrations");
|
|
25
|
-
const files = node_fs_1.default
|
|
26
|
-
.readdirSync(migrationsDir)
|
|
27
|
-
.filter((f) => f.endsWith(".sql"))
|
|
28
|
-
.sort();
|
|
29
|
-
for (const file of files) {
|
|
30
|
-
if (appliedSet.has(file)) {
|
|
31
|
-
console.log(` Skipping ${file} (already applied)`);
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
|
-
console.log(` Applying ${file}...`);
|
|
35
|
-
const sql = node_fs_1.default.readFileSync(node_path_1.default.join(migrationsDir, file), "utf-8");
|
|
36
|
-
const client = await pool.connect();
|
|
37
|
-
try {
|
|
38
|
-
await client.query("BEGIN");
|
|
39
|
-
await client.query(sql);
|
|
40
|
-
await client.query("INSERT INTO _migrations (name) VALUES ($1)", [file]);
|
|
41
|
-
await client.query("COMMIT");
|
|
42
|
-
console.log(` Applied ${file}`);
|
|
43
|
-
}
|
|
44
|
-
catch (err) {
|
|
45
|
-
await client.query("ROLLBACK");
|
|
46
|
-
console.error(` Failed to apply ${file}:`, err);
|
|
47
|
-
throw err;
|
|
48
|
-
}
|
|
49
|
-
finally {
|
|
50
|
-
client.release();
|
|
51
|
-
}
|
|
52
|
-
}
|
|
8
|
+
console.log("Running migrations...");
|
|
9
|
+
await (0, lib_1.migrate)({ pool, enforceRls: process.env.NODE_ENV === "production" });
|
|
53
10
|
console.log("Migrations complete.");
|
|
54
11
|
}
|
|
55
12
|
//# sourceMappingURL=migrate.js.map
|
package/dist/db/migrate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../src/db/migrate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../src/db/migrate.ts"],"names":[],"mappings":";;AAUS,0BAAO;AAVhB,yCAA2D;AAC3D,mDAA0C;AAE1C,KAAK,UAAU,OAAO;IACpB,MAAM,IAAI,GAAG,IAAA,uBAAO,GAAE,CAAC;IACvB,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;IACrC,MAAM,IAAA,aAAa,EAAC,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC,CAAC;IACjF,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;AACtC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"abac.d.ts","sourceRoot":"","sources":["../../src/routes/abac.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAI1C,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,IACd,KAAK,eAAe,KAAG,OAAO,CAAC,IAAI,CAAC,CA0CtE"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createAbacRoutes = createAbacRoutes;
|
|
4
|
+
const core_1 = require("@stratum-hq/core");
|
|
5
|
+
const tenant_scope_js_1 = require("../middleware/tenant-scope.js");
|
|
6
|
+
function createAbacRoutes(stratum) {
|
|
7
|
+
return async function abacRoutes(app) {
|
|
8
|
+
// Tenant-scoped keys can only access ABAC policies for their own tenant subtree
|
|
9
|
+
app.addHook("preHandler", (0, tenant_scope_js_1.createTenantScopeGuard)(stratum, tenant_scope_js_1.fromParamTenantId));
|
|
10
|
+
// POST /api/v1/tenants/:tenantId/abac-policies — Create ABAC policy
|
|
11
|
+
app.post("/", async (request, reply) => {
|
|
12
|
+
const input = core_1.CreateAbacPolicyInputSchema.parse(request.body);
|
|
13
|
+
const policy = await stratum.createAbacPolicy(request.params.tenantId, input);
|
|
14
|
+
reply.status(201).send(policy);
|
|
15
|
+
});
|
|
16
|
+
// GET /api/v1/tenants/:tenantId/abac-policies — List own ABAC policies
|
|
17
|
+
app.get("/", async (request, reply) => {
|
|
18
|
+
const policies = await stratum.getAbacPolicies(request.params.tenantId);
|
|
19
|
+
reply.status(200).send(policies);
|
|
20
|
+
});
|
|
21
|
+
// POST /api/v1/tenants/:tenantId/abac/evaluate — Evaluate an ABAC request
|
|
22
|
+
app.post("/evaluate", async (request, reply) => {
|
|
23
|
+
const evalRequest = core_1.AbacEvaluationRequestSchema.parse(request.body);
|
|
24
|
+
const result = await stratum.evaluateAbac(request.params.tenantId, evalRequest);
|
|
25
|
+
reply.status(200).send(result);
|
|
26
|
+
});
|
|
27
|
+
// DELETE /api/v1/tenants/:tenantId/abac-policies/:policyId — Delete ABAC policy
|
|
28
|
+
app.delete("/:policyId", async (request, reply) => {
|
|
29
|
+
await stratum.deleteAbacPolicy(request.params.tenantId, request.params.policyId);
|
|
30
|
+
reply.status(204).send();
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=abac.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"abac.js","sourceRoot":"","sources":["../../src/routes/abac.ts"],"names":[],"mappings":";;AAKA,4CA2CC;AA9CD,2CAAoJ;AACpJ,mEAA0F;AAE1F,SAAgB,gBAAgB,CAAC,OAAgB;IAC/C,OAAO,KAAK,UAAU,UAAU,CAAC,GAAoB;QACnD,gFAAgF;QAChF,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,IAAA,wCAAsB,EAAC,OAAO,EAAE,mCAAiB,CAAC,CAAC,CAAC;QAE9E,oEAAoE;QACpE,GAAG,CAAC,IAAI,CAAmC,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;YACvE,MAAM,KAAK,GAAG,kCAA2B,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAA0B,CAAC;YACvF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,gBAAgB,CAC3C,OAAO,CAAC,MAAM,CAAC,QAAQ,EACvB,KAAK,CACN,CAAC;YACF,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;QAEH,uEAAuE;QACvE,GAAG,CAAC,GAAG,CAAmC,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;YACtE,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YACxE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;QAEH,0EAA0E;QAC1E,GAAG,CAAC,IAAI,CAAmC,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;YAC/E,MAAM,WAAW,GAAG,kCAA2B,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAA0B,CAAC;YAC7F,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,YAAY,CACvC,OAAO,CAAC,MAAM,CAAC,QAAQ,EACvB,WAAW,CACZ,CAAC;YACF,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;QAEH,gFAAgF;QAChF,GAAG,CAAC,MAAM,CACR,YAAY,EACZ,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;YACvB,MAAM,OAAO,CAAC,gBAAgB,CAC5B,OAAO,CAAC,MAAM,CAAC,QAAQ,EACvB,OAAO,CAAC,MAAM,CAAC,QAAQ,CACxB,CAAC;YACF,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QAC3B,CAAC,CACF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../src/routes/webhooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAkB,MAAM,SAAS,CAAC;AAE1D,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../src/routes/webhooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAkB,MAAM,SAAS,CAAC;AAE1D,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AA6C1C,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,IACd,KAAK,eAAe,KAAG,OAAO,CAAC,IAAI,CAAC,CA8HzE"}
|
package/dist/routes/webhooks.js
CHANGED
|
@@ -3,49 +3,91 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.createWebhookRoutes = createWebhookRoutes;
|
|
4
4
|
const core_1 = require("@stratum-hq/core");
|
|
5
5
|
const audit_logs_js_1 = require("./audit-logs.js");
|
|
6
|
+
const tenant_scope_js_1 = require("../middleware/tenant-scope.js");
|
|
6
7
|
/**
|
|
7
8
|
* Post-fetch tenant access check for webhook routes.
|
|
8
9
|
* Used after loading a webhook by ID to verify the caller's key
|
|
9
10
|
* has access to the webhook's tenant.
|
|
10
|
-
*
|
|
11
|
+
*
|
|
12
|
+
* - Global keys (tenant_id = null) have unrestricted access.
|
|
13
|
+
* - Global webhooks (tenant_id = null) are only accessible to global-scope keys.
|
|
14
|
+
* - Scoped keys may access webhooks belonging to their tenant or any descendant.
|
|
11
15
|
*/
|
|
12
|
-
function assertTenantAccess(request, webhookTenantId) {
|
|
16
|
+
async function assertTenantAccess(request, webhookTenantId, stratum) {
|
|
13
17
|
const apiKey = request.apiKey;
|
|
14
|
-
if (!apiKey
|
|
18
|
+
if (!apiKey)
|
|
15
19
|
return;
|
|
16
|
-
|
|
20
|
+
// Global key: unrestricted access
|
|
21
|
+
if (apiKey.tenant_id === null)
|
|
22
|
+
return;
|
|
23
|
+
// Scoped key cannot access global webhooks
|
|
24
|
+
if (webhookTenantId === null) {
|
|
25
|
+
throw new core_1.ForbiddenError("API key does not have access to this webhook's tenant");
|
|
26
|
+
}
|
|
27
|
+
// Fast path: exact match
|
|
28
|
+
if (apiKey.tenant_id === webhookTenantId)
|
|
29
|
+
return;
|
|
30
|
+
// Hierarchy check: webhook's tenant must be a descendant of the key's tenant
|
|
31
|
+
try {
|
|
32
|
+
const target = await stratum.getTenant(webhookTenantId);
|
|
33
|
+
const ancestorIds = target.ancestry_path.split("/").filter(Boolean);
|
|
34
|
+
if (ancestorIds.includes(apiKey.tenant_id))
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// Tenant not found — fail closed for scoped keys
|
|
17
39
|
throw new core_1.ForbiddenError("API key does not have access to this webhook's tenant");
|
|
18
40
|
}
|
|
41
|
+
throw new core_1.ForbiddenError("API key does not have access to this webhook's tenant");
|
|
19
42
|
}
|
|
20
43
|
function createWebhookRoutes(stratum) {
|
|
21
44
|
return async function webhookRoutes(app) {
|
|
45
|
+
// Tenant-scoped keys can only access webhooks for their own tenant subtree.
|
|
46
|
+
// This guard covers the POST / (create) route where tenant_id is in the body.
|
|
47
|
+
app.addHook("preHandler", (0, tenant_scope_js_1.createTenantScopeGuard)(stratum, tenant_scope_js_1.fromBodyTenantId));
|
|
22
48
|
// POST /api/v1/webhooks — Create webhook
|
|
23
49
|
app.post("/", async (request, reply) => {
|
|
24
50
|
const input = core_1.CreateWebhookInputSchema.parse(request.body);
|
|
25
|
-
//
|
|
26
|
-
if (request.apiKey?.tenant_id !== null && input.tenant_id
|
|
27
|
-
|
|
51
|
+
// Scoped keys cannot create global webhooks
|
|
52
|
+
if (request.apiKey?.tenant_id !== null && input.tenant_id === null) {
|
|
53
|
+
throw new core_1.ForbiddenError("API key does not have access to create global webhooks");
|
|
28
54
|
}
|
|
29
55
|
const webhook = await stratum.createWebhook(input, (0, audit_logs_js_1.buildAuditContext)(request));
|
|
30
56
|
reply.status(201).send(webhook);
|
|
31
57
|
});
|
|
32
58
|
// GET /api/v1/webhooks — List webhooks (optional ?tenant_id=)
|
|
33
59
|
app.get("/", async (request, reply) => {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
60
|
+
const scopedTenantId = request.apiKey?.tenant_id;
|
|
61
|
+
if (scopedTenantId) {
|
|
62
|
+
// Scoped keys: list webhooks for their tenant and all descendants
|
|
63
|
+
const descendants = await stratum.getDescendants(scopedTenantId);
|
|
64
|
+
const allowedIds = new Set([scopedTenantId, ...descendants.map((d) => d.id)]);
|
|
65
|
+
const queryTenantId = request.query.tenant_id;
|
|
66
|
+
// If caller filtered by a specific tenant, ensure it's in the allowed set
|
|
67
|
+
const fetchTenantId = queryTenantId ?? scopedTenantId;
|
|
68
|
+
if (!allowedIds.has(fetchTenantId)) {
|
|
69
|
+
throw new core_1.ForbiddenError("API key does not have access to this tenant's webhooks");
|
|
70
|
+
}
|
|
71
|
+
const webhooks = await stratum.listWebhooks(fetchTenantId);
|
|
72
|
+
// Exclude global webhooks from scoped key responses
|
|
73
|
+
reply.status(200).send(webhooks.filter((w) => w.tenant_id !== null));
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
// Global key: return all (or filtered by query param)
|
|
77
|
+
const webhooks = await stratum.listWebhooks(request.query.tenant_id);
|
|
78
|
+
reply.status(200).send(webhooks);
|
|
79
|
+
}
|
|
38
80
|
});
|
|
39
81
|
// GET /api/v1/webhooks/:id — Get webhook
|
|
40
82
|
app.get("/:id", async (request, reply) => {
|
|
41
83
|
const webhook = await stratum.getWebhook(request.params.id);
|
|
42
|
-
assertTenantAccess(request, webhook.tenant_id);
|
|
84
|
+
await assertTenantAccess(request, webhook.tenant_id, stratum);
|
|
43
85
|
reply.status(200).send(webhook);
|
|
44
86
|
});
|
|
45
87
|
// PATCH /api/v1/webhooks/:id — Update webhook
|
|
46
88
|
app.patch("/:id", async (request, reply) => {
|
|
47
89
|
const existing = await stratum.getWebhook(request.params.id);
|
|
48
|
-
assertTenantAccess(request, existing.tenant_id);
|
|
90
|
+
await assertTenantAccess(request, existing.tenant_id, stratum);
|
|
49
91
|
const input = core_1.UpdateWebhookInputSchema.parse(request.body);
|
|
50
92
|
const webhook = await stratum.updateWebhook(request.params.id, input, (0, audit_logs_js_1.buildAuditContext)(request));
|
|
51
93
|
reply.status(200).send(webhook);
|
|
@@ -53,21 +95,21 @@ function createWebhookRoutes(stratum) {
|
|
|
53
95
|
// DELETE /api/v1/webhooks/:id — Delete webhook
|
|
54
96
|
app.delete("/:id", async (request, reply) => {
|
|
55
97
|
const existing = await stratum.getWebhook(request.params.id);
|
|
56
|
-
assertTenantAccess(request, existing.tenant_id);
|
|
98
|
+
await assertTenantAccess(request, existing.tenant_id, stratum);
|
|
57
99
|
await stratum.deleteWebhook(request.params.id, (0, audit_logs_js_1.buildAuditContext)(request));
|
|
58
100
|
reply.status(204).send();
|
|
59
101
|
});
|
|
60
102
|
// GET /api/v1/webhooks/:id/deliveries — List deliveries for a webhook
|
|
61
103
|
app.get("/:id/deliveries", async (request, reply) => {
|
|
62
104
|
const webhook = await stratum.getWebhook(request.params.id);
|
|
63
|
-
assertTenantAccess(request, webhook.tenant_id);
|
|
105
|
+
await assertTenantAccess(request, webhook.tenant_id, stratum);
|
|
64
106
|
const deliveries = await stratum.listWebhookDeliveries(request.params.id);
|
|
65
107
|
reply.status(200).send(deliveries);
|
|
66
108
|
});
|
|
67
109
|
// POST /api/v1/webhooks/:id/test — Send test event
|
|
68
110
|
app.post("/:id/test", async (request, reply) => {
|
|
69
111
|
const webhook = await stratum.getWebhook(request.params.id);
|
|
70
|
-
assertTenantAccess(request, webhook.tenant_id);
|
|
112
|
+
await assertTenantAccess(request, webhook.tenant_id, stratum);
|
|
71
113
|
const result = await stratum.testWebhook(request.params.id);
|
|
72
114
|
reply.status(200).send(result);
|
|
73
115
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../src/routes/webhooks.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../src/routes/webhooks.ts"],"names":[],"mappings":"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"}
|