bali 1.0.0beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +95 -0
- data/Rakefile +6 -0
- data/bali.gemspec +28 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/bali.rb +198 -0
- data/lib/bali/exceptions/dsl_error.rb +2 -0
- data/lib/bali/objector.rb +63 -0
- data/lib/bali/rule.rb +33 -0
- data/lib/bali/rule_class.rb +31 -0
- data/lib/bali/rule_group.rb +67 -0
- data/lib/bali/version.rb +3 -0
- metadata +122 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f654149dda4ee90b979831aeb07501fb41d22ff9
|
4
|
+
data.tar.gz: 81415f18fc6021d1a65c346b6dd1ef6fc95fc49e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 884958b5928962a8ee79105b897f5b971f4dc73f263f407268e1332a72745f82e2b67c384d8dedb2d7e4744e80bc1af1432fbc34e80d138676a49a9dbe5808cc
|
7
|
+
data.tar.gz: 3e2e565ff189df8a41265f7c3d4ec7d7071150267e9cdf18d330d94d3a14411a30eb30805546be172430a542f60f7491700676dfc2abcc6d6769bab1d3c61fad
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
4
|
+
|
5
|
+
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
|
6
|
+
|
7
|
+
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
8
|
+
|
9
|
+
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
10
|
+
|
11
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
12
|
+
|
13
|
+
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Adam Pahlevi
|
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,95 @@
|
|
1
|
+
# Bali
|
2
|
+
|
3
|
+
[ ![Codeship Status for saveav/bali](https://codeship.com/projects/d2f3ded0-20cf-0133-e425-0eade5a669ff/status?branch=master)](https://codeship.com/projects/95727)
|
4
|
+
|
5
|
+
Bali is a powerful, framework-agnostic, thread-safe Ruby language authorization library. It is a universal authorization library, in the sense that it does not assume you to use specific Ruby library/gem/framework in order for successful use of this gem.
|
6
|
+
|
7
|
+
Bali is short for Bulwark Authorization Library.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
It can be installed directly by using bundler's install:
|
12
|
+
|
13
|
+
$ gem install bali
|
14
|
+
|
15
|
+
Otherwise, if you are using a framework such as Rails, you can add this into your gemfile:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
gem 'bali'
|
19
|
+
```
|
20
|
+
|
21
|
+
And then execute:
|
22
|
+
|
23
|
+
$ bundle
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
### First things first: defining rules
|
28
|
+
|
29
|
+
Rule in Bali is the law determining whether a user (called 'subtarget') can specific operation on a target (which is your resource).
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
Bali.map_rules do
|
33
|
+
rules_for My::Transaction, as: :transaction do
|
34
|
+
describe(:supreme_user) { can_all }
|
35
|
+
describe :admin_user do
|
36
|
+
can_all
|
37
|
+
# a more specific rule would be executed even if can_all is present
|
38
|
+
can :cancel,
|
39
|
+
if: proc { |record| record.payment_channel == "CREDIT_CARD" &&
|
40
|
+
!record.is_settled? }
|
41
|
+
end
|
42
|
+
describe "general user", can: [:update, :edit], cant: [:delete]
|
43
|
+
describe "finance user" do
|
44
|
+
can :update, :delete, :edit
|
45
|
+
can :delete, if: proc { |record| record.is_settled? }
|
46
|
+
end # finance_user description
|
47
|
+
end # rules_for
|
48
|
+
end
|
49
|
+
```
|
50
|
+
|
51
|
+
You may or may not assign an alias name (`as`). Make sure to keep it unique had you decided to give alias name to your rules group.
|
52
|
+
|
53
|
+
### Authorization
|
54
|
+
|
55
|
+
Say:
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
class My::Transaction
|
59
|
+
attr_accessor :is_settled
|
60
|
+
attr_accessor :payment_channel
|
61
|
+
|
62
|
+
alias :is_settled? :is_settled
|
63
|
+
end
|
64
|
+
```
|
65
|
+
|
66
|
+
Assuming that there exist a variable `transaction` which is an instance of `My::Transaction`, we can query about whether the subtarget is granted to perform certain operation:
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
transaction.cant?(:general_user, :delete) # => true
|
70
|
+
transaction.can("general user", :update) # => true
|
71
|
+
transaction.can?(:finance_user, :delete) # depend on context
|
72
|
+
transaction.can?(:monitoring_user, :view) # => true
|
73
|
+
transaction.can?("monitoring user", :view) # => true
|
74
|
+
transaction.can?(:admin_user, :cancel) # depend on context
|
75
|
+
transaction.can?(:supreme_user, :cancel) # => true
|
76
|
+
```
|
77
|
+
|
78
|
+
If a rule is depending on a certain context, then the context will be evaluated to determine whether the subtarget is authorized or not.
|
79
|
+
|
80
|
+
In the above example, deletion of `transaction` is only allowed if the subtarget is a "finance user" and, the `transaction` itself is already settled.
|
81
|
+
|
82
|
+
Rule can also be called on a class, instead of on an object:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
My::Transaction.can?(:bank_corp, :new) # => true
|
86
|
+
```
|
87
|
+
|
88
|
+
## Contributing
|
89
|
+
|
90
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/bali. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
|
91
|
+
|
92
|
+
|
93
|
+
## License
|
94
|
+
|
95
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bali.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'bali/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "bali"
|
8
|
+
spec.version = Bali::VERSION
|
9
|
+
spec.authors = ["Adam Pahlevi"]
|
10
|
+
spec.email = ["adam.pahlevi@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Bali is a powerful, framework-agnostic, thread-safe Ruby language authorization library}
|
13
|
+
spec.description = %q{Bali (Bulwark Authorization Library) is a universal authorization library, in the sense that
|
14
|
+
it does not assume you to use specific Ruby library/gem/framework.}
|
15
|
+
spec.homepage = "https://github.com/saveav/bali"
|
16
|
+
spec.license = "MIT"
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
19
|
+
spec.bindir = "exe"
|
20
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.9"
|
24
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
25
|
+
spec.add_development_dependency "rspec", "~> 3.3"
|
26
|
+
spec.add_development_dependency "pry", "~> 0.10"
|
27
|
+
|
28
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "bali"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/lib/bali.rb
ADDED
@@ -0,0 +1,198 @@
|
|
1
|
+
require_relative "bali/version"
|
2
|
+
require_relative "bali/rule_class"
|
3
|
+
require_relative "bali/rule_group"
|
4
|
+
require_relative "bali/rule"
|
5
|
+
|
6
|
+
require_relative "bali/objector"
|
7
|
+
|
8
|
+
# exception classes
|
9
|
+
require_relative "bali/exceptions/dsl_error"
|
10
|
+
module Bali
|
11
|
+
# mapping class to a RuleClass
|
12
|
+
RULE_CLASS_MAP = {}
|
13
|
+
|
14
|
+
# from symbol to full class name
|
15
|
+
ALIASED_RULE_CLASS_MAP = {}
|
16
|
+
|
17
|
+
# from full class name to symbol
|
18
|
+
REVERSE_ALIASED_RULE_CLASS_MAP = {}
|
19
|
+
end
|
20
|
+
|
21
|
+
module Bali
|
22
|
+
extend self
|
23
|
+
|
24
|
+
def rule_classes
|
25
|
+
RULE_CLASS_MAP
|
26
|
+
end
|
27
|
+
|
28
|
+
def rule_class_for(target)
|
29
|
+
if target.is_a?(Symbol)
|
30
|
+
class_name = ALIASED_RULE_CLASS_MAP[target]
|
31
|
+
raise Bali::DslError, "Rule class is not defined for: #{target}" if class_name.nil?
|
32
|
+
|
33
|
+
rule_class_for(class_name)
|
34
|
+
else
|
35
|
+
rule_class = RULE_CLASS_MAP[target]
|
36
|
+
raise Bali::DslError, "Rule class is not defined for: #{target}" if rule_class.nil?
|
37
|
+
rule_class
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def rule_group_for(target_class, subtarget)
|
42
|
+
rule_class = Bali.rule_class_for(target_class)
|
43
|
+
rule_group = rule_class.rules_for(subtarget)
|
44
|
+
|
45
|
+
rule_group
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_rule_class(rule_class)
|
49
|
+
if rule_class.is_a?(Bali::RuleClass)
|
50
|
+
target = rule_class.target_class
|
51
|
+
alias_target = rule_class.alias_name
|
52
|
+
|
53
|
+
raise "Target must be a class" unless target.is_a?(Class)
|
54
|
+
|
55
|
+
# remove any previous association of rule
|
56
|
+
begin
|
57
|
+
last_associated_alias = Bali::REVERSE_ALIASED_RULE_CLASS_MAP[target]
|
58
|
+
if last_associated_alias
|
59
|
+
Bali::ALIASED_RULE_CLASS_MAP.delete(last_associated_alias)
|
60
|
+
Bali::REVERSE_ALIASED_RULE_CLASS_MAP.delete(target)
|
61
|
+
Bali::RULE_CLASS_MAP.delete(target)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# if "as" is present
|
66
|
+
if alias_target.is_a?(Symbol)
|
67
|
+
Bali::ALIASED_RULE_CLASS_MAP[alias_target] = target
|
68
|
+
Bali::REVERSE_ALIASED_RULE_CLASS_MAP[target] = alias_target
|
69
|
+
end
|
70
|
+
|
71
|
+
Bali::RULE_CLASS_MAP[target] = rule_class
|
72
|
+
else
|
73
|
+
raise "Only allow instance of Bali::RuleClass"
|
74
|
+
end
|
75
|
+
end # add_rule_class
|
76
|
+
end
|
77
|
+
|
78
|
+
module Bali
|
79
|
+
class Bali::MapRulesDsl
|
80
|
+
attr_accessor :current_rule_class
|
81
|
+
|
82
|
+
def initialize
|
83
|
+
@@lock = Mutex.new
|
84
|
+
end
|
85
|
+
|
86
|
+
def rules_for(target_class, target_alias_hash = {}, &block)
|
87
|
+
@@lock.synchronize do
|
88
|
+
self.current_rule_class = Bali::RuleClass.new(target_class)
|
89
|
+
self.current_rule_class.alias_name = target_alias_hash[:as] || target_alias_hash["as"]
|
90
|
+
|
91
|
+
Bali::MapRulesRulesForDsl.new(self).instance_eval(&block)
|
92
|
+
|
93
|
+
# done processing the block, now add the rule class
|
94
|
+
Bali.add_rule_class(self.current_rule_class)
|
95
|
+
|
96
|
+
target_class.include(Bali::Objector) unless target_class.include?(Bali::Objector)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
module Bali
|
103
|
+
class Bali::MapRulesRulesForDsl
|
104
|
+
attr_accessor :map_rules_dsl
|
105
|
+
attr_accessor :current_rule_group
|
106
|
+
|
107
|
+
def initialize(map_rules_dsl)
|
108
|
+
@@lock = Mutex.new
|
109
|
+
self.map_rules_dsl = map_rules_dsl
|
110
|
+
end
|
111
|
+
|
112
|
+
def describe(subtarget, rules = {})
|
113
|
+
target_class = self.map_rules_dsl.current_rule_class.target_class
|
114
|
+
target_alias = self.map_rules_dsl.current_rule_class.alias_name
|
115
|
+
@@lock.synchronize do
|
116
|
+
rule_group = Bali::RuleGroup.new(target_class, target_alias, subtarget)
|
117
|
+
self.current_rule_group = rule_group
|
118
|
+
|
119
|
+
if block_given?
|
120
|
+
the_object = Object.new
|
121
|
+
# the_object would be the record, or the object of class as specified
|
122
|
+
# in rules_for
|
123
|
+
yield the_object
|
124
|
+
else
|
125
|
+
# auth_val is either can or cant
|
126
|
+
rules.each do |auth_val, operations|
|
127
|
+
if operations.is_a?(Array)
|
128
|
+
operations.each do |op|
|
129
|
+
rule = Bali::Rule.new(auth_val, op)
|
130
|
+
rule_group.add_rule(rule)
|
131
|
+
end
|
132
|
+
else
|
133
|
+
operation = operations # well, basically is 1 only
|
134
|
+
rule = Bali::Rule.new(auth_val, operation)
|
135
|
+
rule_group.add_rule(rule)
|
136
|
+
end
|
137
|
+
end # each rules
|
138
|
+
end # block_given?
|
139
|
+
|
140
|
+
# add current_rule_group
|
141
|
+
self.map_rules_dsl.current_rule_class.add_rule_group(rule_group)
|
142
|
+
end # mutex synchronize
|
143
|
+
end # describe
|
144
|
+
|
145
|
+
|
146
|
+
def process_auth_rules(auth_val, operations)
|
147
|
+
conditional_hash = nil
|
148
|
+
|
149
|
+
# scan opreation for hash
|
150
|
+
operations.each do |elm|
|
151
|
+
if elm.is_a?(Hash)
|
152
|
+
conditional_hash = elm
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
if conditional_hash
|
157
|
+
op = operations[0]
|
158
|
+
rule = Bali::Rule.new(auth_val, op)
|
159
|
+
rule.decider = conditional_hash[:if] || conditional_hash["if"]
|
160
|
+
self.current_rule_group.add_rule(rule)
|
161
|
+
else
|
162
|
+
# no conditional hash, proceed adding operations one by one
|
163
|
+
operations.each do |op|
|
164
|
+
rule = Bali::Rule.new(auth_val, op)
|
165
|
+
self.current_rule_group.add_rule(rule)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end # process_auth_rules
|
169
|
+
|
170
|
+
def can(*operations)
|
171
|
+
process_auth_rules(:can, operations)
|
172
|
+
end
|
173
|
+
|
174
|
+
def cant(*operations)
|
175
|
+
process_auth_rules(:cant, operations)
|
176
|
+
end
|
177
|
+
|
178
|
+
def can_all
|
179
|
+
self.current_rule_group.zeus = true
|
180
|
+
end
|
181
|
+
|
182
|
+
end # class
|
183
|
+
end # module
|
184
|
+
|
185
|
+
module Bali
|
186
|
+
extend self
|
187
|
+
def map_rules(&block)
|
188
|
+
dsl_map_rules = Bali::MapRulesDsl.new
|
189
|
+
dsl_map_rules.instance_eval(&block)
|
190
|
+
end
|
191
|
+
|
192
|
+
def clear_rules
|
193
|
+
Bali::RULE_CLASS_MAP.clear
|
194
|
+
Bali::REVERSE_ALIASED_RULE_CLASS_MAP.clear
|
195
|
+
Bali::ALIASED_RULE_CLASS_MAP.clear
|
196
|
+
true
|
197
|
+
end
|
198
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# class that will be included in each instantiated target classes as defined
|
2
|
+
# in map_rules
|
3
|
+
module Bali::Objector
|
4
|
+
def self.included(base)
|
5
|
+
base.extend Bali::Objector::Statics
|
6
|
+
end
|
7
|
+
|
8
|
+
def can?(subtarget, operation)
|
9
|
+
self.class.can?(subtarget, operation, self)
|
10
|
+
end
|
11
|
+
|
12
|
+
def cant?(subtarget, operation)
|
13
|
+
self.class.can?(subtarget, operation, self)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module Bali::Objector::Statics
|
18
|
+
def can?(subtarget, operation, record = self)
|
19
|
+
rule_group = Bali.rule_group_for(record.class, subtarget)
|
20
|
+
rule = rule_group.get_rule(:can, operation)
|
21
|
+
|
22
|
+
# godly subtarget is allowed to do as he wishes
|
23
|
+
# so long that the rule is not specificly defined
|
24
|
+
return true if rule_group.zeus? && rule.nil?
|
25
|
+
|
26
|
+
# default to false when asked about can? but no rule to be found
|
27
|
+
return false if rule.nil?
|
28
|
+
|
29
|
+
if rule.has_decider?
|
30
|
+
# must test first
|
31
|
+
decider = rule.decider
|
32
|
+
if decider.arity == 0
|
33
|
+
decider.() == true
|
34
|
+
else
|
35
|
+
decider.(record) == true
|
36
|
+
end
|
37
|
+
else
|
38
|
+
# rule is properly defined
|
39
|
+
return true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def cant?(subtarget, operation)
|
44
|
+
rule_group = Bali.rule_group_for(self.class, subtarget)
|
45
|
+
rule = rule_group.get_rule(:cant, operation)
|
46
|
+
|
47
|
+
# godly subtarget is not to be prohibited in his endeavours
|
48
|
+
# so long that no specific rule about this operation is defined
|
49
|
+
return false if rule_group.zeus? && rule.nil?
|
50
|
+
|
51
|
+
# default to true when asked about cant? but no rule to be found
|
52
|
+
return true if rule.nil?
|
53
|
+
|
54
|
+
if rule.has_decider?
|
55
|
+
decider = rule.decider
|
56
|
+
if decider.arity == 0
|
57
|
+
decider.() == true
|
58
|
+
else
|
59
|
+
decider.(record) == true
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/bali/rule.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# for internal use, representing rule
|
2
|
+
class Bali::Rule
|
3
|
+
# auth_val is either :can or :cant
|
4
|
+
attr_reader :auth_val
|
5
|
+
|
6
|
+
# what is the operation: create, update, delete, or any other
|
7
|
+
attr_reader :operation
|
8
|
+
|
9
|
+
# if decider is defined, a rule is executed only if decider evaluates to true
|
10
|
+
attr_accessor :decider
|
11
|
+
|
12
|
+
def initialize(auth_val, operation)
|
13
|
+
self.auth_val = auth_val
|
14
|
+
@operation = operation
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def auth_val=(aval)
|
19
|
+
if aval == :can || aval == :cant
|
20
|
+
@auth_val = aval
|
21
|
+
else
|
22
|
+
raise Bali::DslError, "auth_val can only either be :can or :cant"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def is_discouragement?
|
27
|
+
self.auth_val == :cant
|
28
|
+
end
|
29
|
+
|
30
|
+
def has_decider?
|
31
|
+
self.decider.is_a?(Proc)
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# the parent of all Bali::RuleGroup.
|
2
|
+
class Bali::RuleClass
|
3
|
+
attr_reader :target_class
|
4
|
+
attr_accessor :alias_name
|
5
|
+
|
6
|
+
attr_accessor :rule_groups
|
7
|
+
|
8
|
+
def initialize(target_class)
|
9
|
+
if target_class.is_a?(Class)
|
10
|
+
@target_class = target_class
|
11
|
+
else
|
12
|
+
raise Bali::DslError, "Target class must be a Class"
|
13
|
+
end
|
14
|
+
|
15
|
+
self.rule_groups = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_rule_group(rule_group)
|
19
|
+
if rule_group.is_a?(Bali::RuleGroup)
|
20
|
+
target_user = rule_group.subtarget
|
21
|
+
self.rule_groups[target_user.to_sym] = rule_group
|
22
|
+
else
|
23
|
+
raise Bali::DslError, "Rule group must be an instance of Bali::RuleGroup"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def rules_for(subtarget)
|
28
|
+
subtarget = Bali::RuleGroup.canon_name(subtarget)
|
29
|
+
self.rule_groups[subtarget]
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
class Bali::RuleGroup
|
2
|
+
# the target class
|
3
|
+
attr_accessor :target
|
4
|
+
|
5
|
+
# the alias name for the target
|
6
|
+
attr_accessor :alias_tgt
|
7
|
+
|
8
|
+
# the user to which this rule group is applied
|
9
|
+
attr_accessor :subtarget
|
10
|
+
|
11
|
+
# what can be done and what cannot be done
|
12
|
+
attr_accessor :cans, :cants
|
13
|
+
|
14
|
+
# if set to true then the subtarget can do anything
|
15
|
+
attr_accessor :zeus
|
16
|
+
alias :zeus? :zeus
|
17
|
+
|
18
|
+
# allowing "general user" and :general_user to route to the same rule group
|
19
|
+
def self.canon_name(subtarget)
|
20
|
+
if subtarget.is_a?(String)
|
21
|
+
return subtarget.gsub(" ", "_").to_sym
|
22
|
+
else
|
23
|
+
return subtarget
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(target, alias_tgt, subtarget)
|
28
|
+
self.target = target
|
29
|
+
self.alias_tgt = alias_tgt
|
30
|
+
self.subtarget = Bali::RuleGroup.canon_name(subtarget)
|
31
|
+
|
32
|
+
self.cans = {}
|
33
|
+
self.cants = {}
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_rule(rule)
|
37
|
+
# operation cannot be defined twice
|
38
|
+
operation = rule.operation.to_sym
|
39
|
+
|
40
|
+
raise Bali::DslError, "Rule is defined twice for operation #{operation}" if self.cants[operation] && self.cans[operation]
|
41
|
+
|
42
|
+
if rule.is_discouragement?
|
43
|
+
self.cants[rule.operation.to_sym] = rule
|
44
|
+
else
|
45
|
+
self.cans[rule.operation.to_sym] = rule
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def get_rule(auth_val, operation)
|
50
|
+
rule = nil
|
51
|
+
case auth_val
|
52
|
+
when :can, "can"
|
53
|
+
rule = self.cans[operation.to_sym]
|
54
|
+
when :cant, "cant"
|
55
|
+
rule = self.cants[operation.to_sym]
|
56
|
+
else
|
57
|
+
raise Bali::DslError, "Undefined operation: #{auth_val}"
|
58
|
+
end
|
59
|
+
|
60
|
+
rule
|
61
|
+
end
|
62
|
+
|
63
|
+
# all rules
|
64
|
+
def rules
|
65
|
+
self.cans.values + self.cants.values
|
66
|
+
end
|
67
|
+
end
|
data/lib/bali/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bali
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0beta1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Adam Pahlevi
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-08-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.9'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.9'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.3'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.10'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.10'
|
69
|
+
description: "Bali (Bulwark Authorization Library) is a universal authorization library,
|
70
|
+
in the sense that \n it does not assume you to use specific
|
71
|
+
Ruby library/gem/framework."
|
72
|
+
email:
|
73
|
+
- adam.pahlevi@gmail.com
|
74
|
+
executables: []
|
75
|
+
extensions: []
|
76
|
+
extra_rdoc_files: []
|
77
|
+
files:
|
78
|
+
- ".gitignore"
|
79
|
+
- ".rspec"
|
80
|
+
- ".travis.yml"
|
81
|
+
- CODE_OF_CONDUCT.md
|
82
|
+
- Gemfile
|
83
|
+
- LICENSE.txt
|
84
|
+
- README.md
|
85
|
+
- Rakefile
|
86
|
+
- bali.gemspec
|
87
|
+
- bin/console
|
88
|
+
- bin/setup
|
89
|
+
- lib/bali.rb
|
90
|
+
- lib/bali/exceptions/dsl_error.rb
|
91
|
+
- lib/bali/objector.rb
|
92
|
+
- lib/bali/rule.rb
|
93
|
+
- lib/bali/rule_class.rb
|
94
|
+
- lib/bali/rule_group.rb
|
95
|
+
- lib/bali/version.rb
|
96
|
+
homepage: https://github.com/saveav/bali
|
97
|
+
licenses:
|
98
|
+
- MIT
|
99
|
+
metadata: {}
|
100
|
+
post_install_message:
|
101
|
+
rdoc_options: []
|
102
|
+
require_paths:
|
103
|
+
- lib
|
104
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">"
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: 1.3.1
|
114
|
+
requirements: []
|
115
|
+
rubyforge_project:
|
116
|
+
rubygems_version: 2.4.5
|
117
|
+
signing_key:
|
118
|
+
specification_version: 4
|
119
|
+
summary: Bali is a powerful, framework-agnostic, thread-safe Ruby language authorization
|
120
|
+
library
|
121
|
+
test_files: []
|
122
|
+
has_rdoc:
|