[ARVADOS] updated: 03e570095885982d23e234bce8e1c068314b63af

git at public.curoverse.com git at public.curoverse.com
Mon Apr 28 14:07:56 EDT 2014


Summary of changes:
 .../api/app/controllers/application_controller.rb  |   17 ++--
 .../v1/api_client_authorizations_controller.rb     |   32 +++++--
 .../arvados/v1/keep_disks_controller.rb            |    2 +-
 .../app/controllers/arvados/v1/nodes_controller.rb |    2 +-
 .../controllers/arvados/v1/schema_controller.rb    |    6 +-
 .../arvados/v1/virtual_machines_controller.rb      |   16 ---
 services/api/app/controllers/static_controller.rb  |    2 +-
 .../app/controllers/user_sessions_controller.rb    |    2 +-
 .../db/migrate/20140423133559_new_scope_format.rb  |   48 +++++++++
 services/api/db/schema.rb                          |    2 +-
 services/api/lib/current_api_client.rb             |   19 ++--
 .../test/fixtures/api_client_authorizations.yml    |   37 +++++++
 .../api_client_authorizations_controller_test.rb   |   30 ++++++-
 .../api_client_authorizations_scopes_test.rb       |  103 ++++++++++++++++++++
 14 files changed, 270 insertions(+), 48 deletions(-)
 create mode 100644 services/api/db/migrate/20140423133559_new_scope_format.rb
 create mode 100644 services/api/test/integration/api_client_authorizations_scopes_test.rb

       via  03e570095885982d23e234bce8e1c068314b63af (commit)
       via  5c2758ca38d01a905253f86e63be3a5fe03a3871 (commit)
       via  52c4f2b7fe631f3d7ad16105cb2f86cf6c004fc8 (commit)
       via  e54bdba73b65e31b03fd1d43bfe69d0f43bbd8d8 (commit)
       via  0eb59e3acf9f13e89bd010f7f65a4d31554183fc (commit)
       via  915de6c854cd559ebd029b24939149e37b18c8cf (commit)
      from  71b1b7b045419817d1c9dc62a3a296b746d9117c (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.


commit 03e570095885982d23e234bce8e1c068314b63af
Merge: 71b1b7b 5c2758c
Author: Brett Smith <brett at curoverse.com>
Date:   Mon Apr 28 14:07:02 2014 -0400

    Merge branch '1904-object-scopes'
    
    Closes #2642, #2662, #2682.


commit 5c2758ca38d01a905253f86e63be3a5fe03a3871
Author: Brett Smith <brett at curoverse.com>
Date:   Mon Apr 28 13:01:18 2014 -0400

    api: Support filters in API client auths index.
    
    Per comments on Refs #1904.  filters is generally the preferred way to
    do searching now.  I maintained existing limits on what can be
    searched with this method.

diff --git a/services/api/app/controllers/application_controller.rb b/services/api/app/controllers/application_controller.rb
index a86e35b..b091397 100644
--- a/services/api/app/controllers/application_controller.rb
+++ b/services/api/app/controllers/application_controller.rb
@@ -207,6 +207,10 @@ class ApplicationController < ActionController::Base
     end
   end
 
+  def default_orders
+    ["#{table_name}.modified_at desc"]
+  end
+
   def load_limit_offset_order_params
     if params[:limit]
       unless params[:limit].to_s.match(/^\d+$/)
@@ -239,7 +243,7 @@ class ApplicationController < ActionController::Base
       end
     end
     if @orders.empty?
-      @orders << "#{table_name}.modified_at desc"
+      @orders = default_orders
     end
   end
 
diff --git a/services/api/app/controllers/arvados/v1/api_client_authorizations_controller.rb b/services/api/app/controllers/arvados/v1/api_client_authorizations_controller.rb
index ff322a7..dc95b2f 100644
--- a/services/api/app/controllers/arvados/v1/api_client_authorizations_controller.rb
+++ b/services/api/app/controllers/arvados/v1/api_client_authorizations_controller.rb
@@ -34,21 +34,34 @@ class Arvados::V1::ApiClientAuthorizationsController < ApplicationController
 
   protected
 
+  def default_orders
+    ["#{table_name}.created_at desc"]
+  end
+
   def find_objects_for_index
     # Here we are deliberately less helpful about searching for client
-    # authorizations. Rather than use the generic index/where/order
-    # features, we look up tokens belonging to the current user and
-    # filter by exact match on api_token (which we expect in the form
-    # of a where[uuid] parameter to make things easier for API client
-    # libraries).
+    # authorizations.  We look up tokens belonging to the current user
+    # and filter by exact matches on api_token and scopes.
+    wanted_scopes = []
+    if @filters
+      wanted_scopes.concat(@filters.map { |attr, operator, operand|
+        ((attr == 'scopes') and (operator == '=')) ? operand : nil
+      })
+      @filters.select! { |attr, operator, operand|
+        (attr == 'uuid') and (operator == '=')
+      }
+    end
+    if @where
+      wanted_scopes << @where['scopes']
+      @where.select! { |attr, val| attr == 'uuid' }
+    end
     @objects = model_class.
       includes(:user, :api_client).
-      where('user_id=? and (? or api_token=?)', current_user.id, !@where['uuid'], @where['uuid']).
-      order('created_at desc')
-    unless @where['scopes'].nil?
-      @objects = @objects.select { |auth|
-        (auth.scopes & @where['scopes']) == (auth.scopes | @where['scopes'])
-      }
+      where('user_id=?', current_user.id)
+    super
+    wanted_scopes.compact.each do |scope_list|
+      sorted_scopes = scope_list.sort
+      @objects = @objects.select { |auth| auth.scopes.sort == sorted_scopes }
     end
   end
 
diff --git a/services/api/test/functional/arvados/v1/api_client_authorizations_controller_test.rb b/services/api/test/functional/arvados/v1/api_client_authorizations_controller_test.rb
index 0072792..8877719 100644
--- a/services/api/test/functional/arvados/v1/api_client_authorizations_controller_test.rb
+++ b/services/api/test/functional/arvados/v1/api_client_authorizations_controller_test.rb
@@ -37,22 +37,33 @@ class Arvados::V1::ApiClientAuthorizationsControllerTest < ActionController::Tes
     assert_response 403
   end
 
-  test "admin search filters where scopes exactly match" do
-    def check_tokens_by_scopes(scopes, *expected_tokens)
-      expected_tokens.map! { |name| api_client_authorizations(name).api_token }
-      get :index, where: {scopes: scopes}
-      assert_response :success
-      got_tokens = JSON.parse(@response.body)['items']
-        .map { |auth| auth['api_token'] }
-      assert_equal(expected_tokens.sort, got_tokens.sort,
-                   "wrong results for scopes = #{scopes}")
+  def assert_found_tokens(auth, search_params, *expected_tokens)
+    authorize_with auth
+    expected_tokens.map! { |name| api_client_authorizations(name).api_token }
+    get :index, search_params
+    assert_response :success
+    got_tokens = JSON.parse(@response.body)['items']
+      .map { |auth| auth['api_token'] }
+    assert_equal(expected_tokens.sort, got_tokens.sort,
+                 "wrong results for #{search_params.inspect}")
+  end
+
+  # Three-tuples with auth to use, scopes to find, and expected tokens.
+  # Make two tests for each tuple, one searching with where and the other
+  # with filter.
+  [[:admin_trustedclient, [], :admin_noscope],
+   [:active_trustedclient, ["GET /arvados/v1/users"], :active_userlist],
+   [:active_trustedclient,
+    ["POST /arvados/v1/api_client_authorizations",
+     "GET /arvados/v1/api_client_authorizations"],
+    :active_apitokens],
+  ].each do |auth, scopes, *expected|
+    test "#{auth.to_s} can find auths where scopes=#{scopes.inspect}" do
+      assert_found_tokens(auth, {where: {scopes: scopes}}, *expected)
+    end
+
+    test "#{auth.to_s} can find auths filtered with scopes=#{scopes.inspect}" do
+      assert_found_tokens(auth, {filters: [['scopes', '=', scopes]]}, *expected)
     end
-    authorize_with :admin_trustedclient
-    check_tokens_by_scopes([], :admin_noscope)
-    authorize_with :active_trustedclient
-    check_tokens_by_scopes(["GET /arvados/v1/users"], :active_userlist)
-    check_tokens_by_scopes(["POST /arvados/v1/api_client_authorizations",
-                            "GET /arvados/v1/api_client_authorizations"],
-                           :active_apitokens)
   end
 end

commit 52c4f2b7fe631f3d7ad16105cb2f86cf6c004fc8
Author: Brett Smith <brett at curoverse.com>
Date:   Wed Apr 23 16:15:37 2014 -0400

    api: Support scope searching in API token index.

diff --git a/services/api/app/controllers/arvados/v1/api_client_authorizations_controller.rb b/services/api/app/controllers/arvados/v1/api_client_authorizations_controller.rb
index 8fd915d..ff322a7 100644
--- a/services/api/app/controllers/arvados/v1/api_client_authorizations_controller.rb
+++ b/services/api/app/controllers/arvados/v1/api_client_authorizations_controller.rb
@@ -45,6 +45,11 @@ class Arvados::V1::ApiClientAuthorizationsController < ApplicationController
       includes(:user, :api_client).
       where('user_id=? and (? or api_token=?)', current_user.id, !@where['uuid'], @where['uuid']).
       order('created_at desc')
+    unless @where['scopes'].nil?
+      @objects = @objects.select { |auth|
+        (auth.scopes & @where['scopes']) == (auth.scopes | @where['scopes'])
+      }
+    end
   end
 
   def find_object_by_uuid
diff --git a/services/api/test/functional/arvados/v1/api_client_authorizations_controller_test.rb b/services/api/test/functional/arvados/v1/api_client_authorizations_controller_test.rb
index cbb0096..0072792 100644
--- a/services/api/test/functional/arvados/v1/api_client_authorizations_controller_test.rb
+++ b/services/api/test/functional/arvados/v1/api_client_authorizations_controller_test.rb
@@ -1,7 +1,6 @@
 require 'test_helper'
 
 class Arvados::V1::ApiClientAuthorizationsControllerTest < ActionController::TestCase
-
   test "should get index" do
     authorize_with :active_trustedclient
     get :index
@@ -38,4 +37,22 @@ class Arvados::V1::ApiClientAuthorizationsControllerTest < ActionController::Tes
     assert_response 403
   end
 
+  test "admin search filters where scopes exactly match" do
+    def check_tokens_by_scopes(scopes, *expected_tokens)
+      expected_tokens.map! { |name| api_client_authorizations(name).api_token }
+      get :index, where: {scopes: scopes}
+      assert_response :success
+      got_tokens = JSON.parse(@response.body)['items']
+        .map { |auth| auth['api_token'] }
+      assert_equal(expected_tokens.sort, got_tokens.sort,
+                   "wrong results for scopes = #{scopes}")
+    end
+    authorize_with :admin_trustedclient
+    check_tokens_by_scopes([], :admin_noscope)
+    authorize_with :active_trustedclient
+    check_tokens_by_scopes(["GET /arvados/v1/users"], :active_userlist)
+    check_tokens_by_scopes(["POST /arvados/v1/api_client_authorizations",
+                            "GET /arvados/v1/api_client_authorizations"],
+                           :active_apitokens)
+  end
 end

commit e54bdba73b65e31b03fd1d43bfe69d0f43bbd8d8
Author: Brett Smith <brett at curoverse.com>
Date:   Mon Apr 28 14:01:53 2014 -0400

    api: Migrate VM auth scopes to new system.
    
    VirtualMachinesController was the only one doing anything special with
    API token scopes before we provided the more general-purpose system.
    This commit removes its specialized code, and provides a database
    migration to convert those specialized scopes to the general-purpose
    schema.

diff --git a/services/api/app/controllers/arvados/v1/virtual_machines_controller.rb b/services/api/app/controllers/arvados/v1/virtual_machines_controller.rb
index 10b4bd8..e176348 100644
--- a/services/api/app/controllers/arvados/v1/virtual_machines_controller.rb
+++ b/services/api/app/controllers/arvados/v1/virtual_machines_controller.rb
@@ -1,12 +1,8 @@
 class Arvados::V1::VirtualMachinesController < ApplicationController
   skip_before_filter :find_object_by_uuid, :only => :get_all_logins
   skip_before_filter :render_404_if_no_object, :only => :get_all_logins
-  skip_before_filter(:require_auth_scope_all,
-                     :only => [:logins, :get_all_logins])
   before_filter(:admin_required,
                 :only => [:logins, :get_all_logins])
-  before_filter(:require_auth_scope_for_get_all_logins,
-                :only => [:logins, :get_all_logins])
 
   def logins
     get_all_logins
@@ -44,16 +40,4 @@ class Arvados::V1::VirtualMachinesController < ApplicationController
     end
     render json: { kind: "arvados#HashList", items: @response }
   end
-
-  protected
-
-  def require_auth_scope_for_get_all_logins
-    if @object
-      # Client wants all logins for a single VM.
-      require_auth_scope(['all', arvados_v1_virtual_machine_url(@object.uuid)])
-    else
-      # ...for a non-existent VM, or all VMs.
-      require_auth_scope(['all'])
-    end
-  end
 end
diff --git a/services/api/db/migrate/20140423133559_new_scope_format.rb b/services/api/db/migrate/20140423133559_new_scope_format.rb
new file mode 100644
index 0000000..5b69e95
--- /dev/null
+++ b/services/api/db/migrate/20140423133559_new_scope_format.rb
@@ -0,0 +1,48 @@
+# At the time we introduced scopes everywhere, VirtualMachinesController
+# recognized scopes that gave the URL for a VM to grant access to that VM's
+# login list.  This migration converts those VM-specific scopes to the new
+# general format, and back.
+
+class NewScopeFormat < ActiveRecord::Migration
+  include CurrentApiClient
+
+  VM_PATH_REGEX =
+    %r{(/arvados/v1/virtual_machines/[0-9a-z]{5}-[0-9a-z]{5}-[0-9a-z]{15})}
+  OLD_SCOPE_REGEX = %r{^https?://[^/]+#{VM_PATH_REGEX.source}$}
+  NEW_SCOPE_REGEX = %r{^GET #{VM_PATH_REGEX.source}/logins$}
+
+  def fix_scopes_matching(regex)
+    act_as_system_user
+    ApiClientAuthorization.find_each do |auth|
+      auth.scopes = auth.scopes.map do |scope|
+        if match = regex.match(scope)
+          yield match
+        else
+          scope
+        end
+      end
+      auth.save!
+    end
+  end
+
+  def up
+    fix_scopes_matching(OLD_SCOPE_REGEX) do |match|
+      "GET #{match[1]}/logins"
+    end
+  end
+
+  def down
+    case Rails.env
+    when 'test'
+      hostname = 'www.example.com'
+    else
+      require 'socket'
+      hostname = Socket.gethostname
+    end
+    fix_scopes_matching(NEW_SCOPE_REGEX) do |match|
+      Rails.application.routes.url_for(controller: 'virtual_machines',
+                                       uuid: match[1].split('/').last,
+                                       host: hostname, protocol: 'https')
+    end
+  end
+end
diff --git a/services/api/db/schema.rb b/services/api/db/schema.rb
index 988cb87..034ee35 100644
--- a/services/api/db/schema.rb
+++ b/services/api/db/schema.rb
@@ -11,7 +11,7 @@
 #
 # It's strongly recommended to check this file into your version control system.
 
-ActiveRecord::Schema.define(:version => 20140422011506) do
+ActiveRecord::Schema.define(:version => 20140423133559) do
 
   create_table "api_client_authorizations", :force => true do |t|
     t.string   "api_token",                                           :null => false
diff --git a/services/api/test/fixtures/api_client_authorizations.yml b/services/api/test/fixtures/api_client_authorizations.yml
index 77e5048..f772c4f 100644
--- a/services/api/test/fixtures/api_client_authorizations.yml
+++ b/services/api/test/fixtures/api_client_authorizations.yml
@@ -42,7 +42,7 @@ admin_vm:
   api_token: adminvirtualmachineabcdefghijklmnopqrstuvwxyz12345
   expires_at: 2038-01-01 00:00:00
   # scope refers to the testvm fixture.
-  scopes: ["https://www.example.com/arvados/v1/virtual_machines/zzzzz-2x53u-382brsig8rp3064"]
+  scopes: ["GET /arvados/v1/virtual_machines/zzzzz-2x53u-382brsig8rp3064/logins"]
 
 admin_noscope:
   api_client: untrusted

commit 0eb59e3acf9f13e89bd010f7f65a4d31554183fc
Author: Brett Smith <brett at curoverse.com>
Date:   Tue Apr 22 17:45:46 2014 -0400

    api: Introduce path-based API token scopes.
    
    Refs #1904, #2662 for background discussion.

diff --git a/services/api/app/controllers/application_controller.rb b/services/api/app/controllers/application_controller.rb
index 713f2cf..a86e35b 100644
--- a/services/api/app/controllers/application_controller.rb
+++ b/services/api/app/controllers/application_controller.rb
@@ -7,7 +7,7 @@ class ApplicationController < ActionController::Base
   around_filter :thread_with_auth_info, :except => [:render_error, :render_not_found]
 
   before_filter :remote_ip
-  before_filter :require_auth_scope_all, :except => :render_not_found
+  before_filter :require_auth_scope, :except => :render_not_found
   before_filter :catch_redirect_hint
 
   before_filter :find_object_by_uuid, :except => [:index, :create,
@@ -405,12 +405,9 @@ class ApplicationController < ActionController::Base
     end
   end
 
-  def require_auth_scope_all
-    require_login and require_auth_scope(['all'])
-  end
-
-  def require_auth_scope(ok_scopes)
-    unless current_api_client_auth_has_scope(ok_scopes)
+  def require_auth_scope
+    return false unless require_login
+    unless current_api_client_auth_has_scope("#{request.method} #{request.path}")
       render :json => { errors: ['Forbidden'] }.to_json, status: 403
     end
   end
diff --git a/services/api/app/controllers/arvados/v1/keep_disks_controller.rb b/services/api/app/controllers/arvados/v1/keep_disks_controller.rb
index 3d91916..47018d4 100644
--- a/services/api/app/controllers/arvados/v1/keep_disks_controller.rb
+++ b/services/api/app/controllers/arvados/v1/keep_disks_controller.rb
@@ -1,5 +1,5 @@
 class Arvados::V1::KeepDisksController < ApplicationController
-  skip_before_filter :require_auth_scope_all, :only => :ping
+  skip_before_filter :require_auth_scope, :only => :ping
 
   def self._ping_requires_parameters
     {
diff --git a/services/api/app/controllers/arvados/v1/nodes_controller.rb b/services/api/app/controllers/arvados/v1/nodes_controller.rb
index eda8b07..d7a477d 100644
--- a/services/api/app/controllers/arvados/v1/nodes_controller.rb
+++ b/services/api/app/controllers/arvados/v1/nodes_controller.rb
@@ -1,5 +1,5 @@
 class Arvados::V1::NodesController < ApplicationController
-  skip_before_filter :require_auth_scope_all, :only => :ping
+  skip_before_filter :require_auth_scope, :only => :ping
   skip_before_filter :find_object_by_uuid, :only => :ping
   skip_before_filter :render_404_if_no_object, :only => :ping
 
diff --git a/services/api/app/controllers/arvados/v1/schema_controller.rb b/services/api/app/controllers/arvados/v1/schema_controller.rb
index 1cc8496..625519e 100644
--- a/services/api/app/controllers/arvados/v1/schema_controller.rb
+++ b/services/api/app/controllers/arvados/v1/schema_controller.rb
@@ -2,7 +2,7 @@ class Arvados::V1::SchemaController < ApplicationController
   skip_before_filter :find_objects_for_index
   skip_before_filter :find_object_by_uuid
   skip_before_filter :render_404_if_no_object
-  skip_before_filter :require_auth_scope_all
+  skip_before_filter :require_auth_scope
 
   def index
     expires_in 24.hours, public: true
@@ -69,7 +69,7 @@ class Arvados::V1::SchemaController < ApplicationController
         schemas: {},
         resources: {}
       }
-      
+
       ActiveRecord::Base.descendants.reject(&:abstract_class?).each do |k|
         begin
           ctl_class = "Arvados::V1::#{k.to_s.pluralize}Controller".constantize
@@ -175,7 +175,7 @@ class Arvados::V1::SchemaController < ApplicationController
               description:
                  %|List #{k.to_s.pluralize}.
 
-                   The <code>list</code> method returns a 
+                   The <code>list</code> method returns a
                    <a href="/api/resources.html">resource list</a> of
                    matching #{k.to_s.pluralize}. For example:
 
diff --git a/services/api/app/controllers/static_controller.rb b/services/api/app/controllers/static_controller.rb
index fda0880..c71b850 100644
--- a/services/api/app/controllers/static_controller.rb
+++ b/services/api/app/controllers/static_controller.rb
@@ -3,7 +3,7 @@ class StaticController < ApplicationController
 
   skip_before_filter :find_object_by_uuid
   skip_before_filter :render_404_if_no_object
-  skip_before_filter :require_auth_scope_all, :only => [ :home, :login_failure ]
+  skip_before_filter :require_auth_scope, :only => [ :home, :login_failure ]
 
   def home
     if Rails.configuration.respond_to? :workbench_address
diff --git a/services/api/app/controllers/user_sessions_controller.rb b/services/api/app/controllers/user_sessions_controller.rb
index a7391bd..3d4b05a 100644
--- a/services/api/app/controllers/user_sessions_controller.rb
+++ b/services/api/app/controllers/user_sessions_controller.rb
@@ -1,5 +1,5 @@
 class UserSessionsController < ApplicationController
-  before_filter :require_auth_scope_all, :only => [ :destroy ]
+  before_filter :require_auth_scope, :only => [ :destroy ]
 
   skip_before_filter :find_object_by_uuid
   skip_before_filter :render_404_if_no_object
diff --git a/services/api/lib/current_api_client.rb b/services/api/lib/current_api_client.rb
index bbba4dc..0803d54 100644
--- a/services/api/lib/current_api_client.rb
+++ b/services/api/lib/current_api_client.rb
@@ -29,14 +29,17 @@ module CurrentApiClient
     Thread.current[:api_client_ip_address]
   end
 
-  # Does the current API client authorization include any of ok_scopes?
-  def current_api_client_auth_has_scope(ok_scopes)
-    auth_scopes = current_api_client_authorization.andand.scopes || []
-    unless auth_scopes.index('all') or (auth_scopes & ok_scopes).any?
-      logger.warn "Insufficient auth scope: need #{ok_scopes}, #{current_api_client_authorization.inspect} has #{auth_scopes}"
-      return false
-    end
-    true
+  # Is the current API client authorization scoped for the request?
+  def current_api_client_auth_has_scope(req_s)
+    (current_api_client_authorization.andand.scopes || []).select { |scope|
+      if scope == 'all'
+        true
+      elsif scope.end_with? '/'
+        req_s.start_with? scope
+      else
+        req_s == scope
+      end
+    }.any?
   end
 
   def system_user_uuid
diff --git a/services/api/test/fixtures/api_client_authorizations.yml b/services/api/test/fixtures/api_client_authorizations.yml
index 5a715e3..77e5048 100644
--- a/services/api/test/fixtures/api_client_authorizations.yml
+++ b/services/api/test/fixtures/api_client_authorizations.yml
@@ -51,6 +51,28 @@ admin_noscope:
   expires_at: 2038-01-01 00:00:00
   scopes: []
 
+active_userlist:
+  api_client: untrusted
+  user: active
+  api_token: activeuserlistabcdefghijklmnopqrstuvwxyz1234568900
+  expires_at: 2038-01-01 00:00:00
+  scopes: ["GET /arvados/v1/users"]
+
+active_specimens:
+  api_client: untrusted
+  user: active
+  api_token: activespecimensabcdefghijklmnopqrstuvwxyz123456890
+  expires_at: 2038-01-01 00:00:00
+  scopes: ["GET /arvados/v1/specimens/"]
+
+active_apitokens:
+  api_client: trusted_workbench
+  user: active
+  api_token: activeapitokensabcdefghijklmnopqrstuvwxyz123456789
+  expires_at: 2038-01-01 00:00:00
+  scopes: ["GET /arvados/v1/api_client_authorizations",
+           "POST /arvados/v1/api_client_authorizations"]
+
 spectator:
   api_client: untrusted
   user: spectator
diff --git a/services/api/test/integration/api_client_authorizations_scopes_test.rb b/services/api/test/integration/api_client_authorizations_scopes_test.rb
index 7269d38..ba91670 100644
--- a/services/api/test/integration/api_client_authorizations_scopes_test.rb
+++ b/services/api/test/integration/api_client_authorizations_scopes_test.rb
@@ -20,7 +20,6 @@ class Arvados::V1::ApiTokensScopeTest < ActionController::IntegrationTest
   end
 
   def request_with_auth(method, path, params={})
-    https!
     send(method, path, @token.merge(params))
   end
 
@@ -32,6 +31,51 @@ class Arvados::V1::ApiTokensScopeTest < ActionController::IntegrationTest
     request_with_auth(:post_via_redirect, *args)
   end
 
+  test "user list token can only list users" do
+    auth_with :active_userlist
+    get_with_auth v1_url('users')
+    assert_response :success
+    get_with_auth v1_url('users', '')  # Add trailing slash.
+    assert_response :success
+    get_with_auth v1_url('users', 'current')
+    assert_response 403
+    get_with_auth v1_url('virtual_machines')
+    assert_response 403
+  end
+
+  test "specimens token can see exactly owned specimens" do
+    auth_with :active_specimens
+    get_with_auth v1_url('specimens')
+    assert_response 403
+    get_with_auth v1_url('specimens', specimens(:owned_by_active_user).uuid)
+    assert_response :success
+    get_with_auth v1_url('specimens', specimens(:owned_by_spectator).uuid)
+    assert_includes(403..404, @response.status)
+  end
+
+  test "token with multiple scopes can use them all" do
+    def get_token_count
+      get_with_auth v1_url('api_client_authorizations')
+      assert_response :success
+      token_count = JSON.parse(@response.body)['items_available']
+      assert_not_nil(token_count, "could not find token count")
+      token_count
+    end
+    auth_with :active_apitokens
+    # Test the GET scope.
+    token_count = get_token_count
+    # Test the POST scope.
+    post_with_auth(v1_url('api_client_authorizations'),
+                   api_client_authorization: {user_id: users(:active).id})
+    assert_response :success
+    assert_equal(token_count + 1, get_token_count,
+                 "token count suggests POST was not accepted")
+    # Test other requests are denied.
+    get_with_auth v1_url('api_client_authorizations',
+                         api_client_authorizations(:active_apitokens).uuid)
+    assert_response 403
+  end
+
   test "token without scope has no access" do
     # Logs are good for this test, because logs have relatively
     # few access controls enforced at the model level.

commit 915de6c854cd559ebd029b24939149e37b18c8cf
Author: Brett Smith <brett at curoverse.com>
Date:   Tue Apr 22 14:44:34 2014 -0400

    api: Test VM login scopes.
    
    The virtual machine controller is the only one doing anything
    interesting with API token scopes right now.  I'm writing this test
    for that functionality to make sure it stays effective through
    refactoring.

diff --git a/services/api/test/fixtures/api_client_authorizations.yml b/services/api/test/fixtures/api_client_authorizations.yml
index 5cada90..5a715e3 100644
--- a/services/api/test/fixtures/api_client_authorizations.yml
+++ b/services/api/test/fixtures/api_client_authorizations.yml
@@ -36,6 +36,21 @@ active_trustedclient:
   api_token: 27bnddk6x2nmq00a1e3gq43n9tsl5v87a3faqar2ijj8tud5en
   expires_at: 2038-01-01 00:00:00
 
+admin_vm:
+  api_client: untrusted
+  user: admin
+  api_token: adminvirtualmachineabcdefghijklmnopqrstuvwxyz12345
+  expires_at: 2038-01-01 00:00:00
+  # scope refers to the testvm fixture.
+  scopes: ["https://www.example.com/arvados/v1/virtual_machines/zzzzz-2x53u-382brsig8rp3064"]
+
+admin_noscope:
+  api_client: untrusted
+  user: admin
+  api_token: adminnoscopeabcdefghijklmnopqrstuvwxyz123456789012
+  expires_at: 2038-01-01 00:00:00
+  scopes: []
+
 spectator:
   api_client: untrusted
   user: spectator
diff --git a/services/api/test/integration/api_client_authorizations_scopes_test.rb b/services/api/test/integration/api_client_authorizations_scopes_test.rb
new file mode 100644
index 0000000..7269d38
--- /dev/null
+++ b/services/api/test/integration/api_client_authorizations_scopes_test.rb
@@ -0,0 +1,59 @@
+# The v1 API uses token scopes to control access to the REST API at the path
+# level.  This is enforced in the base ApplicationController, making it a
+# functional test that we can run against many different controllers.
+
+require 'test_helper'
+
+class Arvados::V1::ApiTokensScopeTest < ActionController::IntegrationTest
+  fixtures :all
+
+  def setup
+    @token = {}
+  end
+
+  def auth_with(name)
+    @token = {api_token: api_client_authorizations(name).api_token}
+  end
+
+  def v1_url(*parts)
+    (['arvados', 'v1'] + parts).join('/')
+  end
+
+  def request_with_auth(method, path, params={})
+    https!
+    send(method, path, @token.merge(params))
+  end
+
+  def get_with_auth(*args)
+    request_with_auth(:get_via_redirect, *args)
+  end
+
+  def post_with_auth(*args)
+    request_with_auth(:post_via_redirect, *args)
+  end
+
+  test "token without scope has no access" do
+    # Logs are good for this test, because logs have relatively
+    # few access controls enforced at the model level.
+    auth_with :admin_noscope
+    get_with_auth v1_url('logs')
+    assert_response 403
+    get_with_auth v1_url('logs', logs(:log1).uuid)
+    assert_response 403
+    post_with_auth(v1_url('logs'), log: {})
+    assert_response 403
+  end
+
+  test "VM login scopes work" do
+    # A system administration script makes an API token with limited scope
+    # for virtual machines to let it see logins.
+    def vm_logins_url(name)
+      v1_url('virtual_machines', virtual_machines(name).uuid, 'logins')
+    end
+    auth_with :admin_vm
+    get_with_auth vm_logins_url(:testvm)
+    assert_response :success
+    get_with_auth vm_logins_url(:testvm2)
+    assert(@response.status >= 400, "getting testvm2 logins should have failed")
+  end
+end

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


hooks/post-receive
-- 




More information about the arvados-commits mailing list