Skip to content

Commit

Permalink
chore: split vaultsSecretsGet and its tests
Browse files Browse the repository at this point in the history
  • Loading branch information
aryanjassal committed Nov 5, 2024
1 parent 0fb5db3 commit 2f29030
Show file tree
Hide file tree
Showing 9 changed files with 963 additions and 419 deletions.
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
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;
66 changes: 22 additions & 44 deletions src/client/handlers/VaultsSecretsGet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,66 +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>>,
): AsyncGenerator<ClientRPCResponseResult<ContentWithErrorMessage>> {
input: ClientRPCRequestParams<SecretIdentifierMessage>,
): AsyncGenerator<ClientRPCResponseResult<ContentMessage>> {
const { db, vaultManager }: { db: DB; vaultManager: VaultManager } =
this.container;
yield* db.withTransactionG(async function* (tran): AsyncGenerator<
ClientRPCResponseResult<ContentWithErrorMessage>
> {
// 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 (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
3 changes: 3 additions & 0 deletions src/client/handlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,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 @@ -184,6 +185,7 @@ const serverManifest = (container: {
vaultsPull: new VaultsPull(container),
vaultsRename: new VaultsRename(container),
vaultsScan: new VaultsScan(container),
vaultsSecretsCat: new VaultsSecretsCat(container),
vaultsSecretsEnv: new VaultsSecretsEnv(container),
vaultsSecretsGet: new VaultsSecretsGet(container),
vaultsSecretsList: new VaultsSecretsList(container),
Expand Down Expand Up @@ -266,6 +268,7 @@ export {
VaultsPull,
VaultsRename,
VaultsScan,
VaultsSecretsCat,
VaultsSecretsEnv,
VaultsSecretsGet,
VaultsSecretsList,
Expand Down
9 changes: 5 additions & 4 deletions src/client/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,9 +321,9 @@ type ContentMessage = {
secretContent: string;
};

type ContentWithErrorMessage = ContentMessage & {
error?: string;
};
type ContentSuccessMessage = ContentMessage & SuccessMessage;

type ContentOrErrorMessage = ContentSuccessMessage | ErrorMessage;

type SecretContentMessage = SecretIdentifierMessage & ContentMessage;

Expand Down Expand Up @@ -428,7 +428,8 @@ export type {
SecretPathMessage,
SecretIdentifierMessage,
ContentMessage,
ContentWithErrorMessage,
ContentSuccessMessage,
ContentOrErrorMessage,
SecretContentMessage,
SecretDirMessage,
SecretRenameMessage,
Expand Down
2 changes: 1 addition & 1 deletion src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class ErrorPolykeyUnknown<T> extends ErrorPolykey<T> {

class ErrorPolykeyUnexpected<T> extends ErrorPolykey<T> {
static description = 'Unknown error occurred';
exitCode = sysexits.PROTOCOL;
exitCode = sysexits.UNKNOWN;
}

class ErrorPolykeyAgentRunning<T> extends ErrorPolykey<T> {
Expand Down
Loading

0 comments on commit 2f29030

Please sign in to comment.