right_on 0.1.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 +7 -0
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/.travis.yml +14 -0
- data/Gemfile +7 -0
- data/LICENSE +21 -0
- data/README.md +60 -0
- data/Rakefile +12 -0
- data/db/migration.rb +34 -0
- data/db/rights_roles.yml +16 -0
- data/gemfiles/rails3.gemfile +7 -0
- data/gemfiles/rails4.gemfile +6 -0
- data/lib/right_on/action_controller_extensions.rb +68 -0
- data/lib/right_on/generators/USAGE +8 -0
- data/lib/right_on/generators/right_migration_generator.rb +59 -0
- data/lib/right_on/generators/templates/right_migration.rb +32 -0
- data/lib/right_on/permission_denied_response.rb +43 -0
- data/lib/right_on/rails.rb +5 -0
- data/lib/right_on/railtie.rb +12 -0
- data/lib/right_on/restricted_by_right.rb +56 -0
- data/lib/right_on/right.rb +171 -0
- data/lib/right_on/rights_manager.rb +140 -0
- data/lib/right_on/role.rb +16 -0
- data/lib/right_on/role_model.rb +33 -0
- data/lib/right_on/tasks/rights_roles.rake +12 -0
- data/lib/right_on/tasks/seeds_rights.rake +28 -0
- data/lib/right_on/version.rb +3 -0
- data/lib/right_on.rb +12 -0
- data/right_on.gemspec +33 -0
- data/spec/action_controller_extensions_spec.rb +34 -0
- data/spec/right_on_spec.rb +206 -0
- data/spec/role_model_spec.rb +45 -0
- data/spec/schema.rb +44 -0
- data/spec/spec_helper.rb +42 -0
- data/spec/support/bootstrap.rb +15 -0
- data/spec/support/coverage_loader.rb +26 -0
- data/views/_edit_rights.html.haml +15 -0
- data/views/_rights_roles_matrix.html.haml +24 -0
- metadata +268 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0d604e3a0d52e8dd395888dce6b1d201138ea8b6
|
4
|
+
data.tar.gz: 62d8d15da688d03422d5a31b3641a577f92ec42b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 59b6d8bbf60c6328bfc0753b622a05603d53362a762f47f490b1cf39cf8f083f4e0423616fdc28f99e088db2d77d16052e44f4ea8fc8f99daf71e990aab9b3b0
|
7
|
+
data.tar.gz: 829f2b6d9050d1cd92b38f9e70cda0a41e325b72d35cc77443e2e70a17c382f3718c983ca41799eec0177131876ec0ba45b49d0c9018a9edba488552ad044173
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.3.0
|
4
|
+
script: "bundle exec rake spec"
|
5
|
+
gemfile:
|
6
|
+
- gemfiles/rails3.gemfile
|
7
|
+
- gemfiles/rails4.gemfile
|
8
|
+
notifications:
|
9
|
+
email:
|
10
|
+
- support@travellink.com.au
|
11
|
+
flowdock:
|
12
|
+
secure: TZTbtSK+LDly7dRu0eYE3oro7fH0dktkyzeRo67ofPqO5Xdor6U6Eh2njeq6vqiL9tI+2V1MLv90I9tcLrbiz609sY6r+4byL8fQoDRjXMYcOr8bOcOSMldmKgf/42XmxgcFl9Xh+lWkdIdL9e4xdtsytbuK/EVGfA/DEW7E7DE=
|
13
|
+
sudo: false
|
14
|
+
cache: bundler
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) Tom Preston-Werner
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
Right On
|
2
|
+
========
|
3
|
+
|
4
|
+
[](https://travis-ci.org/sealink/right_on)
|
5
|
+
[](https://coveralls.io/r/sealink/right_on)
|
6
|
+
[](https://gemnasium.com/sealink/right_on)
|
7
|
+
[](https://codeclimate.com/github/sealink/right_on)
|
8
|
+
|
9
|
+
|
10
|
+
# DESCRIPTION
|
11
|
+
|
12
|
+
Gives rails applications a way to manage rights/roles
|
13
|
+
|
14
|
+
If you have a class User, then you can use it like so:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
class User < ActiveRecord::Base
|
18
|
+
include RightOn::RoleModel
|
19
|
+
end
|
20
|
+
```
|
21
|
+
|
22
|
+
This will create a many-to-many relationship with roles
|
23
|
+
|
24
|
+
Roles are sets of rights. Generally people will have multiple roles
|
25
|
+
e.g. A senior bank teller might have the following roles:
|
26
|
+
* Senior Bank Teller
|
27
|
+
* Bank Teller
|
28
|
+
* Bank Employee
|
29
|
+
|
30
|
+
The Role class also has a many-to-many relationship with rights
|
31
|
+
|
32
|
+
So a bank employee might have access to the building during regular hours
|
33
|
+
e.g. has a right 'transactions/add' giving him access to the add method of the transactions controller
|
34
|
+
|
35
|
+
Wheras the senior bank teller might be the only one with the 'tellers/create'
|
36
|
+
Thus he is the only one who can create new tellers.
|
37
|
+
|
38
|
+
There are a few types of rights:
|
39
|
+
* Rights giving access to an entire controller (tellers)
|
40
|
+
* Rights giving access to a single action within a controller (e.g. tellers/show)
|
41
|
+
* Rights giving access to multiple actions within a controller (e.g. tellers/read_only or tellers/read_write)
|
42
|
+
* Rights giving access to particular objects, e.g. a right gives you access to contact clients with a type "High Value Clients"
|
43
|
+
* Rights giving custom access. To have affect you need to use the has_right? Helper in you views
|
44
|
+
|
45
|
+
RightOn comes with controller methods to verify if the user has rights. Simply add the following in your app to controllers
|
46
|
+
you want to enforce rights:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
include RightOn::ActionControllerExtensions
|
50
|
+
|
51
|
+
before_filter :verify_rights
|
52
|
+
```
|
53
|
+
|
54
|
+
This will enforce that you have a right matching the controllers right
|
55
|
+
You must have a method "current_user" which is the user model that you've made as the RoleModel
|
56
|
+
|
57
|
+
# INSTALLATION
|
58
|
+
|
59
|
+
Add to your Gemfile:
|
60
|
+
gem 'right_on'
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
desc 'Default: run specs.'
|
4
|
+
task :default => :spec
|
5
|
+
|
6
|
+
require 'rspec/core/rake_task'
|
7
|
+
|
8
|
+
desc "Run specs"
|
9
|
+
RSpec::Core::RakeTask.new do |t|
|
10
|
+
t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
|
11
|
+
# Put spec opts in a file named .rspec in root
|
12
|
+
end
|
data/db/migration.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
class RightOnMigration < ActiveRecord::Migration
|
2
|
+
def self.change
|
3
|
+
create_table :rights do |t|
|
4
|
+
t.string :name, :controller, :action, :limit => 150
|
5
|
+
t.timestamps
|
6
|
+
end
|
7
|
+
|
8
|
+
change_table :rights do |t|
|
9
|
+
t.index :action
|
10
|
+
t.index :name
|
11
|
+
t.index [:controller, :action]
|
12
|
+
end
|
13
|
+
|
14
|
+
create_table :rights_roles, :id => false do |t|
|
15
|
+
t.integer :right_id, :role_id
|
16
|
+
end
|
17
|
+
|
18
|
+
change_table :rights_roles do |t|
|
19
|
+
t.index [:right_id, :role_id]
|
20
|
+
t.index [:role_id, :right_id]
|
21
|
+
end
|
22
|
+
|
23
|
+
create_table :roles do |t|
|
24
|
+
t.string :title
|
25
|
+
t.text :description
|
26
|
+
t.integer :right_id
|
27
|
+
t.timestamps
|
28
|
+
end
|
29
|
+
|
30
|
+
change_table :roles do |t|
|
31
|
+
t.index :right_id
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/db/rights_roles.yml
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
module RightOn
|
2
|
+
|
3
|
+
module ActionControllerExtensions
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.module_eval do
|
7
|
+
helper_method :access_allowed?, :access_allowed_to_controller?
|
8
|
+
class_attribute :rights_from
|
9
|
+
class_attribute :permission_denied_layout
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Checks the access privilege of the user and renders permission_denied page if required
|
14
|
+
def verify_rights
|
15
|
+
access_allowed?(controller_action_options) || permission_denied
|
16
|
+
end
|
17
|
+
|
18
|
+
# Checks the access privilege for a controller
|
19
|
+
def access_allowed_to_controller?(controller)
|
20
|
+
controller_class = "#{controller.to_s.camelcase}Controller".safe_constantize
|
21
|
+
|
22
|
+
# Handle inheritance of rights
|
23
|
+
if controller_class && controller_class.rights_from.present?
|
24
|
+
controller = controller_class.rights_from.to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
access_allowed?(controller)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Checks the access privilege of the user and returns true or false
|
31
|
+
def access_allowed?(opts={})
|
32
|
+
if opts.is_a?(String)
|
33
|
+
controller, action = opts.split('#')
|
34
|
+
opts = {:controller => controller, :action => action}
|
35
|
+
end
|
36
|
+
opts[:controller] ||= params[:controller]
|
37
|
+
opts[:action] ||= params[:action]
|
38
|
+
current_user.rights.any? { |r| r.allowed?(opts.slice(:controller, :action)) }
|
39
|
+
end
|
40
|
+
|
41
|
+
# Called if a security check determines permission is denied
|
42
|
+
def permission_denied
|
43
|
+
@permission_denied_response = RightOn::PermissionDeniedResponse.new(params, controller_action_options)
|
44
|
+
|
45
|
+
respond_to do |format|
|
46
|
+
format.html { render status: 401, template: 'permission_denied', layout: (permission_denied_layout || false) }
|
47
|
+
format.json do
|
48
|
+
render status: 401, json: @permission_denied_response.to_json
|
49
|
+
end
|
50
|
+
format.js do
|
51
|
+
render :update, status: 401 do |page|
|
52
|
+
page.alert(@permission_denied_layout.text_message)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
false
|
58
|
+
end
|
59
|
+
|
60
|
+
def controller_action_options
|
61
|
+
opts = params.slice(:controller, :action)
|
62
|
+
opts[:controller] = rights_from.to_s if rights_from
|
63
|
+
opts
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
Description:
|
2
|
+
Generates a new database migration for adding a right.
|
3
|
+
Pass the right name and an optional list of attribute pairs as arguments.
|
4
|
+
|
5
|
+
A migration class is generated in db/migrate prefixed by a timestamp of the current date and time.
|
6
|
+
|
7
|
+
Example:
|
8
|
+
`rails generate right_on:right_migration my_controllers/my_new_controller#my_new_action`
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module RightOn
|
2
|
+
module Generators
|
3
|
+
class RightMigrationGenerator < Rails::Generators::Base
|
4
|
+
|
5
|
+
include Rails::Generators::Migration
|
6
|
+
|
7
|
+
source_root File.expand_path('../templates', __FILE__)
|
8
|
+
|
9
|
+
argument :name, :type => :string, :required => false
|
10
|
+
|
11
|
+
class_option :controller, :type => :string, :required => false,
|
12
|
+
:desc => "Indicates the right's controller"
|
13
|
+
class_option :action, :type => :string, :required => false,
|
14
|
+
:desc => "Indicates the right's action"
|
15
|
+
class_option :right, :type => :string, :required => false,
|
16
|
+
:desc => "Indicates an existing right. Any role including this right will also include the new right"
|
17
|
+
|
18
|
+
|
19
|
+
def generate_migration
|
20
|
+
raise ArgumentError, "Either name or controller must be specified" if right_controller.blank?
|
21
|
+
migration_template "right_migration.rb", "db/migrate/add_#{parsed_right_name}_right.rb"
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
# Implement the required interface for Rails::Generators::Migration.
|
26
|
+
def self.next_migration_number(dirname) #:nodoc:
|
27
|
+
next_migration_number = current_migration_number(dirname) + 1
|
28
|
+
ActiveRecord::Migration.next_migration_number(next_migration_number)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def right_controller
|
34
|
+
(options[:controller] || name.to_s.split('#')[0]).to_s.underscore.presence
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def right_action
|
39
|
+
(options[:action] || name.to_s.split('#')[1]).to_s.underscore.presence
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
def right_name
|
44
|
+
name.presence || [right_controller, right_action].compact.join('#')
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
def right_for_roles
|
49
|
+
options[:right]
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def parsed_right_name
|
54
|
+
right_name.gsub('/','_').gsub('#','_').underscore
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class Add<%= parsed_right_name.camelize %>Right < ActiveRecord::Migration
|
2
|
+
|
3
|
+
class Right < ActiveRecord::Base
|
4
|
+
has_and_belongs_to_many :roles
|
5
|
+
|
6
|
+
validates_presence_of :name
|
7
|
+
validates_uniqueness_of :name
|
8
|
+
end
|
9
|
+
|
10
|
+
class Role < ActiveRecord::Base
|
11
|
+
has_and_belongs_to_many :rights
|
12
|
+
|
13
|
+
validates_presence_of :title
|
14
|
+
validates_uniqueness_of :title
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.up
|
18
|
+
right_for_roles = Right.find_by_name("<%= right_for_roles %>")
|
19
|
+
Right.create(
|
20
|
+
:controller => '<%= right_controller %>'.presence,
|
21
|
+
:action => '<%= right_action %>'.presence,
|
22
|
+
:name => '<%= right_name %>'.presence,
|
23
|
+
:roles => right_for_roles.roles
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
def self.down
|
29
|
+
Right.destroy_all(:name => '<%= right_name %>')
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module RightOn
|
2
|
+
class PermissionDeniedResponse
|
3
|
+
attr_reader :right_allowed, :roles_allowed, :controller_name
|
4
|
+
|
5
|
+
def initialize(params, controller_action_options)
|
6
|
+
@params = params
|
7
|
+
@right_allowed = Right.all.detect{|right| right.allowed?(controller_action_options)}
|
8
|
+
@roles_allowed = @right_allowed.roles if @right_allowed
|
9
|
+
@controller_name = @params[:controller] unless @right_allowed
|
10
|
+
end
|
11
|
+
|
12
|
+
def text_message
|
13
|
+
if @right_allowed
|
14
|
+
<<-MESSAGE
|
15
|
+
You are not authorised to perform the requested operation.
|
16
|
+
Right required: #{@right_allowed}
|
17
|
+
This right is given to the following roles: #{@roles_allowed.map(&:title).join(", ")}.
|
18
|
+
Contact your system manager to be given this right.
|
19
|
+
MESSAGE
|
20
|
+
else
|
21
|
+
no_right_for_page
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_json
|
26
|
+
{
|
27
|
+
error: 'Permission Denied',
|
28
|
+
right_allowed: (@right_allowed ? @right_allowed.name : no_right_for_page),
|
29
|
+
roles_for_right: (@roles_allowed ? @roles_allowed.map(&:title) : no_roles_for_page)
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def no_right_for_page
|
36
|
+
"No right is defined for this page: #{@controller_name}. Contact your system manager to notify this problem."
|
37
|
+
end
|
38
|
+
|
39
|
+
def no_roles_for_page
|
40
|
+
'N/A (as no right is assigned for this action)'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class Railtie < Rails::Railtie
|
2
|
+
initializer 'right_on.initialize' do
|
3
|
+
ActiveSupport.on_load(:active_record) do
|
4
|
+
require 'right_on/rails'
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
rake_tasks do
|
9
|
+
load "right_on/tasks/seeds_rights.rake"
|
10
|
+
load "right_on/tasks/rights_roles.rake"
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module RightOn
|
2
|
+
module RestrictedByRight
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def restricted_by_right(options = {})
|
10
|
+
options ||= {}
|
11
|
+
group = options.fetch(:group, 'other')
|
12
|
+
|
13
|
+
@right_on_config ||= {}
|
14
|
+
@right_on_config[:restricted_by_right_group] = group
|
15
|
+
|
16
|
+
Right.associate_group(self, group)
|
17
|
+
|
18
|
+
class << self
|
19
|
+
def accessible_to(user)
|
20
|
+
all.select{|o| user.rights.include?(o.right)}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
include InstanceMethods
|
25
|
+
|
26
|
+
belongs_to :right, :class_name => 'RightOn::Right'
|
27
|
+
before_create :create_access_right!
|
28
|
+
after_destroy :destroy_access_right!
|
29
|
+
end
|
30
|
+
|
31
|
+
def restricted_by_right_group
|
32
|
+
(@right_on_config || {})[:restricted_by_right_group]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module InstanceMethods
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def create_access_right!
|
41
|
+
right_name = "#{self.class.name.titleize}: #{name}"
|
42
|
+
self.right = find_right(right_name) || Right.create!(:name => right_name)
|
43
|
+
end
|
44
|
+
|
45
|
+
def find_right(name)
|
46
|
+
Right.find_by(:name => name)
|
47
|
+
end
|
48
|
+
|
49
|
+
def destroy_access_right!
|
50
|
+
self.right.try(:destroy)
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
module RightOn
|
4
|
+
class Right < ActiveRecord::Base
|
5
|
+
|
6
|
+
has_and_belongs_to_many :roles, :class_name => 'RightOn::Role'
|
7
|
+
|
8
|
+
validates_presence_of :name
|
9
|
+
validates_uniqueness_of :name
|
10
|
+
|
11
|
+
scope :ordered, -> { order :name }
|
12
|
+
|
13
|
+
after_save :clear_cache
|
14
|
+
after_destroy :clear_cache
|
15
|
+
|
16
|
+
attr_accessor :group
|
17
|
+
|
18
|
+
class << self
|
19
|
+
@@restricted_by_right_classes = []
|
20
|
+
|
21
|
+
def associate_group(klass, group)
|
22
|
+
# Prevent issues when reloading class using restricted_by_right
|
23
|
+
unless @@restricted_by_right_classes.include?(klass)
|
24
|
+
@@restricted_by_right_classes << klass
|
25
|
+
end
|
26
|
+
has_one klass.table_name.singularize.to_sym, :dependent => :restrict
|
27
|
+
end
|
28
|
+
|
29
|
+
def rights_yaml(file_path)
|
30
|
+
@@rights_yaml = file_path
|
31
|
+
end
|
32
|
+
|
33
|
+
def by_groups
|
34
|
+
rights = []
|
35
|
+
rights += regular_rights_with_group
|
36
|
+
rights += restricted_rights_with_group
|
37
|
+
rights.group_by(&:group)
|
38
|
+
end
|
39
|
+
|
40
|
+
def regular_rights_with_group
|
41
|
+
yaml = YAML::load_file(@@rights_yaml)
|
42
|
+
rights = []
|
43
|
+
rights_by_name = Hash[Right.all.map{|r| [r.name, r]}]
|
44
|
+
yaml['rights'].each_pair do |group, right_names|
|
45
|
+
rights_for_group = []
|
46
|
+
right_names.each do |right_name|
|
47
|
+
if right_name.is_a?(String) # controller
|
48
|
+
r = rights_by_name[right_name]
|
49
|
+
raise right_name if r.nil?
|
50
|
+
rights_for_group << r
|
51
|
+
else right_name.is_a?(Hash) # controller + actions
|
52
|
+
controller, actions = right_name.first
|
53
|
+
r = rights_by_name[controller]
|
54
|
+
if r
|
55
|
+
rights_for_group << r
|
56
|
+
end
|
57
|
+
actions.each do |action|
|
58
|
+
name = "#{controller}##{action}"
|
59
|
+
r = rights_by_name[name]
|
60
|
+
raise name.inspect + "****" + right_name.inspect + '---' + action_right.inspect if r.nil?
|
61
|
+
rights_for_group << r
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
rights_for_group.each{|r| r.group = group}
|
66
|
+
rights += rights_for_group
|
67
|
+
end
|
68
|
+
rights
|
69
|
+
end
|
70
|
+
|
71
|
+
def restricted_rights_with_group
|
72
|
+
rights = []
|
73
|
+
@@restricted_by_right_classes.each do |klass|
|
74
|
+
group = klass.restricted_by_right_group
|
75
|
+
rights += all_rights(klass).map(&:right).each do |right|
|
76
|
+
right.group = group
|
77
|
+
end
|
78
|
+
end
|
79
|
+
rights
|
80
|
+
end
|
81
|
+
|
82
|
+
def all_rights(klass)
|
83
|
+
klass.includes(:right).all
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Is this right allowed for the given context?
|
88
|
+
#
|
89
|
+
# Context params is an option hash:
|
90
|
+
# :controller => controller name
|
91
|
+
# :action => action name
|
92
|
+
#
|
93
|
+
# The context tells us the state of the request being made.
|
94
|
+
|
95
|
+
def allowed?(context={})
|
96
|
+
return false unless controller == context[:controller]
|
97
|
+
if action
|
98
|
+
action_permitted?(context[:action])
|
99
|
+
else
|
100
|
+
# right without action works if no specific right exists
|
101
|
+
# e.g. can't edit if there's a edit or change right defined
|
102
|
+
# as you must used that specific right
|
103
|
+
specific_rights = Array(APPLICABLE_RIGHTS[context[:action].to_sym]) + [context[:action]]
|
104
|
+
specific_rights.all?{|action| Right["#{context[:controller]}##{action}"].nil?}
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
APPLICABLE_RIGHTS = {
|
109
|
+
:new => [:change],
|
110
|
+
:edit => [:change],
|
111
|
+
:update => [:change],
|
112
|
+
:create => [:change],
|
113
|
+
:destroy => [:change],
|
114
|
+
:index => [:change, :view],
|
115
|
+
:show => [:change, :view]
|
116
|
+
}
|
117
|
+
|
118
|
+
CHANGE_ACTIONS = %w(new edit update create destroy index show)
|
119
|
+
|
120
|
+
VIEW_ACTIONS = %w(index show)
|
121
|
+
|
122
|
+
def action_permitted?(context_action)
|
123
|
+
case action.to_sym
|
124
|
+
when :change
|
125
|
+
CHANGE_ACTIONS.include?(context_action)
|
126
|
+
when :view
|
127
|
+
VIEW_ACTIONS.include?(context_action)
|
128
|
+
else
|
129
|
+
action == context_action
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def sensible_name
|
134
|
+
name.humanize.titleize.gsub(/#/, ' - ')
|
135
|
+
end
|
136
|
+
|
137
|
+
def to_s
|
138
|
+
name
|
139
|
+
end
|
140
|
+
|
141
|
+
def self.cache
|
142
|
+
@@cache ||= Rails.cache
|
143
|
+
end
|
144
|
+
|
145
|
+
def self.cache=(cache)
|
146
|
+
@@cache = cache
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.clear_cache
|
150
|
+
cache.delete('Right.all')
|
151
|
+
end
|
152
|
+
|
153
|
+
def clear_cache
|
154
|
+
self.class.clear_cache
|
155
|
+
end
|
156
|
+
|
157
|
+
attr_accessor :rights
|
158
|
+
def self.[](name)
|
159
|
+
@rights = cache.read('Right.all') || calculate_and_write_cache
|
160
|
+
@rights[name]
|
161
|
+
end
|
162
|
+
|
163
|
+
private
|
164
|
+
def self.calculate_and_write_cache
|
165
|
+
right_cache = Hash[Right.all.map{|r|[r.name, r.id]}]
|
166
|
+
cache.write('Right.all', right_cache) or raise RuntimeError, "Could not cache rights"
|
167
|
+
right_cache
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
end
|