@tinycloud/cli 0.6.0-beta.7 → 0.6.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/index.js +123 -22
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -620,6 +620,25 @@ async function localKeySignIn(options) {
|
|
|
620
620
|
// src/auth/browser-auth.ts
|
|
621
621
|
import { createServer } from "http";
|
|
622
622
|
import { createInterface } from "readline";
|
|
623
|
+
var PRIVATE_JWK_FIELDS = /* @__PURE__ */ new Set([
|
|
624
|
+
"d",
|
|
625
|
+
"p",
|
|
626
|
+
"q",
|
|
627
|
+
"dp",
|
|
628
|
+
"dq",
|
|
629
|
+
"qi",
|
|
630
|
+
"oth",
|
|
631
|
+
"k"
|
|
632
|
+
]);
|
|
633
|
+
function publicJwkForDelegation(jwk) {
|
|
634
|
+
const publicJwk = {};
|
|
635
|
+
for (const [key, value] of Object.entries(jwk)) {
|
|
636
|
+
if (!PRIVATE_JWK_FIELDS.has(key)) {
|
|
637
|
+
publicJwk[key] = value;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
return publicJwk;
|
|
641
|
+
}
|
|
623
642
|
async function startAuthFlow(did, options = {}) {
|
|
624
643
|
if (options.paste) {
|
|
625
644
|
return pasteFlow(did, options);
|
|
@@ -641,7 +660,9 @@ function buildAuthUrl(did, options = {}) {
|
|
|
641
660
|
params.set("callback", options.callback);
|
|
642
661
|
}
|
|
643
662
|
if (options.jwk) {
|
|
644
|
-
const jwkB64 = Buffer.from(
|
|
663
|
+
const jwkB64 = Buffer.from(
|
|
664
|
+
JSON.stringify(publicJwkForDelegation(options.jwk))
|
|
665
|
+
).toString("base64url");
|
|
645
666
|
params.set("jwk", jwkB64);
|
|
646
667
|
}
|
|
647
668
|
if (options.host) {
|
|
@@ -1381,6 +1402,15 @@ function registerAuthCommand(program2) {
|
|
|
1381
1402
|
handleError(error);
|
|
1382
1403
|
}
|
|
1383
1404
|
});
|
|
1405
|
+
auth.command("rotate").description("Rotate the active profile session key").option("--paste", "Use manual paste mode instead of browser callback").action(async (options, cmd) => {
|
|
1406
|
+
try {
|
|
1407
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
1408
|
+
const ctx = await ProfileManager.resolveContext(globalOpts);
|
|
1409
|
+
await rotateAuthKey(ctx.profile, ctx.host, { paste: options.paste });
|
|
1410
|
+
} catch (error) {
|
|
1411
|
+
handleError(error);
|
|
1412
|
+
}
|
|
1413
|
+
});
|
|
1384
1414
|
auth.command("status").description("Show current authentication state").action(async (_options, cmd) => {
|
|
1385
1415
|
try {
|
|
1386
1416
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -2106,16 +2136,74 @@ function inferDelegationExpiry(data) {
|
|
|
2106
2136
|
}
|
|
2107
2137
|
return new Date(Date.now() + 60 * 60 * 1e3);
|
|
2108
2138
|
}
|
|
2109
|
-
async function
|
|
2139
|
+
async function rotateAuthKey(profileName, host, options = {}) {
|
|
2140
|
+
const profile = await ProfileManager.getProfile(profileName);
|
|
2141
|
+
const posture = resolveProfilePosture(profile);
|
|
2142
|
+
const oldDid = profile.sessionDid ?? profile.did;
|
|
2143
|
+
if (posture === "delegate-session") {
|
|
2144
|
+
throw new CLIError(
|
|
2145
|
+
"ROTATE_DELEGATE_SESSION_UNSUPPORTED",
|
|
2146
|
+
`Profile "${profileName}" is a delegated session. Request or import a new owner delegation instead of rotating it locally.`,
|
|
2147
|
+
ExitCode.PERMISSION_DENIED
|
|
2148
|
+
);
|
|
2149
|
+
}
|
|
2150
|
+
if (profile.authMethod === "local" || posture === "local-owner-key") {
|
|
2151
|
+
if (!profile.privateKey) {
|
|
2152
|
+
throw new CLIError(
|
|
2153
|
+
"LOCAL_OWNER_KEY_REQUIRED",
|
|
2154
|
+
`Profile "${profileName}" does not have a local owner private key. Run \`tc auth login --method local\` first.`,
|
|
2155
|
+
ExitCode.AUTH_REQUIRED
|
|
2156
|
+
);
|
|
2157
|
+
}
|
|
2158
|
+
await ProfileManager.clearSession(profileName);
|
|
2159
|
+
const result2 = await handleLocalAuth(profileName, host, {
|
|
2160
|
+
emitOutput: false,
|
|
2161
|
+
forceSessionKey: true
|
|
2162
|
+
});
|
|
2163
|
+
outputRotationResult(result2.profile, profileName, oldDid, "local");
|
|
2164
|
+
return;
|
|
2165
|
+
}
|
|
2166
|
+
const { jwk, did } = await withSpinner("Generating session key...", async () => {
|
|
2167
|
+
return generateKey();
|
|
2168
|
+
});
|
|
2169
|
+
await ProfileManager.setKey(profileName, jwk);
|
|
2170
|
+
await ProfileManager.clearSession(profileName);
|
|
2171
|
+
await ProfileManager.setProfile(profileName, {
|
|
2172
|
+
...profile,
|
|
2173
|
+
host,
|
|
2174
|
+
did,
|
|
2175
|
+
sessionDid: did,
|
|
2176
|
+
posture: profile.posture ?? "owner-openkey",
|
|
2177
|
+
operatorType: profile.operatorType ?? "human",
|
|
2178
|
+
authMethod: "openkey"
|
|
2179
|
+
});
|
|
2180
|
+
const result = await refreshOpenKeySession(profileName, host, {
|
|
2181
|
+
paste: options.paste
|
|
2182
|
+
});
|
|
2183
|
+
outputRotationResult(result.profile, profileName, oldDid, "openkey");
|
|
2184
|
+
}
|
|
2185
|
+
function outputRotationResult(profile, profileName, oldDid, authMethod) {
|
|
2186
|
+
outputJson({
|
|
2187
|
+
rotated: true,
|
|
2188
|
+
profile: profileName,
|
|
2189
|
+
oldDid,
|
|
2190
|
+
did: profile.did,
|
|
2191
|
+
sessionDid: profile.sessionDid ?? null,
|
|
2192
|
+
authMethod,
|
|
2193
|
+
spaceId: profile.spaceId ?? null
|
|
2194
|
+
});
|
|
2195
|
+
}
|
|
2196
|
+
async function handleLocalAuth(profileName, host, options = {}) {
|
|
2110
2197
|
const profile = await ProfileManager.getProfile(profileName).catch(() => null);
|
|
2198
|
+
const posture = profile ? resolveProfilePosture(profile) : null;
|
|
2111
2199
|
let privateKey;
|
|
2112
2200
|
let address;
|
|
2113
2201
|
let did;
|
|
2114
2202
|
let sessionDid = profile?.sessionDid;
|
|
2115
|
-
if (profile?.authMethod === "local"
|
|
2203
|
+
if ((profile?.authMethod === "local" || posture === "local-owner-key") && profile.privateKey) {
|
|
2116
2204
|
privateKey = profile.privateKey;
|
|
2117
|
-
address = profile.address;
|
|
2118
|
-
did = profile.did;
|
|
2205
|
+
address = profile.address ?? await deriveAddress(privateKey);
|
|
2206
|
+
did = profile.did.startsWith("did:pkh:") ? profile.did : addressToDID(address, profile.chainId ?? DEFAULT_CHAIN_ID);
|
|
2119
2207
|
if (isInteractive()) {
|
|
2120
2208
|
process.stderr.write(theme.muted("Using existing local key") + "\n");
|
|
2121
2209
|
process.stderr.write(formatField("Address", address) + "\n");
|
|
@@ -2134,7 +2222,7 @@ async function handleLocalAuth(profileName, host) {
|
|
|
2134
2222
|
}
|
|
2135
2223
|
}
|
|
2136
2224
|
const hasKey = await ProfileManager.getKey(profileName);
|
|
2137
|
-
if (!hasKey) {
|
|
2225
|
+
if (options.forceSessionKey || !hasKey) {
|
|
2138
2226
|
const { jwk, did: generatedSessionDid } = await withSpinner("Generating session key...", async () => {
|
|
2139
2227
|
return generateKey();
|
|
2140
2228
|
});
|
|
@@ -2159,7 +2247,7 @@ async function handleLocalAuth(profileName, host) {
|
|
|
2159
2247
|
signature: sessionResult.signature
|
|
2160
2248
|
});
|
|
2161
2249
|
sessionDid = sessionResult.verificationMethod;
|
|
2162
|
-
|
|
2250
|
+
const updatedProfile = {
|
|
2163
2251
|
...profile,
|
|
2164
2252
|
name: profileName,
|
|
2165
2253
|
host,
|
|
@@ -2175,16 +2263,20 @@ async function handleLocalAuth(profileName, host) {
|
|
|
2175
2263
|
authMethod: "local",
|
|
2176
2264
|
privateKey,
|
|
2177
2265
|
address
|
|
2178
|
-
}
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2266
|
+
};
|
|
2267
|
+
await ProfileManager.setProfile(profileName, updatedProfile);
|
|
2268
|
+
if (options.emitOutput ?? true) {
|
|
2269
|
+
outputJson({
|
|
2270
|
+
authenticated: true,
|
|
2271
|
+
profile: profileName,
|
|
2272
|
+
did,
|
|
2273
|
+
sessionDid,
|
|
2274
|
+
address,
|
|
2275
|
+
spaceId: sessionResult.spaceId,
|
|
2276
|
+
authMethod: "local"
|
|
2277
|
+
});
|
|
2278
|
+
}
|
|
2279
|
+
return { profile: updatedProfile, sessionResult };
|
|
2188
2280
|
}
|
|
2189
2281
|
async function handleOpenKeyAuth(profileName, host, paste) {
|
|
2190
2282
|
const { profile, delegationData } = await refreshOpenKeySession(profileName, host, { paste });
|
|
@@ -2957,7 +3049,7 @@ _tc_completions() {
|
|
|
2957
3049
|
commands="init auth kv space delegation share node profile completion"
|
|
2958
3050
|
|
|
2959
3051
|
case "\${COMP_WORDS[1]}" in
|
|
2960
|
-
auth) subcommands="login logout status whoami" ;;
|
|
3052
|
+
auth) subcommands="login logout rotate status whoami" ;;
|
|
2961
3053
|
kv) subcommands="get put delete list head" ;;
|
|
2962
3054
|
space) subcommands="list create info switch" ;;
|
|
2963
3055
|
delegation) subcommands="create list info revoke" ;;
|
|
@@ -3007,7 +3099,7 @@ _tc() {
|
|
|
3007
3099
|
;;
|
|
3008
3100
|
args)
|
|
3009
3101
|
case $words[1] in
|
|
3010
|
-
auth) _values 'subcommand' login logout status whoami ;;
|
|
3102
|
+
auth) _values 'subcommand' login logout rotate status whoami ;;
|
|
3011
3103
|
kv) _values 'subcommand' get put delete list head ;;
|
|
3012
3104
|
space) _values 'subcommand' list create info switch ;;
|
|
3013
3105
|
delegation) _values 'subcommand' create list info revoke ;;
|
|
@@ -3042,7 +3134,7 @@ complete -c tc -n "not __fish_seen_subcommand_from $commands" -a profile -d "Pro
|
|
|
3042
3134
|
complete -c tc -n "not __fish_seen_subcommand_from $commands" -a completion -d "Generate shell completions"
|
|
3043
3135
|
|
|
3044
3136
|
# Subcommands
|
|
3045
|
-
complete -c tc -n "__fish_seen_subcommand_from auth" -a "login logout status whoami"
|
|
3137
|
+
complete -c tc -n "__fish_seen_subcommand_from auth" -a "login logout rotate status whoami"
|
|
3046
3138
|
complete -c tc -n "__fish_seen_subcommand_from kv" -a "get put delete list head"
|
|
3047
3139
|
complete -c tc -n "__fish_seen_subcommand_from space" -a "list create info switch"
|
|
3048
3140
|
complete -c tc -n "__fish_seen_subcommand_from delegation" -a "create list info revoke"
|
|
@@ -3241,6 +3333,12 @@ import {
|
|
|
3241
3333
|
resolveSecretPath
|
|
3242
3334
|
} from "@tinycloud/node-sdk";
|
|
3243
3335
|
var SECRETS_SPACE = "secrets";
|
|
3336
|
+
var SECRET_KV_ABILITIES = {
|
|
3337
|
+
get: "tinycloud.kv/get",
|
|
3338
|
+
put: "tinycloud.kv/put",
|
|
3339
|
+
del: "tinycloud.kv/del",
|
|
3340
|
+
list: "tinycloud.kv/list"
|
|
3341
|
+
};
|
|
3244
3342
|
async function readStdin4() {
|
|
3245
3343
|
const chunks = [];
|
|
3246
3344
|
for await (const chunk of process.stdin) {
|
|
@@ -3326,20 +3424,23 @@ function parseDate(value) {
|
|
|
3326
3424
|
const date = new Date(value);
|
|
3327
3425
|
return Number.isNaN(date.getTime()) ? null : date;
|
|
3328
3426
|
}
|
|
3427
|
+
function secretKvAbility(action) {
|
|
3428
|
+
return SECRET_KV_ABILITIES[action];
|
|
3429
|
+
}
|
|
3329
3430
|
function secretPermissionEntries(params) {
|
|
3330
3431
|
const path = params.action === "list" ? resolveSecretListPrefix(params.options) : resolveSecretPath(params.name ?? "", params.options).permissionPaths.vault;
|
|
3331
3432
|
const permissions = [{
|
|
3332
3433
|
service: "tinycloud.kv",
|
|
3333
3434
|
space: SECRETS_SPACE,
|
|
3334
3435
|
path,
|
|
3335
|
-
actions: [params.action],
|
|
3436
|
+
actions: [secretKvAbility(params.action)],
|
|
3336
3437
|
skipPrefix: true
|
|
3337
3438
|
}];
|
|
3338
3439
|
if (params.action === "get") {
|
|
3339
3440
|
permissions.push({
|
|
3340
3441
|
service: "tinycloud.encryption",
|
|
3341
3442
|
path: params.node.getDefaultEncryptionNetworkId(),
|
|
3342
|
-
actions: ["decrypt"],
|
|
3443
|
+
actions: ["tinycloud.encryption/decrypt"],
|
|
3343
3444
|
skipPrefix: true
|
|
3344
3445
|
});
|
|
3345
3446
|
}
|