Check for permissions on Slack escalate command (#3891)
Related to https://github.com/grafana/oncall/issues/3109 Fixes issue from https://github.com/grafana/oncall/pull/3881 (problem was that there is no organization set in the Slack request, making it impossible to check for user permissions; check permission once an organization is set in the form instead).
This commit is contained in:
parent
6ed1cf8385
commit
adaab1c6ad
3 changed files with 77 additions and 2 deletions
|
|
@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Fix edit default team by admin @mderynck ([#3885](https://github.com/grafana/oncall/pull/3885))
|
||||
- Unblock slack install by skipping check chatops gateway link in OSS deployment @mderynck ([#3893](https://github.com/grafana/oncall/pull/3893))
|
||||
|
||||
### Changed
|
||||
|
||||
- Check for permissions on Slack escalate command ([#3891](https://github.com/grafana/oncall/pull/3891))
|
||||
|
||||
## v1.3.105 (2024-02-13)
|
||||
|
||||
### Fixed
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from rest_framework.response import Response
|
|||
|
||||
from apps.alerts.models import AlertReceiveChannel
|
||||
from apps.alerts.paging import DirectPagingUserTeamValidationError, UserNotifications, direct_paging, user_is_oncall
|
||||
from apps.api.permissions import RBACPermission
|
||||
from apps.api.permissions import RBACPermission, user_is_authorized
|
||||
from apps.schedules.ical_utils import get_cached_oncall_users_for_multiple_schedules
|
||||
from apps.slack.constants import DIVIDER, PRIVATE_METADATA_MAX_LENGTH
|
||||
from apps.slack.errors import SlackAPIChannelNotFoundError
|
||||
|
|
@ -114,7 +114,6 @@ def get_current_items(
|
|||
class StartDirectPaging(scenario_step.ScenarioStep):
|
||||
"""Handle slash command invocation and show initial dialog."""
|
||||
|
||||
REQUIRED_PERMISSIONS = [RBACPermission.Permissions.ALERT_GROUPS_DIRECT_PAGING]
|
||||
command_name = [settings.SLACK_DIRECT_PAGING_SLASH_COMMAND]
|
||||
|
||||
def process_scenario(
|
||||
|
|
@ -147,6 +146,8 @@ class StartDirectPaging(scenario_step.ScenarioStep):
|
|||
class FinishDirectPaging(scenario_step.ScenarioStep):
|
||||
"""Handle page command dialog submit."""
|
||||
|
||||
REQUIRED_PERMISSIONS = [RBACPermission.Permissions.ALERT_GROUPS_DIRECT_PAGING]
|
||||
|
||||
def process_scenario(
|
||||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
|
|
@ -160,6 +161,21 @@ class FinishDirectPaging(scenario_step.ScenarioStep):
|
|||
selected_organization = _get_selected_org_from_payload(
|
||||
payload, input_id_prefix, slack_team_identity, slack_user_identity
|
||||
)
|
||||
|
||||
# get user in the context of the selected_organization
|
||||
user = slack_user_identity.get_user(selected_organization)
|
||||
if not user_is_authorized(user, self.REQUIRED_PERMISSIONS):
|
||||
unauthorized_error = _get_unauthorized_warning(error=True)
|
||||
return Response(
|
||||
{
|
||||
"response_action": "update",
|
||||
"view": render_dialog(
|
||||
slack_user_identity, slack_team_identity, payload, validation_errors=unauthorized_error
|
||||
),
|
||||
},
|
||||
status=200,
|
||||
)
|
||||
|
||||
_, selected_team = _get_selected_team_from_payload(payload, input_id_prefix)
|
||||
user = slack_user_identity.get_user(selected_organization)
|
||||
|
||||
|
|
@ -416,6 +432,11 @@ def render_dialog(
|
|||
if validation_errors:
|
||||
blocks += validation_errors
|
||||
|
||||
# get user in the context of the selected_organization
|
||||
user = slack_user_identity.get_user(selected_organization)
|
||||
if not user_is_authorized(user, FinishDirectPaging.REQUIRED_PERMISSIONS):
|
||||
blocks += _get_unauthorized_warning()
|
||||
|
||||
blocks.append(_get_message_input(payload))
|
||||
|
||||
# Add organization select if more than one organization available for user
|
||||
|
|
@ -446,6 +467,19 @@ def render_dialog(
|
|||
return _get_form_view(submit_routing_uid, blocks, json.dumps(new_private_metadata))
|
||||
|
||||
|
||||
def _get_unauthorized_warning(error=False):
|
||||
icon = ":warning:" if not error else ":no_entry:"
|
||||
text = f"{icon} You do not have permission to perform this action."
|
||||
if not error:
|
||||
text += "\nAsk an admin to upgrade your permissions."
|
||||
return [
|
||||
typing.cast(
|
||||
Block.Section,
|
||||
{"type": "section", "text": {"type": "mrkdwn", "text": text}},
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def _get_form_view(routing_uid: str, blocks: Block.AnyBlocks, private_metadata: str) -> ModalView:
|
||||
view: ModalView = {
|
||||
"type": "modal",
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import pytest
|
|||
from django.utils import timezone
|
||||
|
||||
from apps.alerts.models import AlertReceiveChannel
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
from apps.schedules.models import CustomOnCallShift, OnCallScheduleWeb
|
||||
from apps.slack.scenarios.paging import (
|
||||
DIRECT_PAGING_MESSAGE_INPUT_ID,
|
||||
|
|
@ -75,6 +76,23 @@ def test_initial_state(
|
|||
assert metadata[DataKey.USERS] == {}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("role", (LegacyAccessControlRole.VIEWER, LegacyAccessControlRole.NONE))
|
||||
@pytest.mark.django_db
|
||||
def test_initial_unauthorized(make_organization_and_user_with_slack_identities, role):
|
||||
_, _, slack_team_identity, slack_user_identity = make_organization_and_user_with_slack_identities(role=role)
|
||||
payload = {"channel_id": "123", "trigger_id": "111"}
|
||||
|
||||
step = StartDirectPaging(slack_team_identity)
|
||||
with patch.object(step._slack_client, "views_open") as mock_slack_api_call:
|
||||
step.process_scenario(slack_user_identity, slack_team_identity, payload)
|
||||
|
||||
view = mock_slack_api_call.call_args.kwargs["view"]
|
||||
assert (
|
||||
view["blocks"][0]["text"]["text"]
|
||||
== ":warning: You do not have permission to perform this action.\nAsk an admin to upgrade your permissions."
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_add_user_no_warning(make_organization_and_user_with_slack_identities, make_schedule, make_on_call_shift):
|
||||
organization, user, slack_team_identity, slack_user_identity = make_organization_and_user_with_slack_identities()
|
||||
|
|
@ -231,6 +249,25 @@ def test_trigger_paging_no_team_or_user_selected(make_organization_and_user_with
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("role", (LegacyAccessControlRole.VIEWER, LegacyAccessControlRole.NONE))
|
||||
@pytest.mark.django_db
|
||||
def test_trigger_paging_unauthorized(make_organization_and_user_with_slack_identities, role):
|
||||
organization, user, slack_team_identity, slack_user_identity = make_organization_and_user_with_slack_identities(
|
||||
role=role
|
||||
)
|
||||
payload = make_slack_payload(organization=organization)
|
||||
|
||||
step = FinishDirectPaging(slack_team_identity)
|
||||
with patch.object(step._slack_client, "api_call"):
|
||||
response = step.process_scenario(slack_user_identity, slack_team_identity, payload)
|
||||
response = response.data
|
||||
|
||||
assert response["response_action"] == "update"
|
||||
assert (
|
||||
response["view"]["blocks"][0]["text"]["text"] == ":no_entry: You do not have permission to perform this action."
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_trigger_paging_additional_responders(make_organization_and_user_with_slack_identities, make_team):
|
||||
organization, user, slack_team_identity, slack_user_identity = make_organization_and_user_with_slack_identities()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue