From 785c4e862eff52e69face8a8ec61b847b45ea97f Mon Sep 17 00:00:00 2001 From: Hung Viet Nguyen Date: Sat, 12 Mar 2022 16:58:59 +0700 Subject: [PATCH] Preserve url if user's not logged in Fix #12567 --- .../src/components/PrivateRoute/index.js | 40 ++++++----- .../PrivateRoute/tests/index.test.js | 69 +++++++++++++++++++ .../admin/admin/src/pages/AuthPage/index.js | 27 ++++++-- 3 files changed, 114 insertions(+), 22 deletions(-) create mode 100644 packages/core/admin/admin/src/components/PrivateRoute/tests/index.test.js diff --git a/packages/core/admin/admin/src/components/PrivateRoute/index.js b/packages/core/admin/admin/src/components/PrivateRoute/index.js index 02cecaf0d9..b5cc08bf1d 100644 --- a/packages/core/admin/admin/src/components/PrivateRoute/index.js +++ b/packages/core/admin/admin/src/components/PrivateRoute/index.js @@ -8,28 +8,34 @@ */ import React, { memo } from 'react'; -import { Redirect, Route } from 'react-router-dom'; +import { Redirect, Route, useLocation } from 'react-router-dom'; import PropTypes from 'prop-types'; import { auth } from '@strapi/helper-plugin'; /* eslint-disable react/jsx-curly-newline */ -const PrivateRoute = ({ component: Component, path, ...rest }) => ( - - auth.getToken() !== null ? ( - - ) : ( - - ) - } - /> -); +const PrivateRoute = ({ component: Component, path, ...rest }) => { + const { pathname, search } = useLocation(); + + return ( + + auth.getToken() !== null ? ( + + ) : ( + + ) + } + /> + ); +}; PrivateRoute.propTypes = { component: PropTypes.any.isRequired, diff --git a/packages/core/admin/admin/src/components/PrivateRoute/tests/index.test.js b/packages/core/admin/admin/src/components/PrivateRoute/tests/index.test.js new file mode 100644 index 0000000000..73d5711065 --- /dev/null +++ b/packages/core/admin/admin/src/components/PrivateRoute/tests/index.test.js @@ -0,0 +1,69 @@ +import React from 'react'; + +import { Router, Route, Switch } from 'react-router-dom'; +import { createMemoryHistory } from 'history'; +import { render, screen, waitFor } from '@testing-library/react'; +import { auth } from '@strapi/helper-plugin'; +import PrivateRoute from '..'; + +const ProtectedPage = () => { + return
You are authenticated
; +}; + +const LoginPage = () => { + return
Please login
; +}; + +const history = createMemoryHistory(); + +describe('PrivateRoute', () => { + const renderApp = () => + render( + + + + + + + ); + + afterEach(() => { + auth.clearToken(); + }); + + it('Authenticated users should be able to access protected routes', async () => { + // Login + auth.setToken('access-token'); + renderApp(); + // Visit a protected route + history.push('/protected'); + // Should see the protected route + expect(await screen.findByText('You are authenticated')); + }); + + it('Unauthenticated users should not be able to access protected routes and get redirected', async () => { + renderApp(); + + // Visit `/` + history.push('/'); + // Should redirected to `/auth/login` + await waitFor(() => { + expect(history.location.pathname).toBe('/auth/login'); + // No `redirectTo` in the search params + expect(history.location.search).toBe(''); + expect(screen.getByText('Please login')).toBeInTheDocument(); + }); + + // Visit /settings/application-infos + history.push('/settings/application-infos'); + // Should redirected to `/auth/login` and preserve the `/settings/application-infos` path + await waitFor(() => { + expect(history.location.pathname).toBe('/auth/login'); + // Should preserve url in the params + expect(history.location.search).toBe( + `?redirectTo=${encodeURIComponent('/settings/application-infos')}` + ); + expect(screen.getByText('Please login')).toBeInTheDocument(); + }); + }); +}); diff --git a/packages/core/admin/admin/src/pages/AuthPage/index.js b/packages/core/admin/admin/src/pages/AuthPage/index.js index 063d4c0ce0..8fa7ee9708 100644 --- a/packages/core/admin/admin/src/pages/AuthPage/index.js +++ b/packages/core/admin/admin/src/pages/AuthPage/index.js @@ -14,7 +14,10 @@ import init from './init'; import { initialState, reducer } from './reducer'; const AuthPage = ({ hasAdmin, setHasAdmin }) => { - const { push } = useHistory(); + const { + push, + location: { search }, + } = useHistory(); const { changeLocale } = useLocalesProvider(); const { setSkipped } = useGuidedTour(); const { trackUsage } = useTracking(); @@ -119,7 +122,7 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => { auth.setToken(token, body.rememberMe); auth.setUserInfo(user, body.rememberMe); - push('/'); + redirectToPreviousLocation(); } catch (err) { if (err.response) { const errorMessage = get( @@ -189,8 +192,7 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => { return; } - // Redirect to the homePage - push('/'); + redirectToPreviousLocation(); } catch (err) { trackUsage('didNotCreateFirstAdmin'); @@ -238,6 +240,12 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => { } }; + const redirectToPreviousLocation = () => { + const locationBeforeAuthenticated = decodeURIComponent(query.get('redirectTo')); + const redirectUrl = locationBeforeAuthenticated || '/'; + push(redirectUrl); + }; + // Redirect the user to the login page if // the endpoint does not exist or // there is already an admin user oo @@ -248,7 +256,16 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => { // Redirect the user to the register-admin if it is the first user if (!hasAdmin && authType !== 'register-admin') { - return ; + return ( + /auth/login?redirectTo=%2Fabc => /auth/register-admin?redirectTo=%2Fabc + search, + }} + /> + ); } return (