Skip to content

Commit 8a59d6e

Browse files
committed
feat: changed parsers to allow using vault name without secret path
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
1 parent 3377c18 commit 8a59d6e

25 files changed

+990
-828
lines changed

src/errors.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,21 @@ class ErrorPolykeyCLIMakeDirectory<T> extends ErrorPolykeyCLI<T> {
157157
exitCode = 1;
158158
}
159159

160+
class ErrorPolykeyCLIRenameSecret<T> extends ErrorPolykeyCLI<T> {
161+
static description = 'Failed to rename one or more secrets';
162+
exitCode = 1;
163+
}
164+
165+
class ErrorPolykeyCLIRemoveSecret<T> extends ErrorPolykeyCLI<T> {
166+
static description = 'Failed to remove one or more secrets';
167+
exitCode = 1;
168+
}
169+
170+
class ErrorPolykeyCLICatSecret<T> extends ErrorPolykeyCLI<T> {
171+
static description = 'Failed to concatenate one or more secrets';
172+
exitCode = 1;
173+
}
174+
160175
export {
161176
ErrorPolykeyCLI,
162177
ErrorPolykeyCLIUncaughtException,
@@ -178,4 +193,7 @@ export {
178193
ErrorPolykeyCLIInvalidEnvName,
179194
ErrorPolykeyCLIDuplicateEnvName,
180195
ErrorPolykeyCLIMakeDirectory,
196+
ErrorPolykeyCLIRenameSecret,
197+
ErrorPolykeyCLIRemoveSecret,
198+
ErrorPolykeyCLICatSecret,
181199
};

src/secrets/CommandCat.ts

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as binUtils from '../utils';
44
import * as binOptions from '../utils/options';
55
import * as binParsers from '../utils/parsers';
66
import * as binProcessors from '../utils/processors';
7+
import * as errors from '../errors';
78

89
class CommandGet extends CommandPolykey {
910
constructor(...args: ConstructorParameters<typeof CommandPolykey>) {
@@ -21,7 +22,7 @@ class CommandGet extends CommandPolykey {
2122
this.addOption(binOptions.clientPort);
2223
this.action(async (secretPaths, options) => {
2324
secretPaths = secretPaths.map((path: string) =>
24-
binParsers.parseSecretPathValue(path),
25+
binParsers.parseSecretPath(path),
2526
);
2627
const { default: PolykeyClient } = await import(
2728
'polykey/dist/PolykeyClient'
@@ -77,28 +78,39 @@ class CommandGet extends CommandPolykey {
7778
});
7879
return;
7980
}
80-
await binUtils.retryAuthentication(async (auth) => {
81+
const hasErrored = await binUtils.retryAuthentication(async (auth) => {
82+
// Write secret paths to input stream
8183
const response = await pkClient.rpcClient.methods.vaultsSecretsGet();
82-
await (async () => {
83-
const writer = response.writable.getWriter();
84-
let first = true;
85-
for (const [vaultName, secretPath] of secretPaths) {
86-
await writer.write({
87-
nameOrId: vaultName,
88-
secretName: secretPath,
89-
metadata: first
90-
? { ...auth, options: { continueOnError: true } }
91-
: undefined,
92-
});
93-
first = false;
94-
}
95-
await writer.close();
96-
})();
84+
const writer = response.writable.getWriter();
85+
let first = true;
86+
for (const [vaultName, secretPath] of secretPaths) {
87+
await writer.write({
88+
nameOrId: vaultName,
89+
secretName: secretPath ?? '/',
90+
metadata: first
91+
? { ...auth, options: { continueOnError: true } }
92+
: undefined,
93+
});
94+
first = false;
95+
}
96+
await writer.close();
97+
// Print out incoming data to standard out
98+
let hasErrored = false;
9799
for await (const chunk of response.readable) {
98-
if (chunk.error) process.stderr.write(chunk.error);
99-
else process.stdout.write(chunk.secretContent);
100+
if (chunk.error) {
101+
hasErrored = true;
102+
process.stderr.write(chunk.error);
103+
} else {
104+
process.stdout.write(chunk.secretContent);
105+
}
100106
}
107+
return hasErrored;
101108
}, meta);
109+
if (hasErrored) {
110+
throw new errors.ErrorPolykeyCLICatSecret(
111+
'Failed to concatenate one or more secrets',
112+
);
113+
}
102114
} finally {
103115
if (pkClient! != null) await pkClient.stop();
104116
}

src/secrets/CommandCreate.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class CommandCreate extends CommandPolykey {
7373
pkClient.rpcClient.methods.vaultsSecretsNew({
7474
metadata: auth,
7575
nameOrId: secretPath[0],
76-
secretName: secretPath[1],
76+
secretName: secretPath[1] ?? '/',
7777
secretContent: content.toString('binary'),
7878
}),
7979
meta,

src/secrets/CommandEdit.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class CommandEdit extends CommandPolykey {
1717
this.argument(
1818
'<secretPath>',
1919
'Path to the secret to be edited, specified as <vaultName>:<directoryPath>',
20-
binParsers.parseSecretPathValue,
20+
binParsers.parseSecretPath,
2121
);
2222
this.addOption(binOptions.nodeId);
2323
this.addOption(binOptions.clientHost);
@@ -68,7 +68,7 @@ class CommandEdit extends CommandPolykey {
6868
const writer = res.writable.getWriter();
6969
await writer.write({
7070
nameOrId: secretPath[0],
71-
secretName: secretPath[1],
71+
secretName: secretPath[1] ?? '/',
7272
metadata: auth,
7373
});
7474
await writer.close();

src/secrets/CommandList.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ class CommandList extends CommandPolykey {
1414
this.argument(
1515
'<directoryPath>',
1616
'Directory to list files from, specified as <vaultName>[:<path>]',
17-
binParsers.parseSecretPathOptional,
17+
binParsers.parseSecretPath,
1818
);
1919
this.addOption(binOptions.nodeId);
2020
this.addOption(binOptions.clientHost);
2121
this.addOption(binOptions.clientPort);
22-
this.action(async (vaultPattern, options) => {
22+
this.action(async (secretPath, options) => {
2323
const { default: PolykeyClient } = await import(
2424
'polykey/dist/PolykeyClient'
2525
);
@@ -52,8 +52,8 @@ class CommandList extends CommandPolykey {
5252
const secretPaths: Array<string> = [];
5353
const stream = await pkClient.rpcClient.methods.vaultsSecretsList({
5454
metadata: auth,
55-
nameOrId: vaultPattern[0],
56-
secretName: vaultPattern[1] ?? '/',
55+
nameOrId: secretPath[0],
56+
secretName: secretPath[1] ?? '/',
5757
});
5858
for await (const secret of stream) {
5959
// Remove leading slashes

src/secrets/CommandMkdir.ts

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class CommandMkdir extends CommandPolykey {
2424
this.addOption(binOptions.nodeId);
2525
this.addOption(binOptions.clientHost);
2626
this.addOption(binOptions.clientPort);
27-
this.addOption(binOptions.recursive);
27+
this.addOption(binOptions.parents);
2828
this.action(async (secretPaths, options) => {
2929
secretPaths = secretPaths.map((path: string) =>
3030
binParsers.parseSecretPath(path),
@@ -59,51 +59,53 @@ class CommandMkdir extends CommandPolykey {
5959
},
6060
logger: this.logger.getChild(PolykeyClient.name),
6161
});
62-
const response = await binUtils.retryAuthentication(async (auth) => {
62+
const hasErrored = await binUtils.retryAuthentication(async (auth) => {
63+
// Write directory paths to input stream
6364
const response =
6465
await pkClient.rpcClient.methods.vaultsSecretsMkdir();
6566
const writer = response.writable.getWriter();
6667
let first = true;
6768
for (const [vault, path] of secretPaths) {
6869
await writer.write({
6970
nameOrId: vault,
70-
dirName: path,
71+
dirName: path ?? '/',
7172
metadata: first
72-
? { ...auth, options: { recursive: options.recursive } }
73+
? { ...auth, options: { recursive: options.parents } }
7374
: undefined,
7475
});
7576
first = false;
7677
}
7778
await writer.close();
78-
return response;
79-
}, meta);
80-
81-
let hasErrored = false;
82-
for await (const result of response.readable) {
83-
if (result.type === 'error') {
84-
// TS cannot properly evaluate a type this deeply nested, so we use
85-
// the as keyword to help it. Inside this block, the type of data is
86-
// ensured to be 'error'.
87-
const error = result as ErrorMessage;
88-
hasErrored = true;
89-
let message: string = '';
90-
switch (error.code) {
91-
case 'ENOENT':
92-
message = 'No such secret or directory';
93-
break;
94-
case 'EEXIST':
95-
message = 'Secret or directory exists';
96-
break;
97-
default:
98-
throw new ErrorPolykeyCLIUncaughtException(
99-
`Unexpected error code: ${error.code}`,
100-
);
79+
// Print out incoming data to standard out, or incoming errors to
80+
// standard error.
81+
let hasErrored = false;
82+
for await (const result of response.readable) {
83+
if (result.type === 'error') {
84+
// TS cannot properly evaluate a type this deeply nested, so we use
85+
// the as keyword to help it. Inside this block, the type of data
86+
// is ensured to be 'error'.
87+
const error = result as ErrorMessage;
88+
hasErrored = true;
89+
let message: string = '';
90+
switch (error.code) {
91+
case 'ENOENT':
92+
message = 'No such secret or directory';
93+
break;
94+
case 'EEXIST':
95+
message = 'Secret or directory exists';
96+
break;
97+
default:
98+
throw new ErrorPolykeyCLIUncaughtException(
99+
`Unexpected error code: ${error.code}`,
100+
);
101+
}
102+
process.stderr.write(
103+
`${error.code}: cannot create directory ${error.reason}: ${message}\n`,
104+
);
101105
}
102-
process.stderr.write(
103-
`${error.code}: cannot create directory ${error.reason}: ${message}\n`,
104-
);
105106
}
106-
}
107+
return hasErrored;
108+
}, meta);
107109
if (hasErrored) {
108110
throw new ErrorPolykeyCLIMakeDirectory(
109111
'Failed to create one or more directories',

src/secrets/CommandRemove.ts

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as binUtils from '../utils';
44
import * as binOptions from '../utils/options';
55
import * as binParsers from '../utils/parsers';
66
import * as binProcessors from '../utils/processors';
7+
import * as errors from '../errors';
78

89
class CommandRemove extends CommandPolykey {
910
constructor(...args: ConstructorParameters<typeof CommandPolykey>) {
@@ -20,9 +21,17 @@ class CommandRemove extends CommandPolykey {
2021
this.addOption(binOptions.clientPort);
2122
this.addOption(binOptions.recursive);
2223
this.action(async (secretPaths, options) => {
23-
secretPaths = secretPaths.map((path: string) =>
24-
binParsers.parseSecretPathValue(path),
25-
);
24+
for (let i = 0; i < secretPaths.length; i++) {
25+
const value: string = secretPaths[i];
26+
const parsedValue = binParsers.parseSecretPath(value);
27+
// The vault root cannot be deleted
28+
if (parsedValue[1] == null) {
29+
throw new errors.ErrorPolykeyCLIRemoveSecret(
30+
'EPERM: Cannot remove vault root',
31+
);
32+
}
33+
secretPaths[i] = parsedValue;
34+
}
2635
const { default: PolykeyClient } = await import(
2736
'polykey/dist/PolykeyClient'
2837
);
@@ -50,28 +59,25 @@ class CommandRemove extends CommandPolykey {
5059
options: { nodePath: options.nodePath },
5160
logger: this.logger.getChild(PolykeyClient.name),
5261
});
53-
const response = await binUtils.retryAuthentication(async (auth) => {
62+
await binUtils.retryAuthentication(async (auth) => {
5463
const response =
5564
await pkClient.rpcClient.methods.vaultsSecretsRemove();
56-
await (async () => {
57-
const writer = response.writable.getWriter();
58-
let first = true;
59-
for (const [vault, path] of secretPaths) {
60-
await writer.write({
61-
nameOrId: vault,
62-
secretName: path,
63-
metadata: first
64-
? { ...auth, options: { recursive: options.recursive } }
65-
: undefined,
66-
});
67-
first = false;
68-
}
69-
await writer.close();
70-
})();
71-
return response;
65+
const writer = response.writable.getWriter();
66+
let first = true;
67+
for (const [vault, path] of secretPaths) {
68+
await writer.write({
69+
nameOrId: vault,
70+
secretName: path,
71+
metadata: first
72+
? { ...auth, options: { recursive: options.recursive } }
73+
: undefined,
74+
});
75+
first = false;
76+
}
77+
await writer.close();
78+
// Wait for the program to generate a response (complete processing).
79+
await response.output;
7280
}, meta);
73-
// Wait for the program to generate a response (complete processing).
74-
await response.output;
7581
} finally {
7682
if (pkClient! != null) await pkClient.stop();
7783
}

src/secrets/CommandRename.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as binUtils from '../utils';
44
import * as binOptions from '../utils/options';
55
import * as binParsers from '../utils/parsers';
66
import * as binProcessors from '../utils/processors';
7+
import * as errors from '../errors';
78

89
class CommandRename extends CommandPolykey {
910
constructor(...args: ConstructorParameters<typeof CommandPolykey>) {
@@ -13,13 +14,19 @@ class CommandRename extends CommandPolykey {
1314
this.argument(
1415
'<secretPath>',
1516
'Path to where the secret to be renamed, specified as <vaultName>:<directoryPath>',
16-
binParsers.parseSecretPathValue,
17+
binParsers.parseSecretPath,
1718
);
1819
this.argument('<newSecretName>', 'New name of the secret');
1920
this.addOption(binOptions.nodeId);
2021
this.addOption(binOptions.clientHost);
2122
this.addOption(binOptions.clientPort);
2223
this.action(async (secretPath, newSecretName, options) => {
24+
// Ensure that a valid secret path is provided
25+
if (secretPath[1] == null) {
26+
throw new errors.ErrorPolykeyCLIRenameSecret(
27+
'EPERM: Cannot rename vault root',
28+
);
29+
}
2330
const { default: PolykeyClient } = await import(
2431
'polykey/dist/PolykeyClient'
2532
);

src/secrets/CommandStat.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class CommandStat extends CommandPolykey {
1313
this.argument(
1414
'<secretPath>',
1515
'Path to where the secret, specified as <vaultName>:<directoryPath>',
16-
binParsers.parseSecretPathValue,
16+
binParsers.parseSecretPath,
1717
);
1818
this.addOption(binOptions.nodeId);
1919
this.addOption(binOptions.clientHost);
@@ -55,7 +55,7 @@ class CommandStat extends CommandPolykey {
5555
pkClient.rpcClient.methods.vaultsSecretsStat({
5656
metadata: auth,
5757
nameOrId: secretPath[0],
58-
secretName: secretPath[1],
58+
secretName: secretPath[1] ?? '/',
5959
}),
6060
meta,
6161
);

src/secrets/CommandWrite.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class CommandWrite extends CommandPolykey {
1313
this.argument(
1414
'<secretPath>',
1515
'Path to the secret, specified as <vaultName>:<directoryPath>',
16-
binParsers.parseSecretPathValue,
16+
binParsers.parseSecretPath,
1717
);
1818
this.addOption(binOptions.nodeId);
1919
this.addOption(binOptions.clientHost);
@@ -77,7 +77,7 @@ class CommandWrite extends CommandPolykey {
7777
await pkClient.rpcClient.methods.vaultsSecretsWriteFile({
7878
metadata: auth,
7979
nameOrId: secretPath[0],
80-
secretName: secretPath[1],
80+
secretName: secretPath[1] ?? '/',
8181
secretContent: stdin,
8282
}),
8383
meta,

0 commit comments

Comments
 (0)