mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +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`}
|
||||
|
||||
@@ -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."
|
||||
}
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
@@ -2,6 +2,3 @@ export * from "./enums";
|
||||
export * from "./mutations";
|
||||
export * from "./queries";
|
||||
export * from "./types";
|
||||
|
||||
// Legacy alias for backward compatibility
|
||||
export type PkiSyncData = import("./types").TPkiSync;
|
||||
|
||||
@@ -5,7 +5,7 @@ import { pkiSyncKeys } from "@app/hooks/api/pkiSyncs/queries";
|
||||
import {
|
||||
TCreatePkiSyncDTO,
|
||||
TDeletePkiSyncDTO,
|
||||
TPkiSyncResponse,
|
||||
TPkiSync,
|
||||
TTriggerPkiSyncImportCertificatesDTO,
|
||||
TTriggerPkiSyncRemoveCertificatesDTO,
|
||||
TTriggerPkiSyncSyncCertificatesDTO,
|
||||
@@ -16,12 +16,9 @@ export const useCreatePkiSync = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({ destination, ...params }: TCreatePkiSyncDTO) => {
|
||||
const { data } = await apiRequest.post<TPkiSyncResponse>(
|
||||
`/api/v1/pki-syncs/${destination}`,
|
||||
params
|
||||
);
|
||||
const { data } = await apiRequest.post<TPkiSync>(`/api/v1/pki/syncs/${destination}`, params);
|
||||
|
||||
return data.pkiSync;
|
||||
return data;
|
||||
},
|
||||
onSuccess: (_, { projectId }) =>
|
||||
queryClient.invalidateQueries({ queryKey: pkiSyncKeys.list(projectId) })
|
||||
@@ -32,13 +29,13 @@ export const useUpdatePkiSync = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({ syncId, projectId, destination, ...params }: TUpdatePkiSyncDTO) => {
|
||||
const { data } = await apiRequest.patch<TPkiSyncResponse>(
|
||||
`/api/v1/pki-syncs/${destination}/${syncId}`,
|
||||
const { data } = await apiRequest.patch<TPkiSync>(
|
||||
`/api/v1/pki/syncs/${destination}/${syncId}`,
|
||||
params,
|
||||
{ params: { projectId } }
|
||||
);
|
||||
|
||||
return data.pkiSync;
|
||||
return data;
|
||||
},
|
||||
onSuccess: (_, { syncId, projectId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: pkiSyncKeys.list(projectId) });
|
||||
@@ -51,7 +48,7 @@ export const useDeletePkiSync = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({ syncId, projectId, destination }: TDeletePkiSyncDTO) => {
|
||||
const { data } = await apiRequest.delete(`/api/v1/pki-syncs/${destination}/${syncId}`, {
|
||||
const { data } = await apiRequest.delete(`/api/v1/pki/syncs/${destination}/${syncId}`, {
|
||||
params: { projectId }
|
||||
});
|
||||
|
||||
@@ -67,20 +64,15 @@ export const useDeletePkiSync = () => {
|
||||
export const useTriggerPkiSyncSyncCertificates = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({ syncId, projectId, destination }: TTriggerPkiSyncSyncCertificatesDTO) => {
|
||||
const { data } = await apiRequest.post(
|
||||
`/api/v1/pki-syncs/${destination}/${syncId}/sync`,
|
||||
{},
|
||||
{
|
||||
params: { projectId }
|
||||
}
|
||||
);
|
||||
mutationFn: async ({ syncId, destination }: TTriggerPkiSyncSyncCertificatesDTO) => {
|
||||
const { data } = await apiRequest.post(`/api/v1/pki/syncs/${destination}/${syncId}/sync`);
|
||||
|
||||
return data;
|
||||
},
|
||||
onSuccess: (_, { syncId, projectId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: pkiSyncKeys.list(projectId) });
|
||||
queryClient.invalidateQueries({ queryKey: pkiSyncKeys.byId(syncId, projectId) });
|
||||
onSuccess: (_, { syncId }) => {
|
||||
// Invalidate all PKI sync queries since we don't have projectId here
|
||||
queryClient.invalidateQueries({ queryKey: pkiSyncKeys.all });
|
||||
queryClient.invalidateQueries({ queryKey: ["pkiSync", syncId] });
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -88,24 +80,15 @@ export const useTriggerPkiSyncSyncCertificates = () => {
|
||||
export const useTriggerPkiSyncImportCertificates = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({
|
||||
syncId,
|
||||
projectId,
|
||||
destination
|
||||
}: TTriggerPkiSyncImportCertificatesDTO) => {
|
||||
const { data } = await apiRequest.post(
|
||||
`/api/v1/pki-syncs/${destination}/${syncId}/import`,
|
||||
{},
|
||||
{
|
||||
params: { projectId }
|
||||
}
|
||||
);
|
||||
mutationFn: async ({ syncId, destination }: TTriggerPkiSyncImportCertificatesDTO) => {
|
||||
const { data } = await apiRequest.post(`/api/v1/pki/syncs/${destination}/${syncId}/import`);
|
||||
|
||||
return data;
|
||||
},
|
||||
onSuccess: (_, { syncId, projectId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: pkiSyncKeys.list(projectId) });
|
||||
queryClient.invalidateQueries({ queryKey: pkiSyncKeys.byId(syncId, projectId) });
|
||||
onSuccess: (_, { syncId }) => {
|
||||
// Invalidate all PKI sync queries since we don't have projectId here
|
||||
queryClient.invalidateQueries({ queryKey: pkiSyncKeys.all });
|
||||
queryClient.invalidateQueries({ queryKey: ["pkiSync", syncId] });
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -113,24 +96,15 @@ export const useTriggerPkiSyncImportCertificates = () => {
|
||||
export const useTriggerPkiSyncRemoveCertificates = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({
|
||||
syncId,
|
||||
projectId,
|
||||
destination
|
||||
}: TTriggerPkiSyncRemoveCertificatesDTO) => {
|
||||
const { data } = await apiRequest.post(
|
||||
`/api/v1/pki-syncs/${destination}/${syncId}/remove`,
|
||||
{},
|
||||
{
|
||||
params: { projectId }
|
||||
}
|
||||
);
|
||||
mutationFn: async ({ syncId, destination }: TTriggerPkiSyncRemoveCertificatesDTO) => {
|
||||
const { data } = await apiRequest.post(`/api/v1/pki/syncs/${destination}/${syncId}/remove`);
|
||||
|
||||
return data;
|
||||
},
|
||||
onSuccess: (_, { syncId, projectId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: pkiSyncKeys.list(projectId) });
|
||||
queryClient.invalidateQueries({ queryKey: pkiSyncKeys.byId(syncId, projectId) });
|
||||
onSuccess: (_, { syncId }) => {
|
||||
// Invalidate all PKI sync queries since we don't have projectId here
|
||||
queryClient.invalidateQueries({ queryKey: pkiSyncKeys.all });
|
||||
queryClient.invalidateQueries({ queryKey: ["pkiSync", syncId] });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2,12 +2,7 @@ import { useQuery, UseQueryOptions } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
import { PkiSync, TPkiSyncOption } from "@app/hooks/api/pkiSyncs";
|
||||
import {
|
||||
TListPkiSyncOptions,
|
||||
TListPkiSyncs,
|
||||
TPkiSync,
|
||||
TPkiSyncResponse
|
||||
} from "@app/hooks/api/pkiSyncs/types";
|
||||
import { TListPkiSyncOptions, TListPkiSyncs, TPkiSync } from "@app/hooks/api/pkiSyncs/types";
|
||||
|
||||
export const pkiSyncKeys = {
|
||||
all: ["pki-sync"] as const,
|
||||
@@ -31,7 +26,7 @@ export const usePkiSyncOptions = (
|
||||
return useQuery({
|
||||
queryKey: pkiSyncKeys.options(),
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get<TListPkiSyncOptions>("/api/v1/pki-syncs/options");
|
||||
const { data } = await apiRequest.get<TListPkiSyncOptions>("/api/v1/pki/syncs/options");
|
||||
|
||||
return data.pkiSyncOptions;
|
||||
},
|
||||
@@ -47,7 +42,7 @@ export const usePkiSyncOption = (destination: PkiSync) => {
|
||||
};
|
||||
|
||||
export const fetchPkiSyncsByProjectId = async (projectId: string) => {
|
||||
const { data } = await apiRequest.get<TListPkiSyncs>("/api/v1/pki-syncs", {
|
||||
const { data } = await apiRequest.get<TListPkiSyncs>("/api/v1/pki/syncs", {
|
||||
params: { projectId }
|
||||
});
|
||||
|
||||
@@ -78,11 +73,11 @@ export const useGetPkiSync = (
|
||||
return useQuery({
|
||||
queryKey: pkiSyncKeys.byId(syncId, projectId),
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get<TPkiSyncResponse>(`/api/v1/pki-syncs/${syncId}`, {
|
||||
const { data } = await apiRequest.get<TPkiSync>(`/api/v1/pki/syncs/${syncId}`, {
|
||||
params: { projectId }
|
||||
});
|
||||
|
||||
return data.pkiSync;
|
||||
return data;
|
||||
},
|
||||
...options
|
||||
});
|
||||
|
||||
@@ -5,6 +5,8 @@ import { PkiSyncStatus } from "../enums";
|
||||
export type RootPkiSyncOptions = {
|
||||
canImportCertificates: boolean;
|
||||
canRemoveCertificates: boolean;
|
||||
certificateNamePrefix?: string;
|
||||
certificateNameSchema?: string;
|
||||
};
|
||||
|
||||
export type TRootPkiSync = {
|
||||
|
||||
@@ -9,6 +9,11 @@ export type TPkiSyncOption = {
|
||||
canImportCertificates: boolean;
|
||||
canRemoveCertificates: boolean;
|
||||
enterprise?: boolean;
|
||||
defaultCertificateNameSchema?: string;
|
||||
forbiddenCharacters?: string;
|
||||
allowedCharacterPattern?: string;
|
||||
maxCertificateNameLength?: number;
|
||||
minCertificateNameLength?: number;
|
||||
};
|
||||
|
||||
export type TPkiSync = TAzureKeyVaultPkiSync;
|
||||
@@ -16,7 +21,6 @@ export type TPkiSync = TAzureKeyVaultPkiSync;
|
||||
export type TListPkiSyncs = { pkiSyncs: TPkiSync[] };
|
||||
|
||||
export type TListPkiSyncOptions = { pkiSyncOptions: TPkiSyncOption[] };
|
||||
export type TPkiSyncResponse = { pkiSync: TPkiSync };
|
||||
|
||||
export type TCreatePkiSyncDTO = DiscriminativePick<
|
||||
TPkiSync,
|
||||
@@ -43,19 +47,16 @@ export type TDeletePkiSyncDTO = {
|
||||
|
||||
export type TTriggerPkiSyncSyncCertificatesDTO = {
|
||||
syncId: string;
|
||||
projectId: string;
|
||||
destination: PkiSync;
|
||||
};
|
||||
|
||||
export type TTriggerPkiSyncImportCertificatesDTO = {
|
||||
syncId: string;
|
||||
projectId: string;
|
||||
destination: PkiSync;
|
||||
};
|
||||
|
||||
export type TTriggerPkiSyncRemoveCertificatesDTO = {
|
||||
syncId: string;
|
||||
projectId: string;
|
||||
destination: PkiSync;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { PkiSyncData } from "@app/hooks/api/pkiSyncs";
|
||||
import { TPkiSync } from "@app/hooks/api/pkiSyncs";
|
||||
|
||||
import { getPkiSyncDestinationColValues } from "../helpers";
|
||||
import { PkiSyncTableCell } from "../PkiSyncTableCell";
|
||||
|
||||
type Props = {
|
||||
pkiSync: PkiSyncData;
|
||||
pkiSync: TPkiSync;
|
||||
};
|
||||
|
||||
export const PkiSyncDestinationCol = ({ pkiSync }: Props) => {
|
||||
|
||||
@@ -44,18 +44,18 @@ import { ProjectPermissionSub } from "@app/context";
|
||||
import { ProjectPermissionPkiSyncActions } from "@app/context/ProjectPermissionContext/types";
|
||||
import { PKI_SYNC_MAP } from "@app/helpers/pkiSyncs";
|
||||
import { useToggle } from "@app/hooks";
|
||||
import { PkiSyncData, PkiSyncStatus } from "@app/hooks/api/pkiSyncs";
|
||||
import { PkiSyncStatus, TPkiSync, usePkiSyncOption } from "@app/hooks/api/pkiSyncs";
|
||||
|
||||
import { PkiSyncDestinationCol } from "./PkiSyncDestinationCol";
|
||||
import { PkiSyncTableCell } from "./PkiSyncTableCell";
|
||||
|
||||
type Props = {
|
||||
pkiSync: PkiSyncData;
|
||||
onDelete: (pkiSync: PkiSyncData) => void;
|
||||
onTriggerSyncCertificates: (pkiSync: PkiSyncData) => void;
|
||||
onTriggerImportCertificates: (pkiSync: PkiSyncData) => void;
|
||||
onTriggerRemoveCertificates: (pkiSync: PkiSyncData) => void;
|
||||
onToggleEnable: (pkiSync: PkiSyncData) => void;
|
||||
pkiSync: TPkiSync;
|
||||
onDelete: (pkiSync: TPkiSync) => void;
|
||||
onTriggerSyncCertificates: (pkiSync: TPkiSync) => void;
|
||||
onTriggerImportCertificates: (pkiSync: TPkiSync) => void;
|
||||
onTriggerRemoveCertificates: (pkiSync: TPkiSync) => void;
|
||||
onToggleEnable: (pkiSync: TPkiSync) => void;
|
||||
};
|
||||
|
||||
export const PkiSyncRow = ({
|
||||
@@ -82,6 +82,8 @@ export const PkiSyncRow = ({
|
||||
|
||||
const destinationName = PKI_SYNC_MAP[destination].name;
|
||||
|
||||
const { syncOption } = usePkiSyncOption(destination);
|
||||
|
||||
const [isIdCopied, setIsIdCopied] = useToggle(false);
|
||||
|
||||
const handleCopyId = useCallback(() => {
|
||||
@@ -227,7 +229,7 @@ export const PkiSyncRow = ({
|
||||
{!isAutoSyncEnabled && (
|
||||
<Tooltip
|
||||
className="text-xs"
|
||||
content="Auto-Sync is disabled. Changes to the PKI subscriber will not be automatically synced to the destination."
|
||||
content="Auto-Sync is disabled. Certificate changes in the PKI subscriber will not be automatically synced to the destination."
|
||||
>
|
||||
<div>
|
||||
<Badge className="flex h-5 w-min items-center gap-1.5 whitespace-nowrap bg-mineshaft-400/50 text-bunker-300">
|
||||
@@ -237,7 +239,7 @@ export const PkiSyncRow = ({
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
<PkiSyncImportStatusBadge mini pkiSync={pkiSync} />
|
||||
{syncOption?.canImportCertificates && <PkiSyncImportStatusBadge mini pkiSync={pkiSync} />}
|
||||
<PkiSyncRemoveStatusBadge mini pkiSync={pkiSync} />
|
||||
</div>
|
||||
</Td>
|
||||
@@ -294,36 +296,38 @@ export const PkiSyncRow = ({
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionPkiSyncActions.ImportCertificates}
|
||||
a={permissionSubject}
|
||||
>
|
||||
{(isAllowed: boolean) => (
|
||||
<DropdownMenuItem
|
||||
icon={<FontAwesomeIcon icon={faDownload} />}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onTriggerImportCertificates(pkiSync);
|
||||
}}
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
<Tooltip
|
||||
position="left"
|
||||
sideOffset={42}
|
||||
content={`Import certificates from this ${destinationName} destination into Infisical.`}
|
||||
{syncOption?.canImportCertificates && (
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionPkiSyncActions.ImportCertificates}
|
||||
a={permissionSubject}
|
||||
>
|
||||
{(isAllowed: boolean) => (
|
||||
<DropdownMenuItem
|
||||
icon={<FontAwesomeIcon icon={faDownload} />}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onTriggerImportCertificates(pkiSync);
|
||||
}}
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
<div className="flex h-full w-full items-center justify-between gap-1">
|
||||
<span>Import Certificates</span>
|
||||
<FontAwesomeIcon
|
||||
className="text-bunker-300"
|
||||
size="sm"
|
||||
icon={faInfoCircle}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
<Tooltip
|
||||
position="left"
|
||||
sideOffset={42}
|
||||
content={`Import certificates from this ${destinationName} destination into Infisical.`}
|
||||
>
|
||||
<div className="flex h-full w-full items-center justify-between gap-1">
|
||||
<span>Import Certificates</span>
|
||||
<FontAwesomeIcon
|
||||
className="text-bunker-300"
|
||||
size="sm"
|
||||
icon={faInfoCircle}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
)}
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionPkiSyncActions.RemoveCertificates}
|
||||
a={permissionSubject}
|
||||
|
||||
@@ -46,8 +46,8 @@ import { usePagination, usePopUp, useResetPageHelper } from "@app/hooks";
|
||||
import { OrderByDirection } from "@app/hooks/api/generic/types";
|
||||
import {
|
||||
PkiSync,
|
||||
PkiSyncData,
|
||||
PkiSyncStatus,
|
||||
TPkiSync,
|
||||
useTriggerPkiSyncSyncCertificates,
|
||||
useUpdatePkiSync
|
||||
} from "@app/hooks/api/pkiSyncs";
|
||||
@@ -82,7 +82,7 @@ const getSyncStatusOrderValue = (syncStatus: PkiSyncStatus | null) => {
|
||||
};
|
||||
|
||||
type Props = {
|
||||
pkiSyncs: PkiSyncData[];
|
||||
pkiSyncs: TPkiSync[];
|
||||
};
|
||||
|
||||
const STATUS_ICON_MAP = {
|
||||
@@ -213,15 +213,15 @@ export const PkiSyncsTable = ({ pkiSyncs }: Props) => {
|
||||
|
||||
const isTableFiltered = Boolean(filters.destinations.length || filters.status.length);
|
||||
|
||||
const handleDelete = (pkiSync: PkiSyncData) => handlePopUpOpen("deleteSync", pkiSync);
|
||||
const handleDelete = (pkiSync: TPkiSync) => handlePopUpOpen("deleteSync", pkiSync);
|
||||
|
||||
const handleTriggerImportCertificates = (pkiSync: PkiSyncData) =>
|
||||
const handleTriggerImportCertificates = (pkiSync: TPkiSync) =>
|
||||
handlePopUpOpen("importCertificates", pkiSync);
|
||||
|
||||
const handleTriggerRemoveCertificates = (pkiSync: PkiSyncData) =>
|
||||
const handleTriggerRemoveCertificates = (pkiSync: TPkiSync) =>
|
||||
handlePopUpOpen("removeCertificates", pkiSync);
|
||||
|
||||
const handleToggleEnableSync = async (pkiSync: PkiSyncData) => {
|
||||
const handleToggleEnableSync = async (pkiSync: TPkiSync) => {
|
||||
const destinationName = PKI_SYNC_MAP[pkiSync.destination].name;
|
||||
|
||||
const isAutoSyncEnabled = !pkiSync.isAutoSyncEnabled;
|
||||
@@ -246,13 +246,12 @@ export const PkiSyncsTable = ({ pkiSyncs }: Props) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleTriggerSync = async (pkiSync: PkiSyncData) => {
|
||||
const handleTriggerSync = async (pkiSync: TPkiSync) => {
|
||||
const destinationName = PKI_SYNC_MAP[pkiSync.destination].name;
|
||||
|
||||
try {
|
||||
await triggerSync.mutateAsync({
|
||||
syncId: pkiSync.id,
|
||||
projectId: pkiSync.projectId,
|
||||
destination: pkiSync.destination
|
||||
});
|
||||
|
||||
@@ -462,11 +461,13 @@ export const PkiSyncsTable = ({ pkiSyncs }: Props) => {
|
||||
isOpen={popUp.deleteSync.isOpen}
|
||||
pkiSync={popUp.deleteSync.data}
|
||||
/>
|
||||
<PkiSyncImportCertificatesModal
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("importCertificates", isOpen)}
|
||||
isOpen={popUp.importCertificates.isOpen}
|
||||
pkiSync={popUp.importCertificates.data}
|
||||
/>
|
||||
{popUp.importCertificates.data && (
|
||||
<PkiSyncImportCertificatesModal
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("importCertificates", isOpen)}
|
||||
isOpen={popUp.importCertificates.isOpen}
|
||||
pkiSync={popUp.importCertificates.data}
|
||||
/>
|
||||
)}
|
||||
<PkiSyncRemoveCertificatesModal
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("removeCertificates", isOpen)}
|
||||
isOpen={popUp.removeCertificates.isOpen}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { PkiSyncData } from "@app/hooks/api/pkiSyncs";
|
||||
import { TPkiSync } from "@app/hooks/api/pkiSyncs";
|
||||
|
||||
export const getPkiSyncDestinationColValues = (pkiSync: PkiSyncData) => {
|
||||
export const getPkiSyncDestinationColValues = (pkiSync: TPkiSync) => {
|
||||
const { destination } = pkiSync;
|
||||
|
||||
switch (destination) {
|
||||
|
||||
@@ -42,6 +42,7 @@ import { PKI_SYNC_MAP } from "@app/helpers/pkiSyncs";
|
||||
import { usePopUp, useToggle } from "@app/hooks";
|
||||
import {
|
||||
TPkiSync,
|
||||
usePkiSyncOption,
|
||||
useTriggerPkiSyncSyncCertificates,
|
||||
useUpdatePkiSync
|
||||
} from "@app/hooks/api/pkiSyncs";
|
||||
@@ -67,6 +68,8 @@ export const PkiSyncActionTriggers = ({ pkiSync }: Props) => {
|
||||
const triggerSyncMutation = useTriggerPkiSyncSyncCertificates();
|
||||
const updatePkiSyncMutation = useUpdatePkiSync();
|
||||
|
||||
const { syncOption } = usePkiSyncOption(destination);
|
||||
|
||||
const destinationName = PKI_SYNC_MAP[destination].name;
|
||||
|
||||
const handleCopyId = useCallback(() => {
|
||||
@@ -86,7 +89,6 @@ export const PkiSyncActionTriggers = ({ pkiSync }: Props) => {
|
||||
try {
|
||||
await triggerSyncMutation.mutateAsync({
|
||||
syncId: id,
|
||||
projectId,
|
||||
destination
|
||||
});
|
||||
createNotification({
|
||||
@@ -100,7 +102,7 @@ export const PkiSyncActionTriggers = ({ pkiSync }: Props) => {
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
}, [triggerSyncMutation, id, projectId]);
|
||||
}, [triggerSyncMutation, id, destination]);
|
||||
|
||||
const handleToggleAutoSync = useCallback(async () => {
|
||||
try {
|
||||
@@ -123,14 +125,14 @@ export const PkiSyncActionTriggers = ({ pkiSync }: Props) => {
|
||||
}
|
||||
}, [updatePkiSyncMutation, id, projectId, pkiSync.isAutoSyncEnabled]);
|
||||
|
||||
const permissionSubject = subscriberId
|
||||
? subject(ProjectPermissionSub.PkiSyncs, { subscriberId })
|
||||
: ProjectPermissionSub.PkiSyncs;
|
||||
const permissionSubject = subject(ProjectPermissionSub.PkiSyncs, {
|
||||
subscriberId: subscriberId || ""
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="ml-auto mt-4 flex flex-wrap items-center justify-end gap-2">
|
||||
<PkiSyncImportStatusBadge pkiSync={pkiSync} />
|
||||
{syncOption?.canImportCertificates && <PkiSyncImportStatusBadge pkiSync={pkiSync} />}
|
||||
<PkiSyncRemoveStatusBadge pkiSync={pkiSync} />
|
||||
{pkiSync.isAutoSyncEnabled ? (
|
||||
<Badge
|
||||
@@ -143,7 +145,7 @@ export const PkiSyncActionTriggers = ({ pkiSync }: Props) => {
|
||||
) : (
|
||||
<Tooltip
|
||||
className="text-xs"
|
||||
content="Auto-Sync is disabled. Changes to the PKI subscriber will not be automatically synced to the destination."
|
||||
content="Auto-Sync is disabled. Certificate changes in the PKI subscriber will not be automatically synced to the destination."
|
||||
>
|
||||
<div>
|
||||
<Badge className="flex h-5 w-min items-center gap-1.5 whitespace-nowrap bg-mineshaft-400/50 text-bunker-300">
|
||||
@@ -192,33 +194,35 @@ export const PkiSyncActionTriggers = ({ pkiSync }: Props) => {
|
||||
Copy Sync ID
|
||||
</DropdownMenuItem>
|
||||
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionPkiSyncActions.ImportCertificates}
|
||||
a={permissionSubject}
|
||||
>
|
||||
{(isAllowed: boolean) => (
|
||||
<DropdownMenuItem
|
||||
icon={<FontAwesomeIcon icon={faDownload} />}
|
||||
onClick={() => handlePopUpOpen("importCertificates")}
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
<Tooltip
|
||||
position="left"
|
||||
sideOffset={42}
|
||||
content={`Import certificates from this ${destinationName} destination into Infisical.`}
|
||||
{syncOption?.canImportCertificates && (
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionPkiSyncActions.ImportCertificates}
|
||||
a={permissionSubject}
|
||||
>
|
||||
{(isAllowed: boolean) => (
|
||||
<DropdownMenuItem
|
||||
icon={<FontAwesomeIcon icon={faDownload} />}
|
||||
onClick={() => handlePopUpOpen("importCertificates")}
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
<div className="flex h-full w-full items-center justify-between gap-1">
|
||||
<span>Import Certificates</span>
|
||||
<FontAwesomeIcon
|
||||
className="text-bunker-300"
|
||||
size="sm"
|
||||
icon={faInfoCircle}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
<Tooltip
|
||||
position="left"
|
||||
sideOffset={42}
|
||||
content={`Import certificates from this ${destinationName} destination into Infisical.`}
|
||||
>
|
||||
<div className="flex h-full w-full items-center justify-between gap-1">
|
||||
<span>Import Certificates</span>
|
||||
<FontAwesomeIcon
|
||||
className="text-bunker-300"
|
||||
size="sm"
|
||||
icon={faInfoCircle}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
)}
|
||||
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionPkiSyncActions.RemoveCertificates}
|
||||
@@ -283,11 +287,13 @@ export const PkiSyncActionTriggers = ({ pkiSync }: Props) => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PkiSyncImportCertificatesModal
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("importCertificates", isOpen)}
|
||||
isOpen={popUp.importCertificates.isOpen}
|
||||
pkiSync={pkiSync}
|
||||
/>
|
||||
{syncOption?.canImportCertificates && (
|
||||
<PkiSyncImportCertificatesModal
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("importCertificates", isOpen)}
|
||||
isOpen={popUp.importCertificates.isOpen}
|
||||
pkiSync={pkiSync}
|
||||
/>
|
||||
)}
|
||||
<PkiSyncRemoveCertificatesModal
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("removeCertificates", isOpen)}
|
||||
isOpen={popUp.removeCertificates.isOpen}
|
||||
|
||||
@@ -1752,7 +1752,6 @@ const SecretsManagerPermissionSubjects = (enabled = false) => ({
|
||||
[ProjectPermissionSub.SecretApproval]: enabled,
|
||||
[ProjectPermissionSub.Integrations]: enabled,
|
||||
[ProjectPermissionSub.SecretSyncs]: enabled,
|
||||
[ProjectPermissionSub.PkiSyncs]: enabled,
|
||||
[ProjectPermissionSub.Kms]: enabled,
|
||||
[ProjectPermissionSub.Environments]: enabled,
|
||||
[ProjectPermissionSub.Tags]: enabled,
|
||||
@@ -1774,6 +1773,7 @@ const CertificateManagerPermissionSubjects = (enabled = false) => ({
|
||||
[ProjectPermissionSub.PkiCollections]: enabled,
|
||||
[ProjectPermissionSub.PkiAlerts]: enabled,
|
||||
[ProjectPermissionSub.PkiSubscribers]: enabled,
|
||||
[ProjectPermissionSub.PkiSyncs]: enabled,
|
||||
[ProjectPermissionSub.CertificateAuthorities]: enabled,
|
||||
[ProjectPermissionSub.CertificateTemplates]: enabled,
|
||||
[ProjectPermissionSub.Certificates]: enabled
|
||||
@@ -2021,6 +2021,10 @@ export const RoleTemplates: Record<ProjectType, RoleTemplate[]> = {
|
||||
ProjectPermissionCertificateActions.Read,
|
||||
ProjectPermissionCertificateActions.ReadPrivateKey
|
||||
]
|
||||
},
|
||||
{
|
||||
subject: ProjectPermissionSub.PkiSyncs,
|
||||
actions: [ProjectPermissionPkiSyncActions.Read]
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -2048,6 +2052,10 @@ export const RoleTemplates: Record<ProjectType, RoleTemplate[]> = {
|
||||
{
|
||||
subject: ProjectPermissionSub.Certificates,
|
||||
actions: Object.values(ProjectPermissionCertificateActions)
|
||||
},
|
||||
{
|
||||
subject: ProjectPermissionSub.PkiSyncs,
|
||||
actions: Object.values(ProjectPermissionPkiSyncActions)
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -2148,10 +2156,6 @@ export const RoleTemplates: Record<ProjectType, RoleTemplate[]> = {
|
||||
subject: ProjectPermissionSub.SecretSyncs,
|
||||
actions: [ProjectPermissionSecretSyncActions.Read]
|
||||
},
|
||||
{
|
||||
subject: ProjectPermissionSub.PkiSyncs,
|
||||
actions: [ProjectPermissionPkiSyncActions.Read]
|
||||
},
|
||||
{
|
||||
subject: ProjectPermissionSub.Commits,
|
||||
actions: [ProjectPermissionCommitsActions.Read]
|
||||
@@ -2213,10 +2217,6 @@ export const RoleTemplates: Record<ProjectType, RoleTemplate[]> = {
|
||||
subject: ProjectPermissionSub.SecretSyncs,
|
||||
actions: Object.values(ProjectPermissionSecretSyncActions)
|
||||
},
|
||||
{
|
||||
subject: ProjectPermissionSub.PkiSyncs,
|
||||
actions: Object.values(ProjectPermissionPkiSyncActions)
|
||||
},
|
||||
{
|
||||
subject: ProjectPermissionSub.Commits,
|
||||
actions: Object.values(ProjectPermissionCommitsActions)
|
||||
|
||||
Reference in New Issue
Block a user