From f9e4f7406d7b93820480335b3afa446f5e18db2f Mon Sep 17 00:00:00 2001 From: Evan Purkhiser Date: Wed, 5 Mar 2025 13:59:00 -0800 Subject: [PATCH] feat(alerts): Restrict uptime/crons overview buttons for alerts:write Follow up to https://github.com/getsentry/sentry/pull/86318 --- .../views/insights/uptime/views/overview.tsx | 16 ++++++++++++- .../overviewTimeline/overviewRow.tsx | 12 ++++++++++ static/app/views/monitors/overview.tsx | 23 +++++++++++++++++-- 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/static/app/views/insights/uptime/views/overview.tsx b/static/app/views/insights/uptime/views/overview.tsx index 87bfea1f22a443..13a9dc3546dff7 100644 --- a/static/app/views/insights/uptime/views/overview.tsx +++ b/static/app/views/insights/uptime/views/overview.tsx @@ -2,10 +2,12 @@ import {Fragment} from 'react'; import styled from '@emotion/styled'; import * as qs from 'query-string'; +import {hasEveryAccess} from 'sentry/components/acl/access'; import {LinkButton} from 'sentry/components/button'; import ButtonBar from 'sentry/components/buttonBar'; import EmptyMessage from 'sentry/components/emptyMessage'; import * as Layout from 'sentry/components/layouts/thirds'; +import Link from 'sentry/components/links/link'; import LoadingIndicator from 'sentry/components/loadingIndicator'; import {DatePageFilter} from 'sentry/components/organizations/datePageFilter'; import {EnvironmentPageFilter} from 'sentry/components/organizations/environmentPageFilter'; @@ -17,7 +19,7 @@ import Pagination from 'sentry/components/pagination'; import Panel from 'sentry/components/panels/panel'; import SearchBar from 'sentry/components/searchBar'; import {IconAdd} from 'sentry/icons'; -import {t} from 'sentry/locale'; +import {t, tct} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import {useApiQuery} from 'sentry/utils/queryClient'; import {decodeList, decodeScalar} from 'sentry/utils/queryString'; @@ -26,6 +28,7 @@ import useRouteAnalyticsParams from 'sentry/utils/routeAnalytics/useRouteAnalyti import {useLocation} from 'sentry/utils/useLocation'; import {useNavigate} from 'sentry/utils/useNavigate'; import useOrganization from 'sentry/utils/useOrganization'; +import useProjects from 'sentry/utils/useProjects'; import {makeAlertsPathname} from 'sentry/views/alerts/pathnames'; import type {UptimeRule} from 'sentry/views/alerts/rules/uptime/types'; import {ModulePageProviders} from 'sentry/views/insights/common/components/modulePageProviders'; @@ -41,6 +44,7 @@ export default function UptimeOverview() { const navigate = useNavigate(); const location = useLocation(); const project = decodeList(location.query?.project); + const {projects} = useProjects(); function makeQueryKey() { const {query, environment, owner, cursor, sort, asc} = location.query; @@ -81,6 +85,14 @@ export default function UptimeOverview() { }); }; + const canCreateAlert = + hasEveryAccess(['alerts:write'], {organization}) || + projects.some(p => hasEveryAccess(['alerts:write'], {project: p})); + const permissionTooltipText = tct( + 'Ask your organization owner or manager to [settingsLink:enable alerts access] for you.', + {settingsLink: } + ); + return ( } + disabled={!canCreateAlert} + title={!canCreateAlert ? permissionTooltipText : undefined} > {t('Add Uptime Monitor')} diff --git a/static/app/views/monitors/components/overviewTimeline/overviewRow.tsx b/static/app/views/monitors/components/overviewTimeline/overviewRow.tsx index 1a32d3fadcd3b4..a1de6c8da03143 100644 --- a/static/app/views/monitors/components/overviewTimeline/overviewRow.tsx +++ b/static/app/views/monitors/components/overviewTimeline/overviewRow.tsx @@ -3,6 +3,7 @@ import {css} from '@emotion/react'; import styled from '@emotion/styled'; import pick from 'lodash/pick'; +import {hasEveryAccess} from 'sentry/components/acl/access'; import {Button} from 'sentry/components/button'; import {CheckInPlaceholder} from 'sentry/components/checkInTimeline/checkInPlaceholder'; import {CheckInTimeline} from 'sentry/components/checkInTimeline/checkInTimeline'; @@ -92,6 +93,15 @@ export function OverviewRow({ query, }; + const canDisable = hasEveryAccess(['alerts:write'], { + organization, + project: monitor.project, + }); + const permissionTooltipText = tct( + 'Ask your organization owner or manager to [settingsLink:enable alerts access] for you.', + {settingsLink: } + ); + const monitorDetails = singleMonitorView ? null : ( @@ -126,6 +136,8 @@ export function OverviewRow({ monitor={monitor} size="xs" onToggleStatus={status => onToggleStatus(monitor, status)} + disabled={!canDisable} + title={!canDisable ? permissionTooltipText : undefined} /> )} diff --git a/static/app/views/monitors/overview.tsx b/static/app/views/monitors/overview.tsx index 982d48bdaef67e..2425da9f69f291 100644 --- a/static/app/views/monitors/overview.tsx +++ b/static/app/views/monitors/overview.tsx @@ -4,11 +4,13 @@ import * as qs from 'query-string'; import {openBulkEditMonitorsModal} from 'sentry/actionCreators/modal'; import {deleteProjectProcessingErrorByType} from 'sentry/actionCreators/monitors'; +import {hasEveryAccess} from 'sentry/components/acl/access'; import {Button} from 'sentry/components/button'; import ButtonBar from 'sentry/components/buttonBar'; import FeedbackWidgetButton from 'sentry/components/feedback/widget/feedbackWidgetButton'; import HookOrDefault from 'sentry/components/hookOrDefault'; import * as Layout from 'sentry/components/layouts/thirds'; +import Link from 'sentry/components/links/link'; import LoadingIndicator from 'sentry/components/loadingIndicator'; import {DatePageFilter} from 'sentry/components/organizations/datePageFilter'; import {EnvironmentPageFilter} from 'sentry/components/organizations/environmentPageFilter'; @@ -20,7 +22,7 @@ import Pagination from 'sentry/components/pagination'; import SearchBar from 'sentry/components/searchBar'; import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle'; import {IconAdd, IconList} from 'sentry/icons'; -import {t} from 'sentry/locale'; +import {t, tct} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import {useApiQuery} from 'sentry/utils/queryClient'; import {decodeList, decodeScalar} from 'sentry/utils/queryString'; @@ -30,6 +32,7 @@ import useApi from 'sentry/utils/useApi'; import {useLocation} from 'sentry/utils/useLocation'; import {useNavigate} from 'sentry/utils/useNavigate'; import useOrganization from 'sentry/utils/useOrganization'; +import useProjects from 'sentry/utils/useProjects'; import { CronsLandingPanel, @@ -56,6 +59,7 @@ export default function Monitors() { const platform = decodeScalar(location.query?.platform) ?? null; const guide = decodeScalar(location.query?.guide); const project = decodeList(location.query?.project); + const {projects} = useProjects(); const queryKey = makeMonitorListQueryKey(organization, location.query); @@ -95,6 +99,14 @@ export default function Monitors() { const showAddMonitor = !isValidPlatform(platform) || !isValidGuide(guide); + const canCreateAlert = + hasEveryAccess(['alerts:write'], {organization}) || + projects.some(p => hasEveryAccess(['alerts:write'], {project: p})); + const permissionTooltipText = tct( + 'Ask your organization owner or manager to [settingsLink:enable alerts access] for you.', + {settingsLink: } + ); + return ( @@ -124,11 +136,18 @@ export default function Monitors() { } analyticsEventKey="crons.bulk_edit_modal_button_clicked" analyticsEventName="Crons: Bulk Edit Modal Button Clicked" + disabled={!canCreateAlert} + title={!canCreateAlert ? permissionTooltipText : undefined} > {t('Manage Monitors')} {showAddMonitor && ( - }> + } + disabled={!canCreateAlert} + title={!canCreateAlert ? permissionTooltipText : undefined} + > {t('Add Monitor')} )}