@eeacms/volto-eea-website-policy 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -2
- package/jest-addon.config.js +5 -3
- package/package.json +9 -3
- package/src/components/AzureLogin/Login.jsx +103 -0
- package/src/components/AzureLogin/Login.test.jsx +179 -0
- package/src/index.js +15 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,17 +4,35 @@ All notable changes to this project will be documented in this file. Dates are d
|
|
|
4
4
|
|
|
5
5
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
6
6
|
|
|
7
|
-
### [2.
|
|
7
|
+
### [2.1.0](https://github.com/eea/volto-eea-website-policy/compare/2.0.0...2.1.0) - 9 May 2024
|
|
8
|
+
|
|
9
|
+
#### :rocket: New Features
|
|
10
|
+
|
|
11
|
+
- feat: integrated EEA Azure AD authentication - refs #258877 [Alexandru Ghica - [`7ff72c1`](https://github.com/eea/volto-eea-website-policy/commit/7ff72c1567524ce73ec72e6a30e91d6d29342510)]
|
|
12
|
+
|
|
13
|
+
#### :hammer_and_wrench: Others
|
|
14
|
+
|
|
15
|
+
- Release 2.1.0 [alin - [`da22af4`](https://github.com/eea/volto-eea-website-policy/commit/da22af436092236d7ef6e8568e3eca4813d517b5)]
|
|
16
|
+
## [2.0.0](https://github.com/eea/volto-eea-website-policy/compare/1.15.0...2.0.0) - 22 April 2024
|
|
8
17
|
|
|
9
18
|
#### :rocket: New Features
|
|
10
19
|
|
|
11
20
|
- feat: Release 2.0.0 - Volto 17 support [alin - [`d6cf5c8`](https://github.com/eea/volto-eea-website-policy/commit/d6cf5c8f41950a94cd68e7a36a45b5be8cc6195b)]
|
|
12
|
-
- feat: Default view layout restriction to Manager - refs #268249 [ichim-david - [`1840c1b`](https://github.com/eea/volto-eea-website-policy/commit/1840c1bd9aae6ce18a23800ce618f8bbe47525ab)]
|
|
13
21
|
|
|
14
22
|
#### :nail_care: Enhancements
|
|
15
23
|
|
|
16
24
|
- refactor: Move customizations to volto-eea-website-theme [alin - [`9641437`](https://github.com/eea/volto-eea-website-policy/commit/9641437c7f10ba6c7ec1f896b180b49d2718a7d0)]
|
|
17
25
|
|
|
26
|
+
### [1.15.0](https://github.com/eea/volto-eea-website-policy/compare/1.14.1-beta.1...1.15.0) - 9 May 2024
|
|
27
|
+
|
|
28
|
+
#### :rocket: New Features
|
|
29
|
+
|
|
30
|
+
- feat: integrated EEA Azure AD authentication - refs #258877 [Alexandru Ghica - [`1897659`](https://github.com/eea/volto-eea-website-policy/commit/18976594a9c7b4f2640dc79ab6420f509e7de668)]
|
|
31
|
+
- feat: Default view layout restriction to Manager - refs #268249 [ichim-david - [`1840c1b`](https://github.com/eea/volto-eea-website-policy/commit/1840c1bd9aae6ce18a23800ce618f8bbe47525ab)]
|
|
32
|
+
|
|
33
|
+
#### :hammer_and_wrench: Others
|
|
34
|
+
|
|
35
|
+
- Release 1.15.0 [alin - [`f0d61d8`](https://github.com/eea/volto-eea-website-policy/commit/f0d61d8bd99687f0460acd0a1ad6e200206b5b6a)]
|
|
18
36
|
### [1.14.1-beta.1](https://github.com/eea/volto-eea-website-policy/compare/1.14.1-beta.0...1.14.1-beta.1) - 19 April 2024
|
|
19
37
|
|
|
20
38
|
#### :hammer_and_wrench: Others
|
package/jest-addon.config.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require('dotenv').config({ path: __dirname + '/.env' })
|
|
1
|
+
require('dotenv').config({ path: __dirname + '/.env' });
|
|
2
2
|
|
|
3
3
|
module.exports = {
|
|
4
4
|
testMatch: ['**/src/addons/**/?(*.)+(spec|test).[jt]s?(x)'],
|
|
@@ -8,6 +8,8 @@ module.exports = {
|
|
|
8
8
|
],
|
|
9
9
|
moduleNameMapper: {
|
|
10
10
|
'\\.(css|less|scss|sass)$': 'identity-obj-proxy',
|
|
11
|
+
'@plone-collective/volto-authomatic/(.*)$':
|
|
12
|
+
'<rootDir>/node_modules/@plone-collective/volto-authomatic/src/$1',
|
|
11
13
|
'@plone/volto/cypress': '<rootDir>/node_modules/@plone/volto/cypress',
|
|
12
14
|
'@plone/volto/babel': '<rootDir>/node_modules/@plone/volto/babel',
|
|
13
15
|
'@plone/volto/(.*)$': '<rootDir>/node_modules/@plone/volto/src/$1',
|
|
@@ -26,7 +28,7 @@ module.exports = {
|
|
|
26
28
|
'<rootDir>/node_modules/@plone/volto/jest-addons-loader.js',
|
|
27
29
|
},
|
|
28
30
|
transformIgnorePatterns: [
|
|
29
|
-
'/node_modules/(?!(@plone|@root|@package|@eeacms)/).*/',
|
|
31
|
+
'/node_modules/(?!(@plone|@root|@package|@eeacms|@plone-collective)/).*/',
|
|
30
32
|
],
|
|
31
33
|
transform: {
|
|
32
34
|
'^.+\\.js(x)?$': 'babel-jest',
|
|
@@ -48,4 +50,4 @@ module.exports = {
|
|
|
48
50
|
'<rootDir>/node_modules/@eeacms/volto-eea-website-policy/jest.setup.js',
|
|
49
51
|
],
|
|
50
52
|
}),
|
|
51
|
-
}
|
|
53
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eeacms/volto-eea-website-policy",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "@eeacms/volto-eea-website-policy: Volto add-on",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"author": "European Environment Agency: IDM2 A-Team",
|
|
@@ -17,15 +17,21 @@
|
|
|
17
17
|
"url": "git@github.com:eea/volto-eea-website-policy.git"
|
|
18
18
|
},
|
|
19
19
|
"addons": [
|
|
20
|
-
"@eeacms/volto-widget-dataprovenance"
|
|
20
|
+
"@eeacms/volto-widget-dataprovenance",
|
|
21
|
+
"@plone-collective/volto-authomatic"
|
|
21
22
|
],
|
|
22
23
|
"dependencies": {
|
|
23
|
-
"@eeacms/volto-widget-dataprovenance": "*"
|
|
24
|
+
"@eeacms/volto-widget-dataprovenance": "*",
|
|
25
|
+
"@plone-collective/volto-authomatic": "2.0.1"
|
|
26
|
+
},
|
|
27
|
+
"resolutions": {
|
|
28
|
+
"@plone-collective/volto-authomatic": "2.0.1"
|
|
24
29
|
},
|
|
25
30
|
"devDependencies": {
|
|
26
31
|
"@cypress/code-coverage": "^3.10.0",
|
|
27
32
|
"@plone/scripts": "*",
|
|
28
33
|
"babel-plugin-transform-class-properties": "^6.24.1",
|
|
34
|
+
"cypress": "13.1.0",
|
|
29
35
|
"dotenv": "^16.3.2",
|
|
30
36
|
"husky": "^8.0.3",
|
|
31
37
|
"lint-staged": "^14.0.1",
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Login container.
|
|
3
|
+
* @module components/Login/Login
|
|
4
|
+
*/
|
|
5
|
+
import React, { useEffect, useState } from 'react';
|
|
6
|
+
import {
|
|
7
|
+
authomaticRedirect,
|
|
8
|
+
listAuthOptions,
|
|
9
|
+
oidcRedirect,
|
|
10
|
+
} from '@plone-collective/volto-authomatic/actions';
|
|
11
|
+
import { injectIntl } from 'react-intl';
|
|
12
|
+
import { useSelector, useDispatch } from 'react-redux';
|
|
13
|
+
import { useLocation } from 'react-router-dom';
|
|
14
|
+
import LoginForm from '@plone-collective/volto-authomatic/components/Login/LoginForm';
|
|
15
|
+
import qs from 'query-string';
|
|
16
|
+
import { useCookies } from 'react-cookie';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Get retur url function.
|
|
20
|
+
* @function getReturnUrl
|
|
21
|
+
* @param {Object} location Location object.
|
|
22
|
+
* @returns {string} Return url.
|
|
23
|
+
*/
|
|
24
|
+
function getReturnUrl(location) {
|
|
25
|
+
return `${
|
|
26
|
+
qs.parse(location.search).return_url ||
|
|
27
|
+
(location.pathname === '/azure_login'
|
|
28
|
+
? '/'
|
|
29
|
+
: location.pathname.replace('/azure_login', ''))
|
|
30
|
+
}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Login function.
|
|
35
|
+
* @function Login
|
|
36
|
+
* @returns {JSX.Element} Markup of the Login page.
|
|
37
|
+
*/
|
|
38
|
+
function Login({ intl }) {
|
|
39
|
+
const dispatch = useDispatch();
|
|
40
|
+
const [startedOAuth, setStartedOAuth] = useState(false);
|
|
41
|
+
const [startedOIDC, setStartedOIDC] = useState(false);
|
|
42
|
+
const loading = useSelector((state) => state.authOptions.loading);
|
|
43
|
+
const options = useSelector((state) => state.authOptions.options);
|
|
44
|
+
const loginOAuthValues = useSelector((state) => state.authomaticRedirect);
|
|
45
|
+
const loginOIDCValues = useSelector((state) => state.oidcRedirect);
|
|
46
|
+
const location = useLocation();
|
|
47
|
+
const [, setCookie] = useCookies();
|
|
48
|
+
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
dispatch(listAuthOptions());
|
|
51
|
+
}, [dispatch]);
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
const next_url = loginOAuthValues.next_url;
|
|
55
|
+
const session = loginOAuthValues.session;
|
|
56
|
+
if (next_url && session && startedOAuth) {
|
|
57
|
+
setStartedOAuth(false);
|
|
58
|
+
// Give time to save state to localstorage
|
|
59
|
+
setTimeout(function () {
|
|
60
|
+
window.location.href = next_url;
|
|
61
|
+
}, 500);
|
|
62
|
+
}
|
|
63
|
+
}, [startedOAuth, loginOAuthValues]);
|
|
64
|
+
|
|
65
|
+
const onSelectProvider = (provider) => {
|
|
66
|
+
setStartedOAuth(true);
|
|
67
|
+
setCookie('return_url', getReturnUrl(location), { path: '/' });
|
|
68
|
+
dispatch(authomaticRedirect(provider.id));
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
const next_url = loginOIDCValues.next_url;
|
|
73
|
+
if (next_url && startedOIDC) {
|
|
74
|
+
setStartedOIDC(false);
|
|
75
|
+
// Give time to save state to localstorage
|
|
76
|
+
setTimeout(function () {
|
|
77
|
+
window.location.href = next_url;
|
|
78
|
+
}, 500);
|
|
79
|
+
}
|
|
80
|
+
}, [startedOIDC, loginOIDCValues]);
|
|
81
|
+
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
if (
|
|
84
|
+
options !== undefined &&
|
|
85
|
+
options.length === 1 &&
|
|
86
|
+
options[0].id === 'oidc'
|
|
87
|
+
) {
|
|
88
|
+
setStartedOIDC(true);
|
|
89
|
+
dispatch(oidcRedirect('oidc'));
|
|
90
|
+
}
|
|
91
|
+
}, [options, dispatch]);
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<LoginForm
|
|
95
|
+
loading={loading}
|
|
96
|
+
providers={options}
|
|
97
|
+
action={'login'}
|
|
98
|
+
onSelectProvider={onSelectProvider}
|
|
99
|
+
/>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export default injectIntl(Login);
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
2
|
+
import { Provider } from 'react-intl-redux';
|
|
3
|
+
import configureStore from 'redux-mock-store';
|
|
4
|
+
import { useLocation } from 'react-router-dom';
|
|
5
|
+
import { listAuthOptions } from '@plone-collective/volto-authomatic/actions';
|
|
6
|
+
import '@testing-library/jest-dom/extend-expect';
|
|
7
|
+
|
|
8
|
+
import Login from './Login';
|
|
9
|
+
|
|
10
|
+
const mockStore = configureStore();
|
|
11
|
+
|
|
12
|
+
jest.mock('react-router-dom', () => ({
|
|
13
|
+
...jest.requireActual('react-router-dom'),
|
|
14
|
+
useLocation: jest.fn(),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
jest.useFakeTimers();
|
|
18
|
+
|
|
19
|
+
describe('Login component', () => {
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
useLocation.mockReturnValue({ pathname: '/azure_login' });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('renders correctly with loading state', () => {
|
|
25
|
+
const store = mockStore({
|
|
26
|
+
authOptions: { loading: true, options: [] },
|
|
27
|
+
authomaticRedirect: {
|
|
28
|
+
next_url: '/dashboard',
|
|
29
|
+
session: true,
|
|
30
|
+
},
|
|
31
|
+
oidcRedirect: {
|
|
32
|
+
next_url: '/dashboard',
|
|
33
|
+
session: true,
|
|
34
|
+
},
|
|
35
|
+
intl: {
|
|
36
|
+
locale: 'en',
|
|
37
|
+
messages: {},
|
|
38
|
+
formatMessage: jest.fn(),
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
render(
|
|
43
|
+
<Provider store={store}>
|
|
44
|
+
<Login />
|
|
45
|
+
</Provider>,
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
expect(screen.getByText('Loading')).toBeInTheDocument();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('dispatches listAuthOptions on mount', () => {
|
|
52
|
+
const store = mockStore({
|
|
53
|
+
authOptions: { loading: true, options: [] },
|
|
54
|
+
authomaticRedirect: {
|
|
55
|
+
next_url: '/dashboard',
|
|
56
|
+
session: true,
|
|
57
|
+
},
|
|
58
|
+
oidcRedirect: {
|
|
59
|
+
next_url: '/dashboard',
|
|
60
|
+
session: true,
|
|
61
|
+
},
|
|
62
|
+
intl: {
|
|
63
|
+
locale: 'en',
|
|
64
|
+
messages: {},
|
|
65
|
+
formatMessage: jest.fn(),
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
const dispatch = jest.spyOn(store, 'dispatch');
|
|
69
|
+
|
|
70
|
+
render(
|
|
71
|
+
<Provider store={store}>
|
|
72
|
+
<Login />
|
|
73
|
+
</Provider>,
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
expect(dispatch).toHaveBeenCalledWith(listAuthOptions());
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('renders providers from the store', () => {
|
|
80
|
+
const store = mockStore({
|
|
81
|
+
authOptions: {
|
|
82
|
+
loading: false,
|
|
83
|
+
options: [
|
|
84
|
+
{ id: 'facebook', title: 'Facebook' },
|
|
85
|
+
{ id: 'twitter', title: 'Twitter' },
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
authomaticRedirect: {
|
|
89
|
+
next_url: '/dashboard',
|
|
90
|
+
session: true,
|
|
91
|
+
},
|
|
92
|
+
oidcRedirect: {
|
|
93
|
+
next_url: '/dashboard',
|
|
94
|
+
session: true,
|
|
95
|
+
},
|
|
96
|
+
intl: {
|
|
97
|
+
locale: 'en',
|
|
98
|
+
messages: {},
|
|
99
|
+
formatMessage: jest.fn(),
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
render(
|
|
104
|
+
<Provider store={store}>
|
|
105
|
+
<Login />
|
|
106
|
+
</Provider>,
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
// Assert that the provider buttons are present
|
|
110
|
+
expect(
|
|
111
|
+
screen.getByRole('button', { name: /Facebook/i }),
|
|
112
|
+
).toBeInTheDocument();
|
|
113
|
+
expect(
|
|
114
|
+
screen.getByRole('button', { name: /Twitter/i }),
|
|
115
|
+
).toBeInTheDocument();
|
|
116
|
+
|
|
117
|
+
fireEvent.click(screen.getByRole('button', { name: /Facebook/i }));
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('clicks on GitHub button for OAuth login', () => {
|
|
121
|
+
useLocation.mockReturnValue({ pathname: '/login' });
|
|
122
|
+
|
|
123
|
+
const store = mockStore({
|
|
124
|
+
authOptions: {
|
|
125
|
+
loading: false,
|
|
126
|
+
options: [{ id: 'github', title: 'GitHub' }],
|
|
127
|
+
},
|
|
128
|
+
authomaticRedirect: { next_url: '/dashboard', session: true },
|
|
129
|
+
oidcRedirect: {
|
|
130
|
+
next_url: '/dashboard',
|
|
131
|
+
session: true,
|
|
132
|
+
},
|
|
133
|
+
intl: {
|
|
134
|
+
locale: 'en',
|
|
135
|
+
messages: {},
|
|
136
|
+
formatMessage: jest.fn(),
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
render(
|
|
141
|
+
<Provider store={store}>
|
|
142
|
+
<Login />
|
|
143
|
+
</Provider>,
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
fireEvent.click(screen.getByRole('button', { name: /GitHub/i }));
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('renders correctly with loading state', () => {
|
|
150
|
+
const store = mockStore({
|
|
151
|
+
authOptions: {
|
|
152
|
+
loading: true,
|
|
153
|
+
options: [{ id: 'oidc', title: 'OIDC' }],
|
|
154
|
+
},
|
|
155
|
+
authomaticRedirect: {
|
|
156
|
+
next_url: '/dashboard',
|
|
157
|
+
session: true,
|
|
158
|
+
},
|
|
159
|
+
oidcRedirect: {
|
|
160
|
+
next_url: '/dashboard',
|
|
161
|
+
session: true,
|
|
162
|
+
},
|
|
163
|
+
intl: {
|
|
164
|
+
locale: 'en',
|
|
165
|
+
messages: {},
|
|
166
|
+
formatMessage: jest.fn(),
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
render(
|
|
171
|
+
<Provider store={store}>
|
|
172
|
+
<Login />
|
|
173
|
+
</Provider>,
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
expect(screen.getByText('Loading')).toBeInTheDocument();
|
|
177
|
+
jest.advanceTimersByTime(500);
|
|
178
|
+
});
|
|
179
|
+
});
|
package/src/index.js
CHANGED
|
@@ -2,6 +2,9 @@ import { runtimeConfig } from '@plone/volto/runtime_config';
|
|
|
2
2
|
import installContextNavigationBlock from '@eeacms/volto-eea-website-policy/components/Blocks/ContextNavigation';
|
|
3
3
|
import { appendGroup } from './helpers';
|
|
4
4
|
import { FrequencyOfDissemination } from '@eeacms/volto-eea-website-policy/components/Widgets/FrequencyOfDissemination';
|
|
5
|
+
import Login from '@eeacms/volto-eea-website-policy/components/AzureLogin/Login.jsx';
|
|
6
|
+
import Logout from '@plone-collective/volto-authomatic/components/Logout/Logout.jsx';
|
|
7
|
+
import { Login as VoltoLogin } from '@plone/volto/components';
|
|
5
8
|
|
|
6
9
|
const restrictedBlocks = [
|
|
7
10
|
'imagecards',
|
|
@@ -40,7 +43,7 @@ const overrideBlocks = {
|
|
|
40
43
|
const applyConfig = (config) => {
|
|
41
44
|
// #158717#note-25 any path that isn't static, en or controlpanel is treated as external
|
|
42
45
|
const notInEN =
|
|
43
|
-
/^(?!(#|\/en|\/login-authomatic|\/fallback_login|\/static|\/controlpanel|\/cypress|\/login|\/logout|\/contact-form|\/passwordreset)).*$/;
|
|
46
|
+
/^(?!(#|\/en|\/login-authomatic|\/azure_login|\/fallback_login|\/static|\/controlpanel|\/cypress|\/login|\/logout|\/contact-form|\/passwordreset)).*$/;
|
|
44
47
|
config.settings.externalRoutes = [
|
|
45
48
|
...(config.settings.externalRoutes || []),
|
|
46
49
|
{
|
|
@@ -52,6 +55,17 @@ const applyConfig = (config) => {
|
|
|
52
55
|
},
|
|
53
56
|
];
|
|
54
57
|
|
|
58
|
+
// #258877 Make Azure AD login to be at route /azure_login and EIONET LDAP login to be at /login
|
|
59
|
+
config.addonRoutes = [
|
|
60
|
+
{ path: '/azure_login', component: Login },
|
|
61
|
+
{ path: '/**/azure_login', component: Login },
|
|
62
|
+
{ path: '/login', component: VoltoLogin },
|
|
63
|
+
{ path: '/**/login', component: VoltoLogin },
|
|
64
|
+
{ path: '/logout', component: Logout },
|
|
65
|
+
{ path: '/**/logout', component: Logout },
|
|
66
|
+
...(config.addonRoutes || []),
|
|
67
|
+
];
|
|
68
|
+
|
|
55
69
|
// #160689 Redirect contact-form to contact-us
|
|
56
70
|
config.settings.contactForm = '/en/about/contact-us';
|
|
57
71
|
|