Skip to content

Allow parsing just the vault name without requiring the secret path #305

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,21 @@ class ErrorPolykeyCLIMakeDirectory<T> extends ErrorPolykeyCLI<T> {
exitCode = 1;
}

class ErrorPolykeyCLIRenameSecret<T> extends ErrorPolykeyCLI<T> {
static description = 'Failed to rename one or more secrets';
exitCode = 1;
}

class ErrorPolykeyCLIRemoveSecret<T> extends ErrorPolykeyCLI<T> {
static description = 'Failed to remove one or more secrets';
exitCode = 1;
}

class ErrorPolykeyCLICatSecret<T> extends ErrorPolykeyCLI<T> {
static description = 'Failed to concatenate one or more secrets';
exitCode = 1;
}

export {
ErrorPolykeyCLI,
ErrorPolykeyCLIUncaughtException,
Expand All @@ -178,4 +193,7 @@ export {
ErrorPolykeyCLIInvalidEnvName,
ErrorPolykeyCLIDuplicateEnvName,
ErrorPolykeyCLIMakeDirectory,
ErrorPolykeyCLIRenameSecret,
ErrorPolykeyCLIRemoveSecret,
ErrorPolykeyCLICatSecret,
};
50 changes: 31 additions & 19 deletions src/secrets/CommandCat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as binUtils from '../utils';
import * as binOptions from '../utils/options';
import * as binParsers from '../utils/parsers';
import * as binProcessors from '../utils/processors';
import * as errors from '../errors';

class CommandGet extends CommandPolykey {
constructor(...args: ConstructorParameters<typeof CommandPolykey>) {
Expand All @@ -21,7 +22,7 @@ class CommandGet extends CommandPolykey {
this.addOption(binOptions.clientPort);
this.action(async (secretPaths, options) => {
secretPaths = secretPaths.map((path: string) =>
binParsers.parseSecretPathValue(path),
binParsers.parseSecretPath(path),
);
const { default: PolykeyClient } = await import(
'polykey/dist/PolykeyClient'
Expand Down Expand Up @@ -77,28 +78,39 @@ class CommandGet extends CommandPolykey {
});
return;
}
await binUtils.retryAuthentication(async (auth) => {
const hasErrored = await binUtils.retryAuthentication(async (auth) => {
// Write secret paths to input stream
const response = await pkClient.rpcClient.methods.vaultsSecretsGet();
await (async () => {
const writer = response.writable.getWriter();
let first = true;
for (const [vaultName, secretPath] of secretPaths) {
await writer.write({
nameOrId: vaultName,
secretName: secretPath,
metadata: first
? { ...auth, options: { continueOnError: true } }
: undefined,
});
first = false;
}
await writer.close();
})();
const writer = response.writable.getWriter();
let first = true;
for (const [vaultName, secretPath] of secretPaths) {
await writer.write({
nameOrId: vaultName,
secretName: secretPath ?? '/',
metadata: first
? { ...auth, options: { continueOnError: true } }
: undefined,
});
first = false;
}
await writer.close();
// Print out incoming data to standard out
let hasErrored = false;
for await (const chunk of response.readable) {
if (chunk.error) process.stderr.write(chunk.error);
else process.stdout.write(chunk.secretContent);
if (chunk.error) {
hasErrored = true;
process.stderr.write(chunk.error);
} else {
process.stdout.write(chunk.secretContent);
}
}
return hasErrored;
}, meta);
if (hasErrored) {
throw new errors.ErrorPolykeyCLICatSecret(
'Failed to concatenate one or more secrets',
);
}
} finally {
if (pkClient! != null) await pkClient.stop();
}
Expand Down
2 changes: 1 addition & 1 deletion src/secrets/CommandCreate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class CommandCreate extends CommandPolykey {
pkClient.rpcClient.methods.vaultsSecretsNew({
metadata: auth,
nameOrId: secretPath[0],
secretName: secretPath[1],
secretName: secretPath[1] ?? '/',
secretContent: content.toString('binary'),
}),
meta,
Expand Down
4 changes: 2 additions & 2 deletions src/secrets/CommandEdit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class CommandEdit extends CommandPolykey {
this.argument(
'<secretPath>',
'Path to the secret to be edited, specified as <vaultName>:<directoryPath>',
binParsers.parseSecretPathValue,
binParsers.parseSecretPath,
);
this.addOption(binOptions.nodeId);
this.addOption(binOptions.clientHost);
Expand Down Expand Up @@ -68,7 +68,7 @@ class CommandEdit extends CommandPolykey {
const writer = res.writable.getWriter();
await writer.write({
nameOrId: secretPath[0],
secretName: secretPath[1],
secretName: secretPath[1] ?? '/',
metadata: auth,
});
await writer.close();
Expand Down
8 changes: 4 additions & 4 deletions src/secrets/CommandList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ class CommandList extends CommandPolykey {
this.argument(
'<directoryPath>',
'Directory to list files from, specified as <vaultName>[:<path>]',
binParsers.parseSecretPathOptional,
binParsers.parseSecretPath,
);
this.addOption(binOptions.nodeId);
this.addOption(binOptions.clientHost);
this.addOption(binOptions.clientPort);
this.action(async (vaultPattern, options) => {
this.action(async (secretPath, options) => {
const { default: PolykeyClient } = await import(
'polykey/dist/PolykeyClient'
);
Expand Down Expand Up @@ -52,8 +52,8 @@ class CommandList extends CommandPolykey {
const secretPaths: Array<string> = [];
const stream = await pkClient.rpcClient.methods.vaultsSecretsList({
metadata: auth,
nameOrId: vaultPattern[0],
secretName: vaultPattern[1] ?? '/',
nameOrId: secretPath[0],
secretName: secretPath[1] ?? '/',
});
for await (const secret of stream) {
// Remove leading slashes
Expand Down
64 changes: 33 additions & 31 deletions src/secrets/CommandMkdir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class CommandMkdir extends CommandPolykey {
this.addOption(binOptions.nodeId);
this.addOption(binOptions.clientHost);
this.addOption(binOptions.clientPort);
this.addOption(binOptions.recursive);
this.addOption(binOptions.parents);
this.action(async (secretPaths, options) => {
secretPaths = secretPaths.map((path: string) =>
binParsers.parseSecretPath(path),
Expand Down Expand Up @@ -59,51 +59,53 @@ class CommandMkdir extends CommandPolykey {
},
logger: this.logger.getChild(PolykeyClient.name),
});
const response = await binUtils.retryAuthentication(async (auth) => {
const hasErrored = await binUtils.retryAuthentication(async (auth) => {
// Write directory paths to input stream
const response =
await pkClient.rpcClient.methods.vaultsSecretsMkdir();
const writer = response.writable.getWriter();
let first = true;
for (const [vault, path] of secretPaths) {
await writer.write({
nameOrId: vault,
dirName: path,
dirName: path ?? '/',
metadata: first
? { ...auth, options: { recursive: options.recursive } }
? { ...auth, options: { recursive: options.parents } }
: undefined,
});
first = false;
}
await writer.close();
return response;
}, meta);

let hasErrored = false;
for await (const result of response.readable) {
if (result.type === 'error') {
// TS cannot properly evaluate a type this deeply nested, so we use
// the as keyword to help it. Inside this block, the type of data is
// ensured to be 'error'.
const error = result as ErrorMessage;
hasErrored = true;
let message: string = '';
switch (error.code) {
case 'ENOENT':
message = 'No such secret or directory';
break;
case 'EEXIST':
message = 'Secret or directory exists';
break;
default:
throw new ErrorPolykeyCLIUncaughtException(
`Unexpected error code: ${error.code}`,
);
// Print out incoming data to standard out, or incoming errors to
// standard error.
let hasErrored = false;
for await (const result of response.readable) {
if (result.type === 'error') {
// TS cannot properly evaluate a type this deeply nested, so we use
// the as keyword to help it. Inside this block, the type of data
// is ensured to be 'error'.
const error = result as ErrorMessage;
hasErrored = true;
let message: string = '';
switch (error.code) {
case 'ENOENT':
message = 'No such secret or directory';
break;
case 'EEXIST':
message = 'Secret or directory exists';
break;
default:
throw new ErrorPolykeyCLIUncaughtException(
`Unexpected error code: ${error.code}`,
);
}
process.stderr.write(
`${error.code}: cannot create directory ${error.reason}: ${message}\n`,
);
}
process.stderr.write(
`${error.code}: cannot create directory ${error.reason}: ${message}\n`,
);
}
}
return hasErrored;
}, meta);
if (hasErrored) {
throw new ErrorPolykeyCLIMakeDirectory(
'Failed to create one or more directories',
Expand Down
50 changes: 28 additions & 22 deletions src/secrets/CommandRemove.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as binUtils from '../utils';
import * as binOptions from '../utils/options';
import * as binParsers from '../utils/parsers';
import * as binProcessors from '../utils/processors';
import * as errors from '../errors';

class CommandRemove extends CommandPolykey {
constructor(...args: ConstructorParameters<typeof CommandPolykey>) {
Expand All @@ -20,9 +21,17 @@ class CommandRemove extends CommandPolykey {
this.addOption(binOptions.clientPort);
this.addOption(binOptions.recursive);
this.action(async (secretPaths, options) => {
secretPaths = secretPaths.map((path: string) =>
binParsers.parseSecretPathValue(path),
);
for (let i = 0; i < secretPaths.length; i++) {
const value: string = secretPaths[i];
const parsedValue = binParsers.parseSecretPath(value);
// The vault root cannot be deleted
if (parsedValue[1] == null) {
throw new errors.ErrorPolykeyCLIRemoveSecret(
'EPERM: Cannot remove vault root',
);
}
secretPaths[i] = parsedValue;
}
const { default: PolykeyClient } = await import(
'polykey/dist/PolykeyClient'
);
Expand Down Expand Up @@ -50,28 +59,25 @@ class CommandRemove extends CommandPolykey {
options: { nodePath: options.nodePath },
logger: this.logger.getChild(PolykeyClient.name),
});
const response = await binUtils.retryAuthentication(async (auth) => {
await binUtils.retryAuthentication(async (auth) => {
const response =
await pkClient.rpcClient.methods.vaultsSecretsRemove();
await (async () => {
const writer = response.writable.getWriter();
let first = true;
for (const [vault, path] of secretPaths) {
await writer.write({
nameOrId: vault,
secretName: path,
metadata: first
? { ...auth, options: { recursive: options.recursive } }
: undefined,
});
first = false;
}
await writer.close();
})();
return response;
const writer = response.writable.getWriter();
let first = true;
for (const [vault, path] of secretPaths) {
await writer.write({
nameOrId: vault,
secretName: path,
metadata: first
? { ...auth, options: { recursive: options.recursive } }
: undefined,
});
first = false;
}
await writer.close();
// Wait for the program to generate a response (complete processing).
await response.output;
}, meta);
// Wait for the program to generate a response (complete processing).
await response.output;
} finally {
if (pkClient! != null) await pkClient.stop();
}
Expand Down
9 changes: 8 additions & 1 deletion src/secrets/CommandRename.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as binUtils from '../utils';
import * as binOptions from '../utils/options';
import * as binParsers from '../utils/parsers';
import * as binProcessors from '../utils/processors';
import * as errors from '../errors';

class CommandRename extends CommandPolykey {
constructor(...args: ConstructorParameters<typeof CommandPolykey>) {
Expand All @@ -13,13 +14,19 @@ class CommandRename extends CommandPolykey {
this.argument(
'<secretPath>',
'Path to where the secret to be renamed, specified as <vaultName>:<directoryPath>',
binParsers.parseSecretPathValue,
binParsers.parseSecretPath,
);
this.argument('<newSecretName>', 'New name of the secret');
this.addOption(binOptions.nodeId);
this.addOption(binOptions.clientHost);
this.addOption(binOptions.clientPort);
this.action(async (secretPath, newSecretName, options) => {
// Ensure that a valid secret path is provided
if (secretPath[1] == null) {
throw new errors.ErrorPolykeyCLIRenameSecret(
'EPERM: Cannot rename vault root',
);
}
const { default: PolykeyClient } = await import(
'polykey/dist/PolykeyClient'
);
Expand Down
4 changes: 2 additions & 2 deletions src/secrets/CommandStat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class CommandStat extends CommandPolykey {
this.argument(
'<secretPath>',
'Path to where the secret, specified as <vaultName>:<directoryPath>',
binParsers.parseSecretPathValue,
binParsers.parseSecretPath,
);
this.addOption(binOptions.nodeId);
this.addOption(binOptions.clientHost);
Expand Down Expand Up @@ -55,7 +55,7 @@ class CommandStat extends CommandPolykey {
pkClient.rpcClient.methods.vaultsSecretsStat({
metadata: auth,
nameOrId: secretPath[0],
secretName: secretPath[1],
secretName: secretPath[1] ?? '/',
}),
meta,
);
Expand Down
4 changes: 2 additions & 2 deletions src/secrets/CommandWrite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class CommandWrite extends CommandPolykey {
this.argument(
'<secretPath>',
'Path to the secret, specified as <vaultName>:<directoryPath>',
binParsers.parseSecretPathValue,
binParsers.parseSecretPath,
);
this.addOption(binOptions.nodeId);
this.addOption(binOptions.clientHost);
Expand Down Expand Up @@ -77,7 +77,7 @@ class CommandWrite extends CommandPolykey {
await pkClient.rpcClient.methods.vaultsSecretsWriteFile({
metadata: auth,
nameOrId: secretPath[0],
secretName: secretPath[1],
secretName: secretPath[1] ?? '/',
secretContent: stdin,
}),
meta,
Expand Down
Loading