@tinycloud/cli 1.1.0-beta.18 → 1.1.0-beta.19
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/index.js.map +1 -1
- package/dist/legacy-entry.js +293 -30
- package/dist/legacy-entry.js.map +1 -1
- package/package.json +4 -4
package/dist/legacy-entry.js
CHANGED
|
@@ -13804,6 +13804,9 @@ function permissionRequestsPath(profile) {
|
|
|
13804
13804
|
function grantHistoryPath(profile) {
|
|
13805
13805
|
return join4(PROFILES_DIR, profile, "auth-grants.jsonl");
|
|
13806
13806
|
}
|
|
13807
|
+
function localGrantArtifactsPath(profile) {
|
|
13808
|
+
return join4(PROFILES_DIR, profile, "auth-grant-artifacts.json");
|
|
13809
|
+
}
|
|
13807
13810
|
function createPermissionRequestArtifact(params) {
|
|
13808
13811
|
return buildPermissionRequestArtifact({
|
|
13809
13812
|
profile: params.profileName,
|
|
@@ -13828,6 +13831,32 @@ function didWithoutFragment(did) {
|
|
|
13828
13831
|
async function loadAdditionalDelegations(profile) {
|
|
13829
13832
|
return readAdditionalDelegations(profile);
|
|
13830
13833
|
}
|
|
13834
|
+
async function loadLocalGrantArtifacts(profile) {
|
|
13835
|
+
const path = localGrantArtifactsPath(profile);
|
|
13836
|
+
if (!await fileExists(path)) return [];
|
|
13837
|
+
let parsed;
|
|
13838
|
+
try {
|
|
13839
|
+
parsed = JSON.parse(await readFile2(path, "utf8"));
|
|
13840
|
+
} catch {
|
|
13841
|
+
return [];
|
|
13842
|
+
}
|
|
13843
|
+
if (!Array.isArray(parsed)) return [];
|
|
13844
|
+
return parsed.filter(
|
|
13845
|
+
(value) => value !== null && typeof value === "object" && typeof value.delegationCid === "string" && value.delegation !== null && typeof value.delegation === "object" && value.delegation.cid === value.delegationCid && typeof value.delegation.delegationHeader?.Authorization === "string"
|
|
13846
|
+
);
|
|
13847
|
+
}
|
|
13848
|
+
async function appendLocalGrantArtifact(profile, delegation) {
|
|
13849
|
+
await ProfileManager.ensureProfileDir(profile);
|
|
13850
|
+
await withProfileLock2(profile, async () => {
|
|
13851
|
+
await refuseWriteToDeletedProfile2(profile);
|
|
13852
|
+
const records = await loadLocalGrantArtifacts(profile);
|
|
13853
|
+
if (records.some((record) => record.delegationCid === delegation.cid)) return;
|
|
13854
|
+
await writeJsonAtomic(localGrantArtifactsPath(profile), [
|
|
13855
|
+
...records,
|
|
13856
|
+
{ delegationCid: delegation.cid, delegation }
|
|
13857
|
+
]);
|
|
13858
|
+
});
|
|
13859
|
+
}
|
|
13831
13860
|
async function appendAdditionalDelegations(profile, entries) {
|
|
13832
13861
|
const { mergeDelegationsWithoutRequest } = await import("@tinycloud/operations/delegation-binding");
|
|
13833
13862
|
await updateProfileStore(profile, "additional-delegations", (stored) => ({
|
|
@@ -15246,11 +15275,15 @@ function ownerSpaceId(space, ownerDid) {
|
|
|
15246
15275
|
function permissionTuples(permissions, ownerDid) {
|
|
15247
15276
|
return new Set(permissions.flatMap((permission) => actionTuples(permission, ownerDid)));
|
|
15248
15277
|
}
|
|
15278
|
+
function isRawCidRevocationPermission(permission) {
|
|
15279
|
+
return permission.service === "tinycloud.delegation" && permission.space?.startsWith("urn:cid:") === true && permission.path === "" && permission.actions.includes("tinycloud.delegation/revoke");
|
|
15280
|
+
}
|
|
15249
15281
|
function actionTuples(permission, ownerDid) {
|
|
15250
15282
|
const service = permission.service.startsWith("tinycloud.") ? permission.service : `tinycloud.${permission.service}`;
|
|
15251
|
-
const
|
|
15252
|
-
const
|
|
15253
|
-
const
|
|
15283
|
+
const rawEncryption = isVerifiedRawEncryptionPermission(permission);
|
|
15284
|
+
const rawCidRevocation = isRawCidRevocationPermission(permission);
|
|
15285
|
+
const space = rawEncryption ? ENCRYPTION_MANIFEST_SPACE2 : rawCidRevocation ? permission.space : normalizePkhIdentifier(ownerSpaceId(permission.space ?? "", ownerDid));
|
|
15286
|
+
const path = rawEncryption ? normalizePkhIdentifier(permission.path) : permission.path;
|
|
15254
15287
|
return permission.actions.map((action) => JSON.stringify([service, space, path, action.includes("/") ? action : `${service}/${action}`]));
|
|
15255
15288
|
}
|
|
15256
15289
|
function isLegacyNestedDecrypt(permission, requested, ownerDid, spaceId) {
|
|
@@ -15321,7 +15354,7 @@ function declinedPermissions(requested, signed, ownerDid) {
|
|
|
15321
15354
|
const missing = [...permissionTuples(requested, ownerDid)].filter((tuple) => !granted.has(tuple));
|
|
15322
15355
|
const requestedScopes = /* @__PURE__ */ new Map();
|
|
15323
15356
|
for (const entry of requested) {
|
|
15324
|
-
const space = isVerifiedRawEncryptionPermission(entry) ? entry.space ?? ENCRYPTION_MANIFEST_SPACE2 : ownerSpaceId(entry.space ?? "", ownerDid);
|
|
15357
|
+
const space = isVerifiedRawEncryptionPermission(entry) ? entry.space ?? ENCRYPTION_MANIFEST_SPACE2 : isRawCidRevocationPermission(entry) ? entry.space : ownerSpaceId(entry.space ?? "", ownerDid);
|
|
15325
15358
|
for (const tuple of actionTuples(entry, ownerDid)) {
|
|
15326
15359
|
requestedScopes.set(tuple, { space, path: entry.path });
|
|
15327
15360
|
}
|
|
@@ -15344,7 +15377,10 @@ function scopedLoginPermissions(permissions) {
|
|
|
15344
15377
|
}
|
|
15345
15378
|
function grantRequestPermissions(group, anchorSpace) {
|
|
15346
15379
|
const request = group.map((p) => isRawEncryptionPermission(p) ? { ...p, space: ENCRYPTION_MANIFEST_SPACE2, path: canonicalNetworkUrn(p.path) } : p);
|
|
15347
|
-
|
|
15380
|
+
const capabilitySpace = request.find(
|
|
15381
|
+
(permission) => !isRawEncryptionPermission(permission) && !isRawCidRevocationPermission(permission)
|
|
15382
|
+
)?.space ?? anchorSpace;
|
|
15383
|
+
return withCapabilitiesRead(request, capabilitySpace);
|
|
15348
15384
|
}
|
|
15349
15385
|
function withCapabilitiesRead(permissions, space) {
|
|
15350
15386
|
const hasRead = permissions.some((p) => p.service === "tinycloud.capabilities" && p.path === "" && p.actions.includes(CAPABILITIES_READ) && normalizePkhIdentifier(p.space ?? "") === normalizePkhIdentifier(space));
|
|
@@ -15378,7 +15414,10 @@ function verifySignedSession(data, key, sessionDid, expected = {}) {
|
|
|
15378
15414
|
if (/expir/i.test(error instanceof Error ? error.message : String(error))) {
|
|
15379
15415
|
throw new CLIError("AUTH_EXPIRED", `The approved session has expired. ${notStored}`, ExitCode.AUTH_REQUIRED);
|
|
15380
15416
|
}
|
|
15381
|
-
throw
|
|
15417
|
+
throw Object.assign(
|
|
15418
|
+
new CLIError("OPENKEY_PROOF_INVALID", `OpenKey did not return a complete, verifiable session proof. ${notStored}`, ExitCode.AUTH_REQUIRED),
|
|
15419
|
+
{ cause: error }
|
|
15420
|
+
);
|
|
15382
15421
|
}
|
|
15383
15422
|
const signedExpiry = Date.parse(expiresAt);
|
|
15384
15423
|
if (signedExpiry <= Date.now()) {
|
|
@@ -15395,10 +15434,23 @@ function verifySignedSession(data, key, sessionDid, expected = {}) {
|
|
|
15395
15434
|
}
|
|
15396
15435
|
function verifyScopedLogin(data, key, sessionDid, requested, expected = {}) {
|
|
15397
15436
|
const notStored = expected.purpose === "grant" ? "No grant was stored." : "No scoped session was saved.";
|
|
15398
|
-
|
|
15437
|
+
let signed;
|
|
15438
|
+
try {
|
|
15439
|
+
signed = verifySignedSession(data, key, sessionDid, expected);
|
|
15440
|
+
} catch (error) {
|
|
15441
|
+
const cause = error instanceof Error ? error.cause : void 0;
|
|
15442
|
+
if (requested.some(isRawCidRevocationPermission) && /invalid ReCap resource URI/i.test(cause instanceof Error ? cause.message : String(cause ?? ""))) {
|
|
15443
|
+
throw new CLIError(
|
|
15444
|
+
"RAW_RECAP_RESOURCE_UNSUPPORTED",
|
|
15445
|
+
"This WASM build cannot verify raw urn:cid ReCap resources.",
|
|
15446
|
+
ExitCode.PERMISSION_DENIED
|
|
15447
|
+
);
|
|
15448
|
+
}
|
|
15449
|
+
throw error;
|
|
15450
|
+
}
|
|
15399
15451
|
const spaceId = data.spaceId;
|
|
15400
15452
|
for (const permission of requested) {
|
|
15401
|
-
if (isRawEncryptionPermission(permission)) continue;
|
|
15453
|
+
if (isRawEncryptionPermission(permission) || isRawCidRevocationPermission(permission)) continue;
|
|
15402
15454
|
if (normalizePkhIdentifier(ownerSpaceId(permission.space ?? "", signed.ownerDid)) !== normalizePkhIdentifier(spaceId)) {
|
|
15403
15455
|
throw new CLIError("OPENKEY_SCOPE_MISMATCH", `The approved space differs from the requested space. ${notStored}`, ExitCode.PERMISSION_DENIED);
|
|
15404
15456
|
}
|
|
@@ -16439,6 +16491,13 @@ function registerAuthCommand(program) {
|
|
|
16439
16491
|
yes: options.yes === true
|
|
16440
16492
|
});
|
|
16441
16493
|
const grant = await grantAuthRequest(node, resolvedRequest);
|
|
16494
|
+
await appendLocalGrantArtifact(ctx.profile, grant.delegation);
|
|
16495
|
+
await appendGrantHistory(ctx.profile, {
|
|
16496
|
+
addedCaps: grant.permissions,
|
|
16497
|
+
source: "cli",
|
|
16498
|
+
delegationCid: grant.delegationCid,
|
|
16499
|
+
expiry: grant.expiry
|
|
16500
|
+
});
|
|
16442
16501
|
outputJson(grant);
|
|
16443
16502
|
} catch (error) {
|
|
16444
16503
|
handleError(error);
|
|
@@ -16795,7 +16854,10 @@ async function activateAndStoreOpenKeyGrants(profileName2, host, node, grants, s
|
|
|
16795
16854
|
});
|
|
16796
16855
|
}
|
|
16797
16856
|
async function ensureDelegationAuthority(params) {
|
|
16798
|
-
if (!params.force && params.node.hasRuntimePermissions(params.requested)) return;
|
|
16857
|
+
if (!params.force && params.node.hasRuntimePermissions(params.requested)) return void 0;
|
|
16858
|
+
if (!params.consentAlreadyGiven && (params.persist === false || params.profile.authMethod !== "openkey")) {
|
|
16859
|
+
await requirePermissionConsent(params.requested, params.yes);
|
|
16860
|
+
}
|
|
16799
16861
|
if (params.profile.authMethod === "openkey") {
|
|
16800
16862
|
const key = await ProfileManager.getKey(params.ctx.profile);
|
|
16801
16863
|
if (!key) {
|
|
@@ -16815,6 +16877,37 @@ async function ensureDelegationAuthority(params) {
|
|
|
16815
16877
|
expiry: expiryCap
|
|
16816
16878
|
};
|
|
16817
16879
|
const anchorSpace = params.anchorSpace ?? params.profile.spaceId ?? params.profile.spaceName;
|
|
16880
|
+
if (params.persist === false) {
|
|
16881
|
+
const request = grantRequestPermissions(params.requested, anchorSpace);
|
|
16882
|
+
let delegationData;
|
|
16883
|
+
try {
|
|
16884
|
+
delegationData = await acquireOpenKey(params.profile.did, {
|
|
16885
|
+
jwk: key,
|
|
16886
|
+
host: params.ctx.host,
|
|
16887
|
+
permissions: request,
|
|
16888
|
+
reason: permissionGrantReason(params.reason, request),
|
|
16889
|
+
openkeyHost,
|
|
16890
|
+
expiry: expiryCap === void 0 ? void 0 : openKeyExpiryParam(expiryCap)
|
|
16891
|
+
});
|
|
16892
|
+
} catch (error) {
|
|
16893
|
+
if (params.requested.some(isRawCidRevocationPermission) && /invalid ReCap resource URI/i.test(error instanceof Error ? error.message : String(error))) {
|
|
16894
|
+
throw new CLIError(
|
|
16895
|
+
"RAW_RECAP_RESOURCE_UNSUPPORTED",
|
|
16896
|
+
"The OpenKey/WASM ReCap encoder does not support raw urn:cid revocation resources.",
|
|
16897
|
+
ExitCode.PERMISSION_DENIED
|
|
16898
|
+
);
|
|
16899
|
+
}
|
|
16900
|
+
throw error;
|
|
16901
|
+
}
|
|
16902
|
+
const delegation = portableFromOpenKeyDelegation(
|
|
16903
|
+
delegationData,
|
|
16904
|
+
request,
|
|
16905
|
+
params.ctx.host,
|
|
16906
|
+
proof
|
|
16907
|
+
);
|
|
16908
|
+
await params.node.useRuntimeDelegation(delegation);
|
|
16909
|
+
return delegation;
|
|
16910
|
+
}
|
|
16818
16911
|
const grants = [];
|
|
16819
16912
|
for (const group of groupPermissionsBySpace(params.requested)) {
|
|
16820
16913
|
const request = grantRequestPermissions(group, anchorSpace);
|
|
@@ -16832,21 +16925,11 @@ async function ensureDelegationAuthority(params) {
|
|
|
16832
16925
|
await activateAndStoreOpenKeyGrants(params.ctx.profile, params.ctx.host, params.node, grants, "cli");
|
|
16833
16926
|
return;
|
|
16834
16927
|
}
|
|
16835
|
-
if (isInteractive()) {
|
|
16836
|
-
if (!params.yes) {
|
|
16837
|
-
await confirmPermissionRequest(params.requested);
|
|
16838
|
-
}
|
|
16839
|
-
} else if (!params.yes) {
|
|
16840
|
-
throw new CLIError(
|
|
16841
|
-
"CONFIRMATION_REQUIRED",
|
|
16842
|
-
"Local-key auth grants in non-interactive mode require --yes.",
|
|
16843
|
-
ExitCode.USAGE_ERROR
|
|
16844
|
-
);
|
|
16845
|
-
}
|
|
16846
16928
|
const delegations = await params.node.grantRuntimePermissions(
|
|
16847
16929
|
params.requested,
|
|
16848
16930
|
params.expiryOption !== void 0 ? { expiry: params.expiryOption } : void 0
|
|
16849
16931
|
);
|
|
16932
|
+
if (params.persist === false) return delegations[0];
|
|
16850
16933
|
for (const delegation of delegations) {
|
|
16851
16934
|
const covering = permissionsFromDelegation(delegation);
|
|
16852
16935
|
await appendAdditionalDelegation(
|
|
@@ -16924,6 +17007,17 @@ async function confirmPermissionRequest(permissions) {
|
|
|
16924
17007
|
throw new CLIError("REQUEST_CANCELLED", "Permission request cancelled.", ExitCode.ERROR);
|
|
16925
17008
|
}
|
|
16926
17009
|
}
|
|
17010
|
+
async function requirePermissionConsent(permissions, yes) {
|
|
17011
|
+
if (isInteractive()) {
|
|
17012
|
+
if (!yes) await confirmPermissionRequest(permissions);
|
|
17013
|
+
} else if (!yes) {
|
|
17014
|
+
throw new CLIError(
|
|
17015
|
+
"CONFIRMATION_REQUIRED",
|
|
17016
|
+
"Permission grants in non-interactive mode require --yes.",
|
|
17017
|
+
ExitCode.USAGE_ERROR
|
|
17018
|
+
);
|
|
17019
|
+
}
|
|
17020
|
+
}
|
|
16927
17021
|
function isDangerousPermission(permission) {
|
|
16928
17022
|
if (permission.path === "" || permission.path === "/") return true;
|
|
16929
17023
|
return permission.actions.some(
|
|
@@ -16952,7 +17046,7 @@ function groupPermissionsBySpace(permissions) {
|
|
|
16952
17046
|
const groups = /* @__PURE__ */ new Map();
|
|
16953
17047
|
const rawEntries = [];
|
|
16954
17048
|
for (const permission of permissions) {
|
|
16955
|
-
if (isRawEncryptionPermission(permission)) {
|
|
17049
|
+
if (isRawEncryptionPermission(permission) || isRawCidRevocationPermission(permission)) {
|
|
16956
17050
|
rawEntries.push(permission);
|
|
16957
17051
|
continue;
|
|
16958
17052
|
}
|
|
@@ -16983,11 +17077,13 @@ function portableFromOpenKeyDelegation(data, requested, host, proof) {
|
|
|
16983
17077
|
}
|
|
16984
17078
|
const returnedSpace = data.spaceId;
|
|
16985
17079
|
const effective = session.permissions;
|
|
16986
|
-
const spaced = effective.filter(
|
|
17080
|
+
const spaced = effective.filter(
|
|
17081
|
+
(permission) => !isRawEncryptionPermission(permission) && !isRawCidRevocationPermission(permission)
|
|
17082
|
+
);
|
|
16987
17083
|
const primary = spaced.find((permission) => permission.service !== "tinycloud.capabilities") ?? spaced[0] ?? effective[0];
|
|
16988
17084
|
const resources = effective.map((permission) => ({
|
|
16989
17085
|
service: permission.service.slice("tinycloud.".length),
|
|
16990
|
-
space: isVerifiedRawEncryptionPermission(permission) ? "encryption" : returnedSpace,
|
|
17086
|
+
space: isRawCidRevocationPermission(permission) ? permission.space : isVerifiedRawEncryptionPermission(permission) ? "encryption" : returnedSpace,
|
|
16991
17087
|
path: permission.path,
|
|
16992
17088
|
actions: [...permission.actions],
|
|
16993
17089
|
...permission.caveats === void 0 ? {} : { caveats: structuredClone(permission.caveats) }
|
|
@@ -17463,6 +17559,7 @@ init_profiles();
|
|
|
17463
17559
|
init_formatter();
|
|
17464
17560
|
init_errors();
|
|
17465
17561
|
init_constants();
|
|
17562
|
+
import { parseSignedCompactUcanAttenuation } from "@tinycloud/sdk-core";
|
|
17466
17563
|
function normalizeDid(input) {
|
|
17467
17564
|
const normalized = input.trim();
|
|
17468
17565
|
const fragmentIndex = normalized.indexOf("#");
|
|
@@ -17470,11 +17567,35 @@ function normalizeDid(input) {
|
|
|
17470
17567
|
}
|
|
17471
17568
|
function didMatches(actual, expected) {
|
|
17472
17569
|
if (!actual) return false;
|
|
17473
|
-
|
|
17474
|
-
|
|
17475
|
-
|
|
17476
|
-
|
|
17570
|
+
return normalizeDid(actual) === normalizeDid(expected);
|
|
17571
|
+
}
|
|
17572
|
+
function ownerDidFromSpace(spaceId) {
|
|
17573
|
+
const owner = /^tinycloud:((?:did:)?pkh:eip155:[1-9]\d*:0x[0-9a-fA-F]{40})(?::|\/|$)/.exec(spaceId ?? "")?.[1];
|
|
17574
|
+
if (!owner) return void 0;
|
|
17575
|
+
return owner.startsWith("did:") ? owner : `did:${owner}`;
|
|
17576
|
+
}
|
|
17577
|
+
async function resolveCidBoundTargetSpace(params) {
|
|
17578
|
+
if (!params.ownerDid) return void 0;
|
|
17579
|
+
const grants = await loadLocalGrantArtifacts(params.profileName);
|
|
17580
|
+
for (const grant of grants) {
|
|
17581
|
+
const delegation = grant.delegation;
|
|
17582
|
+
if (grant.delegationCid !== params.cid || delegation.cid !== params.cid) continue;
|
|
17583
|
+
try {
|
|
17584
|
+
const authorization = delegation.delegationHeader.Authorization.replace(/^Bearer /i, "");
|
|
17585
|
+
const signed = parseSignedCompactUcanAttenuation(authorization, params.cid);
|
|
17586
|
+
for (const resource of Object.keys(signed.payload.att)) {
|
|
17587
|
+
const space = /^(tinycloud:[^/]+)\/[^/]+\/.*$/.exec(resource)?.[1];
|
|
17588
|
+
if (!space) continue;
|
|
17589
|
+
const ownerMatch = /^tinycloud:((?:did:)?pkh:eip155:[1-9]\d*:0x[0-9a-fA-F]{40}):/.exec(space);
|
|
17590
|
+
const owner = ownerMatch?.[1];
|
|
17591
|
+
const ownerDid = owner?.startsWith("did:") ? owner : owner ? `did:${owner}` : void 0;
|
|
17592
|
+
if (ownerDid && normalizeDid(ownerDid) === normalizeDid(params.ownerDid)) return space;
|
|
17593
|
+
}
|
|
17594
|
+
} catch {
|
|
17595
|
+
continue;
|
|
17596
|
+
}
|
|
17477
17597
|
}
|
|
17598
|
+
return void 0;
|
|
17478
17599
|
}
|
|
17479
17600
|
function registerDelegationCommand(program) {
|
|
17480
17601
|
const delegation = program.command("delegation").description("Manage delegations");
|
|
@@ -17554,16 +17675,158 @@ function registerDelegationCommand(program) {
|
|
|
17554
17675
|
handleError(error);
|
|
17555
17676
|
}
|
|
17556
17677
|
});
|
|
17557
|
-
delegation.command("revoke <cid>").description("Revoke a delegation").action(async (cid,
|
|
17678
|
+
delegation.command("revoke <cid>").description("Revoke a delegation").option("--yes", "Skip local-key TTY confirmation", false).action(async (cid, options, cmd) => {
|
|
17558
17679
|
try {
|
|
17559
17680
|
const globalOpts = cmd.optsWithGlobals();
|
|
17560
17681
|
const ctx = await ProfileManager.resolveContext(globalOpts);
|
|
17561
17682
|
const node = await ensureAuthenticated(ctx);
|
|
17562
|
-
const
|
|
17683
|
+
const profile = await ProfileManager.getProfile(ctx.profile);
|
|
17684
|
+
const revokePermission = {
|
|
17685
|
+
service: "tinycloud.delegation",
|
|
17686
|
+
space: `urn:cid:${cid}`,
|
|
17687
|
+
path: "",
|
|
17688
|
+
actions: ["tinycloud.delegation/revoke"]
|
|
17689
|
+
};
|
|
17690
|
+
const rawAuthorityCovered = node.hasRuntimePermissions([revokePermission]);
|
|
17691
|
+
if (!rawAuthorityCovered) await requirePermissionConsent([revokePermission], options.yes);
|
|
17692
|
+
let targetFound = false;
|
|
17693
|
+
let targetSpaceSource;
|
|
17694
|
+
let queryDenied = false;
|
|
17695
|
+
let boundSpace;
|
|
17696
|
+
const accountSpaceId = node.accountSpaceId;
|
|
17697
|
+
if (!accountSpaceId) {
|
|
17698
|
+
throw new CLIError(
|
|
17699
|
+
"DELEGATION_QUERY_AUTHORITY_UNAVAILABLE",
|
|
17700
|
+
"Cannot determine the account space needed to query delegation records.",
|
|
17701
|
+
ExitCode.AUTH_REQUIRED
|
|
17702
|
+
);
|
|
17703
|
+
}
|
|
17704
|
+
let cursor;
|
|
17705
|
+
do {
|
|
17706
|
+
const query = await node.delegationManager.query({
|
|
17707
|
+
direction: "all",
|
|
17708
|
+
limit: 100,
|
|
17709
|
+
...cursor === void 0 ? {} : { cursor }
|
|
17710
|
+
});
|
|
17711
|
+
if (!query.ok) {
|
|
17712
|
+
if (query.error.meta?.status === 403) {
|
|
17713
|
+
queryDenied = true;
|
|
17714
|
+
break;
|
|
17715
|
+
}
|
|
17716
|
+
throw cliErrorFromService(query.error);
|
|
17717
|
+
}
|
|
17718
|
+
targetFound = query.data.items.some((delegation2) => delegation2.cid === cid);
|
|
17719
|
+
cursor = targetFound ? void 0 : query.data.nextCursor;
|
|
17720
|
+
} while (cursor !== void 0);
|
|
17721
|
+
targetSpaceSource = "node";
|
|
17722
|
+
if (!targetFound) {
|
|
17723
|
+
const grantHistory = await readGrantHistory(ctx.profile);
|
|
17724
|
+
const recordedGrant = [...grantHistory].reverse().find((entry) => entry.delegationCid === cid);
|
|
17725
|
+
if (recordedGrant) {
|
|
17726
|
+
targetSpaceSource = "local-grant-history";
|
|
17727
|
+
} else if (queryDenied) {
|
|
17728
|
+
boundSpace = await resolveCidBoundTargetSpace({
|
|
17729
|
+
profileName: ctx.profile,
|
|
17730
|
+
ownerDid: profile.ownerDid ?? ownerDidFromSpace(node.accountSpaceId) ?? ownerDidFromSpace(profile.spaceId),
|
|
17731
|
+
cid
|
|
17732
|
+
});
|
|
17733
|
+
if (boundSpace) {
|
|
17734
|
+
targetSpaceSource = "local-signed-grant-artifact";
|
|
17735
|
+
} else {
|
|
17736
|
+
throw new CLIError(
|
|
17737
|
+
"TARGET_NOT_FOUND",
|
|
17738
|
+
`Delegation "${cid}" was not found by the node or local grant sources.`,
|
|
17739
|
+
ExitCode.NOT_FOUND
|
|
17740
|
+
);
|
|
17741
|
+
}
|
|
17742
|
+
} else {
|
|
17743
|
+
throw new CLIError(
|
|
17744
|
+
"TARGET_NOT_FOUND",
|
|
17745
|
+
`Delegation "${cid}" was not found by the node or local grant history.`,
|
|
17746
|
+
ExitCode.NOT_FOUND
|
|
17747
|
+
);
|
|
17748
|
+
}
|
|
17749
|
+
}
|
|
17750
|
+
let authorityScopeSource = "cid-resource";
|
|
17751
|
+
let authorityScopeReason = "The revoke authority is scoped to the exact delegation CID.";
|
|
17752
|
+
let fallbackSpaceId;
|
|
17753
|
+
let commandAuthorityCid;
|
|
17754
|
+
const authorizeFallbackSpace = async (spaceId) => {
|
|
17755
|
+
fallbackSpaceId = spaceId;
|
|
17756
|
+
authorityScopeSource = "local-signed-grant-artifact";
|
|
17757
|
+
authorityScopeReason = "Raw-CID revoke authority could not be issued; the fallback space came from an owner-signed local grant whose Authorization recomputes to this CID.";
|
|
17758
|
+
targetSpaceSource = "local-signed-grant-artifact";
|
|
17759
|
+
const acquired = await ensureDelegationAuthority({
|
|
17760
|
+
ctx,
|
|
17761
|
+
profile,
|
|
17762
|
+
node,
|
|
17763
|
+
requested: [{
|
|
17764
|
+
service: "tinycloud.delegation",
|
|
17765
|
+
space: spaceId === node.accountSpaceId ? "default" : spaceId,
|
|
17766
|
+
path: "",
|
|
17767
|
+
actions: ["tinycloud.delegation/revoke"]
|
|
17768
|
+
}],
|
|
17769
|
+
expiryOption: void 0,
|
|
17770
|
+
reason: `Revoke delegation ${cid} using its CID-bound owner-signed grant artifact`,
|
|
17771
|
+
yes: options.yes,
|
|
17772
|
+
consentAlreadyGiven: true,
|
|
17773
|
+
persist: false
|
|
17774
|
+
});
|
|
17775
|
+
commandAuthorityCid = acquired?.cid;
|
|
17776
|
+
};
|
|
17777
|
+
if (!rawAuthorityCovered) {
|
|
17778
|
+
try {
|
|
17779
|
+
const acquired = await ensureDelegationAuthority({
|
|
17780
|
+
ctx,
|
|
17781
|
+
profile,
|
|
17782
|
+
node,
|
|
17783
|
+
requested: [revokePermission],
|
|
17784
|
+
expiryOption: void 0,
|
|
17785
|
+
reason: `Revoke delegation ${cid}`,
|
|
17786
|
+
yes: options.yes,
|
|
17787
|
+
consentAlreadyGiven: true,
|
|
17788
|
+
persist: false
|
|
17789
|
+
});
|
|
17790
|
+
commandAuthorityCid = acquired?.cid;
|
|
17791
|
+
} catch (error) {
|
|
17792
|
+
if (error === null || typeof error !== "object" || !("code" in error) || error.code !== "RAW_RECAP_RESOURCE_UNSUPPORTED") {
|
|
17793
|
+
throw error;
|
|
17794
|
+
}
|
|
17795
|
+
const fallbackSpace = boundSpace ?? await resolveCidBoundTargetSpace({
|
|
17796
|
+
profileName: ctx.profile,
|
|
17797
|
+
ownerDid: profile.ownerDid ?? ownerDidFromSpace(node.accountSpaceId) ?? ownerDidFromSpace(profile.spaceId),
|
|
17798
|
+
cid
|
|
17799
|
+
});
|
|
17800
|
+
if (!fallbackSpace) {
|
|
17801
|
+
throw new CLIError(
|
|
17802
|
+
"RAW_RECAP_RESOURCE_UNSUPPORTED",
|
|
17803
|
+
`Cannot safely revoke "${cid}": raw CID ReCap resources are unavailable and no matching owner-signed local grant artifact binds its space to the CID.`,
|
|
17804
|
+
ExitCode.PERMISSION_DENIED
|
|
17805
|
+
);
|
|
17806
|
+
}
|
|
17807
|
+
await authorizeFallbackSpace(fallbackSpace);
|
|
17808
|
+
}
|
|
17809
|
+
}
|
|
17810
|
+
const result = fallbackSpaceId ? await node.delegationManager.revoke(cid, {
|
|
17811
|
+
targetSpaceId: fallbackSpaceId,
|
|
17812
|
+
...commandAuthorityCid === void 0 ? {} : { authorityCid: commandAuthorityCid }
|
|
17813
|
+
}) : commandAuthorityCid === void 0 ? await node.revokeDelegation(cid) : await node.delegationManager.revoke(cid, { authorityCid: commandAuthorityCid });
|
|
17563
17814
|
if (!result.ok) {
|
|
17815
|
+
if (result.error.meta?.status === 403 && result.error.message === "Failed to revoke delegation: 403 - Unauthorized Revoker") {
|
|
17816
|
+
const profileDid = profile.ownerDid ?? ownerDidFromSpace(node.accountSpaceId) ?? ownerDidFromSpace(profile.spaceId) ?? "unknown";
|
|
17817
|
+
throw new CLIError(
|
|
17818
|
+
"REVOKE_UNAUTHORIZED",
|
|
17819
|
+
"The node rejected this revocation: Unauthorized Revoker.",
|
|
17820
|
+
ExitCode.PERMISSION_DENIED,
|
|
17821
|
+
{
|
|
17822
|
+
status: 403,
|
|
17823
|
+
hint: `Use a different profile belonging to the delegation's grantor, its recipient, or the owner of a space it covers; the rejected profile was "${ctx.profile}" (DID ${profileDid}). Retry with \`tc --profile <profile-name> delegation revoke ${cid}\`.`
|
|
17824
|
+
}
|
|
17825
|
+
);
|
|
17826
|
+
}
|
|
17564
17827
|
throw cliErrorFromService(result.error);
|
|
17565
17828
|
}
|
|
17566
|
-
outputJson({ cid, revoked: true });
|
|
17829
|
+
outputJson({ cid, revoked: true, targetSpaceSource, authorityScopeSource, authorityScopeReason });
|
|
17567
17830
|
} catch (error) {
|
|
17568
17831
|
handleError(error);
|
|
17569
17832
|
}
|