diff --git a/src/sentry/api/bases/user.py b/src/sentry/api/bases/user.py index e08d4c4d12e4b9..63a545a7619fab 100644 --- a/src/sentry/api/bases/user.py +++ b/src/sentry/api/bases/user.py @@ -1,152 +1,3 @@ -from __future__ import annotations +from sentry.users.api.bases.user import RegionSiloUserEndpoint, UserAndStaffPermission, UserEndpoint -from django.contrib.auth.models import AnonymousUser -from rest_framework.permissions import BasePermission -from rest_framework.request import Request -from typing_extensions import override - -from sentry.api.base import Endpoint -from sentry.api.exceptions import ResourceDoesNotExist -from sentry.api.permissions import SentryPermission, StaffPermissionMixin -from sentry.auth.services.access.service import access_service -from sentry.auth.superuser import is_active_superuser, superuser_has_permission -from sentry.auth.system import is_system_auth -from sentry.models.organization import OrganizationStatus -from sentry.models.organizationmapping import OrganizationMapping -from sentry.models.organizationmembermapping import OrganizationMemberMapping -from sentry.organizations.services.organization import organization_service -from sentry.users.models.user import User -from sentry.users.services.user import RpcUser -from sentry.users.services.user.service import user_service - - -class UserPermission(SentryPermission): - def has_object_permission(self, request: Request, view, user: User | RpcUser | None = None): - if user is None or request.user.id == user.id: - return True - if is_system_auth(request.auth): - return True - if request.auth: - return False - - if is_active_superuser(request): - # collect admin level permissions (only used when a user is active superuser) - permissions = access_service.get_permissions_for_user(request.user.id) - - if superuser_has_permission(request, permissions): - return True - - return False - - -class UserAndStaffPermission(StaffPermissionMixin, UserPermission): - """ - Allows staff to access any endpoints this permission is used on. Note that - UserPermission already includes a check for Superuser - """ - - -class OrganizationUserPermission(UserAndStaffPermission): - scope_map = {"DELETE": ["member:admin"]} - - def has_org_permission(self, request: Request, user): - """ - Org can act on a user account, if the user is a member of only one org - e.g. reset org member's 2FA - """ - - organization_id = self._get_single_organization_id(user) - if organization_id is None: - return False - organization = organization_service.get_organization_by_id( - id=organization_id, user_id=request.user.id - ) - if not organization: - return False - - self.determine_access(request, organization) - assert request.method is not None - allowed_scopes = set(self.scope_map.get(request.method, [])) - return any(request.access.has_scope(s) for s in allowed_scopes) - - @staticmethod - def _get_single_organization_id(user) -> int | None: - """If the user is a member of only one active org, return its ID.""" - - # Multiple OrganizationMemberMappings are okay if only one - # of them points to an *active* organization - membership_ids = OrganizationMemberMapping.objects.filter(user_id=user.id).values_list( - "organization_id", flat=True - ) - - try: - org_mapping = OrganizationMapping.objects.get( - status=OrganizationStatus.ACTIVE, organization_id__in=membership_ids - ) - except (OrganizationMapping.DoesNotExist, OrganizationMapping.MultipleObjectsReturned): - return None - return org_mapping.organization_id - - def has_object_permission(self, request: Request, view, user=None): - if super().has_object_permission(request, view, user): - return True - return self.has_org_permission(request, user) - - -class UserEndpoint(Endpoint): - """ - The base endpoint for APIs that deal with Users. Inherit from this class to - get permission checks and to automatically convert user ID "me" to the - currently logged in user's ID. - """ - - permission_classes: tuple[type[BasePermission], ...] = (UserPermission,) - - @override - def convert_args(self, request: Request, user_id: int | str | None = None, *args, **kwargs): - if user_id == "me": - if not request.user.is_authenticated: - raise ResourceDoesNotExist - user_id = request.user.id - - if user_id is None: - raise ResourceDoesNotExist - - try: - user = User.objects.get(id=user_id) - except (User.DoesNotExist, ValueError): - raise ResourceDoesNotExist - - self.check_object_permissions(request, user) - - kwargs["user"] = user - return args, kwargs - - -class RegionSiloUserEndpoint(Endpoint): - """ - The base endpoint for APIs that deal with Users but live in the region silo. - Inherit from this class to get permission checks and to automatically - convert user ID "me" to the currently logged in user's ID. - """ - - permission_classes = (UserPermission,) - - @override - def convert_args(self, request: Request, user_id: str | None = None, *args, **kwargs): - user: RpcUser | User | None = None - - if user_id == "me": - if isinstance(request.user, AnonymousUser) or not request.user.is_authenticated: - raise ResourceDoesNotExist - user = request.user - elif user_id is not None: - user = user_service.get_user(user_id=int(user_id)) - - if not user: - raise ResourceDoesNotExist - - self.check_object_permissions(request, user) - - kwargs["user"] = user - return args, kwargs +__all__ = ("UserEndpoint", "RegionSiloUserEndpoint", "UserAndStaffPermission") diff --git a/src/sentry/api/endpoints/user_notification_details.py b/src/sentry/api/endpoints/user_notification_details.py index 95afd419922d57..6ddeb967850e0e 100644 --- a/src/sentry/api/endpoints/user_notification_details.py +++ b/src/sentry/api/endpoints/user_notification_details.py @@ -7,9 +7,9 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint -from sentry.api.bases.user import UserEndpoint from sentry.api.serializers import Serializer, serialize from sentry.notifications.types import UserOptionsSettingsKey +from sentry.users.api.bases.user import UserEndpoint from sentry.users.models.user_option import UserOption USER_OPTION_SETTINGS = { diff --git a/src/sentry/api/endpoints/user_notification_email.py b/src/sentry/api/endpoints/user_notification_email.py index 8d72e8817bc26b..b5f7302b7d22dd 100644 --- a/src/sentry/api/endpoints/user_notification_email.py +++ b/src/sentry/api/endpoints/user_notification_email.py @@ -6,7 +6,7 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint -from sentry.api.bases.user import UserEndpoint +from sentry.users.api.bases.user import UserEndpoint from sentry.users.models.user_option import UserOption from sentry.users.models.useremail import UserEmail diff --git a/src/sentry/api/endpoints/user_notification_settings_options.py b/src/sentry/api/endpoints/user_notification_settings_options.py index c646a3d61232ac..20b0ce6089b128 100644 --- a/src/sentry/api/endpoints/user_notification_settings_options.py +++ b/src/sentry/api/endpoints/user_notification_settings_options.py @@ -5,13 +5,13 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint -from sentry.api.bases.user import UserEndpoint from sentry.api.exceptions import ParameterValidationError from sentry.api.serializers import serialize from sentry.api.validators.notifications import validate_type from sentry.models.notificationsettingoption import NotificationSettingOption from sentry.notifications.serializers import NotificationSettingsOptionSerializer from sentry.notifications.validators import UserNotificationSettingOptionWithValueSerializer +from sentry.users.api.bases.user import UserEndpoint from sentry.users.models.user import User diff --git a/src/sentry/api/endpoints/user_notification_settings_options_detail.py b/src/sentry/api/endpoints/user_notification_settings_options_detail.py index d74476ff438b5a..d39efa93cf8fe2 100644 --- a/src/sentry/api/endpoints/user_notification_settings_options_detail.py +++ b/src/sentry/api/endpoints/user_notification_settings_options_detail.py @@ -6,8 +6,8 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint -from sentry.api.bases.user import UserEndpoint from sentry.models.notificationsettingoption import NotificationSettingOption +from sentry.users.api.bases.user import UserEndpoint from sentry.users.models.user import User diff --git a/src/sentry/api/endpoints/user_notification_settings_providers.py b/src/sentry/api/endpoints/user_notification_settings_providers.py index f3455e2aa1cb06..945bf75477f4d6 100644 --- a/src/sentry/api/endpoints/user_notification_settings_providers.py +++ b/src/sentry/api/endpoints/user_notification_settings_providers.py @@ -6,7 +6,6 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint -from sentry.api.bases.user import UserEndpoint from sentry.api.exceptions import ParameterValidationError from sentry.api.serializers import serialize from sentry.api.validators.notifications import validate_type @@ -15,6 +14,7 @@ from sentry.notifications.serializers import NotificationSettingsProviderSerializer from sentry.notifications.types import NotificationSettingsOptionEnum from sentry.notifications.validators import UserNotificationSettingsProvidersDetailsSerializer +from sentry.users.api.bases.user import UserEndpoint from sentry.users.models.user import User diff --git a/src/sentry/api/endpoints/user_organizationintegrations.py b/src/sentry/api/endpoints/user_organizationintegrations.py index 1668e01f92b60b..fb772593004615 100644 --- a/src/sentry/api/endpoints/user_organizationintegrations.py +++ b/src/sentry/api/endpoints/user_organizationintegrations.py @@ -4,11 +4,11 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint -from sentry.api.bases.user import UserEndpoint from sentry.api.paginator import OffsetPaginator from sentry.api.serializers import serialize from sentry.constants import ObjectStatus from sentry.integrations.models.organization_integration import OrganizationIntegration +from sentry.users.api.bases.user import UserEndpoint from sentry.users.services.user.service import user_service diff --git a/src/sentry/api/endpoints/user_organizations.py b/src/sentry/api/endpoints/user_organizations.py index 89b70031be241a..7df96295f03ff9 100644 --- a/src/sentry/api/endpoints/user_organizations.py +++ b/src/sentry/api/endpoints/user_organizations.py @@ -6,10 +6,10 @@ from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import region_silo_endpoint -from sentry.api.bases.user import RegionSiloUserEndpoint from sentry.api.paginator import OffsetPaginator from sentry.api.serializers import serialize from sentry.models.organization import Organization +from sentry.users.api.bases.user import RegionSiloUserEndpoint from sentry.users.services.user import RpcUser diff --git a/src/sentry/api/endpoints/user_subscriptions.py b/src/sentry/api/endpoints/user_subscriptions.py index 5b501ebeae1799..6e983f96afe54d 100644 --- a/src/sentry/api/endpoints/user_subscriptions.py +++ b/src/sentry/api/endpoints/user_subscriptions.py @@ -6,7 +6,7 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint -from sentry.api.bases.user import UserEndpoint +from sentry.users.api.bases.user import UserEndpoint from sentry.users.models.user import User from sentry.users.models.useremail import UserEmail diff --git a/src/sentry/api/permissions.py b/src/sentry/api/permissions.py index b5f99e47716c43..2b6d5adf1ed552 100644 --- a/src/sentry/api/permissions.py +++ b/src/sentry/api/permissions.py @@ -168,7 +168,7 @@ def has_permission(self, request: Request, view: object) -> bool: current_scopes = request.auth.get_scopes() return any(s in allowed_scopes for s in current_scopes) - def has_object_permission(self, request: Request, view: object, obj: Any) -> bool: + def has_object_permission(self, request: Request, view: object | None, obj: Any) -> bool: return False diff --git a/src/sentry/users/api/bases/user.py b/src/sentry/users/api/bases/user.py new file mode 100644 index 00000000000000..5825b4035d51cb --- /dev/null +++ b/src/sentry/users/api/bases/user.py @@ -0,0 +1,162 @@ +from __future__ import annotations + +from typing import Any + +from django.contrib.auth.models import AnonymousUser +from rest_framework.permissions import BasePermission +from rest_framework.request import Request +from typing_extensions import override + +from sentry.api.base import Endpoint +from sentry.api.exceptions import ResourceDoesNotExist +from sentry.api.permissions import SentryPermission, StaffPermissionMixin +from sentry.auth.services.access.service import access_service +from sentry.auth.superuser import is_active_superuser, superuser_has_permission +from sentry.auth.system import is_system_auth +from sentry.models.organization import OrganizationStatus +from sentry.models.organizationmapping import OrganizationMapping +from sentry.models.organizationmembermapping import OrganizationMemberMapping +from sentry.organizations.services.organization import organization_service +from sentry.users.models.user import User +from sentry.users.services.user import RpcUser +from sentry.users.services.user.service import user_service + + +class UserPermission(SentryPermission): + def has_object_permission( + self, request: Request, view: object | None, user: User | RpcUser | None = None + ) -> bool: + if user is None or request.user.id == user.id: + return True + if is_system_auth(request.auth): + return True + if request.auth: + return False + + if is_active_superuser(request): + # collect admin level permissions (only used when a user is active superuser) + permissions = access_service.get_permissions_for_user(request.user.id) + + if superuser_has_permission(request, permissions): + return True + + return False + + +class UserAndStaffPermission(StaffPermissionMixin, UserPermission): + """ + Allows staff to access any endpoints this permission is used on. Note that + UserPermission already includes a check for Superuser + """ + + +class OrganizationUserPermission(UserAndStaffPermission): + scope_map = {"DELETE": ["member:admin"]} + + def has_org_permission(self, request: Request, user: User | RpcUser | None) -> bool: + """ + Org can act on a user account, if the user is a member of only one org + e.g. reset org member's 2FA + """ + assert user, "User must be provided to get organization permissions" + organization_id = self._get_single_organization_id(user) + if organization_id is None: + return False + organization = organization_service.get_organization_by_id( + id=organization_id, user_id=request.user.id + ) + if not organization: + return False + + self.determine_access(request, organization) + assert request.method is not None + allowed_scopes = set(self.scope_map.get(request.method, [])) + return any(request.access.has_scope(s) for s in allowed_scopes) + + @staticmethod + def _get_single_organization_id(user: User | RpcUser) -> int | None: + """If the user is a member of only one active org, return its ID.""" + + # Multiple OrganizationMemberMappings are okay if only one + # of them points to an *active* organization + membership_ids = OrganizationMemberMapping.objects.filter(user_id=user.id).values_list( + "organization_id", flat=True + ) + + try: + org_mapping = OrganizationMapping.objects.get( + status=OrganizationStatus.ACTIVE, organization_id__in=membership_ids + ) + except (OrganizationMapping.DoesNotExist, OrganizationMapping.MultipleObjectsReturned): + return None + return org_mapping.organization_id + + def has_object_permission( + self, request: Request, view: object | None, user: User | RpcUser | None = None + ) -> bool: + if super().has_object_permission(request, view, user): + return True + return self.has_org_permission(request, user) + + +class UserEndpoint(Endpoint): + """ + The base endpoint for APIs that deal with Users. Inherit from this class to + get permission checks and to automatically convert user ID "me" to the + currently logged in user's ID. + """ + + permission_classes: tuple[type[BasePermission], ...] = (UserPermission,) + + @override + def convert_args( + self, request: Request, user_id: int | str | None = None, *args: Any, **kwargs: Any + ) -> Any: + if user_id == "me": + if not request.user.is_authenticated: + raise ResourceDoesNotExist + user_id = request.user.id + + if user_id is None: + raise ResourceDoesNotExist + + try: + user = User.objects.get(id=user_id) + except (User.DoesNotExist, ValueError): + raise ResourceDoesNotExist + + self.check_object_permissions(request, user) + + kwargs["user"] = user + return args, kwargs + + +class RegionSiloUserEndpoint(Endpoint): + """ + The base endpoint for APIs that deal with Users but live in the region silo. + Inherit from this class to get permission checks and to automatically + convert user ID "me" to the currently logged in user's ID. + """ + + permission_classes = (UserPermission,) + + @override + def convert_args( + self, request: Request, user_id: str | None = None, *args: Any, **kwargs: Any + ) -> Any: + user: RpcUser | User | None = None + + if user_id == "me": + if isinstance(request.user, AnonymousUser) or not request.user.is_authenticated: + raise ResourceDoesNotExist + user = request.user + elif user_id is not None: + user = user_service.get_user(user_id=int(user_id)) + + if not user: + raise ResourceDoesNotExist + + self.check_object_permissions(request, user) + + kwargs["user"] = user + return args, kwargs diff --git a/src/sentry/users/api/endpoints/user_authenticator_details.py b/src/sentry/users/api/endpoints/user_authenticator_details.py index 47aeae516cf083..f0dd463623587a 100644 --- a/src/sentry/users/api/endpoints/user_authenticator_details.py +++ b/src/sentry/users/api/endpoints/user_authenticator_details.py @@ -9,7 +9,6 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint -from sentry.api.bases.user import OrganizationUserPermission, UserEndpoint from sentry.api.decorators import sudo_required from sentry.api.serializers import serialize from sentry.auth.authenticators.recovery_code import RecoveryCodeInterface @@ -17,6 +16,7 @@ from sentry.auth.staff import has_staff_option, is_active_staff from sentry.auth.superuser import is_active_superuser from sentry.security.utils import capture_security_activity +from sentry.users.api.bases.user import OrganizationUserPermission, UserEndpoint from sentry.users.api.serializers.authenticator import get_interface_serializer from sentry.users.models.authenticator import Authenticator from sentry.users.models.user import User diff --git a/src/sentry/users/api/endpoints/user_authenticator_enroll.py b/src/sentry/users/api/endpoints/user_authenticator_enroll.py index f7e966056bab19..e1fdfea5d73fd9 100644 --- a/src/sentry/users/api/endpoints/user_authenticator_enroll.py +++ b/src/sentry/users/api/endpoints/user_authenticator_enroll.py @@ -13,7 +13,6 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint -from sentry.api.bases.user import UserEndpoint from sentry.api.decorators import email_verification_required, sudo_required from sentry.api.invite_helper import ApiInviteHelper, remove_invite_details_from_session from sentry.api.serializers import serialize @@ -23,6 +22,7 @@ from sentry.auth.authenticators.u2f import U2fInterface from sentry.organizations.services.organization import organization_service from sentry.security.utils import capture_security_activity +from sentry.users.api.bases.user import UserEndpoint from sentry.users.api.serializers.authenticator import get_interface_serializer from sentry.users.models.authenticator import Authenticator from sentry.users.models.user import User diff --git a/src/sentry/users/api/endpoints/user_authenticator_index.py b/src/sentry/users/api/endpoints/user_authenticator_index.py index ccca2f938d5a6c..0e254f38b138ed 100644 --- a/src/sentry/users/api/endpoints/user_authenticator_index.py +++ b/src/sentry/users/api/endpoints/user_authenticator_index.py @@ -4,8 +4,8 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint -from sentry.api.bases.user import UserEndpoint from sentry.api.serializers import serialize +from sentry.users.api.bases.user import UserEndpoint from sentry.users.api.serializers.authenticator import get_interface_serializer from sentry.users.models.authenticator import Authenticator from sentry.users.models.user import User diff --git a/src/sentry/users/api/endpoints/user_avatar.py b/src/sentry/users/api/endpoints/user_avatar.py index 003c3426bfabeb..3d3336cf9e0cff 100644 --- a/src/sentry/users/api/endpoints/user_avatar.py +++ b/src/sentry/users/api/endpoints/user_avatar.py @@ -8,7 +8,7 @@ from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint from sentry.api.bases.avatar import AvatarMixin -from sentry.api.bases.user import UserEndpoint +from sentry.users.api.bases.user import UserEndpoint from sentry.users.models.user import User from sentry.users.models.user_avatar import UserAvatar from sentry.users.services.user.serial import serialize_rpc_user diff --git a/src/sentry/users/api/endpoints/user_details.py b/src/sentry/users/api/endpoints/user_details.py index 85429d50caf77b..67517f23e49860 100644 --- a/src/sentry/users/api/endpoints/user_details.py +++ b/src/sentry/users/api/endpoints/user_details.py @@ -12,7 +12,6 @@ from sentry import roles from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint -from sentry.api.bases.user import UserAndStaffPermission, UserEndpoint from sentry.api.decorators import sudo_required from sentry.api.endpoints.organization_details import post_org_pending_deletion from sentry.api.serializers import serialize @@ -24,6 +23,7 @@ from sentry.models.organizationmembermapping import OrganizationMemberMapping from sentry.organizations.services.organization import organization_service from sentry.organizations.services.organization.model import RpcOrganizationDeleteState +from sentry.users.api.bases.user import UserAndStaffPermission, UserEndpoint from sentry.users.api.serializers.user import DetailedSelfUserSerializer from sentry.users.models.user import User from sentry.users.models.user_option import UserOption diff --git a/src/sentry/users/api/endpoints/user_emails.py b/src/sentry/users/api/endpoints/user_emails.py index 0ddb15d49a8ba1..5f6008f4434c4d 100644 --- a/src/sentry/users/api/endpoints/user_emails.py +++ b/src/sentry/users/api/endpoints/user_emails.py @@ -8,10 +8,10 @@ from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint -from sentry.api.bases.user import UserEndpoint from sentry.api.decorators import sudo_required from sentry.api.serializers import serialize from sentry.api.validators import AllowedEmailField +from sentry.users.api.bases.user import UserEndpoint from sentry.users.api.serializers.useremail import UserEmailSerializer from sentry.users.models.user import User from sentry.users.models.user_option import UserOption diff --git a/src/sentry/users/api/endpoints/user_emails_confirm.py b/src/sentry/users/api/endpoints/user_emails_confirm.py index 2655ffae9d2590..8817e0b8a49f90 100644 --- a/src/sentry/users/api/endpoints/user_emails_confirm.py +++ b/src/sentry/users/api/endpoints/user_emails_confirm.py @@ -6,9 +6,9 @@ from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint -from sentry.api.bases.user import UserEndpoint from sentry.api.validators import AllowedEmailField from sentry.types.ratelimit import RateLimit, RateLimitCategory +from sentry.users.api.bases.user import UserEndpoint from sentry.users.models.user import User from sentry.users.models.useremail import UserEmail diff --git a/src/sentry/users/api/endpoints/user_identity.py b/src/sentry/users/api/endpoints/user_identity.py index c76efeb360cfdc..c70cc003389523 100644 --- a/src/sentry/users/api/endpoints/user_identity.py +++ b/src/sentry/users/api/endpoints/user_identity.py @@ -3,9 +3,9 @@ from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint -from sentry.api.bases.user import UserEndpoint from sentry.api.paginator import OffsetPaginator from sentry.api.serializers import serialize +from sentry.users.api.bases.user import UserEndpoint from sentry.users.api.serializers.identity import IdentitySerializer from sentry.users.models.identity import Identity from sentry.users.models.user import User diff --git a/src/sentry/users/api/endpoints/user_identity_config.py b/src/sentry/users/api/endpoints/user_identity_config.py index 403b7b8006cc7a..468de568d7c95a 100644 --- a/src/sentry/users/api/endpoints/user_identity_config.py +++ b/src/sentry/users/api/endpoints/user_identity_config.py @@ -8,9 +8,9 @@ from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint -from sentry.api.bases.user import UserAndStaffPermission, UserEndpoint from sentry.api.serializers import serialize from sentry.models.authidentity import AuthIdentity +from sentry.users.api.bases.user import UserAndStaffPermission, UserEndpoint from sentry.users.api.serializers.user_identity_config import ( Status, UserIdentityConfig, diff --git a/src/sentry/users/api/endpoints/user_identity_details.py b/src/sentry/users/api/endpoints/user_identity_details.py index 3261cc93b61d9b..ff1aa4917b3922 100644 --- a/src/sentry/users/api/endpoints/user_identity_details.py +++ b/src/sentry/users/api/endpoints/user_identity_details.py @@ -3,8 +3,8 @@ from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint -from sentry.api.bases.user import UserEndpoint from sentry.models.authidentity import AuthIdentity +from sentry.users.api.bases.user import UserEndpoint from sentry.users.models.user import User diff --git a/src/sentry/users/api/endpoints/user_ips.py b/src/sentry/users/api/endpoints/user_ips.py index bfbd37b8782935..d8650ea2d141cb 100644 --- a/src/sentry/users/api/endpoints/user_ips.py +++ b/src/sentry/users/api/endpoints/user_ips.py @@ -3,10 +3,10 @@ from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint -from sentry.api.bases.user import UserEndpoint from sentry.api.decorators import sudo_required from sentry.api.paginator import DateTimePaginator from sentry.api.serializers import serialize +from sentry.users.api.bases.user import UserEndpoint from sentry.users.api.serializers.userip import UserIPSerializer from sentry.users.models.user import User from sentry.users.models.userip import UserIP diff --git a/src/sentry/users/api/endpoints/user_password.py b/src/sentry/users/api/endpoints/user_password.py index 04681b6c13bb6c..a3a67682131c36 100644 --- a/src/sentry/users/api/endpoints/user_password.py +++ b/src/sentry/users/api/endpoints/user_password.py @@ -8,10 +8,10 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint -from sentry.api.bases.user import UserEndpoint from sentry.auth import password_validation from sentry.security.utils import capture_security_activity from sentry.types.ratelimit import RateLimit, RateLimitCategory +from sentry.users.api.bases.user import UserEndpoint from sentry.users.models.user import User diff --git a/src/sentry/users/api/endpoints/user_permission_details.py b/src/sentry/users/api/endpoints/user_permission_details.py index 7203373fc8b96b..beb55d042d718c 100644 --- a/src/sentry/users/api/endpoints/user_permission_details.py +++ b/src/sentry/users/api/endpoints/user_permission_details.py @@ -9,9 +9,9 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint -from sentry.api.bases.user import UserEndpoint from sentry.api.decorators import sudo_required from sentry.api.permissions import SuperuserOrStaffFeatureFlaggedPermission +from sentry.users.api.bases.user import UserEndpoint from sentry.users.models.user import User from sentry.users.models.userpermission import UserPermission diff --git a/src/sentry/users/api/endpoints/user_permissions.py b/src/sentry/users/api/endpoints/user_permissions.py index b21ab10ed7a300..7ed1142daf0c3b 100644 --- a/src/sentry/users/api/endpoints/user_permissions.py +++ b/src/sentry/users/api/endpoints/user_permissions.py @@ -4,8 +4,8 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint -from sentry.api.bases.user import UserEndpoint from sentry.api.permissions import SuperuserOrStaffFeatureFlaggedPermission +from sentry.users.api.bases.user import UserEndpoint from sentry.users.models.user import User from sentry.users.models.userpermission import UserPermission diff --git a/src/sentry/users/api/endpoints/user_permissions_config.py b/src/sentry/users/api/endpoints/user_permissions_config.py index dc754b26f62340..e15190352671ee 100644 --- a/src/sentry/users/api/endpoints/user_permissions_config.py +++ b/src/sentry/users/api/endpoints/user_permissions_config.py @@ -5,8 +5,8 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint -from sentry.api.bases.user import UserEndpoint from sentry.api.permissions import SuperuserOrStaffFeatureFlaggedPermission +from sentry.users.api.bases.user import UserEndpoint from sentry.users.models.user import User diff --git a/src/sentry/users/api/endpoints/user_regions.py b/src/sentry/users/api/endpoints/user_regions.py index e0669aeabd94dc..54a4fe6c200493 100644 --- a/src/sentry/users/api/endpoints/user_regions.py +++ b/src/sentry/users/api/endpoints/user_regions.py @@ -8,12 +8,12 @@ from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint -from sentry.api.bases.user import UserEndpoint, UserPermission from sentry.auth.superuser import is_active_superuser from sentry.auth.system import is_system_auth from sentry.models.organizationmapping import OrganizationMapping from sentry.models.organizationmembermapping import OrganizationMemberMapping from sentry.types.region import get_region_by_name +from sentry.users.api.bases.user import UserEndpoint, UserPermission from sentry.users.models.user import User from sentry.users.services.user import RpcUser diff --git a/src/sentry/users/api/endpoints/user_role_details.py b/src/sentry/users/api/endpoints/user_role_details.py index 29c2a5e65a4f8f..6cf42fe9943adc 100644 --- a/src/sentry/users/api/endpoints/user_role_details.py +++ b/src/sentry/users/api/endpoints/user_role_details.py @@ -7,10 +7,10 @@ from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint -from sentry.api.bases.user import UserEndpoint from sentry.api.decorators import sudo_required from sentry.api.permissions import SuperuserPermission from sentry.api.serializers import serialize +from sentry.users.api.bases.user import UserEndpoint from sentry.users.models.user import User from sentry.users.models.userrole import UserRole, UserRoleUser diff --git a/src/sentry/users/api/endpoints/user_roles.py b/src/sentry/users/api/endpoints/user_roles.py index 8e26d92b9a9a98..c734a2f8b53a79 100644 --- a/src/sentry/users/api/endpoints/user_roles.py +++ b/src/sentry/users/api/endpoints/user_roles.py @@ -3,9 +3,9 @@ from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint -from sentry.api.bases.user import UserEndpoint from sentry.api.permissions import SuperuserPermission from sentry.api.serializers import serialize +from sentry.users.api.bases.user import UserEndpoint from sentry.users.api.serializers.userrole import UserRoleSerializer from sentry.users.models.user import User from sentry.users.models.userrole import UserRole diff --git a/tests/sentry/users/api/bases/__init__.py b/tests/sentry/users/api/bases/__init__.py new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/tests/sentry/api/bases/test_user.py b/tests/sentry/users/api/bases/test_user.py similarity index 99% rename from tests/sentry/api/bases/test_user.py rename to tests/sentry/users/api/bases/test_user.py index 1d67c768b75431..694bfd5a917357 100644 --- a/tests/sentry/api/bases/test_user.py +++ b/tests/sentry/users/api/bases/test_user.py @@ -3,16 +3,16 @@ import pytest from django.test import override_settings -from sentry.api.bases.user import ( +from sentry.api.exceptions import ResourceDoesNotExist +from sentry.testutils.cases import DRFPermissionTestCase +from sentry.testutils.helpers.options import override_options +from sentry.testutils.silo import all_silo_test, control_silo_test, no_silo_test +from sentry.users.api.bases.user import ( RegionSiloUserEndpoint, UserAndStaffPermission, UserEndpoint, UserPermission, ) -from sentry.api.exceptions import ResourceDoesNotExist -from sentry.testutils.cases import DRFPermissionTestCase -from sentry.testutils.helpers.options import override_options -from sentry.testutils.silo import all_silo_test, control_silo_test, no_silo_test @all_silo_test