projectSecurityAndPrivacyGroups.tsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. import {hasEveryAccess} from 'sentry/components/acl/access';
  2. import type {JsonFormObject} from 'sentry/components/forms/types';
  3. import Link from 'sentry/components/links/link';
  4. import {t, tct} from 'sentry/locale';
  5. import type {Organization} from 'sentry/types/organization';
  6. import type {Project} from 'sentry/types/project';
  7. import {convertMultilineFieldValue, extractMultilineFields} from 'sentry/utils';
  8. import {
  9. formatStoreCrashReports,
  10. getStoreCrashReportsValues,
  11. SettingScope,
  12. } from 'sentry/utils/crashReports';
  13. // Export route to make these forms searchable by label/help
  14. export const route = '/settings/:orgId/projects/:projectId/security-and-privacy/';
  15. // Check if a field has been set AND IS TRUTHY at the organization level.
  16. const hasOrgOverride = ({
  17. organization,
  18. name,
  19. }: {
  20. name: string;
  21. organization: Organization;
  22. }) => organization[name];
  23. function hasProjectWriteAndOrgOverride({
  24. organization,
  25. project,
  26. name,
  27. }: {
  28. name: string;
  29. organization: Organization;
  30. project: Project;
  31. }) {
  32. if (hasOrgOverride({organization, name})) {
  33. return true;
  34. }
  35. return !hasEveryAccess(['project:write'], {organization, project});
  36. }
  37. function projectWriteAndOrgOverrideDisabledReason({
  38. organization,
  39. name,
  40. }: {
  41. name: string;
  42. organization: Organization;
  43. }) {
  44. if (hasOrgOverride({organization, name})) {
  45. return t(
  46. "This option is enforced by your organization's settings and cannot be customized per-project."
  47. );
  48. }
  49. return null;
  50. }
  51. const formGroups: JsonFormObject[] = [
  52. {
  53. title: t('Security & Privacy'),
  54. fields: [
  55. {
  56. name: 'storeCrashReports',
  57. type: 'select',
  58. label: t('Store Native Crash Reports'),
  59. help: ({organization}) =>
  60. tct(
  61. 'Store native crash reports such as Minidumps for improved processing and download in issue details. Overrides [organizationSettingsLink: organization settings].',
  62. {
  63. organizationSettingsLink: (
  64. <Link to={`/settings/${organization.slug}/security-and-privacy/`} />
  65. ),
  66. }
  67. ),
  68. visible: ({features}) => features.has('event-attachments'),
  69. placeholder: ({organization, value}) => {
  70. // empty value means that this project should inherit organization settings
  71. if (value === '') {
  72. return tct('Inherit organization settings ([organizationValue])', {
  73. organizationValue: formatStoreCrashReports(organization.storeCrashReports),
  74. });
  75. }
  76. // HACK: some organization can have limit of stored crash reports a number that's not in the options (legacy reasons),
  77. // we therefore display it in a placeholder
  78. return formatStoreCrashReports(value);
  79. },
  80. choices: ({organization}) =>
  81. getStoreCrashReportsValues(SettingScope.PROJECT).map(value => [
  82. value,
  83. formatStoreCrashReports(value, organization.storeCrashReports),
  84. ]),
  85. },
  86. ],
  87. },
  88. {
  89. title: t('Data Scrubbing'),
  90. fields: [
  91. {
  92. name: 'dataScrubber',
  93. type: 'boolean',
  94. label: t('Data Scrubber'),
  95. disabled: hasProjectWriteAndOrgOverride,
  96. disabledReason: projectWriteAndOrgOverrideDisabledReason,
  97. help: t('Enable server-side data scrubbing'),
  98. 'aria-label': t('Enable server-side data scrubbing'),
  99. // `props` are the props given to FormField
  100. setValue: (val, props) => props.organization?.[props.name] || val,
  101. confirm: {
  102. false: t('Are you sure you want to disable server-side data scrubbing?'),
  103. },
  104. },
  105. {
  106. name: 'dataScrubberDefaults',
  107. type: 'boolean',
  108. disabled: hasProjectWriteAndOrgOverride,
  109. disabledReason: projectWriteAndOrgOverrideDisabledReason,
  110. label: t('Use Default Scrubbers'),
  111. help: t(
  112. 'Apply default scrubbers to prevent things like passwords and credit cards from being stored'
  113. ),
  114. 'aria-label': t(
  115. 'Enable to apply default scrubbers to prevent things like passwords and credit cards from being stored'
  116. ),
  117. // `props` are the props given to FormField
  118. setValue: (val, props) => props.organization?.[props.name] || val,
  119. confirm: {
  120. false: t('Are you sure you want to disable using default scrubbers?'),
  121. },
  122. },
  123. {
  124. name: 'scrubIPAddresses',
  125. type: 'boolean',
  126. disabled: hasProjectWriteAndOrgOverride,
  127. disabledReason: projectWriteAndOrgOverrideDisabledReason,
  128. // `props` are the props given to FormField
  129. setValue: (val, props) => props.organization?.[props.name] || val,
  130. label: t('Prevent Storing of IP Addresses'),
  131. help: t('Preventing IP addresses from being stored for new events'),
  132. 'aria-label': t(
  133. 'Enable to prevent IP addresses from being stored for new events'
  134. ),
  135. confirm: {
  136. false: t('Are you sure you want to disable scrubbing IP addresses?'),
  137. },
  138. },
  139. {
  140. name: 'sensitiveFields',
  141. type: 'string',
  142. multiline: true,
  143. autosize: true,
  144. maxRows: 10,
  145. rows: 1,
  146. placeholder: t('email'),
  147. label: t('Additional Sensitive Fields'),
  148. help: t(
  149. 'Additional field names to match against when scrubbing data. Separate multiple entries with a newline'
  150. ),
  151. 'aria-label': t(
  152. 'Enter additional field names to match against when scrubbing data. Separate multiple entries with a newline'
  153. ),
  154. getValue: val => extractMultilineFields(val),
  155. setValue: val => convertMultilineFieldValue(val),
  156. },
  157. {
  158. name: 'safeFields',
  159. type: 'string',
  160. multiline: true,
  161. autosize: true,
  162. maxRows: 10,
  163. rows: 1,
  164. placeholder: t('business-email'),
  165. label: t('Safe Fields'),
  166. help: t(
  167. 'Field names which data scrubbers should ignore. Separate multiple entries with a newline'
  168. ),
  169. 'aria-label': t(
  170. 'Enter field names which data scrubbers should ignore. Separate multiple entries with a newline'
  171. ),
  172. getValue: val => extractMultilineFields(val),
  173. setValue: val => convertMultilineFieldValue(val),
  174. },
  175. ],
  176. },
  177. ];
  178. export default formGroups;