diff --git a/backend/src/ee/services/permission/permission-types.ts b/backend/src/ee/services/permission/permission-types.ts
index 60fcbec85..8df85054d 100644
--- a/backend/src/ee/services/permission/permission-types.ts
+++ b/backend/src/ee/services/permission/permission-types.ts
@@ -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)
diff --git a/frontend/src/components/permissions/GlobPermissionInfo.tsx b/frontend/src/components/permissions/GlobPermissionInfo.tsx
new file mode 100644
index 000000000..07ba42db3
--- /dev/null
+++ b/frontend/src/components/permissions/GlobPermissionInfo.tsx
@@ -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 (
+
+
A glob pattern uses wildcards to match file names or paths.
+
+
+ setPattern(e.target.value)} />
+
+
+
+
+ setText(e.target.value)} />
+
+
+
+ );
+};
diff --git a/frontend/src/components/permissions/index.tsx b/frontend/src/components/permissions/index.tsx
index 8d523c311..5103b0f73 100644
--- a/frontend/src/components/permissions/index.tsx
+++ b/frontend/src/components/permissions/index.tsx
@@ -1,3 +1,4 @@
+export { GlobPermissionInfo } from "./GlobPermissionInfo";
export { OrgPermissionCan } from "./OrgPermissionCan";
export { PermissionDeniedBanner } from "./PermissionDeniedBanner";
export { ProjectPermissionCan } from "./ProjectPermissionCan";
diff --git a/frontend/src/views/Project/RolePage/components/RolePermissionsSection/components/GeneralPermissionConditions.tsx b/frontend/src/views/Project/RolePage/components/RolePermissionsSection/components/GeneralPermissionConditions.tsx
index da4dd72eb..989b7c19b 100644
--- a/frontend/src/views/Project/RolePage/components/RolePermissionsSection/components/GeneralPermissionConditions.tsx
+++ b/frontend/src/views/Project/RolePage/components/RolePermissionsSection/components/GeneralPermissionConditions.tsx
@@ -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 }:
{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 (
-
+
(
field.onChange(e)}
className="w-full"
>
- Equal
- Not Equal
-
- Glob Match
-
- Contains
+ {renderOperatorSelectItems(condition.lhs)}
)}
/>
+
+
+
+
+
-
+
)}
/>
diff --git a/frontend/src/views/Project/RolePage/components/RolePermissionsSection/components/PermissionConditionHelpers.tsx b/frontend/src/views/Project/RolePage/components/RolePermissionsSection/components/PermissionConditionHelpers.tsx
new file mode 100644
index 000000000..21fad117a
--- /dev/null
+++ b/frontend/src/views/Project/RolePage/components/RolePermissionsSection/components/PermissionConditionHelpers.tsx
@@ -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 ;
+ default:
+ return "";
+ }
+};
+
+export const renderOperatorSelectItems = (type: string) => {
+ if (type === "secretTags") {
+ return Contains;
+ }
+ return (
+ <>
+ Equal
+ Not Equal
+ Glob Match
+ Contains
+ >
+ );
+};
diff --git a/frontend/src/views/Project/RolePage/components/RolePermissionsSection/components/SecretPermissionConditions.tsx b/frontend/src/views/Project/RolePage/components/RolePermissionsSection/components/SecretPermissionConditions.tsx
index a6f94de90..e0e5aa471 100644
--- a/frontend/src/views/Project/RolePage/components/RolePermissionsSection/components/SecretPermissionConditions.tsx
+++ b/frontend/src/views/Project/RolePage/components/RolePermissionsSection/components/SecretPermissionConditions.tsx
@@ -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)
{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 (
-
+
field.onChange(e)}
className="w-full"
>
- Equal
- Not Equal
-
- Glob Match
-
- Contains
+ {renderOperatorSelectItems(condition.lhs)}
)}
/>
+
+
+
+
+
-
+
)}
/>
@@ -126,7 +142,6 @@ export const SecretPermissionConditions = ({ position = 0, isDisabled }: Props)
{errors?.permissions?.secrets?.[position]?.conditions?.message}
)}
-
{}
}