fix(UI): redirect if user is not permissioned to see workspace (#6786)

* fix(UI): redirect if user is not permissioned to see workspace

* fix tests
This commit is contained in:
Kira Pilot
2023-03-27 06:25:31 -07:00
committed by GitHub
parent 08afe3cfad
commit fc21e159b8
5 changed files with 125 additions and 60 deletions
+27
View File
@@ -0,0 +1,27 @@
import { useQuery } from "@tanstack/react-query"
import { checkAuthorization } from "api/api"
export const useReadPagePermissions = (
resource_type: string,
resource_id?: string,
enabled = true,
) => {
const queryKey = ["readPagePermissions", resource_type, resource_id]
const params = {
checks: {
readPagePermissions: {
object: {
resource_type,
resource_id,
},
action: "read",
},
},
}
return useQuery({
queryKey,
queryFn: () => checkAuthorization(params),
enabled,
})
}
@@ -48,6 +48,10 @@ describe("AuditPage", () => {
const mock = jest.spyOn(CreateDayString, "createDayString")
mock.mockImplementation(() => "a minute ago")
jest.spyOn(API, "checkAuthorization").mockResolvedValue({
readPagePermissions: true,
})
// Mock the entitlements
server.use(
rest.get("/api/v2/entitlements", (req, res, ctx) => {
+33 -18
View File
@@ -11,8 +11,15 @@ import { pageTitle } from "util/page"
import { auditMachine } from "xServices/audit/auditXService"
import { PaginationMachineRef } from "xServices/pagination/paginationXService"
import { AuditPageView } from "./AuditPageView"
import { RequirePermission } from "components/RequirePermission/RequirePermission"
import { useReadPagePermissions } from "hooks/useReadPagePermissions"
import { Loader } from "components/Loader/Loader"
const AuditPage: FC = () => {
// we call the below hook to make sure the user has access to view the page
const { data: permissions, isLoading: isLoadingPermissions } =
useReadPagePermissions("audit_log")
const [searchParams, setSearchParams] = useSearchParams()
const filter = searchParams.get("filter") ?? ""
const [auditState, auditSend] = useMachine(auditMachine, {
@@ -28,26 +35,34 @@ const AuditPage: FC = () => {
const { auditLogs, count, apiError } = auditState.context
const paginationRef = auditState.context.paginationRef as PaginationMachineRef
const { audit_log: isAuditLogVisible } = useFeatureVisibility()
const { audit_log: isAuditLogEnabled } = useFeatureVisibility()
if (!permissions || isLoadingPermissions) {
return <Loader />
}
return (
<>
<Helmet>
<title>{pageTitle("Audit")}</title>
</Helmet>
<AuditPageView
filter={filter}
auditLogs={auditLogs}
count={count}
onFilter={(filter) => {
auditSend("FILTER", { filter })
}}
paginationRef={paginationRef}
isNonInitialPage={nonInitialPage(searchParams)}
isAuditLogVisible={isAuditLogVisible}
error={apiError}
/>
</>
<RequirePermission
isFeatureVisible={isAuditLogEnabled && permissions.readPagePermissions}
>
<>
<Helmet>
<title>{pageTitle("Audit")}</title>
</Helmet>
<AuditPageView
filter={filter}
auditLogs={auditLogs}
count={count}
onFilter={(filter) => {
auditSend("FILTER", { filter })
}}
paginationRef={paginationRef}
isNonInitialPage={nonInitialPage(searchParams)}
isAuditLogVisible={isAuditLogEnabled}
error={apiError}
/>
</>
</RequirePermission>
)
}
@@ -31,6 +31,9 @@ const { t } = i18next
// It renders the workspace page and waits for it be loaded
const renderWorkspacePage = async () => {
jest.spyOn(api, "checkAuthorization").mockResolvedValue({
readPagePermissions: true,
})
jest.spyOn(api, "getTemplate").mockResolvedValueOnce(MockTemplate)
jest.spyOn(api, "getTemplateVersionRichParameters").mockResolvedValueOnce([])
renderWithAuth(<WorkspacePage />, {
@@ -191,6 +194,9 @@ describe("WorkspacePage", () => {
it("updates the parameters when they are missing during update", async () => {
// Setup mocks
const user = userEvent.setup()
jest.spyOn(api, "checkAuthorization").mockResolvedValue({
readPagePermissions: true,
})
jest
.spyOn(api, "getWorkspaceByOwnerAndName")
.mockResolvedValueOnce(MockOutdatedWorkspace)
+55 -42
View File
@@ -6,11 +6,14 @@ import { Loader } from "components/Loader/Loader"
import { FC, useEffect } from "react"
import { useParams } from "react-router-dom"
import { firstOrItem } from "util/array"
import { quotaMachine } from "xServices/quotas/quotasXService"
import { workspaceMachine } from "xServices/workspace/workspaceXService"
import { WorkspaceReadyPage } from "./WorkspaceReadyPage"
import { quotaMachine } from "xServices/quotas/quotasXService"
import { RequirePermission } from "components/RequirePermission/RequirePermission"
import { useReadPagePermissions } from "hooks/useReadPagePermissions"
export const WorkspacePage: FC = () => {
const styles = useStyles()
const { username: usernameQueryParam, workspace: workspaceQueryParam } =
useParams()
const username = firstOrItem(usernameQueryParam, null)
@@ -23,9 +26,13 @@ export const WorkspacePage: FC = () => {
getTemplateParametersWarning,
checkPermissionsError,
} = workspaceState.context
// we call the below hook to make sure the user has access to view the page
const { data: permissions, isLoading: isLoadingPermissions } =
useReadPagePermissions("workspace", workspace?.id)
const [quotaState, quotaSend] = useMachine(quotaMachine)
const { getQuotaError } = quotaState.context
const styles = useStyles()
/**
* Get workspace, template, and organization on mount and whenever workspaceId changes.
@@ -41,47 +48,53 @@ export const WorkspacePage: FC = () => {
username && quotaSend({ type: "GET_QUOTA", username })
}, [username, quotaSend])
if (!permissions || isLoadingPermissions) {
return <Loader />
}
return (
<ChooseOne>
<Cond condition={workspaceState.matches("error")}>
<div className={styles.error}>
{Boolean(getWorkspaceError) && (
<AlertBanner severity="error" error={getWorkspaceError} />
)}
{Boolean(getTemplateWarning) && (
<AlertBanner severity="error" error={getTemplateWarning} />
)}
{Boolean(getTemplateParametersWarning) && (
<AlertBanner
severity="error"
error={getTemplateParametersWarning}
/>
)}
{Boolean(checkPermissionsError) && (
<AlertBanner severity="error" error={checkPermissionsError} />
)}
{Boolean(getQuotaError) && (
<AlertBanner severity="error" error={getQuotaError} />
)}
</div>
</Cond>
<Cond
condition={
Boolean(workspace) &&
workspaceState.matches("ready") &&
quotaState.matches("success")
}
>
<WorkspaceReadyPage
workspaceState={workspaceState}
quotaState={quotaState}
workspaceSend={workspaceSend}
/>
</Cond>
<Cond>
<Loader />
</Cond>
</ChooseOne>
<RequirePermission isFeatureVisible={permissions.readPagePermissions}>
<ChooseOne>
<Cond condition={workspaceState.matches("error")}>
<div className={styles.error}>
{Boolean(getWorkspaceError) && (
<AlertBanner severity="error" error={getWorkspaceError} />
)}
{Boolean(getTemplateWarning) && (
<AlertBanner severity="error" error={getTemplateWarning} />
)}
{Boolean(getTemplateParametersWarning) && (
<AlertBanner
severity="error"
error={getTemplateParametersWarning}
/>
)}
{Boolean(checkPermissionsError) && (
<AlertBanner severity="error" error={checkPermissionsError} />
)}
{Boolean(getQuotaError) && (
<AlertBanner severity="error" error={getQuotaError} />
)}
</div>
</Cond>
<Cond
condition={
Boolean(workspace) &&
workspaceState.matches("ready") &&
quotaState.matches("success")
}
>
<WorkspaceReadyPage
workspaceState={workspaceState}
quotaState={quotaState}
workspaceSend={workspaceSend}
/>
</Cond>
<Cond>
<Loader />
</Cond>
</ChooseOne>
</RequirePermission>
)
}