From abedb4b53c830e2c0d63b488267543e7376fa82d Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Fri, 30 May 2025 19:28:16 +0400 Subject: [PATCH] feat(instance-management): organizations overview and control --- backend/src/server/routes/index.ts | 2 + backend/src/server/routes/v1/admin-router.ts | 131 ++++++++++++++++++- 2 files changed, 132 insertions(+), 1 deletion(-) diff --git a/backend/src/server/routes/index.ts b/backend/src/server/routes/index.ts index 5196971f9..05561437a 100644 --- a/backend/src/server/routes/index.ts +++ b/backend/src/server/routes/index.ts @@ -726,12 +726,14 @@ export const registerRoutes = async ( userAliasDAL, identityTokenAuthDAL, identityAccessTokenDAL, + orgMembershipDAL, identityOrgMembershipDAL, authService: loginService, serverCfgDAL: superAdminDAL, kmsRootConfigDAL, orgService, keyStore, + orgDAL, licenseService, kmsService, microsoftTeamsService, diff --git a/backend/src/server/routes/v1/admin-router.ts b/backend/src/server/routes/v1/admin-router.ts index 8610a611b..0bade9904 100644 --- a/backend/src/server/routes/v1/admin-router.ts +++ b/backend/src/server/routes/v1/admin-router.ts @@ -1,7 +1,13 @@ import DOMPurify from "isomorphic-dompurify"; import { z } from "zod"; -import { IdentitiesSchema, OrganizationsSchema, SuperAdminSchema, UsersSchema } from "@app/db/schemas"; +import { + IdentitiesSchema, + OrganizationsSchema, + OrgMembershipsSchema, + SuperAdminSchema, + UsersSchema +} from "@app/db/schemas"; import { getConfig } from "@app/lib/config/env"; import { BadRequestError } from "@app/lib/errors"; import { invalidateCacheLimit, readLimit, writeLimit } from "@app/server/config/rateLimiter"; @@ -161,6 +167,129 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => { } }); + server.route({ + method: "GET", + url: "/organization-management/organizations", + config: { + rateLimit: readLimit + }, + schema: { + querystring: z.object({ + searchTerm: z.string().default(""), + offset: z.coerce.number().default(0), + limit: z.coerce.number().max(100).default(20) + }), + response: { + 200: z.object({ + organizations: OrganizationsSchema.extend({ + members: z + .object({ + user: z.object({ + id: z.string(), + email: z.string().nullish(), + username: z.string(), + firstName: z.string().nullish(), + lastName: z.string().nullish() + }), + membershipId: z.string(), + role: z.string(), + roleId: z.string().nullish() + }) + .array(), + projects: z + .object({ + name: z.string(), + id: z.string(), + slug: z.string(), + createdAt: z.date() + }) + .array() + }).array() + }) + } + }, + onRequest: (req, res, done) => { + verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => { + verifySuperAdmin(req, res, done); + }); + }, + handler: async (req) => { + const organizations = await server.services.superAdmin.getOrganizations({ + ...req.query + }); + + return { + organizations + }; + } + }); + + server.route({ + method: "DELETE", + url: "/organization-management/organizations/:organizationId/memberships/:membershipId", + config: { + rateLimit: writeLimit + }, + schema: { + params: z.object({ + organizationId: z.string(), + membershipId: z.string() + }), + response: { + 200: z.object({ + organizationMembership: OrgMembershipsSchema + }) + } + }, + onRequest: (req, res, done) => { + verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => { + verifySuperAdmin(req, res, done); + }); + }, + handler: async (req) => { + const organizationMembership = await server.services.superAdmin.deleteOrganizationMembership( + req.params.organizationId, + req.params.membershipId, + req.permission.id, + req.permission.type + ); + + return { + organizationMembership + }; + } + }); + + server.route({ + method: "DELETE", + url: "/organization-management/organizations/:organizationId", + config: { + rateLimit: writeLimit + }, + schema: { + params: z.object({ + organizationId: z.string() + }), + response: { + 200: z.object({ + organization: OrganizationsSchema + }) + } + }, + onRequest: (req, res, done) => { + verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => { + verifySuperAdmin(req, res, done); + }); + }, + handler: async (req) => { + const organization = await server.services.superAdmin.deleteOrganization(req.params.organizationId); + + return { + organization + }; + } + }); + server.route({ method: "GET", url: "/identity-management/identities",