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