mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-04 03:26:12 +00:00
Add API specs to groups endpoints, convert projectId groups endpoints to be slug-based
This commit is contained in:
@@ -2,6 +2,7 @@ import slugify from "@sindresorhus/slugify";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { GroupsSchema, OrgMembershipRole, UsersSchema } from "@app/db/schemas";
|
import { GroupsSchema, OrgMembershipRole, UsersSchema } from "@app/db/schemas";
|
||||||
|
import { GROUPS } from "@app/lib/api-docs";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
@@ -12,8 +13,7 @@ export const registerGroupRouter = async (server: FastifyZodProvider) => {
|
|||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
schema: {
|
schema: {
|
||||||
body: z.object({
|
body: z.object({
|
||||||
// TODO: update on frontend to not send organizationId
|
name: z.string().trim().min(1).describe(GROUPS.CREATE.name),
|
||||||
name: z.string().trim().min(1),
|
|
||||||
slug: z
|
slug: z
|
||||||
.string()
|
.string()
|
||||||
.min(5)
|
.min(5)
|
||||||
@@ -21,8 +21,9 @@ export const registerGroupRouter = async (server: FastifyZodProvider) => {
|
|||||||
.refine((v) => slugify(v) === v, {
|
.refine((v) => slugify(v) === v, {
|
||||||
message: "Slug must be a valid slug"
|
message: "Slug must be a valid slug"
|
||||||
})
|
})
|
||||||
.optional(),
|
.optional()
|
||||||
role: z.string().trim().min(1).default(OrgMembershipRole.NoAccess)
|
.describe(GROUPS.CREATE.slug),
|
||||||
|
role: z.string().trim().min(1).default(OrgMembershipRole.NoAccess).describe(GROUPS.CREATE.role)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: GroupsSchema
|
200: GroupsSchema
|
||||||
@@ -47,19 +48,20 @@ export const registerGroupRouter = async (server: FastifyZodProvider) => {
|
|||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
currentSlug: z.string().trim()
|
currentSlug: z.string().trim().describe(GROUPS.UPDATE.currentSlug)
|
||||||
}),
|
}),
|
||||||
body: z
|
body: z
|
||||||
.object({
|
.object({
|
||||||
name: z.string().trim().min(1),
|
name: z.string().trim().min(1).describe(GROUPS.UPDATE.name),
|
||||||
slug: z
|
slug: z
|
||||||
.string()
|
.string()
|
||||||
.min(5)
|
.min(5)
|
||||||
.max(36)
|
.max(36)
|
||||||
.refine((v) => slugify(v) === v, {
|
.refine((v) => slugify(v) === v, {
|
||||||
message: "Slug must be a valid slug"
|
message: "Slug must be a valid slug"
|
||||||
}),
|
})
|
||||||
role: z.string().trim().min(1)
|
.describe(GROUPS.UPDATE.slug),
|
||||||
|
role: z.string().trim().min(1).describe(GROUPS.UPDATE.role)
|
||||||
})
|
})
|
||||||
.partial(),
|
.partial(),
|
||||||
response: {
|
response: {
|
||||||
@@ -81,12 +83,12 @@ export const registerGroupRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:groupSlug",
|
url: "/:slug",
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
groupSlug: z.string().trim()
|
slug: z.string().trim().describe(GROUPS.DELETE.slug)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: GroupsSchema
|
200: GroupsSchema
|
||||||
@@ -94,7 +96,7 @@ export const registerGroupRouter = async (server: FastifyZodProvider) => {
|
|||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const group = await server.services.group.deleteGroup({
|
const group = await server.services.group.deleteGroup({
|
||||||
groupSlug: req.params.groupSlug,
|
groupSlug: req.params.slug,
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
@@ -111,7 +113,7 @@ export const registerGroupRouter = async (server: FastifyZodProvider) => {
|
|||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
slug: z.string().trim()
|
slug: z.string().trim().describe(GROUPS.LIST_USERS.slug)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: UsersSchema.pick({
|
200: UsersSchema.pick({
|
||||||
@@ -147,8 +149,8 @@ export const registerGroupRouter = async (server: FastifyZodProvider) => {
|
|||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
slug: z.string().trim(),
|
slug: z.string().trim().describe(GROUPS.ADD_USER.slug),
|
||||||
username: z.string().trim()
|
username: z.string().trim().describe(GROUPS.ADD_USER.username)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: UsersSchema.pick({
|
200: UsersSchema.pick({
|
||||||
@@ -180,8 +182,8 @@ export const registerGroupRouter = async (server: FastifyZodProvider) => {
|
|||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
slug: z.string().trim(),
|
slug: z.string().trim().describe(GROUPS.DELETE_USER.slug),
|
||||||
username: z.string().trim()
|
username: z.string().trim().describe(GROUPS.DELETE_USER.username)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: UsersSchema.pick({
|
200: UsersSchema.pick({
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import { TUserGroupMembershipDALFactory } from "./user-group-membership-dal";
|
|||||||
type TGroupServiceFactoryDep = {
|
type TGroupServiceFactoryDep = {
|
||||||
userDAL: Pick<TUserDALFactory, "findOne" | "findUserEncKeyByUsername">;
|
userDAL: Pick<TUserDALFactory, "findOne" | "findUserEncKeyByUsername">;
|
||||||
groupDAL: Pick<TGroupDALFactory, "create" | "findOne" | "update" | "delete" | "findAllGroupMembers">;
|
groupDAL: Pick<TGroupDALFactory, "create" | "findOne" | "update" | "delete" | "findAllGroupMembers">;
|
||||||
groupProjectDAL: TGroupProjectDALFactory;
|
groupProjectDAL: Pick<TGroupProjectDALFactory, "find">;
|
||||||
orgDAL: Pick<TOrgDALFactory, "findMembership">;
|
orgDAL: Pick<TOrgDALFactory, "findMembership">;
|
||||||
userGroupMembershipDAL: Pick<
|
userGroupMembershipDAL: Pick<
|
||||||
TUserGroupMembershipDALFactory,
|
TUserGroupMembershipDALFactory,
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ const buildMemberPermission = () => {
|
|||||||
can(OrgPermissionActions.Create, OrgPermissionSubjects.Workspace);
|
can(OrgPermissionActions.Create, OrgPermissionSubjects.Workspace);
|
||||||
can(OrgPermissionActions.Read, OrgPermissionSubjects.Member);
|
can(OrgPermissionActions.Read, OrgPermissionSubjects.Member);
|
||||||
can(OrgPermissionActions.Create, OrgPermissionSubjects.Member);
|
can(OrgPermissionActions.Create, OrgPermissionSubjects.Member);
|
||||||
|
can(OrgPermissionActions.Read, OrgPermissionSubjects.Groups);
|
||||||
can(OrgPermissionActions.Read, OrgPermissionSubjects.Role);
|
can(OrgPermissionActions.Read, OrgPermissionSubjects.Role);
|
||||||
can(OrgPermissionActions.Read, OrgPermissionSubjects.Settings);
|
can(OrgPermissionActions.Read, OrgPermissionSubjects.Settings);
|
||||||
can(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
can(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
||||||
|
|||||||
@@ -145,41 +145,6 @@ export const permissionDALFactory = (db: TDbClient) => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
// const permission = sqlNestRelationships({
|
|
||||||
// data: docs, // TODO: concat?
|
|
||||||
// key: "membershipId",
|
|
||||||
// parentMapper: ({
|
|
||||||
// orgId,
|
|
||||||
// orgAuthEnforced,
|
|
||||||
// membershipId,
|
|
||||||
// membershipCreatedAt,
|
|
||||||
// membershipUpdatedAt,
|
|
||||||
// oldRoleField,
|
|
||||||
// // role
|
|
||||||
// }) => ({
|
|
||||||
// orgId,
|
|
||||||
// orgAuthEnforced,
|
|
||||||
// userId,
|
|
||||||
// role: oldRoleField,
|
|
||||||
// // role,
|
|
||||||
// id: membershipId,
|
|
||||||
// projectId,
|
|
||||||
// createdAt: membershipCreatedAt,
|
|
||||||
// updatedAt: membershipUpdatedAt
|
|
||||||
// }),
|
|
||||||
// childrenMapper: [
|
|
||||||
// {
|
|
||||||
// key: "id",
|
|
||||||
// label: "roles" as const,
|
|
||||||
// mapper: (data) =>
|
|
||||||
// ProjectUserMembershipRolesSchema.extend({
|
|
||||||
// permissions: z.unknown(),
|
|
||||||
// customRoleSlug: z.string().optional().nullable()
|
|
||||||
// }).parse(data)
|
|
||||||
// }
|
|
||||||
// ]
|
|
||||||
// });
|
|
||||||
|
|
||||||
// when introducting cron mode change it here
|
// when introducting cron mode change it here
|
||||||
const activeRoles = permission?.[0]?.roles.filter(
|
const activeRoles = permission?.[0]?.roles.filter(
|
||||||
({ isTemporary, temporaryAccessEndTime }) =>
|
({ isTemporary, temporaryAccessEndTime }) =>
|
||||||
|
|||||||
@@ -1,3 +1,31 @@
|
|||||||
|
export const GROUPS = {
|
||||||
|
CREATE: {
|
||||||
|
name: "The name of the group to create.",
|
||||||
|
slug: "The slug of the group to create.",
|
||||||
|
role: "The role of the group to create."
|
||||||
|
},
|
||||||
|
UPDATE: {
|
||||||
|
currentSlug: "The current slug of the group to update.",
|
||||||
|
name: "The new name of the group to update to.",
|
||||||
|
slug: "The new slug of the group to update to.",
|
||||||
|
role: "The new role of the group to update to."
|
||||||
|
},
|
||||||
|
DELETE: {
|
||||||
|
slug: "The slug of the group to delete"
|
||||||
|
},
|
||||||
|
LIST_USERS: {
|
||||||
|
slug: "The slug of the group to list users for"
|
||||||
|
},
|
||||||
|
ADD_USER: {
|
||||||
|
slug: "The slug of the group to add the user to.",
|
||||||
|
username: "The username of the user to add to the group."
|
||||||
|
},
|
||||||
|
DELETE_USER: {
|
||||||
|
slug: "The slug of the group to remove the user from.",
|
||||||
|
username: "The username of the user to remove from the group."
|
||||||
|
}
|
||||||
|
} as const;
|
||||||
|
|
||||||
export const IDENTITIES = {
|
export const IDENTITIES = {
|
||||||
CREATE: {
|
CREATE: {
|
||||||
name: "The name of the identity to create.",
|
name: "The name of the identity to create.",
|
||||||
@@ -79,6 +107,9 @@ export const ORGANIZATIONS = {
|
|||||||
},
|
},
|
||||||
GET_PROJECTS: {
|
GET_PROJECTS: {
|
||||||
organizationId: "The ID of the organization to get projects from."
|
organizationId: "The ID of the organization to get projects from."
|
||||||
|
},
|
||||||
|
LIST_GROUPS: {
|
||||||
|
organizationId: "The ID of the organization to list groups for."
|
||||||
}
|
}
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@@ -141,6 +172,23 @@ export const PROJECTS = {
|
|||||||
},
|
},
|
||||||
ROLLBACK_TO_SNAPSHOT: {
|
ROLLBACK_TO_SNAPSHOT: {
|
||||||
secretSnapshotId: "The ID of the snapshot to rollback to."
|
secretSnapshotId: "The ID of the snapshot to rollback to."
|
||||||
|
},
|
||||||
|
ADD_GROUP_TO_PROJECT: {
|
||||||
|
projectSlug: "The slug of the project to add the group to.",
|
||||||
|
groupSlug: "The slug of the group to add to the project.",
|
||||||
|
role: "The role for the group to assume in the project."
|
||||||
|
},
|
||||||
|
UPDATE_GROUP_IN_PROJECT: {
|
||||||
|
projectSlug: "The slug of the project to update the group in.",
|
||||||
|
groupSlug: "The slug of the group to update in the project.",
|
||||||
|
roles: "A list of roles to update the group to."
|
||||||
|
},
|
||||||
|
REMOVE_GROUP_FROM_PROJECT: {
|
||||||
|
projectSlug: "The slug of the project to delete the group from.",
|
||||||
|
groupSlug: "The slug of the group to delete from the project."
|
||||||
|
},
|
||||||
|
LIST_GROUPS_IN_PROJECT: {
|
||||||
|
projectSlug: "The slug of the project to list groups for."
|
||||||
}
|
}
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,15 @@ export type TProjectPermission = {
|
|||||||
actorOrgId: string;
|
actorOrgId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// same as TProjectPermission but with projectSlug requirement instead of projectId
|
||||||
|
export type TProjectSlugPermission = {
|
||||||
|
actor: ActorType;
|
||||||
|
actorId: string;
|
||||||
|
projectSlug: string;
|
||||||
|
actorAuthMethod: ActorAuthMethod;
|
||||||
|
actorOrgId: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type RequiredKeys<T> = {
|
export type RequiredKeys<T> = {
|
||||||
[K in keyof T]-?: undefined extends T[K] ? never : K;
|
[K in keyof T]-?: undefined extends T[K] ? never : K;
|
||||||
}[keyof T];
|
}[keyof T];
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
OrgRolesSchema,
|
OrgRolesSchema,
|
||||||
UsersSchema
|
UsersSchema
|
||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
|
import { ORGANIZATIONS } from "@app/lib/api-docs";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
@@ -209,7 +210,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
|||||||
url: "/:organizationId/groups",
|
url: "/:organizationId/groups",
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
organizationId: z.string().trim()
|
organizationId: z.string().trim().describe(ORGANIZATIONS.LIST_GROUPS.organizationId)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@@ -232,7 +233,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
|||||||
const groups = await server.services.org.getOrgGroups({
|
const groups = await server.services.org.getOrgGroups({
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
orgId: req.permission.orgId,
|
orgId: req.params.organizationId,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
actorOrgId: req.permission.orgId
|
actorOrgId: req.permission.orgId
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,15 +15,26 @@ import { ProjectUserMembershipTemporaryMode } from "@app/services/project-member
|
|||||||
export const registerGroupProjectRouter = async (server: FastifyZodProvider) => {
|
export const registerGroupProjectRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: "/:projectId/group-memberships/:groupSlug",
|
url: "/:projectSlug/groups/:groupSlug",
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
schema: {
|
schema: {
|
||||||
|
description: "Add group to project",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
projectId: z.string().trim(),
|
projectSlug: z.string().trim().describe(PROJECTS.ADD_GROUP_TO_PROJECT.projectSlug),
|
||||||
groupSlug: z.string().trim()
|
groupSlug: z.string().trim().describe(PROJECTS.ADD_GROUP_TO_PROJECT.groupSlug)
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
role: z.string().trim().min(1).default(ProjectMembershipRole.NoAccess)
|
role: z
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.min(1)
|
||||||
|
.default(ProjectMembershipRole.NoAccess)
|
||||||
|
.describe(PROJECTS.ADD_GROUP_TO_PROJECT.role)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@@ -32,13 +43,13 @@ export const registerGroupProjectRouter = async (server: FastifyZodProvider) =>
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const groupMembership = await server.services.groupProject.createProjectGroup({
|
const groupMembership = await server.services.groupProject.addGroupToProject({
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
groupSlug: req.params.groupSlug,
|
groupSlug: req.params.groupSlug,
|
||||||
projectId: req.params.projectId,
|
projectSlug: req.params.projectSlug,
|
||||||
role: req.body.role
|
role: req.body.role
|
||||||
});
|
});
|
||||||
return { groupMembership };
|
return { groupMembership };
|
||||||
@@ -47,18 +58,18 @@ export const registerGroupProjectRouter = async (server: FastifyZodProvider) =>
|
|||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
url: "/:projectId/group-memberships/:groupSlug",
|
url: "/:projectSlug/groups/:groupSlug",
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
schema: {
|
schema: {
|
||||||
description: "Update project group memberships",
|
description: "Update group in project",
|
||||||
security: [
|
security: [
|
||||||
{
|
{
|
||||||
bearerAuth: []
|
bearerAuth: []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
projectId: z.string().trim(),
|
projectSlug: z.string().trim().describe(PROJECTS.UPDATE_GROUP_IN_PROJECT.projectSlug),
|
||||||
groupSlug: z.string().trim()
|
groupSlug: z.string().trim().describe(PROJECTS.UPDATE_GROUP_IN_PROJECT.groupSlug)
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
roles: z
|
roles: z
|
||||||
@@ -78,6 +89,7 @@ export const registerGroupProjectRouter = async (server: FastifyZodProvider) =>
|
|||||||
])
|
])
|
||||||
)
|
)
|
||||||
.min(1)
|
.min(1)
|
||||||
|
.describe(PROJECTS.UPDATE_GROUP_IN_PROJECT.roles)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@@ -86,13 +98,13 @@ export const registerGroupProjectRouter = async (server: FastifyZodProvider) =>
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const roles = await server.services.groupProject.updateProjectGroup({
|
const roles = await server.services.groupProject.updateGroupInProject({
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
groupSlug: req.params.groupSlug,
|
groupSlug: req.params.groupSlug,
|
||||||
projectId: req.params.projectId,
|
projectSlug: req.params.projectSlug,
|
||||||
roles: req.body.roles
|
roles: req.body.roles
|
||||||
});
|
});
|
||||||
return { roles };
|
return { roles };
|
||||||
@@ -101,18 +113,18 @@ export const registerGroupProjectRouter = async (server: FastifyZodProvider) =>
|
|||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
url: "/:projectId/group-memberships/:groupSlug",
|
url: "/:projectSlug/groups/:groupSlug",
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
schema: {
|
schema: {
|
||||||
description: "Delete project group memberships",
|
description: "Remove group from project",
|
||||||
security: [
|
security: [
|
||||||
{
|
{
|
||||||
bearerAuth: []
|
bearerAuth: []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
projectId: z.string().trim().describe(PROJECTS.DELETE_IDENTITY_MEMBERSHIP.projectId),
|
projectSlug: z.string().trim().describe(PROJECTS.REMOVE_GROUP_FROM_PROJECT.projectSlug),
|
||||||
groupSlug: z.string().trim().describe(PROJECTS.DELETE_IDENTITY_MEMBERSHIP.identityId)
|
groupSlug: z.string().trim().describe(PROJECTS.REMOVE_GROUP_FROM_PROJECT.groupSlug)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@@ -121,13 +133,13 @@ export const registerGroupProjectRouter = async (server: FastifyZodProvider) =>
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const groupMembership = await server.services.groupProject.deleteProjectGroup({
|
const groupMembership = await server.services.groupProject.removeGroupFromProject({
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
groupSlug: req.params.groupSlug,
|
groupSlug: req.params.groupSlug,
|
||||||
projectId: req.params.projectId
|
projectSlug: req.params.projectSlug
|
||||||
});
|
});
|
||||||
return { groupMembership };
|
return { groupMembership };
|
||||||
}
|
}
|
||||||
@@ -135,17 +147,17 @@ export const registerGroupProjectRouter = async (server: FastifyZodProvider) =>
|
|||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/:projectId/group-memberships",
|
url: "/:projectSlug/groups",
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
schema: {
|
schema: {
|
||||||
description: "Return project group memberships",
|
description: "Return list of groups in project",
|
||||||
security: [
|
security: [
|
||||||
{
|
{
|
||||||
bearerAuth: []
|
bearerAuth: []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
projectId: z.string().trim().describe(PROJECTS.LIST_IDENTITY_MEMBERSHIPS.projectId)
|
projectSlug: z.string().trim().describe(PROJECTS.LIST_GROUPS_IN_PROJECT.projectSlug)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@@ -176,12 +188,12 @@ export const registerGroupProjectRouter = async (server: FastifyZodProvider) =>
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const groupMemberships = await server.services.groupProject.listProjectGroup({
|
const groupMemberships = await server.services.groupProject.listGroupsInProject({
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
projectId: req.params.projectId
|
projectSlug: req.params.projectSlug
|
||||||
});
|
});
|
||||||
return { groupMemberships };
|
return { groupMemberships };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ type TGroupProjectServiceFactoryDep = {
|
|||||||
"create" | "transaction" | "insertMany" | "delete"
|
"create" | "transaction" | "insertMany" | "delete"
|
||||||
>;
|
>;
|
||||||
userGroupMembershipDAL: TUserGroupMembershipDALFactory;
|
userGroupMembershipDAL: TUserGroupMembershipDALFactory;
|
||||||
projectDAL: Pick<TProjectDALFactory, "findById" | "findProjectGhostUser">;
|
projectDAL: Pick<TProjectDALFactory, "findOne" | "findProjectGhostUser">;
|
||||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "findLatestProjectKey" | "delete" | "insertMany" | "transaction">;
|
projectKeyDAL: Pick<TProjectKeyDALFactory, "findLatestProjectKey" | "delete" | "insertMany" | "transaction">;
|
||||||
projectRoleDAL: Pick<TProjectRoleDALFactory, "find">;
|
projectRoleDAL: Pick<TProjectRoleDALFactory, "find">;
|
||||||
projectBotDAL: TProjectBotDALFactory;
|
projectBotDAL: TProjectBotDALFactory;
|
||||||
@@ -54,19 +54,25 @@ export const groupProjectServiceFactory = ({
|
|||||||
projectRoleDAL,
|
projectRoleDAL,
|
||||||
permissionService
|
permissionService
|
||||||
}: TGroupProjectServiceFactoryDep) => {
|
}: TGroupProjectServiceFactoryDep) => {
|
||||||
const createProjectGroup = async ({
|
const addGroupToProject = async ({
|
||||||
groupSlug,
|
groupSlug,
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
projectId,
|
projectSlug,
|
||||||
role
|
role
|
||||||
}: TCreateProjectGroupDTO) => {
|
}: TCreateProjectGroupDTO) => {
|
||||||
|
const project = await projectDAL.findOne({
|
||||||
|
slug: projectSlug
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!project) throw new BadRequestError({ message: `Failed to find project with slug ${projectSlug}` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
project.id,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
);
|
||||||
@@ -75,14 +81,12 @@ export const groupProjectServiceFactory = ({
|
|||||||
const group = await groupDAL.findOne({ orgId: actorOrgId, slug: groupSlug });
|
const group = await groupDAL.findOne({ orgId: actorOrgId, slug: groupSlug });
|
||||||
if (!group) throw new BadRequestError({ message: `Failed to find group with slug ${groupSlug}` });
|
if (!group) throw new BadRequestError({ message: `Failed to find group with slug ${groupSlug}` });
|
||||||
|
|
||||||
const existingGroup = await groupProjectDAL.findOne({ groupId: group.id, projectId });
|
const existingGroup = await groupProjectDAL.findOne({ groupId: group.id, projectId: project.id });
|
||||||
if (existingGroup)
|
if (existingGroup)
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: `Group with slug ${groupSlug} already exists in project with id ${projectId}`
|
message: `Group with slug ${groupSlug} already exists in project with id ${project.id}`
|
||||||
});
|
});
|
||||||
|
|
||||||
const project = await projectDAL.findById(projectId);
|
|
||||||
|
|
||||||
const { permission: rolePermission, role: customRole } = await permissionService.getProjectPermissionByRole(
|
const { permission: rolePermission, role: customRole } = await permissionService.getProjectPermissionByRole(
|
||||||
role,
|
role,
|
||||||
project.id
|
project.id
|
||||||
@@ -119,10 +123,10 @@ export const groupProjectServiceFactory = ({
|
|||||||
// share project key with users in group that have not
|
// share project key with users in group that have not
|
||||||
// individually been added to the project and that are not part of
|
// individually been added to the project and that are not part of
|
||||||
// other groups that are in the project
|
// other groups that are in the project
|
||||||
const groupMembers = await userGroupMembershipDAL.findGroupMembersNotInProject(group.id, projectId);
|
const groupMembers = await userGroupMembershipDAL.findGroupMembersNotInProject(group.id, project.id);
|
||||||
|
|
||||||
if (groupMembers.length) {
|
if (groupMembers.length) {
|
||||||
const ghostUser = await projectDAL.findProjectGhostUser(projectId);
|
const ghostUser = await projectDAL.findProjectGhostUser(project.id);
|
||||||
|
|
||||||
if (!ghostUser) {
|
if (!ghostUser) {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
@@ -130,7 +134,7 @@ export const groupProjectServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const ghostUserLatestKey = await projectKeyDAL.findLatestProjectKey(ghostUser.id, projectId);
|
const ghostUserLatestKey = await projectKeyDAL.findLatestProjectKey(ghostUser.id, project.id);
|
||||||
|
|
||||||
if (!ghostUserLatestKey) {
|
if (!ghostUserLatestKey) {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
@@ -138,7 +142,7 @@ export const groupProjectServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const bot = await projectBotDAL.findOne({ projectId });
|
const bot = await projectBotDAL.findOne({ projectId: project.id });
|
||||||
|
|
||||||
if (!bot) {
|
if (!bot) {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
@@ -168,7 +172,7 @@ export const groupProjectServiceFactory = ({
|
|||||||
nonce,
|
nonce,
|
||||||
senderId: ghostUser.id,
|
senderId: ghostUser.id,
|
||||||
receiverId: id,
|
receiverId: id,
|
||||||
projectId
|
projectId: project.id
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -178,8 +182,8 @@ export const groupProjectServiceFactory = ({
|
|||||||
return projectGroup;
|
return projectGroup;
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateProjectGroup = async ({
|
const updateGroupInProject = async ({
|
||||||
projectId,
|
projectSlug,
|
||||||
groupSlug,
|
groupSlug,
|
||||||
roles,
|
roles,
|
||||||
actor,
|
actor,
|
||||||
@@ -187,10 +191,16 @@ export const groupProjectServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
}: TUpdateProjectGroupDTO) => {
|
}: TUpdateProjectGroupDTO) => {
|
||||||
|
const project = await projectDAL.findOne({
|
||||||
|
slug: projectSlug
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!project) throw new BadRequestError({ message: `Failed to find project with slug ${projectSlug}` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
project.id,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
);
|
||||||
@@ -199,12 +209,12 @@ export const groupProjectServiceFactory = ({
|
|||||||
const group = await groupDAL.findOne({ orgId: actorOrgId, slug: groupSlug });
|
const group = await groupDAL.findOne({ orgId: actorOrgId, slug: groupSlug });
|
||||||
if (!group) throw new BadRequestError({ message: `Failed to find group with slug ${groupSlug}` });
|
if (!group) throw new BadRequestError({ message: `Failed to find group with slug ${groupSlug}` });
|
||||||
|
|
||||||
const projectGroup = await groupProjectDAL.findOne({ groupId: group.id, projectId });
|
const projectGroup = await groupProjectDAL.findOne({ groupId: group.id, projectId: project.id });
|
||||||
if (!projectGroup) throw new BadRequestError({ message: `Failed to find group with slug ${groupSlug}` });
|
if (!projectGroup) throw new BadRequestError({ message: `Failed to find group with slug ${groupSlug}` });
|
||||||
|
|
||||||
const { permission: groupRolePermission } = await permissionService.getProjectPermissionByRole(
|
const { permission: groupRolePermission } = await permissionService.getProjectPermissionByRole(
|
||||||
projectGroup.role,
|
projectGroup.role,
|
||||||
projectId
|
project.id
|
||||||
);
|
);
|
||||||
|
|
||||||
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, groupRolePermission);
|
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, groupRolePermission);
|
||||||
@@ -217,7 +227,7 @@ export const groupProjectServiceFactory = ({
|
|||||||
const hasCustomRole = Boolean(customInputRoles.length);
|
const hasCustomRole = Boolean(customInputRoles.length);
|
||||||
const customRoles = hasCustomRole
|
const customRoles = hasCustomRole
|
||||||
? await projectRoleDAL.find({
|
? await projectRoleDAL.find({
|
||||||
projectId,
|
projectId: project.id,
|
||||||
$in: { slug: customInputRoles.map(({ role }) => role) }
|
$in: { slug: customInputRoles.map(({ role }) => role) }
|
||||||
})
|
})
|
||||||
: [];
|
: [];
|
||||||
@@ -257,69 +267,87 @@ export const groupProjectServiceFactory = ({
|
|||||||
return updatedRoles;
|
return updatedRoles;
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteProjectGroup = async ({
|
const removeGroupFromProject = async ({
|
||||||
|
projectSlug,
|
||||||
groupSlug,
|
groupSlug,
|
||||||
actorId,
|
actorId,
|
||||||
actor,
|
actor,
|
||||||
actorOrgId,
|
actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod
|
||||||
projectId
|
|
||||||
}: TDeleteProjectGroupDTO) => {
|
}: TDeleteProjectGroupDTO) => {
|
||||||
|
const project = await projectDAL.findOne({
|
||||||
|
slug: projectSlug
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!project) throw new BadRequestError({ message: `Failed to find project with slug ${projectSlug}` });
|
||||||
|
|
||||||
const group = await groupDAL.findOne({ orgId: actorOrgId, slug: groupSlug });
|
const group = await groupDAL.findOne({ orgId: actorOrgId, slug: groupSlug });
|
||||||
if (!group) throw new BadRequestError({ message: `Failed to find group with slug ${groupSlug}` });
|
if (!group) throw new BadRequestError({ message: `Failed to find group with slug ${groupSlug}` });
|
||||||
|
|
||||||
const groupProjectMembership = await groupProjectDAL.findOne({ groupId: group.id, projectId });
|
const groupProjectMembership = await groupProjectDAL.findOne({ groupId: group.id, projectId: project.id });
|
||||||
if (!groupProjectMembership) throw new BadRequestError({ message: `Failed to find group with slug ${groupSlug}` });
|
if (!groupProjectMembership) throw new BadRequestError({ message: `Failed to find group with slug ${groupSlug}` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
project.id,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Groups);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Groups);
|
||||||
const { permission: groupRolePermission } = await permissionService.getProjectPermissionByRole(
|
const { permission: groupRolePermission } = await permissionService.getProjectPermissionByRole(
|
||||||
groupProjectMembership.role,
|
groupProjectMembership.role,
|
||||||
projectId
|
project.id
|
||||||
);
|
);
|
||||||
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, groupRolePermission);
|
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, groupRolePermission);
|
||||||
if (!hasRequiredPriviledges) throw new ForbiddenRequestError({ message: "Failed to delete more privileged group" });
|
if (!hasRequiredPriviledges) throw new ForbiddenRequestError({ message: "Failed to delete more privileged group" });
|
||||||
|
|
||||||
const groupMembers = await userGroupMembershipDAL.findGroupMembersNotInProject(group.id, projectId);
|
const groupMembers = await userGroupMembershipDAL.findGroupMembersNotInProject(group.id, project.id);
|
||||||
|
|
||||||
if (groupMembers.length) {
|
if (groupMembers.length) {
|
||||||
await projectKeyDAL.delete({
|
await projectKeyDAL.delete({
|
||||||
projectId,
|
projectId: project.id,
|
||||||
$in: {
|
$in: {
|
||||||
receiverId: groupMembers.map(({ user: { id } }) => id)
|
receiverId: groupMembers.map(({ user: { id } }) => id)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const [deletedGroup] = await groupProjectDAL.delete({ groupId: group.id, projectId });
|
const [deletedGroup] = await groupProjectDAL.delete({ groupId: group.id, projectId: project.id });
|
||||||
|
|
||||||
return deletedGroup;
|
return deletedGroup;
|
||||||
};
|
};
|
||||||
|
|
||||||
const listProjectGroup = async ({ projectId, actor, actorId, actorAuthMethod, actorOrgId }: TListProjectGroupDTO) => {
|
const listGroupsInProject = async ({
|
||||||
|
projectSlug,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
}: TListProjectGroupDTO) => {
|
||||||
|
const project = await projectDAL.findOne({
|
||||||
|
slug: projectSlug
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!project) throw new BadRequestError({ message: `Failed to find project with slug ${projectSlug}` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
project.id,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Groups);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Groups);
|
||||||
|
|
||||||
const groupMemberhips = await groupProjectDAL.findByProjectId(projectId);
|
const groupMemberhips = await groupProjectDAL.findByProjectId(project.id);
|
||||||
return groupMemberhips;
|
return groupMemberhips;
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
createProjectGroup,
|
addGroupToProject,
|
||||||
updateProjectGroup,
|
updateGroupInProject,
|
||||||
deleteProjectGroup,
|
removeGroupFromProject,
|
||||||
listProjectGroup
|
listGroupsInProject
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { TProjectPermission } from "@app/lib/types";
|
import { TProjectSlugPermission } from "@app/lib/types";
|
||||||
|
|
||||||
import { ProjectUserMembershipTemporaryMode } from "../project-membership/project-membership-types";
|
import { ProjectUserMembershipTemporaryMode } from "../project-membership/project-membership-types";
|
||||||
|
|
||||||
export type TCreateProjectGroupDTO = {
|
export type TCreateProjectGroupDTO = {
|
||||||
groupSlug: string;
|
groupSlug: string;
|
||||||
role: string;
|
role: string;
|
||||||
} & TProjectPermission;
|
} & TProjectSlugPermission;
|
||||||
|
|
||||||
export type TUpdateProjectGroupDTO = {
|
export type TUpdateProjectGroupDTO = {
|
||||||
roles: (
|
roles: (
|
||||||
@@ -22,10 +22,10 @@ export type TUpdateProjectGroupDTO = {
|
|||||||
}
|
}
|
||||||
)[];
|
)[];
|
||||||
groupSlug: string;
|
groupSlug: string;
|
||||||
} & TProjectPermission;
|
} & TProjectSlugPermission;
|
||||||
|
|
||||||
export type TDeleteProjectGroupDTO = {
|
export type TDeleteProjectGroupDTO = {
|
||||||
groupSlug: string;
|
groupSlug: string;
|
||||||
} & TProjectPermission;
|
} & TProjectSlugPermission;
|
||||||
|
|
||||||
export type TListProjectGroupDTO = TProjectPermission;
|
export type TListProjectGroupDTO = TProjectSlugPermission;
|
||||||
|
|||||||
@@ -118,7 +118,8 @@ export const orgServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getOrgGroups = async ({ actor, actorId, orgId, actorAuthMethod, actorOrgId }: TGetOrgGroupsDTO) => {
|
const getOrgGroups = async ({ actor, actorId, orgId, actorAuthMethod, actorOrgId }: TGetOrgGroupsDTO) => {
|
||||||
await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Groups);
|
||||||
const groups = await groupDAL.findByOrgId(orgId);
|
const groups = await groupDAL.findByOrgId(orgId);
|
||||||
return groups;
|
return groups;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -51,7 +51,10 @@ export type TFindAllWorkspacesDTO = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type TUpdateOrgDTO = {
|
export type TUpdateOrgDTO = {
|
||||||
|
orgId: string;
|
||||||
data: Partial<{ name: string; slug: string; authEnforced: boolean; scimEnabled: boolean }>;
|
data: Partial<{ name: string; slug: string; authEnforced: boolean; scimEnabled: boolean }>;
|
||||||
} & TOrgPermission;
|
} & TOrgPermission;
|
||||||
|
|
||||||
export type TGetOrgGroupsDTO = TOrgPermission;
|
export type TGetOrgGroupsDTO = {
|
||||||
|
orgId: string;
|
||||||
|
} & TOrgPermission;
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export const workspaceKeys = {
|
|||||||
getWorkspaceIdentityMemberships: (workspaceId: string) =>
|
getWorkspaceIdentityMemberships: (workspaceId: string) =>
|
||||||
[{ workspaceId }, "workspace-identity-memberships"] as const,
|
[{ workspaceId }, "workspace-identity-memberships"] as const,
|
||||||
getWorkspaceGroupMemberships: (workspaceId: string) =>
|
getWorkspaceGroupMemberships: (workspaceId: string) =>
|
||||||
[{ workspaceId }, "workspace-group-memberships"] as const
|
[{ workspaceId }, "workspace-groups"] as const
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchWorkspaceById = async (workspaceId: string) => {
|
const fetchWorkspaceById = async (workspaceId: string) => {
|
||||||
@@ -460,25 +460,25 @@ export const useAddGroupToWorkspace = () => {
|
|||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async ({
|
mutationFn: async ({
|
||||||
groupSlug,
|
groupSlug,
|
||||||
workspaceId,
|
projectSlug,
|
||||||
role
|
role
|
||||||
}: {
|
}: {
|
||||||
groupSlug: string;
|
groupSlug: string;
|
||||||
workspaceId: string;
|
projectSlug: string;
|
||||||
role?: string;
|
role?: string;
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
data: { groupMembership }
|
data: { groupMembership }
|
||||||
} = await apiRequest.post(
|
} = await apiRequest.post(
|
||||||
`/api/v2/workspace/${workspaceId}/group-memberships/${groupSlug}`,
|
`/api/v2/workspace/${projectSlug}/groups/${groupSlug}`,
|
||||||
{
|
{
|
||||||
role
|
role
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return groupMembership;
|
return groupMembership;
|
||||||
},
|
},
|
||||||
onSuccess: (_, { workspaceId }) => {
|
onSuccess: (_, { projectSlug }) => {
|
||||||
queryClient.invalidateQueries(workspaceKeys.getWorkspaceGroupMemberships(workspaceId));
|
queryClient.invalidateQueries(workspaceKeys.getWorkspaceGroupMemberships(projectSlug));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -486,20 +486,19 @@ export const useAddGroupToWorkspace = () => {
|
|||||||
export const useUpdateGroupWorkspaceRole = () => {
|
export const useUpdateGroupWorkspaceRole = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async ({ groupSlug, workspaceId, roles }: TUpdateWorkspaceGroupRoleDTO) => {
|
mutationFn: async ({ groupSlug, projectSlug, roles }: TUpdateWorkspaceGroupRoleDTO) => {
|
||||||
const {
|
const {
|
||||||
data: { groupMembership }
|
data: { groupMembership }
|
||||||
} = await apiRequest.patch(
|
} = await apiRequest.patch(
|
||||||
`/api/v2/workspace/${workspaceId}/group-memberships/${groupSlug}`,
|
`/api/v2/workspace/${projectSlug}/groups/${groupSlug}`, {
|
||||||
{
|
|
||||||
roles
|
roles
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return groupMembership;
|
return groupMembership;
|
||||||
},
|
},
|
||||||
onSuccess: (_, { workspaceId }) => {
|
onSuccess: (_, { projectSlug }) => {
|
||||||
queryClient.invalidateQueries(workspaceKeys.getWorkspaceGroupMemberships(workspaceId));
|
queryClient.invalidateQueries(workspaceKeys.getWorkspaceGroupMemberships(projectSlug));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -509,32 +508,32 @@ export const useDeleteGroupFromWorkspace = () => {
|
|||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async ({
|
mutationFn: async ({
|
||||||
groupSlug,
|
groupSlug,
|
||||||
workspaceId
|
projectSlug
|
||||||
}: {
|
}: {
|
||||||
groupSlug: string;
|
groupSlug: string;
|
||||||
workspaceId: string;
|
projectSlug: string;
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
data: { groupMembership }
|
data: { groupMembership }
|
||||||
} = await apiRequest.delete(
|
} = await apiRequest.delete(
|
||||||
`/api/v2/workspace/${workspaceId}/group-memberships/${groupSlug}`
|
`/api/v2/workspace/${projectSlug}/groups/${groupSlug}`
|
||||||
);
|
);
|
||||||
return groupMembership;
|
return groupMembership;
|
||||||
},
|
},
|
||||||
onSuccess: (_, { workspaceId }) => {
|
onSuccess: (_, { projectSlug }) => {
|
||||||
queryClient.invalidateQueries(workspaceKeys.getWorkspaceGroupMemberships(workspaceId));
|
queryClient.invalidateQueries(workspaceKeys.getWorkspaceGroupMemberships(projectSlug));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGetWorkspaceGroupMemberships = (workspaceId: string) => {
|
export const useGetWorkspaceGroupMemberships = (projectSlug: string) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: workspaceKeys.getWorkspaceGroupMemberships(workspaceId),
|
queryKey: workspaceKeys.getWorkspaceGroupMemberships(projectSlug),
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const {
|
const {
|
||||||
data: { groupMemberships }
|
data: { groupMemberships }
|
||||||
} = await apiRequest.get<{ groupMemberships: TGroupMembership[] }>(
|
} = await apiRequest.get<{ groupMemberships: TGroupMembership[] }>(
|
||||||
`/api/v2/workspace/${workspaceId}/group-memberships`
|
`/api/v2/workspace/${projectSlug}/groups`
|
||||||
);
|
);
|
||||||
return groupMemberships;
|
return groupMemberships;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ export type TUpdateWorkspaceIdentityRoleDTO = {
|
|||||||
|
|
||||||
export type TUpdateWorkspaceGroupRoleDTO = {
|
export type TUpdateWorkspaceGroupRoleDTO = {
|
||||||
groupSlug: string;
|
groupSlug: string;
|
||||||
workspaceId: string;
|
projectSlug: string;
|
||||||
roles: (
|
roles: (
|
||||||
| {
|
| {
|
||||||
role: string;
|
role: string;
|
||||||
|
|||||||
+2
-2
@@ -48,7 +48,7 @@ export const GroupModal = ({
|
|||||||
const workspaceId = currentWorkspace?.id || "";
|
const workspaceId = currentWorkspace?.id || "";
|
||||||
|
|
||||||
const { data: groups } = useGetOrganizationGroups(orgId);
|
const { data: groups } = useGetOrganizationGroups(orgId);
|
||||||
const { data: groupMemberships } = useGetWorkspaceGroupMemberships(workspaceId);
|
const { data: groupMemberships } = useGetWorkspaceGroupMemberships(currentWorkspace?.slug || "");
|
||||||
|
|
||||||
const { data: roles } = useGetProjectRoles(workspaceId);
|
const { data: roles } = useGetProjectRoles(workspaceId);
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ export const GroupModal = ({
|
|||||||
const onFormSubmit = async ({ slug, role }: FormData) => {
|
const onFormSubmit = async ({ slug, role }: FormData) => {
|
||||||
try {
|
try {
|
||||||
await addGroupToWorkspaceMutateAsync({
|
await addGroupToWorkspaceMutateAsync({
|
||||||
workspaceId,
|
projectSlug: currentWorkspace?.slug || "",
|
||||||
groupSlug: slug,
|
groupSlug: slug,
|
||||||
role: role || undefined
|
role: role || undefined
|
||||||
});
|
});
|
||||||
|
|||||||
+1
-1
@@ -252,7 +252,7 @@ export const GroupRoles = ({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await updateGroupWorkspaceRole.mutateAsync({
|
await updateGroupWorkspaceRole.mutateAsync({
|
||||||
workspaceId,
|
projectSlug: currentWorkspace?.slug || "",
|
||||||
groupSlug,
|
groupSlug,
|
||||||
roles: selectedRoles
|
roles: selectedRoles
|
||||||
});
|
});
|
||||||
|
|||||||
+1
-3
@@ -17,8 +17,6 @@ import { GroupTable } from "./GroupsTable";
|
|||||||
export const GroupsSection = () => {
|
export const GroupsSection = () => {
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
|
||||||
const workspaceId = currentWorkspace?.id ?? "";
|
|
||||||
|
|
||||||
const { mutateAsync: deleteMutateAsync } = useDeleteGroupFromWorkspace();
|
const { mutateAsync: deleteMutateAsync } = useDeleteGroupFromWorkspace();
|
||||||
|
|
||||||
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
||||||
@@ -31,7 +29,7 @@ export const GroupsSection = () => {
|
|||||||
try {
|
try {
|
||||||
await deleteMutateAsync({
|
await deleteMutateAsync({
|
||||||
groupSlug,
|
groupSlug,
|
||||||
workspaceId
|
projectSlug: currentWorkspace?.slug || ""
|
||||||
});
|
});
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
|
|||||||
+1
-1
@@ -33,7 +33,7 @@ type Props = {
|
|||||||
|
|
||||||
export const GroupTable = ({ handlePopUpOpen }: Props) => {
|
export const GroupTable = ({ handlePopUpOpen }: Props) => {
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
const { data, isLoading } = useGetWorkspaceGroupMemberships(currentWorkspace?.id || "");
|
const { data, isLoading } = useGetWorkspaceGroupMemberships(currentWorkspace?.slug || "");
|
||||||
return (
|
return (
|
||||||
<TableContainer>
|
<TableContainer>
|
||||||
<Table>
|
<Table>
|
||||||
|
|||||||
Reference in New Issue
Block a user