oncall-engine/engine/conftest.py

883 lines
28 KiB
Python
Raw Normal View History

import json
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
2022-11-29 09:41:56 +01:00
import os
import sys
import typing
import uuid
from importlib import import_module, reload
import pytest
Phone provider refactoring (#1713) # What this PR does This PR moves phone notification logic into separate object PhoneBackend and introduces PhoneProvider interface to hide actual implementation of external phone services provider. It should allow add new phone providers just by implementing one class (See SimplePhoneProvider for example). # Why [Asterisk PR](https://github.com/grafana/oncall/pull/1282) showed that our phone notification system is not flexible. However this is one of the most frequent community questions - how to add "X" phone provider. Also, this refactoring move us one step closer to unifying all notification backends, since with PhoneBackend all phone notification logic is collected in one place and independent from concrete realisation. # Highligts 1. PhoneBackend object - contains all phone notifications business logic. 2. PhoneProvider - interface to external phone services provider. 3. TwilioPhoneProvider and SimplePhoneProvider - two examples of PhoneProvider implementation. 4. PhoneCallRecord and SMSRecord models. I introduced these models to keep phone notification limits logic decoupled from external providers. Existing TwilioPhoneCall and TwilioSMS objects will be migrated to the new table to not to reset limits counter. To be able to receive status callbacks and gather from Twilio TwilioPhoneCall and TwilioSMS still exists, but they are linked to PhoneCallRecord and SMSRecord via fk, to not to leat twilio logic into core code. --------- Co-authored-by: Yulia Shanyrova <yulia.shanyrova@grafana.com>
2023-05-24 14:27:48 +08:00
from celery import Task
from django.db.models.signals import post_save
from django.urls import clear_url_caches
from pytest_factoryboy import register
from rest_framework.test import APIClient
from telegram import Bot
from apps.alerts.models import (
Alert,
AlertGroupLogRecord,
AlertReceiveChannel,
MaintainableObject,
ResolutionNote,
listen_for_alert_model_save,
listen_for_alertgrouplogrecord,
listen_for_alertreceivechannel_model_save,
)
from apps.alerts.signals import user_notification_action_triggered_signal
from apps.alerts.tests.factories import (
AlertFactory,
AlertGroupFactory,
AlertGroupLogRecordFactory,
AlertReceiveChannelFactory,
ChannelFilterFactory,
CustomActionFactory,
EscalationChainFactory,
EscalationPolicyFactory,
InvitationFactory,
ResolutionNoteFactory,
ResolutionNoteSlackMessageFactory,
)
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
2022-11-29 09:41:56 +01:00
from apps.api.permissions import (
ACTION_PREFIX,
GrafanaAPIPermission,
LegacyAccessControlCompatiblePermission,
LegacyAccessControlRole,
RBACPermission,
)
from apps.auth_token.models import ApiAuthToken, PluginAuthToken, SlackAuthToken
from apps.base.models.user_notification_policy_log_record import (
UserNotificationPolicyLogRecord,
listen_for_usernotificationpolicylogrecord_model_save,
)
from apps.base.tests.factories import (
LiveSettingFactory,
UserNotificationPolicyFactory,
UserNotificationPolicyLogRecordFactory,
)
from apps.email.tests.factories import EmailMessageFactory
from apps.heartbeat.tests.factories import IntegrationHeartBeatFactory
Mobile app settings backend (#1571) # What this PR does Adds mobile app settings support to OnCall backend. - Adds a new Django model `MobileAppUserSettings` to store push notification settings - Adds a new endpoint `/mobile_app/v1/user_settings` to fetch/update settings from the mobile app Some additional info on implementation: at first I wanted to extend the messaging backend system to allow storing / retrieving per-user data and implement mobile app settings based on those changes. After some thought I decided not to extend the messaging backend system and have this as functionality specific to the `mobile_app` Django app. Currently the messaging backend system is used by the backend and plugin UI, but mobile app settings are specific only to the mobile app and not configurable in the plugin UI. **tldr: wanted to extend messaging backend system, but decided not to do that** # Usage ## Get settings via API `GET /mobile_app/v1/user_settings` Example response: ```json { "default_notification_sound_name": "default_sound", # sound name without file extension "default_notification_volume_type": "constant", "default_notification_volume": 0.8, "default_notification_volume_override": false, "important_notification_sound_name": "default_sound_important", # sound name without file extension "important_notification_volume_type": "constant", "important_notification_volume": 0.8, "important_notification_override_dnd": true } ``` ## Update settings via API `PUT /mobile_app/v1/user_settings` - see example response above for payload shape. Note that sound names must be passed without file extension. When sending push notifications, the backend will add `.mp3` to sound names and pass it to push notification data for Android. For iOS, sound names will be suffixed with `.aiff` to be used by APNS. ## Get settings from notification data for Android All the settings from example response will be available in push notification data (along with `orgId`, `alertGroupId`, `title`, etc.). Fields `default_notification_volume`, `default_notification_volume_override` and `important_notification_volume` , `important_notification_override_dnd` will be converted to strings due to FCM limitations. Fields `default_notification_sound_name` and `important_notification_sound_name` will be suffixed with `.mp3` in push notification data. ## iOS limitations While Android push notifications are handled purely on the mobile app side, iOS notifications are sent via APNS which imposes some limitations. - Notification volume cannot be overridden for non-critical notifications (so fields `default_notification_volume_override` and `default_notification_volume` will be disregarded for iOS notifications) - It's not possible to control volume type (i.e. "constant" vs "intensifying") via APNS. A possible workaround is to have different sound files for "constant" and "intensifying" and pass that as `default_notification_sound_name` / `important_notification_sound_name`. # Which issue(s) this PR fixes Related to https://github.com/grafana/oncall-private/issues/1602 # Checklist - [x] Tests updated - [x] No changelog updates since the changes are not user-facing
2023-03-22 14:47:18 +00:00
from apps.mobile_app.models import MobileAppAuthToken, MobileAppVerificationToken
Phone provider refactoring (#1713) # What this PR does This PR moves phone notification logic into separate object PhoneBackend and introduces PhoneProvider interface to hide actual implementation of external phone services provider. It should allow add new phone providers just by implementing one class (See SimplePhoneProvider for example). # Why [Asterisk PR](https://github.com/grafana/oncall/pull/1282) showed that our phone notification system is not flexible. However this is one of the most frequent community questions - how to add "X" phone provider. Also, this refactoring move us one step closer to unifying all notification backends, since with PhoneBackend all phone notification logic is collected in one place and independent from concrete realisation. # Highligts 1. PhoneBackend object - contains all phone notifications business logic. 2. PhoneProvider - interface to external phone services provider. 3. TwilioPhoneProvider and SimplePhoneProvider - two examples of PhoneProvider implementation. 4. PhoneCallRecord and SMSRecord models. I introduced these models to keep phone notification limits logic decoupled from external providers. Existing TwilioPhoneCall and TwilioSMS objects will be migrated to the new table to not to reset limits counter. To be able to receive status callbacks and gather from Twilio TwilioPhoneCall and TwilioSMS still exists, but they are linked to PhoneCallRecord and SMSRecord via fk, to not to leat twilio logic into core code. --------- Co-authored-by: Yulia Shanyrova <yulia.shanyrova@grafana.com>
2023-05-24 14:27:48 +08:00
from apps.phone_notifications.phone_backend import PhoneBackend
from apps.phone_notifications.tests.factories import PhoneCallRecordFactory, SMSRecordFactory
from apps.phone_notifications.tests.mock_phone_provider import MockPhoneProvider
from apps.schedules.tests.factories import (
CustomOnCallShiftFactory,
OnCallScheduleCalendarFactory,
OnCallScheduleFactory,
OnCallScheduleICalFactory,
)
from apps.slack.slack_client import SlackClientWithErrorHandling
from apps.slack.tests.factories import (
SlackChannelFactory,
SlackMessageFactory,
SlackTeamIdentityFactory,
SlackUserGroupFactory,
SlackUserIdentityFactory,
)
from apps.telegram.tests.factories import (
TelegramChannelFactory,
TelegramChannelVerificationCodeFactory,
TelegramMessageFactory,
TelegramToUserConnectorFactory,
TelegramVerificationCodeFactory,
)
from apps.user_management.models.user import User, listen_for_user_model_save
2022-10-27 15:40:46 -06:00
from apps.user_management.tests.factories import OrganizationFactory, RegionFactory, TeamFactory, UserFactory
from apps.webhooks.tests.factories import CustomWebhookFactory, WebhookResponseFactory
register(OrganizationFactory)
register(UserFactory)
register(TeamFactory)
register(AlertReceiveChannelFactory)
register(ChannelFilterFactory)
register(EscalationPolicyFactory)
register(OnCallScheduleICalFactory)
register(OnCallScheduleCalendarFactory)
register(CustomOnCallShiftFactory)
register(AlertFactory)
register(AlertGroupFactory)
register(AlertGroupLogRecordFactory)
register(InvitationFactory)
register(CustomActionFactory)
register(SlackUserGroupFactory)
register(SlackUserIdentityFactory)
register(SlackTeamIdentityFactory)
register(SlackMessageFactory)
register(TelegramToUserConnectorFactory)
register(TelegramChannelFactory)
register(TelegramVerificationCodeFactory)
register(TelegramChannelVerificationCodeFactory)
register(TelegramMessageFactory)
register(ResolutionNoteSlackMessageFactory)
Phone provider refactoring (#1713) # What this PR does This PR moves phone notification logic into separate object PhoneBackend and introduces PhoneProvider interface to hide actual implementation of external phone services provider. It should allow add new phone providers just by implementing one class (See SimplePhoneProvider for example). # Why [Asterisk PR](https://github.com/grafana/oncall/pull/1282) showed that our phone notification system is not flexible. However this is one of the most frequent community questions - how to add "X" phone provider. Also, this refactoring move us one step closer to unifying all notification backends, since with PhoneBackend all phone notification logic is collected in one place and independent from concrete realisation. # Highligts 1. PhoneBackend object - contains all phone notifications business logic. 2. PhoneProvider - interface to external phone services provider. 3. TwilioPhoneProvider and SimplePhoneProvider - two examples of PhoneProvider implementation. 4. PhoneCallRecord and SMSRecord models. I introduced these models to keep phone notification limits logic decoupled from external providers. Existing TwilioPhoneCall and TwilioSMS objects will be migrated to the new table to not to reset limits counter. To be able to receive status callbacks and gather from Twilio TwilioPhoneCall and TwilioSMS still exists, but they are linked to PhoneCallRecord and SMSRecord via fk, to not to leat twilio logic into core code. --------- Co-authored-by: Yulia Shanyrova <yulia.shanyrova@grafana.com>
2023-05-24 14:27:48 +08:00
register(PhoneCallRecordFactory)
register(SMSRecordFactory)
register(EmailMessageFactory)
register(IntegrationHeartBeatFactory)
register(LiveSettingFactory)
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
2022-11-29 09:41:56 +01:00
IS_RBAC_ENABLED = os.getenv("ONCALL_TESTING_RBAC_ENABLED", "True") == "True"
@pytest.fixture(autouse=True)
def mock_slack_api_call(monkeypatch):
def mock_api_call(*args, **kwargs):
return {
"status": 200,
"usergroups": [],
"channel": {"id": "TEST_CHANNEL_ID"},
"user": {
"name": "TEST_SLACK_LOGIN",
"real_name": "TEST_SLACK_NAME",
"profile": {"image_512": "TEST_SLACK_IMAGE"},
},
"team": {"name": "TEST_TEAM"},
}
monkeypatch.setattr(SlackClientWithErrorHandling, "api_call", mock_api_call)
@pytest.fixture(autouse=True)
def mock_telegram_bot_username(monkeypatch):
def mock_username(*args, **kwargs):
return "amixr_bot"
monkeypatch.setattr(Bot, "username", mock_username)
Phone provider refactoring (#1713) # What this PR does This PR moves phone notification logic into separate object PhoneBackend and introduces PhoneProvider interface to hide actual implementation of external phone services provider. It should allow add new phone providers just by implementing one class (See SimplePhoneProvider for example). # Why [Asterisk PR](https://github.com/grafana/oncall/pull/1282) showed that our phone notification system is not flexible. However this is one of the most frequent community questions - how to add "X" phone provider. Also, this refactoring move us one step closer to unifying all notification backends, since with PhoneBackend all phone notification logic is collected in one place and independent from concrete realisation. # Highligts 1. PhoneBackend object - contains all phone notifications business logic. 2. PhoneProvider - interface to external phone services provider. 3. TwilioPhoneProvider and SimplePhoneProvider - two examples of PhoneProvider implementation. 4. PhoneCallRecord and SMSRecord models. I introduced these models to keep phone notification limits logic decoupled from external providers. Existing TwilioPhoneCall and TwilioSMS objects will be migrated to the new table to not to reset limits counter. To be able to receive status callbacks and gather from Twilio TwilioPhoneCall and TwilioSMS still exists, but they are linked to PhoneCallRecord and SMSRecord via fk, to not to leat twilio logic into core code. --------- Co-authored-by: Yulia Shanyrova <yulia.shanyrova@grafana.com>
2023-05-24 14:27:48 +08:00
@pytest.fixture(autouse=True)
def mock_phone_provider(monkeypatch):
def mock_get_provider(*args, **kwargs):
return MockPhoneProvider()
monkeypatch.setattr(PhoneBackend, "_get_phone_provider", mock_get_provider)
@pytest.fixture(autouse=True)
def mock_apply_async(monkeypatch):
def mock_apply_async(*args, **kwargs):
return uuid.uuid4()
monkeypatch.setattr(Task, "apply_async", mock_apply_async)
@pytest.fixture
def make_organization():
def _make_organization(**kwargs):
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
2022-11-29 09:41:56 +01:00
return OrganizationFactory(**kwargs, is_rbac_permissions_enabled=IS_RBAC_ENABLED)
return _make_organization
@pytest.fixture
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
2022-11-29 09:41:56 +01:00
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)
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
2022-11-29 09:41:56 +01:00
user = make_user(organization=organization, role=role, **kwargs)
post_save.disconnect(listen_for_user_model_save, sender=User)
return user
return _make_user_for_organization
@pytest.fixture
def make_token_for_organization():
def _make_token_for_organization(organization):
return PluginAuthToken.create_auth_token(organization)
return _make_token_for_organization
add unique idx on user column in mobileapp authtoken table (#1482) # Which issue(s) this PR fixes Solves the (rare) issue where a user could potentially have > 1 mobileapp auth token, leading to 500 errors when trying to interact w/ the authtoken (ex. disconnect a mobile app from a user's profile): ```shell 2023-03-07 10:12:13 source=engine:app google_trace_id=e14bf933d634068a48caf093ce43c7f5/5550677047491218352 logger=django.request Internal Server Error: /api/internal/v1/users/U6WJ3BRLM1TR7/unlink_backend Traceback (most recent call last): File "/usr/local/lib/python3.9/site-packages/django/core/handlers/exception.py", line 47, in inner response = get_response(request) File "/usr/local/lib/python3.9/site-packages/django/core/handlers/base.py", line 181, in _get_response response = wrapped_callback(request, *callback_args, **callback_kwargs) File "/usr/local/lib/python3.9/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view return view_func(*args, **kwargs) File "/usr/local/lib/python3.9/site-packages/rest_framework/viewsets.py", line 125, in view return self.dispatch(request, *args, **kwargs) File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 509, in dispatch response = self.handle_exception(exc) File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 469, in handle_exception self.raise_uncaught_exception(exc) File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception raise exc File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 506, in dispatch response = handler(request, *args, **kwargs) File "/etc/app/apps/api/views/user.py", line 453, in unlink_backend backend.unlink_user(user) File "/etc/app/apps/mobile_app/backend.py", line 34, in unlink_user token = MobileAppAuthToken.objects.get(user=user) File "/usr/local/lib/python3.9/site-packages/django/db/models/manager.py", line 85, in manager_method return getattr(self.get_queryset(), name)(*args, **kwargs) File "/usr/local/lib/python3.9/site-packages/django/db/models/query.py", line 439, in get raise self.model.MultipleObjectsReturned( apps.mobile_app.models.MobileAppAuthToken.MultipleObjectsReturned: get() returned more than one MobileAppAuthToken -- it returned 2! ``` ## Checklist - [x] Tests updated - [ ] Documentation added (N/A) - [x] `CHANGELOG.md` updated
2023-03-08 13:50:57 +01:00
@pytest.fixture
def make_mobile_app_verification_token_for_user():
def _make_mobile_app_verification_token_for_user(user, organization):
return MobileAppVerificationToken.create_auth_token(user, organization)
return _make_mobile_app_verification_token_for_user
Mobile app settings backend (#1571) # What this PR does Adds mobile app settings support to OnCall backend. - Adds a new Django model `MobileAppUserSettings` to store push notification settings - Adds a new endpoint `/mobile_app/v1/user_settings` to fetch/update settings from the mobile app Some additional info on implementation: at first I wanted to extend the messaging backend system to allow storing / retrieving per-user data and implement mobile app settings based on those changes. After some thought I decided not to extend the messaging backend system and have this as functionality specific to the `mobile_app` Django app. Currently the messaging backend system is used by the backend and plugin UI, but mobile app settings are specific only to the mobile app and not configurable in the plugin UI. **tldr: wanted to extend messaging backend system, but decided not to do that** # Usage ## Get settings via API `GET /mobile_app/v1/user_settings` Example response: ```json { "default_notification_sound_name": "default_sound", # sound name without file extension "default_notification_volume_type": "constant", "default_notification_volume": 0.8, "default_notification_volume_override": false, "important_notification_sound_name": "default_sound_important", # sound name without file extension "important_notification_volume_type": "constant", "important_notification_volume": 0.8, "important_notification_override_dnd": true } ``` ## Update settings via API `PUT /mobile_app/v1/user_settings` - see example response above for payload shape. Note that sound names must be passed without file extension. When sending push notifications, the backend will add `.mp3` to sound names and pass it to push notification data for Android. For iOS, sound names will be suffixed with `.aiff` to be used by APNS. ## Get settings from notification data for Android All the settings from example response will be available in push notification data (along with `orgId`, `alertGroupId`, `title`, etc.). Fields `default_notification_volume`, `default_notification_volume_override` and `important_notification_volume` , `important_notification_override_dnd` will be converted to strings due to FCM limitations. Fields `default_notification_sound_name` and `important_notification_sound_name` will be suffixed with `.mp3` in push notification data. ## iOS limitations While Android push notifications are handled purely on the mobile app side, iOS notifications are sent via APNS which imposes some limitations. - Notification volume cannot be overridden for non-critical notifications (so fields `default_notification_volume_override` and `default_notification_volume` will be disregarded for iOS notifications) - It's not possible to control volume type (i.e. "constant" vs "intensifying") via APNS. A possible workaround is to have different sound files for "constant" and "intensifying" and pass that as `default_notification_sound_name` / `important_notification_sound_name`. # Which issue(s) this PR fixes Related to https://github.com/grafana/oncall-private/issues/1602 # Checklist - [x] Tests updated - [x] No changelog updates since the changes are not user-facing
2023-03-22 14:47:18 +00:00
@pytest.fixture
def make_mobile_app_auth_token_for_user():
def _make_mobile_app_auth_token_for_user(user, organization):
return MobileAppAuthToken.create_auth_token(user, organization)
return _make_mobile_app_auth_token_for_user
@pytest.fixture
def make_slack_token_for_user():
def _make_slack_token_for_user(user):
return SlackAuthToken.create_auth_token(organization=user.organization, user=user)
return _make_slack_token_for_user
@pytest.fixture
def make_public_api_token():
def _make_public_api_token(user, organization, name="test_api_token"):
return ApiAuthToken.create_auth_token(user, organization, name)
return _make_public_api_token
@pytest.fixture
def make_user_auth_headers():
def _make_user_auth_headers(
user,
token,
grafana_token: typing.Optional[str] = None,
grafana_context_data: typing.Optional[typing.Dict] = None,
):
instance_context_headers = {"stack_id": user.organization.stack_id, "org_id": user.organization.org_id}
grafana_context_headers = {"UserId": user.user_id}
if grafana_token is not None:
instance_context_headers["grafana_token"] = grafana_token
if grafana_context_data is not None:
grafana_context_headers.update(grafana_context_data)
return {
"HTTP_X-Instance-Context": json.dumps(instance_context_headers),
"HTTP_X-Grafana-Context": json.dumps(grafana_context_headers),
"HTTP_AUTHORIZATION": f"{token}",
}
return _make_user_auth_headers
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
2022-11-29 09:41:56 +01:00
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():
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
2022-11-29 09:41:56 +01:00
def _make_user(role: typing.Optional[LegacyAccessControlRole] = None, **kwargs):
role = LegacyAccessControlRole.ADMIN if role is None else role
permissions = kwargs.pop("permissions", None)
if permissions is None:
permissions_to_grant = ROLE_PERMISSION_MAPPING[role] if IS_RBAC_ENABLED else []
permissions = [GrafanaAPIPermission(action=perm.value) for perm in permissions_to_grant]
return UserFactory(role=role, permissions=permissions, **kwargs)
return _make_user
@pytest.fixture
def make_organization_and_user(make_organization, make_user_for_organization):
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
2022-11-29 09:41:56 +01:00
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
return _make_organization_and_user
@pytest.fixture
def make_organization_and_user_with_slack_identities(
make_organization_with_slack_team_identity, make_user_with_slack_user_identity
):
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
2022-11-29 09:41:56 +01:00
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
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
2022-11-29 09:41:56 +01:00
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
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
2022-11-29 09:41:56 +01:00
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)
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
2022-11-29 09:41:56 +01:00
organization = make_organization(slack_team_identity=slack_team_identity)
return organization, slack_team_identity
return _make_slack_team_identity_with_organization
@pytest.fixture
def make_slack_team_identity():
def _make_slack_team_identity(**kwargs):
slack_team_identity = SlackTeamIdentityFactory(**kwargs)
return slack_team_identity
return _make_slack_team_identity
@pytest.fixture
def make_slack_user_identity():
def _make_slack_user_identity(**kwargs):
slack_user_identity = SlackUserIdentityFactory(**kwargs)
return slack_user_identity
return _make_slack_user_identity
@pytest.fixture
def make_slack_message():
def _make_slack_message(alert_group, **kwargs):
organization = alert_group.channel.organization
slack_message = SlackMessageFactory(
alert_group=alert_group,
organization=organization,
_slack_team_identity=organization.slack_team_identity,
**kwargs,
)
return slack_message
return _make_slack_message
@pytest.fixture
def client_with_user():
def _client_with_user(user):
"""The client with logged in user"""
client = APIClient()
client.force_login(user)
return client
return _client_with_user
@pytest.fixture
def make_team():
def _make_team(organization, **kwargs):
team = TeamFactory(organization=organization, **kwargs)
return team
return _make_team
@pytest.fixture
def make_alert_receive_channel():
def _make_alert_receive_channel(organization, **kwargs):
if "integration" not in kwargs:
kwargs["integration"] = AlertReceiveChannel.INTEGRATION_GRAFANA
post_save.disconnect(listen_for_alertreceivechannel_model_save, sender=AlertReceiveChannel)
alert_receive_channel = AlertReceiveChannelFactory(organization=organization, **kwargs)
post_save.connect(listen_for_alertreceivechannel_model_save, sender=AlertReceiveChannel)
return alert_receive_channel
return _make_alert_receive_channel
@pytest.fixture
def make_alert_receive_channel_with_post_save_signal():
def _make_alert_receive_channel(organization, **kwargs):
if "integration" not in kwargs:
kwargs["integration"] = AlertReceiveChannel.INTEGRATION_GRAFANA
alert_receive_channel = AlertReceiveChannelFactory(organization=organization, **kwargs)
return alert_receive_channel
return _make_alert_receive_channel
@pytest.fixture
def make_channel_filter():
def _make_channel_filter(alert_receive_channel, filtering_term=None, **kwargs):
channel_filter = ChannelFilterFactory(
filtering_term=filtering_term,
alert_receive_channel=alert_receive_channel,
**kwargs,
)
return channel_filter
return _make_channel_filter
@pytest.fixture
def make_channel_filter_with_post_save():
def _make_channel_filter(alert_receive_channel, filtering_term=None, **kwargs):
channel_filter = ChannelFilterFactory(
filtering_term=filtering_term,
alert_receive_channel=alert_receive_channel,
**kwargs,
)
return channel_filter
return _make_channel_filter
@pytest.fixture
def make_escalation_chain():
def _make_escalation_chain(organization, **kwargs):
escalation_chain = EscalationChainFactory(organization=organization, **kwargs)
return escalation_chain
return _make_escalation_chain
@pytest.fixture
def make_escalation_policy():
def _make_escalation_policy(escalation_chain, escalation_policy_step, **kwargs):
escalation_policy = EscalationPolicyFactory(
escalation_chain=escalation_chain, step=escalation_policy_step, **kwargs
)
return escalation_policy
return _make_escalation_policy
@pytest.fixture
def make_user_notification_policy():
def _make_user_notification_policy(user, step, **kwargs):
user_notification_policy = UserNotificationPolicyFactory(user=user, step=step, **kwargs)
return user_notification_policy
return _make_user_notification_policy
@pytest.fixture
def make_user_notification_policy_log_record():
def _make_user_notification_policy_log_record(**kwargs):
post_save.disconnect(
listen_for_usernotificationpolicylogrecord_model_save, sender=UserNotificationPolicyLogRecord
)
user_notification_policy_log_record = UserNotificationPolicyLogRecordFactory(**kwargs)
post_save.connect(listen_for_usernotificationpolicylogrecord_model_save, sender=UserNotificationPolicyLogRecord)
return user_notification_policy_log_record
return _make_user_notification_policy_log_record
@pytest.fixture
def make_integration_escalation_chain_route_escalation_policy(
make_alert_receive_channel,
make_escalation_chain,
make_channel_filter,
make_escalation_policy,
):
def _make_integration_escalation_chain_route_escalation_policy(organization, escalation_policy_step):
alert_receive_channel = make_alert_receive_channel(organization)
escalation_chain = make_escalation_chain(organization)
default_channel_filter = make_channel_filter(
alert_receive_channel, escalation_chain=escalation_chain, is_default=True
)
escalation_policy = make_escalation_policy(escalation_chain, escalation_policy_step)
return alert_receive_channel, escalation_chain, default_channel_filter, escalation_policy
return _make_integration_escalation_chain_route_escalation_policy
@pytest.fixture
def make_invitation():
def _make_invitation(alert_group, author, invitee, **kwargs):
invitation = InvitationFactory(alert_group=alert_group, author=author, invitee=invitee, **kwargs)
return invitation
return _make_invitation
@pytest.fixture
def make_schedule():
def _make_schedule(organization, schedule_class, **kwargs):
factory = OnCallScheduleFactory.get_factory_for_class(schedule_class)
schedule = factory(organization=organization, **kwargs)
return schedule
return _make_schedule
@pytest.fixture
def make_on_call_shift():
def _make_on_call_shift(organization, shift_type, **kwargs):
on_call_shift = CustomOnCallShiftFactory(organization=organization, type=shift_type, **kwargs)
return on_call_shift
return _make_on_call_shift
@pytest.fixture
def make_alert_group():
def _make_alert_group(alert_receive_channel, **kwargs):
alert_group = AlertGroupFactory(channel=alert_receive_channel, **kwargs)
return alert_group
return _make_alert_group
@pytest.fixture
def make_alert_group_log_record():
def _make_alert_group_log_record(alert_group, type, author, **kwargs):
post_save.disconnect(listen_for_alertgrouplogrecord, sender=AlertGroupLogRecord)
log_record = AlertGroupLogRecordFactory(alert_group=alert_group, type=type, author=author, **kwargs)
post_save.connect(listen_for_alertgrouplogrecord, sender=AlertGroupLogRecord)
return log_record
return _make_alert_group_log_record
@pytest.fixture
def make_resolution_note():
def _make_resolution_note(alert_group, source=ResolutionNote.Source.WEB, author=None, **kwargs):
resolution_note = ResolutionNoteFactory(alert_group=alert_group, source=source, author=author, **kwargs)
return resolution_note
return _make_resolution_note
@pytest.fixture
def make_resolution_note_slack_message():
def _make_resolution_note_slack_message(alert_group, user, added_by_user, **kwargs):
return ResolutionNoteSlackMessageFactory(
alert_group=alert_group, user=user, added_by_user=added_by_user, **kwargs
)
return _make_resolution_note_slack_message
@pytest.fixture
def make_alert():
def _make_alert(alert_group, raw_request_data, **kwargs):
post_save.disconnect(listen_for_alert_model_save, sender=Alert)
alert = AlertFactory(group=alert_group, raw_request_data=raw_request_data, **kwargs)
post_save.connect(listen_for_alert_model_save, sender=Alert)
return alert
return _make_alert
@pytest.fixture
def make_alert_with_custom_create_method():
def _make_alert_with_custom_create_method(
title,
message,
image_url,
link_to_upstream_details,
alert_receive_channel,
integration_unique_data,
raw_request_data,
**kwargs,
):
post_save.disconnect(listen_for_alert_model_save, sender=Alert)
alert = Alert.create(
title,
message,
image_url,
link_to_upstream_details,
alert_receive_channel,
integration_unique_data,
raw_request_data,
**kwargs,
)
post_save.connect(listen_for_alert_model_save, sender=Alert)
return alert
return _make_alert_with_custom_create_method
@pytest.fixture
def make_custom_action():
def _make_custom_action(organization, **kwargs):
custom_action = CustomActionFactory(organization=organization, **kwargs)
return custom_action
return _make_custom_action
@pytest.fixture
def make_custom_webhook():
def _make_custom_webhook(organization, **kwargs):
custom_webhook = CustomWebhookFactory(organization=organization, **kwargs)
return custom_webhook
return _make_custom_webhook
@pytest.fixture
def make_webhook_response():
def _make_webhook_response(**kwargs):
webhook_response = WebhookResponseFactory(**kwargs)
return webhook_response
return _make_webhook_response
@pytest.fixture
def make_slack_user_group():
def _make_slack_user_group(slack_team_identity, **kwargs):
slack_user_group = SlackUserGroupFactory(slack_team_identity=slack_team_identity, **kwargs)
return slack_user_group
return _make_slack_user_group
@pytest.fixture
def make_slack_channel():
def _make_slack_channel(slack_team_identity, **kwargs):
schedule = SlackChannelFactory(slack_team_identity=slack_team_identity, **kwargs)
return schedule
return _make_slack_channel
@pytest.fixture()
def mock_start_disable_maintenance_task(monkeypatch):
def mocked_start_disable_maintenance_task(*args, **kwargs):
return uuid.uuid4()
monkeypatch.setattr(MaintainableObject, "start_disable_maintenance_task", mocked_start_disable_maintenance_task)
@pytest.fixture()
def make_organization_and_user_with_plugin_token(make_organization_and_user, make_token_for_organization):
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
2022-11-29 09:41:56 +01:00
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
add unique idx on user column in mobileapp authtoken table (#1482) # Which issue(s) this PR fixes Solves the (rare) issue where a user could potentially have > 1 mobileapp auth token, leading to 500 errors when trying to interact w/ the authtoken (ex. disconnect a mobile app from a user's profile): ```shell 2023-03-07 10:12:13 source=engine:app google_trace_id=e14bf933d634068a48caf093ce43c7f5/5550677047491218352 logger=django.request Internal Server Error: /api/internal/v1/users/U6WJ3BRLM1TR7/unlink_backend Traceback (most recent call last): File "/usr/local/lib/python3.9/site-packages/django/core/handlers/exception.py", line 47, in inner response = get_response(request) File "/usr/local/lib/python3.9/site-packages/django/core/handlers/base.py", line 181, in _get_response response = wrapped_callback(request, *callback_args, **callback_kwargs) File "/usr/local/lib/python3.9/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view return view_func(*args, **kwargs) File "/usr/local/lib/python3.9/site-packages/rest_framework/viewsets.py", line 125, in view return self.dispatch(request, *args, **kwargs) File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 509, in dispatch response = self.handle_exception(exc) File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 469, in handle_exception self.raise_uncaught_exception(exc) File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception raise exc File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 506, in dispatch response = handler(request, *args, **kwargs) File "/etc/app/apps/api/views/user.py", line 453, in unlink_backend backend.unlink_user(user) File "/etc/app/apps/mobile_app/backend.py", line 34, in unlink_user token = MobileAppAuthToken.objects.get(user=user) File "/usr/local/lib/python3.9/site-packages/django/db/models/manager.py", line 85, in manager_method return getattr(self.get_queryset(), name)(*args, **kwargs) File "/usr/local/lib/python3.9/site-packages/django/db/models/query.py", line 439, in get raise self.model.MultipleObjectsReturned( apps.mobile_app.models.MobileAppAuthToken.MultipleObjectsReturned: get() returned more than one MobileAppAuthToken -- it returned 2! ``` ## Checklist - [x] Tests updated - [ ] Documentation added (N/A) - [x] `CHANGELOG.md` updated
2023-03-08 13:50:57 +01:00
@pytest.fixture()
def make_organization_and_user_with_mobile_app_verification_token(
make_organization_and_user, make_mobile_app_verification_token_for_user
):
def _make_organization_and_user_with_mobile_app_verification_token(
role: typing.Optional[LegacyAccessControlRole] = None,
):
organization, user = make_organization_and_user(role)
_, token = make_mobile_app_verification_token_for_user(user, organization)
return organization, user, token
return _make_organization_and_user_with_mobile_app_verification_token
Mobile app settings backend (#1571) # What this PR does Adds mobile app settings support to OnCall backend. - Adds a new Django model `MobileAppUserSettings` to store push notification settings - Adds a new endpoint `/mobile_app/v1/user_settings` to fetch/update settings from the mobile app Some additional info on implementation: at first I wanted to extend the messaging backend system to allow storing / retrieving per-user data and implement mobile app settings based on those changes. After some thought I decided not to extend the messaging backend system and have this as functionality specific to the `mobile_app` Django app. Currently the messaging backend system is used by the backend and plugin UI, but mobile app settings are specific only to the mobile app and not configurable in the plugin UI. **tldr: wanted to extend messaging backend system, but decided not to do that** # Usage ## Get settings via API `GET /mobile_app/v1/user_settings` Example response: ```json { "default_notification_sound_name": "default_sound", # sound name without file extension "default_notification_volume_type": "constant", "default_notification_volume": 0.8, "default_notification_volume_override": false, "important_notification_sound_name": "default_sound_important", # sound name without file extension "important_notification_volume_type": "constant", "important_notification_volume": 0.8, "important_notification_override_dnd": true } ``` ## Update settings via API `PUT /mobile_app/v1/user_settings` - see example response above for payload shape. Note that sound names must be passed without file extension. When sending push notifications, the backend will add `.mp3` to sound names and pass it to push notification data for Android. For iOS, sound names will be suffixed with `.aiff` to be used by APNS. ## Get settings from notification data for Android All the settings from example response will be available in push notification data (along with `orgId`, `alertGroupId`, `title`, etc.). Fields `default_notification_volume`, `default_notification_volume_override` and `important_notification_volume` , `important_notification_override_dnd` will be converted to strings due to FCM limitations. Fields `default_notification_sound_name` and `important_notification_sound_name` will be suffixed with `.mp3` in push notification data. ## iOS limitations While Android push notifications are handled purely on the mobile app side, iOS notifications are sent via APNS which imposes some limitations. - Notification volume cannot be overridden for non-critical notifications (so fields `default_notification_volume_override` and `default_notification_volume` will be disregarded for iOS notifications) - It's not possible to control volume type (i.e. "constant" vs "intensifying") via APNS. A possible workaround is to have different sound files for "constant" and "intensifying" and pass that as `default_notification_sound_name` / `important_notification_sound_name`. # Which issue(s) this PR fixes Related to https://github.com/grafana/oncall-private/issues/1602 # Checklist - [x] Tests updated - [x] No changelog updates since the changes are not user-facing
2023-03-22 14:47:18 +00:00
@pytest.fixture()
def make_organization_and_user_with_mobile_app_auth_token(
make_organization_and_user, make_mobile_app_auth_token_for_user
):
def _make_organization_and_user_with_mobile_app_auth_token(
role: typing.Optional[LegacyAccessControlRole] = None,
):
organization, user = make_organization_and_user(role)
_, token = make_mobile_app_auth_token_for_user(user, organization)
return organization, user, token
return _make_organization_and_user_with_mobile_app_auth_token
@pytest.fixture()
def mock_send_user_notification_signal(monkeypatch):
def mocked_send_signal(*args, **kwargs):
return None
monkeypatch.setattr(user_notification_action_triggered_signal, "send", mocked_send_signal)
@pytest.fixture()
def make_telegram_user_connector():
def _make_telegram_user_connector(user, **kwargs):
return TelegramToUserConnectorFactory(user=user, **kwargs)
return _make_telegram_user_connector
@pytest.fixture()
def make_telegram_channel():
def _make_telegram_channel(organization, is_default_channel=False):
return TelegramChannelFactory(organization=organization, is_default_channel=is_default_channel)
return _make_telegram_channel
@pytest.fixture()
def make_telegram_verification_code():
def _make_telegram_verification_code(user, **kwargs):
return TelegramVerificationCodeFactory(user=user, **kwargs)
return _make_telegram_verification_code
@pytest.fixture()
def make_telegram_channel_verification_code():
def _make_telegram_channel_verification_code(organization, author, **kwargs):
return TelegramChannelVerificationCodeFactory(organization=organization, author=author, **kwargs)
return _make_telegram_channel_verification_code
@pytest.fixture()
def make_telegram_message():
def _make_telegram_message(alert_group, message_type, **kwargs):
return TelegramMessageFactory(alert_group=alert_group, message_type=message_type, **kwargs)
return _make_telegram_message
@pytest.fixture()
Phone provider refactoring (#1713) # What this PR does This PR moves phone notification logic into separate object PhoneBackend and introduces PhoneProvider interface to hide actual implementation of external phone services provider. It should allow add new phone providers just by implementing one class (See SimplePhoneProvider for example). # Why [Asterisk PR](https://github.com/grafana/oncall/pull/1282) showed that our phone notification system is not flexible. However this is one of the most frequent community questions - how to add "X" phone provider. Also, this refactoring move us one step closer to unifying all notification backends, since with PhoneBackend all phone notification logic is collected in one place and independent from concrete realisation. # Highligts 1. PhoneBackend object - contains all phone notifications business logic. 2. PhoneProvider - interface to external phone services provider. 3. TwilioPhoneProvider and SimplePhoneProvider - two examples of PhoneProvider implementation. 4. PhoneCallRecord and SMSRecord models. I introduced these models to keep phone notification limits logic decoupled from external providers. Existing TwilioPhoneCall and TwilioSMS objects will be migrated to the new table to not to reset limits counter. To be able to receive status callbacks and gather from Twilio TwilioPhoneCall and TwilioSMS still exists, but they are linked to PhoneCallRecord and SMSRecord via fk, to not to leat twilio logic into core code. --------- Co-authored-by: Yulia Shanyrova <yulia.shanyrova@grafana.com>
2023-05-24 14:27:48 +08:00
def make_phone_call_record():
def _make_phone_call_record(receiver, **kwargs):
return PhoneCallRecordFactory(receiver=receiver, **kwargs)
Phone provider refactoring (#1713) # What this PR does This PR moves phone notification logic into separate object PhoneBackend and introduces PhoneProvider interface to hide actual implementation of external phone services provider. It should allow add new phone providers just by implementing one class (See SimplePhoneProvider for example). # Why [Asterisk PR](https://github.com/grafana/oncall/pull/1282) showed that our phone notification system is not flexible. However this is one of the most frequent community questions - how to add "X" phone provider. Also, this refactoring move us one step closer to unifying all notification backends, since with PhoneBackend all phone notification logic is collected in one place and independent from concrete realisation. # Highligts 1. PhoneBackend object - contains all phone notifications business logic. 2. PhoneProvider - interface to external phone services provider. 3. TwilioPhoneProvider and SimplePhoneProvider - two examples of PhoneProvider implementation. 4. PhoneCallRecord and SMSRecord models. I introduced these models to keep phone notification limits logic decoupled from external providers. Existing TwilioPhoneCall and TwilioSMS objects will be migrated to the new table to not to reset limits counter. To be able to receive status callbacks and gather from Twilio TwilioPhoneCall and TwilioSMS still exists, but they are linked to PhoneCallRecord and SMSRecord via fk, to not to leat twilio logic into core code. --------- Co-authored-by: Yulia Shanyrova <yulia.shanyrova@grafana.com>
2023-05-24 14:27:48 +08:00
return _make_phone_call_record
@pytest.fixture()
Phone provider refactoring (#1713) # What this PR does This PR moves phone notification logic into separate object PhoneBackend and introduces PhoneProvider interface to hide actual implementation of external phone services provider. It should allow add new phone providers just by implementing one class (See SimplePhoneProvider for example). # Why [Asterisk PR](https://github.com/grafana/oncall/pull/1282) showed that our phone notification system is not flexible. However this is one of the most frequent community questions - how to add "X" phone provider. Also, this refactoring move us one step closer to unifying all notification backends, since with PhoneBackend all phone notification logic is collected in one place and independent from concrete realisation. # Highligts 1. PhoneBackend object - contains all phone notifications business logic. 2. PhoneProvider - interface to external phone services provider. 3. TwilioPhoneProvider and SimplePhoneProvider - two examples of PhoneProvider implementation. 4. PhoneCallRecord and SMSRecord models. I introduced these models to keep phone notification limits logic decoupled from external providers. Existing TwilioPhoneCall and TwilioSMS objects will be migrated to the new table to not to reset limits counter. To be able to receive status callbacks and gather from Twilio TwilioPhoneCall and TwilioSMS still exists, but they are linked to PhoneCallRecord and SMSRecord via fk, to not to leat twilio logic into core code. --------- Co-authored-by: Yulia Shanyrova <yulia.shanyrova@grafana.com>
2023-05-24 14:27:48 +08:00
def make_sms_record():
def _make_sms_record(receiver, **kwargs):
return SMSRecordFactory(receiver=receiver, **kwargs)
Phone provider refactoring (#1713) # What this PR does This PR moves phone notification logic into separate object PhoneBackend and introduces PhoneProvider interface to hide actual implementation of external phone services provider. It should allow add new phone providers just by implementing one class (See SimplePhoneProvider for example). # Why [Asterisk PR](https://github.com/grafana/oncall/pull/1282) showed that our phone notification system is not flexible. However this is one of the most frequent community questions - how to add "X" phone provider. Also, this refactoring move us one step closer to unifying all notification backends, since with PhoneBackend all phone notification logic is collected in one place and independent from concrete realisation. # Highligts 1. PhoneBackend object - contains all phone notifications business logic. 2. PhoneProvider - interface to external phone services provider. 3. TwilioPhoneProvider and SimplePhoneProvider - two examples of PhoneProvider implementation. 4. PhoneCallRecord and SMSRecord models. I introduced these models to keep phone notification limits logic decoupled from external providers. Existing TwilioPhoneCall and TwilioSMS objects will be migrated to the new table to not to reset limits counter. To be able to receive status callbacks and gather from Twilio TwilioPhoneCall and TwilioSMS still exists, but they are linked to PhoneCallRecord and SMSRecord via fk, to not to leat twilio logic into core code. --------- Co-authored-by: Yulia Shanyrova <yulia.shanyrova@grafana.com>
2023-05-24 14:27:48 +08:00
return _make_sms_record
@pytest.fixture()
def make_email_message():
def _make_email_message(receiver, **kwargs):
return EmailMessageFactory(receiver=receiver, **kwargs)
return _make_email_message
@pytest.fixture()
def make_live_setting():
def _make_live_setting(name, **kwargs):
return LiveSettingFactory(name=name, **kwargs)
return _make_live_setting
@pytest.fixture()
def make_integration_heartbeat():
def _make_integration_heartbeat(alert_receive_channel, timeout_seconds=60, last_heartbeat_time=None, **kwargs):
return IntegrationHeartBeatFactory(
alert_receive_channel=alert_receive_channel,
timeout_seconds=timeout_seconds,
last_heartbeat_time=last_heartbeat_time,
**kwargs,
)
return _make_integration_heartbeat
@pytest.fixture
Enforce cloud connection to send push notifications on OSS (#1132) This PR modifies how OSS instances send mobile app push notifications. It also adds frontend warnings when user is trying to use the mobile app without connecting to cloud. - [x] Add public API authentication to `FCMRelayView` and throttle the view to 300 push notifications per instance per minute. This is similar to how SMS and phone call notifications work on OSS instances. - [x] Add frontend warnings based on cloud connectivity - [x] Fix/add frontend tests - [x] Add tests for FCMRelayView and mobile app backend ## Screenshots When a user tries to connect the mobile app in his settings and cloud is not connected (clicking "Connect Cloud OnCall" redirects to the "Cloud" tab): <img width="1088" alt="Screenshot 2023-01-12 at 18 48 58" src="https://user-images.githubusercontent.com/20116910/212156591-86906020-eddf-43f1-9402-7ebb7547c7e6.png"> When a user tries to use mobile push notifications as a personal notification step and cloud is not connected: <img width="764" alt="Screenshot 2023-01-12 at 19 01 10" src="https://user-images.githubusercontent.com/20116910/212157580-9abb0758-79ad-4316-b8cd-15b4fff01502.png"> Now on the "Cloud" tab there's some info about the mobile app (the last section at the bottom of the page): <img width="1245" alt="Screenshot 2023-01-12 at 18 49 10" src="https://user-images.githubusercontent.com/20116910/212156997-c8b70dd5-bf15-4bc7-8eb8-9decdb8ecc80.png"> After connecting to the cloud instance, everything goes back to active and it's now possible to connect the mobile app: <img width="1091" alt="Screenshot 2023-01-12 at 19 08 27" src="https://user-images.githubusercontent.com/20116910/212158811-60d49888-4714-4c0e-850f-3ff6a11a117a.png"> After connecting the app the warning is gone: <img width="764" alt="Screenshot 2023-01-12 at 19 07 00" src="https://user-images.githubusercontent.com/20116910/212158614-677ab889-127f-4d64-bacc-0c26887f3097.png">
2023-01-19 11:15:56 +00:00
def reload_urls(settings):
"""
Reloads Django URLs, especially useful when testing conditionally registered URLs
"""
def _reload_urls():
clear_url_caches()
urlconf = settings.ROOT_URLCONF
if urlconf in sys.modules:
reload(sys.modules[urlconf])
else:
import_module(urlconf)
return _reload_urls
2022-10-27 15:40:46 -06:00
Enforce cloud connection to send push notifications on OSS (#1132) This PR modifies how OSS instances send mobile app push notifications. It also adds frontend warnings when user is trying to use the mobile app without connecting to cloud. - [x] Add public API authentication to `FCMRelayView` and throttle the view to 300 push notifications per instance per minute. This is similar to how SMS and phone call notifications work on OSS instances. - [x] Add frontend warnings based on cloud connectivity - [x] Fix/add frontend tests - [x] Add tests for FCMRelayView and mobile app backend ## Screenshots When a user tries to connect the mobile app in his settings and cloud is not connected (clicking "Connect Cloud OnCall" redirects to the "Cloud" tab): <img width="1088" alt="Screenshot 2023-01-12 at 18 48 58" src="https://user-images.githubusercontent.com/20116910/212156591-86906020-eddf-43f1-9402-7ebb7547c7e6.png"> When a user tries to use mobile push notifications as a personal notification step and cloud is not connected: <img width="764" alt="Screenshot 2023-01-12 at 19 01 10" src="https://user-images.githubusercontent.com/20116910/212157580-9abb0758-79ad-4316-b8cd-15b4fff01502.png"> Now on the "Cloud" tab there's some info about the mobile app (the last section at the bottom of the page): <img width="1245" alt="Screenshot 2023-01-12 at 18 49 10" src="https://user-images.githubusercontent.com/20116910/212156997-c8b70dd5-bf15-4bc7-8eb8-9decdb8ecc80.png"> After connecting to the cloud instance, everything goes back to active and it's now possible to connect the mobile app: <img width="1091" alt="Screenshot 2023-01-12 at 19 08 27" src="https://user-images.githubusercontent.com/20116910/212158811-60d49888-4714-4c0e-850f-3ff6a11a117a.png"> After connecting the app the warning is gone: <img width="764" alt="Screenshot 2023-01-12 at 19 07 00" src="https://user-images.githubusercontent.com/20116910/212158614-677ab889-127f-4d64-bacc-0c26887f3097.png">
2023-01-19 11:15:56 +00:00
@pytest.fixture()
def load_slack_urls(settings, reload_urls):
Enforce cloud connection to send push notifications on OSS (#1132) This PR modifies how OSS instances send mobile app push notifications. It also adds frontend warnings when user is trying to use the mobile app without connecting to cloud. - [x] Add public API authentication to `FCMRelayView` and throttle the view to 300 push notifications per instance per minute. This is similar to how SMS and phone call notifications work on OSS instances. - [x] Add frontend warnings based on cloud connectivity - [x] Fix/add frontend tests - [x] Add tests for FCMRelayView and mobile app backend ## Screenshots When a user tries to connect the mobile app in his settings and cloud is not connected (clicking "Connect Cloud OnCall" redirects to the "Cloud" tab): <img width="1088" alt="Screenshot 2023-01-12 at 18 48 58" src="https://user-images.githubusercontent.com/20116910/212156591-86906020-eddf-43f1-9402-7ebb7547c7e6.png"> When a user tries to use mobile push notifications as a personal notification step and cloud is not connected: <img width="764" alt="Screenshot 2023-01-12 at 19 01 10" src="https://user-images.githubusercontent.com/20116910/212157580-9abb0758-79ad-4316-b8cd-15b4fff01502.png"> Now on the "Cloud" tab there's some info about the mobile app (the last section at the bottom of the page): <img width="1245" alt="Screenshot 2023-01-12 at 18 49 10" src="https://user-images.githubusercontent.com/20116910/212156997-c8b70dd5-bf15-4bc7-8eb8-9decdb8ecc80.png"> After connecting to the cloud instance, everything goes back to active and it's now possible to connect the mobile app: <img width="1091" alt="Screenshot 2023-01-12 at 19 08 27" src="https://user-images.githubusercontent.com/20116910/212158811-60d49888-4714-4c0e-850f-3ff6a11a117a.png"> After connecting the app the warning is gone: <img width="764" alt="Screenshot 2023-01-12 at 19 07 00" src="https://user-images.githubusercontent.com/20116910/212158614-677ab889-127f-4d64-bacc-0c26887f3097.png">
2023-01-19 11:15:56 +00:00
settings.FEATURE_SLACK_INTEGRATION_ENABLED = True
reload_urls()
Enforce cloud connection to send push notifications on OSS (#1132) This PR modifies how OSS instances send mobile app push notifications. It also adds frontend warnings when user is trying to use the mobile app without connecting to cloud. - [x] Add public API authentication to `FCMRelayView` and throttle the view to 300 push notifications per instance per minute. This is similar to how SMS and phone call notifications work on OSS instances. - [x] Add frontend warnings based on cloud connectivity - [x] Fix/add frontend tests - [x] Add tests for FCMRelayView and mobile app backend ## Screenshots When a user tries to connect the mobile app in his settings and cloud is not connected (clicking "Connect Cloud OnCall" redirects to the "Cloud" tab): <img width="1088" alt="Screenshot 2023-01-12 at 18 48 58" src="https://user-images.githubusercontent.com/20116910/212156591-86906020-eddf-43f1-9402-7ebb7547c7e6.png"> When a user tries to use mobile push notifications as a personal notification step and cloud is not connected: <img width="764" alt="Screenshot 2023-01-12 at 19 01 10" src="https://user-images.githubusercontent.com/20116910/212157580-9abb0758-79ad-4316-b8cd-15b4fff01502.png"> Now on the "Cloud" tab there's some info about the mobile app (the last section at the bottom of the page): <img width="1245" alt="Screenshot 2023-01-12 at 18 49 10" src="https://user-images.githubusercontent.com/20116910/212156997-c8b70dd5-bf15-4bc7-8eb8-9decdb8ecc80.png"> After connecting to the cloud instance, everything goes back to active and it's now possible to connect the mobile app: <img width="1091" alt="Screenshot 2023-01-12 at 19 08 27" src="https://user-images.githubusercontent.com/20116910/212158811-60d49888-4714-4c0e-850f-3ff6a11a117a.png"> After connecting the app the warning is gone: <img width="764" alt="Screenshot 2023-01-12 at 19 07 00" src="https://user-images.githubusercontent.com/20116910/212158614-677ab889-127f-4d64-bacc-0c26887f3097.png">
2023-01-19 11:15:56 +00:00
2022-10-27 15:40:46 -06:00
@pytest.fixture
def make_region():
def _make_region(**kwargs):
region = RegionFactory(**kwargs)
return region
return _make_region
@pytest.fixture
def make_organization_and_region(make_organization, make_region):
def _make_organization_and_region():
organization = make_organization()
region = make_region()
organization.migration_destination = region
return organization, region
return _make_organization_and_region
@pytest.fixture()
def make_organization_and_user_with_token(make_organization_and_user, make_public_api_token):
def _make_organization_and_user_with_token():
organization, user = make_organization_and_user()
_, token = make_public_api_token(user, organization)
return organization, user, token
return _make_organization_and_user_with_token