X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/8227b8a1be943fbbf3adb23a3d549dec28efbbba..ef35a5388d60e892835309df2b46b221f8df221d:/services/api/app/models/api_client_authorization.rb diff --git a/services/api/app/models/api_client_authorization.rb b/services/api/app/models/api_client_authorization.rb index 52f2cee064..52922d32b1 100644 --- a/services/api/app/models/api_client_authorization.rb +++ b/services/api/app/models/api_client_authorization.rb @@ -35,7 +35,12 @@ class ApiClientAuthorization < ArvadosModel UNLOGGED_CHANGES = ['last_used_at', 'last_used_by_ip_address', 'updated_at'] def assign_random_api_token - self.api_token ||= rand(2**256).to_s(36) + begin + self.api_token ||= rand(2**256).to_s(36) + rescue ActiveModel::MissingAttributeError + # Ignore the case where self.api_token doesn't exist, which happens when + # the select=[...] is used. + end end def owner_uuid @@ -111,6 +116,37 @@ class ApiClientAuthorization < ArvadosModel clnt end + def self.check_anonymous_user_token(token:, remote:) + case token[0..2] + when 'v2/' + _, token_uuid, secret, optional = token.split('/') + unless token_uuid.andand.length == 27 && secret.andand.length.andand > 0 && + token_uuid == Rails.configuration.ClusterID+"-gj3su-anonymouspublic" + # invalid v2 token, or v2 token for another user + return nil + end + else + # v1 token + secret = token + end + + # Usually, the secret is salted + salted_secret = OpenSSL::HMAC.hexdigest('sha1', Rails.configuration.Users.AnonymousUserToken, remote) + + # The anonymous token could be specified as a full v2 token in the config, + # but the config loader strips it down to the secret part. + # The anonymous token content and minimum length is verified in lib/config + if secret.length >= 0 && (secret == Rails.configuration.Users.AnonymousUserToken || secret == salted_secret) + return ApiClientAuthorization.new(user: User.find_by_uuid(anonymous_user_uuid), + uuid: Rails.configuration.ClusterID+"-gj3su-anonymouspublic", + api_token: secret, + api_client: anonymous_user_token_api_client, + scopes: ['GET /']) + else + return nil + end + end + def self.check_system_root_token token if token == Rails.configuration.SystemRootToken return ApiClientAuthorization.new(user: User.find_by_uuid(system_user_uuid), @@ -126,6 +162,11 @@ class ApiClientAuthorization < ArvadosModel return nil if token.nil? or token.empty? remote ||= Rails.configuration.ClusterID + auth = self.check_anonymous_user_token(token: token, remote: remote) + if !auth.nil? + return auth + end + auth = self.check_system_root_token(token) if !auth.nil? return auth @@ -319,7 +360,17 @@ class ApiClientAuthorization < ArvadosModel user.last_name = "from cluster #{remote_user_prefix}" end - user.save! + begin + user.save! + rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique + Rails.logger.debug("remote user #{remote_user['uuid']} already exists, retrying...") + # Some other request won the race: retry fetching the user record. + user = User.find_by_uuid(remote_user['uuid']) + if !user + Rails.logger.warn("cannot find or create remote user #{remote_user['uuid']}") + return nil + end + end if user.is_invited && !remote_user['is_invited'] # Remote user is not "invited" state, they should be unsetup, which @@ -364,12 +415,24 @@ class ApiClientAuthorization < ArvadosModel exp = [db_current_time + Rails.configuration.Login.RemoteTokenRefresh, remote_token.andand['expires_at']].compact.min scopes = remote_token.andand['scopes'] || ['all'] - auth = ApiClientAuthorization.find_or_create_by(uuid: token_uuid) do |auth| - auth.user = user - auth.api_token = stored_secret - auth.api_client_id = 0 - auth.scopes = scopes - auth.expires_at = exp + begin + retries ||= 0 + auth = ApiClientAuthorization.find_or_create_by(uuid: token_uuid) do |auth| + auth.user = user + auth.api_token = stored_secret + auth.api_client_id = 0 + auth.scopes = scopes + auth.expires_at = exp + end + rescue ActiveRecord::RecordNotUnique + Rails.logger.debug("cached remote token #{token_uuid} already exists, retrying...") + # Some other request won the race: retry just once before erroring out + if (retries += 1) <= 1 + retry + else + Rails.logger.warn("cannot find or create cached remote token #{token_uuid}") + return nil + end end auth.update_attributes!(user: user, api_token: stored_secret,