implement workflow permissions
This commit is contained in:
@@ -17,7 +17,7 @@ async function put(req: NextApiRequest, res: NextApiResponse) {
|
||||
const user = await requestUser(req, res);
|
||||
if (!user) return res.status(401).json({ ok: false });
|
||||
|
||||
if (!["admin", "developer", "corporate", "mastercorporate"].includes(user.type)) {
|
||||
if (!["admin", "developer", "teacher", "corporate", "mastercorporate"].includes(user.type)) {
|
||||
return res.status(403).json({ ok: false });
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ import { ApprovalWorkflow } from "@/interfaces/approval.workflow";
|
||||
import { sessionOptions } from "@/lib/session";
|
||||
import { requestUser } from "@/utils/api";
|
||||
import { deleteApprovalWorkflow, getApprovalWorkflow, updateApprovalWorkflow } from "@/utils/approval.workflows.be";
|
||||
import { getEntityWithRoles } from "@/utils/entities.be";
|
||||
import { doesEntityAllow } from "@/utils/permissions";
|
||||
import { withIronSessionApiRoute } from "iron-session/next";
|
||||
import { ObjectId } from "mongodb";
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
@@ -19,20 +21,30 @@ async function del(req: NextApiRequest, res: NextApiResponse) {
|
||||
const user = await requestUser(req, res);
|
||||
if (!user) return res.status(401).json({ ok: false });
|
||||
|
||||
if (!["admin", "developer", "corporate", "mastercorporate"].includes(user.type)) {
|
||||
if (!["admin", "developer", "teacher", "corporate", "mastercorporate"].includes(user.type)) {
|
||||
return res.status(403).json({ ok: false });
|
||||
}
|
||||
|
||||
const { id } = req.query as { id?: string };
|
||||
const { id } = req.query as { id: string };
|
||||
const workflow = await getApprovalWorkflow("active-workflows", id);
|
||||
|
||||
if (id) return res.status(200).json(await deleteApprovalWorkflow("active-workflows", id));
|
||||
if (!workflow) return res.status(404).json({ ok: false });
|
||||
|
||||
const entity = await getEntityWithRoles(workflow.entityId);
|
||||
if (!entity) return res.status(404).json({ ok: false });
|
||||
|
||||
if (!doesEntityAllow(user, entity, "delete_workflow") && !["admin", "developer"].includes(user.type)) {
|
||||
return res.status(403).json({ ok: false });
|
||||
}
|
||||
|
||||
return res.status(200).json(await deleteApprovalWorkflow("active-workflows", id));
|
||||
}
|
||||
|
||||
async function put(req: NextApiRequest, res: NextApiResponse) {
|
||||
const user = await requestUser(req, res);
|
||||
if (!user) return res.status(401).json({ ok: false });
|
||||
|
||||
if (!["admin", "developer", "corporate", "mastercorporate"].includes(user.type)) {
|
||||
if (!["admin", "developer", "teacher", "corporate", "mastercorporate"].includes(user.type)) {
|
||||
return res.status(403).json({ ok: false });
|
||||
}
|
||||
|
||||
@@ -50,7 +62,7 @@ async function get(req: NextApiRequest, res: NextApiResponse) {
|
||||
const user = await requestUser(req, res);
|
||||
if (!user) return res.status(401).json({ ok: false });
|
||||
|
||||
if (!["admin", "developer", "corporate", "mastercorporate"].includes(user.type)) {
|
||||
if (!["admin", "developer", "teacher", "corporate", "mastercorporate"].includes(user.type)) {
|
||||
return res.status(403).json({ ok: false });
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
|
||||
const user = await requestUser(req, res);
|
||||
if (!user) return res.status(401).json({ ok: false });
|
||||
|
||||
if (!["admin", "developer", "corporate", "mastercorporate"].includes(user.type)) {
|
||||
if (!["admin", "developer", "teacher", "corporate", "mastercorporate"].includes(user.type)) {
|
||||
return res.status(403).json({ ok: false });
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ async function get(req: NextApiRequest, res: NextApiResponse) {
|
||||
const user = await requestUser(req, res);
|
||||
if (!user) return res.status(401).json({ ok: false });
|
||||
|
||||
if (!["admin", "developer", "corporate", "mastercorporate"].includes(user.type)) {
|
||||
if (!["admin", "developer", "teacher", "corporate", "mastercorporate"].includes(user.type)) {
|
||||
return res.status(403).json({ ok: false });
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
|
||||
const user = await requestUser(req, res);
|
||||
if (!user) return res.status(401).json({ ok: false });
|
||||
|
||||
if (!["admin", "developer", "corporate", "mastercorporate"].includes(user.type)) {
|
||||
if (!["admin", "developer", "teacher", "corporate", "mastercorporate"].includes(user.type)) {
|
||||
return res.status(403).json({ ok: false });
|
||||
}
|
||||
|
||||
|
||||
@@ -6,10 +6,12 @@ import Layout from "@/components/High/Layout";
|
||||
import { ApprovalWorkflow, EditableApprovalWorkflow, EditableWorkflowStep, getUserTypeLabelShort } from "@/interfaces/approval.workflow";
|
||||
import { CorporateUser, DeveloperUser, MasterCorporateUser, TeacherUser, User } from "@/interfaces/user";
|
||||
import { sessionOptions } from "@/lib/session";
|
||||
import { redirect, serialize } from "@/utils";
|
||||
import { findBy, redirect, serialize } from "@/utils";
|
||||
import { requestUser } from "@/utils/api";
|
||||
import { getApprovalWorkflow } from "@/utils/approval.workflows.be";
|
||||
import { getEntityWithRoles } from "@/utils/entities.be";
|
||||
import { shouldRedirectHome } from "@/utils/navigation.disabled";
|
||||
import { doesEntityAllow } from "@/utils/permissions";
|
||||
import { getEntityUsers } from "@/utils/users.be";
|
||||
import axios from "axios";
|
||||
import { LayoutGroup, motion } from "framer-motion";
|
||||
@@ -30,9 +32,12 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res, params }
|
||||
const { id } = params as { id: string };
|
||||
|
||||
const workflow: ApprovalWorkflow | null = await getApprovalWorkflow("active-workflows", id);
|
||||
if (!workflow) return redirect("/approval-workflows");
|
||||
|
||||
if (!workflow)
|
||||
return redirect("/approval-workflows")
|
||||
const entityWithRole = await getEntityWithRoles(workflow.entityId);
|
||||
if (!entityWithRole) return redirect("/approval-workflows");
|
||||
|
||||
if (!doesEntityAllow(user, entityWithRole, "edit_workflow")) return redirect("/approval-workflows");
|
||||
|
||||
return {
|
||||
props: serialize({
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import Tip from "@/components/ApprovalWorkflows/Tip";
|
||||
import WorkflowForm from "@/components/ApprovalWorkflows/WorkflowForm";
|
||||
import Layout from "@/components/High/Layout";
|
||||
import Button from "@/components/Low/Button";
|
||||
import Input from "@/components/Low/Input";
|
||||
import Select from "@/components/Low/Select";
|
||||
@@ -8,11 +7,13 @@ import { ApprovalWorkflow, EditableApprovalWorkflow } from "@/interfaces/approva
|
||||
import { Entity } from "@/interfaces/entity";
|
||||
import { CorporateUser, DeveloperUser, MasterCorporateUser, TeacherUser, User } from "@/interfaces/user";
|
||||
import { sessionOptions } from "@/lib/session";
|
||||
import { redirect, serialize } from "@/utils";
|
||||
import { mapBy, redirect, serialize } from "@/utils";
|
||||
import { requestUser } from "@/utils/api";
|
||||
import { getApprovalWorkflowsByEntities } from "@/utils/approval.workflows.be";
|
||||
import { getEntities } from "@/utils/entities.be";
|
||||
import { getEntitiesWithRoles } from "@/utils/entities.be";
|
||||
import { shouldRedirectHome } from "@/utils/navigation.disabled";
|
||||
import { findAllowedEntities } from "@/utils/permissions";
|
||||
import { isAdmin } from "@/utils/users";
|
||||
import { getEntitiesUsers } from "@/utils/users.be";
|
||||
import axios from "axios";
|
||||
import { AnimatePresence, LayoutGroup, motion } from "framer-motion";
|
||||
@@ -31,10 +32,12 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
|
||||
const user = await requestUser(req, res)
|
||||
if (!user) return redirect("/login")
|
||||
|
||||
if (shouldRedirectHome(user) || !["admin", "developer", "corporate", "mastercorporate"].includes(user.type))
|
||||
if (shouldRedirectHome(user) || !["admin", "developer", "teacher", "corporate", "mastercorporate"].includes(user.type))
|
||||
return redirect("/")
|
||||
|
||||
const userEntitiesWithLabel = await getEntities(user.entities.map(entity => entity.id));
|
||||
const entityIDS = mapBy(user.entities, "id");
|
||||
const entities = await getEntitiesWithRoles(isAdmin(user) ? undefined : entityIDS);
|
||||
const userEntitiesWithLabel = findAllowedEntities(user, entities, "configure_workflows");
|
||||
|
||||
const allConfiguredWorkflows = await getApprovalWorkflowsByEntities("configured-workflows", userEntitiesWithLabel.map(entity => entity.id));
|
||||
|
||||
|
||||
@@ -10,12 +10,13 @@ import {ApprovalWorkflow, ApprovalWorkflowStatus, ApprovalWorkflowStatusLabel, S
|
||||
import { Entity, EntityWithRoles } from "@/interfaces/entity";
|
||||
import { User } from "@/interfaces/user";
|
||||
import { sessionOptions } from "@/lib/session";
|
||||
import {redirect, serialize} from "@/utils";
|
||||
import { mapBy, redirect, serialize } from "@/utils";
|
||||
import { requestUser } from "@/utils/api";
|
||||
import { getApprovalWorkflows } from "@/utils/approval.workflows.be";
|
||||
import { getEntities, getEntitiesWithRoles } from "@/utils/entities.be";
|
||||
import { shouldRedirectHome } from "@/utils/navigation.disabled";
|
||||
import {findAllowedEntities} from "@/utils/permissions";
|
||||
import { doesEntityAllow, findAllowedEntities } from "@/utils/permissions";
|
||||
import { isAdmin } from "@/utils/users";
|
||||
import { getSpecificUsers } from "@/utils/users.be";
|
||||
import { createColumnHelper, flexRender, getCoreRowModel, useReactTable } from "@tanstack/react-table";
|
||||
import axios from "axios";
|
||||
@@ -23,37 +24,13 @@ import clsx from "clsx";
|
||||
import { withIronSessionSsr } from "iron-session/next";
|
||||
import Head from "next/head";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { BsTrash } from "react-icons/bs";
|
||||
import { FaRegEdit } from "react-icons/fa";
|
||||
import { IoIosAddCircleOutline } from "react-icons/io";
|
||||
import { toast, ToastContainer } from "react-toastify";
|
||||
|
||||
const columnHelper = createColumnHelper<ApprovalWorkflow>();
|
||||
|
||||
export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
|
||||
const user = await requestUser(req, res);
|
||||
if (!user) return redirect("/login");
|
||||
|
||||
if (shouldRedirectHome(user) || !["admin", "developer", "teacher", "corporate", "mastercorporate"].includes(user.type)) return redirect("/");
|
||||
|
||||
const workflows = await getApprovalWorkflows("active-workflows");
|
||||
|
||||
const allAssigneeIds: string[] = [...new Set(workflows.map((workflow) => workflow.steps.map((step) => step.assignees).flat()).flat())];
|
||||
|
||||
const entities = await getEntitiesWithRoles(user.entities.map((entity) => entity.id));
|
||||
const allowedEntities = findAllowedEntities(user, entities, "view_workflows");
|
||||
|
||||
return {
|
||||
props: serialize({
|
||||
user,
|
||||
initialWorkflows: workflows,
|
||||
workflowsAssignees: await getSpecificUsers(allAssigneeIds),
|
||||
userEntitiesWithLabel: entities,
|
||||
}),
|
||||
};
|
||||
}, sessionOptions);
|
||||
|
||||
const StatusClassNames: { [key in ApprovalWorkflowStatus]: string } = {
|
||||
approved:
|
||||
"bg-green-100 text-green-800 border border-green-300 before:content-[''] before:w-2 before:h-2 before:bg-green-500 before:rounded-full before:inline-block before:mr-2",
|
||||
@@ -84,6 +61,40 @@ const STATUS_OPTIONS = [
|
||||
},
|
||||
];
|
||||
|
||||
const columnHelper = createColumnHelper<ApprovalWorkflow>();
|
||||
|
||||
export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
|
||||
const user = await requestUser(req, res);
|
||||
if (!user) return redirect("/login");
|
||||
|
||||
if (shouldRedirectHome(user) || !["admin", "developer", "teacher", "corporate", "mastercorporate"].includes(user.type)) return redirect("/");
|
||||
|
||||
const workflows = await getApprovalWorkflows("active-workflows");
|
||||
|
||||
const allAssigneeIds: string[] = [
|
||||
...new Set(
|
||||
workflows
|
||||
.map(workflow => workflow.steps
|
||||
.map(step => step.assignees)
|
||||
.flat()
|
||||
).flat()
|
||||
)
|
||||
];
|
||||
|
||||
const entityIDS = mapBy(user.entities, "id");
|
||||
const entities = await getEntitiesWithRoles(isAdmin(user) ? undefined : entityIDS);
|
||||
const allowedEntities = findAllowedEntities(user, entities, "view_workflows");
|
||||
|
||||
return {
|
||||
props: serialize({
|
||||
user,
|
||||
initialWorkflows: workflows,
|
||||
workflowsAssignees: await getSpecificUsers(allAssigneeIds),
|
||||
userEntitiesWithLabel: allowedEntities,
|
||||
}),
|
||||
};
|
||||
}, sessionOptions);
|
||||
|
||||
interface Props {
|
||||
user: User;
|
||||
initialWorkflows: ApprovalWorkflow[];
|
||||
@@ -101,8 +112,10 @@ export default function ApprovalWorkflows({user, initialWorkflows, workflowsAssi
|
||||
const [entityFilter, setEntityFilter] = useState<CustomEntity>(undefined);
|
||||
const [nameFilter, setNameFilter] = useState<string>("");
|
||||
|
||||
const allowedEntities = useAllowedEntities(user, userEntitiesWithLabel, "view_workflows");
|
||||
const allowedSomeEntities = useAllowedEntitiesSomePermissions(user, userEntitiesWithLabel, ["view_workflows", "create_workflow"]);
|
||||
const router = useRouter();
|
||||
|
||||
/* const allowedEntities = useAllowedEntities(user, userEntitiesWithLabel, "view_workflows");
|
||||
const allowedSomeEntities = useAllowedEntitiesSomePermissions(user, userEntitiesWithLabel, ["view_workflows", "create_workflow"]); */
|
||||
|
||||
const ENTITY_OPTIONS = [
|
||||
...userEntitiesWithLabel
|
||||
@@ -157,10 +170,8 @@ export default function ApprovalWorkflows({user, initialWorkflows, workflowsAssi
|
||||
reload();
|
||||
})
|
||||
.catch((reason) => {
|
||||
if (reason.response.status === 404) {
|
||||
toast.error("Approval Workflow not found!");
|
||||
} else if (reason.response.status === 403) {
|
||||
toast.error("You do not have permission to delete an Approval Workflow!");
|
||||
if (reason.response.status === 403) {
|
||||
toast.error("You do not have permission to delete this Approval Workflow!");
|
||||
} else {
|
||||
toast.error("Something went wrong, please try again later.");
|
||||
}
|
||||
@@ -259,6 +270,7 @@ export default function ApprovalWorkflows({user, initialWorkflows, workflowsAssi
|
||||
<button
|
||||
data-tip="Delete"
|
||||
className="cursor-pointer tooltip"
|
||||
disabled={!doesEntityAllow(user, userEntitiesWithLabel.find(entity => entity.id === row.original.entityId)!, "delete_workflow")}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
deleteApprovalWorkflow(row.original._id?.toString(), row.original.name);
|
||||
@@ -267,13 +279,16 @@ export default function ApprovalWorkflows({user, initialWorkflows, workflowsAssi
|
||||
</button>
|
||||
|
||||
{currentStep && !rejected && (
|
||||
<Link
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
<button
|
||||
data-tip="Edit"
|
||||
href={`/approval-workflows/${row.original._id?.toString()}/edit`}
|
||||
className="cursor-pointer tooltip">
|
||||
className="cursor-pointer tooltip"
|
||||
disabled={!doesEntityAllow(user, userEntitiesWithLabel.find(entity => entity.id === row.original.entityId)!, "edit_workflow")}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
router.push(`/approval-workflows/${row.original._id?.toString()}/edit`);
|
||||
}}>
|
||||
<FaRegEdit className="hover:text-mti-purple-light transition ease-in-out duration-300" />
|
||||
</Link>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -109,7 +109,7 @@ const ASSIGNMENT_MANAGEMENT: PermissionLayout[] = [
|
||||
|
||||
const WORKFLOW_MANAGEMENT: PermissionLayout[] = [
|
||||
{label: "View Workflows", key: "view_workflows"},
|
||||
{label: "Create Workflow", key: "create_workflow"},
|
||||
{label: "Configure Workflows", key: "configure_workflows"},
|
||||
{label: "Edit Workflow", key: "edit_workflow"},
|
||||
{label: "Delete Workflow", key: "delete_workflow"},
|
||||
];
|
||||
|
||||
@@ -69,7 +69,7 @@ export type RolePermission =
|
||||
| "view_approval_workflows"
|
||||
| "update_exam_privacy"
|
||||
| "view_workflows"
|
||||
| "create_workflow"
|
||||
| "configure_workflows"
|
||||
| "edit_workflow"
|
||||
| "delete_workflow";
|
||||
|
||||
@@ -81,7 +81,6 @@ export const DEFAULT_PERMISSIONS: RolePermission[] = [
|
||||
"view_entity_roles",
|
||||
"view_statistics",
|
||||
"download_statistics_report",
|
||||
"view_approval_workflows",
|
||||
];
|
||||
|
||||
export const ADMIN_PERMISSIONS: RolePermission[] = [
|
||||
@@ -153,7 +152,7 @@ export const ADMIN_PERMISSIONS: RolePermission[] = [
|
||||
"pay_entity",
|
||||
"view_payment_record",
|
||||
"update_exam_privacy",
|
||||
"create_workflow",
|
||||
"configure_workflows",
|
||||
"view_workflows",
|
||||
"edit_workflow",
|
||||
"delete_workflow",
|
||||
|
||||
Reference in New Issue
Block a user