Merge pull request #839 from Infisical/permissions-audit-log

Add audit logs for workspace user role and read/write permission changes
This commit is contained in:
BlackMagiq
2023-08-09 22:02:48 +07:00
committed by GitHub
9 changed files with 128 additions and 12 deletions
@@ -104,9 +104,9 @@ export const changeMembershipRole = async (req: Request, res: Response) => {
} }
// validate target membership // validate target membership
const membershipToChangeRole = await findMembership({ const membershipToChangeRole = await Membership
_id: membershipId .findById(membershipId)
}); .populate<{ user: IUser }>("user");
if (!membershipToChangeRole) { if (!membershipToChangeRole) {
throw new Error("Failed to find membership to change role"); throw new Error("Failed to find membership to change role");
@@ -127,9 +127,27 @@ export const changeMembershipRole = async (req: Request, res: Response) => {
// user is not an admin member of the workspace // user is not an admin member of the workspace
throw new Error("Insufficient role for changing member roles"); throw new Error("Insufficient role for changing member roles");
} }
const oldRole = membershipToChangeRole.role;
membershipToChangeRole.role = role; membershipToChangeRole.role = role;
await membershipToChangeRole.save(); await membershipToChangeRole.save();
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.UPDATE_USER_WORKSPACE_ROLE,
metadata: {
userId: membershipToChangeRole.user._id.toString(),
email: membershipToChangeRole.user.email,
oldRole,
newRole: membershipToChangeRole.role
}
},
{
workspaceId: membershipToChangeRole.workspace
}
);
return res.status(200).send({ return res.status(200).send({
membership: membershipToChangeRole membership: membershipToChangeRole
@@ -1,10 +1,12 @@
import { Request, Response } from "express"; import { Request, Response } from "express";
import { Membership, Workspace } from "../../../models"; import { IUser, Membership, Workspace } from "../../../models";
import { EventType } from "../../../ee/models";
import { IMembershipPermission } from "../../../models/membership"; import { IMembershipPermission } from "../../../models/membership";
import { BadRequestError, UnauthorizedRequestError } from "../../../utils/errors"; import { BadRequestError, UnauthorizedRequestError } from "../../../utils/errors";
import { ADMIN, MEMBER } from "../../../variables/organization"; import { ADMIN, MEMBER } from "../../../variables/organization";
import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from "../../../variables"; import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from "../../../variables";
import _ from "lodash"; import _ from "lodash";
import { EEAuditLogService } from "../../services";
export const denyMembershipPermissions = async (req: Request, res: Response) => { export const denyMembershipPermissions = async (req: Request, res: Response) => {
const { membershipId } = req.params; const { membershipId } = req.params;
@@ -51,12 +53,33 @@ export const denyMembershipPermissions = async (req: Request, res: Response) =>
{ _id: membershipToModify._id }, { _id: membershipToModify._id },
{ $set: { deniedPermissions: sanitizedMembershipPermissionsUnique } }, { $set: { deniedPermissions: sanitizedMembershipPermissionsUnique } },
{ new: true } { new: true }
) ).populate<{ user: IUser }>("user");
if (!updatedMembershipWithPermissions) { if (!updatedMembershipWithPermissions) {
throw BadRequestError({ message: "The resource has been removed before it can be modified" }) throw BadRequestError({ message: "The resource has been removed before it can be modified" })
} }
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS,
metadata: {
userId: updatedMembershipWithPermissions.user._id.toString(),
email: updatedMembershipWithPermissions.user.email,
deniedPermissions: updatedMembershipWithPermissions.deniedPermissions.map(({
environmentSlug,
ability
}) => ({
environmentSlug,
ability
}))
}
},
{
workspaceId: updatedMembershipWithPermissions.workspace
}
);
res.send({ res.send({
permissionsDenied: updatedMembershipWithPermissions.deniedPermissions, permissionsDenied: updatedMembershipWithPermissions.deniedPermissions,
}) })
+2
View File
@@ -42,4 +42,6 @@ export enum EventType {
CREATE_SECRET_IMPORT = "create-secret-import", CREATE_SECRET_IMPORT = "create-secret-import",
UPDATE_SECRET_IMPORT = "update-secret-import", UPDATE_SECRET_IMPORT = "update-secret-import",
DELETE_SECRET_IMPORT = "delete-secret-import", DELETE_SECRET_IMPORT = "delete-secret-import",
UPDATE_USER_WORKSPACE_ROLE = "update-user-workspace-role",
UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS = "update-user-workspace-denied-permissions"
} }
+25 -1
View File
@@ -346,6 +346,28 @@ interface DeleteSecretImportEvent {
} }
} }
interface UpdateUserRole {
type: EventType.UPDATE_USER_WORKSPACE_ROLE,
metadata: {
userId: string;
email: string;
oldRole: string;
newRole: string;
}
}
interface UpdateUserDeniedPermissions {
type: EventType.UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS,
metadata: {
userId: string;
email: string;
deniedPermissions: {
environmentSlug: string;
ability: string;
}[]
}
}
export type Event = export type Event =
| GetSecretsEvent | GetSecretsEvent
| GetSecretEvent | GetSecretEvent
@@ -376,4 +398,6 @@ export type Event =
| GetSecretImportsEvent | GetSecretImportsEvent
| CreateSecretImportEvent | CreateSecretImportEvent
| UpdateSecretImportEvent | UpdateSecretImportEvent
| DeleteSecretImportEvent; | DeleteSecretImportEvent
| UpdateUserRole
| UpdateUserDeniedPermissions;
@@ -26,9 +26,9 @@ const updateUserProjectPermission = async ({
permissions: denials permissions: denials
}) })
}).then(async (res) => { }).then(async (res) => {
console.log({ // console.log({
permissions: denials // permissions: denials
}, res) // }, res)
if (res && res.status === 200) { if (res && res.status === 200) {
return res.json(); return res.json();
} }
@@ -31,7 +31,8 @@ export const eventToNameMap: { [K in EventType]: string } = {
[EventType.CREATE_SECRET_IMPORT]: "Create secret import", [EventType.CREATE_SECRET_IMPORT]: "Create secret import",
[EventType.UPDATE_SECRET_IMPORT]: "Update secret import", [EventType.UPDATE_SECRET_IMPORT]: "Update secret import",
[EventType.DELETE_SECRET_IMPORT]: "Delete secret import", [EventType.DELETE_SECRET_IMPORT]: "Delete secret import",
[EventType.UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS]: "Update denied permissions",
[EventType.UPDATE_USER_WORKSPACE_ROLE]: "Update user role"
}; };
export const userAgentTTypeoNameMap: { [K in UserAgentType]: string } = { export const userAgentTTypeoNameMap: { [K in UserAgentType]: string } = {
+3 -1
View File
@@ -40,5 +40,7 @@ export enum EventType {
GET_SECRET_IMPORTS = "get-secret-imports", GET_SECRET_IMPORTS = "get-secret-imports",
CREATE_SECRET_IMPORT = "create-secret-import", CREATE_SECRET_IMPORT = "create-secret-import",
UPDATE_SECRET_IMPORT = "update-secret-import", UPDATE_SECRET_IMPORT = "update-secret-import",
DELETE_SECRET_IMPORT = "delete-secret-import" DELETE_SECRET_IMPORT = "delete-secret-import",
UPDATE_USER_WORKSPACE_ROLE = "update-user-workspace-role",
UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS = "update-user-workspace-denied-permissions"
} }
+26 -1
View File
@@ -348,6 +348,29 @@ interface DeleteSecretImportEvent {
} }
} }
interface UpdateUserRole {
type: EventType.UPDATE_USER_WORKSPACE_ROLE,
metadata: {
userId: string;
email: string;
oldRole: string;
newRole: string;
}
}
interface UpdateUserDeniedPermissions {
type: EventType.UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS,
metadata: {
userId: string;
email: string;
deniedPermissions: {
environmentSlug: string;
ability: string;
}[]
}
}
export type Event = export type Event =
| GetSecretsEvent | GetSecretsEvent
| GetSecretEvent | GetSecretEvent
@@ -378,7 +401,9 @@ export type Event =
| GetSecretImportsEvent | GetSecretImportsEvent
| CreateSecretImportEvent | CreateSecretImportEvent
| UpdateSecretImportEvent | UpdateSecretImportEvent
| DeleteSecretImportEvent; | DeleteSecretImportEvent
| UpdateUserRole
| UpdateUserDeniedPermissions;
export type AuditLog = { export type AuditLog = {
_id: string; _id: string;
@@ -279,6 +279,27 @@ export const LogsTableRow = ({
<p>{`Import to path: ${event.metadata.importToSecretPath}`}</p> <p>{`Import to path: ${event.metadata.importToSecretPath}`}</p>
</Td> </Td>
); );
case EventType.UPDATE_USER_WORKSPACE_ROLE:
return (
<Td>
<p>{`Email: ${event.metadata.email}`}</p>
<p>{`Old role: ${event.metadata.oldRole}`}</p>
<p>{`New role: ${event.metadata.newRole}`}</p>
</Td>
);
case EventType.UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS:
return (
<Td>
<p>{`Email: ${event.metadata.email}`}</p>
{event.metadata.deniedPermissions.map((permission) => {
return (
<p key={`audit-log-denied-permission-${event.metadata.userId}-${permission.environmentSlug}-${permission.ability}`}>
{`Denied env-ability: ${permission.environmentSlug}-${permission.ability}`}
</p>
);
})}
</Td>
);
default: default:
return ( return (
<Td /> <Td />