@tinycloud/node-sdk 2.2.0-beta.6 → 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-DdMPUB5s.d.cts → core-DcJ27GsA.d.cts} +71 -4
- package/dist/{core-DdMPUB5s.d.ts → core-DcJ27GsA.d.ts} +71 -4
- package/dist/core.cjs +794 -168
- 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 +700 -71
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +797 -171
- 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 +701 -71
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/core.js
CHANGED
|
@@ -959,6 +959,7 @@ import {
|
|
|
959
959
|
DuckDbService as DuckDbService2,
|
|
960
960
|
HooksService as HooksService2,
|
|
961
961
|
DataVaultService,
|
|
962
|
+
SecretsService,
|
|
962
963
|
createVaultCrypto,
|
|
963
964
|
ServiceContext as ServiceContext2,
|
|
964
965
|
SilentNotificationHandler,
|
|
@@ -1217,6 +1218,151 @@ function extractSiweExpiration(siwe) {
|
|
|
1217
1218
|
return d;
|
|
1218
1219
|
}
|
|
1219
1220
|
|
|
1221
|
+
// src/NodeSecretsService.ts
|
|
1222
|
+
import {
|
|
1223
|
+
ErrorCodes,
|
|
1224
|
+
resolveManifest
|
|
1225
|
+
} from "@tinycloud/sdk-core";
|
|
1226
|
+
var SECRET_NAME_RE = /^[A-Z][A-Z0-9_]*$/;
|
|
1227
|
+
var SECRET_PREFIX = "secrets/";
|
|
1228
|
+
var SECRETS_SPACE = "secrets";
|
|
1229
|
+
function ok() {
|
|
1230
|
+
return { ok: true, data: void 0 };
|
|
1231
|
+
}
|
|
1232
|
+
function secretsError(code, message, cause) {
|
|
1233
|
+
return {
|
|
1234
|
+
ok: false,
|
|
1235
|
+
error: {
|
|
1236
|
+
code,
|
|
1237
|
+
service: "secrets",
|
|
1238
|
+
message,
|
|
1239
|
+
...cause ? { cause } : {}
|
|
1240
|
+
}
|
|
1241
|
+
};
|
|
1242
|
+
}
|
|
1243
|
+
function actionUrn(action) {
|
|
1244
|
+
return `tinycloud.kv/${action}`;
|
|
1245
|
+
}
|
|
1246
|
+
function secretResourcePath(base, name) {
|
|
1247
|
+
return `${base}/${SECRET_PREFIX}${name}`;
|
|
1248
|
+
}
|
|
1249
|
+
function secretPermissionEntries(name, action) {
|
|
1250
|
+
return [
|
|
1251
|
+
{
|
|
1252
|
+
service: "tinycloud.kv",
|
|
1253
|
+
space: SECRETS_SPACE,
|
|
1254
|
+
path: secretResourcePath("keys", name),
|
|
1255
|
+
actions: [action],
|
|
1256
|
+
skipPrefix: true
|
|
1257
|
+
},
|
|
1258
|
+
{
|
|
1259
|
+
service: "tinycloud.kv",
|
|
1260
|
+
space: SECRETS_SPACE,
|
|
1261
|
+
path: secretResourcePath("vault", name),
|
|
1262
|
+
actions: [action],
|
|
1263
|
+
skipPrefix: true
|
|
1264
|
+
}
|
|
1265
|
+
];
|
|
1266
|
+
}
|
|
1267
|
+
function isSecretsSpace(space) {
|
|
1268
|
+
return space === SECRETS_SPACE || space.endsWith(`:${SECRETS_SPACE}`);
|
|
1269
|
+
}
|
|
1270
|
+
var NodeSecretsService = class {
|
|
1271
|
+
constructor(config) {
|
|
1272
|
+
this.config = config;
|
|
1273
|
+
this.shouldRestoreUnlock = false;
|
|
1274
|
+
}
|
|
1275
|
+
get vault() {
|
|
1276
|
+
return this.service.vault;
|
|
1277
|
+
}
|
|
1278
|
+
get isUnlocked() {
|
|
1279
|
+
return this.service.isUnlocked;
|
|
1280
|
+
}
|
|
1281
|
+
async unlock(signer) {
|
|
1282
|
+
const effectiveSigner = signer ?? this.config.getUnlockSigner?.();
|
|
1283
|
+
if (effectiveSigner !== void 0) {
|
|
1284
|
+
this.unlockSigner = effectiveSigner;
|
|
1285
|
+
}
|
|
1286
|
+
const result = await this.service.unlock(effectiveSigner);
|
|
1287
|
+
if (result.ok) {
|
|
1288
|
+
this.shouldRestoreUnlock = true;
|
|
1289
|
+
}
|
|
1290
|
+
return result;
|
|
1291
|
+
}
|
|
1292
|
+
lock() {
|
|
1293
|
+
this.shouldRestoreUnlock = false;
|
|
1294
|
+
this.service.lock();
|
|
1295
|
+
}
|
|
1296
|
+
get(name) {
|
|
1297
|
+
return this.service.get(name);
|
|
1298
|
+
}
|
|
1299
|
+
async put(name, value) {
|
|
1300
|
+
const permission = await this.ensureMutationPermission(name, "put");
|
|
1301
|
+
if (!permission.ok) return permission;
|
|
1302
|
+
return this.service.put(name, value);
|
|
1303
|
+
}
|
|
1304
|
+
async delete(name) {
|
|
1305
|
+
const permission = await this.ensureMutationPermission(name, "del");
|
|
1306
|
+
if (!permission.ok) return permission;
|
|
1307
|
+
return this.service.delete(name);
|
|
1308
|
+
}
|
|
1309
|
+
list() {
|
|
1310
|
+
return this.service.list();
|
|
1311
|
+
}
|
|
1312
|
+
get service() {
|
|
1313
|
+
return this.config.getService();
|
|
1314
|
+
}
|
|
1315
|
+
async ensureMutationPermission(name, action) {
|
|
1316
|
+
if (!SECRET_NAME_RE.test(name)) {
|
|
1317
|
+
return secretsError(
|
|
1318
|
+
ErrorCodes.INVALID_INPUT,
|
|
1319
|
+
`Invalid secret name ${JSON.stringify(name)}. Secret names must match ${SECRET_NAME_RE.source}.`
|
|
1320
|
+
);
|
|
1321
|
+
}
|
|
1322
|
+
if (this.hasMutationPermission(name, action)) {
|
|
1323
|
+
return ok();
|
|
1324
|
+
}
|
|
1325
|
+
if (!this.config.canEscalate()) {
|
|
1326
|
+
return secretsError(
|
|
1327
|
+
ErrorCodes.PERMISSION_DENIED,
|
|
1328
|
+
`Cannot autosign ${actionUrn(action)} for ${name}; TinyCloudNode needs wallet mode with a signer or privateKey.`
|
|
1329
|
+
);
|
|
1330
|
+
}
|
|
1331
|
+
try {
|
|
1332
|
+
await this.config.grantPermissions(secretPermissionEntries(name, action));
|
|
1333
|
+
return this.restoreUnlockAfterEscalation();
|
|
1334
|
+
} catch (error) {
|
|
1335
|
+
return secretsError(
|
|
1336
|
+
ErrorCodes.PERMISSION_DENIED,
|
|
1337
|
+
error instanceof Error ? error.message : `Autosign escalation for ${actionUrn(action)} on ${name} failed.`,
|
|
1338
|
+
error instanceof Error ? error : void 0
|
|
1339
|
+
);
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
async restoreUnlockAfterEscalation() {
|
|
1343
|
+
if (!this.shouldRestoreUnlock) {
|
|
1344
|
+
return ok();
|
|
1345
|
+
}
|
|
1346
|
+
return this.service.unlock(this.unlockSigner);
|
|
1347
|
+
}
|
|
1348
|
+
hasMutationPermission(name, action) {
|
|
1349
|
+
const manifest = this.config.getManifest();
|
|
1350
|
+
if (manifest === void 0) {
|
|
1351
|
+
return false;
|
|
1352
|
+
}
|
|
1353
|
+
const manifests = Array.isArray(manifest) ? manifest : [manifest];
|
|
1354
|
+
const requiredAction = actionUrn(action);
|
|
1355
|
+
return manifests.some((entry) => {
|
|
1356
|
+
const resolved = resolveManifest(entry);
|
|
1357
|
+
return ["keys", "vault"].every(
|
|
1358
|
+
(base) => resolved.resources.some(
|
|
1359
|
+
(resource) => resource.service === "tinycloud.kv" && isSecretsSpace(resource.space) && resource.path === secretResourcePath(base, name) && resource.actions.includes(requiredAction)
|
|
1360
|
+
)
|
|
1361
|
+
);
|
|
1362
|
+
});
|
|
1363
|
+
}
|
|
1364
|
+
};
|
|
1365
|
+
|
|
1220
1366
|
// src/TinyCloudNode.ts
|
|
1221
1367
|
var DEFAULT_HOST = "https://node.tinycloud.xyz";
|
|
1222
1368
|
var _TinyCloudNode = class _TinyCloudNode {
|
|
@@ -1248,6 +1394,30 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1248
1394
|
this.auth = null;
|
|
1249
1395
|
this.tc = null;
|
|
1250
1396
|
this._chainId = 1;
|
|
1397
|
+
this.runtimePermissionGrants = [];
|
|
1398
|
+
this.invokeWithRuntimePermissions = (session, service, path, action, facts) => {
|
|
1399
|
+
return this.wasmBindings.invoke(
|
|
1400
|
+
this.selectInvocationSession(session, service, path, action),
|
|
1401
|
+
service,
|
|
1402
|
+
path,
|
|
1403
|
+
action,
|
|
1404
|
+
facts
|
|
1405
|
+
);
|
|
1406
|
+
};
|
|
1407
|
+
this.invokeAnyWithRuntimePermissions = (session, entries, facts) => {
|
|
1408
|
+
if (!this.wasmBindings.invokeAny) {
|
|
1409
|
+
throw new Error("WASM binding does not support invokeAny");
|
|
1410
|
+
}
|
|
1411
|
+
const grant = this.findGrantForOperations(
|
|
1412
|
+
entries.map((entry) => ({
|
|
1413
|
+
spaceId: entry.spaceId,
|
|
1414
|
+
service: this.invocationServiceName(entry.service),
|
|
1415
|
+
path: entry.path,
|
|
1416
|
+
action: entry.action
|
|
1417
|
+
}))
|
|
1418
|
+
);
|
|
1419
|
+
return this.wasmBindings.invokeAny(grant?.session ?? session, entries, facts);
|
|
1420
|
+
};
|
|
1251
1421
|
this.explicitHost = config.host;
|
|
1252
1422
|
this.config = {
|
|
1253
1423
|
...config,
|
|
@@ -1283,7 +1453,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1283
1453
|
this._sharingService = new SharingService({
|
|
1284
1454
|
hosts: [this.config.host],
|
|
1285
1455
|
// session: undefined - not needed for receive()
|
|
1286
|
-
invoke: this.
|
|
1456
|
+
invoke: this.invokeWithRuntimePermissions,
|
|
1287
1457
|
fetch: globalThis.fetch.bind(globalThis),
|
|
1288
1458
|
keyProvider: this._keyProvider,
|
|
1289
1459
|
registry: this._capabilityRegistry,
|
|
@@ -1351,7 +1521,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1351
1521
|
includeAccountRegistryPermissions: config.includeAccountRegistryPermissions
|
|
1352
1522
|
});
|
|
1353
1523
|
this.tc = new TinyCloud(this.auth, {
|
|
1354
|
-
invokeAny: this.
|
|
1524
|
+
invokeAny: this.invokeAnyWithRuntimePermissions
|
|
1355
1525
|
});
|
|
1356
1526
|
}
|
|
1357
1527
|
syncResolvedHostFromAuth() {
|
|
@@ -1471,7 +1641,12 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1471
1641
|
this._sql = void 0;
|
|
1472
1642
|
this._duckdb = void 0;
|
|
1473
1643
|
this._hooks = void 0;
|
|
1644
|
+
this._vault = void 0;
|
|
1645
|
+
this._baseSecrets = void 0;
|
|
1646
|
+
this._secrets = void 0;
|
|
1647
|
+
this._spaceService = void 0;
|
|
1474
1648
|
this._serviceContext = void 0;
|
|
1649
|
+
this.runtimePermissionGrants = [];
|
|
1475
1650
|
await this.tc.signIn(options);
|
|
1476
1651
|
this.syncResolvedHostFromAuth();
|
|
1477
1652
|
this.initializeServices();
|
|
@@ -1556,7 +1731,12 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1556
1731
|
this._sql = void 0;
|
|
1557
1732
|
this._duckdb = void 0;
|
|
1558
1733
|
this._hooks = void 0;
|
|
1734
|
+
this._vault = void 0;
|
|
1735
|
+
this._baseSecrets = void 0;
|
|
1736
|
+
this._secrets = void 0;
|
|
1737
|
+
this._spaceService = void 0;
|
|
1559
1738
|
this._serviceContext = void 0;
|
|
1739
|
+
this.runtimePermissionGrants = [];
|
|
1560
1740
|
if (sessionData.address) {
|
|
1561
1741
|
this._address = sessionData.address;
|
|
1562
1742
|
}
|
|
@@ -1564,8 +1744,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1564
1744
|
this._chainId = sessionData.chainId;
|
|
1565
1745
|
}
|
|
1566
1746
|
this._serviceContext = new ServiceContext2({
|
|
1567
|
-
invoke: this.
|
|
1568
|
-
invokeAny: this.
|
|
1747
|
+
invoke: this.invokeWithRuntimePermissions,
|
|
1748
|
+
invokeAny: this.invokeAnyWithRuntimePermissions,
|
|
1569
1749
|
fetch: globalThis.fetch.bind(globalThis),
|
|
1570
1750
|
hosts: [this.config.host]
|
|
1571
1751
|
});
|
|
@@ -1589,41 +1769,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1589
1769
|
jwk: sessionData.jwk
|
|
1590
1770
|
};
|
|
1591
1771
|
this._serviceContext.setSession(serviceSession);
|
|
1592
|
-
|
|
1593
|
-
const vaultCrypto = createVaultCrypto({
|
|
1594
|
-
vault_encrypt: wasm.vault_encrypt,
|
|
1595
|
-
vault_decrypt: wasm.vault_decrypt,
|
|
1596
|
-
vault_derive_key: wasm.vault_derive_key,
|
|
1597
|
-
vault_x25519_from_seed: wasm.vault_x25519_from_seed,
|
|
1598
|
-
vault_x25519_dh: wasm.vault_x25519_dh,
|
|
1599
|
-
vault_random_bytes: wasm.vault_random_bytes,
|
|
1600
|
-
vault_sha256: wasm.vault_sha256
|
|
1601
|
-
});
|
|
1602
|
-
const self = this;
|
|
1603
|
-
this._vault = new DataVaultService({
|
|
1604
|
-
spaceId: sessionData.spaceId,
|
|
1605
|
-
crypto: vaultCrypto,
|
|
1606
|
-
tc: {
|
|
1607
|
-
kv: this._kv,
|
|
1608
|
-
ensurePublicSpace: async () => {
|
|
1609
|
-
try {
|
|
1610
|
-
await self.ensurePublicSpace();
|
|
1611
|
-
return { ok: true, data: void 0 };
|
|
1612
|
-
} catch (error) {
|
|
1613
|
-
return { ok: false, error: { code: "STORAGE_ERROR", message: error instanceof Error ? error.message : String(error), service: "vault" } };
|
|
1614
|
-
}
|
|
1615
|
-
},
|
|
1616
|
-
get publicKV() {
|
|
1617
|
-
return self._publicKV ?? self.tc.publicKV;
|
|
1618
|
-
},
|
|
1619
|
-
readPublicSpace: (host, spaceId, key) => TinyCloud.readPublicSpace(host, spaceId, key),
|
|
1620
|
-
makePublicSpaceId: TinyCloud.makePublicSpaceId,
|
|
1621
|
-
did: this.did,
|
|
1622
|
-
address: sessionData.address ?? this._address ?? "",
|
|
1623
|
-
chainId: sessionData.chainId ?? this._chainId,
|
|
1624
|
-
hosts: [this.config.host]
|
|
1625
|
-
}
|
|
1626
|
-
});
|
|
1772
|
+
this._vault = this.createVaultService(sessionData.spaceId, this._kv);
|
|
1627
1773
|
this._vault.initialize(this._serviceContext);
|
|
1628
1774
|
this._serviceContext.registerService("vault", this._vault);
|
|
1629
1775
|
this.initializeV2Services(serviceSession);
|
|
@@ -1685,7 +1831,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1685
1831
|
includeAccountRegistryPermissions: this.config.includeAccountRegistryPermissions
|
|
1686
1832
|
});
|
|
1687
1833
|
this.tc = new TinyCloud(this.auth, {
|
|
1688
|
-
invokeAny: this.
|
|
1834
|
+
invokeAny: this.invokeAnyWithRuntimePermissions
|
|
1689
1835
|
});
|
|
1690
1836
|
this.config.prefix = prefix;
|
|
1691
1837
|
}
|
|
@@ -1729,7 +1875,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1729
1875
|
includeAccountRegistryPermissions: this.config.includeAccountRegistryPermissions
|
|
1730
1876
|
});
|
|
1731
1877
|
this.tc = new TinyCloud(this.auth, {
|
|
1732
|
-
invokeAny: this.
|
|
1878
|
+
invokeAny: this.invokeAnyWithRuntimePermissions
|
|
1733
1879
|
});
|
|
1734
1880
|
this.config.prefix = prefix;
|
|
1735
1881
|
}
|
|
@@ -1742,10 +1888,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1742
1888
|
if (!session) {
|
|
1743
1889
|
return;
|
|
1744
1890
|
}
|
|
1745
|
-
this.tc.initializeServices(this.
|
|
1891
|
+
this.tc.initializeServices(this.invokeWithRuntimePermissions, [this.config.host]);
|
|
1746
1892
|
this._serviceContext = new ServiceContext2({
|
|
1747
|
-
invoke: this.
|
|
1748
|
-
invokeAny: this.
|
|
1893
|
+
invoke: this.invokeWithRuntimePermissions,
|
|
1894
|
+
invokeAny: this.invokeAnyWithRuntimePermissions,
|
|
1749
1895
|
fetch: globalThis.fetch.bind(globalThis),
|
|
1750
1896
|
hosts: [this.config.host]
|
|
1751
1897
|
});
|
|
@@ -1775,6 +1921,28 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1775
1921
|
};
|
|
1776
1922
|
this._serviceContext.setSession(serviceSession);
|
|
1777
1923
|
this.tc.serviceContext.setSession(serviceSession);
|
|
1924
|
+
this._vault = this.createVaultService(session.spaceId, this._kv);
|
|
1925
|
+
this._vault.initialize(this._serviceContext);
|
|
1926
|
+
this._serviceContext.registerService("vault", this._vault);
|
|
1927
|
+
this.initializeV2Services(serviceSession);
|
|
1928
|
+
}
|
|
1929
|
+
createSpaceScopedKVService(spaceId) {
|
|
1930
|
+
const kvService = new KVService2({});
|
|
1931
|
+
if (this._serviceContext) {
|
|
1932
|
+
const spaceScopedContext = new ServiceContext2({
|
|
1933
|
+
invoke: this._serviceContext.invoke,
|
|
1934
|
+
fetch: this._serviceContext.fetch,
|
|
1935
|
+
hosts: this._serviceContext.hosts
|
|
1936
|
+
});
|
|
1937
|
+
const session = this._serviceContext.session;
|
|
1938
|
+
if (session) {
|
|
1939
|
+
spaceScopedContext.setSession({ ...session, spaceId });
|
|
1940
|
+
}
|
|
1941
|
+
kvService.initialize(spaceScopedContext);
|
|
1942
|
+
}
|
|
1943
|
+
return kvService;
|
|
1944
|
+
}
|
|
1945
|
+
createVaultService(spaceId, kv) {
|
|
1778
1946
|
const wasm = this.wasmBindings;
|
|
1779
1947
|
const vaultCrypto = createVaultCrypto({
|
|
1780
1948
|
vault_encrypt: wasm.vault_encrypt,
|
|
@@ -1786,11 +1954,11 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1786
1954
|
vault_sha256: wasm.vault_sha256
|
|
1787
1955
|
});
|
|
1788
1956
|
const self = this;
|
|
1789
|
-
|
|
1790
|
-
spaceId
|
|
1957
|
+
return new DataVaultService({
|
|
1958
|
+
spaceId,
|
|
1791
1959
|
crypto: vaultCrypto,
|
|
1792
1960
|
tc: {
|
|
1793
|
-
kv
|
|
1961
|
+
kv,
|
|
1794
1962
|
ensurePublicSpace: async () => {
|
|
1795
1963
|
try {
|
|
1796
1964
|
await self.ensurePublicSpace();
|
|
@@ -1802,17 +1970,14 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1802
1970
|
get publicKV() {
|
|
1803
1971
|
return self._publicKV ?? self.tc.publicKV;
|
|
1804
1972
|
},
|
|
1805
|
-
readPublicSpace: (host,
|
|
1973
|
+
readPublicSpace: (host, targetSpaceId, key) => TinyCloud.readPublicSpace(host, targetSpaceId, key),
|
|
1806
1974
|
makePublicSpaceId: TinyCloud.makePublicSpaceId,
|
|
1807
1975
|
did: this.did,
|
|
1808
|
-
address: this._address,
|
|
1976
|
+
address: this._address ?? "",
|
|
1809
1977
|
chainId: this._chainId,
|
|
1810
1978
|
hosts: [this.config.host]
|
|
1811
1979
|
}
|
|
1812
1980
|
});
|
|
1813
|
-
this._vault.initialize(this._serviceContext);
|
|
1814
|
-
this._serviceContext.registerService("vault", this._vault);
|
|
1815
|
-
this.initializeV2Services(serviceSession);
|
|
1816
1981
|
}
|
|
1817
1982
|
/**
|
|
1818
1983
|
* Initialize the v2 delegation system services.
|
|
@@ -1896,7 +2061,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1896
2061
|
this._delegationManager = new DelegationManager({
|
|
1897
2062
|
hosts: [this.config.host],
|
|
1898
2063
|
session: serviceSession,
|
|
1899
|
-
invoke: this.
|
|
2064
|
+
invoke: this.invokeWithRuntimePermissions,
|
|
1900
2065
|
fetch: globalThis.fetch.bind(globalThis)
|
|
1901
2066
|
});
|
|
1902
2067
|
this._spaceService = new SpaceService({
|
|
@@ -1907,20 +2072,15 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
1907
2072
|
capabilityRegistry: this._capabilityRegistry,
|
|
1908
2073
|
userDid: this.did,
|
|
1909
2074
|
createKVService: (spaceId) => {
|
|
1910
|
-
|
|
2075
|
+
return this.createSpaceScopedKVService(spaceId);
|
|
2076
|
+
},
|
|
2077
|
+
createVaultService: (spaceId) => {
|
|
2078
|
+
const kvService = this.createSpaceScopedKVService(spaceId);
|
|
2079
|
+
const vaultService = this.createVaultService(spaceId, kvService);
|
|
1911
2080
|
if (this._serviceContext) {
|
|
1912
|
-
|
|
1913
|
-
invoke: this._serviceContext.invoke,
|
|
1914
|
-
fetch: this._serviceContext.fetch,
|
|
1915
|
-
hosts: this._serviceContext.hosts
|
|
1916
|
-
});
|
|
1917
|
-
const session = this._serviceContext.session;
|
|
1918
|
-
if (session) {
|
|
1919
|
-
spaceScopedContext.setSession({ ...session, spaceId });
|
|
1920
|
-
}
|
|
1921
|
-
kvService.initialize(spaceScopedContext);
|
|
2081
|
+
vaultService.initialize(this._serviceContext);
|
|
1922
2082
|
}
|
|
1923
|
-
return
|
|
2083
|
+
return vaultService;
|
|
1924
2084
|
},
|
|
1925
2085
|
// Enable space.delegations.create() via SIWE-based delegation
|
|
1926
2086
|
createDelegation: async (params) => {
|
|
@@ -2157,6 +2317,33 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2157
2317
|
}
|
|
2158
2318
|
return this._vault;
|
|
2159
2319
|
}
|
|
2320
|
+
/**
|
|
2321
|
+
* App-facing secrets API backed by the `secrets` space vault.
|
|
2322
|
+
*/
|
|
2323
|
+
get secrets() {
|
|
2324
|
+
if (!this._spaceService) {
|
|
2325
|
+
throw new Error("Not signed in. Call signIn() first.");
|
|
2326
|
+
}
|
|
2327
|
+
if (!this._secrets) {
|
|
2328
|
+
this._secrets = new NodeSecretsService({
|
|
2329
|
+
getService: () => this.getBaseSecrets(),
|
|
2330
|
+
getManifest: () => this.manifest,
|
|
2331
|
+
grantPermissions: (additional) => this.grantRuntimePermissions(additional),
|
|
2332
|
+
canEscalate: () => this.signer !== void 0 && this.tc !== void 0,
|
|
2333
|
+
getUnlockSigner: () => this.signer ?? void 0
|
|
2334
|
+
});
|
|
2335
|
+
}
|
|
2336
|
+
return this._secrets;
|
|
2337
|
+
}
|
|
2338
|
+
getBaseSecrets() {
|
|
2339
|
+
if (!this._spaceService) {
|
|
2340
|
+
throw new Error("Not signed in. Call signIn() first.");
|
|
2341
|
+
}
|
|
2342
|
+
if (!this._baseSecrets) {
|
|
2343
|
+
this._baseSecrets = new SecretsService(() => this.space("secrets").vault);
|
|
2344
|
+
}
|
|
2345
|
+
return this._baseSecrets;
|
|
2346
|
+
}
|
|
2160
2347
|
/**
|
|
2161
2348
|
* Hooks write stream subscription API.
|
|
2162
2349
|
*/
|
|
@@ -2227,6 +2414,171 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2227
2414
|
}
|
|
2228
2415
|
};
|
|
2229
2416
|
}
|
|
2417
|
+
/**
|
|
2418
|
+
* Check whether the current session or an approved runtime delegation covers
|
|
2419
|
+
* every requested permission.
|
|
2420
|
+
*/
|
|
2421
|
+
hasRuntimePermissions(permissions) {
|
|
2422
|
+
const session = this.auth?.tinyCloudSession;
|
|
2423
|
+
if (!session || !Array.isArray(permissions) || permissions.length === 0) {
|
|
2424
|
+
return false;
|
|
2425
|
+
}
|
|
2426
|
+
const expanded = this.expandPermissionEntries(permissions);
|
|
2427
|
+
if (this.sessionCoversPermissionEntries(session, expanded)) {
|
|
2428
|
+
return true;
|
|
2429
|
+
}
|
|
2430
|
+
return this.findRuntimeGrantsForPermissionEntries(expanded, session).length > 0;
|
|
2431
|
+
}
|
|
2432
|
+
/**
|
|
2433
|
+
* Return installed runtime permission delegations. When `permissions` is
|
|
2434
|
+
* provided, only delegations currently covering those permissions are
|
|
2435
|
+
* returned. Base-session manifest permissions are not represented here.
|
|
2436
|
+
*/
|
|
2437
|
+
getRuntimePermissionDelegations(permissions) {
|
|
2438
|
+
this.pruneExpiredRuntimePermissionGrants();
|
|
2439
|
+
if (permissions === void 0) {
|
|
2440
|
+
return this.runtimePermissionGrants.map((grant) => grant.delegation);
|
|
2441
|
+
}
|
|
2442
|
+
const session = this.auth?.tinyCloudSession;
|
|
2443
|
+
if (!session || !Array.isArray(permissions) || permissions.length === 0) {
|
|
2444
|
+
return [];
|
|
2445
|
+
}
|
|
2446
|
+
const expanded = this.expandPermissionEntries(permissions);
|
|
2447
|
+
return this.findRuntimeGrantsForPermissionEntries(expanded, session).map(
|
|
2448
|
+
(grant) => grant.delegation
|
|
2449
|
+
);
|
|
2450
|
+
}
|
|
2451
|
+
/**
|
|
2452
|
+
* Install a portable runtime permission delegation into this SDK instance so
|
|
2453
|
+
* matching service calls and downstream `delegateTo()` calls can use it.
|
|
2454
|
+
*/
|
|
2455
|
+
async useRuntimeDelegation(delegation) {
|
|
2456
|
+
const session = this.auth?.tinyCloudSession;
|
|
2457
|
+
if (!session) {
|
|
2458
|
+
throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
2459
|
+
}
|
|
2460
|
+
if (delegation.expiry.getTime() <= Date.now()) {
|
|
2461
|
+
throw new SessionExpiredError(delegation.expiry);
|
|
2462
|
+
}
|
|
2463
|
+
const expectedDids = /* @__PURE__ */ new Set([session.verificationMethod, this.sessionDid]);
|
|
2464
|
+
if (!expectedDids.has(delegation.delegateDID)) {
|
|
2465
|
+
throw new Error(
|
|
2466
|
+
`Runtime delegation targets ${delegation.delegateDID} but this session key is ${session.verificationMethod}.`
|
|
2467
|
+
);
|
|
2468
|
+
}
|
|
2469
|
+
const targetHost = delegation.host ?? this.config.host;
|
|
2470
|
+
const activateResult = await activateSessionWithHost2(
|
|
2471
|
+
targetHost,
|
|
2472
|
+
delegation.delegationHeader
|
|
2473
|
+
);
|
|
2474
|
+
if (!activateResult.success) {
|
|
2475
|
+
throw new Error(
|
|
2476
|
+
`Failed to activate runtime permission delegation: ${activateResult.error}`
|
|
2477
|
+
);
|
|
2478
|
+
}
|
|
2479
|
+
this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
|
|
2480
|
+
(grant) => grant.delegation.cid !== delegation.cid
|
|
2481
|
+
);
|
|
2482
|
+
this.runtimePermissionGrants.push(
|
|
2483
|
+
this.runtimeGrantFromDelegation(delegation, session)
|
|
2484
|
+
);
|
|
2485
|
+
}
|
|
2486
|
+
/**
|
|
2487
|
+
* Store additional permissions as narrow delegations to the current session
|
|
2488
|
+
* key. Future service invocations automatically use a stored delegation when
|
|
2489
|
+
* its `(space, service, path, action)` covers the request.
|
|
2490
|
+
*/
|
|
2491
|
+
async grantRuntimePermissions(permissions, options) {
|
|
2492
|
+
if (!Array.isArray(permissions) || permissions.length === 0) {
|
|
2493
|
+
throw new Error("grantRuntimePermissions requires a non-empty permissions array");
|
|
2494
|
+
}
|
|
2495
|
+
const session = this.auth?.tinyCloudSession;
|
|
2496
|
+
if (!session) {
|
|
2497
|
+
throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
2498
|
+
}
|
|
2499
|
+
const sessionExpiry = extractSiweExpiration(session.siwe);
|
|
2500
|
+
if (sessionExpiry !== void 0) {
|
|
2501
|
+
const marginMs = _TinyCloudNode.SESSION_EXPIRY_SAFETY_MARGIN_MS;
|
|
2502
|
+
if (sessionExpiry.getTime() <= Date.now() + marginMs) {
|
|
2503
|
+
throw new SessionExpiredError(sessionExpiry);
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
const expanded = this.expandPermissionEntries(permissions);
|
|
2507
|
+
if (this.sessionCoversPermissionEntries(session, expanded)) {
|
|
2508
|
+
return [];
|
|
2509
|
+
}
|
|
2510
|
+
const existingGrants = this.findRuntimeGrantsForPermissionEntries(expanded, session);
|
|
2511
|
+
if (existingGrants.length > 0) {
|
|
2512
|
+
return existingGrants.map((grant) => grant.delegation);
|
|
2513
|
+
}
|
|
2514
|
+
if (!this.signer) {
|
|
2515
|
+
throw new Error(
|
|
2516
|
+
"grantRuntimePermissions requires wallet mode with a signer or privateKey."
|
|
2517
|
+
);
|
|
2518
|
+
}
|
|
2519
|
+
const bySpace = /* @__PURE__ */ new Map();
|
|
2520
|
+
for (const entry of expanded) {
|
|
2521
|
+
const spaceId = this.resolvePermissionSpace(entry.space, session);
|
|
2522
|
+
const current = bySpace.get(spaceId) ?? [];
|
|
2523
|
+
current.push(entry);
|
|
2524
|
+
bySpace.set(spaceId, current);
|
|
2525
|
+
}
|
|
2526
|
+
const now = /* @__PURE__ */ new Date();
|
|
2527
|
+
const requestedExpiryMs = resolveExpiryMs(options?.expiry);
|
|
2528
|
+
let expiresAt = new Date(now.getTime() + requestedExpiryMs);
|
|
2529
|
+
if (sessionExpiry !== void 0 && sessionExpiry < expiresAt) {
|
|
2530
|
+
expiresAt = sessionExpiry;
|
|
2531
|
+
}
|
|
2532
|
+
const delegations = [];
|
|
2533
|
+
for (const [spaceId, entries] of bySpace) {
|
|
2534
|
+
const abilities = this.permissionsToAbilities(entries);
|
|
2535
|
+
const prepared = this.wasmBindings.prepareSession({
|
|
2536
|
+
abilities,
|
|
2537
|
+
address: this.wasmBindings.ensureEip55(session.address),
|
|
2538
|
+
chainId: session.chainId,
|
|
2539
|
+
domain: this.siweDomain,
|
|
2540
|
+
issuedAt: now.toISOString(),
|
|
2541
|
+
expirationTime: expiresAt.toISOString(),
|
|
2542
|
+
spaceId,
|
|
2543
|
+
jwk: session.jwk
|
|
2544
|
+
});
|
|
2545
|
+
const signature = await this.signer.signMessage(prepared.siwe);
|
|
2546
|
+
const delegatedSession = this.wasmBindings.completeSessionSetup({
|
|
2547
|
+
...prepared,
|
|
2548
|
+
signature
|
|
2549
|
+
});
|
|
2550
|
+
const activateResult = await activateSessionWithHost2(
|
|
2551
|
+
this.config.host,
|
|
2552
|
+
delegatedSession.delegationHeader
|
|
2553
|
+
);
|
|
2554
|
+
if (!activateResult.success) {
|
|
2555
|
+
throw new Error(
|
|
2556
|
+
`Failed to activate runtime permission delegation: ${activateResult.error}`
|
|
2557
|
+
);
|
|
2558
|
+
}
|
|
2559
|
+
const delegation = this.runtimeDelegationFromSession(
|
|
2560
|
+
delegatedSession,
|
|
2561
|
+
entries,
|
|
2562
|
+
spaceId,
|
|
2563
|
+
session,
|
|
2564
|
+
expiresAt
|
|
2565
|
+
);
|
|
2566
|
+
this.runtimePermissionGrants.push({
|
|
2567
|
+
session: {
|
|
2568
|
+
delegationHeader: delegatedSession.delegationHeader,
|
|
2569
|
+
delegationCid: delegatedSession.delegationCid,
|
|
2570
|
+
spaceId,
|
|
2571
|
+
verificationMethod: session.verificationMethod,
|
|
2572
|
+
jwk: session.jwk
|
|
2573
|
+
},
|
|
2574
|
+
delegation,
|
|
2575
|
+
operations: this.permissionOperations(entries, spaceId),
|
|
2576
|
+
expiresAt
|
|
2577
|
+
});
|
|
2578
|
+
delegations.push(delegation);
|
|
2579
|
+
}
|
|
2580
|
+
return delegations;
|
|
2581
|
+
}
|
|
2230
2582
|
/**
|
|
2231
2583
|
* Get the DelegationManager for delegation CRUD operations.
|
|
2232
2584
|
*
|
|
@@ -2295,6 +2647,12 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2295
2647
|
get spaceService() {
|
|
2296
2648
|
return this.spaces;
|
|
2297
2649
|
}
|
|
2650
|
+
/**
|
|
2651
|
+
* Get a Space object by short name or full URI.
|
|
2652
|
+
*/
|
|
2653
|
+
space(nameOrUri) {
|
|
2654
|
+
return this.spaces.get(nameOrUri);
|
|
2655
|
+
}
|
|
2298
2656
|
/**
|
|
2299
2657
|
* Get the SharingService for creating and receiving v2 sharing links.
|
|
2300
2658
|
*
|
|
@@ -2409,7 +2767,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2409
2767
|
if (this._serviceContext) {
|
|
2410
2768
|
const publicKV = new KVService2({ prefix: "" });
|
|
2411
2769
|
const publicContext = new ServiceContext2({
|
|
2412
|
-
invoke: this.
|
|
2770
|
+
invoke: this.invokeWithRuntimePermissions,
|
|
2413
2771
|
fetch: this._serviceContext.fetch,
|
|
2414
2772
|
hosts: this._serviceContext.hosts
|
|
2415
2773
|
});
|
|
@@ -2497,8 +2855,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2497
2855
|
* Issue a delegation using the capability-chain flow.
|
|
2498
2856
|
*
|
|
2499
2857
|
* When every requested permission is a subset of the current
|
|
2500
|
-
* session's recap,
|
|
2501
|
-
*
|
|
2858
|
+
* session's recap, or of one installed runtime permission delegation,
|
|
2859
|
+
* the delegation is signed by the session key via WASM — no wallet
|
|
2860
|
+
* prompt. When at least one is NOT derivable, a
|
|
2502
2861
|
* {@link PermissionNotInManifestError} is raised (carrying the
|
|
2503
2862
|
* missing entries) so the caller can trigger an escalation flow
|
|
2504
2863
|
* (e.g. `TinyCloudWeb.requestPermissions`). Passing
|
|
@@ -2578,6 +2937,23 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2578
2937
|
);
|
|
2579
2938
|
const { subset, missing } = isCapabilitySubset(expandedEntries, granted);
|
|
2580
2939
|
if (!subset) {
|
|
2940
|
+
const runtimeGrant = this.findGrantForOperations(
|
|
2941
|
+
this.permissionEntriesToOperations(expandedEntries, session)
|
|
2942
|
+
);
|
|
2943
|
+
if (runtimeGrant) {
|
|
2944
|
+
const marginMs = _TinyCloudNode.SESSION_EXPIRY_SAFETY_MARGIN_MS;
|
|
2945
|
+
if (runtimeGrant.expiresAt.getTime() <= Date.now() + marginMs) {
|
|
2946
|
+
throw new SessionExpiredError(runtimeGrant.expiresAt);
|
|
2947
|
+
}
|
|
2948
|
+
const runtimeExpiration = runtimeGrant.expiresAt < effectiveExpiration ? runtimeGrant.expiresAt : effectiveExpiration;
|
|
2949
|
+
const delegation2 = await this.createDelegationViaRuntimeGrant(
|
|
2950
|
+
did,
|
|
2951
|
+
expandedEntries,
|
|
2952
|
+
runtimeExpiration,
|
|
2953
|
+
runtimeGrant
|
|
2954
|
+
);
|
|
2955
|
+
return { delegation: delegation2, prompted: false };
|
|
2956
|
+
}
|
|
2581
2957
|
throw new PermissionNotInManifestError(missing, granted);
|
|
2582
2958
|
}
|
|
2583
2959
|
const delegation = await this.createDelegationViaWasmPath(
|
|
@@ -2722,6 +3098,41 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2722
3098
|
host: this.config.host
|
|
2723
3099
|
};
|
|
2724
3100
|
}
|
|
3101
|
+
async createDelegationViaRuntimeGrant(did, entries, expirationTime, grant) {
|
|
3102
|
+
const result = this.createDelegationWrapper({
|
|
3103
|
+
session: grant.session,
|
|
3104
|
+
delegateDID: did,
|
|
3105
|
+
spaceId: grant.session.spaceId,
|
|
3106
|
+
abilities: this.permissionsToAbilities(entries),
|
|
3107
|
+
expirationSecs: Math.floor(expirationTime.getTime() / 1e3)
|
|
3108
|
+
});
|
|
3109
|
+
const primary = result.resources[0];
|
|
3110
|
+
const delegationHeader = { Authorization: result.delegation };
|
|
3111
|
+
const targetHost = grant.delegation.host ?? this.config.host;
|
|
3112
|
+
const activateResult = await activateSessionWithHost2(
|
|
3113
|
+
targetHost,
|
|
3114
|
+
delegationHeader
|
|
3115
|
+
);
|
|
3116
|
+
if (!activateResult.success) {
|
|
3117
|
+
throw new Error(
|
|
3118
|
+
`Failed to activate delegation with host: ${activateResult.error}`
|
|
3119
|
+
);
|
|
3120
|
+
}
|
|
3121
|
+
return {
|
|
3122
|
+
cid: result.cid,
|
|
3123
|
+
delegationHeader,
|
|
3124
|
+
spaceId: grant.session.spaceId,
|
|
3125
|
+
path: primary.path,
|
|
3126
|
+
actions: primary.actions,
|
|
3127
|
+
resources: result.resources,
|
|
3128
|
+
disableSubDelegation: false,
|
|
3129
|
+
expiry: result.expiry,
|
|
3130
|
+
delegateDID: did,
|
|
3131
|
+
ownerAddress: grant.delegation.ownerAddress,
|
|
3132
|
+
chainId: grant.delegation.chainId,
|
|
3133
|
+
host: targetHost
|
|
3134
|
+
};
|
|
3135
|
+
}
|
|
2725
3136
|
resolvePermissionSpace(space, session) {
|
|
2726
3137
|
if (space === void 0) {
|
|
2727
3138
|
return this.wasmBindings.makeSpaceId(
|
|
@@ -2738,6 +3149,223 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
2738
3149
|
}
|
|
2739
3150
|
return this.wasmBindings.makeSpaceId(session.address, session.chainId, space);
|
|
2740
3151
|
}
|
|
3152
|
+
expandPermissionEntries(permissions) {
|
|
3153
|
+
return permissions.map((entry) => ({
|
|
3154
|
+
...entry,
|
|
3155
|
+
actions: expandActionShortNames(entry.service, entry.actions)
|
|
3156
|
+
}));
|
|
3157
|
+
}
|
|
3158
|
+
shortServiceName(service) {
|
|
3159
|
+
const short = SERVICE_LONG_TO_SHORT[service];
|
|
3160
|
+
if (short === void 0) {
|
|
3161
|
+
throw new Error(
|
|
3162
|
+
`unknown service '${service}' \u2014 no short-form mapping`
|
|
3163
|
+
);
|
|
3164
|
+
}
|
|
3165
|
+
return short;
|
|
3166
|
+
}
|
|
3167
|
+
permissionsToAbilities(entries) {
|
|
3168
|
+
const abilities = {};
|
|
3169
|
+
for (const entry of entries) {
|
|
3170
|
+
const service = this.shortServiceName(entry.service);
|
|
3171
|
+
abilities[service] ?? (abilities[service] = {});
|
|
3172
|
+
const existing = abilities[service][entry.path] ?? [];
|
|
3173
|
+
const seen = new Set(existing);
|
|
3174
|
+
for (const action of entry.actions) {
|
|
3175
|
+
if (!seen.has(action)) {
|
|
3176
|
+
existing.push(action);
|
|
3177
|
+
seen.add(action);
|
|
3178
|
+
}
|
|
3179
|
+
}
|
|
3180
|
+
abilities[service][entry.path] = existing;
|
|
3181
|
+
}
|
|
3182
|
+
return abilities;
|
|
3183
|
+
}
|
|
3184
|
+
permissionOperations(entries, spaceId) {
|
|
3185
|
+
return entries.flatMap((entry) => {
|
|
3186
|
+
const service = this.shortServiceName(entry.service);
|
|
3187
|
+
return entry.actions.map((action) => ({
|
|
3188
|
+
spaceId,
|
|
3189
|
+
service,
|
|
3190
|
+
path: entry.path,
|
|
3191
|
+
action
|
|
3192
|
+
}));
|
|
3193
|
+
});
|
|
3194
|
+
}
|
|
3195
|
+
sessionCoversPermissionEntries(session, entries) {
|
|
3196
|
+
try {
|
|
3197
|
+
const granted = parseRecapCapabilities(
|
|
3198
|
+
(siwe) => this.wasmBindings.parseRecapFromSiwe(siwe),
|
|
3199
|
+
session.siwe
|
|
3200
|
+
);
|
|
3201
|
+
return isCapabilitySubset(entries, granted).subset;
|
|
3202
|
+
} catch {
|
|
3203
|
+
return false;
|
|
3204
|
+
}
|
|
3205
|
+
}
|
|
3206
|
+
permissionEntriesToOperations(entries, session) {
|
|
3207
|
+
return entries.flatMap((entry) => {
|
|
3208
|
+
const spaceId = this.resolvePermissionSpace(entry.space, session);
|
|
3209
|
+
const service = this.shortServiceName(entry.service);
|
|
3210
|
+
return entry.actions.map((action) => ({
|
|
3211
|
+
spaceId,
|
|
3212
|
+
service,
|
|
3213
|
+
path: entry.path,
|
|
3214
|
+
action
|
|
3215
|
+
}));
|
|
3216
|
+
});
|
|
3217
|
+
}
|
|
3218
|
+
findRuntimeGrantsForPermissionEntries(entries, session) {
|
|
3219
|
+
const grants = [];
|
|
3220
|
+
const operations = this.permissionEntriesToOperations(entries, session);
|
|
3221
|
+
if (operations.length === 0) {
|
|
3222
|
+
return grants;
|
|
3223
|
+
}
|
|
3224
|
+
for (const operation of operations) {
|
|
3225
|
+
const grant = this.findGrantForOperation(operation);
|
|
3226
|
+
if (!grant) {
|
|
3227
|
+
return [];
|
|
3228
|
+
}
|
|
3229
|
+
if (!grants.includes(grant)) {
|
|
3230
|
+
grants.push(grant);
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3233
|
+
return grants;
|
|
3234
|
+
}
|
|
3235
|
+
runtimeDelegationFromSession(delegatedSession, entries, spaceId, session, expiresAt) {
|
|
3236
|
+
const resources = this.delegatedResourcesForEntries(entries, spaceId);
|
|
3237
|
+
const primary = resources[0];
|
|
3238
|
+
return {
|
|
3239
|
+
cid: delegatedSession.delegationCid,
|
|
3240
|
+
delegationHeader: delegatedSession.delegationHeader,
|
|
3241
|
+
spaceId,
|
|
3242
|
+
path: primary.path,
|
|
3243
|
+
actions: primary.actions,
|
|
3244
|
+
resources,
|
|
3245
|
+
disableSubDelegation: false,
|
|
3246
|
+
expiry: expiresAt,
|
|
3247
|
+
delegateDID: session.verificationMethod,
|
|
3248
|
+
ownerAddress: session.address,
|
|
3249
|
+
chainId: session.chainId,
|
|
3250
|
+
host: this.config.host
|
|
3251
|
+
};
|
|
3252
|
+
}
|
|
3253
|
+
runtimeGrantFromDelegation(delegation, session) {
|
|
3254
|
+
const operations = this.operationsFromDelegation(delegation);
|
|
3255
|
+
return {
|
|
3256
|
+
session: {
|
|
3257
|
+
delegationHeader: delegation.delegationHeader,
|
|
3258
|
+
delegationCid: delegation.cid,
|
|
3259
|
+
spaceId: delegation.spaceId,
|
|
3260
|
+
verificationMethod: session.verificationMethod,
|
|
3261
|
+
jwk: session.jwk
|
|
3262
|
+
},
|
|
3263
|
+
delegation,
|
|
3264
|
+
operations,
|
|
3265
|
+
expiresAt: delegation.expiry
|
|
3266
|
+
};
|
|
3267
|
+
}
|
|
3268
|
+
delegatedResourcesForEntries(entries, spaceId) {
|
|
3269
|
+
return entries.map((entry) => ({
|
|
3270
|
+
service: this.shortServiceName(entry.service),
|
|
3271
|
+
space: spaceId,
|
|
3272
|
+
path: entry.path,
|
|
3273
|
+
actions: [...entry.actions]
|
|
3274
|
+
}));
|
|
3275
|
+
}
|
|
3276
|
+
operationsFromDelegation(delegation) {
|
|
3277
|
+
const resources = delegation.resources !== void 0 && delegation.resources.length > 0 ? delegation.resources : this.flatDelegationResources(delegation);
|
|
3278
|
+
return resources.flatMap(
|
|
3279
|
+
(resource) => resource.actions.map((action) => ({
|
|
3280
|
+
spaceId: resource.space,
|
|
3281
|
+
service: this.invocationServiceName(resource.service),
|
|
3282
|
+
path: resource.path,
|
|
3283
|
+
action
|
|
3284
|
+
}))
|
|
3285
|
+
);
|
|
3286
|
+
}
|
|
3287
|
+
flatDelegationResources(delegation) {
|
|
3288
|
+
const byService = /* @__PURE__ */ new Map();
|
|
3289
|
+
for (const action of delegation.actions) {
|
|
3290
|
+
const service = this.shortServiceName(action.split("/")[0]);
|
|
3291
|
+
const actions = byService.get(service) ?? [];
|
|
3292
|
+
actions.push(action);
|
|
3293
|
+
byService.set(service, actions);
|
|
3294
|
+
}
|
|
3295
|
+
return [...byService.entries()].map(([service, actions]) => ({
|
|
3296
|
+
service,
|
|
3297
|
+
space: delegation.spaceId,
|
|
3298
|
+
path: delegation.path,
|
|
3299
|
+
actions
|
|
3300
|
+
}));
|
|
3301
|
+
}
|
|
3302
|
+
selectInvocationSession(fallback, service, path, action) {
|
|
3303
|
+
const grant = this.findGrantForOperation({
|
|
3304
|
+
spaceId: fallback.spaceId,
|
|
3305
|
+
service: this.invocationServiceName(service),
|
|
3306
|
+
path,
|
|
3307
|
+
action
|
|
3308
|
+
});
|
|
3309
|
+
return grant?.session ?? fallback;
|
|
3310
|
+
}
|
|
3311
|
+
findGrantForOperations(operations) {
|
|
3312
|
+
if (operations.length === 0) {
|
|
3313
|
+
return void 0;
|
|
3314
|
+
}
|
|
3315
|
+
this.pruneExpiredRuntimePermissionGrants();
|
|
3316
|
+
return this.runtimePermissionGrants.find((grant) => {
|
|
3317
|
+
return operations.every(
|
|
3318
|
+
(operation) => grant.operations.some(
|
|
3319
|
+
(granted) => this.operationCovers(granted, operation)
|
|
3320
|
+
)
|
|
3321
|
+
);
|
|
3322
|
+
});
|
|
3323
|
+
}
|
|
3324
|
+
findGrantForOperation(operation) {
|
|
3325
|
+
return this.findGrantForOperations([operation]);
|
|
3326
|
+
}
|
|
3327
|
+
pruneExpiredRuntimePermissionGrants() {
|
|
3328
|
+
const now = Date.now();
|
|
3329
|
+
this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
|
|
3330
|
+
(grant) => grant.expiresAt.getTime() > now
|
|
3331
|
+
);
|
|
3332
|
+
}
|
|
3333
|
+
operationCovers(granted, requested) {
|
|
3334
|
+
return granted.spaceId === requested.spaceId && granted.service === requested.service && this.actionContains(granted.action, requested.action) && this.pathContains(granted.path, requested.path);
|
|
3335
|
+
}
|
|
3336
|
+
actionContains(grantedAction, requestedAction) {
|
|
3337
|
+
if (grantedAction === requestedAction) {
|
|
3338
|
+
return true;
|
|
3339
|
+
}
|
|
3340
|
+
if (grantedAction.endsWith("/*")) {
|
|
3341
|
+
const prefix = grantedAction.slice(0, -2);
|
|
3342
|
+
return requestedAction.startsWith(`${prefix}/`);
|
|
3343
|
+
}
|
|
3344
|
+
return false;
|
|
3345
|
+
}
|
|
3346
|
+
invocationServiceName(service) {
|
|
3347
|
+
return service.startsWith("tinycloud.") ? this.shortServiceName(service) : service;
|
|
3348
|
+
}
|
|
3349
|
+
pathContains(grantedPath, requestedPath) {
|
|
3350
|
+
if (grantedPath === "" || grantedPath === "/") {
|
|
3351
|
+
return true;
|
|
3352
|
+
}
|
|
3353
|
+
if (grantedPath.endsWith("/**")) {
|
|
3354
|
+
return requestedPath.startsWith(grantedPath.slice(0, -3));
|
|
3355
|
+
}
|
|
3356
|
+
if (grantedPath.endsWith("/*")) {
|
|
3357
|
+
const prefix = grantedPath.slice(0, -2);
|
|
3358
|
+
if (!requestedPath.startsWith(prefix)) {
|
|
3359
|
+
return false;
|
|
3360
|
+
}
|
|
3361
|
+
const remainder = requestedPath.slice(prefix.length);
|
|
3362
|
+
return !remainder.includes("/") || remainder === "/";
|
|
3363
|
+
}
|
|
3364
|
+
if (grantedPath.endsWith("/")) {
|
|
3365
|
+
return requestedPath.startsWith(grantedPath);
|
|
3366
|
+
}
|
|
3367
|
+
return grantedPath === requestedPath;
|
|
3368
|
+
}
|
|
2741
3369
|
/**
|
|
2742
3370
|
* Issue a delegation via the legacy wallet-signed SIWE path for a single
|
|
2743
3371
|
* {@link PermissionEntry}. Shares the implementation with the public
|
|
@@ -3129,7 +3757,7 @@ import {
|
|
|
3129
3757
|
SessionExpiredError as SessionExpiredError2,
|
|
3130
3758
|
ManifestValidationError,
|
|
3131
3759
|
composeManifestRequest as composeManifestRequest2,
|
|
3132
|
-
resolveManifest,
|
|
3760
|
+
resolveManifest as resolveManifest2,
|
|
3133
3761
|
validateManifest,
|
|
3134
3762
|
loadManifest,
|
|
3135
3763
|
isCapabilitySubset as isCapabilitySubset2,
|
|
@@ -3158,7 +3786,7 @@ function deserializeDelegation(data) {
|
|
|
3158
3786
|
import { KVService as KVService3, PrefixedKVService } from "@tinycloud/sdk-core";
|
|
3159
3787
|
import { SQLService as SQLService3, SQLAction, DatabaseHandle } from "@tinycloud/sdk-core";
|
|
3160
3788
|
import { DuckDbService as DuckDbService3, DuckDbDatabaseHandle, DuckDbAction } from "@tinycloud/sdk-core";
|
|
3161
|
-
import { DataVaultService as DataVaultService2, VaultHeaders, VaultPublicSpaceKVActions, createVaultCrypto as createVaultCrypto2 } from "@tinycloud/sdk-core";
|
|
3789
|
+
import { DataVaultService as DataVaultService2, VaultHeaders, VaultPublicSpaceKVActions, createVaultCrypto as createVaultCrypto2, SecretsService as SecretsService2 } from "@tinycloud/sdk-core";
|
|
3162
3790
|
import {
|
|
3163
3791
|
DelegationManager as DelegationManager2,
|
|
3164
3792
|
SharingService as SharingService2,
|
|
@@ -3212,6 +3840,7 @@ export {
|
|
|
3212
3840
|
ProtocolMismatchError,
|
|
3213
3841
|
SQLAction,
|
|
3214
3842
|
SQLService3 as SQLService,
|
|
3843
|
+
SecretsService2 as SecretsService,
|
|
3215
3844
|
ServiceContext3 as ServiceContext,
|
|
3216
3845
|
SessionExpiredError2 as SessionExpiredError,
|
|
3217
3846
|
SharingService2 as SharingService,
|
|
@@ -3243,7 +3872,7 @@ export {
|
|
|
3243
3872
|
makePublicSpaceId2 as makePublicSpaceId,
|
|
3244
3873
|
parseExpiry2 as parseExpiry,
|
|
3245
3874
|
parseSpaceUri,
|
|
3246
|
-
resolveManifest,
|
|
3875
|
+
resolveManifest2 as resolveManifest,
|
|
3247
3876
|
resourceCapabilitiesToSpaceAbilitiesMap2 as resourceCapabilitiesToSpaceAbilitiesMap,
|
|
3248
3877
|
serializeDelegation,
|
|
3249
3878
|
validateManifest
|