diff --git a/static/app/views/settings/components/dataSecrecy/index.spec.tsx b/static/app/views/settings/components/dataSecrecy/index.spec.tsx
index b44db0d2095751..72e847e5f48ff1 100644
--- a/static/app/views/settings/components/dataSecrecy/index.spec.tsx
+++ b/static/app/views/settings/components/dataSecrecy/index.spec.tsx
@@ -103,4 +103,38 @@ describe('DataSecrecy', function () {
expect(accessMessage).toBeInTheDocument();
});
});
+
+ it('can update permanent access', async function () {
+ MockApiClient.addMockResponse({
+ url: `/organizations/${organization.slug}/data-secrecy/`,
+ body: {
+ accessStart: '2023-08-29T01:05:00+00:00',
+ accessEnd: '2024-08-29T01:05:00+00:00',
+ },
+ });
+
+ const mockUpdate = MockApiClient.addMockResponse({
+ url: `/organizations/${organization.slug}/`,
+ method: 'PUT',
+ });
+
+ organization.allowSuperuserAccess = false;
+ render(, {organization});
+
+ // Toggle permanent access on
+ const allowAccessToggle = screen.getByRole('checkbox', {
+ name: /Sentry employees will not have access to your data unless granted permission/,
+ });
+ allowAccessToggle.click();
+
+ await waitFor(() => {
+ expect(mockUpdate).toHaveBeenCalledWith(
+ `/organizations/${organization.slug}/`,
+ expect.objectContaining({
+ method: 'PUT',
+ data: {allowSuperuserAccess: true},
+ })
+ );
+ });
+ });
});
diff --git a/static/app/views/settings/components/dataSecrecy/index.tsx b/static/app/views/settings/components/dataSecrecy/index.tsx
index db3d76a8803ea8..3165dba78e8e7d 100644
--- a/static/app/views/settings/components/dataSecrecy/index.tsx
+++ b/static/app/views/settings/components/dataSecrecy/index.tsx
@@ -26,8 +26,13 @@ export default function DataSecrecy() {
const api = useApi();
const organization = useOrganization();
+ // state for the allowSuperuserAccess bit field
const [allowAccess, setAllowAccess] = useState(organization.allowSuperuserAccess);
- const [allowDate, setAllowDate] = useState();
+
+ // state of the data secrecy waiver
+ const [waiver, setWaiver] = useState();
+
+ // state for the allowDateFormData field
const [allowDateFormData, setAllowDateFormData] = useState('');
const {data, refetch} = useApiQuery(
@@ -38,12 +43,9 @@ export default function DataSecrecy() {
}
);
- const hasValidTempAccess =
- allowDate?.accessEnd && moment().toISOString() < allowDate.accessEnd;
-
useEffect(() => {
if (data?.accessEnd) {
- setAllowDate(data);
+ setWaiver(data);
// slice it to yyyy-MM-ddThh:mm format (be aware of the timezone)
const localDate = moment(data.accessEnd).local();
setAllowDateFormData(localDate.format('YYYY-MM-DDTHH:mm'));
@@ -57,10 +59,31 @@ export default function DataSecrecy() {
data: {allowSuperuserAccess: value},
});
setAllowAccess(value);
- addSuccessMessage(t('Successfully updated access.'));
+
+ // if the user has allowed access, we need to remove the temporary access window
+ // only if there is an existing waiver
+ if (value && waiver) {
+ await api.requestPromise(`/organizations/${organization.slug}/data-secrecy/`, {
+ method: 'DELETE',
+ });
+ setAllowDateFormData('');
+ setWaiver(undefined);
+ }
+ addSuccessMessage(
+ value
+ ? waiver
+ ? t(
+ 'Successfully removed temporary access window and allowed support access.'
+ )
+ : t('Successfully allowed support access.')
+ : t('Successfully removed support access.')
+ );
} catch (error) {
addErrorMessage(t('Unable to save changes.'));
}
+
+ // refetch to get the latest waiver data
+ refetch();
};
const updateTempAccessDate = async () => {
@@ -69,7 +92,7 @@ export default function DataSecrecy() {
await api.requestPromise(`/organizations/${organization.slug}/data-secrecy/`, {
method: 'DELETE',
});
- setAllowDate({accessStart: '', accessEnd: ''});
+ setWaiver({accessStart: '', accessEnd: ''});
addSuccessMessage(t('Successfully removed temporary access window.'));
} catch (error) {
addErrorMessage(t('Unable to remove temporary access window.'));
@@ -86,14 +109,11 @@ export default function DataSecrecy() {
};
try {
- await await api.requestPromise(
- `/organizations/${organization.slug}/data-secrecy/`,
- {
- method: 'PUT',
- data: nextData,
- }
- );
- setAllowDate(nextData);
+ await api.requestPromise(`/organizations/${organization.slug}/data-secrecy/`, {
+ method: 'PUT',
+ data: nextData,
+ });
+ setWaiver(nextData);
addSuccessMessage(t('Successfully updated temporary access window.'));
} catch (error) {
addErrorMessage(t('Unable to save changes.'));
@@ -123,10 +143,20 @@ export default function DataSecrecy() {
help: t(
'Open a temporary time window for Sentry employees to access your organization'
),
- disabled: allowAccess && !organization.access.includes('org:write'),
- value: allowAccess ? '' : allowDateFormData,
+ // disable the field if the user has allowed access or if the user does not have org:write access
+ disabled: allowAccess || !organization.access.includes('org:write'),
+ disabledReason: allowAccess
+ ? t('Disable permanent access first to set temporary access')
+ : !organization.access.includes('org:write')
+ ? t('You do not have permission to modify access settings')
+ : undefined,
+ value: allowDateFormData,
onBlur: updateTempAccessDate,
onChange: v => {
+ // Don't allow the user to set the date if they have allowed access
+ if (allowAccess) {
+ return;
+ }
// the picker doesn't like having a datetime string with seconds+ and a timezone,
// so we remove it -- we will add it back when we save the date
const formattedDate = v ? moment(v).format('YYYY-MM-DDTHH:mm') : '';
@@ -140,9 +170,9 @@ export default function DataSecrecy() {
{!allowAccess && (
- {hasValidTempAccess
+ {waiver?.accessEnd && moment().isBefore(moment(waiver.accessEnd))
? tct(`Sentry employees has access to your organization until [date]`, {
- date: formatDateTime(allowDate?.accessEnd as string),
+ date: formatDateTime(waiver?.accessEnd as string),
})
: t('Sentry employees do not have access to your organization')}