Add RBAC Support (#777)
* Modify plugin.json to support RBAC role registration * defines 26 new custom roles in plugin.json. The main roles are: - Admin: read/write access to everything in OnCall - Reader: read access to everything in OnCall - OnCaller : read access to everything in OnCall + edit access to Alert Groups and Schedules - <object-type> Editor: read/write access to everything related to <object-type> - <object-type> Reader: read access for <object-type> - User Settings Admin: read/write access to all user's settings, not just own settings. This is in comparison to User Settings Editor which can only read/write own settings * update changelog and documentation (#686) * implement RBAC for OnCall backend This commit refactors backend authorization. It trys to use RBAC authorization if the org's grafana instance supports it, otherwise it falls back to basic role authorization. * update RBAC backend tests * add tests for RBAC changes - run backend tests as matrix where RBAC is enabled/disabled. When RBAC is enabled, the permissions granted are read from the role grants in the frontend's plugin.json file (instead of relying what we specify in RBACPermission.Permissions) - remove --reuse-db --nomigrations flags from engine/tox.ini - minor autoformatting changes to docker-compose-developer.yml * remove --ds=settings.ci-test from pytest CI command DJANGO_SETTINGS_MODULE is already specified as an env var so this is just unecessary duplication * update gitignore * update github action job name for "test" * RBAC frontend changes * refactors the use of basic roles (ex. Viewer, Editor, Admin) use RBAC permissions (when supported), or falling back to basic roles when RBAC is not supported. - updates the UserAction enum in grafana-plugin/src/state/userAction.ts. Previously this was hardcoded to a list of strings that were being returned by the OnCall API. Now the values here correspond to the permissions in plugin.json (plus a fallback role) * changes per Gabriel's comments: - get rid of group attribute in rbac roles - remove displayName role attribute - remove hidden role attribute - add back role to includes section * don't try to update user timezone if they don't have permission
This commit is contained in:
parent
132cf1da7f
commit
9e598385f4
172 changed files with 4424 additions and 2194 deletions
22
.github/workflows/ci.yml
vendored
22
.github/workflows/ci.yml
vendored
|
|
@ -27,7 +27,7 @@ jobs:
|
|||
run: |
|
||||
pre-commit run --all-files
|
||||
|
||||
test:
|
||||
test-frontend:
|
||||
runs-on: ubuntu-latest
|
||||
container: python:3.9
|
||||
steps:
|
||||
|
|
@ -56,8 +56,12 @@ jobs:
|
|||
docker run -v ${PWD}/docs/sources:/hugo/content/docs/oncall/latest -e HUGO_REFLINKSERRORLEVEL=ERROR --rm grafana/docs-base:latest /bin/bash -c 'make hugo'
|
||||
|
||||
unit-test-backend-mysql-rabbitmq:
|
||||
name: "Backend Tests: MySQL + RabbitMQ (RBAC enabled: ${{ matrix.rbac_enabled }})"
|
||||
runs-on: ubuntu-latest
|
||||
container: python:3.9
|
||||
strategy:
|
||||
matrix:
|
||||
rbac_enabled: ["True", "False"]
|
||||
env:
|
||||
DJANGO_SETTINGS_MODULE: settings.ci-test
|
||||
SLACK_CLIENT_OAUTH_ID: 1
|
||||
|
|
@ -72,7 +76,6 @@ jobs:
|
|||
env:
|
||||
MYSQL_DATABASE: oncall_local_dev
|
||||
MYSQL_ROOT_PASSWORD: local_dev_pwd
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Unit Test Backend
|
||||
|
|
@ -80,11 +83,15 @@ jobs:
|
|||
apt-get update && apt-get install -y netcat
|
||||
cd engine/
|
||||
pip install -r requirements.txt
|
||||
./wait_for_test_mysql_start.sh && pytest --ds=settings.ci-test -x
|
||||
./wait_for_test_mysql_start.sh && ONCALL_TESTING_RBAC_ENABLED=${{ matrix.rbac_enabled }} pytest -x
|
||||
|
||||
unit-test-backend-postgresql-rabbitmq:
|
||||
name: "Backend Tests: PostgreSQL + RabbitMQ (RBAC enabled: ${{ matrix.rbac_enabled }})"
|
||||
runs-on: ubuntu-latest
|
||||
container: python:3.9
|
||||
strategy:
|
||||
matrix:
|
||||
rbac_enabled: ["True", "False"]
|
||||
env:
|
||||
DATABASE_TYPE: postgresql
|
||||
DJANGO_SETTINGS_MODULE: settings.ci-test
|
||||
|
|
@ -112,11 +119,15 @@ jobs:
|
|||
run: |
|
||||
cd engine/
|
||||
pip install -r requirements.txt
|
||||
pytest --ds=settings.ci-test -x
|
||||
ONCALL_TESTING_RBAC_ENABLED=${{ matrix.rbac_enabled }} pytest -x
|
||||
|
||||
unit-test-backend-sqlite-redis:
|
||||
name: "Backend Tests: SQLite + Redis (RBAC enabled: ${{ matrix.rbac_enabled }})"
|
||||
runs-on: ubuntu-latest
|
||||
container: python:3.9
|
||||
strategy:
|
||||
matrix:
|
||||
rbac_enabled: ["True", "False"]
|
||||
env:
|
||||
DATABASE_TYPE: sqlite3
|
||||
BROKER_TYPE: redis
|
||||
|
|
@ -131,7 +142,6 @@ jobs:
|
|||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Unit Test Backend
|
||||
|
|
@ -139,7 +149,7 @@ jobs:
|
|||
apt-get update && apt-get install -y netcat
|
||||
cd engine/
|
||||
pip install -r requirements.txt
|
||||
pytest --ds=settings.ci-test -x
|
||||
ONCALL_TESTING_RBAC_ENABLED=${{ matrix.rbac_enabled }} pytest -x
|
||||
|
||||
docker-build:
|
||||
runs-on: ubuntu-latest
|
||||
|
|
|
|||
|
|
@ -5,14 +5,21 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## v1.1.6 (TBD)
|
||||
## v1.2.0 (TBD)
|
||||
|
||||
### Added
|
||||
|
||||
- RBAC permission support
|
||||
|
||||
### Fixed
|
||||
|
||||
- Got 500 error when saving Outgoing Webhook ([#890](https://github.com/grafana/oncall/issues/890))
|
||||
|
||||
### Changed
|
||||
|
||||
- When editing templates for alert group presentation or outgoing webhooks, errors and warnings are now displayed in the UI as notification popups or displayed in the preview.
|
||||
- Errors and warnings that occur when rendering templates during notification or webhooks will now render and display the error/warning as the result.
|
||||
|
||||
## v1.1.5 (2022-11-24)
|
||||
|
||||
### Added
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ x-oncall-volumes: &oncall-volumes
|
|||
# https://stackoverflow.com/a/60456034
|
||||
- ${ENTERPRISE_ENGINE:-/dev/null}:/etc/app/extensions/engine_enterprise
|
||||
- ${SQLITE_DB_FILE:-/dev/null}:/var/lib/oncall/oncall.db
|
||||
# this is mounted for testing purposes. Some of the authorization tests
|
||||
# reference this file
|
||||
- ./grafana-plugin/src/plugin.json:/etc/grafana-plugin/src/plugin.json
|
||||
|
||||
x-env-files: &oncall-env-files
|
||||
- ./dev/.env.dev
|
||||
|
|
|
|||
|
|
@ -53,13 +53,13 @@ def notify_user_task(
|
|||
organization = alert_group.channel.organization
|
||||
|
||||
if not user.is_notification_allowed:
|
||||
task_logger.info(f"notify_user_task: user {user.pk} notification is not allowed for role {user.role}")
|
||||
task_logger.info(f"notify_user_task: user {user.pk} notification is not allowed")
|
||||
UserNotificationPolicyLogRecord(
|
||||
author=user,
|
||||
type=UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_FAILED,
|
||||
reason=f"notification is not allowed for user with role {user.role}",
|
||||
reason=f"notification is not allowed for user",
|
||||
alert_group=alert_group,
|
||||
notification_error_code=UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_NOT_ALLOWED_USER_ROLE,
|
||||
notification_error_code=UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_FORBIDDEN,
|
||||
).save()
|
||||
return
|
||||
|
||||
|
|
@ -252,9 +252,9 @@ def perform_notification(log_record_pk):
|
|||
UserNotificationPolicyLogRecord(
|
||||
author=user,
|
||||
type=UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_FAILED,
|
||||
reason=f"notification is not allowed for user with role {user.role}",
|
||||
reason=f"notification is not allowed for user",
|
||||
alert_group=alert_group,
|
||||
notification_error_code=UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_NOT_ALLOWED_USER_ROLE,
|
||||
notification_error_code=UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_FORBIDDEN,
|
||||
).save()
|
||||
return
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ from apps.alerts.incident_appearance.renderers.phone_call_renderer import AlertG
|
|||
from apps.alerts.models import AlertGroup
|
||||
from apps.alerts.tasks.delete_alert_group import delete_alert_group
|
||||
from apps.slack.models import SlackMessage
|
||||
from common.constants.role import Role
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
|
@ -14,7 +13,7 @@ def test_render_for_phone_call(
|
|||
make_alert_group,
|
||||
make_alert,
|
||||
):
|
||||
organization, slack_team_identity = make_organization_with_slack_team_identity()
|
||||
organization, _ = make_organization_with_slack_team_identity()
|
||||
alert_receive_channel = make_alert_receive_channel(organization, integration_slack_channel_id="CWER1ASD")
|
||||
|
||||
alert_group = make_alert_group(alert_receive_channel)
|
||||
|
|
@ -59,7 +58,7 @@ def test_delete(
|
|||
|
||||
organization, slack_team_identity = make_organization_with_slack_team_identity()
|
||||
slack_channel = make_slack_channel(slack_team_identity, name="general", slack_id="CWER1ASD")
|
||||
user = make_user(organization=organization, role=Role.ADMIN)
|
||||
user = make_user(organization=organization)
|
||||
|
||||
alert_receive_channel = make_alert_receive_channel(organization, integration_slack_channel_id="CWER1ASD")
|
||||
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ from apps.alerts.escalation_snapshot.serializers.escalation_policy_snapshot impo
|
|||
from apps.alerts.escalation_snapshot.snapshot_classes import EscalationPolicySnapshot
|
||||
from apps.alerts.escalation_snapshot.utils import eta_for_escalation_step_notify_if_time
|
||||
from apps.alerts.models import AlertGroupLogRecord, EscalationPolicy
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
from apps.schedules.ical_utils import list_users_to_notify_from_ical
|
||||
from apps.schedules.models import CustomOnCallShift, OnCallScheduleCalendar
|
||||
from common.constants.role import Role
|
||||
|
||||
|
||||
def get_escalation_policy_snapshot_from_model(escalation_policy):
|
||||
|
|
@ -213,8 +213,8 @@ def test_escalation_step_notify_on_call_schedule_viewer_user(
|
|||
make_schedule,
|
||||
make_on_call_shift,
|
||||
):
|
||||
organization, user, _, channel_filter, alert_group, reason = escalation_step_test_setup
|
||||
viewer = make_user_for_organization(organization=organization, role=Role.VIEWER)
|
||||
organization, _, _, channel_filter, alert_group, reason = escalation_step_test_setup
|
||||
viewer = make_user_for_organization(organization=organization, role=LegacyAccessControlRole.VIEWER)
|
||||
|
||||
schedule = make_schedule(organization, schedule_class=OnCallScheduleCalendar)
|
||||
# create on_call_shift with user to notify
|
||||
|
|
@ -263,7 +263,7 @@ def test_escalation_step_notify_user_group(
|
|||
make_slack_user_group,
|
||||
make_escalation_policy,
|
||||
):
|
||||
organization, user, _, channel_filter, alert_group, reason = escalation_step_test_setup
|
||||
organization, _, _, channel_filter, alert_group, reason = escalation_step_test_setup
|
||||
slack_team_identity = make_slack_team_identity()
|
||||
organization.slack_team_identity = slack_team_identity
|
||||
organization.save()
|
||||
|
|
@ -295,7 +295,7 @@ def test_escalation_step_notify_if_time(
|
|||
escalation_step_test_setup,
|
||||
make_escalation_policy,
|
||||
):
|
||||
organization, user, _, channel_filter, alert_group, reason = escalation_step_test_setup
|
||||
_, _, _, channel_filter, alert_group, reason = escalation_step_test_setup
|
||||
|
||||
# current time is not between from_time and to_time, step returns eta
|
||||
now = timezone.now()
|
||||
|
|
@ -358,7 +358,7 @@ def test_escalation_step_notify_if_time(
|
|||
def test_escalation_step_notify_if_num_alerts_in_window(
|
||||
mocked_execute_tasks, escalation_step_test_setup, make_escalation_policy, make_alert
|
||||
):
|
||||
organization, user, _, channel_filter, alert_group, reason = escalation_step_test_setup
|
||||
_, _, _, channel_filter, alert_group, reason = escalation_step_test_setup
|
||||
|
||||
make_alert(alert_group=alert_group, raw_request_data={})
|
||||
make_alert(alert_group=alert_group, raw_request_data={})
|
||||
|
|
@ -419,7 +419,7 @@ def test_escalation_step_trigger_custom_button(
|
|||
make_custom_action,
|
||||
make_escalation_policy,
|
||||
):
|
||||
organization, _, alert_receive_channel, channel_filter, alert_group, reason = escalation_step_test_setup
|
||||
organization, _, _, channel_filter, alert_group, reason = escalation_step_test_setup
|
||||
|
||||
custom_button = make_custom_action(organization=organization)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@ from unittest.mock import patch
|
|||
import pytest
|
||||
|
||||
from apps.alerts.tasks.notify_user import notify_user_task, perform_notification
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
from apps.base.models.user_notification_policy import UserNotificationPolicy
|
||||
from apps.base.models.user_notification_policy_log_record import UserNotificationPolicyLogRecord
|
||||
from common.constants.role import Role
|
||||
|
||||
NOTIFICATION_UNAUTHORIZED_MSG = "notification is not allowed for user"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
|
@ -131,7 +133,9 @@ def test_notify_user_perform_notification_error_if_viewer(
|
|||
make_user_notification_policy_log_record,
|
||||
):
|
||||
organization = make_organization()
|
||||
user_1 = make_user(organization=organization, role=Role.VIEWER, _verified_phone_number="1234567890")
|
||||
user_1 = make_user(
|
||||
organization=organization, role=LegacyAccessControlRole.VIEWER, _verified_phone_number="1234567890"
|
||||
)
|
||||
user_notification_policy = make_user_notification_policy(
|
||||
user=user_1,
|
||||
step=UserNotificationPolicy.Step.NOTIFY,
|
||||
|
|
@ -150,11 +154,8 @@ def test_notify_user_perform_notification_error_if_viewer(
|
|||
|
||||
error_log_record = UserNotificationPolicyLogRecord.objects.last()
|
||||
assert error_log_record.type == UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_FAILED
|
||||
assert error_log_record.reason == f"notification is not allowed for user with role {user_1.role}"
|
||||
assert (
|
||||
error_log_record.notification_error_code
|
||||
== UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_NOT_ALLOWED_USER_ROLE
|
||||
)
|
||||
assert error_log_record.reason == NOTIFICATION_UNAUTHORIZED_MSG
|
||||
assert error_log_record.notification_error_code == UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_FORBIDDEN
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
|
@ -165,7 +166,9 @@ def test_notify_user_error_if_viewer(
|
|||
make_alert_group,
|
||||
):
|
||||
organization = make_organization()
|
||||
user_1 = make_user(organization=organization, role=Role.VIEWER, _verified_phone_number="1234567890")
|
||||
user_1 = make_user(
|
||||
organization=organization, role=LegacyAccessControlRole.VIEWER, _verified_phone_number="1234567890"
|
||||
)
|
||||
alert_receive_channel = make_alert_receive_channel(organization=organization)
|
||||
alert_group = make_alert_group(alert_receive_channel=alert_receive_channel)
|
||||
|
||||
|
|
@ -173,8 +176,5 @@ def test_notify_user_error_if_viewer(
|
|||
|
||||
error_log_record = UserNotificationPolicyLogRecord.objects.last()
|
||||
assert error_log_record.type == UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_FAILED
|
||||
assert error_log_record.reason == f"notification is not allowed for user with role {user_1.role}"
|
||||
assert (
|
||||
error_log_record.notification_error_code
|
||||
== UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_NOT_ALLOWED_USER_ROLE
|
||||
)
|
||||
assert error_log_record.reason == NOTIFICATION_UNAUTHORIZED_MSG
|
||||
assert error_log_record.notification_error_code == UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_FORBIDDEN
|
||||
|
|
|
|||
|
|
@ -1,5 +1,294 @@
|
|||
from .actions import ActionPermission # noqa: F401
|
||||
from .constants import ALL_BASE_ACTIONS, MODIFY_ACTIONS, READ_ACTIONS # noqa: F401
|
||||
from .methods import MethodPermission # noqa: F401
|
||||
from .owner import IsOwner, IsOwnerOrAdmin, IsOwnerOrAdminOrEditor # noqa: F401
|
||||
from .roles import AnyRole, IsAdmin, IsAdminOrEditor, IsEditor, IsStaff, IsViewer # noqa: F401
|
||||
import enum
|
||||
import typing
|
||||
|
||||
from rest_framework import permissions
|
||||
from rest_framework.authentication import BasicAuthentication, SessionAuthentication
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.viewsets import ViewSet, ViewSetMixin
|
||||
|
||||
from common.utils import getattrd
|
||||
|
||||
ACTION_PREFIX = "grafana-oncall-app"
|
||||
RBAC_PERMISSIONS_ATTR = "rbac_permissions"
|
||||
RBAC_OBJECT_PERMISSIONS_ATTR = "rbac_object_permissions"
|
||||
|
||||
ViewSetOrAPIView = typing.Union[ViewSet, APIView]
|
||||
|
||||
|
||||
class GrafanaAPIPermission(typing.TypedDict):
|
||||
action: str
|
||||
|
||||
|
||||
class Resources(enum.Enum):
|
||||
ALERT_GROUPS = "alert-groups"
|
||||
INTEGRATIONS = "integrations"
|
||||
ESCALATION_CHAINS = "escalation-chains"
|
||||
SCHEDULES = "schedules"
|
||||
CHATOPS = "chatops"
|
||||
OUTGOING_WEBHOOKS = "outgoing-webhooks"
|
||||
MAINTENANCE = "maintenance"
|
||||
API_KEYS = "api-keys"
|
||||
NOTIFICATIONS = "notifications"
|
||||
|
||||
NOTIFICATION_SETTINGS = "notification-settings"
|
||||
USER_SETTINGS = "user-settings"
|
||||
OTHER_SETTINGS = "other-settings"
|
||||
|
||||
|
||||
class Actions(enum.Enum):
|
||||
READ = "read"
|
||||
WRITE = "write"
|
||||
ADMIN = "admin"
|
||||
TEST = "test"
|
||||
EXPORT = "export"
|
||||
UPDATE_SETTINGS = "update-settings"
|
||||
|
||||
|
||||
class LegacyAccessControlRole(enum.IntEnum):
|
||||
ADMIN = 0
|
||||
EDITOR = 1
|
||||
VIEWER = 2
|
||||
|
||||
@classmethod
|
||||
def choices(cls):
|
||||
return tuple((option.value, option.name) for option in cls)
|
||||
|
||||
|
||||
class LegacyAccessControlCompatiblePermission:
|
||||
def __init__(self, resource: Resources, action: Actions, fallback_role: LegacyAccessControlRole) -> None:
|
||||
self.value = f"{ACTION_PREFIX}.{resource.value}:{action.value}"
|
||||
self.fallback_role = fallback_role
|
||||
|
||||
|
||||
def get_most_authorized_role(
|
||||
permissions: typing.List[LegacyAccessControlCompatiblePermission],
|
||||
) -> LegacyAccessControlRole:
|
||||
if not permissions:
|
||||
return LegacyAccessControlRole.VIEWER
|
||||
|
||||
# ex. Admin is 0, Viewer is 2, thereby min makes sense here
|
||||
return min({p.fallback_role for p in permissions}, key=lambda r: r.value)
|
||||
|
||||
|
||||
def user_is_authorized(user, required_permissions: typing.List[LegacyAccessControlCompatiblePermission]) -> bool:
|
||||
if user.organization.is_rbac_permissions_enabled:
|
||||
user_permissions = [u["action"] for u in user.permissions]
|
||||
required_permissions = [p.value for p in required_permissions]
|
||||
return all(permission in user_permissions for permission in required_permissions)
|
||||
return user.role <= get_most_authorized_role(required_permissions).value
|
||||
|
||||
|
||||
class RBACPermission(permissions.BasePermission):
|
||||
class Permissions:
|
||||
ALERT_GROUPS_READ = LegacyAccessControlCompatiblePermission(
|
||||
Resources.ALERT_GROUPS, Actions.READ, LegacyAccessControlRole.VIEWER
|
||||
)
|
||||
ALERT_GROUPS_WRITE = LegacyAccessControlCompatiblePermission(
|
||||
Resources.ALERT_GROUPS, Actions.WRITE, LegacyAccessControlRole.EDITOR
|
||||
)
|
||||
|
||||
INTEGRATIONS_READ = LegacyAccessControlCompatiblePermission(
|
||||
Resources.INTEGRATIONS, Actions.READ, LegacyAccessControlRole.VIEWER
|
||||
)
|
||||
INTEGRATIONS_TEST = LegacyAccessControlCompatiblePermission(
|
||||
Resources.INTEGRATIONS, Actions.TEST, LegacyAccessControlRole.EDITOR
|
||||
)
|
||||
INTEGRATIONS_WRITE = LegacyAccessControlCompatiblePermission(
|
||||
Resources.INTEGRATIONS, Actions.WRITE, LegacyAccessControlRole.ADMIN
|
||||
)
|
||||
|
||||
ESCALATION_CHAINS_READ = LegacyAccessControlCompatiblePermission(
|
||||
Resources.ESCALATION_CHAINS, Actions.READ, LegacyAccessControlRole.VIEWER
|
||||
)
|
||||
ESCALATION_CHAINS_WRITE = LegacyAccessControlCompatiblePermission(
|
||||
Resources.ESCALATION_CHAINS, Actions.WRITE, LegacyAccessControlRole.ADMIN
|
||||
)
|
||||
|
||||
SCHEDULES_READ = LegacyAccessControlCompatiblePermission(
|
||||
Resources.SCHEDULES, Actions.READ, LegacyAccessControlRole.VIEWER
|
||||
)
|
||||
SCHEDULES_WRITE = LegacyAccessControlCompatiblePermission(
|
||||
Resources.SCHEDULES, Actions.WRITE, LegacyAccessControlRole.ADMIN
|
||||
)
|
||||
SCHEDULES_EXPORT = LegacyAccessControlCompatiblePermission(
|
||||
Resources.SCHEDULES, Actions.EXPORT, LegacyAccessControlRole.EDITOR
|
||||
)
|
||||
|
||||
CHATOPS_READ = LegacyAccessControlCompatiblePermission(
|
||||
Resources.CHATOPS, Actions.READ, LegacyAccessControlRole.VIEWER
|
||||
)
|
||||
CHATOPS_WRITE = LegacyAccessControlCompatiblePermission(
|
||||
Resources.CHATOPS, Actions.WRITE, LegacyAccessControlRole.EDITOR
|
||||
)
|
||||
CHATOPS_UPDATE_SETTINGS = LegacyAccessControlCompatiblePermission(
|
||||
Resources.CHATOPS, Actions.UPDATE_SETTINGS, LegacyAccessControlRole.ADMIN
|
||||
)
|
||||
|
||||
OUTGOING_WEBHOOKS_READ = LegacyAccessControlCompatiblePermission(
|
||||
Resources.OUTGOING_WEBHOOKS, Actions.READ, LegacyAccessControlRole.VIEWER
|
||||
)
|
||||
OUTGOING_WEBHOOKS_WRITE = LegacyAccessControlCompatiblePermission(
|
||||
Resources.OUTGOING_WEBHOOKS, Actions.WRITE, LegacyAccessControlRole.ADMIN
|
||||
)
|
||||
|
||||
MAINTENANCE_READ = LegacyAccessControlCompatiblePermission(
|
||||
Resources.MAINTENANCE, Actions.READ, LegacyAccessControlRole.VIEWER
|
||||
)
|
||||
MAINTENANCE_WRITE = LegacyAccessControlCompatiblePermission(
|
||||
Resources.MAINTENANCE, Actions.WRITE, LegacyAccessControlRole.EDITOR
|
||||
)
|
||||
|
||||
API_KEYS_READ = LegacyAccessControlCompatiblePermission(
|
||||
Resources.API_KEYS, Actions.READ, LegacyAccessControlRole.VIEWER
|
||||
)
|
||||
API_KEYS_WRITE = LegacyAccessControlCompatiblePermission(
|
||||
Resources.API_KEYS, Actions.WRITE, LegacyAccessControlRole.EDITOR
|
||||
)
|
||||
|
||||
NOTIFICATIONS_READ = LegacyAccessControlCompatiblePermission(
|
||||
Resources.NOTIFICATIONS, Actions.READ, LegacyAccessControlRole.EDITOR
|
||||
)
|
||||
|
||||
NOTIFICATION_SETTINGS_READ = LegacyAccessControlCompatiblePermission(
|
||||
Resources.NOTIFICATION_SETTINGS, Actions.READ, LegacyAccessControlRole.VIEWER
|
||||
)
|
||||
NOTIFICATION_SETTINGS_WRITE = LegacyAccessControlCompatiblePermission(
|
||||
Resources.NOTIFICATION_SETTINGS, Actions.WRITE, LegacyAccessControlRole.EDITOR
|
||||
)
|
||||
|
||||
USER_SETTINGS_READ = LegacyAccessControlCompatiblePermission(
|
||||
Resources.USER_SETTINGS, Actions.READ, LegacyAccessControlRole.VIEWER
|
||||
)
|
||||
USER_SETTINGS_WRITE = LegacyAccessControlCompatiblePermission(
|
||||
Resources.USER_SETTINGS, Actions.WRITE, LegacyAccessControlRole.EDITOR
|
||||
)
|
||||
USER_SETTINGS_ADMIN = LegacyAccessControlCompatiblePermission(
|
||||
Resources.USER_SETTINGS, Actions.ADMIN, LegacyAccessControlRole.ADMIN
|
||||
)
|
||||
|
||||
OTHER_SETTINGS_READ = LegacyAccessControlCompatiblePermission(
|
||||
Resources.OTHER_SETTINGS, Actions.READ, LegacyAccessControlRole.VIEWER
|
||||
)
|
||||
OTHER_SETTINGS_WRITE = LegacyAccessControlCompatiblePermission(
|
||||
Resources.OTHER_SETTINGS, Actions.WRITE, LegacyAccessControlRole.ADMIN
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _get_view_action(request: Request, view: ViewSetOrAPIView) -> str:
|
||||
"""
|
||||
For right now this needs to support being used in both a ViewSet as well as APIView, we use both interchangably
|
||||
|
||||
Note: `request.method` is returned uppercase
|
||||
"""
|
||||
return view.action if isinstance(view, ViewSetMixin) else request.method.lower()
|
||||
|
||||
def has_permission(self, request: Request, view: ViewSetOrAPIView) -> bool:
|
||||
action = self._get_view_action(request, view)
|
||||
|
||||
rbac_permissions: RBACPermissionsAttribute = getattr(view, RBAC_PERMISSIONS_ATTR, None)
|
||||
|
||||
# first check that the rbac_permissions dict attribute is defined
|
||||
assert (
|
||||
rbac_permissions is not None
|
||||
), f"Must define a {RBAC_PERMISSIONS_ATTR} dict on the ViewSet that is consuming the RBACPermission class"
|
||||
|
||||
action_required_permissions: typing.Union[None, typing.List] = rbac_permissions.get(action, None)
|
||||
|
||||
# next check that the action in question is defined within the rbac_permissions dict attribute
|
||||
assert (
|
||||
action_required_permissions is not None
|
||||
), f"""Each action must be defined within the {RBAC_PERMISSIONS_ATTR} dict on the ViewSet.
|
||||
\nIf an action requires no permissions, its value should explicitly be set to an empty list"""
|
||||
|
||||
return user_is_authorized(request.user, action_required_permissions)
|
||||
|
||||
def has_object_permission(self, request: Request, view: ViewSetOrAPIView, obj: typing.Any) -> bool:
|
||||
rbac_object_permissions: RBACObjectPermissionsAttribute = getattr(view, RBAC_OBJECT_PERMISSIONS_ATTR, None)
|
||||
|
||||
if rbac_object_permissions:
|
||||
action = self._get_view_action(request, view)
|
||||
|
||||
for permission_class, actions in rbac_object_permissions.items():
|
||||
if action in actions:
|
||||
return permission_class.has_object_permission(request, view, obj)
|
||||
return False
|
||||
|
||||
# has_object_permission is called after has_permission, so return True if in view there is not
|
||||
# RBAC_OBJECT_PERMISSIONS_ATTR attr which mean no additional check involving object required
|
||||
return True
|
||||
|
||||
|
||||
class IsOwner(permissions.BasePermission):
|
||||
def __init__(self, ownership_field: typing.Optional[str] = None) -> None:
|
||||
self.ownership_field = ownership_field
|
||||
|
||||
def has_object_permission(self, request: Request, _view: ViewSet, obj: typing.Any) -> bool:
|
||||
owner = obj if self.ownership_field is None else getattrd(obj, self.ownership_field)
|
||||
return owner == request.user
|
||||
|
||||
|
||||
class HasRBACPermissions(permissions.BasePermission):
|
||||
def __init__(self, required_permissions: typing.List[LegacyAccessControlCompatiblePermission]) -> None:
|
||||
self.required_permissions = required_permissions
|
||||
|
||||
def has_object_permission(self, request: Request, _view: ViewSetOrAPIView, _obj: typing.Any) -> bool:
|
||||
return user_is_authorized(request.user, self.required_permissions)
|
||||
|
||||
|
||||
class IsOwnerOrHasRBACPermissions(permissions.BasePermission):
|
||||
def __init__(
|
||||
self,
|
||||
required_permissions: typing.List[LegacyAccessControlCompatiblePermission],
|
||||
ownership_field: typing.Optional[str] = None,
|
||||
) -> None:
|
||||
self.IsOwner = IsOwner(ownership_field)
|
||||
self.HasRBACPermissions = HasRBACPermissions(required_permissions)
|
||||
|
||||
def has_object_permission(self, request: Request, view: ViewSetOrAPIView, obj: typing.Any) -> bool:
|
||||
return self.IsOwner.has_object_permission(request, view, obj) or self.HasRBACPermissions.has_object_permission(
|
||||
request, view, obj
|
||||
)
|
||||
|
||||
|
||||
class IsStaff(permissions.BasePermission):
|
||||
STAFF_AUTH_CLASSES = [BasicAuthentication, SessionAuthentication]
|
||||
|
||||
def has_permission(self, request: Request, _view: ViewSet) -> bool:
|
||||
user = request.user
|
||||
if not any(isinstance(request._authenticator, x) for x in self.STAFF_AUTH_CLASSES):
|
||||
return False
|
||||
if user and user.is_authenticated:
|
||||
return user.is_staff
|
||||
return False
|
||||
|
||||
|
||||
RBACPermissionsAttribute = typing.Dict[str, typing.List[LegacyAccessControlCompatiblePermission]]
|
||||
RBACObjectPermissionsAttribute = typing.Dict[permissions.BasePermission, typing.List[str]]
|
||||
|
||||
|
||||
# The below is legacy, it is only needed currently for backward compatibility w/ users running
|
||||
# older "pinned" version of Grafana in Grafana Cloud
|
||||
_DONT_USE_LEGACY_VIEWER_PERMISSIONS = []
|
||||
_DONT_USE_LEGACY_EDITOR_PERMISSIONS = ["update_incidents", "update_own_settings", "view_other_users"]
|
||||
_DONT_USE_LEGACY_ADMIN_PERMISSIONS = _DONT_USE_LEGACY_EDITOR_PERMISSIONS + [
|
||||
"update_alert_receive_channels",
|
||||
"update_escalation_policies",
|
||||
"update_notification_policies",
|
||||
"update_general_log_channel_id",
|
||||
"update_other_users_settings",
|
||||
"update_integrations",
|
||||
"update_schedules",
|
||||
"update_custom_actions",
|
||||
"update_api_tokens",
|
||||
"update_teams",
|
||||
"update_maintenances",
|
||||
"update_global_settings",
|
||||
"send_demo_alert",
|
||||
]
|
||||
|
||||
DONT_USE_LEGACY_PERMISSION_MAPPING: typing.Dict[LegacyAccessControlRole, typing.List[str]] = {
|
||||
LegacyAccessControlRole.VIEWER: _DONT_USE_LEGACY_VIEWER_PERMISSIONS,
|
||||
LegacyAccessControlRole.EDITOR: _DONT_USE_LEGACY_EDITOR_PERMISSIONS,
|
||||
LegacyAccessControlRole.ADMIN: _DONT_USE_LEGACY_ADMIN_PERMISSIONS,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
from typing import Any
|
||||
|
||||
from rest_framework import permissions
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.viewsets import ViewSet
|
||||
|
||||
|
||||
class ActionPermission(permissions.BasePermission):
|
||||
def has_permission(self, request: Request, view: ViewSet) -> bool:
|
||||
for permission, actions in getattr(view, "action_permissions", {}).items():
|
||||
if view.action in actions:
|
||||
return permission().has_permission(request, view)
|
||||
|
||||
return False
|
||||
|
||||
def has_object_permission(self, request: Request, view: ViewSet, obj: Any) -> bool:
|
||||
# action_object_permissions attr should be used in case permission check require lookup
|
||||
# for some object's properties e.g. team.
|
||||
if getattr(view, "action_object_permissions", None):
|
||||
for permission, actions in getattr(view, "action_object_permissions", {}).items():
|
||||
if view.action in actions:
|
||||
return permission().has_object_permission(request, view, obj)
|
||||
return False
|
||||
else:
|
||||
# has_object_permission is called after has_permission, so return True if in view there is not
|
||||
# action_object_permission attr which mean no additional check involving object required
|
||||
return True
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
READ_ACTIONS = (
|
||||
"list",
|
||||
"retrieve",
|
||||
"metadata",
|
||||
)
|
||||
|
||||
MODIFY_ACTIONS = (
|
||||
"create",
|
||||
"update",
|
||||
"partial_update",
|
||||
"destroy",
|
||||
)
|
||||
|
||||
ALL_BASE_ACTIONS = READ_ACTIONS + MODIFY_ACTIONS
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
from rest_framework import permissions
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.viewsets import ViewSet
|
||||
|
||||
|
||||
class MethodPermission(permissions.BasePermission):
|
||||
def has_permission(self, request: Request, view: ViewSet) -> bool:
|
||||
for permission, methods in getattr(view, "method_permissions", {}).items():
|
||||
if request.method in methods:
|
||||
return permission().has_permission(request, view)
|
||||
|
||||
return False
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
from typing import Any
|
||||
|
||||
from rest_framework import permissions
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.viewsets import ViewSet
|
||||
|
||||
from apps.api.permissions.roles import IsAdmin, IsEditor
|
||||
from common.utils import getattrd
|
||||
|
||||
|
||||
class IsOwner(permissions.BasePermission):
|
||||
def has_object_permission(self, request: Request, view: ViewSet, obj: Any) -> bool:
|
||||
ownership_field = getattr(view, "ownership_field", None)
|
||||
if ownership_field is None:
|
||||
owner = obj
|
||||
else:
|
||||
owner = getattrd(obj, ownership_field)
|
||||
|
||||
return owner == request.user
|
||||
|
||||
|
||||
IsOwnerOrAdmin = IsOwner | IsAdmin
|
||||
|
||||
IsOwnerOrAdminOrEditor = IsOwner | IsAdmin | IsEditor
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
from typing import Any
|
||||
|
||||
from rest_framework import permissions
|
||||
from rest_framework.authentication import BasicAuthentication, SessionAuthentication
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.viewsets import ViewSet
|
||||
|
||||
from common.constants.role import Role
|
||||
|
||||
|
||||
class RolePermission(permissions.BasePermission):
|
||||
ROLE = None
|
||||
|
||||
def has_permission(self, request: Request, view: ViewSet) -> bool:
|
||||
return request.user.role == type(self).ROLE
|
||||
|
||||
def has_object_permission(self, request: Request, view: ViewSet, obj: Any) -> bool:
|
||||
return self.has_permission(request, view)
|
||||
|
||||
|
||||
class IsAdmin(RolePermission):
|
||||
ROLE = Role.ADMIN
|
||||
|
||||
|
||||
class IsEditor(RolePermission):
|
||||
ROLE = Role.EDITOR
|
||||
|
||||
|
||||
class IsViewer(RolePermission):
|
||||
ROLE = Role.VIEWER
|
||||
|
||||
|
||||
IsAdminOrEditor = IsAdmin | IsEditor
|
||||
AnyRole = IsAdmin | IsEditor | IsViewer
|
||||
|
||||
|
||||
class IsStaff(permissions.BasePermission):
|
||||
STAFF_AUTH_CLASSES = [BasicAuthentication, SessionAuthentication]
|
||||
|
||||
def has_permission(self, request: Request, view: ViewSet) -> bool:
|
||||
user = request.user
|
||||
if not any(isinstance(request._authenticator, x) for x in self.STAFF_AUTH_CLASSES):
|
||||
return False
|
||||
if user and user.is_authenticated:
|
||||
return user.is_staff
|
||||
return False
|
||||
|
||||
def has_object_permission(self, request: Request, view: ViewSet, obj: Any) -> bool:
|
||||
return self.has_permission(request, view)
|
||||
428
engine/apps/api/permissions/test_permissions.py
Normal file
428
engine/apps/api/permissions/test_permissions.py
Normal file
|
|
@ -0,0 +1,428 @@
|
|||
import typing
|
||||
|
||||
import pytest
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.viewsets import ViewSetMixin
|
||||
|
||||
from . import (
|
||||
RBAC_PERMISSIONS_ATTR,
|
||||
GrafanaAPIPermission,
|
||||
HasRBACPermissions,
|
||||
IsOwner,
|
||||
IsOwnerOrHasRBACPermissions,
|
||||
LegacyAccessControlCompatiblePermission,
|
||||
RBACObjectPermissionsAttribute,
|
||||
RBACPermission,
|
||||
RBACPermissionsAttribute,
|
||||
get_most_authorized_role,
|
||||
user_is_authorized,
|
||||
)
|
||||
|
||||
|
||||
class MockedOrg:
|
||||
def __init__(self, org_has_rbac_enabled: bool) -> None:
|
||||
self.is_rbac_permissions_enabled = org_has_rbac_enabled
|
||||
|
||||
|
||||
class MockedUser:
|
||||
def __init__(
|
||||
self, permissions: typing.List[LegacyAccessControlCompatiblePermission], org_has_rbac_enabled=True
|
||||
) -> None:
|
||||
self.permissions = [GrafanaAPIPermission(action=perm.value) for perm in permissions]
|
||||
self.role = get_most_authorized_role(permissions)
|
||||
self.organization = MockedOrg(org_has_rbac_enabled)
|
||||
|
||||
|
||||
class MockedSchedule:
|
||||
def __init__(self, user: MockedUser) -> None:
|
||||
self.user = user
|
||||
|
||||
|
||||
class MockedRequest:
|
||||
def __init__(self, user: typing.Optional[MockedUser] = None, method: typing.Optional[str] = None) -> None:
|
||||
if user:
|
||||
self.user = user
|
||||
if method:
|
||||
self.method = method
|
||||
|
||||
|
||||
class MockedViewSet(ViewSetMixin):
|
||||
def __init__(
|
||||
self,
|
||||
action: str,
|
||||
rbac_permissions: typing.Optional[RBACPermissionsAttribute] = None,
|
||||
rbac_object_permissions: typing.Optional[RBACObjectPermissionsAttribute] = None,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.action = action
|
||||
|
||||
if rbac_permissions:
|
||||
self.rbac_permissions = rbac_permissions
|
||||
if rbac_object_permissions:
|
||||
self.rbac_object_permissions = rbac_object_permissions
|
||||
|
||||
|
||||
class MockedAPIView(APIView):
|
||||
def __init__(
|
||||
self,
|
||||
rbac_permissions: typing.Optional[RBACPermissionsAttribute] = None,
|
||||
rbac_object_permissions: typing.Optional[RBACObjectPermissionsAttribute] = None,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
|
||||
if rbac_permissions:
|
||||
self.rbac_permissions = rbac_permissions
|
||||
if rbac_object_permissions:
|
||||
self.rbac_object_permissions = rbac_object_permissions
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"user_permissions,required_permissions,org_has_rbac_enabled,expected_result",
|
||||
[
|
||||
(
|
||||
[RBACPermission.Permissions.ALERT_GROUPS_READ],
|
||||
[RBACPermission.Permissions.ALERT_GROUPS_READ],
|
||||
True,
|
||||
True,
|
||||
),
|
||||
(
|
||||
[RBACPermission.Permissions.ALERT_GROUPS_READ],
|
||||
[RBACPermission.Permissions.ALERT_GROUPS_READ],
|
||||
False,
|
||||
True,
|
||||
),
|
||||
(
|
||||
[RBACPermission.Permissions.ALERT_GROUPS_READ, RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
[RBACPermission.Permissions.ALERT_GROUPS_READ, RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
True,
|
||||
True,
|
||||
),
|
||||
(
|
||||
[RBACPermission.Permissions.ALERT_GROUPS_READ, RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
[RBACPermission.Permissions.ALERT_GROUPS_READ, RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
False,
|
||||
True,
|
||||
),
|
||||
(
|
||||
[RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
[RBACPermission.Permissions.ALERT_GROUPS_READ],
|
||||
True,
|
||||
False,
|
||||
),
|
||||
(
|
||||
[RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
[RBACPermission.Permissions.ALERT_GROUPS_READ],
|
||||
False,
|
||||
True,
|
||||
),
|
||||
(
|
||||
[RBACPermission.Permissions.ALERT_GROUPS_READ],
|
||||
[RBACPermission.Permissions.ALERT_GROUPS_READ, RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
False,
|
||||
False,
|
||||
),
|
||||
(
|
||||
[RBACPermission.Permissions.ALERT_GROUPS_READ],
|
||||
[RBACPermission.Permissions.ALERT_GROUPS_READ, RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
True,
|
||||
False,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_user_is_authorized(user_permissions, required_permissions, org_has_rbac_enabled, expected_result) -> None:
|
||||
user = MockedUser(user_permissions, org_has_rbac_enabled=org_has_rbac_enabled)
|
||||
assert user_is_authorized(user, required_permissions) == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"permissions,expected_role",
|
||||
[
|
||||
([RBACPermission.Permissions.ALERT_GROUPS_READ], RBACPermission.Permissions.ALERT_GROUPS_READ.fallback_role),
|
||||
(
|
||||
[RBACPermission.Permissions.ALERT_GROUPS_READ, RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
RBACPermission.Permissions.ALERT_GROUPS_WRITE.fallback_role,
|
||||
),
|
||||
(
|
||||
[
|
||||
RBACPermission.Permissions.USER_SETTINGS_READ,
|
||||
RBACPermission.Permissions.USER_SETTINGS_WRITE,
|
||||
RBACPermission.Permissions.USER_SETTINGS_ADMIN,
|
||||
],
|
||||
RBACPermission.Permissions.USER_SETTINGS_ADMIN.fallback_role,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_get_most_authorized_role(permissions, expected_role) -> None:
|
||||
assert get_most_authorized_role(permissions) == expected_role
|
||||
|
||||
|
||||
class TestRBACPermission:
|
||||
def test_get_view_action(self) -> None:
|
||||
viewset_action = "viewset_action"
|
||||
viewset = MockedViewSet(viewset_action)
|
||||
|
||||
apiview = MockedAPIView()
|
||||
|
||||
method = "APIVIEW_ACTION"
|
||||
request = MockedRequest(method=method)
|
||||
|
||||
assert RBACPermission._get_view_action(request, viewset) == viewset_action, "it works with a ViewSet"
|
||||
assert RBACPermission._get_view_action(request, apiview) == method.lower(), "it works with an APIView"
|
||||
|
||||
def test_has_permission_works_on_a_viewset_view(self) -> None:
|
||||
required_permission = RBACPermission.Permissions.ALERT_GROUPS_READ
|
||||
|
||||
action = "hello"
|
||||
viewset = MockedViewSet(
|
||||
action=action,
|
||||
rbac_permissions={
|
||||
action: [required_permission],
|
||||
},
|
||||
)
|
||||
|
||||
viewset_with_no_required_permissions = MockedViewSet(
|
||||
action=action,
|
||||
rbac_permissions={
|
||||
action: [],
|
||||
},
|
||||
)
|
||||
|
||||
user_with_permission = MockedUser([required_permission])
|
||||
user_without_permission = MockedUser([RBACPermission.Permissions.ALERT_GROUPS_WRITE])
|
||||
|
||||
assert (
|
||||
RBACPermission().has_permission(MockedRequest(user_with_permission), viewset) is True
|
||||
), "it works on a viewset when the user does have permission"
|
||||
|
||||
assert (
|
||||
RBACPermission().has_permission(MockedRequest(user_without_permission), viewset) is False
|
||||
), "it works on a viewset when the user does have permission"
|
||||
|
||||
assert (
|
||||
RBACPermission().has_permission(
|
||||
MockedRequest(user_without_permission), viewset_with_no_required_permissions
|
||||
)
|
||||
is True
|
||||
), "it works on a viewset when the viewset action does not require permissions"
|
||||
|
||||
def test_has_permission_works_on_an_apiview_view(self) -> None:
|
||||
required_permission = RBACPermission.Permissions.ALERT_GROUPS_READ
|
||||
|
||||
method = "hello"
|
||||
apiview = MockedAPIView(
|
||||
rbac_permissions={
|
||||
method: [required_permission],
|
||||
}
|
||||
)
|
||||
apiview_with_no_permissions = MockedAPIView(
|
||||
rbac_permissions={
|
||||
method: [],
|
||||
}
|
||||
)
|
||||
|
||||
user1 = MockedUser([required_permission])
|
||||
user2 = MockedUser([RBACPermission.Permissions.ALERT_GROUPS_WRITE])
|
||||
|
||||
class Request(MockedRequest):
|
||||
def __init__(self, user: typing.Optional[MockedUser] = None) -> None:
|
||||
super().__init__(user, method)
|
||||
|
||||
assert (
|
||||
RBACPermission().has_permission(Request(user1), apiview) is True
|
||||
), "it works on an APIView when the user has permission"
|
||||
|
||||
assert (
|
||||
RBACPermission().has_permission(Request(user2), apiview) is False
|
||||
), "it works on an APIView when the user does not have permission"
|
||||
|
||||
assert (
|
||||
RBACPermission().has_permission(Request(user2), apiview_with_no_permissions) is True
|
||||
), "it works on a viewset when the viewset action does not require permissions"
|
||||
|
||||
def test_has_permission_throws_assertion_error_if_developer_forgets_to_specify_rbac_permissions(self) -> None:
|
||||
action_slash_method = "hello"
|
||||
error_msg = (
|
||||
f"Must define a {RBAC_PERMISSIONS_ATTR} dict on the ViewSet that is consuming the RBACPermission class"
|
||||
)
|
||||
|
||||
viewset = MockedViewSet(action_slash_method)
|
||||
apiview = MockedAPIView()
|
||||
|
||||
with pytest.raises(AssertionError, match=error_msg):
|
||||
RBACPermission().has_permission(MockedRequest(), viewset)
|
||||
|
||||
with pytest.raises(AssertionError, match=error_msg):
|
||||
RBACPermission().has_permission(MockedRequest(method=action_slash_method), apiview)
|
||||
|
||||
def test_has_permission_throws_assertion_error_if_developer_forgets_to_specify_an_action_in_rbac_permissions(
|
||||
self,
|
||||
) -> None:
|
||||
action_slash_method = "hello"
|
||||
other_action_rbac_permissions = {"bonjour": []}
|
||||
error_msg = f"""Each action must be defined within the {RBAC_PERMISSIONS_ATTR} dict on the ViewSet.
|
||||
\nIf an action requires no permissions, its value should explicitly be set to an empty list"""
|
||||
|
||||
viewset = MockedViewSet(action_slash_method, other_action_rbac_permissions)
|
||||
apiview = MockedAPIView(rbac_permissions=other_action_rbac_permissions)
|
||||
|
||||
with pytest.raises(AssertionError, match=error_msg):
|
||||
RBACPermission().has_permission(MockedRequest(), viewset)
|
||||
|
||||
with pytest.raises(AssertionError, match=error_msg):
|
||||
RBACPermission().has_permission(MockedRequest(method=action_slash_method), apiview)
|
||||
|
||||
def test_has_object_permission_returns_true_if_rbac_object_permissions_not_specified(self) -> None:
|
||||
request = MockedRequest()
|
||||
assert RBACPermission().has_object_permission(request, MockedAPIView(), None) is True
|
||||
assert RBACPermission().has_object_permission(request, MockedViewSet("potato"), None) is True
|
||||
|
||||
def test_has_object_permission_works_if_no_permission_class_specified_for_action(self) -> None:
|
||||
action = "hello"
|
||||
|
||||
request = MockedRequest(None, action)
|
||||
apiview = MockedAPIView(rbac_object_permissions={})
|
||||
viewset = MockedViewSet(action, rbac_object_permissions={})
|
||||
|
||||
assert RBACPermission().has_object_permission(request, apiview, None) is True
|
||||
assert RBACPermission().has_object_permission(request, viewset, None) is True
|
||||
|
||||
def test_has_object_permission_works_when_permission_class_specified_for_action(self) -> None:
|
||||
action = "hello"
|
||||
mocked_permission_class_response = "asdfasdfasdf"
|
||||
|
||||
class MockedPermissionClass:
|
||||
def has_object_permission(self, _req, _view, _obj) -> None:
|
||||
return mocked_permission_class_response
|
||||
|
||||
rbac_object_permissions = {MockedPermissionClass(): (action,)}
|
||||
request = MockedRequest(None, action)
|
||||
apiview = MockedAPIView(rbac_object_permissions=rbac_object_permissions)
|
||||
viewset = MockedViewSet(action, rbac_object_permissions=rbac_object_permissions)
|
||||
|
||||
assert RBACPermission().has_object_permission(request, apiview, None) == mocked_permission_class_response
|
||||
assert RBACPermission().has_object_permission(request, viewset, None) == mocked_permission_class_response
|
||||
|
||||
|
||||
class TestIsOwner:
|
||||
def test_it_works_when_comparing_user_to_object(self) -> None:
|
||||
user1 = MockedUser([])
|
||||
user2 = MockedUser([])
|
||||
request = MockedRequest(user1)
|
||||
IsUser = IsOwner()
|
||||
|
||||
assert IsUser.has_object_permission(request, None, user1) is True
|
||||
assert IsUser.has_object_permission(request, None, user2) is False
|
||||
|
||||
def test_it_works_when_comparing_user_to_ownership_field_object(self) -> None:
|
||||
user1 = MockedUser([])
|
||||
user2 = MockedUser([])
|
||||
schedule = MockedSchedule(user1)
|
||||
IsScheduleOwner = IsOwner("user")
|
||||
|
||||
assert IsScheduleOwner.has_object_permission(MockedRequest(user1), None, schedule) is True
|
||||
assert IsScheduleOwner.has_object_permission(MockedRequest(user2), None, schedule) is False
|
||||
|
||||
def test_it_works_when_comparing_user_to_nested_ownership_field_object(self) -> None:
|
||||
class Thingy:
|
||||
def __init__(self, schedule: MockedSchedule) -> None:
|
||||
self.schedule = schedule
|
||||
|
||||
user1 = MockedUser([])
|
||||
user2 = MockedUser([])
|
||||
schedule = MockedSchedule(user1)
|
||||
thingy = Thingy(schedule)
|
||||
IsScheduleOwner = IsOwner("schedule.user")
|
||||
|
||||
assert IsScheduleOwner.has_object_permission(MockedRequest(user1), None, thingy) is True
|
||||
assert IsScheduleOwner.has_object_permission(MockedRequest(user2), None, thingy) is False
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"user_permissions,required_permissions,expected_result",
|
||||
[
|
||||
(
|
||||
[RBACPermission.Permissions.ALERT_GROUPS_READ],
|
||||
[RBACPermission.Permissions.ALERT_GROUPS_READ],
|
||||
True,
|
||||
),
|
||||
(
|
||||
[RBACPermission.Permissions.ALERT_GROUPS_READ, RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
[RBACPermission.Permissions.ALERT_GROUPS_READ, RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
True,
|
||||
),
|
||||
(
|
||||
[RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
[RBACPermission.Permissions.ALERT_GROUPS_READ],
|
||||
False,
|
||||
),
|
||||
(
|
||||
[RBACPermission.Permissions.ALERT_GROUPS_READ],
|
||||
[RBACPermission.Permissions.ALERT_GROUPS_READ, RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
False,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_HasRBACPermission(user_permissions, required_permissions, expected_result) -> None:
|
||||
request = MockedRequest(MockedUser(user_permissions))
|
||||
assert HasRBACPermissions(required_permissions).has_object_permission(request, None, None) == expected_result
|
||||
|
||||
|
||||
class TestIsOwnerOrHasRBACPermissions:
|
||||
required_permission = RBACPermission.Permissions.SCHEDULES_READ
|
||||
required_permissions = [required_permission]
|
||||
|
||||
def test_it_works_when_user_is_owner_and_does_not_have_permissions(self) -> None:
|
||||
user1 = MockedUser([])
|
||||
schedule = MockedSchedule(user1)
|
||||
request = MockedRequest(user1)
|
||||
|
||||
PermClass = IsOwnerOrHasRBACPermissions(self.required_permissions)
|
||||
assert PermClass.has_object_permission(request, None, user1) is True
|
||||
|
||||
PermClass = IsOwnerOrHasRBACPermissions(self.required_permissions, "user")
|
||||
assert PermClass.has_object_permission(request, None, schedule) is True
|
||||
|
||||
def test_it_works_when_user_is_owner_and_has_permissions(self) -> None:
|
||||
user1 = MockedUser(self.required_permissions)
|
||||
schedule = MockedSchedule(user1)
|
||||
request = MockedRequest(user1)
|
||||
|
||||
PermClass = IsOwnerOrHasRBACPermissions(self.required_permissions)
|
||||
assert PermClass.has_object_permission(request, None, user1) is True
|
||||
|
||||
PermClass = IsOwnerOrHasRBACPermissions(self.required_permissions, "user")
|
||||
assert PermClass.has_object_permission(request, None, schedule) is True
|
||||
|
||||
def test_it_works_when_user_is_not_owner_and_does_not_have_permissions(self) -> None:
|
||||
user1 = MockedUser([])
|
||||
user2 = MockedUser([])
|
||||
schedule = MockedSchedule(user1)
|
||||
request = MockedRequest(user2)
|
||||
|
||||
PermClass = IsOwnerOrHasRBACPermissions(self.required_permissions)
|
||||
assert PermClass.has_object_permission(request, None, user1) is False
|
||||
|
||||
PermClass = IsOwnerOrHasRBACPermissions(self.required_permissions, "user")
|
||||
assert PermClass.has_object_permission(request, None, schedule) is False
|
||||
|
||||
def test_it_works_when_user_is_not_owner_and_has_permissions(self) -> None:
|
||||
user1 = MockedUser([])
|
||||
user2 = MockedUser(self.required_permissions)
|
||||
schedule = MockedSchedule(user1)
|
||||
request = MockedRequest(user2)
|
||||
|
||||
PermClass = IsOwnerOrHasRBACPermissions(self.required_permissions)
|
||||
assert PermClass.has_object_permission(request, None, user1) is True
|
||||
|
||||
PermClass = IsOwnerOrHasRBACPermissions(self.required_permissions, "user")
|
||||
assert PermClass.has_object_permission(request, None, schedule) is True
|
||||
|
||||
class Thingy:
|
||||
def __init__(self, schedule: MockedSchedule) -> None:
|
||||
self.schedule = schedule
|
||||
|
||||
thingy = Thingy(schedule)
|
||||
PermClass = IsOwnerOrHasRBACPermissions(self.required_permissions, "schedule.user")
|
||||
|
||||
assert PermClass.has_object_permission(request, None, thingy) is True
|
||||
assert PermClass.has_object_permission(MockedRequest(MockedUser([])), None, thingy) is False
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
import math
|
||||
import time
|
||||
import typing
|
||||
|
||||
import pytz
|
||||
from django.conf import settings
|
||||
from rest_framework import serializers
|
||||
|
||||
from apps.api.permissions import DONT_USE_LEGACY_PERMISSION_MAPPING
|
||||
from apps.api.serializers.telegram import TelegramToUserConnectorSerializer
|
||||
from apps.base.constants import ADMIN_PERMISSIONS, ALL_ROLES_PERMISSIONS, EDITOR_PERMISSIONS
|
||||
from apps.base.messaging import get_messaging_backends
|
||||
from apps.base.models import UserNotificationPolicy
|
||||
from apps.base.utils import live_settings
|
||||
|
|
@ -16,7 +17,6 @@ from apps.user_management.models import User
|
|||
from apps.user_management.models.user import default_working_hours
|
||||
from common.api_helpers.custom_fields import TeamPrimaryKeyRelatedField
|
||||
from common.api_helpers.mixins import EagerLoadingMixin
|
||||
from common.constants.role import Role
|
||||
|
||||
from .custom_serializers import DynamicFieldsModelSerializer
|
||||
from .organization import FastOrganizationSerializer
|
||||
|
|
@ -52,7 +52,7 @@ class UserSerializer(DynamicFieldsModelSerializer, EagerLoadingMixin):
|
|||
"email",
|
||||
"username",
|
||||
"name",
|
||||
"role",
|
||||
"role", # LEGACY.. this should get removed eventually
|
||||
"avatar",
|
||||
"avatar_full",
|
||||
"timezone",
|
||||
|
|
@ -62,7 +62,7 @@ class UserSerializer(DynamicFieldsModelSerializer, EagerLoadingMixin):
|
|||
"slack_user_identity",
|
||||
"telegram_configuration",
|
||||
"messaging_backends",
|
||||
"permissions",
|
||||
"permissions", # LEGACY.. this should get removed eventually
|
||||
"notification_chain_verbal",
|
||||
"cloud_connection_status",
|
||||
"hide_phone_number",
|
||||
|
|
@ -71,7 +71,7 @@ class UserSerializer(DynamicFieldsModelSerializer, EagerLoadingMixin):
|
|||
"email",
|
||||
"username",
|
||||
"name",
|
||||
"role",
|
||||
"role", # LEGACY.. this should get removed eventually
|
||||
"verified_phone_number",
|
||||
]
|
||||
|
||||
|
|
@ -139,13 +139,8 @@ class UserSerializer(DynamicFieldsModelSerializer, EagerLoadingMixin):
|
|||
serialized_data[backend_id] = backend.serialize_user(obj)
|
||||
return serialized_data
|
||||
|
||||
def get_permissions(self, obj):
|
||||
if obj.role == Role.ADMIN:
|
||||
return ADMIN_PERMISSIONS
|
||||
elif obj.role == Role.EDITOR:
|
||||
return EDITOR_PERMISSIONS
|
||||
else:
|
||||
return ALL_ROLES_PERMISSIONS
|
||||
def get_permissions(self, obj) -> typing.List[str]:
|
||||
return DONT_USE_LEGACY_PERMISSION_MAPPING[obj.role]
|
||||
|
||||
def get_notification_chain_verbal(self, obj):
|
||||
default, important = UserNotificationPolicy.get_short_verbals_for_user(user=obj)
|
||||
|
|
@ -180,7 +175,7 @@ class UserSerializer(DynamicFieldsModelSerializer, EagerLoadingMixin):
|
|||
|
||||
|
||||
class UserHiddenFieldsSerializer(UserSerializer):
|
||||
available_for_all_roles_fields = [
|
||||
fields_available_for_all_users = [
|
||||
"pk",
|
||||
"organization",
|
||||
"current_team",
|
||||
|
|
@ -196,7 +191,7 @@ class UserHiddenFieldsSerializer(UserSerializer):
|
|||
ret = super(UserSerializer, self).to_representation(instance)
|
||||
if instance.id != self.context["request"].user.id:
|
||||
for field in ret:
|
||||
if field not in self.available_for_all_roles_fields:
|
||||
if field not in self.fields_available_for_all_users:
|
||||
ret[field] = "******"
|
||||
ret["hidden_fields"] = True
|
||||
return ret
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from rest_framework.response import Response
|
|||
from rest_framework.test import APIClient
|
||||
|
||||
from apps.alerts.models import AlertGroup, AlertGroupLogRecord
|
||||
from common.constants.role import Role
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
|
||||
alert_raw_request_data = {
|
||||
"evalMatches": [
|
||||
|
|
@ -25,15 +25,6 @@ alert_raw_request_data = {
|
|||
}
|
||||
|
||||
|
||||
# # This function is for creating token and do not to change fixture alert_group_internal_api_setup return values.
|
||||
# # To create token amixr team is needed but in most tests using fixture alert_group_internal_api_setup team is redundant
|
||||
# # So it just extract amixr team form alert_groups.
|
||||
# def create_token_from_initial_test_data(make_func, alert_groups, role):
|
||||
# organization = alert_groups[0].channel.organization
|
||||
# _, token_user_role = make_func(organization, role)
|
||||
# return token_user_role
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def alert_group_internal_api_setup(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
|
|
@ -52,7 +43,7 @@ def alert_group_internal_api_setup(
|
|||
|
||||
@pytest.mark.django_db
|
||||
def test_get_filter_started_at(alert_group_internal_api_setup, make_user_auth_headers):
|
||||
user, token, alert_groups = alert_group_internal_api_setup
|
||||
user, token, _ = alert_group_internal_api_setup
|
||||
client = APIClient()
|
||||
|
||||
url = reverse("api-internal:alertgroup-list")
|
||||
|
|
@ -69,7 +60,7 @@ def test_get_filter_started_at(alert_group_internal_api_setup, make_user_auth_he
|
|||
@pytest.mark.django_db
|
||||
def test_get_filter_resolved_at_alertgroup_empty_result(alert_group_internal_api_setup, make_user_auth_headers):
|
||||
client = APIClient()
|
||||
user, token, alert_groups = alert_group_internal_api_setup
|
||||
user, token, _ = alert_group_internal_api_setup
|
||||
|
||||
url = reverse("api-internal:alertgroup-list")
|
||||
response = client.get(
|
||||
|
|
@ -84,7 +75,7 @@ def test_get_filter_resolved_at_alertgroup_empty_result(alert_group_internal_api
|
|||
@pytest.mark.django_db
|
||||
def test_get_filter_resolved_at_alertgroup_invalid_format(alert_group_internal_api_setup, make_user_auth_headers):
|
||||
client = APIClient()
|
||||
user, token, alert_groups = alert_group_internal_api_setup
|
||||
user, token, _ = alert_group_internal_api_setup
|
||||
|
||||
url = reverse("api-internal:alertgroup-list")
|
||||
response = client.get(
|
||||
|
|
@ -660,19 +651,25 @@ def test_get_filter_with_resolution_note_after_delete_resolution_note(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_alert_group_acknowledge_permissions(
|
||||
make_user_for_organization, alert_group_internal_api_setup, make_user_auth_headers, role, expected_status
|
||||
alert_group_internal_api_setup,
|
||||
make_user_for_organization,
|
||||
make_user_auth_headers,
|
||||
role,
|
||||
expected_status,
|
||||
):
|
||||
client = APIClient()
|
||||
_, token, alert_groups = alert_group_internal_api_setup
|
||||
_, _, new_alert_group, _ = alert_groups
|
||||
organization = new_alert_group.channel.organization
|
||||
user = make_user_for_organization(organization, role)
|
||||
|
||||
client = APIClient()
|
||||
|
||||
url = reverse("api-internal:alertgroup-acknowledge", kwargs={"pk": new_alert_group.public_primary_key})
|
||||
|
||||
with patch(
|
||||
|
|
@ -689,19 +686,24 @@ def test_alert_group_acknowledge_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_alert_group_unacknowledge_permissions(
|
||||
make_user_for_organization, alert_group_internal_api_setup, make_user_auth_headers, role, expected_status
|
||||
alert_group_internal_api_setup,
|
||||
make_user_for_organization,
|
||||
make_user_auth_headers,
|
||||
role,
|
||||
expected_status,
|
||||
):
|
||||
client = APIClient()
|
||||
_, token, alert_groups = alert_group_internal_api_setup
|
||||
_, _, new_alert_group, _ = alert_groups
|
||||
organization = new_alert_group.channel.organization
|
||||
user = make_user_for_organization(organization, role)
|
||||
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:alertgroup-unacknowledge", kwargs={"pk": new_alert_group.public_primary_key})
|
||||
|
||||
with patch(
|
||||
|
|
@ -718,19 +720,24 @@ def test_alert_group_unacknowledge_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_alert_group_resolve_permissions(
|
||||
make_user_for_organization, alert_group_internal_api_setup, make_user_auth_headers, role, expected_status
|
||||
alert_group_internal_api_setup,
|
||||
make_user_for_organization,
|
||||
make_user_auth_headers,
|
||||
role,
|
||||
expected_status,
|
||||
):
|
||||
client = APIClient()
|
||||
_, token, alert_groups = alert_group_internal_api_setup
|
||||
_, _, new_alert_group, _ = alert_groups
|
||||
organization = new_alert_group.channel.organization
|
||||
user = make_user_for_organization(organization, role)
|
||||
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:alertgroup-resolve", kwargs={"pk": new_alert_group.public_primary_key})
|
||||
|
||||
with patch(
|
||||
|
|
@ -747,19 +754,24 @@ def test_alert_group_resolve_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_alert_group_unresolve_permissions(
|
||||
make_user_for_organization, alert_group_internal_api_setup, make_user_auth_headers, role, expected_status
|
||||
alert_group_internal_api_setup,
|
||||
make_user_for_organization,
|
||||
make_user_auth_headers,
|
||||
role,
|
||||
expected_status,
|
||||
):
|
||||
client = APIClient()
|
||||
_, token, alert_groups = alert_group_internal_api_setup
|
||||
_, _, new_alert_group, _ = alert_groups
|
||||
organization = new_alert_group.channel.organization
|
||||
user = make_user_for_organization(organization, role)
|
||||
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:alertgroup-unresolve", kwargs={"pk": new_alert_group.public_primary_key})
|
||||
|
||||
with patch(
|
||||
|
|
@ -776,19 +788,24 @@ def test_alert_group_unresolve_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_alert_group_silence_permissions(
|
||||
make_user_for_organization, alert_group_internal_api_setup, make_user_auth_headers, role, expected_status
|
||||
alert_group_internal_api_setup,
|
||||
make_user_for_organization,
|
||||
make_user_auth_headers,
|
||||
role,
|
||||
expected_status,
|
||||
):
|
||||
client = APIClient()
|
||||
_, token, alert_groups = alert_group_internal_api_setup
|
||||
_, _, new_alert_group, _ = alert_groups
|
||||
organization = new_alert_group.channel.organization
|
||||
user = make_user_for_organization(organization, role)
|
||||
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:alertgroup-silence", kwargs={"pk": new_alert_group.public_primary_key})
|
||||
|
||||
with patch(
|
||||
|
|
@ -805,19 +822,24 @@ def test_alert_group_silence_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_alert_group_unsilence_permissions(
|
||||
make_user_for_organization, alert_group_internal_api_setup, make_user_auth_headers, role, expected_status
|
||||
alert_group_internal_api_setup,
|
||||
make_user_for_organization,
|
||||
make_user_auth_headers,
|
||||
role,
|
||||
expected_status,
|
||||
):
|
||||
client = APIClient()
|
||||
_, token, alert_groups = alert_group_internal_api_setup
|
||||
_, _, new_alert_group, _ = alert_groups
|
||||
organization = new_alert_group.channel.organization
|
||||
user = make_user_for_organization(organization, role)
|
||||
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:alertgroup-unsilence", kwargs={"pk": new_alert_group.public_primary_key})
|
||||
|
||||
with patch(
|
||||
|
|
@ -834,19 +856,24 @@ def test_alert_group_unsilence_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_alert_group_attach_permissions(
|
||||
make_user_for_organization, alert_group_internal_api_setup, make_user_auth_headers, role, expected_status
|
||||
alert_group_internal_api_setup,
|
||||
make_user_for_organization,
|
||||
make_user_auth_headers,
|
||||
role,
|
||||
expected_status,
|
||||
):
|
||||
client = APIClient()
|
||||
_, token, alert_groups = alert_group_internal_api_setup
|
||||
_, _, new_alert_group, _ = alert_groups
|
||||
organization = new_alert_group.channel.organization
|
||||
user = make_user_for_organization(organization, role)
|
||||
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:alertgroup-attach", kwargs={"pk": new_alert_group.public_primary_key})
|
||||
|
||||
with patch(
|
||||
|
|
@ -863,19 +890,24 @@ def test_alert_group_attach_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_alert_group_unattach_permissions(
|
||||
make_user_for_organization, alert_group_internal_api_setup, make_user_auth_headers, role, expected_status
|
||||
alert_group_internal_api_setup,
|
||||
make_user_for_organization,
|
||||
make_user_auth_headers,
|
||||
role,
|
||||
expected_status,
|
||||
):
|
||||
client = APIClient()
|
||||
_, token, alert_groups = alert_group_internal_api_setup
|
||||
_, _, new_alert_group, _ = alert_groups
|
||||
organization = new_alert_group.channel.organization
|
||||
user = make_user_for_organization(organization, role)
|
||||
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:alertgroup-unattach", kwargs={"pk": new_alert_group.public_primary_key})
|
||||
|
||||
with patch(
|
||||
|
|
@ -892,19 +924,24 @@ def test_alert_group_unattach_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_alert_group_list_permissions(
|
||||
make_user_for_organization, alert_group_internal_api_setup, make_user_auth_headers, role, expected_status
|
||||
alert_group_internal_api_setup,
|
||||
make_user_for_organization,
|
||||
make_user_auth_headers,
|
||||
role,
|
||||
expected_status,
|
||||
):
|
||||
client = APIClient()
|
||||
_, token, alert_groups = alert_group_internal_api_setup
|
||||
_, _, new_alert_group, _ = alert_groups
|
||||
organization = new_alert_group.channel.organization
|
||||
user = make_user_for_organization(organization, role)
|
||||
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:alertgroup-list")
|
||||
|
||||
with patch(
|
||||
|
|
@ -921,19 +958,24 @@ def test_alert_group_list_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_alert_group_stats_permissions(
|
||||
make_user_for_organization, alert_group_internal_api_setup, make_user_auth_headers, role, expected_status
|
||||
alert_group_internal_api_setup,
|
||||
make_user_for_organization,
|
||||
make_user_auth_headers,
|
||||
role,
|
||||
expected_status,
|
||||
):
|
||||
client = APIClient()
|
||||
_, token, alert_groups = alert_group_internal_api_setup
|
||||
_, _, new_alert_group, _ = alert_groups
|
||||
organization = new_alert_group.channel.organization
|
||||
user = make_user_for_organization(organization, role)
|
||||
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:alertgroup-stats")
|
||||
|
||||
with patch(
|
||||
|
|
@ -950,19 +992,24 @@ def test_alert_group_stats_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_alert_group_bulk_action_permissions(
|
||||
make_user_for_organization, alert_group_internal_api_setup, make_user_auth_headers, role, expected_status
|
||||
alert_group_internal_api_setup,
|
||||
make_user_for_organization,
|
||||
make_user_auth_headers,
|
||||
role,
|
||||
expected_status,
|
||||
):
|
||||
client = APIClient()
|
||||
_, token, alert_groups = alert_group_internal_api_setup
|
||||
_, _, new_alert_group, _ = alert_groups
|
||||
organization = new_alert_group.channel.organization
|
||||
user = make_user_for_organization(organization, role)
|
||||
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:alertgroup-bulk-action")
|
||||
|
||||
with patch(
|
||||
|
|
@ -977,19 +1024,24 @@ def test_alert_group_bulk_action_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_alert_group_filters_permissions(
|
||||
make_user_for_organization, alert_group_internal_api_setup, make_user_auth_headers, role, expected_status
|
||||
alert_group_internal_api_setup,
|
||||
make_user_for_organization,
|
||||
make_user_auth_headers,
|
||||
role,
|
||||
expected_status,
|
||||
):
|
||||
client = APIClient()
|
||||
_, token, alert_groups = alert_group_internal_api_setup
|
||||
_, _, new_alert_group, _ = alert_groups
|
||||
organization = new_alert_group.channel.organization
|
||||
user = make_user_for_organization(organization, role)
|
||||
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:alertgroup-filters")
|
||||
|
||||
with patch(
|
||||
|
|
@ -1006,19 +1058,24 @@ def test_alert_group_filters_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_alert_group_detail_permissions(
|
||||
make_user_for_organization, alert_group_internal_api_setup, make_user_auth_headers, role, expected_status
|
||||
alert_group_internal_api_setup,
|
||||
make_user_for_organization,
|
||||
make_user_auth_headers,
|
||||
role,
|
||||
expected_status,
|
||||
):
|
||||
client = APIClient()
|
||||
_, token, alert_groups = alert_group_internal_api_setup
|
||||
_, _, new_alert_group, _ = alert_groups
|
||||
organization = new_alert_group.channel.organization
|
||||
user = make_user_for_organization(organization, role)
|
||||
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:alertgroup-detail", kwargs={"pk": new_alert_group.public_primary_key})
|
||||
|
||||
with patch(
|
||||
|
|
@ -1032,10 +1089,7 @@ def test_alert_group_detail_permissions(
|
|||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_silence(
|
||||
alert_group_internal_api_setup,
|
||||
make_user_auth_headers,
|
||||
):
|
||||
def test_silence(alert_group_internal_api_setup, make_user_auth_headers):
|
||||
client = APIClient()
|
||||
user, token, alert_groups = alert_group_internal_api_setup
|
||||
_, _, new_alert_group, _ = alert_groups
|
||||
|
|
@ -1396,9 +1450,9 @@ def test_alert_group_status_field(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_alert_group_preview_template_permissions(
|
||||
|
|
@ -1414,6 +1468,7 @@ def test_alert_group_preview_template_permissions(
|
|||
alert_receive_channel = make_alert_receive_channel(organization)
|
||||
alert_group = make_alert_group(alert_receive_channel)
|
||||
make_alert(alert_group=alert_group, raw_request_data=alert_receive_channel.config.example_payload)
|
||||
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:alertgroup-preview-template", kwargs={"pk": alert_group.public_primary_key})
|
||||
|
||||
|
|
@ -1436,7 +1491,7 @@ def test_alert_group_preview_body_non_existent_template_var(
|
|||
make_alert_group,
|
||||
make_alert,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=Role.ADMIN)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token()
|
||||
alert_receive_channel = make_alert_receive_channel(organization)
|
||||
alert_group = make_alert_group(alert_receive_channel)
|
||||
make_alert(alert_group=alert_group, raw_request_data=alert_receive_channel.config.example_payload)
|
||||
|
|
@ -1459,7 +1514,7 @@ def test_alert_group_preview_body_invalid_template_syntax(
|
|||
make_alert_group,
|
||||
make_alert,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=Role.ADMIN)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token()
|
||||
alert_receive_channel = make_alert_receive_channel(organization)
|
||||
alert_group = make_alert_group(alert_receive_channel)
|
||||
make_alert(alert_group=alert_group, raw_request_data=alert_receive_channel.config.example_payload)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from rest_framework.response import Response
|
|||
from rest_framework.test import APIClient
|
||||
|
||||
from apps.alerts.models import AlertReceiveChannel, EscalationPolicy
|
||||
from common.constants.role import Role
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
|
|
@ -205,9 +205,9 @@ def test_integration_search(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_alert_receive_channel_create_permissions(
|
||||
|
|
@ -235,9 +235,9 @@ def test_alert_receive_channel_create_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_alert_receive_channel_update_permissions(
|
||||
|
|
@ -272,9 +272,9 @@ def test_alert_receive_channel_update_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_204_NO_CONTENT),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_204_NO_CONTENT),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_alert_receive_channel_delete_permissions(
|
||||
|
|
@ -303,7 +303,11 @@ def test_alert_receive_channel_delete_permissions(
|
|||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[(Role.ADMIN, status.HTTP_200_OK), (Role.EDITOR, status.HTTP_200_OK), (Role.VIEWER, status.HTTP_200_OK)],
|
||||
[
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_alert_receive_channel_list_permissions(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
|
|
@ -311,7 +315,7 @@ def test_alert_receive_channel_list_permissions(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
client = APIClient()
|
||||
|
||||
url = reverse("api-internal:alert_receive_channel-list")
|
||||
|
|
@ -330,7 +334,11 @@ def test_alert_receive_channel_list_permissions(
|
|||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[(Role.ADMIN, status.HTTP_200_OK), (Role.EDITOR, status.HTTP_200_OK), (Role.VIEWER, status.HTTP_200_OK)],
|
||||
[
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_alert_receive_channel_detail_permissions(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
|
|
@ -360,9 +368,9 @@ def test_alert_receive_channel_detail_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_alert_receive_channel_send_demo_alert_permissions(
|
||||
|
|
@ -395,9 +403,9 @@ def test_alert_receive_channel_send_demo_alert_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_alert_receive_channel_integration_options_permissions(
|
||||
|
|
@ -426,9 +434,9 @@ def test_alert_receive_channel_integration_options_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_alert_receive_channel_preview_template_permissions(
|
||||
|
|
@ -501,9 +509,9 @@ def test_alert_receive_channel_preview_template_require_notification_channel(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_alert_receive_channel_change_team_permissions(
|
||||
|
|
@ -597,9 +605,9 @@ def test_alert_receive_channel_change_team(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_alert_receive_channel_counters_permissions(
|
||||
|
|
@ -608,7 +616,7 @@ def test_alert_receive_channel_counters_permissions(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
client = APIClient()
|
||||
|
||||
url = reverse(
|
||||
|
|
@ -630,9 +638,9 @@ def test_alert_receive_channel_counters_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_alert_receive_channel_counters_per_integration_permissions(
|
||||
|
|
|
|||
|
|
@ -6,17 +6,17 @@ from rest_framework import status
|
|||
from rest_framework.response import Response
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
from apps.base.messaging import BaseMessagingBackend
|
||||
from common.constants.role import Role
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_alert_receive_channel_template_update_permissions(
|
||||
|
|
@ -48,9 +48,9 @@ def test_alert_receive_channel_template_update_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_alert_receive_channel_template_detail_permissions(
|
||||
|
|
@ -83,7 +83,7 @@ def test_alert_receive_channel_template_include_additional_backend_templates(
|
|||
make_user_auth_headers,
|
||||
make_alert_receive_channel,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=Role.ADMIN)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token()
|
||||
alert_receive_channel = make_alert_receive_channel(
|
||||
organization,
|
||||
messaging_backends_templates={"TESTONLY": {"title": "the-title", "message": "the-message", "image_url": "url"}},
|
||||
|
|
@ -109,7 +109,7 @@ def test_alert_receive_channel_template_include_additional_backend_templates_usi
|
|||
make_user_auth_headers,
|
||||
make_alert_receive_channel,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=Role.ADMIN)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token()
|
||||
alert_receive_channel = make_alert_receive_channel(organization, messaging_backends_templates=None)
|
||||
client = APIClient()
|
||||
|
||||
|
|
@ -138,7 +138,7 @@ def test_update_alert_receive_channel_backend_template_invalid_template(
|
|||
make_user_auth_headers,
|
||||
make_alert_receive_channel,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=Role.ADMIN)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token()
|
||||
alert_receive_channel = make_alert_receive_channel(organization, messaging_backends_templates=None)
|
||||
client = APIClient()
|
||||
|
||||
|
|
@ -160,7 +160,7 @@ def test_update_alert_receive_channel_backend_template_invalid_url(
|
|||
make_user_auth_headers,
|
||||
make_alert_receive_channel,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=Role.ADMIN)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token()
|
||||
alert_receive_channel = make_alert_receive_channel(organization, messaging_backends_templates=None)
|
||||
client = APIClient()
|
||||
|
||||
|
|
@ -182,7 +182,7 @@ def test_update_alert_receive_channel_backend_template_empty_values_allowed(
|
|||
make_user_auth_headers,
|
||||
make_alert_receive_channel,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=Role.ADMIN)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token()
|
||||
alert_receive_channel = make_alert_receive_channel(organization, messaging_backends_templates=None)
|
||||
client = APIClient()
|
||||
|
||||
|
|
@ -208,7 +208,7 @@ def test_update_alert_receive_channel_backend_template_update_values(
|
|||
make_user_auth_headers,
|
||||
make_alert_receive_channel,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=Role.ADMIN)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token()
|
||||
alert_receive_channel = make_alert_receive_channel(
|
||||
organization,
|
||||
messaging_backends_templates={
|
||||
|
|
@ -249,7 +249,7 @@ def test_preview_alert_receive_channel_backend_templater(
|
|||
make_alert_group,
|
||||
make_alert,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=Role.ADMIN)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token()
|
||||
alert_receive_channel = make_alert_receive_channel(organization)
|
||||
default_channel_filter = make_channel_filter(alert_receive_channel, is_default=True)
|
||||
alert_group = make_alert_group(alert_receive_channel, channel_filter=default_channel_filter)
|
||||
|
|
@ -280,7 +280,7 @@ def test_update_alert_receive_channel_templates(
|
|||
# set url here to pass *_url templates validation
|
||||
return "https://grafana.com"
|
||||
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=Role.ADMIN)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token()
|
||||
alert_receive_channel = make_alert_receive_channel(
|
||||
organization,
|
||||
messaging_backends_templates={"TESTONLY": {"title": "the-title", "message": "the-message", "image_url": "url"}},
|
||||
|
|
|
|||
|
|
@ -6,21 +6,20 @@ from rest_framework import status
|
|||
from rest_framework.response import Response
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from common.constants.role import Role
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_channel_filter_create_permissions(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
make_alert_receive_channel,
|
||||
make_user_auth_headers,
|
||||
role,
|
||||
expected_status,
|
||||
|
|
@ -45,9 +44,9 @@ def test_channel_filter_create_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_channel_filter_update_permissions(
|
||||
|
|
@ -83,7 +82,11 @@ def test_channel_filter_update_permissions(
|
|||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[(Role.ADMIN, status.HTTP_200_OK), (Role.EDITOR, status.HTTP_200_OK), (Role.VIEWER, status.HTTP_200_OK)],
|
||||
[
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_channel_filter_list_permissions(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
|
|
@ -114,7 +117,11 @@ def test_channel_filter_list_permissions(
|
|||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[(Role.ADMIN, status.HTTP_200_OK), (Role.EDITOR, status.HTTP_200_OK), (Role.VIEWER, status.HTTP_200_OK)],
|
||||
[
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_channel_filter_retrieve_permissions(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
|
|
@ -146,9 +153,9 @@ def test_channel_filter_retrieve_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_204_NO_CONTENT),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_204_NO_CONTENT),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_channel_filter_delete_permissions(
|
||||
|
|
@ -181,9 +188,9 @@ def test_channel_filter_delete_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_channel_filter_move_to_position_permissions(
|
||||
|
|
@ -216,9 +223,9 @@ def test_channel_filter_move_to_position_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_alert_receive_channel_send_demo_alert_permissions(
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from rest_framework.response import Response
|
|||
from rest_framework.test import APIClient
|
||||
|
||||
from apps.alerts.models import CustomButton
|
||||
from common.constants.role import Role
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
|
||||
TEST_URL = "https://amixr.io"
|
||||
|
||||
|
|
@ -275,14 +275,13 @@ def test_delete_custom_button(custom_button_internal_api_setup, make_user_auth_h
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_custom_button_create_permissions(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
make_custom_action,
|
||||
make_user_auth_headers,
|
||||
role,
|
||||
expected_status,
|
||||
|
|
@ -307,9 +306,9 @@ def test_custom_button_create_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_custom_button_update_permissions(
|
||||
|
|
@ -343,7 +342,11 @@ def test_custom_button_update_permissions(
|
|||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[(Role.ADMIN, status.HTTP_200_OK), (Role.EDITOR, status.HTTP_200_OK), (Role.VIEWER, status.HTTP_200_OK)],
|
||||
[
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_custom_button_list_permissions(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
|
|
@ -372,7 +375,11 @@ def test_custom_button_list_permissions(
|
|||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[(Role.ADMIN, status.HTTP_200_OK), (Role.EDITOR, status.HTTP_200_OK), (Role.VIEWER, status.HTTP_200_OK)],
|
||||
[
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_custom_button_retrieve_permissions(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
|
|
@ -402,9 +409,9 @@ def test_custom_button_retrieve_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_204_NO_CONTENT),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_204_NO_CONTENT),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_custom_button_delete_permissions(
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ def test_delete_escalation_chain(escalation_chain_internal_api_setup, make_user_
|
|||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_update_escalation_chain(escalation_chain_internal_api_setup, make_user_auth_headers, make_organization):
|
||||
def test_update_escalation_chain(escalation_chain_internal_api_setup, make_user_auth_headers):
|
||||
user, token, escalation_chain = escalation_chain_internal_api_setup
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:escalation_chain-detail", kwargs={"pk": escalation_chain.public_primary_key})
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from rest_framework.response import Response
|
|||
from rest_framework.test import APIClient
|
||||
|
||||
from apps.alerts.models import EscalationPolicy
|
||||
from common.constants.role import Role
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
|
|
@ -93,9 +93,9 @@ def test_move_to_position(escalation_policy_internal_api_setup, make_user_auth_h
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_escalation_policy_create_permissions(
|
||||
|
|
@ -130,9 +130,9 @@ def test_escalation_policy_create_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_escalation_policy_update_permissions(
|
||||
|
|
@ -171,9 +171,9 @@ def test_escalation_policy_update_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_escalation_policy_list_permissions(
|
||||
|
|
@ -208,9 +208,9 @@ def test_escalation_policy_list_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_escalation_policy_retrieve_permissions(
|
||||
|
|
@ -245,9 +245,9 @@ def test_escalation_policy_retrieve_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_204_NO_CONTENT),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_204_NO_CONTENT),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_escalation_policy_delete_permissions(
|
||||
|
|
@ -282,9 +282,9 @@ def test_escalation_policy_delete_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_escalation_policy_escalation_options_permissions(
|
||||
|
|
@ -319,9 +319,9 @@ def test_escalation_policy_escalation_options_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_escalation_policy_delay_options_permissions(
|
||||
|
|
@ -357,9 +357,9 @@ def test_escalation_policy_delay_options_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_escalation_policy_move_to_position_permissions(
|
||||
|
|
|
|||
|
|
@ -3,16 +3,16 @@ from django.urls import reverse
|
|||
from rest_framework import status
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from common.constants.role import Role
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_terraform_gitops_permissions(
|
||||
|
|
@ -22,7 +22,7 @@ def test_terraform_gitops_permissions(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
make_escalation_chain(organization)
|
||||
|
||||
client = APIClient()
|
||||
|
|
@ -38,15 +38,15 @@ def test_terraform_gitops_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_terraform_state_permissions(
|
||||
make_organization_and_user_with_plugin_token, make_user_auth_headers, role, expected_status
|
||||
):
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
client = APIClient()
|
||||
|
||||
url = reverse("api-internal:terraform_imports")
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ from rest_framework import status
|
|||
from rest_framework.response import Response
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
from apps.heartbeat.models import IntegrationHeartBeat
|
||||
from common.constants.role import Role
|
||||
|
||||
MOCK_LAST_HEARTBEAT_TIME_VERBAL = "a moment"
|
||||
|
||||
|
|
@ -151,7 +151,7 @@ def test_create_empty_alert_receive_channel_integration_heartbeat(
|
|||
integration_heartbeat_internal_api_setup,
|
||||
make_user_auth_headers,
|
||||
):
|
||||
user, token, alert_receive_channel, integration_heartbeat = integration_heartbeat_internal_api_setup
|
||||
user, token, _, _ = integration_heartbeat_internal_api_setup
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:integration_heartbeat-list")
|
||||
|
||||
|
|
@ -185,9 +185,39 @@ def test_update_integration_heartbeat(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_integration_heartbeat_create_permissions(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
make_user_auth_headers,
|
||||
role,
|
||||
expected_status,
|
||||
):
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
client = APIClient()
|
||||
|
||||
url = reverse("api-internal:integration_heartbeat-list")
|
||||
|
||||
with patch(
|
||||
"apps.api.views.integration_heartbeat.IntegrationHeartBeatView.create",
|
||||
return_value=Response(
|
||||
status=status.HTTP_200_OK,
|
||||
),
|
||||
):
|
||||
response = client.post(url, format="json", **make_user_auth_headers(user, token))
|
||||
assert response.status_code == expected_status
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_integration_heartbeat_update_permissions(
|
||||
|
|
@ -223,7 +253,11 @@ def test_integration_heartbeat_update_permissions(
|
|||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[(Role.ADMIN, status.HTTP_200_OK), (Role.EDITOR, status.HTTP_200_OK), (Role.VIEWER, status.HTTP_200_OK)],
|
||||
[
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_integration_heartbeat_list_permissions(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
|
|
@ -255,9 +289,40 @@ def test_integration_heartbeat_list_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_integration_heartbeat_timeout_options_permissions(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
make_user_auth_headers,
|
||||
role,
|
||||
expected_status,
|
||||
):
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
client = APIClient()
|
||||
|
||||
url = reverse("api-internal:integration_heartbeat-timeout-options")
|
||||
|
||||
with patch(
|
||||
"apps.api.views.integration_heartbeat.IntegrationHeartBeatView.timeout_options",
|
||||
return_value=Response(
|
||||
status=status.HTTP_200_OK,
|
||||
),
|
||||
):
|
||||
response = client.get(url, format="json", **make_user_auth_headers(user, token))
|
||||
|
||||
assert response.status_code == expected_status
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_integration_heartbeat_retrieve_permissions(
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ from rest_framework.test import APIClient
|
|||
from apps.alerts.models import AlertReceiveChannel
|
||||
from apps.user_management.models import Organization
|
||||
|
||||
# TODO: should probably modify these tests to take into account new rbac permissions
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def maintenance_internal_api_setup(
|
||||
|
|
@ -23,7 +25,7 @@ def maintenance_internal_api_setup(
|
|||
def test_start_maintenance_integration(
|
||||
maintenance_internal_api_setup, mock_start_disable_maintenance_task, make_user_auth_headers
|
||||
):
|
||||
token, organization, user, alert_receive_channel = maintenance_internal_api_setup
|
||||
token, _, user, alert_receive_channel = maintenance_internal_api_setup
|
||||
client = APIClient()
|
||||
|
||||
url = reverse("api-internal:start_maintenance")
|
||||
|
|
@ -50,7 +52,7 @@ def test_stop_maintenance_integration(
|
|||
mock_start_disable_maintenance_task,
|
||||
make_user_auth_headers,
|
||||
):
|
||||
token, organization, user, alert_receive_channel = maintenance_internal_api_setup
|
||||
token, _, user, alert_receive_channel = maintenance_internal_api_setup
|
||||
client = APIClient()
|
||||
mode = AlertReceiveChannel.MAINTENANCE
|
||||
duration = AlertReceiveChannel.DURATION_ONE_HOUR.seconds
|
||||
|
|
@ -161,7 +163,7 @@ def test_maintenances_list(
|
|||
def test_empty_maintenances_list(
|
||||
maintenance_internal_api_setup, mock_start_disable_maintenance_task, make_user_auth_headers
|
||||
):
|
||||
token, organization, user, alert_receive_channel = maintenance_internal_api_setup
|
||||
token, _, user, alert_receive_channel = maintenance_internal_api_setup
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:maintenance")
|
||||
response = client.get(url, format="json", **make_user_auth_headers(user, token))
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ from rest_framework import status
|
|||
from rest_framework.response import Response
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
from apps.schedules.models import CustomOnCallShift, OnCallSchedule, OnCallScheduleWeb
|
||||
from common.constants.role import Role
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
|
|
@ -26,7 +26,7 @@ def on_call_shift_internal_api_setup(
|
|||
|
||||
@pytest.mark.django_db
|
||||
def test_create_on_call_shift_rotation(on_call_shift_internal_api_setup, make_user_auth_headers):
|
||||
token, user1, user2, organization, schedule = on_call_shift_internal_api_setup
|
||||
token, user1, user2, _, schedule = on_call_shift_internal_api_setup
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:oncall_shifts-list")
|
||||
start_date = timezone.now().replace(microsecond=0, tzinfo=None)
|
||||
|
|
@ -58,7 +58,7 @@ def test_create_on_call_shift_rotation(on_call_shift_internal_api_setup, make_us
|
|||
|
||||
@pytest.mark.django_db
|
||||
def test_create_on_call_shift_override(on_call_shift_internal_api_setup, make_user_auth_headers):
|
||||
token, user1, user2, organization, schedule = on_call_shift_internal_api_setup
|
||||
token, user1, user2, _, schedule = on_call_shift_internal_api_setup
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:oncall_shifts-list")
|
||||
start_date = timezone.now().replace(microsecond=0, tzinfo=None)
|
||||
|
|
@ -98,7 +98,7 @@ def test_get_on_call_shift(
|
|||
make_on_call_shift,
|
||||
make_user_auth_headers,
|
||||
):
|
||||
token, user1, user2, organization, schedule = on_call_shift_internal_api_setup
|
||||
token, user1, user2, _, schedule = on_call_shift_internal_api_setup
|
||||
|
||||
client = APIClient()
|
||||
start_date = timezone.now().replace(microsecond=0)
|
||||
|
|
@ -144,7 +144,7 @@ def test_list_on_call_shift(
|
|||
make_on_call_shift,
|
||||
make_user_auth_headers,
|
||||
):
|
||||
token, user1, user2, organization, schedule = on_call_shift_internal_api_setup
|
||||
token, user1, user2, _, schedule = on_call_shift_internal_api_setup
|
||||
|
||||
client = APIClient()
|
||||
start_date = timezone.now().replace(microsecond=0)
|
||||
|
|
@ -270,7 +270,7 @@ def test_update_future_on_call_shift(
|
|||
make_user_auth_headers,
|
||||
):
|
||||
"""Test updating the shift that has not started (rotation_start > now)"""
|
||||
token, user1, user2, organization, schedule = on_call_shift_internal_api_setup
|
||||
token, user1, _, _, schedule = on_call_shift_internal_api_setup
|
||||
|
||||
client = APIClient()
|
||||
start_date = (timezone.now() + timezone.timedelta(days=1)).replace(microsecond=0)
|
||||
|
|
@ -337,7 +337,7 @@ def test_update_started_on_call_shift(
|
|||
):
|
||||
"""Test updating the shift that has started (rotation_start < now)"""
|
||||
|
||||
token, user1, user2, organization, schedule = on_call_shift_internal_api_setup
|
||||
token, user1, _, _, schedule = on_call_shift_internal_api_setup
|
||||
|
||||
client = APIClient()
|
||||
start_date = (timezone.now() - timezone.timedelta(hours=1)).replace(microsecond=0)
|
||||
|
|
@ -409,7 +409,7 @@ def test_update_old_on_call_shift_with_future_version(
|
|||
make_user_auth_headers,
|
||||
):
|
||||
"""Test updating the shift that has the newer version (updated_shift is not None)"""
|
||||
token, user1, user2, organization, schedule = on_call_shift_internal_api_setup
|
||||
token, user1, _, _, schedule = on_call_shift_internal_api_setup
|
||||
|
||||
client = APIClient()
|
||||
now = timezone.now().replace(microsecond=0)
|
||||
|
|
@ -498,7 +498,7 @@ def test_update_started_on_call_shift_title(
|
|||
):
|
||||
"""Test updating the title for the shift that has started (rotation_start < now)"""
|
||||
|
||||
token, user1, user2, organization, schedule = on_call_shift_internal_api_setup
|
||||
token, user1, _, _, schedule = on_call_shift_internal_api_setup
|
||||
|
||||
client = APIClient()
|
||||
start_date = (timezone.now() - timezone.timedelta(hours=1)).replace(microsecond=0)
|
||||
|
|
@ -560,7 +560,7 @@ def test_delete_started_on_call_shift(
|
|||
):
|
||||
"""Test deleting the shift that has started (rotation_start < now)"""
|
||||
|
||||
token, user1, user2, organization, schedule = on_call_shift_internal_api_setup
|
||||
token, user1, _, _, schedule = on_call_shift_internal_api_setup
|
||||
|
||||
client = APIClient()
|
||||
start_date = (timezone.now() - timezone.timedelta(hours=1)).replace(microsecond=0)
|
||||
|
|
@ -598,7 +598,7 @@ def test_delete_future_on_call_shift(
|
|||
):
|
||||
"""Test deleting the shift that has not started (rotation_start > now)"""
|
||||
|
||||
token, user1, user2, organization, schedule = on_call_shift_internal_api_setup
|
||||
token, user1, _, _, schedule = on_call_shift_internal_api_setup
|
||||
|
||||
client = APIClient()
|
||||
start_date = (timezone.now() + timezone.timedelta(days=1)).replace(microsecond=0)
|
||||
|
|
@ -631,7 +631,7 @@ def test_create_on_call_shift_invalid_data_rotation_start(
|
|||
on_call_shift_internal_api_setup,
|
||||
make_user_auth_headers,
|
||||
):
|
||||
token, user1, user2, organization, schedule = on_call_shift_internal_api_setup
|
||||
token, user1, _, _, schedule = on_call_shift_internal_api_setup
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:oncall_shifts-list")
|
||||
start_date = timezone.now().replace(microsecond=0, tzinfo=None)
|
||||
|
|
@ -660,7 +660,7 @@ def test_create_on_call_shift_invalid_data_rotation_start(
|
|||
|
||||
@pytest.mark.django_db
|
||||
def test_create_on_call_shift_invalid_data_until(on_call_shift_internal_api_setup, make_user_auth_headers):
|
||||
token, user1, user2, organization, schedule = on_call_shift_internal_api_setup
|
||||
token, user1, user2, _, schedule = on_call_shift_internal_api_setup
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:oncall_shifts-list")
|
||||
start_date = timezone.now().replace(microsecond=0, tzinfo=None)
|
||||
|
|
@ -713,7 +713,7 @@ def test_create_on_call_shift_invalid_data_until(on_call_shift_internal_api_setu
|
|||
|
||||
@pytest.mark.django_db
|
||||
def test_create_on_call_shift_invalid_data_by_day(on_call_shift_internal_api_setup, make_user_auth_headers):
|
||||
token, user1, user2, organization, schedule = on_call_shift_internal_api_setup
|
||||
token, user1, _, _, schedule = on_call_shift_internal_api_setup
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:oncall_shifts-list")
|
||||
start_date = timezone.now().replace(microsecond=0, tzinfo=None)
|
||||
|
|
@ -763,7 +763,7 @@ def test_create_on_call_shift_invalid_data_by_day(on_call_shift_internal_api_set
|
|||
|
||||
@pytest.mark.django_db
|
||||
def test_create_on_call_shift_invalid_data_interval(on_call_shift_internal_api_setup, make_user_auth_headers):
|
||||
token, user1, user2, organization, schedule = on_call_shift_internal_api_setup
|
||||
token, user1, _, _, schedule = on_call_shift_internal_api_setup
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:oncall_shifts-list")
|
||||
start_date = timezone.now().replace(microsecond=0, tzinfo=None)
|
||||
|
|
@ -813,7 +813,7 @@ def test_create_on_call_shift_invalid_data_interval(on_call_shift_internal_api_s
|
|||
|
||||
@pytest.mark.django_db
|
||||
def test_create_on_call_shift_invalid_data_shift_end(on_call_shift_internal_api_setup, make_user_auth_headers):
|
||||
token, user1, user2, organization, schedule = on_call_shift_internal_api_setup
|
||||
token, user1, _, _, schedule = on_call_shift_internal_api_setup
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:oncall_shifts-list")
|
||||
start_date = timezone.now().replace(microsecond=0, tzinfo=None)
|
||||
|
|
@ -866,7 +866,7 @@ def test_create_on_call_shift_invalid_data_rolling_users(
|
|||
on_call_shift_internal_api_setup,
|
||||
make_user_auth_headers,
|
||||
):
|
||||
token, user1, user2, organization, schedule = on_call_shift_internal_api_setup
|
||||
token, user1, user2, _, schedule = on_call_shift_internal_api_setup
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:oncall_shifts-list")
|
||||
start_date = timezone.now().replace(microsecond=0, tzinfo=None)
|
||||
|
|
@ -894,7 +894,7 @@ def test_create_on_call_shift_invalid_data_rolling_users(
|
|||
|
||||
@pytest.mark.django_db
|
||||
def test_create_on_call_shift_override_invalid_data(on_call_shift_internal_api_setup, make_user_auth_headers):
|
||||
token, user1, user2, organization, schedule = on_call_shift_internal_api_setup
|
||||
token, user1, _, _, schedule = on_call_shift_internal_api_setup
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:oncall_shifts-list")
|
||||
start_date = timezone.now().replace(microsecond=0, tzinfo=None)
|
||||
|
|
@ -925,9 +925,9 @@ def test_create_on_call_shift_override_invalid_data(on_call_shift_internal_api_s
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_201_CREATED),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_201_CREATED),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_on_call_shift_create_permissions(
|
||||
|
|
@ -936,7 +936,7 @@ def test_on_call_shift_create_permissions(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
|
||||
client = APIClient()
|
||||
|
||||
|
|
@ -957,9 +957,9 @@ def test_on_call_shift_create_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_on_call_shift_update_permissions(
|
||||
|
|
@ -1005,9 +1005,9 @@ def test_on_call_shift_update_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_on_call_shift_list_permissions(
|
||||
|
|
@ -1016,7 +1016,7 @@ def test_on_call_shift_list_permissions(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
client = APIClient()
|
||||
|
||||
url = reverse("api-internal:oncall_shifts-list")
|
||||
|
|
@ -1036,9 +1036,9 @@ def test_on_call_shift_list_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_on_call_shift_retrieve_permissions(
|
||||
|
|
@ -1079,9 +1079,9 @@ def test_on_call_shift_retrieve_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_204_NO_CONTENT),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_204_NO_CONTENT),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_on_call_shift_delete_permissions(
|
||||
|
|
@ -1122,9 +1122,9 @@ def test_on_call_shift_delete_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_on_call_shift_frequency_options_permissions(
|
||||
|
|
@ -1153,9 +1153,9 @@ def test_on_call_shift_frequency_options_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_on_call_shift_days_options_permissions(
|
||||
|
|
@ -1184,9 +1184,9 @@ def test_on_call_shift_days_options_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_on_call_shift_preview_permissions(
|
||||
|
|
|
|||
|
|
@ -6,30 +6,25 @@ from rest_framework import status
|
|||
from rest_framework.response import Response
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from common.constants.role import Role
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_current_team_retrieve_permissions(
|
||||
make_organization,
|
||||
make_user_for_organization,
|
||||
make_token_for_organization,
|
||||
make_organization_and_user_with_plugin_token,
|
||||
make_user_auth_headers,
|
||||
role,
|
||||
expected_status,
|
||||
):
|
||||
org = make_organization()
|
||||
tester = make_user_for_organization(org, role=role)
|
||||
_, token = make_token_for_organization(org)
|
||||
|
||||
_, tester, token = make_organization_and_user_with_plugin_token(role)
|
||||
client = APIClient()
|
||||
|
||||
url = reverse("api-internal:api-current-team")
|
||||
|
|
@ -48,23 +43,18 @@ def test_current_team_retrieve_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_current_team_update_permissions(
|
||||
make_organization,
|
||||
make_user_for_organization,
|
||||
make_token_for_organization,
|
||||
make_organization_and_user_with_plugin_token,
|
||||
make_user_auth_headers,
|
||||
role,
|
||||
expected_status,
|
||||
):
|
||||
org = make_organization()
|
||||
tester = make_user_for_organization(org, role=role)
|
||||
_, token = make_token_for_organization(org)
|
||||
|
||||
_, tester, token = make_organization_and_user_with_plugin_token(role)
|
||||
client = APIClient()
|
||||
|
||||
url = reverse("api-internal:api-current-team")
|
||||
|
|
@ -84,9 +74,9 @@ def test_current_team_update_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_current_team_get_telegram_verification_code_permissions(
|
||||
|
|
@ -95,8 +85,7 @@ def test_current_team_get_telegram_verification_code_permissions(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, tester, token = make_organization_and_user_with_plugin_token(role)
|
||||
|
||||
_, tester, token = make_organization_and_user_with_plugin_token(role)
|
||||
client = APIClient()
|
||||
|
||||
url = reverse("api-internal:api-get-telegram-verification-code")
|
||||
|
|
@ -109,9 +98,9 @@ def test_current_team_get_telegram_verification_code_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_current_team_get_channel_verification_code_permissions(
|
||||
|
|
@ -120,8 +109,7 @@ def test_current_team_get_channel_verification_code_permissions(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, tester, token = make_organization_and_user_with_plugin_token(role)
|
||||
|
||||
_, tester, token = make_organization_and_user_with_plugin_token(role)
|
||||
client = APIClient()
|
||||
|
||||
url = reverse("api-internal:api-get-channel-verification-code") + "?backend=TESTONLY"
|
||||
|
|
@ -135,8 +123,7 @@ def test_current_team_get_channel_verification_code_ok(
|
|||
make_organization_and_user_with_plugin_token,
|
||||
make_user_auth_headers,
|
||||
):
|
||||
organization, tester, token = make_organization_and_user_with_plugin_token(Role.ADMIN)
|
||||
|
||||
organization, tester, token = make_organization_and_user_with_plugin_token()
|
||||
client = APIClient()
|
||||
|
||||
url = reverse("api-internal:api-get-channel-verification-code") + "?backend=TESTONLY"
|
||||
|
|
@ -156,8 +143,7 @@ def test_current_team_get_channel_verification_code_invalid(
|
|||
make_organization_and_user_with_plugin_token,
|
||||
make_user_auth_headers,
|
||||
):
|
||||
organization, tester, token = make_organization_and_user_with_plugin_token(Role.ADMIN)
|
||||
|
||||
_, tester, token = make_organization_and_user_with_plugin_token()
|
||||
client = APIClient()
|
||||
|
||||
url = reverse("api-internal:api-get-channel-verification-code") + "?backend=INVALID"
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from rest_framework.response import Response
|
|||
from rest_framework.test import APIClient
|
||||
|
||||
from apps.alerts.models import ResolutionNote
|
||||
from common.constants.role import Role
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
|
@ -212,9 +212,9 @@ def test_delete_resolution_note(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_resolution_note_create_permissions(
|
||||
|
|
@ -224,7 +224,7 @@ def test_resolution_note_create_permissions(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
client = APIClient()
|
||||
|
||||
url = reverse("api-internal:resolution_note-list")
|
||||
|
|
@ -245,9 +245,9 @@ def test_resolution_note_create_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_resolution_note_update_permissions(
|
||||
|
|
@ -260,7 +260,7 @@ def test_resolution_note_update_permissions(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
alert_receive_channel = make_alert_receive_channel(organization)
|
||||
alert_group = make_alert_group(alert_receive_channel)
|
||||
resolution_note = make_resolution_note(
|
||||
|
|
@ -289,9 +289,9 @@ def test_resolution_note_update_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_204_NO_CONTENT),
|
||||
(Role.EDITOR, status.HTTP_204_NO_CONTENT),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_204_NO_CONTENT),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_204_NO_CONTENT),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_resolution_note_delete_permissions(
|
||||
|
|
@ -304,7 +304,7 @@ def test_resolution_note_delete_permissions(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
alert_receive_channel = make_alert_receive_channel(organization)
|
||||
alert_group = make_alert_group(alert_receive_channel)
|
||||
resolution_note = make_resolution_note(
|
||||
|
|
@ -331,9 +331,9 @@ def test_resolution_note_delete_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_resolution_note_list_permissions(
|
||||
|
|
@ -343,7 +343,7 @@ def test_resolution_note_list_permissions(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
client = APIClient()
|
||||
|
||||
url = reverse("api-internal:resolution_note-list")
|
||||
|
|
@ -363,9 +363,9 @@ def test_resolution_note_list_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_resolution_note_detail_permissions(
|
||||
|
|
@ -378,7 +378,7 @@ def test_resolution_note_detail_permissions(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
alert_receive_channel = make_alert_receive_channel(organization)
|
||||
alert_group = make_alert_group(alert_receive_channel)
|
||||
resolution_note = make_resolution_note(
|
||||
|
|
|
|||
115
engine/apps/api/tests/test_public_api_tokens.py
Normal file
115
engine/apps/api/tests/test_public_api_tokens.py
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
import pytest
|
||||
from django.urls import reverse
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_public_api_tokens_retrieve_permissions(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
make_user_auth_headers,
|
||||
make_public_api_token,
|
||||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, plugin_token = make_organization_and_user_with_plugin_token(role)
|
||||
api_token, _ = make_public_api_token(user, organization)
|
||||
client = APIClient()
|
||||
|
||||
url = reverse("api-internal:api_token-detail", kwargs={"pk": api_token.id})
|
||||
response = client.get(url, format="json", **make_user_auth_headers(user, plugin_token))
|
||||
|
||||
assert response.status_code == expected_status
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_public_api_tokens_list_permissions(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
make_user_auth_headers,
|
||||
make_public_api_token,
|
||||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, plugin_token = make_organization_and_user_with_plugin_token(role)
|
||||
make_public_api_token(user, organization)
|
||||
client = APIClient()
|
||||
|
||||
url = reverse("api-internal:api_token-list")
|
||||
response = client.get(url, format="json", **make_user_auth_headers(user, plugin_token))
|
||||
|
||||
assert response.status_code == expected_status
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_201_CREATED),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_201_CREATED),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_public_api_tokens_create_permissions(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
make_user_auth_headers,
|
||||
role,
|
||||
expected_status,
|
||||
):
|
||||
_, user, plugin_token = make_organization_and_user_with_plugin_token(role)
|
||||
client = APIClient()
|
||||
|
||||
url = reverse("api-internal:api_token-list")
|
||||
response = client.post(
|
||||
url,
|
||||
data={
|
||||
"name": "helloooo",
|
||||
},
|
||||
format="json",
|
||||
**make_user_auth_headers(user, plugin_token),
|
||||
)
|
||||
|
||||
assert response.status_code == expected_status
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_204_NO_CONTENT),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_204_NO_CONTENT),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_public_api_tokens_delete_permissions(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
make_user_auth_headers,
|
||||
make_public_api_token,
|
||||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, plugin_token = make_organization_and_user_with_plugin_token(role)
|
||||
api_token, _ = make_public_api_token(user, organization)
|
||||
client = APIClient()
|
||||
|
||||
url = reverse("api-internal:api_token-detail", kwargs={"pk": api_token.id})
|
||||
response = client.delete(url, format="json", **make_user_auth_headers(user, plugin_token))
|
||||
|
||||
assert response.status_code == expected_status
|
||||
|
|
@ -3,9 +3,9 @@ from django.urls import reverse
|
|||
from rest_framework import status
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
from apps.auth_token.models import ScheduleExportAuthToken
|
||||
from apps.schedules.models import OnCallScheduleICal
|
||||
from common.constants.role import Role
|
||||
|
||||
ICAL_URL = "https://calendar.google.com/calendar/ical/amixr.io_37gttuakhrtr75ano72p69rt78%40group.calendar.google.com/private-1d00a680ba5be7426c3eb3ef1616e26d/basic.ics" # noqa
|
||||
|
||||
|
|
@ -14,9 +14,9 @@ ICAL_URL = "https://calendar.google.com/calendar/ical/amixr.io_37gttuakhrtr75ano
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_get_schedule_export_token(
|
||||
|
|
@ -26,8 +26,7 @@ def test_get_schedule_export_token(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
schedule = make_schedule(
|
||||
organization,
|
||||
schedule_class=OnCallScheduleICal,
|
||||
|
|
@ -50,9 +49,9 @@ def test_get_schedule_export_token(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_404_NOT_FOUND),
|
||||
(Role.EDITOR, status.HTTP_404_NOT_FOUND),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_404_NOT_FOUND),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_404_NOT_FOUND),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_schedule_export_token_not_found(
|
||||
|
|
@ -62,8 +61,7 @@ def test_schedule_export_token_not_found(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
schedule = make_schedule(
|
||||
organization,
|
||||
schedule_class=OnCallScheduleICal,
|
||||
|
|
@ -84,9 +82,9 @@ def test_schedule_export_token_not_found(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_201_CREATED),
|
||||
(Role.EDITOR, status.HTTP_201_CREATED),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_201_CREATED),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_201_CREATED),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_schedule_create_export_token(
|
||||
|
|
@ -96,8 +94,7 @@ def test_schedule_create_export_token(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
schedule = make_schedule(
|
||||
organization,
|
||||
schedule_class=OnCallScheduleICal,
|
||||
|
|
@ -118,9 +115,9 @@ def test_schedule_create_export_token(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_204_NO_CONTENT),
|
||||
(Role.EDITOR, status.HTTP_204_NO_CONTENT),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_204_NO_CONTENT),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_204_NO_CONTENT),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_schedule_delete_export_token(
|
||||
|
|
@ -130,8 +127,7 @@ def test_schedule_delete_export_token(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
schedule = make_schedule(
|
||||
organization,
|
||||
schedule_class=OnCallScheduleICal,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ from rest_framework.serializers import ValidationError
|
|||
from rest_framework.test import APIClient
|
||||
|
||||
from apps.alerts.models import EscalationPolicy
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
from apps.schedules.ical_utils import memoized_users_in_ical
|
||||
from apps.schedules.models import (
|
||||
CustomOnCallShift,
|
||||
|
|
@ -18,7 +19,6 @@ from apps.schedules.models import (
|
|||
OnCallScheduleICal,
|
||||
OnCallScheduleWeb,
|
||||
)
|
||||
from common.constants.role import Role
|
||||
|
||||
ICAL_URL = "https://calendar.google.com/calendar/ical/amixr.io_37gttuakhrtr75ano72p69rt78%40group.calendar.google.com/private-1d00a680ba5be7426c3eb3ef1616e26d/basic.ics"
|
||||
|
||||
|
|
@ -1062,7 +1062,7 @@ def test_merging_same_shift_events(
|
|||
|
||||
user_a = make_user_for_organization(organization)
|
||||
user_b = make_user_for_organization(organization)
|
||||
user_c = make_user_for_organization(organization, role=Role.VIEWER)
|
||||
user_c = make_user_for_organization(organization, role=LegacyAccessControlRole.VIEWER)
|
||||
# clear users pks <-> organization cache (persisting between tests)
|
||||
memoized_users_in_ical.cache_clear()
|
||||
|
||||
|
|
@ -1158,9 +1158,9 @@ def test_filter_events_invalid_type(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_schedule_create_permissions(
|
||||
|
|
@ -1170,7 +1170,7 @@ def test_schedule_create_permissions(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
make_schedule(
|
||||
organization,
|
||||
schedule_class=OnCallScheduleICal,
|
||||
|
|
@ -1196,9 +1196,9 @@ def test_schedule_create_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_schedule_update_permissions(
|
||||
|
|
@ -1208,7 +1208,7 @@ def test_schedule_update_permissions(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
schedule = make_schedule(
|
||||
organization,
|
||||
schedule_class=OnCallScheduleICal,
|
||||
|
|
@ -1237,7 +1237,11 @@ def test_schedule_update_permissions(
|
|||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[(Role.ADMIN, status.HTTP_200_OK), (Role.EDITOR, status.HTTP_200_OK), (Role.VIEWER, status.HTTP_200_OK)],
|
||||
[
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_schedule_list_permissions(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
|
|
@ -1246,7 +1250,7 @@ def test_schedule_list_permissions(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
make_schedule(
|
||||
organization,
|
||||
schedule_class=OnCallScheduleICal,
|
||||
|
|
@ -1271,7 +1275,11 @@ def test_schedule_list_permissions(
|
|||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[(Role.ADMIN, status.HTTP_200_OK), (Role.EDITOR, status.HTTP_200_OK), (Role.VIEWER, status.HTTP_200_OK)],
|
||||
[
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_schedule_retrieve_permissions(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
|
|
@ -1280,7 +1288,7 @@ def test_schedule_retrieve_permissions(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
schedule = make_schedule(
|
||||
organization,
|
||||
schedule_class=OnCallScheduleICal,
|
||||
|
|
@ -1306,9 +1314,9 @@ def test_schedule_retrieve_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_204_NO_CONTENT),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_204_NO_CONTENT),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_schedule_delete_permissions(
|
||||
|
|
@ -1318,7 +1326,7 @@ def test_schedule_delete_permissions(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
schedule = make_schedule(
|
||||
organization,
|
||||
schedule_class=OnCallScheduleICal,
|
||||
|
|
@ -1344,9 +1352,9 @@ def test_schedule_delete_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_events_permissions(
|
||||
|
|
@ -1356,7 +1364,7 @@ def test_events_permissions(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
schedule = make_schedule(
|
||||
organization,
|
||||
schedule_class=OnCallScheduleICal,
|
||||
|
|
@ -1382,9 +1390,9 @@ def test_events_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_reload_ical_permissions(
|
||||
|
|
@ -1394,7 +1402,7 @@ def test_reload_ical_permissions(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
schedule = make_schedule(
|
||||
organization,
|
||||
schedule_class=OnCallScheduleICal,
|
||||
|
|
@ -1420,9 +1428,9 @@ def test_reload_ical_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_schedule_notify_oncall_shift_freq_options_permissions(
|
||||
|
|
@ -1432,7 +1440,7 @@ def test_schedule_notify_oncall_shift_freq_options_permissions(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
url = reverse("api-internal:schedule-notify-oncall-shift-freq-options")
|
||||
client = APIClient()
|
||||
response = client.get(url, format="json", **make_user_auth_headers(user, token))
|
||||
|
|
@ -1444,9 +1452,9 @@ def test_schedule_notify_oncall_shift_freq_options_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_schedule_notify_empty_oncall_options_permissions(
|
||||
|
|
@ -1456,7 +1464,7 @@ def test_schedule_notify_empty_oncall_options_permissions(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
url = reverse("api-internal:schedule-notify-empty-oncall-options")
|
||||
client = APIClient()
|
||||
response = client.get(url, format="json", **make_user_auth_headers(user, token))
|
||||
|
|
@ -1468,9 +1476,9 @@ def test_schedule_notify_empty_oncall_options_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_schedule_mention_options_permissions(
|
||||
|
|
@ -1480,7 +1488,7 @@ def test_schedule_mention_options_permissions(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
url = reverse("api-internal:schedule-mention-options")
|
||||
client = APIClient()
|
||||
response = client.get(url, format="json", **make_user_auth_headers(user, token))
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from rest_framework import status
|
|||
from rest_framework.response import Response
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from common.constants.role import Role
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
|
||||
|
||||
# Testing permissions, not view itself. So mock is ok here
|
||||
|
|
@ -14,13 +14,16 @@ from common.constants.role import Role
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_set_general_log_channel_permissions(
|
||||
make_organization_and_user_with_plugin_token, make_user_auth_headers, role, expected_status
|
||||
make_organization_and_user_with_plugin_token,
|
||||
make_user_auth_headers,
|
||||
role,
|
||||
expected_status,
|
||||
):
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
client = APIClient()
|
||||
|
|
|
|||
|
|
@ -6,20 +6,23 @@ from rest_framework import status
|
|||
from rest_framework.response import Response
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from common.constants.role import Role
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_slack_channels_list_permissions(
|
||||
make_organization_and_user_with_plugin_token, make_user_auth_headers, role, expected_status
|
||||
make_organization_and_user_with_plugin_token,
|
||||
make_user_auth_headers,
|
||||
role,
|
||||
expected_status,
|
||||
):
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
client = APIClient()
|
||||
|
|
@ -40,13 +43,17 @@ def test_slack_channels_list_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_slack_channels_detail_permissions(
|
||||
make_organization_and_user_with_plugin_token, make_user_auth_headers, role, make_slack_channel, expected_status
|
||||
make_organization_and_user_with_plugin_token,
|
||||
make_user_auth_headers,
|
||||
make_slack_channel,
|
||||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
slack_channel = make_slack_channel(organization.slack_team_identity)
|
||||
|
|
|
|||
|
|
@ -6,16 +6,16 @@ from rest_framework import status
|
|||
from rest_framework.response import Response
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from common.constants.role import Role
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_get_slack_settings_permissions(
|
||||
|
|
@ -24,7 +24,7 @@ def test_get_slack_settings_permissions(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
client = APIClient()
|
||||
|
||||
url = reverse("api-internal:slack-settings")
|
||||
|
|
@ -43,9 +43,9 @@ def test_get_slack_settings_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_update_slack_settings_permissions(
|
||||
|
|
@ -54,7 +54,7 @@ def test_update_slack_settings_permissions(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
client = APIClient()
|
||||
|
||||
url = reverse("api-internal:slack-settings")
|
||||
|
|
@ -73,9 +73,9 @@ def test_update_slack_settings_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_get_acknowledge_remind_options_permissions(
|
||||
|
|
@ -84,7 +84,7 @@ def test_get_acknowledge_remind_options_permissions(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
client = APIClient()
|
||||
|
||||
url = reverse("api-internal:acknowledge-reminder-options")
|
||||
|
|
@ -103,9 +103,9 @@ def test_get_acknowledge_remind_options_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_get_unacknowledge_timeout_options_permissions(
|
||||
|
|
@ -114,7 +114,7 @@ def test_get_unacknowledge_timeout_options_permissions(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
client = APIClient()
|
||||
|
||||
url = reverse("api-internal:unacknowledge-timeout-options")
|
||||
|
|
|
|||
|
|
@ -6,16 +6,16 @@ from rest_framework import status
|
|||
from rest_framework.response import Response
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from common.constants.role import Role
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_subscription_retrieve_permissions(
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ from django.urls import reverse
|
|||
from rest_framework import status
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
from apps.schedules.models import OnCallScheduleCalendar
|
||||
from apps.user_management.models import Team
|
||||
from common.constants.role import Role
|
||||
|
||||
GENERAL_TEAM = Team(public_primary_key=None, name="General", email=None, avatar_url=None)
|
||||
|
||||
|
|
@ -64,28 +64,24 @@ def test_list_teams_for_non_member(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_list_teams_permissions(
|
||||
make_organization,
|
||||
make_token_for_organization,
|
||||
make_user_for_organization,
|
||||
make_organization_and_user_with_plugin_token,
|
||||
make_user_auth_headers,
|
||||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization = make_organization()
|
||||
_, token = make_token_for_organization(organization)
|
||||
user = make_user_for_organization(organization, role=role)
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:team-list")
|
||||
response = client.get(url, format="json", **make_user_auth_headers(user, token))
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.status_code == expected_status
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@ from django.urls import reverse
|
|||
from rest_framework import status
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from common.constants.role import Role
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_not_authorized(make_organization_and_user_with_plugin_token, make_telegram_channel):
|
||||
client = APIClient()
|
||||
|
||||
organization, user, _ = make_organization_and_user_with_plugin_token()
|
||||
organization, _, _ = make_organization_and_user_with_plugin_token()
|
||||
telegram_channel = make_telegram_channel(organization=organization)
|
||||
|
||||
url = reverse("api-internal:telegram_channel-list")
|
||||
|
|
@ -34,9 +34,9 @@ def test_not_authorized(make_organization_and_user_with_plugin_token, make_teleg
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_list_telegram_channels_permissions(
|
||||
|
|
@ -46,8 +46,7 @@ def test_list_telegram_channels_permissions(
|
|||
expected_status,
|
||||
):
|
||||
client = APIClient()
|
||||
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
|
||||
url = reverse("api-internal:telegram_channel-list")
|
||||
response = client.get(url, **make_user_auth_headers(user, token))
|
||||
|
|
@ -59,9 +58,9 @@ def test_list_telegram_channels_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_get_telegram_channels_permissions(
|
||||
|
|
@ -72,8 +71,7 @@ def test_get_telegram_channels_permissions(
|
|||
expected_status,
|
||||
):
|
||||
client = APIClient()
|
||||
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
telegram_channel = make_telegram_channel(organization=organization)
|
||||
|
||||
url = reverse("api-internal:telegram_channel-detail", kwargs={"pk": telegram_channel.public_primary_key})
|
||||
|
|
@ -86,9 +84,9 @@ def test_get_telegram_channels_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_204_NO_CONTENT),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_204_NO_CONTENT),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_delete_telegram_channels_permissions(
|
||||
|
|
@ -100,7 +98,7 @@ def test_delete_telegram_channels_permissions(
|
|||
):
|
||||
client = APIClient()
|
||||
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
telegram_channel = make_telegram_channel(organization=organization)
|
||||
|
||||
url = reverse("api-internal:telegram_channel-detail", kwargs={"pk": telegram_channel.public_primary_key})
|
||||
|
|
@ -113,9 +111,9 @@ def test_delete_telegram_channels_permissions(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_set_default_telegram_channels_permissions(
|
||||
|
|
@ -127,8 +125,7 @@ def test_set_default_telegram_channels_permissions(
|
|||
):
|
||||
client = APIClient()
|
||||
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
telegram_channel = make_telegram_channel(organization=organization)
|
||||
|
||||
url = reverse("api-internal:telegram_channel-set-default", kwargs={"pk": telegram_channel.public_primary_key})
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ def test_get_terraform_file(
|
|||
|
||||
@pytest.mark.django_db
|
||||
def test_get_terraform_imports(make_organization_and_user_with_plugin_token, make_user_auth_headers):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token()
|
||||
_, user, token = make_organization_and_user_with_plugin_token()
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:terraform_imports")
|
||||
response = client.get(url, format="text/plain", **make_user_auth_headers(user, token))
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -3,7 +3,7 @@ from django.urls import reverse
|
|||
from rest_framework import status
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from common.constants.role import Role
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
|
@ -52,13 +52,16 @@ def test_usergroup_list_without_slack_installed(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_usergroup_permissions(
|
||||
make_organization_and_user_with_plugin_token, make_user_auth_headers, role, expected_status
|
||||
make_organization_and_user_with_plugin_token,
|
||||
make_user_auth_headers,
|
||||
role,
|
||||
expected_status,
|
||||
):
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
client = APIClient()
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ from django.utils import timezone
|
|||
from rest_framework import status
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
from apps.base.models import UserNotificationPolicy
|
||||
from common.constants.role import Role
|
||||
|
||||
DEFAULT_NOTIFICATION_CHANNEL = UserNotificationPolicy.NotificationChannel.SLACK
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ def user_notification_policy_internal_api_setup(
|
|||
make_organization_and_user_with_plugin_token, make_user_for_organization, make_user_notification_policy
|
||||
):
|
||||
organization, admin, token = make_organization_and_user_with_plugin_token()
|
||||
user = make_user_for_organization(organization, Role.EDITOR)
|
||||
user = make_user_for_organization(organization, role=LegacyAccessControlRole.EDITOR)
|
||||
|
||||
wait_notification_step = make_user_notification_policy(
|
||||
admin, UserNotificationPolicy.Step.WAIT, wait_delay=timezone.timedelta(minutes=15), important=False
|
||||
|
|
@ -49,7 +49,7 @@ def user_notification_policy_internal_api_setup(
|
|||
|
||||
@pytest.mark.django_db
|
||||
def test_create_notification_policy(user_notification_policy_internal_api_setup, make_user_auth_headers):
|
||||
token, steps, users = user_notification_policy_internal_api_setup
|
||||
token, _, users = user_notification_policy_internal_api_setup
|
||||
admin, _ = users
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:notification_policy-list")
|
||||
|
|
@ -69,7 +69,7 @@ def test_create_notification_policy(user_notification_policy_internal_api_setup,
|
|||
def test_admin_can_create_notification_policy_for_user(
|
||||
user_notification_policy_internal_api_setup, make_user_auth_headers
|
||||
):
|
||||
token, steps, users = user_notification_policy_internal_api_setup
|
||||
token, _, users = user_notification_policy_internal_api_setup
|
||||
admin, user = users
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:notification_policy-list")
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ from django.urls import reverse
|
|||
from rest_framework import status
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
from apps.auth_token.models import UserScheduleExportAuthToken
|
||||
from common.constants.role import Role
|
||||
|
||||
ICAL_URL = "https://calendar.google.com/calendar/ical/amixr.io_37gttuakhrtr75ano72p69rt78%40group.calendar.google.com/private-1d00a680ba5be7426c3eb3ef1616e26d/basic.ics" # noqa
|
||||
|
||||
|
|
@ -13,9 +13,9 @@ ICAL_URL = "https://calendar.google.com/calendar/ical/amixr.io_37gttuakhrtr75ano
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_200_OK),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_get_user_schedule_export_token(
|
||||
|
|
@ -24,8 +24,7 @@ def test_get_user_schedule_export_token(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
|
||||
UserScheduleExportAuthToken.create_auth_token(
|
||||
user=user,
|
||||
|
|
@ -45,9 +44,9 @@ def test_get_user_schedule_export_token(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_404_NOT_FOUND),
|
||||
(Role.EDITOR, status.HTTP_404_NOT_FOUND),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_404_NOT_FOUND),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_404_NOT_FOUND),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_user_schedule_export_token_not_found(
|
||||
|
|
@ -56,8 +55,7 @@ def test_user_schedule_export_token_not_found(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
|
||||
url = reverse("api-internal:user-export-token", kwargs={"pk": user.public_primary_key})
|
||||
|
||||
|
|
@ -72,9 +70,9 @@ def test_user_schedule_export_token_not_found(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_201_CREATED),
|
||||
(Role.EDITOR, status.HTTP_201_CREATED),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_201_CREATED),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_201_CREATED),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_user_schedule_create_export_token(
|
||||
|
|
@ -83,8 +81,7 @@ def test_user_schedule_create_export_token(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
|
||||
url = reverse("api-internal:user-export-token", kwargs={"pk": user.public_primary_key})
|
||||
|
||||
|
|
@ -99,9 +96,9 @@ def test_user_schedule_create_export_token(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_409_CONFLICT),
|
||||
(Role.EDITOR, status.HTTP_409_CONFLICT),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_409_CONFLICT),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_409_CONFLICT),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_user_schedule_create_multiple_export_tokens_fails(
|
||||
|
|
@ -110,8 +107,7 @@ def test_user_schedule_create_multiple_export_tokens_fails(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
|
||||
UserScheduleExportAuthToken.create_auth_token(
|
||||
user=user,
|
||||
|
|
@ -131,9 +127,9 @@ def test_user_schedule_create_multiple_export_tokens_fails(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_204_NO_CONTENT),
|
||||
(Role.EDITOR, status.HTTP_204_NO_CONTENT),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_204_NO_CONTENT),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_204_NO_CONTENT),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_user_schedule_delete_export_token(
|
||||
|
|
@ -142,8 +138,7 @@ def test_user_schedule_delete_export_token(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
|
||||
instance, _ = UserScheduleExportAuthToken.create_auth_token(
|
||||
user=user,
|
||||
|
|
@ -168,9 +163,9 @@ def test_user_schedule_delete_export_token(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_404_NOT_FOUND),
|
||||
(Role.EDITOR, status.HTTP_404_NOT_FOUND),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_404_NOT_FOUND),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_404_NOT_FOUND),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_user_cannot_get_another_users_schedule_token(
|
||||
|
|
@ -179,9 +174,8 @@ def test_user_cannot_get_another_users_schedule_token(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
|
||||
organization1, user1, _ = make_organization_and_user_with_plugin_token(role=role)
|
||||
_, user2, token2 = make_organization_and_user_with_plugin_token(role=role)
|
||||
organization1, user1, _ = make_organization_and_user_with_plugin_token(role)
|
||||
_, user2, token2 = make_organization_and_user_with_plugin_token(role)
|
||||
|
||||
UserScheduleExportAuthToken.create_auth_token(
|
||||
user=user1,
|
||||
|
|
@ -201,9 +195,9 @@ def test_user_cannot_get_another_users_schedule_token(
|
|||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_404_NOT_FOUND),
|
||||
(Role.EDITOR, status.HTTP_404_NOT_FOUND),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_404_NOT_FOUND),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_404_NOT_FOUND),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_user_cannot_delete_another_users_schedule_token(
|
||||
|
|
@ -212,9 +206,8 @@ def test_user_cannot_delete_another_users_schedule_token(
|
|||
role,
|
||||
expected_status,
|
||||
):
|
||||
|
||||
organization1, user1, _ = make_organization_and_user_with_plugin_token(role=role)
|
||||
_, user2, token2 = make_organization_and_user_with_plugin_token(role=role)
|
||||
organization1, user1, _ = make_organization_and_user_with_plugin_token(role)
|
||||
_, user2, token2 = make_organization_and_user_with_plugin_token(role)
|
||||
|
||||
UserScheduleExportAuthToken.create_auth_token(
|
||||
user=user1,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ from rest_framework.response import Response
|
|||
|
||||
from apps.alerts.constants import ActionSource
|
||||
from apps.alerts.models import Alert, AlertGroup, AlertReceiveChannel
|
||||
from apps.api.permissions import MODIFY_ACTIONS, READ_ACTIONS, ActionPermission, AnyRole, IsAdminOrEditor
|
||||
from apps.api.permissions import RBACPermission
|
||||
from apps.api.serializers.alert_group import AlertGroupListSerializer, AlertGroupSerializer
|
||||
from apps.auth_token.auth import PluginAuthentication
|
||||
from apps.mobile_app.auth import MobileAppAuthTokenAuthentication
|
||||
|
|
@ -160,29 +160,29 @@ class AlertGroupView(
|
|||
MobileAppAuthTokenAuthentication,
|
||||
PluginAuthentication,
|
||||
)
|
||||
permission_classes = (IsAuthenticated, ActionPermission)
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
|
||||
action_permissions = {
|
||||
IsAdminOrEditor: (
|
||||
*MODIFY_ACTIONS,
|
||||
"acknowledge",
|
||||
"unacknowledge",
|
||||
"resolve",
|
||||
"unresolve",
|
||||
"attach",
|
||||
"unattach",
|
||||
"silence",
|
||||
"unsilence",
|
||||
"bulk_action",
|
||||
"preview_template",
|
||||
),
|
||||
AnyRole: (
|
||||
*READ_ACTIONS,
|
||||
"stats",
|
||||
"filters",
|
||||
"silence_options",
|
||||
"bulk_action_options",
|
||||
),
|
||||
rbac_permissions = {
|
||||
"metadata": [RBACPermission.Permissions.ALERT_GROUPS_READ],
|
||||
"list": [RBACPermission.Permissions.ALERT_GROUPS_READ],
|
||||
"retrieve": [RBACPermission.Permissions.ALERT_GROUPS_READ],
|
||||
"stats": [RBACPermission.Permissions.ALERT_GROUPS_READ],
|
||||
"filters": [RBACPermission.Permissions.ALERT_GROUPS_READ],
|
||||
"silence_options": [RBACPermission.Permissions.ALERT_GROUPS_READ],
|
||||
"bulk_action_options": [RBACPermission.Permissions.ALERT_GROUPS_READ],
|
||||
"create": [RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
"update": [RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
"destroy": [RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
"acknowledge": [RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
"unacknowledge": [RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
"resolve": [RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
"unresolve": [RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
"attach": [RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
"unattach": [RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
"silence": [RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
"unsilence": [RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
"bulk_action": [RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
"preview_template": [RBACPermission.Permissions.INTEGRATIONS_TEST],
|
||||
}
|
||||
|
||||
http_method_names = ["get", "post"]
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from rest_framework.response import Response
|
|||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from apps.alerts.models import AlertReceiveChannel
|
||||
from apps.api.permissions import MODIFY_ACTIONS, READ_ACTIONS, ActionPermission, AnyRole, IsAdmin, IsAdminOrEditor
|
||||
from apps.api.permissions import RBACPermission
|
||||
from apps.api.serializers.alert_receive_channel import (
|
||||
AlertReceiveChannelSerializer,
|
||||
AlertReceiveChannelUpdateSerializer,
|
||||
|
|
@ -66,19 +66,7 @@ class AlertReceiveChannelView(
|
|||
ModelViewSet,
|
||||
):
|
||||
authentication_classes = (PluginAuthentication,)
|
||||
permission_classes = (IsAuthenticated, ActionPermission)
|
||||
action_permissions = {
|
||||
IsAdmin: (*MODIFY_ACTIONS, "stop_maintenance", "start_maintenance", "change_team"),
|
||||
IsAdminOrEditor: ("send_demo_alert", "preview_template"),
|
||||
AnyRole: (
|
||||
*READ_ACTIONS,
|
||||
"integration_options",
|
||||
"maintenance_duration_options",
|
||||
"maintenance_mode_options",
|
||||
"counters",
|
||||
"counters_per_integration",
|
||||
),
|
||||
}
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
|
||||
model = AlertReceiveChannel
|
||||
serializer_class = AlertReceiveChannelSerializer
|
||||
|
|
@ -90,6 +78,22 @@ class AlertReceiveChannelView(
|
|||
|
||||
filterset_class = AlertReceiveChannelFilter
|
||||
|
||||
rbac_permissions = {
|
||||
"metadata": [RBACPermission.Permissions.INTEGRATIONS_READ],
|
||||
"list": [RBACPermission.Permissions.INTEGRATIONS_READ],
|
||||
"retrieve": [RBACPermission.Permissions.INTEGRATIONS_READ],
|
||||
"integration_options": [RBACPermission.Permissions.INTEGRATIONS_READ],
|
||||
"counters": [RBACPermission.Permissions.INTEGRATIONS_READ],
|
||||
"counters_per_integration": [RBACPermission.Permissions.INTEGRATIONS_READ],
|
||||
"send_demo_alert": [RBACPermission.Permissions.INTEGRATIONS_TEST],
|
||||
"preview_template": [RBACPermission.Permissions.INTEGRATIONS_TEST],
|
||||
"create": [RBACPermission.Permissions.INTEGRATIONS_WRITE],
|
||||
"update": [RBACPermission.Permissions.INTEGRATIONS_WRITE],
|
||||
"partial_update": [RBACPermission.Permissions.INTEGRATIONS_WRITE],
|
||||
"destroy": [RBACPermission.Permissions.INTEGRATIONS_WRITE],
|
||||
"change_team": [RBACPermission.Permissions.INTEGRATIONS_WRITE],
|
||||
}
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
if request.data["integration"] is not None and (
|
||||
request.data["integration"] in AlertReceiveChannel.WEB_INTEGRATION_CHOICES
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from rest_framework.permissions import IsAuthenticated
|
|||
from rest_framework.response import Response
|
||||
|
||||
from apps.alerts.models import AlertReceiveChannel
|
||||
from apps.api.permissions import MODIFY_ACTIONS, READ_ACTIONS, ActionPermission, AnyRole, IsAdmin
|
||||
from apps.api.permissions import RBACPermission
|
||||
from apps.api.serializers.alert_receive_channel import AlertReceiveChannelTemplatesSerializer
|
||||
from apps.auth_token.auth import PluginAuthentication
|
||||
from common.api_helpers.mixins import PublicPrimaryKeyMixin
|
||||
|
|
@ -18,11 +18,14 @@ class AlertReceiveChannelTemplateView(
|
|||
viewsets.GenericViewSet,
|
||||
):
|
||||
authentication_classes = (PluginAuthentication,)
|
||||
permission_classes = (IsAuthenticated, ActionPermission)
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
|
||||
action_permissions = {
|
||||
IsAdmin: MODIFY_ACTIONS,
|
||||
AnyRole: READ_ACTIONS,
|
||||
rbac_permissions = {
|
||||
"metadata": [RBACPermission.Permissions.INTEGRATIONS_READ],
|
||||
"list": [RBACPermission.Permissions.INTEGRATIONS_READ],
|
||||
"retrieve": [RBACPermission.Permissions.INTEGRATIONS_READ],
|
||||
"update": [RBACPermission.Permissions.INTEGRATIONS_WRITE],
|
||||
"partial_update": [RBACPermission.Permissions.INTEGRATIONS_WRITE],
|
||||
}
|
||||
|
||||
model = AlertReceiveChannel
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from rest_framework.response import Response
|
|||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from apps.alerts.models import ChannelFilter
|
||||
from apps.api.permissions import MODIFY_ACTIONS, READ_ACTIONS, ActionPermission, AnyRole, IsAdmin, IsAdminOrEditor
|
||||
from apps.api.permissions import RBACPermission
|
||||
from apps.api.serializers.channel_filter import (
|
||||
ChannelFilterCreateSerializer,
|
||||
ChannelFilterSerializer,
|
||||
|
|
@ -23,11 +23,17 @@ from common.insight_log import EntityEvent, write_resource_insight_log
|
|||
|
||||
class ChannelFilterView(PublicPrimaryKeyMixin, CreateSerializerMixin, UpdateSerializerMixin, ModelViewSet):
|
||||
authentication_classes = (PluginAuthentication,)
|
||||
permission_classes = (IsAuthenticated, ActionPermission)
|
||||
action_permissions = {
|
||||
IsAdmin: (*MODIFY_ACTIONS, "move_to_position"),
|
||||
IsAdminOrEditor: ("send_demo_alert",),
|
||||
AnyRole: READ_ACTIONS,
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
rbac_permissions = {
|
||||
"metadata": [RBACPermission.Permissions.INTEGRATIONS_READ],
|
||||
"list": [RBACPermission.Permissions.INTEGRATIONS_READ],
|
||||
"retrieve": [RBACPermission.Permissions.INTEGRATIONS_READ],
|
||||
"create": [RBACPermission.Permissions.INTEGRATIONS_WRITE],
|
||||
"update": [RBACPermission.Permissions.INTEGRATIONS_WRITE],
|
||||
"partial_update": [RBACPermission.Permissions.INTEGRATIONS_WRITE],
|
||||
"destroy": [RBACPermission.Permissions.INTEGRATIONS_WRITE],
|
||||
"move_to_position": [RBACPermission.Permissions.INTEGRATIONS_WRITE],
|
||||
"send_demo_alert": [RBACPermission.Permissions.INTEGRATIONS_TEST],
|
||||
}
|
||||
|
||||
model = ChannelFilter
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from rest_framework.permissions import IsAuthenticated
|
|||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from apps.alerts.models import CustomButton
|
||||
from apps.api.permissions import MODIFY_ACTIONS, READ_ACTIONS, ActionPermission, AnyRole, IsAdmin
|
||||
from apps.api.permissions import RBACPermission
|
||||
from apps.api.serializers.custom_button import CustomButtonSerializer
|
||||
from apps.auth_token.auth import PluginAuthentication
|
||||
from common.api_helpers.mixins import PublicPrimaryKeyMixin, TeamFilteringMixin
|
||||
|
|
@ -13,10 +13,16 @@ from common.insight_log import EntityEvent, write_resource_insight_log
|
|||
|
||||
class CustomButtonView(TeamFilteringMixin, PublicPrimaryKeyMixin, ModelViewSet):
|
||||
authentication_classes = (PluginAuthentication,)
|
||||
permission_classes = (IsAuthenticated, ActionPermission)
|
||||
action_permissions = {
|
||||
IsAdmin: MODIFY_ACTIONS,
|
||||
AnyRole: READ_ACTIONS,
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
|
||||
rbac_permissions = {
|
||||
"metadata": [RBACPermission.Permissions.OUTGOING_WEBHOOKS_READ],
|
||||
"list": [RBACPermission.Permissions.OUTGOING_WEBHOOKS_READ],
|
||||
"retrieve": [RBACPermission.Permissions.OUTGOING_WEBHOOKS_READ],
|
||||
"create": [RBACPermission.Permissions.OUTGOING_WEBHOOKS_WRITE],
|
||||
"update": [RBACPermission.Permissions.OUTGOING_WEBHOOKS_WRITE],
|
||||
"partial_update": [RBACPermission.Permissions.OUTGOING_WEBHOOKS_WRITE],
|
||||
"destroy": [RBACPermission.Permissions.OUTGOING_WEBHOOKS_WRITE],
|
||||
}
|
||||
|
||||
model = CustomButton
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from rest_framework.permissions import IsAuthenticated
|
|||
from rest_framework.response import Response
|
||||
|
||||
from apps.alerts.models import EscalationChain
|
||||
from apps.api.permissions import MODIFY_ACTIONS, READ_ACTIONS, ActionPermission, AnyRole, IsAdmin
|
||||
from apps.api.permissions import RBACPermission
|
||||
from apps.api.serializers.escalation_chain import EscalationChainListSerializer, EscalationChainSerializer
|
||||
from apps.auth_token.auth import PluginAuthentication
|
||||
from common.api_helpers.exceptions import BadRequest
|
||||
|
|
@ -17,11 +17,17 @@ from common.insight_log import EntityEvent, write_resource_insight_log
|
|||
|
||||
class EscalationChainViewSet(TeamFilteringMixin, PublicPrimaryKeyMixin, ListSerializerMixin, viewsets.ModelViewSet):
|
||||
authentication_classes = (PluginAuthentication,)
|
||||
permission_classes = (IsAuthenticated, ActionPermission)
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
|
||||
action_permissions = {
|
||||
IsAdmin: (*MODIFY_ACTIONS, "copy"),
|
||||
AnyRole: (*READ_ACTIONS, "details"),
|
||||
rbac_permissions = {
|
||||
"metadata": [RBACPermission.Permissions.ESCALATION_CHAINS_READ],
|
||||
"list": [RBACPermission.Permissions.ESCALATION_CHAINS_READ],
|
||||
"retrieve": [RBACPermission.Permissions.ESCALATION_CHAINS_READ],
|
||||
"details": [RBACPermission.Permissions.ESCALATION_CHAINS_READ],
|
||||
"create": [RBACPermission.Permissions.ESCALATION_CHAINS_WRITE],
|
||||
"update": [RBACPermission.Permissions.ESCALATION_CHAINS_WRITE],
|
||||
"destroy": [RBACPermission.Permissions.ESCALATION_CHAINS_WRITE],
|
||||
"copy": [RBACPermission.Permissions.ESCALATION_CHAINS_WRITE],
|
||||
}
|
||||
|
||||
filter_backends = [SearchFilter]
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from rest_framework.response import Response
|
|||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from apps.alerts.models import EscalationPolicy
|
||||
from apps.api.permissions import MODIFY_ACTIONS, READ_ACTIONS, ActionPermission, AnyRole, IsAdmin
|
||||
from apps.api.permissions import RBACPermission
|
||||
from apps.api.serializers.escalation_policy import (
|
||||
EscalationPolicyCreateSerializer,
|
||||
EscalationPolicySerializer,
|
||||
|
|
@ -21,15 +21,19 @@ from common.insight_log import EntityEvent, write_resource_insight_log
|
|||
|
||||
class EscalationPolicyView(PublicPrimaryKeyMixin, CreateSerializerMixin, UpdateSerializerMixin, ModelViewSet):
|
||||
authentication_classes = (PluginAuthentication,)
|
||||
permission_classes = (IsAuthenticated, ActionPermission)
|
||||
action_permissions = {
|
||||
IsAdmin: (*MODIFY_ACTIONS, "move_to_position"),
|
||||
AnyRole: (
|
||||
*READ_ACTIONS,
|
||||
"escalation_options",
|
||||
"delay_options",
|
||||
"num_minutes_in_window_options",
|
||||
),
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
rbac_permissions = {
|
||||
"metadata": [RBACPermission.Permissions.ESCALATION_CHAINS_READ],
|
||||
"list": [RBACPermission.Permissions.ESCALATION_CHAINS_READ],
|
||||
"retrieve": [RBACPermission.Permissions.ESCALATION_CHAINS_READ],
|
||||
"escalation_options": [RBACPermission.Permissions.ESCALATION_CHAINS_READ],
|
||||
"delay_options": [RBACPermission.Permissions.ESCALATION_CHAINS_READ],
|
||||
"num_minutes_in_window_options": [RBACPermission.Permissions.ESCALATION_CHAINS_READ],
|
||||
"create": [RBACPermission.Permissions.ESCALATION_CHAINS_WRITE],
|
||||
"update": [RBACPermission.Permissions.ESCALATION_CHAINS_WRITE],
|
||||
"partial_update": [RBACPermission.Permissions.ESCALATION_CHAINS_WRITE],
|
||||
"destroy": [RBACPermission.Permissions.ESCALATION_CHAINS_WRITE],
|
||||
"move_to_position": [RBACPermission.Permissions.ESCALATION_CHAINS_WRITE],
|
||||
}
|
||||
|
||||
model = EscalationPolicy
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from rest_framework.decorators import action
|
|||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
|
||||
from apps.api.permissions import MODIFY_ACTIONS, READ_ACTIONS, ActionPermission, AnyRole, IsAdmin
|
||||
from apps.api.permissions import RBACPermission
|
||||
from apps.api.serializers.integration_heartbeat import IntegrationHeartBeatSerializer
|
||||
from apps.auth_token.auth import PluginAuthentication
|
||||
from apps.heartbeat.models import IntegrationHeartBeat
|
||||
|
|
@ -20,10 +20,17 @@ class IntegrationHeartBeatView(
|
|||
viewsets.GenericViewSet,
|
||||
):
|
||||
authentication_classes = (PluginAuthentication,)
|
||||
permission_classes = (IsAuthenticated, ActionPermission)
|
||||
action_permissions = {
|
||||
IsAdmin: (*MODIFY_ACTIONS, "activate", "deactivate"),
|
||||
AnyRole: (*READ_ACTIONS, "timeout_options"),
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
rbac_permissions = {
|
||||
"metadata": [RBACPermission.Permissions.INTEGRATIONS_READ],
|
||||
"list": [RBACPermission.Permissions.INTEGRATIONS_READ],
|
||||
"retrieve": [RBACPermission.Permissions.INTEGRATIONS_READ],
|
||||
"timeout_options": [RBACPermission.Permissions.INTEGRATIONS_READ],
|
||||
"create": [RBACPermission.Permissions.INTEGRATIONS_WRITE],
|
||||
"update": [RBACPermission.Permissions.INTEGRATIONS_WRITE],
|
||||
"partial_update": [RBACPermission.Permissions.INTEGRATIONS_WRITE],
|
||||
"activate": [RBACPermission.Permissions.INTEGRATIONS_WRITE],
|
||||
"deactivate": [RBACPermission.Permissions.INTEGRATIONS_WRITE],
|
||||
}
|
||||
|
||||
model = IntegrationHeartBeat
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from rest_framework import status, viewsets
|
|||
from rest_framework.permissions import IsAuthenticated
|
||||
from telegram import error
|
||||
|
||||
from apps.api.permissions import IsAdmin
|
||||
from apps.api.permissions import RBACPermission
|
||||
from apps.api.serializers.live_setting import LiveSettingSerializer
|
||||
from apps.auth_token.auth import PluginAuthentication
|
||||
from apps.base.models import LiveSetting
|
||||
|
|
@ -21,7 +21,14 @@ from common.api_helpers.mixins import PublicPrimaryKeyMixin
|
|||
class LiveSettingViewSet(PublicPrimaryKeyMixin, viewsets.ModelViewSet):
|
||||
serializer_class = LiveSettingSerializer
|
||||
authentication_classes = (PluginAuthentication,)
|
||||
permission_classes = (IsAuthenticated, IsAdmin)
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
rbac_permissions = {
|
||||
"list": [RBACPermission.Permissions.OTHER_SETTINGS_READ],
|
||||
"retrieve": [RBACPermission.Permissions.OTHER_SETTINGS_READ],
|
||||
"create": [RBACPermission.Permissions.OTHER_SETTINGS_WRITE],
|
||||
"update": [RBACPermission.Permissions.OTHER_SETTINGS_WRITE],
|
||||
"destroy": [RBACPermission.Permissions.OTHER_SETTINGS_WRITE],
|
||||
}
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if not settings.FEATURE_LIVE_SETTINGS_ENABLED:
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from rest_framework.views import APIView
|
|||
|
||||
from apps.alerts.models import AlertReceiveChannel
|
||||
from apps.alerts.models.maintainable_object import MaintainableObject
|
||||
from apps.api.permissions import IsAdmin
|
||||
from apps.api.permissions import RBACPermission
|
||||
from apps.auth_token.auth import PluginAuthentication
|
||||
from common.api_helpers.exceptions import BadRequest
|
||||
from common.exceptions import MaintenanceCouldNotBeStartedError
|
||||
|
|
@ -39,7 +39,11 @@ class GetObjectMixin:
|
|||
|
||||
class MaintenanceAPIView(APIView):
|
||||
authentication_classes = (PluginAuthentication,)
|
||||
permission_classes = (IsAuthenticated,)
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
|
||||
rbac_permissions = {
|
||||
"get": [RBACPermission.Permissions.MAINTENANCE_READ],
|
||||
}
|
||||
|
||||
def get(self, request):
|
||||
organization = self.request.auth.organization
|
||||
|
|
@ -77,7 +81,10 @@ class MaintenanceAPIView(APIView):
|
|||
|
||||
class MaintenanceStartAPIView(GetObjectMixin, APIView):
|
||||
authentication_classes = (PluginAuthentication,)
|
||||
permission_classes = (IsAuthenticated, IsAdmin)
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
rbac_permissions = {
|
||||
"post": [RBACPermission.Permissions.MAINTENANCE_WRITE],
|
||||
}
|
||||
|
||||
def post(self, request):
|
||||
mode = request.data.get("mode", None)
|
||||
|
|
@ -110,7 +117,10 @@ class MaintenanceStartAPIView(GetObjectMixin, APIView):
|
|||
|
||||
class MaintenanceStopAPIView(GetObjectMixin, APIView):
|
||||
authentication_classes = (PluginAuthentication,)
|
||||
permission_classes = (IsAuthenticated, IsAdmin)
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
rbac_permissions = {
|
||||
"post": [RBACPermission.Permissions.MAINTENANCE_WRITE],
|
||||
}
|
||||
|
||||
def post(self, request):
|
||||
instance = self.get_object(request)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from rest_framework.permissions import IsAuthenticated
|
|||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from apps.api.permissions import MODIFY_ACTIONS, READ_ACTIONS, ActionPermission, AnyRole, IsAdmin
|
||||
from apps.api.permissions import RBACPermission
|
||||
from apps.api.serializers.on_call_shifts import OnCallShiftSerializer, OnCallShiftUpdateSerializer
|
||||
from apps.auth_token.auth import PluginAuthentication
|
||||
from apps.schedules.models import CustomOnCallShift
|
||||
|
|
@ -18,11 +18,20 @@ from common.insight_log import EntityEvent, write_resource_insight_log
|
|||
|
||||
class OnCallShiftView(PublicPrimaryKeyMixin, UpdateSerializerMixin, ModelViewSet):
|
||||
authentication_classes = (PluginAuthentication,)
|
||||
permission_classes = (IsAuthenticated, ActionPermission)
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
|
||||
action_permissions = {
|
||||
IsAdmin: (*MODIFY_ACTIONS, "preview"),
|
||||
AnyRole: (*READ_ACTIONS, "details", "frequency_options", "days_options"),
|
||||
rbac_permissions = {
|
||||
"metadata": [RBACPermission.Permissions.SCHEDULES_READ],
|
||||
"list": [RBACPermission.Permissions.SCHEDULES_READ],
|
||||
"retrieve": [RBACPermission.Permissions.SCHEDULES_READ],
|
||||
"details": [RBACPermission.Permissions.SCHEDULES_READ],
|
||||
"frequency_options": [RBACPermission.Permissions.SCHEDULES_READ],
|
||||
"days_options": [RBACPermission.Permissions.SCHEDULES_READ],
|
||||
"create": [RBACPermission.Permissions.SCHEDULES_WRITE],
|
||||
"update": [RBACPermission.Permissions.SCHEDULES_WRITE],
|
||||
"partial_update": [RBACPermission.Permissions.SCHEDULES_WRITE],
|
||||
"destroy": [RBACPermission.Permissions.SCHEDULES_WRITE],
|
||||
"preview": [RBACPermission.Permissions.SCHEDULES_WRITE],
|
||||
}
|
||||
|
||||
model = CustomOnCallShift
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from rest_framework.permissions import IsAuthenticated
|
|||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from apps.api.permissions import AnyRole, IsAdmin, MethodPermission
|
||||
from apps.api.permissions import RBACPermission
|
||||
from apps.api.serializers.organization import CurrentOrganizationSerializer
|
||||
from apps.auth_token.auth import PluginAuthentication
|
||||
from apps.base.messaging import get_messaging_backend_from_id
|
||||
|
|
@ -16,9 +16,12 @@ from common.insight_log import EntityEvent, write_resource_insight_log
|
|||
|
||||
class CurrentOrganizationView(APIView):
|
||||
authentication_classes = (PluginAuthentication,)
|
||||
permission_classes = (IsAuthenticated, MethodPermission)
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
|
||||
method_permissions = {IsAdmin: ("PUT",), AnyRole: ("GET",)}
|
||||
rbac_permissions = {
|
||||
"get": [],
|
||||
"put": [RBACPermission.Permissions.OTHER_SETTINGS_WRITE],
|
||||
}
|
||||
|
||||
def get(self, request):
|
||||
organization = request.auth.organization
|
||||
|
|
@ -46,7 +49,11 @@ class CurrentOrganizationView(APIView):
|
|||
|
||||
class GetTelegramVerificationCode(APIView):
|
||||
authentication_classes = (PluginAuthentication,)
|
||||
permission_classes = (IsAuthenticated, IsAdmin)
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
|
||||
rbac_permissions = {
|
||||
"get": [RBACPermission.Permissions.INTEGRATIONS_WRITE],
|
||||
}
|
||||
|
||||
def get(self, request):
|
||||
organization = request.auth.organization
|
||||
|
|
@ -66,7 +73,11 @@ class GetTelegramVerificationCode(APIView):
|
|||
|
||||
class GetChannelVerificationCode(APIView):
|
||||
authentication_classes = (PluginAuthentication,)
|
||||
permission_classes = (IsAuthenticated, IsAdmin)
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
|
||||
rbac_permissions = {
|
||||
"get": [RBACPermission.Permissions.INTEGRATIONS_WRITE],
|
||||
}
|
||||
|
||||
def get(self, request):
|
||||
organization = request.auth.organization
|
||||
|
|
@ -81,7 +92,11 @@ class GetChannelVerificationCode(APIView):
|
|||
|
||||
class SetGeneralChannel(APIView):
|
||||
authentication_classes = (PluginAuthentication,)
|
||||
permission_classes = (IsAuthenticated, IsAdmin)
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
|
||||
rbac_permissions = {
|
||||
"post": [RBACPermission.Permissions.CHATOPS_UPDATE_SETTINGS],
|
||||
}
|
||||
|
||||
def post(self, request):
|
||||
SlackChannel = apps.get_model("slack", "SlackChannel")
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from rest_framework import mixins, status, viewsets
|
|||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
|
||||
from apps.api.permissions import MODIFY_ACTIONS, READ_ACTIONS, IsAdmin
|
||||
from apps.api.permissions import RBACPermission
|
||||
from apps.api.serializers.public_api_token import PublicApiTokenSerializer
|
||||
from apps.auth_token.auth import PluginAuthentication
|
||||
from apps.auth_token.constants import MAX_PUBLIC_API_TOKENS_PER_USER
|
||||
|
|
@ -19,9 +19,14 @@ class PublicApiTokenView(
|
|||
viewsets.GenericViewSet,
|
||||
):
|
||||
authentication_classes = [PluginAuthentication]
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
action_permissions = {IsAdmin: (*MODIFY_ACTIONS, *READ_ACTIONS)}
|
||||
permission_classes = [IsAuthenticated, RBACPermission]
|
||||
rbac_permissions = {
|
||||
"metadata": [RBACPermission.Permissions.API_KEYS_READ],
|
||||
"list": [RBACPermission.Permissions.API_KEYS_READ],
|
||||
"retrieve": [RBACPermission.Permissions.API_KEYS_READ],
|
||||
"create": [RBACPermission.Permissions.API_KEYS_WRITE],
|
||||
"destroy": [RBACPermission.Permissions.API_KEYS_WRITE],
|
||||
}
|
||||
|
||||
model = ApiAuthToken
|
||||
serializer_class = PublicApiTokenSerializer
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from rest_framework.viewsets import ModelViewSet
|
|||
|
||||
from apps.alerts.models import ResolutionNote
|
||||
from apps.alerts.tasks import send_update_resolution_note_signal
|
||||
from apps.api.permissions import MODIFY_ACTIONS, READ_ACTIONS, ActionPermission, AnyRole, IsAdminOrEditor
|
||||
from apps.api.permissions import RBACPermission
|
||||
from apps.api.serializers.resolution_note import ResolutionNoteSerializer, ResolutionNoteUpdateSerializer
|
||||
from apps.auth_token.auth import PluginAuthentication
|
||||
from common.api_helpers.mixins import PublicPrimaryKeyMixin, UpdateSerializerMixin
|
||||
|
|
@ -11,11 +11,16 @@ from common.api_helpers.mixins import PublicPrimaryKeyMixin, UpdateSerializerMix
|
|||
|
||||
class ResolutionNoteView(PublicPrimaryKeyMixin, UpdateSerializerMixin, ModelViewSet):
|
||||
authentication_classes = (PluginAuthentication,)
|
||||
permission_classes = (IsAuthenticated, ActionPermission)
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
|
||||
action_permissions = {
|
||||
IsAdminOrEditor: MODIFY_ACTIONS,
|
||||
AnyRole: READ_ACTIONS,
|
||||
rbac_permissions = {
|
||||
"metadata": [RBACPermission.Permissions.ALERT_GROUPS_READ],
|
||||
"list": [RBACPermission.Permissions.ALERT_GROUPS_READ],
|
||||
"retrieve": [RBACPermission.Permissions.ALERT_GROUPS_READ],
|
||||
"create": [RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
"update": [RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
"partial_update": [RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
"destroy": [RBACPermission.Permissions.ALERT_GROUPS_WRITE],
|
||||
}
|
||||
|
||||
model = ResolutionNote
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ from rest_framework.views import Response
|
|||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from apps.alerts.models import EscalationChain, EscalationPolicy
|
||||
from apps.api.permissions import MODIFY_ACTIONS, READ_ACTIONS, ActionPermission, AnyRole, IsAdmin, IsAdminOrEditor
|
||||
from apps.api.permissions import RBACPermission
|
||||
from apps.api.serializers.schedule_base import ScheduleFastSerializer
|
||||
from apps.api.serializers.schedule_polymorphic import (
|
||||
PolymorphicScheduleCreateSerializer,
|
||||
|
|
@ -56,24 +56,26 @@ class ScheduleView(
|
|||
ModelViewSet,
|
||||
):
|
||||
authentication_classes = (PluginAuthentication,)
|
||||
permission_classes = (IsAuthenticated, ActionPermission)
|
||||
action_permissions = {
|
||||
IsAdmin: (
|
||||
*MODIFY_ACTIONS,
|
||||
"reload_ical",
|
||||
),
|
||||
IsAdminOrEditor: ("export_token",),
|
||||
AnyRole: (
|
||||
*READ_ACTIONS,
|
||||
"events",
|
||||
"filter_events",
|
||||
"next_shifts_per_user",
|
||||
"notify_empty_oncall_options",
|
||||
"notify_oncall_shift_freq_options",
|
||||
"mention_options",
|
||||
"related_escalation_chains",
|
||||
),
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
rbac_permissions = {
|
||||
"metadata": [RBACPermission.Permissions.SCHEDULES_READ],
|
||||
"list": [RBACPermission.Permissions.SCHEDULES_READ],
|
||||
"retrieve": [RBACPermission.Permissions.SCHEDULES_READ],
|
||||
"events": [RBACPermission.Permissions.SCHEDULES_READ],
|
||||
"filter_events": [RBACPermission.Permissions.SCHEDULES_READ],
|
||||
"next_shifts_per_user": [RBACPermission.Permissions.SCHEDULES_READ],
|
||||
"notify_empty_oncall_options": [RBACPermission.Permissions.SCHEDULES_READ],
|
||||
"notify_oncall_shift_freq_options": [RBACPermission.Permissions.SCHEDULES_READ],
|
||||
"mention_options": [RBACPermission.Permissions.SCHEDULES_READ],
|
||||
"related_escalation_chains": [RBACPermission.Permissions.SCHEDULES_READ],
|
||||
"create": [RBACPermission.Permissions.SCHEDULES_WRITE],
|
||||
"update": [RBACPermission.Permissions.SCHEDULES_WRITE],
|
||||
"partial_update": [RBACPermission.Permissions.SCHEDULES_WRITE],
|
||||
"destroy": [RBACPermission.Permissions.SCHEDULES_WRITE],
|
||||
"reload_ical": [RBACPermission.Permissions.SCHEDULES_WRITE],
|
||||
"export_token": [RBACPermission.Permissions.SCHEDULES_EXPORT],
|
||||
}
|
||||
|
||||
filter_backends = [SearchFilter]
|
||||
search_fields = ("name",)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from rest_framework import views
|
|||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
|
||||
from apps.api.permissions import AnyRole, IsAdmin, MethodPermission
|
||||
from apps.api.permissions import RBACPermission
|
||||
from apps.api.serializers.organization_slack_settings import OrganizationSlackSettingsSerializer
|
||||
from apps.auth_token.auth import PluginAuthentication
|
||||
from apps.user_management.models import Organization
|
||||
|
|
@ -11,11 +11,11 @@ from common.insight_log import EntityEvent, write_resource_insight_log
|
|||
|
||||
class SlackTeamSettingsAPIView(views.APIView):
|
||||
authentication_classes = (PluginAuthentication,)
|
||||
permission_classes = (IsAuthenticated, MethodPermission)
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
|
||||
method_permissions = {
|
||||
IsAdmin: ("PUT",),
|
||||
AnyRole: ("GET",),
|
||||
rbac_permissions = {
|
||||
"get": [RBACPermission.Permissions.CHATOPS_READ],
|
||||
"put": [RBACPermission.Permissions.CHATOPS_UPDATE_SETTINGS],
|
||||
}
|
||||
|
||||
serializer_class = OrganizationSlackSettingsSerializer
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from rest_framework.decorators import action
|
|||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
|
||||
from apps.api.permissions import MODIFY_ACTIONS, READ_ACTIONS, ActionPermission, AnyRole, IsAdmin
|
||||
from apps.api.permissions import RBACPermission
|
||||
from apps.api.serializers.telegram import TelegramToOrganizationConnectorSerializer
|
||||
from apps.auth_token.auth import PluginAuthentication
|
||||
from common.api_helpers.mixins import PublicPrimaryKeyMixin
|
||||
|
|
@ -19,11 +19,14 @@ class TelegramChannelViewSet(
|
|||
viewsets.GenericViewSet,
|
||||
):
|
||||
authentication_classes = (PluginAuthentication,)
|
||||
permission_classes = (IsAuthenticated, ActionPermission)
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
|
||||
action_permissions = {
|
||||
IsAdmin: (*MODIFY_ACTIONS, "set_default"),
|
||||
AnyRole: READ_ACTIONS,
|
||||
rbac_permissions = {
|
||||
"metadata": [RBACPermission.Permissions.CHATOPS_READ],
|
||||
"list": [RBACPermission.Permissions.CHATOPS_READ],
|
||||
"retrieve": [RBACPermission.Permissions.CHATOPS_READ],
|
||||
"destroy": [RBACPermission.Permissions.CHATOPS_UPDATE_SETTINGS],
|
||||
"set_default": [RBACPermission.Permissions.CHATOPS_UPDATE_SETTINGS],
|
||||
}
|
||||
|
||||
serializer_class = TelegramToOrganizationConnectorSerializer
|
||||
|
|
|
|||
|
|
@ -16,12 +16,10 @@ from rest_framework.response import Response
|
|||
from rest_framework.views import APIView
|
||||
|
||||
from apps.api.permissions import (
|
||||
MODIFY_ACTIONS,
|
||||
READ_ACTIONS,
|
||||
ActionPermission,
|
||||
AnyRole,
|
||||
IsAdminOrEditor,
|
||||
IsOwnerOrAdmin,
|
||||
IsOwnerOrHasRBACPermissions,
|
||||
LegacyAccessControlRole,
|
||||
RBACPermission,
|
||||
user_is_authorized,
|
||||
)
|
||||
from apps.api.serializers.team import TeamSerializer
|
||||
from apps.api.serializers.user import FilterUserSerializer, UserHiddenFieldsSerializer, UserSerializer
|
||||
|
|
@ -41,7 +39,6 @@ from common.api_helpers.exceptions import Conflict
|
|||
from common.api_helpers.mixins import FilterSerializerMixin, PublicPrimaryKeyMixin
|
||||
from common.api_helpers.paginators import HundredPageSizePaginator
|
||||
from common.api_helpers.utils import create_engine_url
|
||||
from common.constants.role import Role
|
||||
from common.insight_log import (
|
||||
ChatOpsEvent,
|
||||
ChatOpsType,
|
||||
|
|
@ -51,6 +48,7 @@ from common.insight_log import (
|
|||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
IsOwnerOrHasUserSettingsAdminPermission = IsOwnerOrHasRBACPermissions([RBACPermission.Permissions.USER_SETTINGS_ADMIN])
|
||||
|
||||
|
||||
class CurrentUserView(APIView):
|
||||
|
|
@ -89,7 +87,9 @@ class UserFilter(filters.FilterSet):
|
|||
"""
|
||||
|
||||
email = filters.CharFilter(field_name="email", lookup_expr="icontains")
|
||||
roles = filters.MultipleChoiceFilter(field_name="role", choices=Role.choices())
|
||||
roles = filters.MultipleChoiceFilter(
|
||||
field_name="role", choices=LegacyAccessControlRole.choices()
|
||||
) # LEGACY.. this should get removed eventually
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
|
|
@ -109,35 +109,36 @@ class UserView(
|
|||
PluginAuthentication,
|
||||
)
|
||||
|
||||
permission_classes = (IsAuthenticated, ActionPermission)
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
|
||||
# Non-admin users are allowed to list and retrieve users
|
||||
# The overridden get_serializer_class will return
|
||||
# another Serializer for non-admin users with sensitive information hidden
|
||||
action_permissions = {
|
||||
IsAdminOrEditor: (
|
||||
*MODIFY_ACTIONS,
|
||||
"list",
|
||||
"metadata",
|
||||
"verify_number",
|
||||
"forget_number",
|
||||
"get_verification_code",
|
||||
"get_backend_verification_code",
|
||||
"get_telegram_verification_code",
|
||||
"unlink_slack",
|
||||
"unlink_telegram",
|
||||
"unlink_backend",
|
||||
"make_test_call",
|
||||
"export_token",
|
||||
"mobile_app_auth_token",
|
||||
),
|
||||
AnyRole: ("retrieve", "timezone_options"),
|
||||
rbac_permissions = {
|
||||
"retrieve": [RBACPermission.Permissions.USER_SETTINGS_READ],
|
||||
"timezone_options": [RBACPermission.Permissions.USER_SETTINGS_READ],
|
||||
"metadata": [RBACPermission.Permissions.USER_SETTINGS_WRITE],
|
||||
"list": [RBACPermission.Permissions.USER_SETTINGS_WRITE],
|
||||
"update": [RBACPermission.Permissions.USER_SETTINGS_WRITE],
|
||||
"partial_update": [RBACPermission.Permissions.USER_SETTINGS_WRITE],
|
||||
"verify_number": [RBACPermission.Permissions.USER_SETTINGS_WRITE],
|
||||
"forget_number": [RBACPermission.Permissions.USER_SETTINGS_WRITE],
|
||||
"get_verification_code": [RBACPermission.Permissions.USER_SETTINGS_WRITE],
|
||||
"get_backend_verification_code": [RBACPermission.Permissions.USER_SETTINGS_WRITE],
|
||||
"get_telegram_verification_code": [RBACPermission.Permissions.USER_SETTINGS_WRITE],
|
||||
"unlink_slack": [RBACPermission.Permissions.USER_SETTINGS_WRITE],
|
||||
"unlink_telegram": [RBACPermission.Permissions.USER_SETTINGS_WRITE],
|
||||
"unlink_backend": [RBACPermission.Permissions.USER_SETTINGS_WRITE],
|
||||
"make_test_call": [RBACPermission.Permissions.USER_SETTINGS_WRITE],
|
||||
"export_token": [RBACPermission.Permissions.USER_SETTINGS_WRITE],
|
||||
"mobile_app_auth_token": [RBACPermission.Permissions.USER_SETTINGS_WRITE],
|
||||
}
|
||||
|
||||
action_object_permissions = {
|
||||
IsOwnerOrAdmin: (
|
||||
*MODIFY_ACTIONS,
|
||||
*READ_ACTIONS,
|
||||
rbac_object_permissions = {
|
||||
IsOwnerOrHasUserSettingsAdminPermission: [
|
||||
"metadata",
|
||||
"list",
|
||||
"retrieve",
|
||||
"update",
|
||||
"partial_update",
|
||||
"destroy",
|
||||
"verify_number",
|
||||
"forget_number",
|
||||
"get_verification_code",
|
||||
|
|
@ -149,7 +150,7 @@ class UserView(
|
|||
"make_test_call",
|
||||
"export_token",
|
||||
"mobile_app_auth_token",
|
||||
),
|
||||
],
|
||||
}
|
||||
|
||||
filter_serializer_class = FilterUserSerializer
|
||||
|
|
@ -172,14 +173,18 @@ class UserView(
|
|||
filterset_class = UserFilter
|
||||
|
||||
def get_serializer_class(self):
|
||||
is_filters_request = self.request.query_params.get("filters", "false") == "true"
|
||||
request = self.request
|
||||
user = request.user
|
||||
kwargs = self.kwargs
|
||||
|
||||
is_filters_request = request.query_params.get("filters", "false") == "true"
|
||||
if self.action in ["list"] and is_filters_request:
|
||||
return self.get_filter_serializer_class()
|
||||
|
||||
is_users_own_data = (
|
||||
self.kwargs.get("pk") is not None and self.kwargs.get("pk") == self.request.user.public_primary_key
|
||||
)
|
||||
if is_users_own_data or self.request.user.role == Role.ADMIN:
|
||||
is_users_own_data = kwargs.get("pk") is not None and kwargs.get("pk") == user.public_primary_key
|
||||
has_admin_permission = user_is_authorized(user, [RBACPermission.Permissions.USER_SETTINGS_ADMIN])
|
||||
|
||||
if is_users_own_data or has_admin_permission:
|
||||
return UserSerializer
|
||||
return UserHiddenFieldsSerializer
|
||||
|
||||
|
|
|
|||
|
|
@ -6,14 +6,7 @@ from rest_framework.permissions import IsAuthenticated
|
|||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from apps.api.permissions import (
|
||||
MODIFY_ACTIONS,
|
||||
READ_ACTIONS,
|
||||
ActionPermission,
|
||||
AnyRole,
|
||||
IsAdminOrEditor,
|
||||
IsOwnerOrAdmin,
|
||||
)
|
||||
from apps.api.permissions import IsOwnerOrHasRBACPermissions, RBACPermission
|
||||
from apps.api.serializers.user_notification_policy import (
|
||||
UserNotificationPolicySerializer,
|
||||
UserNotificationPolicyUpdateSerializer,
|
||||
|
|
@ -31,18 +24,34 @@ from common.insight_log import EntityEvent, write_resource_insight_log
|
|||
|
||||
class UserNotificationPolicyView(UpdateSerializerMixin, ModelViewSet):
|
||||
authentication_classes = (PluginAuthentication,)
|
||||
permission_classes = (IsAuthenticated, ActionPermission)
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
|
||||
action_permissions = {
|
||||
IsAdminOrEditor: (*MODIFY_ACTIONS, "move_to_position"),
|
||||
AnyRole: (*READ_ACTIONS, "delay_options", "notify_by_options"),
|
||||
}
|
||||
action_object_permissions = {
|
||||
IsOwnerOrAdmin: (*MODIFY_ACTIONS, "move_to_position"),
|
||||
AnyRole: READ_ACTIONS,
|
||||
rbac_permissions = {
|
||||
"metadata": [RBACPermission.Permissions.USER_SETTINGS_READ],
|
||||
"list": [RBACPermission.Permissions.USER_SETTINGS_READ],
|
||||
"retrieve": [RBACPermission.Permissions.USER_SETTINGS_READ],
|
||||
"delay_options": [RBACPermission.Permissions.USER_SETTINGS_READ],
|
||||
"notify_by_options": [RBACPermission.Permissions.USER_SETTINGS_READ],
|
||||
"create": [RBACPermission.Permissions.USER_SETTINGS_WRITE],
|
||||
"update": [RBACPermission.Permissions.USER_SETTINGS_WRITE],
|
||||
"partial_update": [RBACPermission.Permissions.USER_SETTINGS_WRITE],
|
||||
"destroy": [RBACPermission.Permissions.USER_SETTINGS_WRITE],
|
||||
"move_to_position": [RBACPermission.Permissions.USER_SETTINGS_WRITE],
|
||||
}
|
||||
|
||||
ownership_field = "user"
|
||||
IsOwnerOrHasUserSettingsAdminPermission = IsOwnerOrHasRBACPermissions(
|
||||
required_permissions=[RBACPermission.Permissions.USER_SETTINGS_ADMIN], ownership_field="user"
|
||||
)
|
||||
|
||||
rbac_object_permissions = {
|
||||
IsOwnerOrHasUserSettingsAdminPermission: [
|
||||
"create",
|
||||
"update",
|
||||
"partial_update",
|
||||
"destroy",
|
||||
"move_to_position",
|
||||
],
|
||||
}
|
||||
|
||||
model = UserNotificationPolicy
|
||||
serializer_class = UserNotificationPolicySerializer
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@ from rest_framework import exceptions
|
|||
from rest_framework.authentication import BaseAuthentication, get_authorization_header
|
||||
from rest_framework.request import Request
|
||||
|
||||
from apps.api.permissions import RBACPermission, user_is_authorized
|
||||
from apps.grafana_plugin.helpers.gcom import check_token
|
||||
from apps.user_management.models import User
|
||||
from apps.user_management.models.organization import Organization
|
||||
from apps.user_management.models.region import OrganizationMovedException
|
||||
from common.constants.role import Role
|
||||
|
||||
from .constants import SCHEDULE_EXPORT_TOKEN_NAME, SLACK_AUTH_TOKEN_NAME
|
||||
from .exceptions import InvalidToken
|
||||
|
|
@ -29,7 +29,7 @@ class ApiTokenAuthentication(BaseAuthentication):
|
|||
auth = get_authorization_header(request).decode("utf-8")
|
||||
user, auth_token = self.authenticate_credentials(auth)
|
||||
|
||||
if user.role != Role.ADMIN:
|
||||
if not user_is_authorized(user, [RBACPermission.Permissions.API_KEYS_WRITE]):
|
||||
raise exceptions.AuthenticationFailed(
|
||||
"Only users with Admin permissions are allowed to perform this action."
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
# This is temporary solution to not to hardcode permissions on frontend
|
||||
# Is should be removed with one which will collect permission from action_permission views' attribute
|
||||
ALL_PERMISSIONS = [
|
||||
"update_incidents",
|
||||
"update_alert_receive_channels",
|
||||
"update_escalation_policies",
|
||||
"update_notification_policies",
|
||||
"update_general_log_channel_id",
|
||||
"update_own_settings",
|
||||
"update_other_users_settings",
|
||||
"update_integrations",
|
||||
"update_schedules",
|
||||
"update_custom_actions",
|
||||
"update_api_tokens",
|
||||
"update_teams",
|
||||
"update_maintenances",
|
||||
"update_global_settings",
|
||||
"send_demo_alert",
|
||||
"view_other_users",
|
||||
]
|
||||
ADMIN_PERMISSIONS = ALL_PERMISSIONS
|
||||
EDITOR_PERMISSIONS = ["update_incidents", "update_own_settings", "view_other_users"]
|
||||
ALL_ROLES_PERMISSIONS = []
|
||||
|
|
@ -68,7 +68,7 @@ class UserNotificationPolicyLogRecord(models.Model):
|
|||
ERROR_NOTIFICATION_IN_SLACK_CHANNEL_IS_ARCHIVED,
|
||||
ERROR_NOTIFICATION_IN_SLACK_RATELIMIT,
|
||||
ERROR_NOTIFICATION_MESSAGING_BACKEND_ERROR,
|
||||
ERROR_NOTIFICATION_NOT_ALLOWED_USER_ROLE,
|
||||
ERROR_NOTIFICATION_FORBIDDEN,
|
||||
ERROR_NOTIFICATION_TELEGRAM_USER_IS_DEACTIVATED,
|
||||
) = range(27)
|
||||
|
||||
|
|
@ -258,10 +258,8 @@ class UserNotificationPolicyLogRecord(models.Model):
|
|||
result += f"failed to notify {user_verbal} in Slack, because channel is archived"
|
||||
elif self.notification_error_code == UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_IN_SLACK_RATELIMIT:
|
||||
result += f"failed to notify {user_verbal} in Slack due to Slack rate limit"
|
||||
elif (
|
||||
self.notification_error_code == UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_NOT_ALLOWED_USER_ROLE
|
||||
):
|
||||
result += f"failed to notify {user_verbal}, not allowed role"
|
||||
elif self.notification_error_code == UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_FORBIDDEN:
|
||||
result += f"failed to notify {user_verbal}, not allowed"
|
||||
elif (
|
||||
self.notification_error_code
|
||||
== UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_TELEGRAM_USER_IS_DEACTIVATED
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import json
|
||||
import logging
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
from typing import Dict, List, Optional, Tuple, TypedDict
|
||||
from urllib.parse import urljoin
|
||||
|
||||
import requests
|
||||
|
|
@ -9,23 +9,51 @@ from django.conf import settings
|
|||
from rest_framework import status
|
||||
from rest_framework.response import Response
|
||||
|
||||
from apps.api.permissions import ACTION_PREFIX, GrafanaAPIPermission
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class GrafanaUser(TypedDict):
|
||||
orgId: int
|
||||
userId: int
|
||||
email: str
|
||||
name: str
|
||||
avatarUrl: str
|
||||
login: str
|
||||
role: str
|
||||
lastSeenAt: str
|
||||
lastSeenAtAge: str
|
||||
|
||||
|
||||
class GrafanaUserWithPermissions(GrafanaUser):
|
||||
permissions: List[GrafanaAPIPermission]
|
||||
|
||||
|
||||
class GCOMInstanceInfo(TypedDict):
|
||||
id: int
|
||||
orgId: int
|
||||
slug: str
|
||||
orgSlug: str
|
||||
orgName: str
|
||||
url: str
|
||||
status: str
|
||||
|
||||
|
||||
class APIClient:
|
||||
def __init__(self, api_url: str, api_token: str):
|
||||
self.api_url = api_url
|
||||
self.api_token = api_token
|
||||
|
||||
def api_head(self, endpoint: str, body: dict = None) -> Tuple[Optional[Response], dict]:
|
||||
return self.call_api(endpoint, requests.head, body)
|
||||
|
||||
def api_get(self, endpoint: str) -> Tuple[Optional[Response], dict]:
|
||||
return self.call_api(endpoint, requests.get)
|
||||
|
||||
def api_post(self, endpoint: str, body: dict = None) -> Tuple[Optional[Response], dict]:
|
||||
return self.call_api(endpoint, requests.post, body)
|
||||
|
||||
def api_head(self, endpoint: str, body: dict = None) -> Tuple[Optional[Response], dict]:
|
||||
return self.call_api(endpoint, requests.head, body)
|
||||
|
||||
def call_api(self, endpoint: str, http_method, body: dict = None) -> Tuple[Optional[Response], dict]:
|
||||
request_start = time.perf_counter()
|
||||
call_status = {
|
||||
|
|
@ -72,30 +100,60 @@ class APIClient:
|
|||
|
||||
|
||||
class GrafanaAPIClient(APIClient):
|
||||
USER_PERMISSION_ENDPOINT = f"api/access-control/users/permissions?actionPrefix={ACTION_PREFIX}"
|
||||
|
||||
def __init__(self, api_url: str, api_token: str):
|
||||
super().__init__(api_url, api_token)
|
||||
|
||||
def check_token(self) -> Tuple[Optional[Response], dict]:
|
||||
return self.api_head("api/org")
|
||||
|
||||
def get_users(self) -> Tuple[Optional[Response], dict]:
|
||||
def get_users_permissions(self, rbac_is_enabled_for_org: bool) -> Dict[str, List[GrafanaAPIPermission]]:
|
||||
"""
|
||||
Response example:
|
||||
[
|
||||
{
|
||||
'orgId': 1,
|
||||
'userId': 1,
|
||||
'email': 'user@example.com',
|
||||
'name': 'User User',
|
||||
'avatarUrl': '/avatar/79163f696e9e08958c0d3f73c160e2cc',
|
||||
'login': 'user',
|
||||
'role': 'Admin',
|
||||
'lastSeenAt': '2021-06-21T07:01:45Z',
|
||||
'lastSeenAtAge': '9m'
|
||||
},
|
||||
]
|
||||
It is possible that this endpoint may not be available for certain Grafana orgs.
|
||||
Ex: for Grafana Cloud orgs whom have pinned their Grafana version to an earlier version
|
||||
where this endpoint is not available
|
||||
|
||||
The response from the Grafana endpoint will look something like this:
|
||||
{
|
||||
"1": {
|
||||
"grafana-oncall-app.alert-groups:read": [
|
||||
""
|
||||
],
|
||||
"grafana-oncall-app.alert-groups:write": [
|
||||
""
|
||||
]
|
||||
}
|
||||
}
|
||||
"""
|
||||
return self.api_get("api/org/users")
|
||||
if not rbac_is_enabled_for_org:
|
||||
return {}
|
||||
data, _ = self.api_get(self.USER_PERMISSION_ENDPOINT)
|
||||
if data is None:
|
||||
return {}
|
||||
|
||||
all_users_permissions = {}
|
||||
for user_id, user_permissions in data.items():
|
||||
all_users_permissions[user_id] = [GrafanaAPIPermission(action=key) for key, _ in user_permissions.items()]
|
||||
|
||||
return all_users_permissions
|
||||
|
||||
def is_rbac_enabled_for_organization(self) -> bool:
|
||||
_, resp_status = self.api_head(self.USER_PERMISSION_ENDPOINT)
|
||||
return resp_status["status_code"] == status.HTTP_200_OK
|
||||
|
||||
def get_users(self, rbac_is_enabled_for_org: bool) -> List[GrafanaUserWithPermissions]:
|
||||
users, _ = self.api_get("api/org/users")
|
||||
|
||||
if not users:
|
||||
return []
|
||||
|
||||
user_permissions = self.get_users_permissions(rbac_is_enabled_for_org)
|
||||
|
||||
# merge the users permissions response into the org users response
|
||||
for user in users:
|
||||
user["permissions"] = user_permissions.get(str(user["userId"]), [])
|
||||
return users
|
||||
|
||||
def get_teams(self):
|
||||
return self.api_get("api/teams/search?perpage=1000000")
|
||||
|
|
@ -127,6 +185,7 @@ class GcomAPIClient(APIClient):
|
|||
ACTIVE_INSTANCE_QUERY = "instances?status=active"
|
||||
DELETED_INSTANCE_QUERY = "instances?status=deleted&includeDeleted=true"
|
||||
STACK_STATUS_DELETED = "deleted"
|
||||
STACK_STATUS_ACTIVE = "active"
|
||||
|
||||
def __init__(self, api_token: str):
|
||||
super().__init__(settings.GRAFANA_COM_API_URL, api_token)
|
||||
|
|
@ -134,14 +193,15 @@ class GcomAPIClient(APIClient):
|
|||
def check_token(self):
|
||||
return self.api_post("api-keys/check", {"token": self.api_token})
|
||||
|
||||
def get_instance_info(self, stack_id: str):
|
||||
return self.api_get(f"instances/{stack_id}")
|
||||
def get_instance_info(self, stack_id: str) -> Optional[GCOMInstanceInfo]:
|
||||
data, _ = self.api_get(f"instances/{stack_id}?config=true")
|
||||
return data
|
||||
|
||||
def get_instances(self, query: str):
|
||||
return self.api_get(query)
|
||||
|
||||
def is_stack_deleted(self, stack_id: str) -> bool:
|
||||
instance_info, call_status = self.get_instance_info(stack_id)
|
||||
instance_info = self.get_instance_info(stack_id)
|
||||
return instance_info and instance_info.get("status") == self.STACK_STATUS_DELETED
|
||||
|
||||
def post_active_users(self, body):
|
||||
|
|
|
|||
|
|
@ -40,10 +40,12 @@ def check_gcom_permission(token_string: str, context) -> Optional["GcomToken"]:
|
|||
|
||||
logger.debug(f"Start authenticate by making request to gcom api for org={org_id}, stack_id={stack_id}")
|
||||
client = GcomAPIClient(token_string)
|
||||
instance_info, status = client.get_instance_info(stack_id)
|
||||
instance_info = client.get_instance_info(stack_id)
|
||||
if not instance_info or str(instance_info["orgId"]) != org_id:
|
||||
raise InvalidToken
|
||||
|
||||
rbac_is_enabled = client.is_rbac_enabled_for_organization()
|
||||
|
||||
if not organization:
|
||||
DynamicSetting = apps.get_model("base", "DynamicSetting")
|
||||
allow_signup = DynamicSetting.objects.get_or_create(
|
||||
|
|
@ -60,6 +62,7 @@ def check_gcom_permission(token_string: str, context) -> Optional["GcomToken"]:
|
|||
region_slug=instance_info["regionSlug"],
|
||||
gcom_token=token_string,
|
||||
gcom_token_org_last_time_synced=timezone.now(),
|
||||
is_rbac_permissions_enabled=rbac_is_enabled,
|
||||
)
|
||||
else:
|
||||
organization.stack_slug = instance_info["slug"]
|
||||
|
|
@ -69,6 +72,7 @@ def check_gcom_permission(token_string: str, context) -> Optional["GcomToken"]:
|
|||
organization.grafana_url = instance_info["url"]
|
||||
organization.gcom_token = token_string
|
||||
organization.gcom_token_org_last_time_synced = timezone.now()
|
||||
organization.is_rbac_permissions_enabled = rbac_is_enabled
|
||||
organization.save(
|
||||
update_fields=[
|
||||
"stack_slug",
|
||||
|
|
@ -78,6 +82,7 @@ def check_gcom_permission(token_string: str, context) -> Optional["GcomToken"]:
|
|||
"grafana_url",
|
||||
"gcom_token",
|
||||
"gcom_token_org_last_time_synced",
|
||||
"is_rbac_permissions_enabled",
|
||||
]
|
||||
)
|
||||
logger.debug(f"Finish authenticate by making request to gcom api for org={org_id}, stack_id={stack_id}")
|
||||
|
|
|
|||
|
|
@ -68,8 +68,8 @@ def run_organization_sync(organization_pk, force_sync):
|
|||
return
|
||||
if settings.GRAFANA_COM_API_TOKEN and settings.LICENSE == settings.CLOUD_LICENSE_NAME:
|
||||
client = GcomAPIClient(settings.GRAFANA_COM_API_TOKEN)
|
||||
instance_info, status = client.get_instance_info(organization.stack_id)
|
||||
if not instance_info or instance_info["status"] != "active":
|
||||
instance_info = client.get_instance_info(organization.stack_id)
|
||||
if not instance_info or instance_info["status"] != client.STACK_STATUS_ACTIVE:
|
||||
logger.debug(f"Canceling sync for Organization {organization_pk}, as it is no longer active.")
|
||||
return
|
||||
|
||||
|
|
|
|||
60
engine/apps/grafana_plugin/tests/test_grafana_api_client.py
Normal file
60
engine/apps/grafana_plugin/tests/test_grafana_api_client.py
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from rest_framework import status
|
||||
|
||||
from apps.grafana_plugin.helpers.client import GrafanaAPIClient
|
||||
|
||||
API_URL = "/foo/bar"
|
||||
API_TOKEN = "dfjkfdjkfd"
|
||||
|
||||
|
||||
class TestGetUsersPermissions:
|
||||
def test_rbac_is_not_enabled_for_org(self):
|
||||
api_client = GrafanaAPIClient(API_URL, API_TOKEN)
|
||||
permissions = api_client.get_users_permissions(False)
|
||||
assert len(permissions.keys()) == 0
|
||||
|
||||
@patch("apps.grafana_plugin.views.self_hosted_install.GrafanaAPIClient.api_get")
|
||||
def test_api_call_returns_none(self, mocked_grafana_api_client_api_get):
|
||||
mocked_grafana_api_client_api_get.return_value = (None, "dfkjfdkj")
|
||||
|
||||
api_client = GrafanaAPIClient(API_URL, API_TOKEN)
|
||||
|
||||
permissions = api_client.get_users_permissions(True)
|
||||
assert len(permissions.keys()) == 0
|
||||
|
||||
@patch("apps.grafana_plugin.views.self_hosted_install.GrafanaAPIClient.api_get")
|
||||
def test_it_properly_transforms_the_data(self, mocked_grafana_api_client_api_get):
|
||||
mocked_grafana_api_client_api_get.return_value = (
|
||||
{"1": {"grafana-oncall-app.alert-groups:read": [""], "grafana-oncall-app.alert-groups:write": [""]}},
|
||||
"asdfasdf",
|
||||
)
|
||||
|
||||
api_client = GrafanaAPIClient(API_URL, API_TOKEN)
|
||||
|
||||
permissions = api_client.get_users_permissions(True)
|
||||
assert permissions == {
|
||||
"1": [
|
||||
{"action": "grafana-oncall-app.alert-groups:read"},
|
||||
{"action": "grafana-oncall-app.alert-groups:write"},
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
class TestIsRbacEnabledForOrganization:
|
||||
@pytest.mark.parametrize(
|
||||
"grafana_api_status_code,expected",
|
||||
[
|
||||
(status.HTTP_200_OK, True),
|
||||
(status.HTTP_404_NOT_FOUND, False),
|
||||
],
|
||||
)
|
||||
@patch("apps.grafana_plugin.views.self_hosted_install.GrafanaAPIClient.api_head")
|
||||
def test_it_returns_based_on_status_code_of_head_call(
|
||||
self, mocked_grafana_api_client_api_head, grafana_api_status_code, expected
|
||||
):
|
||||
mocked_grafana_api_client_api_head.return_value = (None, {"status_code": grafana_api_status_code})
|
||||
|
||||
api_client = GrafanaAPIClient(API_URL, API_TOKEN)
|
||||
assert api_client.is_rbac_enabled_for_organization() == expected
|
||||
|
|
@ -99,6 +99,7 @@ def test_if_organization_exists_it_is_updated(
|
|||
|
||||
mocked_provision_plugin.return_value = provision_plugin_response
|
||||
mocked_grafana_api_client.return_value.check_token.return_value = (None, {"status_code": status.HTTP_200_OK})
|
||||
mocked_grafana_api_client.return_value.is_rbac_enabled_for_organization.return_value = True
|
||||
|
||||
client = APIClient()
|
||||
url = reverse("grafana-plugin:self-hosted-install")
|
||||
|
|
@ -106,6 +107,8 @@ def test_if_organization_exists_it_is_updated(
|
|||
|
||||
assert mocked_grafana_api_client.called_once_with(api_url=GRAFANA_API_URL, api_token=GRAFANA_TOKEN)
|
||||
assert mocked_grafana_api_client.return_value.check_token.called_once_with()
|
||||
assert mocked_grafana_api_client.return_value.is_rbac_enabled_for_organization.called_once_with()
|
||||
|
||||
assert mocked_sync_organization.called_once_with(organization)
|
||||
assert mocked_provision_plugin.called_once_with()
|
||||
assert mocked_revoke_plugin.called_once_with()
|
||||
|
|
@ -117,6 +120,7 @@ def test_if_organization_exists_it_is_updated(
|
|||
|
||||
assert organization.grafana_url == GRAFANA_API_URL
|
||||
assert organization.api_token == GRAFANA_TOKEN
|
||||
assert organization.is_rbac_permissions_enabled is True
|
||||
|
||||
|
||||
@override_settings(SELF_HOSTED_SETTINGS=SELF_HOSTED_SETTINGS)
|
||||
|
|
@ -136,6 +140,7 @@ def test_if_organization_does_not_exist_it_is_created(
|
|||
|
||||
mocked_provision_plugin.return_value = provision_plugin_response
|
||||
mocked_grafana_api_client.return_value.check_token.return_value = (None, {"status_code": status.HTTP_200_OK})
|
||||
mocked_grafana_api_client.return_value.is_rbac_enabled_for_organization.return_value = True
|
||||
|
||||
client = APIClient()
|
||||
url = reverse("grafana-plugin:self-hosted-install")
|
||||
|
|
@ -146,6 +151,8 @@ def test_if_organization_does_not_exist_it_is_created(
|
|||
|
||||
assert mocked_grafana_api_client.called_once_with(api_url=GRAFANA_API_URL, api_token=GRAFANA_TOKEN)
|
||||
assert mocked_grafana_api_client.return_value.check_token.called_once_with()
|
||||
assert mocked_grafana_api_client.return_value.is_rbac_enabled_for_organization.called_once_with()
|
||||
|
||||
assert mocked_sync_organization.called_once_with(organization)
|
||||
assert mocked_provision_plugin.called_once_with()
|
||||
assert not mocked_revoke_plugin.called
|
||||
|
|
@ -160,3 +167,4 @@ def test_if_organization_does_not_exist_it_is_created(
|
|||
assert organization.region_slug == REGION_SLUG
|
||||
assert organization.grafana_url == GRAFANA_API_URL
|
||||
assert organization.api_token == GRAFANA_TOKEN
|
||||
assert organization.is_rbac_permissions_enabled is True
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ class TestGcomAPIClient:
|
|||
info = None
|
||||
status = None
|
||||
|
||||
STACK_STATUS_ACTIVE = "active"
|
||||
|
||||
def reset(self):
|
||||
self.called = False
|
||||
self.info = None
|
||||
|
|
@ -39,7 +41,7 @@ class TestGcomAPIClient:
|
|||
|
||||
def get_instance_info(self, stack_id: str):
|
||||
self.called = True
|
||||
return self.info, self.status
|
||||
return self.info
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
|
|
|||
|
|
@ -43,11 +43,14 @@ class SelfHostedInstallView(GrafanaHeadersMixin, APIView):
|
|||
return Response(data=provisioning_info, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
organization = Organization.objects.filter(stack_id=stack_id, org_id=org_id).first()
|
||||
rbac_is_enabled = grafana_api_client.is_rbac_enabled_for_organization()
|
||||
|
||||
if organization:
|
||||
organization.revoke_plugin()
|
||||
organization.grafana_url = grafana_url
|
||||
organization.api_token = grafana_api_token
|
||||
organization.save(update_fields=["grafana_url", "api_token"])
|
||||
organization.is_rbac_permissions_enabled = rbac_is_enabled
|
||||
organization.save(update_fields=["grafana_url", "api_token", "is_rbac_permissions_enabled"])
|
||||
else:
|
||||
organization = Organization.objects.create(
|
||||
stack_id=stack_id,
|
||||
|
|
@ -58,6 +61,7 @@ class SelfHostedInstallView(GrafanaHeadersMixin, APIView):
|
|||
region_slug=settings.SELF_HOSTED_SETTINGS["REGION_SLUG"],
|
||||
grafana_url=grafana_url,
|
||||
api_token=grafana_api_token,
|
||||
is_rbac_permissions_enabled=rbac_is_enabled,
|
||||
)
|
||||
|
||||
sync_organization(organization)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ from apps.base.utils import live_settings
|
|||
from apps.oss_installation.models.cloud_user_identity import CloudUserIdentity
|
||||
from apps.user_management.models import User
|
||||
from common.api_helpers.utils import create_engine_url
|
||||
from common.constants.role import Role
|
||||
from settings.base import GRAFANA_CLOUD_ONCALL_API_URL
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -61,7 +60,7 @@ class CloudConnector(models.Model):
|
|||
logger.warning("Unable to sync with cloud. GRAFANA_CLOUD_ONCALL_TOKEN is not set")
|
||||
error_msg = "GRAFANA_CLOUD_ONCALL_TOKEN is not set"
|
||||
|
||||
existing_emails = list(User.objects.filter(role__in=(Role.ADMIN, Role.EDITOR)).values_list("email", flat=True))
|
||||
existing_emails = [user.email for user in User.objects.all() if user.is_notification_allowed]
|
||||
matching_users = []
|
||||
users_url = create_engine_url("api/v1/users", override_base=GRAFANA_CLOUD_ONCALL_API_URL)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from rest_framework.permissions import IsAuthenticated
|
|||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from apps.api.permissions import IsAdmin
|
||||
from apps.api.permissions import RBACPermission
|
||||
from apps.auth_token.auth import PluginAuthentication
|
||||
from apps.base.models import LiveSetting
|
||||
from apps.base.utils import live_settings
|
||||
|
|
@ -13,7 +13,11 @@ from apps.oss_installation.models import CloudConnector, CloudHeartbeat
|
|||
|
||||
class CloudConnectionView(APIView):
|
||||
authentication_classes = (PluginAuthentication,)
|
||||
permission_classes = (IsAuthenticated, IsAdmin)
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
rbac_permissions = {
|
||||
"get": [RBACPermission.Permissions.OTHER_SETTINGS_WRITE],
|
||||
"delete": [RBACPermission.Permissions.OTHER_SETTINGS_WRITE],
|
||||
}
|
||||
|
||||
def get(self, request):
|
||||
connector = CloudConnector.objects.first()
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from rest_framework.permissions import IsAuthenticated
|
|||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from apps.api.permissions import IsAdmin
|
||||
from apps.api.permissions import RBACPermission
|
||||
from apps.auth_token.auth import PluginAuthentication
|
||||
from apps.oss_installation.cloud_heartbeat import get_heartbeat_link, setup_heartbeat_integration
|
||||
from apps.oss_installation.models import CloudConnector, CloudHeartbeat
|
||||
|
|
@ -11,7 +11,10 @@ from apps.oss_installation.models import CloudConnector, CloudHeartbeat
|
|||
|
||||
class CloudHeartbeatView(APIView):
|
||||
authentication_classes = (PluginAuthentication,)
|
||||
permission_classes = (IsAuthenticated, IsAdmin)
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
rbac_permissions = {
|
||||
"post": [RBACPermission.Permissions.OTHER_SETTINGS_WRITE],
|
||||
}
|
||||
|
||||
def post(self, request):
|
||||
connector = CloudConnector.objects.first()
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from rest_framework.permissions import IsAuthenticated
|
|||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from apps.api.permissions import ActionPermission, AnyRole, IsAdmin, IsOwnerOrAdmin
|
||||
from apps.api.permissions import IsOwnerOrHasRBACPermissions, RBACPermission
|
||||
from apps.auth_token.auth import PluginAuthentication
|
||||
from apps.oss_installation.models import CloudConnector, CloudUserIdentity
|
||||
from apps.oss_installation.serializers import CloudUserSerializer
|
||||
|
|
@ -14,17 +14,26 @@ from apps.oss_installation.utils import cloud_user_identity_status
|
|||
from apps.user_management.models import User
|
||||
from common.api_helpers.mixins import PublicPrimaryKeyMixin
|
||||
from common.api_helpers.paginators import HundredPageSizePaginator
|
||||
from common.constants.role import Role
|
||||
|
||||
PERMISSIONS = [RBACPermission.Permissions.OTHER_SETTINGS_WRITE]
|
||||
|
||||
|
||||
class CloudUsersView(HundredPageSizePaginator, APIView):
|
||||
authentication_classes = (PluginAuthentication,)
|
||||
permission_classes = (IsAuthenticated, IsAdmin)
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
|
||||
rbac_permissions = {
|
||||
"get": PERMISSIONS,
|
||||
"post": PERMISSIONS,
|
||||
}
|
||||
|
||||
def get(self, request):
|
||||
organization = request.user.organization
|
||||
|
||||
queryset = User.objects.filter(organization=organization, role__in=[Role.ADMIN, Role.EDITOR])
|
||||
queryset = User.objects.filter(
|
||||
organization=organization,
|
||||
**User.build_permissions_query(RBACPermission.Permissions.NOTIFICATIONS_READ, organization),
|
||||
)
|
||||
|
||||
if request.user.current_team is not None:
|
||||
queryset = queryset.filter(teams=request.user.current_team).distinct()
|
||||
|
|
@ -81,15 +90,24 @@ class CloudUserView(
|
|||
viewsets.GenericViewSet,
|
||||
):
|
||||
authentication_classes = (PluginAuthentication,)
|
||||
permission_classes = (IsAuthenticated, ActionPermission)
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
|
||||
action_permissions = {
|
||||
AnyRole: ("retrieve",),
|
||||
IsAdmin: ("sync",),
|
||||
rbac_permissions = {
|
||||
"retrieve": PERMISSIONS,
|
||||
"sync": PERMISSIONS,
|
||||
}
|
||||
action_object_permissions = {
|
||||
IsOwnerOrAdmin: ("retrieve", "sync"),
|
||||
|
||||
IsOwnerOrHasUserSettingsAdminPermission = IsOwnerOrHasRBACPermissions(
|
||||
[RBACPermission.Permissions.USER_SETTINGS_ADMIN]
|
||||
)
|
||||
|
||||
rbac_object_permissions = {
|
||||
IsOwnerOrHasUserSettingsAdminPermission: [
|
||||
"retrieve",
|
||||
"sync",
|
||||
],
|
||||
}
|
||||
|
||||
serializer_class = CloudUserSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
from rest_framework import serializers
|
||||
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
from apps.slack.models import SlackUserIdentity
|
||||
from apps.user_management.models import User
|
||||
from common.api_helpers.mixins import EagerLoadingMixin
|
||||
from common.constants.role import Role
|
||||
|
||||
|
||||
class SlackUserIdentitySerializer(serializers.ModelSerializer):
|
||||
|
|
@ -21,7 +21,7 @@ class SlackUserIdentitySerializer(serializers.ModelSerializer):
|
|||
class FastUserSerializer(serializers.ModelSerializer):
|
||||
id = serializers.ReadOnlyField(read_only=True, source="public_primary_key")
|
||||
email = serializers.EmailField(read_only=True)
|
||||
role = serializers.SerializerMethodField()
|
||||
role = serializers.SerializerMethodField() # LEGACY, should be removed eventually
|
||||
is_phone_number_verified = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
|
|
@ -30,7 +30,10 @@ class FastUserSerializer(serializers.ModelSerializer):
|
|||
|
||||
@staticmethod
|
||||
def get_role(obj):
|
||||
return Role(obj.role).name.lower()
|
||||
"""
|
||||
LEGACY, should be removed eventually
|
||||
"""
|
||||
return LegacyAccessControlRole(obj.role).name.lower()
|
||||
|
||||
def get_is_phone_number_verified(self, obj):
|
||||
return obj.verified_phone_number is not None
|
||||
|
|
@ -39,8 +42,8 @@ class FastUserSerializer(serializers.ModelSerializer):
|
|||
class UserSerializer(serializers.ModelSerializer, EagerLoadingMixin):
|
||||
id = serializers.ReadOnlyField(read_only=True, source="public_primary_key")
|
||||
email = serializers.EmailField(read_only=True)
|
||||
role = serializers.SerializerMethodField()
|
||||
slack = SlackUserIdentitySerializer(read_only=True, source="slack_user_identity")
|
||||
role = serializers.SerializerMethodField() # LEGACY, should be removed eventually
|
||||
is_phone_number_verified = serializers.SerializerMethodField()
|
||||
|
||||
SELECT_RELATED = [
|
||||
|
|
@ -54,7 +57,10 @@ class UserSerializer(serializers.ModelSerializer, EagerLoadingMixin):
|
|||
|
||||
@staticmethod
|
||||
def get_role(obj):
|
||||
return Role(obj.role).name.lower()
|
||||
"""
|
||||
LEGACY, should be removed eventually
|
||||
"""
|
||||
return LegacyAccessControlRole(obj.role).name.lower()
|
||||
|
||||
def get_is_phone_number_verified(self, obj):
|
||||
return obj.verified_phone_number is not None
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from django.urls import reverse
|
|||
from rest_framework import status
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from common.constants.role import Role
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
|
|
@ -20,7 +20,7 @@ def user_public_api_setup(
|
|||
def test_get_user(
|
||||
user_public_api_setup,
|
||||
):
|
||||
organization, user, token, slack_team_identity, slack_user_identity = user_public_api_setup
|
||||
_, user, token, slack_team_identity, slack_user_identity = user_public_api_setup
|
||||
|
||||
client = APIClient()
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ def test_get_users_list_short(
|
|||
user_public_api_setup,
|
||||
make_user_for_organization,
|
||||
):
|
||||
organization, user_1, token, slack_team_identity, slack_user_identity = user_public_api_setup
|
||||
organization, user_1, token, _, _ = user_public_api_setup
|
||||
user_2 = make_user_for_organization(organization)
|
||||
|
||||
client = APIClient()
|
||||
|
|
@ -145,13 +145,10 @@ def test_forbidden_access(
|
|||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_get_users_list_all_role_users(
|
||||
user_public_api_setup,
|
||||
make_user_for_organization,
|
||||
):
|
||||
def test_get_users_list_all_role_users(user_public_api_setup, make_user_for_organization):
|
||||
organization, admin, token, _, _ = user_public_api_setup
|
||||
editor = make_user_for_organization(organization, role=Role.EDITOR)
|
||||
viewer = make_user_for_organization(organization, role=Role.VIEWER)
|
||||
editor = make_user_for_organization(organization, role=LegacyAccessControlRole.EDITOR)
|
||||
viewer = make_user_for_organization(organization, role=LegacyAccessControlRole.VIEWER)
|
||||
|
||||
client = APIClient()
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from rest_framework.permissions import IsAuthenticated
|
|||
from rest_framework.views import Response
|
||||
from rest_framework.viewsets import ReadOnlyModelViewSet
|
||||
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
from apps.auth_token.auth import ApiTokenAuthentication, UserScheduleExportAuthentication
|
||||
from apps.public_api.custom_renderers import CalendarRenderer
|
||||
from apps.public_api.serializers import FastUserSerializer, UserSerializer
|
||||
|
|
@ -14,7 +15,6 @@ from apps.schedules.models import OnCallSchedule
|
|||
from apps.user_management.models import User
|
||||
from common.api_helpers.mixins import RateLimitHeadersMixin, ShortSerializerMixin
|
||||
from common.api_helpers.paginators import HundredPageSizePaginator
|
||||
from common.constants.role import Role
|
||||
|
||||
|
||||
class UserFilter(filters.FilterSet):
|
||||
|
|
@ -23,7 +23,9 @@ class UserFilter(filters.FilterSet):
|
|||
"""
|
||||
|
||||
email = filters.CharFilter(field_name="email", lookup_expr="iexact")
|
||||
roles = filters.MultipleChoiceFilter(field_name="role", choices=Role.choices())
|
||||
roles = filters.MultipleChoiceFilter(
|
||||
field_name="role", choices=LegacyAccessControlRole.choices()
|
||||
) # LEGACY, should be removed eventually
|
||||
username = filters.CharFilter(field_name="username", lookup_expr="iexact")
|
||||
|
||||
class Meta:
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ from django.db.models import Q
|
|||
from django.utils import timezone
|
||||
from icalendar import Calendar
|
||||
|
||||
from apps.api.permissions import RBACPermission
|
||||
from apps.schedules.constants import (
|
||||
ICAL_ATTENDEE,
|
||||
ICAL_DATETIME_END,
|
||||
|
|
@ -25,7 +26,6 @@ from apps.schedules.constants import (
|
|||
RE_PRIORITY,
|
||||
)
|
||||
from apps.schedules.ical_events import ical_events
|
||||
from common.constants.role import Role
|
||||
from common.utils import timed_lru_cache
|
||||
|
||||
"""
|
||||
|
|
@ -40,11 +40,16 @@ if TYPE_CHECKING:
|
|||
def users_in_ical(usernames_from_ical, organization, include_viewers=False):
|
||||
"""
|
||||
Parse ical file and return list of users found
|
||||
NOTE: only grafana username will be used, consider adding grafana email and id
|
||||
"""
|
||||
# Only grafana username will be used, consider adding grafana email and id
|
||||
from apps.user_management.models import User
|
||||
|
||||
users_found_in_ical = organization.users
|
||||
if not include_viewers:
|
||||
users_found_in_ical = users_found_in_ical.filter(role__in=(Role.ADMIN, Role.EDITOR))
|
||||
# TODO: this is a breaking change....
|
||||
users_found_in_ical = users_found_in_ical.filter(
|
||||
**User.build_permissions_query(RBACPermission.Permissions.SCHEDULES_WRITE, organization)
|
||||
)
|
||||
|
||||
user_emails = [v.lower() for v in usernames_from_ical]
|
||||
users_found_in_ical = users_found_in_ical.filter(
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import pytest
|
|||
import pytz
|
||||
from django.utils import timezone
|
||||
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
from apps.schedules.ical_utils import (
|
||||
list_of_oncall_shifts_from_ical,
|
||||
list_users_to_notify_from_ical,
|
||||
|
|
@ -12,7 +13,6 @@ from apps.schedules.ical_utils import (
|
|||
users_in_ical,
|
||||
)
|
||||
from apps.schedules.models import CustomOnCallShift, OnCallScheduleCalendar
|
||||
from common.constants.role import Role
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
|
@ -26,13 +26,10 @@ def test_users_in_ical_email_case_insensitive(make_organization_and_user, make_u
|
|||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"include_viewers",
|
||||
[True, False],
|
||||
)
|
||||
@pytest.mark.parametrize("include_viewers", [True, False])
|
||||
def test_users_in_ical_viewers_inclusion(make_organization_and_user, make_user_for_organization, include_viewers):
|
||||
organization, user = make_organization_and_user()
|
||||
viewer = make_user_for_organization(organization, Role.VIEWER)
|
||||
viewer = make_user_for_organization(organization, role=LegacyAccessControlRole.VIEWER)
|
||||
|
||||
usernames = [user.username, viewer.username]
|
||||
result = users_in_ical(usernames, organization, include_viewers=include_viewers)
|
||||
|
|
@ -43,15 +40,12 @@ def test_users_in_ical_viewers_inclusion(make_organization_and_user, make_user_f
|
|||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"include_viewers",
|
||||
[True, False],
|
||||
)
|
||||
@pytest.mark.parametrize("include_viewers", [True, False])
|
||||
def test_list_users_to_notify_from_ical_viewers_inclusion(
|
||||
make_organization_and_user, make_user_for_organization, make_schedule, make_on_call_shift, include_viewers
|
||||
):
|
||||
organization, user = make_organization_and_user()
|
||||
viewer = make_user_for_organization(organization, Role.VIEWER)
|
||||
viewer = make_user_for_organization(organization, role=LegacyAccessControlRole.VIEWER)
|
||||
|
||||
schedule = make_schedule(organization, schedule_class=OnCallScheduleCalendar)
|
||||
date = timezone.now().replace(tzinfo=None, microsecond=0)
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ import pytest
|
|||
import pytz
|
||||
from django.utils import timezone
|
||||
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
from apps.schedules.ical_utils import memoized_users_in_ical
|
||||
from apps.schedules.models import CustomOnCallShift, OnCallSchedule, OnCallScheduleCalendar, OnCallScheduleWeb
|
||||
from common.constants.role import Role
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
|
@ -18,7 +18,7 @@ def test_filter_events(make_organization, make_user_for_organization, make_sched
|
|||
name="test_web_schedule",
|
||||
)
|
||||
user = make_user_for_organization(organization)
|
||||
viewer = make_user_for_organization(organization, role=Role.VIEWER)
|
||||
viewer = make_user_for_organization(organization, role=LegacyAccessControlRole.VIEWER)
|
||||
now = timezone.now().replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
start_date = now - timezone.timedelta(days=7)
|
||||
|
||||
|
|
@ -190,7 +190,7 @@ def test_filter_events_include_empty(make_organization, make_user_for_organizati
|
|||
schedule_class=OnCallScheduleWeb,
|
||||
name="test_web_schedule",
|
||||
)
|
||||
user = make_user_for_organization(organization, role=Role.VIEWER)
|
||||
user = make_user_for_organization(organization, role=LegacyAccessControlRole.VIEWER)
|
||||
now = timezone.now().replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
start_date = now - timezone.timedelta(days=7)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,10 +4,11 @@ from django.apps import apps
|
|||
from django.db import models
|
||||
from django.db.models import JSONField
|
||||
|
||||
from apps.api.permissions import RBACPermission
|
||||
from apps.slack.constants import SLACK_INVALID_AUTH_RESPONSE, SLACK_WRONG_TEAM_NAMES
|
||||
from apps.slack.slack_client import SlackClientWithErrorHandling
|
||||
from apps.slack.slack_client.exceptions import SlackAPIException, SlackAPITokenException
|
||||
from common.constants.role import Role
|
||||
from apps.user_management.models.user import User
|
||||
from common.insight_log.chatops_insight_logs import ChatOpsEvent, ChatOpsType, write_chatops_insight_log
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -127,8 +128,10 @@ class SlackTeamIdentity(models.Model):
|
|||
sc = SlackClientWithErrorHandling(self.bot_access_token)
|
||||
members = self.get_conversation_members(sc, channel_id)
|
||||
|
||||
users = organization.users.filter(slack_user_identity__slack_id__in=members, role__in=[Role.ADMIN, Role.EDITOR])
|
||||
return users
|
||||
return organization.users.filter(
|
||||
slack_user_identity__slack_id__in=members,
|
||||
**User.build_permissions_query(RBACPermission.Permissions.CHATOPS_WRITE, organization),
|
||||
)
|
||||
|
||||
def get_conversation_members(self, slack_client, channel_id):
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -7,9 +7,10 @@ from django.db import models
|
|||
from django.db.models import JSONField
|
||||
from django.utils import timezone
|
||||
|
||||
from apps.api.permissions import RBACPermission
|
||||
from apps.slack.slack_client import SlackClientWithErrorHandling
|
||||
from apps.slack.slack_client.exceptions import SlackAPIException
|
||||
from common.constants.role import Role
|
||||
from apps.user_management.models.user import User
|
||||
from common.public_primary_keys import generate_public_primary_key, increase_public_primary_key_length
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -105,7 +106,8 @@ class SlackUserGroup(models.Model):
|
|||
|
||||
def get_users_from_members_for_organization(self, organization):
|
||||
return organization.users.filter(
|
||||
slack_user_identity__slack_id__in=self.members, role__in=[Role.ADMIN, Role.EDITOR]
|
||||
slack_user_identity__slack_id__in=self.members,
|
||||
**User.build_permissions_query(RBACPermission.Permissions.CHATOPS_WRITE, organization),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ from django.db import transaction
|
|||
from jinja2 import TemplateSyntaxError
|
||||
from rest_framework.response import Response
|
||||
|
||||
from apps.api.permissions import RBACPermission
|
||||
from apps.slack.scenarios import scenario_step
|
||||
from common.constants.role import Role
|
||||
from common.insight_log import EntityEvent, write_resource_insight_log
|
||||
from common.jinja_templater import jinja_template_env
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ class OpenAlertAppearanceDialogStep(
|
|||
scenario_step.ScenarioStep.TAG_INCIDENT_ROUTINE,
|
||||
]
|
||||
|
||||
ALLOWED_ROLES = [Role.ADMIN]
|
||||
REQUIRED_PERMISSIONS = [RBACPermission.Permissions.CHATOPS_WRITE]
|
||||
ACTION_VERBOSE = "open Alert Appearance"
|
||||
|
||||
def process_scenario(self, slack_user_identity, slack_team_identity, payload, action=None):
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ from apps.alerts.incident_appearance.renderers.slack_renderer import AlertSlackR
|
|||
from apps.alerts.models import AlertGroup, AlertGroupLogRecord, AlertReceiveChannel, Invitation
|
||||
from apps.alerts.tasks import custom_button_result
|
||||
from apps.alerts.utils import render_curl_command
|
||||
from apps.api.permissions import RBACPermission
|
||||
from apps.slack.constants import CACHE_UPDATE_INCIDENT_SLACK_MESSAGE_LIFETIME, SLACK_RATE_LIMIT_DELAY
|
||||
from apps.slack.scenarios import scenario_step
|
||||
from apps.slack.scenarios.slack_renderer import AlertGroupLogSlackRenderer
|
||||
|
|
@ -31,7 +32,6 @@ from apps.slack.tasks import (
|
|||
update_incident_slack_message,
|
||||
)
|
||||
from apps.slack.utils import get_cache_key_update_incident_slack_message
|
||||
from common.constants.role import Role
|
||||
from common.utils import clean_markup, is_string_with_visible_characters
|
||||
|
||||
from .step_mixins import CheckAlertIsUnarchivedMixin, IncidentActionsAccessControlMixin
|
||||
|
|
@ -222,7 +222,7 @@ class InviteOtherPersonToIncident(
|
|||
scenario_step.ScenarioStep.TAG_INCIDENT_ROUTINE,
|
||||
]
|
||||
|
||||
ALLOWED_ROLES = [Role.ADMIN, Role.EDITOR]
|
||||
REQUIRED_PERMISSIONS = [RBACPermission.Permissions.CHATOPS_WRITE]
|
||||
ACTION_VERBOSE = "invite to incident"
|
||||
|
||||
def process_scenario(self, slack_user_identity, slack_team_identity, payload, action=None):
|
||||
|
|
@ -263,7 +263,7 @@ class SilenceGroupStep(
|
|||
scenario_step.ScenarioStep.TAG_INCIDENT_ROUTINE,
|
||||
]
|
||||
|
||||
ALLOWED_ROLES = [Role.ADMIN, Role.EDITOR]
|
||||
REQUIRED_PERMISSIONS = [RBACPermission.Permissions.CHATOPS_WRITE]
|
||||
ACTION_VERBOSE = "silence incident"
|
||||
|
||||
def process_scenario(self, slack_user_identity, slack_team_identity, payload, action=None):
|
||||
|
|
@ -293,7 +293,7 @@ class UnSilenceGroupStep(
|
|||
scenario_step.ScenarioStep.TAG_INCIDENT_ROUTINE,
|
||||
]
|
||||
|
||||
ALLOWED_ROLES = [Role.ADMIN, Role.EDITOR]
|
||||
REQUIRED_PERMISSIONS = [RBACPermission.Permissions.CHATOPS_WRITE]
|
||||
ACTION_VERBOSE = "unsilence incident"
|
||||
|
||||
def process_scenario(self, slack_user_identity, slack_team_identity, payload, action=None):
|
||||
|
|
@ -317,7 +317,7 @@ class SelectAttachGroupStep(
|
|||
scenario_step.ScenarioStep.TAG_INCIDENT_ROUTINE,
|
||||
]
|
||||
|
||||
ALLOWED_ROLES = [Role.ADMIN, Role.EDITOR]
|
||||
REQUIRED_PERMISSIONS = [RBACPermission.Permissions.CHATOPS_WRITE]
|
||||
ACTION_VERBOSE = "Select Incident for Attaching to"
|
||||
|
||||
def process_scenario(self, slack_user_identity, slack_team_identity, payload, action=None):
|
||||
|
|
@ -473,7 +473,7 @@ class AttachGroupStep(
|
|||
scenario_step.ScenarioStep.TAG_INCIDENT_ROUTINE,
|
||||
]
|
||||
|
||||
ALLOWED_ROLES = [Role.ADMIN, Role.EDITOR]
|
||||
REQUIRED_PERMISSIONS = [RBACPermission.Permissions.CHATOPS_WRITE]
|
||||
ACTION_VERBOSE = "Attach incident"
|
||||
|
||||
def process_signal(self, log_record):
|
||||
|
|
@ -536,7 +536,7 @@ class UnAttachGroupStep(
|
|||
scenario_step.ScenarioStep.TAG_INCIDENT_ROUTINE,
|
||||
]
|
||||
|
||||
ALLOWED_ROLES = [Role.ADMIN, Role.EDITOR]
|
||||
REQUIRED_PERMISSIONS = [RBACPermission.Permissions.CHATOPS_WRITE]
|
||||
ACTION_VERBOSE = "Unattach incident"
|
||||
|
||||
def process_scenario(self, slack_user_identity, slack_team_identity, payload, action=None):
|
||||
|
|
@ -555,7 +555,7 @@ class StopInvitationProcess(CheckAlertIsUnarchivedMixin, IncidentActionsAccessCo
|
|||
scenario_step.ScenarioStep.TAG_INCIDENT_ROUTINE,
|
||||
]
|
||||
|
||||
ALLOWED_ROLES = [Role.ADMIN, Role.EDITOR]
|
||||
REQUIRED_PERMISSIONS = [RBACPermission.Permissions.CHATOPS_WRITE]
|
||||
ACTION_VERBOSE = "stop invitation"
|
||||
|
||||
def process_scenario(self, slack_user_identity, slack_team_identity, payload, action=None):
|
||||
|
|
@ -580,7 +580,8 @@ class CustomButtonProcessStep(
|
|||
scenario_step.ScenarioStep.TAG_INCIDENT_ROUTINE,
|
||||
]
|
||||
|
||||
ALLOWED_ROLES = [Role.ADMIN, Role.EDITOR]
|
||||
# TODO:
|
||||
REQUIRED_PERMISSIONS = [RBACPermission.Permissions.CHATOPS_WRITE]
|
||||
ACTION_VERBOSE = "click custom button"
|
||||
|
||||
def process_scenario(self, slack_user_identity, slack_team_identity, payload, action=None):
|
||||
|
|
@ -642,7 +643,7 @@ class ResolveGroupStep(
|
|||
scenario_step.ScenarioStep.TAG_INCIDENT_ROUTINE,
|
||||
]
|
||||
|
||||
ALLOWED_ROLES = [Role.ADMIN, Role.EDITOR]
|
||||
REQUIRED_PERMISSIONS = [RBACPermission.Permissions.CHATOPS_WRITE]
|
||||
ACTION_VERBOSE = "resolve incident"
|
||||
|
||||
def process_scenario(self, slack_user_identity, slack_team_identity, payload, action=None):
|
||||
|
|
@ -688,7 +689,7 @@ class UnResolveGroupStep(
|
|||
scenario_step.ScenarioStep.TAG_INCIDENT_ROUTINE,
|
||||
]
|
||||
|
||||
ALLOWED_ROLES = [Role.ADMIN, Role.EDITOR]
|
||||
REQUIRED_PERMISSIONS = [RBACPermission.Permissions.CHATOPS_WRITE]
|
||||
ACTION_VERBOSE = "unresolve incident"
|
||||
|
||||
def process_scenario(self, slack_user_identity, slack_team_identity, payload, action=None):
|
||||
|
|
@ -711,7 +712,7 @@ class AcknowledgeGroupStep(
|
|||
scenario_step.ScenarioStep.TAG_INCIDENT_ROUTINE,
|
||||
]
|
||||
|
||||
ALLOWED_ROLES = [Role.ADMIN, Role.EDITOR]
|
||||
REQUIRED_PERMISSIONS = [RBACPermission.Permissions.CHATOPS_WRITE]
|
||||
ACTION_VERBOSE = "acknowledge incident"
|
||||
|
||||
def process_scenario(self, slack_user_identity, slack_team_identity, payload, action=None):
|
||||
|
|
@ -737,7 +738,7 @@ class UnAcknowledgeGroupStep(
|
|||
scenario_step.ScenarioStep.TAG_INCIDENT_ROUTINE,
|
||||
]
|
||||
|
||||
ALLOWED_ROLES = [Role.ADMIN, Role.EDITOR]
|
||||
REQUIRED_PERMISSIONS = [RBACPermission.Permissions.CHATOPS_WRITE]
|
||||
ACTION_VERBOSE = "unacknowledge incident"
|
||||
|
||||
def process_scenario(self, slack_user_identity, slack_team_identity, payload, action=None):
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ from apps.slack.slack_client.exceptions import (
|
|||
SlackAPIRateLimitException,
|
||||
SlackAPITokenException,
|
||||
)
|
||||
from common.constants.role import Role
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -162,15 +161,6 @@ class ScenarioStep(object):
|
|||
step = step_class(slack_team_identity)
|
||||
step.process_scenario(slack_user_identity, slack_team_identity, payload, action=action, **kwargs)
|
||||
|
||||
def get_permission_denied_prompt(self):
|
||||
current_role = self.user.get_role_display()
|
||||
admins_queryset = self.organization.users.filter(role=Role.ADMIN).select_related("slack_user_identity")
|
||||
admins_verbal = "No admins"
|
||||
if admins_queryset.count() > 0:
|
||||
admins_verbal = ", ".join(["<@{}>".format(admin.slack_user_identity.slack_id) for admin in admins_queryset])
|
||||
|
||||
return current_role, admins_verbal
|
||||
|
||||
def open_warning_window(self, payload, warning_text, title=None):
|
||||
if title is None:
|
||||
title = ":warning: Warning"
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import logging
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
from apps.api.permissions import user_is_authorized
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AccessControl(ABC):
|
||||
ALLOWED_ROLES = []
|
||||
REQUIRED_PERMISSIONS = []
|
||||
ACTION_VERBOSE = ""
|
||||
|
||||
def dispatch(self, slack_user_identity, slack_team_identity, payload, action=None):
|
||||
|
|
@ -15,7 +17,7 @@ class AccessControl(ABC):
|
|||
self.send_denied_message(payload)
|
||||
|
||||
def check_membership(self):
|
||||
return self.user.role in self.ALLOWED_ROLES
|
||||
return user_is_authorized(self.user, self.REQUIRED_PERMISSIONS)
|
||||
|
||||
@abstractmethod
|
||||
def send_denied_message(self, payload):
|
||||
|
|
@ -62,9 +64,7 @@ class IncidentActionsAccessControlMixin(AccessControl):
|
|||
|
||||
|
||||
class CheckAlertIsUnarchivedMixin(object):
|
||||
|
||||
ALLOWED_ROLES = []
|
||||
|
||||
REQUIRED_PERMISSIONS = []
|
||||
ACTION_VERBOSE = ""
|
||||
|
||||
def check_alert_is_unarchived(self, slack_team_identity, payload, alert_group, warning=True):
|
||||
|
|
|
|||
|
|
@ -7,24 +7,24 @@ from rest_framework import status
|
|||
from rest_framework.response import Response
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from common.constants.role import Role
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(Role.ADMIN, status.HTTP_200_OK),
|
||||
(Role.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(Role.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
|
||||
],
|
||||
)
|
||||
def test_reset_slack_integration_permissions(
|
||||
make_organization_and_user_with_plugin_token, role, expected_status, load_slack_urls, make_user_auth_headers
|
||||
make_organization_and_user_with_plugin_token, load_slack_urls, make_user_auth_headers, role, expected_status
|
||||
):
|
||||
settings.FEATURE_SLACK_INTEGRATION_ENABLED = True
|
||||
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
_, user, token = make_organization_and_user_with_plugin_token(role=role)
|
||||
client = APIClient()
|
||||
|
||||
url = reverse("reset-slack")
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ from rest_framework.permissions import IsAuthenticated
|
|||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from apps.api.permissions import IsAdmin, MethodPermission
|
||||
from apps.api.permissions import RBACPermission
|
||||
from apps.auth_token.auth import PluginAuthentication
|
||||
from apps.base.utils import live_settings
|
||||
from apps.slack.scenarios.alertgroup_appearance import STEPS_ROUTING as ALERTGROUP_APPEARANCE_ROUTING
|
||||
|
|
@ -533,10 +533,12 @@ class SlackEventApiEndpointView(APIView):
|
|||
|
||||
class ResetSlackView(APIView):
|
||||
|
||||
permission_classes = (IsAuthenticated, MethodPermission)
|
||||
permission_classes = (IsAuthenticated, RBACPermission)
|
||||
authentication_classes = [PluginAuthentication]
|
||||
|
||||
method_permissions = {IsAdmin: {"POST"}}
|
||||
rbac_permissions = {
|
||||
"post": [RBACPermission.Permissions.CHATOPS_UPDATE_SETTINGS],
|
||||
}
|
||||
|
||||
def post(self, request):
|
||||
organization = request.auth.organization
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@ from typing import Callable, Optional, Tuple
|
|||
|
||||
from apps.alerts.constants import ActionSource
|
||||
from apps.alerts.models import AlertGroup
|
||||
from apps.api.permissions import RBACPermission, user_is_authorized
|
||||
from apps.telegram.models import TelegramToUserConnector
|
||||
from apps.telegram.renderers.keyboard import Action
|
||||
from apps.telegram.updates.update_handlers import UpdateHandler
|
||||
from apps.telegram.utils import CallbackQueryFactory
|
||||
from apps.user_management.models import User
|
||||
from common.constants.role import Role
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -58,7 +58,8 @@ class ButtonPressHandler(UpdateHandler):
|
|||
if not user:
|
||||
return False
|
||||
|
||||
return user.organization == alert_group.channel.organization and user.role in [Role.ADMIN, Role.EDITOR]
|
||||
has_permission = user_is_authorized(user, [RBACPermission.Permissions.CHATOPS_WRITE])
|
||||
return user.organization == alert_group.channel.organization and has_permission
|
||||
|
||||
@staticmethod
|
||||
def _get_action_context(data: str) -> ActionContext:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 3.2.15 on 2022-10-25 11:18
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('user_management', '0004_auto_20221025_0316'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='organization',
|
||||
name='is_rbac_permissions_enabled',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='permissions',
|
||||
field=models.JSONField(default=list),
|
||||
),
|
||||
]
|
||||
|
|
@ -192,6 +192,7 @@ class Organization(MaintainableObject):
|
|||
pricing_version = models.PositiveIntegerField(choices=PRICING_CHOICES, default=FREE_PUBLIC_BETA_PRICING)
|
||||
|
||||
is_amixr_migration_started = models.BooleanField(default=False)
|
||||
is_rbac_permissions_enabled = models.BooleanField(default=False)
|
||||
|
||||
class Meta:
|
||||
unique_together = ("stack_id", "org_id")
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import json
|
||||
import logging
|
||||
import typing
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from django.apps import apps
|
||||
|
|
@ -9,13 +11,26 @@ from django.db.models.signals import post_save
|
|||
from django.dispatch import receiver
|
||||
from emoji import demojize
|
||||
|
||||
from apps.api.permissions import (
|
||||
LegacyAccessControlCompatiblePermission,
|
||||
LegacyAccessControlRole,
|
||||
RBACPermission,
|
||||
user_is_authorized,
|
||||
)
|
||||
from apps.schedules.tasks import drop_cached_ical_for_custom_events_for_organization
|
||||
from common.constants.role import Role
|
||||
from common.public_primary_keys import generate_public_primary_key, increase_public_primary_key_length
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PermissionsRegexQuery(typing.TypedDict):
|
||||
permissions__regex: str
|
||||
|
||||
|
||||
class RoleInQuery(typing.TypedDict):
|
||||
role__in: typing.List[int]
|
||||
|
||||
|
||||
def generate_public_primary_key_for_user():
|
||||
prefix = "U"
|
||||
new_public_primary_key = generate_public_primary_key(prefix)
|
||||
|
|
@ -60,8 +75,9 @@ class UserManager(models.Manager):
|
|||
email=user["email"],
|
||||
name=user["name"],
|
||||
username=user["login"],
|
||||
role=Role[user["role"].upper()],
|
||||
role=LegacyAccessControlRole[user["role"].upper()],
|
||||
avatar_url=user["avatarUrl"],
|
||||
permissions=user["permissions"],
|
||||
)
|
||||
for user in grafana_users.values()
|
||||
if user["userId"] not in existing_user_ids
|
||||
|
|
@ -76,23 +92,31 @@ class UserManager(models.Manager):
|
|||
users_to_update = []
|
||||
for user in organization.users.filter(user_id__in=existing_user_ids):
|
||||
grafana_user = grafana_users[user.user_id]
|
||||
g_user_role = Role[grafana_user["role"].upper()]
|
||||
g_user_role = LegacyAccessControlRole[grafana_user["role"].upper()]
|
||||
|
||||
if (
|
||||
user.email != grafana_user["email"]
|
||||
or user.name != grafana_user["name"]
|
||||
or user.username != grafana_user["login"]
|
||||
or user.role != g_user_role
|
||||
or user.avatar_url != grafana_user["avatarUrl"]
|
||||
# instead of looping through the array of permission objects, simply take the hash
|
||||
# of the string representation of the data structures and compare.
|
||||
# Need to first convert the lists of objects to strings because lists/dicts are not hashable
|
||||
# (because lists and dicts are not hashable.. as they are mutable)
|
||||
# https://stackoverflow.com/a/22003440
|
||||
or hash(json.dumps(user.permissions)) != hash(json.dumps(grafana_user["permissions"]))
|
||||
):
|
||||
user.email = grafana_user["email"]
|
||||
user.name = grafana_user["name"]
|
||||
user.username = grafana_user["login"]
|
||||
user.role = g_user_role
|
||||
user.avatar_url = grafana_user["avatarUrl"]
|
||||
user.permissions = grafana_user["permissions"]
|
||||
users_to_update.append(user)
|
||||
|
||||
organization.users.bulk_update(
|
||||
users_to_update, ["email", "name", "username", "role", "avatar_url"], batch_size=5000
|
||||
users_to_update, ["email", "name", "username", "role", "avatar_url", "permissions"], batch_size=5000
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -135,7 +159,7 @@ class User(models.Model):
|
|||
email = models.EmailField()
|
||||
name = models.CharField(max_length=300)
|
||||
username = models.CharField(max_length=300)
|
||||
role = models.PositiveSmallIntegerField(choices=Role.choices())
|
||||
role = models.PositiveSmallIntegerField(choices=LegacyAccessControlRole.choices())
|
||||
avatar_url = models.URLField()
|
||||
|
||||
# don't use "_timezone" directly, use the "timezone" property since it can be populated via slack user identity
|
||||
|
|
@ -154,6 +178,7 @@ class User(models.Model):
|
|||
|
||||
# is_active = None is used to be able to have multiple deleted users with the same user_id
|
||||
is_active = models.BooleanField(null=True, default=True)
|
||||
permissions = models.JSONField(null=False, default=list)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.pk}: {self.username}"
|
||||
|
|
@ -187,13 +212,14 @@ class User(models.Model):
|
|||
return hasattr(self, "telegram_connection")
|
||||
|
||||
def self_or_admin(self, user_to_check, organization) -> bool:
|
||||
has_admin_permission = user_is_authorized(user_to_check, [RBACPermission.Permissions.USER_SETTINGS_ADMIN])
|
||||
return user_to_check.pk == self.pk or (
|
||||
user_to_check.role == Role.ADMIN and organization.pk == user_to_check.organization_id
|
||||
has_admin_permission and organization.pk == user_to_check.organization_id
|
||||
)
|
||||
|
||||
@property
|
||||
def is_notification_allowed(self):
|
||||
return self.role in (Role.ADMIN, Role.EDITOR)
|
||||
return user_is_authorized(self, [RBACPermission.Permissions.NOTIFICATIONS_READ])
|
||||
|
||||
# using in-memory cache instead of redis to avoid pickling python objects
|
||||
# @timed_lru_cache(timeout=100)
|
||||
|
|
@ -249,6 +275,7 @@ class User(models.Model):
|
|||
|
||||
result = {
|
||||
"username": self.username,
|
||||
# LEGACY.. role should get removed eventually.. it's probably safe to remove it now?
|
||||
"role": self.get_role_display(),
|
||||
"notification_policies": notification_policies_verbal,
|
||||
}
|
||||
|
|
@ -262,6 +289,24 @@ class User(models.Model):
|
|||
def insight_logs_metadata(self):
|
||||
return {}
|
||||
|
||||
@staticmethod
|
||||
def build_permissions_query(
|
||||
permission: LegacyAccessControlCompatiblePermission, organization
|
||||
) -> typing.Union[PermissionsRegexQuery, RoleInQuery]:
|
||||
"""
|
||||
This method returns a django query filter that is compatible with RBAC
|
||||
as well as legacy "basic" role based authorization. If a permission is provided we simply do
|
||||
a regex search where the permission column contains the permission value (need to use regex because
|
||||
the JSON contains method is not supported by sqlite)
|
||||
|
||||
If RBAC is not supported for the org, we make the assumption that we are looking for any users with AT LEAST
|
||||
the fallback role. Ex: if the fallback role were editor than we would get editors and admins.
|
||||
"""
|
||||
if organization.is_rbac_permissions_enabled:
|
||||
# https://stackoverflow.com/a/50251879
|
||||
return PermissionsRegexQuery(permissions__regex=r".*{0}.*".format(permission.value))
|
||||
return RoleInQuery(role__lte=permission.fallback_role.value)
|
||||
|
||||
|
||||
# TODO: check whether this signal can be moved to save method of the model
|
||||
@receiver(post_save, sender=User)
|
||||
|
|
|
|||
|
|
@ -14,9 +14,23 @@ logger.setLevel(logging.DEBUG)
|
|||
def sync_organization(organization):
|
||||
client = GrafanaAPIClient(api_url=organization.grafana_url, api_token=organization.api_token)
|
||||
|
||||
api_users, call_status = client.get_users()
|
||||
rbac_is_enabled = client.is_rbac_enabled_for_organization()
|
||||
organization.is_rbac_permissions_enabled = rbac_is_enabled
|
||||
|
||||
sync_instance_info(organization)
|
||||
if organization.gcom_token:
|
||||
gcom_client = GcomAPIClient(organization.gcom_token)
|
||||
instance_info = gcom_client.get_instance_info(organization.stack_id)
|
||||
if not instance_info or str(instance_info["orgId"]) != organization.org_id:
|
||||
return
|
||||
|
||||
organization.stack_slug = instance_info["slug"]
|
||||
organization.org_slug = instance_info["orgSlug"]
|
||||
organization.org_title = instance_info["orgName"]
|
||||
organization.region_slug = instance_info["regionSlug"]
|
||||
organization.grafana_url = instance_info["url"]
|
||||
organization.gcom_token_org_last_time_synced = timezone.now()
|
||||
|
||||
api_users = client.get_users(rbac_is_enabled)
|
||||
|
||||
if api_users:
|
||||
organization.api_token_status = Organization.API_TOKEN_STATUS_OK
|
||||
|
|
@ -34,25 +48,11 @@ def sync_organization(organization):
|
|||
"last_time_synced",
|
||||
"api_token_status",
|
||||
"gcom_token_org_last_time_synced",
|
||||
"is_rbac_permissions_enabled",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def sync_instance_info(organization):
|
||||
if organization.gcom_token:
|
||||
gcom_client = GcomAPIClient(organization.gcom_token)
|
||||
instance_info, _ = gcom_client.get_instance_info(organization.stack_id)
|
||||
if not instance_info or str(instance_info["orgId"]) != organization.org_id:
|
||||
return
|
||||
|
||||
organization.stack_slug = instance_info["slug"]
|
||||
organization.org_slug = instance_info["orgSlug"]
|
||||
organization.org_title = instance_info["orgName"]
|
||||
organization.region_slug = instance_info["regionSlug"]
|
||||
organization.grafana_url = instance_info["url"]
|
||||
organization.gcom_token_org_last_time_synced = timezone.now()
|
||||
|
||||
|
||||
def sync_users_and_teams(client, api_users, organization):
|
||||
# check if api_users are shaped correctly. e.g. for paused instance, the response is not a list.
|
||||
if not api_users or not isinstance(api_users, (tuple, list)):
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import pytest
|
||||
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
from apps.twilioapp.constants import TwilioCallStatuses, TwilioMessageStatuses
|
||||
from common.constants.role import Role
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
|
@ -13,8 +13,8 @@ def test_phone_calls_left(
|
|||
make_alert_group,
|
||||
):
|
||||
organization = make_organization()
|
||||
admin = make_user_for_organization(organization, role=Role.ADMIN)
|
||||
user = make_user_for_organization(organization, role=Role.EDITOR)
|
||||
admin = make_user_for_organization(organization)
|
||||
user = make_user_for_organization(organization, role=LegacyAccessControlRole.EDITOR)
|
||||
alert_receive_channel = make_alert_receive_channel(organization)
|
||||
alert_group = make_alert_group(alert_receive_channel)
|
||||
make_phone_call(receiver=admin, status=TwilioCallStatuses.COMPLETED, represents_alert_group=alert_group)
|
||||
|
|
@ -28,8 +28,8 @@ def test_sms_left(
|
|||
make_organization, make_user_for_organization, make_sms, make_alert_receive_channel, make_alert_group
|
||||
):
|
||||
organization = make_organization()
|
||||
admin = make_user_for_organization(organization, role=Role.ADMIN)
|
||||
user = make_user_for_organization(organization, role=Role.EDITOR)
|
||||
admin = make_user_for_organization(organization)
|
||||
user = make_user_for_organization(organization, role=LegacyAccessControlRole.EDITOR)
|
||||
alert_receive_channel = make_alert_receive_channel(organization)
|
||||
alert_group = make_alert_group(alert_receive_channel)
|
||||
make_sms(receiver=admin, status=TwilioMessageStatuses.SENT, represents_alert_group=alert_group)
|
||||
|
|
@ -48,8 +48,8 @@ def test_phone_calls_and_sms_counts_together(
|
|||
make_alert_group,
|
||||
):
|
||||
organization = make_organization()
|
||||
admin = make_user_for_organization(organization, role=Role.ADMIN)
|
||||
user = make_user_for_organization(organization, role=Role.EDITOR)
|
||||
admin = make_user_for_organization(organization)
|
||||
user = make_user_for_organization(organization, role=LegacyAccessControlRole.EDITOR)
|
||||
alert_receive_channel = make_alert_receive_channel(organization)
|
||||
alert_group = make_alert_group(alert_receive_channel)
|
||||
make_phone_call(receiver=admin, status=TwilioCallStatuses.COMPLETED, represents_alert_group=alert_group)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from django.core.exceptions import ObjectDoesNotExist
|
|||
from apps.grafana_plugin.helpers.client import GcomAPIClient, GrafanaAPIClient
|
||||
from apps.user_management.models import Team, User
|
||||
from apps.user_management.sync import cleanup_organization, sync_organization
|
||||
from conftest import IS_RBAC_ENABLED
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
|
@ -21,6 +22,7 @@ def test_sync_users_for_organization(make_organization, make_user_for_organizati
|
|||
"login": "test",
|
||||
"role": "admin",
|
||||
"avatarUrl": "/test/1234",
|
||||
"permissions": [],
|
||||
}
|
||||
for user_id in (2, 3)
|
||||
)
|
||||
|
|
@ -97,11 +99,7 @@ def test_sync_users_for_team(make_organization, make_user_for_organization, make
|
|||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_sync_organization(
|
||||
make_organization,
|
||||
make_team,
|
||||
make_user_for_organization,
|
||||
):
|
||||
def test_sync_organization(make_organization, make_team, make_user_for_organization):
|
||||
organization = make_organization()
|
||||
|
||||
api_users_response = (
|
||||
|
|
@ -112,6 +110,7 @@ def test_sync_organization(
|
|||
"login": "test",
|
||||
"role": "admin",
|
||||
"avatarUrl": "test.test/test",
|
||||
"permissions": [],
|
||||
},
|
||||
)
|
||||
|
||||
|
|
@ -135,10 +134,11 @@ def test_sync_organization(
|
|||
},
|
||||
)
|
||||
|
||||
with patch.object(GrafanaAPIClient, "get_users", return_value=(api_users_response, {"status_code": 200})):
|
||||
with patch.object(GrafanaAPIClient, "get_teams", return_value=(api_teams_response, None)):
|
||||
with patch.object(GrafanaAPIClient, "get_team_members", return_value=(api_members_response, None)):
|
||||
sync_organization(organization)
|
||||
with patch.object(GrafanaAPIClient, "is_rbac_enabled_for_organization", return_value=IS_RBAC_ENABLED):
|
||||
with patch.object(GrafanaAPIClient, "get_users", return_value=api_users_response):
|
||||
with patch.object(GrafanaAPIClient, "get_teams", return_value=(api_teams_response, None)):
|
||||
with patch.object(GrafanaAPIClient, "get_team_members", return_value=(api_members_response, None)):
|
||||
sync_organization(organization)
|
||||
|
||||
# check that users are populated
|
||||
assert organization.users.count() == 1
|
||||
|
|
@ -154,6 +154,9 @@ def test_sync_organization(
|
|||
assert team.users.count() == 1
|
||||
assert team.users.get() == user
|
||||
|
||||
# check that the rbac flag is properly set on the org
|
||||
assert organization.is_rbac_permissions_enabled == IS_RBAC_ENABLED
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_duplicate_user_ids(make_organization, make_user_for_organization):
|
||||
|
|
@ -178,6 +181,7 @@ def test_duplicate_user_ids(make_organization, make_user_for_organization):
|
|||
"login": "test",
|
||||
"role": "admin",
|
||||
"avatarUrl": "test.test/test",
|
||||
"permissions": [],
|
||||
}
|
||||
]
|
||||
|
||||
|
|
@ -192,7 +196,7 @@ def test_duplicate_user_ids(make_organization, make_user_for_organization):
|
|||
def test_cleanup_organization_deleted(make_organization):
|
||||
organization = make_organization(gcom_token="TEST_GCOM_TOKEN")
|
||||
|
||||
with patch.object(GcomAPIClient, "get_instance_info", return_value=({"status": "deleted"}, None)):
|
||||
with patch.object(GcomAPIClient, "get_instance_info", return_value={"status": "deleted"}):
|
||||
cleanup_organization(organization.id)
|
||||
|
||||
with pytest.raises(ObjectDoesNotExist):
|
||||
|
|
|
|||
|
|
@ -1,20 +1,15 @@
|
|||
# from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from apps.api.permissions import LegacyAccessControlRole
|
||||
from apps.user_management.models import User
|
||||
from common.constants.role import Role
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_self_or_admin(
|
||||
make_organization,
|
||||
make_user_for_organization,
|
||||
):
|
||||
def test_self_or_admin(make_organization, make_user_for_organization):
|
||||
organization = make_organization()
|
||||
admin = make_user_for_organization(organization)
|
||||
second_admin = make_user_for_organization(organization)
|
||||
editor = make_user_for_organization(organization, role=Role.EDITOR)
|
||||
editor = make_user_for_organization(organization, role=LegacyAccessControlRole.EDITOR)
|
||||
|
||||
another_organization = make_organization()
|
||||
admin_from_another_organization = make_user_for_organization(another_organization)
|
||||
|
|
@ -26,10 +21,7 @@ def test_self_or_admin(
|
|||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_lower_email_filter(
|
||||
make_organization,
|
||||
make_user_for_organization,
|
||||
):
|
||||
def test_lower_email_filter(make_organization, make_user_for_organization):
|
||||
organization = make_organization()
|
||||
user = make_user_for_organization(organization, email="TestingUser@test.com")
|
||||
make_user_for_organization(organization, email="testing_user@test.com")
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
from enum import IntEnum
|
||||
|
||||
|
||||
class Role(IntEnum):
|
||||
ADMIN = 0
|
||||
EDITOR = 1
|
||||
VIEWER = 2
|
||||
|
||||
@classmethod
|
||||
def choices(cls):
|
||||
return tuple((option.value, option.name) for option in cls)
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import json
|
||||
import os
|
||||
import sys
|
||||
import typing
|
||||
import uuid
|
||||
|
|
@ -35,6 +36,13 @@ from apps.alerts.tests.factories import (
|
|||
ResolutionNoteFactory,
|
||||
ResolutionNoteSlackMessageFactory,
|
||||
)
|
||||
from apps.api.permissions import (
|
||||
ACTION_PREFIX,
|
||||
GrafanaAPIPermission,
|
||||
LegacyAccessControlCompatiblePermission,
|
||||
LegacyAccessControlRole,
|
||||
RBACPermission,
|
||||
)
|
||||
from apps.auth_token.models import ApiAuthToken, PluginAuthToken
|
||||
from apps.base.models.user_notification_policy_log_record import (
|
||||
UserNotificationPolicyLogRecord,
|
||||
|
|
@ -72,7 +80,6 @@ from apps.telegram.tests.factories import (
|
|||
from apps.twilioapp.tests.factories import PhoneCallFactory, SMSFactory
|
||||
from apps.user_management.models.user import User, listen_for_user_model_save
|
||||
from apps.user_management.tests.factories import OrganizationFactory, RegionFactory, TeamFactory, UserFactory
|
||||
from common.constants.role import Role
|
||||
|
||||
register(OrganizationFactory)
|
||||
register(UserFactory)
|
||||
|
|
@ -112,6 +119,8 @@ register(EmailMessageFactory)
|
|||
register(IntegrationHeartBeatFactory)
|
||||
register(LiveSettingFactory)
|
||||
|
||||
IS_RBAC_ENABLED = os.getenv("ONCALL_TESTING_RBAC_ENABLED", "True") == "True"
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_slack_api_call(monkeypatch):
|
||||
|
|
@ -142,18 +151,16 @@ def mock_telegram_bot_username(monkeypatch):
|
|||
@pytest.fixture
|
||||
def make_organization():
|
||||
def _make_organization(**kwargs):
|
||||
organization = OrganizationFactory(**kwargs)
|
||||
|
||||
return organization
|
||||
return OrganizationFactory(**kwargs, is_rbac_permissions_enabled=IS_RBAC_ENABLED)
|
||||
|
||||
return _make_organization
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def make_user_for_organization():
|
||||
def _make_user_for_organization(organization, role=Role.ADMIN, **kwargs):
|
||||
def make_user_for_organization(make_user):
|
||||
def _make_user_for_organization(organization, role: typing.Optional[LegacyAccessControlRole] = None, **kwargs):
|
||||
post_save.disconnect(listen_for_user_model_save, sender=User)
|
||||
user = UserFactory(organization=organization, role=role, **kwargs)
|
||||
user = make_user(organization=organization, role=role, **kwargs)
|
||||
post_save.disconnect(listen_for_user_model_save, sender=User)
|
||||
return user
|
||||
|
||||
|
|
@ -200,19 +207,84 @@ def make_user_auth_headers():
|
|||
return _make_user_auth_headers
|
||||
|
||||
|
||||
RoleMapping = typing.Dict[LegacyAccessControlRole, typing.List[LegacyAccessControlCompatiblePermission]]
|
||||
|
||||
|
||||
def get_user_permission_role_mapping_from_frontend_plugin_json() -> RoleMapping:
|
||||
"""
|
||||
This is used to take the RBAC permission -> basic role grants on the frontend
|
||||
and test that the RBAC grants work the same way against the backend in terms of authorization
|
||||
"""
|
||||
|
||||
class PluginJSONRoleDefinition(typing.TypedDict):
|
||||
permissions: typing.List[GrafanaAPIPermission]
|
||||
|
||||
class PluginJSONRole(typing.TypedDict):
|
||||
role: PluginJSONRoleDefinition
|
||||
grants: typing.List[str]
|
||||
|
||||
class PluginJSON(typing.TypedDict):
|
||||
roles: typing.List[PluginJSONRole]
|
||||
|
||||
with open("../grafana-plugin/src/plugin.json") as fp:
|
||||
plugin_json: PluginJSON = json.load(fp)
|
||||
|
||||
role_mapping: RoleMapping = {
|
||||
LegacyAccessControlRole.VIEWER: [],
|
||||
LegacyAccessControlRole.EDITOR: [],
|
||||
LegacyAccessControlRole.ADMIN: [],
|
||||
}
|
||||
|
||||
all_permission_classes: typing.Dict[str, LegacyAccessControlCompatiblePermission] = {
|
||||
getattr(RBACPermission.Permissions, attr).value: getattr(RBACPermission.Permissions, attr)
|
||||
for attr in dir(RBACPermission.Permissions)
|
||||
if not attr.startswith("_")
|
||||
}
|
||||
|
||||
# we just care about getting the basic role grants, everything else can be ignored
|
||||
for role in plugin_json["roles"]:
|
||||
if grants := role["grants"]:
|
||||
for permission in role["role"]["permissions"]:
|
||||
# only concerned with grafana-oncall-app specific grants
|
||||
# ignore things like plugins.app:access actions
|
||||
action = permission["action"]
|
||||
permission_class = None
|
||||
|
||||
if action.startswith(ACTION_PREFIX):
|
||||
permission_class = all_permission_classes[action]
|
||||
|
||||
if permission_class:
|
||||
for grant in grants:
|
||||
try:
|
||||
role = LegacyAccessControlRole[grant.upper()]
|
||||
if role not in role_mapping[role]:
|
||||
role_mapping[role].append(permission_class)
|
||||
except KeyError:
|
||||
# may come across grants like "Grafana Admin"
|
||||
# which we can ignore
|
||||
continue
|
||||
|
||||
return role_mapping
|
||||
|
||||
|
||||
ROLE_PERMISSION_MAPPING = get_user_permission_role_mapping_from_frontend_plugin_json()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def make_user():
|
||||
def _make_user(role=Role.ADMIN, **kwargs):
|
||||
user = UserFactory(role=role, **kwargs)
|
||||
|
||||
return user
|
||||
def _make_user(role: typing.Optional[LegacyAccessControlRole] = None, **kwargs):
|
||||
role = LegacyAccessControlRole.ADMIN if role is None else role
|
||||
permissions = ROLE_PERMISSION_MAPPING[role] if IS_RBAC_ENABLED else []
|
||||
return UserFactory(
|
||||
role=role, permissions=[GrafanaAPIPermission(action=perm.value) for perm in permissions], **kwargs
|
||||
)
|
||||
|
||||
return _make_user
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def make_organization_and_user(make_organization, make_user_for_organization):
|
||||
def _make_organization_and_user(role=Role.ADMIN):
|
||||
def _make_organization_and_user(role: typing.Optional[LegacyAccessControlRole] = None):
|
||||
organization = make_organization()
|
||||
user = make_user_for_organization(organization=organization, role=role)
|
||||
return organization, user
|
||||
|
|
@ -224,33 +296,31 @@ def make_organization_and_user(make_organization, make_user_for_organization):
|
|||
def make_organization_and_user_with_slack_identities(
|
||||
make_organization_with_slack_team_identity, make_user_with_slack_user_identity
|
||||
):
|
||||
def _make_organization_and_user_with_slack_identities(role=Role.ADMIN):
|
||||
def _make_organization_and_user_with_slack_identities(role: typing.Optional[LegacyAccessControlRole] = None):
|
||||
organization, slack_team_identity = make_organization_with_slack_team_identity()
|
||||
user, slack_user_identity = make_user_with_slack_user_identity(slack_team_identity, organization, role=role)
|
||||
|
||||
return organization, user, slack_team_identity, slack_user_identity
|
||||
|
||||
return _make_organization_and_user_with_slack_identities
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def make_user_with_slack_user_identity():
|
||||
def _make_slack_user_identity_with_user(slack_team_identity, organization, role=Role.ADMIN, **kwargs):
|
||||
slack_user_identity = SlackUserIdentityFactory(
|
||||
slack_team_identity=slack_team_identity,
|
||||
**kwargs,
|
||||
)
|
||||
user = UserFactory(slack_user_identity=slack_user_identity, organization=organization, role=role)
|
||||
def make_user_with_slack_user_identity(make_user):
|
||||
def _make_slack_user_identity_with_user(
|
||||
slack_team_identity, organization, role: typing.Optional[LegacyAccessControlRole] = None, **kwargs
|
||||
):
|
||||
slack_user_identity = SlackUserIdentityFactory(slack_team_identity=slack_team_identity, **kwargs)
|
||||
user = make_user(slack_user_identity=slack_user_identity, organization=organization, role=role)
|
||||
return user, slack_user_identity
|
||||
|
||||
return _make_slack_user_identity_with_user
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def make_organization_with_slack_team_identity(make_slack_team_identity):
|
||||
def make_organization_with_slack_team_identity(make_slack_team_identity, make_organization):
|
||||
def _make_slack_team_identity_with_organization(**kwargs):
|
||||
slack_team_identity = make_slack_team_identity(**kwargs)
|
||||
organization = OrganizationFactory(slack_team_identity=slack_team_identity)
|
||||
organization = make_organization(slack_team_identity=slack_team_identity)
|
||||
return organization, slack_team_identity
|
||||
|
||||
return _make_slack_team_identity_with_organization
|
||||
|
|
@ -565,10 +635,9 @@ def mock_start_disable_maintenance_task(monkeypatch):
|
|||
|
||||
@pytest.fixture()
|
||||
def make_organization_and_user_with_plugin_token(make_organization_and_user, make_token_for_organization):
|
||||
def _make_organization_and_user_with_plugin_token(role=Role.ADMIN):
|
||||
organization, user = make_organization_and_user(role=role)
|
||||
def _make_organization_and_user_with_plugin_token(role: typing.Optional[LegacyAccessControlRole] = None):
|
||||
organization, user = make_organization_and_user(role)
|
||||
_, token = make_token_for_organization(organization)
|
||||
|
||||
return organization, user, token
|
||||
|
||||
return _make_organization_and_user_with_plugin_token
|
||||
|
|
|
|||
|
|
@ -9,6 +9,6 @@ banned-modules =
|
|||
[pytest]
|
||||
# https://pytest-django.readthedocs.io/en/latest/configuring_django.html#order-of-choosing-settings
|
||||
# https://pytest-django.readthedocs.io/en/latest/database.html
|
||||
addopts = --reuse-db --nomigrations --color=yes --showlocals
|
||||
addopts = --color=yes --showlocals
|
||||
# https://pytest-django.readthedocs.io/en/latest/faq.html#my-tests-are-not-being-found-why
|
||||
python_files = tests.py test_*.py *_tests.py
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue