Skip to content

Commit

Permalink
(Feature) Get team audit logs through secure tunnel
Browse files Browse the repository at this point in the history
  • Loading branch information
Jose Arroyo Rodriguez committed Aug 8, 2024
1 parent f0f559c commit 2e0002b
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 108 deletions.
58 changes: 7 additions & 51 deletions src/command-handlers/teamLogs.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,17 @@
import { StartAuditLogsQueryParams, startAuditLogsQuery, getAuditLogQueryResults } from '../endpoints/index.js';
import { getAuditLogs } from '../endpoints/index.js';
import { getTeamDeviceCredentials, jsonToCsv, epochTimestampToIso } from '../utils/index.js';
import { GenericLog } from '../types/logs.js';
import { logger } from '../logger.js';

export const runTeamLogs = async (options: {
start: string;
end: string;
type: string;
category: string;
csv: boolean;
humanReadable: boolean;
}) => {
export const runTeamLogs = async (options: { start: string; end: string; csv: boolean; humanReadable: boolean }) => {
const teamDeviceCredentials = getTeamDeviceCredentials();

const { start, end, type, category } = options;
const { start, end } = options;

let logs = await getAuditLogs({
teamDeviceCredentials,
startDateRangeUnix: parseInt(start),
endDateRangeUnix: parseInt(end),
logType: type,
category,
queryParams: {
startDateRangeUnixMs: parseInt(start),
endDateRangeUnixMs: parseInt(end),
},
});

if (options.humanReadable) {
Expand All @@ -39,38 +30,3 @@ export const runTeamLogs = async (options: {

logs.forEach((log) => logger.content(JSON.stringify(log)));
};

const MAX_RESULT = 1000;

export const getAuditLogs = async (params: StartAuditLogsQueryParams): Promise<GenericLog[]> => {
const { teamDeviceCredentials } = params;

const { queryExecutionId } = await startAuditLogsQuery(params);

let result = await getAuditLogQueryResults({ teamDeviceCredentials, queryExecutionId, maxResults: MAX_RESULT });
logger.debug(`Query state: ${result.state}`);

while (['QUEUED', 'RUNNING'].includes(result.state)) {
await new Promise((resolve) => setTimeout(resolve, 2000));
result = await getAuditLogQueryResults({ teamDeviceCredentials, queryExecutionId, maxResults: MAX_RESULT });
logger.debug(`Query state: ${result.state}`);
}

if (result.state !== 'SUCCEEDED') {
throw new Error(`Query execution did not succeed: ${result.state}`);
}

let logs = result.results;
while (result.nextToken) {
result = await getAuditLogQueryResults({
teamDeviceCredentials,
queryExecutionId,
maxResults: MAX_RESULT,
nextToken: result.nextToken,
});
logger.debug(`Query state: ${result.state}`);
logs = logs.concat(result.results);
}

return logs.map((log) => JSON.parse(log) as GenericLog);
};
2 changes: 0 additions & 2 deletions src/commands/team/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ export const teamCommands = (params: { program: Command }) => {
customParseTimestampMilliseconds,
Date.now()
)
.option('--type <type>', 'log type')
.option('--category <category>', 'log category')
.option('--csv', 'Output in CSV format')
.option('--human-readable', 'Output dates in human readable format')
.action(runTeamLogs);
Expand Down
120 changes: 65 additions & 55 deletions src/endpoints/getAuditLogs.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,17 @@
import { requestTeamApi } from '../requestApi.js';
import { apiConnect } from '../modules/tunnel-api-connect/apiconnect.js';
import { logger } from '../logger.js';
import { TeamDeviceCredentials } from '../types.js';
import { GenericLog } from '../types/logs.js';

export interface StartAuditLogsQueryParams {
teamDeviceCredentials: TeamDeviceCredentials;

/**
* The start of the date range to query audit logs by. The format is unix timestamp in seconds. Only the date is used, not the time.
*/
startDateRangeUnix: number;
startDateRangeUnixMs: number;
/**
* The end of the date range of to query audit logs by. The format is unix timestamp in seconds. Only the date is used, not the time.
*/
endDateRangeUnix: number;
/**
* The user ID of the author of the audit log.
*/
authorUserId?: number;
/**
* The ID of the user targeted by the audit log action.
*/
targetUserId?: number;
/**
* The ID of the sharing group targeted by the audit log action.
*/
sharingGroupId?: number;
/**
* The types of audit logs to filter by.
*/
logType?: string;
/**
* The categories audit logs to filter by.
*/
category?: string;
/**
* Additional properties to filter by. Refer to the specific audit log schema for property details.
*/
properties?: {
propName: string;
value: string;
}[];
endDateRangeUnixMs: number;
}

export interface StartAuditLogsQueryOutput {
Expand All @@ -48,22 +21,13 @@ export interface StartAuditLogsQueryOutput {
queryExecutionId: string;
}

export const startAuditLogsQuery = (params: StartAuditLogsQueryParams) => {
const { teamDeviceCredentials, ...payload } = params;
return requestTeamApi<StartAuditLogsQueryOutput>({
path: 'auditlogs-teamdevice/StartAuditLogsQuery',
teamUuid: teamDeviceCredentials.uuid,
teamDeviceKeys: {
accessKey: teamDeviceCredentials.accessKey,
secretKey: teamDeviceCredentials.secretKey,
},
payload,
});
};
export interface StartAuditLogsQueryRequest {
path: 'logs-teamdevice/StartAuditLogsQuery';
input: StartAuditLogsQueryParams;
output: StartAuditLogsQueryOutput;
}

export interface GetAuditLogQueryResultsParams {
teamDeviceCredentials: TeamDeviceCredentials;

/**
* The ID associated with the query executed by the RequestAuditLogs endpoint.
*/
Expand Down Expand Up @@ -93,15 +57,61 @@ export interface GetAuditLogQueryResultsOutput {
nextToken?: string;
}

export const getAuditLogQueryResults = (params: GetAuditLogQueryResultsParams) => {
const { teamDeviceCredentials, ...payload } = params;
return requestTeamApi<GetAuditLogQueryResultsOutput>({
path: 'auditlogs-teamdevice/GetAuditLogQueryResults',
teamUuid: teamDeviceCredentials.uuid,
teamDeviceKeys: {
accessKey: teamDeviceCredentials.accessKey,
secretKey: teamDeviceCredentials.secretKey,
export interface GetAuditLogQueryResultsRequest {
path: 'logs-teamdevice/GetAuditLogQueryResults';
input: GetAuditLogQueryResultsParams;
output: GetAuditLogQueryResultsOutput;
}

const MAX_RESULT = 1000;

export const getAuditLogs = async (params: {
queryParams: StartAuditLogsQueryParams;
teamDeviceCredentials: TeamDeviceCredentials;
}): Promise<GenericLog[]> => {
const { teamDeviceCredentials, queryParams } = params;

const api = await apiConnect({
isProduction: true,
enclavePcrList: [
[3, 'dfb6428f132530b8c021bea8cbdba2c87c96308ba7e81c7aff0655ec71228122a9297fd31fe5db7927a7322e396e4c16'],
[8, '4dbb92401207e019e132d86677857081d8e4d21f946f3561b264b7389c6982d3a86bcf9560cef4a2327eac5c5c6ab820'],
],
});

const { queryExecutionId } = await api.sendSecureContent<StartAuditLogsQueryRequest>({
...api,
path: 'logs-teamdevice/StartAuditLogsQuery',
payload: queryParams,
authentication: {
type: 'teamDevice',
teamDeviceKeys: teamDeviceCredentials,
teamUuid: teamDeviceCredentials.uuid,
},
payload,
});

let result: GetAuditLogQueryResultsOutput | undefined;
let logs: string[] = [];

do {
await new Promise((resolve) => setTimeout(resolve, 2000));
result = await api.sendSecureContent<GetAuditLogQueryResultsRequest>({
...api,
path: 'logs-teamdevice/GetAuditLogQueryResults',
payload: { queryExecutionId, maxResults: MAX_RESULT, nextToken: result?.nextToken },
authentication: {
type: 'teamDevice',
teamDeviceKeys: teamDeviceCredentials,
teamUuid: teamDeviceCredentials.uuid,
},
});
logger.debug(`Query state: ${result.state}`);
logs = logs.concat(result.results);
} while (['QUEUED', 'RUNNING'].includes(result.state) || result.nextToken);

if (result.state !== 'SUCCEEDED') {
throw new Error(`Query execution did not succeed: ${result.state}`);
}

return logs.map((log) => JSON.parse(log) as GenericLog);
};

0 comments on commit 2e0002b

Please sign in to comment.