1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-31 00:16:47 +01:00

fix: add logout as an explicit call (#311)

Co-authored-by: Christopher Kolstad <chriswk@getunleash.ai>
This commit is contained in:
Ivar Conradi Østhus 2021-07-20 22:56:57 +02:00 committed by GitHub
parent 1a63d91f95
commit e339e5c01f
20 changed files with 130 additions and 119 deletions

View File

@ -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"
}
}

View File

@ -1,6 +1,4 @@
export const P = 'P';
export const C = 'C';
export const RBAC = 'RBAC';
export const OIDC = 'OIDC';
export const PROJECTFILTERING = false;

View File

@ -83,12 +83,5 @@ Array [
"title": "Admin",
"type": "protected",
},
Object {
"component": [Function],
"layout": "main",
"path": "/logout",
"title": "Sign out",
"type": "unprotected",
},
]
`;

View File

@ -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();
});

View File

@ -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 = ({
}
/>
<Divider />
<div className={styles.iconLinkList}>{renderLinks()}</div>
<div className={styles.iconLinkList}>
{renderLinks()}
<a className={styles.navigationLink} href={`${getBasePath()}/logout`}>
<ExitToApp className={styles.navigationIcon} />
Sign out
</a>
</div>
</div>
</Drawer>
);

View File

@ -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',

View File

@ -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}

View File

@ -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 = ({
<Button
variant="contained"
color="primary"
onClick={logoutUser}
href={`${getBasePath()}/logout`}
>
Logout
</Button>

View File

@ -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'),
};

View File

@ -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 (
<Card shadow={0} className={commonStyles.fullwidth}>
<CardHeader>Logged out</CardHeader>
<CardContent>
You have now been successfully logged out of Unleash.
<br /> <br />
Thank you for using Unleash.{' '}
</CardContent>
</Card>
);
};
LogoutComponent.propTypes = {
logoutUser: PropTypes.func.isRequired,
user: PropTypes.object,
};
export default LogoutComponent;

View File

@ -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);

View File

@ -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: <OidcAuth />,
},
{
label: 'SAML 2.0',
component: <SamlAuth />,
@ -21,13 +25,6 @@ function AdminAuthPage({ authenticationType, history, enableOIDC }) {
},
];
if(enableOIDC) {
tabs.unshift( {
label: 'OpenID Connect',
component: <OidcAuth />,
},)
}
return (
<div>
<AdminMenu history={history} />
@ -71,7 +68,6 @@ AdminAuthPage.propTypes = {
match: PropTypes.object.isRequired,
history: PropTypes.object.isRequired,
authenticationType: PropTypes.string,
enableOIDC: PropTypes.bool,
};
export default AdminAuthPage;

View File

@ -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);

View File

@ -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 }) {
/>
</Grid>
</Grid>
<h3>Optional Configuration</h3>
<Grid container spacing={3}>
<Grid item md={5}>
<strong>Single Sign-out URL</strong>
<p>
(optional) The url to redirect the user to for
signing out of the IDP.
</p>
</Grid>
<Grid item md={6}>
<TextField
onChange={updateField}
label="Single Sign-out URL"
name="signOutUrl"
value={data.signOutUrl || ''}
style={{ width: '400px'}}
variant="outlined"
size="small"
/>
</Grid>
</Grid>
<Grid container spacing={3}>
<Grid item md={5}>
<strong>Service Provider X.509 Certificate</strong>
<p>
(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).
</p>
</Grid>
<Grid item md={7}>
<TextField
onChange={updateField}
label="X.509 Certificate"
name="spCertificate"
value={data.spCertificate || ''}
style={{width: '100%'}}
InputProps={{
style: {
fontSize: '0.6em',
fontFamily: 'monospace',
}}}
multiline
rows={14}
rowsMax={14}
variant="outlined"
size="small"
/>
</Grid>
</Grid>
<Grid container spacing={3}>
<Grid item md={5}>
<strong>Auto-create users</strong>

View File

@ -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 }) => <ProjectList history={history} />;

View File

@ -1,6 +0,0 @@
import React from 'react';
import LogoutContainer from './../../component/user/logout-container';
const render = () => <LogoutContainer />;
export default render;

View File

@ -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,
}),
);
};

View File

@ -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);
};
}

View File

@ -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,
};

View File

@ -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==
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==