[WEB-2357] fix: update and redefine user roles across the platform (#5466)

* chore: removed viewer role

* chore: indentation

* chore: remove viewer role

* chore: handled user permissions in store

* chore: updated the migration file

* chore: updated user permissions store

* chore: removed the owner key

* chore: code refactor

* chore: code refactor

* chore: code refactor

* chore: code refactor

* chore: code refactor

* fix: build error

* chore: updated user permissions store and handled the permissions fetch in workspace and project wrappers

* chore: package user enum updated

* chore: user permission updated

* chore: user permission updated

* chore: resolved build errors

* chore: resolved build error

* chore: resolved build errors

* chore: computedFn deep map issue resolved

* chore: added back migration

* chore: added new field in project table

* chore: removed member store in users

* chore: private project for admins

* chore: workspace notification access validation updated

* fix: workspace member edit option

* fix: project intake permission validation updated

* chore: workspace export settings permission updated

* chore: guest_view_all_issues added

* chore: guest_view_all_issues added

* chore: key changed for guest access

* chore: added validation for individual issues

* chore: changed the dashboard issues count

* chore: added new yarn file

* chore: modified yarn file

* chore: project page permission updated

* chore: project page permission updated

* chore: member setting ux updated

* chore: build error

* fix: yarn lock

* fix: build error

---------

Co-authored-by: gurusainath <gurusainath007@gmail.com>
Co-authored-by: Anmol Singh Bhatia <anmolsinghbhatia@plane.so>
This commit is contained in:
Bavisetti Narayan 2024-09-11 17:10:15 +05:30 committed by GitHub
parent 7013a36629
commit fdcd9a376c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
172 changed files with 2057 additions and 1627 deletions

View file

@ -16,7 +16,8 @@ import { APITokenSettingsLoader } from "@/components/ui";
import { EmptyStateType } from "@/constants/empty-state";
import { API_TOKENS_LIST } from "@/constants/fetch-keys";
// store hooks
import { useUser, useWorkspace } from "@/hooks/store";
import { useUserPermissions, useWorkspace } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// services
import { APITokenService } from "@/services/api_token.service";
@ -28,11 +29,10 @@ const ApiTokensPage = observer(() => {
// router
const { workspaceSlug } = useParams();
// store hooks
const {
canPerformWorkspaceAdminActions,
membership: { currentWorkspaceRole },
} = useUser();
const { currentWorkspace } = useWorkspace();
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
// derived values
const canPerformWorkspaceAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
const { data: tokens } = useSWR(
workspaceSlug && canPerformWorkspaceAdminActions ? API_TOKENS_LIST(workspaceSlug.toString()) : null,
@ -42,7 +42,7 @@ const ApiTokensPage = observer(() => {
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - API Tokens` : undefined;
if (currentWorkspaceRole && !canPerformWorkspaceAdminActions) {
if (workspaceUserInfo && !canPerformWorkspaceAdminActions) {
return <NotAuthorizedView section="settings" />;
}

View file

@ -5,21 +5,20 @@ import { observer } from "mobx-react";
import { NotAuthorizedView } from "@/components/auth-screens";
import { PageHead } from "@/components/core";
// hooks
import { useUser, useWorkspace } from "@/hooks/store";
import { useUserPermissions, useWorkspace } from "@/hooks/store";
// plane web components
import { BillingRoot } from "@/plane-web/components/workspace";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
const BillingSettingsPage = observer(() => {
// store hooks
const {
canPerformWorkspaceAdminActions,
membership: { currentWorkspaceRole },
} = useUser();
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
const { currentWorkspace } = useWorkspace();
// derived values
const canPerformWorkspaceAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Billing & Plans` : undefined;
if (currentWorkspaceRole && !canPerformWorkspaceAdminActions) {
if (workspaceUserInfo && !canPerformWorkspaceAdminActions) {
return <NotAuthorizedView section="settings" />;
}

View file

@ -8,22 +8,23 @@ import ExportGuide from "@/components/exporter/guide";
// helpers
import { cn } from "@/helpers/common.helper";
// hooks
import { useUser, useWorkspace } from "@/hooks/store";
import { useUserPermissions, useWorkspace } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
const ExportsPage = observer(() => {
// store hooks
const {
canPerformWorkspaceViewerActions,
canPerformWorkspaceMemberActions,
membership: { currentWorkspaceRole },
} = useUser();
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
const { currentWorkspace } = useWorkspace();
// derived values
const canPerformWorkspaceMemberActions = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.WORKSPACE
);
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Exports` : undefined;
// if user is not authorized to view this page
if (currentWorkspaceRole && !canPerformWorkspaceViewerActions) {
if (workspaceUserInfo && !canPerformWorkspaceMemberActions) {
return <NotAuthorizedView section="settings" />;
}

View file

@ -4,20 +4,17 @@ import { observer } from "mobx-react";
// components
import { PageHead } from "@/components/core";
import IntegrationGuide from "@/components/integration/guide";
// constants
import { EUserWorkspaceRoles } from "@/constants/workspace";
// hooks
import { useUser, useWorkspace } from "@/hooks/store";
import { useUserPermissions, useWorkspace } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
const ImportsPage = observer(() => {
// store hooks
const {
membership: { currentWorkspaceRole },
} = useUser();
const { currentWorkspace } = useWorkspace();
const { allowPermissions } = useUserPermissions();
// derived values
const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Imports` : undefined;
if (!isAdmin)

View file

@ -8,9 +8,9 @@ import { SingleIntegrationCard } from "@/components/integration";
import { IntegrationAndImportExportBanner, IntegrationsSettingsLoader } from "@/components/ui";
// constants
import { APP_INTEGRATIONS } from "@/constants/fetch-keys";
import { EUserWorkspaceRoles } from "@/constants/workspace";
// hooks
import { useUser, useWorkspace } from "@/hooks/store";
import { useUserPermissions, useWorkspace } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// services
import { IntegrationService } from "@/services/integrations";
@ -20,13 +20,11 @@ const WorkspaceIntegrationsPage = observer(() => {
// router
const { workspaceSlug } = useParams();
// store hooks
const {
membership: { currentWorkspaceRole },
} = useUser();
const { currentWorkspace } = useWorkspace();
const { allowPermissions } = useUserPermissions();
// derived values
const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Integrations` : undefined;
if (!isAdmin)

View file

@ -18,7 +18,8 @@ import { MEMBER_INVITED } from "@/constants/event-tracker";
import { cn } from "@/helpers/common.helper";
import { getUserRole } from "@/helpers/user.helper";
// hooks
import { useEventTracker, useMember, useUser, useWorkspace } from "@/hooks/store";
import { useEventTracker, useMember, useUserPermissions, useWorkspace } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
const WorkspaceMembersSettingsPage = observer(() => {
// states
@ -27,18 +28,20 @@ const WorkspaceMembersSettingsPage = observer(() => {
// router
const { workspaceSlug } = useParams();
// store hooks
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
const { captureEvent } = useEventTracker();
const {
canPerformWorkspaceAdminActions,
canPerformWorkspaceViewerActions,
canPerformWorkspaceMemberActions,
membership: { currentWorkspaceRole },
} = useUser();
const {
workspace: { inviteMembersToWorkspace },
} = useMember();
const { currentWorkspace } = useWorkspace();
// derived values
const canPerformWorkspaceAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
const canPerformWorkspaceMemberActions = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.WORKSPACE
);
const handleWorkspaceInvite = (data: IWorkspaceBulkInviteFormData) => {
if (!workspaceSlug) return;
@ -49,7 +52,7 @@ const WorkspaceMembersSettingsPage = observer(() => {
emails: [
...data.emails.map((email) => ({
email: email.email,
role: getUserRole(email.role),
role: getUserRole(email.role as unknown as EUserPermissions),
})),
],
project_id: undefined,
@ -67,7 +70,7 @@ const WorkspaceMembersSettingsPage = observer(() => {
emails: [
...data.emails.map((email) => ({
email: email.email,
role: getUserRole(email.role),
role: getUserRole(email.role as unknown as EUserPermissions),
})),
],
project_id: undefined,
@ -86,7 +89,7 @@ const WorkspaceMembersSettingsPage = observer(() => {
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Members` : undefined;
// if user is not authorized to view this page
if (currentWorkspaceRole && !canPerformWorkspaceViewerActions) {
if (workspaceUserInfo && !canPerformWorkspaceMemberActions) {
return <NotAuthorizedView section="settings" />;
}

View file

@ -1,11 +1,10 @@
import { observer } from "mobx-react";
import { useParams, usePathname } from "next/navigation";
// constants
import { EUserWorkspaceRoles } from "@/constants/workspace";
// hooks
import { useUser } from "@/hooks/store";
import { useUserPermissions } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
// plane web constants
import { EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
import { WORKSPACE_SETTINGS_LINKS } from "@/plane-web/constants/workspace";
// plane web helpers
import { shouldRenderSettingLink } from "@/plane-web/helpers/workspace.helper";
@ -15,19 +14,14 @@ export const MobileWorkspaceSettingsTabs = observer(() => {
const { workspaceSlug } = useParams();
const pathname = usePathname();
// mobx store
const {
membership: { currentWorkspaceRole },
} = useUser();
// derived values
const workspaceMemberInfo = currentWorkspaceRole || EUserWorkspaceRoles.GUEST;
const { allowPermissions } = useUserPermissions();
return (
<div className="flex-shrink-0 md:hidden sticky inset-0 flex overflow-x-auto bg-custom-background-100 z-10">
{WORKSPACE_SETTINGS_LINKS.map(
(item, index) =>
shouldRenderSettingLink(item.key) &&
workspaceMemberInfo >= item.access && (
allowPermissions(item.access, EUserPermissionsLevel.WORKSPACE, workspaceSlug.toString()) && (
<div
className={`${
item.highlight(pathname, `/${workspaceSlug}`)

View file

@ -6,11 +6,10 @@ import Link from "next/link";
import { useParams, usePathname } from "next/navigation";
// components
import { SidebarNavItem } from "@/components/sidebar";
// constants
import { EUserWorkspaceRoles } from "@/constants/workspace";
// hooks
import { useUser } from "@/hooks/store";
import { useUserPermissions } from "@/hooks/store";
// plane web constants
import { EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
import { WORKSPACE_SETTINGS_LINKS } from "@/plane-web/constants/workspace";
// plane web helpers
import { shouldRenderSettingLink } from "@/plane-web/helpers/workspace.helper";
@ -20,12 +19,7 @@ export const WorkspaceSettingsSidebar = observer(() => {
const { workspaceSlug } = useParams();
const pathname = usePathname();
// mobx store
const {
membership: { currentWorkspaceRole },
} = useUser();
// derived values
const workspaceMemberInfo = currentWorkspaceRole || EUserWorkspaceRoles.GUEST;
const { allowPermissions } = useUserPermissions();
return (
<div className="flex w-[280px] flex-col gap-6">
@ -35,7 +29,7 @@ export const WorkspaceSettingsSidebar = observer(() => {
{WORKSPACE_SETTINGS_LINKS.map(
(link) =>
shouldRenderSettingLink(link.key) &&
workspaceMemberInfo >= link.access && (
allowPermissions(link.access, EUserPermissionsLevel.WORKSPACE, workspaceSlug.toString()) && (
<Link key={link.key} href={`/${workspaceSlug}${link.href}`}>
<SidebarNavItem
key={link.key}

View file

@ -12,7 +12,8 @@ import { LogoSpinner } from "@/components/common";
import { PageHead } from "@/components/core";
import { DeleteWebhookModal, WebhookDeleteSection, WebhookForm } from "@/components/web-hooks";
// hooks
import { useUser, useWebhook, useWorkspace } from "@/hooks/store";
import { useUserPermissions, useWebhook, useWorkspace } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
const WebhookDetailsPage = observer(() => {
// states
@ -20,18 +21,17 @@ const WebhookDetailsPage = observer(() => {
// router
const { workspaceSlug, webhookId } = useParams();
// mobx store
const {
membership: { currentWorkspaceRole },
} = useUser();
const { currentWebhook, fetchWebhookById, updateWebhook } = useWebhook();
const { currentWorkspace } = useWorkspace();
const { allowPermissions } = useUserPermissions();
// TODO: fix this error
// useEffect(() => {
// if (isCreated !== "true") clearSecretKey();
// }, [clearSecretKey, isCreated]);
const isAdmin = currentWorkspaceRole === 20;
// derived values
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Webhook` : undefined;
useSWR(

View file

@ -15,7 +15,8 @@ import { WebhooksList, CreateWebhookModal } from "@/components/web-hooks";
// constants
import { EmptyStateType } from "@/constants/empty-state";
// hooks
import { useUser, useWebhook, useWorkspace } from "@/hooks/store";
import { useUserPermissions, useWebhook, useWorkspace } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
const WebhooksListPage = observer(() => {
// states
@ -23,13 +24,13 @@ const WebhooksListPage = observer(() => {
// router
const { workspaceSlug } = useParams();
// mobx store
const {
canPerformWorkspaceAdminActions,
membership: { currentWorkspaceRole },
} = useUser();
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
const { fetchWebhooks, webhooks, clearSecretKey, webhookSecretKey, createWebhook } = useWebhook();
const { currentWorkspace } = useWorkspace();
const canPerformWorkspaceAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
useSWR(
workspaceSlug && canPerformWorkspaceAdminActions ? `WEBHOOKS_LIST_${workspaceSlug}` : null,
workspaceSlug && canPerformWorkspaceAdminActions ? () => fetchWebhooks(workspaceSlug.toString()) : null
@ -42,7 +43,7 @@ const WebhooksListPage = observer(() => {
if (!showCreateWebhookModal && webhookSecretKey) clearSecretKey();
}, [showCreateWebhookModal, webhookSecretKey, clearSecretKey]);
if (currentWorkspaceRole && !canPerformWorkspaceAdminActions) {
if (workspaceUserInfo && !canPerformWorkspaceAdminActions) {
return <NotAuthorizedView section="settings" />;
}