spaceship/lib/spaceship/client.rb in fastlane_hotfix-2.165.1 vs spaceship/lib/spaceship/client.rb in fastlane_hotfix-2.187.0

- old
+ new

@@ -55,10 +55,11 @@ UnauthorizedAccessError = Spaceship::UnauthorizedAccessError GatewayTimeoutError = Spaceship::GatewayTimeoutError InternalServerError = Spaceship::InternalServerError BadGatewayError = Spaceship::BadGatewayError AccessForbiddenError = Spaceship::AccessForbiddenError + TooManyRequestsError = Spaceship::TooManyRequestsError def self.hostname raise "You must implement self.hostname" end @@ -131,11 +132,11 @@ return @current_team_id if @current_team_id if teams.count > 1 puts("The current user is in #{teams.count} teams. Pass a team ID or call `select_team` to choose a team. Using the first one for now.") end - @current_team_id ||= teams[0]['contentProvider']['contentProviderId'] + @current_team_id ||= user_details_data['sessionToken']['contentProviderId'] end # Set a new team ID which will be used from now on def team_id=(team_id) # First, we verify the team actually exists, because otherwise iTC would return the @@ -144,11 +145,10 @@ # invalid content provider id # available_teams = teams.collect do |team| { team_id: (team["contentProvider"] || {})["contentProviderId"], - public_team_id: (team["contentProvider"] || {})["contentProviderPublicId"], team_name: (team["contentProvider"] || {})["name"] } end result = available_teams.find do |available_team| @@ -159,29 +159,23 @@ error_string = "Could not set team ID to '#{team_id}', only found the following available teams:\n\n#{available_teams.map { |team| "- #{team[:team_id]} (#{team[:team_name]})" }.join("\n")}\n" raise Tunes::Error.new, error_string end response = request(:post) do |req| - req.url("https://appstoreconnect.apple.com/olympus/v1/providerSwitchRequests") + req.url("ra/v2/session/webSession") req.body = { - "data": { - "type": "providerSwitchRequests", - "relationships": { - "provider": { - "data": { - "type": "providers", - "id": result[:public_team_id] - } - } - } - } + contentProviderId: team_id, + dsId: user_detail_data.ds_id # https://github.com/fastlane/fastlane/issues/6711 }.to_json req.headers['Content-Type'] = 'application/json' end handle_itc_response(response.body) + # clear user_details_data cache, as session switch will have changed sessionToken attribute + @_cached_user_details = nil + @current_team_id = team_id end # @return (Hash) Fetches all information of the currently used team def team_information @@ -410,10 +404,11 @@ # This method is used for both the Apple Dev Portal and App Store Connect # This will also handle 2 step verification and 2 factor authentication # # It is called in `send_login_request` of sub classes (which the method `login`, above, transferred over to via `do_login`) + # rubocop:disable Metrics/PerceivedComplexity def send_shared_login_request(user, password) # Check if we have a cached/valid session # # Background: # December 4th 2017 Apple introduced a rate limit - which is of course fine by itself - @@ -513,21 +508,32 @@ else if (response.body || "").include?('invalid="true"') # User Credentials are wrong raise InvalidUserCredentialsError.new, "Invalid username and password combination. Used '#{user}' as the username." elsif response.status == 412 && AUTH_TYPES.include?(response.body["authType"]) + + if try_upgrade_2fa_later(response) + store_cookie + fetch_olympus_session + return true + end + # Need to acknowledge Apple ID and Privacy statement - https://github.com/fastlane/fastlane/issues/12577 # Looking for status of 412 might be enough but might be safer to keep looking only at what is being reported - raise AppleIDAndPrivacyAcknowledgementNeeded.new, "Need to acknowledge to Apple's Apple ID and Privacy statement. Please manually log into https://appleid.apple.com (or https://appstoreconnect.apple.com) to acknowledge the statement." + raise AppleIDAndPrivacyAcknowledgementNeeded.new, "Need to acknowledge to Apple's Apple ID and Privacy statement. " \ + "Please manually log into https://appleid.apple.com (or https://appstoreconnect.apple.com) to acknowledge the statement. " \ + "Your account might also be asked to upgrade to 2FA. " \ + "Set SPACESHIP_SKIP_2FA_UPGRADE=1 for fastlane to automaticaly bypass 2FA upgrade if possible." elsif (response['Set-Cookie'] || "").include?("itctx") raise "Looks like your Apple ID is not enabled for App Store Connect, make sure to be able to login online" else info = [response.body, response['Set-Cookie']] raise Tunes::Error.new, info.join("\n") end end end + # rubocop:enable Metrics/PerceivedComplexity # Get the `itctx` from the new (22nd May 2017) API endpoint "olympus" # Update (29th March 2019) olympus migrates to new appstoreconnect API def fetch_olympus_session response = request(:get, "https://appstoreconnect.apple.com/olympus/v1/session") @@ -653,10 +659,21 @@ sleep(3) unless Object.const_defined?("SpecHelper") retry end raise ex # re-raise the exception + rescue TooManyRequestsError => ex + tries -= 1 + unless tries.zero? + msg = "Timeout received: '#{ex.class}', '#{ex.message}'. Retrying after #{ex.retry_after} seconds (remaining: #{tries})..." + puts(msg) if Spaceship::Globals.verbose? + logger.warn(msg) + + sleep(ex.retry_after) unless Object.const_defined?("SpecHelper") + retry + end + raise ex # re-raise the exception rescue \ Faraday::ParsingError, # <h2>Internal Server Error</h2> with content type json InternalServerError => ex tries -= 1 unless tries.zero? @@ -864,37 +881,37 @@ def send_request(method, url_or_path, params, headers, &block) with_retry do response = @client.send(method, url_or_path, params, headers, &block) log_response(method, url_or_path, response, headers, &block) - resp_hash = response.to_hash - if resp_hash[:status] == 401 - handle_401(response) - end + handle_error(response) if response.body.to_s.include?("<title>302 Found</title>") raise AppleTimeoutError.new, "Apple 302 detected - this might be temporary server error, check https://developer.apple.com/system-status/ to see if there is a known downtime" end if response.body.to_s.include?("<h3>Bad Gateway</h3>") raise BadGatewayError.new, "Apple 502 detected - this might be temporary server error, try again later" end - if resp_hash[:status] == 403 - msg = "Access forbidden" - logger.warn(msg) - raise AccessForbiddenError.new, msg - end - return response end end - def handle_401(response) - msg = "Auth lost" - logger.warn(msg) - raise UnauthorizedAccessError.new, "Unauthorized Access" + def handle_error(response) + case response.status + when 401 + msg = "Auth lost" + logger.warn(msg) + raise UnauthorizedAccessError.new, "Unauthorized Access" + when 403 + msg = "Access forbidden" + logger.warn(msg) + raise AccessForbiddenError.new, msg + when 429 + raise TooManyRequestsError, response.to_hash + end end def send_request_auto_paginate(method, url_or_path, params, headers, &block) response = send_request(method, url_or_path, params, headers, &block) return response unless should_process_next_rel?(response) @@ -919,5 +936,6 @@ end # rubocop:enable Metrics/ClassLength end require 'spaceship/two_step_or_factor_client' +require 'spaceship/upgrade_2fa_later_client'