@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.cjs
CHANGED
|
@@ -18284,22 +18284,6 @@ function secretPermissionEntries(name, action) {
|
|
|
18284
18284
|
function isSecretsSpace(space) {
|
|
18285
18285
|
return space === SECRETS_SPACE || space.endsWith(`:${SECRETS_SPACE}`);
|
|
18286
18286
|
}
|
|
18287
|
-
function composeEscalatedManifest(manifest, additional) {
|
|
18288
|
-
if (Array.isArray(manifest)) {
|
|
18289
|
-
const [primary, ...rest] = manifest;
|
|
18290
|
-
return [
|
|
18291
|
-
{
|
|
18292
|
-
...primary,
|
|
18293
|
-
permissions: [...primary.permissions ?? [], ...additional]
|
|
18294
|
-
},
|
|
18295
|
-
...rest
|
|
18296
|
-
];
|
|
18297
|
-
}
|
|
18298
|
-
return {
|
|
18299
|
-
...manifest,
|
|
18300
|
-
permissions: [...manifest.permissions ?? [], ...additional]
|
|
18301
|
-
};
|
|
18302
|
-
}
|
|
18303
18287
|
var NodeSecretsService = class {
|
|
18304
18288
|
constructor(config) {
|
|
18305
18289
|
this.config = config;
|
|
@@ -18312,10 +18296,11 @@ var NodeSecretsService = class {
|
|
|
18312
18296
|
return this.service.isUnlocked;
|
|
18313
18297
|
}
|
|
18314
18298
|
async unlock(signer) {
|
|
18315
|
-
|
|
18316
|
-
|
|
18299
|
+
const effectiveSigner = signer ?? this.config.getUnlockSigner?.();
|
|
18300
|
+
if (effectiveSigner !== void 0) {
|
|
18301
|
+
this.unlockSigner = effectiveSigner;
|
|
18317
18302
|
}
|
|
18318
|
-
const result = await this.service.unlock(
|
|
18303
|
+
const result = await this.service.unlock(effectiveSigner);
|
|
18319
18304
|
if (result.ok) {
|
|
18320
18305
|
this.shouldRestoreUnlock = true;
|
|
18321
18306
|
}
|
|
@@ -18360,21 +18345,8 @@ var NodeSecretsService = class {
|
|
|
18360
18345
|
`Cannot autosign ${actionUrn(action)} for ${name}; TinyCloudNode needs wallet mode with a signer or privateKey.`
|
|
18361
18346
|
);
|
|
18362
18347
|
}
|
|
18363
|
-
const manifest = this.config.getManifest();
|
|
18364
|
-
if (manifest === void 0) {
|
|
18365
|
-
return secretsError(
|
|
18366
|
-
import_sdk_core4.ErrorCodes.PERMISSION_DENIED,
|
|
18367
|
-
`Cannot autosign ${actionUrn(action)} for ${name}; set a manifest before mutating secrets.`
|
|
18368
|
-
);
|
|
18369
|
-
}
|
|
18370
18348
|
try {
|
|
18371
|
-
this.config.
|
|
18372
|
-
composeEscalatedManifest(
|
|
18373
|
-
manifest,
|
|
18374
|
-
secretPermissionEntries(name, action)
|
|
18375
|
-
)
|
|
18376
|
-
);
|
|
18377
|
-
await this.config.signIn();
|
|
18349
|
+
await this.config.grantPermissions(secretPermissionEntries(name, action));
|
|
18378
18350
|
return this.restoreUnlockAfterEscalation();
|
|
18379
18351
|
} catch (error) {
|
|
18380
18352
|
return secretsError(
|
|
@@ -18439,6 +18411,30 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18439
18411
|
this.auth = null;
|
|
18440
18412
|
this.tc = null;
|
|
18441
18413
|
this._chainId = 1;
|
|
18414
|
+
this.runtimePermissionGrants = [];
|
|
18415
|
+
this.invokeWithRuntimePermissions = (session, service, path, action, facts) => {
|
|
18416
|
+
return this.wasmBindings.invoke(
|
|
18417
|
+
this.selectInvocationSession(session, service, path, action),
|
|
18418
|
+
service,
|
|
18419
|
+
path,
|
|
18420
|
+
action,
|
|
18421
|
+
facts
|
|
18422
|
+
);
|
|
18423
|
+
};
|
|
18424
|
+
this.invokeAnyWithRuntimePermissions = (session, entries, facts) => {
|
|
18425
|
+
if (!this.wasmBindings.invokeAny) {
|
|
18426
|
+
throw new Error("WASM binding does not support invokeAny");
|
|
18427
|
+
}
|
|
18428
|
+
const grant = this.findGrantForOperations(
|
|
18429
|
+
entries.map((entry) => ({
|
|
18430
|
+
spaceId: entry.spaceId,
|
|
18431
|
+
service: this.invocationServiceName(entry.service),
|
|
18432
|
+
path: entry.path,
|
|
18433
|
+
action: entry.action
|
|
18434
|
+
}))
|
|
18435
|
+
);
|
|
18436
|
+
return this.wasmBindings.invokeAny(grant?.session ?? session, entries, facts);
|
|
18437
|
+
};
|
|
18442
18438
|
this.explicitHost = config.host;
|
|
18443
18439
|
this.config = {
|
|
18444
18440
|
...config,
|
|
@@ -18474,7 +18470,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18474
18470
|
this._sharingService = new import_sdk_core5.SharingService({
|
|
18475
18471
|
hosts: [this.config.host],
|
|
18476
18472
|
// session: undefined - not needed for receive()
|
|
18477
|
-
invoke: this.
|
|
18473
|
+
invoke: this.invokeWithRuntimePermissions,
|
|
18478
18474
|
fetch: globalThis.fetch.bind(globalThis),
|
|
18479
18475
|
keyProvider: this._keyProvider,
|
|
18480
18476
|
registry: this._capabilityRegistry,
|
|
@@ -18542,7 +18538,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18542
18538
|
includeAccountRegistryPermissions: config.includeAccountRegistryPermissions
|
|
18543
18539
|
});
|
|
18544
18540
|
this.tc = new import_sdk_core5.TinyCloud(this.auth, {
|
|
18545
|
-
invokeAny: this.
|
|
18541
|
+
invokeAny: this.invokeAnyWithRuntimePermissions
|
|
18546
18542
|
});
|
|
18547
18543
|
}
|
|
18548
18544
|
syncResolvedHostFromAuth() {
|
|
@@ -18667,6 +18663,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18667
18663
|
this._secrets = void 0;
|
|
18668
18664
|
this._spaceService = void 0;
|
|
18669
18665
|
this._serviceContext = void 0;
|
|
18666
|
+
this.runtimePermissionGrants = [];
|
|
18670
18667
|
await this.tc.signIn(options);
|
|
18671
18668
|
this.syncResolvedHostFromAuth();
|
|
18672
18669
|
this.initializeServices();
|
|
@@ -18756,6 +18753,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18756
18753
|
this._secrets = void 0;
|
|
18757
18754
|
this._spaceService = void 0;
|
|
18758
18755
|
this._serviceContext = void 0;
|
|
18756
|
+
this.runtimePermissionGrants = [];
|
|
18759
18757
|
if (sessionData.address) {
|
|
18760
18758
|
this._address = sessionData.address;
|
|
18761
18759
|
}
|
|
@@ -18763,8 +18761,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18763
18761
|
this._chainId = sessionData.chainId;
|
|
18764
18762
|
}
|
|
18765
18763
|
this._serviceContext = new import_sdk_core5.ServiceContext({
|
|
18766
|
-
invoke: this.
|
|
18767
|
-
invokeAny: this.
|
|
18764
|
+
invoke: this.invokeWithRuntimePermissions,
|
|
18765
|
+
invokeAny: this.invokeAnyWithRuntimePermissions,
|
|
18768
18766
|
fetch: globalThis.fetch.bind(globalThis),
|
|
18769
18767
|
hosts: [this.config.host]
|
|
18770
18768
|
});
|
|
@@ -18850,7 +18848,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18850
18848
|
includeAccountRegistryPermissions: this.config.includeAccountRegistryPermissions
|
|
18851
18849
|
});
|
|
18852
18850
|
this.tc = new import_sdk_core5.TinyCloud(this.auth, {
|
|
18853
|
-
invokeAny: this.
|
|
18851
|
+
invokeAny: this.invokeAnyWithRuntimePermissions
|
|
18854
18852
|
});
|
|
18855
18853
|
this.config.prefix = prefix;
|
|
18856
18854
|
}
|
|
@@ -18894,7 +18892,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18894
18892
|
includeAccountRegistryPermissions: this.config.includeAccountRegistryPermissions
|
|
18895
18893
|
});
|
|
18896
18894
|
this.tc = new import_sdk_core5.TinyCloud(this.auth, {
|
|
18897
|
-
invokeAny: this.
|
|
18895
|
+
invokeAny: this.invokeAnyWithRuntimePermissions
|
|
18898
18896
|
});
|
|
18899
18897
|
this.config.prefix = prefix;
|
|
18900
18898
|
}
|
|
@@ -18907,10 +18905,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18907
18905
|
if (!session) {
|
|
18908
18906
|
return;
|
|
18909
18907
|
}
|
|
18910
|
-
this.tc.initializeServices(this.
|
|
18908
|
+
this.tc.initializeServices(this.invokeWithRuntimePermissions, [this.config.host]);
|
|
18911
18909
|
this._serviceContext = new import_sdk_core5.ServiceContext({
|
|
18912
|
-
invoke: this.
|
|
18913
|
-
invokeAny: this.
|
|
18910
|
+
invoke: this.invokeWithRuntimePermissions,
|
|
18911
|
+
invokeAny: this.invokeAnyWithRuntimePermissions,
|
|
18914
18912
|
fetch: globalThis.fetch.bind(globalThis),
|
|
18915
18913
|
hosts: [this.config.host]
|
|
18916
18914
|
});
|
|
@@ -19080,7 +19078,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19080
19078
|
this._delegationManager = new import_sdk_core5.DelegationManager({
|
|
19081
19079
|
hosts: [this.config.host],
|
|
19082
19080
|
session: serviceSession,
|
|
19083
|
-
invoke: this.
|
|
19081
|
+
invoke: this.invokeWithRuntimePermissions,
|
|
19084
19082
|
fetch: globalThis.fetch.bind(globalThis)
|
|
19085
19083
|
});
|
|
19086
19084
|
this._spaceService = new import_sdk_core5.SpaceService({
|
|
@@ -19347,9 +19345,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19347
19345
|
this._secrets = new NodeSecretsService({
|
|
19348
19346
|
getService: () => this.getBaseSecrets(),
|
|
19349
19347
|
getManifest: () => this.manifest,
|
|
19350
|
-
|
|
19351
|
-
|
|
19352
|
-
|
|
19348
|
+
grantPermissions: (additional) => this.grantRuntimePermissions(additional),
|
|
19349
|
+
canEscalate: () => this.signer !== void 0 && this.tc !== void 0,
|
|
19350
|
+
getUnlockSigner: () => this.signer ?? void 0
|
|
19353
19351
|
});
|
|
19354
19352
|
}
|
|
19355
19353
|
return this._secrets;
|
|
@@ -19433,6 +19431,171 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19433
19431
|
}
|
|
19434
19432
|
};
|
|
19435
19433
|
}
|
|
19434
|
+
/**
|
|
19435
|
+
* Check whether the current session or an approved runtime delegation covers
|
|
19436
|
+
* every requested permission.
|
|
19437
|
+
*/
|
|
19438
|
+
hasRuntimePermissions(permissions) {
|
|
19439
|
+
const session = this.auth?.tinyCloudSession;
|
|
19440
|
+
if (!session || !Array.isArray(permissions) || permissions.length === 0) {
|
|
19441
|
+
return false;
|
|
19442
|
+
}
|
|
19443
|
+
const expanded = this.expandPermissionEntries(permissions);
|
|
19444
|
+
if (this.sessionCoversPermissionEntries(session, expanded)) {
|
|
19445
|
+
return true;
|
|
19446
|
+
}
|
|
19447
|
+
return this.findRuntimeGrantsForPermissionEntries(expanded, session).length > 0;
|
|
19448
|
+
}
|
|
19449
|
+
/**
|
|
19450
|
+
* Return installed runtime permission delegations. When `permissions` is
|
|
19451
|
+
* provided, only delegations currently covering those permissions are
|
|
19452
|
+
* returned. Base-session manifest permissions are not represented here.
|
|
19453
|
+
*/
|
|
19454
|
+
getRuntimePermissionDelegations(permissions) {
|
|
19455
|
+
this.pruneExpiredRuntimePermissionGrants();
|
|
19456
|
+
if (permissions === void 0) {
|
|
19457
|
+
return this.runtimePermissionGrants.map((grant) => grant.delegation);
|
|
19458
|
+
}
|
|
19459
|
+
const session = this.auth?.tinyCloudSession;
|
|
19460
|
+
if (!session || !Array.isArray(permissions) || permissions.length === 0) {
|
|
19461
|
+
return [];
|
|
19462
|
+
}
|
|
19463
|
+
const expanded = this.expandPermissionEntries(permissions);
|
|
19464
|
+
return this.findRuntimeGrantsForPermissionEntries(expanded, session).map(
|
|
19465
|
+
(grant) => grant.delegation
|
|
19466
|
+
);
|
|
19467
|
+
}
|
|
19468
|
+
/**
|
|
19469
|
+
* Install a portable runtime permission delegation into this SDK instance so
|
|
19470
|
+
* matching service calls and downstream `delegateTo()` calls can use it.
|
|
19471
|
+
*/
|
|
19472
|
+
async useRuntimeDelegation(delegation) {
|
|
19473
|
+
const session = this.auth?.tinyCloudSession;
|
|
19474
|
+
if (!session) {
|
|
19475
|
+
throw new import_sdk_core5.SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
19476
|
+
}
|
|
19477
|
+
if (delegation.expiry.getTime() <= Date.now()) {
|
|
19478
|
+
throw new import_sdk_core5.SessionExpiredError(delegation.expiry);
|
|
19479
|
+
}
|
|
19480
|
+
const expectedDids = /* @__PURE__ */ new Set([session.verificationMethod, this.sessionDid]);
|
|
19481
|
+
if (!expectedDids.has(delegation.delegateDID)) {
|
|
19482
|
+
throw new Error(
|
|
19483
|
+
`Runtime delegation targets ${delegation.delegateDID} but this session key is ${session.verificationMethod}.`
|
|
19484
|
+
);
|
|
19485
|
+
}
|
|
19486
|
+
const targetHost = delegation.host ?? this.config.host;
|
|
19487
|
+
const activateResult = await (0, import_sdk_core5.activateSessionWithHost)(
|
|
19488
|
+
targetHost,
|
|
19489
|
+
delegation.delegationHeader
|
|
19490
|
+
);
|
|
19491
|
+
if (!activateResult.success) {
|
|
19492
|
+
throw new Error(
|
|
19493
|
+
`Failed to activate runtime permission delegation: ${activateResult.error}`
|
|
19494
|
+
);
|
|
19495
|
+
}
|
|
19496
|
+
this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
|
|
19497
|
+
(grant) => grant.delegation.cid !== delegation.cid
|
|
19498
|
+
);
|
|
19499
|
+
this.runtimePermissionGrants.push(
|
|
19500
|
+
this.runtimeGrantFromDelegation(delegation, session)
|
|
19501
|
+
);
|
|
19502
|
+
}
|
|
19503
|
+
/**
|
|
19504
|
+
* Store additional permissions as narrow delegations to the current session
|
|
19505
|
+
* key. Future service invocations automatically use a stored delegation when
|
|
19506
|
+
* its `(space, service, path, action)` covers the request.
|
|
19507
|
+
*/
|
|
19508
|
+
async grantRuntimePermissions(permissions, options) {
|
|
19509
|
+
if (!Array.isArray(permissions) || permissions.length === 0) {
|
|
19510
|
+
throw new Error("grantRuntimePermissions requires a non-empty permissions array");
|
|
19511
|
+
}
|
|
19512
|
+
const session = this.auth?.tinyCloudSession;
|
|
19513
|
+
if (!session) {
|
|
19514
|
+
throw new import_sdk_core5.SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
19515
|
+
}
|
|
19516
|
+
const sessionExpiry = extractSiweExpiration(session.siwe);
|
|
19517
|
+
if (sessionExpiry !== void 0) {
|
|
19518
|
+
const marginMs = _TinyCloudNode.SESSION_EXPIRY_SAFETY_MARGIN_MS;
|
|
19519
|
+
if (sessionExpiry.getTime() <= Date.now() + marginMs) {
|
|
19520
|
+
throw new import_sdk_core5.SessionExpiredError(sessionExpiry);
|
|
19521
|
+
}
|
|
19522
|
+
}
|
|
19523
|
+
const expanded = this.expandPermissionEntries(permissions);
|
|
19524
|
+
if (this.sessionCoversPermissionEntries(session, expanded)) {
|
|
19525
|
+
return [];
|
|
19526
|
+
}
|
|
19527
|
+
const existingGrants = this.findRuntimeGrantsForPermissionEntries(expanded, session);
|
|
19528
|
+
if (existingGrants.length > 0) {
|
|
19529
|
+
return existingGrants.map((grant) => grant.delegation);
|
|
19530
|
+
}
|
|
19531
|
+
if (!this.signer) {
|
|
19532
|
+
throw new Error(
|
|
19533
|
+
"grantRuntimePermissions requires wallet mode with a signer or privateKey."
|
|
19534
|
+
);
|
|
19535
|
+
}
|
|
19536
|
+
const bySpace = /* @__PURE__ */ new Map();
|
|
19537
|
+
for (const entry of expanded) {
|
|
19538
|
+
const spaceId = this.resolvePermissionSpace(entry.space, session);
|
|
19539
|
+
const current = bySpace.get(spaceId) ?? [];
|
|
19540
|
+
current.push(entry);
|
|
19541
|
+
bySpace.set(spaceId, current);
|
|
19542
|
+
}
|
|
19543
|
+
const now = /* @__PURE__ */ new Date();
|
|
19544
|
+
const requestedExpiryMs = resolveExpiryMs(options?.expiry);
|
|
19545
|
+
let expiresAt = new Date(now.getTime() + requestedExpiryMs);
|
|
19546
|
+
if (sessionExpiry !== void 0 && sessionExpiry < expiresAt) {
|
|
19547
|
+
expiresAt = sessionExpiry;
|
|
19548
|
+
}
|
|
19549
|
+
const delegations = [];
|
|
19550
|
+
for (const [spaceId, entries] of bySpace) {
|
|
19551
|
+
const abilities = this.permissionsToAbilities(entries);
|
|
19552
|
+
const prepared = this.wasmBindings.prepareSession({
|
|
19553
|
+
abilities,
|
|
19554
|
+
address: this.wasmBindings.ensureEip55(session.address),
|
|
19555
|
+
chainId: session.chainId,
|
|
19556
|
+
domain: this.siweDomain,
|
|
19557
|
+
issuedAt: now.toISOString(),
|
|
19558
|
+
expirationTime: expiresAt.toISOString(),
|
|
19559
|
+
spaceId,
|
|
19560
|
+
jwk: session.jwk
|
|
19561
|
+
});
|
|
19562
|
+
const signature2 = await this.signer.signMessage(prepared.siwe);
|
|
19563
|
+
const delegatedSession = this.wasmBindings.completeSessionSetup({
|
|
19564
|
+
...prepared,
|
|
19565
|
+
signature: signature2
|
|
19566
|
+
});
|
|
19567
|
+
const activateResult = await (0, import_sdk_core5.activateSessionWithHost)(
|
|
19568
|
+
this.config.host,
|
|
19569
|
+
delegatedSession.delegationHeader
|
|
19570
|
+
);
|
|
19571
|
+
if (!activateResult.success) {
|
|
19572
|
+
throw new Error(
|
|
19573
|
+
`Failed to activate runtime permission delegation: ${activateResult.error}`
|
|
19574
|
+
);
|
|
19575
|
+
}
|
|
19576
|
+
const delegation = this.runtimeDelegationFromSession(
|
|
19577
|
+
delegatedSession,
|
|
19578
|
+
entries,
|
|
19579
|
+
spaceId,
|
|
19580
|
+
session,
|
|
19581
|
+
expiresAt
|
|
19582
|
+
);
|
|
19583
|
+
this.runtimePermissionGrants.push({
|
|
19584
|
+
session: {
|
|
19585
|
+
delegationHeader: delegatedSession.delegationHeader,
|
|
19586
|
+
delegationCid: delegatedSession.delegationCid,
|
|
19587
|
+
spaceId,
|
|
19588
|
+
verificationMethod: session.verificationMethod,
|
|
19589
|
+
jwk: session.jwk
|
|
19590
|
+
},
|
|
19591
|
+
delegation,
|
|
19592
|
+
operations: this.permissionOperations(entries, spaceId),
|
|
19593
|
+
expiresAt
|
|
19594
|
+
});
|
|
19595
|
+
delegations.push(delegation);
|
|
19596
|
+
}
|
|
19597
|
+
return delegations;
|
|
19598
|
+
}
|
|
19436
19599
|
/**
|
|
19437
19600
|
* Get the DelegationManager for delegation CRUD operations.
|
|
19438
19601
|
*
|
|
@@ -19621,7 +19784,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19621
19784
|
if (this._serviceContext) {
|
|
19622
19785
|
const publicKV = new import_sdk_core5.KVService({ prefix: "" });
|
|
19623
19786
|
const publicContext = new import_sdk_core5.ServiceContext({
|
|
19624
|
-
invoke: this.
|
|
19787
|
+
invoke: this.invokeWithRuntimePermissions,
|
|
19625
19788
|
fetch: this._serviceContext.fetch,
|
|
19626
19789
|
hosts: this._serviceContext.hosts
|
|
19627
19790
|
});
|
|
@@ -19709,8 +19872,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19709
19872
|
* Issue a delegation using the capability-chain flow.
|
|
19710
19873
|
*
|
|
19711
19874
|
* When every requested permission is a subset of the current
|
|
19712
|
-
* session's recap,
|
|
19713
|
-
*
|
|
19875
|
+
* session's recap, or of one installed runtime permission delegation,
|
|
19876
|
+
* the delegation is signed by the session key via WASM — no wallet
|
|
19877
|
+
* prompt. When at least one is NOT derivable, a
|
|
19714
19878
|
* {@link PermissionNotInManifestError} is raised (carrying the
|
|
19715
19879
|
* missing entries) so the caller can trigger an escalation flow
|
|
19716
19880
|
* (e.g. `TinyCloudWeb.requestPermissions`). Passing
|
|
@@ -19790,6 +19954,23 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19790
19954
|
);
|
|
19791
19955
|
const { subset, missing } = (0, import_sdk_core5.isCapabilitySubset)(expandedEntries, granted);
|
|
19792
19956
|
if (!subset) {
|
|
19957
|
+
const runtimeGrant = this.findGrantForOperations(
|
|
19958
|
+
this.permissionEntriesToOperations(expandedEntries, session)
|
|
19959
|
+
);
|
|
19960
|
+
if (runtimeGrant) {
|
|
19961
|
+
const marginMs = _TinyCloudNode.SESSION_EXPIRY_SAFETY_MARGIN_MS;
|
|
19962
|
+
if (runtimeGrant.expiresAt.getTime() <= Date.now() + marginMs) {
|
|
19963
|
+
throw new import_sdk_core5.SessionExpiredError(runtimeGrant.expiresAt);
|
|
19964
|
+
}
|
|
19965
|
+
const runtimeExpiration = runtimeGrant.expiresAt < effectiveExpiration ? runtimeGrant.expiresAt : effectiveExpiration;
|
|
19966
|
+
const delegation2 = await this.createDelegationViaRuntimeGrant(
|
|
19967
|
+
did,
|
|
19968
|
+
expandedEntries,
|
|
19969
|
+
runtimeExpiration,
|
|
19970
|
+
runtimeGrant
|
|
19971
|
+
);
|
|
19972
|
+
return { delegation: delegation2, prompted: false };
|
|
19973
|
+
}
|
|
19793
19974
|
throw new import_sdk_core5.PermissionNotInManifestError(missing, granted);
|
|
19794
19975
|
}
|
|
19795
19976
|
const delegation = await this.createDelegationViaWasmPath(
|
|
@@ -19934,6 +20115,41 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19934
20115
|
host: this.config.host
|
|
19935
20116
|
};
|
|
19936
20117
|
}
|
|
20118
|
+
async createDelegationViaRuntimeGrant(did, entries, expirationTime, grant) {
|
|
20119
|
+
const result = this.createDelegationWrapper({
|
|
20120
|
+
session: grant.session,
|
|
20121
|
+
delegateDID: did,
|
|
20122
|
+
spaceId: grant.session.spaceId,
|
|
20123
|
+
abilities: this.permissionsToAbilities(entries),
|
|
20124
|
+
expirationSecs: Math.floor(expirationTime.getTime() / 1e3)
|
|
20125
|
+
});
|
|
20126
|
+
const primary = result.resources[0];
|
|
20127
|
+
const delegationHeader = { Authorization: result.delegation };
|
|
20128
|
+
const targetHost = grant.delegation.host ?? this.config.host;
|
|
20129
|
+
const activateResult = await (0, import_sdk_core5.activateSessionWithHost)(
|
|
20130
|
+
targetHost,
|
|
20131
|
+
delegationHeader
|
|
20132
|
+
);
|
|
20133
|
+
if (!activateResult.success) {
|
|
20134
|
+
throw new Error(
|
|
20135
|
+
`Failed to activate delegation with host: ${activateResult.error}`
|
|
20136
|
+
);
|
|
20137
|
+
}
|
|
20138
|
+
return {
|
|
20139
|
+
cid: result.cid,
|
|
20140
|
+
delegationHeader,
|
|
20141
|
+
spaceId: grant.session.spaceId,
|
|
20142
|
+
path: primary.path,
|
|
20143
|
+
actions: primary.actions,
|
|
20144
|
+
resources: result.resources,
|
|
20145
|
+
disableSubDelegation: false,
|
|
20146
|
+
expiry: result.expiry,
|
|
20147
|
+
delegateDID: did,
|
|
20148
|
+
ownerAddress: grant.delegation.ownerAddress,
|
|
20149
|
+
chainId: grant.delegation.chainId,
|
|
20150
|
+
host: targetHost
|
|
20151
|
+
};
|
|
20152
|
+
}
|
|
19937
20153
|
resolvePermissionSpace(space, session) {
|
|
19938
20154
|
if (space === void 0) {
|
|
19939
20155
|
return this.wasmBindings.makeSpaceId(
|
|
@@ -19950,6 +20166,223 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19950
20166
|
}
|
|
19951
20167
|
return this.wasmBindings.makeSpaceId(session.address, session.chainId, space);
|
|
19952
20168
|
}
|
|
20169
|
+
expandPermissionEntries(permissions) {
|
|
20170
|
+
return permissions.map((entry) => ({
|
|
20171
|
+
...entry,
|
|
20172
|
+
actions: (0, import_sdk_core5.expandActionShortNames)(entry.service, entry.actions)
|
|
20173
|
+
}));
|
|
20174
|
+
}
|
|
20175
|
+
shortServiceName(service) {
|
|
20176
|
+
const short = import_sdk_core5.SERVICE_LONG_TO_SHORT[service];
|
|
20177
|
+
if (short === void 0) {
|
|
20178
|
+
throw new Error(
|
|
20179
|
+
`unknown service '${service}' \u2014 no short-form mapping`
|
|
20180
|
+
);
|
|
20181
|
+
}
|
|
20182
|
+
return short;
|
|
20183
|
+
}
|
|
20184
|
+
permissionsToAbilities(entries) {
|
|
20185
|
+
const abilities = {};
|
|
20186
|
+
for (const entry of entries) {
|
|
20187
|
+
const service = this.shortServiceName(entry.service);
|
|
20188
|
+
abilities[service] ?? (abilities[service] = {});
|
|
20189
|
+
const existing = abilities[service][entry.path] ?? [];
|
|
20190
|
+
const seen = new Set(existing);
|
|
20191
|
+
for (const action of entry.actions) {
|
|
20192
|
+
if (!seen.has(action)) {
|
|
20193
|
+
existing.push(action);
|
|
20194
|
+
seen.add(action);
|
|
20195
|
+
}
|
|
20196
|
+
}
|
|
20197
|
+
abilities[service][entry.path] = existing;
|
|
20198
|
+
}
|
|
20199
|
+
return abilities;
|
|
20200
|
+
}
|
|
20201
|
+
permissionOperations(entries, spaceId) {
|
|
20202
|
+
return entries.flatMap((entry) => {
|
|
20203
|
+
const service = this.shortServiceName(entry.service);
|
|
20204
|
+
return entry.actions.map((action) => ({
|
|
20205
|
+
spaceId,
|
|
20206
|
+
service,
|
|
20207
|
+
path: entry.path,
|
|
20208
|
+
action
|
|
20209
|
+
}));
|
|
20210
|
+
});
|
|
20211
|
+
}
|
|
20212
|
+
sessionCoversPermissionEntries(session, entries) {
|
|
20213
|
+
try {
|
|
20214
|
+
const granted = (0, import_sdk_core5.parseRecapCapabilities)(
|
|
20215
|
+
(siwe) => this.wasmBindings.parseRecapFromSiwe(siwe),
|
|
20216
|
+
session.siwe
|
|
20217
|
+
);
|
|
20218
|
+
return (0, import_sdk_core5.isCapabilitySubset)(entries, granted).subset;
|
|
20219
|
+
} catch {
|
|
20220
|
+
return false;
|
|
20221
|
+
}
|
|
20222
|
+
}
|
|
20223
|
+
permissionEntriesToOperations(entries, session) {
|
|
20224
|
+
return entries.flatMap((entry) => {
|
|
20225
|
+
const spaceId = this.resolvePermissionSpace(entry.space, session);
|
|
20226
|
+
const service = this.shortServiceName(entry.service);
|
|
20227
|
+
return entry.actions.map((action) => ({
|
|
20228
|
+
spaceId,
|
|
20229
|
+
service,
|
|
20230
|
+
path: entry.path,
|
|
20231
|
+
action
|
|
20232
|
+
}));
|
|
20233
|
+
});
|
|
20234
|
+
}
|
|
20235
|
+
findRuntimeGrantsForPermissionEntries(entries, session) {
|
|
20236
|
+
const grants = [];
|
|
20237
|
+
const operations = this.permissionEntriesToOperations(entries, session);
|
|
20238
|
+
if (operations.length === 0) {
|
|
20239
|
+
return grants;
|
|
20240
|
+
}
|
|
20241
|
+
for (const operation of operations) {
|
|
20242
|
+
const grant = this.findGrantForOperation(operation);
|
|
20243
|
+
if (!grant) {
|
|
20244
|
+
return [];
|
|
20245
|
+
}
|
|
20246
|
+
if (!grants.includes(grant)) {
|
|
20247
|
+
grants.push(grant);
|
|
20248
|
+
}
|
|
20249
|
+
}
|
|
20250
|
+
return grants;
|
|
20251
|
+
}
|
|
20252
|
+
runtimeDelegationFromSession(delegatedSession, entries, spaceId, session, expiresAt) {
|
|
20253
|
+
const resources = this.delegatedResourcesForEntries(entries, spaceId);
|
|
20254
|
+
const primary = resources[0];
|
|
20255
|
+
return {
|
|
20256
|
+
cid: delegatedSession.delegationCid,
|
|
20257
|
+
delegationHeader: delegatedSession.delegationHeader,
|
|
20258
|
+
spaceId,
|
|
20259
|
+
path: primary.path,
|
|
20260
|
+
actions: primary.actions,
|
|
20261
|
+
resources,
|
|
20262
|
+
disableSubDelegation: false,
|
|
20263
|
+
expiry: expiresAt,
|
|
20264
|
+
delegateDID: session.verificationMethod,
|
|
20265
|
+
ownerAddress: session.address,
|
|
20266
|
+
chainId: session.chainId,
|
|
20267
|
+
host: this.config.host
|
|
20268
|
+
};
|
|
20269
|
+
}
|
|
20270
|
+
runtimeGrantFromDelegation(delegation, session) {
|
|
20271
|
+
const operations = this.operationsFromDelegation(delegation);
|
|
20272
|
+
return {
|
|
20273
|
+
session: {
|
|
20274
|
+
delegationHeader: delegation.delegationHeader,
|
|
20275
|
+
delegationCid: delegation.cid,
|
|
20276
|
+
spaceId: delegation.spaceId,
|
|
20277
|
+
verificationMethod: session.verificationMethod,
|
|
20278
|
+
jwk: session.jwk
|
|
20279
|
+
},
|
|
20280
|
+
delegation,
|
|
20281
|
+
operations,
|
|
20282
|
+
expiresAt: delegation.expiry
|
|
20283
|
+
};
|
|
20284
|
+
}
|
|
20285
|
+
delegatedResourcesForEntries(entries, spaceId) {
|
|
20286
|
+
return entries.map((entry) => ({
|
|
20287
|
+
service: this.shortServiceName(entry.service),
|
|
20288
|
+
space: spaceId,
|
|
20289
|
+
path: entry.path,
|
|
20290
|
+
actions: [...entry.actions]
|
|
20291
|
+
}));
|
|
20292
|
+
}
|
|
20293
|
+
operationsFromDelegation(delegation) {
|
|
20294
|
+
const resources = delegation.resources !== void 0 && delegation.resources.length > 0 ? delegation.resources : this.flatDelegationResources(delegation);
|
|
20295
|
+
return resources.flatMap(
|
|
20296
|
+
(resource) => resource.actions.map((action) => ({
|
|
20297
|
+
spaceId: resource.space,
|
|
20298
|
+
service: this.invocationServiceName(resource.service),
|
|
20299
|
+
path: resource.path,
|
|
20300
|
+
action
|
|
20301
|
+
}))
|
|
20302
|
+
);
|
|
20303
|
+
}
|
|
20304
|
+
flatDelegationResources(delegation) {
|
|
20305
|
+
const byService = /* @__PURE__ */ new Map();
|
|
20306
|
+
for (const action of delegation.actions) {
|
|
20307
|
+
const service = this.shortServiceName(action.split("/")[0]);
|
|
20308
|
+
const actions = byService.get(service) ?? [];
|
|
20309
|
+
actions.push(action);
|
|
20310
|
+
byService.set(service, actions);
|
|
20311
|
+
}
|
|
20312
|
+
return [...byService.entries()].map(([service, actions]) => ({
|
|
20313
|
+
service,
|
|
20314
|
+
space: delegation.spaceId,
|
|
20315
|
+
path: delegation.path,
|
|
20316
|
+
actions
|
|
20317
|
+
}));
|
|
20318
|
+
}
|
|
20319
|
+
selectInvocationSession(fallback, service, path, action) {
|
|
20320
|
+
const grant = this.findGrantForOperation({
|
|
20321
|
+
spaceId: fallback.spaceId,
|
|
20322
|
+
service: this.invocationServiceName(service),
|
|
20323
|
+
path,
|
|
20324
|
+
action
|
|
20325
|
+
});
|
|
20326
|
+
return grant?.session ?? fallback;
|
|
20327
|
+
}
|
|
20328
|
+
findGrantForOperations(operations) {
|
|
20329
|
+
if (operations.length === 0) {
|
|
20330
|
+
return void 0;
|
|
20331
|
+
}
|
|
20332
|
+
this.pruneExpiredRuntimePermissionGrants();
|
|
20333
|
+
return this.runtimePermissionGrants.find((grant) => {
|
|
20334
|
+
return operations.every(
|
|
20335
|
+
(operation) => grant.operations.some(
|
|
20336
|
+
(granted) => this.operationCovers(granted, operation)
|
|
20337
|
+
)
|
|
20338
|
+
);
|
|
20339
|
+
});
|
|
20340
|
+
}
|
|
20341
|
+
findGrantForOperation(operation) {
|
|
20342
|
+
return this.findGrantForOperations([operation]);
|
|
20343
|
+
}
|
|
20344
|
+
pruneExpiredRuntimePermissionGrants() {
|
|
20345
|
+
const now = Date.now();
|
|
20346
|
+
this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
|
|
20347
|
+
(grant) => grant.expiresAt.getTime() > now
|
|
20348
|
+
);
|
|
20349
|
+
}
|
|
20350
|
+
operationCovers(granted, requested) {
|
|
20351
|
+
return granted.spaceId === requested.spaceId && granted.service === requested.service && this.actionContains(granted.action, requested.action) && this.pathContains(granted.path, requested.path);
|
|
20352
|
+
}
|
|
20353
|
+
actionContains(grantedAction, requestedAction) {
|
|
20354
|
+
if (grantedAction === requestedAction) {
|
|
20355
|
+
return true;
|
|
20356
|
+
}
|
|
20357
|
+
if (grantedAction.endsWith("/*")) {
|
|
20358
|
+
const prefix = grantedAction.slice(0, -2);
|
|
20359
|
+
return requestedAction.startsWith(`${prefix}/`);
|
|
20360
|
+
}
|
|
20361
|
+
return false;
|
|
20362
|
+
}
|
|
20363
|
+
invocationServiceName(service) {
|
|
20364
|
+
return service.startsWith("tinycloud.") ? this.shortServiceName(service) : service;
|
|
20365
|
+
}
|
|
20366
|
+
pathContains(grantedPath, requestedPath) {
|
|
20367
|
+
if (grantedPath === "" || grantedPath === "/") {
|
|
20368
|
+
return true;
|
|
20369
|
+
}
|
|
20370
|
+
if (grantedPath.endsWith("/**")) {
|
|
20371
|
+
return requestedPath.startsWith(grantedPath.slice(0, -3));
|
|
20372
|
+
}
|
|
20373
|
+
if (grantedPath.endsWith("/*")) {
|
|
20374
|
+
const prefix = grantedPath.slice(0, -2);
|
|
20375
|
+
if (!requestedPath.startsWith(prefix)) {
|
|
20376
|
+
return false;
|
|
20377
|
+
}
|
|
20378
|
+
const remainder = requestedPath.slice(prefix.length);
|
|
20379
|
+
return !remainder.includes("/") || remainder === "/";
|
|
20380
|
+
}
|
|
20381
|
+
if (grantedPath.endsWith("/")) {
|
|
20382
|
+
return requestedPath.startsWith(grantedPath);
|
|
20383
|
+
}
|
|
20384
|
+
return grantedPath === requestedPath;
|
|
20385
|
+
}
|
|
19953
20386
|
/**
|
|
19954
20387
|
* Issue a delegation via the legacy wallet-signed SIWE path for a single
|
|
19955
20388
|
* {@link PermissionEntry}. Shares the implementation with the public
|