effective_roles 1.5.1 → 2.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 3f7ade13d344604fbc2373ac6b02ed8e0d478d90
4
- data.tar.gz: 356f03271e88bcbaf3e094d3ea8aebf5b228c0f7
2
+ SHA256:
3
+ metadata.gz: c2e79ad753b38bbae083f785fe11328f944bdd41c2470751c9ab0e34249f35cc
4
+ data.tar.gz: 5846cd1d6ae353411d97e89b73e0fb97582a27a5a00a8272e2d3391a7a924287
5
5
  SHA512:
6
- metadata.gz: a6295fa39e152391c565f2f81b078461d07ecd3dea8baeee3b3067446ac7f5bff819062e39b536ec3eeaa7d463545db74d9e2f4d17a6b979e5fc820a2f97faa8
7
- data.tar.gz: 51e998f237d577534114355bb834a2611d8b809685054a7baf76101a5a7ea2f5fcadc3b96a21df6fde89577b24af4b7af8b24a4c1d5ec9ce514188c9540fe5c5
6
+ metadata.gz: 57c6dcaa1b1454f20f6ef6f813d9eb25fee44921cdec96b22c37e171ee8d8d52617d009fbc392847914079018c35f962b7579eb61fb99b89dbc51042f3c1fedb
7
+ data.tar.gz: 5bb3f94bce4141aa78a04968e1652824386c2dd2d9c0d1ef4af9d7db5087afdb4e084afaec10d97ac371a8a5ffb63c85002f4d1e9c7133a98763fc1cc412d0c4
@@ -1,4 +1,4 @@
1
- Copyright 2018 Code and Effect Inc.
1
+ Copyright 2019 Code and Effect Inc.
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -41,7 +41,8 @@ Add the mixin to an existing model:
41
41
 
42
42
  ```ruby
43
43
  class Post
44
- acts_as_role_restricted
44
+ acts_as_role_restricted # Singular role, use radio buttons
45
+ acts_as_role_restricted multiple: true # Multiple roles. use check boxes
45
46
  end
46
47
  ```
47
48
 
@@ -153,19 +154,15 @@ See the initializers/effective_roles.rb for more information.
153
154
  }
154
155
  ```
155
156
 
156
- When used in a Form Helper (see below), only the appropriate roles will be displayed.
157
+ When using assignable roles, you must assign the acts_as_role_restricted resource a `current_user` when saving changes to the roles or roles mask:
157
158
 
158
- However, this restriction is not enforced on the controller level, so someone could inspect & re-write the form parameters and still assign a role that they are not allowed to.
159
+ You can do this in one of three ways:
159
160
 
160
- To prevent this, add something like the following code to your controller:
161
+ 1. Setting resource.current_user = current_user in your controller directly.
162
+ 2. Add `before_action :set_effective_roles_current_user` to your ApplicationController
163
+ 3. Using `Effective::CrudController` to do this automatically.
161
164
 
162
- ```ruby
163
- before_filter :only => [:create, :update] do
164
- if params[:user] && params[:user][:roles]
165
- params[:user][:roles] = params[:user][:roles] & EffectiveRoles.assignable_roles_for(current_user, User.new()).map(&:to_s)
166
- end
167
- end
168
- ```
165
+ This restriction is only applied when running within the rails server. Not on rails console or db:seeds.
169
166
 
170
167
  ## Form Helper
171
168
 
@@ -173,6 +170,8 @@ If you pass current_user (or any acts_as_role_restricted object) into these help
173
170
 
174
171
  ### effective_form_with
175
172
 
173
+ Depending on your `acts_as_role_restricted multiple: ` value:
174
+
176
175
  ```ruby
177
176
  effective_form_with(model: @user) do |f|
178
177
  = f.checks :roles, EffectiveRoles.roles_collection(f.object, current_user)
@@ -187,12 +186,12 @@ effective_form_with(model: @user) do |f|
187
186
 
188
187
  ```ruby
189
188
  simple_form_for @user do |f|
190
- = f.input :roles, collection: EffectiveRoles.roles_collection(f.object), as: :check_boxes
189
+ = f.input :roles, collection: EffectiveRoles.roles_collection(f.object, current_user), as: :check_boxes
191
190
  ```
192
191
 
193
192
  ```ruby
194
193
  simple_form_for @user do |f|
195
- = f.input :roles, collection: EffectiveRoles.roles_collection(f.object, current_user), as: :check_boxes
194
+ = f.input :roles, collection: EffectiveRoles.roles_collection(f.object, current_user), as: :radio_buttons
196
195
  ```
197
196
 
198
197
  ### Strong Parameters
@@ -6,25 +6,48 @@
6
6
  #
7
7
  # Mark your model with 'acts_as_role_restricted'
8
8
  #
9
- # and create the migration
9
+ # and create the migration to add the following field:
10
10
  #
11
- # structure do
12
- # roles_mask :integer, :default => 0
13
- # end
11
+ # roles_mask :integer
14
12
  #
15
13
 
16
14
  module ActsAsRoleRestricted
17
15
  extend ActiveSupport::Concern
18
16
 
19
17
  module ActiveRecord
20
- def acts_as_role_restricted(*options)
21
- @acts_as_role_restricted_opts = options || []
18
+ def acts_as_role_restricted(multiple: false)
19
+ @acts_as_role_restricted_opts = { multiple: multiple }
22
20
  include ::ActsAsRoleRestricted
23
21
  end
24
22
  end
25
23
 
26
24
  included do
27
- validates :roles_mask, :numericality => true, :allow_nil => true
25
+ attr_accessor(:current_user) unless respond_to?(:current_user)
26
+
27
+ acts_as_role_restricted_options = @acts_as_role_restricted_opts.dup
28
+ self.send(:define_method, :acts_as_role_restricted_options) { acts_as_role_restricted_options }
29
+
30
+ validates :roles_mask, numericality: true, allow_nil: true
31
+
32
+ validate(if: -> { changes.include?(:roles_mask) }) do
33
+ user = current_user || EffectiveRoles.current_user || (EffectiveLogging.current_user if defined?(EffectiveLogging))
34
+
35
+ if user.blank? && EffectiveRoles.assignable_roles.present? && defined?(Rails::Server)
36
+ self.errors.add(:roles, 'current_user must be present when assigning roles')
37
+ end
38
+
39
+ roles_was = EffectiveRoles.roles_for(changes[:roles_mask].first)
40
+ changed = (roles + roles_was) - (roles & roles_was) # XOR
41
+
42
+ assignable = EffectiveRoles.assignable_roles_collection(self, user) # Returns all roles when user is blank
43
+ unauthorized = changed - assignable
44
+
45
+ authorized = roles.dup
46
+ unauthorized.each { |role| authorized.include?(role) ? authorized.delete(role) : authorized.push(role) }
47
+
48
+ self.roles_mask = EffectiveRoles.roles_mask_for(authorized)
49
+ end
50
+
28
51
  end
29
52
 
30
53
  module ClassMethods
@@ -45,16 +68,15 @@ module ActsAsRoleRestricted
45
68
 
46
69
  def with_role_sql(*roles)
47
70
  roles = roles.flatten.compact
48
- roles = roles.first.try(:roles) if roles.length == 1 and roles.first.respond_to?(:roles)
49
-
71
+ roles = roles.first.roles if roles.length == 1 && roles.first.respond_to?(:roles)
50
72
  roles = (roles.map { |role| role.to_sym } & EffectiveRoles.roles)
73
+
51
74
  roles.map { |role| "(#{self.table_name}.roles_mask & %d > 0)" % 2**EffectiveRoles.roles.index(role) }.join(' OR ')
52
75
  end
53
76
 
54
77
  def without_role(*roles)
55
78
  roles = roles.flatten.compact
56
- roles = roles.first.try(:roles) if roles.length == 1 and roles.first.respond_to?(:roles)
57
-
79
+ roles = roles.first.roles if roles.length == 1 && roles.first.respond_to?(:roles)
58
80
  roles = (roles.map { |role| role.to_sym } & EffectiveRoles.roles)
59
81
 
60
82
  where(
@@ -64,11 +86,11 @@ module ActsAsRoleRestricted
64
86
  end
65
87
 
66
88
  def roles=(roles)
67
- self.roles_mask = (Array(roles).flatten.map(&:to_sym) & EffectiveRoles.roles).map { |r| 2**EffectiveRoles.roles.index(r) }.sum
89
+ self.roles_mask = EffectiveRoles.roles_mask_for(roles)
68
90
  end
69
91
 
70
92
  def roles
71
- EffectiveRoles.roles.reject { |r| ((roles_mask || 0) & 2**EffectiveRoles.roles.index(r)).zero? }
93
+ EffectiveRoles.roles_for(roles_mask)
72
94
  end
73
95
 
74
96
  # if user.is? :admin
@@ -1,5 +1,5 @@
1
1
  EffectiveRoles.setup do |config|
2
- config.roles = [:superadmin, :admin, :member] # Only add to the end of this array. Never prepend roles.
2
+ config.roles = [:superadmin, :admin, :member] # Only add to the end of this array. Never prepend roles.
3
3
 
4
4
  # config.role_descriptions
5
5
  # ========================
@@ -34,6 +34,13 @@ EffectiveRoles.setup do |config|
34
34
  # When current_user is passed into a form helper function (see README.md)
35
35
  # this setting determines which roles that current_user may assign
36
36
  #
37
+ # you must assign current_user to all acts_as_role_restricted resources when saving changes to the roles or roles_mask.
38
+ #
39
+ # You should probably do this in your controller by one of the following methods:
40
+ # 1.) Setting resource.current_user = current_user directly.
41
+ # 2.) Using before_action :set_effective_roles_current_user
42
+ # 3.) Using Effective::CrudController does this automatically.
43
+ #
37
44
  # Use this Hash syntax if you want different permissions depending on the resource being editted
38
45
  #
39
46
  # config.assignable_roles = {
@@ -56,30 +63,6 @@ EffectiveRoles.setup do |config|
56
63
  # :member => [] # Members may not assign any roles
57
64
  # }
58
65
 
59
- # config.disabled_roles
60
- # Which roles should be displayed as disabled
61
- # =========================
62
- # Sometimes you don't want a role to be assignable (see README.md)
63
- # So that you can overload it yourself and assingn the role programatically
64
- #
65
- # Use this Hash syntax if you want different permissions depending on the resource being editted
66
- #
67
- # config.disabled_roles = {
68
- # 'User' => [:member] # When editing a User object, will be unable to assign the member role
69
- # 'Page' => [:superadmin, :admin] # When editing a Page object, will be unable to assign superadmin, admin role
70
- # }
71
- #
72
- # Or just keep it simple, and use this Array syntax of permissions for every resource
73
- #
74
- # config.disabled_roles = [:member]
75
- #
76
- # or
77
- #
78
- # config.disabled_roles = {
79
- # 'User' => [:member]
80
- # }
81
-
82
-
83
66
  # Authorization Method
84
67
  #
85
68
  # This doesn't have anything to do with the roles themselves.
@@ -4,12 +4,9 @@ require 'effective_roles/version'
4
4
  module EffectiveRoles
5
5
  mattr_accessor :roles
6
6
  mattr_accessor :role_descriptions
7
-
8
- mattr_accessor :layout
9
-
10
7
  mattr_accessor :assignable_roles
11
- mattr_accessor :disabled_roles
12
8
 
9
+ mattr_accessor :layout
13
10
  mattr_accessor :authorization_method
14
11
 
15
12
  def self.setup
@@ -37,6 +34,15 @@ module EffectiveRoles
37
34
  raise Effective::AccessDenied unless authorized?(controller, action, resource)
38
35
  end
39
36
 
37
+ # This is set by the "set_effective_roles_current_user" before_filter.
38
+ def self.current_user=(user)
39
+ @effective_roles_current_user = user
40
+ end
41
+
42
+ def self.current_user
43
+ @effective_roles_current_user
44
+ end
45
+
40
46
  # This method converts whatever is given into its roles
41
47
  # Pass an object, Integer, or Symbol to find corresponding role
42
48
  def self.roles_for(obj)
@@ -48,38 +54,67 @@ module EffectiveRoles
48
54
  [roles.find { |role| role == obj }].compact
49
55
  elsif obj.kind_of?(String)
50
56
  [roles.find { |role| role == obj.to_sym }].compact
57
+ elsif obj.kind_of?(Array)
58
+ obj.map { |obj| roles_for(obj) }.flatten.compact
51
59
  elsif obj.nil?
52
60
  []
53
61
  else
54
- raise 'unsupported object passed to EffectiveRoles.roles_for method. Expecting an acts_as_role_restricted object or a roles_mask integer'
62
+ raise 'unsupported object passed to EffectiveRoles.roles_for method. Expecting an acts_as_role_restricted object or a roles_mask integer'
55
63
  end
56
64
  end
57
65
 
58
66
  # EffectiveRoles.roles_mask_for(:admin, :member)
59
67
  def self.roles_mask_for(*roles)
60
- (Array(roles).flatten.map(&:to_sym) & EffectiveRoles.roles).map { |r| 2**EffectiveRoles.roles.index(r) }.sum
68
+ roles_for(roles).map { |r| 2**EffectiveRoles.roles.index(r) }.sum
61
69
  end
62
70
 
63
- def self.roles_collection(obj = nil, user = nil)
64
- assignable_roles_for(user, obj).map do |role|
71
+ def self.roles_collection(resource, current_user = nil, only: nil, except: nil, multiple: nil)
72
+ if assignable_roles.present?
73
+ raise('expected object to respond to is_role_restricted?') unless resource.respond_to?(:is_role_restricted?)
74
+ raise('expected current_user to respond to is_role_restricted?') if current_user && !current_user.respond_to?(:is_role_restricted?)
75
+ end
76
+
77
+ only = Array(only).compact
78
+ except = Array(except).compact
79
+ multiple = resource.acts_as_role_restricted_options[:multiple] if multiple.nil?
80
+ assignable = assignable_roles_collection(resource, current_user, multiple: multiple)
81
+
82
+ roles.map do |role|
83
+ next if only.present? && !only.include?(role)
84
+ next if except.present? && except.include?(role)
85
+
65
86
  [
66
- "#{role}<p class='help-block text-muted'>#{role_description(role, obj)}</p>".html_safe,
87
+ "#{role}<p class='help-block text-muted'>#{role_description(role, resource)}</p>".html_safe,
67
88
  role,
68
- ({:disabled => :disabled} if disabled_roles_for(obj).include?(role))
89
+ ({:disabled => :disabled} unless assignable.include?(role))
69
90
  ]
70
- end
91
+ end.compact
71
92
  end
72
93
 
73
- def self.assignable_roles_for(user, obj = nil)
74
- raise 'EffectiveRoles config.assignable_roles_for must be a Hash, Array or nil' unless [Hash, Array, NilClass].include?(assignable_roles.class)
75
-
76
- return assignable_roles if assignable_roles.kind_of?(Array)
94
+ def self.assignable_roles_collection(resource, current_user = nil, multiple: nil)
77
95
  return roles if assignable_roles.nil?
78
- return roles if !user.respond_to?(:is_role_restricted?) # All roles, if the user (or object) is not role_resticted
79
96
 
80
- assignable = assignable_roles[obj.try(:class).to_s] || assignable_roles || {}
97
+ raise 'EffectiveRoles config.assignable_roles_for must be a Hash, Array or nil' unless [Hash, Array].include?(assignable_roles.class)
98
+ raise('expected resource to respond to is_role_restricted?') unless resource.respond_to?(:is_role_restricted?)
99
+ raise('expected current_user to respond to is_role_restricted?') if current_user && !current_user.respond_to?(:is_role_restricted?)
100
+
101
+ multiple = resource.acts_as_role_restricted_options[:multiple] if multiple.nil?
102
+ current_user ||= (EffectiveRoles.current_user || (EffectiveLogging.current_user if defined?(EffectiveLogging)))
103
+
104
+ assignable = if assignable_roles.kind_of?(Array)
105
+ assignable_roles
106
+ elsif current_user.present?
107
+ current_roles = assignable_roles[resource.try(:class).to_s] || assignable_roles || {}
108
+ current_user.roles.map { |role| current_roles[role] }.flatten.compact.uniq
109
+ else
110
+ assignable_roles[resource.try(:class).to_s] || []
111
+ end
112
+
113
+ # Check boxes
114
+ return assignable if multiple
81
115
 
82
- user.roles.map { |role| assignable[role] }.flatten.compact.uniq
116
+ # Radios
117
+ (resource.roles - assignable).present? ? [] : assignable
83
118
  end
84
119
 
85
120
  # This is used by the effective_roles_summary_table helper method
@@ -142,19 +177,6 @@ module EffectiveRoles
142
177
  (role_descriptions[obj.try(:class).to_s] || {})[role] || role_descriptions[role] || ''
143
178
  end
144
179
 
145
- def self.disabled_roles_for(obj)
146
- raise 'EffectiveRoles config.disabled_roles must be a Hash, Array or nil' unless [Hash, Array, NilClass].include?(disabled_roles.class)
147
-
148
- case disabled_roles
149
- when Array
150
- disabled_roles
151
- when Hash
152
- Array(disabled_roles[obj.try(:class).to_s])
153
- else
154
- []
155
- end
156
- end
157
-
158
180
  def self._authorization_level(controller, role, resource, auth_method)
159
181
  resource = (resource.new() rescue resource) if resource.kind_of?(ActiveRecord::Base)
160
182
 
@@ -2,7 +2,7 @@ module EffectiveRoles
2
2
  class Engine < ::Rails::Engine
3
3
  engine_name 'effective_roles'
4
4
 
5
- config.autoload_paths += Dir["#{config.root}/app/models/concerns"]
5
+ config.autoload_paths += Dir["#{config.root}/app/models/concerns", "#{config.root}/lib/"]
6
6
 
7
7
  # Include acts_as_addressable concern and allow any ActiveRecord object to call it
8
8
  initializer 'effective_roles.active_record' do |app|
@@ -11,6 +11,16 @@ module EffectiveRoles
11
11
  end
12
12
  end
13
13
 
14
+ # Register the log_page_views concern so that it can be called in ActionController or elsewhere
15
+ initializer 'effective_logging.log_changes_action_controller' do |app|
16
+ Rails.application.config.to_prepare do
17
+ ActiveSupport.on_load :action_controller do
18
+ require 'effective_roles/set_current_user'
19
+ ActionController::Base.include(EffectiveRoles::SetCurrentUser::ActionController)
20
+ end
21
+ end
22
+ end
23
+
14
24
  # Set up our default configuration options.
15
25
  initializer "effective_roles.defaults", :before => :load_config_initializers do |app|
16
26
  eval File.read("#{config.root}/config/effective_roles.rb")
@@ -0,0 +1,15 @@
1
+ module EffectiveRoles
2
+ module SetCurrentUser
3
+ module ActionController
4
+
5
+ # Add me to your ApplicationController
6
+ # before_action :set_effective_roles_current_user
7
+
8
+ def set_effective_roles_current_user
9
+ EffectiveRoles.current_user = current_user
10
+ end
11
+
12
+ end
13
+ end
14
+ end
15
+
@@ -1,3 +1,3 @@
1
1
  module EffectiveRoles
2
- VERSION = '1.5.1'.freeze
2
+ VERSION = '2.0.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: effective_roles
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Code and Effect
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-08 00:00:00.000000000 Z
11
+ date: 2019-05-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -46,6 +46,7 @@ files:
46
46
  - config/routes.rb
47
47
  - lib/effective_roles.rb
48
48
  - lib/effective_roles/engine.rb
49
+ - lib/effective_roles/set_current_user.rb
49
50
  - lib/effective_roles/version.rb
50
51
  - lib/generators/effective_roles/install_generator.rb
51
52
  homepage: https://github.com/code-and-effect/effective_roles
@@ -67,8 +68,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
67
68
  - !ruby/object:Gem::Version
68
69
  version: '0'
69
70
  requirements: []
70
- rubyforge_project:
71
- rubygems_version: 2.4.5.1
71
+ rubygems_version: 3.0.3
72
72
  signing_key:
73
73
  specification_version: 4
74
74
  summary: Assign multiple roles to any User or other ActiveRecord object. Select only