superuserWarning.tsx 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import {Fragment, useEffect} from 'react';
  2. import {css} from '@emotion/react';
  3. import styled from '@emotion/styled';
  4. import type {Client} from 'sentry/api';
  5. import {Button} from 'sentry/components/button';
  6. import {Badge} from 'sentry/components/core/badge';
  7. import {prefersStackedNav} from 'sentry/components/nav/prefersStackedNav';
  8. import {Tooltip} from 'sentry/components/tooltip';
  9. import AlertStore from 'sentry/stores/alertStore';
  10. import {space} from 'sentry/styles/space';
  11. import type {Organization} from 'sentry/types/organization';
  12. import useApi from 'sentry/utils/useApi';
  13. const POLICY_URL =
  14. 'https://www.notion.so/sentry/Sentry-Rules-for-Handling-Customer-Data-9612532c37e14eeb943a6a584abbac99';
  15. const SUPERUSER_MESSAGE = 'You are in superuser mode.';
  16. const WARNING_MESSAGE = (
  17. <Fragment>
  18. Please be familiar with the{' '}
  19. <a href={POLICY_URL} target="_none">
  20. rules for handling customer data
  21. </a>
  22. . Misuse of superuser will result in an unpleasant coffee chat with Legal, Security,
  23. and HR.
  24. </Fragment>
  25. );
  26. export function shouldExcludeOrg(organization?: Organization | null) {
  27. return organization?.slug === 'demo';
  28. }
  29. function handleExitSuperuser(api: Client) {
  30. api
  31. .requestPromise('/staff-auth/', {
  32. method: 'DELETE',
  33. })
  34. .then(() => window.location.reload());
  35. }
  36. function ExitSuperuserButton() {
  37. const api = useApi({persistInFlight: true});
  38. return (
  39. <Button
  40. style={{
  41. top: space(0.5),
  42. bottom: space(0.75),
  43. }}
  44. size="xs"
  45. priority="primary"
  46. onClick={() => {
  47. handleExitSuperuser(api);
  48. }}
  49. >
  50. Exit Superuser Mode
  51. </Button>
  52. );
  53. }
  54. type Props = {
  55. organization?: Organization;
  56. };
  57. function SuperuserWarning({organization}: Props) {
  58. const isExcludedOrg = shouldExcludeOrg(organization);
  59. useEffect(() => {
  60. if (!isExcludedOrg) {
  61. AlertStore.addAlert({
  62. id: 'superuser-warning',
  63. message: (
  64. <Fragment>
  65. {SUPERUSER_MESSAGE} {WARNING_MESSAGE}
  66. </Fragment>
  67. ),
  68. type: 'error',
  69. opaque: true,
  70. neverExpire: true,
  71. noDuplicates: true,
  72. });
  73. }
  74. }, [isExcludedOrg]);
  75. if (isExcludedOrg) {
  76. return null;
  77. }
  78. return (
  79. <SuperuserBadge type="warning" stackedNav={prefersStackedNav()}>
  80. <Tooltip
  81. isHoverable
  82. title={
  83. <Fragment>
  84. <ExitSuperuserButton />
  85. <br />
  86. <br />
  87. {WARNING_MESSAGE}
  88. </Fragment>
  89. }
  90. >
  91. Superuser
  92. </Tooltip>
  93. </SuperuserBadge>
  94. );
  95. }
  96. export default SuperuserWarning;
  97. const SuperuserBadge = styled(Badge)<{stackedNav: boolean}>`
  98. position: absolute;
  99. top: -5px;
  100. right: 5px;
  101. ${p =>
  102. p.stackedNav &&
  103. css`
  104. top: -12px;
  105. left: 2px;
  106. right: 2px;
  107. font-size: 10px;
  108. margin: 0;
  109. `}
  110. /* Hiding on smaller screens because it looks misplaced */
  111. @media (max-width: ${p => p.theme.breakpoints.small}) {
  112. display: none;
  113. }
  114. `;