import {Component} from 'react'; import styled from '@emotion/styled'; import {logout} from 'sentry/actionCreators/account'; import {Client} from 'sentry/api'; import Alert from 'sentry/components/alert'; import Form from 'sentry/components/forms/form'; import Hook from 'sentry/components/hook'; import ThemeAndStyleProvider from 'sentry/components/themeAndStyleProvider'; import U2fContainer from 'sentry/components/u2f/u2fContainer'; import {ErrorCodes} from 'sentry/constants/superuserAccessErrors'; import {t} from 'sentry/locale'; import ConfigStore from 'sentry/stores/configStore'; import space from 'sentry/styles/space'; import {Authenticator} from 'sentry/types'; import withApi from 'sentry/utils/withApi'; import Button from './button'; type OnTapProps = NonNullable['onTap']>; type Props = { api: Client; }; type State = { authenticators: Array; error: boolean; errorType: string; showAccessForms: boolean; superuserAccessCategory: string; superuserReason: string; }; class SuperuserAccessForm extends Component { state: State = { authenticators: [], error: false, errorType: '', showAccessForms: true, superuserAccessCategory: '', superuserReason: '', }; componentDidMount() { this.getAuthenticators(); } handleSubmitCOPS = () => { this.setState({ superuserAccessCategory: 'cops_csm', superuserReason: 'COPS and CSM use', }); }; handleSubmit = async data => { const {api} = this.props; const {superuserAccessCategory, superuserReason, authenticators} = this.state; const disableU2FForSUForm = ConfigStore.get('disableU2FForSUForm'); const suAccessCategory = superuserAccessCategory || data.superuserAccessCategory; const suReason = superuserReason || data.superuserReason; if (!authenticators.length && !disableU2FForSUForm) { this.handleError(ErrorCodes.noAuthenticator); return; } if (this.state.showAccessForms && !disableU2FForSUForm) { this.setState({ showAccessForms: false, superuserAccessCategory: suAccessCategory, superuserReason: suReason, }); } else { await api.requestPromise('/auth/', {method: 'PUT', data}); this.handleSuccess(); } }; handleU2fTap = async (data: Parameters[0]) => { const {api} = this.props; try { data.isSuperuserModal = true; data.superuserAccessCategory = this.state.superuserAccessCategory; data.superuserReason = this.state.superuserReason; await api.requestPromise('/auth/', {method: 'PUT', data}); this.handleSuccess(); } catch (err) { this.setState({showAccessForms: true}); // u2fInterface relies on this throw err; } }; handleSuccess = () => { window.location.reload(); }; handleError = err => { let errorType = ''; if (err.status === 403) { if (err.responseJSON.detail.code === 'no_u2f') { errorType = ErrorCodes.noAuthenticator; } else { errorType = ErrorCodes.invalidPassword; } } else if (err.status === 401) { errorType = ErrorCodes.invalidSSOSession; } else if (err.status === 400) { errorType = ErrorCodes.invalidAccessCategory; } else if (err === ErrorCodes.noAuthenticator) { errorType = ErrorCodes.noAuthenticator; } else { errorType = ErrorCodes.unknownError; } this.setState({ error: true, errorType, showAccessForms: true, }); }; handleLogout = async () => { const {api} = this.props; try { await logout(api); } catch { // ignore errors } window.location.assign('/auth/login/'); }; async getAuthenticators() { const {api} = this.props; try { const authenticators = await api.requestPromise('/authenticators/'); this.setState({authenticators: authenticators ?? []}); } catch { // ignore errors } } render() { const {authenticators, error, errorType, showAccessForms} = this.state; if (errorType === ErrorCodes.invalidSSOSession) { this.handleLogout(); return null; } return (
} resetOnError > {error && ( {errorType} )} {showAccessForms && } {!showAccessForms && ( )}
); } } const StyledAlert = styled(Alert)` margin-bottom: 0; `; const BackWrapper = styled('div')` width: 100%; margin-left: ${space(4)}; `; export default withApi(SuperuserAccessForm);