Module: Authorization::AuthorizationInModel

Defined in:
lib/declarative_authorization/in_model.rb

Class Method Summary collapse

Class Method Details

.included(base) ⇒ Object

:nodoc:



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/declarative_authorization/in_model.rb', line 9

def self.included(base) # :nodoc:
  #base.extend(ClassMethods)
  base.module_eval do
    scopes[:with_permissions_to] = lambda do |parent_scope, *args|
      options = args.last.is_a?(Hash) ? args.pop : {}
      privilege = (args[0] || :read).to_sym
      privileges = [privilege]
      context = options[:context] || :"#{parent_scope.table_name}"
      
      user = options[:user] || Authorization.current_user

      engine = Authorization::Engine.instance
      engine.permit!(privileges, :user => user, :skip_attribute_test => true,
                     :context => context)

      obligation_scope_for( privileges, :user => user,
          :context => context, :engine => engine, :model => parent_scope)
    end
    
    # Builds and returns a scope with joins and conditions satisfying all obligations.
    def self.obligation_scope_for( privileges, options = {} )
      options = {
        :user => Authorization.current_user,
        :context => nil,
        :model => self,
        :engine => nil,
      }.merge(options)
      engine ||= Authorization::Engine.instance

      scope = ObligationScope.new( options[:model], {} )
      engine.obligations( privileges, :user => options[:user], :context => options[:context] ).each do |obligation|
        scope.parse!( obligation )
      end
      scope
    end

    # Named scope for limiting query results according to the authorization
    # of the current user.  If no privilege is given, :+read+ is assumed.
    # 
    #   User.with_permissions_to
    #   User.with_permissions_to(:update)
    #   User.with_permissions_to(:update, :context => :users)
    #   
    # As in the case of other named scopes, this one may be chained:
    #   User.with_permission_to.find(:all, :conditions...)
    # 
    # Options
    # [:+context+]
    #   Context for the privilege to be evaluated in; defaults to the
    #   model's table name.
    # [:+user+]
    #   User to be used for gathering obligations; defaults to the
    #   current user.
    #
    def self.with_permissions_to (*args)
      scopes[:with_permissions_to].call(self, *args)
    end
    
    # Activates model security for the current model.  Then, CRUD operations
    # are checked against the authorization of the current user.  The
    # privileges are :+create+, :+read+, :+update+ and :+delete+ in the
    # context of the model.  By default, :+read+ is not checked because of
    # performance impacts, especially with large result sets.
    # 
    #   class User < ActiveRecord::Base
    #     using_access_control
    #   end
    #   
    # If an operation is not permitted, a Authorization::AuthorizationError
    # is raised.
    #
    # To activate model security on all models, call using_access_control
    # on ActiveRecord::Base
    #   ActiveRecord::Base.using_access_control
    # 
    # Available options
    # [:+context+] Specify context different from the models table name.
    # [:+include_read+] Also check for :+read+ privilege after find.
    #
    def self.using_access_control (options = {})
      options = {
        :context => nil,
        :include_read => false
      }.merge(options)

      class_eval do
        [:create, :update, [:destroy, :delete]].each do |action, privilege|
          send(:"before_#{action}") do |object|
            Authorization::Engine.instance.permit!(privilege || action,
              :object => object, :context => options[:context])
          end
        end

        # after_find is only called if after_find is implemented
        after_find do |object|
          Authorization::Engine.instance.permit!(:read, :object => object,
            :context => options[:context])
        end
        
        if options[:include_read]
          def after_find; end
        end

        def self.using_access_control?
          true
        end
      end
    end

    # Returns true if the model is using model security.
    def self.using_access_control?
      false
    end
  end
end