action_policy 0.5.0 → 0.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/CHANGELOG.md +13 -2
 - data/config/rubocop-rspec.yml +17 -0
 - data/lib/.rbnext/1995.next/action_policy/behaviours/policy_for.rb +62 -0
 - data/lib/.rbnext/1995.next/action_policy/behaviours/scoping.rb +35 -0
 - data/lib/.rbnext/1995.next/action_policy/policy/authorization.rb +87 -0
 - data/lib/.rbnext/1995.next/action_policy/utils/pretty_print.rb +159 -0
 - data/lib/.rbnext/2.7/action_policy/behaviours/policy_for.rb +62 -0
 - data/lib/.rbnext/2.7/action_policy/i18n.rb +56 -0
 - data/lib/.rbnext/2.7/action_policy/policy/cache.rb +101 -0
 - data/lib/.rbnext/2.7/action_policy/policy/pre_check.rb +162 -0
 - data/lib/.rbnext/2.7/action_policy/rspec/be_authorized_to.rb +89 -0
 - data/lib/.rbnext/2.7/action_policy/rspec/have_authorized_scope.rb +124 -0
 - data/lib/.rbnext/2.7/action_policy/utils/pretty_print.rb +159 -0
 - data/lib/.rbnext/3.0/action_policy/behaviours/thread_memoized.rb +59 -0
 - data/lib/.rbnext/3.0/action_policy/ext/policy_cache_key.rb +72 -0
 - data/lib/.rbnext/3.0/action_policy/policy/aliases.rb +69 -0
 - data/lib/.rbnext/3.0/action_policy/policy/cache.rb +101 -0
 - data/lib/.rbnext/3.0/action_policy/policy/core.rb +161 -0
 - data/lib/.rbnext/3.0/action_policy/policy/defaults.rb +31 -0
 - data/lib/.rbnext/3.0/action_policy/policy/execution_result.rb +37 -0
 - data/lib/.rbnext/3.0/action_policy/policy/pre_check.rb +162 -0
 - data/lib/.rbnext/3.0/action_policy/policy/reasons.rb +212 -0
 - data/lib/.rbnext/3.0/action_policy/rspec/be_authorized_to.rb +89 -0
 - data/lib/.rbnext/3.0/action_policy/rspec/have_authorized_scope.rb +124 -0
 - data/lib/.rbnext/3.0/action_policy/utils/pretty_print.rb +159 -0
 - data/lib/.rbnext/3.0/action_policy/utils/suggest_message.rb +19 -0
 - data/lib/action_policy/behaviour.rb +2 -2
 - data/lib/action_policy/behaviours/memoized.rb +1 -1
 - data/lib/action_policy/behaviours/namespaced.rb +1 -1
 - data/lib/action_policy/behaviours/policy_for.rb +1 -1
 - data/lib/action_policy/behaviours/scoping.rb +2 -2
 - data/lib/action_policy/behaviours/thread_memoized.rb +1 -1
 - data/lib/action_policy/lookup_chain.rb +11 -27
 - data/lib/action_policy/policy/aliases.rb +2 -2
 - data/lib/action_policy/policy/authorization.rb +2 -2
 - data/lib/action_policy/policy/cache.rb +2 -2
 - data/lib/action_policy/policy/pre_check.rb +2 -2
 - data/lib/action_policy/policy/reasons.rb +4 -2
 - data/lib/action_policy/policy/scoping.rb +2 -2
 - data/lib/action_policy/test_helper.rb +1 -0
 - data/lib/action_policy/version.rb +1 -1
 - metadata +29 -4
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 939f6e73eca2c33b4f9d3575fde063ac346e46bcda7ab05c062ea9a35edf7252
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 8c200d093dd9efa19d6cabd811c4ce29f7c4420efd3e8052c863adbf43c2a632
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 380f476a6716d6fc096d1b6abb6ef68eba62c4ad5fb761427754870582550a25394f24c0b29bab711785ab4806812b9c20ca0ec26f0f4efe7ddca11b762a9585
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 0a63c058b49e2063af867ee74a78066e1e39f5a22d226748ee5780543e40287d672bfc177bedaf91f7036168ac72746a9128dc222a4d8a7bfe0690cdda7b5b49
         
     | 
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -2,6 +2,16 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            ## master
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
      
 5 
     | 
    
         
            +
            ## 0.5.5 (2020-12-28)
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            - Upgrade to Ruby 3.0. ([@palkan][])
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            ## 0.5.4 (2020-12-09)
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            - Add support for RSpec aliases detection when linting policy specs with `rubocop-rspec` 2.0 ([@pirj][])
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            - Fix `strict_namespace: true` lookup option not finding policies in global namespace ([@Be-ngt-oH][])
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
       5 
15 
     | 
    
         
             
            ## 0.5.0 (2020-09-29)
         
     | 
| 
       6 
16 
     | 
    
         | 
| 
       7 
17 
     | 
    
         
             
            - Move `deny!` / `allow!` to core. ([@palkan][])
         
     | 
| 
         @@ -37,7 +47,7 @@ This method is similar to `allowed_to?` but returns an authorization result obje 
     | 
|
| 
       37 
47 
     | 
    
         | 
| 
       38 
48 
     | 
    
         
             
            Fixes [#122](https://github.com/palkan/action_policy/issues/122).
         
     | 
| 
       39 
49 
     | 
    
         | 
| 
       40 
     | 
    
         
            -
            - Separated `#classify`-based and `#camelize`-based symbol lookups. ([Be-ngt-oH][])
         
     | 
| 
      
 50 
     | 
    
         
            +
            - Separated `#classify`-based and `#camelize`-based symbol lookups. ([@Be-ngt-oH][])
         
     | 
| 
       41 
51 
     | 
    
         | 
| 
       42 
52 
     | 
    
         
             
            Only affects Rails apps. Now lookup for `:users` tries to find `UsersPolicy` first (camelize),
         
     | 
| 
       43 
53 
     | 
    
         
             
            and only then search for `UserPolicy` (classify).
         
     | 
| 
         @@ -425,7 +435,8 @@ This value is now stored in a cache (if any) instead of just the call result (`t 
     | 
|
| 
       425 
435 
     | 
    
         
             
            [@ilyasgaraev]: https://github.com/ilyasgaraev
         
     | 
| 
       426 
436 
     | 
    
         
             
            [@brendon]: https://github.com/brendon
         
     | 
| 
       427 
437 
     | 
    
         
             
            [@DmitryTsepelev]: https://github.com/DmitryTsepelev
         
     | 
| 
       428 
     | 
    
         
            -
            [@korolvs]: https://github.com/ 
     | 
| 
      
 438 
     | 
    
         
            +
            [@korolvs]: https://github.com/slavadev
         
     | 
| 
       429 
439 
     | 
    
         
             
            [@nicolas-brousse]: https://github.com/nicolas-brousse
         
     | 
| 
       430 
440 
     | 
    
         
             
            [@somenugget]: https://github.com/somenugget
         
     | 
| 
       431 
441 
     | 
    
         
             
            [@Be-ngt-oH]: https://github.com/Be-ngt-oH
         
     | 
| 
      
 442 
     | 
    
         
            +
            [@pirj]: https://github.com/pirj
         
     | 
| 
         @@ -0,0 +1,62 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module ActionPolicy
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Behaviours
         
     | 
| 
      
 5 
     | 
    
         
            +
                # Adds `policy_for` method
         
     | 
| 
      
 6 
     | 
    
         
            +
                module PolicyFor
         
     | 
| 
      
 7 
     | 
    
         
            +
                  require "action_policy/ext/policy_cache_key"
         
     | 
| 
      
 8 
     | 
    
         
            +
                  using ActionPolicy::Ext::PolicyCacheKey
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  # Returns policy instance for the record.
         
     | 
| 
      
 11 
     | 
    
         
            +
                  def policy_for(record:, with: nil, namespace: authorization_namespace, context: authorization_context, allow_nil: false, default: default_authorization_policy_class)
         
     | 
| 
      
 12 
     | 
    
         
            +
                    policy_class = with || ::ActionPolicy.lookup(
         
     | 
| 
      
 13 
     | 
    
         
            +
                      record,
         
     | 
| 
      
 14 
     | 
    
         
            +
                      namespace: namespace, context: context, allow_nil: allow_nil, default: default
         
     | 
| 
      
 15 
     | 
    
         
            +
                    )
         
     | 
| 
      
 16 
     | 
    
         
            +
                    policy_class&.new(record, **context)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  def authorization_context
         
     | 
| 
      
 20 
     | 
    
         
            +
                    raise NotImplementedError, "Please, define `authorization_context` method!"
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  def authorization_namespace
         
     | 
| 
      
 24 
     | 
    
         
            +
                    # override to provide specific authorization namespace
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  def default_authorization_policy_class
         
     | 
| 
      
 28 
     | 
    
         
            +
                    # override to provide a policy class use when no policy found
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  # Override this method to provide implicit authorization target
         
     | 
| 
      
 32 
     | 
    
         
            +
                  # that would be used in case `record` is not specified in
         
     | 
| 
      
 33 
     | 
    
         
            +
                  # `authorize!` and `allowed_to?` call.
         
     | 
| 
      
 34 
     | 
    
         
            +
                  #
         
     | 
| 
      
 35 
     | 
    
         
            +
                  # It is also used to infer a policy for scoping (in `authorized_scope` method).
         
     | 
| 
      
 36 
     | 
    
         
            +
                  def implicit_authorization_target
         
     | 
| 
      
 37 
     | 
    
         
            +
                    # no-op
         
     | 
| 
      
 38 
     | 
    
         
            +
                  end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  # Return implicit authorization target or raises an exception if it's nil
         
     | 
| 
      
 41 
     | 
    
         
            +
                  def implicit_authorization_target!
         
     | 
| 
      
 42 
     | 
    
         
            +
                    implicit_authorization_target || raise(
         
     | 
| 
      
 43 
     | 
    
         
            +
                      NotFound,
         
     | 
| 
      
 44 
     | 
    
         
            +
                      [
         
     | 
| 
      
 45 
     | 
    
         
            +
                        self,
         
     | 
| 
      
 46 
     | 
    
         
            +
                        "Couldn't find implicit authorization target " \
         
     | 
| 
      
 47 
     | 
    
         
            +
                        "for #{self.class}. " \
         
     | 
| 
      
 48 
     | 
    
         
            +
                        "Please, provide policy class explicitly using `with` option or " \
         
     | 
| 
      
 49 
     | 
    
         
            +
                        "define the `implicit_authorization_target` method."
         
     | 
| 
      
 50 
     | 
    
         
            +
                      ]
         
     | 
| 
      
 51 
     | 
    
         
            +
                    )
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  def policy_for_cache_key(record:, with: nil, namespace: nil, context: authorization_context, **)
         
     | 
| 
      
 55 
     | 
    
         
            +
                    record_key = record._policy_cache_key(use_object_id: true)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    context_key = context.values.map { _1._policy_cache_key(use_object_id: true) }.join(".")
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                    "#{namespace}/#{with}/#{context_key}/#{record_key}"
         
     | 
| 
      
 59 
     | 
    
         
            +
                  end
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
              end
         
     | 
| 
      
 62 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,35 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module ActionPolicy
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Behaviours
         
     | 
| 
      
 5 
     | 
    
         
            +
                # Adds `authorized_scop` method to behaviour
         
     | 
| 
      
 6 
     | 
    
         
            +
                module Scoping
         
     | 
| 
      
 7 
     | 
    
         
            +
                  # Apply scope to the target of the specified type.
         
     | 
| 
      
 8 
     | 
    
         
            +
                  #
         
     | 
| 
      
 9 
     | 
    
         
            +
                  # NOTE: policy lookup consists of the following steps:
         
     | 
| 
      
 10 
     | 
    
         
            +
                  #   - first, check whether `with` option is present
         
     | 
| 
      
 11 
     | 
    
         
            +
                  #   - secondly, try to infer policy class from `target` (non-raising lookup)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  #   - use `implicit_authorization_target` if none of the above works.
         
     | 
| 
      
 13 
     | 
    
         
            +
                  def authorized_scope(target, type: nil, as: :default, scope_options: nil, **options)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    options[:context] && (options[:context] = authorization_context.merge(options[:context]))
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                    policy = policy_for(record: target, allow_nil: true, **options)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    policy ||= policy_for(record: implicit_authorization_target!, **options)
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                    type ||= authorization_scope_type_for(policy, target)
         
     | 
| 
      
 20 
     | 
    
         
            +
                    name = as
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                    Authorizer.scopify(target, policy, type: type, name: name, scope_options: scope_options)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  # For backward compatibility
         
     | 
| 
      
 26 
     | 
    
         
            +
                  alias_method :authorized, :authorized_scope
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  # Infer scope type for target if none provided.
         
     | 
| 
      
 29 
     | 
    
         
            +
                  # Raises an exception if type couldn't be inferred.
         
     | 
| 
      
 30 
     | 
    
         
            +
                  def authorization_scope_type_for(policy, target)
         
     | 
| 
      
 31 
     | 
    
         
            +
                    policy.resolve_scope_type(target)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  end
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
              end
         
     | 
| 
      
 35 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,87 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module ActionPolicy
         
     | 
| 
      
 4 
     | 
    
         
            +
              class AuthorizationContextMissing < Error # :nodoc:
         
     | 
| 
      
 5 
     | 
    
         
            +
                MESSAGE_TEMPLATE = "Missing policy authorization context: %s"
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                attr_reader :message
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def initialize(id)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @message = MESSAGE_TEMPLATE % id
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              module Policy
         
     | 
| 
      
 15 
     | 
    
         
            +
                # Authorization context could include multiple parameters.
         
     | 
| 
      
 16 
     | 
    
         
            +
                #
         
     | 
| 
      
 17 
     | 
    
         
            +
                # It is possible to provide more verificatio contexts, by specifying them in the policy and
         
     | 
| 
      
 18 
     | 
    
         
            +
                # providing them at the authorization step.
         
     | 
| 
      
 19 
     | 
    
         
            +
                #
         
     | 
| 
      
 20 
     | 
    
         
            +
                # For example:
         
     | 
| 
      
 21 
     | 
    
         
            +
                #
         
     | 
| 
      
 22 
     | 
    
         
            +
                #   class ApplicationPolicy < ActionPolicy::Base
         
     | 
| 
      
 23 
     | 
    
         
            +
                #     # Add user and account to the context; it's required to be passed
         
     | 
| 
      
 24 
     | 
    
         
            +
                #     # to a policy constructor and be not nil
         
     | 
| 
      
 25 
     | 
    
         
            +
                #     authorize :user, :account
         
     | 
| 
      
 26 
     | 
    
         
            +
                #
         
     | 
| 
      
 27 
     | 
    
         
            +
                #     # you can skip non-nil check if you want
         
     | 
| 
      
 28 
     | 
    
         
            +
                #     # authorize :account, allow_nil: true
         
     | 
| 
      
 29 
     | 
    
         
            +
                #
         
     | 
| 
      
 30 
     | 
    
         
            +
                #     def manage?
         
     | 
| 
      
 31 
     | 
    
         
            +
                #       # available as a simple accessor
         
     | 
| 
      
 32 
     | 
    
         
            +
                #       account.enabled?
         
     | 
| 
      
 33 
     | 
    
         
            +
                #     end
         
     | 
| 
      
 34 
     | 
    
         
            +
                #   end
         
     | 
| 
      
 35 
     | 
    
         
            +
                #
         
     | 
| 
      
 36 
     | 
    
         
            +
                #   ApplicantPolicy.new(user: user, account: account)
         
     | 
| 
      
 37 
     | 
    
         
            +
                module Authorization
         
     | 
| 
      
 38 
     | 
    
         
            +
                  class << self
         
     | 
| 
      
 39 
     | 
    
         
            +
                    def included(base)
         
     | 
| 
      
 40 
     | 
    
         
            +
                      base.extend ClassMethods
         
     | 
| 
      
 41 
     | 
    
         
            +
                    end
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  attr_reader :authorization_context
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                  def initialize(record = nil, **params)
         
     | 
| 
      
 47 
     | 
    
         
            +
                    super(record)
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                    @authorization_context = {}
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                    self.class.authorization_targets.each do |id, opts|
         
     | 
| 
      
 52 
     | 
    
         
            +
                      raise AuthorizationContextMissing, id unless params.key?(id) || opts[:optional]
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                      val = params.fetch(id, nil)
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                      raise AuthorizationContextMissing, id if val.nil? && opts[:allow_nil] != true
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                      authorization_context[id] = instance_variable_set("@#{id}", val)
         
     | 
| 
      
 59 
     | 
    
         
            +
                    end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                    authorization_context.freeze
         
     | 
| 
      
 62 
     | 
    
         
            +
                  end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                  module ClassMethods # :nodoc:
         
     | 
| 
      
 65 
     | 
    
         
            +
                    def authorize(*ids, allow_nil: false, optional: false)
         
     | 
| 
      
 66 
     | 
    
         
            +
                      allow_nil ||= optional
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                      ids.each do |id|
         
     | 
| 
      
 69 
     | 
    
         
            +
                        authorization_targets[id] = {allow_nil: allow_nil, optional: optional}
         
     | 
| 
      
 70 
     | 
    
         
            +
                      end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                      attr_reader(*ids)
         
     | 
| 
      
 73 
     | 
    
         
            +
                    end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                    def authorization_targets
         
     | 
| 
      
 76 
     | 
    
         
            +
                      return @authorization_targets if instance_variable_defined?(:@authorization_targets)
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                      @authorization_targets = if superclass.respond_to?(:authorization_targets)
         
     | 
| 
      
 79 
     | 
    
         
            +
                        superclass.authorization_targets.dup
         
     | 
| 
      
 80 
     | 
    
         
            +
                      else
         
     | 
| 
      
 81 
     | 
    
         
            +
                        {}
         
     | 
| 
      
 82 
     | 
    
         
            +
                      end
         
     | 
| 
      
 83 
     | 
    
         
            +
                    end
         
     | 
| 
      
 84 
     | 
    
         
            +
                  end
         
     | 
| 
      
 85 
     | 
    
         
            +
                end
         
     | 
| 
      
 86 
     | 
    
         
            +
              end
         
     | 
| 
      
 87 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,159 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            old_verbose = $VERBOSE
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            begin
         
     | 
| 
      
 6 
     | 
    
         
            +
              require "method_source"
         
     | 
| 
      
 7 
     | 
    
         
            +
              # Ignore parse warnings when patch
         
     | 
| 
      
 8 
     | 
    
         
            +
              # Ruby version mismatches
         
     | 
| 
      
 9 
     | 
    
         
            +
              $VERBOSE = nil
         
     | 
| 
      
 10 
     | 
    
         
            +
              require "parser/current"
         
     | 
| 
      
 11 
     | 
    
         
            +
              require "unparser"
         
     | 
| 
      
 12 
     | 
    
         
            +
            rescue LoadError
         
     | 
| 
      
 13 
     | 
    
         
            +
              # do nothing
         
     | 
| 
      
 14 
     | 
    
         
            +
            ensure
         
     | 
| 
      
 15 
     | 
    
         
            +
              $VERBOSE = old_verbose
         
     | 
| 
      
 16 
     | 
    
         
            +
            end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            module ActionPolicy
         
     | 
| 
      
 19 
     | 
    
         
            +
              using RubyNext
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
              # Takes the object and a method name,
         
     | 
| 
      
 22 
     | 
    
         
            +
              # and returns the "annotated" source code for the method:
         
     | 
| 
      
 23 
     | 
    
         
            +
              # code is split into parts by logical operators and each
         
     | 
| 
      
 24 
     | 
    
         
            +
              # part is evaluated separately.
         
     | 
| 
      
 25 
     | 
    
         
            +
              #
         
     | 
| 
      
 26 
     | 
    
         
            +
              # Example:
         
     | 
| 
      
 27 
     | 
    
         
            +
              #
         
     | 
| 
      
 28 
     | 
    
         
            +
              #  class MyClass
         
     | 
| 
      
 29 
     | 
    
         
            +
              #    def access?
         
     | 
| 
      
 30 
     | 
    
         
            +
              #      admin? && access_feed?
         
     | 
| 
      
 31 
     | 
    
         
            +
              #    end
         
     | 
| 
      
 32 
     | 
    
         
            +
              #  end
         
     | 
| 
      
 33 
     | 
    
         
            +
              #
         
     | 
| 
      
 34 
     | 
    
         
            +
              #  puts PrettyPrint.format_method(MyClass.new, :access?)
         
     | 
| 
      
 35 
     | 
    
         
            +
              #
         
     | 
| 
      
 36 
     | 
    
         
            +
              #  #=> MyClass#access?
         
     | 
| 
      
 37 
     | 
    
         
            +
              #  #=> ↳ admin? #=> false
         
     | 
| 
      
 38 
     | 
    
         
            +
              #  #=> AND
         
     | 
| 
      
 39 
     | 
    
         
            +
              #  #=> access_feed? #=> true
         
     | 
| 
      
 40 
     | 
    
         
            +
              module PrettyPrint
         
     | 
| 
      
 41 
     | 
    
         
            +
                TRUE = "\e[32mtrue\e[0m"
         
     | 
| 
      
 42 
     | 
    
         
            +
                FALSE = "\e[31mfalse\e[0m"
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                class Visitor
         
     | 
| 
      
 45 
     | 
    
         
            +
                  attr_reader :lines, :object
         
     | 
| 
      
 46 
     | 
    
         
            +
                  attr_accessor :indent
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                  def initialize(object)
         
     | 
| 
      
 49 
     | 
    
         
            +
                    @object = object
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                  def collect(ast)
         
     | 
| 
      
 53 
     | 
    
         
            +
                    @lines = []
         
     | 
| 
      
 54 
     | 
    
         
            +
                    @indent = 0
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                    visit_node(ast)
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                    lines.join("\n")
         
     | 
| 
      
 59 
     | 
    
         
            +
                  end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                  def visit_node(ast)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    if respond_to?("visit_#{ast.type}")
         
     | 
| 
      
 63 
     | 
    
         
            +
                      send("visit_#{ast.type}", ast)
         
     | 
| 
      
 64 
     | 
    
         
            +
                    else
         
     | 
| 
      
 65 
     | 
    
         
            +
                      visit_missing ast
         
     | 
| 
      
 66 
     | 
    
         
            +
                    end
         
     | 
| 
      
 67 
     | 
    
         
            +
                  end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                  def expression_with_result(sexp)
         
     | 
| 
      
 70 
     | 
    
         
            +
                    expression = Unparser.unparse(sexp)
         
     | 
| 
      
 71 
     | 
    
         
            +
                    "#{expression} #=> #{PrettyPrint.colorize(eval_exp(expression))}"
         
     | 
| 
      
 72 
     | 
    
         
            +
                  end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                  def eval_exp(exp)
         
     | 
| 
      
 75 
     | 
    
         
            +
                    return "<skipped>" if ignore_exp?(exp)
         
     | 
| 
      
 76 
     | 
    
         
            +
                    object.instance_eval(exp)
         
     | 
| 
      
 77 
     | 
    
         
            +
                  rescue => e
         
     | 
| 
      
 78 
     | 
    
         
            +
                    "Failed: #{e.message}"
         
     | 
| 
      
 79 
     | 
    
         
            +
                  end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                  def visit_and(ast)
         
     | 
| 
      
 82 
     | 
    
         
            +
                    visit_node(ast.children[0])
         
     | 
| 
      
 83 
     | 
    
         
            +
                    lines << indented("AND")
         
     | 
| 
      
 84 
     | 
    
         
            +
                    visit_node(ast.children[1])
         
     | 
| 
      
 85 
     | 
    
         
            +
                  end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                  def visit_or(ast)
         
     | 
| 
      
 88 
     | 
    
         
            +
                    visit_node(ast.children[0])
         
     | 
| 
      
 89 
     | 
    
         
            +
                    lines << indented("OR")
         
     | 
| 
      
 90 
     | 
    
         
            +
                    visit_node(ast.children[1])
         
     | 
| 
      
 91 
     | 
    
         
            +
                  end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                  def visit_begin(ast)
         
     | 
| 
      
 94 
     | 
    
         
            +
                    #  Parens
         
     | 
| 
      
 95 
     | 
    
         
            +
                    if ast.children.size == 1
         
     | 
| 
      
 96 
     | 
    
         
            +
                      lines << indented("(")
         
     | 
| 
      
 97 
     | 
    
         
            +
                      self.indent += 2
         
     | 
| 
      
 98 
     | 
    
         
            +
                      visit_node(ast.children[0])
         
     | 
| 
      
 99 
     | 
    
         
            +
                      self.indent -= 2
         
     | 
| 
      
 100 
     | 
    
         
            +
                      lines << indented(")")
         
     | 
| 
      
 101 
     | 
    
         
            +
                    else
         
     | 
| 
      
 102 
     | 
    
         
            +
                      # Multiple expressions
         
     | 
| 
      
 103 
     | 
    
         
            +
                      ast.children.each do |node|
         
     | 
| 
      
 104 
     | 
    
         
            +
                        visit_node(node)
         
     | 
| 
      
 105 
     | 
    
         
            +
                        # restore indent after each expression
         
     | 
| 
      
 106 
     | 
    
         
            +
                        self.indent -= 2
         
     | 
| 
      
 107 
     | 
    
         
            +
                      end
         
     | 
| 
      
 108 
     | 
    
         
            +
                    end
         
     | 
| 
      
 109 
     | 
    
         
            +
                  end
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                  def visit_missing(ast)
         
     | 
| 
      
 112 
     | 
    
         
            +
                    lines << indented(expression_with_result(ast))
         
     | 
| 
      
 113 
     | 
    
         
            +
                  end
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                  def indented(str)
         
     | 
| 
      
 116 
     | 
    
         
            +
                    "#{indent.zero? ? "↳ " : ""}#{" " * indent}#{str}".tap do
         
     | 
| 
      
 117 
     | 
    
         
            +
                      # increase indent after the first expression
         
     | 
| 
      
 118 
     | 
    
         
            +
                      self.indent += 2 if indent.zero?
         
     | 
| 
      
 119 
     | 
    
         
            +
                    end
         
     | 
| 
      
 120 
     | 
    
         
            +
                  end
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                  # Some lines should not be evaled
         
     | 
| 
      
 123 
     | 
    
         
            +
                  def ignore_exp?(exp)
         
     | 
| 
      
 124 
     | 
    
         
            +
                    PrettyPrint.ignore_expressions.any? { exp.match?(_1) }
         
     | 
| 
      
 125 
     | 
    
         
            +
                  end
         
     | 
| 
      
 126 
     | 
    
         
            +
                end
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 129 
     | 
    
         
            +
                  attr_accessor :ignore_expressions
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
                  if defined?(::Unparser) && defined?(::MethodSource)
         
     | 
| 
      
 132 
     | 
    
         
            +
                    def available?() = true
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                    def print_method(object, method_name)
         
     | 
| 
      
 135 
     | 
    
         
            +
                      ast = object.method(method_name).source.then(&Unparser.method(:parse))
         
     | 
| 
      
 136 
     | 
    
         
            +
                      # outer node is a method definition itself
         
     | 
| 
      
 137 
     | 
    
         
            +
                      body = ast.children[2]
         
     | 
| 
      
 138 
     | 
    
         
            +
             
     | 
| 
      
 139 
     | 
    
         
            +
                      Visitor.new(object).collect(body)
         
     | 
| 
      
 140 
     | 
    
         
            +
                    end
         
     | 
| 
      
 141 
     | 
    
         
            +
                  else
         
     | 
| 
      
 142 
     | 
    
         
            +
                    def available?() = false
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
                    def print_method(_, _) = ""
         
     | 
| 
      
 145 
     | 
    
         
            +
                  end
         
     | 
| 
      
 146 
     | 
    
         
            +
             
     | 
| 
      
 147 
     | 
    
         
            +
                  def colorize(val)
         
     | 
| 
      
 148 
     | 
    
         
            +
                    return val unless $stdout.isatty
         
     | 
| 
      
 149 
     | 
    
         
            +
                    return TRUE if val.eql?(true)
         
     | 
| 
      
 150 
     | 
    
         
            +
                    return FALSE if val.eql?(false)
         
     | 
| 
      
 151 
     | 
    
         
            +
                    val
         
     | 
| 
      
 152 
     | 
    
         
            +
                  end
         
     | 
| 
      
 153 
     | 
    
         
            +
                end
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
                self.ignore_expressions = [
         
     | 
| 
      
 156 
     | 
    
         
            +
                  /^\s*binding\.(pry|irb)\s*$/s
         
     | 
| 
      
 157 
     | 
    
         
            +
                ]
         
     | 
| 
      
 158 
     | 
    
         
            +
              end
         
     | 
| 
      
 159 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,62 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module ActionPolicy
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Behaviours
         
     | 
| 
      
 5 
     | 
    
         
            +
                # Adds `policy_for` method
         
     | 
| 
      
 6 
     | 
    
         
            +
                module PolicyFor
         
     | 
| 
      
 7 
     | 
    
         
            +
                  require "action_policy/ext/policy_cache_key"
         
     | 
| 
      
 8 
     | 
    
         
            +
                  using ActionPolicy::Ext::PolicyCacheKey
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  # Returns policy instance for the record.
         
     | 
| 
      
 11 
     | 
    
         
            +
                  def policy_for(record:, with: nil, namespace: authorization_namespace, context: authorization_context, allow_nil: false, default: default_authorization_policy_class)
         
     | 
| 
      
 12 
     | 
    
         
            +
                    policy_class = with || ::ActionPolicy.lookup(
         
     | 
| 
      
 13 
     | 
    
         
            +
                      record,
         
     | 
| 
      
 14 
     | 
    
         
            +
                      namespace: namespace, context: context, allow_nil: allow_nil, default: default
         
     | 
| 
      
 15 
     | 
    
         
            +
                    )
         
     | 
| 
      
 16 
     | 
    
         
            +
                    policy_class&.new(record, **context)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  def authorization_context
         
     | 
| 
      
 20 
     | 
    
         
            +
                    raise NotImplementedError, "Please, define `authorization_context` method!"
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  def authorization_namespace
         
     | 
| 
      
 24 
     | 
    
         
            +
                    # override to provide specific authorization namespace
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  def default_authorization_policy_class
         
     | 
| 
      
 28 
     | 
    
         
            +
                    # override to provide a policy class use when no policy found
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  # Override this method to provide implicit authorization target
         
     | 
| 
      
 32 
     | 
    
         
            +
                  # that would be used in case `record` is not specified in
         
     | 
| 
      
 33 
     | 
    
         
            +
                  # `authorize!` and `allowed_to?` call.
         
     | 
| 
      
 34 
     | 
    
         
            +
                  #
         
     | 
| 
      
 35 
     | 
    
         
            +
                  # It is also used to infer a policy for scoping (in `authorized_scope` method).
         
     | 
| 
      
 36 
     | 
    
         
            +
                  def implicit_authorization_target
         
     | 
| 
      
 37 
     | 
    
         
            +
                    # no-op
         
     | 
| 
      
 38 
     | 
    
         
            +
                  end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  # Return implicit authorization target or raises an exception if it's nil
         
     | 
| 
      
 41 
     | 
    
         
            +
                  def implicit_authorization_target!
         
     | 
| 
      
 42 
     | 
    
         
            +
                    implicit_authorization_target || raise(
         
     | 
| 
      
 43 
     | 
    
         
            +
                      NotFound,
         
     | 
| 
      
 44 
     | 
    
         
            +
                      [
         
     | 
| 
      
 45 
     | 
    
         
            +
                        self,
         
     | 
| 
      
 46 
     | 
    
         
            +
                        "Couldn't find implicit authorization target " \
         
     | 
| 
      
 47 
     | 
    
         
            +
                        "for #{self.class}. " \
         
     | 
| 
      
 48 
     | 
    
         
            +
                        "Please, provide policy class explicitly using `with` option or " \
         
     | 
| 
      
 49 
     | 
    
         
            +
                        "define the `implicit_authorization_target` method."
         
     | 
| 
      
 50 
     | 
    
         
            +
                      ]
         
     | 
| 
      
 51 
     | 
    
         
            +
                    )
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  def policy_for_cache_key(record:, with: nil, namespace: nil, context: authorization_context, **)
         
     | 
| 
      
 55 
     | 
    
         
            +
                    record_key = record._policy_cache_key(use_object_id: true)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    context_key = context.values.map { |_1| _1._policy_cache_key(use_object_id: true) }.join(".")
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                    "#{namespace}/#{with}/#{context_key}/#{record_key}"
         
     | 
| 
      
 59 
     | 
    
         
            +
                  end
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
              end
         
     | 
| 
      
 62 
     | 
    
         
            +
            end
         
     |