diff --git a/app/controllers/api/v0/statistics_controller.rb b/app/controllers/api/v0/statistics_controller.rb index a826122..8d84bd4 100644 --- a/app/controllers/api/v0/statistics_controller.rb +++ b/app/controllers/api/v0/statistics_controller.rb @@ -47,7 +47,7 @@ def completed_plans raise Pundit::NotAuthorizedError unless Api::V0::StatisticsPolicy.new(@user, :statistics).completed_plans? - roles = Role.where("#{Role.creator_condition} OR #{Role.administrator_condition}") + roles = Role.with_access_flags(:administrator, :creator) users = User.unscoped if @user.can_super_admin? && params[:org_id].present? @@ -87,8 +87,7 @@ # Returns the number of created plans within the user's org for the data start_date and end_date specified def created_plans raise Pundit::NotAuthorizedError unless Api::V0::StatisticsPolicy.new(@user, :statistics).plans? - - roles = Role.where("#{Role.creator_condition} OR #{Role.administrator_condition}") + roles = Role.with_access_flags(:administrator, :creator) users = User.unscoped if @user.can_super_admin? && params[:org_id].present? diff --git a/app/controllers/concerns/paginable.rb b/app/controllers/concerns/paginable.rb index ef6fcc9..a2f87b5 100644 --- a/app/controllers/concerns/paginable.rb +++ b/app/controllers/concerns/paginable.rb @@ -1,6 +1,6 @@ module Paginable extend ActiveSupport::Concern - + included do # Renders paginable layout with the partial view passed # partial {String} - Represents a path to where the partial view is stored @@ -13,7 +13,7 @@ def paginable_renderise(partial: nil, controller: nil, action: nil, path_params: {}, query_params: {}, scope: nil, locals: {}, **options) raise ArgumentError, _('scope should be an ActiveRecord::Relation object') unless scope.is_a?(ActiveRecord::Relation) raise ArgumentError, _('path_params should be a Hash object') unless path_params.is_a?(Hash) - raise ArgumentError, _('query_params should be a Hash object') unless query_params.is_a?(Hash) + raise ArgumentError, _('query_params should be a Hash object') unless query_params.is_a?(Hash) raise ArgumentError, _('locals should be a Hash object') unless locals.is_a?(Hash) # Default options @@ -79,17 +79,25 @@ return 'DESC' if direction_upcased == 'ASC' return 'ASC' if direction_upcased == 'DESC' end - # Refine a scope passed to this concern if any of the params (search, sort_field or page) are present + + # Refine a scope passed to this concern if any of the params (search, + # sort_field or page) are present def refine_query(scope) - scope = scope.search(@paginable_params[:search]) if @paginable_params[:search].present? # Can raise NoMethodError if the scope does not define a search method + scope = scope.search(@paginable_params[:search]) if @paginable_params[:search].present? + # Can raise NoMethodError if the scope does not define a search method if @paginable_params[:sort_field].present? - scope = scope.order("#{@paginable_params[:sort_field]} #{upcasing_sort_direction}") # Can raise ActiveRecord::StatementInvalid (e.g. column does not exist, ambiguity on column, etc) + # Can raise ActiveRecord::StatementInvalid (e.g. column does not + # exist, ambiguity on column, etc) + order_sql = ActiveRecord::Base.sanitize("#{@paginable_params[:sort_field]} #{upcasing_sort_direction}") + scope = scope.order(order_sql) end if @paginable_params[:page] != 'ALL' - scope = scope.page(@paginable_params[:page]) # Can raise error if page is not a number + # Can raise error if page is not a number + scope = scope.page(@paginable_params[:page]) end return scope end + # Returns the sort link name for a given sort_field. The link name includes html prevented of being escaped def sort_link_name(sort_field) className = 'fa-sort' diff --git a/app/models/role.rb b/app/models/role.rb index b7cdf28..e48bc58 100644 --- a/app/models/role.rb +++ b/app/models/role.rb @@ -4,7 +4,7 @@ ## # Associationsrequire "role" - + belongs_to :user belongs_to :plan @@ -22,6 +22,21 @@ validates :access, numericality: {greater_than: 0, message: _("can't be less than zero")} ## + # Roles with given FlagShihTzu access flags + # + # @param flags [Array] One or more symbols that represent access flags + # + # @return [ActiveRecord::Relation] + scope :with_access_flags, -> (*flags) { + bad_flag = flags.detect { |flag| !flag.in?(flag_mapping['access'].keys) } + raise ArgumentError, "Unkown access flag '#{bad_flag}'" if bad_flag + access_values = flags.map { |flag| sql_in_for_flag(flag.to_sym, 'access') } + .flatten + .uniq + where(access: access_values) + } + + ## # return the access level for the current project group # 5 if the user is a reviewer # 3 if the user is an administrator diff --git a/app/models/user.rb b/app/models/user.rb index 619c714..780e610 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -290,7 +290,7 @@ # @param val [string] The string to search for, case insensitive. val is duck typed to check whether or not downcase method exist # @return [ActiveRecord::Relation] The result of the search def self.where_case_insensitive(field, val) - User.where("lower(#{field}) = ?", val.respond_to?(:downcase) ? val.downcase : val.to_s) + User.where(field.to_sym => val.to_s.downcase) end # Acknoledge a Notification diff --git a/test/unit/user_test.rb b/test/unit/user_test.rb index 9a1dbce..6f34a9c 100644 --- a/test/unit/user_test.rb +++ b/test/unit/user_test.rb @@ -17,12 +17,12 @@ language: Language.find_by(abbreviation: I18n.locale)) @notification = Notification.create!( - notification_type: Notification.notification_types[:global], - title: 'notification_1', + notification_type: Notification.notification_types[:global], + title: 'notification_1', level: Notification.levels[:info], - body: 'notification 1', - dismissable: false, - starts_at: Date.today, + body: 'notification 1', + dismissable: false, + starts_at: Date.today, expires_at: Date.tomorrow) end @@ -343,7 +343,7 @@ test "after_save removes API token and its perms associated" do previous_api_token = @user.api_token @user.perms = [Perm.add_orgs, Perm.grant_permissions] - previous_perms = @user.perms.to_a + previous_perms = @user.perms.to_a @user.org = Org.where.not(id: @user.org_id).first @user.save assert_not_equal(previous_api_token, @user.api_token)