erp_tech_svcs 3.1.0 → 3.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/app/models/group.rb CHANGED
@@ -140,15 +140,29 @@ class Group < ActiveRecord::Base
140
140
  end
141
141
 
142
142
  def role_class_capabilities
143
- roles.collect{|r| r.class_capabilities }.flatten.uniq.compact
143
+ scope_type = ScopeType.find_by_internal_identifier('class')
144
+ Capability.joins(:capability_type).joins(:capability_accessors).
145
+ where(:capability_accessors => { :capability_accessor_record_type => "SecurityRole" }).
146
+ where("capability_accessor_record_id IN (#{roles.select('security_roles.id').to_sql})").
147
+ where(:scope_type_id => scope_type.id)
144
148
  end
145
149
 
146
150
  def all_class_capabilities
147
- (role_class_capabilities + class_capabilities).uniq
151
+ scope_type = ScopeType.find_by_internal_identifier('class')
152
+ Capability.joins(:capability_type).joins(:capability_accessors).
153
+ where("(capability_accessors.capability_accessor_record_type = 'Group' AND
154
+ capability_accessor_record_id = (#{self.id})) OR
155
+ (capability_accessors.capability_accessor_record_type = 'SecurityRole' AND
156
+ capability_accessor_record_id IN (#{roles.select('security_roles.id').to_sql}))").
157
+ where(:scope_type_id => scope_type.id)
158
+ end
159
+
160
+ def all_uniq_class_capabilities
161
+ all_class_capabilities.all.uniq
148
162
  end
149
163
 
150
164
  def class_capabilities_to_hash
151
- all_class_capabilities.map {|capability|
165
+ all_uniq_class_capabilities.map {|capability|
152
166
  { :capability_type_iid => capability.capability_type.internal_identifier,
153
167
  :capability_resource_type => capability.capability_resource_type
154
168
  }
data/app/models/user.rb CHANGED
@@ -46,7 +46,7 @@ class User < ActiveRecord::Base
46
46
  passed_roles.flatten!
47
47
  passed_roles.each do |role|
48
48
  role_iid = role.is_a?(SecurityRole) ? role.internal_identifier : role.to_s
49
- all_roles.each do |this_role|
49
+ all_uniq_roles.each do |this_role|
50
50
  result = true if (this_role.internal_identifier == role_iid)
51
51
  break if result
52
52
  end
@@ -103,40 +103,70 @@ class User < ActiveRecord::Base
103
103
 
104
104
  # roles assigned to the groups this user belongs to
105
105
  def group_roles
106
- groups.collect{|g| g.roles }.flatten.uniq
106
+ SecurityRole.joins(:parties).
107
+ where(:parties => {:business_party_type => 'Group'}).
108
+ where("parties.business_party_id IN (#{groups.select('groups.id').to_sql})")
107
109
  end
108
110
 
109
111
  # composite roles for this user
110
112
  def all_roles
111
- (group_roles + roles).uniq
113
+ SecurityRole.joins(:parties).joins("LEFT JOIN users ON parties.id=users.party_id").
114
+ where("(parties.business_party_type='Group' AND
115
+ parties.business_party_id IN (#{groups.select('groups.id').to_sql})) OR
116
+ (users.id=#{self.id})")
117
+ end
118
+
119
+ def all_uniq_roles
120
+ all_roles.all.uniq
112
121
  end
113
122
 
114
123
  def group_capabilities
115
- groups.collect{|r| r.capabilities }.flatten.uniq.compact
124
+ Capability.joins(:capability_type).joins(:capability_accessors).
125
+ where(:capability_accessors => { :capability_accessor_record_type => "Group" }).
126
+ where("capability_accessor_record_id IN (#{groups.select('groups.id').to_sql})")
116
127
  end
117
128
 
118
129
  def role_capabilities
119
- all_roles.collect{|r| r.capabilities }.flatten.compact
130
+ Capability.joins(:capability_type).joins(:capability_accessors).
131
+ where(:capability_accessors => { :capability_accessor_record_type => "SecurityRole" }).
132
+ where("capability_accessor_record_id IN (#{all_roles.select('security_roles.id').to_sql})")
120
133
  end
121
134
 
122
135
  def all_capabilities
123
- (role_capabilities + group_capabilities + capabilities).uniq
136
+ Capability.joins(:capability_type).joins(:capability_accessors).
137
+ where("(capability_accessors.capability_accessor_record_type = 'Group' AND
138
+ capability_accessor_record_id IN (#{groups.select('groups.id').to_sql})) OR
139
+ (capability_accessors.capability_accessor_record_type = 'SecurityRole' AND
140
+ capability_accessor_record_id IN (#{all_roles.select('security_roles.id').to_sql})) OR
141
+ (capability_accessors.capability_accessor_record_type = 'User' AND
142
+ capability_accessor_record_id = #{self.id})")
143
+ end
144
+
145
+ def all_uniq_capabilities
146
+ all_capabilities.all.uniq
124
147
  end
125
148
 
126
149
  def group_class_capabilities
127
- groups.collect{|g| g.class_capabilities }.flatten.uniq.compact
150
+ scope_type = ScopeType.find_by_internal_identifier('class')
151
+ group_capabilities.where(:scope_type_id => scope_type.id)
128
152
  end
129
153
 
130
154
  def role_class_capabilities
131
- all_roles.collect{|r| r.class_capabilities }.flatten.uniq.compact
155
+ scope_type = ScopeType.find_by_internal_identifier('class')
156
+ role_capabilities.where(:scope_type_id => scope_type.id)
132
157
  end
133
158
 
134
159
  def all_class_capabilities
135
- (role_class_capabilities + group_class_capabilities + class_capabilities).uniq
160
+ scope_type = ScopeType.find_by_internal_identifier('class')
161
+ all_capabilities.where(:scope_type_id => scope_type.id)
162
+ end
163
+
164
+ def all_uniq_class_capabilities
165
+ all_class_capabilities.all.uniq
136
166
  end
137
167
 
138
168
  def class_capabilities_to_hash
139
- all_class_capabilities.map {|capability|
169
+ all_uniq_class_capabilities.map {|capability|
140
170
  { :capability_type_iid => capability.capability_type.internal_identifier,
141
171
  :capability_resource_type => capability.capability_resource_type
142
172
  }
@@ -77,7 +77,8 @@ module ErpTechSvcs
77
77
 
78
78
  # pass in (capability_type_iid, klass) or (capability) object
79
79
  def add_capability(*capability)
80
- capability = capability.first.is_a?(String) ? get_or_create_capability(capability.first, capability.second) : capability.first
80
+ capability_type_iid = capability.first.is_a?(Symbol) ? capability.first.to_s : capability.first
81
+ capability = capability_type_iid.is_a?(String) ? get_or_create_capability(capability_type_iid, capability.second) : capability.first
81
82
  ca = CapabilityAccessor.find_or_create_by_capability_accessor_record_type_and_capability_accessor_record_id_and_capability_id(get_superclass, self.id, capability.id)
82
83
  self.reload
83
84
  ca
@@ -89,8 +90,12 @@ module ErpTechSvcs
89
90
 
90
91
  def get_or_create_capability(capability_type_iid, klass)
91
92
  capability_type = convert_capability_type(capability_type_iid)
92
- scope_type = ScopeType.find_by_internal_identifier('class')
93
- Capability.find_or_create_by_capability_resource_type_and_capability_type_id_and_scope_type_id(klass, capability_type.id, scope_type.id)
93
+ if klass.is_a?(String)
94
+ scope_type = ScopeType.find_by_internal_identifier('class')
95
+ Capability.find_or_create_by_capability_resource_type_and_capability_type_id_and_scope_type_id(klass, capability_type.id, scope_type.id)
96
+ else
97
+ klass.add_capability(capability_type_iid) # create instance capability
98
+ end
94
99
  end
95
100
 
96
101
  def get_capability(capability_type_iid, klass)
@@ -101,7 +106,8 @@ module ErpTechSvcs
101
106
 
102
107
  # pass in (capability_type_iid, klass) or (capability) object
103
108
  def remove_capability(*capability)
104
- capability = capability.first.is_a?(String) ? get_or_create_capability(capability.first, capability.second) : capability.first
109
+ capability_type_iid = capability.first.is_a?(Symbol) ? capability.first.to_s : capability.first
110
+ capability = capability_type_iid.is_a?(String) ? get_or_create_capability(capability_type_iid, capability.second) : capability.first
105
111
  ca = capability_accessors.where(:capability_accessor_record_type => get_superclass, :capability_accessor_record_id => self.id, :capability_id => capability.id).first
106
112
  ca.destroy unless ca.nil?
107
113
  self.reload
@@ -9,15 +9,24 @@ module ErpTechSvcs
9
9
 
10
10
  module ClassMethods
11
11
 
12
- def protected_with_capabilities
12
+ def protected_with_capabilities(options = {})
13
13
  extend ProtectedByCapabilities::SingletonMethods
14
14
  include ProtectedByCapabilities::InstanceMethods
15
-
16
- has_many :capabilities, :as => :capability_resource
17
15
 
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
16
+ has_many :capabilities, :as => :capability_resource
17
+
18
+ # protect all instance of this class by default
19
+ class_attribute :protect_all_instances
20
+ self.protect_all_instances = (options[:protect_all_instances].nil? ? false : options[:protect_all_instances])
21
+
22
+ # Get records filtered via query scope capabilities
23
+ # By default Compass AE treats query scopes as restrictions
24
+ # A user will see all records unless the user has a capability accessor with a query scope
25
+ # If you set :protect_all_instances => true it is honored via with_user_security & with_instance_security but NOT with_query_security
26
+ # arguments: user, capability_type_iids
27
+ # capability_type_iids is optional and can be a single string or an array of strings
28
+ # Example: which files can this user download? FileAsset.with_query_security(user, 'download').all
29
+ # Example: which website sections can this user either view or edit? WebsiteSection.with_query_security(user, ['view','edit']).all
21
30
  scope :with_query_security, lambda{|*args|
22
31
  raise ArgumentError if args.empty? || args.size > 2
23
32
  user = args.first
@@ -25,11 +34,11 @@ module ErpTechSvcs
25
34
  capability_type_iids = [capability_type_iids] if capability_type_iids.is_a?(String)
26
35
 
27
36
  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
37
+ granted_capabilities = user.all_capabilities.where(:scope_type_id => scope_type.id).where(:capability_resource_type => self.name)
29
38
 
30
39
  unless capability_type_iids.empty?
31
40
  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
41
+ granted_capabilities = granted_capabilities.where("capability_type_id IN (?)", capability_type_ids.join(','))
33
42
  end
34
43
 
35
44
  query = nil
@@ -39,34 +48,45 @@ module ErpTechSvcs
39
48
  query
40
49
  }
41
50
 
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
+ # Get records for this model permitted via instance capabilities
52
+ # If :protect_all_instances => true return only instances user has explicitly been granted access to
53
+ # If :protect_all_instances => false return instances without capabilities or that user is granted access to (default)
51
54
  # arguments: user, capability_type_iids
52
55
  # 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
+ # Example: which files can this user download? FileAsset.with_instance_security(user, 'download').all
57
+ # Example: which website sections can this user either view or edit? WebsiteSection.with_instance_security(user, ['view','edit']).all
58
+ scope :with_instance_security, lambda{|*args|
56
59
  raise ArgumentError if args.empty? || args.size > 2
57
60
  user = args.first
58
61
  capability_type_iids = args.second || []
59
62
  capability_type_iids = [capability_type_iids] if capability_type_iids.is_a?(String)
60
63
 
61
64
  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
65
+ granted_capabilities = user.all_capabilities.where(:scope_type_id => scope_type.id).where(:capability_resource_type => self.name)
63
66
 
64
67
  unless capability_type_iids.empty?
65
68
  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
69
+ granted_capabilities = granted_capabilities.where("capability_type_id IN (#{capability_type_ids.join(',')})")
67
70
  end
68
-
69
- with_query_security(*args).with_instance_security(instance_capabilities - granted_capabilities)
71
+
72
+ denied_capabilities = instance_capabilities.select('capabilities.id').where("capabilities.id NOT IN (#{granted_capabilities.select('capabilities.id').to_sql})")
73
+ deny_count = denied_capabilities.count
74
+
75
+ join_type = (self.protect_all_instances ? 'JOIN' : 'LEFT JOIN')
76
+ query = joins("#{join_type} capabilities AS c ON c.capability_resource_id = #{self.table_name}.id AND c.capability_resource_type = '#{self.name}'").
77
+ group(columns.collect{|c| "#{self.table_name}.#{c.name}" })
78
+ query = (deny_count == 0 ? query.where("c.id IS NULL OR c.id = c.id") : query.where("c.id IS NULL OR c.id NOT IN (#{denied_capabilities.to_sql})"))
79
+ query
80
+ }
81
+
82
+ # Get records for this model that the given user has access to
83
+ # arguments: user, capability_type_iids
84
+ # capability_type_iids is optional and can be a single string or an array of strings
85
+ # Example: which files can this user download? FileAsset.with_user_security(user, 'download').all
86
+ # Example: which website sections can this user either view or edit? WebsiteSection.with_user_security(user, ['view','edit']).all
87
+ scope :with_user_security, lambda{|*args|
88
+ raise ArgumentError if args.empty? || args.size > 2
89
+ with_instance_security(*args).with_query_security(*args)
70
90
  }
71
91
  end
72
92
  end
@@ -102,9 +122,9 @@ module ErpTechSvcs
102
122
  capabilities.where(:scope_type_id => scope_type.id)
103
123
  end
104
124
 
105
- # collect unique roles on capabilities
125
+ # return unique roles on capabilities for this model
106
126
  def capability_roles
107
- capabilities.collect{|c| c.roles }.flatten.uniq
127
+ SecurityRole.joins(:capability_accessors => :capability).where(:capability_accessors => {:capabilities => {:capability_resource_type => get_superclass(self.name) }}).all.uniq
108
128
  end
109
129
 
110
130
  # add a class level capability (capability_resource_id will be NULL)
@@ -147,6 +167,11 @@ module ErpTechSvcs
147
167
 
148
168
  module InstanceMethods
149
169
 
170
+ # convenience method to access class method
171
+ def protect_all_instances
172
+ self.class.protect_all_instances
173
+ end
174
+
150
175
  def add_capability(capability_type_iid)
151
176
  capability_type = convert_capability_type(capability_type_iid)
152
177
  scope_type = ScopeType.find_by_internal_identifier('instance')
@@ -165,11 +190,11 @@ module ErpTechSvcs
165
190
  end
166
191
 
167
192
  def protected_with_capability?(capability_type_iid)
168
- !get_capability(capability_type_iid).nil?
193
+ !get_capability(capability_type_iid).nil? or protect_all_instances
169
194
  end
170
195
 
171
196
  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))
197
+ if (!self.protect_all_instances and !self.protected_with_capability?(capability_type_iid.to_s)) or (user and user.has_capability?(capability_type_iid.to_s, self))
173
198
  return true
174
199
  else
175
200
  return false
@@ -13,15 +13,17 @@ module ErpTechSvcs
13
13
  where(:capability_resource_type => klass).
14
14
  where(:scope_type_id => scope_type.id).
15
15
  where(:capability_types => {:internal_identifier => capability_type_iid}).first
16
+ return nil if capability.nil? # capability not found so return nil
16
17
  else
17
18
  scope_type = ScopeType.find_by_internal_identifier('instance')
18
19
  capability = klass.capabilities.joins(:capability_type).
19
20
  where(:scope_type_id => scope_type.id).
20
21
  where(:capability_types => {:internal_identifier => capability_type_iid}).first
21
- return true if capability.nil? # object is not secured, so return true
22
+ # if capability not found, we see if all instances are protected
23
+ # if all instance are protected, return false, otherwise true
24
+ return !klass.protect_all_instances if capability.nil?
22
25
  end
23
- result = all_capabilities.find{|c| c == capability }
24
- result.nil? ? false : true
26
+ all_capabilities.include?(capability)
25
27
  end
26
28
 
27
29
  # pass in (capability_type_iid, class name or any class instance, a block of code)
@@ -2,7 +2,7 @@ module ErpTechSvcs
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 3
4
4
  MINOR = 1
5
- TINY = 0
5
+ TINY = 1
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].compact.join('.')
8
8
  end
@@ -8,8 +8,8 @@ namespace :erp_tech_svcs do
8
8
 
9
9
  #sync shared
10
10
  puts "Syncing Shared Assets..."
11
- file_support.sync(File.join(file_support.root, '/images'), CompassAeInstance.first)
12
- file_support.sync(File.join(file_support.root, '/files'), CompassAeInstance.first)
11
+ file_support.sync(File.join(file_support.root, '/images'), CompassAeInstance.find_by_internal_identifier('base'))
12
+ file_support.sync(File.join(file_support.root, '/files'), CompassAeInstance.find_by_internal_identifier('base'))
13
13
  puts "Complete"
14
14
 
15
15
  #sync websites
@@ -4,17 +4,20 @@ describe ErpTechSvcs::Extensions::ActiveRecord::HasSecurityRoles do
4
4
  before(:all) do
5
5
  @user = FactoryGirl.create(:user)
6
6
  @user_2 = FactoryGirl.create(:user)
7
- @admin_role = SecurityRole.create(:description => 'Admin', :internal_identifier => 'admin')
8
- @employee_role = SecurityRole.create(:description => 'Employee', :internal_identifier => 'employee')
9
- @manager_role = SecurityRole.create(:description => 'Manager', :internal_identifier => 'manager')
7
+
8
+ @admin_role = SecurityRole.find_or_create_by_description_and_internal_identifier(:description => 'Admin', :internal_identifier => 'admin')
9
+ @employee_role = SecurityRole.find_or_create_by_description_and_internal_identifier(:description => 'Employee', :internal_identifier => 'employee')
10
+ @manager_role = SecurityRole.find_or_create_by_description_and_internal_identifier(:description => 'Manager', :internal_identifier => 'manager')
10
11
  end
11
12
 
12
13
  it "should allow you to add a role" do
13
14
  @user.add_role(@admin_role)
15
+ @user.remove_all_roles
14
16
  end
15
17
 
16
18
  it "should allow you to add multiple roles by Role instance or iid" do
17
19
  @user.add_roles(@admin_role, 'manager')
20
+ @user.remove_all_roles
18
21
  end
19
22
 
20
23
  it "should allow you to add multiple roles by array or arguments" do
@@ -35,11 +38,13 @@ describe ErpTechSvcs::Extensions::ActiveRecord::HasSecurityRoles do
35
38
  end
36
39
 
37
40
  it "should allow you to remove a role" do
41
+ @user.add_role(@admin_role)
38
42
  @user.remove_role(@admin_role)
39
43
  @user.has_role?(@admin_role).should eq false
40
44
  end
41
45
 
42
46
  it "should allow you to remove multiple roles by Role instance or iid" do
47
+ @user.add_roles(@admin_role, 'manager')
43
48
  @user.remove_roles(@employee_role, 'manager')
44
49
  @user.has_role?(@employee_role).should eq false
45
50
  @user.has_role?('manager').should eq false
data/spec/spec_helper.rb CHANGED
@@ -20,6 +20,8 @@ Spork.prefork do
20
20
  require File.expand_path(DUMMY_APP_ROOT + "/config/environment.rb", __FILE__)
21
21
 
22
22
  ActiveRecord::Base.configurations = YAML::load(IO.read(DUMMY_APP_ROOT + "/config/database.yml"))
23
+ `rake db:drop RAILS_ENV=spec`
24
+ `rake db:create RAILS_ENV=spec`
23
25
  ActiveRecord::Base.establish_connection(ENV["DB"] || "spec")
24
26
  ActiveRecord::Migration.verbose = false
25
27
 
@@ -43,12 +45,12 @@ Spork.each_run do
43
45
  #We have to execute the migrations from dummy app directory
44
46
  Dir.chdir DUMMY_APP_ROOT
45
47
  `rake db:drop RAILS_ENV=spec`
48
+ `rake db:create RAILS_ENV=spec`
46
49
  Dir.chdir ENGINE_RAILS_ROOT
47
50
 
48
51
  #We have to execute the migratiapp:compass_ae:install:data_migrationsons from dummy app directory
49
52
  Dir.chdir DUMMY_APP_ROOT
50
53
 
51
-
52
54
  `rake compass_ae:install:migrations RAILS_ENV=spec`
53
55
  `rake compass_ae:install:data_migrations RAILS_ENV=spec`
54
56
  `rake db:migrate RAILS_ENV=spec`
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: erp_tech_svcs
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 3.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-01-11 00:00:00.000000000 Z
12
+ date: 2013-02-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: erp_base_erp_svcs
@@ -305,18 +305,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
305
305
  - - ! '>='
306
306
  - !ruby/object:Gem::Version
307
307
  version: '0'
308
- segments:
309
- - 0
310
- hash: 697401427352755655
311
308
  required_rubygems_version: !ruby/object:Gem::Requirement
312
309
  none: false
313
310
  requirements:
314
311
  - - ! '>='
315
312
  - !ruby/object:Gem::Version
316
313
  version: '0'
317
- segments:
318
- - 0
319
- hash: 697401427352755655
320
314
  requirements: []
321
315
  rubyforge_project:
322
316
  rubygems_version: 1.8.24