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 { TDbClient } from "@app/db";
|
||||||
import { TableName } from "@app/db/schemas";
|
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 type TGroupDALFactory = ReturnType<typeof groupDALFactory>;
|
||||||
|
|
||||||
export const groupDALFactory = (db: TDbClient) => {
|
export const groupDALFactory = (db: TDbClient) => {
|
||||||
const groupOrm = ormify(db, TableName.Groups);
|
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
|
||||||
? {
|
? {
|
||||||
role: customRole ? OrgMembershipRole.Custom : role,
|
role: customRole ? OrgMembershipRole.Custom : role,
|
||||||
roleId: customRole?.id
|
roleId: customRole?.id ?? null
|
||||||
}
|
}
|
||||||
: {})
|
: {})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
IncidentContactsSchema,
|
IncidentContactsSchema,
|
||||||
OrganizationsSchema,
|
OrganizationsSchema,
|
||||||
OrgMembershipsSchema,
|
OrgMembershipsSchema,
|
||||||
|
OrgRolesSchema,
|
||||||
UsersSchema
|
UsersSchema
|
||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
@@ -212,7 +213,17 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
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,
|
actorOrgId: req.permission.orgId,
|
||||||
orgId: req.params.orgId
|
orgId: req.params.orgId
|
||||||
});
|
});
|
||||||
|
|
||||||
return { identityMemberships };
|
return { identityMemberships };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ 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);
|
await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||||
const groups = await groupDAL.find({ orgId });
|
const groups = await groupDAL.findByOrgId(orgId);
|
||||||
return groups;
|
return groups;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
import { TOrgRole } from "../roles/types";
|
||||||
|
|
||||||
|
export type TGroupOrgMembership = TGroup & {
|
||||||
|
customRole?: TOrgRole;
|
||||||
|
}
|
||||||
|
|
||||||
export type TGroup = {
|
export type TGroup = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|||||||
|
|
||||||
import { apiRequest } from "@app/config/request";
|
import { apiRequest } from "@app/config/request";
|
||||||
|
|
||||||
import { TGroup } from "../groups/types";
|
import { TGroupOrgMembership } from "../groups/types";
|
||||||
import { IdentityMembershipOrg } from "../identities/types";
|
import { IdentityMembershipOrg } from "../identities/types";
|
||||||
import {
|
import {
|
||||||
BillingDetails,
|
BillingDetails,
|
||||||
@@ -412,7 +412,7 @@ export const useGetOrganizationGroups = (organizationId: string) => {
|
|||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const {
|
const {
|
||||||
data: { groups }
|
data: { groups }
|
||||||
} = await apiRequest.get<{ groups: TGroup[] }>(`/api/v1/organization/${organizationId}/groups`);
|
} = await apiRequest.get<{ groups: TGroupOrgMembership[] }>(`/api/v1/organization/${organizationId}/groups`);
|
||||||
|
|
||||||
return groups;
|
return groups;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
// import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
|
||||||
import { faMagnifyingGlass, faPencil, faUsers, faXmark } from "@fortawesome/free-solid-svg-icons";
|
import { faMagnifyingGlass, faPencil, faUsers, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
|
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||||
import { OrgPermissionCan } from "@app/components/permissions";
|
import { OrgPermissionCan } from "@app/components/permissions";
|
||||||
import {
|
import {
|
||||||
// Button,
|
|
||||||
EmptyState,
|
EmptyState,
|
||||||
IconButton,
|
IconButton,
|
||||||
Input,
|
Input,
|
||||||
// Select,
|
Select,
|
||||||
// SelectItem,
|
SelectItem,
|
||||||
Table,
|
Table,
|
||||||
TableContainer,
|
TableContainer,
|
||||||
TableSkeleton,
|
TableSkeleton,
|
||||||
@@ -19,14 +18,16 @@ import {
|
|||||||
Th,
|
Th,
|
||||||
THead,
|
THead,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Tr} from "@app/components/v2";
|
Tr
|
||||||
|
} from "@app/components/v2";
|
||||||
import {
|
import {
|
||||||
OrgPermissionActions,
|
OrgPermissionActions,
|
||||||
OrgPermissionSubjects,
|
OrgPermissionSubjects,
|
||||||
useOrganization} from "@app/context";
|
useOrganization} from "@app/context";
|
||||||
import {
|
import {
|
||||||
useGetOrganizationGroups,
|
useGetOrganizationGroups,
|
||||||
// useGetOrgRoles
|
useGetOrgRoles,
|
||||||
|
useUpdateGroup
|
||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
@@ -39,6 +40,11 @@ type Props = {
|
|||||||
groupId?: string;
|
groupId?: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
slug?: string;
|
slug?: string;
|
||||||
|
role?: string;
|
||||||
|
customRole?: {
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
) => void;
|
) => void;
|
||||||
};
|
};
|
||||||
@@ -46,41 +52,40 @@ type Props = {
|
|||||||
export const OrgGroupsTable = ({
|
export const OrgGroupsTable = ({
|
||||||
handlePopUpOpen
|
handlePopUpOpen
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
// const { createNotification } = useNotificationContext();
|
const { createNotification } = useNotificationContext();
|
||||||
const [searchGroupsFilter, setSearchGroupsFilter] = useState("");
|
const [searchGroupsFilter, setSearchGroupsFilter] = useState("");
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
const { isLoading, data: groups } = useGetOrganizationGroups(orgId);
|
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);
|
const handleChangeRole = async ({
|
||||||
console.log("OrgGroupsTable roles: ", groups);
|
currentSlug,
|
||||||
|
role
|
||||||
// const handleChangeRole = ({
|
}: {
|
||||||
// groupId,
|
currentSlug: string;
|
||||||
// role
|
role: string;
|
||||||
// }: {
|
}) => {
|
||||||
// groupId: string;
|
try {
|
||||||
// role: string;
|
await updateMutateAsync({
|
||||||
// }) => {
|
currentSlug,
|
||||||
// try {
|
role
|
||||||
|
});
|
||||||
|
|
||||||
// // TODO
|
createNotification({
|
||||||
|
text: "Successfully updated group role",
|
||||||
// createNotification({
|
type: "success"
|
||||||
// text: "Successfully updated group role",
|
});
|
||||||
// type: "success"
|
} catch (err) {
|
||||||
// });
|
console.error(err);
|
||||||
// } catch (err) {
|
createNotification({
|
||||||
// console.error(err);
|
text: "Failed to update group role",
|
||||||
|
type: "error"
|
||||||
// createNotification({
|
});
|
||||||
// text: "Failed to update group role",
|
}
|
||||||
// type: "error"
|
}
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -102,12 +107,40 @@ export const OrgGroupsTable = ({
|
|||||||
</THead>
|
</THead>
|
||||||
<TBody>
|
<TBody>
|
||||||
{isLoading && <TableSkeleton columns={4} innerKey="org-groups" />}
|
{isLoading && <TableSkeleton columns={4} innerKey="org-groups" />}
|
||||||
{!isLoading && groups?.map(({ id, name, slug }) => {
|
{!isLoading && groups?.map(({ id, name, slug, role, customRole }) => {
|
||||||
return (
|
return (
|
||||||
<Tr className="h-10" key={`org-group-${id}`}>
|
<Tr className="h-10" key={`org-group-${id}`}>
|
||||||
<Td>{name}</Td>
|
<Td>{name}</Td>
|
||||||
<Td>{slug}</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>
|
<Td>
|
||||||
<div className="flex items-center justify-end">
|
<div className="flex items-center justify-end">
|
||||||
<OrgPermissionCan
|
<OrgPermissionCan
|
||||||
@@ -121,7 +154,9 @@ export const OrgGroupsTable = ({
|
|||||||
handlePopUpOpen("group", {
|
handlePopUpOpen("group", {
|
||||||
groupId: id,
|
groupId: id,
|
||||||
name,
|
name,
|
||||||
slug
|
slug,
|
||||||
|
role,
|
||||||
|
customRole
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
size="lg"
|
size="lg"
|
||||||
@@ -143,7 +178,6 @@ export const OrgGroupsTable = ({
|
|||||||
<Tooltip content="Delete group">
|
<Tooltip content="Delete group">
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
console.log("Delete group");
|
|
||||||
handlePopUpOpen("deleteGroup", {
|
handlePopUpOpen("deleteGroup", {
|
||||||
slug,
|
slug,
|
||||||
name
|
name
|
||||||
|
|||||||
@@ -52,9 +52,6 @@ export const IdentityTable = ({ handlePopUpOpen }: Props) => {
|
|||||||
const { data, isLoading } = useGetIdentityMembershipOrgs(orgId);
|
const { data, isLoading } = useGetIdentityMembershipOrgs(orgId);
|
||||||
|
|
||||||
const { data: roles } = useGetOrgRoles(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 }) => {
|
const handleChangeRole = async ({ identityId, role }: { identityId: string; role: string }) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -62,8 +62,6 @@ export const OrgMembersTable = ({ handlePopUpOpen, setCompleteInviteLink }: Prop
|
|||||||
|
|
||||||
const { data: serverDetails } = useFetchServerStatus();
|
const { data: serverDetails } = useFetchServerStatus();
|
||||||
const { data: members, isLoading: isMembersLoading } = useGetOrgUsers(orgId);
|
const { data: members, isLoading: isMembersLoading } = useGetOrgUsers(orgId);
|
||||||
|
|
||||||
console.log("OrgGroupsTable members: ", members);
|
|
||||||
|
|
||||||
const { mutateAsync: addUserMutateAsync } = useAddUserToOrg();
|
const { mutateAsync: addUserMutateAsync } = useAddUserToOrg();
|
||||||
const { mutateAsync: updateUserOrgRole } = useUpdateOrgUserRole();
|
const { mutateAsync: updateUserOrgRole } = useUpdateOrgUserRole();
|
||||||
|
|||||||
Reference in New Issue
Block a user