diff --git a/frontend/package.json b/frontend/package.json index 53c2c65c1b..099321fb8b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -56,6 +56,7 @@ "enzyme-adapter-react-16": "^1.15.6", "enzyme-to-json": "^3.6.1", "fetch-mock": "^9.11.0", + "http-proxy-middleware": "^2.0.1", "immutable": "^4.0.0-rc.12", "lodash.clonedeep": "^4.5.0", "lodash.flow": "^3.5.0", @@ -109,6 +110,5 @@ "no-useless-computed-key": "off", "import/no-anonymous-default-export": "off" } - }, - "proxy": "http://localhost:4242" + } } diff --git a/frontend/src/component/common/flags.js b/frontend/src/component/common/flags.js index 247c687fce..a41f94c48f 100644 --- a/frontend/src/component/common/flags.js +++ b/frontend/src/component/common/flags.js @@ -1,6 +1,4 @@ export const P = 'P'; export const C = 'C'; export const RBAC = 'RBAC'; -export const OIDC = 'OIDC'; - export const PROJECTFILTERING = false; diff --git a/frontend/src/component/menu/__tests__/__snapshots__/routes-test.jsx.snap b/frontend/src/component/menu/__tests__/__snapshots__/routes-test.jsx.snap index 92d83c76f6..742f132ac8 100644 --- a/frontend/src/component/menu/__tests__/__snapshots__/routes-test.jsx.snap +++ b/frontend/src/component/menu/__tests__/__snapshots__/routes-test.jsx.snap @@ -83,12 +83,5 @@ Array [ "title": "Admin", "type": "protected", }, - Object { - "component": [Function], - "layout": "main", - "path": "/logout", - "title": "Sign out", - "type": "unprotected", - }, ] `; diff --git a/frontend/src/component/menu/__tests__/routes-test.jsx b/frontend/src/component/menu/__tests__/routes-test.jsx index 560e08691c..56142f0a30 100644 --- a/frontend/src/component/menu/__tests__/routes-test.jsx +++ b/frontend/src/component/menu/__tests__/routes-test.jsx @@ -1,7 +1,7 @@ import { baseRoutes, getRoute } from '../routes'; test('returns all baseRoutes', () => { - expect(baseRoutes.length).toEqual(12); + expect(baseRoutes).toHaveLength(11); expect(baseRoutes).toMatchSnapshot(); }); diff --git a/frontend/src/component/menu/drawer.jsx b/frontend/src/component/menu/drawer.jsx index ba8814442e..6ea9972c33 100644 --- a/frontend/src/component/menu/drawer.jsx +++ b/frontend/src/component/menu/drawer.jsx @@ -1,13 +1,16 @@ +import React from 'react'; import { Divider, Drawer, List } from '@material-ui/core'; import PropTypes from 'prop-types'; import GitHubIcon from '@material-ui/icons/GitHub'; import LibraryBooksIcon from '@material-ui/icons/LibraryBooks'; +import ExitToApp from '@material-ui/icons/ExitToApp'; import styles from './drawer.module.scss'; import { ReactComponent as LogoIcon } from '../../assets/icons/logo_wbg.svg'; import NavigationLink from './Header/NavigationLink/NavigationLink'; import ConditionallyRender from '../common/ConditionallyRender'; +import { getBasePath } from '../../utils/format-path'; export const DrawerMenu = ({ links = [], @@ -88,7 +91,13 @@ export const DrawerMenu = ({ } /> -
{renderLinks()}
+
+ {renderLinks()} + + + Sign out + +
); diff --git a/frontend/src/component/menu/routes.js b/frontend/src/component/menu/routes.js index d9b8d09fac..36588efb3e 100644 --- a/frontend/src/component/menu/routes.js +++ b/frontend/src/component/menu/routes.js @@ -14,7 +14,6 @@ import ApplicationView from '../../page/applications/view'; import ContextFields from '../../page/context'; import CreateContextField from '../../page/context/create'; import EditContextField from '../../page/context/edit'; -import LogoutFeatures from '../../page/user/logout'; import CreateProject from '../../page/project/create'; import EditProject from '../../page/project/edit'; import ViewProject from '../../page/project/view'; @@ -342,13 +341,6 @@ export const routes = [ type: 'protected', layout: 'main', }, - { - path: '/logout', - title: 'Sign out', - component: LogoutFeatures, - type: 'unprotected', - layout: 'main', - }, { path: '/login', title: 'Log in', diff --git a/frontend/src/component/user/UserProfile/UserProfile.jsx b/frontend/src/component/user/UserProfile/UserProfile.jsx index 4ad497c7f1..7d729a5dbe 100644 --- a/frontend/src/component/user/UserProfile/UserProfile.jsx +++ b/frontend/src/component/user/UserProfile/UserProfile.jsx @@ -14,7 +14,6 @@ const UserProfile = ({ location, fetchUser, updateSettingLocation, - logoutUser, }) => { const [showProfile, setShowProfile] = useState(false); const [currentLocale, setCurrentLocale] = useState([]); @@ -74,7 +73,6 @@ const UserProfile = ({ profile={profile} updateSettingLocation={updateSettingLocation} possibleLocales={possibleLocales} - logoutUser={logoutUser} location={location} setCurrentLocale={setCurrentLocale} currentLocale={currentLocale} diff --git a/frontend/src/component/user/UserProfile/UserProfileContent/UserProfileContent.jsx b/frontend/src/component/user/UserProfile/UserProfileContent/UserProfileContent.jsx index 6c501b5c9d..fe365e8dec 100644 --- a/frontend/src/component/user/UserProfile/UserProfileContent/UserProfileContent.jsx +++ b/frontend/src/component/user/UserProfile/UserProfileContent/UserProfileContent.jsx @@ -17,6 +17,7 @@ import { Alert } from '@material-ui/lab'; import EditProfile from '../EditProfile/EditProfile'; import legacyStyles from '../../user.module.scss'; import usePermissions from '../../../../hooks/usePermissions'; +import { getBasePath } from '../../../../utils/format-path'; const UserProfileContent = ({ showProfile, @@ -26,7 +27,6 @@ const UserProfileContent = ({ imageUrl, currentLocale, setCurrentLocale, - logoutUser, }) => { const commonStyles = useCommonStyles(); const [updatedPassword, setUpdatedPassword] = useState(false); @@ -155,7 +155,7 @@ const UserProfileContent = ({ diff --git a/frontend/src/component/user/UserProfile/index.jsx b/frontend/src/component/user/UserProfile/index.jsx index 1ccafe16e8..1b1337b1a5 100644 --- a/frontend/src/component/user/UserProfile/index.jsx +++ b/frontend/src/component/user/UserProfile/index.jsx @@ -1,11 +1,10 @@ import { connect } from 'react-redux'; import UserProfile from './UserProfile'; -import { fetchUser, logoutUser } from '../../../store/user/actions'; +import { fetchUser } from '../../../store/user/actions'; import { updateSettingForGroup } from '../../../store/settings/actions'; const mapDispatchToProps = { fetchUser, - logoutUser, updateSettingLocation: updateSettingForGroup('location'), }; diff --git a/frontend/src/component/user/logout-component.jsx b/frontend/src/component/user/logout-component.jsx deleted file mode 100644 index bb4855b991..0000000000 --- a/frontend/src/component/user/logout-component.jsx +++ /dev/null @@ -1,29 +0,0 @@ -import React, { useEffect } from 'react'; -import PropTypes from 'prop-types'; -import { Card, CardContent, CardHeader } from '@material-ui/core'; -import { styles as commonStyles } from '../common'; - -const LogoutComponent = ({ logoutUser, user }) => { - useEffect(() => { - if(user) { - logoutUser(); - } - }, [user, logoutUser]); - - return ( - - Logged out - - You have now been successfully logged out of Unleash. -

- Thank you for using Unleash.{' '} -
-
- ); -}; -LogoutComponent.propTypes = { - logoutUser: PropTypes.func.isRequired, - user: PropTypes.object, -}; - -export default LogoutComponent; diff --git a/frontend/src/component/user/logout-container.jsx b/frontend/src/component/user/logout-container.jsx deleted file mode 100644 index 1e5f79c125..0000000000 --- a/frontend/src/component/user/logout-container.jsx +++ /dev/null @@ -1,13 +0,0 @@ -import { connect } from 'react-redux'; -import LogoutComponent from './logout-component'; -import { logoutUser } from '../../store/user/actions'; - -const mapDispatchToProps = { - logoutUser, -}; - -const mapStateToProps = (state) => ({ - user: state.user.get('profile'), -}); - -export default connect(mapStateToProps, mapDispatchToProps)(LogoutComponent); diff --git a/frontend/src/page/admin/auth/authentication.jsx b/frontend/src/page/admin/auth/authentication.jsx index 3c8c4cef99..186d63de33 100644 --- a/frontend/src/page/admin/auth/authentication.jsx +++ b/frontend/src/page/admin/auth/authentication.jsx @@ -9,8 +9,12 @@ import TabNav from '../../../component/common/TabNav/TabNav'; import PageContent from '../../../component/common/PageContent/PageContent'; import ConditionallyRender from '../../../component/common/ConditionallyRender/ConditionallyRender'; -function AdminAuthPage({ authenticationType, history, enableOIDC }) { +function AdminAuthPage({ authenticationType, history }) { const tabs = [ + { + label: 'OpenID Connect', + component: , + }, { label: 'SAML 2.0', component: , @@ -21,13 +25,6 @@ function AdminAuthPage({ authenticationType, history, enableOIDC }) { }, ]; - if(enableOIDC) { - tabs.unshift( { - label: 'OpenID Connect', - component: , - },) - } - return (
@@ -71,7 +68,6 @@ AdminAuthPage.propTypes = { match: PropTypes.object.isRequired, history: PropTypes.object.isRequired, authenticationType: PropTypes.string, - enableOIDC: PropTypes.bool, }; export default AdminAuthPage; diff --git a/frontend/src/page/admin/auth/index.js b/frontend/src/page/admin/auth/index.js index cc34e9cfa4..07d89be806 100644 --- a/frontend/src/page/admin/auth/index.js +++ b/frontend/src/page/admin/auth/index.js @@ -1,10 +1,8 @@ import { connect } from 'react-redux'; import component from './authentication'; -import { OIDC } from '../../../component/common/flags'; const mapStateToProps = state => ({ authenticationType: state.uiConfig.toJS().authenticationType, - enableOIDC: !!state.uiConfig.toJS().flags[OIDC], }); const Container = connect(mapStateToProps, { })(component); diff --git a/frontend/src/page/admin/auth/saml-auth.jsx b/frontend/src/page/admin/auth/saml-auth.jsx index 1496e9ad33..8b1abcb216 100644 --- a/frontend/src/page/admin/auth/saml-auth.jsx +++ b/frontend/src/page/admin/auth/saml-auth.jsx @@ -132,7 +132,7 @@ function SamlAuth({ config, getSamlConfig, updateSamlConfig, unleashUrl }) { label="Single Sign-On URL" name="signOnUrl" value={data.signOnUrl || ''} - style={{ width: '400px' }} + style={{ width: '400px'}} variant="outlined" size="small" required @@ -153,11 +153,12 @@ function SamlAuth({ config, getSamlConfig, updateSamlConfig, unleashUrl }) { label="X.509 Certificate" name="certificate" value={data.certificate || ''} - style={{ - width: '100%', - fontSize: '0.7em', - fontFamily: 'monospace', - }} + style={{width: '100%'}} + InputProps={{ + style: { + fontSize: '0.6em', + fontFamily: 'monospace', + }}} multiline rows={14} rowsMax={14} @@ -167,6 +168,55 @@ function SamlAuth({ config, getSamlConfig, updateSamlConfig, unleashUrl }) { /> +

Optional Configuration

+ + + Single Sign-out URL +

+ (optional) The url to redirect the user to for + signing out of the IDP. +

+
+ + + +
+ + + Service Provider X.509 Certificate +

+ (Optional) The private certificate used by the Service Provider used to sign the SAML 2.0 + request towards the IDP. E.g. used to sign single logout requests (SLO). +

+
+ + + +
Auto-create users diff --git a/frontend/src/page/project/index.js b/frontend/src/page/project/index.js index dde02cc3d8..aea5f5818e 100644 --- a/frontend/src/page/project/index.js +++ b/frontend/src/page/project/index.js @@ -1,5 +1,5 @@ import React from 'react'; -import ProjectList from '../../component/project/ProjectList'; +import ProjectList from '../../component/project/ProjectList/ProjectList'; import PropTypes from 'prop-types'; const render = ({ history }) => ; diff --git a/frontend/src/page/user/logout.js b/frontend/src/page/user/logout.js deleted file mode 100644 index 5a90542feb..0000000000 --- a/frontend/src/page/user/logout.js +++ /dev/null @@ -1,6 +0,0 @@ -import React from 'react'; -import LogoutContainer from './../../component/user/logout-container'; - -const render = () => ; - -export default render; diff --git a/frontend/src/setupProxy.js b/frontend/src/setupProxy.js new file mode 100644 index 0000000000..be4c73aa22 --- /dev/null +++ b/frontend/src/setupProxy.js @@ -0,0 +1,27 @@ +const { createProxyMiddleware } = require('http-proxy-middleware'); + +const API_URL = process.env.UNLEASH_API || 'http://localhost:4242'; + +module.exports = function(app) { + app.use( + '/api', + createProxyMiddleware({ + target: API_URL, + changeOrigin: true, + }), + ); + app.use( + '/auth', + createProxyMiddleware({ + target: API_URL, + changeOrigin: true, + }), + ); + app.use( + '/logout', + createProxyMiddleware({ + target: API_URL, + changeOrigin: true, + }), + ); +}; \ No newline at end of file diff --git a/frontend/src/store/user/actions.js b/frontend/src/store/user/actions.js index 49de02ce64..c9687dc8c9 100644 --- a/frontend/src/store/user/actions.js +++ b/frontend/src/store/user/actions.js @@ -1,7 +1,5 @@ import api from './api'; import { dispatchError } from '../util'; -import { RESET_LOADING } from '../feature-toggle/actions'; -import { getBasePath } from '../../utils/format-path'; export const USER_CHANGE_CURRENT = 'USER_CHANGE_CURRENT'; export const USER_LOGOUT = 'USER_LOGOUT'; export const USER_LOGIN = 'USER_LOGIN'; @@ -62,17 +60,3 @@ export function passwordLogin(path, user) { .then(() => dispatch({ type: USER_LOGIN })); }; } - -export function logoutUser() { - const basepath = getBasePath(); - return dispatch => { - return api - .logoutUser() - .then(() => dispatch({ type: USER_LOGOUT })) - .then(() => dispatch({ type: RESET_LOADING })) - .then(() => { - window.location = `${basepath}`; - }) - .catch(handleError); - }; -} diff --git a/frontend/src/store/user/api.js b/frontend/src/store/user/api.js index 261c39cbff..1e54c701ee 100644 --- a/frontend/src/store/user/api.js +++ b/frontend/src/store/user/api.js @@ -3,13 +3,6 @@ import { throwIfNotSuccess, headers } from '../api-helper'; const URI = formatApiPath('api/admin/user'); -function logoutUser() { - return fetch(formatApiPath('logout'), { - method: 'GET', - credentials: 'include', - }).then(throwIfNotSuccess); -} - function fetchUser() { return fetch(URI, { credentials: 'include' }) .then(throwIfNotSuccess) @@ -53,7 +46,6 @@ const api = { fetchUser, insecureLogin, demoLogin, - logoutUser, passwordLogin, }; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index c01d4861be..99d3ef0043 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1985,6 +1985,13 @@ resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz#3c9ee980f1a10d6021ae6632ca3e79ca2ec4fb50" integrity sha512-giAlZwstKbmvMk1OO7WXSj4OZ0keXAcl2TQq4LWHiiPH2ByaH7WeUzng+Qej8UPxxv+8lRTuouo0iaNDBuzIBA== +"@types/http-proxy@^1.17.5": + version "1.17.6" + resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.6.tgz#62dc3fade227d6ac2862c8f19ee0da9da9fd8616" + integrity sha512-+qsjqR75S/ib0ig0R9WN+CDoZeOBU6F2XLewgC4KVgdXiNHiKKHFEMRHOrs5PbYE97D5vataw5wPj4KLYfUkuQ== + dependencies: + "@types/node" "*" + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" @@ -6276,7 +6283,18 @@ http-proxy-middleware@0.19.1: lodash "^4.17.11" micromatch "^3.1.10" -http-proxy@^1.17.0: +http-proxy-middleware@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.1.tgz#7ef3417a479fb7666a571e09966c66a39bd2c15f" + integrity sha512-cfaXRVoZxSed/BmkA7SwBVNI9Kj7HFltaE5rqYOub5kWzWZ+gofV2koVN1j2rMW7pEfSSlCHGJ31xmuyFyfLOg== + dependencies: + "@types/http-proxy" "^1.17.5" + http-proxy "^1.18.1" + is-glob "^4.0.1" + is-plain-obj "^3.0.0" + micromatch "^4.0.2" + +http-proxy@^1.17.0, http-proxy@^1.18.1: version "1.18.1" resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== @@ -6760,6 +6778,11 @@ is-plain-obj@^1.0.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= +is-plain-obj@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" + integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== + is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -12709,4 +12732,4 @@ yargs@^15.4.1: yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== \ No newline at end of file + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==