mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Weave roles into groups
This commit is contained in:
@@ -1,11 +1,46 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TDbClient } from "@app/db";
|
||||
import { TableName } from "@app/db/schemas";
|
||||
import { ormify } from "@app/lib/knex";
|
||||
import { DatabaseError } from "@app/lib/errors";
|
||||
import { ormify, selectAllTableCols } from "@app/lib/knex";
|
||||
|
||||
export type TGroupDALFactory = ReturnType<typeof groupDALFactory>;
|
||||
|
||||
export const groupDALFactory = (db: TDbClient) => {
|
||||
const groupOrm = ormify(db, TableName.Groups);
|
||||
|
||||
return { ...groupOrm };
|
||||
const findByOrgId = async (orgId: string, tx?: Knex) => {
|
||||
try {
|
||||
const docs = await (tx || db)(TableName.Groups)
|
||||
.where(`${TableName.Groups}.orgId`, orgId)
|
||||
.leftJoin(TableName.OrgRoles, `${TableName.Groups}.roleId`, `${TableName.OrgRoles}.id`)
|
||||
.select(selectAllTableCols(TableName.Groups))
|
||||
// cr stands for custom role
|
||||
.select(db.ref("id").as("crId").withSchema(TableName.OrgRoles))
|
||||
.select(db.ref("name").as("crName").withSchema(TableName.OrgRoles))
|
||||
.select(db.ref("slug").as("crSlug").withSchema(TableName.OrgRoles))
|
||||
.select(db.ref("description").as("crDescription").withSchema(TableName.OrgRoles))
|
||||
.select(db.ref("permissions").as("crPermission").withSchema(TableName.OrgRoles));
|
||||
return docs.map(({ crId, crDescription, crSlug, crPermission, crName, ...el }) => ({
|
||||
...el,
|
||||
customRole: el.roleId
|
||||
? {
|
||||
id: crId,
|
||||
name: crName,
|
||||
slug: crSlug,
|
||||
permissions: crPermission,
|
||||
description: crDescription
|
||||
}
|
||||
: undefined
|
||||
}));
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "FindByOrgId" });
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
findByOrgId,
|
||||
...groupOrm
|
||||
};
|
||||
};
|
||||
|
||||
@@ -105,7 +105,7 @@ export const groupServiceFactory = ({ groupDAL, permissionService, licenseServic
|
||||
...(role
|
||||
? {
|
||||
role: customRole ? OrgMembershipRole.Custom : role,
|
||||
roleId: customRole?.id
|
||||
roleId: customRole?.id ?? null
|
||||
}
|
||||
: {})
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
IncidentContactsSchema,
|
||||
OrganizationsSchema,
|
||||
OrgMembershipsSchema,
|
||||
OrgRolesSchema,
|
||||
UsersSchema
|
||||
} from "@app/db/schemas";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
@@ -212,7 +213,17 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
groups: GroupsSchema.array()
|
||||
groups: GroupsSchema.merge(
|
||||
z.object({
|
||||
customRole: OrgRolesSchema.pick({
|
||||
id: true,
|
||||
name: true,
|
||||
slug: true,
|
||||
permissions: true,
|
||||
description: true
|
||||
}).optional()
|
||||
})
|
||||
).array()
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
@@ -46,6 +46,7 @@ export const registerIdentityOrgRouter = async (server: FastifyZodProvider) => {
|
||||
actorOrgId: req.permission.orgId,
|
||||
orgId: req.params.orgId
|
||||
});
|
||||
|
||||
return { identityMemberships };
|
||||
}
|
||||
});
|
||||
|
||||
@@ -119,7 +119,7 @@ export const orgServiceFactory = ({
|
||||
|
||||
const getOrgGroups = async ({ actor, actorId, orgId, actorAuthMethod, actorOrgId }: TGetOrgGroupsDTO) => {
|
||||
await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
const groups = await groupDAL.find({ orgId });
|
||||
const groups = await groupDAL.findByOrgId(orgId);
|
||||
return groups;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
import { TOrgRole } from "../roles/types";
|
||||
|
||||
export type TGroupOrgMembership = TGroup & {
|
||||
customRole?: TOrgRole;
|
||||
}
|
||||
|
||||
export type TGroup = {
|
||||
id: string;
|
||||
name: string;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
import { TGroup } from "../groups/types";
|
||||
import { TGroupOrgMembership } from "../groups/types";
|
||||
import { IdentityMembershipOrg } from "../identities/types";
|
||||
import {
|
||||
BillingDetails,
|
||||
@@ -412,7 +412,7 @@ export const useGetOrganizationGroups = (organizationId: string) => {
|
||||
queryFn: async () => {
|
||||
const {
|
||||
data: { groups }
|
||||
} = await apiRequest.get<{ groups: TGroup[] }>(`/api/v1/organization/${organizationId}/groups`);
|
||||
} = await apiRequest.get<{ groups: TGroupOrgMembership[] }>(`/api/v1/organization/${organizationId}/groups`);
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { useState } from "react";
|
||||
// import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||
import { faMagnifyingGlass, faPencil, faUsers, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
// Button,
|
||||
EmptyState,
|
||||
IconButton,
|
||||
Input,
|
||||
// Select,
|
||||
// SelectItem,
|
||||
Select,
|
||||
SelectItem,
|
||||
Table,
|
||||
TableContainer,
|
||||
TableSkeleton,
|
||||
@@ -19,14 +18,16 @@ import {
|
||||
Th,
|
||||
THead,
|
||||
Tooltip,
|
||||
Tr} from "@app/components/v2";
|
||||
Tr
|
||||
} from "@app/components/v2";
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSubjects,
|
||||
useOrganization} from "@app/context";
|
||||
import {
|
||||
useGetOrganizationGroups,
|
||||
// useGetOrgRoles
|
||||
useGetOrgRoles,
|
||||
useUpdateGroup
|
||||
} from "@app/hooks/api";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
|
||||
@@ -39,6 +40,11 @@ type Props = {
|
||||
groupId?: string;
|
||||
name?: string;
|
||||
slug?: string;
|
||||
role?: string;
|
||||
customRole?: {
|
||||
name: string;
|
||||
slug: string;
|
||||
}
|
||||
}
|
||||
) => void;
|
||||
};
|
||||
@@ -46,41 +52,40 @@ type Props = {
|
||||
export const OrgGroupsTable = ({
|
||||
handlePopUpOpen
|
||||
}: Props) => {
|
||||
// const { createNotification } = useNotificationContext();
|
||||
const { createNotification } = useNotificationContext();
|
||||
const [searchGroupsFilter, setSearchGroupsFilter] = useState("");
|
||||
const { currentOrg } = useOrganization();
|
||||
const orgId = currentOrg?.id || "";
|
||||
const { isLoading, data: groups } = useGetOrganizationGroups(orgId);
|
||||
const { mutateAsync: updateMutateAsync } = useUpdateGroup();
|
||||
|
||||
// const { data: roles } = useGetOrgRoles(orgId);
|
||||
const { data: roles } = useGetOrgRoles(orgId);
|
||||
|
||||
console.log("OrgGroupsTable groups: ", groups);
|
||||
console.log("OrgGroupsTable roles: ", groups);
|
||||
|
||||
// const handleChangeRole = ({
|
||||
// groupId,
|
||||
// role
|
||||
// }: {
|
||||
// groupId: string;
|
||||
// role: string;
|
||||
// }) => {
|
||||
// try {
|
||||
const handleChangeRole = async ({
|
||||
currentSlug,
|
||||
role
|
||||
}: {
|
||||
currentSlug: string;
|
||||
role: string;
|
||||
}) => {
|
||||
try {
|
||||
await updateMutateAsync({
|
||||
currentSlug,
|
||||
role
|
||||
});
|
||||
|
||||
// // TODO
|
||||
|
||||
// createNotification({
|
||||
// text: "Successfully updated group role",
|
||||
// type: "success"
|
||||
// });
|
||||
// } catch (err) {
|
||||
// console.error(err);
|
||||
|
||||
// createNotification({
|
||||
// text: "Failed to update group role",
|
||||
// type: "error"
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
createNotification({
|
||||
text: "Successfully updated group role",
|
||||
type: "success"
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: "Failed to update group role",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -102,12 +107,40 @@ export const OrgGroupsTable = ({
|
||||
</THead>
|
||||
<TBody>
|
||||
{isLoading && <TableSkeleton columns={4} innerKey="org-groups" />}
|
||||
{!isLoading && groups?.map(({ id, name, slug }) => {
|
||||
{!isLoading && groups?.map(({ id, name, slug, role, customRole }) => {
|
||||
return (
|
||||
<Tr className="h-10" key={`org-group-${id}`}>
|
||||
<Td>{name}</Td>
|
||||
<Td>{slug}</Td>
|
||||
<Td>N/A</Td>
|
||||
<Td>
|
||||
<OrgPermissionCan
|
||||
I={OrgPermissionActions.Edit}
|
||||
a={OrgPermissionSubjects.Identity}
|
||||
>
|
||||
{(isAllowed) => {
|
||||
return (
|
||||
<Select
|
||||
value={role === "custom" ? (customRole?.slug as string) : role}
|
||||
isDisabled={!isAllowed}
|
||||
className="w-40 bg-mineshaft-600"
|
||||
dropdownContainerClassName="border border-mineshaft-600 bg-mineshaft-800"
|
||||
onValueChange={(selectedRole) =>
|
||||
handleChangeRole({
|
||||
currentSlug: slug,
|
||||
role: selectedRole
|
||||
})
|
||||
}
|
||||
>
|
||||
{(roles || []).map(({ slug: roleSlug, name: roleName }) => (
|
||||
<SelectItem value={roleSlug} key={`role-option-${roleSlug}`}>
|
||||
{roleName}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
}}
|
||||
</OrgPermissionCan>
|
||||
</Td>
|
||||
<Td>
|
||||
<div className="flex items-center justify-end">
|
||||
<OrgPermissionCan
|
||||
@@ -121,7 +154,9 @@ export const OrgGroupsTable = ({
|
||||
handlePopUpOpen("group", {
|
||||
groupId: id,
|
||||
name,
|
||||
slug
|
||||
slug,
|
||||
role,
|
||||
customRole
|
||||
});
|
||||
}}
|
||||
size="lg"
|
||||
@@ -143,7 +178,6 @@ export const OrgGroupsTable = ({
|
||||
<Tooltip content="Delete group">
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
console.log("Delete group");
|
||||
handlePopUpOpen("deleteGroup", {
|
||||
slug,
|
||||
name
|
||||
|
||||
@@ -52,9 +52,6 @@ export const IdentityTable = ({ handlePopUpOpen }: Props) => {
|
||||
const { data, isLoading } = useGetIdentityMembershipOrgs(orgId);
|
||||
|
||||
const { data: roles } = useGetOrgRoles(orgId);
|
||||
|
||||
console.log("IdentityTable data: ", data);
|
||||
console.log("IdentityTable roles: ", roles);
|
||||
|
||||
const handleChangeRole = async ({ identityId, role }: { identityId: string; role: string }) => {
|
||||
try {
|
||||
|
||||
@@ -62,8 +62,6 @@ export const OrgMembersTable = ({ handlePopUpOpen, setCompleteInviteLink }: Prop
|
||||
|
||||
const { data: serverDetails } = useFetchServerStatus();
|
||||
const { data: members, isLoading: isMembersLoading } = useGetOrgUsers(orgId);
|
||||
|
||||
console.log("OrgGroupsTable members: ", members);
|
||||
|
||||
const { mutateAsync: addUserMutateAsync } = useAddUserToOrg();
|
||||
const { mutateAsync: updateUserOrgRole } = useUpdateOrgUserRole();
|
||||
|
||||
Reference in New Issue
Block a user