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

View File

@@ -104,9 +104,9 @@ export const changeMembershipRole = async (req: Request, res: Response) => {
}
// validate target membership
const membershipToChangeRole = await findMembership({
_id: membershipId
});
const membershipToChangeRole = await Membership
.findById(membershipId)
.populate<{ user: IUser }>("user");
if (!membershipToChangeRole) {
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
throw new Error("Insufficient role for changing member roles");
}
const oldRole = membershipToChangeRole.role;
membershipToChangeRole.role = role;
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({
membership: membershipToChangeRole

View File

@@ -1,10 +1,12 @@
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 { BadRequestError, UnauthorizedRequestError } from "../../../utils/errors";
import { ADMIN, MEMBER } from "../../../variables/organization";
import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from "../../../variables";
import _ from "lodash";
import { EEAuditLogService } from "../../services";
export const denyMembershipPermissions = async (req: Request, res: Response) => {
const { membershipId } = req.params;
@@ -51,12 +53,33 @@ export const denyMembershipPermissions = async (req: Request, res: Response) =>
{ _id: membershipToModify._id },
{ $set: { deniedPermissions: sanitizedMembershipPermissionsUnique } },
{ new: true }
)
).populate<{ user: IUser }>("user");
if (!updatedMembershipWithPermissions) {
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({
permissionsDenied: updatedMembershipWithPermissions.deniedPermissions,
})

View File

@@ -42,4 +42,6 @@ export enum EventType {
CREATE_SECRET_IMPORT = "create-secret-import",
UPDATE_SECRET_IMPORT = "update-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"
}

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 =
| GetSecretsEvent
| GetSecretEvent
@@ -376,4 +398,6 @@ export type Event =
| GetSecretImportsEvent
| CreateSecretImportEvent
| UpdateSecretImportEvent
| DeleteSecretImportEvent;
| DeleteSecretImportEvent
| UpdateUserRole
| UpdateUserDeniedPermissions;

View File

@@ -26,9 +26,9 @@ const updateUserProjectPermission = async ({
permissions: denials
})
}).then(async (res) => {
console.log({
permissions: denials
}, res)
// console.log({
// permissions: denials
// }, res)
if (res && res.status === 200) {
return res.json();
}

View File

@@ -31,7 +31,8 @@ export const eventToNameMap: { [K in EventType]: string } = {
[EventType.CREATE_SECRET_IMPORT]: "Create secret import",
[EventType.UPDATE_SECRET_IMPORT]: "Update 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 } = {

View File

@@ -40,5 +40,7 @@ export enum EventType {
GET_SECRET_IMPORTS = "get-secret-imports",
CREATE_SECRET_IMPORT = "create-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"
}

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 =
| GetSecretsEvent
| GetSecretEvent
@@ -378,7 +401,9 @@ export type Event =
| GetSecretImportsEvent
| CreateSecretImportEvent
| UpdateSecretImportEvent
| DeleteSecretImportEvent;
| DeleteSecretImportEvent
| UpdateUserRole
| UpdateUserDeniedPermissions;
export type AuditLog = {
_id: string;

View File

@@ -279,6 +279,27 @@ export const LogsTableRow = ({
<p>{`Import to path: ${event.metadata.importToSecretPath}`}</p>
</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:
return (
<Td />