Create wt-odlux directory
[ccsdk/features.git] / sdnr / wt-odlux / odlux / framework / src / views / login.tsx
1 /**
2  * ============LICENSE_START========================================================================
3  * ONAP : ccsdk feature sdnr wt odlux
4  * =================================================================================================
5  * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
6  * =================================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
8  * in compliance with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software distributed under the License
13  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
14  * or implied. See the License for the specific language governing permissions and limitations under
15  * the License.
16  * ============LICENSE_END==========================================================================
17  */
18 import React, { FC, useEffect, useState } from 'react';
19 import { RouteComponentProps, withRouter } from 'react-router-dom';
20
21 import Alert from '@mui/material/Alert';
22 import Avatar from '@mui/material/Avatar';
23 import Button from '@mui/material/Button';
24 import CssBaseline from '@mui/material/CssBaseline';
25 import FormControl from '@mui/material/FormControl';
26 import Input from '@mui/material/Input';
27 import InputLabel from '@mui/material/InputLabel';
28 import Paper from '@mui/material/Paper';
29 import { Theme } from '@mui/material/styles';
30 import Typography from '@mui/material/Typography';
31
32 import { makeStyles } from '@mui/styles';
33
34 import { useApplicationDispatch, useSelectApplicationState } from '../flux/connect';
35 import authenticationService from '../services/authenticationService';
36
37 import { loginUserAction, UpdatePolicies } from '../actions/authentication';
38 import { updateExternalLoginProviderAsyncActionCreator } from '../actions/loginProvider';
39
40 import { AuthPolicy, AuthToken, User } from '../models/authentication';
41
42 const loginIcon = require('../assets/icons/User.svg');
43
44 const styles = makeStyles((theme: Theme) =>{
45   return{
46   layout: {
47     width: 'auto',
48     display: 'block', // Fix IE11 issue.
49     marginLeft: theme.spacing(3),
50     marginRight: theme.spacing(3),
51     [theme.breakpoints.up(400 + Number(theme.spacing(3).replace('px','')) * 2)]: {
52       width: 400,
53       marginLeft: 'auto',
54       marginRight: 'auto',
55     },
56   },
57   paper: {
58     marginTop: theme.spacing(8),
59     display: 'flex',
60     flexDirection: 'column',
61     alignItems: 'center',
62     padding: `${theme.spacing(2)} ${theme.spacing(3)} ${theme.spacing(3)}`,
63   },
64   avatar: {
65     margin: theme.spacing(1),
66     backgroundColor: theme.palette.secondary.main,
67   },
68   form: {
69     width: '100%', // Fix IE11 issue.
70     marginTop: theme.spacing(1),
71   },
72   submit: {
73     marginTop: theme.spacing(3),
74   },
75   lineContainer:{
76     width: '100%',
77     height: 10,
78     borderBottom: '1px solid grey',
79     textAlign: 'center',
80     marginTop:15,
81     marginBottom:5
82   },
83   thirdPartyDivider:{
84     fontSize: 15,
85      backgroundColor: 'white',
86      padding: '0 10px',
87      color: 'grey'
88   }
89 };
90 });
91
92
93 type LoginProps = RouteComponentProps;
94
95 // todo: ggf. redirect to einbauen
96 const LoginComponent:  FC<LoginProps> = (props) => {
97
98   const search = useSelectApplicationState(state => state.framework.navigationState.search);
99   const authentication = useSelectApplicationState(state => state.framework.applicationState.authentication);
100   const externalLoginProviders = useSelectApplicationState(state => state.framework.applicationState.externalLoginProviders);
101   
102   const dispatch = useApplicationDispatch();
103   const updateExternalProviders = () => dispatch(updateExternalLoginProviderAsyncActionCreator());
104   const updateAuthentication = (token: AuthToken | null) => {
105     const user = token && new User(token) || undefined;
106     dispatch(loginUserAction(user));
107   }
108   const updatePolicies = (policies?: AuthPolicy[]) => {
109     return dispatch(new UpdatePolicies(policies));
110   }
111
112   const [isBusy, setBusy] = useState(false);
113   const [username, setUsername] = useState("");
114   const [password, setPassword] = useState("");
115   const [scope, setScope] = useState("sdn");
116   const [message, setMessage] = useState("");
117   const [isServerReady, setIsServerReady] = useState(false);
118
119   useEffect(()=>{
120      if (authentication === "oauth" && (externalLoginProviders == null || externalLoginProviders.length === 0)){
121        updateExternalProviders();
122      }
123
124     authenticationService.getServerReadyState().then(result =>{
125       setIsServerReady(result);
126     })
127   },[]);
128
129   const onSignIn = async (event: React.MouseEvent<HTMLButtonElement>) => {
130     event.preventDefault();
131   
132     setBusy(true);
133
134     const token = authentication === "oauth" 
135       ? await authenticationService.authenticateUserOAuth(username, password, scope)
136       : await authenticationService.authenticateUserBasicAuth(username, password, scope); 
137
138     updateAuthentication(token);
139     setBusy(false);
140
141     if (token) {
142       const query = search && search.replace(/^\?/, "").split('&').map(e => e.split("="));
143       const returnTo = query && query.find(e => e[0] === "returnTo");
144       props.history.replace(returnTo && returnTo[1] || "/");
145     }
146     else {
147
148       if(!isServerReady){
149         const ready = await authenticationService.getServerReadyState();
150         if(ready){
151           setIsServerReady(true);
152         }else{
153           setMessage("Login is currently not possible. Please re-try in a few minutes. If the problem persists, ask your administrator for assistance.");
154         }
155   
156       }else{
157         setMessage("Could not log in. Please check your credentials or ask your administrator for assistance.");
158         setPassword("");
159       }
160     }
161   }
162   
163   const classes = styles();
164   const areProvidersAvailable = externalLoginProviders && externalLoginProviders.length > 0;
165
166   return (
167     <>
168       <CssBaseline />
169       <main className={classes.layout}>
170         <Paper className={classes.paper}>
171           <Avatar className={classes.avatar}>
172             <img src={loginIcon} alt="loginIcon" />
173           </Avatar>
174           <Typography variant="caption">Sign in</Typography>
175           <form className={classes.form}>
176             {areProvidersAvailable &&
177               <>
178                 {
179                   externalLoginProviders!.map((provider, index) => (
180                     <Button
181                       aria-controls="externalLogin"
182                       aria-label={"external-login-identity-provider-" + (index + 1)}
183                       aria-haspopup="true"
184                       fullWidth
185                       variant="contained"
186                       color="inherit"
187                       className={classes.submit} onClick={() => { window.location = provider.loginUrl as any; }}>
188                       {provider.title}
189                     </Button>))
190                 }
191                 <div className={classes.lineContainer}>
192                   <span className={classes.thirdPartyDivider}>
193                     OR
194                   </span>
195                 </div>
196               </>
197             }
198             <FormControl variant="standard" margin="normal" required fullWidth>
199               <InputLabel htmlFor="username">Username</InputLabel>
200               <Input id="username" name="username" autoComplete="username" autoFocus
201                 disabled={isBusy}
202                 value={username}
203                 onChange={event => { setUsername(event.target.value); }} />
204             </FormControl>
205             <FormControl variant="standard" margin="normal" required fullWidth>
206               <InputLabel htmlFor="password">Password</InputLabel>
207               <Input
208                 name="password"
209                 type="password"
210                 id="password"
211                 autoComplete="current-password"
212                 disabled={isBusy}
213                 value={password}
214                 onChange={event => { setPassword(event.target.value); }}
215               />
216             </FormControl>
217             <FormControl variant="standard" margin="normal" required fullWidth>
218               <InputLabel htmlFor="password">Domain</InputLabel>
219               <Input
220                 name="scope"
221                 type="scope"
222                 id="scope"
223                 disabled={isBusy}
224                 value={scope}
225                 onChange={event => { setScope(event.target.value); }}
226               />
227             </FormControl>
228             <Button
229               aria-label="login-button"
230               type="submit"
231               fullWidth
232               variant="contained"
233               color="inherit"
234               disabled={isBusy}
235               className={classes.submit}
236               onClick={onSignIn}
237             >
238               Sign in
239             </Button>
240
241           </form>
242           {message && <Alert severity="error">{message}</Alert>}
243         </Paper>
244       </main>
245     </>
246   );
247 }
248
249 export const Login = withRouter(LoginComponent);
250 export default Login;