controlist 0.1.0
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 +7 -0
- data/.gitignore +15 -0
- data/Gemfile +4 -0
- data/Gemfile.lock.3.2 +106 -0
- data/Gemfile.lock.4.1 +84 -0
- data/Gemfile.lock.4.2 +101 -0
- data/LICENSE.txt +22 -0
- data/README.md +96 -0
- data/Rakefile +8 -0
- data/controlist.gemspec +42 -0
- data/lib/controlist/errors.rb +26 -0
- data/lib/controlist/interceptor.rb +193 -0
- data/lib/controlist/managers/base_manager.rb +31 -0
- data/lib/controlist/managers/thread_based_manager.rb +32 -0
- data/lib/controlist/permission.rb +209 -0
- data/lib/controlist/permissions/advanced_constrain.rb +24 -0
- data/lib/controlist/permissions/constrain.rb +10 -0
- data/lib/controlist/permissions/operation.rb +32 -0
- data/lib/controlist/permissions/ordered_package.rb +56 -0
- data/lib/controlist/permissions/simple_constrain.rb +16 -0
- data/lib/controlist/version.rb +3 -0
- data/lib/controlist.rb +32 -0
- data/test/feature_test.rb +233 -0
- data/test/fixtures/clazz.yml +6 -0
- data/test/fixtures/user.yml +25 -0
- data/test/migrate.rb +20 -0
- data/test/models/clazz.rb +5 -0
- data/test/models/user.rb +5 -0
- data/test/test.sqlite3 +0 -0
- data/test/test_helper.rb +40 -0
- metadata +208 -0
@@ -0,0 +1,193 @@
|
|
1
|
+
module Controlist
|
2
|
+
|
3
|
+
class Interceptor
|
4
|
+
|
5
|
+
PROXY_CLASSES = {}
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def hook
|
10
|
+
hook_read
|
11
|
+
hook_persistence
|
12
|
+
hook_attribute
|
13
|
+
end
|
14
|
+
|
15
|
+
# used by hook_attribute
|
16
|
+
def build_proxy(target)
|
17
|
+
klass = target.class
|
18
|
+
proxy_class = (PROXY_CLASSES[klass] ||= create_value_object_proxy_class klass)
|
19
|
+
proxy_class.new target
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# Avoid ActiveModel::MissingAttributeError due to select(attributes) according to constrains
|
25
|
+
# #suppose attribute_proxy is :_val, value_object_proxy is :_value_object
|
26
|
+
# user = User.find 1
|
27
|
+
# user._val(:name)
|
28
|
+
# user._value_object.name
|
29
|
+
def hook_attribute
|
30
|
+
ActiveRecord::Persistence.class_eval %Q{
|
31
|
+
def #{Controlist.attribute_proxy}(attr)
|
32
|
+
self.#{Controlist.value_object_proxy}[attr]
|
33
|
+
end
|
34
|
+
|
35
|
+
def #{Controlist.value_object_proxy}
|
36
|
+
@#{Controlist.value_object_proxy} ||= Controlist::Interceptor.build_proxy self
|
37
|
+
end
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def hook_persistence
|
42
|
+
if Controlist.is_activerecord3?
|
43
|
+
settings = {
|
44
|
+
create: :create,
|
45
|
+
update: :update,
|
46
|
+
delete: [:delete, :destroy]
|
47
|
+
}
|
48
|
+
else
|
49
|
+
settings = {
|
50
|
+
create: :_create_record,
|
51
|
+
update: :_update_record,
|
52
|
+
delete: [:delete, :destroy]
|
53
|
+
}
|
54
|
+
end
|
55
|
+
settings.each do |operation, methods|
|
56
|
+
Array(methods).each do |method|
|
57
|
+
ActiveRecord::Persistence.module_eval %Q{
|
58
|
+
def #{method}_with_controlist(*args)
|
59
|
+
permission_provider = Controlist.permission_provider
|
60
|
+
unless permission_provider.skip?
|
61
|
+
permission_package = permission_provider.get_permission_package
|
62
|
+
permissions = permission_package.list_#{operation}[self.class] if permission_package
|
63
|
+
if permissions.blank?
|
64
|
+
raise NoPermissionError
|
65
|
+
else
|
66
|
+
passed = false
|
67
|
+
matched_permission = nil
|
68
|
+
permissions.each do |permission|
|
69
|
+
if permission.match_for_persistence(self, Controlist::Permission::#{operation.upcase})
|
70
|
+
Controlist.logger.debug{"Controlist matched to \#{permission.is_allowed ? 'allow' : 'forbid'} \#{permission.inspect}"}
|
71
|
+
if permission.is_allowed
|
72
|
+
passed = true
|
73
|
+
end
|
74
|
+
matched_permission = permission
|
75
|
+
break
|
76
|
+
end
|
77
|
+
end
|
78
|
+
if passed
|
79
|
+
Controlist.logger.debug{"Controlist #{operation} checked: PASSED"}
|
80
|
+
else
|
81
|
+
Controlist.logger.debug{"Controlist #{operation} checked: FORBIDDEN"}
|
82
|
+
if matched_permission.nil?
|
83
|
+
raise NoPermissionError
|
84
|
+
else
|
85
|
+
raise PermissionForbidden.new "Forbidden by permission", matched_permission
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
#{method}_without_controlist(*args)
|
91
|
+
end
|
92
|
+
alias_method_chain :#{method}, :controlist unless method_defined? :#{method}_without_controlist
|
93
|
+
}
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def hook_read
|
99
|
+
if Controlist.is_activerecord3?
|
100
|
+
ActiveRecord::QueryMethods.module_eval do
|
101
|
+
def where!(opts, *rest)
|
102
|
+
return if opts.blank?
|
103
|
+
self.where_values += build_where(opts, rest)
|
104
|
+
end
|
105
|
+
def _select!(*value)
|
106
|
+
self.select_values += Array.wrap(value)
|
107
|
+
end
|
108
|
+
def joins!(*args)
|
109
|
+
return if args.compact.blank?
|
110
|
+
args.flatten!
|
111
|
+
self.joins_values += args
|
112
|
+
end
|
113
|
+
end
|
114
|
+
ActiveRecord::IdentityMap.module_eval do
|
115
|
+
def self.enabled?
|
116
|
+
false
|
117
|
+
end
|
118
|
+
def self.enabled
|
119
|
+
false
|
120
|
+
end
|
121
|
+
end
|
122
|
+
else
|
123
|
+
ActiveRecord::Core::ClassMethods.module_eval do
|
124
|
+
#Bypass find_by_statement_cache, otherwise will use cached sql which may has wrong permissions
|
125
|
+
def find(*args)
|
126
|
+
super
|
127
|
+
end
|
128
|
+
def find_by(*args)
|
129
|
+
super
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
ActiveRecord::Relation.class_eval do
|
134
|
+
def build_arel_with_controlist
|
135
|
+
permission_provider = Controlist.permission_provider
|
136
|
+
if permission_provider.skip? || @controlist_processing
|
137
|
+
build_arel_without_controlist
|
138
|
+
else
|
139
|
+
if @controlist_done
|
140
|
+
raise Controlist::NotReuseableError.new("The relation has built a sql, you can't reuse it, or you can clone it before sql building", self)
|
141
|
+
else
|
142
|
+
@controlist_processing = true
|
143
|
+
permission_provider = Controlist.permission_provider
|
144
|
+
unless permission_provider.skip?
|
145
|
+
permission_package = permission_provider.get_permission_package
|
146
|
+
permissions = permission_package.list_read[@klass] if permission_package
|
147
|
+
if permissions.blank?
|
148
|
+
self.where!("1 != 1")
|
149
|
+
else
|
150
|
+
permissions.each do |permission|
|
151
|
+
permission.handle_for_read self
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
@controlist_processing = false
|
156
|
+
@controlist_done = true
|
157
|
+
build_arel_without_controlist
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
alias_method_chain :build_arel, :controlist unless method_defined? :build_arel_without_controlist
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def create_value_object_proxy_class(klass)
|
166
|
+
attributes = klass.columns.map(&:name)
|
167
|
+
attributes.delete klass.primary_key
|
168
|
+
proxy_class = Class.new
|
169
|
+
code_block = ""
|
170
|
+
attributes.each do |attribute|
|
171
|
+
code_block += %Q{
|
172
|
+
def #{attribute}
|
173
|
+
@target.#{attribute} rescue nil
|
174
|
+
end
|
175
|
+
}
|
176
|
+
end
|
177
|
+
proxy_class.class_eval %Q{
|
178
|
+
def initialize(target)
|
179
|
+
@target = target
|
180
|
+
end
|
181
|
+
def [](attribute)
|
182
|
+
@target[attribute] rescue nil
|
183
|
+
end
|
184
|
+
#{code_block}
|
185
|
+
}
|
186
|
+
proxy_class
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Controlist
|
2
|
+
module Managers
|
3
|
+
class BaseManager
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def get_permission_package
|
8
|
+
raise NotImplementedError
|
9
|
+
end
|
10
|
+
|
11
|
+
def set_permission_package(package)
|
12
|
+
raise NotImplementedError
|
13
|
+
end
|
14
|
+
|
15
|
+
def skip?
|
16
|
+
raise NotImplementedError
|
17
|
+
end
|
18
|
+
|
19
|
+
def open_skip
|
20
|
+
raise NotImplementedError
|
21
|
+
end
|
22
|
+
|
23
|
+
def close_skip
|
24
|
+
raise NotImplementedError
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Controlist
|
2
|
+
module Managers
|
3
|
+
class ThreadBasedManager < BaseManager
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def get_permission_package
|
8
|
+
Thread.current[:permission_package]
|
9
|
+
end
|
10
|
+
|
11
|
+
def set_permission_package(package)
|
12
|
+
Thread.current[:permission_package] = package
|
13
|
+
end
|
14
|
+
|
15
|
+
def skip?
|
16
|
+
Thread.current[:skip_controlist] == true
|
17
|
+
end
|
18
|
+
|
19
|
+
def open_skip
|
20
|
+
Thread.current[:skip_controlist] = true
|
21
|
+
end
|
22
|
+
|
23
|
+
def close_skip
|
24
|
+
Thread.current[:skip_controlist] = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,209 @@
|
|
1
|
+
require 'controlist/permissions/operation'
|
2
|
+
require 'controlist/permissions/constrain'
|
3
|
+
require 'controlist/permissions/simple_constrain'
|
4
|
+
require 'controlist/permissions/advanced_constrain'
|
5
|
+
require 'controlist/permissions/ordered_package'
|
6
|
+
|
7
|
+
module Controlist
|
8
|
+
class Permission
|
9
|
+
|
10
|
+
# properties is hash with property and value pair, operation READ only need keys
|
11
|
+
attr_accessor :klass, :operations, :is_allowed, :constrains, :clause, :joins, :properties, :procs_read
|
12
|
+
|
13
|
+
def initialize(klass, operations, is_allowed = true, constrains=nil)
|
14
|
+
self.procs_read = []
|
15
|
+
self.klass = klass
|
16
|
+
unless operations.nil?
|
17
|
+
if operations.is_a? Array
|
18
|
+
self.operations = operations
|
19
|
+
else
|
20
|
+
self.operations = [operations]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
self.is_allowed = is_allowed
|
24
|
+
self.joins = []
|
25
|
+
if self.operations.nil?
|
26
|
+
init_for_read constrains
|
27
|
+
init_for_persistence constrains
|
28
|
+
else
|
29
|
+
init_for_read constrains if self.operations.include? Controlist::Permissions::READ
|
30
|
+
if self.operations.include?(Controlist::Permissions::CREATE) ||
|
31
|
+
self.operations.include?(Controlist::Permissions::UPDATE) ||
|
32
|
+
self.operations.include?(Controlist::Permissions::DELETE)
|
33
|
+
init_for_persistence constrains
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def apply(*properties)
|
39
|
+
self.properties = {id: nil}
|
40
|
+
properties.each do |property_pair|
|
41
|
+
if property_pair.is_a? Hash
|
42
|
+
self.properties.merge! property_pair
|
43
|
+
else
|
44
|
+
self.properties[property_pair] = nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
def handle_for_read(relation)
|
51
|
+
relation._select!(*self.properties.keys) unless self.properties.blank?
|
52
|
+
relation.joins!(*self.joins) if self.joins.size > 0
|
53
|
+
relation.where!("#{self.clause}") if self.clause
|
54
|
+
unless self.procs_read.blank?
|
55
|
+
# Only support ActiveRecord 4
|
56
|
+
merging_relation = self.klass.unscoped
|
57
|
+
self.procs_read.each do |proc|
|
58
|
+
merging_relation = proc.call(merging_relation)
|
59
|
+
end
|
60
|
+
ActiveRecord::Relation::Merger.new(relation, merging_relation).merge
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
def match_for_persistence(object, operation)
|
66
|
+
properties_matched = match_properties_for_persistence object, operation
|
67
|
+
properties_matched && match_constains_for_persistence(object, operation)
|
68
|
+
end
|
69
|
+
|
70
|
+
def match_properties_for_persistence(object, operation)
|
71
|
+
return true if operation == Controlist::Permissions::DELETE || self.properties.blank?
|
72
|
+
properties_matched = false
|
73
|
+
changes = object.changes
|
74
|
+
self.properties.each do |property, value|
|
75
|
+
change = changes[property]
|
76
|
+
if change && (value.nil? || Array(value).include?(change.last))
|
77
|
+
properties_matched = true
|
78
|
+
break
|
79
|
+
end
|
80
|
+
end
|
81
|
+
Controlist.logger.debug{"Controlist #{operation} properties checked: #{properties_matched}"}
|
82
|
+
properties_matched
|
83
|
+
end
|
84
|
+
|
85
|
+
def match_constains_for_persistence(object, operation)
|
86
|
+
if self.constrains.blank?
|
87
|
+
constrain_matched = true
|
88
|
+
else
|
89
|
+
constrain_matched = self.constrains.any? do |constrain|
|
90
|
+
if constrain.proc_persistence.is_a?(Proc) && constrain.proc_persistence.lambda?
|
91
|
+
inner_matched = constrain.proc_persistence.call object, operation
|
92
|
+
else
|
93
|
+
inner_matched = false
|
94
|
+
property = constrain.property
|
95
|
+
value = constrain.value
|
96
|
+
operator = constrain.operator
|
97
|
+
if constrain.relation.nil?
|
98
|
+
if object.persisted? && (changes = object.changes[property])
|
99
|
+
inner_matched = match_value(changes.first, value, operator)
|
100
|
+
else
|
101
|
+
inner_matched = match_value(object[property], value, operator)
|
102
|
+
end
|
103
|
+
else
|
104
|
+
relation_object = object.send(constrain.relation)
|
105
|
+
inner_matched = (relation_object && match_value(relation_object[property], value, operator))
|
106
|
+
end
|
107
|
+
end
|
108
|
+
inner_matched
|
109
|
+
end
|
110
|
+
end
|
111
|
+
Controlist.logger.debug{"Controlist #{operation} constrains checked: #{constrain_matched}"}
|
112
|
+
constrain_matched
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def init_for_persistence(constrains)
|
118
|
+
return if constrains.nil?
|
119
|
+
if !(constrains.is_a?(Controlist::Permissions::Constrain) ||constrains.is_a?(Array))
|
120
|
+
raise ArgumentError.new("constrains has unknown type #{constrains.class}")
|
121
|
+
end
|
122
|
+
constrains = [constrains] if constrains.is_a? Controlist::Permissions::Constrain
|
123
|
+
constrains.compact!
|
124
|
+
constrains.each do |constrain|
|
125
|
+
raise "Persistence operation can't use constrain clause" unless constrain.clause.blank?
|
126
|
+
end
|
127
|
+
self.constrains = constrains
|
128
|
+
end
|
129
|
+
|
130
|
+
def match_value(left, right, operator)
|
131
|
+
if operator.nil?
|
132
|
+
left == right
|
133
|
+
else
|
134
|
+
left.send(operator.to_sym, right)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def init_for_read(constrains)
|
139
|
+
return if constrains.nil?
|
140
|
+
case constrains
|
141
|
+
when String
|
142
|
+
self.clause = constrains
|
143
|
+
when Array, Controlist::Permissions::Constrain
|
144
|
+
constrains = [constrains] if constrains.is_a? Controlist::Permissions::Constrain
|
145
|
+
self.constrains = constrains.compact
|
146
|
+
self.clause = build_clause
|
147
|
+
self.clause = "not (#{self.clause})" if self.is_allowed == false
|
148
|
+
else
|
149
|
+
raise ArgumentError.new("constrains has unknown type #{constrains.class}")
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def build_clause
|
154
|
+
clause = ""
|
155
|
+
self.constrains.each do |constrain|
|
156
|
+
if constrain.proc_read.is_a?(Proc)
|
157
|
+
self.procs_read << constrain.proc_read
|
158
|
+
next
|
159
|
+
else
|
160
|
+
if !constrain.clause.nil?
|
161
|
+
part_clause = constrain.clause
|
162
|
+
else
|
163
|
+
table_name = append_joins constrain.relation if constrain.relation
|
164
|
+
table_name ||= (constrain.table_name || self.klass.table_name)
|
165
|
+
property = constrain.property
|
166
|
+
value = constrain.value
|
167
|
+
raise ArgumentError.new("property could not be nil") if property.blank?
|
168
|
+
raise ArgumentError.new("value could not be nil") if value.blank?
|
169
|
+
default_operator = '='
|
170
|
+
if value.is_a?(Proc) && value.lambda?
|
171
|
+
Controlist.skip{ value = value.call }
|
172
|
+
end
|
173
|
+
if value.is_a? Array
|
174
|
+
if value.first.is_a? String
|
175
|
+
value = "('" + value.join("','") + "')"
|
176
|
+
else
|
177
|
+
value = "(" + value.join(",") + ")"
|
178
|
+
end
|
179
|
+
default_operator = 'in'
|
180
|
+
else
|
181
|
+
if value.is_a? String
|
182
|
+
if value.upcase == 'NULL'
|
183
|
+
default_operator = 'is'
|
184
|
+
else
|
185
|
+
value = "'#{value}'"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
operator = constrain.operator || default_operator
|
190
|
+
part_clause = "#{table_name}.#{property} #{operator} #{value}"
|
191
|
+
end
|
192
|
+
clause += " and " if clause.length > 0
|
193
|
+
clause += "(#{part_clause})"
|
194
|
+
end
|
195
|
+
end
|
196
|
+
clause
|
197
|
+
end
|
198
|
+
|
199
|
+
def append_joins(relation_name)
|
200
|
+
reflections = self.klass.reflections
|
201
|
+
# Rails 4.2 use string key, instead Rails 4.1 use symbol key
|
202
|
+
relation = reflections[relation_name.to_s] || reflections[relation_name.to_sym]
|
203
|
+
raise "Relation #{relation_name} Not found for class #{self.klass}!" if relation.nil?
|
204
|
+
self.joins << relation_name.to_sym
|
205
|
+
relation.table_name
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
209
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Controlist
|
2
|
+
module Permissions
|
3
|
+
|
4
|
+
class AdvancedConstrain < Constrain
|
5
|
+
|
6
|
+
def initialize(hash)
|
7
|
+
self.property = hash[:property]
|
8
|
+
self.value = hash[:value]
|
9
|
+
self.relation = hash[:relation]
|
10
|
+
self.table_name = hash[:table_name]
|
11
|
+
self.operator = hash[:operator]
|
12
|
+
self.clause = hash[:clause]
|
13
|
+
if Controlist.is_activerecord3? && (hash.has_key?(:proc_read) || hash.has_key?(:proc_persistence))
|
14
|
+
raise NotImplementedError, "Skip proc_read and proc_persistence, that features only be supported in ActiveRecord 4 or later"
|
15
|
+
else
|
16
|
+
self.proc_read = hash[:proc_read]
|
17
|
+
self.proc_persistence = hash[:proc_persistence]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Controlist
|
2
|
+
module Permissions
|
3
|
+
|
4
|
+
CREATE = :create
|
5
|
+
READ = :read
|
6
|
+
UPDATE = :update
|
7
|
+
DELETE = :delete
|
8
|
+
|
9
|
+
module_function
|
10
|
+
|
11
|
+
def is_persistence?(operation)
|
12
|
+
[CREATE, UPDATE, DELETE].include? operation.to_sym
|
13
|
+
end
|
14
|
+
|
15
|
+
def is_create?(operation)
|
16
|
+
CREATE == operation.to_sym
|
17
|
+
end
|
18
|
+
|
19
|
+
def is_read?(operation)
|
20
|
+
READ == operation.to_sym
|
21
|
+
end
|
22
|
+
|
23
|
+
def is_update?(operation)
|
24
|
+
UPDATE == operation.to_sym
|
25
|
+
end
|
26
|
+
|
27
|
+
def is_delete?(operation)
|
28
|
+
DELETE == operation.to_sym
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Controlist
|
2
|
+
module Permissions
|
3
|
+
|
4
|
+
class OrderedPackage
|
5
|
+
|
6
|
+
attr_reader :list_create, :list_read, :list_update, :list_delete, :permissions
|
7
|
+
|
8
|
+
def initialize(*permissions)
|
9
|
+
permissions.compact!
|
10
|
+
@list_create = {}
|
11
|
+
@list_read = {}
|
12
|
+
@list_update = {}
|
13
|
+
@list_delete = {}
|
14
|
+
@permissions = permissions
|
15
|
+
@permissions.freeze # avoid bypassing add_permissions/remove_permissions
|
16
|
+
add_permissions *permissions
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_permissions(*permissions)
|
20
|
+
@permissions += permissions
|
21
|
+
@permissions.freeze
|
22
|
+
permissions.each do |permission|
|
23
|
+
operations = permission.operations
|
24
|
+
add @list_create, permission if operations.nil? || operations.include?(CREATE)
|
25
|
+
add @list_read, permission if operations.nil? || operations.include?(READ)
|
26
|
+
add @list_update, permission if operations.nil? || operations.include?(UPDATE)
|
27
|
+
add @list_delete, permission if operations.nil? || operations.include?(DELETE)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def remove_permissions(*permissions)
|
32
|
+
@permissions -= permissions
|
33
|
+
@permissions.freeze
|
34
|
+
permissions.each do |permission|
|
35
|
+
operations = permission.operations
|
36
|
+
remove @list_create, permission if operations.nil? || operations.include?(CREATE)
|
37
|
+
remove @list_read, permission if operations.nil? || operations.include?(READ)
|
38
|
+
remove @list_update, permission if operations.nil? || operations.include?(UPDATE)
|
39
|
+
remove @list_delete, permission if operations.nil? || operations.include?(DELETE)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def add(list, permission)
|
46
|
+
(list[permission.klass] ||= []) << permission
|
47
|
+
end
|
48
|
+
|
49
|
+
def remove(list, permission)
|
50
|
+
(list[permission.klass] ||= []).delete permission
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Controlist
|
2
|
+
module Permissions
|
3
|
+
|
4
|
+
class SimpleConstrain < Constrain
|
5
|
+
|
6
|
+
def initialize(property, value, hash={})
|
7
|
+
self.property = property.to_s
|
8
|
+
self.value = value
|
9
|
+
self.relation = hash[:relation]
|
10
|
+
self.table_name = hash[:table_name]
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
data/lib/controlist.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require "controlist/version"
|
2
|
+
require "controlist/errors"
|
3
|
+
require "controlist/permission"
|
4
|
+
require "controlist/interceptor"
|
5
|
+
require "controlist/managers/base_manager"
|
6
|
+
|
7
|
+
module Controlist
|
8
|
+
|
9
|
+
class << self
|
10
|
+
|
11
|
+
attr_accessor :permission_provider, :attribute_proxy, :value_object_proxy, :logger
|
12
|
+
|
13
|
+
def initialize(permission_provider, config={})
|
14
|
+
@permission_provider = permission_provider
|
15
|
+
@attribute_proxy = config[:attribute_proxy] || "_val"
|
16
|
+
@value_object_proxy = config[:value_object_proxy] || "_value_object"
|
17
|
+
@logger = config[:logger] || Logger.new(STDOUT)
|
18
|
+
Interceptor.hook
|
19
|
+
end
|
20
|
+
|
21
|
+
def skip
|
22
|
+
@permission_provider.open_skip
|
23
|
+
yield
|
24
|
+
@permission_provider.close_skip
|
25
|
+
end
|
26
|
+
|
27
|
+
def is_activerecord3?
|
28
|
+
ActiveRecord::VERSION::MAJOR == 3
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|