accountSecurityWrapper.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. import {cloneElement} from 'react';
  2. import type {RouteComponentProps} from 'react-router';
  3. import {addErrorMessage} from 'sentry/actionCreators/indicator';
  4. import {fetchOrganizations} from 'sentry/actionCreators/organizations';
  5. import DeprecatedAsyncComponent from 'sentry/components/deprecatedAsyncComponent';
  6. import {t} from 'sentry/locale';
  7. import type {Authenticator} from 'sentry/types/auth';
  8. import type {OrganizationSummary} from 'sentry/types/organization';
  9. import type {UserEmail} from 'sentry/types/user';
  10. import {defined} from 'sentry/utils';
  11. const ENDPOINT = '/users/me/authenticators/';
  12. type Props = {
  13. children: React.ReactElement;
  14. } & RouteComponentProps<{authId: string}, {}> &
  15. DeprecatedAsyncComponent['props'];
  16. type State = {
  17. emails: UserEmail[];
  18. loadingOrganizations: boolean;
  19. authenticators?: Authenticator[] | null;
  20. organizations?: OrganizationSummary[];
  21. } & DeprecatedAsyncComponent['state'];
  22. class AccountSecurityWrapper extends DeprecatedAsyncComponent<Props, State> {
  23. async fetchOrganizations() {
  24. try {
  25. this.setState({loadingOrganizations: true});
  26. const organizations = await fetchOrganizations(this.api);
  27. this.setState({organizations, loadingOrganizations: false});
  28. } catch (e) {
  29. this.setState({error: true, loadingOrganizations: false});
  30. }
  31. }
  32. get shouldRenderLoading() {
  33. return super.shouldRenderLoading || this.state.loadingOrganizations === true;
  34. }
  35. getEndpoints(): ReturnType<DeprecatedAsyncComponent['getEndpoints']> {
  36. return [
  37. ['authenticators', ENDPOINT],
  38. ['emails', '/users/me/emails/'],
  39. ];
  40. }
  41. componentDidMount() {
  42. super.componentDidMount();
  43. this.fetchOrganizations();
  44. }
  45. reloadData() {
  46. this.fetchOrganizations();
  47. super.reloadData();
  48. }
  49. handleDisable = async (auth: Authenticator) => {
  50. if (!auth || !auth.authId) {
  51. return;
  52. }
  53. this.setState({loading: true});
  54. try {
  55. await this.api.requestPromise(`${ENDPOINT}${auth.authId}/`, {method: 'DELETE'});
  56. this.reloadData();
  57. } catch (_err) {
  58. this.setState({loading: false});
  59. addErrorMessage(t('Error disabling %s', auth.name));
  60. }
  61. };
  62. handleRegenerateBackupCodes = async () => {
  63. this.setState({loading: true});
  64. try {
  65. await this.api.requestPromise(`${ENDPOINT}${this.props.params.authId}/`, {
  66. method: 'PUT',
  67. });
  68. this.reloadData();
  69. } catch (_err) {
  70. this.setState({loading: false});
  71. addErrorMessage(t('Error regenerating backup codes'));
  72. }
  73. };
  74. handleRefresh = () => {
  75. this.reloadData();
  76. };
  77. renderBody() {
  78. const {children} = this.props;
  79. const {authenticators, organizations, emails} = this.state;
  80. const enrolled =
  81. authenticators?.filter(auth => auth.isEnrolled && !auth.isBackupInterface) || [];
  82. const countEnrolled = enrolled.length;
  83. const orgsRequire2fa = organizations?.filter(org => org.require2FA) || [];
  84. const deleteDisabled = orgsRequire2fa.length > 0 && countEnrolled === 1;
  85. const hasVerifiedEmail = !!emails?.find(({isVerified}) => isVerified);
  86. // This happens when you switch between children views and the next child
  87. // view is lazy loaded, it can potentially be `null` while the code split
  88. // package is being fetched
  89. if (!defined(children)) {
  90. return null;
  91. }
  92. return cloneElement(this.props.children, {
  93. onDisable: this.handleDisable,
  94. onRegenerateBackupCodes: this.handleRegenerateBackupCodes,
  95. authenticators,
  96. deleteDisabled,
  97. orgsRequire2fa,
  98. countEnrolled,
  99. hasVerifiedEmail,
  100. handleRefresh: this.handleRefresh,
  101. });
  102. }
  103. }
  104. export default AccountSecurityWrapper;