mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-10 19:28:51 +00:00
Merge pull request #2477 from meetcshah19/meet/eng-1519-allow-users-to-change-auth-method-in-the-ui-easily
feat: allow users to replace auth methods
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery, UseQueryOptions } from "@tanstack/react-query";
|
||||||
|
|
||||||
import { apiRequest } from "@app/config/request";
|
import { apiRequest } from "@app/config/request";
|
||||||
|
|
||||||
@@ -13,7 +13,8 @@ import {
|
|||||||
IdentityMembershipOrg,
|
IdentityMembershipOrg,
|
||||||
IdentityOidcAuth,
|
IdentityOidcAuth,
|
||||||
IdentityTokenAuth,
|
IdentityTokenAuth,
|
||||||
IdentityUniversalAuth} from "./types";
|
IdentityUniversalAuth
|
||||||
|
} from "./types";
|
||||||
|
|
||||||
export const identitiesKeys = {
|
export const identitiesKeys = {
|
||||||
getIdentityById: (identityId: string) => [{ identityId }, "identity"] as const,
|
getIdentityById: (identityId: string) => [{ identityId }, "identity"] as const,
|
||||||
@@ -64,9 +65,16 @@ export const useGetIdentityProjectMemberships = (identityId: string) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGetIdentityUniversalAuth = (identityId: string) => {
|
export const useGetIdentityUniversalAuth = (
|
||||||
|
identityId: string,
|
||||||
|
options?: UseQueryOptions<
|
||||||
|
IdentityUniversalAuth,
|
||||||
|
unknown,
|
||||||
|
IdentityUniversalAuth,
|
||||||
|
ReturnType<typeof identitiesKeys.getIdentityUniversalAuth>
|
||||||
|
>
|
||||||
|
) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
enabled: Boolean(identityId),
|
|
||||||
queryKey: identitiesKeys.getIdentityUniversalAuth(identityId),
|
queryKey: identitiesKeys.getIdentityUniversalAuth(identityId),
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const {
|
const {
|
||||||
@@ -76,8 +84,10 @@ export const useGetIdentityUniversalAuth = (identityId: string) => {
|
|||||||
);
|
);
|
||||||
return identityUniversalAuth;
|
return identityUniversalAuth;
|
||||||
},
|
},
|
||||||
|
cacheTime: 0,
|
||||||
staleTime: 0,
|
staleTime: 0,
|
||||||
cacheTime: 0
|
...options,
|
||||||
|
enabled: Boolean(identityId) && (options?.enabled ?? true)
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -96,9 +106,16 @@ export const useGetIdentityUniversalAuthClientSecrets = (identityId: string) =>
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGetIdentityGcpAuth = (identityId: string) => {
|
export const useGetIdentityGcpAuth = (
|
||||||
|
identityId: string,
|
||||||
|
options?: UseQueryOptions<
|
||||||
|
IdentityGcpAuth,
|
||||||
|
unknown,
|
||||||
|
IdentityGcpAuth,
|
||||||
|
ReturnType<typeof identitiesKeys.getIdentityGcpAuth>
|
||||||
|
>
|
||||||
|
) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
enabled: Boolean(identityId),
|
|
||||||
queryKey: identitiesKeys.getIdentityGcpAuth(identityId),
|
queryKey: identitiesKeys.getIdentityGcpAuth(identityId),
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const {
|
const {
|
||||||
@@ -109,13 +126,22 @@ export const useGetIdentityGcpAuth = (identityId: string) => {
|
|||||||
return identityGcpAuth;
|
return identityGcpAuth;
|
||||||
},
|
},
|
||||||
staleTime: 0,
|
staleTime: 0,
|
||||||
cacheTime: 0
|
cacheTime: 0,
|
||||||
|
...options,
|
||||||
|
enabled: Boolean(identityId) && (options?.enabled ?? true)
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGetIdentityAwsAuth = (identityId: string) => {
|
export const useGetIdentityAwsAuth = (
|
||||||
|
identityId: string,
|
||||||
|
options?: UseQueryOptions<
|
||||||
|
IdentityAwsAuth,
|
||||||
|
unknown,
|
||||||
|
IdentityAwsAuth,
|
||||||
|
ReturnType<typeof identitiesKeys.getIdentityAwsAuth>
|
||||||
|
>
|
||||||
|
) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
enabled: Boolean(identityId),
|
|
||||||
queryKey: identitiesKeys.getIdentityAwsAuth(identityId),
|
queryKey: identitiesKeys.getIdentityAwsAuth(identityId),
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const {
|
const {
|
||||||
@@ -126,13 +152,22 @@ export const useGetIdentityAwsAuth = (identityId: string) => {
|
|||||||
return identityAwsAuth;
|
return identityAwsAuth;
|
||||||
},
|
},
|
||||||
staleTime: 0,
|
staleTime: 0,
|
||||||
cacheTime: 0
|
cacheTime: 0,
|
||||||
|
...options,
|
||||||
|
enabled: Boolean(identityId) && (options?.enabled ?? true)
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGetIdentityAzureAuth = (identityId: string) => {
|
export const useGetIdentityAzureAuth = (
|
||||||
|
identityId: string,
|
||||||
|
options?: UseQueryOptions<
|
||||||
|
IdentityAzureAuth,
|
||||||
|
unknown,
|
||||||
|
IdentityAzureAuth,
|
||||||
|
ReturnType<typeof identitiesKeys.getIdentityAzureAuth>
|
||||||
|
>
|
||||||
|
) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
enabled: Boolean(identityId),
|
|
||||||
queryKey: identitiesKeys.getIdentityAzureAuth(identityId),
|
queryKey: identitiesKeys.getIdentityAzureAuth(identityId),
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const {
|
const {
|
||||||
@@ -143,13 +178,22 @@ export const useGetIdentityAzureAuth = (identityId: string) => {
|
|||||||
return identityAzureAuth;
|
return identityAzureAuth;
|
||||||
},
|
},
|
||||||
staleTime: 0,
|
staleTime: 0,
|
||||||
cacheTime: 0
|
cacheTime: 0,
|
||||||
|
...options,
|
||||||
|
enabled: Boolean(identityId) && (options?.enabled ?? true)
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGetIdentityKubernetesAuth = (identityId: string) => {
|
export const useGetIdentityKubernetesAuth = (
|
||||||
|
identityId: string,
|
||||||
|
options?: UseQueryOptions<
|
||||||
|
IdentityKubernetesAuth,
|
||||||
|
unknown,
|
||||||
|
IdentityKubernetesAuth,
|
||||||
|
ReturnType<typeof identitiesKeys.getIdentityKubernetesAuth>
|
||||||
|
>
|
||||||
|
) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
enabled: Boolean(identityId),
|
|
||||||
queryKey: identitiesKeys.getIdentityKubernetesAuth(identityId),
|
queryKey: identitiesKeys.getIdentityKubernetesAuth(identityId),
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const {
|
const {
|
||||||
@@ -160,13 +204,22 @@ export const useGetIdentityKubernetesAuth = (identityId: string) => {
|
|||||||
return identityKubernetesAuth;
|
return identityKubernetesAuth;
|
||||||
},
|
},
|
||||||
staleTime: 0,
|
staleTime: 0,
|
||||||
cacheTime: 0
|
cacheTime: 0,
|
||||||
|
...options,
|
||||||
|
enabled: Boolean(identityId) && (options?.enabled ?? true)
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGetIdentityTokenAuth = (identityId: string) => {
|
export const useGetIdentityTokenAuth = (
|
||||||
|
identityId: string,
|
||||||
|
options?: UseQueryOptions<
|
||||||
|
IdentityTokenAuth,
|
||||||
|
unknown,
|
||||||
|
IdentityTokenAuth,
|
||||||
|
ReturnType<typeof identitiesKeys.getIdentityTokenAuth>
|
||||||
|
>
|
||||||
|
) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
enabled: Boolean(identityId),
|
|
||||||
queryKey: identitiesKeys.getIdentityTokenAuth(identityId),
|
queryKey: identitiesKeys.getIdentityTokenAuth(identityId),
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const {
|
const {
|
||||||
@@ -177,7 +230,9 @@ export const useGetIdentityTokenAuth = (identityId: string) => {
|
|||||||
return identityTokenAuth;
|
return identityTokenAuth;
|
||||||
},
|
},
|
||||||
staleTime: 0,
|
staleTime: 0,
|
||||||
cacheTime: 0
|
cacheTime: 0,
|
||||||
|
...options,
|
||||||
|
enabled: Boolean(identityId) && (options?.enabled ?? true)
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -196,9 +251,16 @@ export const useGetIdentityTokensTokenAuth = (identityId: string) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGetIdentityOidcAuth = (identityId: string) => {
|
export const useGetIdentityOidcAuth = (
|
||||||
|
identityId: string,
|
||||||
|
options?: UseQueryOptions<
|
||||||
|
IdentityOidcAuth,
|
||||||
|
unknown,
|
||||||
|
IdentityOidcAuth,
|
||||||
|
ReturnType<typeof identitiesKeys.getIdentityOidcAuth>
|
||||||
|
>
|
||||||
|
) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
enabled: Boolean(identityId),
|
|
||||||
queryKey: identitiesKeys.getIdentityOidcAuth(identityId),
|
queryKey: identitiesKeys.getIdentityOidcAuth(identityId),
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const {
|
const {
|
||||||
@@ -209,6 +271,8 @@ export const useGetIdentityOidcAuth = (identityId: string) => {
|
|||||||
return identityOidcAuth;
|
return identityOidcAuth;
|
||||||
},
|
},
|
||||||
staleTime: 0,
|
staleTime: 0,
|
||||||
cacheTime: 0
|
cacheTime: 0,
|
||||||
|
...options,
|
||||||
|
enabled: Boolean(identityId) && (options?.enabled ?? true)
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
+119
-94
@@ -21,7 +21,8 @@ import {
|
|||||||
useDeleteIdentityKubernetesAuth,
|
useDeleteIdentityKubernetesAuth,
|
||||||
useDeleteIdentityOidcAuth,
|
useDeleteIdentityOidcAuth,
|
||||||
useDeleteIdentityTokenAuth,
|
useDeleteIdentityTokenAuth,
|
||||||
useDeleteIdentityUniversalAuth} from "@app/hooks/api";
|
useDeleteIdentityUniversalAuth
|
||||||
|
} from "@app/hooks/api";
|
||||||
import { IdentityAuthMethod, identityAuthToNameMap } from "@app/hooks/api/identities";
|
import { IdentityAuthMethod, identityAuthToNameMap } from "@app/hooks/api/identities";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
@@ -54,7 +55,10 @@ const identityAuthMethods = [
|
|||||||
|
|
||||||
const schema = yup
|
const schema = yup
|
||||||
.object({
|
.object({
|
||||||
authMethod: yup.string().required("Auth method is required")
|
authMethod: yup
|
||||||
|
.mixed<IdentityAuthMethod>()
|
||||||
|
.oneOf(Object.values(IdentityAuthMethod))
|
||||||
|
.required("Auth method is required")
|
||||||
})
|
})
|
||||||
.required();
|
.required();
|
||||||
|
|
||||||
@@ -72,14 +76,26 @@ export const IdentityAuthMethodModal = ({ popUp, handlePopUpOpen, handlePopUpTog
|
|||||||
const { mutateAsync: revokeAzureAuth } = useDeleteIdentityAzureAuth();
|
const { mutateAsync: revokeAzureAuth } = useDeleteIdentityAzureAuth();
|
||||||
const { mutateAsync: revokeOidcAuth } = useDeleteIdentityOidcAuth();
|
const { mutateAsync: revokeOidcAuth } = useDeleteIdentityOidcAuth();
|
||||||
|
|
||||||
const { control, watch, setValue } = useForm<FormData>({
|
const initialAuthMethod = popUp?.identityAuthMethod?.data?.authMethod;
|
||||||
|
|
||||||
|
const { control, watch, setValue, reset } = useForm<FormData>({
|
||||||
resolver: yupResolver(schema),
|
resolver: yupResolver(schema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
authMethod: IdentityAuthMethod.UNIVERSAL_AUTH
|
authMethod: initialAuthMethod
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const identityAuthMethodData = popUp?.identityAuthMethod?.data as {
|
useEffect(() => {
|
||||||
|
// reset form on open
|
||||||
|
if (popUp.identityAuthMethod.isOpen)
|
||||||
|
reset({ authMethod: popUp?.identityAuthMethod?.data?.authMethod });
|
||||||
|
}, [popUp.identityAuthMethod.isOpen]);
|
||||||
|
|
||||||
|
const identityAuthMethodData = {
|
||||||
|
identityId: popUp?.identityAuthMethod.data?.identityId,
|
||||||
|
name: popUp?.identityAuthMethod?.data?.name,
|
||||||
|
authMethod: watch("authMethod")
|
||||||
|
} as {
|
||||||
identityId: string;
|
identityId: string;
|
||||||
name: string;
|
name: string;
|
||||||
authMethod?: IdentityAuthMethod;
|
authMethod?: IdentityAuthMethod;
|
||||||
@@ -94,85 +110,10 @@ export const IdentityAuthMethodModal = ({ popUp, handlePopUpOpen, handlePopUpTog
|
|||||||
setValue("authMethod", IdentityAuthMethod.UNIVERSAL_AUTH);
|
setValue("authMethod", IdentityAuthMethod.UNIVERSAL_AUTH);
|
||||||
}, [identityAuthMethodData?.authMethod]);
|
}, [identityAuthMethodData?.authMethod]);
|
||||||
|
|
||||||
const authMethod = watch("authMethod");
|
const onRevokeAuthMethodSubmit = async (authMethod: IdentityAuthMethod) => {
|
||||||
|
if (!orgId || !authMethod) return;
|
||||||
const renderIdentityAuthForm = () => {
|
|
||||||
switch (identityAuthMethodData?.authMethod ?? authMethod) {
|
|
||||||
case IdentityAuthMethod.AWS_AUTH: {
|
|
||||||
return (
|
|
||||||
<IdentityAwsAuthForm
|
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case IdentityAuthMethod.KUBERNETES_AUTH: {
|
|
||||||
return (
|
|
||||||
<IdentityKubernetesAuthForm
|
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case IdentityAuthMethod.GCP_AUTH: {
|
|
||||||
return (
|
|
||||||
<IdentityGcpAuthForm
|
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case IdentityAuthMethod.AZURE_AUTH: {
|
|
||||||
return (
|
|
||||||
<IdentityAzureAuthForm
|
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case IdentityAuthMethod.UNIVERSAL_AUTH: {
|
|
||||||
return (
|
|
||||||
<IdentityUniversalAuthForm
|
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case IdentityAuthMethod.OIDC_AUTH: {
|
|
||||||
return (
|
|
||||||
<IdentityOidcAuthForm
|
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case IdentityAuthMethod.TOKEN_AUTH: {
|
|
||||||
return (
|
|
||||||
<IdentityTokenAuthForm
|
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return <div />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onRevokeAuthMethodSubmit = async () => {
|
|
||||||
if (!identityAuthMethodData.authMethod) return;
|
|
||||||
if (!orgId) return;
|
|
||||||
try {
|
try {
|
||||||
console.log("onRevokeAuthMethodSubmit identityId: ", identityAuthMethodData);
|
switch (authMethod) {
|
||||||
switch (identityAuthMethodData.authMethod) {
|
|
||||||
case IdentityAuthMethod.UNIVERSAL_AUTH: {
|
case IdentityAuthMethod.UNIVERSAL_AUTH: {
|
||||||
await revokeUniversalAuth({
|
await revokeUniversalAuth({
|
||||||
identityId: identityAuthMethodData.identityId,
|
identityId: identityAuthMethodData.identityId,
|
||||||
@@ -227,9 +168,7 @@ export const IdentityAuthMethodModal = ({ popUp, handlePopUpOpen, handlePopUpTog
|
|||||||
}
|
}
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
text: `Successfully removed ${
|
text: `Successfully removed ${identityAuthToNameMap[authMethod]} on ${identityAuthMethodData.name}`,
|
||||||
identityAuthToNameMap[identityAuthMethodData.authMethod]
|
|
||||||
} on ${identityAuthMethodData.name}`,
|
|
||||||
type: "success"
|
type: "success"
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -238,13 +177,95 @@ export const IdentityAuthMethodModal = ({ popUp, handlePopUpOpen, handlePopUpTog
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
createNotification({
|
createNotification({
|
||||||
text: `Failed to remove ${identityAuthToNameMap[identityAuthMethodData.authMethod]} on ${
|
text: `Failed to remove ${identityAuthToNameMap[authMethod]} on ${identityAuthMethodData.name}`,
|
||||||
identityAuthMethodData.name
|
|
||||||
}`,
|
|
||||||
type: "error"
|
type: "error"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const renderIdentityAuthForm = () => {
|
||||||
|
switch (identityAuthMethodData.authMethod) {
|
||||||
|
case IdentityAuthMethod.AWS_AUTH: {
|
||||||
|
return (
|
||||||
|
<IdentityAwsAuthForm
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
identityAuthMethodData={identityAuthMethodData}
|
||||||
|
initialAuthMethod={initialAuthMethod!}
|
||||||
|
revokeAuth={onRevokeAuthMethodSubmit}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case IdentityAuthMethod.KUBERNETES_AUTH: {
|
||||||
|
return (
|
||||||
|
<IdentityKubernetesAuthForm
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
identityAuthMethodData={identityAuthMethodData}
|
||||||
|
initialAuthMethod={initialAuthMethod!}
|
||||||
|
revokeAuth={onRevokeAuthMethodSubmit}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case IdentityAuthMethod.GCP_AUTH: {
|
||||||
|
return (
|
||||||
|
<IdentityGcpAuthForm
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
identityAuthMethodData={identityAuthMethodData}
|
||||||
|
initialAuthMethod={initialAuthMethod!}
|
||||||
|
revokeAuth={onRevokeAuthMethodSubmit}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case IdentityAuthMethod.AZURE_AUTH: {
|
||||||
|
return (
|
||||||
|
<IdentityAzureAuthForm
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
identityAuthMethodData={identityAuthMethodData}
|
||||||
|
initialAuthMethod={initialAuthMethod!}
|
||||||
|
revokeAuth={onRevokeAuthMethodSubmit}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case IdentityAuthMethod.UNIVERSAL_AUTH: {
|
||||||
|
return (
|
||||||
|
<IdentityUniversalAuthForm
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
identityAuthMethodData={identityAuthMethodData}
|
||||||
|
initialAuthMethod={initialAuthMethod!}
|
||||||
|
revokeAuth={onRevokeAuthMethodSubmit}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case IdentityAuthMethod.OIDC_AUTH: {
|
||||||
|
return (
|
||||||
|
<IdentityOidcAuthForm
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
identityAuthMethodData={identityAuthMethodData}
|
||||||
|
initialAuthMethod={initialAuthMethod!}
|
||||||
|
revokeAuth={onRevokeAuthMethodSubmit}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case IdentityAuthMethod.TOKEN_AUTH: {
|
||||||
|
return (
|
||||||
|
<IdentityTokenAuthForm
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
identityAuthMethodData={identityAuthMethodData}
|
||||||
|
initialAuthMethod={initialAuthMethod!}
|
||||||
|
revokeAuth={onRevokeAuthMethodSubmit}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return <div />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
@@ -255,8 +276,10 @@ export const IdentityAuthMethodModal = ({ popUp, handlePopUpOpen, handlePopUpTog
|
|||||||
>
|
>
|
||||||
<ModalContent
|
<ModalContent
|
||||||
title={`${
|
title={`${
|
||||||
identityAuthMethodData?.authMethod ? "Update" : "Configure"
|
identityAuthMethodData.authMethod === initialAuthMethod ? "Update" : "Configure"
|
||||||
} Identity Auth Method for ${identityAuthMethodData?.name ?? ""}`}
|
} Identity Auth Method for ${
|
||||||
|
identityAuthToNameMap[identityAuthMethodData.authMethod!] ?? ""
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
@@ -267,9 +290,10 @@ export const IdentityAuthMethodModal = ({ popUp, handlePopUpOpen, handlePopUpTog
|
|||||||
<Select
|
<Select
|
||||||
defaultValue={field.value}
|
defaultValue={field.value}
|
||||||
{...field}
|
{...field}
|
||||||
onValueChange={(e) => onChange(e)}
|
onValueChange={(e) => {
|
||||||
|
onChange(e);
|
||||||
|
}}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
isDisabled={!!identityAuthMethodData?.authMethod}
|
|
||||||
>
|
>
|
||||||
{identityAuthMethods.map(({ label, value }) => (
|
{identityAuthMethods.map(({ label, value }) => (
|
||||||
<SelectItem value={String(value || "")} key={label}>
|
<SelectItem value={String(value || "")} key={label}>
|
||||||
@@ -295,7 +319,8 @@ export const IdentityAuthMethodModal = ({ popUp, handlePopUpOpen, handlePopUpTog
|
|||||||
} on ${identityAuthMethodData?.name ?? ""}?`}
|
} on ${identityAuthMethodData?.name ?? ""}?`}
|
||||||
onChange={(isOpen) => handlePopUpToggle("revokeAuthMethod", isOpen)}
|
onChange={(isOpen) => handlePopUpToggle("revokeAuthMethod", isOpen)}
|
||||||
deleteKey="confirm"
|
deleteKey="confirm"
|
||||||
onDeleteApproved={onRevokeAuthMethodSubmit}
|
buttonText="Remove"
|
||||||
|
onDeleteApproved={() => onRevokeAuthMethodSubmit(identityAuthMethodData.authMethod!)}
|
||||||
/>
|
/>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
+214
-180
@@ -6,7 +6,7 @@ import { yupResolver } from "@hookform/resolvers/yup";
|
|||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, FormControl, IconButton, Input } from "@app/components/v2";
|
import { Button, DeleteActionModal, FormControl, IconButton, Input } from "@app/components/v2";
|
||||||
import { useOrganization, useSubscription } from "@app/context";
|
import { useOrganization, useSubscription } from "@app/context";
|
||||||
import {
|
import {
|
||||||
useAddIdentityAwsAuth,
|
useAddIdentityAwsAuth,
|
||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
||||||
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { usePopUp, UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
const schema = yup
|
const schema = yup
|
||||||
.object({
|
.object({
|
||||||
@@ -64,12 +64,16 @@ type Props = {
|
|||||||
name: string;
|
name: string;
|
||||||
authMethod?: IdentityAuthMethod;
|
authMethod?: IdentityAuthMethod;
|
||||||
};
|
};
|
||||||
|
initialAuthMethod: IdentityAuthMethod;
|
||||||
|
revokeAuth: (authMethod: IdentityAuthMethod) => Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IdentityAwsAuthForm = ({
|
export const IdentityAwsAuthForm = ({
|
||||||
handlePopUpOpen,
|
handlePopUpOpen,
|
||||||
handlePopUpToggle,
|
handlePopUpToggle,
|
||||||
identityAuthMethodData
|
identityAuthMethodData,
|
||||||
|
initialAuthMethod,
|
||||||
|
revokeAuth
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
@@ -78,7 +82,12 @@ export const IdentityAwsAuthForm = ({
|
|||||||
const { mutateAsync: addMutateAsync } = useAddIdentityAwsAuth();
|
const { mutateAsync: addMutateAsync } = useAddIdentityAwsAuth();
|
||||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityAwsAuth();
|
const { mutateAsync: updateMutateAsync } = useUpdateIdentityAwsAuth();
|
||||||
|
|
||||||
const { data } = useGetIdentityAwsAuth(identityAuthMethodData?.identityId ?? "");
|
const isCurrentAuthMethod = identityAuthMethodData?.authMethod === initialAuthMethod;
|
||||||
|
const { data } = useGetIdentityAwsAuth(identityAuthMethodData?.identityId ?? "", {
|
||||||
|
enabled: isCurrentAuthMethod
|
||||||
|
});
|
||||||
|
|
||||||
|
const internalPopUpState = usePopUp(["overwriteAuthMethod"] as const);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
@@ -175,9 +184,7 @@ export const IdentityAwsAuthForm = ({
|
|||||||
handlePopUpToggle("identityAuthMethod", false);
|
handlePopUpToggle("identityAuthMethod", false);
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
text: `Successfully ${
|
text: `Successfully ${isCurrentAuthMethod ? "updated" : "configured"} auth method`,
|
||||||
identityAuthMethodData?.authMethod ? "updated" : "configured"
|
|
||||||
} auth method`,
|
|
||||||
type: "success"
|
type: "success"
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -191,189 +198,216 @@ export const IdentityAwsAuthForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
<>
|
||||||
<Controller
|
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||||
control={control}
|
<Controller
|
||||||
defaultValue="2592000"
|
control={control}
|
||||||
name="allowedPrincipalArns"
|
defaultValue="2592000"
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="allowedPrincipalArns"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
label="Allowed Principal ARNs"
|
<FormControl
|
||||||
isError={Boolean(error)}
|
label="Allowed Principal ARNs"
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
>
|
errorText={error?.message}
|
||||||
<Input
|
>
|
||||||
{...field}
|
<Input
|
||||||
placeholder="arn:aws:iam::123456789012:role/MyRoleName, arn:aws:iam::123456789012:user/MyUserName..."
|
{...field}
|
||||||
type="text"
|
placeholder="arn:aws:iam::123456789012:role/MyRoleName, arn:aws:iam::123456789012:user/MyUserName..."
|
||||||
/>
|
type="text"
|
||||||
</FormControl>
|
/>
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
<Controller
|
/>
|
||||||
control={control}
|
<Controller
|
||||||
name="allowedAccountIds"
|
control={control}
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="allowedAccountIds"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
label="Allowed Account IDs"
|
<FormControl
|
||||||
isError={Boolean(error)}
|
label="Allowed Account IDs"
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
>
|
errorText={error?.message}
|
||||||
<Input {...field} placeholder="123456789012, ..." />
|
>
|
||||||
</FormControl>
|
<Input {...field} placeholder="123456789012, ..." />
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
<Controller
|
/>
|
||||||
control={control}
|
<Controller
|
||||||
defaultValue="https://sts.amazonaws.com/"
|
control={control}
|
||||||
name="stsEndpoint"
|
defaultValue="https://sts.amazonaws.com/"
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="stsEndpoint"
|
||||||
<FormControl label="STS Endpoint" isError={Boolean(error)} errorText={error?.message}>
|
render={({ field, fieldState: { error } }) => (
|
||||||
<Input {...field} placeholder="https://sts.amazonaws.com/" type="text" />
|
<FormControl label="STS Endpoint" isError={Boolean(error)} errorText={error?.message}>
|
||||||
</FormControl>
|
<Input {...field} placeholder="https://sts.amazonaws.com/" type="text" />
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
<Controller
|
/>
|
||||||
control={control}
|
<Controller
|
||||||
defaultValue="2592000"
|
control={control}
|
||||||
name="accessTokenTTL"
|
defaultValue="2592000"
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="accessTokenTTL"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
label="Access Token TTL (seconds)"
|
<FormControl
|
||||||
isError={Boolean(error)}
|
label="Access Token TTL (seconds)"
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
>
|
errorText={error?.message}
|
||||||
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
>
|
||||||
</FormControl>
|
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
<Controller
|
/>
|
||||||
control={control}
|
<Controller
|
||||||
defaultValue="2592000"
|
control={control}
|
||||||
name="accessTokenMaxTTL"
|
defaultValue="2592000"
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="accessTokenMaxTTL"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
label="Access Token Max TTL (seconds)"
|
<FormControl
|
||||||
isError={Boolean(error)}
|
label="Access Token Max TTL (seconds)"
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
>
|
errorText={error?.message}
|
||||||
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
>
|
||||||
</FormControl>
|
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
<Controller
|
/>
|
||||||
control={control}
|
<Controller
|
||||||
defaultValue="0"
|
control={control}
|
||||||
name="accessTokenNumUsesLimit"
|
defaultValue="0"
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="accessTokenNumUsesLimit"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
label="Access Token Max Number of Uses"
|
<FormControl
|
||||||
isError={Boolean(error)}
|
label="Access Token Max Number of Uses"
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
>
|
errorText={error?.message}
|
||||||
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
>
|
||||||
</FormControl>
|
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
/>
|
||||||
<div className="mb-3 flex items-end space-x-2" key={id}>
|
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
||||||
<Controller
|
<div className="mb-3 flex items-end space-x-2" key={id}>
|
||||||
control={control}
|
<Controller
|
||||||
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
control={control}
|
||||||
defaultValue="0.0.0.0/0"
|
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
||||||
render={({ field, fieldState: { error } }) => {
|
defaultValue="0.0.0.0/0"
|
||||||
return (
|
render={({ field, fieldState: { error } }) => {
|
||||||
<FormControl
|
return (
|
||||||
className="mb-0 flex-grow"
|
<FormControl
|
||||||
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
className="mb-0 flex-grow"
|
||||||
isError={Boolean(error)}
|
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
>
|
errorText={error?.message}
|
||||||
<Input
|
>
|
||||||
value={field.value}
|
<Input
|
||||||
onChange={(e) => {
|
value={field.value}
|
||||||
if (subscription?.ipAllowlisting) {
|
onChange={(e) => {
|
||||||
field.onChange(e);
|
if (subscription?.ipAllowlisting) {
|
||||||
return;
|
field.onChange(e);
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
handlePopUpOpen("upgradePlan");
|
||||||
}}
|
}}
|
||||||
placeholder="123.456.789.0"
|
placeholder="123.456.789.0"
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
onClick={() => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
removeAccessTokenTrustedIp(index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
size="lg"
|
||||||
|
colorSchema="danger"
|
||||||
|
variant="plain"
|
||||||
|
ariaLabel="update"
|
||||||
|
className="p-3"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="my-4 ml-1">
|
||||||
|
<Button
|
||||||
|
variant="outline_bg"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (subscription?.ipAllowlisting) {
|
if (subscription?.ipAllowlisting) {
|
||||||
removeAccessTokenTrustedIp(index);
|
appendAccessTokenTrustedIp({
|
||||||
|
ipAddress: "0.0.0.0/0"
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
handlePopUpOpen("upgradePlan");
|
||||||
}}
|
}}
|
||||||
size="lg"
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
colorSchema="danger"
|
size="xs"
|
||||||
variant="plain"
|
|
||||||
ariaLabel="update"
|
|
||||||
className="p-3"
|
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faXmark} />
|
Add IP Address
|
||||||
</IconButton>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div className="my-4 ml-1">
|
|
||||||
<Button
|
|
||||||
variant="outline_bg"
|
|
||||||
onClick={() => {
|
|
||||||
if (subscription?.ipAllowlisting) {
|
|
||||||
appendAccessTokenTrustedIp({
|
|
||||||
ipAddress: "0.0.0.0/0"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
|
||||||
size="xs"
|
|
||||||
>
|
|
||||||
Add IP Address
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Button
|
|
||||||
className="mr-4"
|
|
||||||
size="sm"
|
|
||||||
type="submit"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
isDisabled={isSubmitting}
|
|
||||||
>
|
|
||||||
{identityAuthMethodData?.authMethod ? "Update" : "Configure"}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
colorSchema="secondary"
|
|
||||||
variant="plain"
|
|
||||||
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{identityAuthMethodData?.authMethod && (
|
<div className="flex justify-between">
|
||||||
<Button
|
<div className="flex items-center">
|
||||||
size="sm"
|
{initialAuthMethod && identityAuthMethodData?.authMethod !== initialAuthMethod ? (
|
||||||
colorSchema="danger"
|
<Button
|
||||||
isLoading={isSubmitting}
|
className="mr-4"
|
||||||
isDisabled={isSubmitting}
|
size="sm"
|
||||||
onClick={() => handlePopUpToggle("revokeAuthMethod", true)}
|
isLoading={isSubmitting}
|
||||||
>
|
isDisabled={isSubmitting}
|
||||||
Remove Auth Method
|
onClick={() => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", true)}
|
||||||
</Button>
|
>
|
||||||
)}
|
Overwrite
|
||||||
</div>
|
</Button>
|
||||||
</form>
|
) : (
|
||||||
|
<Button
|
||||||
|
className="mr-4"
|
||||||
|
size="sm"
|
||||||
|
type="submit"
|
||||||
|
isLoading={isSubmitting}
|
||||||
|
isDisabled={isSubmitting}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
colorSchema="secondary"
|
||||||
|
variant="plain"
|
||||||
|
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{isCurrentAuthMethod && (
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
colorSchema="danger"
|
||||||
|
isLoading={isSubmitting}
|
||||||
|
isDisabled={isSubmitting}
|
||||||
|
onClick={() => handlePopUpToggle("revokeAuthMethod", true)}
|
||||||
|
>
|
||||||
|
Remove Auth Method
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<DeleteActionModal
|
||||||
|
isOpen={internalPopUpState.popUp.overwriteAuthMethod?.isOpen}
|
||||||
|
title={`Are you sure want to overwrite ${initialAuthMethod || "the auth method"} on ${
|
||||||
|
identityAuthMethodData?.name ?? ""
|
||||||
|
}?`}
|
||||||
|
onChange={(isOpen) => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", isOpen)}
|
||||||
|
deleteKey="confirm"
|
||||||
|
buttonText="Overwrite"
|
||||||
|
onDeleteApproved={async () => {
|
||||||
|
await revokeAuth(initialAuthMethod);
|
||||||
|
handleSubmit(onFormSubmit)();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+225
-181
@@ -6,7 +6,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, FormControl, IconButton, Input } from "@app/components/v2";
|
import { Button, DeleteActionModal, FormControl, IconButton, Input } from "@app/components/v2";
|
||||||
import { useOrganization, useSubscription } from "@app/context";
|
import { useOrganization, useSubscription } from "@app/context";
|
||||||
import {
|
import {
|
||||||
useAddIdentityAzureAuth,
|
useAddIdentityAzureAuth,
|
||||||
@@ -15,11 +15,11 @@ import {
|
|||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
||||||
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { usePopUp, UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
const schema = z
|
const schema = z
|
||||||
.object({
|
.object({
|
||||||
tenantId: z.string(),
|
tenantId: z.string().min(1),
|
||||||
resource: z.string(),
|
resource: z.string(),
|
||||||
allowedServicePrincipalIds: z.string(),
|
allowedServicePrincipalIds: z.string(),
|
||||||
accessTokenTTL: z.string().refine((val) => Number(val) <= 315360000, {
|
accessTokenTTL: z.string().refine((val) => Number(val) <= 315360000, {
|
||||||
@@ -52,12 +52,16 @@ type Props = {
|
|||||||
name: string;
|
name: string;
|
||||||
authMethod?: IdentityAuthMethod;
|
authMethod?: IdentityAuthMethod;
|
||||||
};
|
};
|
||||||
|
initialAuthMethod: IdentityAuthMethod;
|
||||||
|
revokeAuth: (authMethod: IdentityAuthMethod) => Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IdentityAzureAuthForm = ({
|
export const IdentityAzureAuthForm = ({
|
||||||
handlePopUpOpen,
|
handlePopUpOpen,
|
||||||
handlePopUpToggle,
|
handlePopUpToggle,
|
||||||
identityAuthMethodData
|
identityAuthMethodData,
|
||||||
|
initialAuthMethod,
|
||||||
|
revokeAuth
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
@@ -66,12 +70,18 @@ export const IdentityAzureAuthForm = ({
|
|||||||
const { mutateAsync: addMutateAsync } = useAddIdentityAzureAuth();
|
const { mutateAsync: addMutateAsync } = useAddIdentityAzureAuth();
|
||||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityAzureAuth();
|
const { mutateAsync: updateMutateAsync } = useUpdateIdentityAzureAuth();
|
||||||
|
|
||||||
const { data } = useGetIdentityAzureAuth(identityAuthMethodData?.identityId ?? "");
|
const isCurrentAuthMethod = identityAuthMethodData?.authMethod === initialAuthMethod;
|
||||||
|
const { data } = useGetIdentityAzureAuth(identityAuthMethodData?.identityId ?? "", {
|
||||||
|
enabled: isCurrentAuthMethod
|
||||||
|
});
|
||||||
|
|
||||||
|
const internalPopUpState = usePopUp(["overwriteAuthMethod"] as const);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
reset,
|
reset,
|
||||||
|
trigger,
|
||||||
formState: { isSubmitting }
|
formState: { isSubmitting }
|
||||||
} = useForm<FormData>({
|
} = useForm<FormData>({
|
||||||
resolver: zodResolver(schema),
|
resolver: zodResolver(schema),
|
||||||
@@ -163,9 +173,7 @@ export const IdentityAzureAuthForm = ({
|
|||||||
handlePopUpToggle("identityAuthMethod", false);
|
handlePopUpToggle("identityAuthMethod", false);
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
text: `Successfully ${
|
text: `Successfully ${isCurrentAuthMethod ? "updated" : "configured"} auth method`,
|
||||||
identityAuthMethodData?.authMethod ? "updated" : "configured"
|
|
||||||
} auth method`,
|
|
||||||
type: "success"
|
type: "success"
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -179,189 +187,225 @@ export const IdentityAzureAuthForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
<>
|
||||||
<Controller
|
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||||
control={control}
|
<Controller
|
||||||
defaultValue="2592000"
|
control={control}
|
||||||
name="tenantId"
|
defaultValue="2592000"
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="tenantId"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
label="Tenant ID"
|
<FormControl
|
||||||
isError={Boolean(error)}
|
label="Tenant ID"
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
isRequired
|
errorText={error?.message}
|
||||||
>
|
isRequired
|
||||||
<Input {...field} placeholder="00000000-0000-0000-0000-000000000000" type="text" />
|
>
|
||||||
</FormControl>
|
<Input {...field} placeholder="00000000-0000-0000-0000-000000000000" type="text" />
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
<Controller
|
/>
|
||||||
control={control}
|
<Controller
|
||||||
name="resource"
|
control={control}
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="resource"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
label="Resource / Audience"
|
<FormControl
|
||||||
isError={Boolean(error)}
|
label="Resource / Audience"
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
>
|
errorText={error?.message}
|
||||||
<Input {...field} placeholder="https://management.azure.com/" />
|
>
|
||||||
</FormControl>
|
<Input {...field} placeholder="https://management.azure.com/" />
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
<Controller
|
/>
|
||||||
control={control}
|
<Controller
|
||||||
name="allowedServicePrincipalIds"
|
control={control}
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="allowedServicePrincipalIds"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
label="Allowed Service Principal IDs"
|
<FormControl
|
||||||
isError={Boolean(error)}
|
label="Allowed Service Principal IDs"
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
>
|
errorText={error?.message}
|
||||||
<Input {...field} placeholder="00000000-0000-0000-0000-000000000000, ..." />
|
>
|
||||||
</FormControl>
|
<Input {...field} placeholder="00000000-0000-0000-0000-000000000000, ..." />
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
<Controller
|
/>
|
||||||
control={control}
|
<Controller
|
||||||
defaultValue="2592000"
|
control={control}
|
||||||
name="accessTokenTTL"
|
defaultValue="2592000"
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="accessTokenTTL"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
label="Access Token TTL (seconds)"
|
<FormControl
|
||||||
isError={Boolean(error)}
|
label="Access Token TTL (seconds)"
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
>
|
errorText={error?.message}
|
||||||
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
>
|
||||||
</FormControl>
|
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
<Controller
|
/>
|
||||||
control={control}
|
<Controller
|
||||||
defaultValue="2592000"
|
control={control}
|
||||||
name="accessTokenMaxTTL"
|
defaultValue="2592000"
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="accessTokenMaxTTL"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
label="Access Token Max TTL (seconds)"
|
<FormControl
|
||||||
isError={Boolean(error)}
|
label="Access Token Max TTL (seconds)"
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
>
|
errorText={error?.message}
|
||||||
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
>
|
||||||
</FormControl>
|
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
<Controller
|
/>
|
||||||
control={control}
|
<Controller
|
||||||
defaultValue="0"
|
control={control}
|
||||||
name="accessTokenNumUsesLimit"
|
defaultValue="0"
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="accessTokenNumUsesLimit"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
label="Access Token Max Number of Uses"
|
<FormControl
|
||||||
isError={Boolean(error)}
|
label="Access Token Max Number of Uses"
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
>
|
errorText={error?.message}
|
||||||
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
>
|
||||||
</FormControl>
|
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
/>
|
||||||
<div className="mb-3 flex items-end space-x-2" key={id}>
|
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
||||||
<Controller
|
<div className="mb-3 flex items-end space-x-2" key={id}>
|
||||||
control={control}
|
<Controller
|
||||||
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
control={control}
|
||||||
defaultValue="0.0.0.0/0"
|
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
||||||
render={({ field, fieldState: { error } }) => {
|
defaultValue="0.0.0.0/0"
|
||||||
return (
|
render={({ field, fieldState: { error } }) => {
|
||||||
<FormControl
|
return (
|
||||||
className="mb-0 flex-grow"
|
<FormControl
|
||||||
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
className="mb-0 flex-grow"
|
||||||
isError={Boolean(error)}
|
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
>
|
errorText={error?.message}
|
||||||
<Input
|
>
|
||||||
value={field.value}
|
<Input
|
||||||
onChange={(e) => {
|
value={field.value}
|
||||||
if (subscription?.ipAllowlisting) {
|
onChange={(e) => {
|
||||||
field.onChange(e);
|
if (subscription?.ipAllowlisting) {
|
||||||
return;
|
field.onChange(e);
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
handlePopUpOpen("upgradePlan");
|
||||||
}}
|
}}
|
||||||
placeholder="123.456.789.0"
|
placeholder="123.456.789.0"
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
onClick={() => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
removeAccessTokenTrustedIp(index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
size="lg"
|
||||||
|
colorSchema="danger"
|
||||||
|
variant="plain"
|
||||||
|
ariaLabel="update"
|
||||||
|
className="p-3"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="my-4 ml-1">
|
||||||
|
<Button
|
||||||
|
variant="outline_bg"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (subscription?.ipAllowlisting) {
|
if (subscription?.ipAllowlisting) {
|
||||||
removeAccessTokenTrustedIp(index);
|
appendAccessTokenTrustedIp({
|
||||||
|
ipAddress: "0.0.0.0/0"
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
handlePopUpOpen("upgradePlan");
|
||||||
}}
|
}}
|
||||||
size="lg"
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
colorSchema="danger"
|
size="xs"
|
||||||
variant="plain"
|
|
||||||
ariaLabel="update"
|
|
||||||
className="p-3"
|
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faXmark} />
|
Add IP Address
|
||||||
</IconButton>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div className="my-4 ml-1">
|
|
||||||
<Button
|
|
||||||
variant="outline_bg"
|
|
||||||
onClick={() => {
|
|
||||||
if (subscription?.ipAllowlisting) {
|
|
||||||
appendAccessTokenTrustedIp({
|
|
||||||
ipAddress: "0.0.0.0/0"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
|
||||||
size="xs"
|
|
||||||
>
|
|
||||||
Add IP Address
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Button
|
|
||||||
className="mr-4"
|
|
||||||
size="sm"
|
|
||||||
type="submit"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
isDisabled={isSubmitting}
|
|
||||||
>
|
|
||||||
{identityAuthMethodData?.authMethod ? "Update" : "Configure"}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
colorSchema="secondary"
|
|
||||||
variant="plain"
|
|
||||||
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{identityAuthMethodData?.authMethod && (
|
<div className="flex justify-between">
|
||||||
<Button
|
<div className="flex items-center">
|
||||||
size="sm"
|
{initialAuthMethod && identityAuthMethodData?.authMethod !== initialAuthMethod ? (
|
||||||
colorSchema="danger"
|
<Button
|
||||||
isLoading={isSubmitting}
|
className="mr-4"
|
||||||
isDisabled={isSubmitting}
|
size="sm"
|
||||||
onClick={() => handlePopUpToggle("revokeAuthMethod", true)}
|
isLoading={isSubmitting}
|
||||||
>
|
isDisabled={isSubmitting}
|
||||||
Remove Auth Method
|
onClick={() => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", true)}
|
||||||
</Button>
|
>
|
||||||
)}
|
Overwrite
|
||||||
</div>
|
</Button>
|
||||||
</form>
|
) : (
|
||||||
|
<Button
|
||||||
|
className="mr-4"
|
||||||
|
size="sm"
|
||||||
|
type="submit"
|
||||||
|
isLoading={isSubmitting}
|
||||||
|
isDisabled={isSubmitting}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
colorSchema="secondary"
|
||||||
|
variant="plain"
|
||||||
|
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{isCurrentAuthMethod && (
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
colorSchema="danger"
|
||||||
|
isLoading={isSubmitting}
|
||||||
|
isDisabled={isSubmitting}
|
||||||
|
onClick={() => handlePopUpToggle("revokeAuthMethod", true)}
|
||||||
|
>
|
||||||
|
Remove Auth Method
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<DeleteActionModal
|
||||||
|
isOpen={internalPopUpState.popUp.overwriteAuthMethod?.isOpen}
|
||||||
|
title={`Are you sure want to overwrite ${initialAuthMethod || "the auth method"} on ${
|
||||||
|
identityAuthMethodData?.name ?? ""
|
||||||
|
}?`}
|
||||||
|
onChange={(isOpen) => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", isOpen)}
|
||||||
|
deleteKey="confirm"
|
||||||
|
buttonText="Overwrite"
|
||||||
|
onDeleteApproved={async () => {
|
||||||
|
const result = await trigger();
|
||||||
|
if (result) {
|
||||||
|
await revokeAuth(initialAuthMethod);
|
||||||
|
handleSubmit(onFormSubmit)();
|
||||||
|
} else {
|
||||||
|
createNotification({
|
||||||
|
text: "Please fill in all required fields",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
internalPopUpState.handlePopUpToggle("overwriteAuthMethod", false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+239
-194
@@ -6,7 +6,15 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, FormControl, IconButton, Input, Select, SelectItem } from "@app/components/v2";
|
import {
|
||||||
|
Button,
|
||||||
|
DeleteActionModal,
|
||||||
|
FormControl,
|
||||||
|
IconButton,
|
||||||
|
Input,
|
||||||
|
Select,
|
||||||
|
SelectItem
|
||||||
|
} from "@app/components/v2";
|
||||||
import { useOrganization, useSubscription } from "@app/context";
|
import { useOrganization, useSubscription } from "@app/context";
|
||||||
import {
|
import {
|
||||||
useAddIdentityGcpAuth,
|
useAddIdentityGcpAuth,
|
||||||
@@ -15,7 +23,7 @@ import {
|
|||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
||||||
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { usePopUp, UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
const schema = z
|
const schema = z
|
||||||
.object({
|
.object({
|
||||||
@@ -53,12 +61,16 @@ type Props = {
|
|||||||
name: string;
|
name: string;
|
||||||
authMethod?: IdentityAuthMethod;
|
authMethod?: IdentityAuthMethod;
|
||||||
};
|
};
|
||||||
|
initialAuthMethod: IdentityAuthMethod;
|
||||||
|
revokeAuth: (authMethod: IdentityAuthMethod) => Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IdentityGcpAuthForm = ({
|
export const IdentityGcpAuthForm = ({
|
||||||
handlePopUpOpen,
|
handlePopUpOpen,
|
||||||
handlePopUpToggle,
|
handlePopUpToggle,
|
||||||
identityAuthMethodData
|
identityAuthMethodData,
|
||||||
|
revokeAuth,
|
||||||
|
initialAuthMethod
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
@@ -67,7 +79,11 @@ export const IdentityGcpAuthForm = ({
|
|||||||
const { mutateAsync: addMutateAsync } = useAddIdentityGcpAuth();
|
const { mutateAsync: addMutateAsync } = useAddIdentityGcpAuth();
|
||||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityGcpAuth();
|
const { mutateAsync: updateMutateAsync } = useUpdateIdentityGcpAuth();
|
||||||
|
|
||||||
const { data } = useGetIdentityGcpAuth(identityAuthMethodData?.identityId ?? "");
|
const isCurrentAuthMethod = identityAuthMethodData?.authMethod === initialAuthMethod;
|
||||||
|
const { data } = useGetIdentityGcpAuth(identityAuthMethodData?.identityId ?? "", {
|
||||||
|
enabled: isCurrentAuthMethod
|
||||||
|
});
|
||||||
|
const internalPopUpState = usePopUp(["overwriteAuthMethod"] as const);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
@@ -100,7 +116,7 @@ export const IdentityGcpAuthForm = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data) {
|
if (data) {
|
||||||
reset({
|
reset({
|
||||||
type: data.type,
|
type: data.type || "gce",
|
||||||
allowedServiceAccounts: data.allowedServiceAccounts,
|
allowedServiceAccounts: data.allowedServiceAccounts,
|
||||||
allowedProjects: data.allowedProjects,
|
allowedProjects: data.allowedProjects,
|
||||||
allowedZones: data.allowedZones,
|
allowedZones: data.allowedZones,
|
||||||
@@ -117,7 +133,7 @@ export const IdentityGcpAuthForm = ({
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
reset({
|
reset({
|
||||||
type: "iam",
|
type: "gce",
|
||||||
allowedServiceAccounts: "",
|
allowedServiceAccounts: "",
|
||||||
allowedProjects: "",
|
allowedProjects: "",
|
||||||
allowedZones: "",
|
allowedZones: "",
|
||||||
@@ -173,9 +189,7 @@ export const IdentityGcpAuthForm = ({
|
|||||||
handlePopUpToggle("identityAuthMethod", false);
|
handlePopUpToggle("identityAuthMethod", false);
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
text: `Successfully ${
|
text: `Successfully ${isCurrentAuthMethod ? "updated" : "configured"} auth method`,
|
||||||
identityAuthMethodData?.authMethod ? "updated" : "configured"
|
|
||||||
} auth method`,
|
|
||||||
type: "success"
|
type: "success"
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -189,213 +203,244 @@ export const IdentityGcpAuthForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
<>
|
||||||
<Controller
|
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||||
control={control}
|
|
||||||
name="type"
|
|
||||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
|
||||||
<FormControl label="Type" isError={Boolean(error)} errorText={error?.message}>
|
|
||||||
<Select
|
|
||||||
defaultValue={field.value}
|
|
||||||
{...field}
|
|
||||||
onValueChange={(e) => onChange(e)}
|
|
||||||
className="w-full"
|
|
||||||
>
|
|
||||||
<SelectItem value="gce" key="gcp-type-gce">
|
|
||||||
GCP ID Token Auth (Recommended)
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="iam" key="gcpiam">
|
|
||||||
GCP IAM Auth
|
|
||||||
</SelectItem>
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue="2592000"
|
|
||||||
name="allowedServiceAccounts"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Allowed Service Account Emails"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
placeholder="[email protected], [email protected]"
|
|
||||||
type="text"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{watchedType === "gce" && (
|
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="allowedProjects"
|
name="type"
|
||||||
|
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
||||||
|
<FormControl label="Type" isError={Boolean(error)} errorText={error?.message}>
|
||||||
|
<Select
|
||||||
|
defaultValue={field.value}
|
||||||
|
{...field}
|
||||||
|
onValueChange={(e) => onChange(e)}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
<SelectItem value="gce" key="gce">
|
||||||
|
GCP ID Token Auth (Recommended)
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="iam" key="iam">
|
||||||
|
GCP IAM Auth
|
||||||
|
</SelectItem>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue="2592000"
|
||||||
|
name="allowedServiceAccounts"
|
||||||
render={({ field, fieldState: { error } }) => (
|
render={({ field, fieldState: { error } }) => (
|
||||||
<FormControl
|
<FormControl
|
||||||
label="Allowed Projects"
|
label="Allowed Service Account Emails"
|
||||||
isError={Boolean(error)}
|
isError={Boolean(error)}
|
||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
>
|
>
|
||||||
<Input {...field} placeholder="my-gcp-project, ..." />
|
<Input
|
||||||
|
{...field}
|
||||||
|
placeholder="[email protected], [email protected]"
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
{watchedType === "gce" && (
|
||||||
{watchedType === "gce" && (
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="allowedZones"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl label="Allowed Zones" isError={Boolean(error)} errorText={error?.message}>
|
|
||||||
<Input {...field} placeholder="us-west2-a, us-central1-a, ..." />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<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>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
|
||||||
<div className="mb-3 flex items-end space-x-2" key={id}>
|
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
name="allowedProjects"
|
||||||
defaultValue="0.0.0.0/0"
|
render={({ field, fieldState: { error } }) => (
|
||||||
render={({ field, fieldState: { error } }) => {
|
<FormControl
|
||||||
return (
|
label="Allowed Projects"
|
||||||
<FormControl
|
isError={Boolean(error)}
|
||||||
className="mb-0 flex-grow"
|
errorText={error?.message}
|
||||||
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
>
|
||||||
isError={Boolean(error)}
|
<Input {...field} placeholder="my-gcp-project, ..." />
|
||||||
errorText={error?.message}
|
</FormControl>
|
||||||
>
|
)}
|
||||||
<Input
|
|
||||||
value={field.value}
|
|
||||||
onChange={(e) => {
|
|
||||||
if (subscription?.ipAllowlisting) {
|
|
||||||
field.onChange(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}}
|
|
||||||
placeholder="123.456.789.0"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<IconButton
|
)}
|
||||||
|
{watchedType === "gce" && (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="allowedZones"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Allowed Zones"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="us-west2-a, us-central1-a, ..." />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<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>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
||||||
|
<div className="mb-3 flex items-end space-x-2" key={id}>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
||||||
|
defaultValue="0.0.0.0/0"
|
||||||
|
render={({ field, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl
|
||||||
|
className="mb-0 flex-grow"
|
||||||
|
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={field.value}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
field.onChange(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
placeholder="123.456.789.0"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
onClick={() => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
removeAccessTokenTrustedIp(index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
size="lg"
|
||||||
|
colorSchema="danger"
|
||||||
|
variant="plain"
|
||||||
|
ariaLabel="update"
|
||||||
|
className="p-3"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="my-4 ml-1">
|
||||||
|
<Button
|
||||||
|
variant="outline_bg"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (subscription?.ipAllowlisting) {
|
if (subscription?.ipAllowlisting) {
|
||||||
removeAccessTokenTrustedIp(index);
|
appendAccessTokenTrustedIp({
|
||||||
|
ipAddress: "0.0.0.0/0"
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
handlePopUpOpen("upgradePlan");
|
||||||
}}
|
}}
|
||||||
size="lg"
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
colorSchema="danger"
|
size="xs"
|
||||||
variant="plain"
|
|
||||||
ariaLabel="update"
|
|
||||||
className="p-3"
|
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faXmark} />
|
Add IP Address
|
||||||
</IconButton>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div className="my-4 ml-1">
|
|
||||||
<Button
|
|
||||||
variant="outline_bg"
|
|
||||||
onClick={() => {
|
|
||||||
if (subscription?.ipAllowlisting) {
|
|
||||||
appendAccessTokenTrustedIp({
|
|
||||||
ipAddress: "0.0.0.0/0"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
|
||||||
size="xs"
|
|
||||||
>
|
|
||||||
Add IP Address
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Button
|
|
||||||
className="mr-4"
|
|
||||||
size="sm"
|
|
||||||
type="submit"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
isDisabled={isSubmitting}
|
|
||||||
>
|
|
||||||
{identityAuthMethodData?.authMethod ? "Update" : "Configure"}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
colorSchema="secondary"
|
|
||||||
variant="plain"
|
|
||||||
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{identityAuthMethodData?.authMethod && (
|
<div className="flex justify-between">
|
||||||
<Button
|
<div className="flex items-center">
|
||||||
size="sm"
|
{initialAuthMethod && identityAuthMethodData?.authMethod !== initialAuthMethod ? (
|
||||||
colorSchema="danger"
|
<Button
|
||||||
isLoading={isSubmitting}
|
className="mr-4"
|
||||||
isDisabled={isSubmitting}
|
size="sm"
|
||||||
onClick={() => handlePopUpToggle("revokeAuthMethod", true)}
|
isLoading={isSubmitting}
|
||||||
>
|
isDisabled={isSubmitting}
|
||||||
Remove Auth Method
|
onClick={() => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", true)}
|
||||||
</Button>
|
>
|
||||||
)}
|
Overwrite
|
||||||
</div>
|
</Button>
|
||||||
</form>
|
) : (
|
||||||
|
<Button
|
||||||
|
className="mr-4"
|
||||||
|
size="sm"
|
||||||
|
type="submit"
|
||||||
|
isLoading={isSubmitting}
|
||||||
|
isDisabled={isSubmitting}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
colorSchema="secondary"
|
||||||
|
variant="plain"
|
||||||
|
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{isCurrentAuthMethod && (
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
colorSchema="danger"
|
||||||
|
isLoading={isSubmitting}
|
||||||
|
isDisabled={isSubmitting}
|
||||||
|
onClick={() => handlePopUpToggle("revokeAuthMethod", true)}
|
||||||
|
>
|
||||||
|
Remove Auth Method
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<DeleteActionModal
|
||||||
|
isOpen={internalPopUpState.popUp.overwriteAuthMethod?.isOpen}
|
||||||
|
title={`Are you sure want to overwrite ${initialAuthMethod || "the auth method"} on ${
|
||||||
|
identityAuthMethodData?.name ?? ""
|
||||||
|
}?`}
|
||||||
|
buttonText="Overwrite"
|
||||||
|
onChange={(isOpen) => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", isOpen)}
|
||||||
|
deleteKey="confirm"
|
||||||
|
onDeleteApproved={async () => {
|
||||||
|
await revokeAuth(initialAuthMethod);
|
||||||
|
handleSubmit(onFormSubmit)();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+283
-231
@@ -6,7 +6,14 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, FormControl, IconButton, Input, TextArea } from "@app/components/v2";
|
import {
|
||||||
|
Button,
|
||||||
|
DeleteActionModal,
|
||||||
|
FormControl,
|
||||||
|
IconButton,
|
||||||
|
Input,
|
||||||
|
TextArea
|
||||||
|
} from "@app/components/v2";
|
||||||
import { useOrganization, useSubscription } from "@app/context";
|
import { useOrganization, useSubscription } from "@app/context";
|
||||||
import {
|
import {
|
||||||
useAddIdentityKubernetesAuth,
|
useAddIdentityKubernetesAuth,
|
||||||
@@ -15,12 +22,12 @@ import {
|
|||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
||||||
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { usePopUp, UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
const schema = z
|
const schema = z
|
||||||
.object({
|
.object({
|
||||||
kubernetesHost: z.string(),
|
kubernetesHost: z.string().min(1),
|
||||||
tokenReviewerJwt: z.string(),
|
tokenReviewerJwt: z.string().min(1),
|
||||||
allowedNames: z.string(),
|
allowedNames: z.string(),
|
||||||
allowedNamespaces: z.string(),
|
allowedNamespaces: z.string(),
|
||||||
allowedAudience: z.string(),
|
allowedAudience: z.string(),
|
||||||
@@ -55,12 +62,16 @@ type Props = {
|
|||||||
name: string;
|
name: string;
|
||||||
authMethod?: IdentityAuthMethod;
|
authMethod?: IdentityAuthMethod;
|
||||||
};
|
};
|
||||||
|
initialAuthMethod: IdentityAuthMethod;
|
||||||
|
revokeAuth: (authMethod: IdentityAuthMethod) => Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IdentityKubernetesAuthForm = ({
|
export const IdentityKubernetesAuthForm = ({
|
||||||
handlePopUpOpen,
|
handlePopUpOpen,
|
||||||
handlePopUpToggle,
|
handlePopUpToggle,
|
||||||
identityAuthMethodData
|
identityAuthMethodData,
|
||||||
|
initialAuthMethod,
|
||||||
|
revokeAuth
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
@@ -69,12 +80,17 @@ export const IdentityKubernetesAuthForm = ({
|
|||||||
const { mutateAsync: addMutateAsync } = useAddIdentityKubernetesAuth();
|
const { mutateAsync: addMutateAsync } = useAddIdentityKubernetesAuth();
|
||||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityKubernetesAuth();
|
const { mutateAsync: updateMutateAsync } = useUpdateIdentityKubernetesAuth();
|
||||||
|
|
||||||
const { data } = useGetIdentityKubernetesAuth(identityAuthMethodData?.identityId ?? "");
|
const isCurrentAuthMethod = identityAuthMethodData?.authMethod === initialAuthMethod;
|
||||||
|
const { data } = useGetIdentityKubernetesAuth(identityAuthMethodData?.identityId ?? "", {
|
||||||
|
enabled: isCurrentAuthMethod
|
||||||
|
});
|
||||||
|
const internalPopUpState = usePopUp(["overwriteAuthMethod"] as const);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
reset,
|
reset,
|
||||||
|
trigger,
|
||||||
formState: { isSubmitting }
|
formState: { isSubmitting }
|
||||||
} = useForm<FormData>({
|
} = useForm<FormData>({
|
||||||
resolver: zodResolver(schema),
|
resolver: zodResolver(schema),
|
||||||
@@ -184,9 +200,7 @@ export const IdentityKubernetesAuthForm = ({
|
|||||||
handlePopUpToggle("identityAuthMethod", false);
|
handlePopUpToggle("identityAuthMethod", false);
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
text: `Successfully ${
|
text: `Successfully ${isCurrentAuthMethod ? "updated" : "configured"} auth method`,
|
||||||
identityAuthMethodData?.authMethod ? "updated" : "configured"
|
|
||||||
} auth method`,
|
|
||||||
type: "success"
|
type: "success"
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -200,239 +214,277 @@ export const IdentityKubernetesAuthForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
<>
|
||||||
<Controller
|
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||||
control={control}
|
<Controller
|
||||||
defaultValue="2592000"
|
control={control}
|
||||||
name="kubernetesHost"
|
defaultValue="2592000"
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="kubernetesHost"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
label="Kubernetes Host / Base Kubernetes API URL "
|
<FormControl
|
||||||
isError={Boolean(error)}
|
label="Kubernetes Host / Base Kubernetes API URL "
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
tooltipText="The host string, host:port pair, or URL to the base of the Kubernetes API server. This can usually be obtained by running 'kubectl cluster-info'"
|
errorText={error?.message}
|
||||||
isRequired
|
tooltipText="The host string, host:port pair, or URL to the base of the Kubernetes API server. This can usually be obtained by running 'kubectl cluster-info'"
|
||||||
>
|
isRequired
|
||||||
<Input {...field} placeholder="https://my-example-k8s-api-host.com" type="text" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="tokenReviewerJwt"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Token Reviewer JWT"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
tooltipText="A long-lived service account JWT token for Infisical to access the TokenReview API to validate other service account JWT tokens submitted by applications/pods."
|
|
||||||
isRequired
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="" type="password" />
|
|
||||||
</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
|
|
||||||
control={control}
|
|
||||||
defaultValue=""
|
|
||||||
name="allowedNamespaces"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Allowed Namespaces"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
tooltipText="An optional comma-separated list of trusted service account names that are allowed to authenticate with Infisical. Leave empty to allow any namespaces."
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="namespaceA, namespaceB" type="text" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue=""
|
|
||||||
name="allowedAudience"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Allowed Audience"
|
|
||||||
isError={Boolean(error)} errorText={error?.message}
|
|
||||||
tooltipText="An optional audience claim that the service account JWT token must have to authenticate with Infisical. Leave empty to allow any audience claim."
|
|
||||||
>
|
>
|
||||||
<Input {...field} placeholder="" type="text" />
|
<Input {...field} placeholder="https://my-example-k8s-api-host.com" type="text" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="caCert"
|
name="tokenReviewerJwt"
|
||||||
render={({ field, fieldState: { error } }) => (
|
render={({ field, fieldState: { error } }) => (
|
||||||
<FormControl
|
<FormControl
|
||||||
label="CA Certificate"
|
label="Token Reviewer JWT"
|
||||||
errorText={error?.message} isError={Boolean(error)}
|
isError={Boolean(error)}
|
||||||
tooltipText="An optional PEM-encoded CA cert for the Kubernetes API server. This is used by the TLS client for secure communication with the Kubernetes API server."
|
errorText={error?.message}
|
||||||
>
|
tooltipText="A long-lived service account JWT token for Infisical to access the TokenReview API to validate other service account JWT tokens submitted by applications/pods."
|
||||||
<TextArea {...field} placeholder="-----BEGIN CERTIFICATE----- ..." />
|
isRequired
|
||||||
</FormControl>
|
>
|
||||||
)}
|
<Input {...field} placeholder="" type="password" />
|
||||||
/>
|
</FormControl>
|
||||||
<Controller
|
)}
|
||||||
control={control}
|
/>
|
||||||
defaultValue="2592000"
|
<Controller
|
||||||
name="accessTokenTTL"
|
control={control}
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="allowedNames"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
label="Access Token TTL (seconds)"
|
<FormControl
|
||||||
tooltipText="The lifetime for an acccess token in seconds. This value will be referenced at renewal time."
|
label="Allowed Service Account Names"
|
||||||
isError={Boolean(error)}
|
isError={Boolean(error)}
|
||||||
errorText={error?.message}
|
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="2592000" type="number" min="1" step="1" />
|
>
|
||||||
</FormControl>
|
<Input {...field} placeholder="service-account-1-name, service-account-1-name" />
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
<Controller
|
/>
|
||||||
control={control}
|
<Controller
|
||||||
defaultValue="2592000"
|
control={control}
|
||||||
name="accessTokenMaxTTL"
|
defaultValue=""
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="allowedNamespaces"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
label="Access Token Max TTL (seconds)"
|
<FormControl
|
||||||
isError={Boolean(error)}
|
label="Allowed Namespaces"
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
tooltipText="The maximum lifetime for an access token in seconds. This value will be referenced at renewal time."
|
errorText={error?.message}
|
||||||
>
|
tooltipText="An optional comma-separated list of trusted service account names that are allowed to authenticate with Infisical. Leave empty to allow any namespaces."
|
||||||
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
>
|
||||||
</FormControl>
|
<Input {...field} placeholder="namespaceA, namespaceB" type="text" />
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
<Controller
|
/>
|
||||||
control={control}
|
<Controller
|
||||||
defaultValue="0"
|
control={control}
|
||||||
name="accessTokenNumUsesLimit"
|
defaultValue=""
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="allowedAudience"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
label="Access Token Max Number of Uses"
|
<FormControl
|
||||||
isError={Boolean(error)}
|
label="Allowed Audience"
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
tooltipText="The maximum number of times that an access token can be used; a value of 0 implies infinite number of uses."
|
errorText={error?.message}
|
||||||
>
|
tooltipText="An optional audience claim that the service account JWT token must have to authenticate with Infisical. Leave empty to allow any audience claim."
|
||||||
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
>
|
||||||
</FormControl>
|
<Input {...field} placeholder="" type="text" />
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
/>
|
||||||
<div className="mb-3 flex items-end space-x-2" key={id}>
|
<Controller
|
||||||
<Controller
|
control={control}
|
||||||
control={control}
|
name="caCert"
|
||||||
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
render={({ field, fieldState: { error } }) => (
|
||||||
defaultValue="0.0.0.0/0"
|
<FormControl
|
||||||
render={({ field, fieldState: { error } }) => {
|
label="CA Certificate"
|
||||||
return (
|
errorText={error?.message}
|
||||||
<FormControl
|
isError={Boolean(error)}
|
||||||
className="mb-0 flex-grow"
|
tooltipText="An optional PEM-encoded CA cert for the Kubernetes API server. This is used by the TLS client for secure communication with the Kubernetes API server."
|
||||||
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
>
|
||||||
isError={Boolean(error)}
|
<TextArea {...field} placeholder="-----BEGIN CERTIFICATE----- ..." />
|
||||||
errorText={error?.message}
|
</FormControl>
|
||||||
tooltipText="The IPs or CIDR ranges that access tokens can be used from. By default, each token is given the 0.0.0.0/0, allowing usage from any network address."
|
)}
|
||||||
>
|
/>
|
||||||
<Input
|
<Controller
|
||||||
value={field.value}
|
control={control}
|
||||||
onChange={(e) => {
|
defaultValue="2592000"
|
||||||
if (subscription?.ipAllowlisting) {
|
name="accessTokenTTL"
|
||||||
field.onChange(e);
|
render={({ field, fieldState: { error } }) => (
|
||||||
return;
|
<FormControl
|
||||||
}
|
label="Access Token TTL (seconds)"
|
||||||
|
tooltipText="The lifetime for an acccess token in seconds. This value will be referenced at renewal time."
|
||||||
|
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}
|
||||||
|
tooltipText="The maximum lifetime for an access token in seconds. This value will be referenced at renewal time."
|
||||||
|
>
|
||||||
|
<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}
|
||||||
|
tooltipText="The maximum number of times that an access token can be used; a value of 0 implies infinite number of uses."
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
||||||
|
<div className="mb-3 flex items-end space-x-2" key={id}>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
||||||
|
defaultValue="0.0.0.0/0"
|
||||||
|
render={({ field, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl
|
||||||
|
className="mb-0 flex-grow"
|
||||||
|
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
tooltipText="The IPs or CIDR ranges that access tokens can be used from. By default, each token is given the 0.0.0.0/0, allowing usage from any network address."
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={field.value}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
field.onChange(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
handlePopUpOpen("upgradePlan");
|
||||||
}}
|
}}
|
||||||
placeholder="123.456.789.0"
|
placeholder="123.456.789.0"
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
onClick={() => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
removeAccessTokenTrustedIp(index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
size="lg"
|
||||||
|
colorSchema="danger"
|
||||||
|
variant="plain"
|
||||||
|
ariaLabel="update"
|
||||||
|
className="p-3"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="my-4 ml-1">
|
||||||
|
<Button
|
||||||
|
variant="outline_bg"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (subscription?.ipAllowlisting) {
|
if (subscription?.ipAllowlisting) {
|
||||||
removeAccessTokenTrustedIp(index);
|
appendAccessTokenTrustedIp({
|
||||||
|
ipAddress: "0.0.0.0/0"
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
handlePopUpOpen("upgradePlan");
|
||||||
}}
|
}}
|
||||||
size="lg"
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
colorSchema="danger"
|
size="xs"
|
||||||
variant="plain"
|
|
||||||
ariaLabel="update"
|
|
||||||
className="p-3"
|
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faXmark} />
|
Add IP Address
|
||||||
</IconButton>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div className="my-4 ml-1">
|
|
||||||
<Button
|
|
||||||
variant="outline_bg"
|
|
||||||
onClick={() => {
|
|
||||||
if (subscription?.ipAllowlisting) {
|
|
||||||
appendAccessTokenTrustedIp({
|
|
||||||
ipAddress: "0.0.0.0/0"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
|
||||||
size="xs"
|
|
||||||
>
|
|
||||||
Add IP Address
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Button
|
|
||||||
className="mr-4"
|
|
||||||
size="sm"
|
|
||||||
type="submit"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
isDisabled={isSubmitting}
|
|
||||||
>
|
|
||||||
{identityAuthMethodData?.authMethod ? "Update" : "Configure"}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
colorSchema="secondary"
|
|
||||||
variant="plain"
|
|
||||||
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{identityAuthMethodData?.authMethod && (
|
<div className="flex justify-between">
|
||||||
<Button
|
<div className="flex items-center">
|
||||||
size="sm"
|
{initialAuthMethod && identityAuthMethodData?.authMethod !== initialAuthMethod ? (
|
||||||
colorSchema="danger"
|
<Button
|
||||||
isLoading={isSubmitting}
|
className="mr-4"
|
||||||
isDisabled={isSubmitting}
|
size="sm"
|
||||||
onClick={() => handlePopUpToggle("revokeAuthMethod", true)}
|
isLoading={isSubmitting}
|
||||||
>
|
isDisabled={isSubmitting}
|
||||||
Remove Auth Method
|
onClick={() => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", true)}
|
||||||
</Button>
|
>
|
||||||
)}
|
Overwrite
|
||||||
</div>
|
</Button>
|
||||||
</form>
|
) : (
|
||||||
|
<Button
|
||||||
|
className="mr-4"
|
||||||
|
size="sm"
|
||||||
|
type="submit"
|
||||||
|
isLoading={isSubmitting}
|
||||||
|
isDisabled={isSubmitting}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
colorSchema="secondary"
|
||||||
|
variant="plain"
|
||||||
|
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{isCurrentAuthMethod && (
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
colorSchema="danger"
|
||||||
|
isLoading={isSubmitting}
|
||||||
|
isDisabled={isSubmitting}
|
||||||
|
onClick={() => handlePopUpToggle("revokeAuthMethod", true)}
|
||||||
|
>
|
||||||
|
Remove Auth Method
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<DeleteActionModal
|
||||||
|
isOpen={internalPopUpState.popUp.overwriteAuthMethod?.isOpen}
|
||||||
|
title={`Are you sure want to overwrite ${initialAuthMethod || "the auth method"} on ${
|
||||||
|
identityAuthMethodData?.name ?? ""
|
||||||
|
}?`}
|
||||||
|
onChange={(isOpen) => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", isOpen)}
|
||||||
|
deleteKey="confirm"
|
||||||
|
buttonText="Overwrite"
|
||||||
|
onDeleteApproved={async () => {
|
||||||
|
const result = await trigger();
|
||||||
|
if (result) {
|
||||||
|
await revokeAuth(initialAuthMethod);
|
||||||
|
handleSubmit(onFormSubmit)();
|
||||||
|
} else {
|
||||||
|
createNotification({
|
||||||
|
text: "Please fill in all required fields",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
internalPopUpState.handlePopUpToggle("overwriteAuthMethod", false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+351
-300
@@ -7,13 +7,21 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, FormControl, IconButton, Input, TextArea, Tooltip } from "@app/components/v2";
|
import {
|
||||||
|
Button,
|
||||||
|
DeleteActionModal,
|
||||||
|
FormControl,
|
||||||
|
IconButton,
|
||||||
|
Input,
|
||||||
|
TextArea,
|
||||||
|
Tooltip
|
||||||
|
} from "@app/components/v2";
|
||||||
import { useOrganization, useSubscription } from "@app/context";
|
import { useOrganization, useSubscription } from "@app/context";
|
||||||
import { useAddIdentityOidcAuth, useUpdateIdentityOidcAuth } from "@app/hooks/api";
|
import { useAddIdentityOidcAuth, useUpdateIdentityOidcAuth } from "@app/hooks/api";
|
||||||
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
||||||
import { useGetIdentityOidcAuth } from "@app/hooks/api/identities/queries";
|
import { useGetIdentityOidcAuth } from "@app/hooks/api/identities/queries";
|
||||||
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { usePopUp, UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
accessTokenTrustedIps: z
|
accessTokenTrustedIps: z
|
||||||
@@ -56,12 +64,16 @@ type Props = {
|
|||||||
name: string;
|
name: string;
|
||||||
authMethod?: IdentityAuthMethod;
|
authMethod?: IdentityAuthMethod;
|
||||||
};
|
};
|
||||||
|
initialAuthMethod: IdentityAuthMethod;
|
||||||
|
revokeAuth: (authMethod: IdentityAuthMethod) => Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IdentityOidcAuthForm = ({
|
export const IdentityOidcAuthForm = ({
|
||||||
handlePopUpOpen,
|
handlePopUpOpen,
|
||||||
handlePopUpToggle,
|
handlePopUpToggle,
|
||||||
identityAuthMethodData
|
identityAuthMethodData,
|
||||||
|
initialAuthMethod,
|
||||||
|
revokeAuth
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
@@ -70,12 +82,17 @@ export const IdentityOidcAuthForm = ({
|
|||||||
const { mutateAsync: addMutateAsync } = useAddIdentityOidcAuth();
|
const { mutateAsync: addMutateAsync } = useAddIdentityOidcAuth();
|
||||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityOidcAuth();
|
const { mutateAsync: updateMutateAsync } = useUpdateIdentityOidcAuth();
|
||||||
|
|
||||||
const { data } = useGetIdentityOidcAuth(identityAuthMethodData?.identityId ?? "");
|
const isCurrentAuthMethod = identityAuthMethodData?.authMethod === initialAuthMethod;
|
||||||
|
const { data } = useGetIdentityOidcAuth(identityAuthMethodData?.identityId ?? "", {
|
||||||
|
enabled: isCurrentAuthMethod
|
||||||
|
});
|
||||||
|
const internalPopUpState = usePopUp(["overwriteAuthMethod"] as const);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
reset,
|
reset,
|
||||||
|
trigger,
|
||||||
formState: { isSubmitting }
|
formState: { isSubmitting }
|
||||||
} = useForm<FormData>({
|
} = useForm<FormData>({
|
||||||
resolver: zodResolver(schema),
|
resolver: zodResolver(schema),
|
||||||
@@ -193,9 +210,7 @@ export const IdentityOidcAuthForm = ({
|
|||||||
handlePopUpToggle("identityAuthMethod", false);
|
handlePopUpToggle("identityAuthMethod", false);
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
text: `Successfully ${
|
text: `Successfully ${isCurrentAuthMethod ? "updated" : "configured"} auth method`,
|
||||||
identityAuthMethodData?.authMethod ? "updated" : "configured"
|
|
||||||
} auth method`,
|
|
||||||
type: "success"
|
type: "success"
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -209,314 +224,350 @@ export const IdentityOidcAuthForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
<>
|
||||||
<Controller
|
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||||
control={control}
|
<Controller
|
||||||
name="oidcDiscoveryUrl"
|
control={control}
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="oidcDiscoveryUrl"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
isRequired
|
<FormControl
|
||||||
label="OIDC Discovery URL"
|
isRequired
|
||||||
isError={Boolean(error)}
|
label="OIDC Discovery URL"
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
>
|
errorText={error?.message}
|
||||||
<Input
|
>
|
||||||
{...field}
|
<Input
|
||||||
placeholder="https://token.actions.githubusercontent.com"
|
{...field}
|
||||||
type="text"
|
placeholder="https://token.actions.githubusercontent.com"
|
||||||
/>
|
type="text"
|
||||||
</FormControl>
|
/>
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
<Controller
|
/>
|
||||||
control={control}
|
<Controller
|
||||||
name="boundIssuer"
|
control={control}
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="boundIssuer"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
isRequired
|
<FormControl
|
||||||
label="Issuer"
|
isRequired
|
||||||
isError={Boolean(error)}
|
label="Issuer"
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
>
|
errorText={error?.message}
|
||||||
<Input
|
>
|
||||||
{...field}
|
<Input
|
||||||
type="text"
|
{...field}
|
||||||
placeholder="https://token.actions.githubusercontent.com"
|
type="text"
|
||||||
/>
|
placeholder="https://token.actions.githubusercontent.com"
|
||||||
</FormControl>
|
/>
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
<Controller
|
/>
|
||||||
control={control}
|
<Controller
|
||||||
name="caCert"
|
control={control}
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="caCert"
|
||||||
<FormControl label="CA Certificate" errorText={error?.message} isError={Boolean(error)}>
|
render={({ field, fieldState: { error } }) => (
|
||||||
<TextArea {...field} placeholder="-----BEGIN CERTIFICATE----- ..." />
|
<FormControl label="CA Certificate" errorText={error?.message} isError={Boolean(error)}>
|
||||||
</FormControl>
|
<TextArea {...field} placeholder="-----BEGIN CERTIFICATE----- ..." />
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
<Controller
|
/>
|
||||||
control={control}
|
<Controller
|
||||||
name="boundSubject"
|
control={control}
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="boundSubject"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
label="Subject"
|
<FormControl
|
||||||
isError={Boolean(error)}
|
label="Subject"
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
icon={
|
errorText={error?.message}
|
||||||
<Tooltip
|
icon={
|
||||||
className="text-center"
|
<Tooltip
|
||||||
content={<span>This field supports glob patterns</span>}
|
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) => (
|
|
||||||
<div className="mb-3 flex items-end space-x-2" key={id}>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name={`boundClaims.${index}.key`}
|
|
||||||
render={({ field, fieldState: { error } }) => {
|
|
||||||
return (
|
|
||||||
<FormControl
|
|
||||||
className="mb-0 flex-grow"
|
|
||||||
label={index === 0 ? "Claims" : undefined}
|
|
||||||
icon={
|
|
||||||
index === 0 ? (
|
|
||||||
<Tooltip
|
|
||||||
className="text-center"
|
|
||||||
content={<span>This field supports glob patterns</span>}
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={faQuestionCircle} size="sm" />
|
|
||||||
</Tooltip>
|
|
||||||
) : undefined
|
|
||||||
}
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
>
|
||||||
<Input
|
<FontAwesomeIcon icon={faQuestionCircle} size="sm" />
|
||||||
value={field.value}
|
</Tooltip>
|
||||||
onChange={(e) => field.onChange(e)}
|
}
|
||||||
placeholder="property"
|
>
|
||||||
/>
|
<Input {...field} type="text" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
);
|
)}
|
||||||
}}
|
/>
|
||||||
/>
|
<Controller
|
||||||
<Controller
|
control={control}
|
||||||
control={control}
|
name="boundAudiences"
|
||||||
name={`boundClaims.${index}.value`}
|
render={({ field, fieldState: { error } }) => (
|
||||||
render={({ field, fieldState: { error } }) => {
|
<FormControl
|
||||||
return (
|
label="Audiences"
|
||||||
<FormControl
|
isError={Boolean(error)}
|
||||||
className="mb-0 flex-grow"
|
errorText={error?.message}
|
||||||
isError={Boolean(error)}
|
icon={
|
||||||
errorText={error?.message}
|
<Tooltip
|
||||||
|
className="text-center"
|
||||||
|
content={<span>This field supports glob patterns</span>}
|
||||||
>
|
>
|
||||||
<Input
|
<FontAwesomeIcon icon={faQuestionCircle} size="sm" />
|
||||||
value={field.value}
|
</Tooltip>
|
||||||
onChange={(e) => field.onChange(e)}
|
}
|
||||||
placeholder="value1, value2"
|
>
|
||||||
/>
|
<Input {...field} type="text" placeholder="service1, service2" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
);
|
)}
|
||||||
}}
|
/>
|
||||||
/>
|
{boundClaimsFields.map(({ id }, index) => (
|
||||||
|
<div className="mb-3 flex items-end space-x-2" key={id}>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name={`boundClaims.${index}.key`}
|
||||||
|
render={({ field, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl
|
||||||
|
className="mb-0 flex-grow"
|
||||||
|
label={index === 0 ? "Claims" : undefined}
|
||||||
|
icon={
|
||||||
|
index === 0 ? (
|
||||||
|
<Tooltip
|
||||||
|
className="text-center"
|
||||||
|
content={<span>This field supports glob patterns</span>}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faQuestionCircle} size="sm" />
|
||||||
|
</Tooltip>
|
||||||
|
) : undefined
|
||||||
|
}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={field.value}
|
||||||
|
onChange={(e) => field.onChange(e)}
|
||||||
|
placeholder="property"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name={`boundClaims.${index}.value`}
|
||||||
|
render={({ field, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl
|
||||||
|
className="mb-0 flex-grow"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={field.value}
|
||||||
|
onChange={(e) => field.onChange(e)}
|
||||||
|
placeholder="value1, value2"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={() => removeBoundClaimField(index)}
|
onClick={() => removeBoundClaimField(index)}
|
||||||
size="lg"
|
size="lg"
|
||||||
colorSchema="danger"
|
colorSchema="danger"
|
||||||
variant="plain"
|
variant="plain"
|
||||||
ariaLabel="update"
|
ariaLabel="update"
|
||||||
className="p-3"
|
className="p-3"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="my-4 ml-1">
|
||||||
|
<Button
|
||||||
|
variant="outline_bg"
|
||||||
|
onClick={() =>
|
||||||
|
appendBoundClaimField({
|
||||||
|
key: "",
|
||||||
|
value: ""
|
||||||
|
})
|
||||||
|
}
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
|
size="xs"
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faXmark} />
|
Add Claims
|
||||||
</IconButton>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
))}
|
<Controller
|
||||||
<div className="my-4 ml-1">
|
control={control}
|
||||||
<Button
|
defaultValue="2592000"
|
||||||
variant="outline_bg"
|
name="accessTokenTTL"
|
||||||
onClick={() =>
|
render={({ field, fieldState: { error } }) => (
|
||||||
appendBoundClaimField({
|
<FormControl
|
||||||
key: "",
|
label="Access Token TTL (seconds)"
|
||||||
value: ""
|
isError={Boolean(error)}
|
||||||
})
|
errorText={error?.message}
|
||||||
}
|
>
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
||||||
size="xs"
|
</FormControl>
|
||||||
>
|
)}
|
||||||
Add Claims
|
/>
|
||||||
</Button>
|
<Controller
|
||||||
</div>
|
control={control}
|
||||||
<Controller
|
defaultValue="2592000"
|
||||||
control={control}
|
name="accessTokenMaxTTL"
|
||||||
defaultValue="2592000"
|
render={({ field, fieldState: { error } }) => (
|
||||||
name="accessTokenTTL"
|
<FormControl
|
||||||
render={({ field, fieldState: { error } }) => (
|
label="Access Token Max TTL (seconds)"
|
||||||
<FormControl
|
isError={Boolean(error)}
|
||||||
label="Access Token TTL (seconds)"
|
errorText={error?.message}
|
||||||
isError={Boolean(error)}
|
>
|
||||||
errorText={error?.message}
|
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
||||||
>
|
</FormControl>
|
||||||
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
)}
|
||||||
</FormControl>
|
/>
|
||||||
)}
|
<Controller
|
||||||
/>
|
control={control}
|
||||||
<Controller
|
defaultValue="0"
|
||||||
control={control}
|
name="accessTokenNumUsesLimit"
|
||||||
defaultValue="2592000"
|
render={({ field, fieldState: { error } }) => (
|
||||||
name="accessTokenMaxTTL"
|
<FormControl
|
||||||
render={({ field, fieldState: { error } }) => (
|
label="Access Token Max Number of Uses"
|
||||||
<FormControl
|
isError={Boolean(error)}
|
||||||
label="Access Token Max TTL (seconds)"
|
errorText={error?.message}
|
||||||
isError={Boolean(error)}
|
>
|
||||||
errorText={error?.message}
|
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
||||||
>
|
</FormControl>
|
||||||
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
)}
|
||||||
</FormControl>
|
/>
|
||||||
)}
|
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
||||||
/>
|
<div className="mb-3 flex items-end space-x-2" key={id}>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
defaultValue="0"
|
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
||||||
name="accessTokenNumUsesLimit"
|
defaultValue="0.0.0.0/0"
|
||||||
render={({ field, fieldState: { error } }) => (
|
render={({ field, fieldState: { error } }) => {
|
||||||
<FormControl
|
return (
|
||||||
label="Access Token Max Number of Uses"
|
<FormControl
|
||||||
isError={Boolean(error)}
|
className="mb-0 flex-grow"
|
||||||
errorText={error?.message}
|
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
||||||
>
|
isError={Boolean(error)}
|
||||||
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
errorText={error?.message}
|
||||||
</FormControl>
|
>
|
||||||
)}
|
<Input
|
||||||
/>
|
value={field.value}
|
||||||
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
onChange={(e) => {
|
||||||
<div className="mb-3 flex items-end space-x-2" key={id}>
|
if (subscription?.ipAllowlisting) {
|
||||||
<Controller
|
field.onChange(e);
|
||||||
control={control}
|
return;
|
||||||
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
}
|
||||||
defaultValue="0.0.0.0/0"
|
|
||||||
render={({ field, fieldState: { error } }) => {
|
|
||||||
return (
|
|
||||||
<FormControl
|
|
||||||
className="mb-0 flex-grow"
|
|
||||||
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
value={field.value}
|
|
||||||
onChange={(e) => {
|
|
||||||
if (subscription?.ipAllowlisting) {
|
|
||||||
field.onChange(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
handlePopUpOpen("upgradePlan");
|
||||||
}}
|
}}
|
||||||
placeholder="123.456.789.0"
|
placeholder="123.456.789.0"
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
onClick={() => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
removeAccessTokenTrustedIp(index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
size="lg"
|
||||||
|
colorSchema="danger"
|
||||||
|
variant="plain"
|
||||||
|
ariaLabel="update"
|
||||||
|
className="p-3"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="my-4 ml-1">
|
||||||
|
<Button
|
||||||
|
variant="outline_bg"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (subscription?.ipAllowlisting) {
|
if (subscription?.ipAllowlisting) {
|
||||||
removeAccessTokenTrustedIp(index);
|
appendAccessTokenTrustedIp({
|
||||||
|
ipAddress: "0.0.0.0/0"
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
handlePopUpOpen("upgradePlan");
|
||||||
}}
|
}}
|
||||||
size="lg"
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
colorSchema="danger"
|
size="xs"
|
||||||
variant="plain"
|
|
||||||
ariaLabel="update"
|
|
||||||
className="p-3"
|
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faXmark} />
|
Add IP Address
|
||||||
</IconButton>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div className="my-4 ml-1">
|
|
||||||
<Button
|
|
||||||
variant="outline_bg"
|
|
||||||
onClick={() => {
|
|
||||||
if (subscription?.ipAllowlisting) {
|
|
||||||
appendAccessTokenTrustedIp({
|
|
||||||
ipAddress: "0.0.0.0/0"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
|
||||||
size="xs"
|
|
||||||
>
|
|
||||||
Add IP Address
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Button
|
|
||||||
className="mr-4"
|
|
||||||
size="sm"
|
|
||||||
type="submit"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
isDisabled={isSubmitting}
|
|
||||||
>
|
|
||||||
{identityAuthMethodData?.authMethod ? "Update" : "Configure"}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
colorSchema="secondary"
|
|
||||||
variant="plain"
|
|
||||||
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{identityAuthMethodData?.authMethod && (
|
<div className="flex justify-between">
|
||||||
<Button
|
<div className="flex items-center">
|
||||||
size="sm"
|
{initialAuthMethod && identityAuthMethodData?.authMethod !== initialAuthMethod ? (
|
||||||
colorSchema="danger"
|
<Button
|
||||||
isLoading={isSubmitting}
|
className="mr-4"
|
||||||
isDisabled={isSubmitting}
|
size="sm"
|
||||||
onClick={() => handlePopUpToggle("revokeAuthMethod", true)}
|
isLoading={isSubmitting}
|
||||||
>
|
isDisabled={isSubmitting}
|
||||||
Remove Auth Method
|
onClick={() => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", true)}
|
||||||
</Button>
|
>
|
||||||
)}
|
Overwrite
|
||||||
</div>
|
</Button>
|
||||||
</form>
|
) : (
|
||||||
|
<Button
|
||||||
|
className="mr-4"
|
||||||
|
size="sm"
|
||||||
|
type="submit"
|
||||||
|
isLoading={isSubmitting}
|
||||||
|
isDisabled={isSubmitting}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
colorSchema="secondary"
|
||||||
|
variant="plain"
|
||||||
|
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{isCurrentAuthMethod && (
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
colorSchema="danger"
|
||||||
|
isLoading={isSubmitting}
|
||||||
|
isDisabled={isSubmitting}
|
||||||
|
onClick={() => handlePopUpToggle("revokeAuthMethod", true)}
|
||||||
|
>
|
||||||
|
Remove Auth Method
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<DeleteActionModal
|
||||||
|
isOpen={internalPopUpState.popUp.overwriteAuthMethod?.isOpen}
|
||||||
|
title={`Are you sure want to overwrite ${initialAuthMethod || "the auth method"} on ${
|
||||||
|
identityAuthMethodData?.name ?? ""
|
||||||
|
}?`}
|
||||||
|
onChange={(isOpen) => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", isOpen)}
|
||||||
|
deleteKey="confirm"
|
||||||
|
buttonText="Overwrite"
|
||||||
|
onDeleteApproved={async () => {
|
||||||
|
const result = await trigger();
|
||||||
|
if (result) {
|
||||||
|
await revokeAuth(initialAuthMethod);
|
||||||
|
handleSubmit(onFormSubmit)();
|
||||||
|
} else {
|
||||||
|
createNotification({
|
||||||
|
text: "Please fill in all required fields",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
internalPopUpState.handlePopUpToggle("overwriteAuthMethod", false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+173
-140
@@ -5,7 +5,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, FormControl, IconButton, Input } from "@app/components/v2";
|
import { Button, DeleteActionModal, FormControl, IconButton, Input } from "@app/components/v2";
|
||||||
import { useOrganization, useSubscription } from "@app/context";
|
import { useOrganization, useSubscription } from "@app/context";
|
||||||
import {
|
import {
|
||||||
useAddIdentityTokenAuth,
|
useAddIdentityTokenAuth,
|
||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
useUpdateIdentityTokenAuth
|
useUpdateIdentityTokenAuth
|
||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { usePopUp, UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
const schema = z
|
const schema = z
|
||||||
.object({
|
.object({
|
||||||
@@ -47,12 +47,16 @@ type Props = {
|
|||||||
name: string;
|
name: string;
|
||||||
authMethod?: IdentityAuthMethod;
|
authMethod?: IdentityAuthMethod;
|
||||||
};
|
};
|
||||||
|
initialAuthMethod: IdentityAuthMethod;
|
||||||
|
revokeAuth: (authMethod: IdentityAuthMethod) => Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IdentityTokenAuthForm = ({
|
export const IdentityTokenAuthForm = ({
|
||||||
handlePopUpOpen,
|
handlePopUpOpen,
|
||||||
handlePopUpToggle,
|
handlePopUpToggle,
|
||||||
identityAuthMethodData
|
identityAuthMethodData,
|
||||||
|
initialAuthMethod,
|
||||||
|
revokeAuth
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
@@ -61,7 +65,11 @@ export const IdentityTokenAuthForm = ({
|
|||||||
const { mutateAsync: addMutateAsync } = useAddIdentityTokenAuth();
|
const { mutateAsync: addMutateAsync } = useAddIdentityTokenAuth();
|
||||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityTokenAuth();
|
const { mutateAsync: updateMutateAsync } = useUpdateIdentityTokenAuth();
|
||||||
|
|
||||||
const { data } = useGetIdentityTokenAuth(identityAuthMethodData?.identityId ?? "");
|
const isCurrentAuthMethod = identityAuthMethodData?.authMethod === initialAuthMethod;
|
||||||
|
const { data } = useGetIdentityTokenAuth(identityAuthMethodData?.identityId ?? "", {
|
||||||
|
enabled: isCurrentAuthMethod
|
||||||
|
});
|
||||||
|
const internalPopUpState = usePopUp(["overwriteAuthMethod"] as const);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
@@ -116,164 +124,189 @@ export const IdentityTokenAuthForm = ({
|
|||||||
handlePopUpToggle("identityAuthMethod", false);
|
handlePopUpToggle("identityAuthMethod", false);
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
text: `Successfully ${
|
text: `Successfully ${isCurrentAuthMethod ? "updated" : "configured"} auth method`,
|
||||||
identityAuthMethodData?.authMethod ? "updated" : "configured"
|
|
||||||
} auth method`,
|
|
||||||
type: "success"
|
type: "success"
|
||||||
});
|
});
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
createNotification({
|
createNotification({
|
||||||
text: `Failed to ${identityAuthMethodData?.authMethod ? "update" : "configure"} identity`,
|
text: `Failed to ${isCurrentAuthMethod ? "update" : "configure"} identity`,
|
||||||
type: "error"
|
type: "error"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
<>
|
||||||
<Controller
|
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||||
control={control}
|
<Controller
|
||||||
defaultValue="2592000"
|
control={control}
|
||||||
name="accessTokenTTL"
|
defaultValue="2592000"
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="accessTokenTTL"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
label="Access Token TTL (seconds)"
|
<FormControl
|
||||||
isError={Boolean(error)}
|
label="Access Token TTL (seconds)"
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
>
|
errorText={error?.message}
|
||||||
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
>
|
||||||
</FormControl>
|
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
<Controller
|
/>
|
||||||
control={control}
|
<Controller
|
||||||
defaultValue="2592000"
|
control={control}
|
||||||
name="accessTokenMaxTTL"
|
defaultValue="2592000"
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="accessTokenMaxTTL"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
label="Access Token Max TTL (seconds)"
|
<FormControl
|
||||||
isError={Boolean(error)}
|
label="Access Token Max TTL (seconds)"
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
>
|
errorText={error?.message}
|
||||||
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
>
|
||||||
</FormControl>
|
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
<Controller
|
/>
|
||||||
control={control}
|
<Controller
|
||||||
defaultValue="0"
|
control={control}
|
||||||
name="accessTokenNumUsesLimit"
|
defaultValue="0"
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="accessTokenNumUsesLimit"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
label="Access Token Max Number of Uses"
|
<FormControl
|
||||||
isError={Boolean(error)}
|
label="Access Token Max Number of Uses"
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
>
|
errorText={error?.message}
|
||||||
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
>
|
||||||
</FormControl>
|
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
/>
|
||||||
<div className="mb-3 flex items-end space-x-2" key={id}>
|
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
||||||
<Controller
|
<div className="mb-3 flex items-end space-x-2" key={id}>
|
||||||
control={control}
|
<Controller
|
||||||
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
control={control}
|
||||||
defaultValue="0.0.0.0/0"
|
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
||||||
render={({ field, fieldState: { error } }) => {
|
defaultValue="0.0.0.0/0"
|
||||||
return (
|
render={({ field, fieldState: { error } }) => {
|
||||||
<FormControl
|
return (
|
||||||
className="mb-0 flex-grow"
|
<FormControl
|
||||||
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
className="mb-0 flex-grow"
|
||||||
isError={Boolean(error)}
|
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
>
|
errorText={error?.message}
|
||||||
<Input
|
>
|
||||||
value={field.value}
|
<Input
|
||||||
onChange={(e) => {
|
value={field.value}
|
||||||
if (subscription?.ipAllowlisting) {
|
onChange={(e) => {
|
||||||
field.onChange(e);
|
if (subscription?.ipAllowlisting) {
|
||||||
return;
|
field.onChange(e);
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
handlePopUpOpen("upgradePlan");
|
||||||
}}
|
}}
|
||||||
placeholder="123.456.789.0"
|
placeholder="123.456.789.0"
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
onClick={() => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
removeAccessTokenTrustedIp(index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
size="lg"
|
||||||
|
colorSchema="danger"
|
||||||
|
variant="plain"
|
||||||
|
ariaLabel="update"
|
||||||
|
className="p-3"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="my-4 ml-1">
|
||||||
|
<Button
|
||||||
|
variant="outline_bg"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (subscription?.ipAllowlisting) {
|
if (subscription?.ipAllowlisting) {
|
||||||
removeAccessTokenTrustedIp(index);
|
appendAccessTokenTrustedIp({
|
||||||
|
ipAddress: "0.0.0.0/0"
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
handlePopUpOpen("upgradePlan");
|
||||||
}}
|
}}
|
||||||
size="lg"
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
colorSchema="danger"
|
size="xs"
|
||||||
variant="plain"
|
|
||||||
ariaLabel="update"
|
|
||||||
className="p-3"
|
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faXmark} />
|
Add IP Address
|
||||||
</IconButton>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div className="my-4 ml-1">
|
|
||||||
<Button
|
|
||||||
variant="outline_bg"
|
|
||||||
onClick={() => {
|
|
||||||
if (subscription?.ipAllowlisting) {
|
|
||||||
appendAccessTokenTrustedIp({
|
|
||||||
ipAddress: "0.0.0.0/0"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
|
||||||
size="xs"
|
|
||||||
>
|
|
||||||
Add IP Address
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Button
|
|
||||||
className="mr-4"
|
|
||||||
size="sm"
|
|
||||||
type="submit"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
isDisabled={isSubmitting}
|
|
||||||
>
|
|
||||||
{identityAuthMethodData?.authMethod ? "Update" : "Configure"}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
colorSchema="secondary"
|
|
||||||
variant="plain"
|
|
||||||
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{identityAuthMethodData?.authMethod && (
|
<div className="flex justify-between">
|
||||||
<Button
|
<div className="flex items-center">
|
||||||
size="sm"
|
{initialAuthMethod && identityAuthMethodData?.authMethod !== initialAuthMethod ? (
|
||||||
colorSchema="danger"
|
<Button
|
||||||
isLoading={isSubmitting}
|
className="mr-4"
|
||||||
isDisabled={isSubmitting}
|
size="sm"
|
||||||
onClick={() => handlePopUpToggle("revokeAuthMethod", true)}
|
isLoading={isSubmitting}
|
||||||
>
|
isDisabled={isSubmitting}
|
||||||
Remove Auth Method
|
onClick={() => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", true)}
|
||||||
</Button>
|
>
|
||||||
)}
|
Overwrite
|
||||||
</div>
|
</Button>
|
||||||
</form>
|
) : (
|
||||||
|
<Button
|
||||||
|
className="mr-4"
|
||||||
|
size="sm"
|
||||||
|
type="submit"
|
||||||
|
isLoading={isSubmitting}
|
||||||
|
isDisabled={isSubmitting}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
colorSchema="secondary"
|
||||||
|
variant="plain"
|
||||||
|
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{isCurrentAuthMethod && (
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
colorSchema="danger"
|
||||||
|
isLoading={isSubmitting}
|
||||||
|
isDisabled={isSubmitting}
|
||||||
|
onClick={() => handlePopUpToggle("revokeAuthMethod", true)}
|
||||||
|
>
|
||||||
|
Remove Auth Method
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<DeleteActionModal
|
||||||
|
isOpen={internalPopUpState.popUp.overwriteAuthMethod?.isOpen}
|
||||||
|
title={`Are you sure want to overwrite ${initialAuthMethod || "the auth method"} on ${
|
||||||
|
identityAuthMethodData?.name ?? ""
|
||||||
|
}?`}
|
||||||
|
onChange={(isOpen) => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", isOpen)}
|
||||||
|
deleteKey="confirm"
|
||||||
|
buttonText="Overwrite"
|
||||||
|
onDeleteApproved={async () => {
|
||||||
|
await revokeAuth(initialAuthMethod);
|
||||||
|
handleSubmit(onFormSubmit)();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+230
-196
@@ -6,7 +6,7 @@ import { yupResolver } from "@hookform/resolvers/yup";
|
|||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, FormControl, IconButton, Input } from "@app/components/v2";
|
import { Button, DeleteActionModal, FormControl, IconButton, Input } from "@app/components/v2";
|
||||||
import { useOrganization, useSubscription } from "@app/context";
|
import { useOrganization, useSubscription } from "@app/context";
|
||||||
import {
|
import {
|
||||||
useAddIdentityUniversalAuth,
|
useAddIdentityUniversalAuth,
|
||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
||||||
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { usePopUp, UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
const schema = yup
|
const schema = yup
|
||||||
.object({
|
.object({
|
||||||
@@ -70,19 +70,28 @@ type Props = {
|
|||||||
name: string;
|
name: string;
|
||||||
authMethod?: IdentityAuthMethod;
|
authMethod?: IdentityAuthMethod;
|
||||||
};
|
};
|
||||||
|
initialAuthMethod: IdentityAuthMethod;
|
||||||
|
revokeAuth: (authMethod: IdentityAuthMethod) => Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IdentityUniversalAuthForm = ({
|
export const IdentityUniversalAuthForm = ({
|
||||||
handlePopUpOpen,
|
handlePopUpOpen,
|
||||||
handlePopUpToggle,
|
handlePopUpToggle,
|
||||||
identityAuthMethodData
|
identityAuthMethodData,
|
||||||
|
initialAuthMethod,
|
||||||
|
revokeAuth
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
const { subscription } = useSubscription();
|
const { subscription } = useSubscription();
|
||||||
const { mutateAsync: addMutateAsync } = useAddIdentityUniversalAuth();
|
const { mutateAsync: addMutateAsync } = useAddIdentityUniversalAuth();
|
||||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityUniversalAuth();
|
const { mutateAsync: updateMutateAsync } = useUpdateIdentityUniversalAuth();
|
||||||
const { data } = useGetIdentityUniversalAuth(identityAuthMethodData?.identityId ?? "");
|
|
||||||
|
const isCurrentAuthMethod = identityAuthMethodData?.authMethod === initialAuthMethod;
|
||||||
|
const { data } = useGetIdentityUniversalAuth(identityAuthMethodData?.identityId ?? "", {
|
||||||
|
enabled: isCurrentAuthMethod
|
||||||
|
});
|
||||||
|
const internalPopUpState = usePopUp(["overwriteAuthMethod"] as const);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
@@ -181,9 +190,7 @@ export const IdentityUniversalAuthForm = ({
|
|||||||
handlePopUpToggle("identityAuthMethod", false);
|
handlePopUpToggle("identityAuthMethod", false);
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
text: `Successfully ${
|
text: `Successfully ${isCurrentAuthMethod ? "updated" : "configured"} auth method`,
|
||||||
identityAuthMethodData?.authMethod ? "updated" : "configured"
|
|
||||||
} auth method`,
|
|
||||||
type: "success"
|
type: "success"
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -203,216 +210,243 @@ export const IdentityUniversalAuthForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
<>
|
||||||
<Controller
|
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||||
control={control}
|
<Controller
|
||||||
defaultValue="2592000"
|
control={control}
|
||||||
name="accessTokenTTL"
|
defaultValue="2592000"
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="accessTokenTTL"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
label="Access Token TTL (seconds)"
|
<FormControl
|
||||||
isError={Boolean(error)}
|
label="Access Token TTL (seconds)"
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
>
|
errorText={error?.message}
|
||||||
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
>
|
||||||
</FormControl>
|
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
<Controller
|
/>
|
||||||
control={control}
|
<Controller
|
||||||
defaultValue="2592000"
|
control={control}
|
||||||
name="accessTokenMaxTTL"
|
defaultValue="2592000"
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="accessTokenMaxTTL"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
label="Access Token Max TTL (seconds)"
|
<FormControl
|
||||||
isError={Boolean(error)}
|
label="Access Token Max TTL (seconds)"
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
>
|
errorText={error?.message}
|
||||||
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
>
|
||||||
</FormControl>
|
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
<Controller
|
/>
|
||||||
control={control}
|
<Controller
|
||||||
defaultValue="0"
|
control={control}
|
||||||
name="accessTokenNumUsesLimit"
|
defaultValue="0"
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="accessTokenNumUsesLimit"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
label="Access Token Max Number of Uses"
|
<FormControl
|
||||||
isError={Boolean(error)}
|
label="Access Token Max Number of Uses"
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
>
|
errorText={error?.message}
|
||||||
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
>
|
||||||
</FormControl>
|
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
{clientSecretTrustedIpsFields.map(({ id }, index) => (
|
/>
|
||||||
<div className="mb-3 flex items-end space-x-2" key={id}>
|
{clientSecretTrustedIpsFields.map(({ id }, index) => (
|
||||||
<Controller
|
<div className="mb-3 flex items-end space-x-2" key={id}>
|
||||||
control={control}
|
<Controller
|
||||||
name={`clientSecretTrustedIps.${index}.ipAddress`}
|
control={control}
|
||||||
defaultValue="0.0.0.0/0"
|
name={`clientSecretTrustedIps.${index}.ipAddress`}
|
||||||
render={({ field, fieldState: { error } }) => {
|
defaultValue="0.0.0.0/0"
|
||||||
return (
|
render={({ field, fieldState: { error } }) => {
|
||||||
<FormControl
|
return (
|
||||||
className="mb-0 flex-grow"
|
<FormControl
|
||||||
label={index === 0 ? "Client Secret Trusted IPs" : undefined}
|
className="mb-0 flex-grow"
|
||||||
isError={Boolean(error)}
|
label={index === 0 ? "Client Secret Trusted IPs" : undefined}
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
>
|
errorText={error?.message}
|
||||||
<Input
|
>
|
||||||
value={field.value}
|
<Input
|
||||||
onChange={(e) => {
|
value={field.value}
|
||||||
if (subscription?.ipAllowlisting) {
|
onChange={(e) => {
|
||||||
field.onChange(e);
|
if (subscription?.ipAllowlisting) {
|
||||||
return;
|
field.onChange(e);
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
handlePopUpOpen("upgradePlan");
|
||||||
}}
|
}}
|
||||||
placeholder="123.456.789.0"
|
placeholder="123.456.789.0"
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
onClick={() => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
removeClientSecretTrustedIp(index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
size="lg"
|
||||||
|
colorSchema="danger"
|
||||||
|
variant="plain"
|
||||||
|
ariaLabel="update"
|
||||||
|
className="p-3"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="my-4 ml-1">
|
||||||
|
<Button
|
||||||
|
variant="outline_bg"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (subscription?.ipAllowlisting) {
|
if (subscription?.ipAllowlisting) {
|
||||||
removeClientSecretTrustedIp(index);
|
appendClientSecretTrustedIp({
|
||||||
|
ipAddress: "0.0.0.0/0"
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
handlePopUpOpen("upgradePlan");
|
||||||
}}
|
}}
|
||||||
size="lg"
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
colorSchema="danger"
|
size="xs"
|
||||||
variant="plain"
|
|
||||||
ariaLabel="update"
|
|
||||||
className="p-3"
|
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faXmark} />
|
Add IP Address
|
||||||
</IconButton>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
))}
|
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
||||||
<div className="my-4 ml-1">
|
<div className="mb-3 flex items-end space-x-2" key={id}>
|
||||||
<Button
|
<Controller
|
||||||
variant="outline_bg"
|
control={control}
|
||||||
onClick={() => {
|
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
||||||
if (subscription?.ipAllowlisting) {
|
defaultValue="0.0.0.0/0"
|
||||||
appendClientSecretTrustedIp({
|
render={({ field, fieldState: { error } }) => {
|
||||||
ipAddress: "0.0.0.0/0"
|
return (
|
||||||
});
|
<FormControl
|
||||||
return;
|
className="mb-0 flex-grow"
|
||||||
}
|
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={field.value}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
field.onChange(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
handlePopUpOpen("upgradePlan");
|
||||||
}}
|
}}
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
placeholder="123.456.789.0"
|
||||||
size="xs"
|
/>
|
||||||
>
|
</FormControl>
|
||||||
Add IP Address
|
);
|
||||||
</Button>
|
}}
|
||||||
</div>
|
/>
|
||||||
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
<IconButton
|
||||||
<div className="mb-3 flex items-end space-x-2" key={id}>
|
onClick={() => {
|
||||||
<Controller
|
if (subscription?.ipAllowlisting) {
|
||||||
control={control}
|
removeAccessTokenTrustedIp(index);
|
||||||
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
return;
|
||||||
defaultValue="0.0.0.0/0"
|
}
|
||||||
render={({ field, fieldState: { error } }) => {
|
|
||||||
return (
|
|
||||||
<FormControl
|
|
||||||
className="mb-0 flex-grow"
|
|
||||||
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
value={field.value}
|
|
||||||
onChange={(e) => {
|
|
||||||
if (subscription?.ipAllowlisting) {
|
|
||||||
field.onChange(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
handlePopUpOpen("upgradePlan");
|
||||||
}}
|
}}
|
||||||
placeholder="123.456.789.0"
|
size="lg"
|
||||||
/>
|
colorSchema="danger"
|
||||||
</FormControl>
|
variant="plain"
|
||||||
);
|
ariaLabel="update"
|
||||||
}}
|
className="p-3"
|
||||||
/>
|
>
|
||||||
<IconButton
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="my-4 ml-1">
|
||||||
|
<Button
|
||||||
|
variant="outline_bg"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (subscription?.ipAllowlisting) {
|
if (subscription?.ipAllowlisting) {
|
||||||
removeAccessTokenTrustedIp(index);
|
appendAccessTokenTrustedIp({
|
||||||
|
ipAddress: "0.0.0.0/0"
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
handlePopUpOpen("upgradePlan");
|
||||||
}}
|
}}
|
||||||
size="lg"
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
colorSchema="danger"
|
size="xs"
|
||||||
variant="plain"
|
|
||||||
ariaLabel="update"
|
|
||||||
className="p-3"
|
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faXmark} />
|
Add IP Address
|
||||||
</IconButton>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div className="my-4 ml-1">
|
|
||||||
<Button
|
|
||||||
variant="outline_bg"
|
|
||||||
onClick={() => {
|
|
||||||
if (subscription?.ipAllowlisting) {
|
|
||||||
appendAccessTokenTrustedIp({
|
|
||||||
ipAddress: "0.0.0.0/0"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
|
||||||
size="xs"
|
|
||||||
>
|
|
||||||
Add IP Address
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Button
|
|
||||||
className="mr-4"
|
|
||||||
size="sm"
|
|
||||||
type="submit"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
isDisabled={isSubmitting}
|
|
||||||
>
|
|
||||||
{identityAuthMethodData?.authMethod ? "Update" : "Configure"}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
colorSchema="secondary"
|
|
||||||
variant="plain"
|
|
||||||
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{identityAuthMethodData?.authMethod && (
|
<div className="flex justify-between">
|
||||||
<Button
|
<div className="flex items-center">
|
||||||
size="sm"
|
{initialAuthMethod && identityAuthMethodData?.authMethod !== initialAuthMethod ? (
|
||||||
colorSchema="danger"
|
<Button
|
||||||
isLoading={isSubmitting}
|
className="mr-4"
|
||||||
isDisabled={isSubmitting}
|
size="sm"
|
||||||
onClick={() => handlePopUpToggle("revokeAuthMethod", true)}
|
isLoading={isSubmitting}
|
||||||
>
|
isDisabled={isSubmitting}
|
||||||
Remove Auth Method
|
onClick={() => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", true)}
|
||||||
</Button>
|
>
|
||||||
)}
|
Overwrite
|
||||||
</div>
|
</Button>
|
||||||
</form>
|
) : (
|
||||||
|
<Button
|
||||||
|
className="mr-4"
|
||||||
|
size="sm"
|
||||||
|
type="submit"
|
||||||
|
isLoading={isSubmitting}
|
||||||
|
isDisabled={isSubmitting}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
colorSchema="secondary"
|
||||||
|
variant="plain"
|
||||||
|
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{isCurrentAuthMethod && (
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
colorSchema="danger"
|
||||||
|
isLoading={isSubmitting}
|
||||||
|
isDisabled={isSubmitting}
|
||||||
|
onClick={() => handlePopUpToggle("revokeAuthMethod", true)}
|
||||||
|
>
|
||||||
|
Remove Auth Method
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<DeleteActionModal
|
||||||
|
isOpen={internalPopUpState.popUp.overwriteAuthMethod?.isOpen}
|
||||||
|
title={`Are you sure want to overwrite ${initialAuthMethod || "the auth method"} on ${
|
||||||
|
identityAuthMethodData?.name ?? ""
|
||||||
|
}?`}
|
||||||
|
onChange={(isOpen) => internalPopUpState.handlePopUpToggle("overwriteAuthMethod", isOpen)}
|
||||||
|
deleteKey="confirm"
|
||||||
|
buttonText="Overwrite"
|
||||||
|
onDeleteApproved={async () => {
|
||||||
|
await revokeAuth(initialAuthMethod);
|
||||||
|
handleSubmit(onFormSubmit)();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user