mirror of
https://github.com/ansible/awx.git
synced 2026-01-11 10:00:01 -03:30
Merge pull request #12312 from nixocio/ui_issue_11167_rebased
Update logout/login redirect for different users
This commit is contained in:
commit
9b0a2b0b76
@ -101,9 +101,9 @@ const AuthorizedRoutes = ({ routeConfig }) => {
|
||||
export function ProtectedRoute({ children, ...rest }) {
|
||||
const {
|
||||
authRedirectTo,
|
||||
setAuthRedirectTo,
|
||||
loginRedirectOverride,
|
||||
isUserBeingLoggedOut,
|
||||
loginRedirectOverride,
|
||||
setAuthRedirectTo,
|
||||
} = useSession();
|
||||
const location = useLocation();
|
||||
|
||||
|
||||
@ -11,3 +11,4 @@ export const JOB_TYPE_URL_SEGMENTS = {
|
||||
export const SESSION_TIMEOUT_KEY = 'awx-session-timeout';
|
||||
export const SESSION_REDIRECT_URL = 'awx-redirect-url';
|
||||
export const PERSISTENT_FILTER_KEY = 'awx-persistent-filter';
|
||||
export const SESSION_USER_ID = 'awx-session-user-id';
|
||||
|
||||
@ -11,7 +11,7 @@ import { DateTime } from 'luxon';
|
||||
import { RootAPI, MeAPI } from 'api';
|
||||
import { isAuthenticated } from 'util/auth';
|
||||
import useRequest from 'hooks/useRequest';
|
||||
import { SESSION_TIMEOUT_KEY } from '../constants';
|
||||
import { SESSION_TIMEOUT_KEY, SESSION_USER_ID } from '../constants';
|
||||
|
||||
// The maximum supported timeout for setTimeout(), in milliseconds,
|
||||
// is the highest number you can represent as a signed 32bit
|
||||
@ -101,6 +101,7 @@ function SessionProvider({ children }) {
|
||||
setIsUserBeingLoggedOut(true);
|
||||
if (!isSessionExpired.current) {
|
||||
setAuthRedirectTo('/logout');
|
||||
window.localStorage.setItem(SESSION_USER_ID, null);
|
||||
}
|
||||
sessionStorage.clear();
|
||||
await RootAPI.logout();
|
||||
@ -167,21 +168,21 @@ function SessionProvider({ children }) {
|
||||
|
||||
const sessionValue = useMemo(
|
||||
() => ({
|
||||
isUserBeingLoggedOut,
|
||||
loginRedirectOverride,
|
||||
authRedirectTo,
|
||||
handleSessionContinue,
|
||||
isSessionExpired,
|
||||
isUserBeingLoggedOut,
|
||||
loginRedirectOverride,
|
||||
logout,
|
||||
sessionCountdown,
|
||||
setAuthRedirectTo,
|
||||
}),
|
||||
[
|
||||
isUserBeingLoggedOut,
|
||||
loginRedirectOverride,
|
||||
authRedirectTo,
|
||||
handleSessionContinue,
|
||||
isSessionExpired,
|
||||
isUserBeingLoggedOut,
|
||||
loginRedirectOverride,
|
||||
logout,
|
||||
sessionCountdown,
|
||||
setAuthRedirectTo,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/* eslint-disable react/jsx-no-useless-fragment */
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import React, { useCallback, useEffect, useRef } from 'react';
|
||||
import { Redirect, withRouter } from 'react-router-dom';
|
||||
|
||||
import { t } from '@lingui/macro';
|
||||
@ -31,7 +31,8 @@ import { AuthAPI, RootAPI } from 'api';
|
||||
import AlertModal from 'components/AlertModal';
|
||||
import ErrorDetail from 'components/ErrorDetail';
|
||||
import { useSession } from 'contexts/Session';
|
||||
import { SESSION_REDIRECT_URL } from '../../constants';
|
||||
import { getCurrentUserId } from 'util/auth';
|
||||
import { SESSION_REDIRECT_URL, SESSION_USER_ID } from '../../constants';
|
||||
|
||||
const loginLogoSrc = 'static/media/logo-login.svg';
|
||||
|
||||
@ -43,6 +44,8 @@ const Login = styled(PFLogin)`
|
||||
|
||||
function AWXLogin({ alt, isAuthenticated }) {
|
||||
const { authRedirectTo, isSessionExpired, setAuthRedirectTo } = useSession();
|
||||
const isNewUser = useRef(true);
|
||||
const hasVerifiedUser = useRef(false);
|
||||
|
||||
const {
|
||||
isLoading: isCustomLoginInfoLoading,
|
||||
@ -112,8 +115,26 @@ function AWXLogin({ alt, isAuthenticated }) {
|
||||
if (isCustomLoginInfoLoading) {
|
||||
return null;
|
||||
}
|
||||
if (isAuthenticated(document.cookie)) {
|
||||
return <Redirect to={authRedirectTo || '/'} />;
|
||||
|
||||
if (isAuthenticated(document.cookie) && !hasVerifiedUser.current) {
|
||||
const currentUserId = getCurrentUserId(document.cookie);
|
||||
const verifyIsNewUser = () => {
|
||||
const previousUserId = JSON.parse(
|
||||
window.localStorage.getItem(SESSION_USER_ID)
|
||||
);
|
||||
if (previousUserId === null) {
|
||||
return true;
|
||||
}
|
||||
return currentUserId.toString() !== previousUserId.toString();
|
||||
};
|
||||
isNewUser.current = verifyIsNewUser();
|
||||
hasVerifiedUser.current = true;
|
||||
window.localStorage.setItem(SESSION_USER_ID, JSON.stringify(currentUserId));
|
||||
}
|
||||
|
||||
if (isAuthenticated(document.cookie) && hasVerifiedUser.current) {
|
||||
const redirect = isNewUser.current ? '/' : authRedirectTo;
|
||||
return <Redirect to={redirect} />;
|
||||
}
|
||||
|
||||
let helperText;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import { createMemoryHistory } from 'history';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { AuthAPI, RootAPI } from 'api';
|
||||
import {
|
||||
@ -7,9 +8,16 @@ import {
|
||||
} from '../../../testUtils/enzymeHelpers';
|
||||
|
||||
import AWXLogin from './Login';
|
||||
import { getCurrentUserId } from 'util/auth';
|
||||
|
||||
import { SESSION_USER_ID } from '../../constants';
|
||||
|
||||
jest.mock('../../api');
|
||||
|
||||
jest.mock('util/auth', () => ({
|
||||
getCurrentUserId: jest.fn(),
|
||||
}));
|
||||
|
||||
RootAPI.readAssetVariables.mockResolvedValue({
|
||||
data: {
|
||||
BRAND_NAME: 'AWX',
|
||||
@ -72,6 +80,13 @@ describe('<Login />', () => {
|
||||
custom_logo: 'images/foo.jpg',
|
||||
},
|
||||
});
|
||||
Object.defineProperty(window, 'localStorage', {
|
||||
value: {
|
||||
getItem: jest.fn(() => '42'),
|
||||
setItem: jest.fn(() => null),
|
||||
},
|
||||
writable: true,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@ -276,15 +291,77 @@ describe('<Login />', () => {
|
||||
expect(RootAPI.login).toHaveBeenCalledWith('un', 'pw');
|
||||
});
|
||||
|
||||
test('render Redirect to / when already authenticated', async () => {
|
||||
test('render Redirect to / when already authenticated as a new user', async () => {
|
||||
getCurrentUserId.mockReturnValue(1);
|
||||
const history = createMemoryHistory({
|
||||
initialEntries: ['/login'],
|
||||
});
|
||||
let wrapper;
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(<AWXLogin isAuthenticated={() => true} />);
|
||||
wrapper = mountWithContexts(<AWXLogin isAuthenticated={() => true} />, {
|
||||
context: {
|
||||
router: { history },
|
||||
session: {
|
||||
authRedirectTo: '/projects',
|
||||
handleSessionContinue: () => {},
|
||||
isSessionExpired: false,
|
||||
isUserBeingLoggedOut: false,
|
||||
loginRedirectOverride: null,
|
||||
logout: () => {},
|
||||
sessionCountdown: 60,
|
||||
setAuthRedirectTo: () => {},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
expect(window.localStorage.getItem).toHaveBeenCalledWith(SESSION_USER_ID);
|
||||
expect(window.localStorage.setItem).toHaveBeenCalledWith(
|
||||
SESSION_USER_ID,
|
||||
'1'
|
||||
);
|
||||
await waitForElement(wrapper, 'Redirect', (el) => el.length === 1);
|
||||
await waitForElement(wrapper, 'Redirect', (el) => el.props().to === '/');
|
||||
});
|
||||
|
||||
test('render redirect to authRedirectTo when authenticated as a previous user', async () => {
|
||||
getCurrentUserId.mockReturnValue(42);
|
||||
const history = createMemoryHistory({
|
||||
initialEntries: ['/login'],
|
||||
});
|
||||
let wrapper;
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(<AWXLogin isAuthenticated={() => true} />, {
|
||||
context: {
|
||||
router: { history },
|
||||
session: {
|
||||
authRedirectTo: '/projects',
|
||||
handleSessionContinue: () => {},
|
||||
isSessionExpired: false,
|
||||
isUserBeingLoggedOut: false,
|
||||
loginRedirectOverride: null,
|
||||
logout: () => {},
|
||||
sessionCountdown: 60,
|
||||
setAuthRedirectTo: () => {},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
wrapper.update();
|
||||
expect(window.localStorage.getItem).toHaveBeenCalledWith(SESSION_USER_ID);
|
||||
expect(window.localStorage.setItem).toHaveBeenCalledWith(
|
||||
SESSION_USER_ID,
|
||||
'42'
|
||||
);
|
||||
wrapper.update();
|
||||
await waitForElement(wrapper, 'Redirect', (el) => el.length === 1);
|
||||
await waitForElement(
|
||||
wrapper,
|
||||
'Redirect',
|
||||
(el) => el.props().to === '/projects'
|
||||
);
|
||||
});
|
||||
|
||||
test('GitHub auth buttons shown', async () => {
|
||||
AuthAPI.read.mockResolvedValue({
|
||||
data: {
|
||||
|
||||
@ -6,3 +6,29 @@ export function isAuthenticated(cookie) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function getCurrentUserId(cookie) {
|
||||
if (!isAuthenticated(cookie)) {
|
||||
return null;
|
||||
}
|
||||
const name = 'current_user';
|
||||
let userId = null;
|
||||
if (cookie && cookie !== '') {
|
||||
const cookies = cookie.split(';');
|
||||
for (let i = 0; i < cookies.length; i++) {
|
||||
const parsedCookie = cookies[i].trim();
|
||||
if (parsedCookie.substring(0, name.length + 1) === `${name}=`) {
|
||||
userId = parseUserId(
|
||||
decodeURIComponent(parsedCookie.substring(name.length + 1))
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
function parseUserId(decodedUserData) {
|
||||
const userData = JSON.parse(decodedUserData);
|
||||
return userData.id;
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { isAuthenticated } from './auth';
|
||||
import { isAuthenticated, getCurrentUserId } from './auth';
|
||||
|
||||
const invalidCookie = 'invalid';
|
||||
const validLoggedOutCookie =
|
||||
@ -19,3 +19,17 @@ describe('isAuthenticated', () => {
|
||||
expect(isAuthenticated(validLoggedInCookie)).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getCurrentUserId', () => {
|
||||
test('returns null for invalid cookie', () => {
|
||||
expect(getCurrentUserId(invalidCookie)).toEqual(null);
|
||||
});
|
||||
|
||||
test('returns null for expired cookie', () => {
|
||||
expect(getCurrentUserId(validLoggedOutCookie)).toEqual(null);
|
||||
});
|
||||
|
||||
test('returns current user id for valid authenticated cookie', () => {
|
||||
expect(getCurrentUserId(validLoggedInCookie)).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user