@tinycloud/node-sdk 2.2.0-beta.7 → 2.2.0-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{core-C3s0bgRe.d.cts → core-DcJ27GsA.d.cts} +58 -4
- package/dist/{core-C3s0bgRe.d.ts → core-DcJ27GsA.d.ts} +58 -4
- package/dist/core.cjs +499 -70
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +2 -2
- package/dist/core.d.ts +2 -2
- package/dist/core.js +502 -73
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +499 -70
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +502 -73
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -17172,7 +17172,7 @@ import {
|
|
|
17172
17172
|
ACCOUNT_REGISTRY_SPACE,
|
|
17173
17173
|
PermissionNotInManifestError,
|
|
17174
17174
|
SessionExpiredError,
|
|
17175
|
-
|
|
17175
|
+
expandPermissionEntries as expandPermissionEntriesCore,
|
|
17176
17176
|
isCapabilitySubset,
|
|
17177
17177
|
parseRecapCapabilities,
|
|
17178
17178
|
SERVICE_LONG_TO_SHORT
|
|
@@ -18264,49 +18264,29 @@ function secretsError(code, message, cause) {
|
|
|
18264
18264
|
}
|
|
18265
18265
|
};
|
|
18266
18266
|
}
|
|
18267
|
-
function
|
|
18268
|
-
return
|
|
18267
|
+
function displayActionUrn(action) {
|
|
18268
|
+
return action === "put" ? "tinycloud.vault/write" : "tinycloud.vault/delete";
|
|
18269
18269
|
}
|
|
18270
|
-
function
|
|
18271
|
-
return
|
|
18270
|
+
function kvActionUrn(action) {
|
|
18271
|
+
return `tinycloud.kv/${action}`;
|
|
18272
18272
|
}
|
|
18273
18273
|
function secretPermissionEntries(name, action) {
|
|
18274
18274
|
return [
|
|
18275
18275
|
{
|
|
18276
|
-
service: "tinycloud.
|
|
18277
|
-
space: SECRETS_SPACE,
|
|
18278
|
-
path: secretResourcePath("keys", name),
|
|
18279
|
-
actions: [action],
|
|
18280
|
-
skipPrefix: true
|
|
18281
|
-
},
|
|
18282
|
-
{
|
|
18283
|
-
service: "tinycloud.kv",
|
|
18276
|
+
service: "tinycloud.vault",
|
|
18284
18277
|
space: SECRETS_SPACE,
|
|
18285
|
-
path:
|
|
18286
|
-
actions: [action],
|
|
18278
|
+
path: `${SECRET_PREFIX}${name}`,
|
|
18279
|
+
actions: [action === "put" ? "write" : "delete"],
|
|
18287
18280
|
skipPrefix: true
|
|
18288
18281
|
}
|
|
18289
18282
|
];
|
|
18290
18283
|
}
|
|
18284
|
+
function secretResourcePath(base2, name) {
|
|
18285
|
+
return `${base2}/${SECRET_PREFIX}${name}`;
|
|
18286
|
+
}
|
|
18291
18287
|
function isSecretsSpace(space) {
|
|
18292
18288
|
return space === SECRETS_SPACE || space.endsWith(`:${SECRETS_SPACE}`);
|
|
18293
18289
|
}
|
|
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
18290
|
var NodeSecretsService = class {
|
|
18311
18291
|
constructor(config) {
|
|
18312
18292
|
this.config = config;
|
|
@@ -18319,10 +18299,11 @@ var NodeSecretsService = class {
|
|
|
18319
18299
|
return this.service.isUnlocked;
|
|
18320
18300
|
}
|
|
18321
18301
|
async unlock(signer) {
|
|
18322
|
-
|
|
18323
|
-
|
|
18302
|
+
const effectiveSigner = signer ?? this.config.getUnlockSigner?.();
|
|
18303
|
+
if (effectiveSigner !== void 0) {
|
|
18304
|
+
this.unlockSigner = effectiveSigner;
|
|
18324
18305
|
}
|
|
18325
|
-
const result = await this.service.unlock(
|
|
18306
|
+
const result = await this.service.unlock(effectiveSigner);
|
|
18326
18307
|
if (result.ok) {
|
|
18327
18308
|
this.shouldRestoreUnlock = true;
|
|
18328
18309
|
}
|
|
@@ -18364,29 +18345,16 @@ var NodeSecretsService = class {
|
|
|
18364
18345
|
if (!this.config.canEscalate()) {
|
|
18365
18346
|
return secretsError(
|
|
18366
18347
|
ErrorCodes.PERMISSION_DENIED,
|
|
18367
|
-
`Cannot autosign ${
|
|
18368
|
-
);
|
|
18369
|
-
}
|
|
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.`
|
|
18348
|
+
`Cannot autosign ${displayActionUrn(action)} for ${name}; TinyCloudNode needs wallet mode with a signer or privateKey.`
|
|
18375
18349
|
);
|
|
18376
18350
|
}
|
|
18377
18351
|
try {
|
|
18378
|
-
this.config.
|
|
18379
|
-
composeEscalatedManifest(
|
|
18380
|
-
manifest,
|
|
18381
|
-
secretPermissionEntries(name, action)
|
|
18382
|
-
)
|
|
18383
|
-
);
|
|
18384
|
-
await this.config.signIn();
|
|
18352
|
+
await this.config.grantPermissions(secretPermissionEntries(name, action));
|
|
18385
18353
|
return this.restoreUnlockAfterEscalation();
|
|
18386
18354
|
} catch (error) {
|
|
18387
18355
|
return secretsError(
|
|
18388
18356
|
ErrorCodes.PERMISSION_DENIED,
|
|
18389
|
-
error instanceof Error ? error.message : `Autosign escalation for ${
|
|
18357
|
+
error instanceof Error ? error.message : `Autosign escalation for ${displayActionUrn(action)} on ${name} failed.`,
|
|
18390
18358
|
error instanceof Error ? error : void 0
|
|
18391
18359
|
);
|
|
18392
18360
|
}
|
|
@@ -18403,7 +18371,7 @@ var NodeSecretsService = class {
|
|
|
18403
18371
|
return false;
|
|
18404
18372
|
}
|
|
18405
18373
|
const manifests = Array.isArray(manifest) ? manifest : [manifest];
|
|
18406
|
-
const requiredAction =
|
|
18374
|
+
const requiredAction = kvActionUrn(action);
|
|
18407
18375
|
return manifests.some((entry) => {
|
|
18408
18376
|
const resolved = resolveManifest(entry);
|
|
18409
18377
|
return ["keys", "vault"].every(
|
|
@@ -18446,6 +18414,30 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18446
18414
|
this.auth = null;
|
|
18447
18415
|
this.tc = null;
|
|
18448
18416
|
this._chainId = 1;
|
|
18417
|
+
this.runtimePermissionGrants = [];
|
|
18418
|
+
this.invokeWithRuntimePermissions = (session, service, path, action, facts) => {
|
|
18419
|
+
return this.wasmBindings.invoke(
|
|
18420
|
+
this.selectInvocationSession(session, service, path, action),
|
|
18421
|
+
service,
|
|
18422
|
+
path,
|
|
18423
|
+
action,
|
|
18424
|
+
facts
|
|
18425
|
+
);
|
|
18426
|
+
};
|
|
18427
|
+
this.invokeAnyWithRuntimePermissions = (session, entries, facts) => {
|
|
18428
|
+
if (!this.wasmBindings.invokeAny) {
|
|
18429
|
+
throw new Error("WASM binding does not support invokeAny");
|
|
18430
|
+
}
|
|
18431
|
+
const grant = this.findGrantForOperations(
|
|
18432
|
+
entries.map((entry) => ({
|
|
18433
|
+
spaceId: entry.spaceId,
|
|
18434
|
+
service: this.invocationServiceName(entry.service),
|
|
18435
|
+
path: entry.path,
|
|
18436
|
+
action: entry.action
|
|
18437
|
+
}))
|
|
18438
|
+
);
|
|
18439
|
+
return this.wasmBindings.invokeAny(grant?.session ?? session, entries, facts);
|
|
18440
|
+
};
|
|
18449
18441
|
this.explicitHost = config.host;
|
|
18450
18442
|
this.config = {
|
|
18451
18443
|
...config,
|
|
@@ -18481,7 +18473,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18481
18473
|
this._sharingService = new SharingService({
|
|
18482
18474
|
hosts: [this.config.host],
|
|
18483
18475
|
// session: undefined - not needed for receive()
|
|
18484
|
-
invoke: this.
|
|
18476
|
+
invoke: this.invokeWithRuntimePermissions,
|
|
18485
18477
|
fetch: globalThis.fetch.bind(globalThis),
|
|
18486
18478
|
keyProvider: this._keyProvider,
|
|
18487
18479
|
registry: this._capabilityRegistry,
|
|
@@ -18549,7 +18541,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18549
18541
|
includeAccountRegistryPermissions: config.includeAccountRegistryPermissions
|
|
18550
18542
|
});
|
|
18551
18543
|
this.tc = new TinyCloud(this.auth, {
|
|
18552
|
-
invokeAny: this.
|
|
18544
|
+
invokeAny: this.invokeAnyWithRuntimePermissions
|
|
18553
18545
|
});
|
|
18554
18546
|
}
|
|
18555
18547
|
syncResolvedHostFromAuth() {
|
|
@@ -18674,6 +18666,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18674
18666
|
this._secrets = void 0;
|
|
18675
18667
|
this._spaceService = void 0;
|
|
18676
18668
|
this._serviceContext = void 0;
|
|
18669
|
+
this.runtimePermissionGrants = [];
|
|
18677
18670
|
await this.tc.signIn(options);
|
|
18678
18671
|
this.syncResolvedHostFromAuth();
|
|
18679
18672
|
this.initializeServices();
|
|
@@ -18763,6 +18756,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18763
18756
|
this._secrets = void 0;
|
|
18764
18757
|
this._spaceService = void 0;
|
|
18765
18758
|
this._serviceContext = void 0;
|
|
18759
|
+
this.runtimePermissionGrants = [];
|
|
18766
18760
|
if (sessionData.address) {
|
|
18767
18761
|
this._address = sessionData.address;
|
|
18768
18762
|
}
|
|
@@ -18770,8 +18764,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18770
18764
|
this._chainId = sessionData.chainId;
|
|
18771
18765
|
}
|
|
18772
18766
|
this._serviceContext = new ServiceContext2({
|
|
18773
|
-
invoke: this.
|
|
18774
|
-
invokeAny: this.
|
|
18767
|
+
invoke: this.invokeWithRuntimePermissions,
|
|
18768
|
+
invokeAny: this.invokeAnyWithRuntimePermissions,
|
|
18775
18769
|
fetch: globalThis.fetch.bind(globalThis),
|
|
18776
18770
|
hosts: [this.config.host]
|
|
18777
18771
|
});
|
|
@@ -18857,7 +18851,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18857
18851
|
includeAccountRegistryPermissions: this.config.includeAccountRegistryPermissions
|
|
18858
18852
|
});
|
|
18859
18853
|
this.tc = new TinyCloud(this.auth, {
|
|
18860
|
-
invokeAny: this.
|
|
18854
|
+
invokeAny: this.invokeAnyWithRuntimePermissions
|
|
18861
18855
|
});
|
|
18862
18856
|
this.config.prefix = prefix;
|
|
18863
18857
|
}
|
|
@@ -18901,7 +18895,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18901
18895
|
includeAccountRegistryPermissions: this.config.includeAccountRegistryPermissions
|
|
18902
18896
|
});
|
|
18903
18897
|
this.tc = new TinyCloud(this.auth, {
|
|
18904
|
-
invokeAny: this.
|
|
18898
|
+
invokeAny: this.invokeAnyWithRuntimePermissions
|
|
18905
18899
|
});
|
|
18906
18900
|
this.config.prefix = prefix;
|
|
18907
18901
|
}
|
|
@@ -18914,10 +18908,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
18914
18908
|
if (!session) {
|
|
18915
18909
|
return;
|
|
18916
18910
|
}
|
|
18917
|
-
this.tc.initializeServices(this.
|
|
18911
|
+
this.tc.initializeServices(this.invokeWithRuntimePermissions, [this.config.host]);
|
|
18918
18912
|
this._serviceContext = new ServiceContext2({
|
|
18919
|
-
invoke: this.
|
|
18920
|
-
invokeAny: this.
|
|
18913
|
+
invoke: this.invokeWithRuntimePermissions,
|
|
18914
|
+
invokeAny: this.invokeAnyWithRuntimePermissions,
|
|
18921
18915
|
fetch: globalThis.fetch.bind(globalThis),
|
|
18922
18916
|
hosts: [this.config.host]
|
|
18923
18917
|
});
|
|
@@ -19087,7 +19081,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19087
19081
|
this._delegationManager = new DelegationManager({
|
|
19088
19082
|
hosts: [this.config.host],
|
|
19089
19083
|
session: serviceSession,
|
|
19090
|
-
invoke: this.
|
|
19084
|
+
invoke: this.invokeWithRuntimePermissions,
|
|
19091
19085
|
fetch: globalThis.fetch.bind(globalThis)
|
|
19092
19086
|
});
|
|
19093
19087
|
this._spaceService = new SpaceService({
|
|
@@ -19354,9 +19348,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19354
19348
|
this._secrets = new NodeSecretsService({
|
|
19355
19349
|
getService: () => this.getBaseSecrets(),
|
|
19356
19350
|
getManifest: () => this.manifest,
|
|
19357
|
-
|
|
19358
|
-
|
|
19359
|
-
|
|
19351
|
+
grantPermissions: (additional) => this.grantRuntimePermissions(additional),
|
|
19352
|
+
canEscalate: () => this.signer !== void 0 && this.tc !== void 0,
|
|
19353
|
+
getUnlockSigner: () => this.signer ?? void 0
|
|
19360
19354
|
});
|
|
19361
19355
|
}
|
|
19362
19356
|
return this._secrets;
|
|
@@ -19440,6 +19434,171 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19440
19434
|
}
|
|
19441
19435
|
};
|
|
19442
19436
|
}
|
|
19437
|
+
/**
|
|
19438
|
+
* Check whether the current session or an approved runtime delegation covers
|
|
19439
|
+
* every requested permission.
|
|
19440
|
+
*/
|
|
19441
|
+
hasRuntimePermissions(permissions) {
|
|
19442
|
+
const session = this.auth?.tinyCloudSession;
|
|
19443
|
+
if (!session || !Array.isArray(permissions) || permissions.length === 0) {
|
|
19444
|
+
return false;
|
|
19445
|
+
}
|
|
19446
|
+
const expanded = this.expandPermissionEntries(permissions);
|
|
19447
|
+
if (this.sessionCoversPermissionEntries(session, expanded)) {
|
|
19448
|
+
return true;
|
|
19449
|
+
}
|
|
19450
|
+
return this.findRuntimeGrantsForPermissionEntries(expanded, session).length > 0;
|
|
19451
|
+
}
|
|
19452
|
+
/**
|
|
19453
|
+
* Return installed runtime permission delegations. When `permissions` is
|
|
19454
|
+
* provided, only delegations currently covering those permissions are
|
|
19455
|
+
* returned. Base-session manifest permissions are not represented here.
|
|
19456
|
+
*/
|
|
19457
|
+
getRuntimePermissionDelegations(permissions) {
|
|
19458
|
+
this.pruneExpiredRuntimePermissionGrants();
|
|
19459
|
+
if (permissions === void 0) {
|
|
19460
|
+
return this.runtimePermissionGrants.map((grant) => grant.delegation);
|
|
19461
|
+
}
|
|
19462
|
+
const session = this.auth?.tinyCloudSession;
|
|
19463
|
+
if (!session || !Array.isArray(permissions) || permissions.length === 0) {
|
|
19464
|
+
return [];
|
|
19465
|
+
}
|
|
19466
|
+
const expanded = this.expandPermissionEntries(permissions);
|
|
19467
|
+
return this.findRuntimeGrantsForPermissionEntries(expanded, session).map(
|
|
19468
|
+
(grant) => grant.delegation
|
|
19469
|
+
);
|
|
19470
|
+
}
|
|
19471
|
+
/**
|
|
19472
|
+
* Install a portable runtime permission delegation into this SDK instance so
|
|
19473
|
+
* matching service calls and downstream `delegateTo()` calls can use it.
|
|
19474
|
+
*/
|
|
19475
|
+
async useRuntimeDelegation(delegation) {
|
|
19476
|
+
const session = this.auth?.tinyCloudSession;
|
|
19477
|
+
if (!session) {
|
|
19478
|
+
throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
19479
|
+
}
|
|
19480
|
+
if (delegation.expiry.getTime() <= Date.now()) {
|
|
19481
|
+
throw new SessionExpiredError(delegation.expiry);
|
|
19482
|
+
}
|
|
19483
|
+
const expectedDids = /* @__PURE__ */ new Set([session.verificationMethod, this.sessionDid]);
|
|
19484
|
+
if (!expectedDids.has(delegation.delegateDID)) {
|
|
19485
|
+
throw new Error(
|
|
19486
|
+
`Runtime delegation targets ${delegation.delegateDID} but this session key is ${session.verificationMethod}.`
|
|
19487
|
+
);
|
|
19488
|
+
}
|
|
19489
|
+
const targetHost = delegation.host ?? this.config.host;
|
|
19490
|
+
const activateResult = await activateSessionWithHost2(
|
|
19491
|
+
targetHost,
|
|
19492
|
+
delegation.delegationHeader
|
|
19493
|
+
);
|
|
19494
|
+
if (!activateResult.success) {
|
|
19495
|
+
throw new Error(
|
|
19496
|
+
`Failed to activate runtime permission delegation: ${activateResult.error}`
|
|
19497
|
+
);
|
|
19498
|
+
}
|
|
19499
|
+
this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
|
|
19500
|
+
(grant) => grant.delegation.cid !== delegation.cid
|
|
19501
|
+
);
|
|
19502
|
+
this.runtimePermissionGrants.push(
|
|
19503
|
+
this.runtimeGrantFromDelegation(delegation, session)
|
|
19504
|
+
);
|
|
19505
|
+
}
|
|
19506
|
+
/**
|
|
19507
|
+
* Store additional permissions as narrow delegations to the current session
|
|
19508
|
+
* key. Future service invocations automatically use a stored delegation when
|
|
19509
|
+
* its `(space, service, path, action)` covers the request.
|
|
19510
|
+
*/
|
|
19511
|
+
async grantRuntimePermissions(permissions, options) {
|
|
19512
|
+
if (!Array.isArray(permissions) || permissions.length === 0) {
|
|
19513
|
+
throw new Error("grantRuntimePermissions requires a non-empty permissions array");
|
|
19514
|
+
}
|
|
19515
|
+
const session = this.auth?.tinyCloudSession;
|
|
19516
|
+
if (!session) {
|
|
19517
|
+
throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
19518
|
+
}
|
|
19519
|
+
const sessionExpiry = extractSiweExpiration(session.siwe);
|
|
19520
|
+
if (sessionExpiry !== void 0) {
|
|
19521
|
+
const marginMs = _TinyCloudNode.SESSION_EXPIRY_SAFETY_MARGIN_MS;
|
|
19522
|
+
if (sessionExpiry.getTime() <= Date.now() + marginMs) {
|
|
19523
|
+
throw new SessionExpiredError(sessionExpiry);
|
|
19524
|
+
}
|
|
19525
|
+
}
|
|
19526
|
+
const expanded = this.expandPermissionEntries(permissions);
|
|
19527
|
+
if (this.sessionCoversPermissionEntries(session, expanded)) {
|
|
19528
|
+
return [];
|
|
19529
|
+
}
|
|
19530
|
+
const existingGrants = this.findRuntimeGrantsForPermissionEntries(expanded, session);
|
|
19531
|
+
if (existingGrants.length > 0) {
|
|
19532
|
+
return existingGrants.map((grant) => grant.delegation);
|
|
19533
|
+
}
|
|
19534
|
+
if (!this.signer) {
|
|
19535
|
+
throw new Error(
|
|
19536
|
+
"grantRuntimePermissions requires wallet mode with a signer or privateKey."
|
|
19537
|
+
);
|
|
19538
|
+
}
|
|
19539
|
+
const bySpace = /* @__PURE__ */ new Map();
|
|
19540
|
+
for (const entry of expanded) {
|
|
19541
|
+
const spaceId = this.resolvePermissionSpace(entry.space, session);
|
|
19542
|
+
const current = bySpace.get(spaceId) ?? [];
|
|
19543
|
+
current.push(entry);
|
|
19544
|
+
bySpace.set(spaceId, current);
|
|
19545
|
+
}
|
|
19546
|
+
const now = /* @__PURE__ */ new Date();
|
|
19547
|
+
const requestedExpiryMs = resolveExpiryMs(options?.expiry);
|
|
19548
|
+
let expiresAt = new Date(now.getTime() + requestedExpiryMs);
|
|
19549
|
+
if (sessionExpiry !== void 0 && sessionExpiry < expiresAt) {
|
|
19550
|
+
expiresAt = sessionExpiry;
|
|
19551
|
+
}
|
|
19552
|
+
const delegations = [];
|
|
19553
|
+
for (const [spaceId, entries] of bySpace) {
|
|
19554
|
+
const abilities = this.permissionsToAbilities(entries);
|
|
19555
|
+
const prepared = this.wasmBindings.prepareSession({
|
|
19556
|
+
abilities,
|
|
19557
|
+
address: this.wasmBindings.ensureEip55(session.address),
|
|
19558
|
+
chainId: session.chainId,
|
|
19559
|
+
domain: this.siweDomain,
|
|
19560
|
+
issuedAt: now.toISOString(),
|
|
19561
|
+
expirationTime: expiresAt.toISOString(),
|
|
19562
|
+
spaceId,
|
|
19563
|
+
jwk: session.jwk
|
|
19564
|
+
});
|
|
19565
|
+
const signature2 = await this.signer.signMessage(prepared.siwe);
|
|
19566
|
+
const delegatedSession = this.wasmBindings.completeSessionSetup({
|
|
19567
|
+
...prepared,
|
|
19568
|
+
signature: signature2
|
|
19569
|
+
});
|
|
19570
|
+
const activateResult = await activateSessionWithHost2(
|
|
19571
|
+
this.config.host,
|
|
19572
|
+
delegatedSession.delegationHeader
|
|
19573
|
+
);
|
|
19574
|
+
if (!activateResult.success) {
|
|
19575
|
+
throw new Error(
|
|
19576
|
+
`Failed to activate runtime permission delegation: ${activateResult.error}`
|
|
19577
|
+
);
|
|
19578
|
+
}
|
|
19579
|
+
const delegation = this.runtimeDelegationFromSession(
|
|
19580
|
+
delegatedSession,
|
|
19581
|
+
entries,
|
|
19582
|
+
spaceId,
|
|
19583
|
+
session,
|
|
19584
|
+
expiresAt
|
|
19585
|
+
);
|
|
19586
|
+
this.runtimePermissionGrants.push({
|
|
19587
|
+
session: {
|
|
19588
|
+
delegationHeader: delegatedSession.delegationHeader,
|
|
19589
|
+
delegationCid: delegatedSession.delegationCid,
|
|
19590
|
+
spaceId,
|
|
19591
|
+
verificationMethod: session.verificationMethod,
|
|
19592
|
+
jwk: session.jwk
|
|
19593
|
+
},
|
|
19594
|
+
delegation,
|
|
19595
|
+
operations: this.permissionOperations(entries, spaceId),
|
|
19596
|
+
expiresAt
|
|
19597
|
+
});
|
|
19598
|
+
delegations.push(delegation);
|
|
19599
|
+
}
|
|
19600
|
+
return delegations;
|
|
19601
|
+
}
|
|
19443
19602
|
/**
|
|
19444
19603
|
* Get the DelegationManager for delegation CRUD operations.
|
|
19445
19604
|
*
|
|
@@ -19628,7 +19787,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19628
19787
|
if (this._serviceContext) {
|
|
19629
19788
|
const publicKV = new KVService2({ prefix: "" });
|
|
19630
19789
|
const publicContext = new ServiceContext2({
|
|
19631
|
-
invoke: this.
|
|
19790
|
+
invoke: this.invokeWithRuntimePermissions,
|
|
19632
19791
|
fetch: this._serviceContext.fetch,
|
|
19633
19792
|
hosts: this._serviceContext.hosts
|
|
19634
19793
|
});
|
|
@@ -19716,8 +19875,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19716
19875
|
* Issue a delegation using the capability-chain flow.
|
|
19717
19876
|
*
|
|
19718
19877
|
* When every requested permission is a subset of the current
|
|
19719
|
-
* session's recap,
|
|
19720
|
-
*
|
|
19878
|
+
* session's recap, or of one installed runtime permission delegation,
|
|
19879
|
+
* the delegation is signed by the session key via WASM — no wallet
|
|
19880
|
+
* prompt. When at least one is NOT derivable, a
|
|
19721
19881
|
* {@link PermissionNotInManifestError} is raised (carrying the
|
|
19722
19882
|
* missing entries) so the caller can trigger an escalation flow
|
|
19723
19883
|
* (e.g. `TinyCloudWeb.requestPermissions`). Passing
|
|
@@ -19767,10 +19927,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19767
19927
|
"delegateTo requires a non-empty permissions array"
|
|
19768
19928
|
);
|
|
19769
19929
|
}
|
|
19770
|
-
const expandedEntries =
|
|
19771
|
-
...entry,
|
|
19772
|
-
actions: expandActionShortNames(entry.service, entry.actions)
|
|
19773
|
-
}));
|
|
19930
|
+
const expandedEntries = this.expandPermissionEntries(permissions);
|
|
19774
19931
|
const now = /* @__PURE__ */ new Date();
|
|
19775
19932
|
const expiryMs = resolveExpiryMs(options?.expiry);
|
|
19776
19933
|
const expirationTime = new Date(now.getTime() + expiryMs);
|
|
@@ -19797,6 +19954,23 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19797
19954
|
);
|
|
19798
19955
|
const { subset, missing } = isCapabilitySubset(expandedEntries, granted);
|
|
19799
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 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
|
+
}
|
|
19800
19974
|
throw new PermissionNotInManifestError(missing, granted);
|
|
19801
19975
|
}
|
|
19802
19976
|
const delegation = await this.createDelegationViaWasmPath(
|
|
@@ -19941,6 +20115,41 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19941
20115
|
host: this.config.host
|
|
19942
20116
|
};
|
|
19943
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 activateSessionWithHost2(
|
|
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
|
+
}
|
|
19944
20153
|
resolvePermissionSpace(space, session) {
|
|
19945
20154
|
if (space === void 0) {
|
|
19946
20155
|
return this.wasmBindings.makeSpaceId(
|
|
@@ -19957,6 +20166,220 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19957
20166
|
}
|
|
19958
20167
|
return this.wasmBindings.makeSpaceId(session.address, session.chainId, space);
|
|
19959
20168
|
}
|
|
20169
|
+
expandPermissionEntries(permissions) {
|
|
20170
|
+
return expandPermissionEntriesCore(permissions);
|
|
20171
|
+
}
|
|
20172
|
+
shortServiceName(service) {
|
|
20173
|
+
const short = SERVICE_LONG_TO_SHORT[service];
|
|
20174
|
+
if (short === void 0) {
|
|
20175
|
+
throw new Error(
|
|
20176
|
+
`unknown service '${service}' \u2014 no short-form mapping`
|
|
20177
|
+
);
|
|
20178
|
+
}
|
|
20179
|
+
return short;
|
|
20180
|
+
}
|
|
20181
|
+
permissionsToAbilities(entries) {
|
|
20182
|
+
const abilities = {};
|
|
20183
|
+
for (const entry of entries) {
|
|
20184
|
+
const service = this.shortServiceName(entry.service);
|
|
20185
|
+
abilities[service] ?? (abilities[service] = {});
|
|
20186
|
+
const existing = abilities[service][entry.path] ?? [];
|
|
20187
|
+
const seen = new Set(existing);
|
|
20188
|
+
for (const action of entry.actions) {
|
|
20189
|
+
if (!seen.has(action)) {
|
|
20190
|
+
existing.push(action);
|
|
20191
|
+
seen.add(action);
|
|
20192
|
+
}
|
|
20193
|
+
}
|
|
20194
|
+
abilities[service][entry.path] = existing;
|
|
20195
|
+
}
|
|
20196
|
+
return abilities;
|
|
20197
|
+
}
|
|
20198
|
+
permissionOperations(entries, spaceId) {
|
|
20199
|
+
return entries.flatMap((entry) => {
|
|
20200
|
+
const service = this.shortServiceName(entry.service);
|
|
20201
|
+
return entry.actions.map((action) => ({
|
|
20202
|
+
spaceId,
|
|
20203
|
+
service,
|
|
20204
|
+
path: entry.path,
|
|
20205
|
+
action
|
|
20206
|
+
}));
|
|
20207
|
+
});
|
|
20208
|
+
}
|
|
20209
|
+
sessionCoversPermissionEntries(session, entries) {
|
|
20210
|
+
try {
|
|
20211
|
+
const granted = parseRecapCapabilities(
|
|
20212
|
+
(siwe) => this.wasmBindings.parseRecapFromSiwe(siwe),
|
|
20213
|
+
session.siwe
|
|
20214
|
+
);
|
|
20215
|
+
return isCapabilitySubset(entries, granted).subset;
|
|
20216
|
+
} catch {
|
|
20217
|
+
return false;
|
|
20218
|
+
}
|
|
20219
|
+
}
|
|
20220
|
+
permissionEntriesToOperations(entries, session) {
|
|
20221
|
+
return entries.flatMap((entry) => {
|
|
20222
|
+
const spaceId = this.resolvePermissionSpace(entry.space, session);
|
|
20223
|
+
const service = this.shortServiceName(entry.service);
|
|
20224
|
+
return entry.actions.map((action) => ({
|
|
20225
|
+
spaceId,
|
|
20226
|
+
service,
|
|
20227
|
+
path: entry.path,
|
|
20228
|
+
action
|
|
20229
|
+
}));
|
|
20230
|
+
});
|
|
20231
|
+
}
|
|
20232
|
+
findRuntimeGrantsForPermissionEntries(entries, session) {
|
|
20233
|
+
const grants = [];
|
|
20234
|
+
const operations = this.permissionEntriesToOperations(entries, session);
|
|
20235
|
+
if (operations.length === 0) {
|
|
20236
|
+
return grants;
|
|
20237
|
+
}
|
|
20238
|
+
for (const operation of operations) {
|
|
20239
|
+
const grant = this.findGrantForOperation(operation);
|
|
20240
|
+
if (!grant) {
|
|
20241
|
+
return [];
|
|
20242
|
+
}
|
|
20243
|
+
if (!grants.includes(grant)) {
|
|
20244
|
+
grants.push(grant);
|
|
20245
|
+
}
|
|
20246
|
+
}
|
|
20247
|
+
return grants;
|
|
20248
|
+
}
|
|
20249
|
+
runtimeDelegationFromSession(delegatedSession, entries, spaceId, session, expiresAt) {
|
|
20250
|
+
const resources = this.delegatedResourcesForEntries(entries, spaceId);
|
|
20251
|
+
const primary = resources[0];
|
|
20252
|
+
return {
|
|
20253
|
+
cid: delegatedSession.delegationCid,
|
|
20254
|
+
delegationHeader: delegatedSession.delegationHeader,
|
|
20255
|
+
spaceId,
|
|
20256
|
+
path: primary.path,
|
|
20257
|
+
actions: primary.actions,
|
|
20258
|
+
resources,
|
|
20259
|
+
disableSubDelegation: false,
|
|
20260
|
+
expiry: expiresAt,
|
|
20261
|
+
delegateDID: session.verificationMethod,
|
|
20262
|
+
ownerAddress: session.address,
|
|
20263
|
+
chainId: session.chainId,
|
|
20264
|
+
host: this.config.host
|
|
20265
|
+
};
|
|
20266
|
+
}
|
|
20267
|
+
runtimeGrantFromDelegation(delegation, session) {
|
|
20268
|
+
const operations = this.operationsFromDelegation(delegation);
|
|
20269
|
+
return {
|
|
20270
|
+
session: {
|
|
20271
|
+
delegationHeader: delegation.delegationHeader,
|
|
20272
|
+
delegationCid: delegation.cid,
|
|
20273
|
+
spaceId: delegation.spaceId,
|
|
20274
|
+
verificationMethod: session.verificationMethod,
|
|
20275
|
+
jwk: session.jwk
|
|
20276
|
+
},
|
|
20277
|
+
delegation,
|
|
20278
|
+
operations,
|
|
20279
|
+
expiresAt: delegation.expiry
|
|
20280
|
+
};
|
|
20281
|
+
}
|
|
20282
|
+
delegatedResourcesForEntries(entries, spaceId) {
|
|
20283
|
+
return entries.map((entry) => ({
|
|
20284
|
+
service: this.shortServiceName(entry.service),
|
|
20285
|
+
space: spaceId,
|
|
20286
|
+
path: entry.path,
|
|
20287
|
+
actions: [...entry.actions]
|
|
20288
|
+
}));
|
|
20289
|
+
}
|
|
20290
|
+
operationsFromDelegation(delegation) {
|
|
20291
|
+
const resources = delegation.resources !== void 0 && delegation.resources.length > 0 ? delegation.resources : this.flatDelegationResources(delegation);
|
|
20292
|
+
return resources.flatMap(
|
|
20293
|
+
(resource) => resource.actions.map((action) => ({
|
|
20294
|
+
spaceId: resource.space,
|
|
20295
|
+
service: this.invocationServiceName(resource.service),
|
|
20296
|
+
path: resource.path,
|
|
20297
|
+
action
|
|
20298
|
+
}))
|
|
20299
|
+
);
|
|
20300
|
+
}
|
|
20301
|
+
flatDelegationResources(delegation) {
|
|
20302
|
+
const byService = /* @__PURE__ */ new Map();
|
|
20303
|
+
for (const action of delegation.actions) {
|
|
20304
|
+
const service = this.shortServiceName(action.split("/")[0]);
|
|
20305
|
+
const actions = byService.get(service) ?? [];
|
|
20306
|
+
actions.push(action);
|
|
20307
|
+
byService.set(service, actions);
|
|
20308
|
+
}
|
|
20309
|
+
return [...byService.entries()].map(([service, actions]) => ({
|
|
20310
|
+
service,
|
|
20311
|
+
space: delegation.spaceId,
|
|
20312
|
+
path: delegation.path,
|
|
20313
|
+
actions
|
|
20314
|
+
}));
|
|
20315
|
+
}
|
|
20316
|
+
selectInvocationSession(fallback, service, path, action) {
|
|
20317
|
+
const grant = this.findGrantForOperation({
|
|
20318
|
+
spaceId: fallback.spaceId,
|
|
20319
|
+
service: this.invocationServiceName(service),
|
|
20320
|
+
path,
|
|
20321
|
+
action
|
|
20322
|
+
});
|
|
20323
|
+
return grant?.session ?? fallback;
|
|
20324
|
+
}
|
|
20325
|
+
findGrantForOperations(operations) {
|
|
20326
|
+
if (operations.length === 0) {
|
|
20327
|
+
return void 0;
|
|
20328
|
+
}
|
|
20329
|
+
this.pruneExpiredRuntimePermissionGrants();
|
|
20330
|
+
return this.runtimePermissionGrants.find((grant) => {
|
|
20331
|
+
return operations.every(
|
|
20332
|
+
(operation) => grant.operations.some(
|
|
20333
|
+
(granted) => this.operationCovers(granted, operation)
|
|
20334
|
+
)
|
|
20335
|
+
);
|
|
20336
|
+
});
|
|
20337
|
+
}
|
|
20338
|
+
findGrantForOperation(operation) {
|
|
20339
|
+
return this.findGrantForOperations([operation]);
|
|
20340
|
+
}
|
|
20341
|
+
pruneExpiredRuntimePermissionGrants() {
|
|
20342
|
+
const now = Date.now();
|
|
20343
|
+
this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
|
|
20344
|
+
(grant) => grant.expiresAt.getTime() > now
|
|
20345
|
+
);
|
|
20346
|
+
}
|
|
20347
|
+
operationCovers(granted, requested) {
|
|
20348
|
+
return granted.spaceId === requested.spaceId && granted.service === requested.service && this.actionContains(granted.action, requested.action) && this.pathContains(granted.path, requested.path);
|
|
20349
|
+
}
|
|
20350
|
+
actionContains(grantedAction, requestedAction) {
|
|
20351
|
+
if (grantedAction === requestedAction) {
|
|
20352
|
+
return true;
|
|
20353
|
+
}
|
|
20354
|
+
if (grantedAction.endsWith("/*")) {
|
|
20355
|
+
const prefix = grantedAction.slice(0, -2);
|
|
20356
|
+
return requestedAction.startsWith(`${prefix}/`);
|
|
20357
|
+
}
|
|
20358
|
+
return false;
|
|
20359
|
+
}
|
|
20360
|
+
invocationServiceName(service) {
|
|
20361
|
+
return service.startsWith("tinycloud.") ? this.shortServiceName(service) : service;
|
|
20362
|
+
}
|
|
20363
|
+
pathContains(grantedPath, requestedPath) {
|
|
20364
|
+
if (grantedPath === "" || grantedPath === "/") {
|
|
20365
|
+
return true;
|
|
20366
|
+
}
|
|
20367
|
+
if (grantedPath.endsWith("/**")) {
|
|
20368
|
+
return requestedPath.startsWith(grantedPath.slice(0, -3));
|
|
20369
|
+
}
|
|
20370
|
+
if (grantedPath.endsWith("/*")) {
|
|
20371
|
+
const prefix = grantedPath.slice(0, -2);
|
|
20372
|
+
if (!requestedPath.startsWith(prefix)) {
|
|
20373
|
+
return false;
|
|
20374
|
+
}
|
|
20375
|
+
const remainder = requestedPath.slice(prefix.length);
|
|
20376
|
+
return !remainder.includes("/") || remainder === "/";
|
|
20377
|
+
}
|
|
20378
|
+
if (grantedPath.endsWith("/")) {
|
|
20379
|
+
return requestedPath.startsWith(grantedPath);
|
|
20380
|
+
}
|
|
20381
|
+
return grantedPath === requestedPath;
|
|
20382
|
+
}
|
|
19960
20383
|
/**
|
|
19961
20384
|
* Issue a delegation via the legacy wallet-signed SIWE path for a single
|
|
19962
20385
|
* {@link PermissionEntry}. Shares the implementation with the public
|
|
@@ -20478,6 +20901,7 @@ import {
|
|
|
20478
20901
|
ACCOUNT_REGISTRY_SPACE as ACCOUNT_REGISTRY_SPACE2,
|
|
20479
20902
|
DEFAULT_MANIFEST_SPACE as DEFAULT_MANIFEST_SPACE2,
|
|
20480
20903
|
DEFAULT_MANIFEST_VERSION,
|
|
20904
|
+
VAULT_PERMISSION_SERVICE,
|
|
20481
20905
|
PermissionNotInManifestError as PermissionNotInManifestError2,
|
|
20482
20906
|
SessionExpiredError as SessionExpiredError2,
|
|
20483
20907
|
ManifestValidationError,
|
|
@@ -20486,7 +20910,9 @@ import {
|
|
|
20486
20910
|
validateManifest,
|
|
20487
20911
|
loadManifest,
|
|
20488
20912
|
isCapabilitySubset as isCapabilitySubset2,
|
|
20489
|
-
expandActionShortNames
|
|
20913
|
+
expandActionShortNames,
|
|
20914
|
+
expandPermissionEntries,
|
|
20915
|
+
expandPermissionEntry,
|
|
20490
20916
|
parseExpiry as parseExpiry2,
|
|
20491
20917
|
resourceCapabilitiesToSpaceAbilitiesMap as resourceCapabilitiesToSpaceAbilitiesMap2
|
|
20492
20918
|
} from "@tinycloud/sdk-core";
|
|
@@ -20590,6 +21016,7 @@ export {
|
|
|
20590
21016
|
TinyCloud2 as TinyCloud,
|
|
20591
21017
|
TinyCloudNode,
|
|
20592
21018
|
UnsupportedFeatureError2 as UnsupportedFeatureError,
|
|
21019
|
+
VAULT_PERMISSION_SERVICE,
|
|
20593
21020
|
VaultHeaders,
|
|
20594
21021
|
VaultPublicSpaceKVActions,
|
|
20595
21022
|
VersionCheckError,
|
|
@@ -20605,7 +21032,9 @@ export {
|
|
|
20605
21032
|
defaultSignStrategy,
|
|
20606
21033
|
defaultSpaceCreationHandler,
|
|
20607
21034
|
deserializeDelegation,
|
|
20608
|
-
|
|
21035
|
+
expandActionShortNames,
|
|
21036
|
+
expandPermissionEntries,
|
|
21037
|
+
expandPermissionEntry,
|
|
20609
21038
|
isCapabilitySubset2 as isCapabilitySubset,
|
|
20610
21039
|
loadManifest,
|
|
20611
21040
|
makePublicSpaceId2 as makePublicSpaceId,
|