Skip to content
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
11 changes: 7 additions & 4 deletions src/sentry/auth/access.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from sentry import features, roles
from sentry.api.exceptions import DataSecrecyError
from sentry.auth.deprecated_scope_translation import translate_deprecated_scopes
from sentry.auth.principal import acting_user_id, has_user
from sentry.auth.principal import acting_user_id, has_user, require_user
from sentry.auth.scope_declaration import check_scope_declaration, check_scope_declarations
from sentry.auth.services.access.service import access_service
from sentry.auth.services.auth import AuthenticatedToken, RpcAuthState, RpcMemberSsoState
Expand Down Expand Up @@ -51,6 +51,7 @@
from sentry.sentry_apps.models.sentry_app import SentryApp
from sentry.service_accounts.auth import is_service_account_auth
from sentry.service_accounts.models import ServiceAccountProjectReplica, ServiceAccountReplica
from sentry.service_accounts.principal import ServiceAccountPrincipal
from sentry.silo.base import SiloMode
from sentry.types.request import _HttpRequestWithUser
from sentry.users.models.user import User
Expand Down Expand Up @@ -1233,7 +1234,9 @@ def organizationless_access(
)


def normalize_valid_user(user: User | RpcUser | AnonymousUser | None) -> User | RpcUser | None:
def normalize_valid_user(
user: User | RpcUser | AnonymousUser | ServiceAccountPrincipal | None,
) -> User | RpcUser | None:
if not user or not has_user(user) or not user.is_active:
return None
return user
Expand Down Expand Up @@ -1289,7 +1292,7 @@ def from_request(
raise DataSecrecyError()

if request.user.is_authenticated and request.user.is_sentry_app:
return _from_sentry_app(request.user, organization=organization)
return _from_sentry_app(require_user(request), organization=organization)

if is_active_superuser(request):
member: OrganizationMember | None = None
Expand Down Expand Up @@ -1375,7 +1378,7 @@ def _from_rpc_sentry_app(context: RpcUserOrganizationContext | None = None) -> A


def from_user(
user: User | RpcUser | AnonymousUser | None,
user: User | RpcUser | AnonymousUser | ServiceAccountPrincipal | None,
organization: Organization | None = None,
scopes: Iterable[str] | None = None,
is_superuser: bool = False,
Expand Down
6 changes: 5 additions & 1 deletion src/sentry/auth/providers/saml2/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from sentry import features, options
from sentry.auth.exceptions import IdentityNotValid
from sentry.auth.helper import AuthHelper
from sentry.auth.principal import acting_user_id
from sentry.auth.provider import Provider
from sentry.auth.store import FLOW_LOGIN
from sentry.auth.view import AuthView
Expand Down Expand Up @@ -482,7 +483,10 @@ def handle_saml_single_logout(request: Request) -> OneLogin_Saml2_Auth:
"""
# Do not handle SLO if a user is in more than 1 organization
# Propagating it to multiple IdPs results in confusion for the user
organizations = user_service.get_organizations(user_id=request.user.id)
user_id = acting_user_id(request.user)
if user_id is None:
return
organizations = user_service.get_organizations(user_id=user_id)
if not len(organizations) == 1:
return

Expand Down
11 changes: 4 additions & 7 deletions src/sentry/auth_v2/endpoints/auth_merge_user_accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from sentry.api.paginator import OffsetPaginator
from sentry.api.serializers import serialize
from sentry.api.serializers.rest_framework.base import CamelSnakeSerializer
from sentry.auth.principal import require_user
from sentry.auth_v2.endpoints.base import AuthV2Endpoint
from sentry.users.api.serializers.user import UserSerializerWithOrgMemberships
from sentry.users.models.user import User
Expand All @@ -35,10 +36,7 @@ class AuthMergeUserAccountsEndpoint(AuthV2Endpoint):
"""

def get(self, request: Request) -> Response:
user = request.user
if not user.is_authenticated:
return Response(status=401)

user = require_user(request)
shared_email = user.email
if not shared_email:
return Response(
Expand All @@ -54,15 +52,14 @@ def get(self, request: Request) -> Response:
)

def post(self, request: Request) -> Response:
if not request.user.is_authenticated:
return Response(status=401)
user = require_user(request)

validator = AuthMergeUserAccountsValidator(data=request.data)
if not validator.is_valid():
return Response(validator.errors, status=400)
result = validator.validated_data

primary_user = User.objects.get(id=request.user.id)
primary_user = User.objects.get(id=user.id)
verification_code = UserMergeVerificationCode.objects.filter(
user_id=primary_user.id
).first()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from sentry.api.api_publish_status import ApiPublishStatus
from sentry.api.base import Endpoint, control_silo_endpoint
from sentry.api.permissions import DisallowAgentToken, SentryIsAuthenticated
from sentry.auth.principal import require_user
from sentry.users.models.user_merge_verification_code import UserMergeVerificationCode


Expand All @@ -21,11 +22,7 @@ class AuthUserMergeVerificationCodeEndpoint(Endpoint):
"""

def post(self, request: Request) -> Response:
user = request.user
if user.id is None:
return Response(
status=403, data={"error": "Must be authenticated to use this endpoint."}
)
user = require_user(request)
try:
# regenerate the code if it exists
code = UserMergeVerificationCode.objects.get(user_id=user.id)
Expand Down
8 changes: 5 additions & 3 deletions src/sentry/auth_v2/utils/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from rest_framework.request import Request

from sentry.api.serializers import Serializer
from sentry.auth.principal import has_user
from sentry.service_accounts.principal import ServiceAccountPrincipal
from sentry.users.models.user import User
from sentry.users.services.user.model import RpcUser

Expand Down Expand Up @@ -35,7 +37,7 @@ def serialize(
self,
obj: Request,
attrs: Mapping[Any, Any],
user: User | RpcUser | AnonymousUser,
user: User | RpcUser | AnonymousUser | ServiceAccountPrincipal,
**kwargs: Any,
) -> SessionSerializerResponse:
"""
Expand Down Expand Up @@ -76,9 +78,9 @@ def initialize_auth_flags(self) -> None:
the user needs to complete on the frontend. Flags are only
set if they don't already exist in the session.
"""
user = self.request.user
if not user or user.is_anonymous:
if not has_user(self.request.user):
return
user = self.request.user

session = self.request.session

Expand Down
3 changes: 2 additions & 1 deletion src/sentry/relocation/api/endpoints/abort.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from sentry.api.exceptions import ResourceDoesNotExist
from sentry.api.permissions import SuperuserOrStaffFeatureFlaggedPermission
from sentry.api.serializers import serialize
from sentry.auth.principal import acting_user_id
from sentry.relocation.models.relocation import Relocation

ERR_NOT_ABORTABLE_STATUS = (
Expand Down Expand Up @@ -43,7 +44,7 @@ def put(self, request: Request, relocation_uuid: str) -> Response:
:auth: required
"""

logger.info("relocations.abort.put.start", extra={"caller": request.user.id})
logger.info("relocations.abort.put.start", extra={"caller": acting_user_id(request.user)})

try:
relocation: Relocation = Relocation.objects.get(uuid=relocation_uuid)
Expand Down
5 changes: 4 additions & 1 deletion src/sentry/relocation/api/endpoints/artifacts/details.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from sentry.api.exceptions import ResourceDoesNotExist, StaffRequired, SuperuserRequired
from sentry.api.permissions import SuperuserOrStaffFeatureFlaggedPermission
from sentry.auth.elevated_mode import has_elevated_mode
from sentry.auth.principal import acting_user_id
from sentry.auth.staff import has_staff_option
from sentry.backup.crypto import (
CryptoKeyVersion,
Expand Down Expand Up @@ -59,7 +60,9 @@ def get(
:auth: required
"""

logger.info("relocations.artifact.details.get.start", extra={"caller": request.user.id})
logger.info(
"relocations.artifact.details.get.start", extra={"caller": acting_user_id(request.user)}
)

# TODO(schew2381): Remove the superuser reference below after feature flag is removed.
# Must be superuser/staff AND have a `UserPermission` of `relocation.admin` to see access!
Expand Down
5 changes: 4 additions & 1 deletion src/sentry/relocation/api/endpoints/artifacts/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from sentry.api.exceptions import ResourceDoesNotExist, StaffRequired, SuperuserRequired
from sentry.api.permissions import SuperuserOrStaffFeatureFlaggedPermission
from sentry.auth.elevated_mode import has_elevated_mode
from sentry.auth.principal import acting_user_id
from sentry.auth.staff import has_staff_option
from sentry.models.files.utils import get_relocation_storage
from sentry.relocation.models.relocation import Relocation
Expand Down Expand Up @@ -40,7 +41,9 @@ def get(self, request: Request, relocation_uuid: str) -> Response:
:auth: required
"""

logger.info("relocations.artifact.index.get.start", extra={"caller": request.user.id})
logger.info(
"relocations.artifact.index.get.start", extra={"caller": acting_user_id(request.user)}
)

# TODO(schew2381): Remove the superuser reference below after feature flag is removed.
# Must be superuser/staff AND have a `UserPermission` of `relocation.admin` to see access!
Expand Down
3 changes: 2 additions & 1 deletion src/sentry/relocation/api/endpoints/cancel.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from sentry.api.exceptions import ResourceDoesNotExist
from sentry.api.permissions import SuperuserOrStaffFeatureFlaggedPermission
from sentry.api.serializers import serialize
from sentry.auth.principal import acting_user_id
from sentry.relocation.api.endpoints import ERR_UNKNOWN_RELOCATION_STEP
from sentry.relocation.models.relocation import Relocation

Expand Down Expand Up @@ -57,7 +58,7 @@ def put(self, request: Request, relocation_uuid: str) -> Response:
:auth: required
"""

logger.info("relocations.cancel.put.start", extra={"caller": request.user.id})
logger.info("relocations.cancel.put.start", extra={"caller": acting_user_id(request.user)})

try:
relocation: Relocation = Relocation.objects.get(uuid=relocation_uuid)
Expand Down
3 changes: 2 additions & 1 deletion src/sentry/relocation/api/endpoints/details.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from sentry.api.exceptions import ResourceDoesNotExist
from sentry.api.permissions import SuperuserOrStaffFeatureFlaggedPermission
from sentry.api.serializers import serialize
from sentry.auth.principal import acting_user_id
from sentry.relocation.models.relocation import Relocation

logger = logging.getLogger("sentry.relocation")
Expand All @@ -33,7 +34,7 @@ def get(self, request: Request, relocation_uuid: str) -> Response:
:auth: required
"""

logger.info("relocations.details.get.start", extra={"caller": request.user.id})
logger.info("relocations.details.get.start", extra={"caller": acting_user_id(request.user)})

try:
return self.respond(serialize(Relocation.objects.get(uuid=relocation_uuid)))
Expand Down
14 changes: 8 additions & 6 deletions src/sentry/relocation/api/endpoints/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from sentry.api.permissions import DisallowAgentToken, SentryIsAuthenticated
from sentry.api.serializers import serialize
from sentry.auth.elevated_mode import has_elevated_mode
from sentry.auth.principal import acting_user_id, is_owned_by_acting_user, require_user
from sentry.models.files.file import File
from sentry.relocation.api.endpoints import ERR_FEATURE_DISABLED
from sentry.relocation.api.serializers.relocation import RelocationSerializer
Expand Down Expand Up @@ -105,7 +106,7 @@ def validate_new_relocation_request(
return Response(status=status.HTTP_400_BAD_REQUEST)

# Only superuser/staff can start relocations for other users.
creator = user_service.get_user(user_id=request.user.id)
creator = user_service.get_user(user_id=require_user(request).id)
if creator is None:
raise RuntimeError("Could not ascertain request user's username")
if not elevated_user and creator.username != owner_username:
Expand Down Expand Up @@ -179,7 +180,7 @@ def get(self, request: Request) -> Response:
:auth: required
"""

logger.info("relocations.index.get.start", extra={"caller": request.user.id})
logger.info("relocations.index.get.start", extra={"caller": acting_user_id(request.user)})

if not request.user.is_authenticated:
return Response(status=status.HTTP_400_BAD_REQUEST)
Expand All @@ -189,7 +190,7 @@ def get(self, request: Request) -> Response:
# TODO(schew2381): Remove the superuser reference below after feature flag is removed.
# Non-superuser/staff can only see their own relocations.
if not has_elevated_mode(request):
queryset = queryset.filter(owner_id=request.user.id)
queryset = queryset.filter(is_owned_by_acting_user(request.user, "owner_id"))

query = request.GET.get("query")
if query:
Expand Down Expand Up @@ -243,7 +244,7 @@ def post(self, request: Request) -> Response:
:auth: required
"""

logger.info("relocations.index.post.start", extra={"caller": request.user.id})
logger.info("relocations.index.post.start", extra={"caller": acting_user_id(request.user)})

if not request.user.is_authenticated:
return Response(status=status.HTTP_400_BAD_REQUEST)
Expand Down Expand Up @@ -277,12 +278,13 @@ def post(self, request: Request) -> Response:
file = File.objects.create(name="raw-relocation-data.tar", type=RELOCATION_FILE_TYPE)
file.putfile(fileobj, blob_size=RELOCATION_BLOB_SIZE, logger=logger)

creator = require_user(request)
with atomic_transaction(
using=(router.db_for_write(Relocation), router.db_for_write(RelocationFile))
):
provenance = Relocation.Provenance.SELF_HOSTED
relocation: Relocation = Relocation.objects.create(
creator_id=request.user.id,
creator_id=creator.id,
owner_id=owner.id,
want_org_slugs=org_slugs,
step=Relocation.Step.UPLOADING.value,
Expand All @@ -301,7 +303,7 @@ def post(self, request: Request) -> Response:
try:
analytics.record(
RelocationCreatedEvent(
creator_id=request.user.id,
creator_id=creator.id,
owner_id=owner.id,
uuid=str(relocation.uuid),
)
Expand Down
3 changes: 2 additions & 1 deletion src/sentry/relocation/api/endpoints/pause.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from sentry.api.exceptions import ResourceDoesNotExist
from sentry.api.permissions import SuperuserOrStaffFeatureFlaggedPermission
from sentry.api.serializers import serialize
from sentry.auth.principal import acting_user_id
from sentry.relocation.api.endpoints import (
ERR_COULD_NOT_PAUSE_RELOCATION_AT_STEP,
ERR_UNKNOWN_RELOCATION_STEP,
Expand Down Expand Up @@ -55,7 +56,7 @@ def put(self, request: Request, relocation_uuid: str) -> Response:
:auth: required
"""

logger.info("relocations.pause.put.start", extra={"caller": request.user.id})
logger.info("relocations.pause.put.start", extra={"caller": acting_user_id(request.user)})

try:
relocation: Relocation = Relocation.objects.get(uuid=relocation_uuid)
Expand Down
5 changes: 4 additions & 1 deletion src/sentry/relocation/api/endpoints/public_key.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from sentry.api.base import Endpoint, cell_silo_endpoint
from sentry.api.permissions import SentryIsAuthenticated
from sentry.auth.elevated_mode import has_elevated_mode
from sentry.auth.principal import acting_user_id
from sentry.backup.crypto import GCPKMSEncryptor, get_default_crypto_key_version
from sentry.relocation.api.endpoints import ERR_FEATURE_DISABLED
from sentry.utils.env import log_gcp_credentials_details
Expand All @@ -35,7 +36,9 @@ def get(self, request: Request) -> Response:
:auth: required
"""

logger.info("publickeys.relocations.get.start", extra={"caller": request.user.id})
logger.info(
"publickeys.relocations.get.start", extra={"caller": acting_user_id(request.user)}
)

# We only honor the `relocation.enabled` flag for non-superusers/staff.
elevated_user = has_elevated_mode(request)
Expand Down
3 changes: 2 additions & 1 deletion src/sentry/relocation/api/endpoints/recover.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from sentry.api.exceptions import ResourceDoesNotExist
from sentry.api.permissions import SuperuserOrStaffFeatureFlaggedPermission
from sentry.api.serializers import serialize
from sentry.auth.principal import acting_user_id
from sentry.relocation.api.endpoints import (
ERR_COULD_NOT_PAUSE_RELOCATION_AT_STEP,
ERR_UNKNOWN_RELOCATION_STEP,
Expand Down Expand Up @@ -123,7 +124,7 @@ def put(self, request: Request, relocation_uuid: str) -> Response:
:auth: required
"""

logger.info("relocations.recover.put.start", extra={"caller": request.user.id})
logger.info("relocations.recover.put.start", extra={"caller": acting_user_id(request.user)})

# Use a `select_for_update` transaction to prevent duplicate tasks from being started by
# racing recover calls.
Expand Down
Loading
Loading