Merge branch '20259-banner-tooltip-doc' refs #20259
[arvados.git] / services / api / app / controllers / arvados / v1 / links_controller.rb
index 19b6d439a473b2da5897000c443ceadbb941b69e..c956bfc9b467b5df3a98ef334961d4eb465bc3fc 100644 (file)
@@ -22,10 +22,12 @@ class Arvados::V1::LinksController < ApplicationController
     resource_attrs.delete :tail_kind
 
     if resource_attrs[:link_class] == 'permission' && Link::PermLevel[resource_attrs[:name]]
-      existing = Link.where(link_class: 'permission',
-                            tail_uuid: resource_attrs[:tail_uuid],
-                            head_uuid: resource_attrs[:head_uuid],
-                            name: Link::PermLevel.keys).first
+      existing = Link.
+                   lock. # select ... for update
+                   where(link_class: 'permission',
+                         tail_uuid: resource_attrs[:tail_uuid],
+                         head_uuid: resource_attrs[:head_uuid],
+                         name: Link::PermLevel.keys).first
       if existing
         @object = existing
         if Link::PermLevel[resource_attrs[:name]] > Link::PermLevel[existing.name]
@@ -37,6 +39,21 @@ class Arvados::V1::LinksController < ApplicationController
           return show
         end
       end
+    elsif resource_attrs[:link_class] == 'permission' &&
+          resource_attrs[:name] == 'can_login' &&
+          resource_attrs[:properties].respond_to?(:has_key?) &&
+          resource_attrs[:properties].has_key?(:username)
+      existing = Link.
+                   lock. # select ... for update
+                   where(link_class: 'permission',
+                         tail_uuid: resource_attrs[:tail_uuid],
+                         head_uuid: resource_attrs[:head_uuid]).
+                   where('properties @> ?', SafeJSON.dump({'username' => resource_attrs[:properties][:username]})).
+                   first
+      if existing
+        @object = existing
+        return show
+      end
     end
 
     super
@@ -57,7 +74,7 @@ class Arvados::V1::LinksController < ApplicationController
 
   protected
 
-  def find_object_by_uuid
+  def find_object_by_uuid(with_lock: false)
     if params[:id] && params[:id].match(/\D/)
       params[:uuid] = params.delete :id
     end
@@ -68,7 +85,7 @@ class Arvados::V1::LinksController < ApplicationController
         .where(uuid: params[:uuid])
         .first
     elsif !current_user
-      super
+      super(with_lock: with_lock)
     else
       # The usual permission-filtering index query is unnecessarily
       # inefficient, and doesn't match all permission links that
@@ -76,11 +93,15 @@ class Arvados::V1::LinksController < ApplicationController
       # by UUID, then check whether (a) its tail_uuid is the current
       # user or (b) its head_uuid is an object the current_user
       # can_manage.
-      link = Link.unscoped.where(uuid: params[:uuid]).first
+      model = Link
+      if with_lock && Rails.configuration.API.LockBeforeUpdate
+        model = model.lock
+      end
+      link = model.unscoped.where(uuid: params[:uuid]).first
       if link && link.link_class != 'permission'
         # Not a permission link. Re-fetch using generic
         # permission-filtering query.
-        super
+        super(with_lock: with_lock)
       elsif link && (current_user.uuid == link.tail_uuid ||
                      current_user.can?(manage: link.head_uuid))
         # Permission granted.