toni 0.0.1
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 +17 -0
- data/.travis.yml +8 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +42 -0
- data/Rakefile +1 -0
- data/lib/toni/anonymous_user.rb +7 -0
- data/lib/toni/builder.rb +90 -0
- data/lib/toni/permission.rb +31 -0
- data/lib/toni/permission_matcher.rb +20 -0
- data/lib/toni/role.rb +42 -0
- data/lib/toni/version.rb +3 -0
- data/lib/toni.rb +64 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/toni/anonymous_user_spec.rb +15 -0
- data/spec/toni/builder_spec.rb +105 -0
- data/spec/toni/permission_matcher_spec.rb +80 -0
- data/spec/toni/permission_spec.rb +114 -0
- data/spec/toni/role_spec.rb +86 -0
- data/spec/toni_spec.rb +75 -0
- data/toni.gemspec +31 -0
- metadata +172 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c0672f47c55346d477e4489c4cefcb1f0e000c9b
|
4
|
+
data.tar.gz: 8cdca30ac45fd0d3674236da33729e9042938400
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 162bce0becb671e9bf13c50ce2667a8b096cef24bf10e0358f1e4f06d33136458994ffea265134c595f8dc9b5adf8ef7da549a0a828a5eed73a6bf8f60c04388
|
7
|
+
data.tar.gz: d20995225334b18c8ac4435ea7c3d2fdf952742679db715f44804d5f3ed1e666c56190ddc96422d0a0b4bfd2d47d5318aea306dab2cc8104ce1b42e3c175460d
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Jalyna
|
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,42 @@
|
|
1
|
+
# Toni
|
2
|
+
[](https://travis-ci.org/jalyna/toni) [](https://codeclimate.com/github/jalyna/toni) [](http://inch-ci.org/github/jalyna/toni)
|
3
|
+
|
4
|
+
Toni enables you to create a centralized authorization rules configuration with a readable DSL,
|
5
|
+
similar to [declarative authorization](https://github.com/stffn/declarative_authorization), but not required to use with Ruby on Rails.
|
6
|
+
Internally the Gem is using RSpec Matchers. You can use Toni with **Sinatra** and **Ruby on Rails**.
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
gem 'toni'
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install toni
|
21
|
+
|
22
|
+
## Getting Started
|
23
|
+
|
24
|
+
### Setup Authorization Rules
|
25
|
+
|
26
|
+
TODO: Write usage instructions here
|
27
|
+
|
28
|
+
### Ruby on Rails
|
29
|
+
|
30
|
+
TODO: Write usage instructions here
|
31
|
+
|
32
|
+
### Sinatra
|
33
|
+
|
34
|
+
TODO: Write usage instructions here
|
35
|
+
|
36
|
+
## Contributing
|
37
|
+
|
38
|
+
1. Fork it
|
39
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
40
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
41
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
42
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/toni/builder.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
require "toni/role"
|
2
|
+
require "toni/permission"
|
3
|
+
require "rspec/matchers"
|
4
|
+
|
5
|
+
class Toni
|
6
|
+
class Builder
|
7
|
+
|
8
|
+
attr_reader :roles
|
9
|
+
|
10
|
+
def initialize(authorization_rules)
|
11
|
+
@roles = {}
|
12
|
+
instance_eval authorization_rules, Toni::AUTH_FILE
|
13
|
+
end
|
14
|
+
|
15
|
+
def role(role_symbol, &block)
|
16
|
+
builder = RoleBuilder.new(role_symbol, &block)
|
17
|
+
@roles[role_symbol] = builder.role
|
18
|
+
end
|
19
|
+
|
20
|
+
class RoleBuilder
|
21
|
+
|
22
|
+
attr_reader :role
|
23
|
+
|
24
|
+
def initialize(role_symbol, &block)
|
25
|
+
@role = Role.new(role_symbol)
|
26
|
+
if block_given?
|
27
|
+
instance_eval &block
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Add an ancestor to the current role
|
32
|
+
def includes(role_symbol)
|
33
|
+
@role.add_ancestor(role_symbol)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Add permissions to specific role
|
37
|
+
def has_permission_on(resource_name, options={}, &block)
|
38
|
+
builder = PermissionBuilder.new(resource_name, options, &block)
|
39
|
+
@role.add_permission(builder.permission)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
class PermissionBuilder
|
45
|
+
|
46
|
+
attr_reader :permission
|
47
|
+
|
48
|
+
def initialize(resource_name, options={}, &block)
|
49
|
+
options[:to] ||= [:manage]
|
50
|
+
@permission = Permission.new(resource_name, options[:to])
|
51
|
+
if block_given?
|
52
|
+
instance_eval &block
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Attribute should always be a column in the database on the resource
|
57
|
+
def expect_attribute(method_name, &block)
|
58
|
+
expect_method(method_name, true, &block)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Methods should be defined on the resource
|
62
|
+
def expect_method(method_name, is_attr = false, &block)
|
63
|
+
builder = ExpectationBuilder.new(&block)
|
64
|
+
permission.add_matcher(method_name, builder, is_attr)
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
class ExpectationBuilder
|
70
|
+
|
71
|
+
include ::RSpec::Matchers
|
72
|
+
|
73
|
+
def initialize(&block)
|
74
|
+
@block = block
|
75
|
+
end
|
76
|
+
|
77
|
+
def execute
|
78
|
+
unless @block.nil?
|
79
|
+
instance_eval &@block
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def current_user
|
84
|
+
Toni.current_user
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "toni/permission_matcher"
|
2
|
+
|
3
|
+
class Toni
|
4
|
+
class Permission
|
5
|
+
|
6
|
+
attr_reader :resource_name, :matchers, :activities
|
7
|
+
|
8
|
+
def initialize(resource_name, activities = [])
|
9
|
+
@resource_name = resource_name
|
10
|
+
@matchers = []
|
11
|
+
@activities = activities
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_matcher(method_name, matcher, is_attr = false)
|
15
|
+
@matchers << PermissionMatcher.new(method_name, matcher, is_attr)
|
16
|
+
end
|
17
|
+
|
18
|
+
def permitted_to?(activity, res, options={})
|
19
|
+
resource_name == (res.is_a?(Symbol) ? res : res.class.authorization_context) &&
|
20
|
+
activities.include?(activity) && matches?(res)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def matches?(object)
|
26
|
+
return true if object.is_a?(Symbol)
|
27
|
+
matchers.all? { |m| m.execute.matches?(object) }
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Toni
|
2
|
+
class PermissionMatcher
|
3
|
+
|
4
|
+
attr_reader :method_name, :matcher, :is_attr
|
5
|
+
|
6
|
+
def initialize(method_name, matcher, is_attr = false)
|
7
|
+
@method_name = method_name
|
8
|
+
@matcher = matcher
|
9
|
+
@is_attr = is_attr
|
10
|
+
end
|
11
|
+
|
12
|
+
def matches?(object)
|
13
|
+
unless object.respond_to?(method_name)
|
14
|
+
raise Toni::NoMethodForMatcherError.new("#{method_name} is not defined for #{object.inspect}")
|
15
|
+
end
|
16
|
+
matcher.matches?(object.send(method_name))
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
data/lib/toni/role.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
class Toni
|
2
|
+
class Role
|
3
|
+
|
4
|
+
attr_reader :role_symbol, :ancestors, :permissions
|
5
|
+
|
6
|
+
def initialize(role_symbol)
|
7
|
+
@role_symbol = role_symbol
|
8
|
+
@ancestors = []
|
9
|
+
@permissions = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_ancestor(role_symbol)
|
13
|
+
@ancestors << role_symbol unless @ancestors.include?(role_symbol)
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_permission(permission)
|
17
|
+
@permissions << permission
|
18
|
+
end
|
19
|
+
|
20
|
+
def permitted_to?(activity, res, options={})
|
21
|
+
unless res.is_a?(Symbol) || res.class.respond_to?(:authorization_context)
|
22
|
+
raise Toni::NoAuthorizationContextProvidedError
|
23
|
+
end
|
24
|
+
current_permissions.any? do |p|
|
25
|
+
p.permitted_to?(activity, res, options)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def current_permissions
|
32
|
+
@permissions.concat(ancestor_permissions)
|
33
|
+
end
|
34
|
+
|
35
|
+
def ancestor_permissions
|
36
|
+
@ancestor_permissions ||= ancestors.map do |ancestor|
|
37
|
+
ancestor.permissions
|
38
|
+
end.flatten
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
data/lib/toni/version.rb
ADDED
data/lib/toni.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require "toni/version"
|
2
|
+
require "toni/anonymous_user"
|
3
|
+
require "toni/builder"
|
4
|
+
|
5
|
+
class Toni
|
6
|
+
AUTH_FILE = File.dirname(__FILE__) + "/authorization_rules.rb"
|
7
|
+
|
8
|
+
class InvalidCurrentUserError < StandardError; end
|
9
|
+
class NoAuthorizationContextProvidedError < StandardError; end
|
10
|
+
class NoMethodForMatcherError < StandardError; end
|
11
|
+
class NotAuthorizedError < StandardError; end
|
12
|
+
|
13
|
+
class << self
|
14
|
+
@@without_authorization = false
|
15
|
+
|
16
|
+
def current_user
|
17
|
+
@@curent_user ||= nil
|
18
|
+
@@curent_user || anonymous_user
|
19
|
+
end
|
20
|
+
|
21
|
+
def current_user=(user)
|
22
|
+
raise InvalidCurrentUserError if !user.nil? && !user.respond_to?(:role_symbols)
|
23
|
+
@@curent_user = user
|
24
|
+
end
|
25
|
+
|
26
|
+
def roles
|
27
|
+
@@roles ||= build
|
28
|
+
end
|
29
|
+
|
30
|
+
def permitted_to?(activity, resource_or_name, options={})
|
31
|
+
return true if @@without_authorization
|
32
|
+
permitted = current_roles.any?{ |r| r.permitted_to?(activity, resource_or_name, options) }
|
33
|
+
if options[:bang] && !permitted
|
34
|
+
raise NotAuthorizedError.new("#{current_user.inspect} is not allowed to #{activity} on #{resource_or_name.inspect}")
|
35
|
+
end
|
36
|
+
|
37
|
+
permitted
|
38
|
+
end
|
39
|
+
|
40
|
+
def current_roles
|
41
|
+
roles.select { |role_symbol, r| current_user.role_symbols.include?(role_symbol) }.values
|
42
|
+
end
|
43
|
+
|
44
|
+
def without_authorization(&block)
|
45
|
+
@@without_authorization = true
|
46
|
+
yield if block_given?
|
47
|
+
@@without_authorization = false
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def build
|
53
|
+
Builder.new(authorization_rules).roles
|
54
|
+
end
|
55
|
+
|
56
|
+
def authorization_rules
|
57
|
+
File.read(AUTH_FILE)
|
58
|
+
end
|
59
|
+
|
60
|
+
def anonymous_user
|
61
|
+
@@anonymous_user ||= AnonymousUser.new
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require "codeclimate-test-reporter"
|
2
|
+
CodeClimate::TestReporter.start
|
3
|
+
|
4
|
+
require "active_record"
|
5
|
+
|
6
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
7
|
+
ActiveRecord::Schema.define do
|
8
|
+
self.verbose = false
|
9
|
+
|
10
|
+
create_table :test, :force => true do |t|
|
11
|
+
t.string :foo
|
12
|
+
t.string :bar
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "toni/anonymous_user"
|
2
|
+
|
3
|
+
describe Toni::AnonymousUser do
|
4
|
+
let(:user) { Toni::AnonymousUser.new }
|
5
|
+
|
6
|
+
describe :role_symbols do
|
7
|
+
|
8
|
+
it { expect(user).to respond_to(:role_symbols) }
|
9
|
+
|
10
|
+
it "provides a guest role by default" do
|
11
|
+
expect(user.role_symbols).to eq([:guest])
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require "toni/builder"
|
2
|
+
|
3
|
+
describe Toni::Builder do
|
4
|
+
|
5
|
+
let(:builder) { Toni::Builder.new("") }
|
6
|
+
|
7
|
+
describe :role do
|
8
|
+
it "creates a new role instance" do
|
9
|
+
expect(builder.role(:user)).to be_instance_of(Toni::Role)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe :roles do
|
14
|
+
it "return hash of defined roles" do
|
15
|
+
builder.role(:user)
|
16
|
+
expect(builder.roles.keys).to eq([:user])
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
describe Toni::Builder::RoleBuilder do
|
23
|
+
|
24
|
+
let(:builder) { Toni::Builder::RoleBuilder.new(:user) }
|
25
|
+
|
26
|
+
describe :role do
|
27
|
+
it "is a Role" do
|
28
|
+
expect(builder.role).to be_instance_of(Toni::Role)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe :includes do
|
33
|
+
it "calls add_ancestor for the given role_symbol" do
|
34
|
+
builder.includes(:parent_user)
|
35
|
+
allow(builder.role).to receive(:add_ancestor).with(:parent_user)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe :has_permission_on do
|
40
|
+
it "calls add_permission on role" do
|
41
|
+
builder.has_permission_on(:my_resource)
|
42
|
+
allow(builder.role).to receive(:add_permission)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
describe Toni::Builder::PermissionBuilder do
|
49
|
+
|
50
|
+
let(:builder) { Toni::Builder::PermissionBuilder.new(:resource_name) }
|
51
|
+
|
52
|
+
describe :permission do
|
53
|
+
it "is a Permission" do
|
54
|
+
expect(builder.permission).to be_instance_of(Toni::Permission)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe :expect_attribute do
|
59
|
+
it "calls expect_method with is_attribute true" do
|
60
|
+
builder.expect_attribute(nil)
|
61
|
+
allow(builder).to receive(:expect_method).with(nil, true)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe :expect_method do
|
66
|
+
it "calls add_matcher on permission per method_name" do
|
67
|
+
builder.expect_method(:foo) { nil }
|
68
|
+
builder.expect_method(:bar) { nil }
|
69
|
+
allow(builder.permission).to receive(:add_matcher).with(:foo, anything)
|
70
|
+
allow(builder.permission).to receive(:add_matcher).with(:bar, anything)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
describe Toni::Builder::ExpectationBuilder do
|
77
|
+
|
78
|
+
let(:builder) { Toni::Builder::ExpectationBuilder.new }
|
79
|
+
|
80
|
+
describe :execute do
|
81
|
+
it "calls block eagerly" do
|
82
|
+
double = double()
|
83
|
+
builder = Toni::Builder::ExpectationBuilder.new do
|
84
|
+
double.call
|
85
|
+
end
|
86
|
+
expect(double).to receive(:call).once
|
87
|
+
builder.execute
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "uses rspec matchers" do
|
92
|
+
it "calls rspec matcher methods" do
|
93
|
+
builder.be_nil
|
94
|
+
allow(RSpec::Matchers).to receive(:be_nil)
|
95
|
+
end
|
96
|
+
|
97
|
+
it "sets the called matcher" do
|
98
|
+
builder = Toni::Builder::ExpectationBuilder.new do
|
99
|
+
be(:cool)
|
100
|
+
end
|
101
|
+
expect(builder.execute).to be_instance_of(RSpec::Matchers::BuiltIn::Equal)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require "toni/permission_matcher"
|
2
|
+
require "toni"
|
3
|
+
require "ostruct"
|
4
|
+
|
5
|
+
describe Toni::PermissionMatcher do
|
6
|
+
|
7
|
+
let(:matcher) do
|
8
|
+
Class.new do
|
9
|
+
def matches?(actual)
|
10
|
+
false
|
11
|
+
end
|
12
|
+
end.new
|
13
|
+
end
|
14
|
+
let(:permission_matcher) { Toni::PermissionMatcher.new(:foo, matcher) }
|
15
|
+
|
16
|
+
describe :method_name do
|
17
|
+
it "sets the method_name on initialize" do
|
18
|
+
expect(Toni::PermissionMatcher.new(:method_name, nil).method_name).to eq(:method_name)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe :matcher do
|
23
|
+
it "sets the matcher on initialize" do
|
24
|
+
expect(permission_matcher.matcher).to eq(matcher)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe :is_attr do
|
29
|
+
it "sets the is_attr on initialize" do
|
30
|
+
expect(permission_matcher.is_attr).to eq(false)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe :matches? do
|
35
|
+
let(:object) do
|
36
|
+
Class.new do
|
37
|
+
def self.authorization_context
|
38
|
+
:resource_name
|
39
|
+
end
|
40
|
+
def foo
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
end.new
|
44
|
+
end
|
45
|
+
let(:object2) do
|
46
|
+
Class.new do
|
47
|
+
def self.authorization_context
|
48
|
+
:resource_name
|
49
|
+
end
|
50
|
+
end.new
|
51
|
+
end
|
52
|
+
|
53
|
+
it "raises NoMethodForMatcherError if method is not there" do
|
54
|
+
expect { permission_matcher.matches?(object2) }.to raise_error(Toni::NoMethodForMatcherError)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "returns false if matcher does not match" do
|
58
|
+
expect(permission_matcher.matches?(object)).to eq(false)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "returns true if matcher does match" do
|
62
|
+
allow(matcher).to receive(:matches?) { true }
|
63
|
+
expect(permission_matcher.matches?(object)).to eq(true)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "returns true if matcher is rspec nil matcher and value is nil" do
|
67
|
+
matcher = RSpec::Matchers::BuiltIn::BeNil.new
|
68
|
+
permission_matcher = Toni::PermissionMatcher.new(:foo, matcher)
|
69
|
+
expect(permission_matcher.matches?(object)).to eq(true)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "returns false if matcher is rspec nil matcher and value is not nil" do
|
73
|
+
allow(object).to receive(:foo) { "a string" }
|
74
|
+
matcher = RSpec::Matchers::BuiltIn::BeNil.new
|
75
|
+
permission_matcher = Toni::PermissionMatcher.new(:foo, matcher)
|
76
|
+
expect(permission_matcher.matches?(object)).to eq(false)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require "toni/permission"
|
2
|
+
require "ostruct"
|
3
|
+
|
4
|
+
describe Toni::Permission do
|
5
|
+
|
6
|
+
let(:permission) { Toni::Permission.new(:my_resource) }
|
7
|
+
|
8
|
+
describe :resource_name do
|
9
|
+
it "sets the resource_name on initialize" do
|
10
|
+
expect(Toni::Permission.new(:a_resource).resource_name).to eq(:a_resource)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe :activities do
|
15
|
+
it "sets the activities on initialize" do
|
16
|
+
expect(Toni::Permission.new(:a_resource, [:read]).activities).to eq([:read])
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe :add_matcher do
|
21
|
+
it "adds the matcher" do
|
22
|
+
permission.add_matcher(:foo, nil)
|
23
|
+
expect(permission.matchers.first).to be_instance_of(Toni::PermissionMatcher)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe :permitted_to? do
|
28
|
+
|
29
|
+
let(:permission) do
|
30
|
+
Toni::Permission.new(:resource_name, [:read, :create])
|
31
|
+
end
|
32
|
+
|
33
|
+
context "with resource_name" do
|
34
|
+
it "should be allowed to read resource_name" do
|
35
|
+
expect(permission.permitted_to?(:read, :resource_name)).to eq(true)
|
36
|
+
end
|
37
|
+
it "should be allowed to delete resource_name" do
|
38
|
+
expect(permission.permitted_to?(:delete, :resource_name)).to eq(false)
|
39
|
+
end
|
40
|
+
it "should be allowed to read xxx" do
|
41
|
+
expect(permission.permitted_to?(:read, :xxx)).to eq(false)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "with matchers" do
|
46
|
+
let(:matcher) { RSpec::Matchers::BuiltIn::BeNil.new }
|
47
|
+
let(:permission) do
|
48
|
+
p = Toni::Permission.new(:resource_name, [:read, :create])
|
49
|
+
p.add_matcher(:foo, matcher)
|
50
|
+
p
|
51
|
+
end
|
52
|
+
let(:object) do
|
53
|
+
Class.new do
|
54
|
+
def self.authorization_context
|
55
|
+
:resource_name
|
56
|
+
end
|
57
|
+
end.new
|
58
|
+
end
|
59
|
+
|
60
|
+
it "always is true when only resource name is given" do
|
61
|
+
allow(permission.matchers.first).to receive(:execute) do
|
62
|
+
Class.new do
|
63
|
+
def matches?(object)
|
64
|
+
false
|
65
|
+
end
|
66
|
+
end.new
|
67
|
+
end
|
68
|
+
expect(permission.permitted_to?(:read, :resource_name)).to eq(true)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "is false when object is given" do
|
72
|
+
allow(permission.matchers.first).to receive(:execute) do
|
73
|
+
Class.new do
|
74
|
+
def matches?(object)
|
75
|
+
false
|
76
|
+
end
|
77
|
+
end.new
|
78
|
+
end
|
79
|
+
expect(permission.permitted_to?(:read, object)).to eq(false)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context "with resource object" do
|
84
|
+
let(:object) do
|
85
|
+
Class.new do
|
86
|
+
def self.authorization_context
|
87
|
+
:resource_name
|
88
|
+
end
|
89
|
+
end.new
|
90
|
+
end
|
91
|
+
let(:object2) do
|
92
|
+
Class.new do
|
93
|
+
def self.authorization_context
|
94
|
+
:xxx
|
95
|
+
end
|
96
|
+
end.new
|
97
|
+
end
|
98
|
+
let(:object3) do
|
99
|
+
Class.new.new
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should be allowed to read object" do
|
103
|
+
expect(permission.permitted_to?(:read, object)).to eq(true)
|
104
|
+
end
|
105
|
+
it "should be allowed to delete object" do
|
106
|
+
expect(permission.permitted_to?(:delete, object)).to eq(false)
|
107
|
+
end
|
108
|
+
it "should be allowed to read xxx" do
|
109
|
+
expect(permission.permitted_to?(:read, object2)).to eq(false)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require "toni/role"
|
2
|
+
require "toni/permission"
|
3
|
+
require "ostruct"
|
4
|
+
|
5
|
+
describe Toni::Role do
|
6
|
+
|
7
|
+
let(:role) { Toni::Role.new(:user)}
|
8
|
+
let(:permission) { Toni::Permission.new(:resource_name)}
|
9
|
+
|
10
|
+
describe :role_symbol do
|
11
|
+
it "sets the role_symbol on initialize" do
|
12
|
+
expect(Toni::Role.new(:everything).role_symbol).to eq(:everything)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe :add_ancestor do
|
17
|
+
it "adds role symbol to ancestors" do
|
18
|
+
role.add_ancestor(:another)
|
19
|
+
expect(role.ancestors).to eq([:another])
|
20
|
+
end
|
21
|
+
|
22
|
+
it "adds role symbol only once" do
|
23
|
+
role.add_ancestor(:another)
|
24
|
+
role.add_ancestor(:another)
|
25
|
+
expect(role.ancestors).to eq([:another])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe :add_permission do
|
30
|
+
it "adds permission to permissions" do
|
31
|
+
role.add_permission(permission)
|
32
|
+
expect(role.permissions).to eq([permission])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe :permitted_to? do
|
37
|
+
|
38
|
+
let(:permission) do
|
39
|
+
Toni::Permission.new(:resource_name, [:read, :create])
|
40
|
+
end
|
41
|
+
|
42
|
+
let(:role) do
|
43
|
+
role = Toni::Role.new(:user)
|
44
|
+
role.add_permission(permission)
|
45
|
+
role
|
46
|
+
end
|
47
|
+
|
48
|
+
let(:object) do
|
49
|
+
Class.new.new
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should raise NoAuthorizationContextProvidedError" do
|
53
|
+
expect { role.permitted_to?(:read, object) }.to raise_error(Toni::NoAuthorizationContextProvidedError)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should call permission permitted_to? and return true if permission is true" do
|
57
|
+
allow(permission).to receive(:permitted_to?).with(:read, :resource_name, anything) { true }
|
58
|
+
expect(role.permitted_to?(:read, :resource_name)).to eq(true)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should call permission permitted_to? and return false if permission is false" do
|
62
|
+
allow(permission).to receive(:permitted_to?).with(:read, :resource_name, anything) { false }
|
63
|
+
expect(role.permitted_to?(:read, :resource_name)).to eq(false)
|
64
|
+
end
|
65
|
+
|
66
|
+
context "inheritance" do
|
67
|
+
let(:parent_role) do
|
68
|
+
role = Toni::Role.new(:parent_user)
|
69
|
+
role.add_permission(permission)
|
70
|
+
role
|
71
|
+
end
|
72
|
+
|
73
|
+
let(:role) do
|
74
|
+
role = Toni::Role.new(:user)
|
75
|
+
role.add_ancestor(parent_role)
|
76
|
+
role
|
77
|
+
end
|
78
|
+
|
79
|
+
it "uses permission from parent role" do
|
80
|
+
allow(permission).to receive(:permitted_to?).with(:read, :resource_name, anything) { true }
|
81
|
+
expect(role.permitted_to?(:read, :resource_name)).to eq(true)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
data/spec/toni_spec.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require "toni"
|
2
|
+
require "ostruct"
|
3
|
+
|
4
|
+
describe Toni do
|
5
|
+
|
6
|
+
let(:role_symbols) { [] }
|
7
|
+
let(:current_user) { OpenStruct.new(role_symbols: role_symbols) }
|
8
|
+
|
9
|
+
describe ".current_user" do
|
10
|
+
it "has a anonymous user as default" do
|
11
|
+
expect(Toni.current_user).to be_instance_of(Toni::AnonymousUser)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe ".current_user=" do
|
16
|
+
it "raises an error when current user does not respond to role_symbols" do
|
17
|
+
expect{ Toni.current_user = OpenStruct.new }.to raise_error(Toni::InvalidCurrentUserError)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "sets current user respond to role_symbols" do
|
21
|
+
Toni.current_user = current_user
|
22
|
+
expect(Toni.current_user).to eq(current_user)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "uses an anonymous user when current user was set to nil" do
|
26
|
+
Toni.current_user = nil
|
27
|
+
expect(Toni.current_user).to be_instance_of(Toni::AnonymousUser)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe ".roles" do
|
32
|
+
pending
|
33
|
+
end
|
34
|
+
|
35
|
+
describe ".current_roles" do
|
36
|
+
pending
|
37
|
+
end
|
38
|
+
|
39
|
+
describe ".without_authorization" do
|
40
|
+
pending
|
41
|
+
end
|
42
|
+
|
43
|
+
describe ".permitted_to?" do
|
44
|
+
let(:role_symbols) { [:user, :seller] }
|
45
|
+
let(:user_role) { Toni::Role.new(:user) }
|
46
|
+
let(:seller_role) { Toni::Role.new(:seller) }
|
47
|
+
let(:roles) { { user: user_role, seller: seller_role } }
|
48
|
+
|
49
|
+
before do
|
50
|
+
allow(Toni).to receive(:roles) { roles }
|
51
|
+
Toni.current_user = current_user
|
52
|
+
end
|
53
|
+
|
54
|
+
it "is not permitted_to if all roles return false" do
|
55
|
+
allow(user_role).to receive(:permitted_to?).with(:read, :resource, anything) { false }
|
56
|
+
allow(seller_role).to receive(:permitted_to?).with(:read, :resource, anything) { false }
|
57
|
+
expect(Toni.permitted_to?(:read, :resource)).to eq(false)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "is permitted_to if at least user_role returns true" do
|
61
|
+
allow(user_role).to receive(:permitted_to?).with(:read, :resource, anything) { true }
|
62
|
+
allow(seller_role).to receive(:permitted_to?).with(:read, :resource, anything) { false }
|
63
|
+
expect(Toni.permitted_to?(:read, :resource)).to eq(true)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "raises error if it is not permitted to and bang is true" do
|
67
|
+
allow(user_role).to receive(:permitted_to?).with(:read, :resource, anything) { false }
|
68
|
+
allow(seller_role).to receive(:permitted_to?).with(:read, :resource, anything) { false }
|
69
|
+
expect { Toni.permitted_to?(:read, :resource, bang: true) }.to raise_error(Toni::NotAuthorizedError)
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
data/toni.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'toni/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "toni"
|
8
|
+
spec.version = Toni::VERSION
|
9
|
+
spec.authors = ["Jalyna"]
|
10
|
+
spec.email = ["jalyna.schroeder@gmail.com"]
|
11
|
+
spec.description = %q{Ruby Authorization Gem using Role Based Access Control}
|
12
|
+
spec.summary = %q{Enables you to create a centralized authorization
|
13
|
+
rules configuration, similar to declarative authorization,
|
14
|
+
but not bound to Ruby on Rails. Internally the Gem is using
|
15
|
+
RSpec Matchers.}
|
16
|
+
spec.homepage = "https://github.com/jalyna/toni"
|
17
|
+
spec.license = "MIT"
|
18
|
+
|
19
|
+
spec.files = `git ls-files`.split($/)
|
20
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
21
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_dependency "rspec-expectations"
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
26
|
+
spec.add_development_dependency "rake"
|
27
|
+
spec.add_development_dependency "rspec"
|
28
|
+
spec.add_development_dependency "activerecord", "~> 4.1"
|
29
|
+
spec.add_development_dependency "rails", "~> 4.1"
|
30
|
+
spec.add_development_dependency "sqlite3"
|
31
|
+
end
|
metadata
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: toni
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jalyna
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-08-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec-expectations
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.3'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: activerecord
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '4.1'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '4.1'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rails
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '4.1'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '4.1'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: sqlite3
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: Ruby Authorization Gem using Role Based Access Control
|
112
|
+
email:
|
113
|
+
- jalyna.schroeder@gmail.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- .gitignore
|
119
|
+
- .travis.yml
|
120
|
+
- Gemfile
|
121
|
+
- LICENSE.txt
|
122
|
+
- README.md
|
123
|
+
- Rakefile
|
124
|
+
- lib/toni.rb
|
125
|
+
- lib/toni/anonymous_user.rb
|
126
|
+
- lib/toni/builder.rb
|
127
|
+
- lib/toni/permission.rb
|
128
|
+
- lib/toni/permission_matcher.rb
|
129
|
+
- lib/toni/role.rb
|
130
|
+
- lib/toni/version.rb
|
131
|
+
- spec/spec_helper.rb
|
132
|
+
- spec/toni/anonymous_user_spec.rb
|
133
|
+
- spec/toni/builder_spec.rb
|
134
|
+
- spec/toni/permission_matcher_spec.rb
|
135
|
+
- spec/toni/permission_spec.rb
|
136
|
+
- spec/toni/role_spec.rb
|
137
|
+
- spec/toni_spec.rb
|
138
|
+
- toni.gemspec
|
139
|
+
homepage: https://github.com/jalyna/toni
|
140
|
+
licenses:
|
141
|
+
- MIT
|
142
|
+
metadata: {}
|
143
|
+
post_install_message:
|
144
|
+
rdoc_options: []
|
145
|
+
require_paths:
|
146
|
+
- lib
|
147
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - '>='
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
153
|
+
requirements:
|
154
|
+
- - '>='
|
155
|
+
- !ruby/object:Gem::Version
|
156
|
+
version: '0'
|
157
|
+
requirements: []
|
158
|
+
rubyforge_project:
|
159
|
+
rubygems_version: 2.0.3
|
160
|
+
signing_key:
|
161
|
+
specification_version: 4
|
162
|
+
summary: Enables you to create a centralized authorization rules configuration, similar
|
163
|
+
to declarative authorization, but not bound to Ruby on Rails. Internally the Gem
|
164
|
+
is using RSpec Matchers.
|
165
|
+
test_files:
|
166
|
+
- spec/spec_helper.rb
|
167
|
+
- spec/toni/anonymous_user_spec.rb
|
168
|
+
- spec/toni/builder_spec.rb
|
169
|
+
- spec/toni/permission_matcher_spec.rb
|
170
|
+
- spec/toni/permission_spec.rb
|
171
|
+
- spec/toni/role_spec.rb
|
172
|
+
- spec/toni_spec.rb
|