mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-20 18:00:54 +00:00
Address PR comments
This commit is contained in:
@@ -15,7 +15,7 @@ type ContentProps = {
|
||||
};
|
||||
|
||||
const Content = ({ pkiSync, onComplete }: ContentProps) => {
|
||||
const { id: syncId, destination, projectId } = pkiSync;
|
||||
const { id: syncId, destination } = pkiSync;
|
||||
const destinationName = PKI_SYNC_MAP[destination].name;
|
||||
|
||||
const triggerImportCertificates = useTriggerPkiSyncImportCertificates();
|
||||
@@ -24,7 +24,6 @@ const Content = ({ pkiSync, onComplete }: ContentProps) => {
|
||||
try {
|
||||
await triggerImportCertificates.mutateAsync({
|
||||
syncId,
|
||||
projectId,
|
||||
destination
|
||||
});
|
||||
|
||||
@@ -57,7 +56,7 @@ const Content = ({ pkiSync, onComplete }: ContentProps) => {
|
||||
</p>
|
||||
<p className="mb-6 text-xs text-bunker-300">
|
||||
This operation will retrieve certificates from {destinationName} and make them available in
|
||||
your PKI collection. Only certificates that are not already imported will be processed.
|
||||
your PKI subscriber. Only certificates that are not already imported will be processed.
|
||||
</p>
|
||||
<div className="mt-8 flex w-full items-center justify-between gap-2">
|
||||
<ModalClose asChild>
|
||||
|
||||
@@ -13,10 +13,10 @@ import { twMerge } from "tailwind-merge";
|
||||
import { Badge, Tooltip } from "@app/components/v2";
|
||||
import { BadgeProps } from "@app/components/v2/Badge/Badge";
|
||||
import { PKI_SYNC_MAP } from "@app/helpers/pkiSyncs";
|
||||
import { PkiSyncData, PkiSyncStatus } from "@app/hooks/api/pkiSyncs";
|
||||
import { PkiSyncStatus, TPkiSync } from "@app/hooks/api/pkiSyncs";
|
||||
|
||||
type Props = {
|
||||
pkiSync: PkiSyncData;
|
||||
pkiSync: TPkiSync;
|
||||
className?: string;
|
||||
mini?: boolean;
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ type ContentProps = {
|
||||
};
|
||||
|
||||
const Content = ({ pkiSync, onComplete }: ContentProps) => {
|
||||
const { id: syncId, destination, projectId } = pkiSync;
|
||||
const { id: syncId, destination } = pkiSync;
|
||||
const destinationName = PKI_SYNC_MAP[destination].name;
|
||||
|
||||
const triggerRemoveCertificates = useTriggerPkiSyncRemoveCertificates();
|
||||
@@ -24,7 +24,6 @@ const Content = ({ pkiSync, onComplete }: ContentProps) => {
|
||||
try {
|
||||
await triggerRemoveCertificates.mutateAsync({
|
||||
syncId,
|
||||
projectId,
|
||||
destination
|
||||
});
|
||||
|
||||
|
||||
@@ -13,10 +13,10 @@ import { twMerge } from "tailwind-merge";
|
||||
import { Badge, Tooltip } from "@app/components/v2";
|
||||
import { BadgeProps } from "@app/components/v2/Badge/Badge";
|
||||
import { PKI_SYNC_MAP } from "@app/helpers/pkiSyncs";
|
||||
import { PkiSyncData, PkiSyncStatus } from "@app/hooks/api/pkiSyncs";
|
||||
import { PkiSyncStatus, TPkiSync } from "@app/hooks/api/pkiSyncs";
|
||||
|
||||
type Props = {
|
||||
pkiSync: PkiSyncData;
|
||||
pkiSync: TPkiSync;
|
||||
className?: string;
|
||||
mini?: boolean;
|
||||
};
|
||||
|
||||
@@ -13,13 +13,13 @@ import {
|
||||
THead,
|
||||
Tr
|
||||
} from "@app/components/v2";
|
||||
import { PkiSyncData } from "@app/hooks/api/pkiSyncs";
|
||||
import { TPkiSync } from "@app/hooks/api/pkiSyncs";
|
||||
|
||||
type Props = {
|
||||
pkiSyncs: PkiSyncData[];
|
||||
onEdit: (pkiSync: PkiSyncData) => void;
|
||||
onDelete: (pkiSync: PkiSyncData) => void;
|
||||
onTrigger: (pkiSync: PkiSyncData) => void;
|
||||
pkiSyncs: TPkiSync[];
|
||||
onEdit: (pkiSync: TPkiSync) => void;
|
||||
onDelete: (pkiSync: TPkiSync) => void;
|
||||
onTrigger: (pkiSync: TPkiSync) => void;
|
||||
};
|
||||
|
||||
const getSyncStatusBadge = (status?: string) => {
|
||||
|
||||
@@ -50,8 +50,9 @@ export const CreatePkiSyncForm = ({ destination, onComplete, onCancel }: Props)
|
||||
destination,
|
||||
isAutoSyncEnabled: true,
|
||||
syncOptions: {
|
||||
canImportCertificates: syncOption?.canImportCertificates ?? true,
|
||||
canRemoveCertificates: syncOption?.canRemoveCertificates ?? true
|
||||
canImportCertificates: syncOption?.canImportCertificates ?? false,
|
||||
canRemoveCertificates: syncOption?.canRemoveCertificates ?? false,
|
||||
certificateNameSchema: syncOption?.defaultCertificateNameSchema
|
||||
}
|
||||
} as Partial<TPkiSyncForm>,
|
||||
reValidateMode: "onChange"
|
||||
@@ -129,10 +130,11 @@ export const CreatePkiSyncForm = ({ destination, onComplete, onCancel }: Props)
|
||||
Certificate Sync Behavior
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-bunker-200">
|
||||
Certificate Syncs are the source of truth for connected third-party services. Any
|
||||
certificate, including associated data, not present or imported in Infisical before
|
||||
syncing will be overwritten, and changes made directly in the connected service outside
|
||||
of infisical may also be overwritten by future syncs.
|
||||
Certificate Syncs manage certificates that are prefixed with "Infisical-" in
|
||||
the destination. Only certificates managed by Infisical will be affected during sync
|
||||
operations. Certificates not created or managed by Infisical will remain untouched, and
|
||||
changes made to Infisical-managed certificates directly in the destination service may
|
||||
be overwritten by future syncs.
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-4 flex gap-4">
|
||||
@@ -190,7 +192,7 @@ export const CreatePkiSyncForm = ({ destination, onComplete, onCancel }: Props)
|
||||
<PkiSyncDestinationFields />
|
||||
</Tab.Panel>
|
||||
<Tab.Panel>
|
||||
<PkiSyncOptionsFields />
|
||||
<PkiSyncOptionsFields destination={destination} />
|
||||
<Controller
|
||||
control={control}
|
||||
name="isAutoSyncEnabled"
|
||||
|
||||
@@ -69,7 +69,7 @@ export const EditPkiSyncForm = ({ pkiSync, fields, onComplete }: Props) => {
|
||||
Component = <PkiSyncDestinationFields />;
|
||||
break;
|
||||
case PkiSyncEditFields.Options:
|
||||
Component = <PkiSyncOptionsFields />;
|
||||
Component = <PkiSyncOptionsFields destination={pkiSync.destination} />;
|
||||
break;
|
||||
case PkiSyncEditFields.Source:
|
||||
Component = <PkiSyncSourceFields />;
|
||||
|
||||
@@ -4,8 +4,8 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Link } from "@tanstack/react-router";
|
||||
|
||||
import { FilterableSelect, FormControl } from "@app/components/v2";
|
||||
import { OrgPermissionSubjects, useOrgPermission, useProject } from "@app/context";
|
||||
import { OrgPermissionAppConnectionActions } from "@app/context/OrgPermissionContext/types";
|
||||
import { ProjectPermissionSub, useProject, useProjectPermission } from "@app/context";
|
||||
import { ProjectPermissionAppConnectionActions } from "@app/context/ProjectPermissionContext/types";
|
||||
import { APP_CONNECTION_MAP } from "@app/helpers/appConnections";
|
||||
import { PKI_SYNC_CONNECTION_MAP } from "@app/helpers/pkiSyncs";
|
||||
import { useListAvailableAppConnections } from "@app/hooks/api/appConnections";
|
||||
@@ -17,7 +17,7 @@ type Props = {
|
||||
};
|
||||
|
||||
export const PkiSyncConnectionField = ({ onChange: callback }: Props) => {
|
||||
const { permission } = useOrgPermission();
|
||||
const { permission } = useProjectPermission();
|
||||
const { control, watch } = useFormContext<TPkiSyncForm>();
|
||||
const { currentProject } = useProject();
|
||||
|
||||
@@ -32,8 +32,8 @@ export const PkiSyncConnectionField = ({ onChange: callback }: Props) => {
|
||||
const connectionName = APP_CONNECTION_MAP[app].name;
|
||||
|
||||
const canCreateConnection = permission.can(
|
||||
OrgPermissionAppConnectionActions.Create,
|
||||
OrgPermissionSubjects.AppConnections
|
||||
ProjectPermissionAppConnectionActions.Create,
|
||||
ProjectPermissionSub.AppConnections
|
||||
);
|
||||
|
||||
const appName = APP_CONNECTION_MAP[PKI_SYNC_CONNECTION_MAP[destination]].name;
|
||||
@@ -47,7 +47,6 @@ export const PkiSyncConnectionField = ({ onChange: callback }: Props) => {
|
||||
<Controller
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
tooltipText="App Connections can be created from the Organization Settings page."
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
label={`${connectionName} Connection`}
|
||||
|
||||
+70
-3
@@ -2,12 +2,19 @@ import { Controller, useFormContext } from "react-hook-form";
|
||||
import { faQuestionCircle } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { FormControl, Switch, Tooltip } from "@app/components/v2";
|
||||
import { FormControl, Input, Switch, Tooltip } from "@app/components/v2";
|
||||
import { PkiSync, usePkiSyncOption } from "@app/hooks/api/pkiSyncs";
|
||||
|
||||
import { TPkiSyncForm } from "../schemas";
|
||||
|
||||
export const PkiSyncOptionsFields = () => {
|
||||
const { control } = useFormContext<TPkiSyncForm>();
|
||||
type Props = {
|
||||
destination?: PkiSync;
|
||||
};
|
||||
|
||||
export const PkiSyncOptionsFields = ({ destination }: Props) => {
|
||||
const { control, watch } = useFormContext<TPkiSyncForm>();
|
||||
const currentDestination = destination || watch("destination");
|
||||
const { syncOption } = usePkiSyncOption(currentDestination);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -87,6 +94,66 @@ export const PkiSyncOptionsFields = () => {
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name="syncOptions.certificateNameSchema"
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
tooltipClassName="max-w-md"
|
||||
tooltipText={
|
||||
<div className="flex flex-col gap-3">
|
||||
<span>
|
||||
When a certificate is synced, values will be injected into the certificate name
|
||||
schema before it reaches the destination. This is useful for organization.
|
||||
</span>
|
||||
|
||||
<div className="flex flex-col">
|
||||
<span>Available placeholders:</span>
|
||||
<ul className="list-disc pl-4 text-sm">
|
||||
<li>
|
||||
<code>{"{{certificateId}}"}</code> - The unique ID of the certificate
|
||||
</li>
|
||||
<li>
|
||||
<code>{"{{environment}}"}</code> - The environment which the certificate is in
|
||||
(e.g. dev, staging, prod)
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{syncOption?.forbiddenCharacters && syncOption.forbiddenCharacters.length > 0 && (
|
||||
<div className="flex flex-col">
|
||||
<span className="text-yellow">
|
||||
Character restrictions for {syncOption.name}:
|
||||
</span>
|
||||
<div className="text-xs text-bunker-300">
|
||||
The following characters are not allowed:{" "}
|
||||
{syncOption.forbiddenCharacters.split("").join(" ")}
|
||||
</div>
|
||||
{syncOption.allowedCharacterPattern && (
|
||||
<div className="mt-1 text-xs text-bunker-300">
|
||||
Only alphanumeric characters and hyphens are allowed (a-z, A-Z, 0-9, -)
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
isError={Boolean(error)}
|
||||
isOptional
|
||||
errorText={error?.message}
|
||||
label="Certificate Name Schema"
|
||||
helperText="Infisical strongly advises setting a Certificate Name Schema to ensure that Infisical only manages the specific certificates you intend, keeping everything else untouched."
|
||||
>
|
||||
<Input
|
||||
value={value || ""}
|
||||
onChange={(e) => onChange(e.target.value || undefined)}
|
||||
placeholder={
|
||||
syncOption?.defaultCertificateNameSchema || "INFISICAL_{{certificateId}}"
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -21,7 +21,38 @@ export const PkiSyncFormSchema = z.object({
|
||||
}),
|
||||
syncOptions: z.object({
|
||||
canImportCertificates: z.boolean().default(false),
|
||||
canRemoveCertificates: z.boolean().default(true)
|
||||
canRemoveCertificates: z.boolean().default(false),
|
||||
certificateNameSchema: z
|
||||
.string()
|
||||
.optional()
|
||||
.refine(
|
||||
(val) => {
|
||||
if (!val) return true;
|
||||
|
||||
const allowedOptionalPlaceholders = ["{{environment}}"];
|
||||
|
||||
const allowedPlaceholdersRegexPart = ["{{certificateId}}", ...allowedOptionalPlaceholders]
|
||||
.map((p) => p.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&")) // Escape regex special characters
|
||||
.join("|");
|
||||
|
||||
const allowedContentRegex = new RegExp(
|
||||
`^([a-zA-Z0-9_\\-/]|${allowedPlaceholdersRegexPart})*$`
|
||||
);
|
||||
const contentIsValid = allowedContentRegex.test(val);
|
||||
|
||||
if (val.trim()) {
|
||||
const certificateIdRegex = /\{\{certificateId\}\}/;
|
||||
const certificateIdIsPresent = certificateIdRegex.test(val);
|
||||
return contentIsValid && certificateIdIsPresent;
|
||||
}
|
||||
|
||||
return contentIsValid;
|
||||
},
|
||||
{
|
||||
message:
|
||||
"Certificate name schema must include exactly one {{certificateId}} placeholder. It can also include {{environment}} placeholders. Only alphanumeric characters (a-z, A-Z, 0-9), dashes (-), underscores (_), and slashes (/) are allowed besides the placeholders."
|
||||
}
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user