action_bouncer 0.0.2 → 0.0.3
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 +4 -4
- data/README.md +94 -0
- data/Rakefile +0 -5
- data/lib/action_bouncer.rb +4 -2
- data/lib/action_bouncer/allowance.rb +32 -0
- data/lib/action_bouncer/authorization.rb +7 -28
- data/lib/action_bouncer/version.rb +1 -1
- data/spec/controllers/all_param_controller_spec.rb +52 -0
- data/spec/controllers/array_params_controller_spec.rb +39 -0
- data/spec/controllers/multiple_allowances_controller_spec.rb +64 -0
- data/spec/controllers/no_setup_controller_spec.rb +22 -0
- data/spec/controllers/symbol_params_controller_spec.rb +34 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +7 -0
- data/spec/dummy/app/controllers/dummy_controller.rb +4 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +29 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +26 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +41 -0
- data/spec/dummy/config/environments/production.rb +79 -0
- data/spec/dummy/config/environments/test.rb +42 -0
- data/spec/dummy/config/initializers/assets.rb +11 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +0 -0
- data/spec/dummy/log/test.log +2711 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/spec_helper.rb +21 -0
- metadata +94 -5
- data/README.rdoc +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ea988351cd9b96ae83cfcb77a9523a6012d87eac
|
4
|
+
data.tar.gz: 9a7046bfc38c1f086854076f45a80a5535ceb621
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 44b598c4812fb59bc9ba1758ee9d59e76119bc89347537771cf50f9ff85abcb252582f741f594f3e7f7512a0b0873e4accbfcae243d61a1286037e68317545fd
|
7
|
+
data.tar.gz: 800b74c749214f02d234e9c7787b61205b2ad2be53720e5b2cb4332c67518575a43d28f4f9fb09d2dc7d34ea8faf5ca434e99d48139d89f3ea37f84745af4d7c
|
data/README.md
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
# ActionBouncer
|
2
|
+
|
3
|
+
[](https://circleci.com/gh/oswaldoferreira/action_bouncer/tree/master)
|
4
|
+
|
5
|
+
It's a dead simple Rails authorization lib for well defined authorization objects interfaces.
|
6
|
+
|
7
|
+
## Installing
|
8
|
+
|
9
|
+
Add it to your gemfile:
|
10
|
+
|
11
|
+
`gem 'action_bouncer'`
|
12
|
+
|
13
|
+
Or manually install it:
|
14
|
+
|
15
|
+
`gem install action_bouncer`
|
16
|
+
|
17
|
+
## Examples
|
18
|
+
|
19
|
+
Allowing user to access specific actions:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
class UsersController < ApplicationController
|
23
|
+
allow :current_user, to: [:index, :new], if: :admin?
|
24
|
+
|
25
|
+
def index
|
26
|
+
end
|
27
|
+
|
28
|
+
def new
|
29
|
+
end
|
30
|
+
|
31
|
+
def edit
|
32
|
+
end
|
33
|
+
end
|
34
|
+
```
|
35
|
+
|
36
|
+
Allowing user to access all actions:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
class UsersController < ApplicationController
|
40
|
+
allow :current_user, to: :all, if: :admin?
|
41
|
+
|
42
|
+
def index
|
43
|
+
end
|
44
|
+
|
45
|
+
def new
|
46
|
+
end
|
47
|
+
|
48
|
+
def edit
|
49
|
+
end
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
53
|
+
Also, you can pass multiple methods that your authorizable object responds to:
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
allow :current_user, to: [:index, :new], if: [:admin?, :leader?]
|
57
|
+
```
|
58
|
+
|
59
|
+
And allow users with different authorizations to access different actions:
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
allow :current_user, to: :index, if: :leader?
|
63
|
+
allow :current_user, to: :all, if: :admin?
|
64
|
+
```
|
65
|
+
|
66
|
+
When not authorized, `ActionBouncer` raises an exception that can be rescued on your `ApplicationController`:
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
class ApplicationController < ActionController::Base
|
70
|
+
protect_from_forgery with: :exception
|
71
|
+
|
72
|
+
before_action :authenticate_user!
|
73
|
+
|
74
|
+
include ActionBouncer
|
75
|
+
|
76
|
+
rescue_from ActionBouncer::Unauthorized,
|
77
|
+
with: :user_not_authorized
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def user_not_authorized
|
82
|
+
render nothing: true, status: :unauthorized
|
83
|
+
end
|
84
|
+
end
|
85
|
+
```
|
86
|
+
|
87
|
+
## Development
|
88
|
+
|
89
|
+
```
|
90
|
+
bundle install
|
91
|
+
bundle exec rspec spec
|
92
|
+
```
|
93
|
+
|
94
|
+
Feel free to create issues and submit pull requests.
|
data/Rakefile
CHANGED
data/lib/action_bouncer.rb
CHANGED
@@ -1,14 +1,16 @@
|
|
1
|
+
require 'action_bouncer/allowance'
|
1
2
|
require 'action_bouncer/authorization'
|
2
3
|
|
3
4
|
module ActionBouncer
|
4
5
|
def self.included(klass)
|
5
6
|
klass.class_eval do
|
6
7
|
def self.allow(resource, options)
|
7
|
-
|
8
|
+
@_allowances ||= []
|
9
|
+
@_allowances << Allowance.new(resource, options)
|
8
10
|
end
|
9
11
|
|
10
12
|
def self._authorization
|
11
|
-
@
|
13
|
+
Authorization.new(@_allowances)
|
12
14
|
end
|
13
15
|
|
14
16
|
before_action { self.class._authorization.try(:authorize!, self) }
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module ActionBouncer
|
2
|
+
class Allowance
|
3
|
+
attr_reader :resource_sym
|
4
|
+
|
5
|
+
def initialize(resource_sym, options)
|
6
|
+
@resource_sym, @options = resource_sym, options
|
7
|
+
end
|
8
|
+
|
9
|
+
def not_allowed?(controller, action)
|
10
|
+
resource = controller.send(@resource_sym)
|
11
|
+
!allowed_action?(action) || !matches_resource_condition?(resource)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def allowed_action?(action)
|
17
|
+
allowed_actions.include?(action.to_sym) || allowed_actions.include?(:all)
|
18
|
+
end
|
19
|
+
|
20
|
+
def matches_resource_condition?(resource)
|
21
|
+
conditions.any? { |condition| resource.send(condition).present? }
|
22
|
+
end
|
23
|
+
|
24
|
+
def allowed_actions
|
25
|
+
Array.wrap(@options[:to])
|
26
|
+
end
|
27
|
+
|
28
|
+
def conditions
|
29
|
+
Array.wrap(@options[:if])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -2,41 +2,20 @@ module ActionBouncer
|
|
2
2
|
class Unauthorized < StandardError; end
|
3
3
|
|
4
4
|
class Authorization
|
5
|
-
|
6
|
-
|
7
|
-
def initialize(resource_sym, options)
|
8
|
-
@resource_sym, @options = resource_sym, options
|
5
|
+
def initialize(allowances)
|
6
|
+
@allowances = allowances
|
9
7
|
end
|
10
8
|
|
11
9
|
def authorize!(controller)
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
fail Unauthorized if unauthorized?(action, resource)
|
10
|
+
return if @allowances.nil?
|
11
|
+
fail Unauthorized if unauthorized?(controller)
|
16
12
|
end
|
17
13
|
|
18
14
|
private
|
19
15
|
|
20
|
-
def unauthorized?(
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
def authorized_action?(action)
|
25
|
-
allowed_actions.include?(action.to_sym) || allowed_actions.include?(:all)
|
26
|
-
end
|
27
|
-
|
28
|
-
def matches_resource_condition?(resource)
|
29
|
-
conditions.any? { |condition| resource.send(condition).present? }
|
30
|
-
end
|
31
|
-
|
32
|
-
def allowed_actions
|
33
|
-
allowed_actions = @options[:to]
|
34
|
-
allowed_actions.is_a?(Array) ? allowed_actions : [allowed_actions]
|
35
|
-
end
|
36
|
-
|
37
|
-
def conditions
|
38
|
-
conditions = @options[:if]
|
39
|
-
conditions.is_a?(Array) ? conditions : [conditions]
|
16
|
+
def unauthorized?(controller)
|
17
|
+
action = controller.send(:params).fetch(:action)
|
18
|
+
@allowances.all? { |allowance| allowance.not_allowed?(controller, action) }
|
40
19
|
end
|
41
20
|
end
|
42
21
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class AllParamController < ActionController::Base
|
4
|
+
helper_method :current_user
|
5
|
+
|
6
|
+
include ActionBouncer
|
7
|
+
|
8
|
+
allow :current_user, to: :all, if: :admin?
|
9
|
+
|
10
|
+
def index
|
11
|
+
render nothing: true, status: :success
|
12
|
+
end
|
13
|
+
|
14
|
+
def new
|
15
|
+
render nothing: true, status: :success
|
16
|
+
end
|
17
|
+
|
18
|
+
def edit
|
19
|
+
render nothing: true, status: :success
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe AllParamController do
|
24
|
+
before do
|
25
|
+
Rails.application.routes.draw do
|
26
|
+
get '/index' => 'all_param#index'
|
27
|
+
get '/new' => 'all_param#new'
|
28
|
+
get '/edit' => 'all_param#edit'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'when authorized' do
|
33
|
+
before do
|
34
|
+
allow(subject).to receive(:current_user) { OpenStruct.new(admin?: true) }
|
35
|
+
end
|
36
|
+
|
37
|
+
it { expect{ get :edit }.not_to raise_error }
|
38
|
+
it { expect{ get :index }.not_to raise_error }
|
39
|
+
it { expect{ get :new }.not_to raise_error }
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'when not authorized' do
|
43
|
+
before do
|
44
|
+
allow(subject).to receive(:current_user) { OpenStruct.new(admin?: false) }
|
45
|
+
end
|
46
|
+
|
47
|
+
it { expect{ get :edit }.to raise_error{ ActionBouncer::Unauthorized } }
|
48
|
+
it { expect{ get :index }.to raise_error{ ActionBouncer::Unauthorized } }
|
49
|
+
it { expect{ get :new }.to raise_error{ ActionBouncer::Unauthorized } }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class ArrayParamsController < ActionController::Base
|
4
|
+
helper_method :current_user
|
5
|
+
|
6
|
+
def current_user
|
7
|
+
OpenStruct.new(admin?: false, leader?: true)
|
8
|
+
end
|
9
|
+
|
10
|
+
include ActionBouncer
|
11
|
+
|
12
|
+
allow :current_user, to: [:index, :new], if: [:admin?, :leader?]
|
13
|
+
|
14
|
+
def index
|
15
|
+
render nothing: true, status: :success
|
16
|
+
end
|
17
|
+
|
18
|
+
def new
|
19
|
+
render nothing: true, status: :success
|
20
|
+
end
|
21
|
+
|
22
|
+
def edit
|
23
|
+
render nothing: true, status: :success
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe ArrayParamsController do
|
28
|
+
before do
|
29
|
+
Rails.application.routes.draw do
|
30
|
+
get '/index' => 'array_params#index'
|
31
|
+
get '/new' => 'array_params#new'
|
32
|
+
get '/edit' => 'array_params#edit'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it { expect{ get :edit }.to raise_error(ActionBouncer::Unauthorized) }
|
37
|
+
it { expect{ get :index }.not_to raise_error }
|
38
|
+
it { expect{ get :new }.not_to raise_error }
|
39
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class MultipleAllowancesController < ActionController::Base
|
4
|
+
helper_method :current_user
|
5
|
+
|
6
|
+
include ActionBouncer
|
7
|
+
|
8
|
+
allow :current_user, to: [:index, :new], if: [:leader?, :admin?]
|
9
|
+
allow :current_user, to: :edit, if: :admin?
|
10
|
+
|
11
|
+
def index
|
12
|
+
render nothing: true, status: :success
|
13
|
+
end
|
14
|
+
|
15
|
+
def new
|
16
|
+
render nothing: true, status: :success
|
17
|
+
end
|
18
|
+
|
19
|
+
def edit
|
20
|
+
render nothing: true, status: :success
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe MultipleAllowancesController do
|
25
|
+
before do
|
26
|
+
Rails.application.routes.draw do
|
27
|
+
get '/index' => 'multiple_allowances#index'
|
28
|
+
get '/new' => 'multiple_allowances#new'
|
29
|
+
get '/edit' => 'multiple_allowances#edit'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'when authorized' do
|
34
|
+
context 'as leader' do
|
35
|
+
before do
|
36
|
+
allow(subject).to receive(:current_user) { OpenStruct.new(leader?: true) }
|
37
|
+
end
|
38
|
+
|
39
|
+
it { expect{ get :index }.not_to raise_error }
|
40
|
+
it { expect{ get :new }.not_to raise_error }
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'as admin' do
|
44
|
+
before do
|
45
|
+
allow(subject).to receive(:current_user) { OpenStruct.new(admin?: true) }
|
46
|
+
end
|
47
|
+
|
48
|
+
it { expect{ get :index }.not_to raise_error }
|
49
|
+
it { expect{ get :new }.not_to raise_error }
|
50
|
+
it { expect{ get :edit }.not_to raise_error }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'when not authorized' do
|
55
|
+
context 'when leader' do
|
56
|
+
before do
|
57
|
+
allow(subject).to receive(:current_user) { OpenStruct.new(leader?: true) }
|
58
|
+
end
|
59
|
+
|
60
|
+
it { expect{ get :edit }.to raise_error{ ActionBouncer::Unauthorized } }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class NoSetupController < ActionController::Base
|
4
|
+
helper_method :current_user
|
5
|
+
|
6
|
+
include ActionBouncer
|
7
|
+
|
8
|
+
def index
|
9
|
+
render nothing: true, status: :success
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe NoSetupController do
|
14
|
+
before do
|
15
|
+
Rails.application.routes.draw do
|
16
|
+
get '/index' => 'no_setup#index'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it { expect{ get :index }.not_to raise_error }
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class SymbolParamsController < ActionController::Base
|
4
|
+
helper_method :current_user
|
5
|
+
|
6
|
+
def current_user
|
7
|
+
OpenStruct.new(admin?: true)
|
8
|
+
end
|
9
|
+
|
10
|
+
include ActionBouncer
|
11
|
+
|
12
|
+
allow :current_user, to: :index, if: :admin?
|
13
|
+
|
14
|
+
def index
|
15
|
+
render nothing: true, status: :success
|
16
|
+
end
|
17
|
+
|
18
|
+
def new
|
19
|
+
render nothing: true, status: :success
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe SymbolParamsController do
|
24
|
+
before do
|
25
|
+
Rails.application.routes.draw do
|
26
|
+
get '/index' => 'symbol_params#index'
|
27
|
+
get '/new' => 'symbol_params#new'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it { expect{ get :new }.to raise_error(ActionBouncer::Unauthorized) }
|
32
|
+
it { expect{ get :index }.not_to raise_error }
|
33
|
+
end
|
34
|
+
|