mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 16:27:40 +00:00
feat: added helper text for operators and improved rendering of selective operators
This commit is contained in:
@@ -11,8 +11,8 @@ export enum PermissionConditionOperators {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const PermissionConditionSchema = {
|
export const PermissionConditionSchema = {
|
||||||
[PermissionConditionOperators.$IN]: z.string().min(1).array(),
|
[PermissionConditionOperators.$IN]: z.string().trim().min(1).array(),
|
||||||
[PermissionConditionOperators.$ALL]: z.string().min(1).array(),
|
[PermissionConditionOperators.$ALL]: z.string().trim().min(1).array(),
|
||||||
[PermissionConditionOperators.$REGEX]: z
|
[PermissionConditionOperators.$REGEX]: z
|
||||||
.string()
|
.string()
|
||||||
.min(1)
|
.min(1)
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import picomatch from "picomatch";
|
||||||
|
|
||||||
|
import { FormControl } from "../v2/FormControl";
|
||||||
|
import { Input } from "../v2/Input";
|
||||||
|
|
||||||
|
export const GlobPermissionInfo = () => {
|
||||||
|
const [pattern, setPattern] = useState("");
|
||||||
|
const [text, setText] = useState("");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="mt-2">A glob pattern uses wildcards to match file names or paths.</div>
|
||||||
|
<div>
|
||||||
|
<FormControl label="Glob pattern" helperText="Examples: /{a,b}, DB_**">
|
||||||
|
<Input value={pattern} onChange={(e) => setPattern(e.target.value)} />
|
||||||
|
</FormControl>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<FormControl
|
||||||
|
label="Test string"
|
||||||
|
helperText="Type value to test"
|
||||||
|
isError={
|
||||||
|
pattern && text ? !picomatch.isMatch(text, pattern, { strictSlashes: false }) : false
|
||||||
|
}
|
||||||
|
errorText="Invalid"
|
||||||
|
>
|
||||||
|
<Input value={text} onChange={(e) => setText(e.target.value)} />
|
||||||
|
</FormControl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
export { GlobPermissionInfo } from "./GlobPermissionInfo";
|
||||||
export { OrgPermissionCan } from "./OrgPermissionCan";
|
export { OrgPermissionCan } from "./OrgPermissionCan";
|
||||||
export { PermissionDeniedBanner } from "./PermissionDeniedBanner";
|
export { PermissionDeniedBanner } from "./PermissionDeniedBanner";
|
||||||
export { ProjectPermissionCan } from "./ProjectPermissionCan";
|
export { ProjectPermissionCan } from "./ProjectPermissionCan";
|
||||||
|
|||||||
+34
-18
@@ -1,14 +1,26 @@
|
|||||||
import { Controller, useFieldArray, useFormContext } from "react-hook-form";
|
import { Controller, useFieldArray, useFormContext } from "react-hook-form";
|
||||||
import { faPlus, faTrash, faWarning } from "@fortawesome/free-solid-svg-icons";
|
import { faInfoCircle, faPlus, faTrash, faWarning } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
import { Button, FormControl, IconButton, Input, Select, SelectItem } from "@app/components/v2";
|
import {
|
||||||
|
Button,
|
||||||
|
FormControl,
|
||||||
|
IconButton,
|
||||||
|
Input,
|
||||||
|
Select,
|
||||||
|
SelectItem,
|
||||||
|
Tooltip
|
||||||
|
} from "@app/components/v2";
|
||||||
import {
|
import {
|
||||||
PermissionConditionOperators,
|
PermissionConditionOperators,
|
||||||
ProjectPermissionSub
|
ProjectPermissionSub
|
||||||
} from "@app/context/ProjectPermissionContext/types";
|
} from "@app/context/ProjectPermissionContext/types";
|
||||||
|
|
||||||
import { TFormSchema } from "../ProjectRoleModifySection.utils";
|
import { TFormSchema } from "../ProjectRoleModifySection.utils";
|
||||||
|
import {
|
||||||
|
getConditionOperatorHelperInfo,
|
||||||
|
renderOperatorSelectItems
|
||||||
|
} from "./PermissionConditionHelpers";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
position?: number;
|
position?: number;
|
||||||
@@ -19,12 +31,6 @@ type Props = {
|
|||||||
| ProjectPermissionSub.SecretImports;
|
| ProjectPermissionSub.SecretImports;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getValueLabel = (type: string) => {
|
|
||||||
if (type === "environment") return "Environment slug";
|
|
||||||
if (type === "secretPath") return "Folder path";
|
|
||||||
return "";
|
|
||||||
};
|
|
||||||
|
|
||||||
export const GeneralPermissionConditions = ({ position = 0, isDisabled, type }: Props) => {
|
export const GeneralPermissionConditions = ({ position = 0, isDisabled, type }: Props) => {
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
@@ -40,7 +46,11 @@ export const GeneralPermissionConditions = ({ position = 0, isDisabled, type }:
|
|||||||
<div className="mt-6 border-t border-t-gray-800 bg-mineshaft-800 pt-2">
|
<div className="mt-6 border-t border-t-gray-800 bg-mineshaft-800 pt-2">
|
||||||
<div className="mt-2 flex flex-col space-y-2">
|
<div className="mt-2 flex flex-col space-y-2">
|
||||||
{items.fields.map((el, index) => {
|
{items.fields.map((el, index) => {
|
||||||
const lhs = watch(`permissions.${type}.${position}.conditions.${index}.lhs`);
|
const condition = watch(`permissions.secrets.${position}.conditions.${index}`) as {
|
||||||
|
lhs: string;
|
||||||
|
rhs: string;
|
||||||
|
operator: string;
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={el.id}
|
key={el.id}
|
||||||
@@ -69,10 +79,10 @@ export const GeneralPermissionConditions = ({ position = 0, isDisabled, type }:
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-36">
|
<div className="flex w-36 items-center space-x-2">
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name={`permissions.${type}.${position}.conditions.${index}.operator`}
|
name={`permissions.secrets.${position}.conditions.${index}.operator`}
|
||||||
render={({ field, fieldState: { error } }) => (
|
render={({ field, fieldState: { error } }) => (
|
||||||
<FormControl
|
<FormControl
|
||||||
isError={Boolean(error?.message)}
|
isError={Boolean(error?.message)}
|
||||||
@@ -85,16 +95,22 @@ export const GeneralPermissionConditions = ({ position = 0, isDisabled, type }:
|
|||||||
onValueChange={(e) => field.onChange(e)}
|
onValueChange={(e) => field.onChange(e)}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
>
|
>
|
||||||
<SelectItem value={PermissionConditionOperators.$EQ}>Equal</SelectItem>
|
{renderOperatorSelectItems(condition.lhs)}
|
||||||
<SelectItem value={PermissionConditionOperators.$NEQ}>Not Equal</SelectItem>
|
|
||||||
<SelectItem value={PermissionConditionOperators.$GLOB}>
|
|
||||||
Glob Match
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value={PermissionConditionOperators.$IN}>Contains</SelectItem>
|
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<div>
|
||||||
|
<Tooltip
|
||||||
|
asChild
|
||||||
|
content={getConditionOperatorHelperInfo(
|
||||||
|
condition.operator as PermissionConditionOperators
|
||||||
|
)}
|
||||||
|
className="max-w-xs"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faInfoCircle} size="xs" className="text-gray-400" />
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-grow">
|
<div className="flex-grow">
|
||||||
<Controller
|
<Controller
|
||||||
@@ -106,7 +122,7 @@ export const GeneralPermissionConditions = ({ position = 0, isDisabled, type }:
|
|||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
className="mb-0 flex-grow"
|
className="mb-0 flex-grow"
|
||||||
>
|
>
|
||||||
<Input {...field} placeholder={getValueLabel(lhs)} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
import { GlobPermissionInfo } from "@app/components/permissions";
|
||||||
|
import { SelectItem } from "@app/components/v2";
|
||||||
|
import { PermissionConditionOperators } from "@app/context/ProjectPermissionContext/types";
|
||||||
|
|
||||||
|
export const getConditionOperatorHelperInfo = (type: PermissionConditionOperators) => {
|
||||||
|
switch (type) {
|
||||||
|
case PermissionConditionOperators.$EQ:
|
||||||
|
return "Value should equal specified value.";
|
||||||
|
case PermissionConditionOperators.$NEQ:
|
||||||
|
return "Value should not equal specified value.";
|
||||||
|
case PermissionConditionOperators.$IN:
|
||||||
|
return "List of comma-separated values that match a given value.";
|
||||||
|
case PermissionConditionOperators.$GLOB:
|
||||||
|
return <GlobPermissionInfo />;
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const renderOperatorSelectItems = (type: string) => {
|
||||||
|
if (type === "secretTags") {
|
||||||
|
return <SelectItem value={PermissionConditionOperators.$IN}>Contains</SelectItem>;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SelectItem value={PermissionConditionOperators.$EQ}>Equal</SelectItem>
|
||||||
|
<SelectItem value={PermissionConditionOperators.$NEQ}>Not Equal</SelectItem>
|
||||||
|
<SelectItem value={PermissionConditionOperators.$GLOB}>Glob Match</SelectItem>
|
||||||
|
<SelectItem value={PermissionConditionOperators.$IN}>Contains</SelectItem>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
+33
-18
@@ -1,23 +1,29 @@
|
|||||||
import { Controller, useFieldArray, useFormContext } from "react-hook-form";
|
import { Controller, useFieldArray, useFormContext } from "react-hook-form";
|
||||||
import { faPlus, faTrash, faWarning } from "@fortawesome/free-solid-svg-icons";
|
import { faInfoCircle, faPlus, faTrash, faWarning } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
import { Button, FormControl, IconButton, Input, Select, SelectItem } from "@app/components/v2";
|
import {
|
||||||
|
Button,
|
||||||
|
FormControl,
|
||||||
|
IconButton,
|
||||||
|
Input,
|
||||||
|
Select,
|
||||||
|
SelectItem,
|
||||||
|
Tooltip
|
||||||
|
} from "@app/components/v2";
|
||||||
import { PermissionConditionOperators } from "@app/context/ProjectPermissionContext/types";
|
import { PermissionConditionOperators } from "@app/context/ProjectPermissionContext/types";
|
||||||
|
|
||||||
import { TFormSchema } from "../ProjectRoleModifySection.utils";
|
import { TFormSchema } from "../ProjectRoleModifySection.utils";
|
||||||
|
import {
|
||||||
|
getConditionOperatorHelperInfo,
|
||||||
|
renderOperatorSelectItems
|
||||||
|
} from "./PermissionConditionHelpers";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
position?: number;
|
position?: number;
|
||||||
isDisabled?: boolean;
|
isDisabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getValueLabel = (type: string) => {
|
|
||||||
if (type === "environment") return "Environment slug";
|
|
||||||
if (type === "secretPath") return "Folder path";
|
|
||||||
return "";
|
|
||||||
};
|
|
||||||
|
|
||||||
export const SecretPermissionConditions = ({ position = 0, isDisabled }: Props) => {
|
export const SecretPermissionConditions = ({ position = 0, isDisabled }: Props) => {
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
@@ -33,7 +39,11 @@ export const SecretPermissionConditions = ({ position = 0, isDisabled }: Props)
|
|||||||
<div className="mt-6 border-t border-t-gray-800 bg-mineshaft-800 pt-2">
|
<div className="mt-6 border-t border-t-gray-800 bg-mineshaft-800 pt-2">
|
||||||
<div className="mt-2 flex flex-col space-y-2">
|
<div className="mt-2 flex flex-col space-y-2">
|
||||||
{items.fields.map((el, index) => {
|
{items.fields.map((el, index) => {
|
||||||
const lhs = watch(`permissions.secrets.${position}.conditions.${index}.lhs`);
|
const condition = watch(`permissions.secrets.${position}.conditions.${index}`) as {
|
||||||
|
lhs: string;
|
||||||
|
rhs: string;
|
||||||
|
operator: string;
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={el.id}
|
key={el.id}
|
||||||
@@ -64,7 +74,7 @@ export const SecretPermissionConditions = ({ position = 0, isDisabled }: Props)
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-36">
|
<div className="flex w-36 items-center space-x-2">
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name={`permissions.secrets.${position}.conditions.${index}.operator`}
|
name={`permissions.secrets.${position}.conditions.${index}.operator`}
|
||||||
@@ -80,16 +90,22 @@ export const SecretPermissionConditions = ({ position = 0, isDisabled }: Props)
|
|||||||
onValueChange={(e) => field.onChange(e)}
|
onValueChange={(e) => field.onChange(e)}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
>
|
>
|
||||||
<SelectItem value={PermissionConditionOperators.$EQ}>Equal</SelectItem>
|
{renderOperatorSelectItems(condition.lhs)}
|
||||||
<SelectItem value={PermissionConditionOperators.$NEQ}>Not Equal</SelectItem>
|
|
||||||
<SelectItem value={PermissionConditionOperators.$GLOB}>
|
|
||||||
Glob Match
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value={PermissionConditionOperators.$IN}>Contains</SelectItem>
|
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<div>
|
||||||
|
<Tooltip
|
||||||
|
asChild
|
||||||
|
content={getConditionOperatorHelperInfo(
|
||||||
|
condition.operator as PermissionConditionOperators
|
||||||
|
)}
|
||||||
|
className="max-w-xs"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faInfoCircle} size="xs" className="text-gray-400" />
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-grow">
|
<div className="flex-grow">
|
||||||
<Controller
|
<Controller
|
||||||
@@ -101,7 +117,7 @@ export const SecretPermissionConditions = ({ position = 0, isDisabled }: Props)
|
|||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
className="mb-0 flex-grow"
|
className="mb-0 flex-grow"
|
||||||
>
|
>
|
||||||
<Input {...field} placeholder={getValueLabel(lhs)} />
|
<Input {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -126,7 +142,6 @@ export const SecretPermissionConditions = ({ position = 0, isDisabled }: Props)
|
|||||||
<span>{errors?.permissions?.secrets?.[position]?.conditions?.message}</span>
|
<span>{errors?.permissions?.secrets?.[position]?.conditions?.message}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div>{}</div>
|
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
|
|||||||
Reference in New Issue
Block a user