Skip to content
Open
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
6 changes: 6 additions & 0 deletions src/sentry/api/bases/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ class ProjectReleasePermission(ProjectPermission):
}


class ProjectDistributionPermission(ProjectPermission):
scope_map = {
"GET": ["project:distribution"],
}


class ProjectEventPermission(ProjectPermission):
scope_map = {
"GET": ["event:read", "event:write", "event:admin"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from sentry.api.api_owners import ApiOwner
from sentry.api.api_publish_status import ApiPublishStatus
from sentry.api.base import region_silo_endpoint
from sentry.api.bases.project import ProjectEndpoint, ProjectReleasePermission
from sentry.api.bases.project import ProjectDistributionPermission, ProjectEndpoint
from sentry.models.project import Project
from sentry.preprod.build_distribution_utils import (
get_download_url_for_artifact,
Expand Down Expand Up @@ -45,7 +45,7 @@ class ProjectPreprodArtifactCheckForUpdatesEndpoint(ProjectEndpoint):
publish_status = {
"GET": ApiPublishStatus.EXPERIMENTAL,
}
permission_classes = (ProjectReleasePermission,)
permission_classes = (ProjectDistributionPermission,)

rate_limits = RateLimitConfig(
limit_overrides={
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
from django.urls import reverse

from sentry.models.orgauthtoken import OrgAuthToken
from sentry.preprod.models import PreprodArtifact, PreprodBuildConfiguration
from sentry.silo.base import SiloMode
from sentry.testutils.cases import APITestCase
from sentry.testutils.silo import assume_test_silo_mode
from sentry.utils.security.orgauthtoken_token import generate_token, hash_token


class ProjectPreprodCheckForUpdatesEndpointTest(APITestCase):
def setUp(self) -> None:
super().setUp()

self.user = self.create_user(email="test@example.com")
self.org = self.create_organization(owner=self.user)
self.org = self.create_organization()
self.project = self.create_project(organization=self.org)
self.api_token = self.create_user_auth_token(
user=self.user, scope_list=["org:admin", "project:admin"]
)

# Create an integration token with project:distribution scope
token_str = generate_token(self.org.slug, "")
with assume_test_silo_mode(SiloMode.CONTROL):
OrgAuthToken.objects.create(
organization_id=self.org.id,
name="Test Integration Token",
token_hashed=hash_token(token_str),
scope_list=["project:distribution"],
)
self.api_token = token_str

self.file = self.create_file(name="test_artifact.apk", type="application/octet-stream")

Expand Down Expand Up @@ -72,7 +83,7 @@ def test_missing_required_parameters(self):
"""Test that missing required parameters return 400"""
url = self._get_url()
response = self.client.get(
url, format="json", HTTP_AUTHORIZATION=f"Bearer {self.api_token.token}"
url, format="json", HTTP_AUTHORIZATION=f"Bearer {self.api_token}"
)
assert response.status_code == 400
assert "Missing required parameters" in response.json()["error"]
Expand All @@ -84,7 +95,7 @@ def test_current_artifact_not_found(self):
url
+ "?app_id=com.example.app&platform=android&build_version=1.0.0&main_binary_identifier=nonexistent",
format="json",
HTTP_AUTHORIZATION=f"Bearer {self.api_token.token}",
HTTP_AUTHORIZATION=f"Bearer {self.api_token}",
)
assert response.status_code == 200
assert response.json()["current"] is None
Expand All @@ -102,7 +113,7 @@ def test_current_artifact_success_ios(self):
url
+ "?app_id=com.example.app&platform=ios&build_version=1.0.0&main_binary_identifier=test-identifier",
format="json",
HTTP_AUTHORIZATION=f"Bearer {self.api_token.token}",
HTTP_AUTHORIZATION=f"Bearer {self.api_token}",
)

assert response.status_code == 200
Expand Down Expand Up @@ -136,7 +147,7 @@ def test_update_detection_android(self):
url
+ "?app_id=com.example.app&platform=android&build_version=1.0.0&main_binary_identifier=test-identifier",
format="json",
HTTP_AUTHORIZATION=f"Bearer {self.api_token.token}",
HTTP_AUTHORIZATION=f"Bearer {self.api_token}",
)

assert response.status_code == 200
Expand Down Expand Up @@ -173,7 +184,7 @@ def test_update_detection_ios(self):
url
+ "?app_id=com.example.app&platform=ios&build_version=1.0.0&main_binary_identifier=test-identifier",
format="json",
HTTP_AUTHORIZATION=f"Bearer {self.api_token.token}",
HTTP_AUTHORIZATION=f"Bearer {self.api_token}",
)

assert response.status_code == 200
Expand Down Expand Up @@ -207,7 +218,7 @@ def test_platform_specific_filtering_android(self):
url
+ "?app_id=com.example.app&platform=android&build_version=1.0.0&main_binary_identifier=test-identifier",
format="json",
HTTP_AUTHORIZATION=f"Bearer {self.api_token.token}",
HTTP_AUTHORIZATION=f"Bearer {self.api_token}",
)

assert response.status_code == 200
Expand Down Expand Up @@ -243,7 +254,7 @@ def test_installable_artifact_filtering(self):
url
+ "?app_id=com.example.app&platform=android&build_version=1.0.0&main_binary_identifier=test-identifier",
format="json",
HTTP_AUTHORIZATION=f"Bearer {self.api_token.token}",
HTTP_AUTHORIZATION=f"Bearer {self.api_token}",
)

assert response.status_code == 200
Expand Down Expand Up @@ -281,7 +292,7 @@ def test_highest_build_number_selection(self):
url
+ "?app_id=com.example.app&platform=android&build_version=1.0.0&main_binary_identifier=test-identifier",
format="json",
HTTP_AUTHORIZATION=f"Bearer {self.api_token.token}",
HTTP_AUTHORIZATION=f"Bearer {self.api_token}",
)

assert response.status_code == 200
Expand All @@ -306,7 +317,7 @@ def test_no_update_available(self):
url
+ "?app_id=com.example.app&platform=android&build_version=1.0.0&main_binary_identifier=test-identifier",
format="json",
HTTP_AUTHORIZATION=f"Bearer {self.api_token.token}",
HTTP_AUTHORIZATION=f"Bearer {self.api_token}",
)

assert response.status_code == 200
Expand Down Expand Up @@ -345,7 +356,7 @@ def test_multiple_artifacts_same_version_different_build_configurations(self):
url
+ "?app_id=com.example.app&platform=android&build_version=1.0.0&main_binary_identifier=test-identifier&build_configuration=debug",
format="json",
HTTP_AUTHORIZATION=f"Bearer {self.api_token.token}",
HTTP_AUTHORIZATION=f"Bearer {self.api_token}",
)

assert response.status_code == 200
Expand Down Expand Up @@ -375,7 +386,7 @@ def test_build_number_filtering(self):
url
+ "?app_id=com.example.app&platform=android&build_version=1.0.0&build_number=42&main_binary_identifier=test-identifier",
format="json",
HTTP_AUTHORIZATION=f"Bearer {self.api_token.token}",
HTTP_AUTHORIZATION=f"Bearer {self.api_token}",
)

assert response.status_code == 200
Expand All @@ -392,7 +403,7 @@ def test_invalid_build_number_format(self):
url
+ "?app_id=com.example.app&platform=android&build_version=1.0.0&build_number=invalid&main_binary_identifier=test-identifier",
format="json",
HTTP_AUTHORIZATION=f"Bearer {self.api_token.token}",
HTTP_AUTHORIZATION=f"Bearer {self.api_token}",
)
assert response.status_code == 400
assert "Invalid build_number format" in response.json()["error"]
Expand All @@ -409,7 +420,7 @@ def test_without_main_binary_identifier_with_build_number(self):
response = self.client.get(
url + "?app_id=com.example.app&platform=android&build_version=1.0.0&build_number=42",
format="json",
HTTP_AUTHORIZATION=f"Bearer {self.api_token.token}",
HTTP_AUTHORIZATION=f"Bearer {self.api_token}",
)

assert response.status_code == 200
Expand All @@ -423,7 +434,7 @@ def test_missing_both_main_binary_identifier_and_build_number(self):
response = self.client.get(
url + "?app_id=com.example.app&platform=android&build_version=1.0.0",
format="json",
HTTP_AUTHORIZATION=f"Bearer {self.api_token.token}",
HTTP_AUTHORIZATION=f"Bearer {self.api_token}",
)

assert response.status_code == 400
Expand Down
Loading