Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/client/callers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import vaultsRename from './vaultsRename';
import vaultsScan from './vaultsScan';
import vaultsSecretsDelete from './vaultsSecretsDelete';
import vaultsSecretsEdit from './vaultsSecretsEdit';
import vaultsSecretsEnv from './vaultsSecretsEnv';
import vaultsSecretsGet from './vaultsSecretsGet';
import vaultsSecretsList from './vaultsSecretsList';
import vaultsSecretsMkdir from './vaultsSecretsMkdir';
Expand Down Expand Up @@ -135,6 +136,7 @@ const clientManifest = {
vaultsScan,
vaultsSecretsDelete,
vaultsSecretsEdit,
vaultsSecretsEnv,
vaultsSecretsGet,
vaultsSecretsList,
vaultsSecretsMkdir,
Expand Down Expand Up @@ -209,6 +211,7 @@ export {
vaultsScan,
vaultsSecretsDelete,
vaultsSecretsEdit,
vaultsSecretsEnv,
vaultsSecretsGet,
vaultsSecretsList,
vaultsSecretsMkdir,
Expand Down
12 changes: 12 additions & 0 deletions src/client/callers/vaultsSecretsEnv.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { HandlerTypes } from '@matrixai/rpc';
import type VaultsSecretsEnv from '../handlers/VaultsSecretsEnv';
import { DuplexCaller } from '@matrixai/rpc';

type CallerTypes = HandlerTypes<VaultsSecretsEnv>;

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

export default vaultsSecretsEnv;
89 changes: 89 additions & 0 deletions src/client/handlers/VaultsSecretsEnv.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import type { DB } from '@matrixai/db';
import type {
ClientRPCRequestParams,
ClientRPCResponseResult,
SecretIdentifierMessage,
SecretContentMessage,
} 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';

class VaultsSecretsList extends DuplexHandler<
{
vaultManager: VaultManager;
db: DB;
},
ClientRPCRequestParams<SecretIdentifierMessage>,
ClientRPCResponseResult<SecretContentMessage>
> {
public handle = async function* (
input: AsyncIterableIterator<
ClientRPCRequestParams<SecretIdentifierMessage>
>,
_cancel,
_meta,
ctx,
): AsyncGenerator<ClientRPCResponseResult<SecretContentMessage>> {
if (ctx.signal.aborted) throw ctx.signal.reason;
const { vaultManager, db }: { vaultManager: VaultManager; db: DB } =
this.container;

return yield* db.withTransactionG(async function* (tran): AsyncGenerator<
ClientRPCResponseResult<SecretContentMessage>
> {
if (ctx.signal.aborted) throw ctx.signal.reason;
for await (const secretIdentifierMessage of input) {
const { nameOrId, secretName } = secretIdentifierMessage;
const vaultIdFromName = await vaultManager.getVaultId(nameOrId, tran);
const vaultId = vaultIdFromName ?? vaultsUtils.decodeVaultId(nameOrId);
if (vaultId == null) {
throw new vaultsErrors.ErrorVaultsVaultUndefined();
}
const secrets = await vaultManager.withVaults(
[vaultId],
async (vault) => {
const results: Array<{
filePath: string;
value: string;
}> = [];
return await vault.readF(async (fs) => {
try {
for await (const filePath of vaultsUtils.walkFs(
fs,
secretName,
)) {
const fileContents = await fs.readFile(filePath);
results.push({
filePath,
value: fileContents.toString(),
});
}
} catch (e) {
if (e.code === 'ENOENT') {
throw new vaultsErrors.ErrorSecretsSecretUndefined(
`Secret with name: ${secretName} does not exist`,
{ cause: e },
);
}
throw e;
}
return results;
});
},
tran,
);
for (const { filePath, value } of secrets) {
yield {
nameOrId: nameOrId,
secretName: filePath,
secretContent: value,
};
}
}
});
};
}

export default VaultsSecretsList;
3 changes: 3 additions & 0 deletions src/client/handlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ import VaultsRename from './VaultsRename';
import VaultsScan from './VaultsScan';
import VaultsSecretsDelete from './VaultsSecretsDelete';
import VaultsSecretsEdit from './VaultsSecretsEdit';
import VaultsSecretsEnv from './VaultsSecretsEnv';
import VaultsSecretsGet from './VaultsSecretsGet';
import VaultsSecretsList from './VaultsSecretsList';
import VaultsSecretsMkdir from './VaultsSecretsMkdir';
Expand Down Expand Up @@ -175,6 +176,7 @@ const serverManifest = (container: {
vaultsScan: new VaultsScan(container),
vaultsSecretsDelete: new VaultsSecretsDelete(container),
vaultsSecretsEdit: new VaultsSecretsEdit(container),
vaultsSecretsEnv: new VaultsSecretsEnv(container),
vaultsSecretsGet: new VaultsSecretsGet(container),
vaultsSecretsList: new VaultsSecretsList(container),
vaultsSecretsMkdir: new VaultsSecretsMkdir(container),
Expand Down Expand Up @@ -249,6 +251,7 @@ export {
VaultsScan,
VaultsSecretsDelete,
VaultsSecretsEdit,
VaultsSecretsEnv,
VaultsSecretsGet,
VaultsSecretsList,
VaultsSecretsMkdir,
Expand Down
2 changes: 1 addition & 1 deletion src/git/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ async function listRefs(
const packedMap = packedRefs(fs, gitdir);
let files: string[] = [];
try {
for await (const file of vaultsUtils.readdirRecursively(
for await (const file of vaultsUtils.readDirRecursively(
fs,
path.join(gitdir, filepath),
)) {
Expand Down
4 changes: 2 additions & 2 deletions src/vaults/VaultOps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ async function addSecretDirectory(
const absoluteDirPath = path.resolve(secretDirectory);

await vault.writeF(async (efs) => {
for await (const secretPath of vaultsUtils.readdirRecursively(
for await (const secretPath of vaultsUtils.readDirRecursively(
fs,
absoluteDirPath,
)) {
Expand Down Expand Up @@ -253,7 +253,7 @@ async function addSecretDirectory(
async function listSecrets(vault: Vault): Promise<string[]> {
return await vault.readF(async (efs) => {
const secrets: string[] = [];
for await (const secret of vaultsUtils.readdirRecursively(efs)) {
for await (const secret of vaultsUtils.readDirRecursively(efs)) {
secrets.push(secret);
}
return secrets;
Expand Down
37 changes: 33 additions & 4 deletions src/vaults/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import type { EncryptedFS } from 'encryptedfs';
import type { VaultRef, VaultAction, CommitId } from './types';
import type {
VaultRef,
VaultAction,
CommitId,
FileSystemReadable,
} from './types';
import type { NodeId } from '../ids/types';
import type { Path } from 'encryptedfs/dist/types';
import path from 'path';
import { pathJoin } from 'encryptedfs/dist/utils';
import { tagLast, refs, vaultActions } from './types';
import * as nodesUtils from '../nodes/utils';
import * as validationErrors from '../validation/errors';
Expand Down Expand Up @@ -36,19 +43,40 @@ function commitAuthor(nodeId: NodeId): { name: string; email: string } {
};
}

async function* readdirRecursively(fs, dir = '.') {
async function* readDirRecursively(fs, dir = '.') {
const dirents = await fs.promises.readdir(dir);
for (const dirent of dirents) {
const res = path.join(dir, dirent.toString());
const stat = await fs.promises.stat(res);
if (stat.isDirectory()) {
yield* readdirRecursively(fs, res);
yield* readDirRecursively(fs, res);
} else if (stat.isFile()) {
yield res;
}
}
}

async function* walkFs(
efs: FileSystemReadable,
path: string = '.',
): AsyncGenerator<string, undefined, undefined> {
const shortList: Array<string> = [path];
let path_: Path | undefined = undefined;
while ((path_ = shortList.shift()) != null) {
const pathStat = await efs.stat(path_);
if (pathStat.isDirectory()) {
// Push contents to shortlist
const newPaths = await efs.readdir(path_);
shortList.push(
...newPaths.map((v) => pathJoin(path_!.toString(), v.toString())),
);
} else {
// Is a file so we yield the path
yield path_;
}
}
}

function isVaultAction(action: any): action is VaultAction {
if (typeof action !== 'string') return false;
return (vaultActions as Readonly<Array<string>>).includes(action);
Expand Down Expand Up @@ -84,7 +112,8 @@ export {
commitAuthor,
isVaultAction,
parseVaultAction,
readdirRecursively,
readDirRecursively,
walkFs,
deleteObject,
};

Expand Down
Loading