Skip to content

ref(control_silo): Move UserPermission model to users module #76145

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/sentry/api/endpoints/user_permission_details.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from sentry.api.bases.user import UserEndpoint
from sentry.api.decorators import sudo_required
from sentry.api.permissions import SuperuserOrStaffFeatureFlaggedPermission
from sentry.models.userpermission import UserPermission
from sentry.users.models.userpermission import UserPermission

audit_logger = logging.getLogger("sentry.audit.user")

Expand Down
2 changes: 1 addition & 1 deletion src/sentry/api/endpoints/user_permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from sentry.api.base import control_silo_endpoint
from sentry.api.bases.user import UserEndpoint
from sentry.api.permissions import SuperuserOrStaffFeatureFlaggedPermission
from sentry.models.userpermission import UserPermission
from sentry.users.models.userpermission import UserPermission


@control_silo_endpoint
Expand Down
2 changes: 1 addition & 1 deletion src/sentry/api/serializers/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@
from sentry.models.organization import OrganizationStatus
from sentry.models.organizationmapping import OrganizationMapping
from sentry.models.organizationmembermapping import OrganizationMemberMapping
from sentry.models.userpermission import UserPermission
from sentry.organizations.services.organization import RpcOrganizationSummary
from sentry.users.models.authenticator import Authenticator
from sentry.users.models.user import User
from sentry.users.models.useremail import UserEmail
from sentry.users.models.userpermission import UserPermission
from sentry.users.models.userrole import UserRoleUser
from sentry.users.services.user import RpcUser
from sentry.utils.avatar import get_gravatar_url
Expand Down
2 changes: 1 addition & 1 deletion src/sentry/backup/services/import_export/impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@
from sentry.hybridcloud.models.outbox import outbox_context
from sentry.models.importchunk import ControlImportChunk, RegionImportChunk
from sentry.models.organizationmember import OrganizationMember
from sentry.models.userpermission import UserPermission
from sentry.silo.base import SiloMode
from sentry.users.models.user import User
from sentry.users.models.userpermission import UserPermission
from sentry.users.models.userrole import UserRoleUser

logger = logging.getLogger(__name__)
Expand Down
2 changes: 1 addition & 1 deletion src/sentry/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from sentry.users.models.lostpasswordhash import * # NOQA
from sentry.users.models.user import * # NOQA
from sentry.users.models.useremail import * # NOQA
from sentry.users.models.userpermission import * # NOQA
from sentry.users.models.userrole import * # NOQA

from .activity import * # NOQA
Expand Down Expand Up @@ -123,5 +124,4 @@
from .tombstone import * # NOQA
from .transaction_threshold import * # NOQA
from .userip import * # NOQA
from .userpermission import * # NOQA
from .userreport import * # NOQA
72 changes: 2 additions & 70 deletions src/sentry/models/userpermission.py
Original file line number Diff line number Diff line change
@@ -1,71 +1,3 @@
from __future__ import annotations
from sentry.users.models.userpermission import UserPermission

from django.db import models

from sentry.backup.dependencies import ImportKind, PrimaryKeyMap, get_model_name
from sentry.backup.helpers import ImportFlags
from sentry.backup.mixins import OverwritableConfigMixin
from sentry.backup.scopes import ImportScope, RelocationScope
from sentry.db.models import FlexibleForeignKey, control_silo_model, sane_repr
from sentry.hybridcloud.models.outbox import ControlOutboxBase
from sentry.hybridcloud.outbox.base import ControlOutboxProducingModel
from sentry.hybridcloud.outbox.category import OutboxCategory
from sentry.types.region import find_regions_for_user


@control_silo_model
class UserPermission(OverwritableConfigMixin, ControlOutboxProducingModel):
"""
Permissions are applied to administrative users and control explicit scope-like permissions within the API.

Generally speaking, they should only apply to active superuser sessions.
"""

__relocation_scope__ = RelocationScope.Config
__relocation_custom_ordinal__ = ["user", "permission"]

user = FlexibleForeignKey("sentry.User")
# permissions should be in the form of 'service-name.permission-name'
permission = models.CharField(max_length=32)

class Meta:
app_label = "sentry"
db_table = "sentry_userpermission"
unique_together = (("user", "permission"),)

__repr__ = sane_repr("user_id", "permission")

@classmethod
def for_user(cls, user_id: int) -> frozenset[str]:
"""
Return a set of permission for the given user ID.
"""
return frozenset(cls.objects.filter(user=user_id).values_list("permission", flat=True))

def outboxes_for_update(self, shard_identifier: int | None = None) -> list[ControlOutboxBase]:
regions = find_regions_for_user(self.user_id)
return [
outbox
for outbox in OutboxCategory.USER_UPDATE.as_control_outboxes(
region_names=regions,
shard_identifier=self.user_id,
object_identifier=self.user_id,
)
]

def normalize_before_relocation_import(
self, pk_map: PrimaryKeyMap, scope: ImportScope, flags: ImportFlags
) -> int | None:
from sentry.users.models.user import User

old_user_id = self.user_id
old_pk = super().normalize_before_relocation_import(pk_map, scope, flags)
if old_pk is None:
return None

# If we are merging users, ignore the imported permissions and use the existing user's
# permissions instead.
if pk_map.get_kind(get_model_name(User), old_user_id) == ImportKind.Existing:
return None

return old_pk
__all__ = ("UserPermission",)
6 changes: 3 additions & 3 deletions src/sentry/runner/commands/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def add(user: str, permission: str) -> None:
"Add a permission to a user."
from django.db import IntegrityError, transaction

from sentry.models.userpermission import UserPermission
from sentry.users.models.userpermission import UserPermission

user_inst = user_param_to_user(user)

Expand All @@ -57,7 +57,7 @@ def add(user: str, permission: str) -> None:
@configuration
def remove(user: str, permission: str) -> None:
"Remove a permission from a user."
from sentry.models.userpermission import UserPermission
from sentry.users.models.userpermission import UserPermission

user_inst = user_param_to_user(user)

Expand All @@ -75,7 +75,7 @@ def remove(user: str, permission: str) -> None:
@configuration
def list(user: str) -> None:
"List permissions for a user."
from sentry.models.userpermission import UserPermission
from sentry.users.models.userpermission import UserPermission

user_inst = user_param_to_user(user)
up_list = UserPermission.objects.filter(user=user_inst).order_by("permission")
Expand Down
2 changes: 1 addition & 1 deletion src/sentry/testutils/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@
from sentry.models.savedsearch import SavedSearch
from sentry.models.servicehook import ServiceHook
from sentry.models.team import Team
from sentry.models.userpermission import UserPermission
from sentry.models.userreport import UserReport
from sentry.organizations.services.organization import RpcOrganization, RpcUserOrganizationContext
from sentry.sentry_apps.apps import SentryAppCreator
Expand Down Expand Up @@ -167,6 +166,7 @@
)
from sentry.users.models.user import User
from sentry.users.models.useremail import UserEmail
from sentry.users.models.userpermission import UserPermission
from sentry.users.models.userrole import UserRole
from sentry.users.services.user import RpcUser
from sentry.utils import loremipsum
Expand Down
71 changes: 71 additions & 0 deletions src/sentry/users/models/userpermission.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from __future__ import annotations

from django.db import models

from sentry.backup.dependencies import ImportKind, PrimaryKeyMap, get_model_name
from sentry.backup.helpers import ImportFlags
from sentry.backup.mixins import OverwritableConfigMixin
from sentry.backup.scopes import ImportScope, RelocationScope
from sentry.db.models import FlexibleForeignKey, control_silo_model, sane_repr
from sentry.hybridcloud.models.outbox import ControlOutboxBase
from sentry.hybridcloud.outbox.base import ControlOutboxProducingModel
from sentry.hybridcloud.outbox.category import OutboxCategory
from sentry.types.region import find_regions_for_user


@control_silo_model
class UserPermission(OverwritableConfigMixin, ControlOutboxProducingModel):
"""
Permissions are applied to administrative users and control explicit scope-like permissions within the API.

Generally speaking, they should only apply to active superuser sessions.
"""

__relocation_scope__ = RelocationScope.Config
__relocation_custom_ordinal__ = ["user", "permission"]

user = FlexibleForeignKey("sentry.User")
# permissions should be in the form of 'service-name.permission-name'
permission = models.CharField(max_length=32)

class Meta:
app_label = "sentry"
db_table = "sentry_userpermission"
unique_together = (("user", "permission"),)

__repr__ = sane_repr("user_id", "permission")

@classmethod
def for_user(cls, user_id: int) -> frozenset[str]:
"""
Return a set of permission for the given user ID.
"""
return frozenset(cls.objects.filter(user=user_id).values_list("permission", flat=True))

def outboxes_for_update(self, shard_identifier: int | None = None) -> list[ControlOutboxBase]:
regions = find_regions_for_user(self.user_id)
return [
outbox
for outbox in OutboxCategory.USER_UPDATE.as_control_outboxes(
region_names=regions,
shard_identifier=self.user_id,
object_identifier=self.user_id,
)
]

def normalize_before_relocation_import(
self, pk_map: PrimaryKeyMap, scope: ImportScope, flags: ImportFlags
) -> int | None:
from sentry.users.models.user import User

old_user_id = self.user_id
old_pk = super().normalize_before_relocation_import(pk_map, scope, flags)
if old_pk is None:
return None

# If we are merging users, ignore the imported permissions and use the existing user's
# permissions instead.
if pk_map.get_kind(get_model_name(User), old_user_id) == ImportKind.Existing:
return None

return old_pk
2 changes: 1 addition & 1 deletion tests/sentry/api/endpoints/test_user_details.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from sentry.models.options.user_option import UserOption
from sentry.models.organization import Organization, OrganizationStatus
from sentry.models.organizationmember import OrganizationMember
from sentry.models.userpermission import UserPermission
from sentry.silo.base import SiloMode
from sentry.tasks.deletion.hybrid_cloud import schedule_hybrid_cloud_foreign_key_jobs
from sentry.testutils.cases import APITestCase
Expand All @@ -14,6 +13,7 @@
from sentry.testutils.outbox import outbox_runner
from sentry.testutils.silo import assume_test_silo_mode, control_silo_test
from sentry.users.models.user import User
from sentry.users.models.userpermission import UserPermission
from sentry.users.models.userrole import UserRole


Expand Down
2 changes: 1 addition & 1 deletion tests/sentry/api/endpoints/test_user_index.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from sentry.models.userpermission import UserPermission
from sentry.testutils.cases import APITestCase
from sentry.testutils.helpers.options import override_options
from sentry.testutils.silo import control_silo_test
from sentry.users.models.userpermission import UserPermission


@control_silo_test
Expand Down
2 changes: 1 addition & 1 deletion tests/sentry/api/endpoints/test_user_permission_details.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from unittest.mock import patch

from sentry.api.permissions import StaffPermission
from sentry.models.userpermission import UserPermission
from sentry.testutils.cases import APITestCase
from sentry.testutils.helpers.options import override_options
from sentry.testutils.silo import control_silo_test
from sentry.users.models.userpermission import UserPermission


@control_silo_test
Expand Down
2 changes: 1 addition & 1 deletion tests/sentry/api/serializers/test_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
from sentry.models.authidentity import AuthIdentity
from sentry.models.authprovider import AuthProvider
from sentry.models.avatars.user_avatar import UserAvatar
from sentry.models.userpermission import UserPermission
from sentry.testutils.cases import TestCase
from sentry.testutils.silo import control_silo_test
from sentry.users.models.authenticator import Authenticator
from sentry.users.models.useremail import UserEmail
from sentry.users.models.userpermission import UserPermission


@control_silo_test
Expand Down
2 changes: 1 addition & 1 deletion tests/sentry/backup/test_exports.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
from sentry.models.organization import Organization
from sentry.models.organizationmember import OrganizationMember
from sentry.models.orgauthtoken import OrgAuthToken
from sentry.models.userpermission import UserPermission
from sentry.testutils.helpers.backups import (
BackupTransactionTestCase,
ValidationError,
Expand All @@ -25,6 +24,7 @@
from sentry.users.models.email import Email
from sentry.users.models.user import User
from sentry.users.models.useremail import UserEmail
from sentry.users.models.userpermission import UserPermission
from sentry.users.models.userrole import UserRole, UserRoleUser
from tests.sentry.backup import get_matching_exportable_models

Expand Down
2 changes: 1 addition & 1 deletion tests/sentry/backup/test_imports.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@
from sentry.models.savedsearch import SavedSearch, Visibility
from sentry.models.team import Team
from sentry.models.userip import UserIP
from sentry.models.userpermission import UserPermission
from sentry.monitors.models import Monitor
from sentry.receivers import create_default_projects
from sentry.silo.base import SiloMode
Expand All @@ -80,6 +79,7 @@
from sentry.users.models.lostpasswordhash import LostPasswordHash
from sentry.users.models.user import User
from sentry.users.models.useremail import UserEmail
from sentry.users.models.userpermission import UserPermission
from sentry.users.models.userrole import UserRole, UserRoleUser
from tests.sentry.backup import (
expect_models,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from sentry.models.userpermission import UserPermission
from sentry.testutils.cases import TestCase
from sentry.testutils.silo import control_silo_test
from sentry.users.models.userpermission import UserPermission


@control_silo_test
Expand Down
Loading