mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat: audit log disable storage flag (#4295)
* feat: audit log disable storage flag * fix: pr changes * fix: revert license fns * Update frontend/src/layouts/OrganizationLayout/components/AuditLogBanner/AuditLogBanner.tsx
This commit is contained in:
@@ -1,8 +1,10 @@
|
|||||||
// weird commonjs-related error in the CI requires us to do the import like this
|
// weird commonjs-related error in the CI requires us to do the import like this
|
||||||
import knex from "knex";
|
import knex from "knex";
|
||||||
|
import { v4 as uuidv4 } from "uuid";
|
||||||
|
|
||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import { TableName, TAuditLogs } from "@app/db/schemas";
|
import { TableName, TAuditLogs } from "@app/db/schemas";
|
||||||
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { DatabaseError, GatewayTimeoutError } from "@app/lib/errors";
|
import { DatabaseError, GatewayTimeoutError } from "@app/lib/errors";
|
||||||
import { ormify, selectAllTableCols, TOrmify } from "@app/lib/knex";
|
import { ormify, selectAllTableCols, TOrmify } from "@app/lib/knex";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
@@ -188,5 +190,20 @@ export const auditLogDALFactory = (db: TDbClient) => {
|
|||||||
logger.info(`${QueueName.DailyResourceCleanUp}: audit log completed`);
|
logger.info(`${QueueName.DailyResourceCleanUp}: audit log completed`);
|
||||||
};
|
};
|
||||||
|
|
||||||
return { ...auditLogOrm, pruneAuditLog, find };
|
const create: TAuditLogDALFactory["create"] = async (tx) => {
|
||||||
|
const config = getConfig();
|
||||||
|
|
||||||
|
if (config.DISABLE_AUDIT_LOG_STORAGE) {
|
||||||
|
return {
|
||||||
|
...tx,
|
||||||
|
id: uuidv4(),
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return auditLogOrm.create(tx);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { ...auditLogOrm, create, pruneAuditLog, find };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ const envSchema = z
|
|||||||
AUDIT_LOGS_DB_ROOT_CERT: zpStr(
|
AUDIT_LOGS_DB_ROOT_CERT: zpStr(
|
||||||
z.string().describe("Postgres database base64-encoded CA cert for Audit logs").optional()
|
z.string().describe("Postgres database base64-encoded CA cert for Audit logs").optional()
|
||||||
),
|
),
|
||||||
|
DISABLE_AUDIT_LOG_STORAGE: zodStrBool.default("false").optional().describe("Disable audit log storage"),
|
||||||
MAX_LEASE_LIMIT: z.coerce.number().default(10000),
|
MAX_LEASE_LIMIT: z.coerce.number().default(10000),
|
||||||
DB_ROOT_CERT: zpStr(z.string().describe("Postgres database base64-encoded CA cert").optional()),
|
DB_ROOT_CERT: zpStr(z.string().describe("Postgres database base64-encoded CA cert").optional()),
|
||||||
DB_HOST: zpStr(z.string().describe("Postgres database host").optional()),
|
DB_HOST: zpStr(z.string().describe("Postgres database host").optional()),
|
||||||
@@ -482,6 +483,15 @@ export const overwriteSchema: {
|
|||||||
fields: { key: keyof TEnvConfig; description?: string }[];
|
fields: { key: keyof TEnvConfig; description?: string }[];
|
||||||
};
|
};
|
||||||
} = {
|
} = {
|
||||||
|
auditLogs: {
|
||||||
|
name: "Audit Logs",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
key: "DISABLE_AUDIT_LOG_STORAGE",
|
||||||
|
description: "Disable audit log storage"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
aws: {
|
aws: {
|
||||||
name: "AWS",
|
name: "AWS",
|
||||||
fields: [
|
fields: [
|
||||||
|
|||||||
@@ -2144,7 +2144,8 @@ export const registerRoutes = async (
|
|||||||
inviteOnlySignup: z.boolean().optional(),
|
inviteOnlySignup: z.boolean().optional(),
|
||||||
redisConfigured: z.boolean().optional(),
|
redisConfigured: z.boolean().optional(),
|
||||||
secretScanningConfigured: z.boolean().optional(),
|
secretScanningConfigured: z.boolean().optional(),
|
||||||
samlDefaultOrgSlug: z.string().optional()
|
samlDefaultOrgSlug: z.string().optional(),
|
||||||
|
auditLogStorageDisabled: z.boolean().optional()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2171,7 +2172,8 @@ export const registerRoutes = async (
|
|||||||
inviteOnlySignup: Boolean(serverCfg.allowSignUp),
|
inviteOnlySignup: Boolean(serverCfg.allowSignUp),
|
||||||
redisConfigured: cfg.isRedisConfigured,
|
redisConfigured: cfg.isRedisConfigured,
|
||||||
secretScanningConfigured: cfg.isSecretScanningConfigured,
|
secretScanningConfigured: cfg.isSecretScanningConfigured,
|
||||||
samlDefaultOrgSlug: cfg.samlDefaultOrgSlug
|
samlDefaultOrgSlug: cfg.samlDefaultOrgSlug,
|
||||||
|
auditLogStorageDisabled: Boolean(cfg.DISABLE_AUDIT_LOG_STORAGE)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -65,7 +65,14 @@ Example values:
|
|||||||
default="false"
|
default="false"
|
||||||
optional
|
optional
|
||||||
>
|
>
|
||||||
Determines whether your Infisical instance can automatically read the service account token of the pod it's running on. Used for features such as the IRSA auth method.
|
Determines whether your Infisical instance can automatically read the service
|
||||||
|
account token of the pod it's running on. Used for features such as the IRSA
|
||||||
|
auth method.
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="DISABLE_AUDIT_LOG_STORAGE" type="string" default="false">
|
||||||
|
Disable storing audit logs in the database. This is useful if you're using
|
||||||
|
audit log streams and don't want to store them in the database.
|
||||||
</ParamField>
|
</ParamField>
|
||||||
|
|
||||||
## CORS
|
## CORS
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ export type TServerConfig = {
|
|||||||
initialized: boolean;
|
initialized: boolean;
|
||||||
allowSignUp: boolean;
|
allowSignUp: boolean;
|
||||||
allowedSignUpDomain?: string | null;
|
allowedSignUpDomain?: string | null;
|
||||||
|
disableAuditLogStorage: boolean;
|
||||||
isMigrationModeOn?: boolean;
|
isMigrationModeOn?: boolean;
|
||||||
trustSamlEmails: boolean;
|
trustSamlEmails: boolean;
|
||||||
trustLdapEmails: boolean;
|
trustLdapEmails: boolean;
|
||||||
|
|||||||
@@ -5,4 +5,5 @@ export type ServerStatus = {
|
|||||||
secretScanningConfigured: boolean;
|
secretScanningConfigured: boolean;
|
||||||
redisConfigured: boolean;
|
redisConfigured: boolean;
|
||||||
samlDefaultOrgSlug: string;
|
samlDefaultOrgSlug: string;
|
||||||
|
auditLogStorageDisabled: boolean;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,10 +6,11 @@ import { twMerge } from "tailwind-merge";
|
|||||||
|
|
||||||
import { CreateOrgModal } from "@app/components/organization/CreateOrgModal";
|
import { CreateOrgModal } from "@app/components/organization/CreateOrgModal";
|
||||||
import { Banner } from "@app/components/page-frames/Banner";
|
import { Banner } from "@app/components/page-frames/Banner";
|
||||||
import { useServerConfig } from "@app/context";
|
import { useServerConfig, useSubscription } from "@app/context";
|
||||||
import { usePopUp } from "@app/hooks";
|
import { usePopUp } from "@app/hooks";
|
||||||
import { useFetchServerStatus } from "@app/hooks/api";
|
import { useFetchServerStatus } from "@app/hooks/api";
|
||||||
|
|
||||||
|
import { AuditLogBanner } from "./components/AuditLogBanner";
|
||||||
import { InsecureConnectionBanner } from "./components/InsecureConnectionBanner";
|
import { InsecureConnectionBanner } from "./components/InsecureConnectionBanner";
|
||||||
import { Navbar } from "./components/NavBar";
|
import { Navbar } from "./components/NavBar";
|
||||||
import { OrgSidebar } from "./components/OrgSidebar";
|
import { OrgSidebar } from "./components/OrgSidebar";
|
||||||
@@ -31,6 +32,7 @@ export const OrganizationLayout = () => {
|
|||||||
const containerHeight = config.pageFrameContent ? "h-[94vh]" : "h-screen";
|
const containerHeight = config.pageFrameContent ? "h-[94vh]" : "h-screen";
|
||||||
|
|
||||||
const { data: serverDetails, isLoading } = useFetchServerStatus();
|
const { data: serverDetails, isLoading } = useFetchServerStatus();
|
||||||
|
const { subscription } = useSubscription();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -41,6 +43,7 @@ export const OrganizationLayout = () => {
|
|||||||
<Navbar />
|
<Navbar />
|
||||||
{!isLoading && !serverDetails?.redisConfigured && <RedisBanner />}
|
{!isLoading && !serverDetails?.redisConfigured && <RedisBanner />}
|
||||||
{!isLoading && !serverDetails?.emailConfigured && <SmtpBanner />}
|
{!isLoading && !serverDetails?.emailConfigured && <SmtpBanner />}
|
||||||
|
{!isLoading && subscription.auditLogs && <AuditLogBanner />}
|
||||||
{!window.isSecureContext && <InsecureConnectionBanner />}
|
{!window.isSecureContext && <InsecureConnectionBanner />}
|
||||||
<div className="flex flex-grow flex-col overflow-y-hidden md:flex-row">
|
<div className="flex flex-grow flex-col overflow-y-hidden md:flex-row">
|
||||||
<OrgSidebar isHidden={isInsideProject} />
|
<OrgSidebar isHidden={isInsideProject} />
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { useOrganization } from "@app/context";
|
||||||
|
import { useFetchServerStatus, useGetAuditLogStreams } from "@app/hooks/api";
|
||||||
|
|
||||||
|
import { OrgAlertBanner } from "../OrgAlertBanner";
|
||||||
|
|
||||||
|
export const AuditLogBanner = () => {
|
||||||
|
const org = useOrganization();
|
||||||
|
const { data: status, isLoading: isLoadingStatus } = useFetchServerStatus();
|
||||||
|
const { data: streams, isLoading: isLoadingStreams } = useGetAuditLogStreams(org.currentOrg.id);
|
||||||
|
|
||||||
|
if (isLoadingStreams || isLoadingStatus || !streams) return null;
|
||||||
|
|
||||||
|
if (status?.auditLogStorageDisabled && !streams.length) {
|
||||||
|
return (
|
||||||
|
<OrgAlertBanner
|
||||||
|
text="Attention: Audit logs storage is disabled but no audit log streams have been configured."
|
||||||
|
link="https://infisical.com/docs/documentation/platform/audit-log-streams/audit-log-streams"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./AuditLogBanner";
|
||||||
@@ -12,6 +12,7 @@ export const AuditLogsPage = () => {
|
|||||||
<link rel="icon" href="/infisical.ico" />
|
<link rel="icon" href="/infisical.ico" />
|
||||||
<meta property="og:image" content="/images/message.png" />
|
<meta property="og:image" content="/images/message.png" />
|
||||||
</Helmet>
|
</Helmet>
|
||||||
|
|
||||||
<div className="flex h-full w-full justify-center bg-bunker-800 text-white">
|
<div className="flex h-full w-full justify-center bg-bunker-800 text-white">
|
||||||
<div className="w-full max-w-7xl">
|
<div className="w-full max-w-7xl">
|
||||||
<PageHeader
|
<PageHeader
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Fragment } from "react";
|
import { Fragment } from "react";
|
||||||
import { faFile } from "@fortawesome/free-solid-svg-icons";
|
import { faCancel, faFile } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -16,7 +16,7 @@ import {
|
|||||||
Tr
|
Tr
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { Timezone } from "@app/helpers/datetime";
|
import { Timezone } from "@app/helpers/datetime";
|
||||||
import { useGetAuditLogs } from "@app/hooks/api";
|
import { useFetchServerStatus, useGetAuditLogs } from "@app/hooks/api";
|
||||||
import { TGetAuditLogsFilter } from "@app/hooks/api/auditLogs/types";
|
import { TGetAuditLogsFilter } from "@app/hooks/api/auditLogs/types";
|
||||||
|
|
||||||
import { LogsTableRow } from "./LogsTableRow";
|
import { LogsTableRow } from "./LogsTableRow";
|
||||||
@@ -30,6 +30,8 @@ type Props = {
|
|||||||
const AUDIT_LOG_LIMIT = 30;
|
const AUDIT_LOG_LIMIT = 30;
|
||||||
|
|
||||||
export const LogsTable = ({ filter, refetchInterval, timezone }: Props) => {
|
export const LogsTable = ({ filter, refetchInterval, timezone }: Props) => {
|
||||||
|
const { data: status } = useFetchServerStatus();
|
||||||
|
|
||||||
// Determine the project ID for filtering
|
// Determine the project ID for filtering
|
||||||
const filterProjectId =
|
const filterProjectId =
|
||||||
// Use the projectId from the filter if it exists
|
// Use the projectId from the filter if it exists
|
||||||
@@ -79,7 +81,11 @@ export const LogsTable = ({ filter, refetchInterval, timezone }: Props) => {
|
|||||||
{isEmpty && (
|
{isEmpty && (
|
||||||
<Tr>
|
<Tr>
|
||||||
<Td colSpan={3}>
|
<Td colSpan={3}>
|
||||||
<EmptyState title="No audit logs on file" icon={faFile} />
|
{status?.auditLogStorageDisabled ? (
|
||||||
|
<EmptyState title="Audit log storage is disabled" icon={faCancel} />
|
||||||
|
) : (
|
||||||
|
<EmptyState title="No audit logs on file" icon={faFile} />
|
||||||
|
)}
|
||||||
</Td>
|
</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user