123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 |
- import {Component, Fragment} from 'react';
- import type {RouteComponentProps} from 'react-router';
- import styled from '@emotion/styled';
- import type {Client} from 'sentry/api';
- import {Alert} from 'sentry/components/alert';
- import {Button} from 'sentry/components/button';
- import LoadingError from 'sentry/components/loadingError';
- import LoadingIndicator from 'sentry/components/loadingIndicator';
- import NavTabs from 'sentry/components/navTabs';
- import {t, tct} from 'sentry/locale';
- import {space} from 'sentry/styles/space';
- import type {AuthConfig} from 'sentry/types';
- import withApi from 'sentry/utils/withApi';
- import LoginForm from './loginForm';
- import RegisterForm from './registerForm';
- import SsoForm from './ssoForm';
- const FORM_COMPONENTS = {
- login: LoginForm,
- register: RegisterForm,
- sso: SsoForm,
- } as const;
- type ActiveTab = keyof typeof FORM_COMPONENTS;
- type TabConfig = [key: ActiveTab, label: string, disabled?: boolean];
- type Props = RouteComponentProps<{orgId?: string}, {}> & {
- api: Client;
- };
- type State = {
- activeTab: ActiveTab;
- authConfig: null | AuthConfig;
- error: null | boolean;
- loading: boolean;
- };
- class Login extends Component<Props, State> {
- state: State = {
- loading: true,
- error: null,
- activeTab: 'login',
- authConfig: null,
- };
- componentDidMount() {
- this.fetchData();
- }
- handleSetTab = (activeTab: ActiveTab, event: React.MouseEvent) => {
- this.setState({activeTab});
- event.preventDefault();
- };
- fetchData = async () => {
- const {api} = this.props;
- try {
- const response = await api.requestPromise('/auth/config/');
- const {vsts_login_link, github_login_link, google_login_link, ...config} = response;
- const authConfig = {
- ...config,
- vstsLoginLink: vsts_login_link,
- githubLoginLink: github_login_link,
- googleLoginLink: google_login_link,
- };
- this.setState({authConfig});
- } catch (e) {
- this.setState({error: true});
- }
- this.setState({loading: false});
- };
- get hasAuthProviders() {
- if (this.state.authConfig === null) {
- return false;
- }
- const {githubLoginLink, googleLoginLink, vstsLoginLink} = this.state.authConfig;
- return !!(githubLoginLink || vstsLoginLink || googleLoginLink);
- }
- render() {
- const {loading, error, activeTab, authConfig} = this.state;
- const FormComponent = FORM_COMPONENTS[activeTab];
- const tabs: TabConfig[] = [
- ['login', t('Login')],
- ['sso', t('Single Sign-On')],
- ['register', t('Register'), !authConfig?.canRegister],
- ];
- const renderTab = ([key, label, disabled]: TabConfig) =>
- !disabled && (
- <li key={key} className={activeTab === key ? 'active' : ''}>
- <a href="#" onClick={e => this.handleSetTab(key, e)}>
- {label}
- </a>
- </li>
- );
- const {orgId} = this.props.params;
- return (
- <Fragment>
- <Header>
- <Heading>{t('Sign in to continue')}</Heading>
- <AuthNavTabs>{tabs.map(renderTab)}</AuthNavTabs>
- </Header>
- {loading && <LoadingIndicator />}
- {error && (
- <StyledLoadingError
- message={t('Unable to load authentication configuration')}
- onRetry={this.fetchData}
- />
- )}
- {!loading && authConfig !== null && !error && (
- <FormWrapper hasAuthProviders={this.hasAuthProviders}>
- {orgId !== undefined && (
- <Alert
- type="warning"
- trailingItems={
- <Button to="/" size="xs">
- Reload
- </Button>
- }
- >
- {tct(
- "Experimental SPA mode does not currently support SSO style login. To develop against the [org] you'll need to copy your production session cookie.",
- {org: this.props.params.orgId}
- )}
- </Alert>
- )}
- <FormComponent {...{authConfig}} />
- </FormWrapper>
- )}
- </Fragment>
- );
- }
- }
- const StyledLoadingError = styled(LoadingError)`
- margin: ${space(2)};
- `;
- const Header = styled('div')`
- border-bottom: 1px solid ${p => p.theme.border};
- padding: 20px 40px 0;
- `;
- const Heading = styled('h3')`
- font-size: 24px;
- margin: 0 0 20px 0;
- `;
- const AuthNavTabs = styled(NavTabs)`
- margin: 0;
- `;
- const FormWrapper = styled('div')<{hasAuthProviders: boolean}>`
- padding: 35px;
- width: ${p => (p.hasAuthProviders ? '600px' : '490px')};
- `;
- export default withApi(Login);
|