Skip to content

Commit

Permalink
feat: Refactor access control logic for all pages (#297)
Browse files Browse the repository at this point in the history
* Update apps/platform/store/UserRole.ts

Co-authored-by: Puru Dahal <[email protected]>

* feat: Refactor access control logic for all pages

* chore: format code

* chore: update variable naming convention

* chore: Remove unused variables

* chore: update variable naming convention

---------

Co-authored-by: Puru Dahal <[email protected]>
  • Loading branch information
ttebify and dahal authored Mar 25, 2023
1 parent 3ea3620 commit c261bbe
Show file tree
Hide file tree
Showing 23 changed files with 280 additions and 453 deletions.
21 changes: 11 additions & 10 deletions apps/platform/components/members/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React from "react";
import useFuse from "@/hooks/useFuse";
import { UserType } from "@/types/resources";
import { trpc } from "@/utils/trpc";
import { Access, UserRole } from "@prisma/client";
import { UserRole } from "@prisma/client";
import clsx from "clsx";
import { Lock, Trash, Unlock, UserX } from "lucide-react";
import MemberTabs from "@/components/members/MemberTabs";
Expand All @@ -18,7 +18,7 @@ interface TableProps {
tab: Tab;
setTab: (tab: Tab) => void;
members: UserType[];
userAccessInCurrentProject: Access;
currentRole: UserRole;
projectId: string;
user: UserType;
}
Expand All @@ -34,7 +34,7 @@ const MembersTable = ({
members,
tab,
setTab,
userAccessInCurrentProject: userAccess,
currentRole,
projectId,
user,
}: TableProps) => {
Expand Down Expand Up @@ -101,12 +101,13 @@ const MembersTable = ({
memeberUpdateMutation.mutate({
projectId,
newRole: user.newRole,
currentUserRole: userAccess.role,
currentUserRole: currentRole,
targetUserId: user.userId,
targetUserRole: user.currentRole,
});
},
[memeberUpdateMutation, projectId, userAccess.role],
// eslint-disable-next-line react-hooks/exhaustive-deps
[projectId, currentRole],
);

const onUpdateMemberStatus = useCallback(
Expand All @@ -119,13 +120,13 @@ const MembersTable = ({
memberStatusMutation.mutate({
projectId,
status,
currentUserRole: userAccess.role,
currentUserRole: currentRole,
targetUserId: user.userId,
targetUserRole: user.currentRole,
});
}
},
[memberStatusMutation, projectId, userAccess.role],
[memberStatusMutation, projectId, currentRole],
);

return (
Expand Down Expand Up @@ -209,11 +210,11 @@ const MembersTable = ({
disabled={
fetching ||
!(
userAccess.role === UserRole.owner ||
userAccess.role === UserRole.maintainer
currentRole === UserRole.owner ||
currentRole === UserRole.maintainer
) ||
member.id === user.id ||
(userAccess.role === UserRole.maintainer &&
(currentRole === UserRole.maintainer &&
member.role === UserRole.owner)
}
>
Expand Down
6 changes: 3 additions & 3 deletions apps/platform/components/projects/CreateProjectModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,15 @@ const CreateProjectModal = () => {
});

const createNewProject: SubmitHandler<Project> = async (data) => {
const { name, slug } = data;
const { name } = data;
setLoading(true);

if (!name || !slug) {
if (!name || !kebabSlug) {
setLoading(false);
return;
}

projectMutation.mutate({ project: { name, slug } });
projectMutation.mutate({ project: { name, slug: kebabSlug } });
reset();
};

Expand Down
10 changes: 5 additions & 5 deletions apps/platform/components/projects/ProjectSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
import React from "react";
import { getProjectSettingTabs } from "@/utils/helpers";
import { Project } from "@prisma/client";
import type { Project, UserRole } from "@prisma/client";
import Tabs from "@/components/settings/Tabs";

type ActiveType = "general" | "branches" | "danger";

interface SettingsProps {
projects: Project[];
currentProject: Project;
roleInCurrentProject: string;
currentRole: UserRole;
active: ActiveType;
children?: React.ReactNode;
}

const ProjectSettings = ({
projects,
currentProject,
roleInCurrentProject,
currentRole,
active,
children,
}: SettingsProps) => {
const tabData = React.useMemo(
() => getProjectSettingTabs(roleInCurrentProject, currentProject.slug),
[currentProject.slug],
() => getProjectSettingTabs(currentRole, currentProject.slug),
[currentProject.slug, currentRole],
);

return (
Expand Down
7 changes: 4 additions & 3 deletions apps/platform/components/projects/Tabs.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Link from "next/link";
import { useRouter } from "next/router";
import { getNavigationTabs } from "@/utils/helpers";
import type { UserRole } from "@prisma/client";
import {
GitBranch,
GitPullRequest,
Expand All @@ -19,17 +20,17 @@ function classNames(...classes) {
interface Props {
active: string;
projectSlug: string;
roleInCurrentProject: string;
currentRole: UserRole;
}

export default function Tabs({
active,
projectSlug,
roleInCurrentProject,
currentRole,
}: Props) {
const router = useRouter();
const projectUrl = `/projects/${projectSlug}`;
const tabs = getNavigationTabs(roleInCurrentProject, projectUrl);
const tabs = getNavigationTabs(currentRole, projectUrl);

return (
<div>
Expand Down
5 changes: 3 additions & 2 deletions apps/platform/components/pulls/DetailedPrTitle.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import Link from "next/link";
import type { PullRequestStatus } from "@prisma/client";
import { GitPullRequest } from "lucide-react";

interface DetailedPrTitleProps {
author: string;
title: string;
prId: number;
status: "open" | "closed";
status: PullRequestStatus;
base?: string;
current?: string;
}
Expand All @@ -26,7 +27,7 @@ export default function DetailedPrTitle({
</h1>

<div className="mt-2 flex items-center gap-2">
<div className="inline-flex inline-flex items-center items-center gap-1 rounded-full bg-emerald-200 px-4 py-2 text-xs font-semibold text-emerald-700">
<div className="inline-flex items-center gap-1 rounded-full bg-emerald-200 px-4 py-2 text-xs font-semibold text-emerald-700">
<GitPullRequest className="h-4 w-4" strokeWidth={2} />
<span>{status}</span>
</div>
Expand Down
13 changes: 7 additions & 6 deletions apps/platform/layouts/Project.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Project } from "@prisma/client";
import { Fragment } from "react";
import type { Project, UserRole } from "@prisma/client";
import { useSession } from "next-auth/react";
import { Toaster } from "react-hot-toast";
import { Tabs } from "@/components/projects";
Expand All @@ -8,7 +9,7 @@ interface Props {
tab?: string;
projects: Project[];
currentProject: Project;
roleInCurrentProject: string;
currentRole: UserRole;
children: React.ReactNode;
}

Expand All @@ -17,14 +18,14 @@ const ProjectLayout = ({
projects,
children,
currentProject,
roleInCurrentProject,
currentRole,
}: Props) => {
const { data: session, status } = useSession();
const user = session?.user;

if (status === "authenticated") {
return (
<>
<Fragment>
<Container>
<Nav
user={user}
Expand All @@ -33,7 +34,7 @@ const ProjectLayout = ({
/>
</Container>
<Tabs
roleInCurrentProject={roleInCurrentProject}
currentRole={currentRole}
projectSlug={currentProject?.slug}
active={tab || "project"}
/>
Expand All @@ -43,7 +44,7 @@ const ProjectLayout = ({
</Container>

<Toaster position="top-right" />
</>
</Fragment>
);
} else {
return (
Expand Down
3 changes: 2 additions & 1 deletion apps/platform/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@
"remark-gfm": "^3.0.1",
"remark-html": "^15.0.2",
"superjson": "^1.12.1",
"zod": "^3.20.6"
"zod": "^3.20.6",
"zustand": "^4.3.6"
},
"devDependencies": {
"@faker-js/faker": "^7.6.0",
Expand Down
80 changes: 13 additions & 67 deletions apps/platform/pages/projects/[slug]/audits.tsx
Original file line number Diff line number Diff line change
@@ -1,94 +1,40 @@
import { type GetServerSidePropsContext } from "next";
import ProjectLayout from "@/layouts/Project";
import { getServerSideSession } from "@/utils/session";
import { Project } from "@prisma/client";
import prisma from "@/lib/prisma";
import { withAccessControl } from "@/utils/withAccessControl";
import { Project, UserRole } from "@prisma/client";

/**
* A functional component that represents a project.
* @param {Props} props - The props for the component.
* @param {Projects} props.projects - The projects the user has access to.
* @param {currentProject} props.currentProject - The current project.
* @param {roleInProject} props.roleInProject - The user role in current project.
*/

interface Props {
projects: Project[];
currentProject: Project;
roleInProject: UserRole;
}

export const AuditLogsPage = ({ projects, currentProject }: Props) => {
export const AuditLogsPage = ({
projects,
currentProject,
roleInProject,
}: Props) => {
return (
<ProjectLayout
tab="audits"
projects={projects}
currentRole={roleInProject}
currentProject={currentProject}
>
<h1>AuditLogsPage for {currentProject.name}</h1>
</ProjectLayout>
);
};

export async function getServerSideProps(context: GetServerSidePropsContext) {
const session = await getServerSideSession(context);
const user = session?.user;

// @ts-ignore
const { slug } = context.params;

if (!user) {
return {
redirect: {
destination: "/auth",
permanent: false,
},
};
}

const access = await prisma.access.findMany({
where: {
// @ts-ignore
userId: user.id,
},
select: {
id: true,
project: {
select: {
id: true,
name: true,
slug: true,
updatedAt: true,
},
},
},
});

if (!access) {
return {
redirect: {
destination: "/projects",
permanent: false,
},
};
}

const projects = access.map((access) => access.project);
const currentProject = projects.find((project) => project.slug === slug);

if (!currentProject) {
return {
redirect: {
destination: "/projects",
permanent: false,
},
};
}

return {
props: {
currentProject: JSON.parse(JSON.stringify(currentProject)),
projects: JSON.parse(JSON.stringify(projects)),
},
};
}
export const getServerSideProps = withAccessControl({
hasAccess: { maintainer: true, developer: true, guest: true, owner: true },
});

export default AuditLogsPage;
Loading

0 comments on commit c261bbe

Please sign in to comment.