improvements: address feedback

This commit is contained in:
Scott Wilson
2025-01-24 20:21:21 -08:00
parent 598d14fc54
commit 79a616dc1c
8 changed files with 278 additions and 226 deletions
@@ -21,7 +21,7 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
secretVersioning: true, secretVersioning: true,
pitRecovery: false, pitRecovery: false,
ipAllowlisting: false, ipAllowlisting: false,
rbac: false, rbac: true,
customRateLimits: false, customRateLimits: false,
customAlerts: false, customAlerts: false,
secretAccessInsights: false, secretAccessInsights: false,
@@ -278,13 +278,7 @@ export const IdentityJwtAuthForm = ({
<form <form
onSubmit={handleSubmit(onFormSubmit, (fields) => { onSubmit={handleSubmit(onFormSubmit, (fields) => {
setTabValue( setTabValue(
[ ["accessTokenTrustedIps"].includes(Object.keys(fields)[0])
"accessTokenTrustedIps",
"boundIssuer",
"boundAudiences",
"boundSubject",
"boundClaims"
].includes(Object.keys(fields)[0])
? IdentityFormTab.Advanced ? IdentityFormTab.Advanced
: IdentityFormTab.Configuration : IdentityFormTab.Configuration
); );
@@ -365,7 +359,6 @@ export const IdentityJwtAuthForm = ({
/> />
</> </>
)} )}
{selectedConfigurationType === IdentityJwtConfigurationType.STATIC && ( {selectedConfigurationType === IdentityJwtConfigurationType.STATIC && (
<> <>
{publicKeyFields.map(({ id }, index) => ( {publicKeyFields.map(({ id }, index) => (
@@ -430,50 +423,6 @@ export const IdentityJwtAuthForm = ({
</div> </div>
</> </>
)} )}
<Controller
control={control}
defaultValue="2592000"
name="accessTokenTTL"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Access Token TTL (seconds)"
isError={Boolean(error)}
errorText={error?.message}
>
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
</FormControl>
)}
/>
<Controller
control={control}
defaultValue="2592000"
name="accessTokenMaxTTL"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Access Token Max TTL (seconds)"
isError={Boolean(error)}
errorText={error?.message}
>
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
</FormControl>
)}
/>
<Controller
control={control}
defaultValue="0"
name="accessTokenNumUsesLimit"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Access Token Max Number of Uses"
isError={Boolean(error)}
errorText={error?.message}
>
<Input {...field} placeholder="0" type="number" min="0" step="1" />
</FormControl>
)}
/>
</TabPanel>
<TabPanel value={IdentityFormTab.Advanced}>
<Controller <Controller
control={control} control={control}
name="boundIssuer" name="boundIssuer"
@@ -604,6 +553,50 @@ export const IdentityJwtAuthForm = ({
Add Claims Add Claims
</Button> </Button>
</div> </div>
<Controller
control={control}
defaultValue="2592000"
name="accessTokenTTL"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Access Token TTL (seconds)"
isError={Boolean(error)}
errorText={error?.message}
>
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
</FormControl>
)}
/>
<Controller
control={control}
defaultValue="2592000"
name="accessTokenMaxTTL"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Access Token Max TTL (seconds)"
isError={Boolean(error)}
errorText={error?.message}
>
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
</FormControl>
)}
/>
<Controller
control={control}
defaultValue="0"
name="accessTokenNumUsesLimit"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Access Token Max Number of Uses"
isError={Boolean(error)}
errorText={error?.message}
>
<Input {...field} placeholder="0" type="number" min="0" step="1" />
</FormControl>
)}
/>
</TabPanel>
<TabPanel value={IdentityFormTab.Advanced}>
{accessTokenTrustedIpsFields.map(({ id }, index) => ( {accessTokenTrustedIpsFields.map(({ id }, index) => (
<div className="mb-3 flex items-end space-x-2" key={id}> <div className="mb-3 flex items-end space-x-2" key={id}>
<Controller <Controller
@@ -219,7 +219,9 @@ export const IdentityKubernetesAuthForm = ({
"tokenReviewerJwt", "tokenReviewerJwt",
"accessTokenTTL", "accessTokenTTL",
"accessTokenMaxTTL", "accessTokenMaxTTL",
"accessTokenNumUsesLimit" "accessTokenNumUsesLimit",
"allowedNames",
"allowedNamespaces"
].includes(Object.keys(fields)[0]) ].includes(Object.keys(fields)[0])
? IdentityFormTab.Configuration ? IdentityFormTab.Configuration
: IdentityFormTab.Advanced : IdentityFormTab.Advanced
@@ -263,6 +265,35 @@ export const IdentityKubernetesAuthForm = ({
</FormControl> </FormControl>
)} )}
/> />
<Controller
control={control}
defaultValue=""
name="allowedNamespaces"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Allowed Namespaces"
isError={Boolean(error)}
errorText={error?.message}
tooltipText="A comma-separated list of trusted namespaces that service accounts must belong to authenticate with Infisical."
>
<Input {...field} placeholder="namespaceA, namespaceB" type="text" />
</FormControl>
)}
/>
<Controller
control={control}
name="allowedNames"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Allowed Service Account Names"
isError={Boolean(error)}
tooltipText="An optional comma-separated list of trusted service account names that are allowed to authenticate with Infisical. Leave empty to allow any service account."
errorText={error?.message}
>
<Input {...field} placeholder="service-account-1-name, service-account-1-name" />
</FormControl>
)}
/>
<Controller <Controller
control={control} control={control}
defaultValue="2592000" defaultValue="2592000"
@@ -310,35 +341,6 @@ export const IdentityKubernetesAuthForm = ({
/> />
</TabPanel> </TabPanel>
<TabPanel value={IdentityFormTab.Advanced}> <TabPanel value={IdentityFormTab.Advanced}>
<Controller
control={control}
name="allowedNames"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Allowed Service Account Names"
isError={Boolean(error)}
tooltipText="An optional comma-separated list of trusted service account names that are allowed to authenticate with Infisical. Leave empty to allow any service account."
errorText={error?.message}
>
<Input {...field} placeholder="service-account-1-name, service-account-1-name" />
</FormControl>
)}
/>
<Controller
control={control}
defaultValue=""
name="allowedNamespaces"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Allowed Namespaces"
isError={Boolean(error)}
errorText={error?.message}
tooltipText="A comma-separated list of trusted namespaces that service accounts must belong to authenticate with Infisical."
>
<Input {...field} placeholder="namespaceA, namespaceB" type="text" />
</FormControl>
)}
/>
<Controller <Controller
control={control} control={control}
defaultValue="" defaultValue=""
@@ -223,13 +223,7 @@ export const IdentityOidcAuthForm = ({
<form <form
onSubmit={handleSubmit(onFormSubmit, (fields) => { onSubmit={handleSubmit(onFormSubmit, (fields) => {
setTabValue( setTabValue(
[ ["accessTokenTrustedIps", "caCert", "boundClaims"].includes(Object.keys(fields)[0])
"accessTokenTrustedIps",
"caCert",
"boundAudiences",
"boundSubject",
"boundClaims"
].includes(Object.keys(fields)[0])
? IdentityFormTab.Advanced ? IdentityFormTab.Advanced
: IdentityFormTab.Configuration : IdentityFormTab.Configuration
); );
@@ -277,6 +271,48 @@ export const IdentityOidcAuthForm = ({
</FormControl> </FormControl>
)} )}
/> />
<Controller
control={control}
name="boundSubject"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Subject"
isError={Boolean(error)}
errorText={error?.message}
icon={
<Tooltip
className="text-center"
content={<span>This field supports glob patterns</span>}
>
<FontAwesomeIcon icon={faQuestionCircle} size="sm" />
</Tooltip>
}
>
<Input {...field} type="text" />
</FormControl>
)}
/>
<Controller
control={control}
name="boundAudiences"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Audiences"
isError={Boolean(error)}
errorText={error?.message}
icon={
<Tooltip
className="text-center"
content={<span>This field supports glob patterns</span>}
>
<FontAwesomeIcon icon={faQuestionCircle} size="sm" />
</Tooltip>
}
>
<Input {...field} type="text" placeholder="service1, service2" />
</FormControl>
)}
/>
<Controller <Controller
control={control} control={control}
defaultValue="2592000" defaultValue="2592000"
@@ -334,48 +370,6 @@ export const IdentityOidcAuthForm = ({
</FormControl> </FormControl>
)} )}
/> />
<Controller
control={control}
name="boundSubject"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Subject"
isError={Boolean(error)}
errorText={error?.message}
icon={
<Tooltip
className="text-center"
content={<span>This field supports glob patterns</span>}
>
<FontAwesomeIcon icon={faQuestionCircle} size="sm" />
</Tooltip>
}
>
<Input {...field} type="text" />
</FormControl>
)}
/>
<Controller
control={control}
name="boundAudiences"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Audiences"
isError={Boolean(error)}
errorText={error?.message}
icon={
<Tooltip
className="text-center"
content={<span>This field supports glob patterns</span>}
>
<FontAwesomeIcon icon={faQuestionCircle} size="sm" />
</Tooltip>
}
>
<Input {...field} type="text" placeholder="service1, service2" />
</FormControl>
)}
/>
{boundClaimsFields.map(({ id }, index) => ( {boundClaimsFields.map(({ id }, index) => (
<div className="mb-3 flex items-end space-x-2" key={id}> <div className="mb-3 flex items-end space-x-2" key={id}>
<Controller <Controller
@@ -1,7 +1,9 @@
import { faCog, faPlus } from "@fortawesome/free-solid-svg-icons"; import { faCog, faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { OrgPermissionCan } from "@app/components/permissions";
import { Button } from "@app/components/v2"; import { Button } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
import { IdentityAuthMethod, identityAuthToNameMap, useGetIdentityById } from "@app/hooks/api"; import { IdentityAuthMethod, identityAuthToNameMap, useGetIdentityById } from "@app/hooks/api";
import { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
@@ -45,21 +47,26 @@ export const IdentityAuthenticationSection = ({ identityId, handlePopUpOpen }: P
{!Object.values(IdentityAuthMethod).every((method) => {!Object.values(IdentityAuthMethod).every((method) =>
data.identity.authMethods.includes(method) data.identity.authMethods.includes(method)
) && ( ) && (
<Button <OrgPermissionCan I={OrgPermissionActions.Edit} a={OrgPermissionSubjects.Identity}>
onClick={() => { {(isAllowed) => (
handlePopUpOpen("identityAuthMethod", { <Button
identityId, isDisabled={!isAllowed}
name: data.identity.name, onClick={() => {
allAuthMethods: data.identity.authMethods handlePopUpOpen("identityAuthMethod", {
}); identityId,
}} name: data.identity.name,
variant="outline_bg" allAuthMethods: data.identity.authMethods
className="mt-3 w-full" });
size="xs" }}
leftIcon={<FontAwesomeIcon icon={faPlus} />} variant="outline_bg"
> className="mt-3 w-full"
{data.identity.authMethods.length ? "Add" : "Create"} Auth Method size="xs"
</Button> leftIcon={<FontAwesomeIcon icon={faPlus} />}
>
{data.identity.authMethods.length ? "Add" : "Create"} Auth Method
</Button>
)}
</OrgPermissionCan>
)} )}
</div> </div>
) : ( ) : (
@@ -4,6 +4,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { format } from "date-fns"; import { format } from "date-fns";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { OrgPermissionCan } from "@app/components/permissions";
import { import {
Button, Button,
DeleteActionModal, DeleteActionModal,
@@ -19,6 +20,7 @@ import {
Tooltip, Tooltip,
Tr Tr
} from "@app/components/v2"; } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
import { usePopUp } from "@app/hooks"; import { usePopUp } from "@app/hooks";
import { useRevokeIdentityTokenAuthToken } from "@app/hooks/api"; import { useRevokeIdentityTokenAuthToken } from "@app/hooks/api";
import { IdentityAccessToken } from "@app/hooks/api/identities/types"; import { IdentityAccessToken } from "@app/hooks/api/identities/types";
@@ -77,18 +79,23 @@ export const IdentityTokenAuthTokensTable = ({ tokens, identityId }: Props) => {
<div className="col-span-2 mt-3"> <div className="col-span-2 mt-3">
<div className="flex items-end justify-between border-b border-mineshaft-500 pb-2"> <div className="flex items-end justify-between border-b border-mineshaft-500 pb-2">
<span className="text-bunker-300">Access Tokens</span> <span className="text-bunker-300">Access Tokens</span>
<Button <OrgPermissionCan I={OrgPermissionActions.Edit} a={OrgPermissionSubjects.Identity}>
size="xs" {(isAllowed) => (
onClick={() => { <Button
handlePopUpOpen("token", { size="xs"
identityId isDisabled={!isAllowed}
}); onClick={() => {
}} handlePopUpOpen("token", {
leftIcon={<FontAwesomeIcon icon={faPlus} />} identityId
colorSchema="secondary" });
> }}
Add Token leftIcon={<FontAwesomeIcon icon={faPlus} />}
</Button> colorSchema="secondary"
>
Add Token
</Button>
)}
</OrgPermissionCan>
</div> </div>
<TableContainer className="mt-4 rounded-none border-none"> <TableContainer className="mt-4 rounded-none border-none">
<Table> <Table>
@@ -136,40 +143,56 @@ export const IdentityTokenAuthTokensTable = ({ tokens, identityId }: Props) => {
</Td> </Td>
<Td> <Td>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Tooltip content="Edit Token"> <OrgPermissionCan
<IconButton I={OrgPermissionActions.Edit}
onClick={() => { a={OrgPermissionSubjects.Identity}
handlePopUpOpen("token", { >
identityId, {(isAllowed) => (
tokenId: id, <Tooltip content={isAllowed ? "Edit Token" : "Access Restricted"}>
name <IconButton
}); isDisabled={!isAllowed}
}} onClick={() => {
size="xs" handlePopUpOpen("token", {
variant="plain" identityId,
ariaLabel="Edit token" tokenId: id,
> name
<FontAwesomeIcon icon={faEdit} /> });
</IconButton> }}
</Tooltip> size="xs"
variant="plain"
ariaLabel="Edit token"
>
<FontAwesomeIcon icon={faEdit} />
</IconButton>
</Tooltip>
)}
</OrgPermissionCan>
{!isAccessTokenRevoked && ( {!isAccessTokenRevoked && (
<Tooltip content="Revoke Token"> <OrgPermissionCan
<IconButton I={OrgPermissionActions.Edit}
onClick={() => { a={OrgPermissionSubjects.Identity}
handlePopUpOpen("revokeToken", { >
identityId, {(isAllowed) => (
tokenId: id, <Tooltip content={isAllowed ? "Revoke Token" : "Access Restricted"}>
name <IconButton
}); isDisabled={!isAllowed}
}} onClick={() => {
size="xs" handlePopUpOpen("revokeToken", {
colorSchema="danger" identityId,
variant="plain" tokenId: id,
ariaLabel="Revoke token" name
> });
<FontAwesomeIcon icon={faBan} /> }}
</IconButton> size="xs"
</Tooltip> colorSchema="danger"
variant="plain"
ariaLabel="Revoke token"
>
<FontAwesomeIcon icon={faBan} />
</IconButton>
</Tooltip>
)}
</OrgPermissionCan>
)} )}
</div> </div>
</Td> </Td>
@@ -4,6 +4,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { format } from "date-fns"; import { format } from "date-fns";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { OrgPermissionCan } from "@app/components/permissions";
import { import {
Button, Button,
DeleteActionModal, DeleteActionModal,
@@ -19,6 +20,7 @@ import {
Tooltip, Tooltip,
Tr Tr
} from "@app/components/v2"; } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
import { usePopUp } from "@app/hooks"; import { usePopUp } from "@app/hooks";
import { useRevokeIdentityUniversalAuthClientSecret } from "@app/hooks/api"; import { useRevokeIdentityUniversalAuthClientSecret } from "@app/hooks/api";
import { ClientSecretData } from "@app/hooks/api/identities/types"; import { ClientSecretData } from "@app/hooks/api/identities/types";
@@ -66,18 +68,23 @@ export const IdentityUniversalAuthClientSecretsTable = ({ clientSecrets, identit
<div className="col-span-2"> <div className="col-span-2">
<div className="flex items-end justify-between border-b border-mineshaft-500 pb-2"> <div className="flex items-end justify-between border-b border-mineshaft-500 pb-2">
<span className="text-bunker-300">Client Secrets</span> <span className="text-bunker-300">Client Secrets</span>
<Button <OrgPermissionCan I={OrgPermissionActions.Edit} a={OrgPermissionSubjects.Identity}>
size="xs" {(isAllowed) => (
onClick={() => { <Button
handlePopUpOpen("clientSecret", { isDisabled={!isAllowed}
identityId size="xs"
}); onClick={() => {
}} handlePopUpOpen("clientSecret", {
leftIcon={<FontAwesomeIcon icon={faPlus} />} identityId
colorSchema="secondary" });
> }}
Add Client Secret leftIcon={<FontAwesomeIcon icon={faPlus} />}
</Button> colorSchema="secondary"
>
Add Client Secret
</Button>
)}
</OrgPermissionCan>
</div> </div>
<TableContainer className="mt-4 rounded-none border-none"> <TableContainer className="mt-4 rounded-none border-none">
<Table> <Table>
@@ -121,22 +128,30 @@ export const IdentityUniversalAuthClientSecretsTable = ({ clientSecrets, identit
{expiresAt ? format(expiresAt, "yyyy-MM-dd") : "-"} {expiresAt ? format(expiresAt, "yyyy-MM-dd") : "-"}
</Td> </Td>
<Td> <Td>
<Tooltip content="Delete Secret"> <OrgPermissionCan
<IconButton I={OrgPermissionActions.Edit}
onClick={() => { a={OrgPermissionSubjects.Identity}
handlePopUpOpen("revokeClientSecret", { >
clientSecretPrefix, {(isAllowed) => (
clientSecretId: id <Tooltip content={isAllowed ? "Delete Secret" : "Access Restricted"}>
}); <IconButton
}} isDisabled={!isAllowed}
size="xs" onClick={() => {
colorSchema="danger" handlePopUpOpen("revokeClientSecret", {
variant="plain" clientSecretPrefix,
ariaLabel="Delete secret" clientSecretId: id
> });
<FontAwesomeIcon icon={faTrash} /> }}
</IconButton> size="xs"
</Tooltip> colorSchema="danger"
variant="plain"
ariaLabel="Delete secret"
>
<FontAwesomeIcon icon={faTrash} />
</IconButton>
</Tooltip>
)}
</OrgPermissionCan>
</Td> </Td>
</Tr> </Tr>
); );
@@ -2,6 +2,7 @@ import { ReactNode } from "react";
import { faChevronDown, faEdit, faTrash } from "@fortawesome/free-solid-svg-icons"; import { faChevronDown, faEdit, faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { OrgPermissionCan } from "@app/components/permissions";
import { import {
Button, Button,
DropdownMenu, DropdownMenu,
@@ -9,6 +10,7 @@ import {
DropdownMenuItem, DropdownMenuItem,
DropdownMenuTrigger DropdownMenuTrigger
} from "@app/components/v2"; } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
type Props = { type Props = {
children: ReactNode; children: ReactNode;
@@ -34,12 +36,28 @@ export const ViewIdentityContentWrapper = ({ children, onDelete, onEdit }: Props
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent className="min-w-[120px]" align="end"> <DropdownMenuContent className="min-w-[120px]" align="end">
<DropdownMenuItem onClick={onEdit} icon={<FontAwesomeIcon icon={faEdit} />}> <OrgPermissionCan I={OrgPermissionActions.Edit} a={OrgPermissionSubjects.Identity}>
Edit {(isAllowed) => (
</DropdownMenuItem> <DropdownMenuItem
<DropdownMenuItem onClick={onDelete} icon={<FontAwesomeIcon icon={faTrash} />}> isDisabled={!isAllowed}
Delete onClick={onEdit}
</DropdownMenuItem> icon={<FontAwesomeIcon icon={faEdit} />}
>
Edit
</DropdownMenuItem>
)}
</OrgPermissionCan>
<OrgPermissionCan I={OrgPermissionActions.Edit} a={OrgPermissionSubjects.Identity}>
{(isAllowed) => (
<DropdownMenuItem
isDisabled={!isAllowed}
onClick={onDelete}
icon={<FontAwesomeIcon icon={faTrash} />}
>
Delete
</DropdownMenuItem>
)}
</OrgPermissionCan>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
</div> </div>