Add basic organization moved exception handling and middleware
This commit is contained in:
parent
0a1a9ab4d8
commit
febe1b2185
9 changed files with 129 additions and 1 deletions
|
|
@ -18,6 +18,7 @@ from .exceptions import InvalidToken
|
|||
from .models import ApiAuthToken, PluginAuthToken, ScheduleExportAuthToken, SlackAuthToken, UserScheduleExportAuthToken
|
||||
from .models.mobile_app_auth_token import MobileAppAuthToken
|
||||
from .models.mobile_app_verification_token import MobileAppVerificationToken
|
||||
from ..user_management.models.region import OrganizationMovedException
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
|
@ -46,6 +47,10 @@ class ApiTokenAuthentication(BaseAuthentication):
|
|||
auth_token = self.model.validate_token_string(token)
|
||||
except InvalidToken:
|
||||
raise exceptions.AuthenticationFailed("Invalid token.")
|
||||
|
||||
if auth_token.organization.migration_destination is not None:
|
||||
raise OrganizationMovedException(auth_token.organization)
|
||||
|
||||
return auth_token.user, auth_token
|
||||
|
||||
|
||||
|
|
@ -167,6 +172,9 @@ class ScheduleExportAuthentication(BaseAuthentication):
|
|||
except InvalidToken:
|
||||
raise exceptions.AuthenticationFailed("Invalid token.")
|
||||
|
||||
if auth_token.organization.migration_destination is not None:
|
||||
raise OrganizationMovedException(auth_token.organization)
|
||||
|
||||
if auth_token.schedule.public_primary_key != public_primary_key:
|
||||
raise exceptions.AuthenticationFailed("Invalid schedule export token for schedule")
|
||||
|
||||
|
|
@ -197,6 +205,9 @@ class UserScheduleExportAuthentication(BaseAuthentication):
|
|||
except InvalidToken:
|
||||
raise exceptions.AuthenticationFailed("Invalid token")
|
||||
|
||||
if auth_token.organization.migration_destination is not None:
|
||||
raise OrganizationMovedException(auth_token.organization)
|
||||
|
||||
if auth_token.user.public_primary_key != public_primary_key:
|
||||
raise exceptions.AuthenticationFailed("Invalid schedule export token for user")
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ from django.core.cache import cache
|
|||
from django.core.exceptions import PermissionDenied
|
||||
from django.db import OperationalError
|
||||
|
||||
from apps.user_management.models.region import OrganizationMovedException
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
|
@ -64,6 +66,9 @@ class AlertChannelDefiningMixin(object):
|
|||
logger.info("Cache is empty!")
|
||||
raise
|
||||
|
||||
if alert_receive_channel.organization.migration_destination is not None:
|
||||
raise OrganizationMovedException(alert_receive_channel.organization)
|
||||
|
||||
del kwargs["alert_channel_key"]
|
||||
kwargs["alert_receive_channel"] = alert_receive_channel
|
||||
|
||||
|
|
|
|||
41
engine/apps/user_management/middlewares.py
Normal file
41
engine/apps/user_management/middlewares.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import logging
|
||||
import re
|
||||
|
||||
import requests
|
||||
from django.http import HttpResponse
|
||||
from django.utils.deprecation import MiddlewareMixin
|
||||
|
||||
from apps.user_management.models.region import OrganizationMovedException
|
||||
from common.api_helpers.utils import create_engine_url
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OrganizationMovedMiddleware(MiddlewareMixin):
|
||||
def process_exception(self, request, exception):
|
||||
if isinstance(exception, OrganizationMovedException):
|
||||
region = exception.organization.migration_destination
|
||||
url = create_engine_url(request.path, override_base=region.oncall_backend_url)
|
||||
if request.META['QUERY_STRING']:
|
||||
url = f"{url}?{request.META['QUERY_STRING']}"
|
||||
|
||||
regex = re.compile('^HTTP_')
|
||||
headers = dict(
|
||||
(regex.sub('', header), value) for (header, value) in request.META.items() if header.startswith('HTTP_')
|
||||
)
|
||||
|
||||
if request.method == "GET":
|
||||
response = requests.get(url, headers=headers)
|
||||
elif request.method == "POST":
|
||||
response = requests.post(url, data=request.body, headers=headers)
|
||||
elif request.method == "PUT":
|
||||
response = requests.put(url, data=request.body, headers=headers)
|
||||
elif request.method == "DELETE":
|
||||
response = requests.delete(url, headers=headers)
|
||||
elif request.method == "OPTIONS":
|
||||
response = requests.options(url, headers=headers)
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
return HttpResponse(response.content, status=response.status_code)
|
||||
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
# Generated by Django 3.2.15 on 2022-10-20 18:45
|
||||
|
||||
import apps.user_management.models.region
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('user_management', '0004_organization_region_slug'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Region',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('public_primary_key', models.CharField(default=apps.user_management.models.region.generate_public_primary_key_for_region, max_length=20, unique=True, validators=[django.core.validators.MinLengthValidator(13)])),
|
||||
('name', models.CharField(max_length=300)),
|
||||
('slug', models.CharField(max_length=300, unique=True)),
|
||||
('oncall_backend_url', models.URLField()),
|
||||
('is_default', models.BooleanField(default=False)),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='organization',
|
||||
name='migration_destination',
|
||||
field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='regions', to='user_management.region'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.15 on 2022-10-20 18:46
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('user_management', '0005_auto_20221020_1845'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='region',
|
||||
name='slug',
|
||||
field=models.CharField(max_length=50, unique=True),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
from .user import User # noqa: F401, isort: skip
|
||||
from .organization import Organization # noqa: F401
|
||||
from .region import Region # noqa: F401
|
||||
from .team import Team # noqa: F401
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ class Organization(MaintainableObject):
|
|||
on_delete=models.SET_NULL,
|
||||
related_name="regions",
|
||||
default=None,
|
||||
null=True,
|
||||
)
|
||||
|
||||
grafana_url = models.URLField()
|
||||
|
|
|
|||
|
|
@ -1,10 +1,24 @@
|
|||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.validators import MinLengthValidator
|
||||
from django.db import models
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
|
||||
from apps.user_management.models import Organization
|
||||
from common.public_primary_keys import generate_public_primary_key, increase_public_primary_key_length
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OrganizationMovedException(Exception):
|
||||
|
||||
def __init__(self, organization: Organization):
|
||||
self.organization = organization
|
||||
|
||||
|
||||
def generate_public_primary_key_for_region():
|
||||
prefix = "R"
|
||||
new_public_primary_key = generate_public_primary_key(prefix)
|
||||
|
|
@ -19,6 +33,10 @@ def generate_public_primary_key_for_region():
|
|||
return new_public_primary_key
|
||||
|
||||
|
||||
def redirect_organization_request(organization: Organization, request: Request):
|
||||
logger.info("**** Redirect! ****")
|
||||
|
||||
|
||||
class Region(models.Model):
|
||||
public_primary_key = models.CharField(
|
||||
max_length=20,
|
||||
|
|
@ -28,6 +46,6 @@ class Region(models.Model):
|
|||
)
|
||||
|
||||
name = models.CharField(max_length=300)
|
||||
slug = models.CharField(max_length=300, unique=True)
|
||||
slug = models.CharField(max_length=50, unique=True)
|
||||
oncall_backend_url = models.URLField()
|
||||
is_default = models.BooleanField(default=False)
|
||||
|
|
|
|||
|
|
@ -237,6 +237,7 @@ MIDDLEWARE = [
|
|||
"social_django.middleware.SocialAuthExceptionMiddleware",
|
||||
"apps.social_auth.middlewares.SocialAuthAuthCanceledExceptionMiddleware",
|
||||
"apps.integrations.middlewares.IntegrationExceptionMiddleware",
|
||||
"apps.user_management.middlewares.OrganizationMovedMiddleware",
|
||||
]
|
||||
|
||||
LOG_REQUEST_ID_HEADER = "HTTP_X_CLOUD_TRACE_CONTEXT"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue