def post(self, request, project): if not features.has(APP_STORE_CONNECT_FEATURE_NAME, project.organization, actor=request.user): return Response(status=404) serializer = AppStoreConnectRequestSmsSerializer(data=request.data) if not serializer.is_valid(): return Response(serializer.errors, status=400) session = requests.Session() encrypted_context = serializer.validated_data.get("sessionContext") key = project.get_option(CREDENTIALS_KEY_NAME) if key is None: return Response( "Invalid state. Must first call appstoreconnect/start/ endpoint.", status=400) try: # recover the headers set in the first step authentication session_context = encrypt.decrypt_object(encrypted_context, key) headers = ITunesHeaders( session_id=session_context.get("session_id"), scnt=session_context.get("scnt")) auth_key = session_context.get("auth_key") except ValueError: return Response("Invalid validation context passed.", status=400) phone_info = itunes_connect.get_trusted_phone_info( session, service_key=auth_key, headers=headers) if phone_info is None: return Response("Could not get phone info", status=400) init_phone_login = itunes_connect.initiate_phone_login( session, service_key=auth_key, headers=headers, phone_id=phone_info.id, push_mode=phone_info.push_mode, ) if init_phone_login is None: return Response("Phone 2fa failed", status=500) # success, return the new session context (add phone_id and push mode to the session context) session_context["phone_id"] = phone_info.id session_context["push_mode"] = phone_info.push_mode encrypted_context = encrypt.encrypt_object(session_context, key) return Response({"sessionContext": encrypted_context}, status=200)
def post(self, request, project): if not features.has(APP_STORE_CONNECT_FEATURE_NAME, project.organization, actor=request.user): return Response(status=404) serializer = AppStoreCreateCredentialsSerializer(data=request.data) if not serializer.is_valid(): return Response(serializer.errors, status=400) key = project.get_option(CREDENTIALS_KEY_NAME) if key is None: # probably stage 1 login was not called return Response( "Invalid state. Must first call appstoreconnect/start/ endpoint.", status=400) credentials = serializer.validated_data encrypted_context = credentials.pop("sessionContext") try: validation_context = encrypt.decrypt_object(encrypted_context, key) itunes_session = validation_context.get("itunes_session") encrypted = { "itunesSession": itunes_session, "itunesPassword": credentials.get("itunesPassword"), "appconnectPrivateKey": credentials.get("appconnectPrivateKey"), } credentials["encrypted"] = encrypt.encrypt_object(encrypted, key) credentials["type"] = "appStoreConnect" credentials["itunesCreated"] = validation_context.get( "itunes_created") credentials["id"] = uuid4().hex credentials["name"] = "Apple App Store Connect" # TODO(flub): validate this using the JSON schema in sentry.lang.native.symbolicator except ValueError: return Response("Invalid validation context passed.", status=400) return Response(credentials, status=200)
def post(self, request, project): if not features.has(app_store_connect_feature_name(), project.organization, actor=request.user): return Response(status=404) serializer = AppStoreCreateCredentialsSerializer(data=request.data) if not serializer.is_valid(): return Response(serializer.errors, status=400) key = project.get_option(credentials_key_name()) if key is None: # probably stage 1 login was not called return Response( "Invalid state. Must first call appstoreconnect/start/ endpoint.", status=400) credentials = serializer.validated_data encrypted_context = credentials.pop("sessionContext") try: validation_context = encrypt.decrypt_object(encrypted_context, key) itunes_session = validation_context.get("itunes_session") encrypted = { "itunesSession": itunes_session, "itunesPassword": credentials.pop("itunesPassword"), "appconnectPrivateKey": credentials.pop("appconnectPrivateKey"), } credentials["encrypted"] = encrypt.encrypt_object(encrypted, key) credentials["type"] = "appStoreConnect" credentials["id"] = uuid4().hex credentials["name"] = "Apple App Store Connect" except ValueError: return Response("Invalid validation context passed.", status=400) return Response(credentials, status=200)
def post(self, request, project): if not features.has(APP_STORE_CONNECT_FEATURE_NAME, project.organization, actor=request.user): return Response(status=404) serializer = AppStoreConnect2FactorAuthSerializer(data=request.data) if not serializer.is_valid(): return Response(serializer.errors, status=400) encrypted_context = serializer.validated_data.get("sessionContext") key = project.get_option(CREDENTIALS_KEY_NAME) use_sms = serializer.validated_data.get("useSms") code = serializer.validated_data.get("code") if key is None: # probably stage 1 login was not called return Response( "Invalid state. Must first call appstoreconnect/start/ endpoint.", status=400) try: # recover the headers set in the first step authentication session_context = encrypt.decrypt_object(encrypted_context, key) headers = ITunesHeaders( session_id=session_context.get("session_id"), scnt=session_context.get("scnt")) auth_key = session_context.get("auth_key") session = requests.Session() if use_sms: phone_id = session_context.get("phone_id") push_mode = session_context.get("push_mode") success = itunes_connect.send_phone_authentication_confirmation_code( session, service_key=auth_key, headers=headers, phone_id=phone_id, push_mode=push_mode, security_code=code, ) else: success = itunes_connect.send_authentication_confirmation_code( session, service_key=auth_key, headers=headers, security_code=code) if success: session_info = itunes_connect.get_session_info(session) if session_info is None: return Response("session info failed", status=500) existing_providers = get_path(session_info, "availableProviders") providers = [{ "name": provider.get("name"), "organizationId": provider.get("providerId") } for provider in existing_providers] prs_id = get_path(session_info, "user", "prsId") itunes_session = itunes_connect.get_session_cookie(session) session_context = { "auth_key": auth_key, "session_id": headers.session_id, "scnt": headers.scnt, "itunes_session": itunes_session, "itunes_person_id": prs_id, "itunes_created": datetime.datetime.utcnow(), } encrypted_context = encrypt.encrypt_object( session_context, key) response_body = { "sessionContext": encrypted_context, "organizations": providers } return Response(response_body, status=200) else: return Response("2FA failed.", status=401) except ValueError: return Response("Invalid validation context passed.", status=400)
def post(self, request, project): if not features.has(APP_STORE_CONNECT_FEATURE_NAME, project.organization, actor=request.user): return Response(status=404) serializer = AppStoreConnectStartAuthSerializer(data=request.data) if not serializer.is_valid(): return Response(serializer.errors, status=400) user_name = serializer.validated_data.get("itunesUser") password = serializer.validated_data.get("itunesPassword") credentials_id = serializer.validated_data.get("id") key = project.get_option(CREDENTIALS_KEY_NAME) if key is None: # no encryption key for this project, create one key = encrypt.create_key() project.update_option(CREDENTIALS_KEY_NAME, key) else: # we have an encryption key, see if the credentials were not # supplied and we just want to re validate the session if user_name is None or password is None: # credentials not supplied use saved credentials credentials = get_app_store_config(project, credentials_id) if key is None or credentials is None: return Response("No credentials provided.", status=400) try: secrets = encrypt.decrypt_object( credentials.get("encrypted"), key) except ValueError: return Response("Invalid credentials state.", status=500) user_name = credentials.get("itunesUser") password = secrets.get("itunesPassword") if user_name is None or password is None: return Response("Invalid credentials.", status=500) session = requests.session() auth_key = itunes_connect.get_auth_service_key(session) if auth_key is None: return Response("Could not contact itunes store.", status=500) if user_name is None: return Response("No user name provided.", status=400) if password is None: return Response("No password provided.", status=400) init_login_result = itunes_connect.initiate_login( session, service_key=auth_key, account_name=user_name, password=password) if init_login_result is None: return Response("ITunes login failed.", status=401) # send session context to be used in next calls session_context = { "auth_key": auth_key, "session_id": init_login_result.session_id, "scnt": init_login_result.scnt, } return Response( {"sessionContext": encrypt.encrypt_object(session_context, key)}, status=200)
def post(self, request, project, credentials_id): if not features.has(APP_STORE_CONNECT_FEATURE_NAME, project.organization, actor=request.user): return Response(status=404) serializer = AppStoreUpdateCredentialsSerializer(data=request.data) if not serializer.is_valid(): return Response(serializer.errors, status=400) # get the existing credentials symbol_source_config = get_app_store_config(project, credentials_id) key = project.get_option(CREDENTIALS_KEY_NAME) if key is None or symbol_source_config is None: return Response(status=404) try: secrets = encrypt.decrypt_object( symbol_source_config.pop("encrypted"), key) except ValueError: return Response(status=500) # get the new credentials new_credentials = serializer.validated_data encrypted_context = new_credentials.get("sessionContext") new_itunes_session = None new_itunes_created = None if encrypted_context is not None: try: validation_context = encrypt.decrypt_object( encrypted_context, key) new_itunes_session = validation_context.get("itunes_session") new_itunes_created = validation_context.get("itunes_created") except ValueError: return Response("Invalid validation context passed.", status=400) new_secrets = {} if new_itunes_session is not None: new_secrets["itunesSession"] = new_itunes_session new_itunes_password = new_credentials.get("itunesPassword") if new_itunes_password is not None: new_secrets["itunesPassword"] = new_itunes_password new_appconnect_private_key = new_credentials.get( "appconnectPrivateKey") if new_appconnect_private_key is not None: new_secrets["appconnectPrivateKey"] = new_appconnect_private_key # merge the new and existing credentials try: secrets.update(new_secrets) symbol_source_config.update(new_credentials) symbol_source_config["encrypted"] = encrypt.encrypt_object( secrets, key) symbol_source_config["itunesCreated"] = new_itunes_created symbol_source_config["id"] = uuid4().hex # TODO(flub): validate this using the JSON schema in sentry.lang.native.symbolicator except ValueError: return Response("Invalid validation context passed.", status=400) return Response(symbol_source_config, status=200)
def post(self, request, project, credentials_id): if not features.has(app_store_connect_feature_name(), project.organization, actor=request.user): return Response(status=404) serializer = AppStoreUpdateCredentialsSerializer(data=request.data) if not serializer.is_valid(): return Response(serializer.errors, status=400) # get the existing credentials 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.pop("encrypted"), key) except ValueError: return Response(status=500) # get the new credentials new_credentials = serializer.validated_data encrypted_context = new_credentials.get("sessionContext") new_itunes_session = None if encrypted_context is not None: try: validation_context = encrypt.decrypt_object( encrypted_context, key) new_itunes_session = validation_context.get("itunes_session") except ValueError: return Response("Invalid validation context passed.", status=400) new_secrets = {} if new_itunes_session is not None: new_secrets["itunesSession"] = new_itunes_session new_itunes_password = new_credentials.get("itunesPassword") if new_itunes_password is not None: new_secrets["itunesPassword"] = new_itunes_password new_appconnect_private_key = new_credentials.get( "appconnectPrivateKey") if new_appconnect_private_key is not None: new_secrets["appconnectPrivateKey"] = new_appconnect_private_key # merge the new and existing credentials try: secrets.update(new_secrets) credentials.update(new_credentials) credentials["encrypted"] = encrypt.encrypt_object(secrets, key) credentials["id"] = uuid4().hex except ValueError: return Response("Invalid validation context passed.", status=400) return Response(credentials, status=200)