misc: added trigger for privilege upgrade

This commit is contained in:
Sheen Capadngan
2025-03-13 22:21:44 +08:00
parent cc2c4b16bf
commit fc651f6645
13 changed files with 417 additions and 13 deletions
@@ -0,0 +1,30 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasColumn(TableName.Organization, "shouldUseNewPrivilegeSystem"))) {
await knex.schema.alterTable(TableName.Organization, (t) => {
t.boolean("shouldUseNewPrivilegeSystem");
t.string("privilegeUpgradeInitiatedByUsername");
t.dateTime("privilegeUpgradeInitiatedAt");
});
await knex(TableName.Organization).update({
shouldUseNewPrivilegeSystem: false
});
await knex.schema.alterTable(TableName.Organization, (t) => {
t.boolean("shouldUseNewPrivilegeSystem").defaultTo(true).notNullable().alter();
});
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasColumn(TableName.Organization, "shouldUseNewPrivilegeSystem")) {
await knex.schema.alterTable(TableName.Organization, (t) => {
t.dropColumn("shouldUseNewPrivilegeSystem");
t.dropColumn("privilegeUpgradeInitiatedByUsername");
t.dropColumn("privilegeUpgradeInitiatedAt");
});
}
}
+4 -1
View File
@@ -22,7 +22,10 @@ export const OrganizationsSchema = z.object({
kmsEncryptedDataKey: zodBuffer.nullable().optional(),
defaultMembershipRole: z.string().default("member"),
enforceMfa: z.boolean().default(false),
selectedMfaMethod: z.string().nullable().optional()
selectedMfaMethod: z.string().nullable().optional(),
shouldUseNewPrivilegeSystem: z.boolean().default(true),
privilegeUpgradeInitiatedByUsername: z.string().nullable().optional(),
privilegeUpgradeInitiatedAt: z.date().nullable().optional()
});
export type TOrganizations = z.infer<typeof OrganizationsSchema>;
@@ -4,7 +4,6 @@ import {
AuditLogsSchema,
GroupsSchema,
IncidentContactsSchema,
OrganizationsSchema,
OrgMembershipsSchema,
OrgRolesSchema,
UsersSchema
@@ -57,7 +56,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
}),
response: {
200: z.object({
organization: OrganizationsSchema
organization: sanitizedOrganizationSchema
})
}
},
@@ -262,7 +261,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
response: {
200: z.object({
message: z.string(),
organization: OrganizationsSchema
organization: sanitizedOrganizationSchema
})
}
},
@@ -1,7 +1,6 @@
import { z } from "zod";
import {
OrganizationsSchema,
OrgMembershipsSchema,
ProjectMembershipsSchema,
ProjectsSchema,
@@ -15,6 +14,7 @@ import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { GenericResourceNameSchema } from "@app/server/lib/schemas";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
import { sanitizedOrganizationSchema } from "@app/services/org/org-schema";
export const registerOrgRouter = async (server: FastifyZodProvider) => {
server.route({
@@ -335,7 +335,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
}),
response: {
200: z.object({
organization: OrganizationsSchema
organization: sanitizedOrganizationSchema
})
}
},
@@ -365,7 +365,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
}),
response: {
200: z.object({
organization: OrganizationsSchema,
organization: sanitizedOrganizationSchema,
accessToken: z.string()
})
}
@@ -396,4 +396,30 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
return { organization, accessToken: tokens.accessToken };
}
});
server.route({
method: "POST",
url: "/privilege-system-upgrade",
config: {
rateLimit: writeLimit
},
schema: {
response: {
200: z.object({
organization: sanitizedOrganizationSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const organization = await server.services.org.upgradePrivilegeSystem({
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
orgId: req.permission.orgId
});
return { organization };
}
});
};
+4 -1
View File
@@ -12,5 +12,8 @@ export const sanitizedOrganizationSchema = OrganizationsSchema.pick({
kmsDefaultKeyId: true,
defaultMembershipRole: true,
enforceMfa: true,
selectedMfaMethod: true
selectedMfaMethod: true,
shouldUseNewPrivilegeSystem: true,
privilegeUpgradeInitiatedByUsername: true,
privilegeUpgradeInitiatedAt: true
});
+42 -1
View File
@@ -77,6 +77,7 @@ import {
TResendOrgMemberInvitationDTO,
TUpdateOrgDTO,
TUpdateOrgMembershipDTO,
TUpgradePrivilegeSystemDTO,
TVerifyUserToOrgDTO
} from "./org-types";
@@ -282,6 +283,45 @@ export const orgServiceFactory = ({
};
};
const upgradePrivilegeSystem = async ({
actorId,
actorOrgId,
actorAuthMethod,
orgId
}: TUpgradePrivilegeSystemDTO) => {
const { membership } = await permissionService.getUserOrgPermission(actorId, orgId, actorAuthMethod, actorOrgId);
if (membership.role != OrgMembershipRole.Admin) {
throw new ForbiddenRequestError({
message: "Insufficient privileges - only the organization admin can upgrade the privilege system."
});
}
return orgDAL.transaction(async (tx) => {
const org = await orgDAL.findById(actorOrgId, tx);
if (org.shouldUseNewPrivilegeSystem) {
throw new BadRequestError({
message: "Privilege system already upgraded"
});
}
const user = await userDAL.findById(actorId, tx);
if (!user) {
throw new NotFoundError({ message: `User with ID '${actorId}' not found` });
}
return orgDAL.updateById(
actorOrgId,
{
shouldUseNewPrivilegeSystem: true,
privilegeUpgradeInitiatedAt: new Date(),
privilegeUpgradeInitiatedByUsername: user.username
},
tx
);
});
};
/*
* Update organization details
* */
@@ -1310,6 +1350,7 @@ export const orgServiceFactory = ({
getOrgGroups,
listProjectMembershipsByOrgMembershipId,
findOrgBySlug,
resendOrgMemberInvitation
resendOrgMemberInvitation,
upgradePrivilegeSystem
};
};
+2
View File
@@ -75,6 +75,8 @@ export type TUpdateOrgDTO = {
}>;
} & TOrgPermission;
export type TUpgradePrivilegeSystemDTO = Omit<TOrgPermission, "actor">;
export type TGetOrgGroupsDTO = TOrgPermission;
export type TListProjectMembershipsByOrgMembershipIdDTO = {