def post(self, request, project): if not features.has(APP_STORE_CONNECT_FEATURE_NAME, project.organization, actor=request.user): return Response(status=404) serializer = AppStoreConnectCredentialsSerializer(data=request.data) if not serializer.is_valid(): return Response(serializer.errors, status=400) data = serializer.validated_data credentials = appstore_connect.AppConnectCredentials( key_id=data.get("appconnectKey"), key=data.get("appconnectPrivateKey"), issuer_id=data.get("appconnectIssuer"), ) session = requests.Session() apps = appstore_connect.get_apps(session, credentials) if apps is None: return Response("App connect authentication error.", status=401) apps = [{ "name": app.name, "bundleId": app.bundle_id, "appId": app.app_id } for app in apps] result = {"apps": apps} return Response(result, status=200)
def get(self, request, project, credentials_id): if not features.has(APP_STORE_CONNECT_FEATURE_NAME, project.organization, actor=request.user): return Response(status=404) symbol_source_cfg = get_app_store_config(project, credentials_id) key = project.get_option(CREDENTIALS_KEY_NAME) if key is None or symbol_source_cfg is None: return Response(status=404) if symbol_source_cfg.get("itunesCreated") is not None: expiration_date = (dateutil.parser.isoparse( symbol_source_cfg.get("itunesCreated")) + ITUNES_TOKEN_VALIDITY) else: expiration_date = None try: secrets = encrypt.decrypt_object( symbol_source_cfg.get("encrypted"), key) except ValueError: return Response(status=500) credentials = appstore_connect.AppConnectCredentials( key_id=symbol_source_cfg.get("appconnectKey"), key=secrets.get("appconnectPrivateKey"), issuer_id=symbol_source_cfg.get("appconnectIssuer"), ) session = requests.Session() apps = appstore_connect.get_apps(session, credentials) appstore_valid = apps is not None itunes_connect.load_session_cookie(session, secrets.get("itunesSession")) itunes_session_info = itunes_connect.get_session_info(session) itunes_session_valid = itunes_session_info is not None return Response( { "appstoreCredentialsValid": appstore_valid, "itunesSessionValid": itunes_session_valid, "itunesSessionRefreshAt": expiration_date if itunes_session_valid else None, }, status=200, )
def post(self, request: Request, project: Project) -> Response: serializer = AppStoreConnectCredentialsSerializer(data=request.data) if not serializer.is_valid(): return Response(serializer.errors, status=400) data = serializer.validated_data cfg_id: Optional[str] = data.get("id") apc_key: Optional[str] = data.get("appconnectKey") apc_private_key: Optional[str] = data.get("appconnectPrivateKey") apc_issuer: Optional[str] = data.get("appconnectIssuer") if cfg_id: try: current_config = appconnect.AppStoreConnectConfig.from_project_config( project, cfg_id) except KeyError: return Response(status=404) if not apc_key: apc_key = current_config.appconnectKey if not apc_private_key: apc_private_key = current_config.appconnectPrivateKey if not apc_issuer: apc_issuer = current_config.appconnectIssuer if not apc_key or not apc_private_key or not apc_issuer: return Response("Incomplete API credentials", status=400) credentials = appstore_connect.AppConnectCredentials( key_id=apc_key, key=apc_private_key, issuer_id=apc_issuer, ) session = requests.Session() try: apps = appstore_connect.get_apps(session, credentials) except appstore_connect.UnauthorizedError: raise AppConnectAuthenticationError except appstore_connect.ForbiddenError: raise AppConnectForbiddenError if apps is None: raise AppConnectAuthenticationError all_apps = [{ "name": app.name, "bundleId": app.bundle_id, "appId": app.app_id } for app in apps] result = {"apps": all_apps} return Response(result, status=200)
def get(self, request, project, credentials_id): if not features.has(app_store_connect_feature_name(), project.organization, actor=request.user): return Response(status=404) credentials = get_app_store_credentials(project, credentials_id) key = project.get_option(credentials_key_name()) if key is None or credentials is None: return Response(status=404) try: secrets = encrypt.decrypt_object(credentials.get("encrypted"), key) except ValueError: return Response(status=500) credentials = appstore_connect.AppConnectCredentials( key_id=credentials.get("appconnectKey"), key=secrets.get("appconnectPrivateKey"), issuer_id=credentials.get("appconnectIssuer"), ) session = requests.Session() apps = appstore_connect.get_apps(session, credentials) appstore_valid = apps is not None itunes_connect.load_session_cookie(session, secrets.get("itunesSession")) itunes_session_info = itunes_connect.get_session_info(session) itunes_session_valid = itunes_session_info is not None return Response( { "appstoreCredentialsValid": appstore_valid, "itunesSessionValid": itunes_session_valid, }, status=200, )
def get(self, request: Request, project: Project, credentials_id: str) -> Response: try: symbol_source_cfg = appconnect.AppStoreConnectConfig.from_project_config( project, credentials_id) except KeyError: return Response(status=404) credentials = appstore_connect.AppConnectCredentials( key_id=symbol_source_cfg.appconnectKey, key=symbol_source_cfg.appconnectPrivateKey, issuer_id=symbol_source_cfg.appconnectIssuer, ) session = requests.Session() apps = appstore_connect.get_apps(session, credentials) try: itunes_client = itunes_connect.ITunesClient.from_session_cookie( symbol_source_cfg.itunesSession) itunes_session_info = itunes_client.request_session_info() except itunes_connect.SessionExpiredError: itunes_session_info = None pending_downloads = AppConnectBuild.objects.filter( project=project, fetched=False).count() latest_build = (AppConnectBuild.objects.filter( project=project, bundle_id=symbol_source_cfg.bundleId).order_by( "-uploaded_to_appstore").first()) if latest_build is None: latestBuildVersion = None latestBuildNumber = None else: latestBuildVersion = latest_build.bundle_short_version latestBuildNumber = latest_build.bundle_version try: check_entry = LatestAppConnectBuildsCheck.objects.get( project=project, source_id=symbol_source_cfg.id) # If the source was only just created then it's possible that sentry hasn't checked for any # new builds for it yet. except LatestAppConnectBuildsCheck.DoesNotExist: last_checked_builds = None else: last_checked_builds = check_entry.last_checked return Response( { "appstoreCredentialsValid": apps is not None, "pendingDownloads": pending_downloads, "latestBuildVersion": latestBuildVersion, "latestBuildNumber": latestBuildNumber, "lastCheckedBuilds": last_checked_builds, "promptItunesSession": bool(pending_downloads and itunes_session_info is None), }, status=200, )
def get(self, request: Request, project: Project) -> Response: config_ids = appconnect.AppStoreConnectConfig.all_config_ids(project) statuses = {} for config_id in config_ids: try: symbol_source_cfg = appconnect.AppStoreConnectConfig.from_project_config( project, config_id) except KeyError: continue credentials = appstore_connect.AppConnectCredentials( key_id=symbol_source_cfg.appconnectKey, key=symbol_source_cfg.appconnectPrivateKey, issuer_id=symbol_source_cfg.appconnectIssuer, ) session = requests.Session() try: apps = appstore_connect.get_apps(session, credentials) except appstore_connect.UnauthorizedError: asc_credentials = { "status": "invalid", "code": AppConnectAuthenticationError.code, } except appstore_connect.ForbiddenError: asc_credentials = { "status": "invalid", "code": AppConnectForbiddenError.code } else: if apps: asc_credentials = {"status": "valid"} else: asc_credentials = { "status": "invalid", "code": AppConnectAuthenticationError.code, } # TODO: is it possible to set up two configs pointing to the same app? pending_downloads = AppConnectBuild.objects.filter( project=project, app_id=symbol_source_cfg.appId, fetched=False).count() latest_build = (AppConnectBuild.objects.filter( project=project, bundle_id=symbol_source_cfg.bundleId).order_by( "-uploaded_to_appstore").first()) if latest_build is None: latestBuildVersion = None latestBuildNumber = None else: latestBuildVersion = latest_build.bundle_short_version latestBuildNumber = latest_build.bundle_version try: check_entry = LatestAppConnectBuildsCheck.objects.get( project=project, source_id=symbol_source_cfg.id) # If the source was only just created then it's possible that sentry hasn't checked for any # new builds for it yet. except LatestAppConnectBuildsCheck.DoesNotExist: last_checked_builds = None else: last_checked_builds = check_entry.last_checked statuses[config_id] = { "credentials": asc_credentials, "pendingDownloads": pending_downloads, "latestBuildVersion": latestBuildVersion, "latestBuildNumber": latestBuildNumber, "lastCheckedBuilds": last_checked_builds, } return Response(statuses, status=200)