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.
Files changed (38) hide show
  1. data/app/models/attribute_type.rb +1 -1
  2. data/app/models/capability.rb +54 -2
  3. data/app/models/capability_accessor.rb +4 -0
  4. data/app/models/extensions/party.rb +1 -0
  5. data/app/models/file_asset.rb +15 -5
  6. data/app/models/group.rb +149 -0
  7. data/app/models/scope_type.rb +5 -0
  8. data/app/models/security_role.rb +46 -0
  9. data/app/models/user.rb +99 -5
  10. data/db/data_migrations/20110109173616_create_capability_scope_types.rb +14 -0
  11. data/db/data_migrations/20121116155018_create_group_relationship_and_role_types.rb +19 -0
  12. data/db/data_migrations/20121130212146_note_capabilities.rb +23 -0
  13. data/db/migrate/20080805000010_base_tech_services.rb +44 -31
  14. data/db/migrate/20121116151510_create_groups.rb +18 -0
  15. data/db/migrate/20121126171612_upgrade_security.rb +53 -0
  16. data/db/migrate/20121126173506_upgrade_security2.rb +274 -0
  17. data/lib/erp_tech_svcs/engine.rb +7 -7
  18. data/lib/erp_tech_svcs/extensions/active_record/base.rb +17 -0
  19. data/lib/erp_tech_svcs/extensions/active_record/has_capability_accessors.rb +131 -0
  20. data/lib/erp_tech_svcs/extensions/active_record/has_file_assets.rb +9 -1
  21. data/lib/erp_tech_svcs/extensions/active_record/has_security_roles.rb +89 -0
  22. data/lib/erp_tech_svcs/extensions/active_record/protected_with_capabilities.rb +203 -0
  23. data/lib/erp_tech_svcs/extensions.rb +4 -2
  24. data/lib/erp_tech_svcs/file_support/manager.rb +1 -1
  25. data/lib/erp_tech_svcs/file_support/s3_manager.rb +3 -3
  26. data/lib/erp_tech_svcs/utils/compass_access_negotiator.rb +29 -24
  27. data/lib/erp_tech_svcs/version.rb +1 -1
  28. data/spec/lib/erp_tech_svcs/extensions/active_record/has_roles_spec.rb +4 -5
  29. data/spec/models/role_spec.rb +2 -2
  30. data/spec/models/secured_model_spec.rb +1 -1
  31. data/spec/models/user_spec.rb +2 -2
  32. metadata +137 -129
  33. data/app/models/capable_model.rb +0 -4
  34. data/app/models/role.rb +0 -17
  35. data/app/models/secured_model.rb +0 -15
  36. data/db/data_migrations/20120109173616_create_download_capability_type.rb +0 -13
  37. data/lib/erp_tech_svcs/extensions/active_record/has_capabilities.rb +0 -152
  38. 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/has_roles'
4
- require 'erp_tech_svcs/extensions/active_record/has_capabilities'
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.has_capabilities?
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.has_capabilities? ? :private : :public_read) unless file.nil?
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.has_capabilities? ? :private : :public_read) unless file.nil?
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.has_capabilities? ? :private : :public_read) unless file.nil?
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
- def has_capability?(model, capability_type, resource)
6
- result = true
7
- result = model.user_has_capability?(capability_type.to_s, resource, self) if model.has_capabilities?
8
- result
9
- end
10
-
11
- def with_capability(model, capability_type, resource, &block)
12
- if model.has_capabilities?
13
- if model.user_has_capability?(capability_type.to_s, resource, self)
14
- yield
15
- else
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
- yield
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
- def has_access_to_widget?(widget)
24
- widget.has_access?(self)
25
- end
26
-
27
- def valid_widgets(application)
28
- widgets = []
29
- application.widgets.each do |widget|
30
- widgets << widget if self.has_access_to_widget?(widget)
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
@@ -2,7 +2,7 @@ module ErpTechSvcs
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 3
4
4
  MINOR = 0
5
- TINY = 10
5
+ TINY = 11
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].compact.join('.')
8
8
  end
@@ -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 = Role.create(:internal_identifier => 'admin')
8
- @employee_role = Role.create(:internal_identifier => 'employee')
9
- @manager_role = Role.create(:internal_identifier => 'manager')
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
- Role.destroy_all
63
+ SecurityRole.destroy_all
65
64
  end
66
65
 
67
66
 
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe Role do
4
4
 
5
5
  before(:all) do
6
- @role = Role.create(:internal_identifier => 'employee')
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
- Role.destroy_all
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 = Role.create(:description => 'Admin', :internal_identifier => 'admin')
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
 
@@ -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 = Role.create(:internal_identifier => 'employee')
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
- Role.destroy_all
24
+ SecurityRole.destroy_all
25
25
  end
26
26
 
27
27
  end