@numa-tech/numa 1.12.10 → 1.12.11
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 +13 -12
- package/dist/ai-registry/commands.js +33 -33
- package/dist/ai-registry/commands.js.map +1 -1
- package/dist/app-config.d.ts +0 -1
- package/dist/app-config.js +4 -2
- package/dist/app-config.js.map +1 -1
- package/dist/authorization/access-request-client.d.ts +45 -0
- package/dist/authorization/access-request-client.js +93 -0
- package/dist/authorization/access-request-client.js.map +1 -0
- package/dist/authorization/access-request-commands.d.ts +25 -0
- package/dist/authorization/access-request-commands.js +196 -0
- package/dist/authorization/access-request-commands.js.map +1 -0
- package/dist/authorization/access-request-schemas.d.ts +123 -0
- package/dist/authorization/access-request-schemas.js +67 -0
- package/dist/authorization/access-request-schemas.js.map +1 -0
- package/dist/authorization/client.d.ts +50 -0
- package/dist/authorization/client.js +113 -0
- package/dist/authorization/client.js.map +1 -0
- package/dist/authorization/commands.d.ts +40 -0
- package/dist/authorization/commands.js +296 -0
- package/dist/authorization/commands.js.map +1 -0
- package/dist/authorization/errors.d.ts +13 -0
- package/dist/authorization/errors.js +70 -0
- package/dist/authorization/errors.js.map +1 -0
- package/dist/authorization/schemas.d.ts +187 -0
- package/dist/authorization/schemas.js +101 -0
- package/dist/authorization/schemas.js.map +1 -0
- package/dist/cli.js +30 -8
- package/dist/cli.js.map +1 -1
- package/dist/command-catalog.js +136 -26
- package/dist/command-catalog.js.map +1 -1
- package/dist/web-console-page.d.ts +1 -1
- package/dist/web-console-page.js +1 -1
- package/dist/web-console.js +2 -2
- package/dist/web-console.js.map +1 -1
- package/examples/ai-registry/README.md +18 -0
- package/examples/ai-registry/nacos-instance.username-password.example.json +25 -0
- package/package.json +4 -4
- package/skills/numa-ai-registry/SKILL.md +5 -5
- package/skills/numa-ai-registry/evals/evals.json +1 -1
- package/skills/numa-ai-registry/references/command-contract.md +22 -22
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { resolveBackendUrl } from "../backend.js";
|
|
2
|
+
import { ensureAccessToken, fetchWithKeycloakIdentity } from "../oauth.js";
|
|
3
|
+
import { AuthorizationError, authorizationHttpErrorCode } from "./errors.js";
|
|
4
|
+
import { AuthorizationApiErrorSchema, AuthorizationRoleListSchema, AuthorizationRoleSchema, KeycloakBootstrapRunSchema, KeycloakBootstrapStartSchema, RoleProvisionPreviewSchema, RoleProvisionRunSchema } from "./schemas.js";
|
|
5
|
+
const AUTHORIZATION_ROOT = "/api/v1/admin/authorization";
|
|
6
|
+
const ROLE_ROOT = `${AUTHORIZATION_ROOT}/roles`;
|
|
7
|
+
const RUN_ROOT = `${AUTHORIZATION_ROOT}/role-provisioning/runs`;
|
|
8
|
+
const BOOTSTRAP_ROOT = `${AUTHORIZATION_ROOT}/bootstrap`;
|
|
9
|
+
async function responseBody(response) {
|
|
10
|
+
const text = await response.text();
|
|
11
|
+
if (!text)
|
|
12
|
+
return {};
|
|
13
|
+
try {
|
|
14
|
+
return JSON.parse(text);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return text;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function safeRequestId(value) {
|
|
21
|
+
return typeof value === "string"
|
|
22
|
+
&& value.length <= 256
|
|
23
|
+
&& !/[\u0000-\u001f\u007f]/u.test(value)
|
|
24
|
+
? value
|
|
25
|
+
: undefined;
|
|
26
|
+
}
|
|
27
|
+
function requestIdFrom(response, body) {
|
|
28
|
+
if (body && typeof body === "object" && "requestId" in body) {
|
|
29
|
+
const requestId = safeRequestId(body.requestId);
|
|
30
|
+
if (requestId)
|
|
31
|
+
return requestId;
|
|
32
|
+
}
|
|
33
|
+
return safeRequestId(response.headers.get("x-request-id"));
|
|
34
|
+
}
|
|
35
|
+
function apiFailure(response, body) {
|
|
36
|
+
const parsed = AuthorizationApiErrorSchema.safeParse(body);
|
|
37
|
+
const code = authorizationHttpErrorCode(response.status);
|
|
38
|
+
const messages = {
|
|
39
|
+
400: "The authorization management request was invalid.",
|
|
40
|
+
401: "Authorization management requires a valid Numa login.",
|
|
41
|
+
403: "The platform denied this authorization management operation.",
|
|
42
|
+
409: "Authorization role state changed or conflicts with this request.",
|
|
43
|
+
412: "Authorization role version no longer matches the preview.",
|
|
44
|
+
422: "Authorization role provisioning input was rejected by the platform.",
|
|
45
|
+
502: "The platform could not reach or authorize its Keycloak role service."
|
|
46
|
+
};
|
|
47
|
+
return new AuthorizationError(`Authorization API HTTP ${response.status}: ${messages[response.status] ?? "Request failed."}`, code, response.status, requestIdFrom(response, body), parsed.success ? parsed.data.code : undefined, parsed.success ? parsed.data.runId : undefined);
|
|
48
|
+
}
|
|
49
|
+
export class AuthorizationClient {
|
|
50
|
+
config;
|
|
51
|
+
fetchImpl;
|
|
52
|
+
tokenProvider;
|
|
53
|
+
constructor(config, dependencies = {}) {
|
|
54
|
+
this.config = config;
|
|
55
|
+
this.fetchImpl = dependencies.fetch ?? fetch;
|
|
56
|
+
this.tokenProvider = dependencies.tokenProvider ?? ensureAccessToken;
|
|
57
|
+
}
|
|
58
|
+
async authenticatedFetch(path, init = {}) {
|
|
59
|
+
// Every public method supplies a compile-time fixed path rooted under the DevOps
|
|
60
|
+
// authorization API. No caller-controlled absolute URL is accepted here.
|
|
61
|
+
const url = resolveBackendUrl(this.config, { method: "GET", path });
|
|
62
|
+
const headers = new Headers(init.headers);
|
|
63
|
+
headers.set("Accept", "application/json");
|
|
64
|
+
if (init.body != null)
|
|
65
|
+
headers.set("Content-Type", "application/json");
|
|
66
|
+
return fetchWithKeycloakIdentity(this.config, url, () => ({ ...init, headers }), { fetch: this.fetchImpl, tokenProvider: this.tokenProvider });
|
|
67
|
+
}
|
|
68
|
+
async json(path, schema, init = {}, expectedStatuses = [200]) {
|
|
69
|
+
const response = await this.authenticatedFetch(path, init);
|
|
70
|
+
const body = await responseBody(response);
|
|
71
|
+
if (!response.ok)
|
|
72
|
+
throw apiFailure(response, body);
|
|
73
|
+
if (!expectedStatuses.includes(response.status)) {
|
|
74
|
+
throw new AuthorizationError(`Authorization API returned HTTP ${response.status}; expected ${expectedStatuses.join(" or ")}.`, "authorization_invalid_response", response.status, requestIdFrom(response, body));
|
|
75
|
+
}
|
|
76
|
+
const parsed = schema.safeParse(body);
|
|
77
|
+
if (!parsed.success) {
|
|
78
|
+
throw new AuthorizationError("Authorization API returned a response that does not match the safe CLI contract.", "authorization_invalid_response", response.status, requestIdFrom(response, body));
|
|
79
|
+
}
|
|
80
|
+
return parsed.data;
|
|
81
|
+
}
|
|
82
|
+
listRoles() {
|
|
83
|
+
return this.json(ROLE_ROOT, AuthorizationRoleListSchema);
|
|
84
|
+
}
|
|
85
|
+
createRole(input) {
|
|
86
|
+
return this.json(ROLE_ROOT, AuthorizationRoleSchema, {
|
|
87
|
+
method: "POST",
|
|
88
|
+
body: JSON.stringify(input)
|
|
89
|
+
}, [201]);
|
|
90
|
+
}
|
|
91
|
+
previewRole(roleId) {
|
|
92
|
+
return this.json(`${ROLE_ROOT}/${roleId}/provisioning/preview`, RoleProvisionPreviewSchema);
|
|
93
|
+
}
|
|
94
|
+
applyRole(roleId, input) {
|
|
95
|
+
return this.json(`${ROLE_ROOT}/${roleId}/provisioning`, RoleProvisionRunSchema, {
|
|
96
|
+
method: "POST",
|
|
97
|
+
body: JSON.stringify(input)
|
|
98
|
+
}, [200, 201]);
|
|
99
|
+
}
|
|
100
|
+
getProvisioningRun(runId) {
|
|
101
|
+
return this.json(`${RUN_ROOT}/${runId}`, RoleProvisionRunSchema);
|
|
102
|
+
}
|
|
103
|
+
startKeycloakBootstrap(input) {
|
|
104
|
+
return this.json(`${BOOTSTRAP_ROOT}/sessions`, KeycloakBootstrapStartSchema, {
|
|
105
|
+
method: "POST",
|
|
106
|
+
body: JSON.stringify(input)
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
getKeycloakBootstrapRun(runId) {
|
|
110
|
+
return this.json(`${BOOTSTRAP_ROOT}/runs/${runId}`, KeycloakBootstrapRunSchema);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/authorization/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import { type ApplyRoleProvisionInput, type AuthorizationApi, type CreateAuthorizationRoleInput, type StartKeycloakBootstrapInput } from "./client.js";
|
|
3
|
+
import type { KeycloakBootstrapRun, KeycloakBootstrapStart, RoleProvisionPreview, RoleProvisionRun } from "./schemas.js";
|
|
4
|
+
import { type AccessRequestApi } from "./access-request-client.js";
|
|
5
|
+
export interface AuthorizationCommandDependencies {
|
|
6
|
+
clientFactory?: () => AuthorizationApi;
|
|
7
|
+
accessRequestClientFactory?: () => AccessRequestApi;
|
|
8
|
+
openBrowser?: (url: string) => Promise<void>;
|
|
9
|
+
sleep?: (milliseconds: number) => Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
interface ProvisionOptions {
|
|
12
|
+
apply?: boolean;
|
|
13
|
+
expectedRemoteDigest?: string;
|
|
14
|
+
idempotencyKey?: string;
|
|
15
|
+
}
|
|
16
|
+
interface BootstrapOptions {
|
|
17
|
+
noOpen?: boolean;
|
|
18
|
+
}
|
|
19
|
+
interface BootstrapApplyOptions extends BootstrapOptions {
|
|
20
|
+
expectedPlanDigest: string;
|
|
21
|
+
idempotencyKey: string;
|
|
22
|
+
confirmRealm: string;
|
|
23
|
+
}
|
|
24
|
+
export declare function stableAuthorizationOutput(value: unknown): unknown;
|
|
25
|
+
export declare function parseAuthorizationRoleId(value: string): number;
|
|
26
|
+
export declare function parseProvisioningRunId(value: string): number;
|
|
27
|
+
export declare function parseBootstrapApplyInput(options: BootstrapApplyOptions): StartKeycloakBootstrapInput;
|
|
28
|
+
export declare function waitForKeycloakBootstrap(client: Pick<AuthorizationApi, "getKeycloakBootstrapRun">, started: KeycloakBootstrapStart, sleep?: (milliseconds: number) => Promise<void>): Promise<KeycloakBootstrapRun>;
|
|
29
|
+
export declare function parseRoleCreateInput(options: {
|
|
30
|
+
code: string;
|
|
31
|
+
name: string;
|
|
32
|
+
description?: string;
|
|
33
|
+
}): CreateAuthorizationRoleInput;
|
|
34
|
+
export declare function parseProvisionOptions(preview: RoleProvisionPreview | undefined, options: ProvisionOptions): ApplyRoleProvisionInput | undefined;
|
|
35
|
+
export declare function provisionAuthorizationRole(client: Pick<AuthorizationApi, "previewRole" | "applyRole">, roleId: number, options: ProvisionOptions): Promise<{
|
|
36
|
+
preview: RoleProvisionPreview;
|
|
37
|
+
run?: RoleProvisionRun;
|
|
38
|
+
}>;
|
|
39
|
+
export declare function registerAuthorizationCommands(program: Command, dependencies?: AuthorizationCommandDependencies): void;
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
3
|
+
import open from "open";
|
|
4
|
+
import { loadAppConfig } from "../app-config.js";
|
|
5
|
+
import { loadConfig } from "../config.js";
|
|
6
|
+
import { AuthorizationClient } from "./client.js";
|
|
7
|
+
import { AuthorizationError, sanitizeAuthorizationOutput } from "./errors.js";
|
|
8
|
+
import { AccessRequestClient } from "./access-request-client.js";
|
|
9
|
+
import { registerAccessRequestCommands } from "./access-request-commands.js";
|
|
10
|
+
const ROLE_PREFIX = "mci-devops-platform-";
|
|
11
|
+
const CONTROL_CHARACTERS = /[\u0000-\u001f\u007f]/u;
|
|
12
|
+
const IDEMPOTENCY_KEY = /^[A-Za-z0-9][A-Za-z0-9._:-]{7,199}$/u;
|
|
13
|
+
function addExamples(command, examples, notes = []) {
|
|
14
|
+
const noteSection = notes.length
|
|
15
|
+
? `\n说明:\n${notes.map((item) => ` - ${item}`).join("\n")}\n`
|
|
16
|
+
: "";
|
|
17
|
+
return command.addHelpText("after", `${noteSection}\nExamples:\n${examples.map((item) => ` ${item}`).join("\n")}\n`);
|
|
18
|
+
}
|
|
19
|
+
function snakeCase(key) {
|
|
20
|
+
return key.replace(/([a-z0-9])([A-Z])/gu, "$1_$2").toLowerCase();
|
|
21
|
+
}
|
|
22
|
+
export function stableAuthorizationOutput(value) {
|
|
23
|
+
const safe = sanitizeAuthorizationOutput(value);
|
|
24
|
+
if (Array.isArray(safe))
|
|
25
|
+
return safe.map(stableAuthorizationOutput);
|
|
26
|
+
if (safe && typeof safe === "object") {
|
|
27
|
+
return Object.fromEntries(Object.entries(safe).map(([key, item]) => [snakeCase(key), stableAuthorizationOutput(item)]));
|
|
28
|
+
}
|
|
29
|
+
return safe;
|
|
30
|
+
}
|
|
31
|
+
async function jsonEnabled(command) {
|
|
32
|
+
const config = await loadAppConfig();
|
|
33
|
+
return Boolean(command.optsWithGlobals().json || config.output === "json");
|
|
34
|
+
}
|
|
35
|
+
async function output(command, payload, textValue) {
|
|
36
|
+
if (await jsonEnabled(command)) {
|
|
37
|
+
process.stdout.write(`${JSON.stringify(stableAuthorizationOutput(payload), null, 2)}\n`);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
process.stdout.write(`${String(sanitizeAuthorizationOutput(textValue))}\n`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function boundedText(value, label, maximum) {
|
|
44
|
+
const trimmed = value.trim();
|
|
45
|
+
if (!trimmed || trimmed.length > maximum || CONTROL_CHARACTERS.test(trimmed)) {
|
|
46
|
+
throw new AuthorizationError(`${label} must be non-empty, contain no control characters, and be at most ${maximum} characters.`, "authorization_input_invalid");
|
|
47
|
+
}
|
|
48
|
+
return trimmed;
|
|
49
|
+
}
|
|
50
|
+
export function parseAuthorizationRoleId(value) {
|
|
51
|
+
const parsed = Number(value);
|
|
52
|
+
if (!Number.isSafeInteger(parsed) || parsed < 1 || String(parsed) !== value.trim()) {
|
|
53
|
+
throw new AuthorizationError("role-id must be a positive integer.", "authorization_input_invalid");
|
|
54
|
+
}
|
|
55
|
+
return parsed;
|
|
56
|
+
}
|
|
57
|
+
export function parseProvisioningRunId(value) {
|
|
58
|
+
const parsed = Number(value);
|
|
59
|
+
if (!Number.isSafeInteger(parsed) || parsed < 1 || String(parsed) !== value.trim()) {
|
|
60
|
+
throw new AuthorizationError("run-id must be a positive integer.", "authorization_input_invalid");
|
|
61
|
+
}
|
|
62
|
+
return parsed;
|
|
63
|
+
}
|
|
64
|
+
export function parseBootstrapApplyInput(options) {
|
|
65
|
+
const expectedPlanDigest = boundedText(options.expectedPlanDigest, "expected-plan-digest", 64);
|
|
66
|
+
if (!/^[a-f0-9]{64}$/u.test(expectedPlanDigest)) {
|
|
67
|
+
throw new AuthorizationError("expected-plan-digest must be the 64-character lowercase SHA-256 value returned by bootstrap PLAN.", "authorization_input_invalid");
|
|
68
|
+
}
|
|
69
|
+
const idempotencyKey = options.idempotencyKey.trim();
|
|
70
|
+
if (!IDEMPOTENCY_KEY.test(idempotencyKey)) {
|
|
71
|
+
throw new AuthorizationError("idempotency-key must be 8-200 characters, start with a letter or number, and use only letters, numbers, . _ : or -.", "authorization_input_invalid");
|
|
72
|
+
}
|
|
73
|
+
const confirmRealm = boundedText(options.confirmRealm, "confirm-realm", 256);
|
|
74
|
+
if (!/^APPLY [A-Za-z0-9._-]+$/u.test(confirmRealm)) {
|
|
75
|
+
throw new AuthorizationError("confirm-realm must use the exact format 'APPLY <realm>'.", "authorization_input_invalid");
|
|
76
|
+
}
|
|
77
|
+
return { mode: "APPLY", expectedPlanDigest, idempotencyKey, confirmRealm };
|
|
78
|
+
}
|
|
79
|
+
export async function waitForKeycloakBootstrap(client, started, sleep = (milliseconds) => delay(milliseconds)) {
|
|
80
|
+
let deadline = Date.parse(started.expiresAt) + 15_000;
|
|
81
|
+
for (;;) {
|
|
82
|
+
const run = await client.getKeycloakBootstrapRun(started.runId);
|
|
83
|
+
if (["SUCCEEDED", "FAILED", "EXPIRED"].includes(run.status))
|
|
84
|
+
return run;
|
|
85
|
+
const serverDeadline = Date.parse(run.expiresAt) + 15_000;
|
|
86
|
+
if (Number.isFinite(serverDeadline))
|
|
87
|
+
deadline = Math.max(deadline, serverDeadline);
|
|
88
|
+
if (!Number.isFinite(deadline) || Date.now() >= deadline) {
|
|
89
|
+
throw new AuthorizationError(`Remote bootstrap run ${started.runId} did not finish before its authorization session expired.`, "authorization_conflict", 409, undefined, "BOOTSTRAP_SESSION_EXPIRED", started.runId);
|
|
90
|
+
}
|
|
91
|
+
await sleep(1_000);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
export function parseRoleCreateInput(options) {
|
|
95
|
+
const code = boundedText(options.code, "code", 80);
|
|
96
|
+
if (!code.startsWith(ROLE_PREFIX)
|
|
97
|
+
|| code.includes("..")
|
|
98
|
+
|| !/^[A-Za-z0-9][A-Za-z0-9._-]*$/u.test(code)) {
|
|
99
|
+
throw new AuthorizationError(`code must start with '${ROLE_PREFIX}', use only letters, numbers, '.', '_' or '-', and must not contain '..'.`, "authorization_input_invalid");
|
|
100
|
+
}
|
|
101
|
+
const name = boundedText(options.name, "name", 160);
|
|
102
|
+
const description = options.description == null
|
|
103
|
+
? undefined
|
|
104
|
+
: boundedText(options.description, "description", 2_000);
|
|
105
|
+
return { code, name, ...(description ? { description } : {}), status: "ACTIVE" };
|
|
106
|
+
}
|
|
107
|
+
export function parseProvisionOptions(preview, options) {
|
|
108
|
+
if (!options.apply) {
|
|
109
|
+
if (options.expectedRemoteDigest != null || options.idempotencyKey != null) {
|
|
110
|
+
throw new AuthorizationError("--expected-remote-digest and --idempotency-key are only valid together with --apply.", "authorization_input_invalid");
|
|
111
|
+
}
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
if (!options.expectedRemoteDigest || !options.idempotencyKey) {
|
|
115
|
+
throw new AuthorizationError("--apply requires both --expected-remote-digest and --idempotency-key.", "authorization_input_invalid");
|
|
116
|
+
}
|
|
117
|
+
const expectedRemoteDigest = boundedText(options.expectedRemoteDigest, "expected-remote-digest", 256);
|
|
118
|
+
if (!/^[a-f0-9]{64}$/u.test(expectedRemoteDigest)) {
|
|
119
|
+
throw new AuthorizationError("expected-remote-digest must be the 64-character lowercase SHA-256 value returned by preview.", "authorization_input_invalid");
|
|
120
|
+
}
|
|
121
|
+
const idempotencyKey = options.idempotencyKey.trim();
|
|
122
|
+
if (!IDEMPOTENCY_KEY.test(idempotencyKey)) {
|
|
123
|
+
throw new AuthorizationError("idempotency-key must be 8-200 characters, start with a letter or number, and use only letters, numbers, . _ : or -.", "authorization_input_invalid");
|
|
124
|
+
}
|
|
125
|
+
if (!preview) {
|
|
126
|
+
throw new AuthorizationError("A fresh provisioning preview is required before apply.", "authorization_input_invalid");
|
|
127
|
+
}
|
|
128
|
+
if (preview.remoteDigest !== expectedRemoteDigest) {
|
|
129
|
+
throw new AuthorizationError("expected-remote-digest does not match the fresh preview; review the new plan before applying.", "authorization_conflict", 409);
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
expectedRoleVersion: preview.roleVersion,
|
|
133
|
+
expectedRemoteDigest,
|
|
134
|
+
idempotencyKey
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
export async function provisionAuthorizationRole(client, roleId, options) {
|
|
138
|
+
const preview = await client.previewRole(roleId);
|
|
139
|
+
const applyInput = parseProvisionOptions(preview, options);
|
|
140
|
+
if (!applyInput)
|
|
141
|
+
return { preview };
|
|
142
|
+
return { preview, run: await client.applyRole(roleId, applyInput) };
|
|
143
|
+
}
|
|
144
|
+
function roleLine(role) {
|
|
145
|
+
return `${role.id}\t${role.status}\t${role.code}\t${role.name}\tv${role.version}`;
|
|
146
|
+
}
|
|
147
|
+
function previewLine(preview) {
|
|
148
|
+
return `${preview.roleId}\t${preview.remoteState}\t${preview.action}\t${preview.roleClientId}/${preview.roleName}\t${preview.remoteDigest}`;
|
|
149
|
+
}
|
|
150
|
+
function runLine(run) {
|
|
151
|
+
return `${run.runId}\t${run.status}\t${run.action}\t${run.roleClientId}/${run.roleName}`;
|
|
152
|
+
}
|
|
153
|
+
function bootstrapRunLine(run) {
|
|
154
|
+
const digest = run.planDigest ?? "-";
|
|
155
|
+
return `${run.runId}\t${run.mode}\t${run.status}\t${digest}`;
|
|
156
|
+
}
|
|
157
|
+
export function registerAuthorizationCommands(program, dependencies = {}) {
|
|
158
|
+
const client = () => dependencies.clientFactory?.()
|
|
159
|
+
?? new AuthorizationClient(loadConfig("platform"));
|
|
160
|
+
const openBrowser = dependencies.openBrowser ?? (async (url) => { await open(url); });
|
|
161
|
+
const sleep = dependencies.sleep ?? ((milliseconds) => delay(milliseconds));
|
|
162
|
+
const authorization = program.command("authorization")
|
|
163
|
+
.description("通过平台授权管理业务角色并制备固定 Keycloak Client Role")
|
|
164
|
+
.addHelpText("after", `
|
|
165
|
+
安全边界:
|
|
166
|
+
CLI 只使用当前 Numa PKCE 用户身份访问 DevOps 同源 API。
|
|
167
|
+
realm、client、Keycloak URL 和 service-account secret 均由服务端固定,不能通过本命令覆盖。
|
|
168
|
+
创建平台角色不会自动写 Keycloak;provision 默认只预览,写入必须显式 --apply。
|
|
169
|
+
`);
|
|
170
|
+
registerAccessRequestCommands(authorization, {
|
|
171
|
+
clientFactory: dependencies.accessRequestClientFactory
|
|
172
|
+
?? (() => new AccessRequestClient(loadConfig("platform")))
|
|
173
|
+
});
|
|
174
|
+
const role = authorization.command("role")
|
|
175
|
+
.description("管理平台 auth_role 并执行受控的 Keycloak Client Role 制备");
|
|
176
|
+
addExamples(role.command("list")
|
|
177
|
+
.description("列出平台角色及其版本;不直接访问 Keycloak")
|
|
178
|
+
.action(async (_options, command) => {
|
|
179
|
+
const roles = await client().listRoles();
|
|
180
|
+
await output(command, { ok: true, roles }, roles.map(roleLine).join("\n") || "没有找到平台角色");
|
|
181
|
+
}), ["numa authorization role list", "numa authorization role list --json"]);
|
|
182
|
+
addExamples(role.command("create")
|
|
183
|
+
.description("只创建 ACTIVE 平台角色;不会自动制备 Keycloak Client Role")
|
|
184
|
+
.requiredOption("--code <prefixed-role>", `带固定应用前缀的角色标识(${ROLE_PREFIX}...)`)
|
|
185
|
+
.requiredOption("--name <display-name>", "角色显示名称")
|
|
186
|
+
.option("--description <text>", "角色说明")
|
|
187
|
+
.action(async (options, command) => {
|
|
188
|
+
const created = await client().createRole(parseRoleCreateInput(options));
|
|
189
|
+
const nextCommand = `numa authorization role provision ${created.id}`;
|
|
190
|
+
await output(command, { ok: true, role: created, next_step: { command: nextCommand, mode: "preview" } }, `${roleLine(created)}\n下一步(仅预览): ${nextCommand}`);
|
|
191
|
+
}), [
|
|
192
|
+
"numa authorization role create --code mci-devops-platform-auditor --name '审计员' --json",
|
|
193
|
+
"numa authorization role provision 42"
|
|
194
|
+
], [
|
|
195
|
+
"请求体由 CLI 构造且 status 固定为 ACTIVE;服务端仍会校验并规范化角色标识。",
|
|
196
|
+
"创建成功后必须先执行 provision 预览,不会自动写入 Keycloak。"
|
|
197
|
+
]);
|
|
198
|
+
addExamples(role.command("provision <role-id>")
|
|
199
|
+
.description("默认预览固定 Client Role 的 CREATE/UPDATE/NOOP;显式 --apply 才执行")
|
|
200
|
+
.option("--apply", "按新鲜预览执行 Keycloak Client Role 制备")
|
|
201
|
+
.option("--expected-remote-digest <digest>", "必须与本次新鲜预览一致的远端摘要")
|
|
202
|
+
.option("--idempotency-key <key>", "同一次执行重试必须复用的稳定键")
|
|
203
|
+
.action(async (roleIdValue, options, command) => {
|
|
204
|
+
const roleId = parseAuthorizationRoleId(roleIdValue);
|
|
205
|
+
// Validate flag combinations before authentication, then fetch a fresh preview.
|
|
206
|
+
if (!options.apply)
|
|
207
|
+
parseProvisionOptions(undefined, options);
|
|
208
|
+
else if (!options.expectedRemoteDigest || !options.idempotencyKey)
|
|
209
|
+
parseProvisionOptions(undefined, options);
|
|
210
|
+
const result = await provisionAuthorizationRole(client(), roleId, options);
|
|
211
|
+
await output(command, result.run
|
|
212
|
+
? { ok: result.run.status !== "FAILED", preview: result.preview, provisioning_run: result.run }
|
|
213
|
+
: { ok: true, preview: result.preview, apply_required: result.preview.action !== "NOOP" }, result.run ? runLine(result.run) : previewLine(result.preview));
|
|
214
|
+
if (result.run?.status === "FAILED")
|
|
215
|
+
process.exitCode = 1;
|
|
216
|
+
}), [
|
|
217
|
+
"numa authorization role provision 42 --json",
|
|
218
|
+
`numa authorization role provision 42 --apply --expected-remote-digest ${"a".repeat(64)} --idempotency-key role-42-20260820 --json`
|
|
219
|
+
], [
|
|
220
|
+
"每次 apply 都会先重新预览;摘要不一致时本地拒绝执行,服务端仍会再次校验 role version 和远端摘要。",
|
|
221
|
+
"非交互和 JSON 模式不读取 prompt;缺少显式 apply 参数会稳定失败。"
|
|
222
|
+
]);
|
|
223
|
+
addExamples(role.command("provisioning-run <run-id>")
|
|
224
|
+
.description("查询角色制备运行记录及安全错误摘要")
|
|
225
|
+
.action(async (runIdValue, _options, command) => {
|
|
226
|
+
const run = await client().getProvisioningRun(parseProvisioningRunId(runIdValue));
|
|
227
|
+
await output(command, { ok: run.status !== "FAILED", provisioning_run: run }, runLine(run));
|
|
228
|
+
if (run.status === "FAILED")
|
|
229
|
+
process.exitCode = 1;
|
|
230
|
+
}), ["numa authorization role provisioning-run 123 --json"]);
|
|
231
|
+
const bootstrap = authorization.command("bootstrap")
|
|
232
|
+
.description("通过一次新鲜管理员网页登录,在 DevOps 内存中远程制备 Keycloak 权限")
|
|
233
|
+
.addHelpText("after", `
|
|
234
|
+
安全边界:
|
|
235
|
+
CLI 不接收、不保存也不转发 bootstrap 管理员 token。
|
|
236
|
+
DevOps 回调在单次执行的内存中使用 token,多次调用 Keycloak 后立即丢弃。
|
|
237
|
+
PLAN 和 APPLY 分别要求一次新鲜登录;正常流程不需要本机 Keycloak 或 kcadm。
|
|
238
|
+
`);
|
|
239
|
+
const startAndWait = async (command, request, options) => {
|
|
240
|
+
const api = client();
|
|
241
|
+
const started = await api.startKeycloakBootstrap(request);
|
|
242
|
+
if (!started.loginUrl && ["SUCCEEDED", "FAILED", "EXPIRED"].includes(started.status)) {
|
|
243
|
+
const run = await api.getKeycloakBootstrapRun(started.runId);
|
|
244
|
+
await output(command, { ok: run.status === "SUCCEEDED", bootstrap_run: run }, bootstrapRunLine(run));
|
|
245
|
+
if (run.status !== "SUCCEEDED")
|
|
246
|
+
process.exitCode = 1;
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (options.noOpen || !started.loginUrl) {
|
|
250
|
+
await output(command, { ok: started.status !== "FAILED" && started.status !== "EXPIRED", bootstrap_session: started }, started.loginUrl
|
|
251
|
+
? `${started.runId}\t${started.mode}\t${started.status}\n请在浏览器打开: ${started.loginUrl}`
|
|
252
|
+
: `${started.runId}\t${started.mode}\t${started.status}`);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
process.stderr.write(`正在打开 Keycloak 管理员登录(run ${started.runId})…\n`);
|
|
256
|
+
try {
|
|
257
|
+
await openBrowser(started.loginUrl);
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
process.stderr.write(`无法自动打开浏览器,请手动打开:\n${started.loginUrl}\n`);
|
|
261
|
+
}
|
|
262
|
+
const run = await waitForKeycloakBootstrap(api, started, sleep);
|
|
263
|
+
await output(command, { ok: run.status === "SUCCEEDED", bootstrap_run: run }, bootstrapRunLine(run));
|
|
264
|
+
if (run.status !== "SUCCEEDED")
|
|
265
|
+
process.exitCode = 1;
|
|
266
|
+
};
|
|
267
|
+
addExamples(bootstrap.command("plan")
|
|
268
|
+
.description("新鲜登录后只读取并生成固定权限调和计划")
|
|
269
|
+
.option("--no-open", "不打开浏览器;返回短时 login URL 和 run ID")
|
|
270
|
+
.action(async (options, command) => {
|
|
271
|
+
await startAndWait(command, { mode: "PLAN" }, options);
|
|
272
|
+
}), [
|
|
273
|
+
"numa authorization bootstrap plan --json",
|
|
274
|
+
"numa authorization bootstrap plan --no-open --json"
|
|
275
|
+
]);
|
|
276
|
+
addExamples(bootstrap.command("apply")
|
|
277
|
+
.description("新鲜登录后按 PLAN digest 执行固定权限调和和真实 CRUD 验证")
|
|
278
|
+
.requiredOption("--expected-plan-digest <digest>", "PLAN 返回的精确 SHA-256 摘要")
|
|
279
|
+
.requiredOption("--confirm-realm <confirmation>", "精确确认文本,例如 'APPLY numa-realm'")
|
|
280
|
+
.requiredOption("--idempotency-key <key>", "同一次 APPLY 重试必须复用的稳定键")
|
|
281
|
+
.option("--no-open", "不打开浏览器;返回短时 login URL 和 run ID")
|
|
282
|
+
.action(async (options, command) => {
|
|
283
|
+
await startAndWait(command, parseBootstrapApplyInput(options), options);
|
|
284
|
+
}), [
|
|
285
|
+
`numa authorization bootstrap apply --expected-plan-digest ${"a".repeat(64)} --confirm-realm 'APPLY numa-realm' --idempotency-key bootstrap-20260820-01 --json`
|
|
286
|
+
]);
|
|
287
|
+
addExamples(bootstrap.command("status <run-id>")
|
|
288
|
+
.description("使用当前平台身份查询远程 bootstrap 的安全运行记录")
|
|
289
|
+
.action(async (runIdValue, _options, command) => {
|
|
290
|
+
const run = await client().getKeycloakBootstrapRun(parseProvisioningRunId(runIdValue));
|
|
291
|
+
await output(command, { ok: run.status === "SUCCEEDED", bootstrap_run: run }, bootstrapRunLine(run));
|
|
292
|
+
if (["FAILED", "EXPIRED"].includes(run.status))
|
|
293
|
+
process.exitCode = 1;
|
|
294
|
+
}), ["numa authorization bootstrap status 123 --json"]);
|
|
295
|
+
}
|
|
296
|
+
//# sourceMappingURL=commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/authorization/commands.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare function redactAuthorizationText(value: string): string;
|
|
2
|
+
export declare function sanitizeAuthorizationOutput(value: unknown, key?: string): unknown;
|
|
3
|
+
export type AuthorizationErrorCode = "authorization_authentication_required" | "authorization_forbidden" | "authorization_conflict" | "authorization_precondition_failed" | "authorization_validation_failed" | "authorization_keycloak_unavailable" | "authorization_api_error" | "authorization_invalid_response" | "authorization_input_invalid";
|
|
4
|
+
export declare function authorizationHttpErrorCode(status: number): AuthorizationErrorCode;
|
|
5
|
+
export declare function authorizationExitCode(error: AuthorizationError): number;
|
|
6
|
+
export declare class AuthorizationError extends Error {
|
|
7
|
+
readonly code: AuthorizationErrorCode;
|
|
8
|
+
readonly status?: number | undefined;
|
|
9
|
+
readonly requestId?: string | undefined;
|
|
10
|
+
readonly serverCode?: string | undefined;
|
|
11
|
+
readonly runId?: number | undefined;
|
|
12
|
+
constructor(message: string, code: AuthorizationErrorCode, status?: number | undefined, requestId?: string | undefined, serverCode?: string | undefined, runId?: number | undefined);
|
|
13
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const SENSITIVE_KEY = /(?:authorization|access[_-]?token|refresh[_-]?token|api[_-]?key|client[_-]?secret|password|passwd|credential|secret|token|keycloak.*uuid|client.*uuid|internal.*client.*id)$/iu;
|
|
2
|
+
export function redactAuthorizationText(value) {
|
|
3
|
+
return value
|
|
4
|
+
.replace(/\b(Bearer|Basic)\s+[^\s,;]+/giu, "$1 [REDACTED]")
|
|
5
|
+
.replace(/([?&](?:access_token|refresh_token|token|signature|credential|api[_-]?key)=)[^&#\s]+/giu, "$1[REDACTED]")
|
|
6
|
+
.replace(/\b((?:password|passwd|token|api[_-]?key|access[_-]?token|refresh[_-]?token|secret|credential)\s*[=:]\s*)[^\s,;]+/giu, "$1[REDACTED]")
|
|
7
|
+
.replace(/(https?:\/\/)[^\s/@:]+:[^\s/@]+@/giu, "$1[REDACTED]@")
|
|
8
|
+
.replace(/(?<![A-Za-z0-9_-])eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}(?![A-Za-z0-9_-])/gu, "[REDACTED_JWT]");
|
|
9
|
+
}
|
|
10
|
+
export function sanitizeAuthorizationOutput(value, key = "") {
|
|
11
|
+
if (SENSITIVE_KEY.test(key))
|
|
12
|
+
return "[REDACTED]";
|
|
13
|
+
if (typeof value === "string") {
|
|
14
|
+
const redacted = redactAuthorizationText(value);
|
|
15
|
+
return /^(?:errorSummary|detail|message)$/iu.test(key)
|
|
16
|
+
? redacted
|
|
17
|
+
.replace(/https?:\/\/[^\s"']+/giu, "[REDACTED_URL]")
|
|
18
|
+
.replace(/\b[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\b/giu, "[REDACTED_UUID]")
|
|
19
|
+
: redacted;
|
|
20
|
+
}
|
|
21
|
+
if (Array.isArray(value))
|
|
22
|
+
return value.map((item) => sanitizeAuthorizationOutput(item));
|
|
23
|
+
if (value && typeof value === "object") {
|
|
24
|
+
return Object.fromEntries(Object.entries(value)
|
|
25
|
+
.filter(([name]) => !/(?:keycloak.*uuid|client.*uuid|internal.*client.*id)$/iu.test(name))
|
|
26
|
+
.map(([name, item]) => [name, sanitizeAuthorizationOutput(item, name)]));
|
|
27
|
+
}
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
export function authorizationHttpErrorCode(status) {
|
|
31
|
+
switch (status) {
|
|
32
|
+
case 400: return "authorization_input_invalid";
|
|
33
|
+
case 401: return "authorization_authentication_required";
|
|
34
|
+
case 403: return "authorization_forbidden";
|
|
35
|
+
case 409: return "authorization_conflict";
|
|
36
|
+
case 412: return "authorization_precondition_failed";
|
|
37
|
+
case 422: return "authorization_validation_failed";
|
|
38
|
+
case 502: return "authorization_keycloak_unavailable";
|
|
39
|
+
default: return "authorization_api_error";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export function authorizationExitCode(error) {
|
|
43
|
+
switch (error.status) {
|
|
44
|
+
case 400: return 2;
|
|
45
|
+
case 401: return 3;
|
|
46
|
+
case 403: return 4;
|
|
47
|
+
case 409:
|
|
48
|
+
case 412: return 5;
|
|
49
|
+
case 422: return 2;
|
|
50
|
+
case 502: return 6;
|
|
51
|
+
default: return 1;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export class AuthorizationError extends Error {
|
|
55
|
+
code;
|
|
56
|
+
status;
|
|
57
|
+
requestId;
|
|
58
|
+
serverCode;
|
|
59
|
+
runId;
|
|
60
|
+
constructor(message, code, status, requestId, serverCode, runId) {
|
|
61
|
+
super(redactAuthorizationText(message));
|
|
62
|
+
this.code = code;
|
|
63
|
+
this.status = status;
|
|
64
|
+
this.requestId = requestId;
|
|
65
|
+
this.serverCode = serverCode;
|
|
66
|
+
this.runId = runId;
|
|
67
|
+
this.name = "AuthorizationError";
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/authorization/errors.ts"],"names":[],"mappings":"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"}
|