[ARVADOS] created: 1.1.2-14-g87647b5

Git user git at public.curoverse.com
Fri Jan 5 10:41:34 EST 2018


        at  87647b5d3d72ae0c291fcdf1ee3b4a46b3af91c0 (commit)


commit 87647b5d3d72ae0c291fcdf1ee3b4a46b3af91c0
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date:   Fri Jan 5 10:38:04 2018 -0500

    12702: Add /arvados/v1/users/{uuid}/update_uuid API.
    
    Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>

diff --git a/services/api/app/controllers/arvados/v1/users_controller.rb b/services/api/app/controllers/arvados/v1/users_controller.rb
index 5e12352..dc7e62f 100644
--- a/services/api/app/controllers/arvados/v1/users_controller.rb
+++ b/services/api/app/controllers/arvados/v1/users_controller.rb
@@ -9,7 +9,7 @@ class Arvados::V1::UsersController < ApplicationController
     [:activate, :current, :system, :setup]
   skip_before_filter :render_404_if_no_object, only:
     [:activate, :current, :system, :setup]
-  before_filter :admin_required, only: [:setup, :unsetup]
+  before_filter :admin_required, only: [:setup, :unsetup, :update_uuid]
 
   def current
     if current_user
@@ -118,6 +118,13 @@ class Arvados::V1::UsersController < ApplicationController
     show
   end
 
+  # Change UUID to a new (unused) uuid and transfer all owned/linked
+  # objects accordingly.
+  def update_uuid
+    @object.update_uuid(new_uuid: params[:new_uuid])
+    show
+  end
+
   protected
 
   def self._setup_requires_parameters
@@ -140,6 +147,14 @@ class Arvados::V1::UsersController < ApplicationController
     }
   end
 
+  def self._update_uuid_requires_parameters
+    {
+      new_uuid: {
+        type: 'string', required: true,
+      },
+    }
+  end
+
   def apply_filters(model_class=nil)
     return super if @read_users.any?(&:is_admin)
     if params[:uuid] != current_user.andand.uuid
diff --git a/services/api/app/models/user.rb b/services/api/app/models/user.rb
index f3ca759..212047f 100644
--- a/services/api/app/models/user.rb
+++ b/services/api/app/models/user.rb
@@ -254,6 +254,29 @@ class User < ArvadosModel
     end
   end
 
+  def update_uuid(new_uuid:)
+    if !current_user.andand.is_admin
+      raise PermissionDeniedError
+    end
+    if self.class != self.class.resource_class_for_uuid(new_uuid)
+      raise "invalid new_uuid #{new_uuid.inspect}"
+    end
+    transaction(requires_new: true) do
+      reload
+      old_uuid = self.uuid
+      self.uuid = new_uuid
+      save!(validate: false)
+      ActiveRecord::Base.descendants.reject(&:abstract_class?).each do |klass|
+        klass.columns.each do |col|
+          if col.name.end_with?('_uuid')
+            column = col.name.to_sym
+            klass.where(column => old_uuid).update_all(column => new_uuid)
+          end
+        end
+      end
+    end
+  end
+
   protected
 
   def ensure_ownership_path_leads_to_user
diff --git a/services/api/config/routes.rb b/services/api/config/routes.rb
index 5b6fe80..fcd5c34 100644
--- a/services/api/config/routes.rb
+++ b/services/api/config/routes.rb
@@ -79,6 +79,7 @@ Server::Application.routes.draw do
         post 'activate', on: :member
         post 'setup', on: :collection
         post 'unsetup', on: :member
+        post 'update_uuid', on: :member
       end
       resources :virtual_machines do
         get 'logins', on: :member
diff --git a/services/api/test/functional/arvados/v1/users_controller_test.rb b/services/api/test/functional/arvados/v1/users_controller_test.rb
index b75479f..176104e 100644
--- a/services/api/test/functional/arvados/v1/users_controller_test.rb
+++ b/services/api/test/functional/arvados/v1/users_controller_test.rb
@@ -794,6 +794,28 @@ class Arvados::V1::UsersControllerTest < ActionController::TestCase
                     "user's writable_by should include its owner_uuid")
   end
 
+  test 'update_uuid as admin' do
+    authorize_with :admin
+    orig_uuid = users(:active).uuid
+    post :update_uuid, {
+           id: orig_uuid,
+           new_uuid: 'zbbbb-tpzed-abcde12345abcde',
+         }
+    assert_response :success
+    assert_empty User.where(uuid: orig_uuid)
+  end
+
+  test 'update_uuid as non-admin' do
+    authorize_with :active
+    orig_uuid = users(:active).uuid
+    post :update_uuid, {
+           id: orig_uuid,
+           new_uuid: 'zbbbb-tpzed-abcde12345abcde',
+         }
+    assert_response 403
+    assert_not_empty User.where(uuid: orig_uuid)
+  end
+
 
   NON_ADMIN_USER_DATA = ["uuid", "kind", "is_active", "email", "first_name",
                          "last_name"].sort
diff --git a/services/api/test/unit/user_test.rb b/services/api/test/unit/user_test.rb
index 3d8fb9d..3c85bd1 100644
--- a/services/api/test/unit/user_test.rb
+++ b/services/api/test/unit/user_test.rb
@@ -721,4 +721,73 @@ class UserTest < ActiveSupport::TestCase
     end
   end
 
+  [
+    'zzzzz-borkd-abcde12345abcde',
+    'zzzzz-j7d0g-abcde12345abcde',
+    'zzzzz-tpzed-borkd',
+  ].each do |new_uuid|
+    test "update_uuid to invalid uuid #{new_uuid}" do
+      u = users(:active)
+      orig_uuid = u.uuid
+      act_as_system_user do
+        assert_raises do
+          u.update_uuid(new_uuid: new_uuid)
+        end
+      end
+      # "Successfully aborted orig->new" outcome looks the same as
+      # "successfully updated new->orig".
+      assert_update_success(old_uuid: new_uuid, new_uuid: orig_uuid)
+    end
+  end
+
+  [:active, :spectator, :admin].each do |target|
+    test "update_uuid on #{target} as non-admin user" do
+      act_as_user users(:active) do
+        assert_raises(ArvadosModel::PermissionDeniedError) do
+          users(target).update_uuid(new_uuid: 'zzzzz-tpzed-abcde12345abcde')
+        end
+      end
+    end
+  end
+
+  test "update_uuid to existing uuid" do
+    u = users(:active)
+    orig_uuid = u.uuid
+    new_uuid = users(:admin).uuid
+    act_as_system_user do
+      assert_raises do
+        u.update_uuid(new_uuid: new_uuid)
+      end
+    end
+    u.reload
+    assert_equal u.uuid, orig_uuid
+    assert_not_empty Collection.where(owner_uuid: orig_uuid)
+    assert_not_empty Group.where(owner_uuid: orig_uuid)
+  end
+
+  [
+    'zbbbb-tpzed-abcde12345abcde',
+    'zzzzz-tpzed-abcde12345abcde',
+  ].each do |new_uuid|
+    test "update_uuid to unused uuid #{new_uuid}" do
+      u = users(:active)
+      orig_uuid = u.uuid
+      act_as_system_user do
+        u.update_uuid(new_uuid: new_uuid)
+      end
+      assert_update_success(old_uuid: orig_uuid, new_uuid: new_uuid)
+    end
+  end
+
+  def assert_update_success(old_uuid:, new_uuid:)
+    [[User, :uuid],
+     [Link, :head_uuid],
+     [Link, :tail_uuid],
+     [Group, :owner_uuid],
+     [Collection, :owner_uuid],
+    ].each do |klass, attr|
+      assert_empty klass.where(attr => old_uuid)
+      assert_not_empty klass.where(attr => new_uuid)
+    end
+  end
 end

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list