mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 09:30:09 +00:00
feat(secret-rotation): Okta Client Secret Rotation
This commit is contained in:
+38
@@ -0,0 +1,38 @@
|
||||
import { CredentialDisplay } from "@app/components/secret-rotations-v2/ViewSecretRotationV2GeneratedCredentials/shared/CredentialDisplay";
|
||||
import { TOktaClientSecretRotationGeneratedCredentialsResponse } from "@app/hooks/api/secretRotationsV2/types/okta-client-secret-rotation";
|
||||
|
||||
import { ViewRotationGeneratedCredentialsDisplay } from "./shared";
|
||||
|
||||
type Props = {
|
||||
generatedCredentialsResponse: TOktaClientSecretRotationGeneratedCredentialsResponse;
|
||||
};
|
||||
|
||||
export const ViewOktaClientSecretRotationGeneratedCredentials = ({
|
||||
generatedCredentialsResponse: { generatedCredentials, activeIndex }
|
||||
}: Props) => {
|
||||
const inactiveIndex = activeIndex === 0 ? 1 : 0;
|
||||
|
||||
const activeCredentials = generatedCredentials[activeIndex];
|
||||
const inactiveCredentials = generatedCredentials[inactiveIndex];
|
||||
|
||||
return (
|
||||
<ViewRotationGeneratedCredentialsDisplay
|
||||
activeCredentials={
|
||||
<>
|
||||
<CredentialDisplay label="Client ID">{activeCredentials?.clientId}</CredentialDisplay>
|
||||
<CredentialDisplay isSensitive label="Client Secret">
|
||||
{activeCredentials?.clientSecret}
|
||||
</CredentialDisplay>
|
||||
</>
|
||||
}
|
||||
inactiveCredentials={
|
||||
<>
|
||||
<CredentialDisplay label="Client ID">{inactiveCredentials?.clientId}</CredentialDisplay>
|
||||
<CredentialDisplay isSensitive label="Client Secret">
|
||||
{inactiveCredentials?.clientSecret}
|
||||
</CredentialDisplay>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
+8
@@ -22,6 +22,7 @@ import {
|
||||
|
||||
import { ViewSqlCredentialsRotationGeneratedCredentials } from "./shared";
|
||||
import { ViewAwsIamUserSecretRotationGeneratedCredentials } from "./ViewAwsIamUserSecretRotationGeneratedCredentials";
|
||||
import { ViewOktaClientSecretRotationGeneratedCredentials } from "./ViewOktaClientSecretRotationGeneratedCredentials";
|
||||
|
||||
type Props = {
|
||||
secretRotation?: TSecretRotationV2;
|
||||
@@ -99,6 +100,13 @@ const Content = ({ secretRotation }: ContentProps) => {
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case SecretRotation.OktaClientSecret:
|
||||
Component = (
|
||||
<ViewOktaClientSecretRotationGeneratedCredentials
|
||||
generatedCredentialsResponse={generatedCredentialsResponse}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unhandled View Generated Credential Rotation Type");
|
||||
}
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
import { Controller, useFormContext } from "react-hook-form";
|
||||
import { SingleValue } from "react-select";
|
||||
|
||||
import { TSecretRotationV2Form } from "@app/components/secret-rotations-v2/forms/schemas";
|
||||
import { FilterableSelect, FormControl } from "@app/components/v2";
|
||||
import { useOktaConnectionListApps } from "@app/hooks/api/appConnections/okta";
|
||||
import { TOktaApp } from "@app/hooks/api/appConnections/okta/types";
|
||||
import { SecretRotation } from "@app/hooks/api/secretRotationsV2";
|
||||
|
||||
export const OktaClientSecretRotationParametersFields = () => {
|
||||
const { control, watch, setValue } = useFormContext<
|
||||
TSecretRotationV2Form & {
|
||||
type: SecretRotation.OktaClientSecret;
|
||||
}
|
||||
>();
|
||||
|
||||
const connectionId = watch("connection.id");
|
||||
|
||||
const { data: apps, isPending: isAppsPending } = useOktaConnectionListApps(connectionId, {
|
||||
enabled: Boolean(connectionId)
|
||||
});
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name="parameters.clientId"
|
||||
control={control}
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
label="OpenID Connect Application"
|
||||
>
|
||||
<FilterableSelect
|
||||
menuPlacement="top"
|
||||
isLoading={isAppsPending && Boolean(connectionId)}
|
||||
isDisabled={!connectionId}
|
||||
value={apps?.find((app) => app.id === value) ?? null}
|
||||
onChange={(option) => {
|
||||
onChange((option as SingleValue<TOktaApp>)?.id ?? null);
|
||||
setValue("parameters.clientId", (option as SingleValue<TOktaApp>)?.id ?? "");
|
||||
}}
|
||||
options={apps}
|
||||
placeholder="Select an application..."
|
||||
getOptionLabel={(option) => option.label}
|
||||
getOptionValue={(option) => option.id}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
+3
-1
@@ -7,6 +7,7 @@ import { Auth0ClientSecretRotationParametersFields } from "./Auth0ClientSecretRo
|
||||
import { AwsIamUserSecretRotationParametersFields } from "./AwsIamUserSecretRotationParametersFields";
|
||||
import { AzureClientSecretRotationParametersFields } from "./AzureClientSecretRotationParametersFields";
|
||||
import { LdapPasswordRotationParametersFields } from "./LdapPasswordRotationParametersFields";
|
||||
import { OktaClientSecretRotationParametersFields } from "./OktaClientSecretRotationParametersFields";
|
||||
import { SqlCredentialsRotationParametersFields } from "./shared";
|
||||
|
||||
const COMPONENT_MAP: Record<SecretRotation, React.FC> = {
|
||||
@@ -17,7 +18,8 @@ const COMPONENT_MAP: Record<SecretRotation, React.FC> = {
|
||||
[SecretRotation.Auth0ClientSecret]: Auth0ClientSecretRotationParametersFields,
|
||||
[SecretRotation.AzureClientSecret]: AzureClientSecretRotationParametersFields,
|
||||
[SecretRotation.LdapPassword]: LdapPasswordRotationParametersFields,
|
||||
[SecretRotation.AwsIamUserSecret]: AwsIamUserSecretRotationParametersFields
|
||||
[SecretRotation.AwsIamUserSecret]: AwsIamUserSecretRotationParametersFields,
|
||||
[SecretRotation.OktaClientSecret]: OktaClientSecretRotationParametersFields
|
||||
};
|
||||
|
||||
export const SecretRotationV2ParametersFields = () => {
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
import { useFormContext } from "react-hook-form";
|
||||
|
||||
import { TSecretRotationV2Form } from "@app/components/secret-rotations-v2/forms/schemas";
|
||||
import { GenericFieldLabel } from "@app/components/v2";
|
||||
import { SecretRotation } from "@app/hooks/api/secretRotationsV2";
|
||||
|
||||
import { SecretRotationReviewSection } from "./shared";
|
||||
|
||||
export const OktaClientSecretRotationReviewFields = () => {
|
||||
const { watch } = useFormContext<
|
||||
TSecretRotationV2Form & {
|
||||
type: SecretRotation.OktaClientSecret;
|
||||
}
|
||||
>();
|
||||
|
||||
const [parameters, { clientId, clientSecret }] = watch(["parameters", "secretsMapping"]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SecretRotationReviewSection label="Parameters">
|
||||
<GenericFieldLabel label="App ID">{parameters.clientId}</GenericFieldLabel>
|
||||
</SecretRotationReviewSection>
|
||||
<SecretRotationReviewSection label="Secrets Mapping">
|
||||
<GenericFieldLabel label="Client ID">{clientId}</GenericFieldLabel>
|
||||
<GenericFieldLabel label="Client Secret">{clientSecret}</GenericFieldLabel>
|
||||
</SecretRotationReviewSection>
|
||||
</>
|
||||
);
|
||||
};
|
||||
+3
-1
@@ -10,6 +10,7 @@ import { Auth0ClientSecretRotationReviewFields } from "./Auth0ClientSecretRotati
|
||||
import { AwsIamUserSecretRotationReviewFields } from "./AwsIamUserSecretRotationReviewFields";
|
||||
import { AzureClientSecretRotationReviewFields } from "./AzureClientSecretRotationReviewFields";
|
||||
import { LdapPasswordRotationReviewFields } from "./LdapPasswordRotationReviewFields";
|
||||
import { OktaClientSecretRotationReviewFields } from "./OktaClientSecretRotationReviewFields";
|
||||
import { SqlCredentialsRotationReviewFields } from "./shared";
|
||||
|
||||
const COMPONENT_MAP: Record<SecretRotation, React.FC> = {
|
||||
@@ -20,7 +21,8 @@ const COMPONENT_MAP: Record<SecretRotation, React.FC> = {
|
||||
[SecretRotation.Auth0ClientSecret]: Auth0ClientSecretRotationReviewFields,
|
||||
[SecretRotation.AzureClientSecret]: AzureClientSecretRotationReviewFields,
|
||||
[SecretRotation.LdapPassword]: LdapPasswordRotationReviewFields,
|
||||
[SecretRotation.AwsIamUserSecret]: AwsIamUserSecretRotationReviewFields
|
||||
[SecretRotation.AwsIamUserSecret]: AwsIamUserSecretRotationReviewFields,
|
||||
[SecretRotation.OktaClientSecret]: OktaClientSecretRotationReviewFields
|
||||
};
|
||||
|
||||
export const SecretRotationV2ReviewFields = () => {
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
import { Controller, useFormContext } from "react-hook-form";
|
||||
|
||||
import { TSecretRotationV2Form } from "@app/components/secret-rotations-v2/forms/schemas";
|
||||
import { FormControl, Input } from "@app/components/v2";
|
||||
import { SecretRotation, useSecretRotationV2Option } from "@app/hooks/api/secretRotationsV2";
|
||||
|
||||
import { SecretsMappingTable } from "./shared";
|
||||
|
||||
export const OktaClientSecretRotationSecretsMappingFields = () => {
|
||||
const { control } = useFormContext<
|
||||
TSecretRotationV2Form & {
|
||||
type: SecretRotation.OktaClientSecret;
|
||||
}
|
||||
>();
|
||||
|
||||
const { rotationOption } = useSecretRotationV2Option(SecretRotation.OktaClientSecret);
|
||||
|
||||
const items = [
|
||||
{
|
||||
name: "Client ID",
|
||||
input: (
|
||||
<Controller
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl isError={Boolean(error)} errorText={error?.message}>
|
||||
<Input
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder={rotationOption?.template.secretsMapping.clientId}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
control={control}
|
||||
name="secretsMapping.clientId"
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: "Client Secret",
|
||||
input: (
|
||||
<Controller
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl isError={Boolean(error)} errorText={error?.message}>
|
||||
<Input
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder={rotationOption?.template.secretsMapping.clientSecret}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
control={control}
|
||||
name="secretsMapping.clientSecret"
|
||||
/>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
return <SecretsMappingTable items={items} />;
|
||||
};
|
||||
+3
-1
@@ -7,6 +7,7 @@ import { Auth0ClientSecretRotationSecretsMappingFields } from "./Auth0ClientSecr
|
||||
import { AwsIamUserSecretRotationSecretsMappingFields } from "./AwsIamUserSecretRotationSecretsMappingFields";
|
||||
import { AzureClientSecretRotationSecretsMappingFields } from "./AzureClientSecretRotationSecretsMappingFields";
|
||||
import { LdapPasswordRotationSecretsMappingFields } from "./LdapPasswordRotationSecretsMappingFields";
|
||||
import { OktaClientSecretRotationSecretsMappingFields } from "./OktaClientSecretRotationSecretsMappingFields";
|
||||
import { SqlCredentialsRotationSecretsMappingFields } from "./shared";
|
||||
|
||||
const COMPONENT_MAP: Record<SecretRotation, React.FC> = {
|
||||
@@ -17,7 +18,8 @@ const COMPONENT_MAP: Record<SecretRotation, React.FC> = {
|
||||
[SecretRotation.Auth0ClientSecret]: Auth0ClientSecretRotationSecretsMappingFields,
|
||||
[SecretRotation.AzureClientSecret]: AzureClientSecretRotationSecretsMappingFields,
|
||||
[SecretRotation.LdapPassword]: LdapPasswordRotationSecretsMappingFields,
|
||||
[SecretRotation.AwsIamUserSecret]: AwsIamUserSecretRotationSecretsMappingFields
|
||||
[SecretRotation.AwsIamUserSecret]: AwsIamUserSecretRotationSecretsMappingFields,
|
||||
[SecretRotation.OktaClientSecret]: OktaClientSecretRotationSecretsMappingFields
|
||||
};
|
||||
|
||||
export const SecretRotationV2SecretsMappingFields = () => {
|
||||
|
||||
@@ -10,6 +10,7 @@ import { PostgresCredentialsRotationSchema } from "@app/components/secret-rotati
|
||||
import { SecretRotation } from "@app/hooks/api/secretRotationsV2";
|
||||
import { LdapPasswordRotationMethod } from "@app/hooks/api/secretRotationsV2/types/ldap-password-rotation";
|
||||
|
||||
import { OktaClientSecretRotationSchema } from "./okta-client-secret-rotation-schema";
|
||||
import { OracleDBCredentialsRotationSchema } from "./oracledb-credentials-rotation-schema";
|
||||
|
||||
export const SecretRotationV2FormSchema = (isUpdate: boolean) =>
|
||||
@@ -23,7 +24,8 @@ export const SecretRotationV2FormSchema = (isUpdate: boolean) =>
|
||||
MySqlCredentialsRotationSchema,
|
||||
OracleDBCredentialsRotationSchema,
|
||||
LdapPasswordRotationSchema,
|
||||
AwsIamUserSecretRotationSchema
|
||||
AwsIamUserSecretRotationSchema,
|
||||
OktaClientSecretRotationSchema
|
||||
]),
|
||||
z.object({ id: z.string().optional() })
|
||||
)
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { BaseSecretRotationSchema } from "@app/components/secret-rotations-v2/forms/schemas/base-secret-rotation-v2-schema";
|
||||
import { SecretRotation } from "@app/hooks/api/secretRotationsV2";
|
||||
|
||||
export const OktaClientSecretRotationSchema = z
|
||||
.object({
|
||||
type: z.literal(SecretRotation.OktaClientSecret),
|
||||
parameters: z.object({
|
||||
clientId: z.string().trim().min(1, "App ID required")
|
||||
}),
|
||||
secretsMapping: z.object({
|
||||
clientId: z.string().trim().min(1, "Client ID required"),
|
||||
clientSecret: z.string().trim().min(1, "Client Secret required")
|
||||
})
|
||||
})
|
||||
.merge(BaseSecretRotationSchema);
|
||||
@@ -44,6 +44,11 @@ export const SECRET_ROTATION_MAP: Record<
|
||||
name: "AWS IAM User Secret",
|
||||
image: "Amazon Web Services.png",
|
||||
size: 50
|
||||
},
|
||||
[SecretRotation.OktaClientSecret]: {
|
||||
name: "Okta Client Secret",
|
||||
image: "Okta.png",
|
||||
size: 50
|
||||
}
|
||||
};
|
||||
|
||||
@@ -55,7 +60,8 @@ export const SECRET_ROTATION_CONNECTION_MAP: Record<SecretRotation, AppConnectio
|
||||
[SecretRotation.Auth0ClientSecret]: AppConnection.Auth0,
|
||||
[SecretRotation.AzureClientSecret]: AppConnection.AzureClientSecrets,
|
||||
[SecretRotation.LdapPassword]: AppConnection.LDAP,
|
||||
[SecretRotation.AwsIamUserSecret]: AppConnection.AWS
|
||||
[SecretRotation.AwsIamUserSecret]: AppConnection.AWS,
|
||||
[SecretRotation.OktaClientSecret]: AppConnection.Okta
|
||||
};
|
||||
|
||||
// if a rotation can potentially have downtime due to rotating a single credential set this to false
|
||||
@@ -67,7 +73,8 @@ export const IS_ROTATION_DUAL_CREDENTIALS: Record<SecretRotation, boolean> = {
|
||||
[SecretRotation.Auth0ClientSecret]: false,
|
||||
[SecretRotation.AzureClientSecret]: true,
|
||||
[SecretRotation.LdapPassword]: false,
|
||||
[SecretRotation.AwsIamUserSecret]: true
|
||||
[SecretRotation.AwsIamUserSecret]: true,
|
||||
[SecretRotation.OktaClientSecret]: true
|
||||
};
|
||||
|
||||
export const getRotateAtLocal = ({ hours, minutes }: TSecretRotationV2["rotateAtUtc"]) => {
|
||||
|
||||
@@ -6,7 +6,8 @@ export enum SecretRotation {
|
||||
Auth0ClientSecret = "auth0-client-secret",
|
||||
AzureClientSecret = "azure-client-secret",
|
||||
LdapPassword = "ldap-password",
|
||||
AwsIamUserSecret = "aws-iam-user-secret"
|
||||
AwsIamUserSecret = "aws-iam-user-secret",
|
||||
OktaClientSecret = "okta-client-secret"
|
||||
}
|
||||
|
||||
export enum SecretRotationStatus {
|
||||
|
||||
@@ -35,6 +35,11 @@ import {
|
||||
TMySqlCredentialsRotation,
|
||||
TMySqlCredentialsRotationGeneratedCredentialsResponse
|
||||
} from "./mysql-credentials-rotation";
|
||||
import {
|
||||
TOktaClientSecretRotation,
|
||||
TOktaClientSecretRotationGeneratedCredentialsResponse,
|
||||
TOktaClientSecretRotationOption
|
||||
} from "./okta-client-secret-rotation";
|
||||
import {
|
||||
TOracleDBCredentialsRotation,
|
||||
TOracleDBCredentialsRotationGeneratedCredentialsResponse
|
||||
@@ -49,6 +54,7 @@ export type TSecretRotationV2 = (
|
||||
| TAzureClientSecretRotation
|
||||
| TLdapPasswordRotation
|
||||
| TAwsIamUserSecretRotation
|
||||
| TOktaClientSecretRotation
|
||||
) & {
|
||||
secrets: (SecretV3RawSanitized | null)[];
|
||||
};
|
||||
@@ -58,7 +64,8 @@ export type TSecretRotationV2Option =
|
||||
| TAuth0ClientSecretRotationOption
|
||||
| TAzureClientSecretRotationOption
|
||||
| TLdapPasswordRotationOption
|
||||
| TAwsIamUserSecretRotationOption;
|
||||
| TAwsIamUserSecretRotationOption
|
||||
| TOktaClientSecretRotationOption;
|
||||
|
||||
export type TListSecretRotationV2Options = { secretRotationOptions: TSecretRotationV2Option[] };
|
||||
|
||||
@@ -72,7 +79,8 @@ export type TViewSecretRotationGeneratedCredentialsResponse =
|
||||
| TAuth0ClientSecretRotationGeneratedCredentialsResponse
|
||||
| TAzureClientSecretRotationGeneratedCredentialsResponse
|
||||
| TLdapPasswordRotationGeneratedCredentialsResponse
|
||||
| TAwsIamUserSecretRotationGeneratedCredentialsResponse;
|
||||
| TAwsIamUserSecretRotationGeneratedCredentialsResponse
|
||||
| TOktaClientSecretRotationGeneratedCredentialsResponse;
|
||||
|
||||
export type TCreateSecretRotationV2DTO = DiscriminativePick<
|
||||
TSecretRotationV2,
|
||||
@@ -124,6 +132,7 @@ export type TSecretRotationOptionMap = {
|
||||
[SecretRotation.AzureClientSecret]: TAzureClientSecretRotationOption;
|
||||
[SecretRotation.LdapPassword]: TLdapPasswordRotationOption;
|
||||
[SecretRotation.AwsIamUserSecret]: TAwsIamUserSecretRotationOption;
|
||||
[SecretRotation.OktaClientSecret]: TOktaClientSecretRotationOption;
|
||||
};
|
||||
|
||||
export type TSecretRotationGeneratedCredentialsResponseMap = {
|
||||
@@ -135,4 +144,5 @@ export type TSecretRotationGeneratedCredentialsResponseMap = {
|
||||
[SecretRotation.AzureClientSecret]: TAzureClientSecretRotationGeneratedCredentialsResponse;
|
||||
[SecretRotation.LdapPassword]: TLdapPasswordRotationGeneratedCredentialsResponse;
|
||||
[SecretRotation.AwsIamUserSecret]: TAwsIamUserSecretRotationGeneratedCredentialsResponse;
|
||||
[SecretRotation.OktaClientSecret]: TOktaClientSecretRotationGeneratedCredentialsResponse;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||
import { SecretRotation } from "@app/hooks/api/secretRotationsV2";
|
||||
import {
|
||||
TSecretRotationV2Base,
|
||||
TSecretRotationV2GeneratedCredentialsResponseBase
|
||||
} from "@app/hooks/api/secretRotationsV2/types/shared";
|
||||
|
||||
export type TOktaClientSecretRotation = TSecretRotationV2Base & {
|
||||
type: SecretRotation.OktaClientSecret;
|
||||
parameters: {
|
||||
clientId: string;
|
||||
};
|
||||
secretsMapping: {
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type TOktaClientSecretRotationGeneratedCredentials = {
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
};
|
||||
|
||||
export type TOktaClientSecretRotationGeneratedCredentialsResponse =
|
||||
TSecretRotationV2GeneratedCredentialsResponseBase<
|
||||
SecretRotation.OktaClientSecret,
|
||||
TOktaClientSecretRotationGeneratedCredentials
|
||||
>;
|
||||
|
||||
export type TOktaClientSecretRotationOption = {
|
||||
name: string;
|
||||
type: SecretRotation.OktaClientSecret;
|
||||
connection: AppConnection.Okta;
|
||||
template: {
|
||||
secretsMapping: TOktaClientSecretRotation["secretsMapping"];
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user