forked from stffn/declarative_authorization
-
Notifications
You must be signed in to change notification settings - Fork 2
/
in_model.rb
162 lines (144 loc) · 5.85 KB
/
in_model.rb
1
2
3
4
5
6
7
8
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
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
# Authorization::AuthorizationInModel
require File.dirname(__FILE__) + '/authorization.rb'
require File.dirname(__FILE__) + '/obligation_scope.rb'
module Authorization
module AuthorizationInModel
# If the user meets the given privilege, permitted_to? returns true
# and yields to the optional block.
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
# Works similar to the permitted_to? method, but doesn't accept a block
# and throws the authorization exceptions, just like Engine#permit!
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
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 =
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
# 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)
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
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
end
end