[ARVADOS] created: b268423c81bddfb3e0a6de14a404b4e8efc97977

git at public.curoverse.com git at public.curoverse.com
Tue Apr 1 15:35:46 EDT 2014


        at  b268423c81bddfb3e0a6de14a404b4e8efc97977 (commit)


commit b268423c81bddfb3e0a6de14a404b4e8efc97977
Author: Tom Clegg <tom at curoverse.com>
Date:   Tue Apr 1 15:36:02 2014 -0400

    Document group-level administrator semantics.

diff --git a/doc/api/permission-model.html.textile.liquid b/doc/api/permission-model.html.textile.liquid
index 5481a1c..baa300a 100644
--- a/doc/api/permission-model.html.textile.liquid
+++ b/doc/api/permission-model.html.textile.liquid
@@ -15,7 +15,7 @@ Each API transaction (read, write, create, etc.) is done on behalf of a person.
 
 A user (person) is permitted to act on an object if there is a path (series of permission Links) from the acting user to the object in which
 
-* Every intervening object is a Group, and
+* Every intervening object is a Group or a User, and
 * Every intervening permission Link allows the current action
 
 Each object has exactly one _owner_, which can be either a User or a Group.
@@ -70,7 +70,23 @@ h3. 3. Group-managed objects
 
 Three lab members are working together on a project. All Specimens, Links, Jobs, etc. can be modified by any of the three lab members. _Other_ lab members, who are not working on this project, can view but not modify these objects.
 
-h3. 4. Segregated roles
+h3. 4. Group-level administrator
+
+The Ashton Lab administrator, Alison, manages user accounts within her lab. She can enable and disable accounts, and exercise any permission that her lab members have.
+
+George has read-only access to the same set of accounts. This lets him see things like user activity and resource usage reports, without worrying about accidentally messing up anyone's data.
+
+table(table table-bordered table-condensed).
+|Tail                   |Permission     |Head                      |Effect|
+|Group: Ashton Lab Admin|can_manage     |User: Lab Member 1        |Lab member 1 is in this administrative group|
+|Group: Ashton Lab Admin|can_manage     |User: Lab Member 2        |Lab member 1 is in this administrative group|
+|Group: Ashton Lab Admin|can_manage     |User: Lab Member 3        |Lab member 1 is in this administrative group|
+|Group: Ashton Lab Admin|can_manage     |User: Alison              |Alison is in this administrative group|
+|Group: Ashton Lab Admin|can_manage     |User: George              |George is in this administrative group|
+|Alison                 |can_manage     |Group: Ashton Lab Admin   |Alison can do everything the above lab members can do|
+|George                 |can_read       |Group: Ashton Lab Admin   |George can read everything the above lab members can read|
+
+h3. 5. Segregated roles
 
 Granwyth, at the Hulatberi Lab, sets up a Factory Robot which uses a hosted Arvados site to do work for the Hulatberi Lab.
 

commit 935dec2c33e2ab45bc97146305e5937bab116d18
Author: Tom Clegg <tom at curoverse.com>
Date:   Tue Apr 1 15:16:18 2014 -0400

    Support group-wide administrator privileges.
    
    If an admin user or group has permission to read/write/manage a target
    user, the admin's permission extends to all other objects on which the
    target user has permission.

diff --git a/services/api/app/models/user.rb b/services/api/app/models/user.rb
index 0539247..fc09426 100644
--- a/services/api/app/models/user.rb
+++ b/services/api/app/models/user.rb
@@ -79,10 +79,10 @@ class User < ArvadosModel
         Group.where('owner_uuid in (?)', lookup_uuids).each do |group|
           newgroups << [group.owner_uuid, group.uuid, 'can_manage']
         end
-        Link.where('tail_uuid in (?) and link_class = ? and head_kind = ?',
+        Link.where('tail_uuid in (?) and link_class = ? and head_kind in (?)',
                    lookup_uuids,
                    'permission',
-                   'arvados#group').each do |link|
+                   ['arvados#group', 'arvados#user']).each do |link|
           newgroups << [link.tail_uuid, link.head_uuid, link.name]
         end
         newgroups.each do |tail_uuid, head_uuid, perm_name|
diff --git a/services/api/test/fixtures/api_client_authorizations.yml b/services/api/test/fixtures/api_client_authorizations.yml
index 60e9fbd..f60ba01 100644
--- a/services/api/test/fixtures/api_client_authorizations.yml
+++ b/services/api/test/fixtures/api_client_authorizations.yml
@@ -12,6 +12,18 @@ admin_trustedclient:
   api_token: 1a9ffdcga2o7cw8q12dndskomgs1ygli3ns9k2o9hgzgmktc78
   expires_at: 2038-01-01 00:00:00
 
+miniadmin:
+  api_client: untrusted
+  user: miniadmin
+  api_token: 2zb2y9pw3e70270te7oe3ewaantea3adyxjascvkz0zob7q7xb
+  expires_at: 2038-01-01 00:00:00
+
+rominiadmin:
+  api_client: untrusted
+  user: rominiadmin
+  api_token: 5tsb2pc3zlatn1ortl98s2tqsehpby88wmmnzmpsjmzwa6payh
+  expires_at: 2038-01-01 00:00:00
+
 active:
   api_client: untrusted
   user: active
diff --git a/services/api/test/fixtures/groups.yml b/services/api/test/fixtures/groups.yml
index 5810259..320302d 100644
--- a/services/api/test/fixtures/groups.yml
+++ b/services/api/test/fixtures/groups.yml
@@ -26,3 +26,8 @@ all_users:
   uuid: zzzzz-j7d0g-fffffffffffffff
   owner_uuid: zzzzz-tpzed-d9tiejq69daie8f
   name: All users
+
+testusergroup_admins:
+  uuid: zzzzz-j7d0g-48foin4vonvc2at
+  owner_uuid: zzzzz-tpzed-000000000000000
+  name: Administrators of a subset of users
diff --git a/services/api/test/fixtures/links.yml b/services/api/test/fixtures/links.yml
index 4e10b56..2aa6dab 100644
--- a/services/api/test/fixtures/links.yml
+++ b/services/api/test/fixtures/links.yml
@@ -237,3 +237,51 @@ foo_repository_readable_by_spectator:
   head_kind: arvados#repository
   head_uuid: zzzzz-2x53u-382brsig8rp3666
   properties: {}
+
+miniadmin_user_is_a_testusergroup_admin:
+  uuid: zzzzz-o0j2j-38vvkciz7qc12j9
+  owner_uuid: zzzzz-tpzed-000000000000000
+  created_at: 2014-04-01 13:53:33 -0400
+  modified_by_client_uuid: zzzzz-ozdt8-brczlopd8u8d0jr
+  modified_by_user_uuid: zzzzz-tpzed-000000000000000
+  modified_at: 2014-04-01 13:53:33 -0400
+  updated_at: 2014-04-01 13:53:33 -0400
+  tail_kind: arvados#user
+  tail_uuid: zzzzz-tpzed-2bg9x0oeydcw5hm
+  link_class: permission
+  name: can_manage
+  head_kind: arvados#group
+  head_uuid: zzzzz-j7d0g-48foin4vonvc2at
+  properties: {}
+
+rominiadmin_user_is_a_testusergroup_admin:
+  uuid: zzzzz-o0j2j-6b0hz5hr107mc90
+  owner_uuid: zzzzz-tpzed-000000000000000
+  created_at: 2014-04-01 13:53:33 -0400
+  modified_by_client_uuid: zzzzz-ozdt8-brczlopd8u8d0jr
+  modified_by_user_uuid: zzzzz-tpzed-000000000000000
+  modified_at: 2014-04-01 13:53:33 -0400
+  updated_at: 2014-04-01 13:53:33 -0400
+  tail_kind: arvados#user
+  tail_uuid: zzzzz-tpzed-4hvxm4n25emegis
+  link_class: permission
+  name: can_read
+  head_kind: arvados#group
+  head_uuid: zzzzz-j7d0g-48foin4vonvc2at
+  properties: {}
+
+testusergroup_can_manage_active_user:
+  uuid: zzzzz-o0j2j-2vaqhxz6hsf4k1d
+  owner_uuid: zzzzz-tpzed-000000000000000
+  created_at: 2014-04-01 13:56:10 -0400
+  modified_by_client_uuid: zzzzz-ozdt8-brczlopd8u8d0jr
+  modified_by_user_uuid: zzzzz-tpzed-000000000000000
+  modified_at: 2014-04-01 13:56:10 -0400
+  updated_at: 2014-04-01 13:56:10 -0400
+  tail_kind: arvados#group
+  tail_uuid: zzzzz-j7d0g-48foin4vonvc2at
+  link_class: permission
+  name: can_manage
+  head_kind: arvados#user
+  head_uuid: zzzzz-tpzed-xurymjxw79nv3jz
+  properties: {}
diff --git a/services/api/test/fixtures/specimens.yml b/services/api/test/fixtures/specimens.yml
new file mode 100644
index 0000000..070a5fe
--- /dev/null
+++ b/services/api/test/fixtures/specimens.yml
@@ -0,0 +1,11 @@
+owned_by_active_user:
+  uuid: zzzzz-2x53u-3zx463qyo0k4xrn
+  owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz
+
+owned_by_private_group:
+  uuid: zzzzz-2x53u-5m3qwg45g3nlpu6
+  owner_uuid: zzzzz-j7d0g-rew6elm53kancon
+
+owned_by_spectator:
+  uuid: zzzzz-2x53u-3b0xxwzlbzxq5yr
+  owner_uuid: zzzzz-tpzed-l1s2piq4t4mps8r
diff --git a/services/api/test/fixtures/users.yml b/services/api/test/fixtures/users.yml
index fd2d6bc..c02ab61 100644
--- a/services/api/test/fixtures/users.yml
+++ b/services/api/test/fixtures/users.yml
@@ -10,6 +10,26 @@ admin:
   is_admin: true
   prefs: {}
 
+miniadmin:
+  uuid: zzzzz-tpzed-2bg9x0oeydcw5hm
+  email: miniadmin at arvados.local
+  first_name: TestCase
+  last_name: User Group Administrator
+  identity_url: https://miniadmin.openid.local
+  is_active: true
+  is_admin: false
+  prefs: {}
+
+rominiadmin:
+  uuid: zzzzz-tpzed-4hvxm4n25emegis
+  email: rominiadmin at arvados.local
+  first_name: TestCase
+  last_name: Read-Only User Group Administrator
+  identity_url: https://rominiadmin.openid.local
+  is_active: true
+  is_admin: false
+  prefs: {}
+
 active:
   uuid: zzzzz-tpzed-xurymjxw79nv3jz
   email: active-user at arvados.local
diff --git a/services/api/test/integration/permissions_test.rb b/services/api/test/integration/permissions_test.rb
index c6597d5..40a77e7 100644
--- a/services/api/test/integration/permissions_test.rb
+++ b/services/api/test/integration/permissions_test.rb
@@ -3,12 +3,13 @@ require 'test_helper'
 class PermissionsTest < ActionDispatch::IntegrationTest
   fixtures :users, :groups, :api_client_authorizations, :collections
 
-  test "adding and removing direct can_read links" do
-    auth = {'HTTP_AUTHORIZATION' => "OAuth2 #{api_client_authorizations(:spectator).api_token}"}
-    admin_auth = {'HTTP_AUTHORIZATION' => "OAuth2 #{api_client_authorizations(:admin).api_token}"}
+  def auth auth_fixture
+    {'HTTP_AUTHORIZATION' => "OAuth2 #{api_client_authorizations(auth_fixture).api_token}"}
+  end
 
+  test "adding and removing direct can_read links" do
     # try to read collection as spectator
-    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth
+    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth(:spectator)
     assert_response 404
 
     # try to add permission as spectator
@@ -23,7 +24,7 @@ class PermissionsTest < ActionDispatch::IntegrationTest
         head_uuid: collections(:foo_file).uuid,
         properties: {}
       }
-    }, auth
+    }, auth(:spectator)
     assert_response 422
 
     # add permission as admin
@@ -38,34 +39,31 @@ class PermissionsTest < ActionDispatch::IntegrationTest
         head_uuid: collections(:foo_file).uuid,
         properties: {}
       }
-    }, admin_auth
+    }, auth(:admin)
     u = jresponse['uuid']
     assert_response :success
 
     # read collection as spectator
-    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth
+    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth(:spectator)
     assert_response :success
 
     # try to delete permission as spectator
-    delete "/arvados/v1/links/#{u}", {:format => :json}, auth
+    delete "/arvados/v1/links/#{u}", {:format => :json}, auth(:spectator)
     assert_response 403
 
     # delete permission as admin
-    delete "/arvados/v1/links/#{u}", {:format => :json}, admin_auth
+    delete "/arvados/v1/links/#{u}", {:format => :json}, auth(:admin)
     assert_response :success
 
     # try to read collection as spectator
-    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth
+    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth(:spectator)
     assert_response 404
   end
 
 
   test "adding can_read links from user to group, group to collection" do
-    auth = {'HTTP_AUTHORIZATION' => "OAuth2 #{api_client_authorizations(:spectator).api_token}"}
-    admin_auth = {'HTTP_AUTHORIZATION' => "OAuth2 #{api_client_authorizations(:admin).api_token}"}
-    
     # try to read collection as spectator
-    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth
+    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth(:spectator)
     assert_response 404
 
     # add permission for spectator to read group
@@ -80,11 +78,11 @@ class PermissionsTest < ActionDispatch::IntegrationTest
         head_uuid: groups(:private).uuid,
         properties: {}
       }
-    }, admin_auth
+    }, auth(:admin)
     assert_response :success
 
     # try to read collection as spectator
-    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth
+    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth(:spectator)
     assert_response 404
 
     # add permission for group to read collection
@@ -99,31 +97,28 @@ class PermissionsTest < ActionDispatch::IntegrationTest
         head_uuid: collections(:foo_file).uuid,
         properties: {}
       }
-    }, admin_auth
+    }, auth(:admin)
     u = jresponse['uuid']
     assert_response :success
 
     # try to read collection as spectator
-    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth
+    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth(:spectator)
     assert_response :success
 
     # delete permission for group to read collection
-    delete "/arvados/v1/links/#{u}", {:format => :json}, admin_auth
+    delete "/arvados/v1/links/#{u}", {:format => :json}, auth(:admin)
     assert_response :success
 
     # try to read collection as spectator
-    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth
+    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth(:spectator)
     assert_response 404
     
   end
 
 
   test "adding can_read links from group to collection, user to group" do
-    auth = {'HTTP_AUTHORIZATION' => "OAuth2 #{api_client_authorizations(:spectator).api_token}"}
-    admin_auth = {'HTTP_AUTHORIZATION' => "OAuth2 #{api_client_authorizations(:admin).api_token}"}
-    
     # try to read collection as spectator
-    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth
+    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth(:spectator)
     assert_response 404
 
     # add permission for group to read collection
@@ -138,11 +133,11 @@ class PermissionsTest < ActionDispatch::IntegrationTest
         head_uuid: collections(:foo_file).uuid,
         properties: {}
       }
-    }, admin_auth
+    }, auth(:admin)
     assert_response :success
 
     # try to read collection as spectator
-    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth
+    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth(:spectator)
     assert_response 404
 
     # add permission for spectator to read group
@@ -157,30 +152,27 @@ class PermissionsTest < ActionDispatch::IntegrationTest
         head_uuid: groups(:private).uuid,
         properties: {}
       }
-    }, admin_auth
+    }, auth(:admin)
     u = jresponse['uuid']
     assert_response :success
 
     # try to read collection as spectator
-    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth
+    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth(:spectator)
     assert_response :success
 
     # delete permission for spectator to read group
-    delete "/arvados/v1/links/#{u}", {:format => :json}, admin_auth
+    delete "/arvados/v1/links/#{u}", {:format => :json}, auth(:admin)
     assert_response :success
 
     # try to read collection as spectator
-    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth
+    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth(:spectator)
     assert_response 404
     
   end
 
   test "adding can_read links from user to group, group to group, group to collection" do
-    auth = {'HTTP_AUTHORIZATION' => "OAuth2 #{api_client_authorizations(:spectator).api_token}"}
-    admin_auth = {'HTTP_AUTHORIZATION' => "OAuth2 #{api_client_authorizations(:admin).api_token}"}
-    
     # try to read collection as spectator
-    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth
+    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth(:spectator)
     assert_response 404
 
     # add permission for user to read group
@@ -195,7 +187,7 @@ class PermissionsTest < ActionDispatch::IntegrationTest
         head_uuid: groups(:private).uuid,
         properties: {}
       }
-    }, admin_auth
+    }, auth(:admin)
     assert_response :success
 
     # add permission for group to read group
@@ -210,7 +202,7 @@ class PermissionsTest < ActionDispatch::IntegrationTest
         head_uuid: groups(:empty_lonely_group).uuid,
         properties: {}
       }
-    }, admin_auth
+    }, auth(:admin)
     assert_response :success
 
     # add permission for group to read collection
@@ -225,20 +217,92 @@ class PermissionsTest < ActionDispatch::IntegrationTest
         head_uuid: collections(:foo_file).uuid,
         properties: {}
       }
-    }, admin_auth
+    }, auth(:admin)
     u = jresponse['uuid']
     assert_response :success
 
     # try to read collection as spectator
-    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth
+    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth(:spectator)
     assert_response :success
 
     # delete permission for group to read collection
-    delete "/arvados/v1/links/#{u}", {:format => :json}, admin_auth
+    delete "/arvados/v1/links/#{u}", {:format => :json}, auth(:admin)
     assert_response :success
 
     # try to read collection as spectator
-    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth
+    get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth(:spectator)
+    assert_response 404
+  end
+
+  test "read-only group-admin sees correct subset of user list" do
+    get "/arvados/v1/users", {:format => :json}, auth(:rominiadmin)
+    assert_response :success
+    resp_uuids = jresponse['items'].collect { |i| i['uuid'] }
+    [[true, users(:rominiadmin).uuid],
+     [true, users(:active).uuid],
+     [false, users(:miniadmin).uuid],
+     [false, users(:spectator).uuid]].each do |should_find, uuid|
+      assert_equal should_find, !resp_uuids.index(uuid).nil?, "rominiadmin should #{'not ' if !should_find}see #{uuid} in user list"
+    end
+  end
+
+  test "read-only group-admin cannot modify administered user" do
+    put "/arvados/v1/users/#{users(:active).uuid}", {
+      :user => {
+        first_name: 'KilroyWasHere'
+      },
+      :format => :json
+    }, auth(:rominiadmin)
+    assert_response 403
+  end
+
+  test "read-only group-admin cannot read or update non-administered user" do
+    get "/arvados/v1/users/#{users(:spectator).uuid}", {
+      :format => :json
+    }, auth(:rominiadmin)
+    assert_response 404
+
+    put "/arvados/v1/users/#{users(:spectator).uuid}", {
+      :user => {
+        first_name: 'KilroyWasHere'
+      },
+      :format => :json
+    }, auth(:rominiadmin)
     assert_response 404
   end
+
+  test "RO group-admin finds user's specimens, RW group-admin can update" do
+    [[:rominiadmin, false],
+     [:miniadmin, true]].each do |which_user, update_should_succeed|
+      get "/arvados/v1/specimens", {:format => :json}, auth(which_user)
+      assert_response :success
+      resp_uuids = jresponse['items'].collect { |i| i['uuid'] }
+      [[true, specimens(:owned_by_active_user).uuid],
+       [true, specimens(:owned_by_private_group).uuid],
+       [false, specimens(:owned_by_spectator).uuid],
+      ].each do |should_find, uuid|
+        assert_equal(should_find, !resp_uuids.index(uuid).nil?,
+                     "%s should%s see %s in specimen list" %
+                     [which_user.to_s,
+                      should_find ? '' : 'not ',
+                      uuid])
+        put "/arvados/v1/specimens/#{uuid}", {
+          :specimen => {
+            properties: {
+              miniadmin_was_here: true
+            }
+          },
+          :format => :json
+        }, auth(which_user)
+        if !should_find
+          assert_response 404
+        elsif !update_should_succeed
+          assert_response 403
+        else
+          assert_response :success
+        end
+      end
+    end
+  end
+
 end

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


hooks/post-receive
-- 




More information about the arvados-commits mailing list