Added authz logic to MI

This commit is contained in:
Tuan Dang
2023-11-25 18:37:20 +07:00
parent 29016fbb23
commit 5b923c25b5
19 changed files with 286 additions and 133 deletions
@@ -16,7 +16,7 @@ export enum OrgPermissionSubjects {
Sso = "sso",
Billing = "billing",
SecretScanning = "secret-scanning",
ServiceTokens = "service-tokens"
MachineIdentity = "machine-identity"
}
export type OrgPermissionSet =
@@ -29,6 +29,6 @@ export type OrgPermissionSet =
| [OrgPermissionActions, OrgPermissionSubjects.Sso]
| [OrgPermissionActions, OrgPermissionSubjects.SecretScanning]
| [OrgPermissionActions, OrgPermissionSubjects.Billing]
| [OrgPermissionActions, OrgPermissionSubjects.ServiceTokens];
| [OrgPermissionActions, OrgPermissionSubjects.MachineIdentity];
export type TOrgPermission = MongoAbility<OrgPermissionSet>;
@@ -22,7 +22,8 @@ export enum ProjectPermissionSub {
Secrets = "secrets",
SecretRollback = "secret-rollback",
SecretApproval = "secret-approval",
SecretRotation = "secret-rotation"
SecretRotation = "secret-rotation",
MachineIdentity = "machine-identity"
}
type SubjectFields = {
@@ -44,6 +45,7 @@ export type ProjectPermissionSet =
| [ProjectPermissionActions, ProjectPermissionSub.Environments]
| [ProjectPermissionActions, ProjectPermissionSub.IpAllowList]
| [ProjectPermissionActions, ProjectPermissionSub.Settings]
| [ProjectPermissionActions, ProjectPermissionSub.MachineIdentity]
| [ProjectPermissionActions, ProjectPermissionSub.ServiceTokens]
| [ProjectPermissionActions, ProjectPermissionSub.SecretApproval]
| [ProjectPermissionActions, ProjectPermissionSub.SecretRotation]
@@ -26,7 +26,7 @@ export const MembersPage = withPermission(
<TabList>
<Tab value={TabSections.Member}>People</Tab>
<Tab value={TabSections.MachineIdentities}>Machine Identities</Tab>
<Tab value={TabSections.Roles}>Roles</Tab>
<Tab value={TabSections.Roles}>Organization Roles</Tab>
</TabList>
<TabPanel value={TabSections.Member}>
<OrgMembersTab />
@@ -32,7 +32,7 @@ import {
useGetRoles,
useUpdateMachineIdentity
} from "@app/hooks/api";
import { ServiceTokenV3TrustedIp } from "@app/hooks/api/serviceTokens/types";
import { MachineTrustedIp } from "@app/hooks/api/machineIdentities/types";
import { UsePopUpState } from "@app/hooks/usePopUp";
enum TabSections {
@@ -50,7 +50,7 @@ const expirations = [
];
const schema = yup.object({
name: yup.string().required("ST V3 name is required"),
name: yup.string().required("MI name is required"),
expiresIn: yup.string(),
accessTokenTTL: yup
.string()
@@ -63,7 +63,7 @@ const schema = yup.object({
return !Number.isNaN(num) && num > 0 && String(num) === value;
})
.required("Access Token TTL is required"),
role: yup.string().required("ST V3 role is required"),
role: yup.string(),
trustedIps: yup
.array(
yup.object({
@@ -146,7 +146,7 @@ export const AddMachineIdentityModal = ({
name: string;
slug: string;
};
trustedIps: ServiceTokenV3TrustedIp[];
trustedIps: MachineTrustedIp[];
accessTokenTTL: number;
isRefreshTokenRotationEnabled: boolean;
};
@@ -161,7 +161,7 @@ export const AddMachineIdentityModal = ({
trustedIps: machineIdentity.trustedIps.map(({
ipAddress,
prefix
}: ServiceTokenV3TrustedIp) => {
}: MachineTrustedIp) => {
return ({
ipAddress: `${ipAddress}${prefix !== undefined ? `/${prefix}` : ""}`
});
@@ -206,9 +206,9 @@ export const AddMachineIdentityModal = ({
await updateMutateAsync({
machineId: machineIdentity.machineId,
name,
role,
role: role || undefined,
trustedIps,
expiresIn: expiresIn === "" ? undefined : Number(expiresIn),
expiresIn: (!expiresIn) ? undefined : Number(expiresIn),
accessTokenTTL: Number(accessTokenTTL),
isRefreshTokenRotationEnabled
});
@@ -218,10 +218,10 @@ export const AddMachineIdentityModal = ({
const { refreshToken } = await createMutateAsync({
name,
role,
role: role || undefined,
organizationId: orgId,
trustedIps,
expiresIn: expiresIn === "" ? undefined : Number(expiresIn),
expiresIn: (!expiresIn) ? undefined : Number(expiresIn),
accessTokenTTL: Number(accessTokenTTL),
isRefreshTokenRotationEnabled
});
@@ -53,7 +53,7 @@ export const MachineIdentitySection = withPermission(
</p>
<OrgPermissionCan
I={OrgPermissionActions.Create}
a={OrgPermissionSubjects.ServiceTokens}
a={OrgPermissionSubjects.MachineIdentity}
>
{(isAllowed) => (
<Button
@@ -92,5 +92,5 @@ export const MachineIdentitySection = withPermission(
</div>
);
},
{ action: OrgPermissionActions.Read, subject: OrgPermissionSubjects.ServiceTokens }
{ action: OrgPermissionActions.Read, subject: OrgPermissionSubjects.MachineIdentity }
);
@@ -1,4 +1,3 @@
import { useCallback } from "react";
import { faPencil,faServer, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { format } from "date-fns";
@@ -28,7 +27,7 @@ import {
useGetRoles,
useUpdateMachineIdentity
} from "@app/hooks/api";
import { ServiceTokenV3TrustedIp } from "@app/hooks/api/serviceTokens/types"
import { MachineTrustedIp } from "@app/hooks/api/machineIdentities/types";
import { UsePopUpState } from "@app/hooks/usePopUp";
type Props = {
@@ -42,7 +41,7 @@ type Props = {
name: string;
slug: string;
};
trustedIps?: ServiceTokenV3TrustedIp[];
trustedIps?: MachineTrustedIp[];
accessTokenTTL?: number;
isRefreshTokenRotationEnabled?: boolean;
}
@@ -115,13 +114,6 @@ export const MachineIdentityTable = ({
// });
// }
// }
const findRoleFromId = useCallback(
(roleId: string) => {
return (roles || []).find(({ _id: id }) => id === roleId);
},
[roles]
);
return (
<TableContainer>
@@ -163,7 +155,7 @@ export const MachineIdentityTable = ({
{/* <Td>
<OrgPermissionCan
I={OrgPermissionActions.Edit}
a={OrgPermissionSubjects.ServiceTokens}
a={OrgPermissionSubjects.MachineIdentity}
>
{(isAllowed) => (
<Switch
@@ -183,13 +175,13 @@ export const MachineIdentityTable = ({
<Td>
<OrgPermissionCan
I={OrgPermissionActions.Edit}
a={OrgPermissionSubjects.ServiceTokens}
a={OrgPermissionSubjects.MachineIdentity}
>
{(isAllowed) => {
return (
<Select
value={
role === "custom" ? findRoleFromId(customRole)?.slug : role
role === "custom" ? customRole.slug : role
}
isDisabled={!isAllowed}
className="w-40 bg-mineshaft-600"
@@ -231,7 +223,7 @@ export const MachineIdentityTable = ({
<Td className="flex justify-end">
<OrgPermissionCan
I={OrgPermissionActions.Edit}
a={OrgPermissionSubjects.ServiceTokens}
a={OrgPermissionSubjects.MachineIdentity}
>
{(isAllowed) => (
<IconButton
@@ -258,7 +250,7 @@ export const MachineIdentityTable = ({
</OrgPermissionCan>
<OrgPermissionCan
I={OrgPermissionActions.Delete}
a={OrgPermissionSubjects.ServiceTokens}
a={OrgPermissionSubjects.MachineIdentity}
>
{(isAllowed) => (
<IconButton
@@ -5,10 +5,10 @@ import {
faContactCard,
faMagnifyingGlass,
faMoneyBill,
faServer,
faSignIn,
faUserCog,
faUsers
} from "@fortawesome/free-solid-svg-icons";
faUsers} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod";
@@ -40,6 +40,12 @@ const SIMPLE_PERMISSION_OPTIONS = [
icon: faUsers,
formName: "member"
},
{
title: "Machine identity management",
subtitle: "Create, view, update and remove machine identities from the organization",
icon: faServer,
formName: "machine-identity"
},
{
title: "Billing & usage",
subtitle: "Modify organization subscription plan",
@@ -31,7 +31,8 @@ export const formSchema = z.object({
"incident-contact": generalPermissionSchema,
"secret-scanning": generalPermissionSchema,
sso: generalPermissionSchema,
billing: generalPermissionSchema
billing: generalPermissionSchema,
"machine-identity": generalPermissionSchema
})
.optional()
});
@@ -32,7 +32,7 @@ export const MembersPage = withProjectPermission(
<Tab value={TabSections.Member}>People</Tab>
<Tab value={TabSections.MachineIdentities}>Machine Identities</Tab>
<Tab value={TabSections.ServiceTokens}>Service Tokens</Tab>
<Tab value={TabSections.Roles}>Roles</Tab>
<Tab value={TabSections.Roles}>Project Roles</Tab>
</TabList>
<TabPanel value={TabSections.Member}>
<motion.div
@@ -26,8 +26,8 @@ import {
import { UsePopUpState } from "@app/hooks/usePopUp";
const schema = yup.object({
machineId: yup.string().required("ST V3 id is required"),
role: yup.string().required("ST V3 role is required")
machineId: yup.string().required("Machine identity id is required"),
role: yup.string().required("Machine identity role is required")
}).required();
export type FormData = yup.InferType<typeof schema>;
@@ -124,7 +124,7 @@ export const AddMachineIdentityModal = ({
defaultValue={filteredMachineMembershipOrgs?.[0]?._id}
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
<FormControl
label="Service Account"
label="Machine Identity"
errorText={error?.message}
isError={Boolean(error)}
>
@@ -66,7 +66,7 @@ export const MachineIdentitySection = withProjectPermission(
</p>
<ProjectPermissionCan
I={ProjectPermissionActions.Create}
a={ProjectPermissionSub.ServiceTokens}
a={ProjectPermissionSub.MachineIdentity}
>
{(isAllowed) => (
<Button
@@ -104,5 +104,5 @@ export const MachineIdentitySection = withProjectPermission(
</div>
);
},
{ action: ProjectPermissionActions.Read, subject: ProjectPermissionSub.ServiceTokens }
{ action: ProjectPermissionActions.Read, subject: ProjectPermissionSub.MachineIdentity }
);
@@ -135,7 +135,7 @@ export const MachineIdentityTable = ({
<Td>
<ProjectPermissionCan
I={ProjectPermissionActions.Edit}
a={ProjectPermissionSub.ServiceTokens}
a={ProjectPermissionSub.MachineIdentity}
>
{(isAllowed) => {
return (
@@ -156,7 +156,7 @@ export const MachineIdentityTable = ({
{(roles || [])
.map(({ slug, name: roleName }) => (
<SelectItem value={slug} key={`owner-option-${slug}`}>
{roleName}
{roleName}
</SelectItem>
))}
</Select>
@@ -168,7 +168,7 @@ export const MachineIdentityTable = ({
<Td className="flex justify-end">
<ProjectPermissionCan
I={ProjectPermissionActions.Delete}
a={ProjectPermissionSub.ServiceTokens}
a={ProjectPermissionSub.MachineIdentity}
>
{(isAllowed) => (
<IconButton