mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 21:28:18 +00:00
feat: switched audit log stream from project level to org level
This commit is contained in:
@@ -13,8 +13,8 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
t.text("encryptedTokenTag");
|
t.text("encryptedTokenTag");
|
||||||
t.string("encryptedTokenAlgorithm");
|
t.string("encryptedTokenAlgorithm");
|
||||||
t.string("encryptedTokenKeyEncoding");
|
t.string("encryptedTokenKeyEncoding");
|
||||||
t.string("projectId").notNullable();
|
t.uuid("orgId").notNullable();
|
||||||
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
|
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
|
||||||
t.timestamps(true, true, true);
|
t.timestamps(true, true, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const AuditLogStreamsSchema = z.object({
|
|||||||
encryptedTokenTag: z.string().nullable().optional(),
|
encryptedTokenTag: z.string().nullable().optional(),
|
||||||
encryptedTokenAlgorithm: z.string().nullable().optional(),
|
encryptedTokenAlgorithm: z.string().nullable().optional(),
|
||||||
encryptedTokenKeyEncoding: z.string().nullable().optional(),
|
encryptedTokenKeyEncoding: z.string().nullable().optional(),
|
||||||
projectId: z.string(),
|
orgId: z.string().uuid(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date()
|
updatedAt: z.date()
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ export const registerAuditLogStreamRouter = async (server: FastifyZodProvider) =
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
body: z.object({
|
body: z.object({
|
||||||
projectSlug: z.string().min(1).describe(AUDIT_LOG_STREAMS.CREATE.projectSlug),
|
|
||||||
url: z.string().min(1).describe(AUDIT_LOG_STREAMS.CREATE.url),
|
url: z.string().min(1).describe(AUDIT_LOG_STREAMS.CREATE.url),
|
||||||
token: z.string().optional().describe(AUDIT_LOG_STREAMS.CREATE.token)
|
token: z.string().optional().describe(AUDIT_LOG_STREAMS.CREATE.token)
|
||||||
}),
|
}),
|
||||||
@@ -38,7 +37,6 @@ export const registerAuditLogStreamRouter = async (server: FastifyZodProvider) =
|
|||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
projectSlug: req.body.projectSlug,
|
|
||||||
url: req.body.url,
|
url: req.body.url,
|
||||||
token: req.body.token
|
token: req.body.token
|
||||||
});
|
});
|
||||||
@@ -174,9 +172,6 @@ export const registerAuditLogStreamRouter = async (server: FastifyZodProvider) =
|
|||||||
bearerAuth: []
|
bearerAuth: []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
querystring: z.object({
|
|
||||||
projectSlug: z.string().describe(AUDIT_LOG_STREAMS.LIST.projectSlug)
|
|
||||||
}),
|
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
auditLogStreams: SanitizedAuditLogStreamSchema.array()
|
auditLogStreams: SanitizedAuditLogStreamSchema.array()
|
||||||
@@ -189,8 +184,7 @@ export const registerAuditLogStreamRouter = async (server: FastifyZodProvider) =
|
|||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod
|
||||||
projectSlug: req.query.projectSlug
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return { auditLogStreams };
|
return { auditLogStreams };
|
||||||
|
|||||||
@@ -4,11 +4,10 @@ import { SecretKeyEncoding } from "@app/db/schemas";
|
|||||||
import { infisicalSymmetricDecrypt, infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
|
import { infisicalSymmetricDecrypt, infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { validateLocalIps } from "@app/lib/validator";
|
import { validateLocalIps } from "@app/lib/validator";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
|
||||||
|
|
||||||
import { TLicenseServiceFactory } from "../license/license-service";
|
import { TLicenseServiceFactory } from "../license/license-service";
|
||||||
|
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
||||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission";
|
|
||||||
import { TAuditLogStreamDALFactory } from "./audit-log-stream-dal";
|
import { TAuditLogStreamDALFactory } from "./audit-log-stream-dal";
|
||||||
import {
|
import {
|
||||||
TCreateAuditLogStreamDTO,
|
TCreateAuditLogStreamDTO,
|
||||||
@@ -20,8 +19,7 @@ import {
|
|||||||
|
|
||||||
type TAuditLogStreamServiceFactoryDep = {
|
type TAuditLogStreamServiceFactoryDep = {
|
||||||
auditLogStreamDAL: TAuditLogStreamDALFactory;
|
auditLogStreamDAL: TAuditLogStreamDALFactory;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||||
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug">;
|
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -30,40 +28,29 @@ export type TAuditLogStreamServiceFactory = ReturnType<typeof auditLogStreamServ
|
|||||||
export const auditLogStreamServiceFactory = ({
|
export const auditLogStreamServiceFactory = ({
|
||||||
auditLogStreamDAL,
|
auditLogStreamDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
projectDAL,
|
|
||||||
licenseService
|
licenseService
|
||||||
}: TAuditLogStreamServiceFactoryDep) => {
|
}: TAuditLogStreamServiceFactoryDep) => {
|
||||||
const create = async ({
|
const create = async ({ url, actor, token, actorId, actorOrgId, actorAuthMethod }: TCreateAuditLogStreamDTO) => {
|
||||||
projectSlug,
|
if (!actorOrgId) throw new BadRequestError({ message: "Missing org id from token" });
|
||||||
url,
|
|
||||||
actor,
|
|
||||||
token,
|
|
||||||
actorId,
|
|
||||||
actorOrgId,
|
|
||||||
actorAuthMethod
|
|
||||||
}: TCreateAuditLogStreamDTO) => {
|
|
||||||
const plan = await licenseService.getPlan(actorOrgId);
|
const plan = await licenseService.getPlan(actorOrgId);
|
||||||
if (!plan.auditLogStreams)
|
if (!plan.auditLogStreams)
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: "Failed to create audit log streams due to plan restriction. Upgrade plan to create group."
|
message: "Failed to create audit log streams due to plan restriction. Upgrade plan to create group."
|
||||||
});
|
});
|
||||||
|
|
||||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
if (!project) throw new BadRequestError({ message: "Project not found" });
|
|
||||||
const projectId = project.id;
|
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Settings);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Settings);
|
||||||
|
|
||||||
validateLocalIps(url);
|
validateLocalIps(url);
|
||||||
|
|
||||||
const totalStreams = await auditLogStreamDAL.find({ projectId });
|
const totalStreams = await auditLogStreamDAL.find({ orgId: actorOrgId });
|
||||||
if (totalStreams.length >= plan.auditLogStreamLimit) {
|
if (totalStreams.length >= plan.auditLogStreamLimit) {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message:
|
message:
|
||||||
@@ -72,7 +59,7 @@ export const auditLogStreamServiceFactory = ({
|
|||||||
}
|
}
|
||||||
const encryptedToken = token ? infisicalSymmetricEncypt(token) : undefined;
|
const encryptedToken = token ? infisicalSymmetricEncypt(token) : undefined;
|
||||||
const logStream = await auditLogStreamDAL.create({
|
const logStream = await auditLogStreamDAL.create({
|
||||||
projectId,
|
orgId: actorOrgId,
|
||||||
url,
|
url,
|
||||||
...(encryptedToken
|
...(encryptedToken
|
||||||
? {
|
? {
|
||||||
@@ -96,6 +83,8 @@ export const auditLogStreamServiceFactory = ({
|
|||||||
actorOrgId,
|
actorOrgId,
|
||||||
actorAuthMethod
|
actorAuthMethod
|
||||||
}: TUpdateAuditLogStreamDTO) => {
|
}: TUpdateAuditLogStreamDTO) => {
|
||||||
|
if (!actorOrgId) throw new BadRequestError({ message: "Missing org id from token" });
|
||||||
|
|
||||||
const plan = await licenseService.getPlan(actorOrgId);
|
const plan = await licenseService.getPlan(actorOrgId);
|
||||||
if (!plan.auditLogStreams)
|
if (!plan.auditLogStreams)
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
@@ -105,20 +94,13 @@ export const auditLogStreamServiceFactory = ({
|
|||||||
const logStream = await auditLogStreamDAL.findById(id);
|
const logStream = await auditLogStreamDAL.findById(id);
|
||||||
if (!logStream) throw new BadRequestError({ message: "Audit log stream not found" });
|
if (!logStream) throw new BadRequestError({ message: "Audit log stream not found" });
|
||||||
|
|
||||||
const { projectId } = logStream;
|
const { orgId } = logStream;
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||||
actor,
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings);
|
||||||
actorId,
|
|
||||||
projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId
|
|
||||||
);
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings);
|
|
||||||
|
|
||||||
if (url) validateLocalIps(url);
|
if (url) validateLocalIps(url);
|
||||||
const encryptedToken = token ? infisicalSymmetricEncypt(token) : undefined;
|
const encryptedToken = token ? infisicalSymmetricEncypt(token) : undefined;
|
||||||
const updatedLogStream = await auditLogStreamDAL.updateById(id, {
|
const updatedLogStream = await auditLogStreamDAL.updateById(id, {
|
||||||
projectId,
|
|
||||||
url,
|
url,
|
||||||
...(encryptedToken
|
...(encryptedToken
|
||||||
? {
|
? {
|
||||||
@@ -134,18 +116,14 @@ export const auditLogStreamServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const deleteById = async ({ id, actor, actorId, actorOrgId, actorAuthMethod }: TDeleteAuditLogStreamDTO) => {
|
const deleteById = async ({ id, actor, actorId, actorOrgId, actorAuthMethod }: TDeleteAuditLogStreamDTO) => {
|
||||||
|
if (!actorOrgId) throw new BadRequestError({ message: "Missing org id from token" });
|
||||||
|
|
||||||
const logStream = await auditLogStreamDAL.findById(id);
|
const logStream = await auditLogStreamDAL.findById(id);
|
||||||
if (!logStream) throw new BadRequestError({ message: "Audit log stream not found" });
|
if (!logStream) throw new BadRequestError({ message: "Audit log stream not found" });
|
||||||
|
|
||||||
const { projectId } = logStream;
|
const { orgId } = logStream;
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||||
actor,
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Settings);
|
||||||
actorId,
|
|
||||||
projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId
|
|
||||||
);
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Settings);
|
|
||||||
|
|
||||||
const deletedLogStream = await auditLogStreamDAL.deleteById(id);
|
const deletedLogStream = await auditLogStreamDAL.deleteById(id);
|
||||||
return deletedLogStream;
|
return deletedLogStream;
|
||||||
@@ -155,15 +133,10 @@ export const auditLogStreamServiceFactory = ({
|
|||||||
const logStream = await auditLogStreamDAL.findById(id);
|
const logStream = await auditLogStreamDAL.findById(id);
|
||||||
if (!logStream) throw new BadRequestError({ message: "Audit log stream not found" });
|
if (!logStream) throw new BadRequestError({ message: "Audit log stream not found" });
|
||||||
|
|
||||||
const { projectId } = logStream;
|
const { orgId } = logStream;
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||||
actor,
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Settings);
|
||||||
actorId,
|
|
||||||
projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId
|
|
||||||
);
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Settings);
|
|
||||||
const token =
|
const token =
|
||||||
logStream?.encryptedTokenCiphertext && logStream?.encryptedTokenIV && logStream?.encryptedTokenTag
|
logStream?.encryptedTokenCiphertext && logStream?.encryptedTokenIV && logStream?.encryptedTokenTag
|
||||||
? infisicalSymmetricDecrypt({
|
? infisicalSymmetricDecrypt({
|
||||||
@@ -177,21 +150,17 @@ export const auditLogStreamServiceFactory = ({
|
|||||||
return { ...logStream, token };
|
return { ...logStream, token };
|
||||||
};
|
};
|
||||||
|
|
||||||
const list = async ({ projectSlug, actor, actorId, actorOrgId, actorAuthMethod }: TListAuditLogStreamDTO) => {
|
const list = async ({ actor, actorId, actorOrgId, actorAuthMethod }: TListAuditLogStreamDTO) => {
|
||||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
if (!project) throw new BadRequestError({ message: "Project not found" });
|
|
||||||
const projectId = project.id;
|
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Settings);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Settings);
|
||||||
|
|
||||||
const logStreams = await auditLogStreamDAL.find({ projectId });
|
const logStreams = await auditLogStreamDAL.find({ orgId: actorOrgId });
|
||||||
return logStreams;
|
return logStreams;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,22 @@
|
|||||||
import { TProjectPermission } from "@app/lib/types";
|
import { TOrgPermission } from "@app/lib/types";
|
||||||
|
|
||||||
export type TCreateAuditLogStreamDTO = Omit<TProjectPermission, "projectId"> & {
|
export type TCreateAuditLogStreamDTO = Omit<TOrgPermission, "orgId"> & {
|
||||||
projectSlug: string;
|
|
||||||
url: string;
|
url: string;
|
||||||
token?: string;
|
token?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TUpdateAuditLogStreamDTO = Omit<TProjectPermission, "projectId"> & {
|
export type TUpdateAuditLogStreamDTO = Omit<TOrgPermission, "orgId"> & {
|
||||||
id: string;
|
id: string;
|
||||||
url?: string;
|
url?: string;
|
||||||
token?: string;
|
token?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TDeleteAuditLogStreamDTO = Omit<TProjectPermission, "projectId"> & {
|
export type TDeleteAuditLogStreamDTO = Omit<TOrgPermission, "orgId"> & {
|
||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TListAuditLogStreamDTO = Omit<TProjectPermission, "projectId"> & {
|
export type TListAuditLogStreamDTO = Omit<TOrgPermission, "orgId">;
|
||||||
projectSlug: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TGetDetailsAuditLogStreamDTO = Omit<TProjectPermission, "projectId"> & {
|
export type TGetDetailsAuditLogStreamDTO = Omit<TOrgPermission, "orgId"> & {
|
||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ export const auditLogQueueServiceFactory = ({
|
|||||||
userAgentType
|
userAgentType
|
||||||
});
|
});
|
||||||
|
|
||||||
const logStreams = projectId ? await auditLogStreamDAL.find({ projectId }) : [];
|
const logStreams = orgId ? await auditLogStreamDAL.find({ orgId }) : [];
|
||||||
await Promise.allSettled(
|
await Promise.allSettled(
|
||||||
logStreams.map(
|
logStreams.map(
|
||||||
async ({ url, encryptedTokenTag, encryptedTokenIV, encryptedTokenKeyEncoding, encryptedTokenCiphertext }) => {
|
async ({ url, encryptedTokenTag, encryptedTokenIV, encryptedTokenKeyEncoding, encryptedTokenCiphertext }) => {
|
||||||
|
|||||||
@@ -617,7 +617,6 @@ export const INTEGRATION = {
|
|||||||
|
|
||||||
export const AUDIT_LOG_STREAMS = {
|
export const AUDIT_LOG_STREAMS = {
|
||||||
CREATE: {
|
CREATE: {
|
||||||
projectSlug: "The slug of the project to create audit log stream.",
|
|
||||||
url: "The socket URL to push logs to.",
|
url: "The socket URL to push logs to.",
|
||||||
token: "Authentication token for the external provider used for identification."
|
token: "Authentication token for the external provider used for identification."
|
||||||
},
|
},
|
||||||
@@ -629,9 +628,6 @@ export const AUDIT_LOG_STREAMS = {
|
|||||||
DELETE: {
|
DELETE: {
|
||||||
id: "The ID of the audit log stream to delete."
|
id: "The ID of the audit log stream to delete."
|
||||||
},
|
},
|
||||||
LIST: {
|
|
||||||
projectSlug: "The slug of the project to list audit log streams."
|
|
||||||
},
|
|
||||||
GET_BY_ID: {
|
GET_BY_ID: {
|
||||||
id: "The ID of the audit log stream to get details."
|
id: "The ID of the audit log stream to get details."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export type TOrgPermission = {
|
|||||||
actorId: string;
|
actorId: string;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
actorAuthMethod: ActorAuthMethod;
|
actorAuthMethod: ActorAuthMethod;
|
||||||
actorOrgId: string | undefined;
|
actorOrgId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TProjectPermission = {
|
export type TProjectPermission = {
|
||||||
|
|||||||
@@ -251,7 +251,6 @@ export const registerRoutes = async (
|
|||||||
});
|
});
|
||||||
const auditLogService = auditLogServiceFactory({ auditLogDAL, permissionService, auditLogQueue });
|
const auditLogService = auditLogServiceFactory({ auditLogDAL, permissionService, auditLogQueue });
|
||||||
const auditLogStreamService = auditLogStreamServiceFactory({
|
const auditLogStreamService = auditLogStreamServiceFactory({
|
||||||
projectDAL,
|
|
||||||
licenseService,
|
licenseService,
|
||||||
permissionService,
|
permissionService,
|
||||||
auditLogStreamDAL
|
auditLogStreamDAL
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ export const useCreateAuditLogStream = () => {
|
|||||||
);
|
);
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
onSuccess: (_, { projectSlug }) => {
|
onSuccess: (_, { orgId }) => {
|
||||||
queryClient.invalidateQueries(auditLogStreamKeys.list(projectSlug));
|
queryClient.invalidateQueries(auditLogStreamKeys.list(orgId));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -38,8 +38,8 @@ export const useUpdateAuditLogStream = () => {
|
|||||||
);
|
);
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
onSuccess: (_, { projectSlug }) => {
|
onSuccess: (_, { orgId }) => {
|
||||||
queryClient.invalidateQueries(auditLogStreamKeys.list(projectSlug));
|
queryClient.invalidateQueries(auditLogStreamKeys.list(orgId));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -54,8 +54,8 @@ export const useDeleteAuditLogStream = () => {
|
|||||||
);
|
);
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
onSuccess: (_, { projectSlug }) => {
|
onSuccess: (_, { orgId }) => {
|
||||||
queryClient.invalidateQueries(auditLogStreamKeys.list(projectSlug));
|
queryClient.invalidateQueries(auditLogStreamKeys.list(orgId));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,28 +5,23 @@ import { apiRequest } from "@app/config/request";
|
|||||||
import { TAuditLogStream } from "./types";
|
import { TAuditLogStream } from "./types";
|
||||||
|
|
||||||
export const auditLogStreamKeys = {
|
export const auditLogStreamKeys = {
|
||||||
list: (projectSlug: string) => ["audit-log-stream", { projectSlug }],
|
list: (orgId: string) => ["audit-log-stream", { orgId }],
|
||||||
getById: (id: string) => ["audit-log-stream-details", { id }]
|
getById: (id: string) => ["audit-log-stream-details", { id }]
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchAuditLogStreams = async (projectSlug: string) => {
|
const fetchAuditLogStreams = async () => {
|
||||||
const { data } = await apiRequest.get<{ auditLogStreams: TAuditLogStream[] }>(
|
const { data } = await apiRequest.get<{ auditLogStreams: TAuditLogStream[] }>(
|
||||||
"/api/v1/audit-log-streams",
|
"/api/v1/audit-log-streams"
|
||||||
{
|
|
||||||
params: {
|
|
||||||
projectSlug
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return data.auditLogStreams;
|
return data.auditLogStreams;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGetAuditLogStreams = (projectSlug: string) =>
|
export const useGetAuditLogStreams = (orgId: string) =>
|
||||||
useQuery({
|
useQuery({
|
||||||
queryKey: auditLogStreamKeys.list(projectSlug),
|
queryKey: auditLogStreamKeys.list(orgId),
|
||||||
queryFn: () => fetchAuditLogStreams(projectSlug),
|
queryFn: () => fetchAuditLogStreams(),
|
||||||
enabled: Boolean(projectSlug)
|
enabled: Boolean(orgId)
|
||||||
});
|
});
|
||||||
|
|
||||||
const fetchAuditLogStreamDetails = async (id: string) => {
|
const fetchAuditLogStreamDetails = async (id: string) => {
|
||||||
|
|||||||
@@ -5,19 +5,19 @@ export type TAuditLogStream = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type TCreateAuditLogStreamDTO = {
|
export type TCreateAuditLogStreamDTO = {
|
||||||
projectSlug: string;
|
|
||||||
url: string;
|
url: string;
|
||||||
token?: string;
|
token?: string;
|
||||||
|
orgId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TUpdateAuditLogStreamDTO = {
|
export type TUpdateAuditLogStreamDTO = {
|
||||||
id: string;
|
id: string;
|
||||||
projectSlug: string;
|
|
||||||
url?: string;
|
url?: string;
|
||||||
token?: string;
|
token?: string;
|
||||||
|
orgId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TDeleteAuditLogStreamDTO = {
|
export type TDeleteAuditLogStreamDTO = {
|
||||||
id: string;
|
id: string;
|
||||||
projectSlug: string;
|
orgId: string;
|
||||||
};
|
};
|
||||||
|
|||||||
+5
-5
@@ -3,7 +3,7 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, FormControl, Input, Spinner } from "@app/components/v2";
|
import { Button, FormControl, Input, Spinner } from "@app/components/v2";
|
||||||
import { useWorkspace } from "@app/context";
|
import { useOrganization } from "@app/context";
|
||||||
import {
|
import {
|
||||||
useCreateAuditLogStream,
|
useCreateAuditLogStream,
|
||||||
useGetAuditLogStreamDetails,
|
useGetAuditLogStreamDetails,
|
||||||
@@ -23,8 +23,8 @@ type TForm = z.infer<typeof formSchema>;
|
|||||||
|
|
||||||
export const AuditLogStreamForm = ({ id = "", onClose }: Props) => {
|
export const AuditLogStreamForm = ({ id = "", onClose }: Props) => {
|
||||||
const isEdit = Boolean(id);
|
const isEdit = Boolean(id);
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentOrg } = useOrganization();
|
||||||
const projectSlug = currentWorkspace?.slug || "";
|
const orgId = currentOrg?.id || "";
|
||||||
|
|
||||||
const auditLogStream = useGetAuditLogStreamDetails(id);
|
const auditLogStream = useGetAuditLogStreamDetails(id);
|
||||||
const createAuditLogStream = useCreateAuditLogStream();
|
const createAuditLogStream = useCreateAuditLogStream();
|
||||||
@@ -43,7 +43,7 @@ export const AuditLogStreamForm = ({ id = "", onClose }: Props) => {
|
|||||||
try {
|
try {
|
||||||
await updateAuditLogStream.mutateAsync({
|
await updateAuditLogStream.mutateAsync({
|
||||||
id,
|
id,
|
||||||
projectSlug,
|
orgId,
|
||||||
token,
|
token,
|
||||||
url
|
url
|
||||||
});
|
});
|
||||||
@@ -69,7 +69,7 @@ export const AuditLogStreamForm = ({ id = "", onClose }: Props) => {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await createAuditLogStream.mutateAsync({
|
await createAuditLogStream.mutateAsync({
|
||||||
projectSlug,
|
orgId,
|
||||||
token,
|
token,
|
||||||
url
|
url
|
||||||
});
|
});
|
||||||
+22
-25
@@ -2,7 +2,7 @@ import { faPlug, faPlus } from "@fortawesome/free-solid-svg-icons";
|
|||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
import { OrgPermissionCan } from "@app/components/permissions";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
DeleteActionModal,
|
DeleteActionModal,
|
||||||
@@ -19,21 +19,21 @@ import {
|
|||||||
UpgradePlanModal
|
UpgradePlanModal
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import {
|
import {
|
||||||
ProjectPermissionActions,
|
OrgPermissionActions,
|
||||||
ProjectPermissionSub,
|
OrgPermissionSubjects,
|
||||||
useSubscription,
|
useOrganization,
|
||||||
useWorkspace
|
useSubscription
|
||||||
} from "@app/context";
|
} from "@app/context";
|
||||||
import { withProjectPermission } from "@app/hoc";
|
import { withPermission } from "@app/hoc";
|
||||||
import { usePopUp } from "@app/hooks";
|
import { usePopUp } from "@app/hooks";
|
||||||
import { useDeleteAuditLogStream, useGetAuditLogStreams } from "@app/hooks/api";
|
import { useDeleteAuditLogStream, useGetAuditLogStreams } from "@app/hooks/api";
|
||||||
|
|
||||||
import { AuditLogStreamForm } from "./AuditLogStreamForm";
|
import { AuditLogStreamForm } from "./AuditLogStreamForm";
|
||||||
|
|
||||||
export const AuditLogStreamsTab = withProjectPermission(
|
export const AuditLogStreamsTab = withPermission(
|
||||||
() => {
|
() => {
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentOrg } = useOrganization();
|
||||||
const projectSlug = currentWorkspace?.slug || "";
|
const orgId = currentOrg?.id || "";
|
||||||
const { popUp, handlePopUpOpen, handlePopUpToggle, handlePopUpClose } = usePopUp([
|
const { popUp, handlePopUpOpen, handlePopUpToggle, handlePopUpClose } = usePopUp([
|
||||||
"auditLogStreamForm",
|
"auditLogStreamForm",
|
||||||
"deleteAuditLogStream",
|
"deleteAuditLogStream",
|
||||||
@@ -42,7 +42,7 @@ export const AuditLogStreamsTab = withProjectPermission(
|
|||||||
const { subscription } = useSubscription();
|
const { subscription } = useSubscription();
|
||||||
|
|
||||||
const { data: auditLogStreams, isLoading: isAuditLogStreamsLoading } =
|
const { data: auditLogStreams, isLoading: isAuditLogStreamsLoading } =
|
||||||
useGetAuditLogStreams(projectSlug);
|
useGetAuditLogStreams(orgId);
|
||||||
|
|
||||||
// mutation
|
// mutation
|
||||||
const { mutateAsync: deleteAuditLogStream } = useDeleteAuditLogStream();
|
const { mutateAsync: deleteAuditLogStream } = useDeleteAuditLogStream();
|
||||||
@@ -52,7 +52,7 @@ export const AuditLogStreamsTab = withProjectPermission(
|
|||||||
const auditLogStreamId = popUp?.deleteAuditLogStream?.data as string;
|
const auditLogStreamId = popUp?.deleteAuditLogStream?.data as string;
|
||||||
await deleteAuditLogStream({
|
await deleteAuditLogStream({
|
||||||
id: auditLogStreamId,
|
id: auditLogStreamId,
|
||||||
projectSlug
|
orgId
|
||||||
});
|
});
|
||||||
handlePopUpClose("deleteAuditLogStream");
|
handlePopUpClose("deleteAuditLogStream");
|
||||||
createNotification({
|
createNotification({
|
||||||
@@ -72,10 +72,7 @@ export const AuditLogStreamsTab = withProjectPermission(
|
|||||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<p className="text-xl font-semibold text-mineshaft-100">Audit Log Streams</p>
|
<p className="text-xl font-semibold text-mineshaft-100">Audit Log Streams</p>
|
||||||
<ProjectPermissionCan
|
<OrgPermissionCan I={OrgPermissionActions.Create} a={OrgPermissionSubjects.Settings}>
|
||||||
I={ProjectPermissionActions.Create}
|
|
||||||
a={ProjectPermissionSub.Settings}
|
|
||||||
>
|
|
||||||
{(isAllowed) => (
|
{(isAllowed) => (
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -91,7 +88,7 @@ export const AuditLogStreamsTab = withProjectPermission(
|
|||||||
Create
|
Create
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</ProjectPermissionCan>
|
</OrgPermissionCan>
|
||||||
</div>
|
</div>
|
||||||
<p className="mb-8 text-gray-400">
|
<p className="mb-8 text-gray-400">
|
||||||
Manage audit log streams to send audit log to any logging providers with syslog support.
|
Manage audit log streams to send audit log to any logging providers with syslog support.
|
||||||
@@ -124,9 +121,9 @@ export const AuditLogStreamsTab = withProjectPermission(
|
|||||||
</Td>
|
</Td>
|
||||||
<Td>
|
<Td>
|
||||||
<div className="flex items-center justify-end space-x-2">
|
<div className="flex items-center justify-end space-x-2">
|
||||||
<ProjectPermissionCan
|
<OrgPermissionCan
|
||||||
I={ProjectPermissionActions.Edit}
|
I={OrgPermissionActions.Edit}
|
||||||
a={ProjectPermissionSub.Settings}
|
a={OrgPermissionSubjects.Settings}
|
||||||
>
|
>
|
||||||
{(isAllowed) => (
|
{(isAllowed) => (
|
||||||
<Button
|
<Button
|
||||||
@@ -138,10 +135,10 @@ export const AuditLogStreamsTab = withProjectPermission(
|
|||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</ProjectPermissionCan>
|
</OrgPermissionCan>
|
||||||
<ProjectPermissionCan
|
<OrgPermissionCan
|
||||||
I={ProjectPermissionActions.Delete}
|
I={OrgPermissionActions.Delete}
|
||||||
a={ProjectPermissionSub.Settings}
|
a={OrgPermissionSubjects.Settings}
|
||||||
>
|
>
|
||||||
{(isAllowed) => (
|
{(isAllowed) => (
|
||||||
<Button
|
<Button
|
||||||
@@ -155,7 +152,7 @@ export const AuditLogStreamsTab = withProjectPermission(
|
|||||||
Delete
|
Delete
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</ProjectPermissionCan>
|
</OrgPermissionCan>
|
||||||
</div>
|
</div>
|
||||||
</Td>
|
</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
@@ -196,5 +193,5 @@ export const AuditLogStreamsTab = withProjectPermission(
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
{ action: ProjectPermissionActions.Read, subject: ProjectPermissionSub.Settings }
|
{ action: OrgPermissionActions.Read, subject: OrgPermissionSubjects.Settings }
|
||||||
);
|
);
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
import { Fragment } from "react";
|
import { Fragment } from "react";
|
||||||
import { Tab } from "@headlessui/react";
|
import { Tab } from "@headlessui/react";
|
||||||
|
|
||||||
|
import { AuditLogStreamsTab } from "../AuditLogStreamTab";
|
||||||
import { OrgAuthTab } from "../OrgAuthTab";
|
import { OrgAuthTab } from "../OrgAuthTab";
|
||||||
import { OrgGeneralTab } from "../OrgGeneralTab";
|
import { OrgGeneralTab } from "../OrgGeneralTab";
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{ name: "General", key: "tab-org-general" },
|
{ name: "General", key: "tab-org-general" },
|
||||||
{ name: "Security", key: "tab-org-security" }
|
{ name: "Security", key: "tab-org-security" },
|
||||||
|
{ name: "Audit Log Streams", key: "tag-audit-log-streams" }
|
||||||
];
|
];
|
||||||
export const OrgTabGroup = () => {
|
export const OrgTabGroup = () => {
|
||||||
return (
|
return (
|
||||||
@@ -17,9 +19,8 @@ export const OrgTabGroup = () => {
|
|||||||
{({ selected }) => (
|
{({ selected }) => (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={`w-30 mx-2 mr-4 py-2 text-sm font-medium outline-none ${
|
className={`w-30 mx-2 mr-4 py-2 text-sm font-medium outline-none ${selected ? "border-b border-white text-white" : "text-mineshaft-400"
|
||||||
selected ? "border-b border-white text-white" : "text-mineshaft-400"
|
}`}
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
{tab.name}
|
{tab.name}
|
||||||
</button>
|
</button>
|
||||||
@@ -34,6 +35,9 @@ export const OrgTabGroup = () => {
|
|||||||
<Tab.Panel>
|
<Tab.Panel>
|
||||||
<OrgAuthTab />
|
<OrgAuthTab />
|
||||||
</Tab.Panel>
|
</Tab.Panel>
|
||||||
|
<Tab.Panel>
|
||||||
|
<AuditLogStreamsTab />
|
||||||
|
</Tab.Panel>
|
||||||
</Tab.Panels>
|
</Tab.Panels>
|
||||||
</Tab.Group>
|
</Tab.Group>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,12 +4,10 @@ import { Tab } from "@headlessui/react";
|
|||||||
|
|
||||||
import { ProjectGeneralTab } from "./components/ProjectGeneralTab";
|
import { ProjectGeneralTab } from "./components/ProjectGeneralTab";
|
||||||
import { WebhooksTab } from "./components/WebhooksTab";
|
import { WebhooksTab } from "./components/WebhooksTab";
|
||||||
import { AuditLogStreamsTab } from "./components";
|
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{ name: "General", key: "tab-project-general" },
|
{ name: "General", key: "tab-project-general" },
|
||||||
{ name: "Webhooks", key: "tab-project-webhooks" },
|
{ name: "Webhooks", key: "tab-project-webhooks" },
|
||||||
{ name: "Audit Log Streams", key: "tab-project-audit-log-stream" }
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export const ProjectSettingsPage = () => {
|
export const ProjectSettingsPage = () => {
|
||||||
@@ -43,9 +41,6 @@ export const ProjectSettingsPage = () => {
|
|||||||
<Tab.Panel>
|
<Tab.Panel>
|
||||||
<WebhooksTab />
|
<WebhooksTab />
|
||||||
</Tab.Panel>
|
</Tab.Panel>
|
||||||
<Tab.Panel>
|
|
||||||
<AuditLogStreamsTab />
|
|
||||||
</Tab.Panel>
|
|
||||||
</Tab.Panels>
|
</Tab.Panels>
|
||||||
</Tab.Group>
|
</Tab.Group>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
export { AuditLogStreamsTab } from "./AuditLogStreamTab";
|
|
||||||
export { AutoCapitalizationSection } from "./AutoCapitalizationSection";
|
export { AutoCapitalizationSection } from "./AutoCapitalizationSection";
|
||||||
export { DeleteProjectSection } from "./DeleteProjectSection";
|
export { DeleteProjectSection } from "./DeleteProjectSection";
|
||||||
export { E2EESection } from "./E2EESection";
|
export { E2EESection } from "./E2EESection";
|
||||||
|
|||||||
Reference in New Issue
Block a user