Skip to content

Commit 35f08e2

Browse files
committed
feat: removed undefined errors from vaultops
tests: added tests for VaultsSecretsWriteFile chore: updated secrets remove to properly operate on multiple paths chore: working on proper error wrapping for unexpected errors chore: added comments and cleaned up VaultOps chore: split vaultsSecretsGet and its tests chore: dealing with an edge case when error cannot be serialised chore: cleaned up files chore: added intelligent error message synthesis deps: updated version of @matrixai/rpc fix: handle attempt to remove vault root
1 parent 8c1cc9a commit 35f08e2

File tree

18 files changed

+1368
-524
lines changed

18 files changed

+1368
-524
lines changed

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
"@matrixai/mdns": "^1.2.6",
8181
"@matrixai/quic": "^1.2.10",
8282
"@matrixai/resources": "^1.1.5",
83-
"@matrixai/rpc": "^0.5.1",
83+
"@matrixai/rpc": "^0.6.0",
8484
"@matrixai/timer": "^1.1.3",
8585
"@matrixai/workers": "^1.3.7",
8686
"@matrixai/ws": "^1.1.7",

src/client/callers/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ import vaultsPermissionUnset from './vaultsPermissionUnset';
6363
import vaultsPull from './vaultsPull';
6464
import vaultsRename from './vaultsRename';
6565
import vaultsScan from './vaultsScan';
66+
import vaultsSecretsCat from './vaultsSecretsCat';
6667
import vaultsSecretsEnv from './vaultsSecretsEnv';
6768
import vaultsSecretsGet from './vaultsSecretsGet';
6869
import vaultsSecretsList from './vaultsSecretsList';
@@ -144,6 +145,7 @@ const clientManifest = {
144145
vaultsPull,
145146
vaultsRename,
146147
vaultsScan,
148+
vaultsSecretsCat,
147149
vaultsSecretsEnv,
148150
vaultsSecretsGet,
149151
vaultsSecretsList,
@@ -224,6 +226,7 @@ export {
224226
vaultsPull,
225227
vaultsRename,
226228
vaultsScan,
229+
vaultsSecretsCat,
227230
vaultsSecretsEnv,
228231
vaultsSecretsGet,
229232
vaultsSecretsList,
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import type { HandlerTypes } from '@matrixai/rpc';
2+
import type VaultsSecretsCat from '../handlers/VaultsSecretsCat';
3+
import { DuplexCaller } from '@matrixai/rpc';
4+
5+
type CallerTypes = HandlerTypes<VaultsSecretsCat>;
6+
7+
const vaultsSecretsCat = new DuplexCaller<
8+
CallerTypes['input'],
9+
CallerTypes['output']
10+
>();
11+
12+
export default vaultsSecretsCat;

src/client/callers/vaultsSecretsGet.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import type { HandlerTypes } from '@matrixai/rpc';
22
import type VaultsSecretsGet from '../handlers/VaultsSecretsGet';
3-
import { DuplexCaller } from '@matrixai/rpc';
3+
import { ServerCaller } from '@matrixai/rpc';
44

55
type CallerTypes = HandlerTypes<VaultsSecretsGet>;
66

7-
const vaultsSecretsGet = new DuplexCaller<
7+
const vaultsSecretsGet = new ServerCaller<
88
CallerTypes['input'],
99
CallerTypes['output']
1010
>();

src/client/callers/vaultsSecretsRemove.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import type { HandlerTypes } from '@matrixai/rpc';
22
import type VaultsSecretsRemove from '../handlers/VaultsSecretsRemove';
3-
import { ClientCaller } from '@matrixai/rpc';
3+
import { DuplexCaller } from '@matrixai/rpc';
44

55
type CallerTypes = HandlerTypes<VaultsSecretsRemove>;
66

7-
const vaultsSecretsRemove = new ClientCaller<
7+
const vaultsSecretsRemove = new DuplexCaller<
88
CallerTypes['input'],
99
CallerTypes['output']
1010
>();
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import type { DB } from '@matrixai/db';
2+
import type {
3+
ClientRPCRequestParams,
4+
ClientRPCResponseResult,
5+
ContentOrErrorMessage,
6+
SecretIdentifierMessage,
7+
} from '../types';
8+
import type VaultManager from '../../vaults/VaultManager';
9+
import { DuplexHandler } from '@matrixai/rpc';
10+
import * as vaultsUtils from '../../vaults/utils';
11+
import * as vaultsErrors from '../../vaults/errors';
12+
import * as vaultOps from '../../vaults/VaultOps';
13+
14+
// This method takes in multiple secret paths, and either returns the file
15+
// contents, or an `ErrorMessage` signifying the error. To read a single secret
16+
// instead, refer to `VaultsSecretsGet`.
17+
class VaultsSecretsCat extends DuplexHandler<
18+
{
19+
db: DB;
20+
vaultManager: VaultManager;
21+
},
22+
ClientRPCRequestParams<SecretIdentifierMessage>,
23+
ClientRPCResponseResult<ContentOrErrorMessage>
24+
> {
25+
public handle = async function* (
26+
input: AsyncIterable<ClientRPCRequestParams<SecretIdentifierMessage>>,
27+
): AsyncGenerator<ClientRPCResponseResult<ContentOrErrorMessage>> {
28+
const { db, vaultManager }: { db: DB; vaultManager: VaultManager } =
29+
this.container;
30+
yield* db.withTransactionG(async function* (tran): AsyncGenerator<
31+
ClientRPCResponseResult<ContentOrErrorMessage>
32+
> {
33+
// As we need to preserve the order of parameters, we need to loop over
34+
// them individually, as grouping them would make them go out of order.
35+
for await (const secretIdentiferMessage of input) {
36+
const { nameOrId, secretName } = secretIdentiferMessage;
37+
const vaultIdFromName = await vaultManager.getVaultId(nameOrId, tran);
38+
const vaultId = vaultIdFromName ?? vaultsUtils.decodeVaultId(nameOrId);
39+
if (vaultId == null) throw new vaultsErrors.ErrorVaultsVaultUndefined();
40+
yield await vaultManager.withVaults(
41+
[vaultId],
42+
async (vault) => {
43+
try {
44+
const content = await vaultOps.getSecret(vault, secretName);
45+
return {
46+
type: 'success',
47+
success: true,
48+
secretContent: content.toString('binary'),
49+
};
50+
} catch (e) {
51+
if (
52+
e instanceof vaultsErrors.ErrorSecretsSecretUndefined ||
53+
e instanceof vaultsErrors.ErrorSecretsIsDirectory
54+
) {
55+
return {
56+
type: 'error',
57+
code: e.cause.code,
58+
reason: secretName,
59+
};
60+
}
61+
throw e;
62+
}
63+
},
64+
tran,
65+
);
66+
}
67+
});
68+
};
69+
}
70+
71+
export default VaultsSecretsCat;

src/client/handlers/VaultsSecretsGet.ts

Lines changed: 22 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,72 +2,44 @@ import type { DB } from '@matrixai/db';
22
import type {
33
ClientRPCRequestParams,
44
ClientRPCResponseResult,
5-
ContentWithErrorMessage,
5+
ContentMessage,
66
SecretIdentifierMessage,
77
} from '../types';
88
import type VaultManager from '../../vaults/VaultManager';
9-
import { DuplexHandler } from '@matrixai/rpc';
9+
import { ServerHandler } from '@matrixai/rpc';
1010
import * as vaultsUtils from '../../vaults/utils';
1111
import * as vaultsErrors from '../../vaults/errors';
1212
import * as vaultOps from '../../vaults/VaultOps';
1313

14-
class VaultsSecretsGet extends DuplexHandler<
14+
// This method only returns the contents of a single secret, and throws an error
15+
// if the secret couldn't be read. To read multiple secrets, refer to
16+
// `VaultsSecretsCat`.
17+
class VaultsSecretsGet extends ServerHandler<
1518
{
1619
db: DB;
1720
vaultManager: VaultManager;
1821
},
1922
ClientRPCRequestParams<SecretIdentifierMessage>,
20-
ClientRPCResponseResult<ContentWithErrorMessage>
23+
ClientRPCResponseResult<ContentMessage>
2124
> {
2225
public handle = async function* (
23-
input: AsyncIterable<ClientRPCRequestParams<SecretIdentifierMessage>>,
24-
_cancel,
25-
_meta,
26-
ctx,
27-
): AsyncGenerator<ClientRPCResponseResult<ContentWithErrorMessage>> {
28-
if (ctx.signal.aborted) throw ctx.signal.reason;
26+
input: ClientRPCRequestParams<SecretIdentifierMessage>,
27+
): AsyncGenerator<ClientRPCResponseResult<ContentMessage>> {
2928
const { db, vaultManager }: { db: DB; vaultManager: VaultManager } =
3029
this.container;
31-
yield* db.withTransactionG(async function* (tran): AsyncGenerator<
32-
ClientRPCResponseResult<ContentWithErrorMessage>
33-
> {
34-
if (ctx.signal.aborted) throw ctx.signal.reason;
35-
// As we need to preserve the order of parameters, we need to loop over
36-
// them individually, as grouping them would make them go out of order.
37-
let metadata: any = undefined;
38-
for await (const secretIdentiferMessage of input) {
39-
if (ctx.signal.aborted) throw ctx.signal.reason;
40-
if (metadata == null) metadata = secretIdentiferMessage.metadata ?? {};
41-
const { nameOrId, secretName } = secretIdentiferMessage;
42-
const vaultIdFromName = await vaultManager.getVaultId(nameOrId, tran);
43-
const vaultId = vaultIdFromName ?? vaultsUtils.decodeVaultId(nameOrId);
44-
if (vaultId == null) throw new vaultsErrors.ErrorVaultsVaultUndefined();
45-
yield await vaultManager.withVaults(
46-
[vaultId],
47-
async (vault) => {
48-
try {
49-
const content = await vaultOps.getSecret(vault, secretName);
50-
return { secretContent: content.toString('binary') };
51-
} catch (e) {
52-
if (metadata?.options?.continueOnError === true) {
53-
if (e instanceof vaultsErrors.ErrorSecretsSecretUndefined) {
54-
return {
55-
secretContent: '',
56-
error: `${e.name}: ${secretName}: No such secret or directory\n`,
57-
};
58-
} else if (e instanceof vaultsErrors.ErrorSecretsIsDirectory) {
59-
return {
60-
secretContent: '',
61-
error: `${e.name}: ${secretName}: Is a directory\n`,
62-
};
63-
}
64-
}
65-
throw e;
66-
}
67-
},
68-
tran,
69-
);
70-
}
30+
yield await db.withTransactionF(async (tran) => {
31+
const vaultIdFromName = await vaultManager.getVaultId(
32+
input.nameOrId,
33+
tran,
34+
);
35+
const vaultId =
36+
vaultIdFromName ?? vaultsUtils.decodeVaultId(input.nameOrId);
37+
if (vaultId == null) throw new vaultsErrors.ErrorVaultsVaultUndefined();
38+
// Get the contents of the file
39+
return await vaultManager.withVaults([vaultId], async (vault) => {
40+
const content = await vaultOps.getSecret(vault, input.secretName);
41+
return { secretContent: content.toString('binary') };
42+
});
7143
});
7244
};
7345
}

src/client/handlers/VaultsSecretsMkdir.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,25 @@ class VaultsSecretsMkdir extends DuplexHandler<
4848
yield await vaultManager.withVaults(
4949
[vaultId],
5050
async (vault) => {
51-
return await vaultOps.mkdir(vault, dirName, {
52-
recursive: metadata?.options?.recursive,
53-
});
51+
try {
52+
await vaultOps.mkdir(vault, dirName, {
53+
recursive: metadata?.options?.recursive,
54+
});
55+
return { type: 'success', success: true };
56+
} catch (e) {
57+
if (
58+
e instanceof vaultsErrors.ErrorVaultsRecursive ||
59+
e instanceof vaultsErrors.ErrorSecretsSecretDefined
60+
) {
61+
return {
62+
type: 'error',
63+
code: e.cause.code,
64+
reason: dirName,
65+
};
66+
} else {
67+
throw e;
68+
}
69+
}
5470
},
5571
tran,
5672
);

0 commit comments

Comments
 (0)