github.com/argoproj/argo-cd@v1.8.7/ui/src/app/login/components/login.tsx (about) 1 import {FormField} from 'argo-ui'; 2 import * as PropTypes from 'prop-types'; 3 import * as React from 'react'; 4 import {Form, Text} from 'react-form'; 5 import {RouteComponentProps} from 'react-router'; 6 7 import {AppContext} from '../../shared/context'; 8 import {AuthSettings} from '../../shared/models'; 9 import {services} from '../../shared/services'; 10 11 require('./login.scss'); 12 13 export interface LoginForm { 14 username: string; 15 password: string; 16 } 17 18 interface State { 19 authSettings: AuthSettings; 20 loginError: string; 21 loginInProgress: boolean; 22 returnUrl: string; 23 ssoLoginError: string; 24 } 25 26 export class Login extends React.Component<RouteComponentProps<{}>, State> { 27 public static contextTypes = { 28 apis: PropTypes.object 29 }; 30 31 public static getDerivedStateFromProps(props: RouteComponentProps<{}>): Partial<State> { 32 const search = new URLSearchParams(props.history.location.search); 33 const returnUrl = search.get('return_url') || ''; 34 const ssoLoginError = search.get('sso_error') || ''; 35 return {ssoLoginError, returnUrl}; 36 } 37 38 constructor(props: RouteComponentProps<{}>) { 39 super(props); 40 this.state = {authSettings: null, loginError: null, returnUrl: null, ssoLoginError: null, loginInProgress: false}; 41 } 42 43 public async componentDidMount() { 44 this.setState({ 45 authSettings: await services.authService.settings() 46 }); 47 } 48 49 public render() { 50 const authSettings = this.state.authSettings; 51 const ssoConfigured = authSettings && ((authSettings.dexConfig && (authSettings.dexConfig.connectors || []).length > 0) || authSettings.oidcConfig); 52 return ( 53 <div className='login'> 54 <div className='login__content'> 55 <div className='login__text'>Let's get stuff deployed!</div> 56 <div className='argo__logo' /> 57 </div> 58 <div className='login__box'> 59 <div className='login__logo width-control'> 60 <img className='logo-image' src='assets/images/argo_o.svg' alt='argo' /> 61 </div> 62 {ssoConfigured && ( 63 <div className='login__box_saml width-control'> 64 <a href={`auth/login?return_url=${encodeURIComponent(this.state.returnUrl)}`}> 65 <button className='argo-button argo-button--base argo-button--full-width argo-button--xlg'> 66 {(authSettings.oidcConfig && <span>Log in via {authSettings.oidcConfig.name}</span>) || 67 (authSettings.dexConfig.connectors.length === 1 && <span>Log in via {authSettings.dexConfig.connectors[0].name}</span>) || ( 68 <span>SSO Login</span> 69 )} 70 </button> 71 </a> 72 {this.state.ssoLoginError && <div className='argo-form-row__error-msg'>{this.state.ssoLoginError}</div>} 73 {authSettings && !authSettings.userLoginsDisabled && ( 74 <div className='login__saml-separator'> 75 <span>or</span> 76 </div> 77 )} 78 </div> 79 )} 80 {authSettings && !authSettings.userLoginsDisabled && ( 81 <Form 82 onSubmit={(params: LoginForm) => this.login(params.username, params.password, this.state.returnUrl)} 83 validateError={(params: LoginForm) => ({ 84 username: !params.username && 'Username is required', 85 password: !params.password && 'Password is required' 86 })}> 87 {formApi => ( 88 <form role='form' className='width-control' onSubmit={formApi.submitForm}> 89 <div className='argo-form-row'> 90 <FormField formApi={formApi} label='Username' field='username' component={Text} /> 91 </div> 92 <div className='argo-form-row'> 93 <FormField formApi={formApi} label='Password' field='password' component={Text} componentProps={{type: 'password'}} /> 94 {this.state.loginError && <div className='argo-form-row__error-msg'>{this.state.loginError}</div>} 95 </div> 96 <div className='login__form-row'> 97 <button disabled={this.state.loginInProgress} className='argo-button argo-button--full-width argo-button--xlg' type='submit'> 98 Sign In 99 </button> 100 </div> 101 </form> 102 )} 103 </Form> 104 )} 105 {authSettings && authSettings.userLoginsDisabled && !ssoConfigured && ( 106 <div className='argo-form-row__error-msg'>Login is disabled. Please contact your system administrator.</div> 107 )} 108 <div className='login__footer'> 109 <a href='https://argoproj.io' target='_blank'> 110 <img className='logo-image' src='assets/images/argologo.svg' alt='argo' /> 111 </a> 112 </div> 113 </div> 114 </div> 115 ); 116 } 117 118 private async login(username: string, password: string, returnURL: string) { 119 try { 120 this.setState({loginError: '', loginInProgress: true}); 121 this.appContext.apis.navigation.goto('.', {sso_error: null}); 122 await services.users.login(username, password); 123 this.setState({loginInProgress: false}); 124 if (returnURL) { 125 const url = new URL(returnURL); 126 this.appContext.apis.navigation.goto(url.pathname + url.search); 127 } else { 128 this.appContext.apis.navigation.goto('/applications'); 129 } 130 } catch (e) { 131 this.setState({loginError: e.response.body.error, loginInProgress: false}); 132 } 133 } 134 135 private get appContext(): AppContext { 136 return this.context as AppContext; 137 } 138 }