toni 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -1
- data/Gemfile +4 -1
- data/README.md +104 -5
- data/examples/sinatra/README.md +9 -0
- data/examples/sinatra/app.rb +33 -0
- data/examples/sinatra/config/authorization_rules.rb +7 -0
- data/examples/sinatra/config.ru +2 -0
- data/examples/sinatra/spec/authorization_rules_spec.rb +20 -0
- data/examples/sinatra/spec/spec_helper.rb +10 -0
- data/examples/sinatra/user.rb +7 -0
- data/lib/toni/anonymous_user.rb +1 -1
- data/lib/toni/builder.rb +10 -3
- data/lib/toni/permission.rb +4 -4
- data/lib/toni/permission_matcher.rb +38 -4
- data/lib/toni/role.rb +4 -4
- data/lib/toni/rspec_helper.rb +27 -0
- data/lib/toni/sinatra.rb +33 -0
- data/lib/toni/version.rb +2 -2
- data/lib/toni.rb +15 -15
- data/spec/spec_helper.rb +6 -1
- data/spec/toni/anonymous_user_spec.rb +1 -0
- data/spec/toni/builder_spec.rb +38 -7
- data/spec/toni/permission_matcher_spec.rb +40 -0
- data/spec/toni/permission_spec.rb +1 -0
- data/spec/toni/role_spec.rb +1 -0
- data/spec/toni/rspec_helper_spec.rb +37 -0
- data/spec/toni/sinatra_spec.rb +78 -0
- data/spec/toni_spec.rb +32 -3
- data/toni.gemspec +1 -1
- metadata +16 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7ac2b196171e40bf0d69e5c191c4a9435ce3855a
|
4
|
+
data.tar.gz: 587bbdcb7dd9a86db28551388a8efe786b6ceaa6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bdadf5d1db3e86f10963fda52f7b810d2f1ad87c071a0959d871f3f28a9bbac9dae2e28838eeaa7bfb3e4eb9f60ecf7fe88476cb2bff28e00de6415fd940b177
|
7
|
+
data.tar.gz: aa782e08c89471234beb82c2690f29c6ed0f8a10dc9e597e2ef8cd159784bf86d59278daf56e318301d43d6b83324d4c00e5fb6331618e64e2abca87c3752902
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
@@ -3,4 +3,7 @@ source 'https://rubygems.org'
|
|
3
3
|
# Specify your gem's dependencies in toni.gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
|
-
gem "codeclimate-test-reporter", group: :test, require: nil
|
6
|
+
gem "codeclimate-test-reporter", group: :test, require: nil
|
7
|
+
gem "sqlite3", group: :test, platform: :ruby
|
8
|
+
gem "jdbc-sqlite3", group: :test, platform: :jruby
|
9
|
+
gem "activerecord-jdbcsqlite3-adapter", group: :test, platform: :jruby
|
data/README.md
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# Toni
|
2
|
-
[![Build Status](https://travis-ci.org/jalyna/toni.svg?branch=master)](https://travis-ci.org/jalyna/toni) [![Code Climate](https://codeclimate.com/github/jalyna/toni/badges/gpa.svg)](https://codeclimate.com/github/jalyna/toni) [![Inline docs](http://inch-ci.org/github/jalyna/toni.png?branch=master)](http://inch-ci.org/github/jalyna/toni)
|
2
|
+
[![Gem Version](https://badge.fury.io/rb/toni.svg)](http://badge.fury.io/rb/toni) [![Build Status](https://travis-ci.org/jalyna/toni.svg?branch=master)](https://travis-ci.org/jalyna/toni) [![Code Climate](https://codeclimate.com/github/jalyna/toni/badges/gpa.svg)](https://codeclimate.com/github/jalyna/toni) [![Test Coverage](https://codeclimate.com/github/jalyna/toni/badges/coverage.svg)](https://codeclimate.com/github/jalyna/toni) [![Inline docs](http://inch-ci.org/github/jalyna/toni.png?branch=master)](http://inch-ci.org/github/jalyna/toni)
|
3
3
|
|
4
4
|
Toni enables you to create a centralized authorization rules configuration with a readable DSL,
|
5
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**.
|
6
|
+
Internally the Gem is using RSpec Matchers. You can use Toni with **[Sinatra](#sinatra)** and **[Ruby on Rails](#ruby-on-rails)**.
|
7
7
|
|
8
8
|
## Installation
|
9
9
|
|
@@ -23,14 +23,113 @@ Or install it yourself as:
|
|
23
23
|
|
24
24
|
### Setup Authorization Rules
|
25
25
|
|
26
|
-
|
26
|
+
To setup the rules that your toni uses you have to create a `authorization_rules.rb` file e.g. in your config folder:
|
27
27
|
|
28
|
-
|
28
|
+
```ruby
|
29
|
+
# Guest role gets automatically assigned if you don't have a current_user
|
30
|
+
role :guest do
|
31
|
+
has_permission_on :books, to: :read
|
32
|
+
end
|
29
33
|
|
30
|
-
|
34
|
+
role :author do
|
35
|
+
has_permission_on :books, to: [:create, :read, :update, :delete] do
|
36
|
+
expect_attribute :author_id { eq(current_user.id) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
role :admin do
|
41
|
+
has_permission_on :books, to: [:create, :read, :update, :delete]
|
42
|
+
has_permission_on :users, to: :delete do
|
43
|
+
expect_attribute :id { not.eq(current_user.id) }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
```
|
47
|
+
|
48
|
+
### Assign Roles
|
49
|
+
|
50
|
+
To provide roles it is necessary to pass a `current_user` that provides a `role_symbols` method.
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
class User
|
54
|
+
def role_symbols
|
55
|
+
[:user, :admin]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
Toni.current_user = User.new
|
60
|
+
```
|
61
|
+
|
62
|
+
What logic this method contains is up to you.
|
31
63
|
|
32
64
|
### Sinatra
|
33
65
|
|
66
|
+
The Toni Sinatra Module provides some usefule helpers and an error handling which can be useful
|
67
|
+
if you want to map not authorized errors. And there is a `permitted_to` method where you can pass a block that gets executed when the current user has the demanded permissions.
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
require "toni/sinatra"
|
71
|
+
|
72
|
+
class AuthorizationApp < Sinatra::Base
|
73
|
+
# Register Toni Sinatra
|
74
|
+
register Toni::Sinatra
|
75
|
+
|
76
|
+
before do
|
77
|
+
# Set user after authentication
|
78
|
+
Toni.current_user = User.new
|
79
|
+
end
|
80
|
+
|
81
|
+
not_authorized do |e|
|
82
|
+
# Map NotAuthorizedError to 403
|
83
|
+
halt 403
|
84
|
+
end
|
85
|
+
|
86
|
+
set :show_exceptions, false
|
87
|
+
|
88
|
+
get '/' do
|
89
|
+
# This won't throw an exception, remove bang: false if you
|
90
|
+
# want that the not_authorized error handler is called
|
91
|
+
permitted_to(:read, :books, bang: false) do
|
92
|
+
"You can read books!"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
get '/another_page' do
|
97
|
+
without_authorization do
|
98
|
+
"Hello world"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
```
|
103
|
+
|
104
|
+
A more detailed example can be found in [examples/sinatra](examples/sinatra).
|
105
|
+
|
106
|
+
### Testing with RSpec
|
107
|
+
|
108
|
+
To setup Toni's RspecHelper you have to change `spec_helper.rb`:
|
109
|
+
```ruby
|
110
|
+
require "toni/rspec_helper"
|
111
|
+
RSpec.configure { |c| c.include Toni::RSpecHelper }
|
112
|
+
```
|
113
|
+
|
114
|
+
Then you are able to test permissions with following matcher:
|
115
|
+
```ruby
|
116
|
+
expect(user).to have_permission_on :books, to: :read
|
117
|
+
expect(user).to_not have_permission_on :books, to: :create
|
118
|
+
```
|
119
|
+
|
120
|
+
By default your specs are running with authorization. If you want to exclude parts or execute with a specific user you can use:
|
121
|
+
```ruby
|
122
|
+
without_authorization do
|
123
|
+
book.create
|
124
|
+
end
|
125
|
+
|
126
|
+
with_user(user) do
|
127
|
+
book.save
|
128
|
+
end
|
129
|
+
```
|
130
|
+
|
131
|
+
### Ruby on Rails
|
132
|
+
|
34
133
|
TODO: Write usage instructions here
|
35
134
|
|
36
135
|
## Contributing
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'toni'
|
2
|
+
require 'toni/sinatra'
|
3
|
+
require 'sinatra'
|
4
|
+
require 'CGI'
|
5
|
+
require './user'
|
6
|
+
|
7
|
+
class AuthorizationApp < Sinatra::Base
|
8
|
+
register Toni::Sinatra
|
9
|
+
|
10
|
+
before do
|
11
|
+
# Set user after authentication
|
12
|
+
Toni.current_user = User.new
|
13
|
+
end
|
14
|
+
|
15
|
+
not_authorized do |e|
|
16
|
+
# Map NotAuthorizedError to 403
|
17
|
+
halt 403, CGI.escapeHTML(e.message)
|
18
|
+
end
|
19
|
+
|
20
|
+
set :show_exceptions, false
|
21
|
+
|
22
|
+
get '/' do
|
23
|
+
permitted_to(:read, :books, bang: false) do
|
24
|
+
"You can read books!"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
get '/create' do
|
29
|
+
permitted_to(:create, :books) do
|
30
|
+
"You can create books"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Authorization Rules" do
|
4
|
+
describe "user" do
|
5
|
+
it "is permitted to read books" do
|
6
|
+
user = double("user", role_symbols: [:user])
|
7
|
+
expect(user).to have_permission_on :books, to: :read
|
8
|
+
end
|
9
|
+
it "is not permitted to create books" do
|
10
|
+
user = double("user", role_symbols: [:user])
|
11
|
+
expect(user).to_not have_permission_on :books, to: :create
|
12
|
+
end
|
13
|
+
end
|
14
|
+
describe "admin" do
|
15
|
+
it "is permitted to create books" do
|
16
|
+
user = double("user", role_symbols: [:admin])
|
17
|
+
expect(user).to have_permission_on :books, to: :create
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/toni/anonymous_user.rb
CHANGED
data/lib/toni/builder.rb
CHANGED
@@ -2,7 +2,7 @@ require "toni/role"
|
|
2
2
|
require "toni/permission"
|
3
3
|
require "rspec/matchers"
|
4
4
|
|
5
|
-
|
5
|
+
module Toni
|
6
6
|
class Builder
|
7
7
|
|
8
8
|
attr_reader :roles
|
@@ -46,8 +46,7 @@ class Toni
|
|
46
46
|
attr_reader :permission
|
47
47
|
|
48
48
|
def initialize(resource_name, options={}, &block)
|
49
|
-
options[:to]
|
50
|
-
@permission = Permission.new(resource_name, options[:to])
|
49
|
+
@permission = Permission.new(resource_name, [*options[:to]])
|
51
50
|
if block_given?
|
52
51
|
instance_eval &block
|
53
52
|
end
|
@@ -84,6 +83,14 @@ class Toni
|
|
84
83
|
Toni.current_user
|
85
84
|
end
|
86
85
|
|
86
|
+
def not
|
87
|
+
Toni::PermissionMatcher::NotMatcher.new
|
88
|
+
end
|
89
|
+
|
90
|
+
def permitted_to(activity)
|
91
|
+
Toni::PermissionMatcher::PermittedToMatcher.new(activity)
|
92
|
+
end
|
93
|
+
|
87
94
|
end
|
88
95
|
|
89
96
|
end
|
data/lib/toni/permission.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
require "toni/permission_matcher"
|
2
2
|
|
3
|
-
|
3
|
+
module Toni
|
4
4
|
class Permission
|
5
5
|
|
6
6
|
attr_reader :resource_name, :matchers, :activities
|
7
7
|
|
8
8
|
def initialize(resource_name, activities = [])
|
9
|
-
@resource_name
|
10
|
-
@matchers
|
11
|
-
@activities
|
9
|
+
@resource_name = resource_name
|
10
|
+
@matchers = []
|
11
|
+
@activities = activities
|
12
12
|
end
|
13
13
|
|
14
14
|
def add_matcher(method_name, matcher, is_attr = false)
|
@@ -1,12 +1,12 @@
|
|
1
|
-
|
1
|
+
module Toni
|
2
2
|
class PermissionMatcher
|
3
3
|
|
4
4
|
attr_reader :method_name, :matcher, :is_attr
|
5
5
|
|
6
6
|
def initialize(method_name, matcher, is_attr = false)
|
7
|
-
@method_name
|
8
|
-
@matcher
|
9
|
-
@is_attr
|
7
|
+
@method_name = method_name
|
8
|
+
@matcher = matcher
|
9
|
+
@is_attr = is_attr
|
10
10
|
end
|
11
11
|
|
12
12
|
def matches?(object)
|
@@ -16,5 +16,39 @@ class Toni
|
|
16
16
|
matcher.matches?(object.send(method_name))
|
17
17
|
end
|
18
18
|
|
19
|
+
class NotMatcher
|
20
|
+
attr_reader :matcher
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@matcher = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def method_missing(method_name, *args)
|
27
|
+
builder = Toni::Builder::ExpectationBuilder.new
|
28
|
+
if builder.respond_to?(method_name)
|
29
|
+
@matcher = builder.send(method_name, *args)
|
30
|
+
else
|
31
|
+
super
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def matches?(object)
|
36
|
+
raise Toni::MissingMatcherError if @matcher.nil?
|
37
|
+
!@matcher.matches?(object)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class PermittedToMatcher
|
42
|
+
|
43
|
+
def initialize(activity)
|
44
|
+
@activity = activity
|
45
|
+
end
|
46
|
+
|
47
|
+
def matches?(object)
|
48
|
+
Toni.permitted_to?(@activity, object)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
19
53
|
end
|
20
54
|
end
|
data/lib/toni/role.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
-
|
1
|
+
module Toni
|
2
2
|
class Role
|
3
3
|
|
4
4
|
attr_reader :role_symbol, :ancestors, :permissions
|
5
5
|
|
6
6
|
def initialize(role_symbol)
|
7
|
-
@role_symbol
|
8
|
-
@ancestors
|
9
|
-
@permissions
|
7
|
+
@role_symbol = role_symbol
|
8
|
+
@ancestors = []
|
9
|
+
@permissions = []
|
10
10
|
end
|
11
11
|
|
12
12
|
def add_ancestor(role_symbol)
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rspec/expectations'
|
2
|
+
require 'toni'
|
3
|
+
|
4
|
+
module Toni
|
5
|
+
module RSpecHelper
|
6
|
+
extend RSpec::Matchers::DSL
|
7
|
+
|
8
|
+
def with_user(user, &block)
|
9
|
+
Toni.current_user = user
|
10
|
+
result = yield
|
11
|
+
Toni.current_user = nil
|
12
|
+
result
|
13
|
+
end
|
14
|
+
|
15
|
+
def without_authorization(&block)
|
16
|
+
Toni.without_authorization(&block)
|
17
|
+
end
|
18
|
+
|
19
|
+
matcher :have_permission_on do |resource_or_name, options = {}|
|
20
|
+
match do |user|
|
21
|
+
with_user(user) do
|
22
|
+
[*options[:to]].all? { |a| Toni.permitted_to?(a, resource_or_name) }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/toni/sinatra.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
|
3
|
+
module Toni
|
4
|
+
module Sinatra
|
5
|
+
module AuthorizationHelper
|
6
|
+
def permitted_to(activity, resource_or_name, options={}, &block)
|
7
|
+
options[:bang] = true unless options.key?(:bang)
|
8
|
+
if Toni.permitted_to?(activity, resource_or_name, options)
|
9
|
+
block.call if block_given?
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def without_authorization(&block)
|
14
|
+
Toni.without_authorization(&block)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module AuthorizationErrorHandler
|
19
|
+
def not_authorized(&block)
|
20
|
+
error do
|
21
|
+
if env['sinatra.error'].instance_of? Toni::NotAuthorizedError
|
22
|
+
instance_exec env['sinatra.error'], &block if block_given?
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.registered(app)
|
29
|
+
app.helpers AuthorizationHelper
|
30
|
+
app.register AuthorizationErrorHandler
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/toni/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
VERSION = "0.0.
|
1
|
+
module Toni
|
2
|
+
VERSION = "0.0.2"
|
3
3
|
end
|
data/lib/toni.rb
CHANGED
@@ -2,36 +2,35 @@ require "toni/version"
|
|
2
2
|
require "toni/anonymous_user"
|
3
3
|
require "toni/builder"
|
4
4
|
|
5
|
-
|
6
|
-
AUTH_FILE =
|
5
|
+
module Toni
|
6
|
+
AUTH_FILE = "config/authorization_rules.rb"
|
7
7
|
|
8
8
|
class InvalidCurrentUserError < StandardError; end
|
9
9
|
class NoAuthorizationContextProvidedError < StandardError; end
|
10
10
|
class NoMethodForMatcherError < StandardError; end
|
11
11
|
class NotAuthorizedError < StandardError; end
|
12
|
+
class MissingMatcherError < StandardError; end
|
12
13
|
|
13
14
|
class << self
|
14
|
-
@@without_authorization = false
|
15
|
-
|
16
15
|
def current_user
|
17
|
-
|
18
|
-
|
16
|
+
@curent_user ||= nil
|
17
|
+
@curent_user || anonymous_user
|
19
18
|
end
|
20
19
|
|
21
20
|
def current_user=(user)
|
22
21
|
raise InvalidCurrentUserError if !user.nil? && !user.respond_to?(:role_symbols)
|
23
|
-
|
22
|
+
@curent_user = user
|
24
23
|
end
|
25
24
|
|
26
25
|
def roles
|
27
|
-
|
26
|
+
@roles ||= build
|
28
27
|
end
|
29
28
|
|
30
29
|
def permitted_to?(activity, resource_or_name, options={})
|
31
|
-
return true if
|
30
|
+
return true if @without_authorization
|
32
31
|
permitted = current_roles.any?{ |r| r.permitted_to?(activity, resource_or_name, options) }
|
33
32
|
if options[:bang] && !permitted
|
34
|
-
raise NotAuthorizedError.new("#{current_user.inspect} is not allowed to #{activity} on #{resource_or_name.inspect}")
|
33
|
+
raise NotAuthorizedError.new("#{current_user.to_s} with #{current_user.role_symbols.inspect} is not allowed to #{activity} on #{resource_or_name.inspect}")
|
35
34
|
end
|
36
35
|
|
37
36
|
permitted
|
@@ -42,9 +41,10 @@ class Toni
|
|
42
41
|
end
|
43
42
|
|
44
43
|
def without_authorization(&block)
|
45
|
-
|
46
|
-
|
47
|
-
|
44
|
+
@without_authorization = true
|
45
|
+
result = block_given? ? yield : nil
|
46
|
+
@without_authorization = false
|
47
|
+
result
|
48
48
|
end
|
49
49
|
|
50
50
|
private
|
@@ -58,7 +58,7 @@ class Toni
|
|
58
58
|
end
|
59
59
|
|
60
60
|
def anonymous_user
|
61
|
-
|
61
|
+
@anonymous_user ||= AnonymousUser.new
|
62
62
|
end
|
63
63
|
end
|
64
|
-
end
|
64
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -3,7 +3,12 @@ CodeClimate::TestReporter.start
|
|
3
3
|
|
4
4
|
require "active_record"
|
5
5
|
|
6
|
-
|
6
|
+
if ENV["RUBY_VERSION"].include?("jruby")
|
7
|
+
ActiveRecord::Base.establish_connection(:adapter => "jdbcsqlite3", :database => ":memory:")
|
8
|
+
else
|
9
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
10
|
+
end
|
11
|
+
|
7
12
|
ActiveRecord::Schema.define do
|
8
13
|
self.verbose = false
|
9
14
|
|
data/spec/toni/builder_spec.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require "spec_helper"
|
1
2
|
require "toni/builder"
|
2
3
|
|
3
4
|
describe Toni::Builder do
|
@@ -31,15 +32,15 @@ describe Toni::Builder::RoleBuilder do
|
|
31
32
|
|
32
33
|
describe :includes do
|
33
34
|
it "calls add_ancestor for the given role_symbol" do
|
35
|
+
expect(builder.role).to receive(:add_ancestor).with(:parent_user)
|
34
36
|
builder.includes(:parent_user)
|
35
|
-
allow(builder.role).to receive(:add_ancestor).with(:parent_user)
|
36
37
|
end
|
37
38
|
end
|
38
39
|
|
39
40
|
describe :has_permission_on do
|
40
41
|
it "calls add_permission on role" do
|
42
|
+
expect(builder.role).to receive(:add_permission)
|
41
43
|
builder.has_permission_on(:my_resource)
|
42
|
-
allow(builder.role).to receive(:add_permission)
|
43
44
|
end
|
44
45
|
end
|
45
46
|
|
@@ -49,6 +50,17 @@ describe Toni::Builder::PermissionBuilder do
|
|
49
50
|
|
50
51
|
let(:builder) { Toni::Builder::PermissionBuilder.new(:resource_name) }
|
51
52
|
|
53
|
+
describe :initialize do
|
54
|
+
it "sets the activtities" do
|
55
|
+
builder = Toni::Builder::PermissionBuilder.new(:resource_name, to: [:create, :read])
|
56
|
+
expect(builder.permission.activities).to eq([:create, :read])
|
57
|
+
end
|
58
|
+
it "sets single activtities" do
|
59
|
+
builder = Toni::Builder::PermissionBuilder.new(:resource_name, to: :read)
|
60
|
+
expect(builder.permission.activities).to eq([:read])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
52
64
|
describe :permission do
|
53
65
|
it "is a Permission" do
|
54
66
|
expect(builder.permission).to be_instance_of(Toni::Permission)
|
@@ -57,17 +69,17 @@ describe Toni::Builder::PermissionBuilder do
|
|
57
69
|
|
58
70
|
describe :expect_attribute do
|
59
71
|
it "calls expect_method with is_attribute true" do
|
72
|
+
expect(builder).to receive(:expect_method).with(nil, true)
|
60
73
|
builder.expect_attribute(nil)
|
61
|
-
allow(builder).to receive(:expect_method).with(nil, true)
|
62
74
|
end
|
63
75
|
end
|
64
76
|
|
65
77
|
describe :expect_method do
|
66
78
|
it "calls add_matcher on permission per method_name" do
|
79
|
+
expect(builder.permission).to receive(:add_matcher).with(:foo, anything, false)
|
80
|
+
expect(builder.permission).to receive(:add_matcher).with(:bar, anything, false)
|
67
81
|
builder.expect_method(:foo) { nil }
|
68
82
|
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
83
|
end
|
72
84
|
end
|
73
85
|
|
@@ -78,7 +90,7 @@ describe Toni::Builder::ExpectationBuilder do
|
|
78
90
|
let(:builder) { Toni::Builder::ExpectationBuilder.new }
|
79
91
|
|
80
92
|
describe :execute do
|
81
|
-
it "calls block
|
93
|
+
it "calls block lazely" do
|
82
94
|
double = double()
|
83
95
|
builder = Toni::Builder::ExpectationBuilder.new do
|
84
96
|
double.call
|
@@ -90,8 +102,8 @@ describe Toni::Builder::ExpectationBuilder do
|
|
90
102
|
|
91
103
|
describe "uses rspec matchers" do
|
92
104
|
it "calls rspec matcher methods" do
|
93
|
-
builder.be_nil
|
94
105
|
allow(RSpec::Matchers).to receive(:be_nil)
|
106
|
+
builder.be_nil
|
95
107
|
end
|
96
108
|
|
97
109
|
it "sets the called matcher" do
|
@@ -102,4 +114,23 @@ describe Toni::Builder::ExpectationBuilder do
|
|
102
114
|
end
|
103
115
|
end
|
104
116
|
|
117
|
+
describe :not do
|
118
|
+
it "should create a NotMatcher" do
|
119
|
+
expect(builder.not).to be_instance_of(Toni::PermissionMatcher::NotMatcher)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe :permitted_to do
|
124
|
+
it "should create a PermittedToMatcher" do
|
125
|
+
expect(builder.permitted_to(:read)).to be_instance_of(Toni::PermissionMatcher::PermittedToMatcher)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
describe :current_user do
|
130
|
+
it "gets the toni user" do
|
131
|
+
expect(Toni).to receive(:current_user)
|
132
|
+
builder.current_user
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
105
136
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require "spec_helper"
|
1
2
|
require "toni/permission_matcher"
|
2
3
|
require "toni"
|
3
4
|
require "ostruct"
|
@@ -77,4 +78,43 @@ describe Toni::PermissionMatcher do
|
|
77
78
|
end
|
78
79
|
end
|
79
80
|
|
81
|
+
end
|
82
|
+
|
83
|
+
describe Toni::PermissionMatcher::NotMatcher do
|
84
|
+
|
85
|
+
let(:not_matcher) { Toni::PermissionMatcher::NotMatcher.new }
|
86
|
+
|
87
|
+
describe :method_missing do
|
88
|
+
it "looks up method in ExpectationBuilder" do
|
89
|
+
expect { not_matcher.be_truthy }.to_not raise_error
|
90
|
+
end
|
91
|
+
it "throws error when method does not exist" do
|
92
|
+
expect { not_matcher.that_does_not_exist }.to raise_error
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe :matches? do
|
97
|
+
it "raises error when matcher is nil" do
|
98
|
+
expect { not_matcher.matches?(nil) }.to raise_error(Toni::MissingMatcherError)
|
99
|
+
end
|
100
|
+
it "uses matches method of matcher and flips the boolean" do
|
101
|
+
not_matcher.eq(nil)
|
102
|
+
expect(not_matcher.matches?(nil)).to eq(false)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
describe Toni::PermissionMatcher::PermittedToMatcher do
|
109
|
+
|
110
|
+
let(:permitted_to_matcher) { Toni::PermissionMatcher::PermittedToMatcher.new(:read) }
|
111
|
+
|
112
|
+
describe :matches? do
|
113
|
+
it "calls Toni.permitted_to? for given attribute" do
|
114
|
+
double = double
|
115
|
+
expect(Toni).to receive(:permitted_to?).with(:read, double)
|
116
|
+
permitted_to_matcher.matches?(double)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
80
120
|
end
|
data/spec/toni/role_spec.rb
CHANGED
@@ -0,0 +1,37 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "toni/rspec_helper"
|
3
|
+
|
4
|
+
describe Toni::RSpecHelper do
|
5
|
+
include Toni::RSpecHelper
|
6
|
+
|
7
|
+
describe :without_authorization do
|
8
|
+
it "calls Toni without_authorization" do
|
9
|
+
expect(Toni).to receive(:without_authorization)
|
10
|
+
without_authorization
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe :with_user do
|
15
|
+
it "calls block with given user and return the result of the block" do
|
16
|
+
user = double("user")
|
17
|
+
expect(Toni).to receive(:current_user=).with(user)
|
18
|
+
expect(Toni).to receive(:current_user=).with(nil)
|
19
|
+
expect(with_user(user) { 1 }).to eq(1)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "have_permission_on matcher" do
|
24
|
+
it "returns true if all permissions are true for the given activities" do
|
25
|
+
user = double("user", role_symbols: [])
|
26
|
+
expect(Toni).to receive(:permitted_to?).with(:create, :books) { true }
|
27
|
+
expect(Toni).to receive(:permitted_to?).with(:read, :books) { true }
|
28
|
+
expect(user).to have_permission_on(:books, to: [:create, :read])
|
29
|
+
end
|
30
|
+
it "returns false if one permissions is not true for the given activities" do
|
31
|
+
user = double("user", role_symbols: [])
|
32
|
+
expect(Toni).to receive(:permitted_to?).with(:create, :books) { true }
|
33
|
+
expect(Toni).to receive(:permitted_to?).with(:read, :books) { false }
|
34
|
+
expect(user).not_to have_permission_on(:books, to: [:create, :read])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "toni/sinatra"
|
3
|
+
require "rack/test"
|
4
|
+
|
5
|
+
describe Toni::Sinatra::AuthorizationHelper do
|
6
|
+
let(:helper) {
|
7
|
+
Class.new do
|
8
|
+
include Toni::Sinatra::AuthorizationHelper
|
9
|
+
end.new
|
10
|
+
}
|
11
|
+
|
12
|
+
describe :permitted_to do
|
13
|
+
it "calls permitted_to? of toni, sets bang to true and calls the given block" do
|
14
|
+
double = double("block")
|
15
|
+
expect(Toni).to receive(:permitted_to?).with(:read, :books, bang: true) { true }
|
16
|
+
expect(double).to receive(:call)
|
17
|
+
helper.permitted_to(:read, :books) do
|
18
|
+
double.call
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it "does not call the block if it is not permitted to" do
|
23
|
+
double = double("block")
|
24
|
+
expect(Toni).to receive(:permitted_to?).with(:read, :books, bang: false) { false }
|
25
|
+
expect(double).to_not receive(:call)
|
26
|
+
helper.permitted_to(:read, :books, bang: false) do
|
27
|
+
double.call
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe :without_authorization do
|
33
|
+
it "calls toni without_authorization" do
|
34
|
+
expect(Toni).to receive(:without_authorization)
|
35
|
+
helper.without_authorization
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe Toni::Sinatra::AuthorizationErrorHandler do
|
41
|
+
include Rack::Test::Methods
|
42
|
+
|
43
|
+
let(:app) {
|
44
|
+
Class.new(Sinatra::Base) do
|
45
|
+
register Toni::Sinatra
|
46
|
+
|
47
|
+
set :show_exceptions, false
|
48
|
+
set :raise_errors, false
|
49
|
+
|
50
|
+
not_authorized do |e|
|
51
|
+
halt 403, e.message
|
52
|
+
end
|
53
|
+
|
54
|
+
get '/' do
|
55
|
+
permitted_to(:read, :books) do
|
56
|
+
"permitted"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
}
|
61
|
+
|
62
|
+
describe :not_authorized do
|
63
|
+
it "does not call error handler when permitted to" do
|
64
|
+
allow(Toni).to receive(:permitted_to?).with(:read, :books, anything) { true }
|
65
|
+
get '/'
|
66
|
+
expect(last_response.body).to eq("permitted")
|
67
|
+
end
|
68
|
+
|
69
|
+
it "calls error handler when permitted to" do
|
70
|
+
allow(Toni).to receive(:permitted_to?).with(:read, :books, anything) do
|
71
|
+
raise Toni::NotAuthorizedError.new("not authorized")
|
72
|
+
end
|
73
|
+
get '/'
|
74
|
+
expect(last_response.status).to eq(403)
|
75
|
+
expect(last_response.body).to eq("not authorized")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/spec/toni_spec.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require "spec_helper"
|
1
2
|
require "toni"
|
2
3
|
require "ostruct"
|
3
4
|
|
@@ -6,6 +7,19 @@ describe Toni do
|
|
6
7
|
let(:role_symbols) { [] }
|
7
8
|
let(:current_user) { OpenStruct.new(role_symbols: role_symbols) }
|
8
9
|
|
10
|
+
before do
|
11
|
+
allow(File).to receive(:read).with(Toni::AUTH_FILE) do
|
12
|
+
<<-eos
|
13
|
+
role :guest do
|
14
|
+
has_permission_on :x, to: [:read, :delete]
|
15
|
+
end
|
16
|
+
role :user do
|
17
|
+
has_permission_on :test, to: [:read, :create]
|
18
|
+
end
|
19
|
+
eos
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
9
23
|
describe ".current_user" do
|
10
24
|
it "has a anonymous user as default" do
|
11
25
|
expect(Toni.current_user).to be_instance_of(Toni::AnonymousUser)
|
@@ -29,15 +43,30 @@ describe Toni do
|
|
29
43
|
end
|
30
44
|
|
31
45
|
describe ".roles" do
|
32
|
-
|
46
|
+
it "reads roles from auth_file" do
|
47
|
+
expect(Toni.roles.keys).to eq([:guest, :user])
|
48
|
+
end
|
33
49
|
end
|
34
50
|
|
35
51
|
describe ".current_roles" do
|
36
|
-
|
52
|
+
let(:role_symbols) { [:user] }
|
53
|
+
it "gets the roles that are in the role_symbols methods" do
|
54
|
+
Toni.current_user = current_user
|
55
|
+
expect(Toni.current_roles).to eq([Toni.roles[:user]])
|
56
|
+
end
|
37
57
|
end
|
38
58
|
|
39
59
|
describe ".without_authorization" do
|
40
|
-
|
60
|
+
it "executes block without authorization and returns result of the given block" do
|
61
|
+
with_authorization = Toni.permitted_to?(:something, :test)
|
62
|
+
without_authorization = false
|
63
|
+
expect(Toni.without_authorization do
|
64
|
+
without_authorization = Toni.permitted_to?(:something, :test)
|
65
|
+
3
|
66
|
+
end).to eq(3)
|
67
|
+
expect(with_authorization).to eq(false)
|
68
|
+
expect(without_authorization).to eq(true)
|
69
|
+
end
|
41
70
|
end
|
42
71
|
|
43
72
|
describe ".permitted_to?" do
|
data/toni.gemspec
CHANGED
@@ -27,5 +27,5 @@ Gem::Specification.new do |spec|
|
|
27
27
|
spec.add_development_dependency "rspec"
|
28
28
|
spec.add_development_dependency "activerecord", "~> 4.1"
|
29
29
|
spec.add_development_dependency "rails", "~> 4.1"
|
30
|
-
spec.add_development_dependency "
|
30
|
+
spec.add_development_dependency "sinatra"
|
31
31
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: toni
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jalyna
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-08-
|
11
|
+
date: 2014-08-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec-expectations
|
@@ -95,7 +95,7 @@ dependencies:
|
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '4.1'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: sinatra
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
101
|
- - '>='
|
@@ -121,12 +121,21 @@ files:
|
|
121
121
|
- LICENSE.txt
|
122
122
|
- README.md
|
123
123
|
- Rakefile
|
124
|
+
- examples/sinatra/README.md
|
125
|
+
- examples/sinatra/app.rb
|
126
|
+
- examples/sinatra/config.ru
|
127
|
+
- examples/sinatra/config/authorization_rules.rb
|
128
|
+
- examples/sinatra/spec/authorization_rules_spec.rb
|
129
|
+
- examples/sinatra/spec/spec_helper.rb
|
130
|
+
- examples/sinatra/user.rb
|
124
131
|
- lib/toni.rb
|
125
132
|
- lib/toni/anonymous_user.rb
|
126
133
|
- lib/toni/builder.rb
|
127
134
|
- lib/toni/permission.rb
|
128
135
|
- lib/toni/permission_matcher.rb
|
129
136
|
- lib/toni/role.rb
|
137
|
+
- lib/toni/rspec_helper.rb
|
138
|
+
- lib/toni/sinatra.rb
|
130
139
|
- lib/toni/version.rb
|
131
140
|
- spec/spec_helper.rb
|
132
141
|
- spec/toni/anonymous_user_spec.rb
|
@@ -134,6 +143,8 @@ files:
|
|
134
143
|
- spec/toni/permission_matcher_spec.rb
|
135
144
|
- spec/toni/permission_spec.rb
|
136
145
|
- spec/toni/role_spec.rb
|
146
|
+
- spec/toni/rspec_helper_spec.rb
|
147
|
+
- spec/toni/sinatra_spec.rb
|
137
148
|
- spec/toni_spec.rb
|
138
149
|
- toni.gemspec
|
139
150
|
homepage: https://github.com/jalyna/toni
|
@@ -169,4 +180,6 @@ test_files:
|
|
169
180
|
- spec/toni/permission_matcher_spec.rb
|
170
181
|
- spec/toni/permission_spec.rb
|
171
182
|
- spec/toni/role_spec.rb
|
183
|
+
- spec/toni/rspec_helper_spec.rb
|
184
|
+
- spec/toni/sinatra_spec.rb
|
172
185
|
- spec/toni_spec.rb
|