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