mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +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 = {
|
||||
[PermissionConditionOperators.$IN]: z.string().min(1).array(),
|
||||
[PermissionConditionOperators.$ALL]: z.string().min(1).array(),
|
||||
[PermissionConditionOperators.$IN]: z.string().trim().min(1).array(),
|
||||
[PermissionConditionOperators.$ALL]: z.string().trim().min(1).array(),
|
||||
[PermissionConditionOperators.$REGEX]: z
|
||||
.string()
|
||||
.min(1)
|
||||
|
||||
33
frontend/src/components/permissions/GlobPermissionInfo.tsx
Normal file
33
frontend/src/components/permissions/GlobPermissionInfo.tsx
Normal file
@@ -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 { PermissionDeniedBanner } from "./PermissionDeniedBanner";
|
||||
export { ProjectPermissionCan } from "./ProjectPermissionCan";
|
||||
|
||||
@@ -1,14 +1,26 @@
|
||||
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 { Button, FormControl, IconButton, Input, Select, SelectItem } from "@app/components/v2";
|
||||
import {
|
||||
Button,
|
||||
FormControl,
|
||||
IconButton,
|
||||
Input,
|
||||
Select,
|
||||
SelectItem,
|
||||
Tooltip
|
||||
} from "@app/components/v2";
|
||||
import {
|
||||
PermissionConditionOperators,
|
||||
ProjectPermissionSub
|
||||
} from "@app/context/ProjectPermissionContext/types";
|
||||
|
||||
import { TFormSchema } from "../ProjectRoleModifySection.utils";
|
||||
import {
|
||||
getConditionOperatorHelperInfo,
|
||||
renderOperatorSelectItems
|
||||
} from "./PermissionConditionHelpers";
|
||||
|
||||
type Props = {
|
||||
position?: number;
|
||||
@@ -19,12 +31,6 @@ type Props = {
|
||||
| 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) => {
|
||||
const {
|
||||
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-2 flex flex-col space-y-2">
|
||||
{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 (
|
||||
<div
|
||||
key={el.id}
|
||||
@@ -69,10 +79,10 @@ export const GeneralPermissionConditions = ({ position = 0, isDisabled, type }:
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-36">
|
||||
<div className="flex w-36 items-center space-x-2">
|
||||
<Controller
|
||||
control={control}
|
||||
name={`permissions.${type}.${position}.conditions.${index}.operator`}
|
||||
name={`permissions.secrets.${position}.conditions.${index}.operator`}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
isError={Boolean(error?.message)}
|
||||
@@ -85,16 +95,22 @@ export const GeneralPermissionConditions = ({ position = 0, isDisabled, type }:
|
||||
onValueChange={(e) => field.onChange(e)}
|
||||
className="w-full"
|
||||
>
|
||||
<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>
|
||||
{renderOperatorSelectItems(condition.lhs)}
|
||||
</Select>
|
||||
</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 className="flex-grow">
|
||||
<Controller
|
||||
@@ -106,7 +122,7 @@ export const GeneralPermissionConditions = ({ position = 0, isDisabled, type }:
|
||||
errorText={error?.message}
|
||||
className="mb-0 flex-grow"
|
||||
>
|
||||
<Input {...field} placeholder={getValueLabel(lhs)} />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,23 +1,29 @@
|
||||
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 { 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 { TFormSchema } from "../ProjectRoleModifySection.utils";
|
||||
import {
|
||||
getConditionOperatorHelperInfo,
|
||||
renderOperatorSelectItems
|
||||
} from "./PermissionConditionHelpers";
|
||||
|
||||
type Props = {
|
||||
position?: number;
|
||||
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) => {
|
||||
const {
|
||||
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-2 flex flex-col space-y-2">
|
||||
{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 (
|
||||
<div
|
||||
key={el.id}
|
||||
@@ -64,7 +74,7 @@ export const SecretPermissionConditions = ({ position = 0, isDisabled }: Props)
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-36">
|
||||
<div className="flex w-36 items-center space-x-2">
|
||||
<Controller
|
||||
control={control}
|
||||
name={`permissions.secrets.${position}.conditions.${index}.operator`}
|
||||
@@ -80,16 +90,22 @@ export const SecretPermissionConditions = ({ position = 0, isDisabled }: Props)
|
||||
onValueChange={(e) => field.onChange(e)}
|
||||
className="w-full"
|
||||
>
|
||||
<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>
|
||||
{renderOperatorSelectItems(condition.lhs)}
|
||||
</Select>
|
||||
</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 className="flex-grow">
|
||||
<Controller
|
||||
@@ -101,7 +117,7 @@ export const SecretPermissionConditions = ({ position = 0, isDisabled }: Props)
|
||||
errorText={error?.message}
|
||||
className="mb-0 flex-grow"
|
||||
>
|
||||
<Input {...field} placeholder={getValueLabel(lhs)} />
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
@@ -126,7 +142,6 @@ export const SecretPermissionConditions = ({ position = 0, isDisabled }: Props)
|
||||
<span>{errors?.permissions?.secrets?.[position]?.conditions?.message}</span>
|
||||
</div>
|
||||
)}
|
||||
<div>{}</div>
|
||||
<div>
|
||||
<Button
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
|
||||
Reference in New Issue
Block a user