diff --git a/services/app-api/src/resolvers/rate/fetchRate.ts b/services/app-api/src/resolvers/rate/fetchRate.ts index 90357d8ed4..e3a61991e3 100644 --- a/services/app-api/src/resolvers/rate/fetchRate.ts +++ b/services/app-api/src/resolvers/rate/fetchRate.ts @@ -4,9 +4,12 @@ import { setSuccessAttributesOnActiveSpan, } from '../attributeHelper' import { NotFoundError } from '../../postgres' -import type { QueryResolvers } from '../../gen/gqlServer' +import type { QueryResolvers, State } from '../../gen/gqlServer' import type { Store } from '../../postgres' import { GraphQLError } from 'graphql' +import { isStateUser } from '../../domain-models' +import { logError } from '../../logger' +import { ForbiddenError } from 'apollo-server-core' export function fetchRateResolver(store: Store): QueryResolvers['fetchRate'] { return async (_parent, { input }, context) => { @@ -35,6 +38,23 @@ export function fetchRateResolver(store: Store): QueryResolvers['fetchRate'] { }) } + if (isStateUser(user)) { + const stateForCurrentUser: State['code'] = user.stateCode + if (rateWithHistory.stateCode !== stateForCurrentUser) { + logError( + 'fetchRate', + 'State users are not authorized to fetch rate data from a different state.' + ) + setErrorAttributesOnActiveSpan( + 'State users are not authorized to fetch rate data from a different state.', + span + ) + throw new ForbiddenError( + 'State users are not authorized to fetch rate data from a different state.' + ) + } + } + setSuccessAttributesOnActiveSpan(span) return { rate: rateWithHistory } } diff --git a/services/app-web/src/pages/Errors/ErrorForbiddenPage.tsx b/services/app-web/src/pages/Errors/ErrorForbiddenPage.tsx new file mode 100644 index 0000000000..7841f4448b --- /dev/null +++ b/services/app-web/src/pages/Errors/ErrorForbiddenPage.tsx @@ -0,0 +1,28 @@ +import React from 'react' +import styles from './Errors.module.scss' +import { GridContainer } from '@trussworks/react-uswds' +import { PageHeading } from '../../components' + +interface ForbiddenErrorPageProps { + errorMsg?: string +} + +export const ErrorForbiddenPage = ({ + errorMsg, +}: ForbiddenErrorPageProps): React.ReactElement => { + return ( +
+ + Forbidden + {errorMsg ? ( +

{errorMsg}

+ ) : ( +

+ You do not have permission to view the requested file or + resource. +

+ )} +
+
+ ) +} diff --git a/services/app-web/src/pages/RateEdit/RateEdit.tsx b/services/app-web/src/pages/RateEdit/RateEdit.tsx index f4d6b89b07..57e379166a 100644 --- a/services/app-web/src/pages/RateEdit/RateEdit.tsx +++ b/services/app-web/src/pages/RateEdit/RateEdit.tsx @@ -14,6 +14,8 @@ import { RouteT, RoutesRecord } from '../../constants' import { PageBannerAlerts } from '../StateSubmission/StateSubmissionForm' import { useAuth } from '../../contexts/AuthContext' import { FormContainer } from '../StateSubmission/FormContainer' +import { ErrorForbiddenPage } from '../Errors/ErrorForbiddenPage' +import { Error404 } from '../Errors/Error404Page' export type SubmitOrUpdateRate = ( rateID: string, @@ -84,7 +86,20 @@ export const RateEdit = (): React.ReactElement => { ) } else if (fetchError || !rate) { - return + //error handling for a state user that tries to access rates for a different state + if (fetchError?.graphQLErrors[0]?.extensions?.code === 'FORBIDDEN') { + return ( + + ) + } else if ( + fetchError?.graphQLErrors[0]?.extensions?.code === 'NOT_FOUND' + ) { + return + } else { + return + } } if (rate.status !== 'UNLOCKED') { diff --git a/services/app-web/src/pages/RateSummary/RateSummary.tsx b/services/app-web/src/pages/RateSummary/RateSummary.tsx index 3d02150aa7..e2c9cee06c 100644 --- a/services/app-web/src/pages/RateSummary/RateSummary.tsx +++ b/services/app-web/src/pages/RateSummary/RateSummary.tsx @@ -10,6 +10,8 @@ import { GenericErrorPage } from '../Errors/GenericErrorPage' import { RoutesRecord } from '../../constants' import { SingleRateSummarySection } from '../../components/SubmissionSummarySection/RateDetailsSummarySection/SingleRateSummarySection' import { useAuth } from '../../contexts/AuthContext' +import { ErrorForbiddenPage } from '../Errors/ErrorForbiddenPage' +import { Error404 } from '../Errors/Error404Page' type RouteParams = { id: string @@ -50,7 +52,16 @@ export const RateSummary = (): React.ReactElement => { ) } else if (error || !rate || !currentRateRev?.formData) { - return + //error handling for a state user that tries to access rates for a different state + if (error?.graphQLErrors[0]?.extensions?.code === 'FORBIDDEN') { + return ( + + ) + } else if (error?.graphQLErrors[0]?.extensions?.code === 'NOT_FOUND') { + return + } else { + return + } } // Redirecting a state user to the edit page if rate is unlocked