mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
Feat/auth hosted section (#280)
This commit is contained in:
parent
d7e6219070
commit
4b826715a7
158
frontend/src/component/user/HostedAuth/HostedAuth.jsx
Normal file
158
frontend/src/component/user/HostedAuth/HostedAuth.jsx
Normal file
@ -0,0 +1,158 @@
|
||||
import React, { useState } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Grid, TextField, Typography } from '@material-ui/core';
|
||||
import LockRounded from '@material-ui/icons/LockRounded';
|
||||
import { useHistory } from 'react-router';
|
||||
import { useCommonStyles } from '../../../common.styles';
|
||||
import { useStyles } from './HostedAuth.styles';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { GoogleSvg } from './Icons';
|
||||
|
||||
const PasswordAuth = ({ authDetails, passwordLogin, loadInitialData }) => {
|
||||
const commonStyles = useCommonStyles();
|
||||
const styles = useStyles();
|
||||
const history = useHistory();
|
||||
const [username, setUsername] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [errors, setErrors] = useState({
|
||||
usernameError: '',
|
||||
passwordError: '',
|
||||
});
|
||||
|
||||
const handleSubmit = async evt => {
|
||||
evt.preventDefault();
|
||||
|
||||
if (!username) {
|
||||
setErrors(prev => ({
|
||||
...prev,
|
||||
usernameError: 'This is a required field',
|
||||
}));
|
||||
}
|
||||
if (!password) {
|
||||
setErrors(prev => ({
|
||||
...prev,
|
||||
passwordError: 'This is a required field',
|
||||
}));
|
||||
}
|
||||
|
||||
if (!password || !username) {
|
||||
return;
|
||||
}
|
||||
|
||||
const user = { username, password };
|
||||
const path = evt.target.action;
|
||||
|
||||
try {
|
||||
await passwordLogin(path, user);
|
||||
await loadInitialData();
|
||||
history.push(`/`);
|
||||
} catch (error) {
|
||||
if (error.statusCode === 404 || error.statusCode === 400) {
|
||||
setErrors(prev => ({
|
||||
...prev,
|
||||
apiError: 'Invalid login details',
|
||||
}));
|
||||
setPassword('');
|
||||
setUsername('');
|
||||
} else {
|
||||
setErrors({
|
||||
apiError: 'Unknown error while trying to authenticate.',
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const { usernameError, passwordError, apiError } = errors;
|
||||
const { options = [] } = authDetails;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<br />
|
||||
<div>
|
||||
{options.map(o => (
|
||||
<div
|
||||
key={o.type}
|
||||
className={classnames(
|
||||
styles.contentContainer,
|
||||
commonStyles.contentSpacingY
|
||||
)}
|
||||
>
|
||||
<Button color="primary" variant="outlined" href={o.path} startIcon={o.type === 'google' ? <GoogleSvg /> : <LockRounded />}>
|
||||
{o.message}
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<p className={styles.fancyLine}>or</p>
|
||||
<form onSubmit={handleSubmit} action={authDetails.path}>
|
||||
<Typography variant="subtitle2" className={styles.apiError}>
|
||||
{apiError}
|
||||
</Typography>
|
||||
<div
|
||||
className={classnames(
|
||||
styles.contentContainer,
|
||||
commonStyles.contentSpacingY
|
||||
)}
|
||||
>
|
||||
<TextField
|
||||
label="Username or email"
|
||||
name="username"
|
||||
type="string"
|
||||
onChange={evt => setUsername(evt.target.value)}
|
||||
value={username}
|
||||
error={!!usernameError}
|
||||
helperText={usernameError}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
/>
|
||||
<TextField
|
||||
label="Password"
|
||||
onChange={evt => setPassword(evt.target.value)}
|
||||
name="password"
|
||||
type="password"
|
||||
value={password}
|
||||
error={!!passwordError}
|
||||
helperText={passwordError}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
/>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={6}>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
type="submit"
|
||||
style={{ maxWidth: '150px' }}
|
||||
>
|
||||
Sign in
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Link to="/forgotten-password">
|
||||
<Typography variant="body2" align="right">
|
||||
Forgot your password?
|
||||
</Typography>
|
||||
</Link>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<br />
|
||||
<br />
|
||||
<Typography variant="body2" align="center">Don't have an account? <br /> <a href="https://www.unleash-hosted.com/pricing">Sign up</a></Typography>
|
||||
|
||||
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
PasswordAuth.propTypes = {
|
||||
authDetails: PropTypes.object.isRequired,
|
||||
passwordLogin: PropTypes.func.isRequired,
|
||||
loadInitialData: PropTypes.func.isRequired,
|
||||
history: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default PasswordAuth;
|
40
frontend/src/component/user/HostedAuth/HostedAuth.styles.js
Normal file
40
frontend/src/component/user/HostedAuth/HostedAuth.styles.js
Normal file
@ -0,0 +1,40 @@
|
||||
import { makeStyles } from '@material-ui/styles';
|
||||
|
||||
export const useStyles = makeStyles(theme => ({
|
||||
loginContainer: {
|
||||
minWidth: '350px',
|
||||
[theme.breakpoints.down('xs')]: {
|
||||
width: '100%',
|
||||
minWidth: 'auto',
|
||||
},
|
||||
},
|
||||
contentContainer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
},
|
||||
apiError: {
|
||||
color: theme.palette.error.main,
|
||||
},
|
||||
|
||||
fancyLine: {
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
margin: '10px 0',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
textAlign: 'center',
|
||||
color: 'gray',
|
||||
'&::before': {
|
||||
content: '""',
|
||||
borderTop: '1px solid silver',
|
||||
margin: '0 20px 0 0',
|
||||
flex: '1 0 20px',
|
||||
},
|
||||
'&::after': {
|
||||
content: '""',
|
||||
borderTop: '1px solid silver',
|
||||
margin: '0 20px 0 0',
|
||||
flex: '1 0 20px',
|
||||
}
|
||||
}
|
||||
}));
|
102
frontend/src/component/user/HostedAuth/Icons.jsx
Normal file
102
frontend/src/component/user/HostedAuth/Icons.jsx
Normal file
@ -0,0 +1,102 @@
|
||||
export const GoogleSvg = () => (
|
||||
<svg
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="46px"
|
||||
height="46px"
|
||||
viewBox="0 0 46 46"
|
||||
style={{display: 'block', width: '36px', height: '36px'}}
|
||||
>
|
||||
<defs>
|
||||
<filter
|
||||
x="-50%"
|
||||
y="-50%"
|
||||
width="200%"
|
||||
height="200%"
|
||||
filterUnits="objectBoundingBox"
|
||||
id="filter-1"
|
||||
>
|
||||
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1" />
|
||||
<feGaussianBlur
|
||||
stdDeviation="0.5"
|
||||
in="shadowOffsetOuter1"
|
||||
result="shadowBlurOuter1"
|
||||
/>
|
||||
<feColorMatrix
|
||||
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.168 0"
|
||||
in="shadowBlurOuter1"
|
||||
type="matrix"
|
||||
result="shadowMatrixOuter1"
|
||||
/>
|
||||
<feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter2" />
|
||||
<feGaussianBlur
|
||||
stdDeviation="0.5"
|
||||
in="shadowOffsetOuter2"
|
||||
result="shadowBlurOuter2"
|
||||
/>
|
||||
<feColorMatrix
|
||||
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.084 0"
|
||||
in="shadowBlurOuter2"
|
||||
type="matrix"
|
||||
result="shadowMatrixOuter2"
|
||||
/>
|
||||
<feMerge>
|
||||
<feMergeNode in="shadowMatrixOuter1" />
|
||||
<feMergeNode in="shadowMatrixOuter2" />
|
||||
<feMergeNode in="SourceGraphic" />
|
||||
</feMerge>
|
||||
</filter>
|
||||
<rect id="path-2" x="0" y="0" width="40" height="40" rx="2" />
|
||||
</defs>
|
||||
<g
|
||||
id="Google-Button"
|
||||
stroke="none"
|
||||
strokeWidth="1"
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
>
|
||||
<g id="9-PATCH" transform="translate(-608.000000, -160.000000)" />
|
||||
<g
|
||||
id="btn_google_light_normal"
|
||||
transform="translate(-1.000000, -1.000000)"
|
||||
>
|
||||
<g
|
||||
id="button"
|
||||
transform="translate(4.000000, 4.000000)"
|
||||
filter="url(#filter-1)"
|
||||
>
|
||||
<g id="button-bg">
|
||||
<use fill="#FFFFFF" fillRule="evenodd" />
|
||||
<use fill="none" />
|
||||
<use fill="none" />
|
||||
<use fill="none" />
|
||||
</g>
|
||||
</g>
|
||||
<g id="logo_googleg_48dp" transform="translate(15.000000, 15.000000)">
|
||||
<path
|
||||
d="M17.64,9.20454545 C17.64,8.56636364 17.5827273,7.95272727 17.4763636,7.36363636 L9,7.36363636 L9,10.845 L13.8436364,10.845 C13.635,11.97 13.0009091,12.9231818 12.0477273,13.5613636 L12.0477273,15.8195455 L14.9563636,15.8195455 C16.6581818,14.2527273 17.64,11.9454545 17.64,9.20454545 L17.64,9.20454545 Z"
|
||||
id="Shape"
|
||||
fill="#4285F4"
|
||||
/>
|
||||
<path
|
||||
d="M9,18 C11.43,18 13.4672727,17.1940909 14.9563636,15.8195455 L12.0477273,13.5613636 C11.2418182,14.1013636 10.2109091,14.4204545 9,14.4204545 C6.65590909,14.4204545 4.67181818,12.8372727 3.96409091,10.71 L0.957272727,10.71 L0.957272727,13.0418182 C2.43818182,15.9831818 5.48181818,18 9,18 L9,18 Z"
|
||||
id="Shape"
|
||||
fill="#34A853"
|
||||
/>
|
||||
<path
|
||||
d="M3.96409091,10.71 C3.78409091,10.17 3.68181818,9.59318182 3.68181818,9 C3.68181818,8.40681818 3.78409091,7.83 3.96409091,7.29 L3.96409091,4.95818182 L0.957272727,4.95818182 C0.347727273,6.17318182 0,7.54772727 0,9 C0,10.4522727 0.347727273,11.8268182 0.957272727,13.0418182 L3.96409091,10.71 L3.96409091,10.71 Z"
|
||||
id="Shape"
|
||||
fill="#FBBC05"
|
||||
/>
|
||||
<path
|
||||
d="M9,3.57954545 C10.3213636,3.57954545 11.5077273,4.03363636 12.4404545,4.92545455 L15.0218182,2.34409091 C13.4631818,0.891818182 11.4259091,0 9,0 C5.48181818,0 2.43818182,2.01681818 0.957272727,4.95818182 L3.96409091,7.29 C4.67181818,5.16272727 6.65590909,3.57954545 9,3.57954545 L9,3.57954545 Z"
|
||||
id="Shape"
|
||||
fill="#EA4335"
|
||||
/>
|
||||
<path d="M0,0 L18,0 L18,18 L0,18 L0,0 Z" id="Shape" />
|
||||
</g>
|
||||
<g id="handles_square" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
@ -134,7 +134,7 @@ const PasswordAuth = ({ authDetails, passwordLogin, loadInitialData }) => {
|
||||
)}
|
||||
>
|
||||
<Button color="primary" variant="contained" href={o.path}>
|
||||
{o.value}
|
||||
{o.message || o.value}
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
|
@ -3,11 +3,13 @@ import PropTypes from 'prop-types';
|
||||
import SimpleAuth from './SimpleAuth/SimpleAuth';
|
||||
import AuthenticationCustomComponent from './authentication-custom-component';
|
||||
import PasswordAuth from './PasswordAuth/PasswordAuth';
|
||||
import HostedAuth from './HostedAuth/HostedAuth';
|
||||
import DemoAuth from './DemoAuth';
|
||||
|
||||
const SIMPLE_TYPE = 'unsecure';
|
||||
const DEMO_TYPE = 'demo';
|
||||
const PASSWORD_TYPE = 'password';
|
||||
const HOSTED_TYPE = 'enterprise-hosted';
|
||||
|
||||
class AuthComponent extends React.Component {
|
||||
static propTypes = {
|
||||
@ -51,6 +53,15 @@ class AuthComponent extends React.Component {
|
||||
history={this.props.history}
|
||||
/>
|
||||
);
|
||||
} else if (authDetails.type === HOSTED_TYPE) {
|
||||
content = (
|
||||
<HostedAuth
|
||||
passwordLogin={this.props.passwordLogin}
|
||||
authDetails={authDetails}
|
||||
loadInitialData={this.props.loadInitialData}
|
||||
history={this.props.history}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
content = (
|
||||
<AuthenticationCustomComponent authDetails={authDetails} />
|
||||
|
@ -36,12 +36,24 @@ function AdminAuthPage({ authenticationType, history }) {
|
||||
in order configure Single Sign-on.</Alert>
|
||||
}
|
||||
/>
|
||||
<ConditionallyRender condition={authenticationType === 'demo'}
|
||||
show={
|
||||
<Alert severity="warning">
|
||||
You are running Unleash in demo mode. You have to use the Enterprise edition
|
||||
in order configure Single Sign-on.</Alert>
|
||||
}
|
||||
/>
|
||||
<ConditionallyRender condition={authenticationType === 'custom'}
|
||||
show={
|
||||
<Alert severity="warning">You have decided to use custom authentication type. You have to use the Enterprise edition
|
||||
in order configure Single Sign-on from the user interface.</Alert>
|
||||
}
|
||||
/>
|
||||
<ConditionallyRender condition={authenticationType === 'hosted'}
|
||||
show={
|
||||
<Alert severity="info">You Unleash instance is managed by the Unleash-hosted team.</Alert>
|
||||
}
|
||||
/>
|
||||
</PageContent>
|
||||
</div>
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user