mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 15:40:47 +00:00
Merge branch 'main' into feat/addSecretImportsToOverview
This commit is contained in:
+40
-2
@@ -1,9 +1,9 @@
|
||||
import { ReactNode } from "react";
|
||||
import { Controller, useFormContext } from "react-hook-form";
|
||||
import { faTriangleExclamation } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faQuestionCircle, faTriangleExclamation } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { FormControl, Select, SelectItem } from "@app/components/v2";
|
||||
import { FormControl, Select, SelectItem, Switch, Tooltip } from "@app/components/v2";
|
||||
import { SECRET_SYNC_INITIAL_SYNC_BEHAVIOR_MAP, SECRET_SYNC_MAP } from "@app/helpers/secretSyncs";
|
||||
import { SecretSync, useSecretSyncOption } from "@app/hooks/api/secretSyncs";
|
||||
|
||||
@@ -116,6 +116,44 @@ export const SecretSyncOptionsFields = ({ hideInitialSync }: Props) => {
|
||||
</>
|
||||
)}
|
||||
{AdditionalSyncOptionsFieldsComponent}
|
||||
<Controller
|
||||
control={control}
|
||||
name="syncOptions.disableSecretDeletion"
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => {
|
||||
return (
|
||||
<FormControl isError={Boolean(error)} errorText={error?.message}>
|
||||
<Switch
|
||||
className="bg-mineshaft-400/80 shadow-inner data-[state=checked]:bg-green/80"
|
||||
id="auto-sync-enabled"
|
||||
thumbClassName="bg-mineshaft-800"
|
||||
onCheckedChange={onChange}
|
||||
isChecked={value}
|
||||
>
|
||||
<p className="w-[11rem]">
|
||||
Disable Secret Deletion{" "}
|
||||
<Tooltip
|
||||
className="max-w-md"
|
||||
content={
|
||||
<>
|
||||
<p>
|
||||
When enabled, Infisical will <span className="font-semibold">not</span>{" "}
|
||||
remove secrets from the destination during a sync.
|
||||
</p>
|
||||
<p className="mt-4">
|
||||
Enable this option if you intend to manage some secrets manually outside
|
||||
of Infisical.
|
||||
</p>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<FontAwesomeIcon icon={faQuestionCircle} size="sm" className="ml-1" />
|
||||
</Tooltip>
|
||||
</p>
|
||||
</Switch>
|
||||
</FormControl>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{/* <Controller
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
|
||||
+6
@@ -36,6 +36,7 @@ export const SecretSyncReviewFields = () => {
|
||||
secretPath,
|
||||
syncOptions: {
|
||||
// appendSuffix, prependPrefix,
|
||||
disableSecretDeletion,
|
||||
initialSyncBehavior
|
||||
},
|
||||
destination,
|
||||
@@ -111,6 +112,11 @@ export const SecretSyncReviewFields = () => {
|
||||
{/* <SecretSyncLabel label="Prepend Prefix">{prependPrefix}</SecretSyncLabel>
|
||||
<SecretSyncLabel label="Append Suffix">{appendSuffix}</SecretSyncLabel> */}
|
||||
{AdditionalSyncOptionsFieldsComponent}
|
||||
{disableSecretDeletion && (
|
||||
<SecretSyncLabel label="Secret Deletion">
|
||||
<Badge variant="primary">Disabled</Badge>
|
||||
</SecretSyncLabel>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-3">
|
||||
|
||||
@@ -7,7 +7,8 @@ export const BaseSecretSyncSchema = <T extends AnyZodObject | undefined = undefi
|
||||
additionalSyncOptions?: T
|
||||
) => {
|
||||
const baseSyncOptionsSchema = z.object({
|
||||
initialSyncBehavior: z.nativeEnum(SecretSyncInitialSyncBehavior)
|
||||
initialSyncBehavior: z.nativeEnum(SecretSyncInitialSyncBehavior),
|
||||
disableSecretDeletion: z.boolean().optional().default(false)
|
||||
// scott: removed temporarily for evaluation of template formatting
|
||||
// prependPrefix: z
|
||||
// .string()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useInfiniteQuery, useQuery, UseQueryOptions } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
import { Identity } from "@app/hooks/api/identities/types";
|
||||
|
||||
import { User } from "../types";
|
||||
import {
|
||||
@@ -10,7 +11,6 @@ import {
|
||||
TGetServerRootKmsEncryptionDetails,
|
||||
TServerConfig
|
||||
} from "./types";
|
||||
import { Identity } from "@app/hooks/api/identities/types";
|
||||
|
||||
export const adminStandaloneKeys = {
|
||||
getUsers: "get-users",
|
||||
|
||||
@@ -462,6 +462,7 @@ export const useUpdateIdentityOidcAuth = () => {
|
||||
boundIssuer,
|
||||
boundAudiences,
|
||||
boundClaims,
|
||||
claimMetadataMapping,
|
||||
boundSubject
|
||||
}) => {
|
||||
const {
|
||||
@@ -478,7 +479,8 @@ export const useUpdateIdentityOidcAuth = () => {
|
||||
accessTokenTTL,
|
||||
accessTokenMaxTTL,
|
||||
accessTokenNumUsesLimit,
|
||||
accessTokenTrustedIps
|
||||
accessTokenTrustedIps,
|
||||
claimMetadataMapping
|
||||
}
|
||||
);
|
||||
|
||||
@@ -504,6 +506,7 @@ export const useAddIdentityOidcAuth = () => {
|
||||
boundIssuer,
|
||||
boundAudiences,
|
||||
boundClaims,
|
||||
claimMetadataMapping,
|
||||
boundSubject,
|
||||
accessTokenTTL,
|
||||
accessTokenMaxTTL,
|
||||
@@ -524,7 +527,8 @@ export const useAddIdentityOidcAuth = () => {
|
||||
accessTokenTTL,
|
||||
accessTokenMaxTTL,
|
||||
accessTokenNumUsesLimit,
|
||||
accessTokenTrustedIps
|
||||
accessTokenTrustedIps,
|
||||
claimMetadataMapping
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -193,6 +193,7 @@ export type IdentityOidcAuth = {
|
||||
boundIssuer: string;
|
||||
boundAudiences: string;
|
||||
boundClaims: Record<string, string>;
|
||||
claimMetadataMapping?: Record<string, string>;
|
||||
boundSubject: string;
|
||||
accessTokenTTL: number;
|
||||
accessTokenMaxTTL: number;
|
||||
@@ -208,6 +209,7 @@ export type AddIdentityOidcAuthDTO = {
|
||||
boundIssuer: string;
|
||||
boundAudiences: string;
|
||||
boundClaims: Record<string, string>;
|
||||
claimMetadataMapping?: Record<string, string>;
|
||||
boundSubject: string;
|
||||
accessTokenTTL: number;
|
||||
accessTokenMaxTTL: number;
|
||||
@@ -225,6 +227,7 @@ export type UpdateIdentityOidcAuthDTO = {
|
||||
boundIssuer?: string;
|
||||
boundAudiences?: string;
|
||||
boundClaims?: Record<string, string>;
|
||||
claimMetadataMapping?: Record<string, string>;
|
||||
boundSubject?: string;
|
||||
accessTokenTTL?: number;
|
||||
accessTokenMaxTTL?: number;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { SecretSyncInitialSyncBehavior, SecretSyncStatus } from "@app/hooks/api/
|
||||
|
||||
export type RootSyncOptions = {
|
||||
initialSyncBehavior: SecretSyncInitialSyncBehavior;
|
||||
disableSecretDeletion?: boolean;
|
||||
// prependPrefix?: string;
|
||||
// appendSuffix?: string;
|
||||
};
|
||||
|
||||
@@ -28,13 +28,13 @@ import {
|
||||
useGetServerRootKmsEncryptionDetails,
|
||||
useUpdateServerConfig
|
||||
} from "@app/hooks/api";
|
||||
import { IdentityPanel } from "@app/pages/admin/OverviewPage/components/IdentityPanel";
|
||||
|
||||
import { AuthPanel } from "./components/AuthPanel";
|
||||
import { EncryptionPanel } from "./components/EncryptionPanel";
|
||||
import { IntegrationPanel } from "./components/IntegrationPanel";
|
||||
import { RateLimitPanel } from "./components/RateLimitPanel";
|
||||
import { UserPanel } from "./components/UserPanel";
|
||||
import { IdentityPanel } from "@app/pages/admin/OverviewPage/components/IdentityPanel";
|
||||
|
||||
enum TabSections {
|
||||
Settings = "settings",
|
||||
|
||||
+109
-1
@@ -52,6 +52,15 @@ const schema = z.object({
|
||||
value: z.string()
|
||||
})
|
||||
),
|
||||
claimMetadataMapping: z
|
||||
.array(
|
||||
z.object({
|
||||
key: z.string(),
|
||||
value: z.string()
|
||||
})
|
||||
)
|
||||
.optional()
|
||||
.nullable(),
|
||||
boundSubject: z.string().optional().default("")
|
||||
});
|
||||
|
||||
@@ -99,7 +108,6 @@ export const IdentityOidcAuthForm = ({
|
||||
accessTokenTrustedIps: [{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }]
|
||||
}
|
||||
});
|
||||
|
||||
const {
|
||||
fields: boundClaimsFields,
|
||||
append: appendBoundClaimField,
|
||||
@@ -109,6 +117,15 @@ export const IdentityOidcAuthForm = ({
|
||||
name: "boundClaims"
|
||||
});
|
||||
|
||||
const {
|
||||
fields: claimMetadataMappingFields,
|
||||
append: appendClaimMetadataMappingField,
|
||||
remove: removeClaimMetadataMappingField
|
||||
} = useFieldArray({
|
||||
control,
|
||||
name: "claimMetadataMapping"
|
||||
});
|
||||
|
||||
const {
|
||||
fields: accessTokenTrustedIpsFields,
|
||||
append: appendAccessTokenTrustedIp,
|
||||
@@ -126,6 +143,12 @@ export const IdentityOidcAuthForm = ({
|
||||
key,
|
||||
value
|
||||
})),
|
||||
claimMetadataMapping: data?.claimMetadataMapping
|
||||
? Object.entries(data.claimMetadataMapping).map(([key, value]) => ({
|
||||
key,
|
||||
value
|
||||
}))
|
||||
: undefined,
|
||||
boundSubject: data.boundSubject,
|
||||
accessTokenTTL: String(data.accessTokenTTL),
|
||||
accessTokenMaxTTL: String(data.accessTokenMaxTTL),
|
||||
@@ -164,6 +187,7 @@ export const IdentityOidcAuthForm = ({
|
||||
boundIssuer,
|
||||
boundAudiences,
|
||||
boundClaims,
|
||||
claimMetadataMapping,
|
||||
boundSubject
|
||||
}: FormData) => {
|
||||
try {
|
||||
@@ -180,6 +204,9 @@ export const IdentityOidcAuthForm = ({
|
||||
boundIssuer,
|
||||
boundAudiences,
|
||||
boundClaims: Object.fromEntries(boundClaims.map((entry) => [entry.key, entry.value])),
|
||||
claimMetadataMapping: claimMetadataMapping
|
||||
? Object.fromEntries(claimMetadataMapping.map((entry) => [entry.key, entry.value]))
|
||||
: undefined,
|
||||
boundSubject,
|
||||
accessTokenTTL: Number(accessTokenTTL),
|
||||
accessTokenMaxTTL: Number(accessTokenMaxTTL),
|
||||
@@ -194,6 +221,9 @@ export const IdentityOidcAuthForm = ({
|
||||
boundIssuer,
|
||||
boundAudiences,
|
||||
boundClaims: Object.fromEntries(boundClaims.map((entry) => [entry.key, entry.value])),
|
||||
claimMetadataMapping: claimMetadataMapping
|
||||
? Object.fromEntries(claimMetadataMapping.map((entry) => [entry.key, entry.value]))
|
||||
: undefined,
|
||||
boundSubject,
|
||||
organizationId: orgId,
|
||||
accessTokenTTL: Number(accessTokenTTL),
|
||||
@@ -449,6 +479,84 @@ export const IdentityOidcAuthForm = ({
|
||||
Add Claims
|
||||
</Button>
|
||||
</div>
|
||||
{claimMetadataMappingFields.map(({ id }, index) => (
|
||||
<div className="mb-3 flex items-end space-x-2" key={id}>
|
||||
<Controller
|
||||
control={control}
|
||||
name={`claimMetadataMapping.${index}.key`}
|
||||
render={({ field, fieldState: { error } }) => {
|
||||
return (
|
||||
<FormControl
|
||||
className="mb-0 flex-grow"
|
||||
label={index === 0 ? "Claim Metadata Mapping" : undefined}
|
||||
icon={
|
||||
index === 0 ? (
|
||||
<Tooltip
|
||||
className="text-center"
|
||||
content="Map token claims to metadata. This can later be accessed in attribute based permission as identity.metadata.oidc.claims.<>."
|
||||
>
|
||||
<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={`claimMetadataMapping.${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="key1.nested-key2"
|
||||
/>
|
||||
</FormControl>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
onClick={() => removeClaimMetadataMappingField(index)}
|
||||
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={() =>
|
||||
appendClaimMetadataMappingField({
|
||||
key: "",
|
||||
value: ""
|
||||
})
|
||||
}
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
size="xs"
|
||||
>
|
||||
Add Mapping
|
||||
</Button>
|
||||
</div>
|
||||
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
||||
<div className="mb-3 flex items-end space-x-2" key={id}>
|
||||
<Controller
|
||||
|
||||
+20
@@ -111,6 +111,26 @@ export const ViewIdentityOidcAuthContent = ({
|
||||
</Tooltip>
|
||||
)}
|
||||
</IdentityAuthFieldDisplay>
|
||||
<IdentityAuthFieldDisplay className="col-span-2" label="Claim Metadata Mapping">
|
||||
{data.claimMetadataMapping && Object.keys(data.claimMetadataMapping).length && (
|
||||
<Tooltip
|
||||
side="right"
|
||||
className="max-w-xl p-2"
|
||||
content={
|
||||
<pre className="whitespace-pre-wrap rounded bg-mineshaft-600 p-2">
|
||||
{JSON.stringify(data.claimMetadataMapping, null, 2)}
|
||||
</pre>
|
||||
}
|
||||
>
|
||||
<div className="w-min">
|
||||
<Badge className="flex h-5 w-min items-center gap-1.5 whitespace-nowrap bg-mineshaft-400/50 text-bunker-300">
|
||||
<FontAwesomeIcon icon={faEye} />
|
||||
<span>Reveal</span>
|
||||
</Badge>
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
</IdentityAuthFieldDisplay>
|
||||
</ViewIdentityContentWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Link, useNavigate, useRouter, useSearch } from "@tanstack/react-router";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
@@ -50,8 +51,10 @@ import {
|
||||
import { ROUTE_PATHS } from "@app/const/routes";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionDynamicSecretActions,
|
||||
ProjectPermissionSub,
|
||||
useProjectPermission,
|
||||
useSubscription,
|
||||
useWorkspace
|
||||
} from "@app/context";
|
||||
import { useDebounce, usePagination, usePopUp, useResetPageHelper } from "@app/hooks";
|
||||
@@ -72,6 +75,7 @@ import { SecretType, SecretV3RawSanitized, TSecretFolder } from "@app/hooks/api/
|
||||
import { ProjectType, ProjectVersion } from "@app/hooks/api/workspace/types";
|
||||
import { useDynamicSecretOverview, useFolderOverview, useSecretOverview } from "@app/hooks/utils";
|
||||
|
||||
import { CreateDynamicSecretForm } from "../SecretDashboardPage/components/ActionBar/CreateDynamicSecretForm";
|
||||
import { FolderForm } from "../SecretDashboardPage/components/ActionBar/FolderForm";
|
||||
import { CreateSecretForm } from "./components/CreateSecretForm";
|
||||
import { FolderBreadCrumbs } from "./components/FolderBreadCrumbs";
|
||||
@@ -135,6 +139,7 @@ export const OverviewPage = () => {
|
||||
const [searchFilter, setSearchFilter] = useState("");
|
||||
const [debouncedSearchFilter, setDebouncedSearchFilter] = useDebounce(searchFilter);
|
||||
const secretPath = (routerSearch?.secretPath as string) || "/";
|
||||
const { subscription } = useSubscription();
|
||||
|
||||
const [filter, setFilter] = useState<Filter>(DEFAULT_FILTER_STATE);
|
||||
const [filterHistory, setFilterHistory] = useState<
|
||||
@@ -182,6 +187,15 @@ export const OverviewPage = () => {
|
||||
}, []);
|
||||
|
||||
const userAvailableEnvs = currentWorkspace?.environments || [];
|
||||
const userAvailableDynamicSecretEnvs = userAvailableEnvs.filter((env) =>
|
||||
permission.can(
|
||||
ProjectPermissionDynamicSecretActions.CreateRootCredential,
|
||||
subject(ProjectPermissionSub.DynamicSecrets, {
|
||||
environment: env.slug,
|
||||
secretPath
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
const [visibleEnvs, setVisibleEnvs] = useState(userAvailableEnvs);
|
||||
|
||||
@@ -268,7 +282,9 @@ export const OverviewPage = () => {
|
||||
"addSecretsInAllEnvs",
|
||||
"addFolder",
|
||||
"misc",
|
||||
"updateFolder"
|
||||
"updateFolder",
|
||||
"addDynamicSecret",
|
||||
"upgradePlan"
|
||||
] as const);
|
||||
|
||||
const handleFolderCreate = async (folderName: string, description: string | null) => {
|
||||
@@ -882,20 +898,43 @@ export const OverviewPage = () => {
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
leftIcon={<FontAwesomeIcon icon={faFolderPlus} />}
|
||||
leftIcon={<FontAwesomeIcon icon={faFolderPlus} className="pr-2" />}
|
||||
onClick={() => {
|
||||
handlePopUpOpen("addFolder");
|
||||
handlePopUpClose("misc");
|
||||
}}
|
||||
isDisabled={!isAllowed}
|
||||
variant="outline_bg"
|
||||
className="h-10"
|
||||
className="h-10 text-left"
|
||||
isFullWidth
|
||||
>
|
||||
Add Folder
|
||||
</Button>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
<Tooltip
|
||||
content={
|
||||
userAvailableDynamicSecretEnvs.length === 0 ? "Access restricted" : ""
|
||||
}
|
||||
>
|
||||
<Button
|
||||
leftIcon={<FontAwesomeIcon icon={faFingerprint} className="pr-2" />}
|
||||
onClick={() => {
|
||||
if (subscription?.dynamicSecret) {
|
||||
handlePopUpOpen("addDynamicSecret");
|
||||
handlePopUpClose("misc");
|
||||
return;
|
||||
}
|
||||
handlePopUpOpen("upgradePlan");
|
||||
}}
|
||||
isDisabled={userAvailableDynamicSecretEnvs.length === 0}
|
||||
variant="outline_bg"
|
||||
className="h-10 text-left"
|
||||
isFullWidth
|
||||
>
|
||||
Add Dynamic Secret
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
@@ -1214,6 +1253,24 @@ export const OverviewPage = () => {
|
||||
/>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
<CreateDynamicSecretForm
|
||||
isOpen={popUp.addDynamicSecret.isOpen}
|
||||
onToggle={(isOpen) => handlePopUpToggle("addDynamicSecret", isOpen)}
|
||||
projectSlug={projectSlug}
|
||||
environments={userAvailableDynamicSecretEnvs}
|
||||
secretPath={secretPath}
|
||||
/>
|
||||
{subscription && (
|
||||
<UpgradePlanModal
|
||||
isOpen={popUp.upgradePlan.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||
text={
|
||||
subscription.slug === null
|
||||
? "You can perform this action under an Enterprise license"
|
||||
: "You can perform this action if you switch to Infisical's Team plan"
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
+2
-1
@@ -655,8 +655,9 @@ export const ActionBar = ({
|
||||
isOpen={popUp.addDynamicSecret.isOpen}
|
||||
onToggle={(isOpen) => handlePopUpToggle("addDynamicSecret", isOpen)}
|
||||
projectSlug={projectSlug}
|
||||
environment={environment}
|
||||
environments={[{ slug: environment, name: environment, id: "not-used" }]}
|
||||
secretPath={secretPath}
|
||||
isSingleEnvironmentMode
|
||||
/>
|
||||
<Modal
|
||||
isOpen={popUp.addFolder.isOpen}
|
||||
|
||||
+41
-7
@@ -11,6 +11,7 @@ import {
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
Button,
|
||||
FilterableSelect,
|
||||
FormControl,
|
||||
Input,
|
||||
SecretInput,
|
||||
@@ -18,6 +19,7 @@ import {
|
||||
} from "@app/components/v2";
|
||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||
|
||||
const formSchema = z.object({
|
||||
provider: z.object({
|
||||
@@ -50,7 +52,8 @@ const formSchema = z.object({
|
||||
if (valMs > 24 * 60 * 60 * 1000)
|
||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
||||
}),
|
||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||
environment: z.object({ name: z.string(), slug: z.string() })
|
||||
});
|
||||
type TForm = z.infer<typeof formSchema>;
|
||||
|
||||
@@ -59,15 +62,17 @@ type Props = {
|
||||
onCancel: () => void;
|
||||
secretPath: string;
|
||||
projectSlug: string;
|
||||
environment: string;
|
||||
environments: WorkspaceEnv[];
|
||||
isSingleEnvironmentMode?: boolean;
|
||||
};
|
||||
|
||||
export const AwsElastiCacheInputForm = ({
|
||||
onCompleted,
|
||||
onCancel,
|
||||
environment,
|
||||
environments,
|
||||
secretPath,
|
||||
projectSlug
|
||||
projectSlug,
|
||||
isSingleEnvironmentMode
|
||||
}: Props) => {
|
||||
const {
|
||||
control,
|
||||
@@ -87,13 +92,20 @@ export const AwsElastiCacheInputForm = ({
|
||||
revocationStatement: `{
|
||||
"UserId": "{{username}}"
|
||||
}`
|
||||
}
|
||||
},
|
||||
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||
}
|
||||
});
|
||||
|
||||
const createDynamicSecret = useCreateDynamicSecret();
|
||||
|
||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
||||
const handleCreateDynamicSecret = async ({
|
||||
name,
|
||||
maxTTL,
|
||||
provider,
|
||||
defaultTTL,
|
||||
environment
|
||||
}: TForm) => {
|
||||
// wait till previous request is finished
|
||||
if (createDynamicSecret.isPending) return;
|
||||
try {
|
||||
@@ -104,7 +116,7 @@ export const AwsElastiCacheInputForm = ({
|
||||
path: secretPath,
|
||||
defaultTTL,
|
||||
projectSlug,
|
||||
environmentSlug: environment
|
||||
environmentSlug: environment.slug
|
||||
});
|
||||
onCompleted();
|
||||
} catch {
|
||||
@@ -299,6 +311,28 @@ export const AwsElastiCacheInputForm = ({
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
{!isSingleEnvironmentMode && (
|
||||
<Controller
|
||||
control={control}
|
||||
name="environment"
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Environment"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<FilterableSelect
|
||||
options={environments}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder="Select the environment to create secret in..."
|
||||
getOptionLabel={(option) => option.name}
|
||||
getOptionValue={(option) => option.slug}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+45
-8
@@ -5,9 +5,10 @@ import { z } from "zod";
|
||||
|
||||
import { TtlFormLabel } from "@app/components/features";
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { Button, FormControl, Input, TextArea } from "@app/components/v2";
|
||||
import { Button, FilterableSelect, FormControl, Input, TextArea } from "@app/components/v2";
|
||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||
|
||||
const formSchema = z.object({
|
||||
provider: z.object({
|
||||
@@ -40,7 +41,8 @@ const formSchema = z.object({
|
||||
if (valMs > 24 * 60 * 60 * 1000)
|
||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
||||
}),
|
||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||
environment: z.object({ name: z.string(), slug: z.string() })
|
||||
});
|
||||
type TForm = z.infer<typeof formSchema>;
|
||||
|
||||
@@ -49,29 +51,41 @@ type Props = {
|
||||
onCancel: () => void;
|
||||
secretPath: string;
|
||||
projectSlug: string;
|
||||
environment: string;
|
||||
environments: WorkspaceEnv[];
|
||||
isSingleEnvironmentMode?: boolean;
|
||||
};
|
||||
|
||||
export const AwsIamInputForm = ({
|
||||
onCompleted,
|
||||
onCancel,
|
||||
environment,
|
||||
environments,
|
||||
secretPath,
|
||||
projectSlug
|
||||
projectSlug,
|
||||
isSingleEnvironmentMode
|
||||
}: Props) => {
|
||||
const {
|
||||
control,
|
||||
formState: { isSubmitting },
|
||||
handleSubmit
|
||||
} = useForm<TForm>({
|
||||
resolver: zodResolver(formSchema)
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||
}
|
||||
});
|
||||
|
||||
const createDynamicSecret = useCreateDynamicSecret();
|
||||
|
||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
||||
const handleCreateDynamicSecret = async ({
|
||||
name,
|
||||
maxTTL,
|
||||
provider,
|
||||
defaultTTL,
|
||||
environment
|
||||
}: TForm) => {
|
||||
// wait till previous request is finished
|
||||
if (createDynamicSecret.isPending) return;
|
||||
|
||||
try {
|
||||
await createDynamicSecret.mutateAsync({
|
||||
provider: { type: DynamicSecretProviders.AwsIam, inputs: provider },
|
||||
@@ -80,7 +94,7 @@ export const AwsIamInputForm = ({
|
||||
path: secretPath,
|
||||
defaultTTL,
|
||||
projectSlug,
|
||||
environmentSlug: environment
|
||||
environmentSlug: environment.slug
|
||||
});
|
||||
onCompleted();
|
||||
} catch {
|
||||
@@ -286,6 +300,29 @@ export const AwsIamInputForm = ({
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
{!isSingleEnvironmentMode && (
|
||||
<Controller
|
||||
control={control}
|
||||
name="environment"
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Environment"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<FilterableSelect
|
||||
options={environments}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder="Select the environment to create secret in..."
|
||||
getOptionLabel={(option) => option.name}
|
||||
getOptionValue={(option) => option.slug}
|
||||
menuPlacement="top"
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+39
-8
@@ -12,7 +12,7 @@ import { z } from "zod";
|
||||
|
||||
import { TtlFormLabel } from "@app/components/features";
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { Button, FormControl, Input } from "@app/components/v2";
|
||||
import { Button, FilterableSelect, FormControl, Input } from "@app/components/v2";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -23,6 +23,7 @@ import { Tooltip } from "@app/components/v2/Tooltip";
|
||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||
import { useGetDynamicSecretProviderData } from "@app/hooks/api/dynamicSecret/queries";
|
||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||
|
||||
const formSchema = z.object({
|
||||
selectedUsers: z.array(
|
||||
@@ -60,7 +61,8 @@ const formSchema = z.object({
|
||||
name: z
|
||||
.string()
|
||||
.min(1)
|
||||
.refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
||||
.refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||
environment: z.object({ name: z.string(), slug: z.string() })
|
||||
});
|
||||
type TForm = z.infer<typeof formSchema>;
|
||||
|
||||
@@ -69,15 +71,17 @@ type Props = {
|
||||
onCancel: () => void;
|
||||
secretPath: string;
|
||||
projectSlug: string;
|
||||
environment: string;
|
||||
environments: WorkspaceEnv[];
|
||||
isSingleEnvironmentMode?: boolean;
|
||||
};
|
||||
|
||||
export const AzureEntraIdInputForm = ({
|
||||
onCompleted,
|
||||
onCancel,
|
||||
environment,
|
||||
environments,
|
||||
secretPath,
|
||||
projectSlug
|
||||
projectSlug,
|
||||
isSingleEnvironmentMode
|
||||
}: Props) => {
|
||||
const {
|
||||
control,
|
||||
@@ -85,7 +89,10 @@ export const AzureEntraIdInputForm = ({
|
||||
watch,
|
||||
handleSubmit
|
||||
} = useForm<TForm>({
|
||||
resolver: zodResolver(formSchema)
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||
}
|
||||
});
|
||||
const tenantId = watch("provider.tenantId");
|
||||
const applicationId = watch("provider.applicationId");
|
||||
@@ -107,7 +114,8 @@ export const AzureEntraIdInputForm = ({
|
||||
selectedUsers,
|
||||
provider,
|
||||
maxTTL,
|
||||
defaultTTL
|
||||
defaultTTL,
|
||||
environment
|
||||
}: TForm) => {
|
||||
// wait till previous request is finished
|
||||
if (createDynamicSecret.isPending) return;
|
||||
@@ -129,7 +137,7 @@ export const AzureEntraIdInputForm = ({
|
||||
path: secretPath,
|
||||
defaultTTL,
|
||||
projectSlug,
|
||||
environmentSlug: environment
|
||||
environmentSlug: environment.slug
|
||||
});
|
||||
});
|
||||
onCompleted();
|
||||
@@ -373,6 +381,29 @@ export const AzureEntraIdInputForm = ({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{!isSingleEnvironmentMode && (
|
||||
<Controller
|
||||
control={control}
|
||||
name="environment"
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Environment"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<FilterableSelect
|
||||
options={environments}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder="Select the environment to create secret in..."
|
||||
getOptionLabel={(option) => option.name}
|
||||
getOptionValue={(option) => option.slug}
|
||||
menuPlacement="top"
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-4 flex items-center space-x-4">
|
||||
<Button type="submit" isLoading={isSubmitting} isDisabled={isLoading || isError}>
|
||||
|
||||
+43
-7
@@ -11,6 +11,7 @@ import {
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
Button,
|
||||
FilterableSelect,
|
||||
FormControl,
|
||||
Input,
|
||||
SecretInput,
|
||||
@@ -18,6 +19,7 @@ import {
|
||||
} from "@app/components/v2";
|
||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||
|
||||
const formSchema = z.object({
|
||||
provider: z.object({
|
||||
@@ -52,7 +54,8 @@ const formSchema = z.object({
|
||||
if (valMs > 24 * 60 * 60 * 1000)
|
||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
||||
}),
|
||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||
environment: z.object({ name: z.string(), slug: z.string() })
|
||||
});
|
||||
type TForm = z.infer<typeof formSchema>;
|
||||
|
||||
@@ -61,7 +64,8 @@ type Props = {
|
||||
onCancel: () => void;
|
||||
secretPath: string;
|
||||
projectSlug: string;
|
||||
environment: string;
|
||||
environments: WorkspaceEnv[];
|
||||
isSingleEnvironmentMode?: boolean;
|
||||
};
|
||||
|
||||
const getSqlStatements = () => {
|
||||
@@ -76,9 +80,10 @@ const getSqlStatements = () => {
|
||||
export const CassandraInputForm = ({
|
||||
onCompleted,
|
||||
onCancel,
|
||||
environment,
|
||||
environments,
|
||||
secretPath,
|
||||
projectSlug
|
||||
projectSlug,
|
||||
isSingleEnvironmentMode
|
||||
}: Props) => {
|
||||
const {
|
||||
control,
|
||||
@@ -87,15 +92,23 @@ export const CassandraInputForm = ({
|
||||
} = useForm<TForm>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
provider: getSqlStatements()
|
||||
provider: getSqlStatements(),
|
||||
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||
}
|
||||
});
|
||||
|
||||
const createDynamicSecret = useCreateDynamicSecret();
|
||||
|
||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
||||
const handleCreateDynamicSecret = async ({
|
||||
name,
|
||||
maxTTL,
|
||||
provider,
|
||||
defaultTTL,
|
||||
environment
|
||||
}: TForm) => {
|
||||
// wait till previous request is finished
|
||||
if (createDynamicSecret.isPending) return;
|
||||
|
||||
try {
|
||||
await createDynamicSecret.mutateAsync({
|
||||
provider: { type: DynamicSecretProviders.Cassandra, inputs: provider },
|
||||
@@ -104,7 +117,7 @@ export const CassandraInputForm = ({
|
||||
path: secretPath,
|
||||
defaultTTL,
|
||||
projectSlug,
|
||||
environmentSlug: environment
|
||||
environmentSlug: environment.slug
|
||||
});
|
||||
onCompleted();
|
||||
} catch {
|
||||
@@ -345,6 +358,29 @@ export const CassandraInputForm = ({
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
{!isSingleEnvironmentMode && (
|
||||
<Controller
|
||||
control={control}
|
||||
name="environment"
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Environment"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<FilterableSelect
|
||||
options={environments}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder="Select the environment to create secret in..."
|
||||
getOptionLabel={(option) => option.name}
|
||||
getOptionValue={(option) => option.slug}
|
||||
menuPlacement="top"
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+36
-18
@@ -17,6 +17,7 @@ import { AnimatePresence, motion } from "framer-motion";
|
||||
|
||||
import { Modal, ModalContent } from "@app/components/v2";
|
||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||
|
||||
import { AwsElastiCacheInputForm } from "./AwsElastiCacheInputForm";
|
||||
import { AwsIamInputForm } from "./AwsIamInputForm";
|
||||
@@ -38,8 +39,9 @@ type Props = {
|
||||
isOpen?: boolean;
|
||||
onToggle: (isOpen: boolean) => void;
|
||||
projectSlug: string;
|
||||
environment: string;
|
||||
environments: WorkspaceEnv[];
|
||||
secretPath: string;
|
||||
isSingleEnvironmentMode?: boolean;
|
||||
};
|
||||
|
||||
enum WizardSteps {
|
||||
@@ -129,8 +131,9 @@ export const CreateDynamicSecretForm = ({
|
||||
isOpen,
|
||||
onToggle,
|
||||
projectSlug,
|
||||
environment,
|
||||
secretPath
|
||||
environments,
|
||||
secretPath,
|
||||
isSingleEnvironmentMode
|
||||
}: Props) => {
|
||||
const [wizardStep, setWizardStep] = useState(WizardSteps.SelectProvider);
|
||||
const [selectedProvider, setSelectedProvider] = useState<DynamicSecretProviders | null>(null);
|
||||
@@ -197,7 +200,8 @@ export const CreateDynamicSecretForm = ({
|
||||
onCancel={handleFormReset}
|
||||
projectSlug={projectSlug}
|
||||
secretPath={secretPath}
|
||||
environment={environment}
|
||||
environments={environments}
|
||||
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
@@ -215,7 +219,8 @@ export const CreateDynamicSecretForm = ({
|
||||
onCancel={handleFormReset}
|
||||
projectSlug={projectSlug}
|
||||
secretPath={secretPath}
|
||||
environment={environment}
|
||||
environments={environments}
|
||||
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
@@ -233,7 +238,8 @@ export const CreateDynamicSecretForm = ({
|
||||
onCancel={handleFormReset}
|
||||
projectSlug={projectSlug}
|
||||
secretPath={secretPath}
|
||||
environment={environment}
|
||||
environments={environments}
|
||||
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
@@ -251,7 +257,8 @@ export const CreateDynamicSecretForm = ({
|
||||
onCancel={handleFormReset}
|
||||
projectSlug={projectSlug}
|
||||
secretPath={secretPath}
|
||||
environment={environment}
|
||||
environments={environments}
|
||||
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
@@ -269,7 +276,8 @@ export const CreateDynamicSecretForm = ({
|
||||
onCancel={handleFormReset}
|
||||
projectSlug={projectSlug}
|
||||
secretPath={secretPath}
|
||||
environment={environment}
|
||||
environments={environments}
|
||||
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
@@ -287,7 +295,8 @@ export const CreateDynamicSecretForm = ({
|
||||
onCancel={handleFormReset}
|
||||
projectSlug={projectSlug}
|
||||
secretPath={secretPath}
|
||||
environment={environment}
|
||||
environments={environments}
|
||||
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
@@ -305,7 +314,8 @@ export const CreateDynamicSecretForm = ({
|
||||
onCancel={handleFormReset}
|
||||
projectSlug={projectSlug}
|
||||
secretPath={secretPath}
|
||||
environment={environment}
|
||||
environments={environments}
|
||||
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
@@ -323,7 +333,8 @@ export const CreateDynamicSecretForm = ({
|
||||
onCancel={handleFormReset}
|
||||
projectSlug={projectSlug}
|
||||
secretPath={secretPath}
|
||||
environment={environment}
|
||||
environments={environments}
|
||||
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
@@ -341,7 +352,8 @@ export const CreateDynamicSecretForm = ({
|
||||
onCancel={handleFormReset}
|
||||
projectSlug={projectSlug}
|
||||
secretPath={secretPath}
|
||||
environment={environment}
|
||||
environments={environments}
|
||||
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
@@ -359,7 +371,8 @@ export const CreateDynamicSecretForm = ({
|
||||
onCancel={handleFormReset}
|
||||
projectSlug={projectSlug}
|
||||
secretPath={secretPath}
|
||||
environment={environment}
|
||||
environments={environments}
|
||||
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
@@ -377,7 +390,8 @@ export const CreateDynamicSecretForm = ({
|
||||
onCancel={handleFormReset}
|
||||
projectSlug={projectSlug}
|
||||
secretPath={secretPath}
|
||||
environment={environment}
|
||||
environments={environments}
|
||||
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
@@ -395,7 +409,8 @@ export const CreateDynamicSecretForm = ({
|
||||
onCancel={handleFormReset}
|
||||
projectSlug={projectSlug}
|
||||
secretPath={secretPath}
|
||||
environment={environment}
|
||||
environments={environments}
|
||||
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
@@ -413,7 +428,8 @@ export const CreateDynamicSecretForm = ({
|
||||
onCancel={handleFormReset}
|
||||
projectSlug={projectSlug}
|
||||
secretPath={secretPath}
|
||||
environment={environment}
|
||||
environments={environments}
|
||||
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
@@ -432,7 +448,8 @@ export const CreateDynamicSecretForm = ({
|
||||
onCancel={handleFormReset}
|
||||
projectSlug={projectSlug}
|
||||
secretPath={secretPath}
|
||||
environment={environment}
|
||||
environments={environments}
|
||||
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
@@ -450,7 +467,8 @@ export const CreateDynamicSecretForm = ({
|
||||
onCancel={handleFormReset}
|
||||
projectSlug={projectSlug}
|
||||
secretPath={secretPath}
|
||||
environment={environment}
|
||||
environments={environments}
|
||||
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
+42
-7
@@ -9,6 +9,7 @@ import { TtlFormLabel } from "@app/components/features";
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import {
|
||||
Button,
|
||||
FilterableSelect,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
IconButton,
|
||||
@@ -19,6 +20,7 @@ import {
|
||||
} from "@app/components/v2";
|
||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||
|
||||
const authMethods = [
|
||||
{
|
||||
@@ -73,7 +75,8 @@ const formSchema = z.object({
|
||||
if (valMs > 24 * 60 * 60 * 1000)
|
||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
||||
}),
|
||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||
environment: z.object({ name: z.string(), slug: z.string() })
|
||||
});
|
||||
type TForm = z.infer<typeof formSchema>;
|
||||
|
||||
@@ -82,15 +85,17 @@ type Props = {
|
||||
onCancel: () => void;
|
||||
secretPath: string;
|
||||
projectSlug: string;
|
||||
environment: string;
|
||||
environments: WorkspaceEnv[];
|
||||
isSingleEnvironmentMode?: boolean;
|
||||
};
|
||||
|
||||
export const ElasticSearchInputForm = ({
|
||||
onCompleted,
|
||||
onCancel,
|
||||
environment,
|
||||
environments,
|
||||
secretPath,
|
||||
projectSlug
|
||||
projectSlug,
|
||||
isSingleEnvironmentMode
|
||||
}: Props) => {
|
||||
const {
|
||||
control,
|
||||
@@ -107,13 +112,20 @@ export const ElasticSearchInputForm = ({
|
||||
},
|
||||
roles: ["superuser"],
|
||||
port: 443
|
||||
}
|
||||
},
|
||||
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||
}
|
||||
});
|
||||
|
||||
const createDynamicSecret = useCreateDynamicSecret();
|
||||
|
||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
||||
const handleCreateDynamicSecret = async ({
|
||||
name,
|
||||
maxTTL,
|
||||
provider,
|
||||
defaultTTL,
|
||||
environment
|
||||
}: TForm) => {
|
||||
// wait till previous request is finished
|
||||
if (createDynamicSecret.isPending) return;
|
||||
try {
|
||||
@@ -124,7 +136,7 @@ export const ElasticSearchInputForm = ({
|
||||
path: secretPath,
|
||||
defaultTTL,
|
||||
projectSlug,
|
||||
environmentSlug: environment
|
||||
environmentSlug: environment.slug
|
||||
});
|
||||
onCompleted();
|
||||
} catch {
|
||||
@@ -406,6 +418,29 @@ export const ElasticSearchInputForm = ({
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{!isSingleEnvironmentMode && (
|
||||
<Controller
|
||||
control={control}
|
||||
name="environment"
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Environment"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<FilterableSelect
|
||||
options={environments}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder="Select the environment to create secret in..."
|
||||
getOptionLabel={(option) => option.name}
|
||||
getOptionValue={(option) => option.slug}
|
||||
menuPlacement="top"
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+49
-7
@@ -7,9 +7,18 @@ import { z } from "zod";
|
||||
|
||||
import { TtlFormLabel } from "@app/components/features";
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { Button, FormControl, Input, Select, SelectItem, TextArea } from "@app/components/v2";
|
||||
import {
|
||||
Button,
|
||||
FilterableSelect,
|
||||
FormControl,
|
||||
Input,
|
||||
Select,
|
||||
SelectItem,
|
||||
TextArea
|
||||
} from "@app/components/v2";
|
||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||
|
||||
enum CredentialType {
|
||||
Dynamic = "dynamic",
|
||||
@@ -69,7 +78,8 @@ const formSchema = z.object({
|
||||
if (valMs > 24 * 60 * 60 * 1000)
|
||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
||||
}),
|
||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||
environment: z.object({ name: z.string(), slug: z.string() })
|
||||
});
|
||||
|
||||
type TForm = z.infer<typeof formSchema>;
|
||||
@@ -79,7 +89,8 @@ type Props = {
|
||||
onCancel: () => void;
|
||||
secretPath: string;
|
||||
projectSlug: string;
|
||||
environment: string;
|
||||
environments: WorkspaceEnv[];
|
||||
isSingleEnvironmentMode?: boolean;
|
||||
};
|
||||
|
||||
export const LdapInputForm = ({
|
||||
@@ -87,7 +98,8 @@ export const LdapInputForm = ({
|
||||
onCancel,
|
||||
secretPath,
|
||||
projectSlug,
|
||||
environment
|
||||
environments,
|
||||
isSingleEnvironmentMode
|
||||
}: Props) => {
|
||||
const {
|
||||
control,
|
||||
@@ -107,7 +119,8 @@ export const LdapInputForm = ({
|
||||
revocationLdif: "",
|
||||
rollbackLdif: "",
|
||||
credentialType: CredentialType.Dynamic
|
||||
}
|
||||
},
|
||||
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||
}
|
||||
});
|
||||
|
||||
@@ -115,7 +128,13 @@ export const LdapInputForm = ({
|
||||
|
||||
const createDynamicSecret = useCreateDynamicSecret();
|
||||
|
||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
||||
const handleCreateDynamicSecret = async ({
|
||||
name,
|
||||
maxTTL,
|
||||
provider,
|
||||
defaultTTL,
|
||||
environment
|
||||
}: TForm) => {
|
||||
// wait till previous request is finished
|
||||
if (createDynamicSecret.isPending) return;
|
||||
try {
|
||||
@@ -126,7 +145,7 @@ export const LdapInputForm = ({
|
||||
path: secretPath,
|
||||
defaultTTL,
|
||||
projectSlug,
|
||||
environmentSlug: environment
|
||||
environmentSlug: environment.slug
|
||||
});
|
||||
onCompleted();
|
||||
} catch {
|
||||
@@ -369,6 +388,29 @@ export const LdapInputForm = ({
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{!isSingleEnvironmentMode && (
|
||||
<Controller
|
||||
control={control}
|
||||
name="environment"
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Environment"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<FilterableSelect
|
||||
options={environments}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder="Select the environment to create secret in..."
|
||||
getOptionLabel={(option) => option.name}
|
||||
getOptionValue={(option) => option.slug}
|
||||
menuPlacement="top"
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+42
-7
@@ -13,6 +13,7 @@ import {
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
Button,
|
||||
FilterableSelect,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
IconButton,
|
||||
@@ -22,6 +23,7 @@ import {
|
||||
} from "@app/components/v2";
|
||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||
|
||||
const formSchema = z.object({
|
||||
provider: z.object({
|
||||
@@ -63,7 +65,8 @@ const formSchema = z.object({
|
||||
if (valMs > 24 * 60 * 60 * 1000)
|
||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
||||
}),
|
||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||
environment: z.object({ name: z.string(), slug: z.string() })
|
||||
});
|
||||
type TForm = z.infer<typeof formSchema>;
|
||||
|
||||
@@ -72,7 +75,8 @@ type Props = {
|
||||
onCancel: () => void;
|
||||
secretPath: string;
|
||||
projectSlug: string;
|
||||
environment: string;
|
||||
environments: WorkspaceEnv[];
|
||||
isSingleEnvironmentMode?: boolean;
|
||||
};
|
||||
|
||||
const ATLAS_SCOPE_TYPES = [
|
||||
@@ -93,9 +97,10 @@ const ATLAS_SCOPE_TYPES = [
|
||||
export const MongoAtlasInputForm = ({
|
||||
onCompleted,
|
||||
onCancel,
|
||||
environment,
|
||||
environments,
|
||||
secretPath,
|
||||
projectSlug
|
||||
projectSlug,
|
||||
isSingleEnvironmentMode
|
||||
}: Props) => {
|
||||
const {
|
||||
control,
|
||||
@@ -108,7 +113,8 @@ export const MongoAtlasInputForm = ({
|
||||
defaultValues: {
|
||||
provider: {
|
||||
roles: [{ databaseName: "", roleName: "" }]
|
||||
}
|
||||
},
|
||||
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||
}
|
||||
});
|
||||
|
||||
@@ -124,7 +130,13 @@ export const MongoAtlasInputForm = ({
|
||||
|
||||
const createDynamicSecret = useCreateDynamicSecret();
|
||||
|
||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
||||
const handleCreateDynamicSecret = async ({
|
||||
name,
|
||||
maxTTL,
|
||||
provider,
|
||||
defaultTTL,
|
||||
environment
|
||||
}: TForm) => {
|
||||
// wait till previous request is finished
|
||||
if (createDynamicSecret.isPending) return;
|
||||
try {
|
||||
@@ -135,7 +147,7 @@ export const MongoAtlasInputForm = ({
|
||||
path: secretPath,
|
||||
defaultTTL,
|
||||
projectSlug,
|
||||
environmentSlug: environment
|
||||
environmentSlug: environment.slug
|
||||
});
|
||||
onCompleted();
|
||||
} catch {
|
||||
@@ -438,6 +450,29 @@ export const MongoAtlasInputForm = ({
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
{!isSingleEnvironmentMode && (
|
||||
<Controller
|
||||
control={control}
|
||||
name="environment"
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Environment"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<FilterableSelect
|
||||
options={environments}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder="Select the environment to create secret in..."
|
||||
getOptionLabel={(option) => option.name}
|
||||
getOptionValue={(option) => option.slug}
|
||||
menuPlacement="top"
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+50
-8
@@ -7,9 +7,18 @@ import { z } from "zod";
|
||||
|
||||
import { TtlFormLabel } from "@app/components/features";
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { Button, FormControl, FormLabel, IconButton, Input, SecretInput } from "@app/components/v2";
|
||||
import {
|
||||
Button,
|
||||
FilterableSelect,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
IconButton,
|
||||
Input,
|
||||
SecretInput
|
||||
} from "@app/components/v2";
|
||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||
|
||||
const formSchema = z.object({
|
||||
provider: z.object({
|
||||
@@ -46,7 +55,8 @@ const formSchema = z.object({
|
||||
if (valMs > 24 * 60 * 60 * 1000)
|
||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
||||
}),
|
||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||
environment: z.object({ name: z.string(), slug: z.string() })
|
||||
});
|
||||
type TForm = z.infer<typeof formSchema>;
|
||||
|
||||
@@ -55,15 +65,17 @@ type Props = {
|
||||
onCancel: () => void;
|
||||
secretPath: string;
|
||||
projectSlug: string;
|
||||
environment: string;
|
||||
environments: WorkspaceEnv[];
|
||||
isSingleEnvironmentMode?: boolean;
|
||||
};
|
||||
|
||||
export const MongoDBDatabaseInputForm = ({
|
||||
onCompleted,
|
||||
onCancel,
|
||||
environment,
|
||||
environments,
|
||||
secretPath,
|
||||
projectSlug
|
||||
projectSlug,
|
||||
isSingleEnvironmentMode
|
||||
}: Props) => {
|
||||
const {
|
||||
control,
|
||||
@@ -76,7 +88,8 @@ export const MongoDBDatabaseInputForm = ({
|
||||
defaultValues: {
|
||||
provider: {
|
||||
roles: [{ roleName: "readWrite" }]
|
||||
}
|
||||
},
|
||||
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||
}
|
||||
});
|
||||
|
||||
@@ -87,7 +100,13 @@ export const MongoDBDatabaseInputForm = ({
|
||||
|
||||
const createDynamicSecret = useCreateDynamicSecret();
|
||||
|
||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
||||
const handleCreateDynamicSecret = async ({
|
||||
name,
|
||||
maxTTL,
|
||||
provider,
|
||||
defaultTTL,
|
||||
environment
|
||||
}: TForm) => {
|
||||
// wait till previous request is finished
|
||||
if (createDynamicSecret.isPending) return;
|
||||
try {
|
||||
@@ -105,7 +124,7 @@ export const MongoDBDatabaseInputForm = ({
|
||||
path: secretPath,
|
||||
defaultTTL,
|
||||
projectSlug,
|
||||
environmentSlug: environment
|
||||
environmentSlug: environment.slug
|
||||
});
|
||||
onCompleted();
|
||||
} catch {
|
||||
@@ -321,6 +340,29 @@ export const MongoDBDatabaseInputForm = ({
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{!isSingleEnvironmentMode && (
|
||||
<Controller
|
||||
control={control}
|
||||
name="environment"
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Environment"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<FilterableSelect
|
||||
options={environments}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder="Select the environment to create secret in..."
|
||||
getOptionLabel={(option) => option.name}
|
||||
getOptionValue={(option) => option.slug}
|
||||
menuPlacement="top"
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+50
-8
@@ -7,9 +7,18 @@ import { z } from "zod";
|
||||
|
||||
import { TtlFormLabel } from "@app/components/features";
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { Button, FormControl, FormLabel, IconButton, Input, SecretInput } from "@app/components/v2";
|
||||
import {
|
||||
Button,
|
||||
FilterableSelect,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
IconButton,
|
||||
Input,
|
||||
SecretInput
|
||||
} from "@app/components/v2";
|
||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||
|
||||
const formSchema = z.object({
|
||||
provider: z.object({
|
||||
@@ -50,7 +59,8 @@ const formSchema = z.object({
|
||||
if (valMs > 24 * 60 * 60 * 1000)
|
||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
||||
}),
|
||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||
environment: z.object({ name: z.string(), slug: z.string() })
|
||||
});
|
||||
type TForm = z.infer<typeof formSchema>;
|
||||
|
||||
@@ -59,15 +69,17 @@ type Props = {
|
||||
onCancel: () => void;
|
||||
secretPath: string;
|
||||
projectSlug: string;
|
||||
environment: string;
|
||||
environments: WorkspaceEnv[];
|
||||
isSingleEnvironmentMode?: boolean;
|
||||
};
|
||||
|
||||
export const RabbitMqInputForm = ({
|
||||
onCompleted,
|
||||
onCancel,
|
||||
environment,
|
||||
environments,
|
||||
secretPath,
|
||||
projectSlug
|
||||
projectSlug,
|
||||
isSingleEnvironmentMode
|
||||
}: Props) => {
|
||||
const {
|
||||
control,
|
||||
@@ -89,13 +101,20 @@ export const RabbitMqInputForm = ({
|
||||
}
|
||||
},
|
||||
tags: []
|
||||
}
|
||||
},
|
||||
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||
}
|
||||
});
|
||||
|
||||
const createDynamicSecret = useCreateDynamicSecret();
|
||||
|
||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
||||
const handleCreateDynamicSecret = async ({
|
||||
name,
|
||||
maxTTL,
|
||||
provider,
|
||||
defaultTTL,
|
||||
environment
|
||||
}: TForm) => {
|
||||
// wait till previous request is finished
|
||||
if (createDynamicSecret.isPending) return;
|
||||
try {
|
||||
@@ -106,7 +125,7 @@ export const RabbitMqInputForm = ({
|
||||
path: secretPath,
|
||||
defaultTTL,
|
||||
projectSlug,
|
||||
environmentSlug: environment
|
||||
environmentSlug: environment.slug
|
||||
});
|
||||
onCompleted();
|
||||
} catch {
|
||||
@@ -405,6 +424,29 @@ export const RabbitMqInputForm = ({
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{!isSingleEnvironmentMode && (
|
||||
<Controller
|
||||
control={control}
|
||||
name="environment"
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Environment"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<FilterableSelect
|
||||
options={environments}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder="Select the environment to create secret in..."
|
||||
getOptionLabel={(option) => option.name}
|
||||
getOptionValue={(option) => option.slug}
|
||||
menuPlacement="top"
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+42
-7
@@ -11,6 +11,7 @@ import {
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
Button,
|
||||
FilterableSelect,
|
||||
FormControl,
|
||||
Input,
|
||||
SecretInput,
|
||||
@@ -18,6 +19,7 @@ import {
|
||||
} from "@app/components/v2";
|
||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||
|
||||
const formSchema = z.object({
|
||||
provider: z.object({
|
||||
@@ -50,7 +52,8 @@ const formSchema = z.object({
|
||||
if (valMs > 24 * 60 * 60 * 1000)
|
||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
||||
}),
|
||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||
environment: z.object({ name: z.string(), slug: z.string() })
|
||||
});
|
||||
type TForm = z.infer<typeof formSchema>;
|
||||
|
||||
@@ -59,15 +62,17 @@ type Props = {
|
||||
onCancel: () => void;
|
||||
secretPath: string;
|
||||
projectSlug: string;
|
||||
environment: string;
|
||||
environments: WorkspaceEnv[];
|
||||
isSingleEnvironmentMode?: boolean;
|
||||
};
|
||||
|
||||
export const RedisInputForm = ({
|
||||
onCompleted,
|
||||
onCancel,
|
||||
environment,
|
||||
environments,
|
||||
secretPath,
|
||||
projectSlug
|
||||
projectSlug,
|
||||
isSingleEnvironmentMode
|
||||
}: Props) => {
|
||||
const {
|
||||
control,
|
||||
@@ -81,13 +86,20 @@ export const RedisInputForm = ({
|
||||
port: 6379,
|
||||
creationStatement: "ACL SETUSER {{username}} on >{{password}} ~* &* +@all",
|
||||
revocationStatement: "ACL DELUSER {{username}}"
|
||||
}
|
||||
},
|
||||
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||
}
|
||||
});
|
||||
|
||||
const createDynamicSecret = useCreateDynamicSecret();
|
||||
|
||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
||||
const handleCreateDynamicSecret = async ({
|
||||
name,
|
||||
maxTTL,
|
||||
provider,
|
||||
defaultTTL,
|
||||
environment
|
||||
}: TForm) => {
|
||||
// wait till previous request is finished
|
||||
if (createDynamicSecret.isPending) return;
|
||||
try {
|
||||
@@ -98,7 +110,7 @@ export const RedisInputForm = ({
|
||||
path: secretPath,
|
||||
defaultTTL,
|
||||
projectSlug,
|
||||
environmentSlug: environment
|
||||
environmentSlug: environment.slug
|
||||
});
|
||||
onCompleted();
|
||||
} catch {
|
||||
@@ -313,6 +325,29 @@ export const RedisInputForm = ({
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
{!isSingleEnvironmentMode && (
|
||||
<Controller
|
||||
control={control}
|
||||
name="environment"
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Environment"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<FilterableSelect
|
||||
options={environments}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder="Select the environment to create secret in..."
|
||||
getOptionLabel={(option) => option.name}
|
||||
getOptionValue={(option) => option.slug}
|
||||
menuPlacement="top"
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+42
-7
@@ -11,12 +11,14 @@ import {
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
Button,
|
||||
FilterableSelect,
|
||||
FormControl,
|
||||
Input,
|
||||
TextArea
|
||||
} from "@app/components/v2";
|
||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||
|
||||
const formSchema = z.object({
|
||||
provider: z.object({
|
||||
@@ -48,7 +50,8 @@ const formSchema = z.object({
|
||||
if (valMs > 24 * 60 * 60 * 1000)
|
||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
||||
}),
|
||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||
environment: z.object({ name: z.string(), slug: z.string() })
|
||||
});
|
||||
type TForm = z.infer<typeof formSchema>;
|
||||
|
||||
@@ -57,15 +60,17 @@ type Props = {
|
||||
onCancel: () => void;
|
||||
secretPath: string;
|
||||
projectSlug: string;
|
||||
environment: string;
|
||||
environments: WorkspaceEnv[];
|
||||
isSingleEnvironmentMode?: boolean;
|
||||
};
|
||||
|
||||
export const SapAseInputForm = ({
|
||||
onCompleted,
|
||||
onCancel,
|
||||
environment,
|
||||
environments,
|
||||
secretPath,
|
||||
projectSlug
|
||||
projectSlug,
|
||||
isSingleEnvironmentMode
|
||||
}: Props) => {
|
||||
const {
|
||||
control,
|
||||
@@ -82,13 +87,20 @@ sp_adduser '{{username}}', '{{username}}', null;
|
||||
sp_role 'grant', 'mon_role', '{{username}}';`,
|
||||
revocationStatement: `sp_dropuser '{{username}}';
|
||||
sp_droplogin '{{username}}';`
|
||||
}
|
||||
},
|
||||
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||
}
|
||||
});
|
||||
|
||||
const createDynamicSecret = useCreateDynamicSecret();
|
||||
|
||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
||||
const handleCreateDynamicSecret = async ({
|
||||
name,
|
||||
maxTTL,
|
||||
provider,
|
||||
defaultTTL,
|
||||
environment
|
||||
}: TForm) => {
|
||||
// wait till previous request is finished
|
||||
if (createDynamicSecret.isPending) return;
|
||||
try {
|
||||
@@ -99,7 +111,7 @@ sp_droplogin '{{username}}';`
|
||||
path: secretPath,
|
||||
defaultTTL,
|
||||
projectSlug,
|
||||
environmentSlug: environment
|
||||
environmentSlug: environment.slug
|
||||
});
|
||||
onCompleted();
|
||||
} catch {
|
||||
@@ -291,6 +303,29 @@ sp_droplogin '{{username}}';`
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
{!isSingleEnvironmentMode && (
|
||||
<Controller
|
||||
control={control}
|
||||
name="environment"
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Environment"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<FilterableSelect
|
||||
options={environments}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder="Select the environment to create secret in..."
|
||||
getOptionLabel={(option) => option.name}
|
||||
getOptionValue={(option) => option.slug}
|
||||
menuPlacement="top"
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+42
-7
@@ -11,6 +11,7 @@ import {
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
Button,
|
||||
FilterableSelect,
|
||||
FormControl,
|
||||
Input,
|
||||
SecretInput,
|
||||
@@ -18,6 +19,7 @@ import {
|
||||
} from "@app/components/v2";
|
||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||
|
||||
const formSchema = z.object({
|
||||
provider: z.object({
|
||||
@@ -50,7 +52,8 @@ const formSchema = z.object({
|
||||
if (valMs > 24 * 60 * 60 * 1000)
|
||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
||||
}),
|
||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||
environment: z.object({ name: z.string(), slug: z.string() })
|
||||
});
|
||||
type TForm = z.infer<typeof formSchema>;
|
||||
|
||||
@@ -59,15 +62,17 @@ type Props = {
|
||||
onCancel: () => void;
|
||||
secretPath: string;
|
||||
projectSlug: string;
|
||||
environment: string;
|
||||
environments: WorkspaceEnv[];
|
||||
isSingleEnvironmentMode?: boolean;
|
||||
};
|
||||
|
||||
export const SapHanaInputForm = ({
|
||||
onCompleted,
|
||||
onCancel,
|
||||
environment,
|
||||
environments,
|
||||
secretPath,
|
||||
projectSlug
|
||||
projectSlug,
|
||||
isSingleEnvironmentMode
|
||||
}: Props) => {
|
||||
const {
|
||||
control,
|
||||
@@ -82,13 +87,20 @@ GRANT "MONITORING" TO {{username}};`,
|
||||
revocationStatement: `REVOKE "MONITORING" FROM {{username}};
|
||||
DROP USER {{username}};`,
|
||||
renewStatement: "ALTER USER {{username}} VALID UNTIL '{{expiration}}';"
|
||||
}
|
||||
},
|
||||
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||
}
|
||||
});
|
||||
|
||||
const createDynamicSecret = useCreateDynamicSecret();
|
||||
|
||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
||||
const handleCreateDynamicSecret = async ({
|
||||
name,
|
||||
maxTTL,
|
||||
provider,
|
||||
defaultTTL,
|
||||
environment
|
||||
}: TForm) => {
|
||||
// wait till previous request is finished
|
||||
if (createDynamicSecret.isPending) return;
|
||||
try {
|
||||
@@ -99,7 +111,7 @@ DROP USER {{username}};`,
|
||||
path: secretPath,
|
||||
defaultTTL,
|
||||
projectSlug,
|
||||
environmentSlug: environment
|
||||
environmentSlug: environment.slug
|
||||
});
|
||||
onCompleted();
|
||||
} catch {
|
||||
@@ -311,6 +323,29 @@ DROP USER {{username}};`,
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
{!isSingleEnvironmentMode && (
|
||||
<Controller
|
||||
control={control}
|
||||
name="environment"
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Environment"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<FilterableSelect
|
||||
options={environments}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder="Select the environment to create secret in..."
|
||||
getOptionLabel={(option) => option.name}
|
||||
getOptionValue={(option) => option.slug}
|
||||
menuPlacement="top"
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+42
-7
@@ -13,12 +13,14 @@ import {
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
Button,
|
||||
FilterableSelect,
|
||||
FormControl,
|
||||
Input,
|
||||
TextArea
|
||||
} from "@app/components/v2";
|
||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||
|
||||
const formSchema = z.object({
|
||||
provider: z.object({
|
||||
@@ -54,7 +56,8 @@ const formSchema = z.object({
|
||||
.string()
|
||||
.trim()
|
||||
.min(1)
|
||||
.refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
||||
.refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||
environment: z.object({ name: z.string(), slug: z.string() })
|
||||
});
|
||||
type TForm = z.infer<typeof formSchema>;
|
||||
|
||||
@@ -63,15 +66,17 @@ type Props = {
|
||||
onCancel: () => void;
|
||||
secretPath: string;
|
||||
projectSlug: string;
|
||||
environment: string;
|
||||
environments: WorkspaceEnv[];
|
||||
isSingleEnvironmentMode?: boolean;
|
||||
};
|
||||
|
||||
export const SnowflakeInputForm = ({
|
||||
onCompleted,
|
||||
onCancel,
|
||||
environment,
|
||||
environments,
|
||||
secretPath,
|
||||
projectSlug
|
||||
projectSlug,
|
||||
isSingleEnvironmentMode
|
||||
}: Props) => {
|
||||
const {
|
||||
control,
|
||||
@@ -85,13 +90,20 @@ export const SnowflakeInputForm = ({
|
||||
"CREATE USER {{username}} PASSWORD = '{{password}}' DEFAULT_ROLE = public DEFAULT_SECONDARY_ROLES = ('ALL') MUST_CHANGE_PASSWORD = FALSE DAYS_TO_EXPIRY = {{expiration}};",
|
||||
revocationStatement: "DROP USER {{username}};",
|
||||
renewStatement: "ALTER USER {{username}} SET DAYS_TO_EXPIRY = {{expiration}};"
|
||||
}
|
||||
},
|
||||
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||
}
|
||||
});
|
||||
|
||||
const createDynamicSecret = useCreateDynamicSecret();
|
||||
|
||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
||||
const handleCreateDynamicSecret = async ({
|
||||
name,
|
||||
maxTTL,
|
||||
provider,
|
||||
defaultTTL,
|
||||
environment
|
||||
}: TForm) => {
|
||||
// wait till previous request is finished
|
||||
if (createDynamicSecret.isPending) return;
|
||||
try {
|
||||
@@ -102,7 +114,7 @@ export const SnowflakeInputForm = ({
|
||||
path: secretPath,
|
||||
defaultTTL,
|
||||
projectSlug,
|
||||
environmentSlug: environment
|
||||
environmentSlug: environment.slug
|
||||
});
|
||||
onCompleted();
|
||||
} catch (err) {
|
||||
@@ -314,6 +326,29 @@ export const SnowflakeInputForm = ({
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
{!isSingleEnvironmentMode && (
|
||||
<Controller
|
||||
control={control}
|
||||
name="environment"
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Environment"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<FilterableSelect
|
||||
options={environments}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder="Select the environment to create secret in..."
|
||||
getOptionLabel={(option) => option.name}
|
||||
getOptionValue={(option) => option.slug}
|
||||
menuPlacement="top"
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+43
-7
@@ -12,6 +12,7 @@ import {
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
Button,
|
||||
FilterableSelect,
|
||||
FormControl,
|
||||
Input,
|
||||
SecretInput,
|
||||
@@ -22,6 +23,7 @@ import {
|
||||
import { useWorkspace } from "@app/context";
|
||||
import { gatewaysQueryKeys, useCreateDynamicSecret } from "@app/hooks/api";
|
||||
import { DynamicSecretProviders, SqlProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||
|
||||
const passwordRequirementsSchema = z
|
||||
.object({
|
||||
@@ -79,7 +81,8 @@ const formSchema = z.object({
|
||||
if (valMs > 24 * 60 * 60 * 1000)
|
||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
||||
}),
|
||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||
environment: z.object({ name: z.string(), slug: z.string() })
|
||||
});
|
||||
type TForm = z.infer<typeof formSchema>;
|
||||
|
||||
@@ -88,7 +91,8 @@ type Props = {
|
||||
onCancel: () => void;
|
||||
secretPath: string;
|
||||
projectSlug: string;
|
||||
environment: string;
|
||||
environments: WorkspaceEnv[];
|
||||
isSingleEnvironmentMode?: boolean;
|
||||
};
|
||||
|
||||
const getSqlStatements = (provider: SqlProviders) => {
|
||||
@@ -145,9 +149,10 @@ const getDefaultPort = (provider: SqlProviders) => {
|
||||
export const SqlDatabaseInputForm = ({
|
||||
onCompleted,
|
||||
onCancel,
|
||||
environment,
|
||||
environments,
|
||||
secretPath,
|
||||
projectSlug
|
||||
projectSlug,
|
||||
isSingleEnvironmentMode
|
||||
}: Props) => {
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
|
||||
@@ -172,7 +177,8 @@ export const SqlDatabaseInputForm = ({
|
||||
},
|
||||
allowedSymbols: "-_.~!*"
|
||||
}
|
||||
}
|
||||
},
|
||||
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||
}
|
||||
});
|
||||
|
||||
@@ -181,9 +187,16 @@ export const SqlDatabaseInputForm = ({
|
||||
gatewaysQueryKeys.listProjectGateways({ projectId: currentWorkspace.id })
|
||||
);
|
||||
|
||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
||||
const handleCreateDynamicSecret = async ({
|
||||
name,
|
||||
maxTTL,
|
||||
provider,
|
||||
defaultTTL,
|
||||
environment
|
||||
}: TForm) => {
|
||||
// wait till previous request is finished
|
||||
if (createDynamicSecret.isPending) return;
|
||||
|
||||
try {
|
||||
await createDynamicSecret.mutateAsync({
|
||||
provider: { type: DynamicSecretProviders.SqlDatabase, inputs: provider },
|
||||
@@ -192,7 +205,7 @@ export const SqlDatabaseInputForm = ({
|
||||
path: secretPath,
|
||||
defaultTTL,
|
||||
projectSlug,
|
||||
environmentSlug: environment
|
||||
environmentSlug: environment.slug
|
||||
});
|
||||
onCompleted();
|
||||
} catch {
|
||||
@@ -649,6 +662,29 @@ export const SqlDatabaseInputForm = ({
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
{!isSingleEnvironmentMode && (
|
||||
<Controller
|
||||
control={control}
|
||||
name="environment"
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Environment"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<FilterableSelect
|
||||
options={environments}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder="Select the environment to create secret in..."
|
||||
getOptionLabel={(option) => option.name}
|
||||
getOptionValue={(option) => option.slug}
|
||||
menuPlacement="top"
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+43
-8
@@ -5,9 +5,17 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { Button, FormControl, Input, Select, SelectItem } from "@app/components/v2";
|
||||
import {
|
||||
Button,
|
||||
FilterableSelect,
|
||||
FormControl,
|
||||
Input,
|
||||
Select,
|
||||
SelectItem
|
||||
} from "@app/components/v2";
|
||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||
|
||||
enum ConfigType {
|
||||
URL = "url",
|
||||
@@ -52,7 +60,8 @@ const formSchema = z.object({
|
||||
.string()
|
||||
.trim()
|
||||
.min(1)
|
||||
.refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
||||
.refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||
environment: z.object({ name: z.string(), slug: z.string() })
|
||||
});
|
||||
|
||||
type TForm = z.infer<typeof formSchema>;
|
||||
@@ -62,15 +71,17 @@ type Props = {
|
||||
onCancel: () => void;
|
||||
secretPath: string;
|
||||
projectSlug: string;
|
||||
environment: string;
|
||||
environments: WorkspaceEnv[];
|
||||
isSingleEnvironmentMode?: boolean;
|
||||
};
|
||||
|
||||
export const TotpInputForm = ({
|
||||
onCompleted,
|
||||
onCancel,
|
||||
environment,
|
||||
environments,
|
||||
secretPath,
|
||||
projectSlug
|
||||
projectSlug,
|
||||
isSingleEnvironmentMode
|
||||
}: Props) => {
|
||||
const {
|
||||
control,
|
||||
@@ -82,7 +93,8 @@ export const TotpInputForm = ({
|
||||
defaultValues: {
|
||||
provider: {
|
||||
configType: ConfigType.URL
|
||||
}
|
||||
},
|
||||
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||
}
|
||||
});
|
||||
|
||||
@@ -90,7 +102,7 @@ export const TotpInputForm = ({
|
||||
|
||||
const createDynamicSecret = useCreateDynamicSecret();
|
||||
|
||||
const handleCreateDynamicSecret = async ({ name, provider }: TForm) => {
|
||||
const handleCreateDynamicSecret = async ({ name, provider, environment }: TForm) => {
|
||||
// wait till previous request is finished
|
||||
if (createDynamicSecret.isPending) return;
|
||||
try {
|
||||
@@ -101,7 +113,7 @@ export const TotpInputForm = ({
|
||||
path: secretPath,
|
||||
defaultTTL: "1m",
|
||||
projectSlug,
|
||||
environmentSlug: environment
|
||||
environmentSlug: environment.slug
|
||||
});
|
||||
onCompleted();
|
||||
} catch (err) {
|
||||
@@ -295,6 +307,29 @@ export const TotpInputForm = ({
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
{!isSingleEnvironmentMode && (
|
||||
<Controller
|
||||
control={control}
|
||||
name="environment"
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Environment"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<FilterableSelect
|
||||
options={environments}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder="Select the environment to create secret in..."
|
||||
getOptionLabel={(option) => option.name}
|
||||
getOptionValue={(option) => option.slug}
|
||||
menuPlacement="top"
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+4
-1
@@ -306,7 +306,10 @@ export const SecretItem = memo(
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<div key="actions" className="flex h-full flex-shrink-0 self-start transition-all group-hover:gap-x-2">
|
||||
<div
|
||||
key="actions"
|
||||
className="flex h-full flex-shrink-0 self-start transition-all group-hover:gap-x-2"
|
||||
>
|
||||
<Tooltip content="Copy secret">
|
||||
<IconButton
|
||||
isDisabled={secret.secretValueHidden}
|
||||
|
||||
+24
-20
@@ -4,7 +4,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import { SecretSyncLabel } from "@app/components/secret-syncs";
|
||||
import { IconButton } from "@app/components/v2";
|
||||
import { Badge, IconButton } from "@app/components/v2";
|
||||
import { ProjectPermissionSub } from "@app/context";
|
||||
import { ProjectPermissionSecretSyncActions } from "@app/context/ProjectPermissionContext/types";
|
||||
import { SECRET_SYNC_INITIAL_SYNC_BEHAVIOR_MAP } from "@app/helpers/secretSyncs";
|
||||
@@ -24,7 +24,8 @@ export const SecretSyncOptionsSection = ({ secretSync, onEditOptions }: Props) =
|
||||
syncOptions: {
|
||||
// appendSuffix,
|
||||
// prependPrefix,
|
||||
initialSyncBehavior
|
||||
initialSyncBehavior,
|
||||
disableSecretDeletion
|
||||
}
|
||||
} = secretSync;
|
||||
|
||||
@@ -58,24 +59,22 @@ export const SecretSyncOptionsSection = ({ secretSync, onEditOptions }: Props) =
|
||||
<div className="flex w-full flex-col gap-3 rounded-lg border border-mineshaft-600 bg-mineshaft-900 px-4 py-3">
|
||||
<div className="flex items-center justify-between border-b border-mineshaft-400 pb-2">
|
||||
<h3 className="font-semibold text-mineshaft-100">Sync Options</h3>
|
||||
{AdditionalSyncOptionsComponent && (
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionSecretSyncActions.Edit}
|
||||
a={ProjectPermissionSub.SecretSyncs}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<IconButton
|
||||
variant="plain"
|
||||
colorSchema="secondary"
|
||||
isDisabled={!isAllowed}
|
||||
ariaLabel="Edit sync options"
|
||||
onClick={onEditOptions}
|
||||
>
|
||||
<FontAwesomeIcon icon={faEdit} />
|
||||
</IconButton>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
)}
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionSecretSyncActions.Edit}
|
||||
a={ProjectPermissionSub.SecretSyncs}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<IconButton
|
||||
variant="plain"
|
||||
colorSchema="secondary"
|
||||
isDisabled={!isAllowed}
|
||||
ariaLabel="Edit sync options"
|
||||
onClick={onEditOptions}
|
||||
>
|
||||
<FontAwesomeIcon icon={faEdit} />
|
||||
</IconButton>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
</div>
|
||||
<div>
|
||||
<div className="space-y-3">
|
||||
@@ -85,6 +84,11 @@ export const SecretSyncOptionsSection = ({ secretSync, onEditOptions }: Props) =
|
||||
{/* <SecretSyncLabel label="Prefix">{prependPrefix}</SecretSyncLabel>
|
||||
<SecretSyncLabel label="Suffix">{appendSuffix}</SecretSyncLabel> */}
|
||||
{AdditionalSyncOptionsComponent}
|
||||
{disableSecretDeletion && (
|
||||
<SecretSyncLabel label="Secret Deletion">
|
||||
<Badge variant="primary">Disabled</Badge>
|
||||
</SecretSyncLabel>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user