diff --git a/.github/workflows/forms-flow-admin-cd.yml b/.github/workflows/forms-flow-admin-cd.yml
index d99fd6bc9..31ec73cb9 100644
--- a/.github/workflows/forms-flow-admin-cd.yml
+++ b/.github/workflows/forms-flow-admin-cd.yml
@@ -6,6 +6,7 @@ on:
- main
- develop
- release/*
+ - feature/FWF-3316-permission-matrix
paths:
- "forms-flow-admin/**"
- "VERSION"
diff --git a/.github/workflows/forms-flow-integration-cd.yml b/.github/workflows/forms-flow-integration-cd.yml
index 92e8da83a..53f8ddcb6 100644
--- a/.github/workflows/forms-flow-integration-cd.yml
+++ b/.github/workflows/forms-flow-integration-cd.yml
@@ -6,6 +6,7 @@ on:
- main
- develop
- release/*
+ - feature/FWF-3316-permission-matrix
paths:
- "forms-flow-integration/**"
- "VERSION"
diff --git a/.github/workflows/forms-flow-nav.cd.yml b/.github/workflows/forms-flow-nav.cd.yml
index d157d4fa0..c9ad5b775 100644
--- a/.github/workflows/forms-flow-nav.cd.yml
+++ b/.github/workflows/forms-flow-nav.cd.yml
@@ -6,6 +6,7 @@ on:
- main
- develop
- release/*
+ - feature/FWF-3316-permission-matrix
paths:
- "forms-flow-nav/**"
- "VERSION"
diff --git a/.github/workflows/forms-flow-service.yml b/.github/workflows/forms-flow-service.yml
index 9bf4596a3..8f6ede2a8 100644
--- a/.github/workflows/forms-flow-service.yml
+++ b/.github/workflows/forms-flow-service.yml
@@ -6,6 +6,7 @@ on:
- main
- develop
- release/*
+ - feature/FWF-3316-permission-matrix
paths:
- "forms-flow-service/**"
- "VERSION"
diff --git a/.github/workflows/forms-flow-theme.yml b/.github/workflows/forms-flow-theme.yml
index 33037713e..6f5dfa948 100644
--- a/.github/workflows/forms-flow-theme.yml
+++ b/.github/workflows/forms-flow-theme.yml
@@ -6,6 +6,7 @@ on:
- main
- develop
- release/*
+ - feature/FWF-3316-permission-matrix
paths:
- "forms-flow-theme/**"
- "VERSION"
diff --git a/VERSION b/VERSION
index fb5d1e208..07abcadc7 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-v6.1.0-alpha
+v6.1.0-rbac-alpha
diff --git a/forms-flow-admin/src/components/AccessDenied/AccessDenied.js b/forms-flow-admin/src/components/AccessDenied/AccessDenied.js
new file mode 100644
index 000000000..b0478f8c9
--- /dev/null
+++ b/forms-flow-admin/src/components/AccessDenied/AccessDenied.js
@@ -0,0 +1,46 @@
+// AccessDeniedIcon.js
+import React from 'react';
+
+const AccessDeniedIcon = () => (
+
+
+);
+
+export default AccessDeniedIcon;
diff --git a/forms-flow-admin/src/components/AccessDenied/accessDenied.scss b/forms-flow-admin/src/components/AccessDenied/accessDenied.scss
new file mode 100644
index 000000000..aac677799
--- /dev/null
+++ b/forms-flow-admin/src/components/AccessDenied/accessDenied.scss
@@ -0,0 +1,12 @@
+.access-denied-text {
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 2rem;
+ color: var(--ff-black);
+ opacity: 1;
+ }
+ .access-denied{
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 1rem;
+ color: var(--ff-black);
+ opacity: 1;
+ }
\ No newline at end of file
diff --git a/forms-flow-admin/src/components/AccessDenied/index.js b/forms-flow-admin/src/components/AccessDenied/index.js
new file mode 100644
index 000000000..fa0e4a3c1
--- /dev/null
+++ b/forms-flow-admin/src/components/AccessDenied/index.js
@@ -0,0 +1,45 @@
+import React from "react";
+import AccessDeniedIcon from "./AccessDenied.js";
+import './accessDenied.scss';
+import { useTranslation } from "react-i18next";
+import { BASE_ROUTE } from "../../constants/index";
+import { useHistory } from "react-router-dom";
+
+
+const AccessDenied = ({ userRoles }) => {
+ const { t } = useTranslation();
+ const history = useHistory();
+
+ const handleLogout = () => {
+ const kcInstance = kcServiceInstance();
+ kcInstance.userLogout();
+ };
+
+ const handleReturn = () => {
+ history.push(BASE_ROUTE);
+ };
+
+ const showReturnToLogin = userRoles?.length === 0;
+ const showReturnToHome = userRoles?.length > 0;
+
+ return (
+
+
+
{t("Access Denied")}
+
{t("You don't have permission to access this page.")}
+
{t("Please contact your administrator or try again later.")}
+ {showReturnToLogin && (
+
+ )}
+ {showReturnToHome && (
+
+ )}
+
+ );
+};
+
+export default AccessDenied;
diff --git a/forms-flow-admin/src/components/roles/roles.tsx b/forms-flow-admin/src/components/roles/roles.tsx
index eb86e468d..5f62c3cda 100644
--- a/forms-flow-admin/src/components/roles/roles.tsx
+++ b/forms-flow-admin/src/components/roles/roles.tsx
@@ -7,7 +7,12 @@ import paginationFactory from "react-bootstrap-table2-paginator";
import Form from "react-bootstrap/Form";
import Button from "react-bootstrap/Button";
import { fetchUsers } from "../../services/users";
-import { CreateRole, DeleteRole, UpdateRole } from "../../services/roles";
+import {
+ CreateRole,
+ DeleteRole,
+ UpdateRole,
+ fetchPermissions,
+} from "../../services/roles";
import Modal from "react-bootstrap/Modal";
import Loading from "../loading";
import DropdownButton from "react-bootstrap/DropdownButton";
@@ -21,6 +26,7 @@ import {
} from "../../constants";
import { DEFAULT_ROLES } from "../../constants";
+import {removingTenantId} from "../../utils/utils.js";
const Roles = React.memo((props: any) => {
const { t } = useTranslation();
const { tenantId } = useParams();
@@ -36,13 +42,18 @@ const Roles = React.memo((props: any) => {
const [showRoleModal, setShowRoleModal] = useState(false);
const [showEditRoleModal, setShowEditRoleModal] = useState(false);
const [loading, setLoading] = React.useState(false);
- const [payload, setPayload] = React.useState({ name: "", description: "" });
+ const [payload, setPayload] = React.useState({
+ name: "",
+ description: "",
+ permissions: [],
+ });
// Toggle for Delete Confirm modal
const [showConfirmDelete, setShowConfirmDelete] = React.useState(false);
const initialRoleType = {
name: "",
id: "",
description: "",
+ permissions: [],
};
const [deleteCandidate, setDeleteCandidate] = React.useState(initialRoleType);
const [selectedRoleIdentifier, setSelectedRoleIdentifier] =
@@ -50,30 +61,60 @@ const Roles = React.memo((props: any) => {
const [editCandidate, setEditCandidate] = React.useState(initialRoleType);
const [disabled, setDisabled] = React.useState(true);
const [search, setSerach] = React.useState("");
+ const [permission, setPermission] = React.useState([]);
const filterList = (filterTerm, List) => {
- let roleList = List.filter((role) => {
+ let roleList = removingTenantId(List,tenantId);
+ let newRoleList = roleList.filter((role) => {
return role.name.toLowerCase().search(filterTerm.toLowerCase()) !== -1;
});
- return roleList;
+ return newRoleList;
};
React.useEffect(() => {
- setDisabled(!(payload.name?.trim() && payload.description?.trim()));
+ setDisabled(
+ !(
+ payload.name?.trim() &&
+ payload.description?.trim() &&
+ payload.permissions.length !== 0
+ )
+ );
}, [payload]);
React.useEffect(() => {
setDisabled(
- !(editCandidate.name?.trim() && editCandidate.description?.trim())
+ !(
+ editCandidate.name?.trim() &&
+ editCandidate.description?.trim() &&
+ editCandidate.permissions.length !== 0
+ )
);
}, [editCandidate]);
React.useEffect(() => {
+ let updatedRoles = props.roles;
+
if (search) {
- return setRoles(filterList(search, props.roles));
+ updatedRoles = filterList(search, updatedRoles);
}
- setRoles(props.roles);
- }, [props.roles]);
+
+ if (updatedRoles.length > 0) {
+ updatedRoles = removingTenantId(updatedRoles,tenantId);
+ }
+
+ setRoles(updatedRoles);
+ }, [props.roles, search]);
+
+ React.useEffect(() => {
+ fetchPermissions(
+ (data) => {
+ setPermission(data);
+ },
+ (err) => {
+ setError(err);
+ }
+ );
+ }, []);
const handlFilter = (e) => {
setSerach(e.target.value);
@@ -103,9 +144,39 @@ const Roles = React.memo((props: any) => {
const handleChangeDescription = (e) => {
setPayload({ ...payload, description: e.target.value });
};
+ const handlePermissionCheck = (
+ permissionName: string,
+ dependsOn: string[]
+ ) => {
+ let updatedPermissions: string[] = [...payload.permissions];
+ const isChecked = updatedPermissions.includes(permissionName);
+
+ if (!isChecked) {
+ updatedPermissions.push(permissionName);
+ dependsOn.forEach((dependency) => {
+ if (!updatedPermissions.includes(dependency)) {
+ updatedPermissions.push(dependency);
+ }
+ });
+ } else {
+ updatedPermissions = updatedPermissions.filter(
+ (permission) => permission !== permissionName
+ );
+ dependsOn.forEach((dependency) => {
+ updatedPermissions = updatedPermissions.filter(
+ (permission) => permission !== dependency
+ );
+ });
+ }
+ setPayload({ ...payload, permissions: updatedPermissions });
+ };
const validateRolePayload = (payload) => {
- return !(payload.name === "" || payload.description === "");
+ return !(
+ payload.name === "" ||
+ payload.description === "" ||
+ payload.permissions.length === 0
+ );
};
//check regex exept _ -
const hasSpecialCharacters = (text) => {
@@ -121,21 +192,21 @@ const Roles = React.memo((props: any) => {
if (!validateRolePayload(payload)) {
return;
}
- if (KEYCLOAK_ENABLE_CLIENT_AUTH) {
- if (hasSpecialCharacters(payload.name)) {
- toast.error(
- t("Role names cannot contain special characters except _ , -")
- );
- return;
- }
- } else {
- if (hasSpecialCharacterswithslash(payload.name)) {
- toast.error(
- t("Role names cannot contain special characters except _ , - , / ")
- );
- return;
- }
+ // if (KEYCLOAK_ENABLE_CLIENT_AUTH) {
+ // if (hasSpecialCharacters(payload.name)) {
+ // toast.error(
+ // t("Role names cannot contain special characters except _ , -")
+ // );
+ // return;
+ // }
+ // } else {
+ if (hasSpecialCharacterswithslash(payload.name)) {
+ toast.error(
+ t("Role names cannot contain special characters except _ , - , / ")
+ );
+ return;
}
+ // }
setDisabled(true);
CreateRole(
payload,
@@ -155,21 +226,21 @@ const Roles = React.memo((props: any) => {
if (!validateRolePayload(editCandidate)) {
return;
}
- if (KEYCLOAK_ENABLE_CLIENT_AUTH) {
- if (hasSpecialCharacters(editCandidate.name)) {
- toast.error(
- t("Role names cannot contain special characters except _ , -")
- );
- return;
- }
- } else {
- if (hasSpecialCharacterswithslash(editCandidate.name)) {
- toast.error(
- t("Role names cannot contain special characters except _ , - , / ")
- );
- return;
- }
+ // if (KEYCLOAK_ENABLE_CLIENT_AUTH) {
+ // if (hasSpecialCharacters(editCandidate.name)) {
+ // toast.error(
+ // t("Role names cannot contain special characters except _ , -")
+ // );
+ // return;
+ // }
+ // } else {
+ if (hasSpecialCharacterswithslash(editCandidate.name)) {
+ toast.error(
+ t("Role names cannot contain special characters except _ , - , / ")
+ );
+ return;
}
+ // }
setDisabled(true);
UpdateRole(
selectedRoleIdentifier,
@@ -186,7 +257,6 @@ const Roles = React.memo((props: any) => {
}
);
};
-
// handlers for user list popover
const handleClick = (event, rowData) => {
setShow(!show);
@@ -216,10 +286,37 @@ const Roles = React.memo((props: any) => {
setEditCandidate({ ...editCandidate, description: e.target.value });
};
+ const handleEditPermissionCheck = (
+ permissionName: string,
+ dependsOn: string[]
+ ) => {
+ let updatedPermissions: string[] = [...editCandidate.permissions];
+ const isChecked = updatedPermissions.includes(permissionName);
+
+ if (!isChecked) {
+ updatedPermissions.push(permissionName);
+ dependsOn.forEach((dependency) => {
+ if (!updatedPermissions.includes(dependency)) {
+ updatedPermissions.push(dependency);
+ }
+ });
+ } else {
+ updatedPermissions = updatedPermissions.filter(
+ (permission) => permission !== permissionName
+ );
+ dependsOn.forEach((dependency) => {
+ updatedPermissions = updatedPermissions.filter(
+ (permission) => permission !== dependency
+ );
+ });
+ }
+ setEditCandidate({ ...editCandidate, permissions: updatedPermissions });
+ };
+
// handlers for role create/edit modal
const handleCloseRoleModal = () => {
setShowRoleModal(false);
- setPayload({ name: "", description: "" });
+ setPayload({ name: "", description: "", permissions: [] });
};
const handleShowRoleModal = () => setShowRoleModal(true);
const handleCloseEditRoleModal = () => {
@@ -258,8 +355,16 @@ const Roles = React.memo((props: any) => {
return DEFAULT_ROLES.includes(role);
}
};
- // Delete confirmation
+
+
+ const clearSearch = () => {
+ setSerach("");
+ let updatedRoleName = removingTenantId(props.roles,tenantId);
+ setRoles(updatedRoleName);
+ };
+
+ // Delete confirmation
const confirmDelete = () => (
@@ -294,111 +399,180 @@ const Roles = React.memo((props: any) => {
const showCreateModal = () => (
-
- {t("Create Role")}
-
-
-
-
- {t("Role Name")}
-
- *
-
-
- {t("Description")}
-
- *
-
-
-
-
-
-
-
+ {t("Permissions")}
+
+ *
+
+ {permission.map((permission) => (
+
+
+ handlePermissionCheck(
+ permission.name,
+ permission.depends_on
+ )
+ }
+ aria-label={t(permission.description)}
+ />
+
+ ))}
+
+
+
+
+
+
+
);
const showEditModal = () => (
-
- {t("Edit Role")}
-
-
-
-
- {t("Role Name")}
-
- *
-
-
- {t("Description")}
-
- *
-
-
-
-
-
-
-
+ {t("Permissions")}
+
+ *
+
+ {permission.map((permission) => (
+
+
+ handleEditPermissionCheck(
+ permission.name,
+ permission.depends_on
+ )
+ }
+ aria-label={t(permission.description)}
+ />
+
+ ))}
+
+
+
+
+
+
+
);
@@ -541,11 +715,12 @@ const Roles = React.memo((props: any) => {
className="fa fa-pencil me-4"
style={{ color: "#7E7E7F", cursor: "pointer" }}
onClick={() => {
- setSelectedRoleIdentifier(
- KEYCLOAK_ENABLE_CLIENT_AUTH ? rowData.name : rowData.id
- );
+ setSelectedRoleIdentifier(rowData.id);
setEditCandidate(rowData);
handleShowEditRoleModal();
+ // setSelectedRoleIdentifier(
+ // KEYCLOAK_ENABLE_CLIENT_AUTH ? rowData.name : rowData.id
+ // );
}}
data-testid="admin-roles-edit-icon"
/>
@@ -581,10 +756,7 @@ const Roles = React.memo((props: any) => {
{search.length > 0 && (