@o3co/create-auth-policy-verifier 0.3.1
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/LICENSE +201 -0
- package/README.ja.md +94 -0
- package/README.md +94 -0
- package/dist/__tests__/index.test.d.mts +2 -0
- package/dist/__tests__/index.test.d.mts.map +1 -0
- package/dist/__tests__/index.test.mjs +213 -0
- package/dist/cli.d.mts +3 -0
- package/dist/cli.d.mts.map +1 -0
- package/dist/cli.mjs +5 -0
- package/dist/index.d.mts +18 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +169 -0
- package/package.json +31 -0
- package/templates/standalone/.dockerignore +9 -0
- package/templates/standalone/Dockerfile +55 -0
- package/templates/standalone/LICENSE +201 -0
- package/templates/standalone/Makefile +12 -0
- package/templates/standalone/README.ja.md +84 -0
- package/templates/standalone/README.md +84 -0
- package/templates/standalone/config/application.conf +35 -0
- package/templates/standalone/docker-compose.test.yml +5 -0
- package/templates/standalone/docker-compose.yml +13 -0
- package/templates/standalone/package.json +34 -0
- package/templates/standalone/src/__tests__/smoke.test.mts +103 -0
- package/templates/standalone/src/main.mts +35 -0
- package/templates/standalone/tsconfig.json +27 -0
- package/templates/standalone/vitest.config.mts +10 -0
- package/templates/versions.json +5 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@o3co/auth-policy-verifier-standalone",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"packageManager": "pnpm@10.29.3",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=22.0.0"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"start": "node dist/main.mjs",
|
|
14
|
+
"test": "vitest run",
|
|
15
|
+
"debug": "NODE_OPTIONS='--conditions=development' tsx watch src/main.mts"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@o3co/auth.policy-verifier.core": "workspace:*",
|
|
19
|
+
"@o3co/auth.policy-verifier.builtins": "workspace:*",
|
|
20
|
+
"@o3co/auth.policy-verifier.server": "workspace:*",
|
|
21
|
+
"@o3co/auth.utils": "^0.0.2",
|
|
22
|
+
"@o3co/ts.hocon": "^1.1.0",
|
|
23
|
+
"express": "^5.2.1"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^25.1.0",
|
|
27
|
+
"@types/supertest": "^7.2.0",
|
|
28
|
+
"jose": "^6.2.2",
|
|
29
|
+
"supertest": "^7.2.2",
|
|
30
|
+
"tsx": "^4.21.0",
|
|
31
|
+
"typescript": "^5.9.3",
|
|
32
|
+
"vitest": "^4.1.2"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 1o1 Co. Ltd.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* Smoke tests for the standalone template.
|
|
6
|
+
*
|
|
7
|
+
* These tests verify that the app created by the standalone template boots correctly
|
|
8
|
+
* and that core HTTP endpoints respond as expected.
|
|
9
|
+
*
|
|
10
|
+
* The composition here intentionally mirrors main.mts — using builtinCollectorsModule
|
|
11
|
+
* and registry keys that match application.conf — so that renames or interface changes
|
|
12
|
+
* in the builtins package are caught here before reaching production.
|
|
13
|
+
*
|
|
14
|
+
* Note: ResourceActionPermissionRuleCollector is omitted from the smoke config because
|
|
15
|
+
* it requires a permission store (e.g. StaticPermissionCollector) to grant any access.
|
|
16
|
+
* The scope-based check via ResourceActionScopeRuleCollector is sufficient to exercise
|
|
17
|
+
* the allow/deny paths in isolation.
|
|
18
|
+
*/
|
|
19
|
+
import { builtinCollectorsModule } from "@o3co/auth.policy-verifier.builtins";
|
|
20
|
+
import {
|
|
21
|
+
AppConfigSchema,
|
|
22
|
+
builtinKeyResolversModule,
|
|
23
|
+
createApp,
|
|
24
|
+
} from "@o3co/auth.policy-verifier.server";
|
|
25
|
+
import { SignJWT } from "jose";
|
|
26
|
+
import request from "supertest";
|
|
27
|
+
import { describe, expect, it } from "vitest";
|
|
28
|
+
|
|
29
|
+
const JWT_SECRET = "standalone-smoke-secret";
|
|
30
|
+
const secretKey = new TextEncoder().encode(JWT_SECRET);
|
|
31
|
+
|
|
32
|
+
async function signToken(payload: Record<string, unknown>): Promise<string> {
|
|
33
|
+
return new SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuedAt().sign(secretKey);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Config mirrors the structure of application.conf, using the same builtin registry keys.
|
|
37
|
+
// PayloadScopeCollector extracts scopes from the JWT "scope" claim.
|
|
38
|
+
// ResourceActionScopeRuleCollector requires scope "<action>:<resourceType>" to be present.
|
|
39
|
+
// DotNotationResourceParser is the default and matches the omitted resource.parser in application.conf.
|
|
40
|
+
const baseConfig = AppConfigSchema.parse({
|
|
41
|
+
oauth: { jwt: { secret: JWT_SECRET, validate: true } },
|
|
42
|
+
attribute: {
|
|
43
|
+
collectors: [
|
|
44
|
+
{ collector: "PayloadScopeCollector" },
|
|
45
|
+
{ collector: "PayloadSubjectIdCollector" },
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
rule: {
|
|
49
|
+
collectors: [{ collector: "ResourceActionScopeRuleCollector" }],
|
|
50
|
+
},
|
|
51
|
+
// resource.parser defaults to "DotNotationResourceParser" — matches application.conf omission
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe("standalone smoke", () => {
|
|
55
|
+
it("GET /healthcheck returns 200", async () => {
|
|
56
|
+
const app = await createApp({
|
|
57
|
+
pathResolver: (s: string) => s,
|
|
58
|
+
config: baseConfig,
|
|
59
|
+
modules: [builtinCollectorsModule, builtinKeyResolversModule],
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const res = await request(app).get("/healthcheck");
|
|
63
|
+
expect(res.status).toBe(200);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("POST /verify with sufficient scope returns 200 (allow)", async () => {
|
|
67
|
+
const app = await createApp({
|
|
68
|
+
pathResolver: (s: string) => s,
|
|
69
|
+
config: baseConfig,
|
|
70
|
+
modules: [builtinCollectorsModule, builtinKeyResolversModule],
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// PayloadScopeCollector extracts "read:document" from the JWT scope claim.
|
|
74
|
+
// ResourceActionScopeRuleCollector requires scope "<action>:<resourceType>".
|
|
75
|
+
// DotNotationResourceParser maps "document" → resourceType "document".
|
|
76
|
+
const token = await signToken({ scope: "read:document" });
|
|
77
|
+
const res = await request(app)
|
|
78
|
+
.post("/verify")
|
|
79
|
+
.set("Authorization", `Bearer ${token}`)
|
|
80
|
+
.send({ resource: "document", action: "read" });
|
|
81
|
+
|
|
82
|
+
expect(res.status).toBe(200);
|
|
83
|
+
expect(res.body.decision).toBe("allow");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("POST /verify with insufficient scope returns 403 (deny)", async () => {
|
|
87
|
+
const app = await createApp({
|
|
88
|
+
pathResolver: (s: string) => s,
|
|
89
|
+
config: baseConfig,
|
|
90
|
+
modules: [builtinCollectorsModule, builtinKeyResolversModule],
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Token has "write:document" but action is "read", so HasScope("read:document") fails.
|
|
94
|
+
const token = await signToken({ scope: "write:document" });
|
|
95
|
+
const res = await request(app)
|
|
96
|
+
.post("/verify")
|
|
97
|
+
.set("Authorization", `Bearer ${token}`)
|
|
98
|
+
.send({ resource: "document", action: "read" });
|
|
99
|
+
|
|
100
|
+
expect(res.status).toBe(403);
|
|
101
|
+
expect(res.body.decision).toBe("deny");
|
|
102
|
+
});
|
|
103
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 1o1 Co. Ltd.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* Standalone entrypoint — creates and starts the policy-verifier server.
|
|
6
|
+
*/
|
|
7
|
+
import { resolve } from "node:path";
|
|
8
|
+
import { builtinCollectorsModule } from "@o3co/auth.policy-verifier.builtins";
|
|
9
|
+
import {
|
|
10
|
+
AppConfigSchema,
|
|
11
|
+
builtinKeyResolversModule,
|
|
12
|
+
createApp,
|
|
13
|
+
} from "@o3co/auth.policy-verifier.server";
|
|
14
|
+
import { createLogger, gracefulShutdown } from "@o3co/auth.utils";
|
|
15
|
+
import { parseFile } from "@o3co/ts.hocon";
|
|
16
|
+
import { validate } from "@o3co/ts.hocon/zod";
|
|
17
|
+
|
|
18
|
+
const logger = createLogger("policy-verifier");
|
|
19
|
+
|
|
20
|
+
const config = validate(
|
|
21
|
+
parseFile(resolve(import.meta.dirname, "../config/application.conf")),
|
|
22
|
+
AppConfigSchema,
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const app = await createApp({
|
|
26
|
+
pathResolver: import.meta.resolve,
|
|
27
|
+
config,
|
|
28
|
+
modules: [builtinCollectorsModule, builtinKeyResolversModule],
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const server = app.listen(config.http.port, config.http.hostname, () => {
|
|
32
|
+
logger.info(`listening on http://${config.http.hostname}:${config.http.port}`);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
gracefulShutdown(server);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "esnext",
|
|
4
|
+
"lib": [
|
|
5
|
+
"esnext"
|
|
6
|
+
],
|
|
7
|
+
"module": "nodenext",
|
|
8
|
+
"moduleResolution": "nodenext",
|
|
9
|
+
"declarationMap": true,
|
|
10
|
+
"declaration": true,
|
|
11
|
+
"emitDeclarationOnly": false,
|
|
12
|
+
"strict": true,
|
|
13
|
+
"forceConsistentCasingInFileNames": true,
|
|
14
|
+
"allowArbitraryExtensions": true,
|
|
15
|
+
"esModuleInterop": true,
|
|
16
|
+
"skipLibCheck": true,
|
|
17
|
+
"rootDir": "./src",
|
|
18
|
+
"outDir": "./dist"
|
|
19
|
+
},
|
|
20
|
+
"include": [
|
|
21
|
+
"src/**/*"
|
|
22
|
+
],
|
|
23
|
+
"exclude": [
|
|
24
|
+
"node_modules",
|
|
25
|
+
"dist"
|
|
26
|
+
]
|
|
27
|
+
}
|