Module: Authorization::AuthorizationInModel

Defined in:
lib/declarative_authorization/in_model.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object

:nodoc:


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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/declarative_authorization/in_model.rb', line 34

def self.included(base) # :nodoc:
  #base.extend(ClassMethods)
  base.module_eval do
    if Rails.version < "3.1"
      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 =
            if options[:context]
              options[:context]
            elsif parent_scope.respond_to?(:proxy_reflection)
              parent_scope.proxy_reflection.klass.name.tableize.to_sym
            elsif parent_scope.respond_to?(:decl_auth_context)
              parent_scope.decl_auth_context
            else
              parent_scope.name.tableize.to_sym
            end

        user = options[:user] || Authorization.current_user

        engine = options[: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
    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 = options[:engine] || Authorization::Engine.instance

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

      obligation_scope.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)
      if Rails.version < "3.1"
        scopes[:with_permissions_to].call(self, *args)
      else
        options = args.last.is_a?(Hash) ? args.pop : {}
        privilege = (args[0] || :read).to_sym
        privileges = [privilege]
        parent_scope = where(nil)
        context =
            if options[:context]
              options[:context]
            elsif parent_scope.klass.respond_to?(:decl_auth_context)
              parent_scope.klass.decl_auth_context
            else
              parent_scope.klass.name.tableize.to_sym
            end

        user = options[:user] || Authorization.current_user

        engine = options[: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.klass)
      end
    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
        
        if options[:include_read]
          # 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 Rails.version < "3"
            def after_find; end
          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

Instance Method Details

#permitted_to!(privilege, options = {}) ⇒ Object

Works similar to the permitted_to? method, but doesn't accept a block and throws the authorization exceptions, just like Engine#permit!


24
25
26
27
28
29
30
31
32
# File 'lib/declarative_authorization/in_model.rb', line 24

def permitted_to! (privilege, options = {} )
  options = {
    :user =>  Authorization.current_user,
    :object => self
  }.merge(options)
  Authorization::Engine.instance.permit!(privilege,
      {:user => options[:user],
       :object => options[:object]})
end

#permitted_to?(privilege, options = {}, &block) ⇒ Boolean

If the user meets the given privilege, permitted_to? returns true and yields to the optional block.

Returns:

  • (Boolean)

11
12
13
14
15
16
17
18
19
20
# File 'lib/declarative_authorization/in_model.rb', line 11

def permitted_to? (privilege, options = {}, &block)
  options = {
    :user =>  Authorization.current_user,
    :object => self
  }.merge(options)
  Authorization::Engine.instance.permit?(privilege,
      {:user => options[:user],
       :object => options[:object]},
      &block)
end