Add dashboard-specific secret and secret import audit logs, re-touch audit logs v2 UI

This commit is contained in:
Tuan Dang
2023-08-08 19:03:24 +07:00
parent 6cb8cf53f8
commit 0cad823267
19 changed files with 472 additions and 55 deletions
@@ -1,11 +1,12 @@
import { EventType, UserAgentType } from "./enums";
export const eventToNameMap: { [K in EventType]: string } = {
[EventType.GET_SECRETS]: "Get secrets",
[EventType.GET_SECRET]: "Get secret",
[EventType.GET_SECRETS]: "List secrets",
[EventType.GET_SECRET]: "Read secret",
[EventType.CREATE_SECRET]: "Create secret",
[EventType.UPDATE_SECRET]: "Update secret",
[EventType.DELETE_SECRET]: "Delete secret",
[EventType.GET_WORKSPACE_KEY]: "Read project key",
[EventType.AUTHORIZE_INTEGRATION]: "Authorize integration",
[EventType.UNAUTHORIZE_INTEGRATION]: "Unauthorize integration",
[EventType.CREATE_INTEGRATION]: "Create integration",
@@ -26,6 +27,11 @@ export const eventToNameMap: { [K in EventType]: string } = {
[EventType.CREATE_WEBHOOK]: "Create webhook",
[EventType.UPDATE_WEBHOOK_STATUS]: "Update webhook status",
[EventType.DELETE_WEBHOOK]: "Delete webhook",
[EventType.GET_SECRET_IMPORTS]: "List secret imports",
[EventType.CREATE_SECRET_IMPORT]: "Create secret import",
[EventType.UPDATE_SECRET_IMPORT]: "Update secret import",
[EventType.DELETE_SECRET_IMPORT]: "Delete secret import",
};
export const userAgentTTypeoNameMap: { [K in UserAgentType]: string } = {
+6 -1
View File
@@ -16,6 +16,7 @@ export enum EventType {
CREATE_SECRET = "create-secret",
UPDATE_SECRET = "update-secret",
DELETE_SECRET = "delete-secret",
GET_WORKSPACE_KEY = "get-workspace-key",
AUTHORIZE_INTEGRATION = "authorize-integration",
UNAUTHORIZE_INTEGRATION = "unauthorize-integration",
CREATE_INTEGRATION = "create-integration",
@@ -35,5 +36,9 @@ export enum EventType {
DELETE_FOLDER = "delete-folder",
CREATE_WEBHOOK = "create-webhook",
UPDATE_WEBHOOK_STATUS = "update-webhook-status",
DELETE_WEBHOOK = "delete-webhook"
DELETE_WEBHOOK = "delete-webhook",
GET_SECRET_IMPORTS = "get-secret-imports",
CREATE_SECRET_IMPORT = "create-secret-import",
UPDATE_SECRET_IMPORT = "update-secret-import",
DELETE_SECRET_IMPORT = "delete-secret-import"
}
+72 -6
View File
@@ -82,17 +82,24 @@ interface DeleteSecretEvent {
}
}
interface GetWorkspaceKeyEvent {
type: EventType.GET_WORKSPACE_KEY,
metadata: {
keyId: string;
}
}
interface AuthorizeIntegrationEvent {
type: EventType.AUTHORIZE_INTEGRATION;
metadata: {
integration: string; // TODO: fix type
integration: string;
}
}
interface UnauthorizeIntegrationEvent {
type: EventType.UNAUTHORIZE_INTEGRATION;
metadata: {
integration: string; // TODO: fix type
integration: string;
}
}
@@ -100,12 +107,18 @@ interface CreateIntegrationEvent {
type: EventType.CREATE_INTEGRATION;
metadata: {
integrationId: string;
integration: string; // TODO: fix type
integration: string;
environment: string;
secretPath: string;
url?: string;
app?: string;
appId?: string;
targetEnvironment?: string;
targetEnvironmentId?: string; // TODO: consider adding other vars
targetEnvironmentId?: string;
targetService?: string;
targetServiceId?: string;
path?: string;
region?: string;
}
}
@@ -113,12 +126,18 @@ interface DeleteIntegrationEvent {
type: EventType.DELETE_INTEGRATION;
metadata: {
integrationId: string;
integration: string; // TODO: fix type
integration: string;
environment: string;
secretPath: string;
url?: string;
app?: string;
appId?: string;
targetEnvironment?: string;
targetEnvironmentId?: string;
targetService?: string;
targetServiceId?: string;
path?: string;
region?: string;
}
}
@@ -277,12 +296,55 @@ interface DeleteWebhookEvent {
}
}
interface GetSecretImportsEvent {
type: EventType.GET_SECRET_IMPORTS,
metadata: {
environment: string;
secretImportId: string;
folderId: string;
numberOfImports: number;
}
}
interface CreateSecretImportEvent {
type: EventType.CREATE_SECRET_IMPORT,
metadata: {
environment: string;
secretImportId: string;
folderId: string;
importEnvironment: string;
importSecretPath: string;
}
}
interface UpdateSecretImportEvent {
type: EventType.UPDATE_SECRET_IMPORT,
metadata: {
environment: string;
secretImportId: string;
folderId: string;
numberOfImports: number;
}
}
interface DeleteSecretImportEvent {
type: EventType.DELETE_SECRET_IMPORT,
metadata: {
environment: string;
secretImportId: string;
folderId: string;
importEnvironment: string;
importSecretPath: string;
}
}
export type Event =
| GetSecretsEvent
| GetSecretEvent
| CreateSecretEvent
| UpdateSecretEvent
| DeleteSecretEvent
| GetWorkspaceKeyEvent
| AuthorizeIntegrationEvent
| UnauthorizeIntegrationEvent
| CreateIntegrationEvent
@@ -302,7 +364,11 @@ export type Event =
| DeleteFolderEvent
| CreateWebhookEvent
| UpdateWebhookStatusEvent
| DeleteWebhookEvent;
| DeleteWebhookEvent
| GetSecretImportsEvent
| CreateSecretImportEvent
| UpdateSecretImportEvent
| DeleteSecretImportEvent;
export type AuditLog = {
_id: string;
+1 -1
View File
@@ -79,7 +79,7 @@ export type UpdateSecretArg = {
export type CreateSecretArg = Omit<UpdateSecretArg, "_id">;
export type DeleteSecretArg = { _id: string };
export type DeleteSecretArg = { _id: string, secretName: string; };
export type BatchSecretDTO = {
workspaceId: string;
@@ -149,7 +149,7 @@ export const DashboardPage = () => {
const [snapshotId, setSnaphotId] = useState<string | null>(null);
const [selectedEnv, setSelectedEnv] = useState<WorkspaceEnv | null>(null);
const [sortDir, setSortDir] = useState<"asc" | "desc">("asc");
const deletedSecretIds = useRef<string[]>([]);
const deletedSecretIds = useRef<{ id: string; secretName: string; }[]>([]);
const { hasUnsavedChanges, setHasUnsavedChanges } = useLeaveConfirm({ initialValue: false });
const folderId = router.query.folderId as string;
@@ -498,9 +498,15 @@ export const DashboardPage = () => {
// record all deleted ids
// This will make final deletion easier
const onSecretDelete = useCallback((index: number, id?: string, overrideId?: string) => {
if (id) deletedSecretIds.current.push(id);
if (overrideId) deletedSecretIds.current.push(overrideId);
const onSecretDelete = useCallback((index: number, secretName: string, id?: string, overrideId?: string) => {
if (id) deletedSecretIds.current.push({
id,
secretName
});
if (overrideId) deletedSecretIds.current.push({
id: overrideId,
secretName
});
remove(index);
// just the case if this is called from drawer
handlePopUpClose("secretDetails");
@@ -182,15 +182,18 @@ const deepCompareSecrets = (lhs: DecryptedSecret, rhs: any) =>
JSON.stringify(lhs.tags) === JSON.stringify(rhs.tags);
export const transformSecretsToBatchSecretReq = (
deletedSecretIds: string[],
deletedSecretIds: { id: string; secretName: string; }[],
latestFileKey: any,
secrets: FormData["secrets"],
intialValues: DecryptedSecret[] = []
) => {
// deleted secrets
const secretsToBeDeleted: BatchSecretDTO["requests"] = deletedSecretIds.map((id) => ({
const secretsToBeDeleted: BatchSecretDTO["requests"] = deletedSecretIds.map(({ id, secretName }) => ({
method: "DELETE",
secret: { _id: id }
secret: {
_id: id,
secretName
}
}));
const secretsToBeUpdated: BatchSecretDTO["requests"] = [];
@@ -261,7 +264,7 @@ export const transformSecretsToBatchSecretReq = (
if (idOverride) {
// if action is deleted meaning override has been removed but id is kept to collect at this point
if (overrideAction === SecretActionType.Deleted) {
secretsToBeDeleted.push({ method: "DELETE", secret: { _id: idOverride } });
secretsToBeDeleted.push({ method: "DELETE", secret: { _id: idOverride, secretName: key } });
} else {
// if not deleted action then as id is there its an updated
const initialSecretValue = intialValues?.find(({ _id: secId }) => secId === _id)!;
@@ -67,7 +67,7 @@ type Props = {
isSecretValueHidden: boolean;
searchTerm: string;
// to record the ids of deleted ones
onSecretDelete: (index: number, id?: string, overrideId?: string) => void;
onSecretDelete: (index: number, secretName: string, id?: string, overrideId?: string) => void;
// sidebar control props
onRowExpand: (secId: string | undefined, index: number) => void;
// tag props
@@ -495,7 +495,9 @@ export const SecretInputRow = memo(
colorSchema="danger"
ariaLabel="delete"
isDisabled={isReadOnly || isRollbackMode}
onClick={() => onSecretDelete(index, secId, idOverride)}
onClick={() => {
onSecretDelete(index, secKey, secId, idOverride);
}}
>
<FontAwesomeIcon icon={faXmark} />
</IconButton>
@@ -73,7 +73,7 @@ export const LogsFilter = ({
className="w-40 mr-4"
>
<Select
defaultValue={field.value}
{...(field.value ? { value: field.value } : { placeholder: "Select" })}
{...field}
onValueChange={(e) => onChange(e)}
className="w-full"
@@ -99,7 +99,7 @@ export const LogsFilter = ({
className="w-40 mr-4"
>
<Select
defaultValue={field.value}
{...(field.value ? { value: field.value } : { placeholder: "Select" })}
{...field}
onValueChange={(e) => onChange(e)}
className="w-full"
@@ -121,7 +121,7 @@ export const LogsFilter = ({
className="w-40 mr-4"
>
<Select
defaultValue={field.value}
{...(field.value ? { value: field.value } : { placeholder: "Select" })}
{...field}
onValueChange={(e) => onChange(e)}
className="w-full"
@@ -189,7 +189,7 @@ export const LogsFilter = ({
}}
/>
</div>
<div>
{/* <div>
<Button
isLoading={false}
colorSchema="primary"
@@ -198,15 +198,15 @@ export const LogsFilter = ({
leftIcon={<FontAwesomeIcon icon={faFilterCircleXmark} className="mr-2" />}
onClick={() => reset({
eventType: undefined,
actor: "",
userAgentType: "",
actor: undefined,
userAgentType: undefined,
startDate: undefined,
endDate: undefined
})}
>
Clear filters
</Button>
</div>
</div> */}
</div>
);
}
@@ -32,12 +32,14 @@ export const LogsSection = () => {
const perPage = watch("perPage") as number;
return (
<div className="p-4 bg-mineshaft-900 mb-6 rounded-lg border border-mineshaft-600">
<div className="flex items-center mb-8">
<div
// className="p-4 bg-mineshaft-900 mb-6 rounded-lg border border-mineshaft-600"
>
{/* <div className="flex items-center mb-8">
<h2 className="text-xl font-semibold flex-1 text-white">
Audit Logs
</h2>
</div>
</div> */}
<LogsFilter
control={control}
reset={reset}
@@ -100,6 +100,9 @@ export const LogsTableRow = ({
{event.metadata.app && (
<p>{`Target app: ${event.metadata.app}`}</p>
)}
{event.metadata.appId && (
<p>{`Target app: ${event.metadata.appId}`}</p>
)}
{event.metadata.targetEnvironment && (
<p>{`Target environment: ${event.metadata.targetEnvironment}`}</p>
)}
@@ -117,6 +120,9 @@ export const LogsTableRow = ({
{event.metadata.app && (
<p>{`Target App: ${event.metadata.app}`}</p>
)}
{event.metadata.appId && (
<p>{`Target app: ${event.metadata.appId}`}</p>
)}
{event.metadata.targetEnvironment && (
<p>{`Target environment: ${event.metadata.targetEnvironment}`}</p>
)}
@@ -219,7 +225,7 @@ export const LogsTableRow = ({
return (
<Td>
<p>{`Environment: ${event.metadata.environment}`}</p>
<p>{`Secret Path: ${event.metadata.secretPath}`}</p>
<p>{`Secret path: ${event.metadata.secretPath}`}</p>
<p>{`Webhook URL: ${event.metadata.webhookUrl}`}</p>
<p>{`Disabled: ${event.metadata.isDisabled}`}</p>
</Td>
@@ -228,7 +234,7 @@ export const LogsTableRow = ({
return (
<Td>
<p>{`Environment: ${event.metadata.environment}`}</p>
<p>{`Secret Path: ${event.metadata.secretPath}`}</p>
<p>{`Secret path: ${event.metadata.secretPath}`}</p>
<p>{`Webhook URL: ${event.metadata.webhookUrl}`}</p>
<p>{`Disabled: ${event.metadata.isDisabled}`}</p>
</Td>
@@ -237,14 +243,42 @@ export const LogsTableRow = ({
return (
<Td>
<p>{`Environment: ${event.metadata.environment}`}</p>
<p>{`Secret Path: ${event.metadata.secretPath}`}</p>
<p>{`Secret path: ${event.metadata.secretPath}`}</p>
<p>{`Webhook URL: ${event.metadata.webhookUrl}`}</p>
<p>{`Disabled: ${event.metadata.isDisabled}`}</p>
</Td>
);
case EventType.GET_SECRET_IMPORTS:
return (
<Td>
<p>{`Environment: ${event.metadata.environment}`}</p>
<p>{`# Imported paths: ${event.metadata.numberOfImports}`}</p>
</Td>
);
case EventType.CREATE_SECRET_IMPORT:
return (
<Td>
<p>{`Environment: ${event.metadata.environment}`}</p>
<p>{`Imported path: ${event.metadata.importSecretPath}`}</p>
</Td>
);
case EventType.UPDATE_SECRET_IMPORT:
return (
<Td>
<p>{`Environment: ${event.metadata.environment}`}</p>
<p>{`# Imported path: ${event.metadata.numberOfImports}`}</p>
</Td>
);
case EventType.DELETE_SECRET_IMPORT:
return (
<Td>
<p>{`Environment: ${event.metadata.environment}`}</p>
<p>{`Imported path: ${event.metadata.importSecretPath}`}</p>
</Td>
);
default:
return (
<Td>Test</Td>
<Td></Td>
);
}
}