Skip to content

Commit

Permalink
feat: removed undefined errors from vaultops
Browse files Browse the repository at this point in the history
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
  • Loading branch information
aryanjassal committed Nov 11, 2024
1 parent 8c1cc9a commit 35f08e2
Show file tree
Hide file tree
Showing 18 changed files with 1,368 additions and 524 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
"@matrixai/mdns": "^1.2.6",
"@matrixai/quic": "^1.2.10",
"@matrixai/resources": "^1.1.5",
"@matrixai/rpc": "^0.5.1",
"@matrixai/rpc": "^0.6.0",
"@matrixai/timer": "^1.1.3",
"@matrixai/workers": "^1.3.7",
"@matrixai/ws": "^1.1.7",
Expand Down
3 changes: 3 additions & 0 deletions src/client/callers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import vaultsPermissionUnset from './vaultsPermissionUnset';
import vaultsPull from './vaultsPull';
import vaultsRename from './vaultsRename';
import vaultsScan from './vaultsScan';
import vaultsSecretsCat from './vaultsSecretsCat';
import vaultsSecretsEnv from './vaultsSecretsEnv';
import vaultsSecretsGet from './vaultsSecretsGet';
import vaultsSecretsList from './vaultsSecretsList';
Expand Down Expand Up @@ -144,6 +145,7 @@ const clientManifest = {
vaultsPull,
vaultsRename,
vaultsScan,
vaultsSecretsCat,
vaultsSecretsEnv,
vaultsSecretsGet,
vaultsSecretsList,
Expand Down Expand Up @@ -224,6 +226,7 @@ export {
vaultsPull,
vaultsRename,
vaultsScan,
vaultsSecretsCat,
vaultsSecretsEnv,
vaultsSecretsGet,
vaultsSecretsList,
Expand Down
12 changes: 12 additions & 0 deletions src/client/callers/vaultsSecretsCat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { HandlerTypes } from '@matrixai/rpc';
import type VaultsSecretsCat from '../handlers/VaultsSecretsCat';
import { DuplexCaller } from '@matrixai/rpc';

type CallerTypes = HandlerTypes<VaultsSecretsCat>;

const vaultsSecretsCat = new DuplexCaller<
CallerTypes['input'],
CallerTypes['output']
>();

export default vaultsSecretsCat;
4 changes: 2 additions & 2 deletions src/client/callers/vaultsSecretsGet.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { HandlerTypes } from '@matrixai/rpc';
import type VaultsSecretsGet from '../handlers/VaultsSecretsGet';
import { DuplexCaller } from '@matrixai/rpc';
import { ServerCaller } from '@matrixai/rpc';

type CallerTypes = HandlerTypes<VaultsSecretsGet>;

const vaultsSecretsGet = new DuplexCaller<
const vaultsSecretsGet = new ServerCaller<
CallerTypes['input'],
CallerTypes['output']
>();
Expand Down
4 changes: 2 additions & 2 deletions src/client/callers/vaultsSecretsRemove.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { HandlerTypes } from '@matrixai/rpc';
import type VaultsSecretsRemove from '../handlers/VaultsSecretsRemove';
import { ClientCaller } from '@matrixai/rpc';
import { DuplexCaller } from '@matrixai/rpc';

type CallerTypes = HandlerTypes<VaultsSecretsRemove>;

const vaultsSecretsRemove = new ClientCaller<
const vaultsSecretsRemove = new DuplexCaller<
CallerTypes['input'],
CallerTypes['output']
>();
Expand Down
71 changes: 71 additions & 0 deletions src/client/handlers/VaultsSecretsCat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import type { DB } from '@matrixai/db';
import type {
ClientRPCRequestParams,
ClientRPCResponseResult,
ContentOrErrorMessage,
SecretIdentifierMessage,
} from '../types';
import type VaultManager from '../../vaults/VaultManager';
import { DuplexHandler } from '@matrixai/rpc';
import * as vaultsUtils from '../../vaults/utils';
import * as vaultsErrors from '../../vaults/errors';
import * as vaultOps from '../../vaults/VaultOps';

// This method takes in multiple secret paths, and either returns the file
// contents, or an `ErrorMessage` signifying the error. To read a single secret
// instead, refer to `VaultsSecretsGet`.
class VaultsSecretsCat extends DuplexHandler<
{
db: DB;
vaultManager: VaultManager;
},
ClientRPCRequestParams<SecretIdentifierMessage>,
ClientRPCResponseResult<ContentOrErrorMessage>
> {
public handle = async function* (
input: AsyncIterable<ClientRPCRequestParams<SecretIdentifierMessage>>,
): AsyncGenerator<ClientRPCResponseResult<ContentOrErrorMessage>> {
const { db, vaultManager }: { db: DB; vaultManager: VaultManager } =
this.container;
yield* db.withTransactionG(async function* (tran): AsyncGenerator<
ClientRPCResponseResult<ContentOrErrorMessage>
> {
// As we need to preserve the order of parameters, we need to loop over
// them individually, as grouping them would make them go out of order.
for await (const secretIdentiferMessage of input) {
const { nameOrId, secretName } = secretIdentiferMessage;
const vaultIdFromName = await vaultManager.getVaultId(nameOrId, tran);
const vaultId = vaultIdFromName ?? vaultsUtils.decodeVaultId(nameOrId);
if (vaultId == null) throw new vaultsErrors.ErrorVaultsVaultUndefined();
yield await vaultManager.withVaults(
[vaultId],
async (vault) => {
try {
const content = await vaultOps.getSecret(vault, secretName);
return {
type: 'success',
success: true,
secretContent: content.toString('binary'),
};
} catch (e) {
if (
e instanceof vaultsErrors.ErrorSecretsSecretUndefined ||
e instanceof vaultsErrors.ErrorSecretsIsDirectory
) {
return {
type: 'error',
code: e.cause.code,
reason: secretName,
};
}
throw e;
}
},
tran,
);
}
});
};
}

export default VaultsSecretsCat;
72 changes: 22 additions & 50 deletions src/client/handlers/VaultsSecretsGet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,72 +2,44 @@ import type { DB } from '@matrixai/db';
import type {
ClientRPCRequestParams,
ClientRPCResponseResult,
ContentWithErrorMessage,
ContentMessage,
SecretIdentifierMessage,
} from '../types';
import type VaultManager from '../../vaults/VaultManager';
import { DuplexHandler } from '@matrixai/rpc';
import { ServerHandler } from '@matrixai/rpc';
import * as vaultsUtils from '../../vaults/utils';
import * as vaultsErrors from '../../vaults/errors';
import * as vaultOps from '../../vaults/VaultOps';

class VaultsSecretsGet extends DuplexHandler<
// This method only returns the contents of a single secret, and throws an error
// if the secret couldn't be read. To read multiple secrets, refer to
// `VaultsSecretsCat`.
class VaultsSecretsGet extends ServerHandler<
{
db: DB;
vaultManager: VaultManager;
},
ClientRPCRequestParams<SecretIdentifierMessage>,
ClientRPCResponseResult<ContentWithErrorMessage>
ClientRPCResponseResult<ContentMessage>
> {
public handle = async function* (
input: AsyncIterable<ClientRPCRequestParams<SecretIdentifierMessage>>,
_cancel,
_meta,
ctx,
): AsyncGenerator<ClientRPCResponseResult<ContentWithErrorMessage>> {
if (ctx.signal.aborted) throw ctx.signal.reason;
input: ClientRPCRequestParams<SecretIdentifierMessage>,
): AsyncGenerator<ClientRPCResponseResult<ContentMessage>> {
const { db, vaultManager }: { db: DB; vaultManager: VaultManager } =
this.container;
yield* db.withTransactionG(async function* (tran): AsyncGenerator<
ClientRPCResponseResult<ContentWithErrorMessage>
> {
if (ctx.signal.aborted) throw ctx.signal.reason;
// As we need to preserve the order of parameters, we need to loop over
// them individually, as grouping them would make them go out of order.
let metadata: any = undefined;
for await (const secretIdentiferMessage of input) {
if (ctx.signal.aborted) throw ctx.signal.reason;
if (metadata == null) metadata = secretIdentiferMessage.metadata ?? {};
const { nameOrId, secretName } = secretIdentiferMessage;
const vaultIdFromName = await vaultManager.getVaultId(nameOrId, tran);
const vaultId = vaultIdFromName ?? vaultsUtils.decodeVaultId(nameOrId);
if (vaultId == null) throw new vaultsErrors.ErrorVaultsVaultUndefined();
yield await vaultManager.withVaults(
[vaultId],
async (vault) => {
try {
const content = await vaultOps.getSecret(vault, secretName);
return { secretContent: content.toString('binary') };
} catch (e) {
if (metadata?.options?.continueOnError === true) {
if (e instanceof vaultsErrors.ErrorSecretsSecretUndefined) {
return {
secretContent: '',
error: `${e.name}: ${secretName}: No such secret or directory\n`,
};
} else if (e instanceof vaultsErrors.ErrorSecretsIsDirectory) {
return {
secretContent: '',
error: `${e.name}: ${secretName}: Is a directory\n`,
};
}
}
throw e;
}
},
tran,
);
}
yield await db.withTransactionF(async (tran) => {
const vaultIdFromName = await vaultManager.getVaultId(
input.nameOrId,
tran,
);
const vaultId =
vaultIdFromName ?? vaultsUtils.decodeVaultId(input.nameOrId);
if (vaultId == null) throw new vaultsErrors.ErrorVaultsVaultUndefined();
// Get the contents of the file
return await vaultManager.withVaults([vaultId], async (vault) => {
const content = await vaultOps.getSecret(vault, input.secretName);
return { secretContent: content.toString('binary') };
});
});
};
}
Expand Down
22 changes: 19 additions & 3 deletions src/client/handlers/VaultsSecretsMkdir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,25 @@ class VaultsSecretsMkdir extends DuplexHandler<
yield await vaultManager.withVaults(
[vaultId],
async (vault) => {
return await vaultOps.mkdir(vault, dirName, {
recursive: metadata?.options?.recursive,
});
try {
await vaultOps.mkdir(vault, dirName, {
recursive: metadata?.options?.recursive,
});
return { type: 'success', success: true };
} catch (e) {
if (
e instanceof vaultsErrors.ErrorVaultsRecursive ||
e instanceof vaultsErrors.ErrorSecretsSecretDefined
) {
return {
type: 'error',
code: e.cause.code,
reason: dirName,
};
} else {
throw e;
}
}
},
tran,
);
Expand Down
Loading

0 comments on commit 35f08e2

Please sign in to comment.