Merge branch '21666-provision-test-improvement'
[arvados.git] / services / api / app / models / api_client_authorization.rb
index ef73d79c176d28961e597ef6df28536816304aa2..83112786764d64377f3e6b7983fb1e3310ca59db 100644 (file)
@@ -294,6 +294,10 @@ class ApiClientAuthorization < ArvadosModel
         raise "remote cluster #{upstream_cluster_id} returned invalid token uuid #{token_uuid.inspect}"
       end
     rescue HTTPClient::BadResponseError => e
+      if e.res.status_code >= 400 && e.res.status_code < 500
+        # Remote cluster does not accept this token.
+        return nil
+      end
       # CurrentApiToken#call and ApplicationController#render_error will
       # propagate the status code from the #http_status method, so define
       # that here.
@@ -363,71 +367,17 @@ class ApiClientAuthorization < ArvadosModel
     if user.nil? and remote_user.nil?
       Rails.logger.warn "remote token #{token.inspect} rejected: cannot get owner #{remote_user_uuid} from database or remote cluster"
       return nil
+    end
+
     # Invariant:    remote_user_prefix == upstream_cluster_id
     # therefore:    remote_user_prefix != Rails.configuration.ClusterID
     # Add or update user and token in local database so we can
     # validate subsequent requests faster.
-    elsif user.nil?
-      # Create a new record for this user.
-      user = User.new(uuid: remote_user['uuid'],
-                      is_active: false,
-                      is_admin: false,
-                      email: remote_user['email'],
-                      owner_uuid: system_user_uuid)
-      user.set_initial_username(requested: remote_user['username'])
-    end
 
-    # Sync user record if we loaded a remote user.
     act_as_system_user do
-      if remote_user
-        %w[first_name last_name email prefs].each do |attr|
-          user.send(attr+'=', remote_user[attr])
-        end
-
-        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.uncached do
-            User.find_by_uuid(remote_user['uuid'])
-          end
-          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
-          # also makes them inactive.
-          user.unsetup
-        else
-          if !user.is_invited && remote_user['is_invited'] and
-            (remote_user_prefix == Rails.configuration.Login.LoginCluster or
-             Rails.configuration.Users.AutoSetupNewUsers or
-             Rails.configuration.Users.NewUsersAreActive or
-             Rails.configuration.RemoteClusters[remote_user_prefix].andand["ActivateUsers"])
-            user.setup
-          end
-
-          if !user.is_active && remote_user['is_active'] && user.is_invited and
-            (remote_user_prefix == Rails.configuration.Login.LoginCluster or
-             Rails.configuration.Users.NewUsersAreActive or
-             Rails.configuration.RemoteClusters[remote_user_prefix].andand["ActivateUsers"])
-            user.update!(is_active: true)
-          elsif user.is_active && !remote_user['is_active']
-            user.update!(is_active: false)
-          end
-
-          if remote_user_prefix == Rails.configuration.Login.LoginCluster and
-            user.is_active and
-            user.is_admin != remote_user['is_admin']
-            # Remote cluster controls our user database, including the
-            # admin flag.
-            user.update!(is_admin: remote_user['is_admin'])
-          end
-        end
+      if remote_user && remote_user_uuid != anonymous_user_uuid
+        # Sync user record if we loaded a remote user.
+        user = User.update_remote_user remote_user
       end
 
       # If stored_secret is set, we save stored_secret in the database
@@ -453,8 +403,17 @@ class ApiClientAuthorization < ArvadosModel
         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
+        # Another request won the race (trying to find_or_create the
+        # same token UUID) ...and/or... there is an expired entry with
+        # the same secret but a different UUID (e.g., the token is an
+        # OIDC access token and [a] our database has an expired cached
+        # row that was not used above, and [b] the remote cluster had
+        # deleted its expired cached row so it assigned a new UUID).
+        #
+        # Delete any conflicting row if any. Retry twice (in case we
+        # hit both of those situations at once), then give up.
+        if (retries += 1) <= 2
+          ApiClientAuthorization.where('api_token=? and uuid<>?', stored_secret, token_uuid).delete_all
           retry
         else
           Rails.logger.warn("cannot find or create cached remote token #{token_uuid}")