Skip to content

Commit

Permalink
fix: restrict viewer permission url access
Browse files Browse the repository at this point in the history
  • Loading branch information
joonatank committed Jan 20, 2025
1 parent 4418dd4 commit e4972a2
Show file tree
Hide file tree
Showing 3 changed files with 193 additions and 12 deletions.
103 changes: 103 additions & 0 deletions apps/admin-ui/gql/gql-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8408,6 +8408,20 @@ export type ReservationsQuery = {
} | null;
};

export type GetReservationPermissionsQueryVariables = Exact<{
id: Scalars["ID"]["input"];
}>;

export type GetReservationPermissionsQuery = {
reservation?: {
id: string;
reservationUnits: Array<{
id: string;
unit?: { id: string; pk?: number | null } | null;
}>;
} | null;
};

export type DeleteResourceMutationVariables = Exact<{
input: ResourceDeleteMutationInput;
}>;
Expand Down Expand Up @@ -15502,6 +15516,95 @@ export type ReservationsQueryResult = Apollo.QueryResult<
ReservationsQuery,
ReservationsQueryVariables
>;
export const GetReservationPermissionsDocument = gql`
query GetReservationPermissions($id: ID!) {
reservation(id: $id) {
id
reservationUnits {
id
unit {
id
pk
}
}
}
}
`;

/**
* __useGetReservationPermissionsQuery__
*
* To run a query within a React component, call `useGetReservationPermissionsQuery` and pass it any options that fit your needs.
* When your component renders, `useGetReservationPermissionsQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useGetReservationPermissionsQuery({
* variables: {
* id: // value for 'id'
* },
* });
*/
export function useGetReservationPermissionsQuery(
baseOptions: Apollo.QueryHookOptions<
GetReservationPermissionsQuery,
GetReservationPermissionsQueryVariables
> &
(
| { variables: GetReservationPermissionsQueryVariables; skip?: boolean }
| { skip: boolean }
)
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useQuery<
GetReservationPermissionsQuery,
GetReservationPermissionsQueryVariables
>(GetReservationPermissionsDocument, options);
}
export function useGetReservationPermissionsLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<
GetReservationPermissionsQuery,
GetReservationPermissionsQueryVariables
>
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useLazyQuery<
GetReservationPermissionsQuery,
GetReservationPermissionsQueryVariables
>(GetReservationPermissionsDocument, options);
}
export function useGetReservationPermissionsSuspenseQuery(
baseOptions?:
| Apollo.SkipToken
| Apollo.SuspenseQueryHookOptions<
GetReservationPermissionsQuery,
GetReservationPermissionsQueryVariables
>
) {
const options =
baseOptions === Apollo.skipToken
? baseOptions
: { ...defaultOptions, ...baseOptions };
return Apollo.useSuspenseQuery<
GetReservationPermissionsQuery,
GetReservationPermissionsQueryVariables
>(GetReservationPermissionsDocument, options);
}
export type GetReservationPermissionsQueryHookResult = ReturnType<
typeof useGetReservationPermissionsQuery
>;
export type GetReservationPermissionsLazyQueryHookResult = ReturnType<
typeof useGetReservationPermissionsLazyQuery
>;
export type GetReservationPermissionsSuspenseQueryHookResult = ReturnType<
typeof useGetReservationPermissionsSuspenseQuery
>;
export type GetReservationPermissionsQueryResult = Apollo.QueryResult<
GetReservationPermissionsQuery,
GetReservationPermissionsQueryVariables
>;
export const DeleteResourceDocument = gql`
mutation DeleteResource($input: ResourceDeleteMutationInput!) {
deleteResource(input: $input) {
Expand Down
2 changes: 1 addition & 1 deletion apps/admin-ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ function ClientApp({
<Route
path="/reservations/*"
element={withAuthorization(
<ReservationsRouter />,
<ReservationsRouter apiBaseUrl={apiBaseUrl} feedbackUrl={feedbackUrl} />,

Check failure on line 130 in apps/admin-ui/src/App.tsx

View workflow job for this annotation

GitHub Actions / Lint admin ui

Replace `·apiBaseUrl={apiBaseUrl}·feedbackUrl={feedbackUrl}` with `⏎················apiBaseUrl={apiBaseUrl}⏎················feedbackUrl={feedbackUrl}⏎·············`
apiBaseUrl,
feedbackUrl
)}
Expand Down
100 changes: 89 additions & 11 deletions apps/admin-ui/src/spa/reservations/router.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,99 @@
import React from "react";
import { Route, Routes } from "react-router-dom";
import { Route, Routes, useParams } from "react-router-dom";
import { RequestedPage } from "./requested";
import { ListReservationsPage } from ".";
import { EditPage } from "./[id]/edit";
import { ReservationPage } from "./[id]";
import { SeriesPage } from "./[id]/series";
import { useGetReservationPermissionsQuery, UserPermissionChoice } from "@gql/gql-types";

Check failure on line 8 in apps/admin-ui/src/spa/reservations/router.tsx

View workflow job for this annotation

GitHub Actions / Lint admin ui

Replace `·useGetReservationPermissionsQuery,·UserPermissionChoice·` with `⏎··useGetReservationPermissionsQuery,⏎··UserPermissionChoice,⏎`
import { gql } from "@apollo/client";
import { base64encode, filterNonNullable } from "common/src/helpers";
import { useCheckPermission } from "@/hooks";
import Error403 from "@/common/Error403";
import { CenterSpinner } from "common/styles/util";

// TODO there is no index? (all and requested works like index but not really)
const ReservationsRouter = (): JSX.Element => (
<Routes>
<Route index element={<ListReservationsPage />} />
<Route path="requested" element={<RequestedPage />} />
<Route path="all" element={<ListReservationsPage />} />
<Route path=":id" element={<ReservationPage />} />
<Route path=":id/edit" element={<EditPage />} />
<Route path=":id/series" element={<SeriesPage />} />
</Routes>
);
function ReservationsRouter({
apiBaseUrl,
feedbackUrl,
}: RouterProps): JSX.Element {

Check failure on line 20 in apps/admin-ui/src/spa/reservations/router.tsx

View workflow job for this annotation

GitHub Actions / Lint admin ui

Delete `⏎`
return (
<Routes>
<Route index element={<ListReservationsPage />} />
<Route path="requested" element={<RequestedPage />} />
<Route path="all" element={<ListReservationsPage />} />
<Route path=":id" element={<ReservationPage />} />
<Route
path=":id/edit"
element={<PermCheckedRoute

Check failure on line 29 in apps/admin-ui/src/spa/reservations/router.tsx

View workflow job for this annotation

GitHub Actions / Lint admin ui

Insert `⏎··········`
element={<EditPage />}

Check failure on line 30 in apps/admin-ui/src/spa/reservations/router.tsx

View workflow job for this annotation

GitHub Actions / Lint admin ui

Insert `··`
apiBaseUrl={apiBaseUrl}

Check failure on line 31 in apps/admin-ui/src/spa/reservations/router.tsx

View workflow job for this annotation

GitHub Actions / Lint admin ui

Insert `··`
feedbackUrl={feedbackUrl}

Check failure on line 32 in apps/admin-ui/src/spa/reservations/router.tsx

View workflow job for this annotation

GitHub Actions / Lint admin ui

Insert `··`
/> }

Check failure on line 33 in apps/admin-ui/src/spa/reservations/router.tsx

View workflow job for this annotation

GitHub Actions / Lint admin ui

Replace `/>` with `··/>⏎·······`
/>
<Route
path=":id/series"
element={<PermCheckedRoute

Check failure on line 37 in apps/admin-ui/src/spa/reservations/router.tsx

View workflow job for this annotation

GitHub Actions / Lint admin ui

Insert `⏎··········`
element={<SeriesPage />}
apiBaseUrl={apiBaseUrl}
feedbackUrl={feedbackUrl}
/>}
/>
</Routes>
);
}

export const GET_RESERVATION_PERMISSION_QUERY = gql`
query GetReservationPermissions($id: ID!) {
reservation(id: $id) {
id
reservationUnits {
id
unit {
id
pk
}
}
}
}
`;

function useCheckReservationPermissions(pk?: string) {
const id = base64encode(`ReservationNode:${pk}`);
const { data, loading: qLoading } = useGetReservationPermissionsQuery({
variables: { id },
skip: !pk,
});
const units = filterNonNullable(data?.reservation?.reservationUnits.map((ru) => ru.unit?.pk));
const { hasPermission, isLoading } = useCheckPermission({
units,
permission: UserPermissionChoice.CanManageReservations,
});
return { hasPermission, loading: qLoading || isLoading };
}

type RouterProps = {
apiBaseUrl: string;
feedbackUrl: string;
}

type PermCheckedRouteProps = {
element: JSX.Element;
} & RouterProps;

/// Custom permission checks that use backend queries for the check
/// requires the query because we don't known the unit the reservation is for
function PermCheckedRoute({ element, apiBaseUrl, feedbackUrl }: PermCheckedRouteProps) {
const { id } = useParams();
const { hasPermission, loading } = useCheckReservationPermissions(id);
if (loading) {
return <CenterSpinner />;
}
if (!hasPermission) {
return <Error403 apiBaseUrl={apiBaseUrl} feedbackUrl={feedbackUrl} />;
}
return element;
}

export default ReservationsRouter;

0 comments on commit e4972a2

Please sign in to comment.