mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +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