@tinycloud/node-sdk 2.2.0-beta.7 → 2.2.0-beta.9
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/dist/{core-C3s0bgRe.d.cts → core-DcJ27GsA.d.cts} +58 -4
- package/dist/{core-C3s0bgRe.d.ts → core-DcJ27GsA.d.ts} +58 -4
- package/dist/core.cjs +499 -70
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +2 -2
- package/dist/core.d.ts +2 -2
- package/dist/core.js +502 -73
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +499 -70
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +502 -73
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/core.cjs
CHANGED
|
@@ -56,6 +56,7 @@ __export(core_exports, {
|
|
|
56
56
|
TinyCloud: () => import_sdk_core7.TinyCloud,
|
|
57
57
|
TinyCloudNode: () => TinyCloudNode,
|
|
58
58
|
UnsupportedFeatureError: () => import_sdk_core17.UnsupportedFeatureError,
|
|
59
|
+
VAULT_PERMISSION_SERVICE: () => import_sdk_core9.VAULT_PERMISSION_SERVICE,
|
|
59
60
|
VaultHeaders: () => import_sdk_core13.VaultHeaders,
|
|
60
61
|
VaultPublicSpaceKVActions: () => import_sdk_core13.VaultPublicSpaceKVActions,
|
|
61
62
|
VersionCheckError: () => import_sdk_core17.VersionCheckError,
|
|
@@ -72,6 +73,8 @@ __export(core_exports, {
|
|
|
72
73
|
defaultSpaceCreationHandler: () => import_sdk_core8.defaultSpaceCreationHandler,
|
|
73
74
|
deserializeDelegation: () => deserializeDelegation,
|
|
74
75
|
expandActionShortNames: () => import_sdk_core9.expandActionShortNames,
|
|
76
|
+
expandPermissionEntries: () => import_sdk_core9.expandPermissionEntries,
|
|
77
|
+
expandPermissionEntry: () => import_sdk_core9.expandPermissionEntry,
|
|
75
78
|
isCapabilitySubset: () => import_sdk_core9.isCapabilitySubset,
|
|
76
79
|
loadManifest: () => import_sdk_core9.loadManifest,
|
|
77
80
|
makePublicSpaceId: () => import_sdk_core16.makePublicSpaceId,
|
|
@@ -1272,49 +1275,29 @@ function secretsError(code, message, cause) {
|
|
|
1272
1275
|
}
|
|
1273
1276
|
};
|
|
1274
1277
|
}
|
|
1275
|
-
function
|
|
1276
|
-
return
|
|
1278
|
+
function displayActionUrn(action) {
|
|
1279
|
+
return action === "put" ? "tinycloud.vault/write" : "tinycloud.vault/delete";
|
|
1277
1280
|
}
|
|
1278
|
-
function
|
|
1279
|
-
return
|
|
1281
|
+
function kvActionUrn(action) {
|
|
1282
|
+
return `tinycloud.kv/${action}`;
|
|
1280
1283
|
}
|
|
1281
1284
|
function secretPermissionEntries(name, action) {
|
|
1282
1285
|
return [
|
|
1283
1286
|
{
|
|
1284
|
-
service: "tinycloud.
|
|
1287
|
+
service: "tinycloud.vault",
|
|
1285
1288
|
space: SECRETS_SPACE,
|
|
1286
|
-
path:
|
|
1287
|
-
actions: [action],
|
|
1288
|
-
skipPrefix: true
|
|
1289
|
-
},
|
|
1290
|
-
{
|
|
1291
|
-
service: "tinycloud.kv",
|
|
1292
|
-
space: SECRETS_SPACE,
|
|
1293
|
-
path: secretResourcePath("vault", name),
|
|
1294
|
-
actions: [action],
|
|
1289
|
+
path: `${SECRET_PREFIX}${name}`,
|
|
1290
|
+
actions: [action === "put" ? "write" : "delete"],
|
|
1295
1291
|
skipPrefix: true
|
|
1296
1292
|
}
|
|
1297
1293
|
];
|
|
1298
1294
|
}
|
|
1295
|
+
function secretResourcePath(base, name) {
|
|
1296
|
+
return `${base}/${SECRET_PREFIX}${name}`;
|
|
1297
|
+
}
|
|
1299
1298
|
function isSecretsSpace(space) {
|
|
1300
1299
|
return space === SECRETS_SPACE || space.endsWith(`:${SECRETS_SPACE}`);
|
|
1301
1300
|
}
|
|
1302
|
-
function composeEscalatedManifest(manifest, additional) {
|
|
1303
|
-
if (Array.isArray(manifest)) {
|
|
1304
|
-
const [primary, ...rest] = manifest;
|
|
1305
|
-
return [
|
|
1306
|
-
{
|
|
1307
|
-
...primary,
|
|
1308
|
-
permissions: [...primary.permissions ?? [], ...additional]
|
|
1309
|
-
},
|
|
1310
|
-
...rest
|
|
1311
|
-
];
|
|
1312
|
-
}
|
|
1313
|
-
return {
|
|
1314
|
-
...manifest,
|
|
1315
|
-
permissions: [...manifest.permissions ?? [], ...additional]
|
|
1316
|
-
};
|
|
1317
|
-
}
|
|
1318
1301
|
var NodeSecretsService = class {
|
|
1319
1302
|
constructor(config) {
|
|
1320
1303
|
this.config = config;
|
|
@@ -1327,10 +1310,11 @@ var NodeSecretsService = class {
|
|
|
1327
1310
|
return this.service.isUnlocked;
|
|
1328
1311
|
}
|
|
1329
1312
|
async unlock(signer) {
|
|
1330
|
-
|
|
1331
|
-
|
|
1313
|
+
const effectiveSigner = signer ?? this.config.getUnlockSigner?.();
|
|
1314
|
+
if (effectiveSigner !== void 0) {
|
|
1315
|
+
this.unlockSigner = effectiveSigner;
|
|
1332
1316
|
}
|
|
1333
|
-
const result = await this.service.unlock(
|
|
1317
|
+
const result = await this.service.unlock(effectiveSigner);
|
|
1334
1318
|
if (result.ok) {
|
|
1335
1319
|
this.shouldRestoreUnlock = true;
|
|
1336
1320
|
}
|
|
@@ -1372,29 +1356,16 @@ var NodeSecretsService = class {
|
|
|
1372
1356
|
if (!this.config.canEscalate()) {
|
|
1373
1357
|
return secretsError(
|
|
1374
1358
|
import_sdk_core5.ErrorCodes.PERMISSION_DENIED,
|
|
1375
|
-
`Cannot autosign ${
|
|
1376
|
-
);
|
|
1377
|
-
}
|
|
1378
|
-
const manifest = this.config.getManifest();
|
|
1379
|
-
if (manifest === void 0) {
|
|
1380
|
-
return secretsError(
|
|
1381
|
-
import_sdk_core5.ErrorCodes.PERMISSION_DENIED,
|
|
1382
|
-
`Cannot autosign ${actionUrn(action)} for ${name}; set a manifest before mutating secrets.`
|
|
1359
|
+
`Cannot autosign ${displayActionUrn(action)} for ${name}; TinyCloudNode needs wallet mode with a signer or privateKey.`
|
|
1383
1360
|
);
|
|
1384
1361
|
}
|
|
1385
1362
|
try {
|
|
1386
|
-
this.config.
|
|
1387
|
-
composeEscalatedManifest(
|
|
1388
|
-
manifest,
|
|
1389
|
-
secretPermissionEntries(name, action)
|
|
1390
|
-
)
|
|
1391
|
-
);
|
|
1392
|
-
await this.config.signIn();
|
|
1363
|
+
await this.config.grantPermissions(secretPermissionEntries(name, action));
|
|
1393
1364
|
return this.restoreUnlockAfterEscalation();
|
|
1394
1365
|
} catch (error) {
|
|
1395
1366
|
return secretsError(
|
|
1396
1367
|
import_sdk_core5.ErrorCodes.PERMISSION_DENIED,
|
|
1397
|
-
error instanceof Error ? error.message : `Autosign escalation for ${
|
|
1368
|
+
error instanceof Error ? error.message : `Autosign escalation for ${displayActionUrn(action)} on ${name} failed.`,
|
|
1398
1369
|
error instanceof Error ? error : void 0
|
|
1399
1370
|
);
|
|
1400
1371
|
}
|
|
@@ -1411,7 +1382,7 @@ var NodeSecretsService = class {
|
|
|
1411
1382
|
return false;
|
|
1412
1383
|
}
|
|
1413
1384
|
const manifests = Array.isArray(manifest) ? manifest : [manifest];
|
|
1414
|
-
const requiredAction =
|
|
1385
|
+
const requiredAction = kvActionUrn(action);
|
|
1415
1386
|
return manifests.some((entry) => {
|
|
1416
1387
|
const resolved = (0, import_sdk_core5.resolveManifest)(entry);
|
|
1417
1388
|
return ["keys", "vault"].every(
|
|
@@ -1454,6 +1425,30 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1454
1425
|
this.auth = null;
|
|
1455
1426
|
this.tc = null;
|
|
1456
1427
|
this._chainId = 1;
|
|
1428
|
+
this.runtimePermissionGrants = [];
|
|
1429
|
+
this.invokeWithRuntimePermissions = (session, service, path, action, facts) => {
|
|
1430
|
+
return this.wasmBindings.invoke(
|
|
1431
|
+
this.selectInvocationSession(session, service, path, action),
|
|
1432
|
+
service,
|
|
1433
|
+
path,
|
|
1434
|
+
action,
|
|
1435
|
+
facts
|
|
1436
|
+
);
|
|
1437
|
+
};
|
|
1438
|
+
this.invokeAnyWithRuntimePermissions = (session, entries, facts) => {
|
|
1439
|
+
if (!this.wasmBindings.invokeAny) {
|
|
1440
|
+
throw new Error("WASM binding does not support invokeAny");
|
|
1441
|
+
}
|
|
1442
|
+
const grant = this.findGrantForOperations(
|
|
1443
|
+
entries.map((entry) => ({
|
|
1444
|
+
spaceId: entry.spaceId,
|
|
1445
|
+
service: this.invocationServiceName(entry.service),
|
|
1446
|
+
path: entry.path,
|
|
1447
|
+
action: entry.action
|
|
1448
|
+
}))
|
|
1449
|
+
);
|
|
1450
|
+
return this.wasmBindings.invokeAny(grant?.session ?? session, entries, facts);
|
|
1451
|
+
};
|
|
1457
1452
|
this.explicitHost = config.host;
|
|
1458
1453
|
this.config = {
|
|
1459
1454
|
...config,
|
|
@@ -1489,7 +1484,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1489
1484
|
this._sharingService = new import_sdk_core6.SharingService({
|
|
1490
1485
|
hosts: [this.config.host],
|
|
1491
1486
|
// session: undefined - not needed for receive()
|
|
1492
|
-
invoke: this.
|
|
1487
|
+
invoke: this.invokeWithRuntimePermissions,
|
|
1493
1488
|
fetch: globalThis.fetch.bind(globalThis),
|
|
1494
1489
|
keyProvider: this._keyProvider,
|
|
1495
1490
|
registry: this._capabilityRegistry,
|
|
@@ -1557,7 +1552,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1557
1552
|
includeAccountRegistryPermissions: config.includeAccountRegistryPermissions
|
|
1558
1553
|
});
|
|
1559
1554
|
this.tc = new import_sdk_core6.TinyCloud(this.auth, {
|
|
1560
|
-
invokeAny: this.
|
|
1555
|
+
invokeAny: this.invokeAnyWithRuntimePermissions
|
|
1561
1556
|
});
|
|
1562
1557
|
}
|
|
1563
1558
|
syncResolvedHostFromAuth() {
|
|
@@ -1682,6 +1677,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1682
1677
|
this._secrets = void 0;
|
|
1683
1678
|
this._spaceService = void 0;
|
|
1684
1679
|
this._serviceContext = void 0;
|
|
1680
|
+
this.runtimePermissionGrants = [];
|
|
1685
1681
|
await this.tc.signIn(options);
|
|
1686
1682
|
this.syncResolvedHostFromAuth();
|
|
1687
1683
|
this.initializeServices();
|
|
@@ -1771,6 +1767,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1771
1767
|
this._secrets = void 0;
|
|
1772
1768
|
this._spaceService = void 0;
|
|
1773
1769
|
this._serviceContext = void 0;
|
|
1770
|
+
this.runtimePermissionGrants = [];
|
|
1774
1771
|
if (sessionData.address) {
|
|
1775
1772
|
this._address = sessionData.address;
|
|
1776
1773
|
}
|
|
@@ -1778,8 +1775,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1778
1775
|
this._chainId = sessionData.chainId;
|
|
1779
1776
|
}
|
|
1780
1777
|
this._serviceContext = new import_sdk_core6.ServiceContext({
|
|
1781
|
-
invoke: this.
|
|
1782
|
-
invokeAny: this.
|
|
1778
|
+
invoke: this.invokeWithRuntimePermissions,
|
|
1779
|
+
invokeAny: this.invokeAnyWithRuntimePermissions,
|
|
1783
1780
|
fetch: globalThis.fetch.bind(globalThis),
|
|
1784
1781
|
hosts: [this.config.host]
|
|
1785
1782
|
});
|
|
@@ -1865,7 +1862,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1865
1862
|
includeAccountRegistryPermissions: this.config.includeAccountRegistryPermissions
|
|
1866
1863
|
});
|
|
1867
1864
|
this.tc = new import_sdk_core6.TinyCloud(this.auth, {
|
|
1868
|
-
invokeAny: this.
|
|
1865
|
+
invokeAny: this.invokeAnyWithRuntimePermissions
|
|
1869
1866
|
});
|
|
1870
1867
|
this.config.prefix = prefix;
|
|
1871
1868
|
}
|
|
@@ -1909,7 +1906,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1909
1906
|
includeAccountRegistryPermissions: this.config.includeAccountRegistryPermissions
|
|
1910
1907
|
});
|
|
1911
1908
|
this.tc = new import_sdk_core6.TinyCloud(this.auth, {
|
|
1912
|
-
invokeAny: this.
|
|
1909
|
+
invokeAny: this.invokeAnyWithRuntimePermissions
|
|
1913
1910
|
});
|
|
1914
1911
|
this.config.prefix = prefix;
|
|
1915
1912
|
}
|
|
@@ -1922,10 +1919,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1922
1919
|
if (!session) {
|
|
1923
1920
|
return;
|
|
1924
1921
|
}
|
|
1925
|
-
this.tc.initializeServices(this.
|
|
1922
|
+
this.tc.initializeServices(this.invokeWithRuntimePermissions, [this.config.host]);
|
|
1926
1923
|
this._serviceContext = new import_sdk_core6.ServiceContext({
|
|
1927
|
-
invoke: this.
|
|
1928
|
-
invokeAny: this.
|
|
1924
|
+
invoke: this.invokeWithRuntimePermissions,
|
|
1925
|
+
invokeAny: this.invokeAnyWithRuntimePermissions,
|
|
1929
1926
|
fetch: globalThis.fetch.bind(globalThis),
|
|
1930
1927
|
hosts: [this.config.host]
|
|
1931
1928
|
});
|
|
@@ -2095,7 +2092,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2095
2092
|
this._delegationManager = new import_sdk_core6.DelegationManager({
|
|
2096
2093
|
hosts: [this.config.host],
|
|
2097
2094
|
session: serviceSession,
|
|
2098
|
-
invoke: this.
|
|
2095
|
+
invoke: this.invokeWithRuntimePermissions,
|
|
2099
2096
|
fetch: globalThis.fetch.bind(globalThis)
|
|
2100
2097
|
});
|
|
2101
2098
|
this._spaceService = new import_sdk_core6.SpaceService({
|
|
@@ -2362,9 +2359,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2362
2359
|
this._secrets = new NodeSecretsService({
|
|
2363
2360
|
getService: () => this.getBaseSecrets(),
|
|
2364
2361
|
getManifest: () => this.manifest,
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2362
|
+
grantPermissions: (additional) => this.grantRuntimePermissions(additional),
|
|
2363
|
+
canEscalate: () => this.signer !== void 0 && this.tc !== void 0,
|
|
2364
|
+
getUnlockSigner: () => this.signer ?? void 0
|
|
2368
2365
|
});
|
|
2369
2366
|
}
|
|
2370
2367
|
return this._secrets;
|
|
@@ -2448,6 +2445,171 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2448
2445
|
}
|
|
2449
2446
|
};
|
|
2450
2447
|
}
|
|
2448
|
+
/**
|
|
2449
|
+
* Check whether the current session or an approved runtime delegation covers
|
|
2450
|
+
* every requested permission.
|
|
2451
|
+
*/
|
|
2452
|
+
hasRuntimePermissions(permissions) {
|
|
2453
|
+
const session = this.auth?.tinyCloudSession;
|
|
2454
|
+
if (!session || !Array.isArray(permissions) || permissions.length === 0) {
|
|
2455
|
+
return false;
|
|
2456
|
+
}
|
|
2457
|
+
const expanded = this.expandPermissionEntries(permissions);
|
|
2458
|
+
if (this.sessionCoversPermissionEntries(session, expanded)) {
|
|
2459
|
+
return true;
|
|
2460
|
+
}
|
|
2461
|
+
return this.findRuntimeGrantsForPermissionEntries(expanded, session).length > 0;
|
|
2462
|
+
}
|
|
2463
|
+
/**
|
|
2464
|
+
* Return installed runtime permission delegations. When `permissions` is
|
|
2465
|
+
* provided, only delegations currently covering those permissions are
|
|
2466
|
+
* returned. Base-session manifest permissions are not represented here.
|
|
2467
|
+
*/
|
|
2468
|
+
getRuntimePermissionDelegations(permissions) {
|
|
2469
|
+
this.pruneExpiredRuntimePermissionGrants();
|
|
2470
|
+
if (permissions === void 0) {
|
|
2471
|
+
return this.runtimePermissionGrants.map((grant) => grant.delegation);
|
|
2472
|
+
}
|
|
2473
|
+
const session = this.auth?.tinyCloudSession;
|
|
2474
|
+
if (!session || !Array.isArray(permissions) || permissions.length === 0) {
|
|
2475
|
+
return [];
|
|
2476
|
+
}
|
|
2477
|
+
const expanded = this.expandPermissionEntries(permissions);
|
|
2478
|
+
return this.findRuntimeGrantsForPermissionEntries(expanded, session).map(
|
|
2479
|
+
(grant) => grant.delegation
|
|
2480
|
+
);
|
|
2481
|
+
}
|
|
2482
|
+
/**
|
|
2483
|
+
* Install a portable runtime permission delegation into this SDK instance so
|
|
2484
|
+
* matching service calls and downstream `delegateTo()` calls can use it.
|
|
2485
|
+
*/
|
|
2486
|
+
async useRuntimeDelegation(delegation) {
|
|
2487
|
+
const session = this.auth?.tinyCloudSession;
|
|
2488
|
+
if (!session) {
|
|
2489
|
+
throw new import_sdk_core6.SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
2490
|
+
}
|
|
2491
|
+
if (delegation.expiry.getTime() <= Date.now()) {
|
|
2492
|
+
throw new import_sdk_core6.SessionExpiredError(delegation.expiry);
|
|
2493
|
+
}
|
|
2494
|
+
const expectedDids = /* @__PURE__ */ new Set([session.verificationMethod, this.sessionDid]);
|
|
2495
|
+
if (!expectedDids.has(delegation.delegateDID)) {
|
|
2496
|
+
throw new Error(
|
|
2497
|
+
`Runtime delegation targets ${delegation.delegateDID} but this session key is ${session.verificationMethod}.`
|
|
2498
|
+
);
|
|
2499
|
+
}
|
|
2500
|
+
const targetHost = delegation.host ?? this.config.host;
|
|
2501
|
+
const activateResult = await (0, import_sdk_core6.activateSessionWithHost)(
|
|
2502
|
+
targetHost,
|
|
2503
|
+
delegation.delegationHeader
|
|
2504
|
+
);
|
|
2505
|
+
if (!activateResult.success) {
|
|
2506
|
+
throw new Error(
|
|
2507
|
+
`Failed to activate runtime permission delegation: ${activateResult.error}`
|
|
2508
|
+
);
|
|
2509
|
+
}
|
|
2510
|
+
this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
|
|
2511
|
+
(grant) => grant.delegation.cid !== delegation.cid
|
|
2512
|
+
);
|
|
2513
|
+
this.runtimePermissionGrants.push(
|
|
2514
|
+
this.runtimeGrantFromDelegation(delegation, session)
|
|
2515
|
+
);
|
|
2516
|
+
}
|
|
2517
|
+
/**
|
|
2518
|
+
* Store additional permissions as narrow delegations to the current session
|
|
2519
|
+
* key. Future service invocations automatically use a stored delegation when
|
|
2520
|
+
* its `(space, service, path, action)` covers the request.
|
|
2521
|
+
*/
|
|
2522
|
+
async grantRuntimePermissions(permissions, options) {
|
|
2523
|
+
if (!Array.isArray(permissions) || permissions.length === 0) {
|
|
2524
|
+
throw new Error("grantRuntimePermissions requires a non-empty permissions array");
|
|
2525
|
+
}
|
|
2526
|
+
const session = this.auth?.tinyCloudSession;
|
|
2527
|
+
if (!session) {
|
|
2528
|
+
throw new import_sdk_core6.SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
2529
|
+
}
|
|
2530
|
+
const sessionExpiry = extractSiweExpiration(session.siwe);
|
|
2531
|
+
if (sessionExpiry !== void 0) {
|
|
2532
|
+
const marginMs = _TinyCloudNode.SESSION_EXPIRY_SAFETY_MARGIN_MS;
|
|
2533
|
+
if (sessionExpiry.getTime() <= Date.now() + marginMs) {
|
|
2534
|
+
throw new import_sdk_core6.SessionExpiredError(sessionExpiry);
|
|
2535
|
+
}
|
|
2536
|
+
}
|
|
2537
|
+
const expanded = this.expandPermissionEntries(permissions);
|
|
2538
|
+
if (this.sessionCoversPermissionEntries(session, expanded)) {
|
|
2539
|
+
return [];
|
|
2540
|
+
}
|
|
2541
|
+
const existingGrants = this.findRuntimeGrantsForPermissionEntries(expanded, session);
|
|
2542
|
+
if (existingGrants.length > 0) {
|
|
2543
|
+
return existingGrants.map((grant) => grant.delegation);
|
|
2544
|
+
}
|
|
2545
|
+
if (!this.signer) {
|
|
2546
|
+
throw new Error(
|
|
2547
|
+
"grantRuntimePermissions requires wallet mode with a signer or privateKey."
|
|
2548
|
+
);
|
|
2549
|
+
}
|
|
2550
|
+
const bySpace = /* @__PURE__ */ new Map();
|
|
2551
|
+
for (const entry of expanded) {
|
|
2552
|
+
const spaceId = this.resolvePermissionSpace(entry.space, session);
|
|
2553
|
+
const current = bySpace.get(spaceId) ?? [];
|
|
2554
|
+
current.push(entry);
|
|
2555
|
+
bySpace.set(spaceId, current);
|
|
2556
|
+
}
|
|
2557
|
+
const now = /* @__PURE__ */ new Date();
|
|
2558
|
+
const requestedExpiryMs = resolveExpiryMs(options?.expiry);
|
|
2559
|
+
let expiresAt = new Date(now.getTime() + requestedExpiryMs);
|
|
2560
|
+
if (sessionExpiry !== void 0 && sessionExpiry < expiresAt) {
|
|
2561
|
+
expiresAt = sessionExpiry;
|
|
2562
|
+
}
|
|
2563
|
+
const delegations = [];
|
|
2564
|
+
for (const [spaceId, entries] of bySpace) {
|
|
2565
|
+
const abilities = this.permissionsToAbilities(entries);
|
|
2566
|
+
const prepared = this.wasmBindings.prepareSession({
|
|
2567
|
+
abilities,
|
|
2568
|
+
address: this.wasmBindings.ensureEip55(session.address),
|
|
2569
|
+
chainId: session.chainId,
|
|
2570
|
+
domain: this.siweDomain,
|
|
2571
|
+
issuedAt: now.toISOString(),
|
|
2572
|
+
expirationTime: expiresAt.toISOString(),
|
|
2573
|
+
spaceId,
|
|
2574
|
+
jwk: session.jwk
|
|
2575
|
+
});
|
|
2576
|
+
const signature = await this.signer.signMessage(prepared.siwe);
|
|
2577
|
+
const delegatedSession = this.wasmBindings.completeSessionSetup({
|
|
2578
|
+
...prepared,
|
|
2579
|
+
signature
|
|
2580
|
+
});
|
|
2581
|
+
const activateResult = await (0, import_sdk_core6.activateSessionWithHost)(
|
|
2582
|
+
this.config.host,
|
|
2583
|
+
delegatedSession.delegationHeader
|
|
2584
|
+
);
|
|
2585
|
+
if (!activateResult.success) {
|
|
2586
|
+
throw new Error(
|
|
2587
|
+
`Failed to activate runtime permission delegation: ${activateResult.error}`
|
|
2588
|
+
);
|
|
2589
|
+
}
|
|
2590
|
+
const delegation = this.runtimeDelegationFromSession(
|
|
2591
|
+
delegatedSession,
|
|
2592
|
+
entries,
|
|
2593
|
+
spaceId,
|
|
2594
|
+
session,
|
|
2595
|
+
expiresAt
|
|
2596
|
+
);
|
|
2597
|
+
this.runtimePermissionGrants.push({
|
|
2598
|
+
session: {
|
|
2599
|
+
delegationHeader: delegatedSession.delegationHeader,
|
|
2600
|
+
delegationCid: delegatedSession.delegationCid,
|
|
2601
|
+
spaceId,
|
|
2602
|
+
verificationMethod: session.verificationMethod,
|
|
2603
|
+
jwk: session.jwk
|
|
2604
|
+
},
|
|
2605
|
+
delegation,
|
|
2606
|
+
operations: this.permissionOperations(entries, spaceId),
|
|
2607
|
+
expiresAt
|
|
2608
|
+
});
|
|
2609
|
+
delegations.push(delegation);
|
|
2610
|
+
}
|
|
2611
|
+
return delegations;
|
|
2612
|
+
}
|
|
2451
2613
|
/**
|
|
2452
2614
|
* Get the DelegationManager for delegation CRUD operations.
|
|
2453
2615
|
*
|
|
@@ -2636,7 +2798,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2636
2798
|
if (this._serviceContext) {
|
|
2637
2799
|
const publicKV = new import_sdk_core6.KVService({ prefix: "" });
|
|
2638
2800
|
const publicContext = new import_sdk_core6.ServiceContext({
|
|
2639
|
-
invoke: this.
|
|
2801
|
+
invoke: this.invokeWithRuntimePermissions,
|
|
2640
2802
|
fetch: this._serviceContext.fetch,
|
|
2641
2803
|
hosts: this._serviceContext.hosts
|
|
2642
2804
|
});
|
|
@@ -2724,8 +2886,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2724
2886
|
* Issue a delegation using the capability-chain flow.
|
|
2725
2887
|
*
|
|
2726
2888
|
* When every requested permission is a subset of the current
|
|
2727
|
-
* session's recap,
|
|
2728
|
-
*
|
|
2889
|
+
* session's recap, or of one installed runtime permission delegation,
|
|
2890
|
+
* the delegation is signed by the session key via WASM — no wallet
|
|
2891
|
+
* prompt. When at least one is NOT derivable, a
|
|
2729
2892
|
* {@link PermissionNotInManifestError} is raised (carrying the
|
|
2730
2893
|
* missing entries) so the caller can trigger an escalation flow
|
|
2731
2894
|
* (e.g. `TinyCloudWeb.requestPermissions`). Passing
|
|
@@ -2775,10 +2938,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2775
2938
|
"delegateTo requires a non-empty permissions array"
|
|
2776
2939
|
);
|
|
2777
2940
|
}
|
|
2778
|
-
const expandedEntries =
|
|
2779
|
-
...entry,
|
|
2780
|
-
actions: (0, import_sdk_core6.expandActionShortNames)(entry.service, entry.actions)
|
|
2781
|
-
}));
|
|
2941
|
+
const expandedEntries = this.expandPermissionEntries(permissions);
|
|
2782
2942
|
const now = /* @__PURE__ */ new Date();
|
|
2783
2943
|
const expiryMs = resolveExpiryMs(options?.expiry);
|
|
2784
2944
|
const expirationTime = new Date(now.getTime() + expiryMs);
|
|
@@ -2805,6 +2965,23 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2805
2965
|
);
|
|
2806
2966
|
const { subset, missing } = (0, import_sdk_core6.isCapabilitySubset)(expandedEntries, granted);
|
|
2807
2967
|
if (!subset) {
|
|
2968
|
+
const runtimeGrant = this.findGrantForOperations(
|
|
2969
|
+
this.permissionEntriesToOperations(expandedEntries, session)
|
|
2970
|
+
);
|
|
2971
|
+
if (runtimeGrant) {
|
|
2972
|
+
const marginMs = _TinyCloudNode.SESSION_EXPIRY_SAFETY_MARGIN_MS;
|
|
2973
|
+
if (runtimeGrant.expiresAt.getTime() <= Date.now() + marginMs) {
|
|
2974
|
+
throw new import_sdk_core6.SessionExpiredError(runtimeGrant.expiresAt);
|
|
2975
|
+
}
|
|
2976
|
+
const runtimeExpiration = runtimeGrant.expiresAt < effectiveExpiration ? runtimeGrant.expiresAt : effectiveExpiration;
|
|
2977
|
+
const delegation2 = await this.createDelegationViaRuntimeGrant(
|
|
2978
|
+
did,
|
|
2979
|
+
expandedEntries,
|
|
2980
|
+
runtimeExpiration,
|
|
2981
|
+
runtimeGrant
|
|
2982
|
+
);
|
|
2983
|
+
return { delegation: delegation2, prompted: false };
|
|
2984
|
+
}
|
|
2808
2985
|
throw new import_sdk_core6.PermissionNotInManifestError(missing, granted);
|
|
2809
2986
|
}
|
|
2810
2987
|
const delegation = await this.createDelegationViaWasmPath(
|
|
@@ -2949,6 +3126,41 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2949
3126
|
host: this.config.host
|
|
2950
3127
|
};
|
|
2951
3128
|
}
|
|
3129
|
+
async createDelegationViaRuntimeGrant(did, entries, expirationTime, grant) {
|
|
3130
|
+
const result = this.createDelegationWrapper({
|
|
3131
|
+
session: grant.session,
|
|
3132
|
+
delegateDID: did,
|
|
3133
|
+
spaceId: grant.session.spaceId,
|
|
3134
|
+
abilities: this.permissionsToAbilities(entries),
|
|
3135
|
+
expirationSecs: Math.floor(expirationTime.getTime() / 1e3)
|
|
3136
|
+
});
|
|
3137
|
+
const primary = result.resources[0];
|
|
3138
|
+
const delegationHeader = { Authorization: result.delegation };
|
|
3139
|
+
const targetHost = grant.delegation.host ?? this.config.host;
|
|
3140
|
+
const activateResult = await (0, import_sdk_core6.activateSessionWithHost)(
|
|
3141
|
+
targetHost,
|
|
3142
|
+
delegationHeader
|
|
3143
|
+
);
|
|
3144
|
+
if (!activateResult.success) {
|
|
3145
|
+
throw new Error(
|
|
3146
|
+
`Failed to activate delegation with host: ${activateResult.error}`
|
|
3147
|
+
);
|
|
3148
|
+
}
|
|
3149
|
+
return {
|
|
3150
|
+
cid: result.cid,
|
|
3151
|
+
delegationHeader,
|
|
3152
|
+
spaceId: grant.session.spaceId,
|
|
3153
|
+
path: primary.path,
|
|
3154
|
+
actions: primary.actions,
|
|
3155
|
+
resources: result.resources,
|
|
3156
|
+
disableSubDelegation: false,
|
|
3157
|
+
expiry: result.expiry,
|
|
3158
|
+
delegateDID: did,
|
|
3159
|
+
ownerAddress: grant.delegation.ownerAddress,
|
|
3160
|
+
chainId: grant.delegation.chainId,
|
|
3161
|
+
host: targetHost
|
|
3162
|
+
};
|
|
3163
|
+
}
|
|
2952
3164
|
resolvePermissionSpace(space, session) {
|
|
2953
3165
|
if (space === void 0) {
|
|
2954
3166
|
return this.wasmBindings.makeSpaceId(
|
|
@@ -2965,6 +3177,220 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2965
3177
|
}
|
|
2966
3178
|
return this.wasmBindings.makeSpaceId(session.address, session.chainId, space);
|
|
2967
3179
|
}
|
|
3180
|
+
expandPermissionEntries(permissions) {
|
|
3181
|
+
return (0, import_sdk_core6.expandPermissionEntries)(permissions);
|
|
3182
|
+
}
|
|
3183
|
+
shortServiceName(service) {
|
|
3184
|
+
const short = import_sdk_core6.SERVICE_LONG_TO_SHORT[service];
|
|
3185
|
+
if (short === void 0) {
|
|
3186
|
+
throw new Error(
|
|
3187
|
+
`unknown service '${service}' \u2014 no short-form mapping`
|
|
3188
|
+
);
|
|
3189
|
+
}
|
|
3190
|
+
return short;
|
|
3191
|
+
}
|
|
3192
|
+
permissionsToAbilities(entries) {
|
|
3193
|
+
const abilities = {};
|
|
3194
|
+
for (const entry of entries) {
|
|
3195
|
+
const service = this.shortServiceName(entry.service);
|
|
3196
|
+
abilities[service] ?? (abilities[service] = {});
|
|
3197
|
+
const existing = abilities[service][entry.path] ?? [];
|
|
3198
|
+
const seen = new Set(existing);
|
|
3199
|
+
for (const action of entry.actions) {
|
|
3200
|
+
if (!seen.has(action)) {
|
|
3201
|
+
existing.push(action);
|
|
3202
|
+
seen.add(action);
|
|
3203
|
+
}
|
|
3204
|
+
}
|
|
3205
|
+
abilities[service][entry.path] = existing;
|
|
3206
|
+
}
|
|
3207
|
+
return abilities;
|
|
3208
|
+
}
|
|
3209
|
+
permissionOperations(entries, spaceId) {
|
|
3210
|
+
return entries.flatMap((entry) => {
|
|
3211
|
+
const service = this.shortServiceName(entry.service);
|
|
3212
|
+
return entry.actions.map((action) => ({
|
|
3213
|
+
spaceId,
|
|
3214
|
+
service,
|
|
3215
|
+
path: entry.path,
|
|
3216
|
+
action
|
|
3217
|
+
}));
|
|
3218
|
+
});
|
|
3219
|
+
}
|
|
3220
|
+
sessionCoversPermissionEntries(session, entries) {
|
|
3221
|
+
try {
|
|
3222
|
+
const granted = (0, import_sdk_core6.parseRecapCapabilities)(
|
|
3223
|
+
(siwe) => this.wasmBindings.parseRecapFromSiwe(siwe),
|
|
3224
|
+
session.siwe
|
|
3225
|
+
);
|
|
3226
|
+
return (0, import_sdk_core6.isCapabilitySubset)(entries, granted).subset;
|
|
3227
|
+
} catch {
|
|
3228
|
+
return false;
|
|
3229
|
+
}
|
|
3230
|
+
}
|
|
3231
|
+
permissionEntriesToOperations(entries, session) {
|
|
3232
|
+
return entries.flatMap((entry) => {
|
|
3233
|
+
const spaceId = this.resolvePermissionSpace(entry.space, session);
|
|
3234
|
+
const service = this.shortServiceName(entry.service);
|
|
3235
|
+
return entry.actions.map((action) => ({
|
|
3236
|
+
spaceId,
|
|
3237
|
+
service,
|
|
3238
|
+
path: entry.path,
|
|
3239
|
+
action
|
|
3240
|
+
}));
|
|
3241
|
+
});
|
|
3242
|
+
}
|
|
3243
|
+
findRuntimeGrantsForPermissionEntries(entries, session) {
|
|
3244
|
+
const grants = [];
|
|
3245
|
+
const operations = this.permissionEntriesToOperations(entries, session);
|
|
3246
|
+
if (operations.length === 0) {
|
|
3247
|
+
return grants;
|
|
3248
|
+
}
|
|
3249
|
+
for (const operation of operations) {
|
|
3250
|
+
const grant = this.findGrantForOperation(operation);
|
|
3251
|
+
if (!grant) {
|
|
3252
|
+
return [];
|
|
3253
|
+
}
|
|
3254
|
+
if (!grants.includes(grant)) {
|
|
3255
|
+
grants.push(grant);
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
return grants;
|
|
3259
|
+
}
|
|
3260
|
+
runtimeDelegationFromSession(delegatedSession, entries, spaceId, session, expiresAt) {
|
|
3261
|
+
const resources = this.delegatedResourcesForEntries(entries, spaceId);
|
|
3262
|
+
const primary = resources[0];
|
|
3263
|
+
return {
|
|
3264
|
+
cid: delegatedSession.delegationCid,
|
|
3265
|
+
delegationHeader: delegatedSession.delegationHeader,
|
|
3266
|
+
spaceId,
|
|
3267
|
+
path: primary.path,
|
|
3268
|
+
actions: primary.actions,
|
|
3269
|
+
resources,
|
|
3270
|
+
disableSubDelegation: false,
|
|
3271
|
+
expiry: expiresAt,
|
|
3272
|
+
delegateDID: session.verificationMethod,
|
|
3273
|
+
ownerAddress: session.address,
|
|
3274
|
+
chainId: session.chainId,
|
|
3275
|
+
host: this.config.host
|
|
3276
|
+
};
|
|
3277
|
+
}
|
|
3278
|
+
runtimeGrantFromDelegation(delegation, session) {
|
|
3279
|
+
const operations = this.operationsFromDelegation(delegation);
|
|
3280
|
+
return {
|
|
3281
|
+
session: {
|
|
3282
|
+
delegationHeader: delegation.delegationHeader,
|
|
3283
|
+
delegationCid: delegation.cid,
|
|
3284
|
+
spaceId: delegation.spaceId,
|
|
3285
|
+
verificationMethod: session.verificationMethod,
|
|
3286
|
+
jwk: session.jwk
|
|
3287
|
+
},
|
|
3288
|
+
delegation,
|
|
3289
|
+
operations,
|
|
3290
|
+
expiresAt: delegation.expiry
|
|
3291
|
+
};
|
|
3292
|
+
}
|
|
3293
|
+
delegatedResourcesForEntries(entries, spaceId) {
|
|
3294
|
+
return entries.map((entry) => ({
|
|
3295
|
+
service: this.shortServiceName(entry.service),
|
|
3296
|
+
space: spaceId,
|
|
3297
|
+
path: entry.path,
|
|
3298
|
+
actions: [...entry.actions]
|
|
3299
|
+
}));
|
|
3300
|
+
}
|
|
3301
|
+
operationsFromDelegation(delegation) {
|
|
3302
|
+
const resources = delegation.resources !== void 0 && delegation.resources.length > 0 ? delegation.resources : this.flatDelegationResources(delegation);
|
|
3303
|
+
return resources.flatMap(
|
|
3304
|
+
(resource) => resource.actions.map((action) => ({
|
|
3305
|
+
spaceId: resource.space,
|
|
3306
|
+
service: this.invocationServiceName(resource.service),
|
|
3307
|
+
path: resource.path,
|
|
3308
|
+
action
|
|
3309
|
+
}))
|
|
3310
|
+
);
|
|
3311
|
+
}
|
|
3312
|
+
flatDelegationResources(delegation) {
|
|
3313
|
+
const byService = /* @__PURE__ */ new Map();
|
|
3314
|
+
for (const action of delegation.actions) {
|
|
3315
|
+
const service = this.shortServiceName(action.split("/")[0]);
|
|
3316
|
+
const actions = byService.get(service) ?? [];
|
|
3317
|
+
actions.push(action);
|
|
3318
|
+
byService.set(service, actions);
|
|
3319
|
+
}
|
|
3320
|
+
return [...byService.entries()].map(([service, actions]) => ({
|
|
3321
|
+
service,
|
|
3322
|
+
space: delegation.spaceId,
|
|
3323
|
+
path: delegation.path,
|
|
3324
|
+
actions
|
|
3325
|
+
}));
|
|
3326
|
+
}
|
|
3327
|
+
selectInvocationSession(fallback, service, path, action) {
|
|
3328
|
+
const grant = this.findGrantForOperation({
|
|
3329
|
+
spaceId: fallback.spaceId,
|
|
3330
|
+
service: this.invocationServiceName(service),
|
|
3331
|
+
path,
|
|
3332
|
+
action
|
|
3333
|
+
});
|
|
3334
|
+
return grant?.session ?? fallback;
|
|
3335
|
+
}
|
|
3336
|
+
findGrantForOperations(operations) {
|
|
3337
|
+
if (operations.length === 0) {
|
|
3338
|
+
return void 0;
|
|
3339
|
+
}
|
|
3340
|
+
this.pruneExpiredRuntimePermissionGrants();
|
|
3341
|
+
return this.runtimePermissionGrants.find((grant) => {
|
|
3342
|
+
return operations.every(
|
|
3343
|
+
(operation) => grant.operations.some(
|
|
3344
|
+
(granted) => this.operationCovers(granted, operation)
|
|
3345
|
+
)
|
|
3346
|
+
);
|
|
3347
|
+
});
|
|
3348
|
+
}
|
|
3349
|
+
findGrantForOperation(operation) {
|
|
3350
|
+
return this.findGrantForOperations([operation]);
|
|
3351
|
+
}
|
|
3352
|
+
pruneExpiredRuntimePermissionGrants() {
|
|
3353
|
+
const now = Date.now();
|
|
3354
|
+
this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
|
|
3355
|
+
(grant) => grant.expiresAt.getTime() > now
|
|
3356
|
+
);
|
|
3357
|
+
}
|
|
3358
|
+
operationCovers(granted, requested) {
|
|
3359
|
+
return granted.spaceId === requested.spaceId && granted.service === requested.service && this.actionContains(granted.action, requested.action) && this.pathContains(granted.path, requested.path);
|
|
3360
|
+
}
|
|
3361
|
+
actionContains(grantedAction, requestedAction) {
|
|
3362
|
+
if (grantedAction === requestedAction) {
|
|
3363
|
+
return true;
|
|
3364
|
+
}
|
|
3365
|
+
if (grantedAction.endsWith("/*")) {
|
|
3366
|
+
const prefix = grantedAction.slice(0, -2);
|
|
3367
|
+
return requestedAction.startsWith(`${prefix}/`);
|
|
3368
|
+
}
|
|
3369
|
+
return false;
|
|
3370
|
+
}
|
|
3371
|
+
invocationServiceName(service) {
|
|
3372
|
+
return service.startsWith("tinycloud.") ? this.shortServiceName(service) : service;
|
|
3373
|
+
}
|
|
3374
|
+
pathContains(grantedPath, requestedPath) {
|
|
3375
|
+
if (grantedPath === "" || grantedPath === "/") {
|
|
3376
|
+
return true;
|
|
3377
|
+
}
|
|
3378
|
+
if (grantedPath.endsWith("/**")) {
|
|
3379
|
+
return requestedPath.startsWith(grantedPath.slice(0, -3));
|
|
3380
|
+
}
|
|
3381
|
+
if (grantedPath.endsWith("/*")) {
|
|
3382
|
+
const prefix = grantedPath.slice(0, -2);
|
|
3383
|
+
if (!requestedPath.startsWith(prefix)) {
|
|
3384
|
+
return false;
|
|
3385
|
+
}
|
|
3386
|
+
const remainder = requestedPath.slice(prefix.length);
|
|
3387
|
+
return !remainder.includes("/") || remainder === "/";
|
|
3388
|
+
}
|
|
3389
|
+
if (grantedPath.endsWith("/")) {
|
|
3390
|
+
return requestedPath.startsWith(grantedPath);
|
|
3391
|
+
}
|
|
3392
|
+
return grantedPath === requestedPath;
|
|
3393
|
+
}
|
|
2968
3394
|
/**
|
|
2969
3395
|
* Issue a delegation via the legacy wallet-signed SIWE path for a single
|
|
2970
3396
|
* {@link PermissionEntry}. Shares the implementation with the public
|
|
@@ -3413,6 +3839,7 @@ var import_sdk_core18 = require("@tinycloud/sdk-core");
|
|
|
3413
3839
|
TinyCloud,
|
|
3414
3840
|
TinyCloudNode,
|
|
3415
3841
|
UnsupportedFeatureError,
|
|
3842
|
+
VAULT_PERMISSION_SERVICE,
|
|
3416
3843
|
VaultHeaders,
|
|
3417
3844
|
VaultPublicSpaceKVActions,
|
|
3418
3845
|
VersionCheckError,
|
|
@@ -3429,6 +3856,8 @@ var import_sdk_core18 = require("@tinycloud/sdk-core");
|
|
|
3429
3856
|
defaultSpaceCreationHandler,
|
|
3430
3857
|
deserializeDelegation,
|
|
3431
3858
|
expandActionShortNames,
|
|
3859
|
+
expandPermissionEntries,
|
|
3860
|
+
expandPermissionEntry,
|
|
3432
3861
|
isCapabilitySubset,
|
|
3433
3862
|
loadManifest,
|
|
3434
3863
|
makePublicSpaceId,
|