mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 22:27:22 +00:00
feat: added wizard and fixed assume privilege issue
This commit is contained in:
+3
-2
@@ -2,12 +2,13 @@ import { faInfoCircle } from "@fortawesome/free-solid-svg-icons";
|
|||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
import { Button } from "@app/components/v2";
|
import { Button } from "@app/components/v2";
|
||||||
import { useProject, useProjectPermission } from "@app/context";
|
import { useOrganization, useProject, useProjectPermission } from "@app/context";
|
||||||
import { getProjectHomePage } from "@app/helpers/project";
|
import { getProjectHomePage } from "@app/helpers/project";
|
||||||
import { useRemoveAssumeProjectPrivilege } from "@app/hooks/api";
|
import { useRemoveAssumeProjectPrivilege } from "@app/hooks/api";
|
||||||
import { ActorType } from "@app/hooks/api/auditLogs/enums";
|
import { ActorType } from "@app/hooks/api/auditLogs/enums";
|
||||||
|
|
||||||
export const AssumePrivilegeModeBanner = () => {
|
export const AssumePrivilegeModeBanner = () => {
|
||||||
|
const { isSubOrganization, currentOrg } = useOrganization();
|
||||||
const { currentProject } = useProject();
|
const { currentProject } = useProject();
|
||||||
const exitAssumePrivilegeMode = useRemoveAssumeProjectPrivilege();
|
const exitAssumePrivilegeMode = useRemoveAssumeProjectPrivilege();
|
||||||
const { assumedPrivilegeDetails } = useProjectPermission();
|
const { assumedPrivilegeDetails } = useProjectPermission();
|
||||||
@@ -36,7 +37,7 @@ export const AssumePrivilegeModeBanner = () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
const url = getProjectHomePage(currentProject.type, currentProject.environments);
|
const url = `${getProjectHomePage(currentProject.type, currentProject.environments)}${isSubOrganization ? `?subOrganization=${currentOrg.slug}` : ""}`;
|
||||||
window.location.href = url.replace("$projectId", currentProject.id);
|
window.location.href = url.replace("$projectId", currentProject.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+103
-45
@@ -22,7 +22,7 @@ import {
|
|||||||
} from "@app/context";
|
} from "@app/context";
|
||||||
import { OrgPermissionMachineIdentityAuthTemplateActions } from "@app/context/OrgPermissionContext/types";
|
import { OrgPermissionMachineIdentityAuthTemplateActions } from "@app/context/OrgPermissionContext/types";
|
||||||
import { withPermission } from "@app/hoc";
|
import { withPermission } from "@app/hoc";
|
||||||
import { useDeleteOrgIdentity } from "@app/hooks/api";
|
import { subOrganizationsQuery, useDeleteOrgIdentity } from "@app/hooks/api";
|
||||||
import { useDeleteIdentityAuthTemplate } from "@app/hooks/api/identityAuthTemplates";
|
import { useDeleteIdentityAuthTemplate } from "@app/hooks/api/identityAuthTemplates";
|
||||||
import { usePopUp } from "@app/hooks/usePopUp";
|
import { usePopUp } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
@@ -33,6 +33,15 @@ import { IdentityTokenAuthTokenModal } from "./IdentityTokenAuthTokenModal";
|
|||||||
import { MachineAuthTemplateUsagesModal } from "./MachineAuthTemplateUsagesModal";
|
import { MachineAuthTemplateUsagesModal } from "./MachineAuthTemplateUsagesModal";
|
||||||
import { OrgIdentityLinkForm } from "./OrgIdentityLinkForm";
|
import { OrgIdentityLinkForm } from "./OrgIdentityLinkForm";
|
||||||
import { OrgIdentityModal } from "./OrgIdentityModal";
|
import { OrgIdentityModal } from "./OrgIdentityModal";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { AnimatePresence, motion } from "framer-motion";
|
||||||
|
import { LinkIcon, PlusIcon } from "lucide-react";
|
||||||
|
|
||||||
|
enum IdentityWizardSteps {
|
||||||
|
SelectAction = "select-action",
|
||||||
|
LinkIdentity = "link-identity",
|
||||||
|
OrganizationIdentity = "project-identity"
|
||||||
|
}
|
||||||
|
|
||||||
export const IdentitySection = withPermission(
|
export const IdentitySection = withPermission(
|
||||||
() => {
|
() => {
|
||||||
@@ -40,6 +49,8 @@ export const IdentitySection = withPermission(
|
|||||||
const { currentOrg, isSubOrganization } = useOrganization();
|
const { currentOrg, isSubOrganization } = useOrganization();
|
||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
|
|
||||||
|
const [wizardStep, setWizardStep] = useState(IdentityWizardSteps.SelectAction);
|
||||||
|
|
||||||
const { mutateAsync: deleteMutateAsync } = useDeleteOrgIdentity();
|
const { mutateAsync: deleteMutateAsync } = useDeleteOrgIdentity();
|
||||||
const { mutateAsync: deleteTemplateMutateAsync } = useDeleteIdentityAuthTemplate();
|
const { mutateAsync: deleteTemplateMutateAsync } = useDeleteIdentityAuthTemplate();
|
||||||
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||||
@@ -54,7 +65,6 @@ export const IdentitySection = withPermission(
|
|||||||
"editTemplate",
|
"editTemplate",
|
||||||
"deleteTemplate",
|
"deleteTemplate",
|
||||||
"viewUsages",
|
"viewUsages",
|
||||||
"linkIdentity",
|
|
||||||
"addOptions"
|
"addOptions"
|
||||||
] as const);
|
] as const);
|
||||||
|
|
||||||
@@ -119,6 +129,11 @@ export const IdentitySection = withPermission(
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isSubOrganization) {
|
||||||
|
setWizardStep(IdentityWizardSteps.OrganizationIdentity);
|
||||||
|
}
|
||||||
|
|
||||||
handlePopUpOpen("identity");
|
handlePopUpOpen("identity");
|
||||||
}}
|
}}
|
||||||
isDisabled={!isAllowed}
|
isDisabled={!isAllowed}
|
||||||
@@ -127,42 +142,6 @@ export const IdentitySection = withPermission(
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</OrgPermissionCan>
|
</OrgPermissionCan>
|
||||||
{isSubOrganization && (
|
|
||||||
<DropdownMenu
|
|
||||||
open={popUp.addOptions.isOpen}
|
|
||||||
onOpenChange={(isOpen) => handlePopUpToggle("addOptions", isOpen)}
|
|
||||||
>
|
|
||||||
<DropdownMenuTrigger>
|
|
||||||
<Button
|
|
||||||
variant="outline_bg"
|
|
||||||
className="rounded-l-none border-l-mineshaft-800 px-3"
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={faChevronDown} />
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent align="end" sideOffset={6} className="p-1">
|
|
||||||
<OrgPermissionCan
|
|
||||||
I={OrgPermissionIdentityActions.Create}
|
|
||||||
a={OrgPermissionSubjects.Identity}
|
|
||||||
>
|
|
||||||
{(isAllowed) => (
|
|
||||||
<Button
|
|
||||||
variant="outline_bg"
|
|
||||||
className="w-full"
|
|
||||||
isDisabled={!isAllowed}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faLink} />}
|
|
||||||
onClick={() => {
|
|
||||||
handlePopUpClose("addOptions");
|
|
||||||
handlePopUpOpen("linkIdentity");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Assign Org Identity
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</OrgPermissionCan>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<IdentityTable handlePopUpOpen={handlePopUpOpen} />
|
<IdentityTable handlePopUpOpen={handlePopUpOpen} />
|
||||||
@@ -202,7 +181,6 @@ export const IdentitySection = withPermission(
|
|||||||
</div>
|
</div>
|
||||||
<IdentityAuthTemplatesTable handlePopUpOpen={handlePopUpOpen} />
|
<IdentityAuthTemplatesTable handlePopUpOpen={handlePopUpOpen} />
|
||||||
</div>
|
</div>
|
||||||
<OrgIdentityModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
|
||||||
<IdentityAuthTemplateModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
<IdentityAuthTemplateModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||||
<MachineAuthTemplateUsagesModal
|
<MachineAuthTemplateUsagesModal
|
||||||
isOpen={popUp.viewUsages.isOpen}
|
isOpen={popUp.viewUsages.isOpen}
|
||||||
@@ -216,19 +194,99 @@ export const IdentitySection = withPermission(
|
|||||||
?.name || ""
|
?.name || ""
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
<IdentityTokenAuthTokenModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={popUp.linkIdentity.isOpen}
|
isOpen={popUp.identity.isOpen}
|
||||||
onOpenChange={(isOpen) => handlePopUpToggle("linkIdentity", isOpen)}
|
onOpenChange={(open) => {
|
||||||
|
handlePopUpToggle("identity", open);
|
||||||
|
if (!open) {
|
||||||
|
setWizardStep(IdentityWizardSteps.SelectAction);
|
||||||
|
}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<ModalContent
|
<ModalContent
|
||||||
title="Assign Existing Identity"
|
|
||||||
subTitle="Assign an existing identity from your root organization to this sub organization. The identity will continue to be managed at its original scope."
|
|
||||||
bodyClassName="overflow-visible"
|
bodyClassName="overflow-visible"
|
||||||
|
title="Add Identity"
|
||||||
|
subTitle={
|
||||||
|
isSubOrganization ? "Create a new identity or assign an existing identity" : undefined
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<OrgIdentityLinkForm onClose={() => handlePopUpClose("linkIdentity")} />
|
<AnimatePresence mode="wait">
|
||||||
|
{wizardStep === IdentityWizardSteps.SelectAction && (
|
||||||
|
<motion.div
|
||||||
|
key="select-type-step"
|
||||||
|
transition={{ duration: 0.1 }}
|
||||||
|
initial={{ opacity: 0, translateX: 30 }}
|
||||||
|
animate={{ opacity: 1, translateX: 0 }}
|
||||||
|
exit={{ opacity: 0, translateX: -30 }}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="cursor-pointer rounded-md border border-mineshaft-600 p-4 transition-all hover:bg-mineshaft-700"
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
onClick={() => setWizardStep(IdentityWizardSteps.OrganizationIdentity)}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
setWizardStep(IdentityWizardSteps.OrganizationIdentity);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<PlusIcon size="1rem" />
|
||||||
|
<div>Create New Identity</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 text-xs text-mineshaft-300">
|
||||||
|
Create a new machine identity specifically for this sub-organization. This
|
||||||
|
identity will be managed at the sub-organization level.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="mt-4 cursor-pointer rounded-md border border-mineshaft-600 p-4 transition-all hover:bg-mineshaft-700"
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
onClick={() => setWizardStep(IdentityWizardSteps.LinkIdentity)}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
setWizardStep(IdentityWizardSteps.LinkIdentity);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<LinkIcon size="1rem" />
|
||||||
|
<div>Assign Existing Identity</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 text-xs text-mineshaft-300">
|
||||||
|
Assign an existing identity from your parent organization. The identity will
|
||||||
|
continue to be managed at its original scope.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
{wizardStep === IdentityWizardSteps.OrganizationIdentity && (
|
||||||
|
<motion.div
|
||||||
|
key="identity-step"
|
||||||
|
transition={{ duration: 0.1 }}
|
||||||
|
initial={{ opacity: 0, translateX: 30 }}
|
||||||
|
animate={{ opacity: 1, translateX: 0 }}
|
||||||
|
exit={{ opacity: 0, translateX: -30 }}
|
||||||
|
>
|
||||||
|
<OrgIdentityModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
{wizardStep === IdentityWizardSteps.LinkIdentity && (
|
||||||
|
<motion.div
|
||||||
|
key="link-step"
|
||||||
|
transition={{ duration: 0.1 }}
|
||||||
|
initial={{ opacity: 0, translateX: 30 }}
|
||||||
|
animate={{ opacity: 1, translateX: 0 }}
|
||||||
|
exit={{ opacity: 0, translateX: -30 }}
|
||||||
|
>
|
||||||
|
<OrgIdentityLinkForm onClose={() => handlePopUpClose("identity")} />
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
<IdentityTokenAuthTokenModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
|
||||||
<DeleteActionModal
|
<DeleteActionModal
|
||||||
isOpen={popUp.deleteIdentity.isOpen}
|
isOpen={popUp.deleteIdentity.isOpen}
|
||||||
title={`Are you sure you want to delete ${
|
title={`Are you sure you want to delete ${
|
||||||
|
|||||||
+138
-155
@@ -173,164 +173,147 @@ export const OrgIdentityModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||||
isOpen={popUp?.identity?.isOpen}
|
{isOrgIdentity && (
|
||||||
onOpenChange={(isOpen) => {
|
<Controller
|
||||||
handlePopUpToggle("identity", isOpen);
|
control={control}
|
||||||
reset();
|
defaultValue=""
|
||||||
}}
|
name="name"
|
||||||
>
|
render={({ field, fieldState: { error } }) => (
|
||||||
<ModalContent
|
<FormControl
|
||||||
bodyClassName="overflow-visible"
|
className="mb-4"
|
||||||
title={`${popUp?.identity?.data ? "Update" : "Create"} Identity`}
|
label="Name"
|
||||||
>
|
isError={Boolean(error)}
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
errorText={error?.message}
|
||||||
{isOrgIdentity && (
|
>
|
||||||
<Controller
|
<Input {...field} placeholder="Machine 1" />
|
||||||
control={control}
|
</FormControl>
|
||||||
defaultValue=""
|
|
||||||
name="name"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
className="mb-4"
|
|
||||||
label="Name"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="Machine 1" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
<Controller
|
/>
|
||||||
control={control}
|
)}
|
||||||
name="role"
|
<Controller
|
||||||
render={({ field: { onChange, value }, fieldState: { error } }) => (
|
control={control}
|
||||||
<FormControl
|
name="role"
|
||||||
label={`${popUp?.identity?.data ? "Update" : ""} Role`}
|
render={({ field: { onChange, value }, fieldState: { error } }) => (
|
||||||
errorText={error?.message}
|
<FormControl
|
||||||
isError={Boolean(error)}
|
label={`${popUp?.identity?.data ? "Update" : ""} Role`}
|
||||||
|
errorText={error?.message}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
>
|
||||||
|
<FilterableSelect
|
||||||
|
placeholder="Select role..."
|
||||||
|
options={roles}
|
||||||
|
onChange={onChange}
|
||||||
|
value={value}
|
||||||
|
getOptionValue={(option) => option.slug}
|
||||||
|
getOptionLabel={(option) => option.name}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{isOrgIdentity && (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="hasDeleteProtection"
|
||||||
|
render={({ field: { onChange, value }, fieldState: { error } }) => (
|
||||||
|
<FormControl errorText={error?.message} isError={Boolean(error)}>
|
||||||
|
<Switch
|
||||||
|
className="mr-2 ml-0 bg-mineshaft-400/80 shadow-inner data-[state=checked]:bg-green/80"
|
||||||
|
containerClassName="flex-row-reverse w-fit"
|
||||||
|
id="delete-protection-enabled"
|
||||||
|
thumbClassName="bg-mineshaft-800"
|
||||||
|
onCheckedChange={onChange}
|
||||||
|
isChecked={value}
|
||||||
>
|
>
|
||||||
<FilterableSelect
|
<p>Delete Protection {value ? "Enabled" : "Disabled"}</p>
|
||||||
placeholder="Select role..."
|
</Switch>
|
||||||
options={roles}
|
</FormControl>
|
||||||
onChange={onChange}
|
|
||||||
value={value}
|
|
||||||
getOptionValue={(option) => option.slug}
|
|
||||||
getOptionLabel={(option) => option.name}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{isOrgIdentity && (
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="hasDeleteProtection"
|
|
||||||
render={({ field: { onChange, value }, fieldState: { error } }) => (
|
|
||||||
<FormControl errorText={error?.message} isError={Boolean(error)}>
|
|
||||||
<Switch
|
|
||||||
className="mr-2 ml-0 bg-mineshaft-400/80 shadow-inner data-[state=checked]:bg-green/80"
|
|
||||||
containerClassName="flex-row-reverse w-fit"
|
|
||||||
id="delete-protection-enabled"
|
|
||||||
thumbClassName="bg-mineshaft-800"
|
|
||||||
onCheckedChange={onChange}
|
|
||||||
isChecked={value}
|
|
||||||
>
|
|
||||||
<p>Delete Protection {value ? "Enabled" : "Disabled"}</p>
|
|
||||||
</Switch>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
{isOrgIdentity && (
|
/>
|
||||||
<>
|
)}
|
||||||
<div>
|
{isOrgIdentity && (
|
||||||
<FormLabel label="Metadata" />
|
<>
|
||||||
</div>
|
<div>
|
||||||
<div className="mb-3 flex flex-col space-y-2">
|
<FormLabel label="Metadata" />
|
||||||
{metadataFormFields.fields.map(({ id: metadataFieldId }, i) => (
|
|
||||||
<div key={metadataFieldId} className="flex items-end space-x-2">
|
|
||||||
<div className="grow">
|
|
||||||
{i === 0 && <span className="text-xs text-mineshaft-400">Key</span>}
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name={`metadata.${i}.key`}
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
isError={Boolean(error?.message)}
|
|
||||||
errorText={error?.message}
|
|
||||||
className="mb-0"
|
|
||||||
>
|
|
||||||
<Input {...field} />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="grow">
|
|
||||||
{i === 0 && (
|
|
||||||
<FormLabel
|
|
||||||
label="Value"
|
|
||||||
className="text-xs text-mineshaft-400"
|
|
||||||
isOptional
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name={`metadata.${i}.value`}
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
isError={Boolean(error?.message)}
|
|
||||||
errorText={error?.message}
|
|
||||||
className="mb-0"
|
|
||||||
>
|
|
||||||
<Input {...field} />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<IconButton
|
|
||||||
ariaLabel="delete key"
|
|
||||||
className="bottom-0.5 h-9"
|
|
||||||
variant="outline_bg"
|
|
||||||
onClick={() => metadataFormFields.remove(i)}
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={faTrash} />
|
|
||||||
</IconButton>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div className="mt-2 flex justify-end">
|
|
||||||
<Button
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
|
||||||
size="xs"
|
|
||||||
variant="outline_bg"
|
|
||||||
onClick={() => metadataFormFields.append({ key: "", value: "" })}
|
|
||||||
>
|
|
||||||
Add Key
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Button
|
|
||||||
className="mr-4"
|
|
||||||
size="sm"
|
|
||||||
type="submit"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
isDisabled={isSubmitting}
|
|
||||||
>
|
|
||||||
{popUp?.identity?.data ? "Update" : "Create"}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
colorSchema="secondary"
|
|
||||||
variant="plain"
|
|
||||||
onClick={() => handlePopUpToggle("identity", false)}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
<div className="mb-3 flex flex-col space-y-2">
|
||||||
</ModalContent>
|
{metadataFormFields.fields.map(({ id: metadataFieldId }, i) => (
|
||||||
</Modal>
|
<div key={metadataFieldId} className="flex items-end space-x-2">
|
||||||
|
<div className="grow">
|
||||||
|
{i === 0 && <span className="text-xs text-mineshaft-400">Key</span>}
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name={`metadata.${i}.key`}
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
isError={Boolean(error?.message)}
|
||||||
|
errorText={error?.message}
|
||||||
|
className="mb-0"
|
||||||
|
>
|
||||||
|
<Input {...field} />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="grow">
|
||||||
|
{i === 0 && (
|
||||||
|
<FormLabel label="Value" className="text-xs text-mineshaft-400" isOptional />
|
||||||
|
)}
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name={`metadata.${i}.value`}
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
isError={Boolean(error?.message)}
|
||||||
|
errorText={error?.message}
|
||||||
|
className="mb-0"
|
||||||
|
>
|
||||||
|
<Input {...field} />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<IconButton
|
||||||
|
ariaLabel="delete key"
|
||||||
|
className="bottom-0.5 h-9"
|
||||||
|
variant="outline_bg"
|
||||||
|
onClick={() => metadataFormFields.remove(i)}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faTrash} />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="mt-2 flex justify-end">
|
||||||
|
<Button
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
|
size="xs"
|
||||||
|
variant="outline_bg"
|
||||||
|
onClick={() => metadataFormFields.append({ key: "", value: "" })}
|
||||||
|
>
|
||||||
|
Add Key
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<div className="flex items-center">
|
||||||
|
<Button
|
||||||
|
className="mr-4"
|
||||||
|
size="sm"
|
||||||
|
type="submit"
|
||||||
|
isLoading={isSubmitting}
|
||||||
|
isDisabled={isSubmitting}
|
||||||
|
>
|
||||||
|
{popUp?.identity?.data ? "Update" : "Create"}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
colorSchema="secondary"
|
||||||
|
variant="plain"
|
||||||
|
onClick={() => handlePopUpToggle("identity", false)}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+12
-2
@@ -7,7 +7,7 @@ import { Link, useNavigate, useParams } from "@tanstack/react-router";
|
|||||||
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
|
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { OrgPermissionCan } from "@app/components/permissions";
|
import { OrgPermissionCan } from "@app/components/permissions";
|
||||||
import { DeleteActionModal, PageHeader } from "@app/components/v2";
|
import { DeleteActionModal, Modal, ModalContent, PageHeader } from "@app/components/v2";
|
||||||
import { ROUTE_PATHS } from "@app/const/routes";
|
import { ROUTE_PATHS } from "@app/const/routes";
|
||||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects, useOrganization } from "@app/context";
|
import { OrgPermissionIdentityActions, OrgPermissionSubjects, useOrganization } from "@app/context";
|
||||||
import { useDeleteOrgIdentity, useGetOrgIdentityMembershipById } from "@app/hooks/api";
|
import { useDeleteOrgIdentity, useGetOrgIdentityMembershipById } from "@app/hooks/api";
|
||||||
@@ -100,7 +100,17 @@ const Page = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<OrgIdentityModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
<Modal
|
||||||
|
isOpen={popUp?.identity?.isOpen}
|
||||||
|
onOpenChange={(isOpen) => handlePopUpToggle("identity", isOpen)}
|
||||||
|
>
|
||||||
|
<ModalContent
|
||||||
|
bodyClassName="overflow-visible"
|
||||||
|
title={`${popUp?.identity?.data ? "Update" : "Create"} Identity`}
|
||||||
|
>
|
||||||
|
<OrgIdentityModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
<IdentityAuthMethodModal
|
<IdentityAuthMethodModal
|
||||||
popUp={popUp}
|
popUp={popUp}
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
|||||||
+3
-2
@@ -24,7 +24,7 @@ import {
|
|||||||
THead,
|
THead,
|
||||||
Tr
|
Tr
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { useProject } from "@app/context";
|
import { useOrganization, useProject } from "@app/context";
|
||||||
import { getProjectHomePage } from "@app/helpers/project";
|
import { getProjectHomePage } from "@app/helpers/project";
|
||||||
import {
|
import {
|
||||||
getUserTablePreference,
|
getUserTablePreference,
|
||||||
@@ -86,6 +86,7 @@ export const GroupMembersTable = ({ groupMembership }: Props) => {
|
|||||||
setUserTablePreference("projectGroupMembersTable", PreferenceKey.PerPage, newPerPage);
|
setUserTablePreference("projectGroupMembersTable", PreferenceKey.PerPage, newPerPage);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { isSubOrganization, currentOrg } = useOrganization();
|
||||||
const { currentProject } = useProject();
|
const { currentProject } = useProject();
|
||||||
|
|
||||||
const { data: groupMemberships, isPending } = useListProjectGroupUsers({
|
const { data: groupMemberships, isPending } = useListProjectGroupUsers({
|
||||||
@@ -153,7 +154,7 @@ export const GroupMembersTable = ({ groupMembership }: Props) => {
|
|||||||
text: "User privilege assumption has started"
|
text: "User privilege assumption has started"
|
||||||
});
|
});
|
||||||
|
|
||||||
const url = getProjectHomePage(currentProject.type, currentProject.environments);
|
const url = `${getProjectHomePage(currentProject.type, currentProject.environments)}${isSubOrganization ? `?subOrganization=${currentOrg.slug}` : ""}`;
|
||||||
window.location.href = url.replace("$projectId", currentProject.id);
|
window.location.href = url.replace("$projectId", currentProject.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
ProjectPermissionActions,
|
ProjectPermissionActions,
|
||||||
ProjectPermissionIdentityActions,
|
ProjectPermissionIdentityActions,
|
||||||
ProjectPermissionSub,
|
ProjectPermissionSub,
|
||||||
|
useOrganization,
|
||||||
useProject
|
useProject
|
||||||
} from "@app/context";
|
} from "@app/context";
|
||||||
import { getProjectBaseURL, getProjectHomePage } from "@app/helpers/project";
|
import { getProjectBaseURL, getProjectHomePage } from "@app/helpers/project";
|
||||||
@@ -46,6 +47,7 @@ const Page = () => {
|
|||||||
select: (el) => el.identityId as string
|
select: (el) => el.identityId as string
|
||||||
});
|
});
|
||||||
const { currentProject, projectId } = useProject();
|
const { currentProject, projectId } = useProject();
|
||||||
|
const { currentOrg, isSubOrganization } = useOrganization();
|
||||||
|
|
||||||
const { data: identityMembershipDetails, isPending: isMembershipDetailsLoading } =
|
const { data: identityMembershipDetails, isPending: isMembershipDetailsLoading } =
|
||||||
useGetProjectIdentityMembershipV2(projectId, identityId);
|
useGetProjectIdentityMembershipV2(projectId, identityId);
|
||||||
@@ -54,6 +56,8 @@ const Page = () => {
|
|||||||
useDeleteProjectIdentityMembership();
|
useDeleteProjectIdentityMembership();
|
||||||
|
|
||||||
const isProjectIdentity = Boolean(identityMembershipDetails?.identity.projectId);
|
const isProjectIdentity = Boolean(identityMembershipDetails?.identity.projectId);
|
||||||
|
const isNonScopedIdentity =
|
||||||
|
!isProjectIdentity && currentOrg.id !== identityMembershipDetails?.identity?.orgId;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: identity,
|
data: identity,
|
||||||
@@ -86,7 +90,7 @@ const Page = () => {
|
|||||||
type: "success",
|
type: "success",
|
||||||
text: "Identity privilege assumption has started"
|
text: "Identity privilege assumption has started"
|
||||||
});
|
});
|
||||||
const url = getProjectHomePage(currentProject.type, currentProject.environments);
|
const url = `${getProjectHomePage(currentProject.type, currentProject.environments)}${isSubOrganization && isNonScopedIdentity ? `?subOrganization=${currentOrg.slug}` : ""}`;
|
||||||
window.location.href = url.replace("$projectId", currentProject.id);
|
window.location.href = url.replace("$projectId", currentProject.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export const Page = () => {
|
|||||||
strict: false,
|
strict: false,
|
||||||
select: (el) => el.membershipId as string
|
select: (el) => el.membershipId as string
|
||||||
});
|
});
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg, isSubOrganization } = useOrganization();
|
||||||
const { currentProject, projectId } = useProject();
|
const { currentProject, projectId } = useProject();
|
||||||
|
|
||||||
const { data: membershipDetails, isPending: isMembershipDetailsLoading } =
|
const { data: membershipDetails, isPending: isMembershipDetailsLoading } =
|
||||||
@@ -73,7 +73,7 @@ export const Page = () => {
|
|||||||
text: "User privilege assumption has started"
|
text: "User privilege assumption has started"
|
||||||
});
|
});
|
||||||
|
|
||||||
const url = getProjectHomePage(currentProject.type, currentProject.environments);
|
const url = `${getProjectHomePage(currentProject.type, currentProject.environments)}${isSubOrganization ? `?subOrganization=${currentOrg.slug}` : ""}`;
|
||||||
window.location.href = url.replace("$projectId", currentProject.id);
|
window.location.href = url.replace("$projectId", currentProject.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user