Skip to content

Commit

Permalink
feat: index and login-redirect pages follow redirection rules
Browse files Browse the repository at this point in the history
  • Loading branch information
matthieu-foucault committed Nov 5, 2021
1 parent 1dc9230 commit 02496e5
Show file tree
Hide file tree
Showing 22 changed files with 355 additions and 151 deletions.
91 changes: 0 additions & 91 deletions app/components/PageRedirectHandler.tsx

This file was deleted.

56 changes: 56 additions & 0 deletions app/components/SessionExpiryHandler.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React, { useEffect, useState } from "react";
import { useRouter } from "next/router";
import type { SessionExpiryHandlerQuery } from "__generated__/SessionExpiryHandlerQuery.graphql";
import { graphql, fetchQuery, useRelayEnvironment } from "react-relay";
import { SessionTimeoutHandler, SessionRefresher } from "@bcgov-cas/sso-react";

const sessionQuery = graphql`
query SessionExpiryHandlerQuery {
session {
__typename
}
}
`;

const SessionExpiryHandler: React.FC = () => {
const [hasSession, setHasSession] = useState(false);

const router = useRouter();
const environment = useRelayEnvironment();

useEffect(() => {
const checkSessionAndGroups = async () => {
const { session } = await fetchQuery<SessionExpiryHandlerQuery>(
environment,
sessionQuery,
{}
).toPromise();
setHasSession(!!session);
};
checkSessionAndGroups();
// This effect only needs to be run once on mount, even if the relay environment changes
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

const handleSessionExpired = () => {
router.push({
pathname: "/login-redirect",
query: {
redirectTo: router.asPath,
sessionIdled: true,
},
});
};

if (hasSession)
return (
<>
<SessionRefresher />
<SessionTimeoutHandler onSessionExpired={handleSessionExpired} />
</>
);

return null;
};

export default SessionExpiryHandler;
6 changes: 0 additions & 6 deletions app/data/group-constants.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
export const GUEST = "Guest";
export const NON_IDIR_USER = "NON_IDIR_USER";
export const UNAUTHORIZED_IDIR_USER = "UNAUTHORIZED_IDIR_USER";
export const REALM_ADMINISTRATOR = "Realm Administrator";

export const CIF_INTERNAL = "cif_internal";

export const CIF_EXTERNAL = "cif_external";

export const CIF_ADMIN = "cif_admin";


export const ADMIN_ROLES = [REALM_ADMINISTRATOR, CIF_ADMIN];

2 changes: 1 addition & 1 deletion app/data/groups.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"cif_internal": {
"pgRole": "cif_internal",
"priority": 0,
"path": "/dashboard"
"path": "/internal"
},
"cif_admin": {
"pgRole": "cif_admin",
Expand Down
12 changes: 11 additions & 1 deletion app/data/pagesAuthorization.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
[
{
"routePaths": ["/", "/login-redirect", "/login"],
"routePaths": ["/", "/login-redirect"],
"isProtected": false
},
{
"routePaths": ["/admin(.*)"],
"isProtected": true,
"allowedRoles": ["cif_admin", "Realm Administrator"]
},
{
"routePaths": ["/internal"],
"isProtected": true,
"allowedRoles": ["cif_internal"]
},
{
"routePaths": ["/internal/(.*)"],
"isProtected": true,
"allowedRoles": ["cif_internal", "cif_admin", "Realm Administrator"]
}
]
4 changes: 1 addition & 3 deletions app/lib/authorization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,14 @@ export const isRouteAuthorized = (
) => {
const authRules = pagesAuthorization.find(({ routePaths }) =>
routePaths.some((routePath) =>
match(routePath, { decode: decodeURIComponent })(route)
match(routePath, { decode: decodeURIComponent, endsWith: "?" })(route)
)
);

if (!authRules) {
return false;
}

console.log(authRules);

const { isProtected, allowedRoles = [] } = authRules;

if (!isProtected) {
Expand Down
3 changes: 0 additions & 3 deletions app/lib/relay/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ import { Network, Environment, Store, RecordSource } from "relay-runtime";

export function createServerNetwork({ cookieHeader }) {
return Network.create(async (params, variables) => {
console.log(cookieHeader);
console.log("params", params);
console.log("variables", variables);
const response = await fetch("http://localhost:3004/graphql", {
method: "POST",
credentials: "include",
Expand Down
15 changes: 9 additions & 6 deletions app/lib/relay/withRelayOptions.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
import { getClientEnvironment } from "./client";
import { getUserGroups } from "server/helpers/userGroupAuthentication";
import { getUserGroupLandingRoute } from "lib/userGroups";
import { isRouteAuthorized } from "lib/authorization";
import type { NextPageContext } from "next";
import { WiredOptions } from "relay-nextjs/wired/component";

const withRelayOptions = {
const withRelayOptions: WiredOptions<any> = {
fallback: <div>Loading...</div>,
createClientEnvironment: () => getClientEnvironment()!,
createServerEnvironment: async (ctx) => {
createServerEnvironment: async (ctx: NextPageContext) => {
const { createServerEnvironment } = await import("./server");
return createServerEnvironment({ cookieHeader: ctx.req.headers.cookie });
},
serverSideProps: async (ctx) => {
serverSideProps: async (ctx: NextPageContext) => {
// Server-side redirection of the user to their landing route, if they are logged in
const { getUserGroups } = await import("server/helpers/userGroupAuthentication");

const groups = getUserGroups(ctx.req);
const isAuthorized = isRouteAuthorized(ctx.req.path, groups);
const isAuthorized = isRouteAuthorized(ctx.req.url, groups);

if (isAuthorized) return {};

if (groups.length === 0) {
return {
redirect: {
destination: `/login-redirect?redirectTo=${encodeURIComponent(
ctx.req.path
ctx.req.url
)}`,
},
};
Expand Down
6 changes: 3 additions & 3 deletions app/lib/userGroups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const compactGroups = (groups) => {
return groups.filter((group) => allDefinedGroups.includes(group));
};

export const getPriorityGroupData = (validGroups) => {
export const getPriorityGroupData = (validGroups: readonly string[]) => {
// Find the highest priority group
let priorityGroup = {
name: "Guest",
Expand All @@ -29,14 +29,14 @@ export const getPriorityGroupData = (validGroups) => {
return priorityGroup;
};

export const getPriorityGroup = (groupNames) => {
export const getPriorityGroup = (groupNames: readonly string[]) => {
const validGroups = compactGroups(groupNames);
const priorityGroupData = getPriorityGroupData(validGroups);

return priorityGroupData.name;
};

export const getUserGroupLandingRoute = (groupNames) => {
export const getUserGroupLandingRoute = (groupNames: readonly string[]) => {
const validGroups = compactGroups(groupNames);
const priorityGroupData = getPriorityGroupData(validGroups);

Expand Down
1 change: 1 addition & 0 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"jest": "^27.3.1",
"nodemon": "^2.0.13",
"prettier": "^2.4.1",
"react-test-renderer": "^17.0.2",
"ts-node": "^10.3.0",
"typescript": "^4.4.4"
},
Expand Down
4 changes: 2 additions & 2 deletions app/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { getInitialPreloadedQuery, getRelayProps } from "relay-nextjs/app";
import { getClientEnvironment } from "../lib/relay/client";
import "normalize.css";
import BCGovTypography from "components/BCGovTypography";
import PageRedirectHandler from "components/PageRedirectHandler";
import SessionExpiryHandler from "components/SessionExpiryHandler";

const clientEnv = getClientEnvironment();
const initialPreloadedQuery = getInitialPreloadedQuery({
Expand All @@ -17,7 +17,7 @@ function MyApp({ Component, pageProps }: AppProps) {

return (
<RelayEnvironmentProvider environment={env}>
{typeof window !== "undefined" && <PageRedirectHandler />}
{typeof window !== "undefined" && <SessionExpiryHandler />}
<BCGovTypography />
<Component {...pageProps} {...relayProps} />
</RelayEnvironmentProvider>
Expand Down
2 changes: 1 addition & 1 deletion app/pages/_document.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class MyDocument extends Document<DocumentProps> {
<link rel="icon" href="/icons/bcid-favicon-32x32.png" />
<relayDocument.Script />
</Head>
<title>cif</title>
<title>CleanBC Industry Fund</title>
<body>
<Main />
<NextScript />
Expand Down
10 changes: 5 additions & 5 deletions app/pages/admin/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import DefaultLayout from "components/Layout/DefaultLayout";
import { withRelay, RelayProps } from "relay-nextjs";
import { graphql, usePreloadedQuery } from "react-relay/hooks";
import Link from "next/link";
import { adminQuery } from "__generated__/adminQuery.graphql";
import { adminLandingQuery } from "__generated__/adminLandingQuery.graphql";
import withRelayOptions from "lib/relay/withRelayOptions";

const AdminQuery = graphql`
query adminQuery {
query adminLandingQuery {
query {
session {
...DefaultLayout_session
Expand All @@ -15,10 +15,10 @@ const AdminQuery = graphql`
}
`;

function Users({ preloadedQuery }: RelayProps<{}, adminQuery>) {
function AdminLanding({ preloadedQuery }: RelayProps<{}, adminLandingQuery>) {
const { query } = usePreloadedQuery(AdminQuery, preloadedQuery);
return (
<DefaultLayout session={query.session}>
<DefaultLayout session={query.session} title="CIF Projects Administration">
<Link href="admin/users">
<a>Users</a>
</Link>
Expand All @@ -30,4 +30,4 @@ function Users({ preloadedQuery }: RelayProps<{}, adminQuery>) {
);
}

export default withRelay(Users, AdminQuery, withRelayOptions);
export default withRelay(AdminLanding, AdminQuery, withRelayOptions);
25 changes: 0 additions & 25 deletions app/pages/dashboard.tsx

This file was deleted.

Loading

0 comments on commit 02496e5

Please sign in to comment.