erp_tech_svcs 3.0.10 → 3.0.11

Sign up to get free protection for your applications and to get access to all the features.
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