effective_roles 1.5.1 → 2.0.0

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