class Eaco::DSL::ACL

Block-less DSL to set up the {ACL} machinery onto an authorized {Resource}.

Public Class Methods

new(*) click to toggle source

Performs ACL setup on the target Resource model.

@return [nil]

Calls superclass method
# File lib/eaco/dsl/acl.rb, line 28
def initialize(*)
  super

  define_acl_subclass
  define_role_getters
  install_persistance
  install_strategy

  nil
end

Private Instance Methods

adapter() click to toggle source

Tries to identify which ORM adapter to use for the target class.

@return [Class] the adapter implementation or nil if not available.

# File lib/eaco/dsl/acl.rb, line 156
def adapter
  { 'ActiveRecord::Base'     => Eaco::Adapters::ActiveRecord,
    'CouchRest::Model::Base' => Eaco::Adapters::CouchrestModel,
  }.fetch(orm.name, nil)
end
define_acl_subclass() click to toggle source

Creates the ACL constant on the target, inheriting from {Eaco::ACL}. Removes if it is already set, so that a reload of the authorization rules refreshes also these constants.

The ACL subclass can be retrieved using the .acl singleton method on the {Resource} class.

@return [void]

# File lib/eaco/dsl/acl.rb, line 51
def define_acl_subclass
  target_eval do
    remove_const(:ACL) if const_defined?(:ACL, false)

    Class.new(Eaco::ACL).tap do |acl_class|
      define_singleton_method(:acl) { acl_class }
      const_set(:ACL, acl_class)
    end
  end
end
define_role_getters() click to toggle source

Define getter methods on the ACL for each role, syntactic sugar for calling {ACL#find_by_role}.

Example:

If a reader role is defined, allows doing resource.acl.readers and returns all the designators having the reader role set.

@return [void]

# File lib/eaco/dsl/acl.rb, line 73
def define_role_getters
  roles = self.target.roles

  target.acl.instance_eval do
    roles.each do |role|
      define_method(role.to_s.pluralize) { find_by_role(role) }
    end
  end
end
install_authorized_collection_strategy() click to toggle source

Looks up the authorized collection strategy within the Adapter, using the :using option given to the authorize Resource DSL

@see DSL::Resource

@return [void]

# File lib/eaco/dsl/acl.rb, line 145
def install_authorized_collection_strategy
  if adapter && (strategy = adapter.strategies[ options.fetch(:using, nil) ])
    target.extend strategy
  end
end
install_persistance() click to toggle source

Sets up the persistance layer for ACLs (#acl and #acl=).

These APIs can be implemented directly in your Resource model, as long as the acl accessor accepts and returns the Resource model's ACL subclass (see {.define_acl_subclass})

See each adapter for the details of the extraction strategies they provide.

@return [void]

# File lib/eaco/dsl/acl.rb, line 95
      def install_persistance
        if adapter
          target.send(:include, adapter)
          install_authorized_collection_strategy

        elsif (target.instance_methods & [:acl, :acl=]).size != 2
          raise Malformed, <<-EOF
            Don't know how to persist ACLs using <#{target}>'s ORM
            (identified as <#{orm}>). Please define an `acl' instance
            accessor on <#{target}> that accepts and returns a <#{target.acl}>.
          EOF
        end
      end
install_strategy() click to toggle source

Sets up the authorized collection extraction strategy (.accessible_by).

This API can be implemented directly in your model, as long as .accessible_by returns an Enumerable collection.

@return [void]

# File lib/eaco/dsl/acl.rb, line 118
      def install_strategy
        unless target.respond_to?(:accessible_by)
          strategies = adapter ? adapter.strategies.keys : []

          raise Malformed, <<-EOF
            Don't know how to look up authorized records on <#{target}>'s
            ORM (identified as <#{orm}>). To authorize <#{target}>

            #{ if strategies.size > 0
              "either use one of the available strategies: #{strategies.join(', ')} or"
            end }

            please define your own #{target}.accessible_by method.
            You may at one point want to move this in a new strategy,
            and send a pull request :-).
          EOF
        end
      end
orm() click to toggle source

Tries to naively identify which ORM the target model is using.

TODO support more stuff

@return [Class] the ORM base class.

# File lib/eaco/dsl/acl.rb, line 169
def orm
  if defined?(ActiveRecord::Base) && target.ancestors.include?(ActiveRecord::Base)
    ActiveRecord::Base
  else
    target.superclass # Naive
  end
end