authorule 1.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 +7 -0
- data/.gitignore +29 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +9 -0
- data/CHANGELOG.md +12 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +45 -0
- data/LICENSE.txt +22 -0
- data/README.md +123 -0
- data/Rakefile +7 -0
- data/authorule.gemspec +27 -0
- data/lib/authorule/permission.rb +123 -0
- data/lib/authorule/permission_accessors.rb +47 -0
- data/lib/authorule/permission_holder.rb +55 -0
- data/lib/authorule/railtie.rb +21 -0
- data/lib/authorule/rule.rb +111 -0
- data/lib/authorule/rule_base.rb +141 -0
- data/lib/authorule/version.rb +3 -0
- data/lib/authorule.rb +79 -0
- data/lib/tasks/permissions.rake +17 -0
- data/spec/authorule/permission_holder_spec.rb +137 -0
- data/spec/authorule/rule_base_spec.rb +127 -0
- data/spec/authorule_spec.rb +113 -0
- data/spec/spec_helper.rb +19 -0
- metadata +143 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 94b612796ca264f8a74cc1a5279093c602cffe77
|
4
|
+
data.tar.gz: 8753373eca10d39f80ccbfa89469d5e6b7e30e34
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 90a3b01b1e02017f54ec9c3a931d0662a36b424e7f3c5c8c38f8277b2ceea8f8e646d5addad3cbf06261c095bce725302e71dcd1b204a718b57dda5a6c8858cf
|
7
|
+
data.tar.gz: 2ed129fc2f22109705614d3fd0f807eb79b88bb465503fd22c48751db6c1c266dc5c1291ba9fec4253b1b362c9be029a9e4285ee2e73beb0785eac275c5c85a2
|
data/.gitignore
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
2
|
+
#
|
3
|
+
# If you find yourself ignoring temporary files generated by your text editor
|
4
|
+
# or operating system, you probably want to add a global ignore instead:
|
5
|
+
# git config --global core.excludesfile ~/.gitignore_global
|
6
|
+
|
7
|
+
# Ignore .DS_Store
|
8
|
+
.DS_Store
|
9
|
+
|
10
|
+
# Ignore the output files.
|
11
|
+
/pkg
|
12
|
+
|
13
|
+
# Ignore bundler and database config and precompiled assets
|
14
|
+
/.bundle
|
15
|
+
/.env
|
16
|
+
|
17
|
+
# Ignore all logfiles and tempfiles.
|
18
|
+
/tmp
|
19
|
+
|
20
|
+
# Ignore TextMate projects
|
21
|
+
*.tmproj
|
22
|
+
*.sublime-project
|
23
|
+
*.sublime-workspace
|
24
|
+
|
25
|
+
# Documentation files and other stuff
|
26
|
+
.yardoc
|
27
|
+
/doc
|
28
|
+
/nbproject
|
29
|
+
/coverage
|
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
flux
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.0.0-p247
|
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
authorule (0.0.2)
|
5
|
+
activesupport (~> 3.2)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activemodel (3.2.14)
|
11
|
+
activesupport (= 3.2.14)
|
12
|
+
builder (~> 3.0.0)
|
13
|
+
activerecord (3.2.14)
|
14
|
+
activemodel (= 3.2.14)
|
15
|
+
activesupport (= 3.2.14)
|
16
|
+
arel (~> 3.0.2)
|
17
|
+
tzinfo (~> 0.3.29)
|
18
|
+
activesupport (3.2.14)
|
19
|
+
i18n (~> 0.6, >= 0.6.4)
|
20
|
+
multi_json (~> 1.0)
|
21
|
+
arel (3.0.2)
|
22
|
+
builder (3.0.4)
|
23
|
+
diff-lcs (1.2.4)
|
24
|
+
i18n (0.6.5)
|
25
|
+
multi_json (1.7.9)
|
26
|
+
rake (10.1.0)
|
27
|
+
rspec (2.14.1)
|
28
|
+
rspec-core (~> 2.14.0)
|
29
|
+
rspec-expectations (~> 2.14.0)
|
30
|
+
rspec-mocks (~> 2.14.0)
|
31
|
+
rspec-core (2.14.5)
|
32
|
+
rspec-expectations (2.14.3)
|
33
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
34
|
+
rspec-mocks (2.14.3)
|
35
|
+
tzinfo (0.3.37)
|
36
|
+
|
37
|
+
PLATFORMS
|
38
|
+
ruby
|
39
|
+
|
40
|
+
DEPENDENCIES
|
41
|
+
activerecord (~> 3.2)
|
42
|
+
authorule!
|
43
|
+
bundler (~> 1.3)
|
44
|
+
rake
|
45
|
+
rspec (~> 2.14)
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Joost Lubach
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
# Authorule
|
2
|
+
|
3
|
+
Rule based authorization library.
|
4
|
+
|
5
|
+
[<img src="https://secure.travis-ci.org/yoazt/authorule.png?branch=master" alt="Build Status" />](http://travis-ci.org/yoazt/authorule)
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'authorule'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install authorule
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
### Write rule model
|
24
|
+
|
25
|
+
Write a permission rule model. By default, `Authorule` expects this to be called `PermissionRule`. You may add any associations in the class, but this is not required by this gem.
|
26
|
+
|
27
|
+
class PermissionRule < ActiveRecord::Base
|
28
|
+
include Authorule::Rule
|
29
|
+
|
30
|
+
belongs_to :user
|
31
|
+
end
|
32
|
+
|
33
|
+
Use the following migration for this class (TODO: write a generator):
|
34
|
+
|
35
|
+
class CreatePermissionRules < ActiveRecord::Migration
|
36
|
+
def change
|
37
|
+
create_table :permission_rules do |t|
|
38
|
+
t.boolean :allow, :default => true
|
39
|
+
t.string :kind, :limit => 20
|
40
|
+
t.string :name, :limit => 80
|
41
|
+
t.string :action, :limit => 20
|
42
|
+
|
43
|
+
# --> Add any other columns here.
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
### Write permission holder
|
49
|
+
|
50
|
+
Write a permission holder model. This is typically a `User` object. Include `Authorule::PermissionHolder` into this class, and call `is_permission_holder!`.
|
51
|
+
|
52
|
+
class User < ActiveRecord::Base
|
53
|
+
include Authorule::PermissionHolder
|
54
|
+
|
55
|
+
is_permission_holder!
|
56
|
+
end
|
57
|
+
|
58
|
+
This creates an association and a rule base accessor. By default, it is assumed that the rule class is called `PermissionRule`.
|
59
|
+
|
60
|
+
### Write a custom permission class
|
61
|
+
|
62
|
+
Write a permission class. Each permission class should at a minimum:
|
63
|
+
|
64
|
+
1. Register itself under a name.
|
65
|
+
2. Provide a way to resolve any argument into a permission target.
|
66
|
+
3. Provide a way to list all permission targets.
|
67
|
+
|
68
|
+
A permission target is the object that you wish to secure using the permission. The following example is a permission that secures access to any ActiveRecord object. The target is the class (e.g. 'Allow user X to access Active Record class Y.'), but the permission can also resolve model instances.
|
69
|
+
|
70
|
+
class ResourcePermission < Authorule::Permission
|
71
|
+
|
72
|
+
# Register under name :resource.
|
73
|
+
register :resource
|
74
|
+
|
75
|
+
# Resolution.
|
76
|
+
resolve do |arg|
|
77
|
+
if arg.is_a?(ActiveRecord::Base)
|
78
|
+
arg.class
|
79
|
+
elsif arg.is_a?(Class) && arg < ActiveRecord::Base
|
80
|
+
arg
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
list do
|
85
|
+
classes = []
|
86
|
+
Dir[ Rails.root + 'app/models' + '*.rb' ].each do |file|
|
87
|
+
klass = File.basename(file, '.rb').camelize.safe_constantize
|
88
|
+
classes << klass if klass
|
89
|
+
end
|
90
|
+
classes
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
### Checking permissions
|
96
|
+
|
97
|
+
You can now give any user a set of rules, e.g.:
|
98
|
+
|
99
|
+
1. Allow access to everything
|
100
|
+
2. Deny access to ActiveRecord class 'Account'
|
101
|
+
|
102
|
+
This allows the user to access all other ActiveRecord classes (and their objects).
|
103
|
+
|
104
|
+
To check a permission, you can call:
|
105
|
+
|
106
|
+
User.may_access?(Account)
|
107
|
+
|
108
|
+
or
|
109
|
+
|
110
|
+
User.may_access?(Account.new)
|
111
|
+
|
112
|
+
(because it resolves into a class), or the equivalent
|
113
|
+
|
114
|
+
permission = Authorule.resolve(Account.new)
|
115
|
+
User.has_permission?(permission)
|
116
|
+
|
117
|
+
## Contributing
|
118
|
+
|
119
|
+
1. Fork it
|
120
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
121
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
122
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
123
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/authorule.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'authorule/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "authorule"
|
8
|
+
spec.version = Authorule::VERSION
|
9
|
+
spec.authors = ["Joost Lubach"]
|
10
|
+
spec.email = ["joost@yoazt.com"]
|
11
|
+
spec.description = %q[Rule-based programmable permission system.]
|
12
|
+
spec.summary = %q[Rule-based programmable permission system.]
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^spec/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "activesupport", "~> 3.2"
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
spec.add_development_dependency "activerecord", "~> 3.2"
|
26
|
+
spec.add_development_dependency "rspec", "~> 2.14"
|
27
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module Authorule
|
2
|
+
|
3
|
+
# A permission. This is an object that can be used to check if someone has access to a certain permissable.
|
4
|
+
#
|
5
|
+
# Note: do not confuse a permission with a {PermissionRule} or {PermissionRuleBase}. This class doesn't indicate
|
6
|
+
# that a user has been granted a permission. It simply encapsulates a permission query.
|
7
|
+
#
|
8
|
+
# This class should also not be confused with a {CustomPermission}, which is an application-defined custom
|
9
|
+
# permission.
|
10
|
+
#
|
11
|
+
# == Usage
|
12
|
+
#
|
13
|
+
# permission = Permission.resolve(Campaign, :destroy)
|
14
|
+
# @user.has_permission?(permission) # Granted that @user < UI::PermissionHolder
|
15
|
+
#
|
16
|
+
# Or even simpler:
|
17
|
+
#
|
18
|
+
# @user.may?(:destroy, @campaign)
|
19
|
+
#
|
20
|
+
# == Object resolution
|
21
|
+
#
|
22
|
+
# Any object can be converted into a permission, if a suitable {Schema} can be found. For example, any UI resource
|
23
|
+
# can be resolved into a resource permission, but also any resource model class or even resource symbol. This
|
24
|
+
# allows for the following equivalent calls:
|
25
|
+
#
|
26
|
+
# @user.may?(:destroy, UI.application.resources[:campaign])
|
27
|
+
# @user.may?(:destroy, @campaign)
|
28
|
+
# @user.may?(:destroy, Campaign)
|
29
|
+
# @user.may?(:destroy, :campaign)
|
30
|
+
#
|
31
|
+
# The UI library defines a few schemas, for example one for resource permissions, and one for UI space permissions.
|
32
|
+
# There is also a custom permission schema - allowing the application designer to define additional permissions.
|
33
|
+
# These can be referred to throughout the UI library.
|
34
|
+
#
|
35
|
+
# @see PermissionHolder
|
36
|
+
# @see RuleBase
|
37
|
+
# @see Rule
|
38
|
+
class Permission
|
39
|
+
|
40
|
+
######
|
41
|
+
# Initialization
|
42
|
+
|
43
|
+
# Initializes a new permission.
|
44
|
+
#
|
45
|
+
# @param object
|
46
|
+
# The object of the permission.
|
47
|
+
# @param [Symbol|nil] action
|
48
|
+
# The action the user wishes to perform.
|
49
|
+
def initialize(object, action = nil)
|
50
|
+
@object = object
|
51
|
+
@action = action.try(:to_sym)
|
52
|
+
end
|
53
|
+
|
54
|
+
######
|
55
|
+
# Attributes
|
56
|
+
|
57
|
+
# @!attribute [r] object
|
58
|
+
# @return [Symbol] The object of the permission.
|
59
|
+
attr_reader :object
|
60
|
+
|
61
|
+
# @!attribute [r] action
|
62
|
+
# @return [Symbol|nil] The action the user wishes to perform.
|
63
|
+
attr_reader :action
|
64
|
+
|
65
|
+
# @!attribute [r] kind
|
66
|
+
# @return [Symbol] The kind of permission. This is delegated to the current class.
|
67
|
+
def kind
|
68
|
+
self.class.kind
|
69
|
+
end
|
70
|
+
|
71
|
+
# @!attribute [r] name
|
72
|
+
# @return [String] The name of the permission. This is delegated to {#object}.
|
73
|
+
def name
|
74
|
+
object.name
|
75
|
+
end
|
76
|
+
|
77
|
+
# @!attribute [r] available_actions
|
78
|
+
# @return [Array] The available actions for the permission.
|
79
|
+
def available_actions
|
80
|
+
[]
|
81
|
+
end
|
82
|
+
|
83
|
+
######
|
84
|
+
# Dependencies
|
85
|
+
|
86
|
+
# Retrieves an array of permissions consisting of dependencies and the permission itself.
|
87
|
+
def with_dependencies
|
88
|
+
dependencies + [ self ]
|
89
|
+
end
|
90
|
+
|
91
|
+
# Resolves dependencies for this permission. To be implemented by subclasses.
|
92
|
+
def dependencies
|
93
|
+
[]
|
94
|
+
end
|
95
|
+
|
96
|
+
######
|
97
|
+
# Registration & metadata
|
98
|
+
|
99
|
+
class << self
|
100
|
+
|
101
|
+
attr_reader :kind, :resolve_block, :list_block
|
102
|
+
|
103
|
+
# Registers a permission class under a specific kind.
|
104
|
+
def register(kind)
|
105
|
+
Authorule.register kind, self
|
106
|
+
@kind = kind
|
107
|
+
end
|
108
|
+
|
109
|
+
# Defines a block that resolves any argument into a suitable permission target.
|
110
|
+
def resolve(&block)
|
111
|
+
@resolve_block = block
|
112
|
+
end
|
113
|
+
|
114
|
+
# Defines a block that lists all suitable permission targets in the application.
|
115
|
+
def list(&block)
|
116
|
+
@list_block = block
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Authorule
|
2
|
+
|
3
|
+
# Provides methods 'may?', 'may_access?' and their negative counterparts. You must make sure
|
4
|
+
# to implement method 'has_permission?' in your class.
|
5
|
+
module PermissionAccessors
|
6
|
+
|
7
|
+
# Determines whether a holder in this group may perform the specified action on the specified target.
|
8
|
+
#
|
9
|
+
# @param [#to_s] action
|
10
|
+
# The action to perform. The available actions differ per permissions. The full list can be found
|
11
|
+
# in {UI::Permission}.
|
12
|
+
# @param target
|
13
|
+
# The target the holder wishes to operate on. This target may be any object and is passed to the UI
|
14
|
+
# permission checker as is, which will convert it into a permission path.
|
15
|
+
def may?(action, target)
|
16
|
+
permission = Authorule.resolve(target, action)
|
17
|
+
unless permission.available_actions.try(:include?, action)
|
18
|
+
raise ArgumentError, "action :#{action} not available for permission of kind :#{permission.class.kind}"
|
19
|
+
end
|
20
|
+
|
21
|
+
has_permission? permission
|
22
|
+
end
|
23
|
+
|
24
|
+
# Checks a permission without querying a specific action.
|
25
|
+
# @see #may?
|
26
|
+
def may_access?(target)
|
27
|
+
permission = Authorule.resolve(target)
|
28
|
+
has_permission? permission
|
29
|
+
end
|
30
|
+
|
31
|
+
# Determines whether a holder may not perform the specified action on the specified target.
|
32
|
+
# @see #may?
|
33
|
+
def may_not?(action, target)
|
34
|
+
!may?(action, target)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Determines whether a holder may not access the specified target.
|
38
|
+
# @see #may_not?
|
39
|
+
# @see #may?
|
40
|
+
def may_not_access?(target)
|
41
|
+
!may_access?(target)
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Authorule
|
2
|
+
|
3
|
+
# Makes any ActiveModel/ActiveRecord-like class a UI permission holder.
|
4
|
+
#
|
5
|
+
# == Usage
|
6
|
+
#
|
7
|
+
# class User
|
8
|
+
# include Authorule::PermissionHolder
|
9
|
+
# is_permission_holder!
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# * A (has many) +permission_rules+ association is added to the model (though the
|
13
|
+
# name may be changed in the {.is_permission_holder!} method).
|
14
|
+
# * A {#may?} and {#may_not?} method is added.
|
15
|
+
module PermissionHolder
|
16
|
+
extend ActiveSupport::Concern
|
17
|
+
|
18
|
+
include PermissionAccessors
|
19
|
+
|
20
|
+
module ClassMethods
|
21
|
+
|
22
|
+
# Marks this class as a permission holder with the given options.
|
23
|
+
#
|
24
|
+
# @option options [#to_sym] association_name (:permission_rules)
|
25
|
+
# The name of the permission rules association.
|
26
|
+
def is_permission_holder!(options = {})
|
27
|
+
association_name = options[:association_name] || :permission_rules
|
28
|
+
|
29
|
+
class_eval <<-RUBY, __FILE__, __LINE__+1
|
30
|
+
has_many :#{association_name}
|
31
|
+
|
32
|
+
def permission_rule_base(reload = false)
|
33
|
+
@permission_rule_base = nil if reload
|
34
|
+
@permission_rule_base ||= RuleBase.new(#{association_name}(true))
|
35
|
+
end
|
36
|
+
RUBY
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
######
|
42
|
+
# has_permission?
|
43
|
+
|
44
|
+
# Determines whether this holder has the given permission by running it through his rule base.
|
45
|
+
def has_permission?(permission)
|
46
|
+
unless respond_to?(:permission_rule_base)
|
47
|
+
raise "class not set up as permission holder, call is_permission_holder! first"
|
48
|
+
end
|
49
|
+
|
50
|
+
permission_rule_base.run permission
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.expand_path('..', __FILE__)
|
2
|
+
|
3
|
+
module Authorule
|
4
|
+
|
5
|
+
class Railtie < Rails::Railtie
|
6
|
+
|
7
|
+
generators do
|
8
|
+
Dir[ File.expand_path('../generators/*/*_generator.rb', __FILE__) ].each do |path|
|
9
|
+
require path
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
rake_tasks do
|
14
|
+
Dir[ File.expand_path('../tasks/**/*.rake', __FILE__) ].each do |path|
|
15
|
+
load path
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module Authorule
|
2
|
+
|
3
|
+
# A permission rule. Each rule allows or denies the permission holder one permission.
|
4
|
+
#
|
5
|
+
# == Usage
|
6
|
+
#
|
7
|
+
# Create a model class, and include this mixin into it, e.g.
|
8
|
+
#
|
9
|
+
# class PermissionRule < ActiveRecord::Base
|
10
|
+
# include Authorule::Rule
|
11
|
+
#
|
12
|
+
# belongs_to :user
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# @see RuleBase
|
16
|
+
module Rule
|
17
|
+
extend ActiveSupport::Concern
|
18
|
+
|
19
|
+
######
|
20
|
+
# Attributes & validations
|
21
|
+
|
22
|
+
included do
|
23
|
+
validates_inclusion_of :allow, :in => [ true, false ]
|
24
|
+
validates_presence_of :kind, :name
|
25
|
+
|
26
|
+
validates_length_of :kind, :maximum => 20
|
27
|
+
validates_length_of :name, :maximum => 80
|
28
|
+
validates_length_of :action, :maximum => 20, :allow_blank => true
|
29
|
+
|
30
|
+
# Make sure to coerce a blank value for action into an absolute nil.
|
31
|
+
before_validation { self.action = nil if self.action.blank? }
|
32
|
+
end
|
33
|
+
|
34
|
+
######
|
35
|
+
# Rule creation accessors
|
36
|
+
|
37
|
+
# Builds an allow rule for the given kind and name.
|
38
|
+
def self.allow(kind, name, attributes = {})
|
39
|
+
new attributes.merge(:kind => kind, :name => name, :allow => true)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Creates an allow rule for the given kind and name.
|
43
|
+
def self.allow!(kind, name, attributes = {})
|
44
|
+
allow(kind, name, attributes).save
|
45
|
+
end
|
46
|
+
|
47
|
+
# Builds a deny rule for the given kind and name.
|
48
|
+
def self.deny(kind, name, attributes = {})
|
49
|
+
new attributes.merge(:kind => kind, :name => name, :allow => false)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Creates a deny rule for the given kind and name.
|
53
|
+
def self.deny!(kind, name, attributes = {})
|
54
|
+
deny(kind, name, attributes).save
|
55
|
+
end
|
56
|
+
|
57
|
+
# Builds an 'allow all' rule.
|
58
|
+
#
|
59
|
+
# == Examples
|
60
|
+
#
|
61
|
+
# Rule.allow_all # => kind 'all', name 'all'
|
62
|
+
# Rule.allow_all(:resource) # => kind 'resource', name 'all'
|
63
|
+
def self.allow_all(kind = :all, attributes = {})
|
64
|
+
new attributes.merge(:kind => kind, :name => 'all', :allow => true)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Creates an 'allow all' rule.
|
68
|
+
# @see .allow_all
|
69
|
+
def self.allow_all!(kind = :all, attributes = {})
|
70
|
+
allow_all(kind, attributes).save
|
71
|
+
end
|
72
|
+
|
73
|
+
# Creates a 'deny all' rule.
|
74
|
+
#
|
75
|
+
# == Examples
|
76
|
+
#
|
77
|
+
# Rule.deny_all # => kind 'all', name 'all'
|
78
|
+
# Rule.deny_all(:resource) # => kind 'resource', name 'all'
|
79
|
+
def self.deny_all(kind = :all, attributes = {})
|
80
|
+
new attributes.merge(:kind => kind, :name => 'all', :allow => false)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Creates an 'deny all' rule.
|
84
|
+
# @see .deny_all
|
85
|
+
def self.deny_all!(kind = :all, attributes = {})
|
86
|
+
allow_all(kind, attributes).save
|
87
|
+
end
|
88
|
+
|
89
|
+
######
|
90
|
+
# Rule key
|
91
|
+
|
92
|
+
# @!attribute [r] key
|
93
|
+
# @return [String] A unique key identifying this rule.
|
94
|
+
def key
|
95
|
+
if kind == 'all'
|
96
|
+
'all'
|
97
|
+
else
|
98
|
+
[ kind, name, action ].compact.join(':')
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
######
|
103
|
+
# Misc
|
104
|
+
|
105
|
+
def to_display
|
106
|
+
key
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|