Skip to content

Commit

Permalink
fix(auth): UI logged-in detection for other auths
Browse files Browse the repository at this point in the history
Defer to the API, specifically the presence of the `login` action, to determine if a
user is logged in instead of depending on the implementation detail of the UI JWT login
process.

Refs #134784
  • Loading branch information
rpatterson committed Jun 21, 2021
1 parent e37c702 commit 8649248
Show file tree
Hide file tree
Showing 13 changed files with 65 additions and 61 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

### Bugfix

- Detect when a user has logged in by means other than JWT, such as ZMI `Basic`
authentication or the classic HTML Plone `@login` view @rpatterson

- Fixed docs for config.settings.externalRoutes @giuliaghisini

### Internal
Expand Down
8 changes: 5 additions & 3 deletions src/actions/types/types.test.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { getTypes } from './types';
import { GET_TYPES } from '@plone/volto/constants/ActionTypes';
import { arrayWIdsToObject } from '@plone/volto/helpers/Utils/Utils';

const actions = { user: [{ id: 'logout' }] };
const actionsById = arrayWIdsToObject(actions);

describe('Types action', () => {
describe('getTypes', () => {
it('should create an action to get the types', () => {
const getState = () => ({
userSession: {
token: 'thetoken',
},
actions: { actions, actionsById },
});
const url = '/blog';
const dispatch = jest.fn();
Expand Down
4 changes: 1 addition & 3 deletions src/components/manage/Contents/Contents.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,14 @@ const actions = {
title: 'Contents',
},
],
user: [{ id: 'logout' }],
};
const actionsById = arrayWIdsToObject(actions);

describe('Contents', () => {
it('renders a folder contents view component', () => {
const store = mockStore({
actions: { actions, actionsById },
userSession: {
token: '14134234123qwdaf',
},
search: {
items: [
{
Expand Down
47 changes: 16 additions & 31 deletions src/components/manage/Edit/Edit.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import React from 'react';
import renderer from 'react-test-renderer';
import configureStore from 'redux-mock-store';
import { Provider } from 'react-intl-redux';
import jwt from 'jsonwebtoken';

import { arrayWIdsToObject } from '@plone/volto/helpers/Utils/Utils';
import { __test__ as Edit } from './Edit';

const mockStore = configureStore();
Expand All @@ -13,24 +13,23 @@ jest.mock('react-portal', () => ({
}));
jest.mock('../Form/Form', () => jest.fn(() => <div className="Form" />));

const actions = {
document_actions: [],
object: [
{
icon: '',
id: 'edit',
title: 'Edit',
},
],
user: [{ id: 'logout' }],
};
const actionsById = arrayWIdsToObject(actions);

describe('Edit', () => {
it('renders an empty edit component', () => {
const store = mockStore({
userSession: {
token: jwt.sign({ fullname: 'John Doe' }, 'secret'),
},
actions: {
actions: {
document_actions: [],
object: [
{
icon: '',
id: 'edit',
title: 'Edit',
},
],
},
},
actions: { actions, actionsById },
schema: {
schema: null,
},
Expand Down Expand Up @@ -61,21 +60,7 @@ describe('Edit', () => {

it('renders an edit component', () => {
const store = mockStore({
userSession: {
token: jwt.sign({ fullname: 'John Doe' }, 'secret'),
},
actions: {
actions: {
document_actions: [],
object: [
{
icon: '',
id: 'edit',
title: 'Edit',
},
],
},
},
actions: { actions, actionsById },
schema: {
schema: {
some: 'field',
Expand Down
2 changes: 1 addition & 1 deletion src/components/manage/Toolbar/Toolbar.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ const actions = {
title: 'Log out',
},
],
}
};
const actionsById = arrayWIdsToObject(actions);

describe('Toolbar', () => {
Expand Down
13 changes: 9 additions & 4 deletions src/components/theme/Anontools/Anontools.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ import { Provider } from 'react-intl-redux';
import { MemoryRouter } from 'react-router-dom';

import Anontools from './Anontools';
import { arrayWIdsToObject } from '@plone/volto/helpers/Utils/Utils';

const mockStore = configureStore();

const actions = { user: [{ id: 'login' }] };
const actionsById = arrayWIdsToObject(actions);

describe('Anontools', () => {
it('renders an anontools component when no token is specified', () => {
it('renders an anontools component when no use is logged in', () => {
const store = mockStore({
userSession: { token: null },
actions: { actions, actionsById },
content: { data: { '@id': 'myid' } },
intl: {
locale: 'en',
Expand All @@ -29,9 +33,10 @@ describe('Anontools', () => {
expect(json).toMatchSnapshot();
});

it('should not render an anontools component when a token is specified', () => {
it('should not render an anontools component when a user is logged in', () => {
const actions = { user: [{ id: 'logout' }] };
const store = mockStore({
userSession: { token: '1234' },
actions: { actions, actionsById: arrayWIdsToObject(actions) },
content: { data: {} },
intl: {
locale: 'en',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Anontools renders an anontools component when no token is specified 1`] = `
exports[`Anontools renders an anontools component when no use is logged in 1`] = `
<div
className="ui pointing secondary right floated menu"
>
Expand All @@ -19,4 +19,4 @@ exports[`Anontools renders an anontools component when no token is specified 1`]
</div>
`;

exports[`Anontools should not render an anontools component when a token is specified 1`] = `null`;
exports[`Anontools should not render an anontools component when a user is logged in 1`] = `null`;
8 changes: 5 additions & 3 deletions src/components/theme/App/App.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { MemoryRouter } from 'react-router-dom';
import config from '@plone/volto/registry';

import { __test__ as App } from './App';
import { arrayWIdsToObject } from '@plone/volto/helpers/Utils/Utils';

beforeAll(() => {
config.settings.navDepth = 1;
Expand Down Expand Up @@ -35,12 +36,13 @@ jest.mock('semantic-ui-react', () => ({
}));
jest.mock('../Footer/Footer', () => jest.fn(() => <div id="footer" />));

const actions = { user: [{ id: 'logout' }] };
const actionsById = arrayWIdsToObject(actions);

describe('App', () => {
it('renders a app component', () => {
const store = mockStore({
userSession: {
token: 'abcdefgh',
},
actions: { actions, actionsById },
content: { data: { id: 'content', '@type': 'Document' } },
apierror: {},
intl: {
Expand Down
3 changes: 1 addition & 2 deletions src/components/theme/Header/Header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ class Header extends Component {
* @property {Object} defaultProps Default properties.
* @static
*/
static defaultProps = {
};
static defaultProps = {};

/**
* Render method.
Expand Down
6 changes: 5 additions & 1 deletion src/components/theme/Header/Header.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import configureStore from 'redux-mock-store';
import { Provider } from 'react-intl-redux';

import Header from './Header';
import { arrayWIdsToObject } from '@plone/volto/helpers/Utils/Utils';

const mockStore = configureStore();

Expand All @@ -21,10 +22,13 @@ jest.mock('../LanguageSelector/LanguageSelector', () =>
jest.fn(() => <div id="language-selector" />),
);

const actions = { user: [{ id: 'login' }] };
const actionsById = arrayWIdsToObject(actions);

describe('Header', () => {
it('renders a header component', () => {
const store = mockStore({
userSession: { token: null },
actions: { actions, actionsById },
intl: {
locale: 'en',
messages: {},
Expand Down
5 changes: 5 additions & 0 deletions src/components/theme/Login/Login.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { MemoryRouter } from 'react-router-dom';
import config from '@plone/volto/registry';

import Login from './Login';
import { arrayWIdsToObject } from '@plone/volto/helpers/Utils/Utils';

beforeAll(() => {
config.settings.nonContentRoutes = [];
Expand All @@ -14,9 +15,13 @@ beforeAll(() => {

const mockStore = configureStore();

const actions = { user: [{ id: 'login' }] };
const actionsById = arrayWIdsToObject(actions);

describe('Login', () => {
it('renders a login component', () => {
const store = mockStore({
actions: { actions, actionsById },
userSession: {
login: {},
},
Expand Down
18 changes: 8 additions & 10 deletions src/components/theme/View/View.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Provider } from 'react-intl-redux';

import View from './View';
import config from '@plone/volto/registry';
import { arrayWIdsToObject } from '@plone/volto/helpers/Utils/Utils';

beforeAll(() => {
config.set('views', {
Expand Down Expand Up @@ -125,18 +126,18 @@ const actions = {
},
{
icon: '',
id: 'logout',
title: 'Log out',
id: 'login',
title: 'Log in',
},
],
};
const actionsById = arrayWIdsToObject(actions);

describe('View', () => {
it('renders an empty view', () => {
const store = mockStore({
actions: { actions },
actions: { actions, actionsById },
content: { get: { error: null } },
userSession: { token: null },
apierror: {},
intl: {
locale: 'en',
Expand All @@ -154,9 +155,8 @@ describe('View', () => {

it('renders a summary view', () => {
const store = mockStore({
actions: { actions },
actions: { actions, actionsById },
content: { data: { layout: 'summary_view' }, get: { error: null } },
userSession: { token: null },
apierror: {},
intl: {
locale: 'en',
Expand All @@ -174,9 +174,8 @@ describe('View', () => {

it('renders a tabular view', () => {
const store = mockStore({
actions: { actions },
actions: { actions, actionsById },
content: { data: { layout: 'tabular_view' }, get: { error: null } },
userSession: { token: null },
apierror: {},
intl: {
locale: 'en',
Expand All @@ -194,9 +193,8 @@ describe('View', () => {

it('renders a document view', () => {
const store = mockStore({
actions: { actions },
actions: { actions, actionsById },
content: { data: {}, get: { error: null } },
userSession: { token: null },
apierror: {},
intl: {
locale: 'en',
Expand Down
5 changes: 4 additions & 1 deletion src/selectors/userSession/userSession.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import jwtDecode from 'jwt-decode';
* @returns {boolean} `true` if a user is currently authenticated, `false` otherwise.
*/
export function loggedIn(state) {
return !!state.userSession.token;
/* The user may be authenticated by different means, including outside the UI. Defer
* to the response from Plone, sepcifically whether Plone presents an option to log
* in. */
return !state.actions.actionsById.user.login;
}

/**
Expand Down

0 comments on commit 8649248

Please sign in to comment.