Skip to content

Commit

Permalink
feat: changed parsers to allow using vault name without secret path
Browse files Browse the repository at this point in the history
chore: vault commands are now using vaultNameParser

chore: writing fastcheck tests for parsers

chore: jestified outputFormatter tests

feat: updated tests for every secret command to allow undefined secret path

fix: consistently using command instead of [...command] in secret tests

chore: stream consumption must be inside retryAuthentication

chore: tests consistently use command

fix: lint

fix: mkdir

fix: variable name
  • Loading branch information
aryanjassal committed Oct 29, 2024
1 parent 3377c18 commit 8a59d6e
Show file tree
Hide file tree
Showing 25 changed files with 990 additions and 828 deletions.
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

0 comments on commit 8a59d6e

Please sign in to comment.