Merge branch 'heads/main' into daniel/srp-removal-round-2

This commit is contained in:
Daniel Hougaard
2025-07-30 04:44:57 +04:00
474 changed files with 5372 additions and 28647 deletions
@@ -1,26 +1,14 @@
import { FunctionComponent, ReactNode } from "react";
import { BoundCanProps, Can } from "@casl/react";
import { faLock } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { TOrgPermission, useOrgPermission } from "@app/context/OrgPermissionContext";
import { Tooltip } from "../v2";
import { AccessRestrictedBanner, Tooltip } from "../v2";
export const OrgPermissionGuardBanner = () => {
return (
<div className="container mx-auto flex h-full items-center justify-center">
<div className="flex items-end space-x-12 rounded-md bg-mineshaft-800 p-16 text-bunker-300">
<div>
<FontAwesomeIcon icon={faLock} size="6x" />
</div>
<div>
<div className="mb-2 text-4xl font-medium">Access Restricted</div>
<div className="text-sm">
Your role has limited permissions, please <br /> contact your admin to gain access
</div>
</div>
</div>
<AccessRestrictedBanner />
</div>
);
};
@@ -1,15 +1,12 @@
import { ReactNode } from "react";
import { faLock } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { twMerge } from "tailwind-merge";
import { AccessRestrictedBanner } from "@app/components/v2";
type Props = {
containerClassName?: string;
className?: string;
children?: ReactNode;
};
export const PermissionDeniedBanner = ({ containerClassName, className, children }: Props) => {
export const PermissionDeniedBanner = ({ containerClassName }: Props) => {
return (
<div
className={twMerge(
@@ -17,22 +14,7 @@ export const PermissionDeniedBanner = ({ containerClassName, className, children
containerClassName
)}
>
<div className={twMerge("rounded-md bg-mineshaft-800 p-16 text-bunker-300", className)}>
<div className="flex items-end space-x-12">
<div>
<FontAwesomeIcon icon={faLock} size="6x" />
</div>
<div>
<div className="mb-2 text-4xl font-medium">Access Restricted</div>
{children || (
<div className="text-sm">
Your role has limited permissions, please <br /> contact your administrator to gain
access
</div>
)}
</div>
</div>
</div>
<AccessRestrictedBanner />
</div>
);
};
@@ -1,9 +1,8 @@
import { FunctionComponent, ReactNode } from "react";
import { AbilityTuple, MongoAbility } from "@casl/ability";
import { Can } from "@casl/react";
import { faLock } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { AccessRestrictedBanner } from "@app/components/v2";
import { ProjectPermissionSet, useProjectPermission } from "@app/context/ProjectPermissionContext";
import { Tooltip } from "../v2/Tooltip";
@@ -11,17 +10,7 @@ import { Tooltip } from "../v2/Tooltip";
export const ProjectPermissionGuardBanner = () => {
return (
<div className="container mx-auto flex h-full items-center justify-center">
<div className="flex items-end space-x-12 rounded-md bg-mineshaft-800 p-16 text-bunker-300">
<div>
<FontAwesomeIcon icon={faLock} size="6x" />
</div>
<div>
<div className="mb-2 text-4xl font-medium">Access Restricted</div>
<div className="text-sm">
Your role has limited permissions, please <br /> contact your admin to gain access
</div>
</div>
</div>
<AccessRestrictedBanner />
</div>
);
};
@@ -271,6 +271,8 @@ const NewProjectForm = ({ onOpenChange }: NewProjectFormProps) => {
value={value}
onValueChange={onChange}
className="w-full"
position="popper"
dropdownContainerClassName="max-w-none"
>
{projectTemplates.length
? projectTemplates.map((template) => (
@@ -306,6 +308,9 @@ const NewProjectForm = ({ onOpenChange }: NewProjectFormProps) => {
onChange(e);
}}
className="mb-12 w-full bg-mineshaft-600"
position="popper"
dropdownContainerClassName="max-w-none -top-1"
side="top"
>
<SelectItem value={INTERNAL_KMS_KEY_ID} key="kms-internal">
Default Infisical KMS
@@ -76,7 +76,6 @@ export const CreateSecretRotationV2Modal = ({ onOpenChange, isOpen, ...props }:
</div>
)
}
onPointerDownOutside={(e) => e.preventDefault()}
className={selectedRotation ? "max-w-2xl" : "max-w-3xl"}
subTitle={
selectedRotation ? undefined : "Select a provider to create a secret rotation for."
@@ -75,7 +75,6 @@ export const CreateSecretScanningDataSourceModal = ({ onOpenChange, isOpen, ...p
</div>
)
}
onPointerDownOutside={(e) => e.preventDefault()}
className={selectedDataSource ? "max-w-2xl" : "max-w-3xl"}
subTitle={
selectedDataSource ? undefined : "Select a data source to configure secret scanning for."
@@ -56,7 +56,6 @@ export const CreateSecretSyncModal = ({ onOpenChange, selectSync = null, ...prop
"Add Sync"
)
}
onPointerDownOutside={(e) => e.preventDefault()}
className="max-w-2xl"
bodyClassName="overflow-visible"
subTitle={selectedSync ? undefined : "Select a third-party service to sync secrets to."}
@@ -9,7 +9,7 @@ import {
useRenderConnectionListServices
} from "@app/hooks/api/appConnections/render";
import { SecretSync } from "@app/hooks/api/secretSyncs";
import { RenderSyncScope, RenderSyncType } from "@app/hooks/api/secretSyncs/render-sync";
import { RenderSyncScope, RenderSyncType } from "@app/hooks/api/secretSyncs/types/render-sync";
import { TSecretSyncForm } from "../schemas";
@@ -0,0 +1,40 @@
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 { SecretSync } from "@app/hooks/api/secretSyncs";
import { TSecretSyncForm } from "../schemas";
export const RenderSyncOptionsFields = () => {
const { control } = useFormContext<TSecretSyncForm & { destination: SecretSync.Render }>();
return (
<Controller
name="syncOptions.autoRedeployServices"
control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl className="mt-4" isError={Boolean(error?.message)} errorText={error?.message}>
<Switch
className="bg-mineshaft-400/50 shadow-inner data-[state=checked]:bg-green/80"
id="auto-redeploy-services"
thumbClassName="bg-mineshaft-800"
isChecked={value}
onCheckedChange={onChange}
>
Auto Redeploy Services On Sync
<Tooltip
className="max-w-md"
content={
<p>If enabled, services will be automatically redeployed upon secret changes.</p>
}
>
<FontAwesomeIcon icon={faQuestionCircle} size="sm" className="ml-1" />
</Tooltip>
</Switch>
</FormControl>
)}
/>
);
};
@@ -14,6 +14,7 @@ import { SecretSync, useSecretSyncOption } from "@app/hooks/api/secretSyncs";
import { TSecretSyncForm } from "../schemas";
import { AwsParameterStoreSyncOptionsFields } from "./AwsParameterStoreSyncOptionsFields";
import { AwsSecretsManagerSyncOptionsFields } from "./AwsSecretsManagerSyncOptionsFields";
import { RenderSyncOptionsFields } from "./RenderSyncOptionsFields";
type Props = {
hideInitialSync?: boolean;
@@ -38,6 +39,9 @@ export const SecretSyncOptionsFields = ({ hideInitialSync }: Props) => {
case SecretSync.AWSSecretsManager:
AdditionalSyncOptionsFieldsComponent = <AwsSecretsManagerSyncOptionsFields />;
break;
case SecretSync.Render:
AdditionalSyncOptionsFieldsComponent = <RenderSyncOptionsFields />;
break;
case SecretSync.GitHub:
case SecretSync.GCPSecretManager:
case SecretSync.AzureKeyVault:
@@ -54,7 +58,6 @@ export const SecretSyncOptionsFields = ({ hideInitialSync }: Props) => {
case SecretSync.OnePass:
case SecretSync.OCIVault:
case SecretSync.Heroku:
case SecretSync.Render:
case SecretSync.Flyio:
case SecretSync.GitLab:
case SecretSync.CloudflarePages:
@@ -2,8 +2,29 @@ import { useFormContext } from "react-hook-form";
import { GenericFieldLabel } from "@app/components/secret-syncs";
import { TSecretSyncForm } from "@app/components/secret-syncs/forms/schemas";
import { Badge } from "@app/components/v2";
import { SecretSync } from "@app/hooks/api/secretSyncs";
export const RenderSyncOptionsReviewFields = () => {
const { watch } = useFormContext<TSecretSyncForm & { destination: SecretSync.Render }>();
const [{ autoRedeployServices }] = watch(["syncOptions"]);
return (
<div>
{autoRedeployServices ? (
<GenericFieldLabel label="Auto Redeploy Services">
<Badge variant="success">Enabled</Badge>
</GenericFieldLabel>
) : (
<GenericFieldLabel label="Auto Redeploy Services">
<Badge variant="danger">Disabled</Badge>
</GenericFieldLabel>
)}
</div>
);
};
export const RenderSyncReviewFields = () => {
const { watch } = useFormContext<TSecretSyncForm & { destination: SecretSync.Render }>();
const serviceName = watch("destinationConfig.serviceName");
@@ -35,7 +35,7 @@ import { HumanitecSyncReviewFields } from "./HumanitecSyncReviewFields";
import { OCIVaultSyncReviewFields } from "./OCIVaultSyncReviewFields";
import { OnePassSyncReviewFields } from "./OnePassSyncReviewFields";
import { RailwaySyncReviewFields } from "./RailwaySyncReviewFields";
import { RenderSyncReviewFields } from "./RenderSyncReviewFields";
import { RenderSyncOptionsReviewFields, RenderSyncReviewFields } from "./RenderSyncReviewFields";
import { SupabaseSyncReviewFields } from "./SupabaseSyncReviewFields";
import { TeamCitySyncReviewFields } from "./TeamCitySyncReviewFields";
import { TerraformCloudSyncReviewFields } from "./TerraformCloudSyncReviewFields";
@@ -121,6 +121,7 @@ export const SecretSyncReviewFields = () => {
break;
case SecretSync.Render:
DestinationFieldsComponent = <RenderSyncReviewFields />;
AdditionalSyncOptionsFieldsComponent = <RenderSyncOptionsReviewFields />;
break;
case SecretSync.Flyio:
DestinationFieldsComponent = <FlyioSyncReviewFields />;
@@ -2,9 +2,13 @@ import { z } from "zod";
import { BaseSecretSyncSchema } from "@app/components/secret-syncs/forms/schemas/base-secret-sync-schema";
import { SecretSync } from "@app/hooks/api/secretSyncs";
import { RenderSyncScope, RenderSyncType } from "@app/hooks/api/secretSyncs/render-sync";
import { RenderSyncScope, RenderSyncType } from "@app/hooks/api/secretSyncs/types/render-sync";
export const RenderSyncDestinationSchema = BaseSecretSyncSchema().merge(
export const RenderSyncDestinationSchema = BaseSecretSyncSchema(
z.object({
autoRedeployServices: z.boolean().optional()
})
).merge(
z.object({
destination: z.literal(SecretSync.Render),
destinationConfig: z.discriminatedUnion("scope", [
@@ -0,0 +1,26 @@
import { ReactNode } from "react";
type Props = {
title?: string;
body?: ReactNode;
};
export const AccessRestrictedBanner = ({
title = "Access Restricted",
body = (
<>
Your current role doesn&#39;t provide access to this feature.
<br /> Contact your administrator to request access.
</>
)
}: Props) => {
return (
<div className="flex items-center rounded-md border border-mineshaft-500 bg-gradient-to-br from-mineshaft-900 to-mineshaft-600 px-16 py-12 text-center text-bunker-300">
<div>
<div className="text-4xl font-medium text-bunker-100">{title}</div>
<div className="-mt-1 text-sm">{body}</div>
</div>
</div>
);
};
@@ -0,0 +1 @@
export * from "./AccessRestrictedBanner";
@@ -1,5 +1,5 @@
import { forwardRef, TextareaHTMLAttributes, useCallback, useMemo, useRef, useState } from "react";
import { faCircle, faFolder, faKey } from "@fortawesome/free-solid-svg-icons";
import { faFolder, faKey, faLayerGroup } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import * as Popover from "@radix-ui/react-popover";
@@ -79,7 +79,7 @@ export const InfisicalSecretInput = forwardRef<HTMLTextAreaElement, Props>(
const { currentWorkspace } = useWorkspace();
const workspaceId = currentWorkspace?.id || "";
const [debouncedValue] = useDebounce(value, 500);
const [debouncedValue] = useDebounce(value, 100);
const [highlightedIndex, setHighlightedIndex] = useState(-1);
@@ -142,7 +142,7 @@ export const InfisicalSecretInput = forwardRef<HTMLTextAreaElement, Props>(
const suggestions = useMemo(() => {
if (!isPopupOpen) return [];
// reset highlight whenever recomputation happens
setHighlightedIndex(-1);
setHighlightedIndex(0);
const suggestionsArr: ReferenceItem[] = [];
const predicate = suggestionSource.predicate.toLowerCase();
@@ -298,17 +298,21 @@ export const InfisicalSecretInput = forwardRef<HTMLTextAreaElement, Props>(
}}
>
<div
className="h-full w-full max-w-60 flex-col items-center justify-center rounded-md text-white"
className="h-full w-full flex-col items-center justify-center rounded-md text-white"
ref={popoverContentRef}
>
{suggestions.map((item, i) => {
let entryIcon;
let subText;
if (item.type === ReferenceType.SECRET) {
entryIcon = faKey;
entryIcon = <FontAwesomeIcon icon={faKey} className="text-bunker-300" />;
subText = "Secret";
} else if (item.type === ReferenceType.ENVIRONMENT) {
entryIcon = faCircle;
entryIcon = <FontAwesomeIcon icon={faLayerGroup} className="text-green-700" />;
subText = "Environment";
} else {
entryIcon = faFolder;
entryIcon = <FontAwesomeIcon icon={faFolder} className="text-yellow-700" />;
subText = "Folder";
}
return (
@@ -327,22 +331,22 @@ export const InfisicalSecretInput = forwardRef<HTMLTextAreaElement, Props>(
}}
onMouseEnter={() => setHighlightedIndex(i)}
style={{ pointerEvents: "auto" }}
className="flex items-center justify-between border-mineshaft-600 text-left"
className="flex w-full items-center justify-between border-mineshaft-600 text-left"
key={`secret-reference-secret-${i + 1}`}
>
<div
className={`${
highlightedIndex === i ? "bg-gray-600" : ""
} text-md relative mb-0.5 flex w-full cursor-pointer select-none items-center justify-between rounded-md px-2 py-2 outline-none transition-all hover:bg-mineshaft-500 data-[highlighted]:bg-mineshaft-500`}
highlightedIndex === i ? "bg-mineshaft-500" : ""
} text-md relative flex w-full cursor-pointer select-none items-center justify-between px-2 py-2 outline-none transition-all hover:bg-mineshaft-700 data-[highlighted]:bg-mineshaft-700`}
>
<div className="flex w-full gap-2">
<div className="flex items-center text-yellow-700">
<FontAwesomeIcon
icon={entryIcon}
size={item.type === ReferenceType.ENVIRONMENT ? "xs" : "1x"}
/>
<div className="flex w-full items-start gap-2">
<div className="mt-1 flex items-center">{entryIcon}</div>
<div className="text-md w-10/12 truncate text-left">
{item.label}
<div className="mb-[0.1rem] text-xs leading-3 text-bunker-400">
{subText}
</div>
</div>
<div className="text-md w-10/12 truncate text-left">{item.label}</div>
</div>
</div>
</div>
@@ -20,6 +20,7 @@ type Props = {
isMulti?: boolean;
iconClassName?: string;
dropdownContainerStyle?: React.CSSProperties;
side?: SelectPrimitive.SelectContentProps["side"];
};
export type SelectProps = Omit<SelectPrimitive.SelectProps, "disabled"> & Props;
@@ -37,6 +38,7 @@ export const Select = forwardRef<HTMLButtonElement, SelectProps>(
containerClassName,
iconClassName,
dropdownContainerStyle,
side,
...props
},
ref
@@ -78,6 +80,7 @@ export const Select = forwardRef<HTMLButtonElement, SelectProps>(
</SelectPrimitive.Trigger>
<SelectPrimitive.Portal>
<SelectPrimitive.Content
side={side}
className={twMerge(
"relative top-1 z-[100] max-w-sm overflow-hidden rounded-md border border-mineshaft-600 bg-mineshaft-900 font-inter text-bunker-100 shadow-md",
position === "popper" && "max-h-72",
+1
View File
@@ -1,4 +1,5 @@
/* eslint-disable react-refresh/only-export-components */
export * from "./AccessRestrictedBanner";
export * from "./Accordion";
export * from "./Alert";
export * from "./Badge";