@siremzam/sentinel 0.3.0 → 0.3.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/README.md +573 -176
- package/dist/engine-C6IASR5F.d.cts +283 -0
- package/dist/engine-C6IASR5F.d.ts +283 -0
- package/dist/index.cjs +877 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +58 -0
- package/dist/index.d.ts +58 -11
- package/dist/index.js +838 -6
- package/dist/index.js.map +1 -1
- package/dist/middleware/express.cjs +58 -0
- package/dist/middleware/express.cjs.map +1 -0
- package/dist/middleware/express.d.cts +35 -0
- package/dist/middleware/express.d.ts +6 -6
- package/dist/middleware/express.js +31 -39
- package/dist/middleware/express.js.map +1 -1
- package/dist/middleware/fastify.cjs +59 -0
- package/dist/middleware/fastify.cjs.map +1 -0
- package/dist/middleware/fastify.d.cts +29 -0
- package/dist/middleware/fastify.d.ts +6 -6
- package/dist/middleware/fastify.js +32 -39
- package/dist/middleware/fastify.js.map +1 -1
- package/dist/middleware/nestjs.cjs +84 -0
- package/dist/middleware/nestjs.cjs.map +1 -0
- package/dist/middleware/nestjs.d.cts +67 -0
- package/dist/middleware/nestjs.d.ts +9 -9
- package/dist/middleware/nestjs.js +51 -76
- package/dist/middleware/nestjs.js.map +1 -1
- package/dist/server.cjs +184 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +54 -0
- package/dist/server.d.ts +10 -8
- package/dist/server.js +149 -153
- package/dist/server.js.map +1 -1
- package/package.json +22 -9
- package/dist/engine.d.ts +0 -70
- package/dist/engine.d.ts.map +0 -1
- package/dist/engine.js +0 -562
- package/dist/engine.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/middleware/express.d.ts.map +0 -1
- package/dist/middleware/fastify.d.ts.map +0 -1
- package/dist/middleware/nestjs.d.ts.map +0 -1
- package/dist/policy-builder.d.ts +0 -39
- package/dist/policy-builder.d.ts.map +0 -1
- package/dist/policy-builder.js +0 -92
- package/dist/policy-builder.js.map +0 -1
- package/dist/role-hierarchy.d.ts +0 -42
- package/dist/role-hierarchy.d.ts.map +0 -1
- package/dist/role-hierarchy.js +0 -87
- package/dist/role-hierarchy.js.map +0 -1
- package/dist/serialization.d.ts +0 -52
- package/dist/serialization.d.ts.map +0 -1
- package/dist/serialization.js +0 -144
- package/dist/serialization.js.map +0 -1
- package/dist/server.d.ts.map +0 -1
- package/dist/types.d.ts +0 -137
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -27
- package/dist/types.js.map +0 -1
package/dist/server.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
1
|
+
import * as http from 'http';
|
|
2
|
+
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
3
|
+
import { R as ResourceContext, S as SchemaDefinition, A as AccessEngine, r as Subject } from './engine-C6IASR5F.js';
|
|
4
|
+
|
|
5
|
+
interface ServerOptions<S extends SchemaDefinition> {
|
|
5
6
|
engine: AccessEngine<S>;
|
|
6
7
|
port?: number;
|
|
7
8
|
host?: string;
|
|
@@ -19,7 +20,7 @@ export interface ServerOptions<S extends SchemaDefinition> {
|
|
|
19
20
|
/** Maximum request body size in bytes. Defaults to 1 MB. */
|
|
20
21
|
maxBodyBytes?: number;
|
|
21
22
|
}
|
|
22
|
-
|
|
23
|
+
interface EvalRequestBody {
|
|
23
24
|
subject: {
|
|
24
25
|
id: string;
|
|
25
26
|
roles: {
|
|
@@ -44,9 +45,10 @@ export interface EvalRequestBody {
|
|
|
44
45
|
* **Security**: This server has no authentication by default.
|
|
45
46
|
* In production, provide an `authenticate` hook or run behind a VPN/service mesh.
|
|
46
47
|
*/
|
|
47
|
-
|
|
48
|
+
declare function createAuthServer<S extends SchemaDefinition>(options: ServerOptions<S>): {
|
|
48
49
|
start(): Promise<void>;
|
|
49
50
|
stop(): Promise<void>;
|
|
50
|
-
httpServer:
|
|
51
|
+
httpServer: http.Server<typeof IncomingMessage, typeof ServerResponse>;
|
|
51
52
|
};
|
|
52
|
-
|
|
53
|
+
|
|
54
|
+
export { type EvalRequestBody, type ServerOptions, createAuthServer };
|
package/dist/server.js
CHANGED
|
@@ -1,163 +1,159 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// src/server.ts
|
|
2
|
+
import { createServer } from "http";
|
|
3
|
+
var DEFAULT_MAX_BODY_BYTES = 1024 * 1024;
|
|
3
4
|
function readBody(req, maxBytes) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
chunks.push(chunk);
|
|
24
|
-
});
|
|
25
|
-
req.on("end", () => {
|
|
26
|
-
settle(() => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
27
|
-
});
|
|
28
|
-
req.on("error", (err) => {
|
|
29
|
-
settle(() => reject(err));
|
|
30
|
-
});
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
const chunks = [];
|
|
7
|
+
let received = 0;
|
|
8
|
+
let settled = false;
|
|
9
|
+
function settle(fn) {
|
|
10
|
+
if (!settled) {
|
|
11
|
+
settled = true;
|
|
12
|
+
fn();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
req.on("data", (chunk) => {
|
|
16
|
+
if (settled) return;
|
|
17
|
+
received += chunk.length;
|
|
18
|
+
if (received > maxBytes) {
|
|
19
|
+
req.resume();
|
|
20
|
+
settle(() => reject(new Error(`Request body exceeds ${maxBytes} bytes`)));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
chunks.push(chunk);
|
|
31
24
|
});
|
|
25
|
+
req.on("end", () => {
|
|
26
|
+
settle(() => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
27
|
+
});
|
|
28
|
+
req.on("error", (err) => {
|
|
29
|
+
settle(() => reject(err));
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
32
|
}
|
|
33
33
|
function sendJson(res, status, body) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
const json = JSON.stringify(body);
|
|
35
|
+
res.writeHead(status, {
|
|
36
|
+
"Content-Type": "application/json",
|
|
37
|
+
"Content-Length": Buffer.byteLength(json)
|
|
38
|
+
});
|
|
39
|
+
res.end(json);
|
|
40
40
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
41
|
+
function createAuthServer(options) {
|
|
42
|
+
const {
|
|
43
|
+
engine,
|
|
44
|
+
port = 3100,
|
|
45
|
+
host = "0.0.0.0",
|
|
46
|
+
maxBodyBytes = DEFAULT_MAX_BODY_BYTES
|
|
47
|
+
} = options;
|
|
48
|
+
const startTime = Date.now();
|
|
49
|
+
const server = createServer(async (req, res) => {
|
|
50
|
+
const pathname = new URL(req.url ?? "/", "http://localhost").pathname;
|
|
51
|
+
const method = req.method ?? "GET";
|
|
52
|
+
try {
|
|
53
|
+
if (options.authenticate) {
|
|
54
|
+
const authed = await options.authenticate(req);
|
|
55
|
+
if (authed !== true) {
|
|
56
|
+
sendJson(res, 401, { error: "Unauthorized" });
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (method === "GET" && pathname === "/health") {
|
|
61
|
+
const body = {
|
|
62
|
+
status: "ok",
|
|
63
|
+
rulesLoaded: engine.getRules().length,
|
|
64
|
+
uptime: Date.now() - startTime
|
|
65
|
+
};
|
|
66
|
+
sendJson(res, 200, body);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (method === "GET" && pathname === "/rules") {
|
|
70
|
+
const rules = engine.getRules().map((r) => ({
|
|
71
|
+
id: r.id,
|
|
72
|
+
effect: r.effect,
|
|
73
|
+
roles: r.roles,
|
|
74
|
+
actions: r.actions,
|
|
75
|
+
resources: r.resources,
|
|
76
|
+
priority: r.priority,
|
|
77
|
+
description: r.description,
|
|
78
|
+
hasConditions: (r.conditions?.length ?? 0) > 0
|
|
79
|
+
}));
|
|
80
|
+
sendJson(res, 200, { rules });
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (method === "POST" && pathname === "/evaluate") {
|
|
84
|
+
let raw;
|
|
85
|
+
try {
|
|
86
|
+
raw = await readBody(req, maxBodyBytes);
|
|
87
|
+
} catch (err) {
|
|
88
|
+
sendJson(res, 413, {
|
|
89
|
+
error: err instanceof Error ? err.message : "Payload too large"
|
|
90
|
+
});
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
let body;
|
|
58
94
|
try {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
if (method === "GET" && pathname === "/health") {
|
|
67
|
-
const body = {
|
|
68
|
-
status: "ok",
|
|
69
|
-
rulesLoaded: engine.getRules().length,
|
|
70
|
-
uptime: Date.now() - startTime,
|
|
71
|
-
};
|
|
72
|
-
sendJson(res, 200, body);
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
if (method === "GET" && pathname === "/rules") {
|
|
76
|
-
const rules = engine.getRules().map((r) => ({
|
|
77
|
-
id: r.id,
|
|
78
|
-
effect: r.effect,
|
|
79
|
-
roles: r.roles,
|
|
80
|
-
actions: r.actions,
|
|
81
|
-
resources: r.resources,
|
|
82
|
-
priority: r.priority,
|
|
83
|
-
description: r.description,
|
|
84
|
-
hasConditions: (r.conditions?.length ?? 0) > 0,
|
|
85
|
-
}));
|
|
86
|
-
sendJson(res, 200, { rules });
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
if (method === "POST" && pathname === "/evaluate") {
|
|
90
|
-
let raw;
|
|
91
|
-
try {
|
|
92
|
-
raw = await readBody(req, maxBodyBytes);
|
|
93
|
-
}
|
|
94
|
-
catch (err) {
|
|
95
|
-
sendJson(res, 413, {
|
|
96
|
-
error: err instanceof Error ? err.message : "Payload too large",
|
|
97
|
-
});
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
let body;
|
|
101
|
-
try {
|
|
102
|
-
body = JSON.parse(raw);
|
|
103
|
-
}
|
|
104
|
-
catch {
|
|
105
|
-
sendJson(res, 400, { error: "Invalid JSON body" });
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
if (!body.subject || !body.action || !body.resource) {
|
|
109
|
-
sendJson(res, 400, {
|
|
110
|
-
error: "Missing required fields: subject, action, resource",
|
|
111
|
-
});
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
if (typeof body.action !== "string" || typeof body.resource !== "string") {
|
|
115
|
-
sendJson(res, 400, { error: "action and resource must be strings" });
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
if (typeof body.subject !== "object" ||
|
|
119
|
-
typeof body.subject.id !== "string" ||
|
|
120
|
-
!Array.isArray(body.subject.roles)) {
|
|
121
|
-
sendJson(res, 400, {
|
|
122
|
-
error: "subject must have a string id and a roles array",
|
|
123
|
-
});
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
const subject = options.resolveSubject
|
|
127
|
-
? await options.resolveSubject(body)
|
|
128
|
-
: body.subject;
|
|
129
|
-
const decision = engine.evaluate(subject, body.action, body.resource, body.resourceContext ?? {}, body.tenantId);
|
|
130
|
-
const response = {
|
|
131
|
-
allowed: decision.allowed,
|
|
132
|
-
effect: decision.effect,
|
|
133
|
-
reason: decision.reason,
|
|
134
|
-
matchedRuleId: decision.matchedRule?.id ?? null,
|
|
135
|
-
durationMs: decision.durationMs,
|
|
136
|
-
};
|
|
137
|
-
sendJson(res, 200, response);
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
sendJson(res, 404, { error: "Not found" });
|
|
95
|
+
body = JSON.parse(raw);
|
|
96
|
+
} catch {
|
|
97
|
+
sendJson(res, 400, { error: "Invalid JSON body" });
|
|
98
|
+
return;
|
|
141
99
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
100
|
+
if (!body.subject || !body.action || !body.resource) {
|
|
101
|
+
sendJson(res, 400, {
|
|
102
|
+
error: "Missing required fields: subject, action, resource"
|
|
103
|
+
});
|
|
104
|
+
return;
|
|
145
105
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
106
|
+
if (typeof body.action !== "string" || typeof body.resource !== "string") {
|
|
107
|
+
sendJson(res, 400, { error: "action and resource must be strings" });
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (typeof body.subject !== "object" || typeof body.subject.id !== "string" || !Array.isArray(body.subject.roles)) {
|
|
111
|
+
sendJson(res, 400, {
|
|
112
|
+
error: "subject must have a string id and a roles array"
|
|
113
|
+
});
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const subject = options.resolveSubject ? await options.resolveSubject(body) : body.subject;
|
|
117
|
+
const decision = engine.evaluate(
|
|
118
|
+
subject,
|
|
119
|
+
body.action,
|
|
120
|
+
body.resource,
|
|
121
|
+
body.resourceContext ?? {},
|
|
122
|
+
body.tenantId
|
|
123
|
+
);
|
|
124
|
+
const response = {
|
|
125
|
+
allowed: decision.allowed,
|
|
126
|
+
effect: decision.effect,
|
|
127
|
+
reason: decision.reason,
|
|
128
|
+
matchedRuleId: decision.matchedRule?.id ?? null,
|
|
129
|
+
durationMs: decision.durationMs
|
|
130
|
+
};
|
|
131
|
+
sendJson(res, 200, response);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
sendJson(res, 404, { error: "Not found" });
|
|
135
|
+
} catch (err) {
|
|
136
|
+
const message = err instanceof Error ? err.message : "Internal server error";
|
|
137
|
+
sendJson(res, 500, { error: message });
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
return {
|
|
141
|
+
start() {
|
|
142
|
+
return new Promise((resolve) => {
|
|
143
|
+
server.listen(port, host, () => {
|
|
144
|
+
resolve();
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
},
|
|
148
|
+
stop() {
|
|
149
|
+
return new Promise((resolve, reject) => {
|
|
150
|
+
server.close((err) => err ? reject(err) : resolve());
|
|
151
|
+
});
|
|
152
|
+
},
|
|
153
|
+
httpServer: server
|
|
154
|
+
};
|
|
162
155
|
}
|
|
156
|
+
export {
|
|
157
|
+
createAuthServer
|
|
158
|
+
};
|
|
163
159
|
//# sourceMappingURL=server.js.map
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAA6C,MAAM,WAAW,CAAC;AAIpF,MAAM,sBAAsB,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO;AAmDnD,SAAS,QAAQ,CAAC,GAAoB,EAAE,QAAgB;IACtD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,SAAS,MAAM,CAAC,EAAc;YAC5B,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,GAAG,IAAI,CAAC;gBACf,EAAE,EAAE,CAAC;YACP,CAAC;QACH,CAAC;QAED,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YAC/B,IAAI,OAAO;gBAAE,OAAO;YACpB,QAAQ,IAAI,KAAK,CAAC,MAAM,CAAC;YACzB,IAAI,QAAQ,GAAG,QAAQ,EAAE,CAAC;gBACxB,GAAG,CAAC,MAAM,EAAE,CAAC;gBACb,MAAM,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,wBAAwB,QAAQ,QAAQ,CAAC,CAAC,CAAC,CAAC;gBAC1E,OAAO;YACT,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YACjB,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACjE,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACtB,MAAM,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,QAAQ,CAAC,GAAmB,EAAE,MAAc,EAAE,IAAa;IAClE,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAClC,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE;QACpB,cAAc,EAAE,kBAAkB;QAClC,gBAAgB,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC;KAC1C,CAAC,CAAC;IACH,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAAyB;IAEzB,MAAM,EACJ,MAAM,EACN,IAAI,GAAG,IAAI,EACX,IAAI,GAAG,SAAS,EAChB,YAAY,GAAG,sBAAsB,GACtC,GAAG,OAAO,CAAC;IACZ,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE7B,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QAC7C,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,kBAAkB,CAAC,CAAC,QAAQ,CAAC;QACtE,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,KAAK,CAAC;QAEnC,IAAI,CAAC;YACH,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;gBACzB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;gBAC/C,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;oBACpB,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;oBAC9C,OAAO;gBACT,CAAC;YACH,CAAC;YAED,IAAI,MAAM,KAAK,KAAK,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC/C,MAAM,IAAI,GAAmB;oBAC3B,MAAM,EAAE,IAAI;oBACZ,WAAW,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,MAAM;oBACrC,MAAM,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;iBAC/B,CAAC;gBACF,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;gBACzB,OAAO;YACT,CAAC;YAED,IAAI,MAAM,KAAK,KAAK,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBAC9C,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;oBAC1C,EAAE,EAAE,CAAC,CAAC,EAAE;oBACR,MAAM,EAAE,CAAC,CAAC,MAAM;oBAChB,KAAK,EAAE,CAAC,CAAC,KAAK;oBACd,OAAO,EAAE,CAAC,CAAC,OAAO;oBAClB,SAAS,EAAE,CAAC,CAAC,SAAS;oBACtB,QAAQ,EAAE,CAAC,CAAC,QAAQ;oBACpB,WAAW,EAAE,CAAC,CAAC,WAAW;oBAC1B,aAAa,EAAE,CAAC,CAAC,CAAC,UAAU,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC;iBAC/C,CAAC,CAAC,CAAC;gBACJ,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;gBAC9B,OAAO;YACT,CAAC;YAED,IAAI,MAAM,KAAK,MAAM,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;gBAClD,IAAI,GAAW,CAAC;gBAChB,IAAI,CAAC;oBACH,GAAG,GAAG,MAAM,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;gBAC1C,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE;wBACjB,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,mBAAmB;qBAChE,CAAC,CAAC;oBACH,OAAO;gBACT,CAAC;gBAED,IAAI,IAAqB,CAAC;gBAC1B,IAAI,CAAC;oBACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBACzB,CAAC;gBAAC,MAAM,CAAC;oBACP,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAAC;oBACnD,OAAO;gBACT,CAAC;gBAED,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACpD,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE;wBACjB,KAAK,EAAE,oDAAoD;qBAC5D,CAAC,CAAC;oBACH,OAAO;gBACT,CAAC;gBAED,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,IAAI,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;oBACzE,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,qCAAqC,EAAE,CAAC,CAAC;oBACrE,OAAO;gBACT,CAAC;gBAED,IACE,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ;oBAChC,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,KAAK,QAAQ;oBACnC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAClC,CAAC;oBACD,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE;wBACjB,KAAK,EAAE,iDAAiD;qBACzD,CAAC,CAAC;oBACH,OAAO;gBACT,CAAC;gBAED,MAAM,OAAO,GAAe,OAAO,CAAC,cAAc;oBAChD,CAAC,CAAC,MAAM,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC;oBACpC,CAAC,CAAE,IAAI,CAAC,OAAiC,CAAC;gBAE5C,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAC9B,OAAO,EACP,IAAI,CAAC,MAA+C,EACpD,IAAI,CAAC,QAAiD,EACtD,IAAI,CAAC,eAAe,IAAI,EAAE,EAC1B,IAAI,CAAC,QAAQ,CACd,CAAC;gBAEF,MAAM,QAAQ,GAAqB;oBACjC,OAAO,EAAE,QAAQ,CAAC,OAAO;oBACzB,MAAM,EAAE,QAAQ,CAAC,MAAM;oBACvB,MAAM,EAAE,QAAQ,CAAC,MAAM;oBACvB,aAAa,EAAE,QAAQ,CAAC,WAAW,EAAE,EAAE,IAAI,IAAI;oBAC/C,UAAU,EAAE,QAAQ,CAAC,UAAU;iBAChC,CAAC;gBACF,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;gBAC7B,OAAO;YACT,CAAC;YAED,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;QAC7C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,uBAAuB,CAAC;YAC7E,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QACzC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,KAAK;YACH,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC7B,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE;oBAC7B,OAAO,EAAE,CAAC;gBACZ,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC;QAED,IAAI;YACF,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBACrC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YACzD,CAAC,CAAC,CAAC;QACL,CAAC;QAED,UAAU,EAAE,MAAM;KACnB,CAAC;AACJ,CAAC"}
|
|
1
|
+
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["import { createServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport type { AccessEngine } from \"./engine.js\";\nimport type { SchemaDefinition, Subject, ResourceContext } from \"./types.js\";\n\nconst DEFAULT_MAX_BODY_BYTES = 1024 * 1024; // 1 MB\n\n// ---------------------------------------------------------------------------\n// Standalone HTTP authorization server\n// ---------------------------------------------------------------------------\n\nexport interface ServerOptions<S extends SchemaDefinition> {\n engine: AccessEngine<S>;\n port?: number;\n host?: string;\n /**\n * Optional hook to resolve a Subject from the request body.\n * Defaults to using body.subject directly.\n */\n resolveSubject?: (body: EvalRequestBody) => Subject<S> | Promise<Subject<S>>;\n /**\n * Optional authentication hook. Return true to allow the request,\n * false to reject with 401. Called before any endpoint logic.\n * If not provided, all requests are allowed (suitable for internal networks only).\n */\n authenticate?: (req: IncomingMessage) => boolean | Promise<boolean>;\n /** Maximum request body size in bytes. Defaults to 1 MB. */\n maxBodyBytes?: number;\n}\n\nexport interface EvalRequestBody {\n subject: {\n id: string;\n roles: { role: string; tenantId?: string }[];\n attributes?: Record<string, unknown>;\n };\n action: string;\n resource: string;\n resourceContext?: ResourceContext;\n tenantId?: string;\n}\n\ninterface EvalResponseBody {\n allowed: boolean;\n effect: string;\n reason: string;\n matchedRuleId: string | null;\n durationMs: number;\n}\n\ninterface HealthResponse {\n status: \"ok\";\n rulesLoaded: number;\n uptime: number;\n}\n\nfunction readBody(req: IncomingMessage, maxBytes: number): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n let received = 0;\n let settled = false;\n\n function settle(fn: () => void) {\n if (!settled) {\n settled = true;\n fn();\n }\n }\n\n req.on(\"data\", (chunk: Buffer) => {\n if (settled) return;\n received += chunk.length;\n if (received > maxBytes) {\n req.resume();\n settle(() => reject(new Error(`Request body exceeds ${maxBytes} bytes`)));\n return;\n }\n chunks.push(chunk);\n });\n req.on(\"end\", () => {\n settle(() => resolve(Buffer.concat(chunks).toString(\"utf-8\")));\n });\n req.on(\"error\", (err) => {\n settle(() => reject(err));\n });\n });\n}\n\nfunction sendJson(res: ServerResponse, status: number, body: unknown): void {\n const json = JSON.stringify(body);\n res.writeHead(status, {\n \"Content-Type\": \"application/json\",\n \"Content-Length\": Buffer.byteLength(json),\n });\n res.end(json);\n}\n\n/**\n * Create a standalone HTTP authorization server.\n *\n * Endpoints:\n * POST /evaluate — evaluate an authorization request\n * GET /health — health check + rule count\n * GET /rules — list all loaded rules (without conditions)\n *\n * **Security**: This server has no authentication by default.\n * In production, provide an `authenticate` hook or run behind a VPN/service mesh.\n */\nexport function createAuthServer<S extends SchemaDefinition>(\n options: ServerOptions<S>,\n) {\n const {\n engine,\n port = 3100,\n host = \"0.0.0.0\",\n maxBodyBytes = DEFAULT_MAX_BODY_BYTES,\n } = options;\n const startTime = Date.now();\n\n const server = createServer(async (req, res) => {\n const pathname = new URL(req.url ?? \"/\", \"http://localhost\").pathname;\n const method = req.method ?? \"GET\";\n\n try {\n if (options.authenticate) {\n const authed = await options.authenticate(req);\n if (authed !== true) {\n sendJson(res, 401, { error: \"Unauthorized\" });\n return;\n }\n }\n\n if (method === \"GET\" && pathname === \"/health\") {\n const body: HealthResponse = {\n status: \"ok\",\n rulesLoaded: engine.getRules().length,\n uptime: Date.now() - startTime,\n };\n sendJson(res, 200, body);\n return;\n }\n\n if (method === \"GET\" && pathname === \"/rules\") {\n const rules = engine.getRules().map((r) => ({\n id: r.id,\n effect: r.effect,\n roles: r.roles,\n actions: r.actions,\n resources: r.resources,\n priority: r.priority,\n description: r.description,\n hasConditions: (r.conditions?.length ?? 0) > 0,\n }));\n sendJson(res, 200, { rules });\n return;\n }\n\n if (method === \"POST\" && pathname === \"/evaluate\") {\n let raw: string;\n try {\n raw = await readBody(req, maxBodyBytes);\n } catch (err) {\n sendJson(res, 413, {\n error: err instanceof Error ? err.message : \"Payload too large\",\n });\n return;\n }\n\n let body: EvalRequestBody;\n try {\n body = JSON.parse(raw);\n } catch {\n sendJson(res, 400, { error: \"Invalid JSON body\" });\n return;\n }\n\n if (!body.subject || !body.action || !body.resource) {\n sendJson(res, 400, {\n error: \"Missing required fields: subject, action, resource\",\n });\n return;\n }\n\n if (typeof body.action !== \"string\" || typeof body.resource !== \"string\") {\n sendJson(res, 400, { error: \"action and resource must be strings\" });\n return;\n }\n\n if (\n typeof body.subject !== \"object\" ||\n typeof body.subject.id !== \"string\" ||\n !Array.isArray(body.subject.roles)\n ) {\n sendJson(res, 400, {\n error: \"subject must have a string id and a roles array\",\n });\n return;\n }\n\n const subject: Subject<S> = options.resolveSubject\n ? await options.resolveSubject(body)\n : (body.subject as unknown as Subject<S>);\n\n const decision = engine.evaluate(\n subject,\n body.action as Parameters<typeof engine.evaluate>[1],\n body.resource as Parameters<typeof engine.evaluate>[2],\n body.resourceContext ?? {},\n body.tenantId,\n );\n\n const response: EvalResponseBody = {\n allowed: decision.allowed,\n effect: decision.effect,\n reason: decision.reason,\n matchedRuleId: decision.matchedRule?.id ?? null,\n durationMs: decision.durationMs,\n };\n sendJson(res, 200, response);\n return;\n }\n\n sendJson(res, 404, { error: \"Not found\" });\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Internal server error\";\n sendJson(res, 500, { error: message });\n }\n });\n\n return {\n start(): Promise<void> {\n return new Promise((resolve) => {\n server.listen(port, host, () => {\n resolve();\n });\n });\n },\n\n stop(): Promise<void> {\n return new Promise((resolve, reject) => {\n server.close((err) => (err ? reject(err) : resolve()));\n });\n },\n\n httpServer: server,\n };\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@siremzam/sentinel",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "TypeScript-first, domain-driven authorization engine for modern SaaS apps",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -8,34 +8,44 @@
|
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
10
|
"types": "./dist/index.d.ts",
|
|
11
|
-
"import": "./dist/index.js"
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"require": "./dist/index.cjs",
|
|
13
|
+
"default": "./dist/index.js"
|
|
12
14
|
},
|
|
13
15
|
"./middleware/express": {
|
|
14
16
|
"types": "./dist/middleware/express.d.ts",
|
|
15
|
-
"import": "./dist/middleware/express.js"
|
|
17
|
+
"import": "./dist/middleware/express.js",
|
|
18
|
+
"require": "./dist/middleware/express.cjs",
|
|
19
|
+
"default": "./dist/middleware/express.js"
|
|
16
20
|
},
|
|
17
21
|
"./middleware/fastify": {
|
|
18
22
|
"types": "./dist/middleware/fastify.d.ts",
|
|
19
|
-
"import": "./dist/middleware/fastify.js"
|
|
23
|
+
"import": "./dist/middleware/fastify.js",
|
|
24
|
+
"require": "./dist/middleware/fastify.cjs",
|
|
25
|
+
"default": "./dist/middleware/fastify.js"
|
|
20
26
|
},
|
|
21
27
|
"./middleware/nestjs": {
|
|
22
28
|
"types": "./dist/middleware/nestjs.d.ts",
|
|
23
|
-
"import": "./dist/middleware/nestjs.js"
|
|
29
|
+
"import": "./dist/middleware/nestjs.js",
|
|
30
|
+
"require": "./dist/middleware/nestjs.cjs",
|
|
31
|
+
"default": "./dist/middleware/nestjs.js"
|
|
24
32
|
},
|
|
25
33
|
"./server": {
|
|
26
34
|
"types": "./dist/server.d.ts",
|
|
27
|
-
"import": "./dist/server.js"
|
|
35
|
+
"import": "./dist/server.js",
|
|
36
|
+
"require": "./dist/server.cjs",
|
|
37
|
+
"default": "./dist/server.js"
|
|
28
38
|
}
|
|
29
39
|
},
|
|
30
40
|
"files": [
|
|
31
41
|
"dist"
|
|
32
42
|
],
|
|
33
43
|
"scripts": {
|
|
34
|
-
"build": "
|
|
44
|
+
"build": "tsup",
|
|
35
45
|
"test": "vitest run",
|
|
36
46
|
"test:watch": "vitest",
|
|
37
47
|
"test:coverage": "vitest run --coverage",
|
|
38
|
-
"lint": "eslint src
|
|
48
|
+
"lint": "eslint src/",
|
|
39
49
|
"typecheck": "tsc --noEmit",
|
|
40
50
|
"benchmark": "tsx benchmarks/run.ts",
|
|
41
51
|
"prepublishOnly": "npm run build"
|
|
@@ -57,7 +67,7 @@
|
|
|
57
67
|
"type": "git",
|
|
58
68
|
"url": "git+https://github.com/vegtelenseg/sentinel.git"
|
|
59
69
|
},
|
|
60
|
-
"homepage": "https://github.
|
|
70
|
+
"homepage": "https://vegtelenseg.github.io/sentinel-example/",
|
|
61
71
|
"bugs": {
|
|
62
72
|
"url": "https://github.com/vegtelenseg/sentinel/issues"
|
|
63
73
|
},
|
|
@@ -65,8 +75,11 @@
|
|
|
65
75
|
"devDependencies": {
|
|
66
76
|
"@types/node": "^22.0.0",
|
|
67
77
|
"@vitest/coverage-v8": "^3.0.0",
|
|
78
|
+
"eslint": "^9.0.0",
|
|
79
|
+
"tsup": "^8.0.0",
|
|
68
80
|
"tsx": "^4.21.0",
|
|
69
81
|
"typescript": "^5.7.0",
|
|
82
|
+
"typescript-eslint": "^8.0.0",
|
|
70
83
|
"vitest": "^3.0.0"
|
|
71
84
|
},
|
|
72
85
|
"engines": {
|
package/dist/engine.d.ts
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import type { SchemaDefinition, InferAction, InferResource, PolicyRule, Decision, Subject, ResourceContext, DecisionListener, EngineOptions, ExplainResult } from "./types.js";
|
|
2
|
-
import { RuleBuilder } from "./policy-builder.js";
|
|
3
|
-
import type { RoleHierarchy } from "./role-hierarchy.js";
|
|
4
|
-
export interface AccessEngineOptions<S extends SchemaDefinition> extends EngineOptions<S> {
|
|
5
|
-
roleHierarchy?: RoleHierarchy<S>;
|
|
6
|
-
/**
|
|
7
|
-
* Enable LRU cache for evaluation results.
|
|
8
|
-
* Only caches evaluations of rules WITHOUT conditions (context-independent).
|
|
9
|
-
* Rules with conditions are never cached since their result depends on resourceContext.
|
|
10
|
-
*/
|
|
11
|
-
cacheSize?: number;
|
|
12
|
-
}
|
|
13
|
-
export declare class AccessEngine<S extends SchemaDefinition> {
|
|
14
|
-
private compiled;
|
|
15
|
-
private listeners;
|
|
16
|
-
private asyncConditions;
|
|
17
|
-
private _defaultDeny;
|
|
18
|
-
private _strictTenancy;
|
|
19
|
-
private hierarchy?;
|
|
20
|
-
private cache?;
|
|
21
|
-
private conditionErrorHandler?;
|
|
22
|
-
constructor(options: AccessEngineOptions<S>);
|
|
23
|
-
addRule(rule: PolicyRule<S>): this;
|
|
24
|
-
addRules(...rules: PolicyRule<S>[]): this;
|
|
25
|
-
removeRule(ruleId: string): boolean;
|
|
26
|
-
getRules(): ReadonlyArray<PolicyRule<S>>;
|
|
27
|
-
clearRules(): void;
|
|
28
|
-
clearCache(): void;
|
|
29
|
-
get cacheStats(): {
|
|
30
|
-
size: number;
|
|
31
|
-
maxSize: number;
|
|
32
|
-
} | null;
|
|
33
|
-
allow(): RuleBuilder<S>;
|
|
34
|
-
deny(): RuleBuilder<S>;
|
|
35
|
-
onDecision(listener: DecisionListener<S>): () => void;
|
|
36
|
-
private emit;
|
|
37
|
-
evaluate(subject: Subject<S>, action: InferAction<S>, resource: InferResource<S>, resourceContext?: ResourceContext, tenantId?: string): Decision<S>;
|
|
38
|
-
evaluateAsync(subject: Subject<S>, action: InferAction<S>, resource: InferResource<S>, resourceContext?: ResourceContext, tenantId?: string): Promise<Decision<S>>;
|
|
39
|
-
permitted(subject: Subject<S>, resource: InferResource<S>, actions: InferAction<S>[], resourceContext?: ResourceContext, tenantId?: string): Set<InferAction<S>>;
|
|
40
|
-
permittedAsync(subject: Subject<S>, resource: InferResource<S>, actions: InferAction<S>[], resourceContext?: ResourceContext, tenantId?: string): Promise<Set<InferAction<S>>>;
|
|
41
|
-
explain(subject: Subject<S>, action: InferAction<S>, resource: InferResource<S>, resourceContext?: ResourceContext, tenantId?: string): ExplainResult<S>;
|
|
42
|
-
explainAsync(subject: Subject<S>, action: InferAction<S>, resource: InferResource<S>, resourceContext?: ResourceContext, tenantId?: string): Promise<ExplainResult<S>>;
|
|
43
|
-
can(subject: Subject<S>): PerformStep<S>;
|
|
44
|
-
private validateInput;
|
|
45
|
-
private enforceTenancy;
|
|
46
|
-
private buildContext;
|
|
47
|
-
private evaluateConditionsSync;
|
|
48
|
-
private emitConditionError;
|
|
49
|
-
private matchRules;
|
|
50
|
-
private sortCandidates;
|
|
51
|
-
private matchesAction;
|
|
52
|
-
private resolveRoles;
|
|
53
|
-
private buildDecision;
|
|
54
|
-
}
|
|
55
|
-
declare class PerformStep<S extends SchemaDefinition> {
|
|
56
|
-
private engine;
|
|
57
|
-
private subject;
|
|
58
|
-
constructor(engine: AccessEngine<S>, subject: Subject<S>);
|
|
59
|
-
perform(action: InferAction<S>): OnStep<S>;
|
|
60
|
-
}
|
|
61
|
-
declare class OnStep<S extends SchemaDefinition> {
|
|
62
|
-
private engine;
|
|
63
|
-
private subject;
|
|
64
|
-
private action;
|
|
65
|
-
constructor(engine: AccessEngine<S>, subject: Subject<S>, action: InferAction<S>);
|
|
66
|
-
on(resource: InferResource<S>, resourceContext?: ResourceContext, tenantId?: string): Decision<S>;
|
|
67
|
-
onAsync(resource: InferResource<S>, resourceContext?: ResourceContext, tenantId?: string): Promise<Decision<S>>;
|
|
68
|
-
}
|
|
69
|
-
export {};
|
|
70
|
-
//# sourceMappingURL=engine.d.ts.map
|
package/dist/engine.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
|