def self.searchable_columns operator
textonly_operator = !operator.match(/[<=>]/)
self.columns.collect do |col|
- if col.name == 'owner_uuid'
- nil
- elsif [:string, :text].index(col.type)
+ if [:string, :text].index(col.type)
col.name
elsif !textonly_operator and [:datetime, :integer].index(col.type)
col.name
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!
+
+ # 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 (?)",
+ "uuid IN (SELECT head_uuid FROM links WHERE link_class='permission' AND tail_uuid IN (#{sanitized_uuid_list}))"]
+ 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
-
- 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
+ # 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 == Log
- or_object_uuid = ", #{table_name}.object_uuid"
- or_object_owner = "OR (#{table_name}.object_owner_uuid in (#{sanitized_uuid_list}))"
+ # 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')
+ where(sql_conds.join(' OR '), *sql_params)
+ else
+ # At least one user is admin, so don't bother to apply any restrictions.
+ self
end
-
- # 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
self.class.kind
end
- def self.readable_by (u)
+ def self.readable_by (*u)
self
end