Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature : Added server side verification for all routes . #3491

Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[Admin Docs](/)

***

# Variable: VERIFY\_ROLE

> `const` **VERIFY\_ROLE**: `DocumentNode`

Defined in: [src/GraphQl/Queries/Queries.ts:867](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/GraphQl/Queries/Queries.ts#L867)
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[Admin Docs](/)

***

# Enumeration: UserRole

Defined in: [src/components/CheckIn/types.ts:45](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/components/CheckIn/types.ts#L45)

## Enumeration Members

### ADMIN

> **ADMIN**: `"admin"`

Defined in: [src/components/CheckIn/types.ts:47](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/components/CheckIn/types.ts#L47)

***

### SUPER\_ADMIN

> **SUPER\_ADMIN**: `"superAdmin"`

Defined in: [src/components/CheckIn/types.ts:48](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/components/CheckIn/types.ts#L48)

***

### USER

> **USER**: `"user"`

Defined in: [src/components/CheckIn/types.ts:46](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/components/CheckIn/types.ts#L46)
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[Admin Docs](/)

***

# Type Alias: VerifyRoleResponse

> **VerifyRoleResponse**: `object`

Defined in: [src/components/CheckIn/types.ts:51](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/components/CheckIn/types.ts#L51)

## Type declaration

### verifyRole

> **verifyRole**: `object`

#### verifyRole.isAuthorized

> **isAuthorized**: `boolean`

#### verifyRole.role

> **role**: [`UserRole`](../enumerations/UserRole.md)
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

> **default**(): `Element`

Defined in: [src/components/SecuredRoute/SecuredRoute.tsx:16](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/components/SecuredRoute/SecuredRoute.tsx#L16)
Defined in: [src/components/SecuredRoute/SecuredRoute.tsx:19](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/components/SecuredRoute/SecuredRoute.tsx#L19)

A route guard that checks if the user is logged in and has the necessary permissions.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

> **default**(): `Element`

Defined in: [src/components/UserPortal/SecuredRouteForUser/SecuredRouteForUser.tsx:14](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/components/UserPortal/SecuredRouteForUser/SecuredRouteForUser.tsx#L14)
Defined in: [src/components/UserPortal/SecuredRouteForUser/SecuredRouteForUser.tsx:18](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/components/UserPortal/SecuredRouteForUser/SecuredRouteForUser.tsx#L18)

A component that guards routes by checking if the user is logged in.
If the user is logged in and does not have 'AdminFor' set, the child routes are rendered.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

> **default**(): `Element`

Defined in: [src/screens/PageNotFound/PageNotFound.tsx:15](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/screens/PageNotFound/PageNotFound.tsx#L15)
Defined in: [src/screens/PageNotFound/PageNotFound.tsx:16](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/screens/PageNotFound/PageNotFound.tsx#L16)

The `PageNotFound` component displays a 404 error page when a user navigates to a non-existent route.
It shows a message indicating that the page was not found and provides a link to redirect users back
Expand Down
50 changes: 39 additions & 11 deletions src/App.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import { Provider } from 'react-redux';
import { MockedProvider } from '@apollo/react-testing';
import { BrowserRouter } from 'react-router-dom';
import { I18nextProvider } from 'react-i18next';
import { describe, it, expect, vi } from 'vitest';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import App from './App';
import { store } from 'state/store';
import { CHECK_AUTH } from 'GraphQl/Queries/Queries';
import { CHECK_AUTH, VERIFY_ROLE } from 'GraphQl/Queries/Queries';
import i18nForTest from './utils/i18nForTest';
import { StaticMockLink } from 'utils/StaticMockLink';

// Mock external dependencies
vi.mock('@mui/x-charts/PieChart', () => ({
pieArcLabelClasses: vi.fn(),
PieChart: vi.fn().mockImplementation(() => <>Test</>),
Expand Down Expand Up @@ -55,19 +56,45 @@ const MOCKS = [
},
},
},
{
request: {
query: VERIFY_ROLE,
},
result: {
data: {
verifyRole: {
isAuthorized: true,
role: 'user',
},
},
},
},
];

const link = new StaticMockLink(MOCKS, true);
const link2 = new StaticMockLink([], true);
const link2 = new StaticMockLink(
[
{
request: { query: CHECK_AUTH },
result: { data: { checkAuth: null } }, // Simulating logged-out state
},
{
request: { query: VERIFY_ROLE },
result: { data: { verifyRole: { isAuthorized: false, role: '' } } },
}, // Ensure verifyRole exists, even if null
],
true,
);

const wait = (ms = 100): Promise<void> =>
new Promise((resolve) => setTimeout(resolve, ms));

async function wait(ms = 100): Promise<void> {
await new Promise((resolve) => {
setTimeout(resolve, ms);
describe('App Component Tests', () => {
beforeEach(() => {
window.history.pushState({}, '', '/');
});
}

describe('Testing the App Component', () => {
it('Component should be rendered properly and user is logged in', async () => {
it('should render properly when user is logged in', async () => {
render(
<MockedProvider addTypename={false} link={link}>
<BrowserRouter>
Expand All @@ -84,15 +111,16 @@ describe('Testing the App Component', () => {

window.history.pushState({}, '', '/orglist');
await wait();

expect(window.location.pathname).toBe('/orglist');
expect(
screen.getByText(
'An open source application by Palisadoes Foundation volunteers',
),
).toBeTruthy();
).toBeInTheDocument();
});

it('Component should be rendered properly and user is logged out', async () => {
it('should render properly when user is logged out', async () => {
render(
<MockedProvider addTypename={false} link={link2}>
<BrowserRouter>
Expand Down
8 changes: 8 additions & 0 deletions src/GraphQl/Queries/Queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,14 @@ export const GET_COMMUNITY_SESSION_TIMEOUT_DATA = gql`
}
}
`;
export const VERIFY_ROLE = gql`
query verifyRole {
verifyRole {
isAuthorized
role
}
}
`;

// get the list of Action Item Categories
export { ACTION_ITEM_CATEGORY_LIST } from './ActionItemCategoryQueries';
Expand Down
12 changes: 12 additions & 0 deletions src/components/CheckIn/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,15 @@ export interface InterfaceTableData {
id: string;
checkInData: InterfaceTableCheckIn;
}
export enum UserRole {
USER = 'user',
ADMIN = 'admin',
SUPER_ADMIN = 'superAdmin',
}

export type VerifyRoleResponse = {
verifyRole: {
isAuthorized: boolean;
role: UserRole;
};
};
65 changes: 56 additions & 9 deletions src/components/SecuredRoute/SecuredRoute.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import React from 'react';
import { Navigate, Outlet } from 'react-router-dom';
import { useQuery } from '@apollo/client';
import { UserRole, type VerifyRoleResponse } from 'components/CheckIn/types';
import { VERIFY_ROLE } from 'GraphQl/Queries/Queries';
import React, { useEffect } from 'react';
import { Navigate, Outlet, useLocation } from 'react-router-dom';
import { toast } from 'react-toastify';
import PageNotFound from 'screens/PageNotFound/PageNotFound';
import useLocalStorage from 'utils/useLocalstorage';
Expand All @@ -14,14 +17,58 @@
* @returns The JSX element representing the secured route.
*/
const SecuredRoute = (): JSX.Element => {
const isLoggedIn = getItem('IsLoggedIn');
const adminFor = getItem('AdminFor');

return isLoggedIn === 'TRUE' ? (
<>{adminFor != null ? <Outlet /> : <PageNotFound />}</>
) : (
<Navigate to="/" replace />
const location = useLocation();
const { data, loading, error, refetch } = useQuery<VerifyRoleResponse>(
VERIFY_ROLE,
{
skip: !getItem('token'),
context: {
headers: {
Authorization: `Bearer ${getItem('token')}`,
},
},
},
);
const [token, setToken] = React.useState(getItem('token'));

useEffect(() => {
const newToken = getItem('token');
if (newToken !== token) {
setToken(newToken);

Check warning on line 37 in src/components/SecuredRoute/SecuredRoute.tsx

View check run for this annotation

Codecov / codecov/patch

src/components/SecuredRoute/SecuredRoute.tsx#L37

Added line #L37 was not covered by tests
}
}, []);

useEffect(() => {
refetch(); // Refetch when token updates
}, [token]);

if (loading) {
return <div> Loading.....</div>;

Check warning on line 46 in src/components/SecuredRoute/SecuredRoute.tsx

View check run for this annotation

Codecov / codecov/patch

src/components/SecuredRoute/SecuredRoute.tsx#L46

Added line #L46 was not covered by tests
} else if (error) {
return <div>Error During Routing ...</div>;

Check warning on line 48 in src/components/SecuredRoute/SecuredRoute.tsx

View check run for this annotation

Codecov / codecov/patch

src/components/SecuredRoute/SecuredRoute.tsx#L48

Added line #L48 was not covered by tests
} else {
console.log('Mocked Data:', data);
if (!data || !data.verifyRole) {
return <div>Error During Routing ...</div>;
}
const { isAuthorized = false, role = '' } = (data as VerifyRoleResponse)
.verifyRole;
const restrictedRoutesForAdmin = ['/member', '/users', '/communityProfile'];

Check warning on line 56 in src/components/SecuredRoute/SecuredRoute.tsx

View check run for this annotation

Codecov / codecov/patch

src/components/SecuredRoute/SecuredRoute.tsx#L56

Added line #L56 was not covered by tests
if (isAuthorized) {
if (role == UserRole.SUPER_ADMIN) {
return <Outlet />;

Check warning on line 59 in src/components/SecuredRoute/SecuredRoute.tsx

View check run for this annotation

Codecov / codecov/patch

src/components/SecuredRoute/SecuredRoute.tsx#L59

Added line #L59 was not covered by tests
} else if (role == UserRole.ADMIN) {
if (restrictedRoutesForAdmin.includes(location.pathname)) {
return <PageNotFound />;

Check warning on line 62 in src/components/SecuredRoute/SecuredRoute.tsx

View check run for this annotation

Codecov / codecov/patch

src/components/SecuredRoute/SecuredRoute.tsx#L62

Added line #L62 was not covered by tests
}
return <Outlet />;

Check warning on line 64 in src/components/SecuredRoute/SecuredRoute.tsx

View check run for this annotation

Codecov / codecov/patch

src/components/SecuredRoute/SecuredRoute.tsx#L64

Added line #L64 was not covered by tests
} else {
return <PageNotFound />;

Check warning on line 66 in src/components/SecuredRoute/SecuredRoute.tsx

View check run for this annotation

Codecov / codecov/patch

src/components/SecuredRoute/SecuredRoute.tsx#L66

Added line #L66 was not covered by tests
}
} else {
return <Navigate to="/" replace />;

Check warning on line 69 in src/components/SecuredRoute/SecuredRoute.tsx

View check run for this annotation

Codecov / codecov/patch

src/components/SecuredRoute/SecuredRoute.tsx#L69

Added line #L69 was not covered by tests
}
}
};

// Time constants for session timeout and inactivity interval
Expand Down
Loading
Loading