@aigne/afs-rotation 1.11.0-beta.12
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.md +26 -0
- package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs +11 -0
- package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs +10 -0
- package/dist/index.cjs +406 -0
- package/dist/index.d.cts +115 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +115 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +407 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +58 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Proprietary License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2025 ArcBlock, Inc. All Rights Reserved.
|
|
4
|
+
|
|
5
|
+
This software and associated documentation files (the "Software") are proprietary
|
|
6
|
+
and confidential. Unauthorized copying, modification, distribution, or use of
|
|
7
|
+
this Software, via any medium, is strictly prohibited.
|
|
8
|
+
|
|
9
|
+
The Software is provided for internal use only within ArcBlock, Inc. and its
|
|
10
|
+
authorized affiliates.
|
|
11
|
+
|
|
12
|
+
## No License Granted
|
|
13
|
+
|
|
14
|
+
No license, express or implied, is granted to any party for any purpose.
|
|
15
|
+
All rights are reserved by ArcBlock, Inc.
|
|
16
|
+
|
|
17
|
+
## Public Artifact Distribution
|
|
18
|
+
|
|
19
|
+
Portions of this Software may be released publicly under separate open-source
|
|
20
|
+
licenses (such as MIT License) through designated public repositories. Such
|
|
21
|
+
public releases are governed by their respective licenses and do not affect
|
|
22
|
+
the proprietary nature of this repository.
|
|
23
|
+
|
|
24
|
+
## Contact
|
|
25
|
+
|
|
26
|
+
For licensing inquiries, contact: legal@arcblock.io
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
|
|
2
|
+
//#region \0@oxc-project+runtime@0.108.0/helpers/decorate.js
|
|
3
|
+
function __decorate(decorators, target, key, desc) {
|
|
4
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
5
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
6
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
7
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
//#endregion
|
|
11
|
+
exports.__decorate = __decorate;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region \0@oxc-project+runtime@0.108.0/helpers/decorate.js
|
|
2
|
+
function __decorate(decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
//#endregion
|
|
10
|
+
export { __decorate };
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
const require_decorate = require('./_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs');
|
|
2
|
+
let _aigne_afs = require("@aigne/afs");
|
|
3
|
+
let _aigne_afs_provider = require("@aigne/afs/provider");
|
|
4
|
+
let ufo = require("ufo");
|
|
5
|
+
let zod = require("zod");
|
|
6
|
+
|
|
7
|
+
//#region src/index.ts
|
|
8
|
+
/**
|
|
9
|
+
* AFS Rotation Provider — OAuth2 token rotation with vault integration.
|
|
10
|
+
*
|
|
11
|
+
* Tree: /rotation/{config-name} → rotation config and status
|
|
12
|
+
* Actions: rotate (per config), rotate-all (root)
|
|
13
|
+
* Reads refresh tokens from vault, calls OAuth2 token endpoint, writes new tokens back.
|
|
14
|
+
* No scheduler — on-demand only. Scheduling is external infrastructure.
|
|
15
|
+
*/
|
|
16
|
+
const rotationConfigSchema = zod.z.object({
|
|
17
|
+
name: zod.z.string(),
|
|
18
|
+
refreshTokenPath: zod.z.string(),
|
|
19
|
+
accessTokenPath: zod.z.string(),
|
|
20
|
+
tokenEndpoint: zod.z.string().url(),
|
|
21
|
+
clientId: zod.z.string(),
|
|
22
|
+
clientSecretPath: zod.z.string().optional(),
|
|
23
|
+
grantType: zod.z.string().optional(),
|
|
24
|
+
extraParams: zod.z.record(zod.z.string(), zod.z.string()).optional(),
|
|
25
|
+
rotateRefreshToken: zod.z.boolean().optional()
|
|
26
|
+
});
|
|
27
|
+
const afsRotationOptionsSchema = zod.z.object({
|
|
28
|
+
name: zod.z.string().optional().describe("Module name"),
|
|
29
|
+
description: zod.z.string().optional().describe("Module description"),
|
|
30
|
+
accessMode: zod.z.enum(["readonly", "readwrite"]).optional().describe("Access mode"),
|
|
31
|
+
configs: zod.z.array(rotationConfigSchema).describe("Rotation configurations")
|
|
32
|
+
});
|
|
33
|
+
function parseVaultPath(vaultPath) {
|
|
34
|
+
const parts = vaultPath.split("/").filter(Boolean);
|
|
35
|
+
if (parts.length !== 2) throw new Error(`Invalid vault path "${vaultPath}" — expected "group/name"`);
|
|
36
|
+
return {
|
|
37
|
+
group: parts[0],
|
|
38
|
+
name: parts[1]
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
var AFSRotation = class extends _aigne_afs_provider.AFSBaseProvider {
|
|
42
|
+
name;
|
|
43
|
+
description;
|
|
44
|
+
accessMode;
|
|
45
|
+
vault;
|
|
46
|
+
configs;
|
|
47
|
+
statuses;
|
|
48
|
+
fetchFn;
|
|
49
|
+
constructor(options) {
|
|
50
|
+
super();
|
|
51
|
+
this.name = options.name || "rotation";
|
|
52
|
+
this.description = options.description || "OAuth2 token rotation";
|
|
53
|
+
this.accessMode = options.accessMode || "readwrite";
|
|
54
|
+
this.vault = options.vault;
|
|
55
|
+
this.fetchFn = options.fetchFn || globalThis.fetch;
|
|
56
|
+
this.configs = /* @__PURE__ */ new Map();
|
|
57
|
+
this.statuses = /* @__PURE__ */ new Map();
|
|
58
|
+
for (const cfg of options.configs) {
|
|
59
|
+
rotationConfigSchema.parse(cfg);
|
|
60
|
+
this.configs.set(cfg.name, cfg);
|
|
61
|
+
this.statuses.set(cfg.name, {
|
|
62
|
+
configName: cfg.name,
|
|
63
|
+
lastRotation: null,
|
|
64
|
+
lastResult: null,
|
|
65
|
+
lastError: null,
|
|
66
|
+
rotationCount: 0
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
static securityProfiles() {
|
|
71
|
+
return {
|
|
72
|
+
admin: {
|
|
73
|
+
actionPolicy: "full",
|
|
74
|
+
accessMode: "readwrite"
|
|
75
|
+
},
|
|
76
|
+
system: {
|
|
77
|
+
actionPolicy: "safe",
|
|
78
|
+
accessMode: "readonly"
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
static schema() {
|
|
83
|
+
return afsRotationOptionsSchema;
|
|
84
|
+
}
|
|
85
|
+
static treeSchema() {
|
|
86
|
+
return {
|
|
87
|
+
operations: [
|
|
88
|
+
"list",
|
|
89
|
+
"read",
|
|
90
|
+
"exec",
|
|
91
|
+
"stat",
|
|
92
|
+
"explain"
|
|
93
|
+
],
|
|
94
|
+
tree: {
|
|
95
|
+
"/": {
|
|
96
|
+
kind: "rotation:root",
|
|
97
|
+
operations: [
|
|
98
|
+
"list",
|
|
99
|
+
"read",
|
|
100
|
+
"exec"
|
|
101
|
+
],
|
|
102
|
+
actions: ["rotate-all"]
|
|
103
|
+
},
|
|
104
|
+
"/{name}": {
|
|
105
|
+
kind: "rotation:config",
|
|
106
|
+
operations: [
|
|
107
|
+
"list",
|
|
108
|
+
"read",
|
|
109
|
+
"exec"
|
|
110
|
+
],
|
|
111
|
+
actions: ["rotate"]
|
|
112
|
+
},
|
|
113
|
+
"/{name}/status": {
|
|
114
|
+
kind: "rotation:status",
|
|
115
|
+
operations: ["read"]
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
auth: { type: "custom" },
|
|
119
|
+
bestFor: ["OAuth2 token rotation", "credential refresh"],
|
|
120
|
+
notFor: ["secret storage"]
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
static manifest() {
|
|
124
|
+
return {
|
|
125
|
+
name: "rotation",
|
|
126
|
+
description: "OAuth2 token rotation.\n- Reads refresh tokens from vault, calls token endpoints, writes new tokens back\n- On-demand rotation via actions\n- Security profiles: admin (readwrite), system (readonly)",
|
|
127
|
+
uriTemplate: "rotation://",
|
|
128
|
+
category: "security",
|
|
129
|
+
schema: afsRotationOptionsSchema,
|
|
130
|
+
tags: [
|
|
131
|
+
"rotation",
|
|
132
|
+
"oauth",
|
|
133
|
+
"tokens",
|
|
134
|
+
"secrets"
|
|
135
|
+
],
|
|
136
|
+
capabilityTags: [
|
|
137
|
+
"read-write",
|
|
138
|
+
"auth:none",
|
|
139
|
+
"local"
|
|
140
|
+
],
|
|
141
|
+
security: {
|
|
142
|
+
riskLevel: "external",
|
|
143
|
+
resourceAccess: ["internet"],
|
|
144
|
+
dataSensitivity: ["credentials"],
|
|
145
|
+
notes: ["Makes HTTP requests to OAuth2 token endpoints", "Reads and writes secrets in vault"]
|
|
146
|
+
},
|
|
147
|
+
capabilities: { network: { egress: true } }
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
static async load({ config } = {}) {
|
|
151
|
+
afsRotationOptionsSchema.parse(config || {});
|
|
152
|
+
throw new Error("AFSRotation requires a vault instance — use `new AFSRotation({ vault, configs })` directly");
|
|
153
|
+
}
|
|
154
|
+
getConfig(name) {
|
|
155
|
+
const cfg = this.configs.get(name);
|
|
156
|
+
if (!cfg) throw new _aigne_afs.AFSNotFoundError((0, ufo.joinURL)("/", name));
|
|
157
|
+
return cfg;
|
|
158
|
+
}
|
|
159
|
+
getStatus(name) {
|
|
160
|
+
return this.statuses.get(name) || {
|
|
161
|
+
configName: name,
|
|
162
|
+
lastRotation: null,
|
|
163
|
+
lastResult: null,
|
|
164
|
+
lastError: null,
|
|
165
|
+
rotationCount: 0
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
parsePath(ctx) {
|
|
169
|
+
return (ctx.params?.path || "").split("/").filter(Boolean);
|
|
170
|
+
}
|
|
171
|
+
buildConfigEntry(name) {
|
|
172
|
+
const status = this.getStatus(name);
|
|
173
|
+
const cfg = this.configs.get(name);
|
|
174
|
+
return this.buildEntry((0, ufo.joinURL)("/", name), { meta: {
|
|
175
|
+
childrenCount: 1,
|
|
176
|
+
configName: name,
|
|
177
|
+
tokenEndpoint: cfg.tokenEndpoint,
|
|
178
|
+
lastRotation: status.lastRotation,
|
|
179
|
+
lastResult: status.lastResult
|
|
180
|
+
} });
|
|
181
|
+
}
|
|
182
|
+
buildStatusEntry(name) {
|
|
183
|
+
const status = this.getStatus(name);
|
|
184
|
+
return this.buildEntry((0, ufo.joinURL)("/", name, "status"), {
|
|
185
|
+
content: status,
|
|
186
|
+
meta: {
|
|
187
|
+
kind: "rotation:status",
|
|
188
|
+
lastRotation: status.lastRotation,
|
|
189
|
+
lastResult: status.lastResult,
|
|
190
|
+
rotationCount: status.rotationCount
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
buildRootEntry() {
|
|
195
|
+
return this.buildEntry("/", { meta: {
|
|
196
|
+
childrenCount: this.configs.size,
|
|
197
|
+
provider: "rotation",
|
|
198
|
+
configCount: this.configs.size
|
|
199
|
+
} });
|
|
200
|
+
}
|
|
201
|
+
async rotate(configName) {
|
|
202
|
+
const cfg = this.getConfig(configName);
|
|
203
|
+
const status = this.getStatus(configName);
|
|
204
|
+
try {
|
|
205
|
+
const refreshRef = parseVaultPath(cfg.refreshTokenPath);
|
|
206
|
+
const refreshToken = await this.vault.getSecret(refreshRef.group, refreshRef.name);
|
|
207
|
+
if (!refreshToken) throw new Error(`Refresh token not found at vault path: ${cfg.refreshTokenPath}`);
|
|
208
|
+
let clientSecret;
|
|
209
|
+
if (cfg.clientSecretPath) {
|
|
210
|
+
const secretRef = parseVaultPath(cfg.clientSecretPath);
|
|
211
|
+
clientSecret = await this.vault.getSecret(secretRef.group, secretRef.name);
|
|
212
|
+
if (!clientSecret) throw new Error(`Client secret not found at vault path: ${cfg.clientSecretPath}`);
|
|
213
|
+
}
|
|
214
|
+
const params = new URLSearchParams();
|
|
215
|
+
params.set("grant_type", cfg.grantType || "refresh_token");
|
|
216
|
+
params.set("refresh_token", refreshToken);
|
|
217
|
+
params.set("client_id", cfg.clientId);
|
|
218
|
+
if (clientSecret) params.set("client_secret", clientSecret);
|
|
219
|
+
if (cfg.extraParams) for (const [k, v] of Object.entries(cfg.extraParams)) params.set(k, v);
|
|
220
|
+
const response = await this.fetchFn(cfg.tokenEndpoint, {
|
|
221
|
+
method: "POST",
|
|
222
|
+
headers: {
|
|
223
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
224
|
+
Accept: "application/json"
|
|
225
|
+
},
|
|
226
|
+
body: params.toString()
|
|
227
|
+
});
|
|
228
|
+
if (!response.ok) {
|
|
229
|
+
const body = await response.text();
|
|
230
|
+
throw new Error(`Token endpoint returned ${response.status}: ${body}`);
|
|
231
|
+
}
|
|
232
|
+
const tokenResponse = await response.json();
|
|
233
|
+
const accessToken = tokenResponse.access_token;
|
|
234
|
+
if (!accessToken) throw new Error("Token response missing access_token field");
|
|
235
|
+
const accessRef = parseVaultPath(cfg.accessTokenPath);
|
|
236
|
+
await this.vault.setSecret(accessRef.group, accessRef.name, accessToken);
|
|
237
|
+
if (cfg.rotateRefreshToken && tokenResponse.refresh_token) await this.vault.setSecret(refreshRef.group, refreshRef.name, tokenResponse.refresh_token);
|
|
238
|
+
status.lastRotation = Date.now();
|
|
239
|
+
status.lastResult = "success";
|
|
240
|
+
status.lastError = null;
|
|
241
|
+
status.rotationCount++;
|
|
242
|
+
this.statuses.set(configName, status);
|
|
243
|
+
return status;
|
|
244
|
+
} catch (err) {
|
|
245
|
+
status.lastRotation = Date.now();
|
|
246
|
+
status.lastResult = "error";
|
|
247
|
+
status.lastError = err instanceof Error ? err.message : String(err);
|
|
248
|
+
this.statuses.set(configName, status);
|
|
249
|
+
throw err;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
async rotateAll() {
|
|
253
|
+
const results = {};
|
|
254
|
+
for (const [name] of this.configs) try {
|
|
255
|
+
results[name] = await this.rotate(name);
|
|
256
|
+
} catch {
|
|
257
|
+
results[name] = this.getStatus(name);
|
|
258
|
+
}
|
|
259
|
+
return results;
|
|
260
|
+
}
|
|
261
|
+
async handleMeta(ctx) {
|
|
262
|
+
const parts = this.parsePath(ctx);
|
|
263
|
+
const metaPath = parts.length === 0 ? "/.meta" : (0, ufo.joinURL)("/", ...parts, ".meta");
|
|
264
|
+
if (parts.length === 0) return this.buildEntry(metaPath, { meta: {
|
|
265
|
+
childrenCount: this.configs.size,
|
|
266
|
+
provider: "rotation",
|
|
267
|
+
configCount: this.configs.size
|
|
268
|
+
} });
|
|
269
|
+
if (parts.length === 1) {
|
|
270
|
+
const name = parts[0];
|
|
271
|
+
this.getConfig(name);
|
|
272
|
+
return this.buildEntry(metaPath, { meta: {
|
|
273
|
+
childrenCount: 1,
|
|
274
|
+
configName: name
|
|
275
|
+
} });
|
|
276
|
+
}
|
|
277
|
+
if (parts.length === 2 && parts[1] === "status") {
|
|
278
|
+
const status = this.getStatus(parts[0]);
|
|
279
|
+
return this.buildEntry(metaPath, { meta: {
|
|
280
|
+
kind: "rotation:status",
|
|
281
|
+
lastResult: status.lastResult
|
|
282
|
+
} });
|
|
283
|
+
}
|
|
284
|
+
throw new _aigne_afs.AFSNotFoundError((0, ufo.joinURL)("/", ...parts));
|
|
285
|
+
}
|
|
286
|
+
async handleList(ctx) {
|
|
287
|
+
const parts = this.parsePath(ctx);
|
|
288
|
+
if (parts.length === 0) return { data: [...this.configs.keys()].sort().map((n) => this.buildConfigEntry(n)) };
|
|
289
|
+
if (parts.length === 1) {
|
|
290
|
+
this.getConfig(parts[0]);
|
|
291
|
+
return { data: [this.buildStatusEntry(parts[0])] };
|
|
292
|
+
}
|
|
293
|
+
if (parts.length === 2 && parts[1] === "status") return { data: [] };
|
|
294
|
+
throw new _aigne_afs.AFSNotFoundError((0, ufo.joinURL)("/", ...parts));
|
|
295
|
+
}
|
|
296
|
+
async handleReadCapabilities() {
|
|
297
|
+
const operations = this.getOperationsDeclaration();
|
|
298
|
+
const manifest = {
|
|
299
|
+
schemaVersion: 1,
|
|
300
|
+
provider: this.name,
|
|
301
|
+
description: this.description,
|
|
302
|
+
tools: [],
|
|
303
|
+
actions: [],
|
|
304
|
+
operations
|
|
305
|
+
};
|
|
306
|
+
return this.buildEntry("/.meta/.capabilities", {
|
|
307
|
+
content: manifest,
|
|
308
|
+
meta: {
|
|
309
|
+
kind: "afs:capabilities",
|
|
310
|
+
operations
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
async handleRead(ctx) {
|
|
315
|
+
const parts = this.parsePath(ctx);
|
|
316
|
+
if (parts.length === 0) return this.buildRootEntry();
|
|
317
|
+
if (parts.length === 1) {
|
|
318
|
+
const name = parts[0];
|
|
319
|
+
this.getConfig(name);
|
|
320
|
+
return this.buildConfigEntry(name);
|
|
321
|
+
}
|
|
322
|
+
if (parts.length === 2 && parts[1] === "status") return this.buildStatusEntry(parts[0]);
|
|
323
|
+
throw new _aigne_afs.AFSNotFoundError((0, ufo.joinURL)("/", ...parts));
|
|
324
|
+
}
|
|
325
|
+
async handleStat(ctx) {
|
|
326
|
+
const parts = this.parsePath(ctx);
|
|
327
|
+
if (parts.length === 0) return { data: this.buildRootEntry() };
|
|
328
|
+
if (parts.length === 1) {
|
|
329
|
+
const name = parts[0];
|
|
330
|
+
this.getConfig(name);
|
|
331
|
+
return { data: this.buildConfigEntry(name) };
|
|
332
|
+
}
|
|
333
|
+
if (parts.length === 2 && parts[1] === "status") return { data: this.buildStatusEntry(parts[0]) };
|
|
334
|
+
throw new _aigne_afs.AFSNotFoundError((0, ufo.joinURL)("/", ...parts));
|
|
335
|
+
}
|
|
336
|
+
async listConfigActions(ctx) {
|
|
337
|
+
const name = ctx.params.name;
|
|
338
|
+
this.getConfig(name);
|
|
339
|
+
return { data: [{
|
|
340
|
+
id: "rotate",
|
|
341
|
+
path: (0, ufo.joinURL)("/", name, ".actions", "rotate"),
|
|
342
|
+
meta: {
|
|
343
|
+
kind: "action",
|
|
344
|
+
description: `Rotate tokens for ${name}`,
|
|
345
|
+
childrenCount: 0
|
|
346
|
+
}
|
|
347
|
+
}] };
|
|
348
|
+
}
|
|
349
|
+
async execRotate(ctx) {
|
|
350
|
+
const name = ctx.params.name;
|
|
351
|
+
return {
|
|
352
|
+
success: true,
|
|
353
|
+
data: await this.rotate(name)
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
async listRootActions() {
|
|
357
|
+
return { data: [{
|
|
358
|
+
id: "rotate-all",
|
|
359
|
+
path: (0, ufo.joinURL)("/", ".actions", "rotate-all"),
|
|
360
|
+
meta: {
|
|
361
|
+
kind: "action",
|
|
362
|
+
description: "Rotate tokens for all configurations",
|
|
363
|
+
childrenCount: 0
|
|
364
|
+
}
|
|
365
|
+
}] };
|
|
366
|
+
}
|
|
367
|
+
async execRotateAll() {
|
|
368
|
+
const results = await this.rotateAll();
|
|
369
|
+
return {
|
|
370
|
+
success: Object.values(results).every((s) => s.lastResult === "success"),
|
|
371
|
+
data: results
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
async handleExplain(ctx) {
|
|
375
|
+
const parts = this.parsePath(ctx);
|
|
376
|
+
if (parts.length >= 1) {
|
|
377
|
+
const name = parts[0];
|
|
378
|
+
const cfg = this.configs.get(name);
|
|
379
|
+
if (cfg) {
|
|
380
|
+
const status = this.getStatus(name);
|
|
381
|
+
return {
|
|
382
|
+
format: "text",
|
|
383
|
+
content: `Rotation config "${name}".\nToken endpoint: ${cfg.tokenEndpoint}\nClient ID: ${cfg.clientId}\nLast rotation: ${status.lastRotation ? new Date(status.lastRotation).toISOString() : "never"}\nStatus: ${status.lastResult || "pending"}`
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return {
|
|
388
|
+
format: "text",
|
|
389
|
+
content: `AFS Rotation — OAuth2 token rotation. ${this.configs.size} config(s). Use actions to trigger rotation.`
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Meta)("/:path*")], AFSRotation.prototype, "handleMeta", null);
|
|
394
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.List)("/:path*")], AFSRotation.prototype, "handleList", null);
|
|
395
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/.meta/.capabilities")], AFSRotation.prototype, "handleReadCapabilities", null);
|
|
396
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/:path*")], AFSRotation.prototype, "handleRead", null);
|
|
397
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Stat)("/:path*")], AFSRotation.prototype, "handleStat", null);
|
|
398
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Actions)("/:name")], AFSRotation.prototype, "listConfigActions", null);
|
|
399
|
+
require_decorate.__decorate([_aigne_afs_provider.Actions.Exec("/:name", "rotate", "Rotate tokens for this config")], AFSRotation.prototype, "execRotate", null);
|
|
400
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Actions)("/")], AFSRotation.prototype, "listRootActions", null);
|
|
401
|
+
require_decorate.__decorate([_aigne_afs_provider.Actions.Exec("/", "rotate-all", "Rotate all configured tokens")], AFSRotation.prototype, "execRotateAll", null);
|
|
402
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Explain)("/:path*")], AFSRotation.prototype, "handleExplain", null);
|
|
403
|
+
AFSRotation.load = AFSRotation.load;
|
|
404
|
+
|
|
405
|
+
//#endregion
|
|
406
|
+
exports.AFSRotation = AFSRotation;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { AFSAccessMode, AFSEntry, AFSExplainResult, AFSListResult, AFSModuleLoadParams, AFSStatResult, ProviderManifest, ProviderTreeSchema, SecurityProfile } from "@aigne/afs";
|
|
2
|
+
import { AFSBaseProvider, RouteContext } from "@aigne/afs/provider";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
//#region src/index.d.ts
|
|
6
|
+
interface RotationConfig {
|
|
7
|
+
/** Config name (used as path segment). */
|
|
8
|
+
name: string;
|
|
9
|
+
/** Vault secret path: "group/name" for the refresh token. */
|
|
10
|
+
refreshTokenPath: string;
|
|
11
|
+
/** Vault secret path: "group/name" for the access token to update. */
|
|
12
|
+
accessTokenPath: string;
|
|
13
|
+
/** OAuth2 token endpoint URL. */
|
|
14
|
+
tokenEndpoint: string;
|
|
15
|
+
/** OAuth2 client ID. */
|
|
16
|
+
clientId: string;
|
|
17
|
+
/** Vault secret path for client secret (confidential clients). */
|
|
18
|
+
clientSecretPath?: string;
|
|
19
|
+
/** OAuth2 grant type. Default: "refresh_token". */
|
|
20
|
+
grantType?: string;
|
|
21
|
+
/** Additional parameters to include in the token request. */
|
|
22
|
+
extraParams?: Record<string, string>;
|
|
23
|
+
/** Optional: also write the new refresh token if returned. */
|
|
24
|
+
rotateRefreshToken?: boolean;
|
|
25
|
+
}
|
|
26
|
+
interface RotationStatus {
|
|
27
|
+
configName: string;
|
|
28
|
+
lastRotation: number | null;
|
|
29
|
+
lastResult: "success" | "error" | null;
|
|
30
|
+
lastError: string | null;
|
|
31
|
+
rotationCount: number;
|
|
32
|
+
}
|
|
33
|
+
/** Interface for vault access — decoupled from AFSVault class. */
|
|
34
|
+
interface VaultAccess {
|
|
35
|
+
getSecret(group: string, name: string): Promise<string | undefined>;
|
|
36
|
+
setSecret(group: string, name: string, value: string): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
interface AFSRotationOptions {
|
|
39
|
+
name?: string;
|
|
40
|
+
description?: string;
|
|
41
|
+
accessMode?: AFSAccessMode;
|
|
42
|
+
/** Vault access for reading/writing secrets. */
|
|
43
|
+
vault: VaultAccess;
|
|
44
|
+
/** Rotation configurations. */
|
|
45
|
+
configs: RotationConfig[];
|
|
46
|
+
/** Custom fetch function (for testing). */
|
|
47
|
+
fetchFn?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
48
|
+
}
|
|
49
|
+
declare class AFSRotation extends AFSBaseProvider {
|
|
50
|
+
readonly name: string;
|
|
51
|
+
readonly description: string;
|
|
52
|
+
readonly accessMode: AFSAccessMode;
|
|
53
|
+
private vault;
|
|
54
|
+
private configs;
|
|
55
|
+
private statuses;
|
|
56
|
+
private fetchFn;
|
|
57
|
+
constructor(options: AFSRotationOptions);
|
|
58
|
+
static securityProfiles(): Record<string, SecurityProfile>;
|
|
59
|
+
static schema(): z.ZodObject<{
|
|
60
|
+
name: z.ZodOptional<z.ZodString>;
|
|
61
|
+
description: z.ZodOptional<z.ZodString>;
|
|
62
|
+
accessMode: z.ZodOptional<z.ZodEnum<{
|
|
63
|
+
readonly: "readonly";
|
|
64
|
+
readwrite: "readwrite";
|
|
65
|
+
}>>;
|
|
66
|
+
configs: z.ZodArray<z.ZodObject<{
|
|
67
|
+
name: z.ZodString;
|
|
68
|
+
refreshTokenPath: z.ZodString;
|
|
69
|
+
accessTokenPath: z.ZodString;
|
|
70
|
+
tokenEndpoint: z.ZodString;
|
|
71
|
+
clientId: z.ZodString;
|
|
72
|
+
clientSecretPath: z.ZodOptional<z.ZodString>;
|
|
73
|
+
grantType: z.ZodOptional<z.ZodString>;
|
|
74
|
+
extraParams: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
75
|
+
rotateRefreshToken: z.ZodOptional<z.ZodBoolean>;
|
|
76
|
+
}, z.core.$strip>>;
|
|
77
|
+
}, z.core.$strip>;
|
|
78
|
+
static treeSchema(): ProviderTreeSchema;
|
|
79
|
+
static manifest(): ProviderManifest;
|
|
80
|
+
static load({
|
|
81
|
+
config
|
|
82
|
+
}?: AFSModuleLoadParams): Promise<AFSRotation>;
|
|
83
|
+
private getConfig;
|
|
84
|
+
private getStatus;
|
|
85
|
+
private parsePath;
|
|
86
|
+
private buildConfigEntry;
|
|
87
|
+
private buildStatusEntry;
|
|
88
|
+
private buildRootEntry;
|
|
89
|
+
rotate(configName: string): Promise<RotationStatus>;
|
|
90
|
+
rotateAll(): Promise<Record<string, RotationStatus>>;
|
|
91
|
+
handleMeta(ctx: RouteContext): Promise<AFSEntry | undefined>;
|
|
92
|
+
handleList(ctx: RouteContext): Promise<AFSListResult>;
|
|
93
|
+
handleReadCapabilities(): Promise<AFSEntry | undefined>;
|
|
94
|
+
handleRead(ctx: RouteContext): Promise<AFSEntry | undefined>;
|
|
95
|
+
handleStat(ctx: RouteContext): Promise<AFSStatResult>;
|
|
96
|
+
listConfigActions(ctx: RouteContext<{
|
|
97
|
+
name: string;
|
|
98
|
+
}>): Promise<AFSListResult>;
|
|
99
|
+
execRotate(ctx: RouteContext<{
|
|
100
|
+
name: string;
|
|
101
|
+
action: string;
|
|
102
|
+
}>): Promise<{
|
|
103
|
+
success: boolean;
|
|
104
|
+
data?: unknown;
|
|
105
|
+
}>;
|
|
106
|
+
listRootActions(): Promise<AFSListResult>;
|
|
107
|
+
execRotateAll(): Promise<{
|
|
108
|
+
success: boolean;
|
|
109
|
+
data?: unknown;
|
|
110
|
+
}>;
|
|
111
|
+
handleExplain(ctx: RouteContext): Promise<AFSExplainResult>;
|
|
112
|
+
}
|
|
113
|
+
//#endregion
|
|
114
|
+
export { AFSRotation, AFSRotationOptions, RotationConfig, RotationStatus, VaultAccess };
|
|
115
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;UAsCiB,cAAA;EAQf;EANA,IAAA;EAUA;EARA,gBAAA;EAYA;EAVA,eAAA;EAYA;EAVA,aAAA;EAUkB;EARlB,QAAA;EAW6B;EAT7B,gBAAA;EAS6B;EAP7B,SAAA;EASA;EAPA,WAAA,GAAc,MAAA;EASd;EAPA,kBAAA;AAAA;AAAA,UAGe,cAAA;EACf,UAAA;EACA,YAAA;EACA,UAAA;EACA,SAAA;EACA,aAAA;AAAA;;UAIe,WAAA;EACf,SAAA,CAAU,KAAA,UAAe,IAAA,WAAe,OAAA;EACxC,SAAA,CAAU,KAAA,UAAe,IAAA,UAAc,KAAA,WAAgB,OAAA;AAAA;AAAA,UAGxC,kBAAA;EACf,IAAA;EACA,WAAA;EACA,UAAA,GAAa,aAAA;EAHE;EAKf,KAAA,EAAO,WAAA;;EAEP,OAAA,EAAS,cAAA;EAFF;EAIP,OAAA,IAAW,KAAA,EAAO,WAAA,GAAc,GAAA,EAAK,IAAA,GAAO,WAAA,KAAgB,OAAA,CAAQ,QAAA;AAAA;AAAA,cAkCzD,WAAA,SAAoB,eAAA;EAAA,SACtB,IAAA;EAAA,SACA,WAAA;EAAA,SACA,UAAA,EAAY,aAAA;EAAA,QAEb,KAAA;EAAA,QACA,OAAA;EAAA,QACA,QAAA;EAAA,QACA,OAAA;cAEI,OAAA,EAAS,kBAAA;EAAA,OAuBd,gBAAA,CAAA,GAAoB,MAAA,SAAe,eAAA;EAAA,OAanC,MAAA,CAAA,GAAM,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;SAIN,UAAA,CAAA,GAAc,kBAAA;EAAA,OAsBd,QAAA,CAAA,GAAY,gBAAA;EAAA,OAyBN,IAAA,CAAA;IAAO;EAAA,IAAU,mBAAA,GAA2B,OAAA,CAAQ,WAAA;EAAA,QAUzD,SAAA;EAAA,QAMA,SAAA;EAAA,QAYA,SAAA;EAAA,QAKA,gBAAA;EAAA,QAcA,gBAAA;EAAA,QAaA,cAAA;EAYF,MAAA,CAAO,UAAA,WAAqB,OAAA,CAAQ,cAAA;EAqFpC,SAAA,CAAA,GAAa,OAAA,CAAQ,MAAA,SAAe,cAAA;EAepC,UAAA,CAAW,GAAA,EAAK,YAAA,GAAe,OAAA,CAAQ,QAAA;EA4BvC,UAAA,CAAW,GAAA,EAAK,YAAA,GAAe,OAAA,CAAQ,aAAA;EAkBvC,sBAAA,CAAA,GAA0B,OAAA,CAAQ,QAAA;EAiBlC,UAAA,CAAW,GAAA,EAAK,YAAA,GAAe,OAAA,CAAQ,QAAA;EAgBvC,UAAA,CAAW,GAAA,EAAK,YAAA,GAAe,OAAA,CAAQ,aAAA;EAkBvC,iBAAA,CAAkB,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAkB,OAAA,CAAQ,aAAA;EAmBhE,UAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;IAAc,MAAA;EAAA,KACjC,OAAA;IAAU,OAAA;IAAkB,IAAA;EAAA;EASzB,eAAA,CAAA,GAAmB,OAAA,CAAQ,aAAA;EAiB3B,aAAA,CAAA,GAAiB,OAAA;IAAU,OAAA;IAAkB,IAAA;EAAA;EAS7C,aAAA,CAAc,GAAA,EAAK,YAAA,GAAe,OAAA,CAAQ,gBAAA;AAAA"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { AFSAccessMode, AFSEntry, AFSExplainResult, AFSListResult, AFSModuleLoadParams, AFSStatResult, ProviderManifest, ProviderTreeSchema, SecurityProfile } from "@aigne/afs";
|
|
2
|
+
import { AFSBaseProvider, RouteContext } from "@aigne/afs/provider";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
//#region src/index.d.ts
|
|
6
|
+
interface RotationConfig {
|
|
7
|
+
/** Config name (used as path segment). */
|
|
8
|
+
name: string;
|
|
9
|
+
/** Vault secret path: "group/name" for the refresh token. */
|
|
10
|
+
refreshTokenPath: string;
|
|
11
|
+
/** Vault secret path: "group/name" for the access token to update. */
|
|
12
|
+
accessTokenPath: string;
|
|
13
|
+
/** OAuth2 token endpoint URL. */
|
|
14
|
+
tokenEndpoint: string;
|
|
15
|
+
/** OAuth2 client ID. */
|
|
16
|
+
clientId: string;
|
|
17
|
+
/** Vault secret path for client secret (confidential clients). */
|
|
18
|
+
clientSecretPath?: string;
|
|
19
|
+
/** OAuth2 grant type. Default: "refresh_token". */
|
|
20
|
+
grantType?: string;
|
|
21
|
+
/** Additional parameters to include in the token request. */
|
|
22
|
+
extraParams?: Record<string, string>;
|
|
23
|
+
/** Optional: also write the new refresh token if returned. */
|
|
24
|
+
rotateRefreshToken?: boolean;
|
|
25
|
+
}
|
|
26
|
+
interface RotationStatus {
|
|
27
|
+
configName: string;
|
|
28
|
+
lastRotation: number | null;
|
|
29
|
+
lastResult: "success" | "error" | null;
|
|
30
|
+
lastError: string | null;
|
|
31
|
+
rotationCount: number;
|
|
32
|
+
}
|
|
33
|
+
/** Interface for vault access — decoupled from AFSVault class. */
|
|
34
|
+
interface VaultAccess {
|
|
35
|
+
getSecret(group: string, name: string): Promise<string | undefined>;
|
|
36
|
+
setSecret(group: string, name: string, value: string): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
interface AFSRotationOptions {
|
|
39
|
+
name?: string;
|
|
40
|
+
description?: string;
|
|
41
|
+
accessMode?: AFSAccessMode;
|
|
42
|
+
/** Vault access for reading/writing secrets. */
|
|
43
|
+
vault: VaultAccess;
|
|
44
|
+
/** Rotation configurations. */
|
|
45
|
+
configs: RotationConfig[];
|
|
46
|
+
/** Custom fetch function (for testing). */
|
|
47
|
+
fetchFn?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
48
|
+
}
|
|
49
|
+
declare class AFSRotation extends AFSBaseProvider {
|
|
50
|
+
readonly name: string;
|
|
51
|
+
readonly description: string;
|
|
52
|
+
readonly accessMode: AFSAccessMode;
|
|
53
|
+
private vault;
|
|
54
|
+
private configs;
|
|
55
|
+
private statuses;
|
|
56
|
+
private fetchFn;
|
|
57
|
+
constructor(options: AFSRotationOptions);
|
|
58
|
+
static securityProfiles(): Record<string, SecurityProfile>;
|
|
59
|
+
static schema(): z.ZodObject<{
|
|
60
|
+
name: z.ZodOptional<z.ZodString>;
|
|
61
|
+
description: z.ZodOptional<z.ZodString>;
|
|
62
|
+
accessMode: z.ZodOptional<z.ZodEnum<{
|
|
63
|
+
readonly: "readonly";
|
|
64
|
+
readwrite: "readwrite";
|
|
65
|
+
}>>;
|
|
66
|
+
configs: z.ZodArray<z.ZodObject<{
|
|
67
|
+
name: z.ZodString;
|
|
68
|
+
refreshTokenPath: z.ZodString;
|
|
69
|
+
accessTokenPath: z.ZodString;
|
|
70
|
+
tokenEndpoint: z.ZodString;
|
|
71
|
+
clientId: z.ZodString;
|
|
72
|
+
clientSecretPath: z.ZodOptional<z.ZodString>;
|
|
73
|
+
grantType: z.ZodOptional<z.ZodString>;
|
|
74
|
+
extraParams: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
75
|
+
rotateRefreshToken: z.ZodOptional<z.ZodBoolean>;
|
|
76
|
+
}, z.core.$strip>>;
|
|
77
|
+
}, z.core.$strip>;
|
|
78
|
+
static treeSchema(): ProviderTreeSchema;
|
|
79
|
+
static manifest(): ProviderManifest;
|
|
80
|
+
static load({
|
|
81
|
+
config
|
|
82
|
+
}?: AFSModuleLoadParams): Promise<AFSRotation>;
|
|
83
|
+
private getConfig;
|
|
84
|
+
private getStatus;
|
|
85
|
+
private parsePath;
|
|
86
|
+
private buildConfigEntry;
|
|
87
|
+
private buildStatusEntry;
|
|
88
|
+
private buildRootEntry;
|
|
89
|
+
rotate(configName: string): Promise<RotationStatus>;
|
|
90
|
+
rotateAll(): Promise<Record<string, RotationStatus>>;
|
|
91
|
+
handleMeta(ctx: RouteContext): Promise<AFSEntry | undefined>;
|
|
92
|
+
handleList(ctx: RouteContext): Promise<AFSListResult>;
|
|
93
|
+
handleReadCapabilities(): Promise<AFSEntry | undefined>;
|
|
94
|
+
handleRead(ctx: RouteContext): Promise<AFSEntry | undefined>;
|
|
95
|
+
handleStat(ctx: RouteContext): Promise<AFSStatResult>;
|
|
96
|
+
listConfigActions(ctx: RouteContext<{
|
|
97
|
+
name: string;
|
|
98
|
+
}>): Promise<AFSListResult>;
|
|
99
|
+
execRotate(ctx: RouteContext<{
|
|
100
|
+
name: string;
|
|
101
|
+
action: string;
|
|
102
|
+
}>): Promise<{
|
|
103
|
+
success: boolean;
|
|
104
|
+
data?: unknown;
|
|
105
|
+
}>;
|
|
106
|
+
listRootActions(): Promise<AFSListResult>;
|
|
107
|
+
execRotateAll(): Promise<{
|
|
108
|
+
success: boolean;
|
|
109
|
+
data?: unknown;
|
|
110
|
+
}>;
|
|
111
|
+
handleExplain(ctx: RouteContext): Promise<AFSExplainResult>;
|
|
112
|
+
}
|
|
113
|
+
//#endregion
|
|
114
|
+
export { AFSRotation, AFSRotationOptions, RotationConfig, RotationStatus, VaultAccess };
|
|
115
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;UAsCiB,cAAA;EAQf;EANA,IAAA;EAUA;EARA,gBAAA;EAYA;EAVA,eAAA;EAYA;EAVA,aAAA;EAUkB;EARlB,QAAA;EAW6B;EAT7B,gBAAA;EAS6B;EAP7B,SAAA;EASA;EAPA,WAAA,GAAc,MAAA;EASd;EAPA,kBAAA;AAAA;AAAA,UAGe,cAAA;EACf,UAAA;EACA,YAAA;EACA,UAAA;EACA,SAAA;EACA,aAAA;AAAA;;UAIe,WAAA;EACf,SAAA,CAAU,KAAA,UAAe,IAAA,WAAe,OAAA;EACxC,SAAA,CAAU,KAAA,UAAe,IAAA,UAAc,KAAA,WAAgB,OAAA;AAAA;AAAA,UAGxC,kBAAA;EACf,IAAA;EACA,WAAA;EACA,UAAA,GAAa,aAAA;EAHE;EAKf,KAAA,EAAO,WAAA;;EAEP,OAAA,EAAS,cAAA;EAFF;EAIP,OAAA,IAAW,KAAA,EAAO,WAAA,GAAc,GAAA,EAAK,IAAA,GAAO,WAAA,KAAgB,OAAA,CAAQ,QAAA;AAAA;AAAA,cAkCzD,WAAA,SAAoB,eAAA;EAAA,SACtB,IAAA;EAAA,SACA,WAAA;EAAA,SACA,UAAA,EAAY,aAAA;EAAA,QAEb,KAAA;EAAA,QACA,OAAA;EAAA,QACA,QAAA;EAAA,QACA,OAAA;cAEI,OAAA,EAAS,kBAAA;EAAA,OAuBd,gBAAA,CAAA,GAAoB,MAAA,SAAe,eAAA;EAAA,OAanC,MAAA,CAAA,GAAM,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;SAIN,UAAA,CAAA,GAAc,kBAAA;EAAA,OAsBd,QAAA,CAAA,GAAY,gBAAA;EAAA,OAyBN,IAAA,CAAA;IAAO;EAAA,IAAU,mBAAA,GAA2B,OAAA,CAAQ,WAAA;EAAA,QAUzD,SAAA;EAAA,QAMA,SAAA;EAAA,QAYA,SAAA;EAAA,QAKA,gBAAA;EAAA,QAcA,gBAAA;EAAA,QAaA,cAAA;EAYF,MAAA,CAAO,UAAA,WAAqB,OAAA,CAAQ,cAAA;EAqFpC,SAAA,CAAA,GAAa,OAAA,CAAQ,MAAA,SAAe,cAAA;EAepC,UAAA,CAAW,GAAA,EAAK,YAAA,GAAe,OAAA,CAAQ,QAAA;EA4BvC,UAAA,CAAW,GAAA,EAAK,YAAA,GAAe,OAAA,CAAQ,aAAA;EAkBvC,sBAAA,CAAA,GAA0B,OAAA,CAAQ,QAAA;EAiBlC,UAAA,CAAW,GAAA,EAAK,YAAA,GAAe,OAAA,CAAQ,QAAA;EAgBvC,UAAA,CAAW,GAAA,EAAK,YAAA,GAAe,OAAA,CAAQ,aAAA;EAkBvC,iBAAA,CAAkB,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAkB,OAAA,CAAQ,aAAA;EAmBhE,UAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;IAAc,MAAA;EAAA,KACjC,OAAA;IAAU,OAAA;IAAkB,IAAA;EAAA;EASzB,eAAA,CAAA,GAAmB,OAAA,CAAQ,aAAA;EAiB3B,aAAA,CAAA,GAAiB,OAAA;IAAU,OAAA;IAAkB,IAAA;EAAA;EAS7C,aAAA,CAAc,GAAA,EAAK,YAAA,GAAe,OAAA,CAAQ,gBAAA;AAAA"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
import { __decorate } from "./_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs";
|
|
2
|
+
import { AFSNotFoundError } from "@aigne/afs";
|
|
3
|
+
import { AFSBaseProvider, Actions, Explain, List, Meta, Read, Stat } from "@aigne/afs/provider";
|
|
4
|
+
import { joinURL } from "ufo";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
|
|
7
|
+
//#region src/index.ts
|
|
8
|
+
/**
|
|
9
|
+
* AFS Rotation Provider — OAuth2 token rotation with vault integration.
|
|
10
|
+
*
|
|
11
|
+
* Tree: /rotation/{config-name} → rotation config and status
|
|
12
|
+
* Actions: rotate (per config), rotate-all (root)
|
|
13
|
+
* Reads refresh tokens from vault, calls OAuth2 token endpoint, writes new tokens back.
|
|
14
|
+
* No scheduler — on-demand only. Scheduling is external infrastructure.
|
|
15
|
+
*/
|
|
16
|
+
const rotationConfigSchema = z.object({
|
|
17
|
+
name: z.string(),
|
|
18
|
+
refreshTokenPath: z.string(),
|
|
19
|
+
accessTokenPath: z.string(),
|
|
20
|
+
tokenEndpoint: z.string().url(),
|
|
21
|
+
clientId: z.string(),
|
|
22
|
+
clientSecretPath: z.string().optional(),
|
|
23
|
+
grantType: z.string().optional(),
|
|
24
|
+
extraParams: z.record(z.string(), z.string()).optional(),
|
|
25
|
+
rotateRefreshToken: z.boolean().optional()
|
|
26
|
+
});
|
|
27
|
+
const afsRotationOptionsSchema = z.object({
|
|
28
|
+
name: z.string().optional().describe("Module name"),
|
|
29
|
+
description: z.string().optional().describe("Module description"),
|
|
30
|
+
accessMode: z.enum(["readonly", "readwrite"]).optional().describe("Access mode"),
|
|
31
|
+
configs: z.array(rotationConfigSchema).describe("Rotation configurations")
|
|
32
|
+
});
|
|
33
|
+
function parseVaultPath(vaultPath) {
|
|
34
|
+
const parts = vaultPath.split("/").filter(Boolean);
|
|
35
|
+
if (parts.length !== 2) throw new Error(`Invalid vault path "${vaultPath}" — expected "group/name"`);
|
|
36
|
+
return {
|
|
37
|
+
group: parts[0],
|
|
38
|
+
name: parts[1]
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
var AFSRotation = class extends AFSBaseProvider {
|
|
42
|
+
name;
|
|
43
|
+
description;
|
|
44
|
+
accessMode;
|
|
45
|
+
vault;
|
|
46
|
+
configs;
|
|
47
|
+
statuses;
|
|
48
|
+
fetchFn;
|
|
49
|
+
constructor(options) {
|
|
50
|
+
super();
|
|
51
|
+
this.name = options.name || "rotation";
|
|
52
|
+
this.description = options.description || "OAuth2 token rotation";
|
|
53
|
+
this.accessMode = options.accessMode || "readwrite";
|
|
54
|
+
this.vault = options.vault;
|
|
55
|
+
this.fetchFn = options.fetchFn || globalThis.fetch;
|
|
56
|
+
this.configs = /* @__PURE__ */ new Map();
|
|
57
|
+
this.statuses = /* @__PURE__ */ new Map();
|
|
58
|
+
for (const cfg of options.configs) {
|
|
59
|
+
rotationConfigSchema.parse(cfg);
|
|
60
|
+
this.configs.set(cfg.name, cfg);
|
|
61
|
+
this.statuses.set(cfg.name, {
|
|
62
|
+
configName: cfg.name,
|
|
63
|
+
lastRotation: null,
|
|
64
|
+
lastResult: null,
|
|
65
|
+
lastError: null,
|
|
66
|
+
rotationCount: 0
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
static securityProfiles() {
|
|
71
|
+
return {
|
|
72
|
+
admin: {
|
|
73
|
+
actionPolicy: "full",
|
|
74
|
+
accessMode: "readwrite"
|
|
75
|
+
},
|
|
76
|
+
system: {
|
|
77
|
+
actionPolicy: "safe",
|
|
78
|
+
accessMode: "readonly"
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
static schema() {
|
|
83
|
+
return afsRotationOptionsSchema;
|
|
84
|
+
}
|
|
85
|
+
static treeSchema() {
|
|
86
|
+
return {
|
|
87
|
+
operations: [
|
|
88
|
+
"list",
|
|
89
|
+
"read",
|
|
90
|
+
"exec",
|
|
91
|
+
"stat",
|
|
92
|
+
"explain"
|
|
93
|
+
],
|
|
94
|
+
tree: {
|
|
95
|
+
"/": {
|
|
96
|
+
kind: "rotation:root",
|
|
97
|
+
operations: [
|
|
98
|
+
"list",
|
|
99
|
+
"read",
|
|
100
|
+
"exec"
|
|
101
|
+
],
|
|
102
|
+
actions: ["rotate-all"]
|
|
103
|
+
},
|
|
104
|
+
"/{name}": {
|
|
105
|
+
kind: "rotation:config",
|
|
106
|
+
operations: [
|
|
107
|
+
"list",
|
|
108
|
+
"read",
|
|
109
|
+
"exec"
|
|
110
|
+
],
|
|
111
|
+
actions: ["rotate"]
|
|
112
|
+
},
|
|
113
|
+
"/{name}/status": {
|
|
114
|
+
kind: "rotation:status",
|
|
115
|
+
operations: ["read"]
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
auth: { type: "custom" },
|
|
119
|
+
bestFor: ["OAuth2 token rotation", "credential refresh"],
|
|
120
|
+
notFor: ["secret storage"]
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
static manifest() {
|
|
124
|
+
return {
|
|
125
|
+
name: "rotation",
|
|
126
|
+
description: "OAuth2 token rotation.\n- Reads refresh tokens from vault, calls token endpoints, writes new tokens back\n- On-demand rotation via actions\n- Security profiles: admin (readwrite), system (readonly)",
|
|
127
|
+
uriTemplate: "rotation://",
|
|
128
|
+
category: "security",
|
|
129
|
+
schema: afsRotationOptionsSchema,
|
|
130
|
+
tags: [
|
|
131
|
+
"rotation",
|
|
132
|
+
"oauth",
|
|
133
|
+
"tokens",
|
|
134
|
+
"secrets"
|
|
135
|
+
],
|
|
136
|
+
capabilityTags: [
|
|
137
|
+
"read-write",
|
|
138
|
+
"auth:none",
|
|
139
|
+
"local"
|
|
140
|
+
],
|
|
141
|
+
security: {
|
|
142
|
+
riskLevel: "external",
|
|
143
|
+
resourceAccess: ["internet"],
|
|
144
|
+
dataSensitivity: ["credentials"],
|
|
145
|
+
notes: ["Makes HTTP requests to OAuth2 token endpoints", "Reads and writes secrets in vault"]
|
|
146
|
+
},
|
|
147
|
+
capabilities: { network: { egress: true } }
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
static async load({ config } = {}) {
|
|
151
|
+
afsRotationOptionsSchema.parse(config || {});
|
|
152
|
+
throw new Error("AFSRotation requires a vault instance — use `new AFSRotation({ vault, configs })` directly");
|
|
153
|
+
}
|
|
154
|
+
getConfig(name) {
|
|
155
|
+
const cfg = this.configs.get(name);
|
|
156
|
+
if (!cfg) throw new AFSNotFoundError(joinURL("/", name));
|
|
157
|
+
return cfg;
|
|
158
|
+
}
|
|
159
|
+
getStatus(name) {
|
|
160
|
+
return this.statuses.get(name) || {
|
|
161
|
+
configName: name,
|
|
162
|
+
lastRotation: null,
|
|
163
|
+
lastResult: null,
|
|
164
|
+
lastError: null,
|
|
165
|
+
rotationCount: 0
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
parsePath(ctx) {
|
|
169
|
+
return (ctx.params?.path || "").split("/").filter(Boolean);
|
|
170
|
+
}
|
|
171
|
+
buildConfigEntry(name) {
|
|
172
|
+
const status = this.getStatus(name);
|
|
173
|
+
const cfg = this.configs.get(name);
|
|
174
|
+
return this.buildEntry(joinURL("/", name), { meta: {
|
|
175
|
+
childrenCount: 1,
|
|
176
|
+
configName: name,
|
|
177
|
+
tokenEndpoint: cfg.tokenEndpoint,
|
|
178
|
+
lastRotation: status.lastRotation,
|
|
179
|
+
lastResult: status.lastResult
|
|
180
|
+
} });
|
|
181
|
+
}
|
|
182
|
+
buildStatusEntry(name) {
|
|
183
|
+
const status = this.getStatus(name);
|
|
184
|
+
return this.buildEntry(joinURL("/", name, "status"), {
|
|
185
|
+
content: status,
|
|
186
|
+
meta: {
|
|
187
|
+
kind: "rotation:status",
|
|
188
|
+
lastRotation: status.lastRotation,
|
|
189
|
+
lastResult: status.lastResult,
|
|
190
|
+
rotationCount: status.rotationCount
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
buildRootEntry() {
|
|
195
|
+
return this.buildEntry("/", { meta: {
|
|
196
|
+
childrenCount: this.configs.size,
|
|
197
|
+
provider: "rotation",
|
|
198
|
+
configCount: this.configs.size
|
|
199
|
+
} });
|
|
200
|
+
}
|
|
201
|
+
async rotate(configName) {
|
|
202
|
+
const cfg = this.getConfig(configName);
|
|
203
|
+
const status = this.getStatus(configName);
|
|
204
|
+
try {
|
|
205
|
+
const refreshRef = parseVaultPath(cfg.refreshTokenPath);
|
|
206
|
+
const refreshToken = await this.vault.getSecret(refreshRef.group, refreshRef.name);
|
|
207
|
+
if (!refreshToken) throw new Error(`Refresh token not found at vault path: ${cfg.refreshTokenPath}`);
|
|
208
|
+
let clientSecret;
|
|
209
|
+
if (cfg.clientSecretPath) {
|
|
210
|
+
const secretRef = parseVaultPath(cfg.clientSecretPath);
|
|
211
|
+
clientSecret = await this.vault.getSecret(secretRef.group, secretRef.name);
|
|
212
|
+
if (!clientSecret) throw new Error(`Client secret not found at vault path: ${cfg.clientSecretPath}`);
|
|
213
|
+
}
|
|
214
|
+
const params = new URLSearchParams();
|
|
215
|
+
params.set("grant_type", cfg.grantType || "refresh_token");
|
|
216
|
+
params.set("refresh_token", refreshToken);
|
|
217
|
+
params.set("client_id", cfg.clientId);
|
|
218
|
+
if (clientSecret) params.set("client_secret", clientSecret);
|
|
219
|
+
if (cfg.extraParams) for (const [k, v] of Object.entries(cfg.extraParams)) params.set(k, v);
|
|
220
|
+
const response = await this.fetchFn(cfg.tokenEndpoint, {
|
|
221
|
+
method: "POST",
|
|
222
|
+
headers: {
|
|
223
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
224
|
+
Accept: "application/json"
|
|
225
|
+
},
|
|
226
|
+
body: params.toString()
|
|
227
|
+
});
|
|
228
|
+
if (!response.ok) {
|
|
229
|
+
const body = await response.text();
|
|
230
|
+
throw new Error(`Token endpoint returned ${response.status}: ${body}`);
|
|
231
|
+
}
|
|
232
|
+
const tokenResponse = await response.json();
|
|
233
|
+
const accessToken = tokenResponse.access_token;
|
|
234
|
+
if (!accessToken) throw new Error("Token response missing access_token field");
|
|
235
|
+
const accessRef = parseVaultPath(cfg.accessTokenPath);
|
|
236
|
+
await this.vault.setSecret(accessRef.group, accessRef.name, accessToken);
|
|
237
|
+
if (cfg.rotateRefreshToken && tokenResponse.refresh_token) await this.vault.setSecret(refreshRef.group, refreshRef.name, tokenResponse.refresh_token);
|
|
238
|
+
status.lastRotation = Date.now();
|
|
239
|
+
status.lastResult = "success";
|
|
240
|
+
status.lastError = null;
|
|
241
|
+
status.rotationCount++;
|
|
242
|
+
this.statuses.set(configName, status);
|
|
243
|
+
return status;
|
|
244
|
+
} catch (err) {
|
|
245
|
+
status.lastRotation = Date.now();
|
|
246
|
+
status.lastResult = "error";
|
|
247
|
+
status.lastError = err instanceof Error ? err.message : String(err);
|
|
248
|
+
this.statuses.set(configName, status);
|
|
249
|
+
throw err;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
async rotateAll() {
|
|
253
|
+
const results = {};
|
|
254
|
+
for (const [name] of this.configs) try {
|
|
255
|
+
results[name] = await this.rotate(name);
|
|
256
|
+
} catch {
|
|
257
|
+
results[name] = this.getStatus(name);
|
|
258
|
+
}
|
|
259
|
+
return results;
|
|
260
|
+
}
|
|
261
|
+
async handleMeta(ctx) {
|
|
262
|
+
const parts = this.parsePath(ctx);
|
|
263
|
+
const metaPath = parts.length === 0 ? "/.meta" : joinURL("/", ...parts, ".meta");
|
|
264
|
+
if (parts.length === 0) return this.buildEntry(metaPath, { meta: {
|
|
265
|
+
childrenCount: this.configs.size,
|
|
266
|
+
provider: "rotation",
|
|
267
|
+
configCount: this.configs.size
|
|
268
|
+
} });
|
|
269
|
+
if (parts.length === 1) {
|
|
270
|
+
const name = parts[0];
|
|
271
|
+
this.getConfig(name);
|
|
272
|
+
return this.buildEntry(metaPath, { meta: {
|
|
273
|
+
childrenCount: 1,
|
|
274
|
+
configName: name
|
|
275
|
+
} });
|
|
276
|
+
}
|
|
277
|
+
if (parts.length === 2 && parts[1] === "status") {
|
|
278
|
+
const status = this.getStatus(parts[0]);
|
|
279
|
+
return this.buildEntry(metaPath, { meta: {
|
|
280
|
+
kind: "rotation:status",
|
|
281
|
+
lastResult: status.lastResult
|
|
282
|
+
} });
|
|
283
|
+
}
|
|
284
|
+
throw new AFSNotFoundError(joinURL("/", ...parts));
|
|
285
|
+
}
|
|
286
|
+
async handleList(ctx) {
|
|
287
|
+
const parts = this.parsePath(ctx);
|
|
288
|
+
if (parts.length === 0) return { data: [...this.configs.keys()].sort().map((n) => this.buildConfigEntry(n)) };
|
|
289
|
+
if (parts.length === 1) {
|
|
290
|
+
this.getConfig(parts[0]);
|
|
291
|
+
return { data: [this.buildStatusEntry(parts[0])] };
|
|
292
|
+
}
|
|
293
|
+
if (parts.length === 2 && parts[1] === "status") return { data: [] };
|
|
294
|
+
throw new AFSNotFoundError(joinURL("/", ...parts));
|
|
295
|
+
}
|
|
296
|
+
async handleReadCapabilities() {
|
|
297
|
+
const operations = this.getOperationsDeclaration();
|
|
298
|
+
const manifest = {
|
|
299
|
+
schemaVersion: 1,
|
|
300
|
+
provider: this.name,
|
|
301
|
+
description: this.description,
|
|
302
|
+
tools: [],
|
|
303
|
+
actions: [],
|
|
304
|
+
operations
|
|
305
|
+
};
|
|
306
|
+
return this.buildEntry("/.meta/.capabilities", {
|
|
307
|
+
content: manifest,
|
|
308
|
+
meta: {
|
|
309
|
+
kind: "afs:capabilities",
|
|
310
|
+
operations
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
async handleRead(ctx) {
|
|
315
|
+
const parts = this.parsePath(ctx);
|
|
316
|
+
if (parts.length === 0) return this.buildRootEntry();
|
|
317
|
+
if (parts.length === 1) {
|
|
318
|
+
const name = parts[0];
|
|
319
|
+
this.getConfig(name);
|
|
320
|
+
return this.buildConfigEntry(name);
|
|
321
|
+
}
|
|
322
|
+
if (parts.length === 2 && parts[1] === "status") return this.buildStatusEntry(parts[0]);
|
|
323
|
+
throw new AFSNotFoundError(joinURL("/", ...parts));
|
|
324
|
+
}
|
|
325
|
+
async handleStat(ctx) {
|
|
326
|
+
const parts = this.parsePath(ctx);
|
|
327
|
+
if (parts.length === 0) return { data: this.buildRootEntry() };
|
|
328
|
+
if (parts.length === 1) {
|
|
329
|
+
const name = parts[0];
|
|
330
|
+
this.getConfig(name);
|
|
331
|
+
return { data: this.buildConfigEntry(name) };
|
|
332
|
+
}
|
|
333
|
+
if (parts.length === 2 && parts[1] === "status") return { data: this.buildStatusEntry(parts[0]) };
|
|
334
|
+
throw new AFSNotFoundError(joinURL("/", ...parts));
|
|
335
|
+
}
|
|
336
|
+
async listConfigActions(ctx) {
|
|
337
|
+
const name = ctx.params.name;
|
|
338
|
+
this.getConfig(name);
|
|
339
|
+
return { data: [{
|
|
340
|
+
id: "rotate",
|
|
341
|
+
path: joinURL("/", name, ".actions", "rotate"),
|
|
342
|
+
meta: {
|
|
343
|
+
kind: "action",
|
|
344
|
+
description: `Rotate tokens for ${name}`,
|
|
345
|
+
childrenCount: 0
|
|
346
|
+
}
|
|
347
|
+
}] };
|
|
348
|
+
}
|
|
349
|
+
async execRotate(ctx) {
|
|
350
|
+
const name = ctx.params.name;
|
|
351
|
+
return {
|
|
352
|
+
success: true,
|
|
353
|
+
data: await this.rotate(name)
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
async listRootActions() {
|
|
357
|
+
return { data: [{
|
|
358
|
+
id: "rotate-all",
|
|
359
|
+
path: joinURL("/", ".actions", "rotate-all"),
|
|
360
|
+
meta: {
|
|
361
|
+
kind: "action",
|
|
362
|
+
description: "Rotate tokens for all configurations",
|
|
363
|
+
childrenCount: 0
|
|
364
|
+
}
|
|
365
|
+
}] };
|
|
366
|
+
}
|
|
367
|
+
async execRotateAll() {
|
|
368
|
+
const results = await this.rotateAll();
|
|
369
|
+
return {
|
|
370
|
+
success: Object.values(results).every((s) => s.lastResult === "success"),
|
|
371
|
+
data: results
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
async handleExplain(ctx) {
|
|
375
|
+
const parts = this.parsePath(ctx);
|
|
376
|
+
if (parts.length >= 1) {
|
|
377
|
+
const name = parts[0];
|
|
378
|
+
const cfg = this.configs.get(name);
|
|
379
|
+
if (cfg) {
|
|
380
|
+
const status = this.getStatus(name);
|
|
381
|
+
return {
|
|
382
|
+
format: "text",
|
|
383
|
+
content: `Rotation config "${name}".\nToken endpoint: ${cfg.tokenEndpoint}\nClient ID: ${cfg.clientId}\nLast rotation: ${status.lastRotation ? new Date(status.lastRotation).toISOString() : "never"}\nStatus: ${status.lastResult || "pending"}`
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return {
|
|
388
|
+
format: "text",
|
|
389
|
+
content: `AFS Rotation — OAuth2 token rotation. ${this.configs.size} config(s). Use actions to trigger rotation.`
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
__decorate([Meta("/:path*")], AFSRotation.prototype, "handleMeta", null);
|
|
394
|
+
__decorate([List("/:path*")], AFSRotation.prototype, "handleList", null);
|
|
395
|
+
__decorate([Read("/.meta/.capabilities")], AFSRotation.prototype, "handleReadCapabilities", null);
|
|
396
|
+
__decorate([Read("/:path*")], AFSRotation.prototype, "handleRead", null);
|
|
397
|
+
__decorate([Stat("/:path*")], AFSRotation.prototype, "handleStat", null);
|
|
398
|
+
__decorate([Actions("/:name")], AFSRotation.prototype, "listConfigActions", null);
|
|
399
|
+
__decorate([Actions.Exec("/:name", "rotate", "Rotate tokens for this config")], AFSRotation.prototype, "execRotate", null);
|
|
400
|
+
__decorate([Actions("/")], AFSRotation.prototype, "listRootActions", null);
|
|
401
|
+
__decorate([Actions.Exec("/", "rotate-all", "Rotate all configured tokens")], AFSRotation.prototype, "execRotateAll", null);
|
|
402
|
+
__decorate([Explain("/:path*")], AFSRotation.prototype, "handleExplain", null);
|
|
403
|
+
AFSRotation.load = AFSRotation.load;
|
|
404
|
+
|
|
405
|
+
//#endregion
|
|
406
|
+
export { AFSRotation };
|
|
407
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * AFS Rotation Provider — OAuth2 token rotation with vault integration.\n *\n * Tree: /rotation/{config-name} → rotation config and status\n * Actions: rotate (per config), rotate-all (root)\n * Reads refresh tokens from vault, calls OAuth2 token endpoint, writes new tokens back.\n * No scheduler — on-demand only. Scheduling is external infrastructure.\n */\n\nimport {\n type AFSAccessMode,\n type AFSEntry,\n type AFSExplainResult,\n type AFSListResult,\n type AFSModuleClass,\n type AFSModuleLoadParams,\n AFSNotFoundError,\n type AFSStatResult,\n type CapabilitiesManifest,\n type ProviderManifest,\n type ProviderTreeSchema,\n type SecurityProfile,\n} from \"@aigne/afs\";\nimport {\n Actions,\n AFSBaseProvider,\n Explain,\n List,\n Meta,\n Read,\n type RouteContext,\n Stat,\n} from \"@aigne/afs/provider\";\nimport { joinURL } from \"ufo\";\nimport { z } from \"zod\";\n\n// ─── Types ───────────────────────────────────────────────────────────\n\nexport interface RotationConfig {\n /** Config name (used as path segment). */\n name: string;\n /** Vault secret path: \"group/name\" for the refresh token. */\n refreshTokenPath: string;\n /** Vault secret path: \"group/name\" for the access token to update. */\n accessTokenPath: string;\n /** OAuth2 token endpoint URL. */\n tokenEndpoint: string;\n /** OAuth2 client ID. */\n clientId: string;\n /** Vault secret path for client secret (confidential clients). */\n clientSecretPath?: string;\n /** OAuth2 grant type. Default: \"refresh_token\". */\n grantType?: string;\n /** Additional parameters to include in the token request. */\n extraParams?: Record<string, string>;\n /** Optional: also write the new refresh token if returned. */\n rotateRefreshToken?: boolean;\n}\n\nexport interface RotationStatus {\n configName: string;\n lastRotation: number | null;\n lastResult: \"success\" | \"error\" | null;\n lastError: string | null;\n rotationCount: number;\n}\n\n/** Interface for vault access — decoupled from AFSVault class. */\nexport interface VaultAccess {\n getSecret(group: string, name: string): Promise<string | undefined>;\n setSecret(group: string, name: string, value: string): Promise<void>;\n}\n\nexport interface AFSRotationOptions {\n name?: string;\n description?: string;\n accessMode?: AFSAccessMode;\n /** Vault access for reading/writing secrets. */\n vault: VaultAccess;\n /** Rotation configurations. */\n configs: RotationConfig[];\n /** Custom fetch function (for testing). */\n fetchFn?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\n}\n\nconst rotationConfigSchema = z.object({\n name: z.string(),\n refreshTokenPath: z.string(),\n accessTokenPath: z.string(),\n tokenEndpoint: z.string().url(),\n clientId: z.string(),\n clientSecretPath: z.string().optional(),\n grantType: z.string().optional(),\n extraParams: z.record(z.string(), z.string()).optional(),\n rotateRefreshToken: z.boolean().optional(),\n});\n\nconst afsRotationOptionsSchema = z.object({\n name: z.string().optional().describe(\"Module name\"),\n description: z.string().optional().describe(\"Module description\"),\n accessMode: z.enum([\"readonly\", \"readwrite\"]).optional().describe(\"Access mode\"),\n configs: z.array(rotationConfigSchema).describe(\"Rotation configurations\"),\n});\n\n// ─── Helper ──────────────────────────────────────────────────────────\n\nfunction parseVaultPath(vaultPath: string): { group: string; name: string } {\n const parts = vaultPath.split(\"/\").filter(Boolean);\n if (parts.length !== 2) {\n throw new Error(`Invalid vault path \"${vaultPath}\" — expected \"group/name\"`);\n }\n return { group: parts[0]!, name: parts[1]! };\n}\n\n// ─── Provider ────────────────────────────────────────────────────────\n\nexport class AFSRotation extends AFSBaseProvider {\n readonly name: string;\n readonly description: string;\n readonly accessMode: AFSAccessMode;\n\n private vault: VaultAccess;\n private configs: Map<string, RotationConfig>;\n private statuses: Map<string, RotationStatus>;\n private fetchFn: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\n\n constructor(options: AFSRotationOptions) {\n super();\n this.name = options.name || \"rotation\";\n this.description = options.description || \"OAuth2 token rotation\";\n this.accessMode = options.accessMode || \"readwrite\";\n this.vault = options.vault;\n this.fetchFn = options.fetchFn || globalThis.fetch;\n\n this.configs = new Map();\n this.statuses = new Map();\n for (const cfg of options.configs) {\n rotationConfigSchema.parse(cfg);\n this.configs.set(cfg.name, cfg);\n this.statuses.set(cfg.name, {\n configName: cfg.name,\n lastRotation: null,\n lastResult: null,\n lastError: null,\n rotationCount: 0,\n });\n }\n }\n\n static securityProfiles(): Record<string, SecurityProfile> {\n return {\n admin: {\n actionPolicy: \"full\",\n accessMode: \"readwrite\",\n },\n system: {\n actionPolicy: \"safe\",\n accessMode: \"readonly\",\n },\n };\n }\n\n static schema() {\n return afsRotationOptionsSchema;\n }\n\n static treeSchema(): ProviderTreeSchema {\n return {\n operations: [\"list\", \"read\", \"exec\", \"stat\", \"explain\"],\n tree: {\n \"/\": {\n kind: \"rotation:root\",\n operations: [\"list\", \"read\", \"exec\"],\n actions: [\"rotate-all\"],\n },\n \"/{name}\": {\n kind: \"rotation:config\",\n operations: [\"list\", \"read\", \"exec\"],\n actions: [\"rotate\"],\n },\n \"/{name}/status\": { kind: \"rotation:status\", operations: [\"read\"] },\n },\n auth: { type: \"custom\" },\n bestFor: [\"OAuth2 token rotation\", \"credential refresh\"],\n notFor: [\"secret storage\"],\n };\n }\n\n static manifest(): ProviderManifest {\n return {\n name: \"rotation\",\n description:\n \"OAuth2 token rotation.\\n- Reads refresh tokens from vault, calls token endpoints, writes new tokens back\\n- On-demand rotation via actions\\n- Security profiles: admin (readwrite), system (readonly)\",\n uriTemplate: \"rotation://\",\n category: \"security\",\n schema: afsRotationOptionsSchema,\n tags: [\"rotation\", \"oauth\", \"tokens\", \"secrets\"],\n capabilityTags: [\"read-write\", \"auth:none\", \"local\"],\n security: {\n riskLevel: \"external\",\n resourceAccess: [\"internet\"],\n dataSensitivity: [\"credentials\"],\n notes: [\n \"Makes HTTP requests to OAuth2 token endpoints\",\n \"Reads and writes secrets in vault\",\n ],\n },\n capabilities: {\n network: { egress: true },\n },\n };\n }\n\n static async load({ config }: AFSModuleLoadParams = {}): Promise<AFSRotation> {\n const _parsed = afsRotationOptionsSchema.parse(config || {});\n // Vault must be injected — cannot be loaded from config alone\n throw new Error(\n \"AFSRotation requires a vault instance — use `new AFSRotation({ vault, configs })` directly\",\n );\n }\n\n // ─── Helpers ─────────────────────────────────────────────────────\n\n private getConfig(name: string): RotationConfig {\n const cfg = this.configs.get(name);\n if (!cfg) throw new AFSNotFoundError(joinURL(\"/\", name));\n return cfg;\n }\n\n private getStatus(name: string): RotationStatus {\n return (\n this.statuses.get(name) || {\n configName: name,\n lastRotation: null,\n lastResult: null,\n lastError: null,\n rotationCount: 0,\n }\n );\n }\n\n private parsePath(ctx: RouteContext): string[] {\n const pathStr = (ctx.params as Record<string, string | undefined>)?.path || \"\";\n return pathStr.split(\"/\").filter(Boolean);\n }\n\n private buildConfigEntry(name: string): AFSEntry {\n const status = this.getStatus(name);\n const cfg = this.configs.get(name)!;\n return this.buildEntry(joinURL(\"/\", name), {\n meta: {\n childrenCount: 1,\n configName: name,\n tokenEndpoint: cfg.tokenEndpoint,\n lastRotation: status.lastRotation,\n lastResult: status.lastResult,\n },\n });\n }\n\n private buildStatusEntry(name: string): AFSEntry {\n const status = this.getStatus(name);\n return this.buildEntry(joinURL(\"/\", name, \"status\"), {\n content: status,\n meta: {\n kind: \"rotation:status\",\n lastRotation: status.lastRotation,\n lastResult: status.lastResult,\n rotationCount: status.rotationCount,\n },\n });\n }\n\n private buildRootEntry(): AFSEntry {\n return this.buildEntry(\"/\", {\n meta: {\n childrenCount: this.configs.size,\n provider: \"rotation\",\n configCount: this.configs.size,\n },\n });\n }\n\n // ─── Core Rotation Logic ─────────────────────────────────────────\n\n async rotate(configName: string): Promise<RotationStatus> {\n const cfg = this.getConfig(configName);\n const status = this.getStatus(configName);\n\n try {\n // 1. Read refresh token from vault\n const refreshRef = parseVaultPath(cfg.refreshTokenPath);\n const refreshToken = await this.vault.getSecret(refreshRef.group, refreshRef.name);\n if (!refreshToken) {\n throw new Error(`Refresh token not found at vault path: ${cfg.refreshTokenPath}`);\n }\n\n // 2. Read client secret if configured\n let clientSecret: string | undefined;\n if (cfg.clientSecretPath) {\n const secretRef = parseVaultPath(cfg.clientSecretPath);\n clientSecret = await this.vault.getSecret(secretRef.group, secretRef.name);\n if (!clientSecret) {\n throw new Error(`Client secret not found at vault path: ${cfg.clientSecretPath}`);\n }\n }\n\n // 3. Build token request\n const params = new URLSearchParams();\n params.set(\"grant_type\", cfg.grantType || \"refresh_token\");\n params.set(\"refresh_token\", refreshToken);\n params.set(\"client_id\", cfg.clientId);\n if (clientSecret) params.set(\"client_secret\", clientSecret);\n if (cfg.extraParams) {\n for (const [k, v] of Object.entries(cfg.extraParams)) {\n params.set(k, v);\n }\n }\n\n // 4. Call token endpoint\n const response = await this.fetchFn(cfg.tokenEndpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n Accept: \"application/json\",\n },\n body: params.toString(),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Token endpoint returned ${response.status}: ${body}`);\n }\n\n const tokenResponse = (await response.json()) as Record<string, unknown>;\n const accessToken = tokenResponse.access_token as string | undefined;\n if (!accessToken) {\n throw new Error(\"Token response missing access_token field\");\n }\n\n // 5. Write new access token to vault\n const accessRef = parseVaultPath(cfg.accessTokenPath);\n await this.vault.setSecret(accessRef.group, accessRef.name, accessToken);\n\n // 6. Optionally rotate refresh token too\n if (cfg.rotateRefreshToken && tokenResponse.refresh_token) {\n await this.vault.setSecret(\n refreshRef.group,\n refreshRef.name,\n tokenResponse.refresh_token as string,\n );\n }\n\n // 7. Update status\n status.lastRotation = Date.now();\n status.lastResult = \"success\";\n status.lastError = null;\n status.rotationCount++;\n this.statuses.set(configName, status);\n\n return status;\n } catch (err) {\n status.lastRotation = Date.now();\n status.lastResult = \"error\";\n status.lastError = err instanceof Error ? err.message : String(err);\n this.statuses.set(configName, status);\n throw err;\n }\n }\n\n async rotateAll(): Promise<Record<string, RotationStatus>> {\n const results: Record<string, RotationStatus> = {};\n for (const [name] of this.configs) {\n try {\n results[name] = await this.rotate(name);\n } catch {\n results[name] = this.getStatus(name);\n }\n }\n return results;\n }\n\n // ─── Route Handlers ───────────────────────────────────────────────\n\n @Meta(\"/:path*\")\n async handleMeta(ctx: RouteContext): Promise<AFSEntry | undefined> {\n const parts = this.parsePath(ctx);\n const metaPath = parts.length === 0 ? \"/.meta\" : joinURL(\"/\", ...parts, \".meta\");\n\n if (parts.length === 0) {\n return this.buildEntry(metaPath, {\n meta: {\n childrenCount: this.configs.size,\n provider: \"rotation\",\n configCount: this.configs.size,\n },\n });\n }\n if (parts.length === 1) {\n const name = parts[0]!;\n this.getConfig(name);\n return this.buildEntry(metaPath, { meta: { childrenCount: 1, configName: name } });\n }\n if (parts.length === 2 && parts[1] === \"status\") {\n const status = this.getStatus(parts[0]!);\n return this.buildEntry(metaPath, {\n meta: { kind: \"rotation:status\", lastResult: status.lastResult },\n });\n }\n throw new AFSNotFoundError(joinURL(\"/\", ...parts));\n }\n\n @List(\"/:path*\")\n async handleList(ctx: RouteContext): Promise<AFSListResult> {\n const parts = this.parsePath(ctx);\n\n if (parts.length === 0) {\n const children = [...this.configs.keys()].sort().map((n) => this.buildConfigEntry(n));\n return { data: children };\n }\n if (parts.length === 1) {\n this.getConfig(parts[0]!);\n return { data: [this.buildStatusEntry(parts[0]!)] };\n }\n if (parts.length === 2 && parts[1] === \"status\") {\n return { data: [] }; // leaf node\n }\n throw new AFSNotFoundError(joinURL(\"/\", ...parts));\n }\n\n @Read(\"/.meta/.capabilities\")\n async handleReadCapabilities(): Promise<AFSEntry | undefined> {\n const operations = this.getOperationsDeclaration();\n const manifest: CapabilitiesManifest = {\n schemaVersion: 1,\n provider: this.name,\n description: this.description,\n tools: [],\n actions: [],\n operations,\n };\n return this.buildEntry(\"/.meta/.capabilities\", {\n content: manifest,\n meta: { kind: \"afs:capabilities\", operations },\n });\n }\n\n @Read(\"/:path*\")\n async handleRead(ctx: RouteContext): Promise<AFSEntry | undefined> {\n const parts = this.parsePath(ctx);\n\n if (parts.length === 0) return this.buildRootEntry();\n if (parts.length === 1) {\n const name = parts[0]!;\n this.getConfig(name);\n return this.buildConfigEntry(name);\n }\n if (parts.length === 2 && parts[1] === \"status\") {\n return this.buildStatusEntry(parts[0]!);\n }\n throw new AFSNotFoundError(joinURL(\"/\", ...parts));\n }\n\n @Stat(\"/:path*\")\n async handleStat(ctx: RouteContext): Promise<AFSStatResult> {\n const parts = this.parsePath(ctx);\n\n if (parts.length === 0) return { data: this.buildRootEntry() };\n if (parts.length === 1) {\n const name = parts[0]!;\n this.getConfig(name);\n return { data: this.buildConfigEntry(name) };\n }\n if (parts.length === 2 && parts[1] === \"status\") {\n return { data: this.buildStatusEntry(parts[0]!) };\n }\n throw new AFSNotFoundError(joinURL(\"/\", ...parts));\n }\n\n // ── Actions: per-config rotate ────────────────────────────────\n\n @Actions(\"/:name\")\n async listConfigActions(ctx: RouteContext<{ name: string }>): Promise<AFSListResult> {\n const name = ctx.params.name;\n this.getConfig(name);\n return {\n data: [\n {\n id: \"rotate\",\n path: joinURL(\"/\", name, \".actions\", \"rotate\"),\n meta: {\n kind: \"action\",\n description: `Rotate tokens for ${name}`,\n childrenCount: 0,\n },\n },\n ],\n };\n }\n\n @Actions.Exec(\"/:name\", \"rotate\", \"Rotate tokens for this config\")\n async execRotate(\n ctx: RouteContext<{ name: string; action: string }>,\n ): Promise<{ success: boolean; data?: unknown }> {\n const name = ctx.params.name;\n const status = await this.rotate(name);\n return { success: true, data: status };\n }\n\n // ── Actions: root rotate-all ──────────────────────────────────\n\n @Actions(\"/\")\n async listRootActions(): Promise<AFSListResult> {\n return {\n data: [\n {\n id: \"rotate-all\",\n path: joinURL(\"/\", \".actions\", \"rotate-all\"),\n meta: {\n kind: \"action\",\n description: \"Rotate tokens for all configurations\",\n childrenCount: 0,\n },\n },\n ],\n };\n }\n\n @Actions.Exec(\"/\", \"rotate-all\", \"Rotate all configured tokens\")\n async execRotateAll(): Promise<{ success: boolean; data?: unknown }> {\n const results = await this.rotateAll();\n const allSuccess = Object.values(results).every((s) => s.lastResult === \"success\");\n return { success: allSuccess, data: results };\n }\n\n // ── Search ────────────────────────────────────────────────────\n\n @Explain(\"/:path*\")\n async handleExplain(ctx: RouteContext): Promise<AFSExplainResult> {\n const parts = this.parsePath(ctx);\n\n if (parts.length >= 1) {\n const name = parts[0]!;\n const cfg = this.configs.get(name);\n if (cfg) {\n const status = this.getStatus(name);\n return {\n format: \"text\",\n content:\n `Rotation config \"${name}\".\\n` +\n `Token endpoint: ${cfg.tokenEndpoint}\\n` +\n `Client ID: ${cfg.clientId}\\n` +\n `Last rotation: ${status.lastRotation ? new Date(status.lastRotation).toISOString() : \"never\"}\\n` +\n `Status: ${status.lastResult || \"pending\"}`,\n };\n }\n }\n return {\n format: \"text\",\n content: `AFS Rotation — OAuth2 token rotation. ${this.configs.size} config(s). Use actions to trigger rotation.`,\n };\n }\n}\n\n// Register as loadable module\n(AFSRotation as unknown as AFSModuleClass).load = AFSRotation.load;\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aigne/afs-rotation",
|
|
3
|
+
"version": "1.11.0-beta.12",
|
|
4
|
+
"description": "AFS Rotation provider — OAuth2 token rotation with vault integration",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"author": "Arcblock <blocklet@arcblock.io> https://github.com/arcblock",
|
|
10
|
+
"homepage": "https://github.com/arcblock/afs",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/arcblock/afs"
|
|
14
|
+
},
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/arcblock/afs/issues"
|
|
17
|
+
},
|
|
18
|
+
"type": "module",
|
|
19
|
+
"main": "./dist/index.cjs",
|
|
20
|
+
"module": "./dist/index.mjs",
|
|
21
|
+
"types": "./dist/index.d.cts",
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"require": "./dist/index.cjs",
|
|
25
|
+
"import": "./dist/index.mjs"
|
|
26
|
+
},
|
|
27
|
+
"./*": "./*"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"LICENSE",
|
|
32
|
+
"README.md",
|
|
33
|
+
"CHANGELOG.md"
|
|
34
|
+
],
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"ufo": "^1.6.3",
|
|
37
|
+
"zod": "^4.0.0",
|
|
38
|
+
"@aigne/afs": "^1.11.0-beta.12"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/bun": "^1.3.6",
|
|
42
|
+
"npm-run-all": "^4.1.5",
|
|
43
|
+
"rimraf": "^6.1.2",
|
|
44
|
+
"tsdown": "0.20.0-beta.3",
|
|
45
|
+
"typescript": "5.9.2",
|
|
46
|
+
"@aigne/scripts": "0.0.0",
|
|
47
|
+
"@aigne/typescript-config": "0.0.0",
|
|
48
|
+
"@aigne/afs-testing": "1.11.0-beta.12",
|
|
49
|
+
"@aigne/afs-vault": "1.11.0-beta.12"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsdown",
|
|
53
|
+
"check-types": "tsc --noEmit",
|
|
54
|
+
"clean": "rimraf dist coverage",
|
|
55
|
+
"test": "bun test",
|
|
56
|
+
"test:coverage": "bun test --coverage --coverage-reporter=lcov --coverage-reporter=text"
|
|
57
|
+
}
|
|
58
|
+
}
|