Skip to content

Commit

Permalink
feat: adding a generalized audit domain command for inspecting audi…
Browse files Browse the repository at this point in the history
…t events

[ci skip]
  • Loading branch information
tegefaulkes committed May 31, 2024
1 parent 1dd4cba commit 0947cc6
Show file tree
Hide file tree
Showing 6 changed files with 495 additions and 3 deletions.
106 changes: 106 additions & 0 deletions src/audit/CommandAudit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import type PolykeyClient from 'polykey/dist/PolykeyClient';
import * as binOptions from '../utils/options';
import * as binProcessors from '../utils/processors';
import * as binUtils from '../utils';
import CommandPolykey from '../CommandPolykey';

class CommandIdentities extends CommandPolykey {
constructor(...args: ConstructorParameters<typeof CommandPolykey>) {
super(...args);
this.name('audit');
this.description('Displays audit event history');
this.addOption(binOptions.nodeId);
this.addOption(binOptions.clientHost);
this.addOption(binOptions.clientPort);
this.addOption(binOptions.seekStart);
this.addOption(binOptions.seekEnd);
this.addOption(binOptions.follow);
this.addOption(binOptions.events);
this.addOption(binOptions.limit);
this.addOption(binOptions.order);
this.action(async (options) => {
const { default: PolykeyClient } = await import(
'polykey/dist/PolykeyClient'
);
const auditUtils = await import('polykey/dist/audit/utils');
const clientOptions = await binProcessors.processClientOptions(
options.nodePath,
options.nodeId,
options.clientHost,
options.clientPort,
this.fs,
this.logger.getChild(binProcessors.processClientOptions.name),
);
const auth = await binProcessors.processAuthentication(
options.passwordFile,
this.fs,
);
let pkClient: PolykeyClient;
this.exitHandlers.handlers.push(async () => {
if (pkClient != null) await pkClient.stop();
});
try {
pkClient = await PolykeyClient.createPolykeyClient({
nodeId: clientOptions.nodeId,
host: clientOptions.clientHost,
port: clientOptions.clientPort,
options: {
nodePath: options.nodePath,
},
logger: this.logger.getChild(PolykeyClient.name),
});
// Creating an infinite timer to hold the process open
const holdOpenTimer = setTimeout(() => {}, 2 ** 30);
// We set up the readable stream watching the discovery events here
await binUtils
.retryAuthentication(async (auth) => {
const seek: number = options.seekStart;
const seekEnd: number | undefined = options.seekEnd;
const order: 'asc' | 'desc' = options.order;
const limit: number | undefined = options.limit;
const awaitFutureEvents = options.follow;
const readableStream =
await pkClient.rpcClient.methods.auditEventsGet({
awaitFutureEvents,
paths: auditUtils.filterSubPaths(options.events ?? [[]]),
seek,
seekEnd,
order,
limit,
metadata: auth,
});
// Tracks vertices that are relevant to our current search
for await (const result of readableStream) {
const sanitizedResult = {
id: result.id,
path: result.path.join('.'),
data: result.data,
};
if (options.format === 'json') {
process.stdout.write(
binUtils.outputFormatter({
type: 'json',
data: sanitizedResult,
}),
);
} else {
process.stdout.write(
binUtils.outputFormatter({
type: 'dict',
data: { '>': sanitizedResult },
}),
);
}
}
}, auth)
.finally(() => {
clearTimeout(holdOpenTimer);
});
} finally {
if (pkClient! != null) await pkClient.stop();
}
});
}
}

export default CommandIdentities;
1 change: 1 addition & 0 deletions src/audit/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './CommandAudit';
19 changes: 16 additions & 3 deletions src/identities/CommandDiscover.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import type PolykeyClient from 'polykey/dist/PolykeyClient';
import type { GestaltId } from 'polykey/dist/gestalts/types';
import type {
AuditEventDiscoveryVertex,
AuditEventToAuditEventSerialized,
} from 'polykey/dist/audit/types';
import CommandPolykey from '../CommandPolykey';
import * as binOptions from '../utils/options';
import * as binUtils from '../utils';
Expand Down Expand Up @@ -63,7 +67,7 @@ class CommandDiscover extends CommandPolykey {
const readableStream =
await pkClient.rpcClient.methods.auditEventsGet({
awaitFutureEvents: true,
path: ['discovery', 'vertex'],
paths: [['discovery', 'vertex']],
seek: Date.now(),
metadata: auth,
});
Expand All @@ -74,8 +78,17 @@ class CommandDiscover extends CommandPolykey {
// Adding the initial vertex
relevantSet.add(gestaltUtils.encodeGestaltId(gestaltId));
for await (const result of readableStream) {
const event = result.path[2];
const { vertex, parent } = result.data;
if (
result.path[0] !== 'discovery' ||
result.path[1] !== 'vertex'
) {
utils.never('Should be a discovery vertex event');
}
// We're only requesting discovery vertex events, so we need to re-cast the type here
const resultTyped =
result as AuditEventToAuditEventSerialized<AuditEventDiscoveryVertex>;
const event = resultTyped.path[2];
const { vertex, parent } = resultTyped.data;
// Skip if the vertex and parent are not relevant
if (
!relevantSet.has(vertex) &&
Expand Down
2 changes: 2 additions & 0 deletions src/polykey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ async function polykeyMain(argv: Array<string>): Promise<number> {
const { default: config } = await import('polykey/dist/config');
const { default: CommandBootstrap } = await import('./bootstrap');
const { default: CommandAgent } = await import('./agent');
const { default: CommandAudit } = await import('./audit');
const { default: CommandVaults } = await import('./vaults');
const { default: CommandSecrets } = await import('./secrets');
const { default: CommandKeys } = await import('./keys');
Expand All @@ -164,6 +165,7 @@ async function polykeyMain(argv: Array<string>): Promise<number> {
rootCommand.description('Polykey CLI');
rootCommand.addCommand(new CommandBootstrap({ exitHandlers, fs }));
rootCommand.addCommand(new CommandAgent({ exitHandlers, fs }));
rootCommand.addCommand(new CommandAudit({ exitHandlers, fs }));
rootCommand.addCommand(new CommandNodes({ exitHandlers, fs }));
rootCommand.addCommand(new CommandSecrets({ exitHandlers, fs }));
rootCommand.addCommand(new CommandKeys({ exitHandlers, fs }));
Expand Down
63 changes: 63 additions & 0 deletions src/utils/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,63 @@ const discoveryMonitor = new commander.Option(
'Enabling monitoring will cause discover to output discovery events as they happen and will exit once all children are processed',
).default(false);

const parseDate = (value: string): number => {
if (value.toLowerCase() === 'now') return Date.now();
const date = Date.parse(value);
if (isNaN(date)) throw Error('Invalid data');
return date;
};

const seekStart = new commander.Option(
'--seek-start [seekStart]',
`time to start seeking from`,
)
.argParser(parseDate)
.default(0);

const seekEnd = new commander.Option(
'--seek-end [seekEnd]',
`time to seek until`,
)
.argParser(parseDate)
.default(undefined);

const follow = new commander.Option(
'--follow',
'If enabled, future events will be outputted as they happen',
).default(false);

const events = new commander.Option(
'--events [events...]',
'Filter for specified event paths',
)
.argParser(
(
value: string,
previous: Array<Array<string>> | undefined,
): Array<Array<string>> => {
const parsedPath = value.split('.');
const out = previous ?? [];
out.push(parsedPath);
return out;
},
)
.default(undefined);

const limit = new commander.Option(
'--limit [limit]',
'Limit the number of emitted events',
)
.argParser(parseInt)
.default(undefined);

const order = new commander.Option(
'--order [order]',
'Filter for specified events',
)
.choices(['asc', 'desc'])
.default('asc');

export {
nodePath,
format,
Expand Down Expand Up @@ -272,4 +329,10 @@ export {
envInvalid,
envDuplicate,
discoveryMonitor,
seekStart,
seekEnd,
follow,
events,
limit,
order,
};
Loading

0 comments on commit 0947cc6

Please sign in to comment.