@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/index.js
CHANGED
|
@@ -18291,22 +18291,6 @@ function secretPermissionEntries(name, action) {
|
|
|
18291
18291
|
function isSecretsSpace(space) {
|
|
18292
18292
|
return space === SECRETS_SPACE || space.endsWith(`:${SECRETS_SPACE}`);
|
|
18293
18293
|
}
|
|
18294
|
-
function composeEscalatedManifest(manifest, additional) {
|
|
18295
|
-
if (Array.isArray(manifest)) {
|
|
18296
|
-
const [primary, ...rest] = manifest;
|
|
18297
|
-
return [
|
|
18298
|
-
{
|
|
18299
|
-
...primary,
|
|
18300
|
-
permissions: [...primary.permissions ?? [], ...additional]
|
|
18301
|
-
},
|
|
18302
|
-
...rest
|
|
18303
|
-
];
|
|
18304
|
-
}
|
|
18305
|
-
return {
|
|
18306
|
-
...manifest,
|
|
18307
|
-
permissions: [...manifest.permissions ?? [], ...additional]
|
|
18308
|
-
};
|
|
18309
|
-
}
|
|
18310
18294
|
var NodeSecretsService = class {
|
|
18311
18295
|
constructor(config) {
|
|
18312
18296
|
this.config = config;
|
|
@@ -18319,10 +18303,11 @@ var NodeSecretsService = class {
|
|
|
18319
18303
|
return this.service.isUnlocked;
|
|
18320
18304
|
}
|
|
18321
18305
|
async unlock(signer) {
|
|
18322
|
-
|
|
18323
|
-
|
|
18306
|
+
const effectiveSigner = signer ?? this.config.getUnlockSigner?.();
|
|
18307
|
+
if (effectiveSigner !== void 0) {
|
|
18308
|
+
this.unlockSigner = effectiveSigner;
|
|
18324
18309
|
}
|
|
18325
|
-
const result = await this.service.unlock(
|
|
18310
|
+
const result = await this.service.unlock(effectiveSigner);
|
|
18326
18311
|
if (result.ok) {
|
|
18327
18312
|
this.shouldRestoreUnlock = true;
|
|
18328
18313
|
}
|
|
@@ -18367,21 +18352,8 @@ var NodeSecretsService = class {
|
|
|
18367
18352
|
`Cannot autosign ${actionUrn(action)} for ${name}; TinyCloudNode needs wallet mode with a signer or privateKey.`
|
|
18368
18353
|
);
|
|
18369
18354
|
}
|
|
18370
|
-
const manifest = this.config.getManifest();
|
|
18371
|
-
if (manifest === void 0) {
|
|
18372
|
-
return secretsError(
|
|
18373
|
-
ErrorCodes.PERMISSION_DENIED,
|
|
18374
|
-
`Cannot autosign ${actionUrn(action)} for ${name}; set a manifest before mutating secrets.`
|
|
18375
|
-
);
|
|
18376
|
-
}
|
|
18377
18355
|
try {
|
|
18378
|
-
this.config.
|
|
18379
|
-
composeEscalatedManifest(
|
|
18380
|
-
manifest,
|
|
18381
|
-
secretPermissionEntries(name, action)
|
|
18382
|
-
)
|
|
18383
|
-
);
|
|
18384
|
-
await this.config.signIn();
|
|
18356
|
+
await this.config.grantPermissions(secretPermissionEntries(name, action));
|
|
18385
18357
|
return this.restoreUnlockAfterEscalation();
|
|
18386
18358
|
} catch (error) {
|
|
18387
18359
|
return secretsError(
|
|
@@ -18446,6 +18418,30 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18446
18418
|
this.auth = null;
|
|
18447
18419
|
this.tc = null;
|
|
18448
18420
|
this._chainId = 1;
|
|
18421
|
+
this.runtimePermissionGrants = [];
|
|
18422
|
+
this.invokeWithRuntimePermissions = (session, service, path, action, facts) => {
|
|
18423
|
+
return this.wasmBindings.invoke(
|
|
18424
|
+
this.selectInvocationSession(session, service, path, action),
|
|
18425
|
+
service,
|
|
18426
|
+
path,
|
|
18427
|
+
action,
|
|
18428
|
+
facts
|
|
18429
|
+
);
|
|
18430
|
+
};
|
|
18431
|
+
this.invokeAnyWithRuntimePermissions = (session, entries, facts) => {
|
|
18432
|
+
if (!this.wasmBindings.invokeAny) {
|
|
18433
|
+
throw new Error("WASM binding does not support invokeAny");
|
|
18434
|
+
}
|
|
18435
|
+
const grant = this.findGrantForOperations(
|
|
18436
|
+
entries.map((entry) => ({
|
|
18437
|
+
spaceId: entry.spaceId,
|
|
18438
|
+
service: this.invocationServiceName(entry.service),
|
|
18439
|
+
path: entry.path,
|
|
18440
|
+
action: entry.action
|
|
18441
|
+
}))
|
|
18442
|
+
);
|
|
18443
|
+
return this.wasmBindings.invokeAny(grant?.session ?? session, entries, facts);
|
|
18444
|
+
};
|
|
18449
18445
|
this.explicitHost = config.host;
|
|
18450
18446
|
this.config = {
|
|
18451
18447
|
...config,
|
|
@@ -18481,7 +18477,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18481
18477
|
this._sharingService = new SharingService({
|
|
18482
18478
|
hosts: [this.config.host],
|
|
18483
18479
|
// session: undefined - not needed for receive()
|
|
18484
|
-
invoke: this.
|
|
18480
|
+
invoke: this.invokeWithRuntimePermissions,
|
|
18485
18481
|
fetch: globalThis.fetch.bind(globalThis),
|
|
18486
18482
|
keyProvider: this._keyProvider,
|
|
18487
18483
|
registry: this._capabilityRegistry,
|
|
@@ -18549,7 +18545,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18549
18545
|
includeAccountRegistryPermissions: config.includeAccountRegistryPermissions
|
|
18550
18546
|
});
|
|
18551
18547
|
this.tc = new TinyCloud(this.auth, {
|
|
18552
|
-
invokeAny: this.
|
|
18548
|
+
invokeAny: this.invokeAnyWithRuntimePermissions
|
|
18553
18549
|
});
|
|
18554
18550
|
}
|
|
18555
18551
|
syncResolvedHostFromAuth() {
|
|
@@ -18674,6 +18670,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18674
18670
|
this._secrets = void 0;
|
|
18675
18671
|
this._spaceService = void 0;
|
|
18676
18672
|
this._serviceContext = void 0;
|
|
18673
|
+
this.runtimePermissionGrants = [];
|
|
18677
18674
|
await this.tc.signIn(options);
|
|
18678
18675
|
this.syncResolvedHostFromAuth();
|
|
18679
18676
|
this.initializeServices();
|
|
@@ -18763,6 +18760,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18763
18760
|
this._secrets = void 0;
|
|
18764
18761
|
this._spaceService = void 0;
|
|
18765
18762
|
this._serviceContext = void 0;
|
|
18763
|
+
this.runtimePermissionGrants = [];
|
|
18766
18764
|
if (sessionData.address) {
|
|
18767
18765
|
this._address = sessionData.address;
|
|
18768
18766
|
}
|
|
@@ -18770,8 +18768,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18770
18768
|
this._chainId = sessionData.chainId;
|
|
18771
18769
|
}
|
|
18772
18770
|
this._serviceContext = new ServiceContext2({
|
|
18773
|
-
invoke: this.
|
|
18774
|
-
invokeAny: this.
|
|
18771
|
+
invoke: this.invokeWithRuntimePermissions,
|
|
18772
|
+
invokeAny: this.invokeAnyWithRuntimePermissions,
|
|
18775
18773
|
fetch: globalThis.fetch.bind(globalThis),
|
|
18776
18774
|
hosts: [this.config.host]
|
|
18777
18775
|
});
|
|
@@ -18857,7 +18855,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18857
18855
|
includeAccountRegistryPermissions: this.config.includeAccountRegistryPermissions
|
|
18858
18856
|
});
|
|
18859
18857
|
this.tc = new TinyCloud(this.auth, {
|
|
18860
|
-
invokeAny: this.
|
|
18858
|
+
invokeAny: this.invokeAnyWithRuntimePermissions
|
|
18861
18859
|
});
|
|
18862
18860
|
this.config.prefix = prefix;
|
|
18863
18861
|
}
|
|
@@ -18901,7 +18899,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18901
18899
|
includeAccountRegistryPermissions: this.config.includeAccountRegistryPermissions
|
|
18902
18900
|
});
|
|
18903
18901
|
this.tc = new TinyCloud(this.auth, {
|
|
18904
|
-
invokeAny: this.
|
|
18902
|
+
invokeAny: this.invokeAnyWithRuntimePermissions
|
|
18905
18903
|
});
|
|
18906
18904
|
this.config.prefix = prefix;
|
|
18907
18905
|
}
|
|
@@ -18914,10 +18912,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18914
18912
|
if (!session) {
|
|
18915
18913
|
return;
|
|
18916
18914
|
}
|
|
18917
|
-
this.tc.initializeServices(this.
|
|
18915
|
+
this.tc.initializeServices(this.invokeWithRuntimePermissions, [this.config.host]);
|
|
18918
18916
|
this._serviceContext = new ServiceContext2({
|
|
18919
|
-
invoke: this.
|
|
18920
|
-
invokeAny: this.
|
|
18917
|
+
invoke: this.invokeWithRuntimePermissions,
|
|
18918
|
+
invokeAny: this.invokeAnyWithRuntimePermissions,
|
|
18921
18919
|
fetch: globalThis.fetch.bind(globalThis),
|
|
18922
18920
|
hosts: [this.config.host]
|
|
18923
18921
|
});
|
|
@@ -19087,7 +19085,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19087
19085
|
this._delegationManager = new DelegationManager({
|
|
19088
19086
|
hosts: [this.config.host],
|
|
19089
19087
|
session: serviceSession,
|
|
19090
|
-
invoke: this.
|
|
19088
|
+
invoke: this.invokeWithRuntimePermissions,
|
|
19091
19089
|
fetch: globalThis.fetch.bind(globalThis)
|
|
19092
19090
|
});
|
|
19093
19091
|
this._spaceService = new SpaceService({
|
|
@@ -19354,9 +19352,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19354
19352
|
this._secrets = new NodeSecretsService({
|
|
19355
19353
|
getService: () => this.getBaseSecrets(),
|
|
19356
19354
|
getManifest: () => this.manifest,
|
|
19357
|
-
|
|
19358
|
-
|
|
19359
|
-
|
|
19355
|
+
grantPermissions: (additional) => this.grantRuntimePermissions(additional),
|
|
19356
|
+
canEscalate: () => this.signer !== void 0 && this.tc !== void 0,
|
|
19357
|
+
getUnlockSigner: () => this.signer ?? void 0
|
|
19360
19358
|
});
|
|
19361
19359
|
}
|
|
19362
19360
|
return this._secrets;
|
|
@@ -19440,6 +19438,171 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19440
19438
|
}
|
|
19441
19439
|
};
|
|
19442
19440
|
}
|
|
19441
|
+
/**
|
|
19442
|
+
* Check whether the current session or an approved runtime delegation covers
|
|
19443
|
+
* every requested permission.
|
|
19444
|
+
*/
|
|
19445
|
+
hasRuntimePermissions(permissions) {
|
|
19446
|
+
const session = this.auth?.tinyCloudSession;
|
|
19447
|
+
if (!session || !Array.isArray(permissions) || permissions.length === 0) {
|
|
19448
|
+
return false;
|
|
19449
|
+
}
|
|
19450
|
+
const expanded = this.expandPermissionEntries(permissions);
|
|
19451
|
+
if (this.sessionCoversPermissionEntries(session, expanded)) {
|
|
19452
|
+
return true;
|
|
19453
|
+
}
|
|
19454
|
+
return this.findRuntimeGrantsForPermissionEntries(expanded, session).length > 0;
|
|
19455
|
+
}
|
|
19456
|
+
/**
|
|
19457
|
+
* Return installed runtime permission delegations. When `permissions` is
|
|
19458
|
+
* provided, only delegations currently covering those permissions are
|
|
19459
|
+
* returned. Base-session manifest permissions are not represented here.
|
|
19460
|
+
*/
|
|
19461
|
+
getRuntimePermissionDelegations(permissions) {
|
|
19462
|
+
this.pruneExpiredRuntimePermissionGrants();
|
|
19463
|
+
if (permissions === void 0) {
|
|
19464
|
+
return this.runtimePermissionGrants.map((grant) => grant.delegation);
|
|
19465
|
+
}
|
|
19466
|
+
const session = this.auth?.tinyCloudSession;
|
|
19467
|
+
if (!session || !Array.isArray(permissions) || permissions.length === 0) {
|
|
19468
|
+
return [];
|
|
19469
|
+
}
|
|
19470
|
+
const expanded = this.expandPermissionEntries(permissions);
|
|
19471
|
+
return this.findRuntimeGrantsForPermissionEntries(expanded, session).map(
|
|
19472
|
+
(grant) => grant.delegation
|
|
19473
|
+
);
|
|
19474
|
+
}
|
|
19475
|
+
/**
|
|
19476
|
+
* Install a portable runtime permission delegation into this SDK instance so
|
|
19477
|
+
* matching service calls and downstream `delegateTo()` calls can use it.
|
|
19478
|
+
*/
|
|
19479
|
+
async useRuntimeDelegation(delegation) {
|
|
19480
|
+
const session = this.auth?.tinyCloudSession;
|
|
19481
|
+
if (!session) {
|
|
19482
|
+
throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
19483
|
+
}
|
|
19484
|
+
if (delegation.expiry.getTime() <= Date.now()) {
|
|
19485
|
+
throw new SessionExpiredError(delegation.expiry);
|
|
19486
|
+
}
|
|
19487
|
+
const expectedDids = /* @__PURE__ */ new Set([session.verificationMethod, this.sessionDid]);
|
|
19488
|
+
if (!expectedDids.has(delegation.delegateDID)) {
|
|
19489
|
+
throw new Error(
|
|
19490
|
+
`Runtime delegation targets ${delegation.delegateDID} but this session key is ${session.verificationMethod}.`
|
|
19491
|
+
);
|
|
19492
|
+
}
|
|
19493
|
+
const targetHost = delegation.host ?? this.config.host;
|
|
19494
|
+
const activateResult = await activateSessionWithHost2(
|
|
19495
|
+
targetHost,
|
|
19496
|
+
delegation.delegationHeader
|
|
19497
|
+
);
|
|
19498
|
+
if (!activateResult.success) {
|
|
19499
|
+
throw new Error(
|
|
19500
|
+
`Failed to activate runtime permission delegation: ${activateResult.error}`
|
|
19501
|
+
);
|
|
19502
|
+
}
|
|
19503
|
+
this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
|
|
19504
|
+
(grant) => grant.delegation.cid !== delegation.cid
|
|
19505
|
+
);
|
|
19506
|
+
this.runtimePermissionGrants.push(
|
|
19507
|
+
this.runtimeGrantFromDelegation(delegation, session)
|
|
19508
|
+
);
|
|
19509
|
+
}
|
|
19510
|
+
/**
|
|
19511
|
+
* Store additional permissions as narrow delegations to the current session
|
|
19512
|
+
* key. Future service invocations automatically use a stored delegation when
|
|
19513
|
+
* its `(space, service, path, action)` covers the request.
|
|
19514
|
+
*/
|
|
19515
|
+
async grantRuntimePermissions(permissions, options) {
|
|
19516
|
+
if (!Array.isArray(permissions) || permissions.length === 0) {
|
|
19517
|
+
throw new Error("grantRuntimePermissions requires a non-empty permissions array");
|
|
19518
|
+
}
|
|
19519
|
+
const session = this.auth?.tinyCloudSession;
|
|
19520
|
+
if (!session) {
|
|
19521
|
+
throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
19522
|
+
}
|
|
19523
|
+
const sessionExpiry = extractSiweExpiration(session.siwe);
|
|
19524
|
+
if (sessionExpiry !== void 0) {
|
|
19525
|
+
const marginMs = _TinyCloudNode.SESSION_EXPIRY_SAFETY_MARGIN_MS;
|
|
19526
|
+
if (sessionExpiry.getTime() <= Date.now() + marginMs) {
|
|
19527
|
+
throw new SessionExpiredError(sessionExpiry);
|
|
19528
|
+
}
|
|
19529
|
+
}
|
|
19530
|
+
const expanded = this.expandPermissionEntries(permissions);
|
|
19531
|
+
if (this.sessionCoversPermissionEntries(session, expanded)) {
|
|
19532
|
+
return [];
|
|
19533
|
+
}
|
|
19534
|
+
const existingGrants = this.findRuntimeGrantsForPermissionEntries(expanded, session);
|
|
19535
|
+
if (existingGrants.length > 0) {
|
|
19536
|
+
return existingGrants.map((grant) => grant.delegation);
|
|
19537
|
+
}
|
|
19538
|
+
if (!this.signer) {
|
|
19539
|
+
throw new Error(
|
|
19540
|
+
"grantRuntimePermissions requires wallet mode with a signer or privateKey."
|
|
19541
|
+
);
|
|
19542
|
+
}
|
|
19543
|
+
const bySpace = /* @__PURE__ */ new Map();
|
|
19544
|
+
for (const entry of expanded) {
|
|
19545
|
+
const spaceId = this.resolvePermissionSpace(entry.space, session);
|
|
19546
|
+
const current = bySpace.get(spaceId) ?? [];
|
|
19547
|
+
current.push(entry);
|
|
19548
|
+
bySpace.set(spaceId, current);
|
|
19549
|
+
}
|
|
19550
|
+
const now = /* @__PURE__ */ new Date();
|
|
19551
|
+
const requestedExpiryMs = resolveExpiryMs(options?.expiry);
|
|
19552
|
+
let expiresAt = new Date(now.getTime() + requestedExpiryMs);
|
|
19553
|
+
if (sessionExpiry !== void 0 && sessionExpiry < expiresAt) {
|
|
19554
|
+
expiresAt = sessionExpiry;
|
|
19555
|
+
}
|
|
19556
|
+
const delegations = [];
|
|
19557
|
+
for (const [spaceId, entries] of bySpace) {
|
|
19558
|
+
const abilities = this.permissionsToAbilities(entries);
|
|
19559
|
+
const prepared = this.wasmBindings.prepareSession({
|
|
19560
|
+
abilities,
|
|
19561
|
+
address: this.wasmBindings.ensureEip55(session.address),
|
|
19562
|
+
chainId: session.chainId,
|
|
19563
|
+
domain: this.siweDomain,
|
|
19564
|
+
issuedAt: now.toISOString(),
|
|
19565
|
+
expirationTime: expiresAt.toISOString(),
|
|
19566
|
+
spaceId,
|
|
19567
|
+
jwk: session.jwk
|
|
19568
|
+
});
|
|
19569
|
+
const signature2 = await this.signer.signMessage(prepared.siwe);
|
|
19570
|
+
const delegatedSession = this.wasmBindings.completeSessionSetup({
|
|
19571
|
+
...prepared,
|
|
19572
|
+
signature: signature2
|
|
19573
|
+
});
|
|
19574
|
+
const activateResult = await activateSessionWithHost2(
|
|
19575
|
+
this.config.host,
|
|
19576
|
+
delegatedSession.delegationHeader
|
|
19577
|
+
);
|
|
19578
|
+
if (!activateResult.success) {
|
|
19579
|
+
throw new Error(
|
|
19580
|
+
`Failed to activate runtime permission delegation: ${activateResult.error}`
|
|
19581
|
+
);
|
|
19582
|
+
}
|
|
19583
|
+
const delegation = this.runtimeDelegationFromSession(
|
|
19584
|
+
delegatedSession,
|
|
19585
|
+
entries,
|
|
19586
|
+
spaceId,
|
|
19587
|
+
session,
|
|
19588
|
+
expiresAt
|
|
19589
|
+
);
|
|
19590
|
+
this.runtimePermissionGrants.push({
|
|
19591
|
+
session: {
|
|
19592
|
+
delegationHeader: delegatedSession.delegationHeader,
|
|
19593
|
+
delegationCid: delegatedSession.delegationCid,
|
|
19594
|
+
spaceId,
|
|
19595
|
+
verificationMethod: session.verificationMethod,
|
|
19596
|
+
jwk: session.jwk
|
|
19597
|
+
},
|
|
19598
|
+
delegation,
|
|
19599
|
+
operations: this.permissionOperations(entries, spaceId),
|
|
19600
|
+
expiresAt
|
|
19601
|
+
});
|
|
19602
|
+
delegations.push(delegation);
|
|
19603
|
+
}
|
|
19604
|
+
return delegations;
|
|
19605
|
+
}
|
|
19443
19606
|
/**
|
|
19444
19607
|
* Get the DelegationManager for delegation CRUD operations.
|
|
19445
19608
|
*
|
|
@@ -19628,7 +19791,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19628
19791
|
if (this._serviceContext) {
|
|
19629
19792
|
const publicKV = new KVService2({ prefix: "" });
|
|
19630
19793
|
const publicContext = new ServiceContext2({
|
|
19631
|
-
invoke: this.
|
|
19794
|
+
invoke: this.invokeWithRuntimePermissions,
|
|
19632
19795
|
fetch: this._serviceContext.fetch,
|
|
19633
19796
|
hosts: this._serviceContext.hosts
|
|
19634
19797
|
});
|
|
@@ -19716,8 +19879,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19716
19879
|
* Issue a delegation using the capability-chain flow.
|
|
19717
19880
|
*
|
|
19718
19881
|
* When every requested permission is a subset of the current
|
|
19719
|
-
* session's recap,
|
|
19720
|
-
*
|
|
19882
|
+
* session's recap, or of one installed runtime permission delegation,
|
|
19883
|
+
* the delegation is signed by the session key via WASM — no wallet
|
|
19884
|
+
* prompt. When at least one is NOT derivable, a
|
|
19721
19885
|
* {@link PermissionNotInManifestError} is raised (carrying the
|
|
19722
19886
|
* missing entries) so the caller can trigger an escalation flow
|
|
19723
19887
|
* (e.g. `TinyCloudWeb.requestPermissions`). Passing
|
|
@@ -19797,6 +19961,23 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19797
19961
|
);
|
|
19798
19962
|
const { subset, missing } = isCapabilitySubset(expandedEntries, granted);
|
|
19799
19963
|
if (!subset) {
|
|
19964
|
+
const runtimeGrant = this.findGrantForOperations(
|
|
19965
|
+
this.permissionEntriesToOperations(expandedEntries, session)
|
|
19966
|
+
);
|
|
19967
|
+
if (runtimeGrant) {
|
|
19968
|
+
const marginMs = _TinyCloudNode.SESSION_EXPIRY_SAFETY_MARGIN_MS;
|
|
19969
|
+
if (runtimeGrant.expiresAt.getTime() <= Date.now() + marginMs) {
|
|
19970
|
+
throw new SessionExpiredError(runtimeGrant.expiresAt);
|
|
19971
|
+
}
|
|
19972
|
+
const runtimeExpiration = runtimeGrant.expiresAt < effectiveExpiration ? runtimeGrant.expiresAt : effectiveExpiration;
|
|
19973
|
+
const delegation2 = await this.createDelegationViaRuntimeGrant(
|
|
19974
|
+
did,
|
|
19975
|
+
expandedEntries,
|
|
19976
|
+
runtimeExpiration,
|
|
19977
|
+
runtimeGrant
|
|
19978
|
+
);
|
|
19979
|
+
return { delegation: delegation2, prompted: false };
|
|
19980
|
+
}
|
|
19800
19981
|
throw new PermissionNotInManifestError(missing, granted);
|
|
19801
19982
|
}
|
|
19802
19983
|
const delegation = await this.createDelegationViaWasmPath(
|
|
@@ -19941,6 +20122,41 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19941
20122
|
host: this.config.host
|
|
19942
20123
|
};
|
|
19943
20124
|
}
|
|
20125
|
+
async createDelegationViaRuntimeGrant(did, entries, expirationTime, grant) {
|
|
20126
|
+
const result = this.createDelegationWrapper({
|
|
20127
|
+
session: grant.session,
|
|
20128
|
+
delegateDID: did,
|
|
20129
|
+
spaceId: grant.session.spaceId,
|
|
20130
|
+
abilities: this.permissionsToAbilities(entries),
|
|
20131
|
+
expirationSecs: Math.floor(expirationTime.getTime() / 1e3)
|
|
20132
|
+
});
|
|
20133
|
+
const primary = result.resources[0];
|
|
20134
|
+
const delegationHeader = { Authorization: result.delegation };
|
|
20135
|
+
const targetHost = grant.delegation.host ?? this.config.host;
|
|
20136
|
+
const activateResult = await activateSessionWithHost2(
|
|
20137
|
+
targetHost,
|
|
20138
|
+
delegationHeader
|
|
20139
|
+
);
|
|
20140
|
+
if (!activateResult.success) {
|
|
20141
|
+
throw new Error(
|
|
20142
|
+
`Failed to activate delegation with host: ${activateResult.error}`
|
|
20143
|
+
);
|
|
20144
|
+
}
|
|
20145
|
+
return {
|
|
20146
|
+
cid: result.cid,
|
|
20147
|
+
delegationHeader,
|
|
20148
|
+
spaceId: grant.session.spaceId,
|
|
20149
|
+
path: primary.path,
|
|
20150
|
+
actions: primary.actions,
|
|
20151
|
+
resources: result.resources,
|
|
20152
|
+
disableSubDelegation: false,
|
|
20153
|
+
expiry: result.expiry,
|
|
20154
|
+
delegateDID: did,
|
|
20155
|
+
ownerAddress: grant.delegation.ownerAddress,
|
|
20156
|
+
chainId: grant.delegation.chainId,
|
|
20157
|
+
host: targetHost
|
|
20158
|
+
};
|
|
20159
|
+
}
|
|
19944
20160
|
resolvePermissionSpace(space, session) {
|
|
19945
20161
|
if (space === void 0) {
|
|
19946
20162
|
return this.wasmBindings.makeSpaceId(
|
|
@@ -19957,6 +20173,223 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19957
20173
|
}
|
|
19958
20174
|
return this.wasmBindings.makeSpaceId(session.address, session.chainId, space);
|
|
19959
20175
|
}
|
|
20176
|
+
expandPermissionEntries(permissions) {
|
|
20177
|
+
return permissions.map((entry) => ({
|
|
20178
|
+
...entry,
|
|
20179
|
+
actions: expandActionShortNames(entry.service, entry.actions)
|
|
20180
|
+
}));
|
|
20181
|
+
}
|
|
20182
|
+
shortServiceName(service) {
|
|
20183
|
+
const short = SERVICE_LONG_TO_SHORT[service];
|
|
20184
|
+
if (short === void 0) {
|
|
20185
|
+
throw new Error(
|
|
20186
|
+
`unknown service '${service}' \u2014 no short-form mapping`
|
|
20187
|
+
);
|
|
20188
|
+
}
|
|
20189
|
+
return short;
|
|
20190
|
+
}
|
|
20191
|
+
permissionsToAbilities(entries) {
|
|
20192
|
+
const abilities = {};
|
|
20193
|
+
for (const entry of entries) {
|
|
20194
|
+
const service = this.shortServiceName(entry.service);
|
|
20195
|
+
abilities[service] ?? (abilities[service] = {});
|
|
20196
|
+
const existing = abilities[service][entry.path] ?? [];
|
|
20197
|
+
const seen = new Set(existing);
|
|
20198
|
+
for (const action of entry.actions) {
|
|
20199
|
+
if (!seen.has(action)) {
|
|
20200
|
+
existing.push(action);
|
|
20201
|
+
seen.add(action);
|
|
20202
|
+
}
|
|
20203
|
+
}
|
|
20204
|
+
abilities[service][entry.path] = existing;
|
|
20205
|
+
}
|
|
20206
|
+
return abilities;
|
|
20207
|
+
}
|
|
20208
|
+
permissionOperations(entries, spaceId) {
|
|
20209
|
+
return entries.flatMap((entry) => {
|
|
20210
|
+
const service = this.shortServiceName(entry.service);
|
|
20211
|
+
return entry.actions.map((action) => ({
|
|
20212
|
+
spaceId,
|
|
20213
|
+
service,
|
|
20214
|
+
path: entry.path,
|
|
20215
|
+
action
|
|
20216
|
+
}));
|
|
20217
|
+
});
|
|
20218
|
+
}
|
|
20219
|
+
sessionCoversPermissionEntries(session, entries) {
|
|
20220
|
+
try {
|
|
20221
|
+
const granted = parseRecapCapabilities(
|
|
20222
|
+
(siwe) => this.wasmBindings.parseRecapFromSiwe(siwe),
|
|
20223
|
+
session.siwe
|
|
20224
|
+
);
|
|
20225
|
+
return isCapabilitySubset(entries, granted).subset;
|
|
20226
|
+
} catch {
|
|
20227
|
+
return false;
|
|
20228
|
+
}
|
|
20229
|
+
}
|
|
20230
|
+
permissionEntriesToOperations(entries, session) {
|
|
20231
|
+
return entries.flatMap((entry) => {
|
|
20232
|
+
const spaceId = this.resolvePermissionSpace(entry.space, session);
|
|
20233
|
+
const service = this.shortServiceName(entry.service);
|
|
20234
|
+
return entry.actions.map((action) => ({
|
|
20235
|
+
spaceId,
|
|
20236
|
+
service,
|
|
20237
|
+
path: entry.path,
|
|
20238
|
+
action
|
|
20239
|
+
}));
|
|
20240
|
+
});
|
|
20241
|
+
}
|
|
20242
|
+
findRuntimeGrantsForPermissionEntries(entries, session) {
|
|
20243
|
+
const grants = [];
|
|
20244
|
+
const operations = this.permissionEntriesToOperations(entries, session);
|
|
20245
|
+
if (operations.length === 0) {
|
|
20246
|
+
return grants;
|
|
20247
|
+
}
|
|
20248
|
+
for (const operation of operations) {
|
|
20249
|
+
const grant = this.findGrantForOperation(operation);
|
|
20250
|
+
if (!grant) {
|
|
20251
|
+
return [];
|
|
20252
|
+
}
|
|
20253
|
+
if (!grants.includes(grant)) {
|
|
20254
|
+
grants.push(grant);
|
|
20255
|
+
}
|
|
20256
|
+
}
|
|
20257
|
+
return grants;
|
|
20258
|
+
}
|
|
20259
|
+
runtimeDelegationFromSession(delegatedSession, entries, spaceId, session, expiresAt) {
|
|
20260
|
+
const resources = this.delegatedResourcesForEntries(entries, spaceId);
|
|
20261
|
+
const primary = resources[0];
|
|
20262
|
+
return {
|
|
20263
|
+
cid: delegatedSession.delegationCid,
|
|
20264
|
+
delegationHeader: delegatedSession.delegationHeader,
|
|
20265
|
+
spaceId,
|
|
20266
|
+
path: primary.path,
|
|
20267
|
+
actions: primary.actions,
|
|
20268
|
+
resources,
|
|
20269
|
+
disableSubDelegation: false,
|
|
20270
|
+
expiry: expiresAt,
|
|
20271
|
+
delegateDID: session.verificationMethod,
|
|
20272
|
+
ownerAddress: session.address,
|
|
20273
|
+
chainId: session.chainId,
|
|
20274
|
+
host: this.config.host
|
|
20275
|
+
};
|
|
20276
|
+
}
|
|
20277
|
+
runtimeGrantFromDelegation(delegation, session) {
|
|
20278
|
+
const operations = this.operationsFromDelegation(delegation);
|
|
20279
|
+
return {
|
|
20280
|
+
session: {
|
|
20281
|
+
delegationHeader: delegation.delegationHeader,
|
|
20282
|
+
delegationCid: delegation.cid,
|
|
20283
|
+
spaceId: delegation.spaceId,
|
|
20284
|
+
verificationMethod: session.verificationMethod,
|
|
20285
|
+
jwk: session.jwk
|
|
20286
|
+
},
|
|
20287
|
+
delegation,
|
|
20288
|
+
operations,
|
|
20289
|
+
expiresAt: delegation.expiry
|
|
20290
|
+
};
|
|
20291
|
+
}
|
|
20292
|
+
delegatedResourcesForEntries(entries, spaceId) {
|
|
20293
|
+
return entries.map((entry) => ({
|
|
20294
|
+
service: this.shortServiceName(entry.service),
|
|
20295
|
+
space: spaceId,
|
|
20296
|
+
path: entry.path,
|
|
20297
|
+
actions: [...entry.actions]
|
|
20298
|
+
}));
|
|
20299
|
+
}
|
|
20300
|
+
operationsFromDelegation(delegation) {
|
|
20301
|
+
const resources = delegation.resources !== void 0 && delegation.resources.length > 0 ? delegation.resources : this.flatDelegationResources(delegation);
|
|
20302
|
+
return resources.flatMap(
|
|
20303
|
+
(resource) => resource.actions.map((action) => ({
|
|
20304
|
+
spaceId: resource.space,
|
|
20305
|
+
service: this.invocationServiceName(resource.service),
|
|
20306
|
+
path: resource.path,
|
|
20307
|
+
action
|
|
20308
|
+
}))
|
|
20309
|
+
);
|
|
20310
|
+
}
|
|
20311
|
+
flatDelegationResources(delegation) {
|
|
20312
|
+
const byService = /* @__PURE__ */ new Map();
|
|
20313
|
+
for (const action of delegation.actions) {
|
|
20314
|
+
const service = this.shortServiceName(action.split("/")[0]);
|
|
20315
|
+
const actions = byService.get(service) ?? [];
|
|
20316
|
+
actions.push(action);
|
|
20317
|
+
byService.set(service, actions);
|
|
20318
|
+
}
|
|
20319
|
+
return [...byService.entries()].map(([service, actions]) => ({
|
|
20320
|
+
service,
|
|
20321
|
+
space: delegation.spaceId,
|
|
20322
|
+
path: delegation.path,
|
|
20323
|
+
actions
|
|
20324
|
+
}));
|
|
20325
|
+
}
|
|
20326
|
+
selectInvocationSession(fallback, service, path, action) {
|
|
20327
|
+
const grant = this.findGrantForOperation({
|
|
20328
|
+
spaceId: fallback.spaceId,
|
|
20329
|
+
service: this.invocationServiceName(service),
|
|
20330
|
+
path,
|
|
20331
|
+
action
|
|
20332
|
+
});
|
|
20333
|
+
return grant?.session ?? fallback;
|
|
20334
|
+
}
|
|
20335
|
+
findGrantForOperations(operations) {
|
|
20336
|
+
if (operations.length === 0) {
|
|
20337
|
+
return void 0;
|
|
20338
|
+
}
|
|
20339
|
+
this.pruneExpiredRuntimePermissionGrants();
|
|
20340
|
+
return this.runtimePermissionGrants.find((grant) => {
|
|
20341
|
+
return operations.every(
|
|
20342
|
+
(operation) => grant.operations.some(
|
|
20343
|
+
(granted) => this.operationCovers(granted, operation)
|
|
20344
|
+
)
|
|
20345
|
+
);
|
|
20346
|
+
});
|
|
20347
|
+
}
|
|
20348
|
+
findGrantForOperation(operation) {
|
|
20349
|
+
return this.findGrantForOperations([operation]);
|
|
20350
|
+
}
|
|
20351
|
+
pruneExpiredRuntimePermissionGrants() {
|
|
20352
|
+
const now = Date.now();
|
|
20353
|
+
this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
|
|
20354
|
+
(grant) => grant.expiresAt.getTime() > now
|
|
20355
|
+
);
|
|
20356
|
+
}
|
|
20357
|
+
operationCovers(granted, requested) {
|
|
20358
|
+
return granted.spaceId === requested.spaceId && granted.service === requested.service && this.actionContains(granted.action, requested.action) && this.pathContains(granted.path, requested.path);
|
|
20359
|
+
}
|
|
20360
|
+
actionContains(grantedAction, requestedAction) {
|
|
20361
|
+
if (grantedAction === requestedAction) {
|
|
20362
|
+
return true;
|
|
20363
|
+
}
|
|
20364
|
+
if (grantedAction.endsWith("/*")) {
|
|
20365
|
+
const prefix = grantedAction.slice(0, -2);
|
|
20366
|
+
return requestedAction.startsWith(`${prefix}/`);
|
|
20367
|
+
}
|
|
20368
|
+
return false;
|
|
20369
|
+
}
|
|
20370
|
+
invocationServiceName(service) {
|
|
20371
|
+
return service.startsWith("tinycloud.") ? this.shortServiceName(service) : service;
|
|
20372
|
+
}
|
|
20373
|
+
pathContains(grantedPath, requestedPath) {
|
|
20374
|
+
if (grantedPath === "" || grantedPath === "/") {
|
|
20375
|
+
return true;
|
|
20376
|
+
}
|
|
20377
|
+
if (grantedPath.endsWith("/**")) {
|
|
20378
|
+
return requestedPath.startsWith(grantedPath.slice(0, -3));
|
|
20379
|
+
}
|
|
20380
|
+
if (grantedPath.endsWith("/*")) {
|
|
20381
|
+
const prefix = grantedPath.slice(0, -2);
|
|
20382
|
+
if (!requestedPath.startsWith(prefix)) {
|
|
20383
|
+
return false;
|
|
20384
|
+
}
|
|
20385
|
+
const remainder = requestedPath.slice(prefix.length);
|
|
20386
|
+
return !remainder.includes("/") || remainder === "/";
|
|
20387
|
+
}
|
|
20388
|
+
if (grantedPath.endsWith("/")) {
|
|
20389
|
+
return requestedPath.startsWith(grantedPath);
|
|
20390
|
+
}
|
|
20391
|
+
return grantedPath === requestedPath;
|
|
20392
|
+
}
|
|
19960
20393
|
/**
|
|
19961
20394
|
* Issue a delegation via the legacy wallet-signed SIWE path for a single
|
|
19962
20395
|
* {@link PermissionEntry}. Shares the implementation with the public
|