erp_tech_svcs 3.0.10 → 3.0.11
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.
- data/app/models/attribute_type.rb +1 -1
- data/app/models/capability.rb +54 -2
- data/app/models/capability_accessor.rb +4 -0
- data/app/models/extensions/party.rb +1 -0
- data/app/models/file_asset.rb +15 -5
- data/app/models/group.rb +149 -0
- data/app/models/scope_type.rb +5 -0
- data/app/models/security_role.rb +46 -0
- data/app/models/user.rb +99 -5
- data/db/data_migrations/20110109173616_create_capability_scope_types.rb +14 -0
- data/db/data_migrations/20121116155018_create_group_relationship_and_role_types.rb +19 -0
- data/db/data_migrations/20121130212146_note_capabilities.rb +23 -0
- data/db/migrate/20080805000010_base_tech_services.rb +44 -31
- data/db/migrate/20121116151510_create_groups.rb +18 -0
- data/db/migrate/20121126171612_upgrade_security.rb +53 -0
- data/db/migrate/20121126173506_upgrade_security2.rb +274 -0
- data/lib/erp_tech_svcs/engine.rb +7 -7
- data/lib/erp_tech_svcs/extensions/active_record/base.rb +17 -0
- data/lib/erp_tech_svcs/extensions/active_record/has_capability_accessors.rb +131 -0
- data/lib/erp_tech_svcs/extensions/active_record/has_file_assets.rb +9 -1
- data/lib/erp_tech_svcs/extensions/active_record/has_security_roles.rb +89 -0
- data/lib/erp_tech_svcs/extensions/active_record/protected_with_capabilities.rb +203 -0
- data/lib/erp_tech_svcs/extensions.rb +4 -2
- data/lib/erp_tech_svcs/file_support/manager.rb +1 -1
- data/lib/erp_tech_svcs/file_support/s3_manager.rb +3 -3
- data/lib/erp_tech_svcs/utils/compass_access_negotiator.rb +29 -24
- data/lib/erp_tech_svcs/version.rb +1 -1
- data/spec/lib/erp_tech_svcs/extensions/active_record/has_roles_spec.rb +4 -5
- data/spec/models/role_spec.rb +2 -2
- data/spec/models/secured_model_spec.rb +1 -1
- data/spec/models/user_spec.rb +2 -2
- metadata +137 -129
- data/app/models/capable_model.rb +0 -4
- data/app/models/role.rb +0 -17
- data/app/models/secured_model.rb +0 -15
- data/db/data_migrations/20120109173616_create_download_capability_type.rb +0 -13
- data/lib/erp_tech_svcs/extensions/active_record/has_capabilities.rb +0 -152
- data/lib/erp_tech_svcs/extensions/active_record/has_roles.rb +0 -130
@@ -0,0 +1,203 @@
|
|
1
|
+
module ErpTechSvcs
|
2
|
+
module Extensions
|
3
|
+
module ActiveRecord
|
4
|
+
module ProtectedByCapabilities
|
5
|
+
|
6
|
+
def self.included(base)
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
|
12
|
+
def protected_with_capabilities
|
13
|
+
extend ProtectedByCapabilities::SingletonMethods
|
14
|
+
include ProtectedByCapabilities::InstanceMethods
|
15
|
+
|
16
|
+
has_many :capabilities, :as => :capability_resource
|
17
|
+
|
18
|
+
# get records filtered via query scope capabilities
|
19
|
+
# by default Compass AE treats query scopes as restrictions
|
20
|
+
# a user will see all records unless the user has a capability accessor with a query scope
|
21
|
+
scope :with_query_security, lambda{|*args|
|
22
|
+
raise ArgumentError if args.empty? || args.size > 2
|
23
|
+
user = args.first
|
24
|
+
capability_type_iids = args.second || []
|
25
|
+
capability_type_iids = [capability_type_iids] if capability_type_iids.is_a?(String)
|
26
|
+
|
27
|
+
scope_type = ScopeType.find_by_internal_identifier('query')
|
28
|
+
granted_capabilities = user.all_capabilities.collect{|c| c if c.scope_type_id == scope_type.id and c.capability_resource_type == self.name }.compact
|
29
|
+
|
30
|
+
unless capability_type_iids.empty?
|
31
|
+
capability_type_ids = capability_type_iids.collect{|type| convert_capability_type(type).id }
|
32
|
+
granted_capabilities = granted_capabilities.collect{|c| c if capability_type_ids.include?(c.capability_type_id)}.compact
|
33
|
+
end
|
34
|
+
|
35
|
+
query = nil
|
36
|
+
granted_capabilities.each do |scope_capability|
|
37
|
+
query = query.nil? ? where(scope_capability.scope_query) : query.where(scope_capability.scope_query)
|
38
|
+
end
|
39
|
+
query
|
40
|
+
}
|
41
|
+
|
42
|
+
# get records for this model without capabilities or that are not in a list of denied capabilities
|
43
|
+
scope :with_instance_security, lambda{|denied_capabilities|
|
44
|
+
query = joins("LEFT JOIN capabilities AS c ON c.capability_resource_id = #{self.table_name}.id AND c.capability_resource_type = '#{self.name}'").
|
45
|
+
group(columns.collect{|c| "#{self.table_name}.#{c.name}" })
|
46
|
+
query = (denied_capabilities.empty? ? query.where("c.id IS NULL OR c.id = c.id") : query.where("c.id IS NULL OR c.id NOT IN (?)", denied_capabilities.collect{|c| c.id }))
|
47
|
+
query
|
48
|
+
}
|
49
|
+
|
50
|
+
# get records for this model that the given user has access to
|
51
|
+
# arguments: user, capability_type_iids
|
52
|
+
# capability_type_iids is optional and can be a single string or an array of strings
|
53
|
+
# Example: which files can this user download? FileAsset.with_user_security(user, 'download').all
|
54
|
+
# Example: which website sections can this user either view or edit? WebsiteSection.with_user_security(user, ['view','edit']).all
|
55
|
+
scope :with_user_security, lambda{|*args|
|
56
|
+
raise ArgumentError if args.empty? || args.size > 2
|
57
|
+
user = args.first
|
58
|
+
capability_type_iids = args.second || []
|
59
|
+
capability_type_iids = [capability_type_iids] if capability_type_iids.is_a?(String)
|
60
|
+
|
61
|
+
scope_type = ScopeType.find_by_internal_identifier('instance')
|
62
|
+
granted_capabilities = user.all_capabilities.collect{|c| c if c.scope_type_id == scope_type.id and c.capability_resource_type == self.name }.compact
|
63
|
+
|
64
|
+
unless capability_type_iids.empty?
|
65
|
+
capability_type_ids = capability_type_iids.collect{|type| convert_capability_type(type).id }
|
66
|
+
granted_capabilities = granted_capabilities.collect{|c| c if capability_type_ids.include?(c.capability_type_id)}.compact
|
67
|
+
end
|
68
|
+
|
69
|
+
with_query_security(*args).with_instance_security(instance_capabilities - granted_capabilities)
|
70
|
+
}
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
module SingletonMethods
|
75
|
+
# class method to get all capabilities for this model
|
76
|
+
def capabilities
|
77
|
+
Capability.where('capability_resource_type = ?', get_superclass(self.name))
|
78
|
+
end
|
79
|
+
|
80
|
+
# class method to get all capabilities for this model
|
81
|
+
def all_capabilities
|
82
|
+
capabilities
|
83
|
+
end
|
84
|
+
|
85
|
+
# class method to get only class capabilities for this model
|
86
|
+
def class_capabilities
|
87
|
+
scope_capabilities('class')
|
88
|
+
end
|
89
|
+
|
90
|
+
# class method to get only query capabilities for this model
|
91
|
+
def query_capabilities
|
92
|
+
scope_capabilities('query')
|
93
|
+
end
|
94
|
+
|
95
|
+
# class method to get only instance capabilities for this model
|
96
|
+
def instance_capabilities
|
97
|
+
scope_capabilities('instance')
|
98
|
+
end
|
99
|
+
|
100
|
+
def scope_capabilities(scope_type_iid)
|
101
|
+
scope_type = ScopeType.find_by_internal_identifier(scope_type_iid)
|
102
|
+
capabilities.where(:scope_type_id => scope_type.id)
|
103
|
+
end
|
104
|
+
|
105
|
+
# collect unique roles on capabilities
|
106
|
+
def capability_roles
|
107
|
+
capabilities.collect{|c| c.roles }.flatten.uniq
|
108
|
+
end
|
109
|
+
|
110
|
+
# add a class level capability (capability_resource_id will be NULL)
|
111
|
+
# the purpose of this is primarily for actions like create where the record does not exist yet
|
112
|
+
# this will allow us to assign the create capability to a User or Role so that we can ask the question "can a user create a record for this model?"
|
113
|
+
def add_capability(capability_type_iid)
|
114
|
+
capability_type = convert_capability_type(capability_type_iid)
|
115
|
+
scope_type = ScopeType.find_by_internal_identifier('class')
|
116
|
+
Capability.find_or_create_by_capability_resource_type_and_capability_type_id_and_scope_type_id(get_superclass(self.name), capability_type.id, scope_type.id)
|
117
|
+
end
|
118
|
+
|
119
|
+
def protect_with_capability(capability_type_iid)
|
120
|
+
add_capability(capability_type_iid)
|
121
|
+
end
|
122
|
+
|
123
|
+
def get_capability(capability_type_iid)
|
124
|
+
capability_type = convert_capability_type(capability_type_iid)
|
125
|
+
scope_type = ScopeType.find_by_internal_identifier('class')
|
126
|
+
capabilities.where(:capability_resource_type => get_superclass(self.name), :capability_type_id => capability_type.id, :scope_type_id => scope_type.id).first
|
127
|
+
end
|
128
|
+
|
129
|
+
# remove a class level capability
|
130
|
+
def remove_capability(capability_type_iid)
|
131
|
+
capability = get_capability(capability_type_iid)
|
132
|
+
capability.destroy unless capability.nil?
|
133
|
+
end
|
134
|
+
|
135
|
+
def unprotect_with_capability(capability_type_iid)
|
136
|
+
remove_capability(capability_type_iid)
|
137
|
+
end
|
138
|
+
|
139
|
+
def convert_capability_type(type)
|
140
|
+
return type if type.is_a?(CapabilityType)
|
141
|
+
return nil unless (type.is_a?(String) || type.is_a?(Symbol))
|
142
|
+
ct = CapabilityType.find_by_internal_identifier(type.to_s)
|
143
|
+
return ct unless ct.nil?
|
144
|
+
CapabilityType.create(:internal_identifier => type.to_s, :description => type.to_s.titleize)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
module InstanceMethods
|
149
|
+
|
150
|
+
def add_capability(capability_type_iid)
|
151
|
+
capability_type = convert_capability_type(capability_type_iid)
|
152
|
+
scope_type = ScopeType.find_by_internal_identifier('instance')
|
153
|
+
capability = Capability.find_or_create_by_capability_resource_type_and_capability_resource_id_and_capability_type_id_and_scope_type_id(get_superclass, self.id, capability_type.id, scope_type.id)
|
154
|
+
self.reload
|
155
|
+
capability
|
156
|
+
end
|
157
|
+
|
158
|
+
def protect_with_capability(capability_type_iid)
|
159
|
+
add_capability(capability_type_iid)
|
160
|
+
end
|
161
|
+
|
162
|
+
def get_capability(capability_type_iid)
|
163
|
+
capability_type = convert_capability_type(capability_type_iid)
|
164
|
+
capabilities.where(:capability_type_id => capability_type.id).first
|
165
|
+
end
|
166
|
+
|
167
|
+
def protected_with_capability?(capability_type_iid)
|
168
|
+
!get_capability(capability_type_iid).nil?
|
169
|
+
end
|
170
|
+
|
171
|
+
def allow_access?(user, capability_type_iid)
|
172
|
+
if !self.protected_with_capability?(capability_type_iid.to_s) or (user and user.has_capability?(capability_type_iid.to_s, self))
|
173
|
+
return true
|
174
|
+
else
|
175
|
+
return false
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def remove_capability(capability_type_iid)
|
180
|
+
capability = get_capability(capability_type_iid)
|
181
|
+
capability.destroy unless capability.nil?
|
182
|
+
self.reload
|
183
|
+
capability
|
184
|
+
end
|
185
|
+
|
186
|
+
def unprotect_with_capability(capability_type_iid)
|
187
|
+
remove_capability(capability_type_iid)
|
188
|
+
end
|
189
|
+
|
190
|
+
def protected_with_capabilities?
|
191
|
+
!capabilities.empty?
|
192
|
+
end
|
193
|
+
|
194
|
+
private
|
195
|
+
def convert_capability_type(type)
|
196
|
+
self.class.convert_capability_type(type)
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
@@ -1,7 +1,9 @@
|
|
1
1
|
#active record extensions
|
2
|
+
require 'erp_tech_svcs/extensions/active_record/base'
|
2
3
|
require 'erp_tech_svcs/extensions/active_record/has_file_assets'
|
3
|
-
require 'erp_tech_svcs/extensions/active_record/
|
4
|
-
require 'erp_tech_svcs/extensions/active_record/
|
4
|
+
require 'erp_tech_svcs/extensions/active_record/has_security_roles'
|
5
|
+
require 'erp_tech_svcs/extensions/active_record/protected_with_capabilities'
|
6
|
+
require 'erp_tech_svcs/extensions/active_record/has_capability_accessors'
|
5
7
|
require 'erp_tech_svcs/extensions/active_record/acts_as_versioned'
|
6
8
|
require 'erp_tech_svcs/extensions/active_record/has_relational_dynamic_attributes'
|
7
9
|
|
@@ -93,7 +93,7 @@ module ErpTechSvcs
|
|
93
93
|
#attempt set security if this file is a file_asset model
|
94
94
|
file = files.find{|file| file.directory == parent[:id].gsub(root,'') and file.name == item[:text]}
|
95
95
|
unless file.nil?
|
96
|
-
child_hash[:isSecured] = file.
|
96
|
+
child_hash[:isSecured] = file.is_secured?
|
97
97
|
child_hash[:iconCls] = 'icon-document_lock' if child_hash[:isSecured]
|
98
98
|
child_hash[:size] = file.data_file_size
|
99
99
|
child_hash[:width] = file.width
|
@@ -99,7 +99,7 @@ module ErpTechSvcs
|
|
99
99
|
|
100
100
|
def update_file(path, content)
|
101
101
|
file = FileAsset.where(:name => ::File.basename(path)).where(:directory => ::File.dirname(path)).first
|
102
|
-
acl = (file.
|
102
|
+
acl = (file.is_secured? ? :private : :public_read) unless file.nil?
|
103
103
|
options = (file.nil? ? {} : {:acl => acl, :content_type => file.content_type })
|
104
104
|
path = path.sub(%r{^/},'')
|
105
105
|
bucket.objects[path].write(content, options)
|
@@ -127,7 +127,7 @@ module ErpTechSvcs
|
|
127
127
|
message = FILE_DOES_NOT_EXIST
|
128
128
|
else
|
129
129
|
file = FileAsset.where(:name => ::File.basename(path)).where(:directory => ::File.dirname(path)).first
|
130
|
-
acl = (file.
|
130
|
+
acl = (file.is_secured? ? :private : :public_read) unless file.nil?
|
131
131
|
options = (file.nil? ? {} : {:acl => acl})
|
132
132
|
name = File.basename(path)
|
133
133
|
path = path.sub(%r{^/},'')
|
@@ -154,7 +154,7 @@ module ErpTechSvcs
|
|
154
154
|
new_path = path_pieces.join('/')
|
155
155
|
begin
|
156
156
|
file = FileAsset.where(:name => ::File.basename(path)).where(:directory => ::File.dirname(path)).first
|
157
|
-
acl = (file.
|
157
|
+
acl = (file.is_secured? ? :private : :public_read) unless file.nil?
|
158
158
|
options = (file.nil? ? {} : {:acl => acl})
|
159
159
|
path = path.sub(%r{^/},'')
|
160
160
|
new_path = new_path.sub(%r{^/},'')
|
@@ -2,36 +2,41 @@ module ErpTechSvcs
|
|
2
2
|
module Utils
|
3
3
|
module CompassAccessNegotiator
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
raise ErpTechSvcs::Utils::CompassAccessNegotiator::Errors::UserDoesNotHaveCapability
|
17
|
-
end
|
5
|
+
# pass in (capability_type_iid, class name) or (capability_type_iid, any class instance)
|
6
|
+
# Example: can user upload files? user.has_capability?('upload', 'FileAsset')
|
7
|
+
# Example: can download this file? user.has_capability?('download', file_asset)
|
8
|
+
def has_capability?(capability_type_iid, klass)
|
9
|
+
capability_type_iid = capability_type_iid.to_s if capability_type_iid.is_a?(Symbol)
|
10
|
+
if klass.is_a?(String)
|
11
|
+
scope_type = ScopeType.find_by_internal_identifier('class')
|
12
|
+
capability = Capability.joins(:capability_type).
|
13
|
+
where(:capability_resource_type => klass).
|
14
|
+
where(:scope_type_id => scope_type.id).
|
15
|
+
where(:capability_types => {:internal_identifier => capability_type_iid}).first
|
18
16
|
else
|
19
|
-
|
17
|
+
scope_type = ScopeType.find_by_internal_identifier('instance')
|
18
|
+
capability = klass.capabilities.joins(:capability_type).
|
19
|
+
where(:scope_type_id => scope_type.id).
|
20
|
+
where(:capability_types => {:internal_identifier => capability_type_iid}).first
|
21
|
+
return true if capability.nil? # object is not secured, so return true
|
20
22
|
end
|
23
|
+
result = all_capabilities.find{|c| c == capability }
|
24
|
+
result.nil? ? false : true
|
21
25
|
end
|
22
26
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
# pass in (capability_type_iid, class name or any class instance, a block of code)
|
28
|
+
# Example: do something if user can download this file:
|
29
|
+
# user.with_capability('download', file_asset) do
|
30
|
+
# something
|
31
|
+
# end
|
32
|
+
def with_capability(capability_type_iid, klass_instance, &block)
|
33
|
+
if self.has_capability?(capability_type_iid, klass_instance)
|
34
|
+
yield
|
35
|
+
else
|
36
|
+
raise ErpTechSvcs::Utils::CompassAccessNegotiator::Errors::UserDoesNotHaveCapability
|
31
37
|
end
|
32
|
-
widgets
|
33
38
|
end
|
34
|
-
|
39
|
+
|
35
40
|
module Errors
|
36
41
|
class CapabilityDoesNotExist < StandardError
|
37
42
|
def to_s
|
@@ -4,9 +4,9 @@ describe ErpTechSvcs::Extensions::ActiveRecord::HasRoles do
|
|
4
4
|
before(:all) do
|
5
5
|
@user = Factory(:user)
|
6
6
|
@user_2 = Factory(:user)
|
7
|
-
@admin_role =
|
8
|
-
@employee_role =
|
9
|
-
@manager_role =
|
7
|
+
@admin_role = SecurityRole.create(:internal_identifier => 'admin')
|
8
|
+
@employee_role = SecurityRole.create(:internal_identifier => 'employee')
|
9
|
+
@manager_role = SecurityRole.create(:internal_identifier => 'manager')
|
10
10
|
end
|
11
11
|
|
12
12
|
it "should allow you to add a role" do
|
@@ -54,14 +54,13 @@ describe ErpTechSvcs::Extensions::ActiveRecord::HasRoles do
|
|
54
54
|
it "should allow you to check access for user on model" do
|
55
55
|
@user.add_roles(@admin_role)
|
56
56
|
@user_2.add_roles(@admin_role)
|
57
|
-
@user_2.has_access?(@user).should eq true
|
58
57
|
@user.remove_all_roles
|
59
58
|
@user_2.remove_all_roles
|
60
59
|
end
|
61
60
|
|
62
61
|
after(:all) do
|
63
62
|
User.destroy_all
|
64
|
-
|
63
|
+
SecurityRole.destroy_all
|
65
64
|
end
|
66
65
|
|
67
66
|
|
data/spec/models/role_spec.rb
CHANGED
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe Role do
|
4
4
|
|
5
5
|
before(:all) do
|
6
|
-
@role =
|
6
|
+
@role = SecurityRole.create(:internal_identifier => 'employee')
|
7
7
|
end
|
8
8
|
|
9
9
|
it "can transform into xml" do
|
@@ -11,7 +11,7 @@ describe Role do
|
|
11
11
|
end
|
12
12
|
|
13
13
|
after(:all) do
|
14
|
-
|
14
|
+
SecurityRole.destroy_all
|
15
15
|
end
|
16
16
|
|
17
17
|
end
|
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe SecuredModel do
|
4
4
|
|
5
5
|
it "can find models by Class and role" do
|
6
|
-
admin_role =
|
6
|
+
admin_role = SecurityRole.create(:description => 'Admin', :internal_identifier => 'admin')
|
7
7
|
admin_user = User.create(:username => "admin",:email => "admin@portablemind.com")
|
8
8
|
admin_user.add_role('admin')
|
9
9
|
|
data/spec/models/user_spec.rb
CHANGED
@@ -7,7 +7,7 @@ describe User do
|
|
7
7
|
end
|
8
8
|
|
9
9
|
it "should allow you to add and remove roles" do
|
10
|
-
role =
|
10
|
+
role = SecurityRole.create(:internal_identifier => 'employee')
|
11
11
|
@user.add_role(role)
|
12
12
|
@user.has_role?(role).should eq true
|
13
13
|
@user.remove_role(role)
|
@@ -21,7 +21,7 @@ describe User do
|
|
21
21
|
|
22
22
|
after(:all) do
|
23
23
|
User.destroy_all
|
24
|
-
|
24
|
+
SecurityRole.destroy_all
|
25
25
|
end
|
26
26
|
|
27
27
|
end
|