Merge branch 'origin-2608-websocket-event-bus-alt2'
authorPeter Amstutz <peter.amstutz@curoverse.com>
Wed, 30 Apr 2014 20:54:40 +0000 (16:54 -0400)
committerPeter Amstutz <peter.amstutz@curoverse.com>
Wed, 30 Apr 2014 20:54:40 +0000 (16:54 -0400)
Conflicts:
services/api/app/models/arvados_model.rb
services/api/test/test_helper.rb

1  2 
services/api/app/controllers/application_controller.rb
services/api/app/models/arvados_model.rb
services/api/test/test_helper.rb

index f83cd3428f5e644d4b68793ab3ef0c8e8d23c5f9,3891bde52e6995848877be97935db38b44355c30..4b5a27d99621d4d25e4c66fcb30581fadace4186
@@@ -1,21 -4,20 +4,22 @@@ require 'record_filters
  class ApplicationController < ActionController::Base
    include CurrentApiClient
    include ThemesForRails::ActionController
+   include LoadParam
+   include RecordFilters
  
 +  ERROR_ACTIONS = [:render_error, :render_not_found]
 +
    respond_to :json
    protect_from_forgery
  
-   around_filter :thread_with_auth_info, except: ERROR_ACTIONS
    before_filter :respond_with_json_by_default
    before_filter :remote_ip
 -  before_filter :require_auth_scope, :except => :render_not_found
 -  before_filter :catch_redirect_hint
 +  before_filter :load_read_auths
 +  before_filter :require_auth_scope, except: ERROR_ACTIONS
  
 -  before_filter :find_object_by_uuid, :except => [:index, :create,
 -                                                  :render_error,
 -                                                  :render_not_found]
 +  before_filter :catch_redirect_hint
 +  before_filter(:find_object_by_uuid,
 +                except: [:index, :create] + ERROR_ACTIONS)
    before_filter :load_limit_offset_order_params, only: [:index, :owned_items]
    before_filter :load_where_param, only: [:index, :owned_items]
    before_filter :load_filters_param, only: [:index, :owned_items]
  
    protected
  
-   def load_where_param
-     if params[:where].nil? or params[:where] == ""
-       @where = {}
-     elsif params[:where].is_a? Hash
-       @where = params[:where]
-     elsif params[:where].is_a? String
-       begin
-         @where = Oj.load(params[:where])
-         raise unless @where.is_a? Hash
-       rescue
-         raise ArgumentError.new("Could not parse \"where\" param as an object")
-       end
-     end
-     @where = @where.with_indifferent_access
-   end
-   def load_filters_param
-     @filters ||= []
-     if params[:filters].is_a? Array
-       @filters += params[:filters]
-     elsif params[:filters].is_a? String and !params[:filters].empty?
-       begin
-         f = Oj.load params[:filters]
-         raise unless f.is_a? Array
-         @filters += f
-       rescue
-         raise ArgumentError.new("Could not parse \"filters\" param as an array")
-       end
-     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+$/)
-         raise ArgumentError.new("Invalid value for limit parameter")
-       end
-       @limit = params[:limit].to_i
-     else
-       @limit = DEFAULT_LIMIT
-     end
-     if params[:offset]
-       unless params[:offset].to_s.match(/^\d+$/)
-         raise ArgumentError.new("Invalid value for offset parameter")
-       end
-       @offset = params[:offset].to_i
-     else
-       @offset = 0
-     end
-     @orders = []
-     if params[:order]
-       params[:order].split(',').each do |order|
-         attr, direction = order.strip.split " "
-         direction ||= 'asc'
-         if attr.match /^[a-z][_a-z0-9]+$/ and
-             model_class.columns.collect(&:name).index(attr) and
-             ['asc','desc'].index direction.downcase
-           @orders << "#{table_name}.#{attr} #{direction.downcase}"
-         end
-       end
-     end
-     if @orders.empty?
-       @orders = default_orders
-     end
-   end
    def find_objects_for_index
 -    @objects ||= model_class.readable_by(current_user)
 +    @objects ||= model_class.readable_by(*@read_users)
      apply_where_limit_order_params
    end
  
index f7da9df768c7cd5687f258f683cd0493ae5b624a,097b8db9df134c14a908e22667ee102d43fd84eb..1dcd9e2e82d8c2041158322f6a4b5ce7545203f9
@@@ -59,39 -59,65 +59,71 @@@ class ArvadosModel < ActiveRecord::Bas
      self.columns.select { |col| col.name == attr.to_s }.first
    end
  
--  # def eager_load_associations
--  #   self.class.columns.each do |col|
--  #     re = col.name.match /^(.*)_kind$/
--  #     if (re and
--  #         self.respond_to? re[1].to_sym and
--  #         (auuid = self.send((re[1] + '_uuid').to_sym)) and
--  #         (aclass = self.class.kind_class(self.send(col.name.to_sym))) and
--  #         (aobject = aclass.where('uuid=?', auuid).first))
--  #       self.instance_variable_set('@'+re[1], aobject)
--  #     end
--  #   end
--  # end
--
 -  def self.readable_by user
 -    if user.is_admin
 -      # Admins can read anything, so return immediately.
 -      return self
 -    end
++  # Return a query with read permissions restricted to the union of of the
++  # permissions of the members of users_list, i.e. if something is readable by
++  # any user in users_list, it will be readable in the query returned by this
++  # function.
 +  def self.readable_by(*users_list)
++    # Get rid of troublesome nils
 +    users_list.compact!
-     user_uuids = users_list.map { |u| u.uuid }
-     uuid_list = user_uuids + users_list.flat_map { |u| u.groups_i_can(:read) }
-     sanitized_uuid_list = uuid_list.
-       collect { |uuid| sanitize(uuid) }.join(', ')
-     sql_conds = []
-     sql_params = []
++
++    # Check if any of the users are admin.  If so, we're done.
 +    if users_list.select { |u| u.is_admin }.empty?
++
++      # Collect the uuids for each user and any groups readable by each user.
++      user_uuids = users_list.map { |u| u.uuid }
++      uuid_list = user_uuids + users_list.flat_map { |u| u.groups_i_can(:read) }
++      sanitized_uuid_list = uuid_list.
++        collect { |uuid| sanitize(uuid) }.join(', ')
++      sql_conds = []
++      sql_params = []
++      or_object_uuid = ''
++
++      # This row is owned by a member of users_list, or owned by a group
++      # readable by a member of users_list
++      # or
++      # This row uuid is the uuid of a member of users_list
++      # or
++      # A permission link exists ('write' and 'manage' implicitly include
++      # 'read') from a member of users_list, or a group readable by users_list,
++      # to this row, or to the owner of this row (see join() below).
 +      sql_conds += ["#{table_name}.owner_uuid in (?)",
 +                    "#{table_name}.uuid in (?)",
 +                    "permissions.head_uuid IS NOT NULL"]
 +      sql_params += [uuid_list, user_uuids]
++
 +      if self == Link and users_list.any?
++        # This row is a 'permission' or 'resources' link class
++        # The uuid for a member of users_list is referenced in either the head
++        # or tail of the link
 +        sql_conds += ["(#{table_name}.link_class in (#{sanitize 'permission'}, #{sanitize 'resources'}) AND (#{table_name}.head_uuid IN (?) OR #{table_name}.tail_uuid IN (?)))"]
 +        sql_params += [user_uuids, user_uuids]
 +      end
 -    uuid_list = [user.uuid, *user.groups_i_can(:read)]
 -    sanitized_uuid_list = uuid_list.
 -      collect { |uuid| sanitize(uuid) }.join(', ')
 -    or_references_me = ''
++      if self == Log and users_list.any?
++        # Link head points to the object described by this row
++        or_object_uuid = ", #{table_name}.object_uuid"
 -    if self == User
 -      or_row_is_me = "OR (#{table_name}.uuid=#{sanitize user.uuid})"
 -    end
++        # This object described by this row is owned by this user, or owned by a group readable by this user
++        sql_conds += ["#{table_name}.object_owner_uuid in (?)"]
++        sql_params += [uuid_list]
++      end
 -    if self == Link
 -      or_references_me = "OR (#{table_name}.link_class in (#{sanitize 'permission'}, #{sanitize 'resources'}) AND #{sanitize user.uuid} IN (#{table_name}.head_uuid, #{table_name}.tail_uuid))"
 -    end
++      # Link head points to this row, or to the owner of this row (the thing to be read)
++      #
++      # Link tail originates from this user, or a group that is readable by this
++      # user (the identity with authorization to read)
++      #
++      # Link class is 'permission' ('write' and 'manage' implicitly include 'read')
++
++      joins("LEFT JOIN links permissions ON permissions.head_uuid in (#{table_name}.owner_uuid, #{table_name}.uuid #{or_object_uuid}) AND permissions.tail_uuid in (#{sanitized_uuid_list}) AND permissions.link_class='permission'")
++        .where(sql_conds.join(' OR '), *sql_params).uniq
 -    if self == Log
 -      or_object_uuid = ", #{table_name}.object_uuid"
 -      or_object_owner = "OR (#{table_name}.object_owner_uuid in (#{sanitized_uuid_list}))"
++    else
++      # At least one user is admin, so don't bother to apply any restrictions.
++      self
      end
-     joins("LEFT JOIN links permissions ON permissions.head_uuid in (#{table_name}.owner_uuid, #{table_name}.uuid) AND permissions.tail_uuid in (#{sanitized_uuid_list}) AND permissions.link_class='permission'")
-       .where(sql_conds.join(' OR '), *sql_params)
 -    # Link head points to this row, or to the owner of this row (the thing to be read)
 -    # (or the object described by this row, for logs table only)
 -    # Link tail originates from this user, or a group that is readable by this
 -    # user (the identity with authorization to read)
 -    # Link is any permission link ('write' and 'manage' implicitly include 'read')
 -    # The existence of such a link is tested in the where clause as permissions.head_uuid IS NOT NULL.
 -    # or
 -    # This row is owned by this user, or owned by a group readable by this user
 -    # or
 -    # This is the users table
 -    # This row uuid is equal this user uuid
 -    # or
 -    # This is the links table
 -    # This row is a permission link
 -    # The current user is referenced in either the head or tail of the link
 -    # or
 -    # This is the logs table
 -    # This object described by this row is owned by this user, or owned by a group readable by this user
 -
 -    joins("LEFT JOIN links permissions ON permissions.head_uuid in (#{table_name}.owner_uuid, #{table_name}.uuid #{or_object_uuid}) AND permissions.tail_uuid in (#{sanitized_uuid_list}) AND permissions.link_class='permission'").
 -      where("permissions.head_uuid IS NOT NULL OR #{table_name}.owner_uuid in (?) #{or_row_is_me} #{or_references_me} #{or_object_owner}",
 -            uuid_list).uniq
    end
  
    def logged_attributes
index 3e81ff7432b591482ee20a0ce81af6737d821869,70988ad108ef5a9dfd4d4cfe536d944edf8ed19b..869d87f7db19f24d63743b04d410404ba4379082
@@@ -37,9 -21,15 +37,9 @@@ class ActiveSupport::TestCas
      self.request.headers["Accept"] = "text/json"
    end
  
 -  def json_response
 -    @json_response ||= ActiveSupport::JSON.decode @response.body
 -  end
 -
    def authorize_with(api_client_auth_name)
-     self.request.env['HTTP_AUTHORIZATION'] = "OAuth2 #{api_token(api_client_auth_name)}"
+     ArvadosApiToken.new.call ({"rack.input" => "", "HTTP_AUTHORIZATION" => "OAuth2 #{api_client_authorizations(api_client_auth_name).api_token}"})
    end
 -
 -  # Add more helper methods to be used by all tests here...
  end
  
  class ActionDispatch::IntegrationTest