@roarpeng/graphflow 1.14.1 → 1.15.0
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/CHANGELOG.md +14 -0
- package/README.md +3 -3
- package/README.zh.md +1 -1
- package/dist/config/schema.d.ts +3 -1
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/graph/client-factory.d.ts.map +1 -1
- package/dist/graph/client-factory.js +4 -1
- package/dist/graph/client-factory.js.map +1 -1
- package/dist/graph/graphify-mcp-client.d.ts +33 -0
- package/dist/graph/graphify-mcp-client.d.ts.map +1 -1
- package/dist/graph/graphify-mcp-client.js +66 -5
- package/dist/graph/graphify-mcp-client.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/security/rbac.d.ts +39 -0
- package/dist/security/rbac.d.ts.map +1 -0
- package/dist/security/rbac.js +213 -0
- package/dist/security/rbac.js.map +1 -0
- package/dist/security/token-auth.d.ts +15 -2
- package/dist/security/token-auth.d.ts.map +1 -1
- package/dist/security/token-auth.js +86 -5
- package/dist/security/token-auth.js.map +1 -1
- package/dist/surfaces/cli/index.js +79 -4
- package/dist/surfaces/cli/index.js.map +1 -1
- package/dist/surfaces/cli/init.d.ts +3 -0
- package/dist/surfaces/cli/init.d.ts.map +1 -1
- package/dist/surfaces/cli/init.js +18 -0
- package/dist/surfaces/cli/init.js.map +1 -1
- package/dist/surfaces/cli/output.d.ts.map +1 -1
- package/dist/surfaces/cli/output.js +7 -3
- package/dist/surfaces/cli/output.js.map +1 -1
- package/dist/surfaces/cli/runtime/graph.d.ts +13 -0
- package/dist/surfaces/cli/runtime/graph.d.ts.map +1 -1
- package/dist/surfaces/cli/runtime/graph.js +97 -9
- package/dist/surfaces/cli/runtime/graph.js.map +1 -1
- package/dist/surfaces/cli/runtime/routing.d.ts +1 -1
- package/dist/surfaces/cli/runtime/routing.d.ts.map +1 -1
- package/dist/surfaces/cli/runtime/routing.js +12 -2
- package/dist/surfaces/cli/runtime/routing.js.map +1 -1
- package/dist/surfaces/cli/runtime/types.d.ts +3 -0
- package/dist/surfaces/cli/runtime/types.d.ts.map +1 -1
- package/dist/surfaces/cli/runtime.d.ts +1 -1
- package/dist/surfaces/cli/runtime.d.ts.map +1 -1
- package/dist/surfaces/cli/runtime.js +3 -2
- package/dist/surfaces/cli/runtime.js.map +1 -1
- package/dist/surfaces/mcp/server.d.ts +2 -0
- package/dist/surfaces/mcp/server.d.ts.map +1 -1
- package/dist/surfaces/mcp/server.js +77 -37
- package/dist/surfaces/mcp/server.js.map +1 -1
- package/dist/surfaces/mcp/tool-handlers.d.ts.map +1 -1
- package/dist/surfaces/mcp/tool-handlers.js +2 -0
- package/dist/surfaces/mcp/tool-handlers.js.map +1 -1
- package/dist/surfaces/team/diagnose.d.ts +18 -0
- package/dist/surfaces/team/diagnose.d.ts.map +1 -0
- package/dist/surfaces/team/diagnose.js +57 -0
- package/dist/surfaces/team/diagnose.js.map +1 -0
- package/dist/surfaces/team/ops.d.ts +16 -0
- package/dist/surfaces/team/ops.d.ts.map +1 -0
- package/dist/surfaces/team/ops.js +137 -0
- package/dist/surfaces/team/ops.js.map +1 -0
- package/dist/surfaces/team/server.d.ts +48 -0
- package/dist/surfaces/team/server.d.ts.map +1 -0
- package/dist/surfaces/team/server.js +393 -0
- package/dist/surfaces/team/server.js.map +1 -0
- package/package.json +1 -1
- package/plugin.json +1 -1
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Team HTTP RBAC — viewer | contributor | admin.
|
|
4
|
+
*
|
|
5
|
+
* Local-first surfaces (stdio, loopback file/sqlite, unauthenticated loopback
|
|
6
|
+
* HTTP) never consult this module. Shared HTTP surfaces fail closed: a missing
|
|
7
|
+
* role is a deny, not a silent grant.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.TeamAuthorizationError = exports.TEAM_PERMISSIONS = exports.TEAM_ROLES = void 0;
|
|
11
|
+
exports.isTeamRole = isTeamRole;
|
|
12
|
+
exports.permissionsForRole = permissionsForRole;
|
|
13
|
+
exports.roleSatisfies = roleSatisfies;
|
|
14
|
+
exports.hasPermission = hasPermission;
|
|
15
|
+
exports.roleFromScopes = roleFromScopes;
|
|
16
|
+
exports.defaultScopesForRole = defaultScopesForRole;
|
|
17
|
+
exports.permissionForTeamMethod = permissionForTeamMethod;
|
|
18
|
+
exports.permissionForMcpTool = permissionForMcpTool;
|
|
19
|
+
exports.authorizePermission = authorizePermission;
|
|
20
|
+
exports.authorizeTeamMethod = authorizeTeamMethod;
|
|
21
|
+
exports.authorizeMcpTool = authorizeMcpTool;
|
|
22
|
+
exports.assertAuthorized = assertAuthorized;
|
|
23
|
+
exports.TEAM_ROLES = ["viewer", "contributor", "admin"];
|
|
24
|
+
exports.TEAM_PERMISSIONS = [
|
|
25
|
+
"graph.read",
|
|
26
|
+
"graph.write",
|
|
27
|
+
"graph.delete",
|
|
28
|
+
"skill.read",
|
|
29
|
+
"skill.sync",
|
|
30
|
+
"artifact.import",
|
|
31
|
+
"governance.mutate",
|
|
32
|
+
"diagnose.read",
|
|
33
|
+
];
|
|
34
|
+
const ROLE_RANK = {
|
|
35
|
+
viewer: 0,
|
|
36
|
+
contributor: 1,
|
|
37
|
+
admin: 2,
|
|
38
|
+
};
|
|
39
|
+
const ROLE_PERMISSIONS = {
|
|
40
|
+
viewer: ["graph.read", "skill.read", "diagnose.read"],
|
|
41
|
+
contributor: [
|
|
42
|
+
"graph.read",
|
|
43
|
+
"graph.write",
|
|
44
|
+
"skill.read",
|
|
45
|
+
"skill.sync",
|
|
46
|
+
"artifact.import",
|
|
47
|
+
"diagnose.read",
|
|
48
|
+
],
|
|
49
|
+
admin: [
|
|
50
|
+
"graph.read",
|
|
51
|
+
"graph.write",
|
|
52
|
+
"graph.delete",
|
|
53
|
+
"skill.read",
|
|
54
|
+
"skill.sync",
|
|
55
|
+
"artifact.import",
|
|
56
|
+
"governance.mutate",
|
|
57
|
+
"diagnose.read",
|
|
58
|
+
],
|
|
59
|
+
};
|
|
60
|
+
/** Graphify / team-memory JSON-RPC methods. */
|
|
61
|
+
const TEAM_METHOD_PERMISSION = {
|
|
62
|
+
"graph.query_subgraph": "graph.read",
|
|
63
|
+
"graph.get_nodes": "graph.read",
|
|
64
|
+
"graph.get_neighbors": "graph.read",
|
|
65
|
+
"graph.read_snapshot": "graph.read",
|
|
66
|
+
"team.health": "diagnose.read",
|
|
67
|
+
"graph.upsert_nodes": "graph.write",
|
|
68
|
+
"graph.upsert_edges": "graph.write",
|
|
69
|
+
"graph.delete_node": "graph.delete",
|
|
70
|
+
"graph.delete_edge": "graph.delete",
|
|
71
|
+
"skill.sync_push": "skill.sync",
|
|
72
|
+
"skill.sync_pull": "skill.read",
|
|
73
|
+
"artifact.import": "artifact.import",
|
|
74
|
+
"governance.quarantine": "governance.mutate",
|
|
75
|
+
"governance.review": "governance.mutate",
|
|
76
|
+
};
|
|
77
|
+
/** GraphFlow MCP tool names on the shared HTTP surface. */
|
|
78
|
+
const MCP_TOOL_PERMISSION = {
|
|
79
|
+
graphflow_context: "graph.read",
|
|
80
|
+
graphflow_diagnose: "diagnose.read",
|
|
81
|
+
graphflow_skill_insights: "skill.read",
|
|
82
|
+
graphflow_skill_guide: "diagnose.read",
|
|
83
|
+
graphflow_run: "graph.write",
|
|
84
|
+
graphflow_plan: "graph.write",
|
|
85
|
+
graphflow_index: "graph.write",
|
|
86
|
+
graphflow_insight: "graph.write",
|
|
87
|
+
graphflow_report_outcome: "graph.write",
|
|
88
|
+
graphflow_artifact: "artifact.import",
|
|
89
|
+
};
|
|
90
|
+
function isTeamRole(value) {
|
|
91
|
+
return typeof value === "string" && exports.TEAM_ROLES.includes(value);
|
|
92
|
+
}
|
|
93
|
+
function permissionsForRole(role) {
|
|
94
|
+
return ROLE_PERMISSIONS[role];
|
|
95
|
+
}
|
|
96
|
+
function roleSatisfies(actual, required) {
|
|
97
|
+
return ROLE_RANK[actual] >= ROLE_RANK[required];
|
|
98
|
+
}
|
|
99
|
+
function hasPermission(role, permission) {
|
|
100
|
+
if (!role)
|
|
101
|
+
return false;
|
|
102
|
+
return ROLE_PERMISSIONS[role].includes(permission);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Map JWT `scope` tokens (and a few aliases) to the strongest matching role.
|
|
106
|
+
* Unknown scopes are ignored — absence of a known role scope means unresolved.
|
|
107
|
+
*/
|
|
108
|
+
function roleFromScopes(scopes) {
|
|
109
|
+
let best;
|
|
110
|
+
for (const raw of scopes) {
|
|
111
|
+
const scope = raw.trim().toLowerCase();
|
|
112
|
+
if (!scope)
|
|
113
|
+
continue;
|
|
114
|
+
const mapped = mapScopeToken(scope);
|
|
115
|
+
if (!mapped)
|
|
116
|
+
continue;
|
|
117
|
+
if (!best || ROLE_RANK[mapped] > ROLE_RANK[best])
|
|
118
|
+
best = mapped;
|
|
119
|
+
}
|
|
120
|
+
return best;
|
|
121
|
+
}
|
|
122
|
+
function mapScopeToken(scope) {
|
|
123
|
+
if (scope === "memory:admin" ||
|
|
124
|
+
scope === "admin" ||
|
|
125
|
+
scope === "role:admin" ||
|
|
126
|
+
scope === "graphflow:admin") {
|
|
127
|
+
return "admin";
|
|
128
|
+
}
|
|
129
|
+
if (scope === "memory:write" ||
|
|
130
|
+
scope === "contributor" ||
|
|
131
|
+
scope === "role:contributor" ||
|
|
132
|
+
scope === "editor" ||
|
|
133
|
+
scope === "role:editor" ||
|
|
134
|
+
scope === "graphflow:write") {
|
|
135
|
+
return "contributor";
|
|
136
|
+
}
|
|
137
|
+
if (scope === "memory:read" ||
|
|
138
|
+
scope === "viewer" ||
|
|
139
|
+
scope === "role:viewer" ||
|
|
140
|
+
scope === "graphflow:read") {
|
|
141
|
+
return "viewer";
|
|
142
|
+
}
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
function defaultScopesForRole(role) {
|
|
146
|
+
if (role === "admin")
|
|
147
|
+
return "memory:read memory:write memory:admin";
|
|
148
|
+
if (role === "contributor")
|
|
149
|
+
return "memory:read memory:write";
|
|
150
|
+
return "memory:read";
|
|
151
|
+
}
|
|
152
|
+
function permissionForTeamMethod(method) {
|
|
153
|
+
return TEAM_METHOD_PERMISSION[method];
|
|
154
|
+
}
|
|
155
|
+
function permissionForMcpTool(toolName) {
|
|
156
|
+
return MCP_TOOL_PERMISSION[toolName];
|
|
157
|
+
}
|
|
158
|
+
function authorizePermission(role, permission, subject = "action") {
|
|
159
|
+
if (!role) {
|
|
160
|
+
return {
|
|
161
|
+
ok: false,
|
|
162
|
+
permission,
|
|
163
|
+
reason: `RBAC denied ${subject}: role unresolved (fail closed)`,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
if (!hasPermission(role, permission)) {
|
|
167
|
+
return {
|
|
168
|
+
ok: false,
|
|
169
|
+
role,
|
|
170
|
+
permission,
|
|
171
|
+
reason: `RBAC denied ${subject}: role '${role}' lacks '${permission}'`,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
return { ok: true, role, permission };
|
|
175
|
+
}
|
|
176
|
+
function authorizeTeamMethod(role, method) {
|
|
177
|
+
const permission = permissionForTeamMethod(method);
|
|
178
|
+
if (!permission) {
|
|
179
|
+
return {
|
|
180
|
+
ok: false,
|
|
181
|
+
...(role ? { role } : {}),
|
|
182
|
+
reason: `RBAC denied ${method}: unknown method (fail closed)`,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
return authorizePermission(role, permission, method);
|
|
186
|
+
}
|
|
187
|
+
function authorizeMcpTool(role, toolName) {
|
|
188
|
+
const permission = permissionForMcpTool(toolName);
|
|
189
|
+
if (!permission) {
|
|
190
|
+
return {
|
|
191
|
+
ok: false,
|
|
192
|
+
...(role ? { role } : {}),
|
|
193
|
+
reason: `RBAC denied ${toolName}: unknown tool (fail closed)`,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return authorizePermission(role, permission, toolName);
|
|
197
|
+
}
|
|
198
|
+
class TeamAuthorizationError extends Error {
|
|
199
|
+
statusCode = 403;
|
|
200
|
+
decision;
|
|
201
|
+
constructor(decision) {
|
|
202
|
+
super(decision.reason ?? "RBAC denied");
|
|
203
|
+
this.name = "TeamAuthorizationError";
|
|
204
|
+
this.decision = decision;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
exports.TeamAuthorizationError = TeamAuthorizationError;
|
|
208
|
+
function assertAuthorized(decision) {
|
|
209
|
+
if (!decision.ok) {
|
|
210
|
+
throw new TeamAuthorizationError(decision);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=rbac.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rbac.js","sourceRoot":"","sources":["../../src/security/rbac.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { type TeamRole } from "./rbac.js";
|
|
1
2
|
export interface TokenAuthConfig {
|
|
2
3
|
/** Shared-secret bearer tokens for local/team deployments. */
|
|
3
4
|
bearerTokens?: readonly string[];
|
|
5
|
+
/** Optional role assignment for each bearer token value. */
|
|
6
|
+
bearerRoleMap?: Readonly<Record<string, TeamRole>>;
|
|
4
7
|
/** HS256 JWT secret. */
|
|
5
8
|
jwtSecret?: string;
|
|
6
9
|
/** RS256/ES256 public key PEM for OIDC providers. */
|
|
@@ -9,15 +12,25 @@ export interface TokenAuthConfig {
|
|
|
9
12
|
audience?: string;
|
|
10
13
|
requiredScope?: string;
|
|
11
14
|
}
|
|
12
|
-
export
|
|
15
|
+
export interface AccessTokenResult {
|
|
13
16
|
authenticated: boolean;
|
|
14
17
|
subject?: string;
|
|
15
18
|
reason?: string;
|
|
16
|
-
|
|
19
|
+
role?: TeamRole;
|
|
20
|
+
scopes?: string[];
|
|
21
|
+
}
|
|
22
|
+
export declare function verifyAccessToken(authorizationHeader: string | undefined, config: TokenAuthConfig): Promise<AccessTokenResult>;
|
|
17
23
|
export declare function issueLocalJwt(subject: string, secret: string, options?: {
|
|
18
24
|
issuer?: string;
|
|
19
25
|
audience?: string;
|
|
20
26
|
scope?: string;
|
|
27
|
+
role?: TeamRole;
|
|
21
28
|
ttlSeconds?: number;
|
|
22
29
|
}): string;
|
|
30
|
+
/** Parse `viewer:secret`, `admin:secret`, or `secret:admin` into token + role. Bare tokens have no role. */
|
|
31
|
+
export declare function parseRoleTaggedBearer(raw: string): {
|
|
32
|
+
token: string;
|
|
33
|
+
role?: TeamRole;
|
|
34
|
+
};
|
|
35
|
+
export declare function credentialsConfigured(config: TokenAuthConfig | undefined): boolean;
|
|
23
36
|
//# sourceMappingURL=token-auth.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-auth.d.ts","sourceRoot":"","sources":["../../src/security/token-auth.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"token-auth.d.ts","sourceRoot":"","sources":["../../src/security/token-auth.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,QAAQ,EACd,MAAM,WAAW,CAAC;AAEnB,MAAM,WAAW,eAAe;IAC9B,8DAA8D;IAC9D,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,4DAA4D;IAC5D,aAAa,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;IACnD,wBAAwB;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAkFD,wBAAsB,iBAAiB,CACrC,mBAAmB,EAAE,MAAM,GAAG,SAAS,EACvC,MAAM,EAAE,eAAe,GACtB,OAAO,CAAC,iBAAiB,CAAC,CAqE5B;AAED,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;IACP,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CAChB,GACL,MAAM,CAkBR;AAED,4GAA4G;AAC5G,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,QAAQ,CAAA;CAAE,CAcrF;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,GAAG,OAAO,CAOlF"}
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.verifyAccessToken = verifyAccessToken;
|
|
4
4
|
exports.issueLocalJwt = issueLocalJwt;
|
|
5
|
+
exports.parseRoleTaggedBearer = parseRoleTaggedBearer;
|
|
6
|
+
exports.credentialsConfigured = credentialsConfigured;
|
|
5
7
|
const node_crypto_1 = require("node:crypto");
|
|
8
|
+
const rbac_js_1 = require("./rbac.js");
|
|
6
9
|
function splitToken(token) {
|
|
7
10
|
const parts = token.split(".");
|
|
8
11
|
if (parts.length !== 3)
|
|
@@ -23,14 +26,54 @@ function secureEquals(left, right) {
|
|
|
23
26
|
const b = Buffer.from(right);
|
|
24
27
|
return a.length === b.length && (0, node_crypto_1.timingSafeEqual)(a, b);
|
|
25
28
|
}
|
|
29
|
+
function parseScopeList(value) {
|
|
30
|
+
if (typeof value === "string") {
|
|
31
|
+
return value.split(/[\s,]+/).map((item) => item.trim()).filter(Boolean);
|
|
32
|
+
}
|
|
33
|
+
if (Array.isArray(value)) {
|
|
34
|
+
return value.filter((item) => typeof item === "string" && item.trim().length > 0);
|
|
35
|
+
}
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
function roleFromClaims(payload) {
|
|
39
|
+
if ((0, rbac_js_1.isTeamRole)(payload.role))
|
|
40
|
+
return payload.role;
|
|
41
|
+
const roles = Array.isArray(payload.roles) ? payload.roles : [];
|
|
42
|
+
const fromRoles = (0, rbac_js_1.roleFromScopes)(roles.filter((item) => typeof item === "string"));
|
|
43
|
+
if (fromRoles)
|
|
44
|
+
return fromRoles;
|
|
45
|
+
return (0, rbac_js_1.roleFromScopes)([...parseScopeList(payload.scope), ...parseScopeList(payload.scp)]);
|
|
46
|
+
}
|
|
47
|
+
function bearerCandidates(config) {
|
|
48
|
+
const seen = new Set();
|
|
49
|
+
const out = [];
|
|
50
|
+
for (const token of config.bearerTokens ?? []) {
|
|
51
|
+
if (token && !seen.has(token)) {
|
|
52
|
+
seen.add(token);
|
|
53
|
+
out.push(token);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
for (const token of Object.keys(config.bearerRoleMap ?? {})) {
|
|
57
|
+
if (token && !seen.has(token)) {
|
|
58
|
+
seen.add(token);
|
|
59
|
+
out.push(token);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
26
64
|
async function verifyAccessToken(authorizationHeader, config) {
|
|
27
65
|
if (!authorizationHeader?.startsWith("Bearer ")) {
|
|
28
66
|
return { authenticated: false, reason: "missing bearer token" };
|
|
29
67
|
}
|
|
30
68
|
const token = authorizationHeader.slice(7).trim();
|
|
31
|
-
for (const expected of config
|
|
69
|
+
for (const expected of bearerCandidates(config)) {
|
|
32
70
|
if (expected && secureEquals(token, expected)) {
|
|
33
|
-
|
|
71
|
+
const role = config.bearerRoleMap?.[expected];
|
|
72
|
+
return {
|
|
73
|
+
authenticated: true,
|
|
74
|
+
subject: "bearer",
|
|
75
|
+
...(role ? { role } : {}),
|
|
76
|
+
};
|
|
34
77
|
}
|
|
35
78
|
}
|
|
36
79
|
const jwt = splitToken(token);
|
|
@@ -54,6 +97,13 @@ async function verifyAccessToken(authorizationHeader, config) {
|
|
|
54
97
|
}
|
|
55
98
|
if (!verified)
|
|
56
99
|
return { authenticated: false, reason: "signature rejected" };
|
|
100
|
+
const now = Math.floor(Date.now() / 1000);
|
|
101
|
+
if (typeof jwt.payload.exp === "number" && jwt.payload.exp < now) {
|
|
102
|
+
return { authenticated: false, reason: "token expired" };
|
|
103
|
+
}
|
|
104
|
+
if (typeof jwt.payload.nbf === "number" && jwt.payload.nbf > now) {
|
|
105
|
+
return { authenticated: false, reason: "token not yet valid" };
|
|
106
|
+
}
|
|
57
107
|
if (config.issuer && jwt.payload.iss !== config.issuer) {
|
|
58
108
|
return { authenticated: false, reason: "issuer rejected" };
|
|
59
109
|
}
|
|
@@ -61,21 +111,30 @@ async function verifyAccessToken(authorizationHeader, config) {
|
|
|
61
111
|
if (config.audience && !audience.includes(config.audience)) {
|
|
62
112
|
return { authenticated: false, reason: "audience rejected" };
|
|
63
113
|
}
|
|
64
|
-
const scopes =
|
|
114
|
+
const scopes = [...parseScopeList(jwt.payload.scope), ...parseScopeList(jwt.payload.scp)];
|
|
65
115
|
if (config.requiredScope && !scopes.includes(config.requiredScope)) {
|
|
66
116
|
return { authenticated: false, reason: "scope rejected" };
|
|
67
117
|
}
|
|
68
|
-
|
|
118
|
+
const role = roleFromClaims(jwt.payload);
|
|
119
|
+
return {
|
|
120
|
+
authenticated: true,
|
|
121
|
+
subject: typeof jwt.payload.sub === "string" ? jwt.payload.sub : "jwt",
|
|
122
|
+
...(role ? { role } : {}),
|
|
123
|
+
...(scopes.length > 0 ? { scopes } : {}),
|
|
124
|
+
};
|
|
69
125
|
}
|
|
70
126
|
function issueLocalJwt(subject, secret, options = {}) {
|
|
71
127
|
const encode = (value) => Buffer.from(JSON.stringify(value)).toString("base64url");
|
|
72
128
|
const now = Math.floor(Date.now() / 1000);
|
|
129
|
+
const role = options.role;
|
|
130
|
+
const scope = options.scope ?? (role ? (0, rbac_js_1.defaultScopesForRole)(role) : undefined);
|
|
73
131
|
const header = encode({ alg: "HS256", typ: "JWT" });
|
|
74
132
|
const payload = encode({
|
|
75
133
|
sub: subject,
|
|
76
134
|
...(options.issuer ? { iss: options.issuer } : {}),
|
|
77
135
|
...(options.audience ? { aud: options.audience } : {}),
|
|
78
|
-
...(
|
|
136
|
+
...(scope ? { scope } : {}),
|
|
137
|
+
...(role ? { role } : {}),
|
|
79
138
|
iat: now,
|
|
80
139
|
exp: now + (options.ttlSeconds ?? 3600),
|
|
81
140
|
});
|
|
@@ -83,4 +142,26 @@ function issueLocalJwt(subject, secret, options = {}) {
|
|
|
83
142
|
const signature = (0, node_crypto_1.createHmac)("sha256", secret).update(data).digest("base64url");
|
|
84
143
|
return `${data}.${signature}`;
|
|
85
144
|
}
|
|
145
|
+
/** Parse `viewer:secret`, `admin:secret`, or `secret:admin` into token + role. Bare tokens have no role. */
|
|
146
|
+
function parseRoleTaggedBearer(raw) {
|
|
147
|
+
const trimmed = raw.trim();
|
|
148
|
+
const sep = trimmed.indexOf(":");
|
|
149
|
+
if (sep > 0) {
|
|
150
|
+
const left = trimmed.slice(0, sep).trim();
|
|
151
|
+
const right = trimmed.slice(sep + 1).trim();
|
|
152
|
+
if (right && (0, rbac_js_1.isTeamRole)(left.toLowerCase())) {
|
|
153
|
+
return { token: right, role: left.toLowerCase() };
|
|
154
|
+
}
|
|
155
|
+
if (left && (0, rbac_js_1.isTeamRole)(right.toLowerCase())) {
|
|
156
|
+
return { token: left, role: right.toLowerCase() };
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return { token: trimmed };
|
|
160
|
+
}
|
|
161
|
+
function credentialsConfigured(config) {
|
|
162
|
+
return Boolean(config?.bearerTokens?.length ||
|
|
163
|
+
(config?.bearerRoleMap && Object.keys(config.bearerRoleMap).length > 0) ||
|
|
164
|
+
config?.jwtSecret ||
|
|
165
|
+
config?.publicKeyPem);
|
|
166
|
+
}
|
|
86
167
|
//# sourceMappingURL=token-auth.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-auth.js","sourceRoot":"","sources":["../../src/security/token-auth.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"token-auth.js","sourceRoot":"","sources":["../../src/security/token-auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -31,6 +31,10 @@ async function executeCommand(command, args, configPath) {
|
|
|
31
31
|
if (command === "doctor") {
|
|
32
32
|
const { buildDoctorReport, formatDoctorLegacyText } = require("./init");
|
|
33
33
|
const data = buildDoctorReport(process.cwd());
|
|
34
|
+
if (data.team?.enabled) {
|
|
35
|
+
const { probeTeamDiagnosis } = await import("../team/diagnose.js");
|
|
36
|
+
data.team = await probeTeamDiagnosis(configPath);
|
|
37
|
+
}
|
|
34
38
|
if (!data.ok) {
|
|
35
39
|
process.exitCode = 1;
|
|
36
40
|
}
|
|
@@ -59,9 +63,22 @@ async function executeCommand(command, args, configPath) {
|
|
|
59
63
|
legacyText: `MCP removal complete`,
|
|
60
64
|
};
|
|
61
65
|
}
|
|
66
|
+
if (command === "team") {
|
|
67
|
+
return executeTeamCommand(args);
|
|
68
|
+
}
|
|
69
|
+
if (command === "diagnose") {
|
|
70
|
+
const data = (0, runtime_1.diagnoseRoutingResult)(configPath);
|
|
71
|
+
const { probeTeamDiagnosis } = await import("../team/diagnose.js");
|
|
72
|
+
data.team = await probeTeamDiagnosis(configPath);
|
|
73
|
+
return {
|
|
74
|
+
command: "diagnose",
|
|
75
|
+
data,
|
|
76
|
+
legacyText: (0, runtime_1.diagnoseRouting)(configPath, data),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
62
79
|
if (command === "mcp" && args[0] === "serve") {
|
|
63
80
|
if (!args.includes("--http")) {
|
|
64
|
-
console.log("Usage: graphflow mcp serve --http [--host <host>] [--port <port>] [--endpoint </path>] [--stateful] [--sse-only]");
|
|
81
|
+
console.log("Usage: graphflow mcp serve --http [--host <host>] [--port <port>] [--endpoint </path>] [--stateful] [--sse-only] [--http-token <role:token>] [--rbac]");
|
|
65
82
|
process.exitCode = 1;
|
|
66
83
|
return undefined;
|
|
67
84
|
}
|
|
@@ -584,16 +601,27 @@ async function executeCommand(command, args, configPath) {
|
|
|
584
601
|
// overwrite semantics. Team golden queries ride along and merge into
|
|
585
602
|
// `.graphflow/team-golden.json` (dedupe by text, local-first order).
|
|
586
603
|
const directionArg = args[1]?.trim().toLowerCase();
|
|
587
|
-
if (directionArg !== "export" && directionArg !== "import") {
|
|
588
|
-
console.log("Usage: graphflow skill sync <export|import> [--path <file>] [--force]");
|
|
604
|
+
if (directionArg !== "export" && directionArg !== "import" && directionArg !== "push" && directionArg !== "pull") {
|
|
605
|
+
console.log("Usage: graphflow skill sync <export|import|push|pull> [--path <file>] [--force]");
|
|
589
606
|
console.log(" import MERGES per-skill-id: newer updatedAt wins; ties keep local; --force overwrites.");
|
|
590
607
|
console.log(" team golden queries ride along -> .graphflow/team-golden.json (dedupe, local-first).");
|
|
608
|
+
console.log(" push/pull use graphPolicy.mcpEndpoint (mcp-http team server) and keep MERGE on pull.");
|
|
591
609
|
process.exitCode = 1;
|
|
592
610
|
return undefined;
|
|
593
611
|
}
|
|
594
612
|
const pathIdx = args.indexOf("--path");
|
|
595
613
|
const customPath = pathIdx >= 0 ? args[pathIdx + 1]?.trim() : undefined;
|
|
596
614
|
const force = args.includes("--force");
|
|
615
|
+
if (directionArg === "push" || directionArg === "pull") {
|
|
616
|
+
const data = await (0, runtime_1.syncSkillPackageRemote)(configPath, directionArg, { force });
|
|
617
|
+
return {
|
|
618
|
+
command: "skill-sync",
|
|
619
|
+
data,
|
|
620
|
+
legacyText: data.direction === "push"
|
|
621
|
+
? `direction=push; path=${data.path}; skillCount=${data.skillCount ?? 0}; revision=${data.revision ?? 0}`
|
|
622
|
+
: `direction=pull; path=${data.path}; imported=${data.imported ?? 0}; skipped=${data.skipped ?? 0}; updated=${data.updated ?? 0}; revision=${data.revision ?? 0}`,
|
|
623
|
+
};
|
|
624
|
+
}
|
|
597
625
|
const data = await (0, runtime_1.syncSkillPackageRuntime)(configPath, directionArg, customPath, { force });
|
|
598
626
|
return {
|
|
599
627
|
command: "skill-sync",
|
|
@@ -721,10 +749,12 @@ async function executeCommand(command, args, configPath) {
|
|
|
721
749
|
}
|
|
722
750
|
if (command === "route" && args[0] === "diagnose") {
|
|
723
751
|
const data = (0, runtime_1.diagnoseRoutingResult)(configPath);
|
|
752
|
+
const { probeTeamDiagnosis } = await import("../team/diagnose.js");
|
|
753
|
+
data.team = await probeTeamDiagnosis(configPath);
|
|
724
754
|
return {
|
|
725
755
|
command: "route-diagnose",
|
|
726
756
|
data,
|
|
727
|
-
legacyText: (0, runtime_1.diagnoseRouting)(configPath),
|
|
757
|
+
legacyText: (0, runtime_1.diagnoseRouting)(configPath, data),
|
|
728
758
|
};
|
|
729
759
|
}
|
|
730
760
|
if (command === "artifact" && args[0] === "export") {
|
|
@@ -1109,6 +1139,51 @@ main().catch((error) => {
|
|
|
1109
1139
|
console.error("GraphFlow execution failed:", error);
|
|
1110
1140
|
process.exitCode = 1;
|
|
1111
1141
|
});
|
|
1142
|
+
async function executeTeamCommand(args) {
|
|
1143
|
+
const sub = args[0]?.trim();
|
|
1144
|
+
if (sub === "serve") {
|
|
1145
|
+
const { startTeamServerFromArgv } = await import("../team/ops.js");
|
|
1146
|
+
const started = await startTeamServerFromArgv(args.slice(1));
|
|
1147
|
+
const { httpServer: _httpServer, close: _close, ...data } = started;
|
|
1148
|
+
return {
|
|
1149
|
+
command: "team-serve",
|
|
1150
|
+
data,
|
|
1151
|
+
legacyText: `GraphFlow team memory listening on ${data.url} ` +
|
|
1152
|
+
`(auth=${data.requireAuth ? "required" : "optional"}; rbac=on; store=${data.storeRoot})`,
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1155
|
+
if (sub === "issue-token") {
|
|
1156
|
+
const { issueTeamTokenFromArgv } = await import("../team/ops.js");
|
|
1157
|
+
const data = issueTeamTokenFromArgv(args.slice(1));
|
|
1158
|
+
return {
|
|
1159
|
+
command: "team-issue-token",
|
|
1160
|
+
data,
|
|
1161
|
+
legacyText: `subject=${data.subject}; role=${data.role}; ttl=${data.ttlSeconds}; token=${data.token}`,
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1164
|
+
if (sub === "example-config") {
|
|
1165
|
+
const { buildTeamClientExampleConfig } = await import("../team/ops.js");
|
|
1166
|
+
const endpoint = (0, output_1.readCliFlagValue)(args, "--endpoint");
|
|
1167
|
+
const tenant = (0, output_1.readCliFlagValue)(args, "--tenant");
|
|
1168
|
+
const apiKey = (0, output_1.readCliFlagValue)(args, "--api-key");
|
|
1169
|
+
const data = buildTeamClientExampleConfig({
|
|
1170
|
+
...(endpoint ? { endpoint } : {}),
|
|
1171
|
+
...(tenant ? { tenant } : {}),
|
|
1172
|
+
...(apiKey ? { apiKey } : {}),
|
|
1173
|
+
});
|
|
1174
|
+
return {
|
|
1175
|
+
command: "team-example-config",
|
|
1176
|
+
data,
|
|
1177
|
+
legacyText: JSON.stringify(data, null, 2),
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
console.log("Usage: graphflow team <serve|issue-token|example-config>");
|
|
1181
|
+
console.log(" team serve [--host <host>] [--port <port>] [--store <dir>] [--http-token <role:token>] [--http-jwt-secret <secret>] [--allow-tenant <id>]");
|
|
1182
|
+
console.log(" team issue-token --subject <id> --role <viewer|contributor|admin> [--secret <jwt>] [--ttl <seconds>]");
|
|
1183
|
+
console.log(" team example-config [--endpoint <url>] [--tenant <id>]");
|
|
1184
|
+
process.exitCode = 1;
|
|
1185
|
+
return undefined;
|
|
1186
|
+
}
|
|
1112
1187
|
function buildInsightLegacyText(data) {
|
|
1113
1188
|
const lines = [];
|
|
1114
1189
|
lines.push("=== Six Thinking Hats Insight ===");
|