Skip to content

Commit

Permalink
ref(appstore-connect): Make iTunes credentials optional and stop (re)…
Browse files Browse the repository at this point in the history
…writing them to DB (#29863)
  • Loading branch information
relaxolotl authored Nov 9, 2021
1 parent c3a3349 commit 393b078
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 101 deletions.
19 changes: 0 additions & 19 deletions src/sentry/api/endpoints/project_app_store_connect_credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
validates and includes the status of API credentials associated with this app.
See :class:`AppStoreConnectCredentialsValidateEndpoint`.
"""
import datetime
import logging
from typing import Dict, Optional, Union
from uuid import uuid4
Expand Down Expand Up @@ -210,15 +209,6 @@ def post(self, request: Request, project: Project) -> Response:
config["id"] = uuid4().hex
config["name"] = config["appName"]

# TODO(itunes): Deprecated fields. Needs to be removed alongside a migration, so this uses
# placeholders as a temporary workaround until that migration happens.
config["itunesCreated"] = datetime.datetime.now()
config["itunesSession"] = "deprecated-field-do-not-use"
config["orgPublicId"] = "deprecated-field-please-do-not-use--"
config["orgName"] = "deprecated-field-do-not-use"
config["itunesUser"] = "deprecated-field-do-not-use"
config["itunesPassword"] = "deprecated-field-do-not-use"

try:
validated_config = appconnect.AppStoreConnectConfig.from_json(config)
except ValueError:
Expand Down Expand Up @@ -295,15 +285,6 @@ def post(self, request: Request, project: Project, credentials_id: str) -> Respo
except KeyError:
return Response(status=404)

# Deprecated fields. Needs to be removed alongside a migration, so this uses
# placeholders as a temporary workaround until that migration happens.
data["itunesCreated"] = datetime.datetime.now()
data["itunesSession"] = "deprecated-field-do-not-use"
data["orgPublicId"] = "deprecated-field-please-do-not-use--"
data["orgName"] = "deprecated-field-do-not-use"
data["itunesUser"] = "deprecated-field-do-not-use"
data["itunesPassword"] = "deprecated-field-do-not-use"

# Any secrets set to None during validation are meant to be no-ops, so remove them to avoid
# erasing the existing values
for secret in secret_fields(symbol_source_config.type):
Expand Down
33 changes: 16 additions & 17 deletions src/sentry/lang/native/appconnect.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@
import dataclasses
import logging
import pathlib
from datetime import datetime
from typing import Any, Dict, List

import dateutil
import jsonschema
import requests
import sentry_sdk
Expand Down Expand Up @@ -53,6 +51,17 @@ class NoDsymsError(Exception):
pass


# TODO(itunes): Remove when the fields are removed from DB
DEPRECATED_FIELDS = [
"itunesUser",
"itunesCreated",
"itunesPassword",
"itunesSession",
"orgPublicId",
"orgName",
]


@dataclasses.dataclass(frozen=True)
class AppStoreConnectConfig:
"""The symbol source configuration for an App Store Connect source.
Expand Down Expand Up @@ -93,14 +102,6 @@ class AppStoreConnectConfig:
# This is guaranteed to be unique and should map 1:1 to ``appId``.
bundleId: str

# TODO(itunes): Deprecated fields. These must be removed alongside a migration.
itunesUser: str
itunesPassword: str
itunesSession: str
itunesCreated: datetime
orgPublicId: str
orgName: str

def __post_init__(self) -> None:
# All fields are required.
for field in dataclasses.fields(self):
Expand All @@ -120,13 +121,13 @@ def from_json(cls, data: Dict[str, Any]) -> "AppStoreConnectConfig":
symbol source configuration.
"""
# TODO(itunes): Remove logic related to iTunes fields when the fields are removed
if isinstance(data["itunesCreated"], datetime):
data["itunesCreated"] = data["itunesCreated"].isoformat()
for field in DEPRECATED_FIELDS:
if field in data:
del data[field]
try:
jsonschema.validate(data, APP_STORE_CONNECT_SCHEMA)
except jsonschema.exceptions.ValidationError as e:
raise InvalidConfigError from e
data["itunesCreated"] = dateutil.parser.isoparse(data["itunesCreated"])
return cls(**data)

@classmethod
Expand Down Expand Up @@ -177,9 +178,6 @@ def to_json(self) -> Dict[str, Any]:
data = dict()
for field in dataclasses.fields(self):
value = getattr(self, field.name)
# TODO(itunes): Remove logic related to iTunes fields when the fields are removed
if field.name == "itunesCreated":
value = value.isoformat()
data[field.name] = value
try:
jsonschema.validate(data, APP_STORE_CONNECT_SCHEMA)
Expand All @@ -197,7 +195,8 @@ def to_redacted_json(self) -> Dict[str, Any]:
"""
data = self.to_json()
for to_redact in secret_fields("appStoreConnect"):
data[to_redact] = {"hidden-secret": True}
if to_redact in data:
data[to_redact] = {"hidden-secret": True}
return data

def update_project_symbol_source(self, project: Project, allow_multiple: bool) -> json.JSONData:
Expand Down
7 changes: 0 additions & 7 deletions src/sentry/lang/native/symbolicator.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,6 @@
"appName",
"appId",
"bundleId",
# TODO(itunes): All of the below fields are deprecated. Remove together with migration.
"itunesUser",
"itunesCreated",
"itunesPassword",
"itunesSession",
"orgPublicId",
"orgName",
],
"additionalProperties": False,
}
Expand Down
71 changes: 19 additions & 52 deletions tests/sentry/lang/native/test_appconnect.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,9 @@ def data(self, now: datetime) -> json.JSONData:
"appconnectIssuer": "abc123" * 6,
"appconnectKey": "abc123",
"appconnectPrivateKey": "---- BEGIN PRIVATE KEY ---- ABC123...",
"itunesUser": "someone@example.com",
"itunesPassword": "a secret",
"itunesSession": "ABC123",
"itunesCreated": now.isoformat(),
"appName": "Sample Application",
"appId": "1234",
"bundleId": "com.example.app",
"orgPublicId": "71105f98-7743-4844-ab70-2c901e2ea13d",
"orgName": "Example Organisation",
}

def test_from_json_basic(self, data: json.JSONData, now: datetime) -> None:
Expand All @@ -49,45 +43,38 @@ def test_from_json_basic(self, data: json.JSONData, now: datetime) -> None:
assert config.name == data["name"]
assert config.appconnectIssuer == data["appconnectIssuer"]
assert config.appconnectPrivateKey == data["appconnectPrivateKey"]
assert config.itunesUser == data["itunesUser"]
assert config.itunesPassword == data["itunesPassword"]
assert config.itunesSession == data["itunesSession"]
assert config.itunesCreated == now
assert config.appName == data["appName"]
assert config.bundleId == data["bundleId"]
assert config.orgPublicId == data["orgPublicId"]
assert config.orgName == data["orgName"]

def test_from_json_isoformat(self, data: json.JSONData, now: datetime) -> None:
data["itunesCreated"] = now.isoformat()
config = appconnect.AppStoreConnectConfig.from_json(data)
assert config.itunesCreated == now

def test_from_json_datetime(self, data: json.JSONData, now: datetime) -> None:
data["itunesCreated"] = now
config = appconnect.AppStoreConnectConfig.from_json(data)
assert config.itunesCreated == now

def test_to_json(self, data: json.JSONData, now: datetime) -> None:
config = appconnect.AppStoreConnectConfig.from_json(data)
new_data = config.to_json()

# Fixup our input to expected JSON format
data["itunesCreated"] = now.isoformat()

assert new_data == data

def test_to_redacted_json(self, data: json.JSONData, now: datetime) -> None:
config = appconnect.AppStoreConnectConfig.from_json(data)
new_data = config.to_redacted_json()

# Fixup our input to expected JSON format
data["itunesCreated"] = now.isoformat()
# Redacted secrets
data["appconnectPrivateKey"] = {"hidden-secret": True}
assert "itunesPassword" not in data
assert "itunesSession" not in data

assert new_data == data

def test_to_redacted_json_with_deprecated(self, data: json.JSONData, now: datetime) -> None:
data_with_deprecated = data
data_with_deprecated["itunesPassword"] = "honk"
data_with_deprecated["itunesSession"] = "beep"

config = appconnect.AppStoreConnectConfig.from_json(data)
new_data = config.to_redacted_json()

# Redacted secrets
data["appconnectPrivateKey"] = {"hidden-secret": True}
data["itunesPassword"] = {"hidden-secret": True}
data["itunesSession"] = {"hidden-secret": True}
assert "itunesPassword" not in data
assert "itunesSession" not in data

assert new_data == data

Expand All @@ -110,15 +97,9 @@ def config(self) -> appconnect.AppStoreConnectConfig:
appconnectIssuer="abc123" * 6,
appconnectKey="abc123key",
appconnectPrivateKey="----BEGIN PRIVATE KEY---- blabla",
itunesUser="me@example.com",
itunesPassword="secret",
itunesSession="THE-COOKIE",
itunesCreated=datetime.utcnow(),
appName="My App",
appId="123",
bundleId="com.example.app",
orgPublicId="71105f98-7743-4844-ab70-2c901e2ea13d",
orgName="Example Com",
)

@pytest.mark.django_db # type: ignore
Expand Down Expand Up @@ -156,7 +137,6 @@ def test_new_sources_with_existing(
new_sources.append(cfg.to_json())
assert stored_sources == new_sources

# TODO(itunes): Update when iTunes fields are removed
@pytest.mark.django_db # type: ignore
def test_update(
self, default_project: "Project", config: appconnect.AppStoreConnectConfig
Expand All @@ -169,24 +149,17 @@ def test_update(
name=config.name,
appconnectIssuer=config.appconnectIssuer,
appconnectKey=config.appconnectKey,
appconnectPrivateKey=config.appconnectPrivateKey,
itunesUser=config.itunesUser,
itunesPassword=config.itunesPassword,
itunesSession="A NEW COOKIE",
itunesCreated=datetime.utcnow(),
appconnectPrivateKey="A NEW KEY",
appName=config.appName,
appId=config.appId,
bundleId=config.bundleId,
orgPublicId=config.orgPublicId,
orgName=config.orgName,
)

updated.update_project_symbol_source(default_project, allow_multiple=False)

current = appconnect.AppStoreConnectConfig.from_project_config(default_project, config.id)
assert current.itunesSession == "A NEW COOKIE"
assert current.appconnectPrivateKey == "A NEW KEY"

# TODO(itunes): Update when iTunes fields are removed
@pytest.mark.django_db # type: ignore
def test_update_no_matching_id(
self, default_project: "Project", config: appconnect.AppStoreConnectConfig
Expand All @@ -199,16 +172,10 @@ def test_update_no_matching_id(
name=config.name,
appconnectIssuer=config.appconnectIssuer,
appconnectKey=config.appconnectKey,
appconnectPrivateKey=config.appconnectPrivateKey,
itunesUser=config.itunesUser,
itunesPassword=config.itunesPassword,
itunesSession="A NEW COOKIE",
itunesCreated=datetime.utcnow(),
appconnectPrivateKey="A NEW KEY",
appName=config.appName,
appId=config.appId,
bundleId=config.bundleId,
orgPublicId=config.orgPublicId,
orgName=config.orgName,
)

with pytest.raises(ValueError):
Expand Down
6 changes: 0 additions & 6 deletions tests/sentry/tasks/test_app_store_connect.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,9 @@ def config(self):
appconnectIssuer="abc123" * 6,
appconnectKey="abc123key",
appconnectPrivateKey="----BEGIN PRIVATE KEY---- blabla",
itunesUser="me@example.com",
itunesPassword="secret",
itunesSession="THE-COOKIE",
itunesCreated=datetime.utcnow(),
appName="My App",
appId="123",
bundleId="com.example.app",
orgPublicId="71105f98-7743-4844-ab70-2c901e2ea13d",
orgName="Example Com",
)

@pytest.fixture
Expand Down

0 comments on commit 393b078

Please sign in to comment.