74 lines
		
	
	
		
			2.1 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			74 lines
		
	
	
		
			2.1 KiB
		
	
	
	
		
			Ruby
		
	
	
	
# frozen_string_literal: true
 | 
						|
 | 
						|
require 'gitlab/utils/all'
 | 
						|
 | 
						|
module Support
 | 
						|
  module AbilityCheck
 | 
						|
    def self.inject(mod)
 | 
						|
      mod.prepend AbilityExtension
 | 
						|
    end
 | 
						|
 | 
						|
    module AbilityExtension
 | 
						|
      def before_check(policy, ability, user, subject, opts)
 | 
						|
        return super if Checker.ok?(policy, ability)
 | 
						|
 | 
						|
        warn(<<~WARNING)
 | 
						|
          Ability #{ability.inspect} in #{policy.class} not found.
 | 
						|
          user=#{user.inspect}, subject=#{subject}, opts=#{opts.inspect}"
 | 
						|
 | 
						|
          To exclude this check add this entry to #{Checker::TODO_YAML}:
 | 
						|
          #{policy.class}:
 | 
						|
          - #{ability}
 | 
						|
        WARNING
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    module Checker
 | 
						|
      include Gitlab::Utils::StrongMemoize
 | 
						|
      extend self
 | 
						|
 | 
						|
      TODO_YAML = File.join(__dir__, 'ability_check_todo.yml')
 | 
						|
 | 
						|
      def ok?(policy, ability)
 | 
						|
        ignored?(policy, ability) || ability_found?(policy, ability)
 | 
						|
      end
 | 
						|
 | 
						|
      private
 | 
						|
 | 
						|
      def ignored?(policy, ability)
 | 
						|
        todo_list[policy.class.name]&.include?(ability.to_s)
 | 
						|
      end
 | 
						|
 | 
						|
      # Use Policy#has_ability? instead after it has been accepted and released.
 | 
						|
      # See https://gitlab.com/gitlab-org/ruby/gems/declarative-policy/-/issues/25
 | 
						|
      def ability_found?(policy, ability)
 | 
						|
        # NilPolicy has no abilities. Ignore it.
 | 
						|
        return true if policy.is_a?(DeclarativePolicy::NilPolicy)
 | 
						|
 | 
						|
        # Search in current policy first
 | 
						|
        return true if policy.class.ability_map.map.key?(ability)
 | 
						|
 | 
						|
        # Search recursively in all delegations otherwise.
 | 
						|
        # This is potentially slow.
 | 
						|
        # Stolen from:
 | 
						|
        # https://gitlab.com/gitlab-org/ruby/gems/declarative-policy/-/blob/d691e/lib/declarative_policy/base.rb#L360-369
 | 
						|
        policy.class.delegations.any? do |_, block|
 | 
						|
          new_subject = policy.instance_eval(&block)
 | 
						|
          new_policy = policy.policy_for(new_subject)
 | 
						|
 | 
						|
          ability_found?(new_policy, ability)
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      def todo_list
 | 
						|
        hash = YAML.load_file(TODO_YAML)
 | 
						|
        return {} unless hash.is_a?(Hash)
 | 
						|
 | 
						|
        hash.transform_values(&:to_set)
 | 
						|
      end
 | 
						|
 | 
						|
      strong_memoize_attr :todo_list
 | 
						|
    end
 | 
						|
  end
 | 
						|
end
 |