authorize_action 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.travis.yml +11 -0
- data/CHANGES.md +3 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +267 -0
- data/Rakefile +7 -0
- data/authorize_action.gemspec +22 -0
- data/lib/authorize_action.rb +31 -0
- data/lib/authorize_action/rails.rb +19 -0
- data/lib/authorize_action/sinatra.rb +29 -0
- data/spec/authorize_action/rails_spec.rb +24 -0
- data/spec/authorize_action/sinatra_spec.rb +92 -0
- data/spec/authorize_action_spec.rb +77 -0
- data/spec/spec_helper.rb +12 -0
- metadata +106 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: ea616d0e04fe1d10782138a9703afd606c193fd8
|
|
4
|
+
data.tar.gz: 5b0e67d8cb38d9863cd5336871f4351057241662
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: af325e32de44eacb20318980a019f6725ba5e0e69e109e4e8c7e5b4f52417d71a09c127d4b69248c0a70f8f913a500a678ee0c187fa11b7f9c417b16822ea768
|
|
7
|
+
data.tar.gz: d0089969c565d7dc1d3860a3e329c0a9287f5c62efaa1b49b640315aff10cc4520408be5cff15d30eb8d551918815889a1e204ef535d798feac0be9e57964109
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/CHANGES.md
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) Jarmo Pertman
|
|
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,267 @@
|
|
|
1
|
+
# AuthorizeAction
|
|
2
|
+
|
|
3
|
+
[](http://badge.fury.io/rb/authorize_action)
|
|
4
|
+
[](http://travis-ci.org/jarmo/authorize_action)
|
|
5
|
+
[](https://coveralls.io/r/jarmo/authorize_action?branch=master)
|
|
6
|
+
[](https://gemnasium.com/jarmo/authorize_action)
|
|
7
|
+
[](https://codeclimate.com/github/jarmo/authorize_action)
|
|
8
|
+
|
|
9
|
+
Really **secure** and **simple** authorization library for your [Rails](http://rubyonrails.org/), [Sinatra](http://www.sinatrarb.com/) or whatever web framework, which doesn't suck.
|
|
10
|
+
|
|
11
|
+
According to [Ruby Toolbox](https://www.ruby-toolbox.com/categories/rails_authorization) there are
|
|
12
|
+
at least 28 authorization libraries/frameworks available just for Rails. And yet, they all suck.
|
|
13
|
+
At least that's why I've never used any of them in my projects.
|
|
14
|
+
All of this is the reason I've finally decided to create my own library to solve
|
|
15
|
+
the problem of authorization once and for all for all Ruby (web) frameworks.
|
|
16
|
+
|
|
17
|
+
Here are a few reasons, why [authorize_action](https://github.com/jarmo/authorize_action) is better
|
|
18
|
+
compared to all the rest:
|
|
19
|
+
|
|
20
|
+
* Is **secure** by default
|
|
21
|
+
* _authorize_action_ uses a **white-list** approach, which means that all controller actions
|
|
22
|
+
are forbidden by default. No more security breaches when you forget to write an authorization rule.
|
|
23
|
+
|
|
24
|
+
* Does only **one** thing and does it well
|
|
25
|
+
* _authorize_action_ doesn't deal with any other thing except **authorization**.
|
|
26
|
+
It does not provide any authentication (see [Devise](https://github.com/plataformatec/devise) for that),
|
|
27
|
+
roles or permissions functionality. It just checks if a user has access to a controller action or not. That's it.
|
|
28
|
+
|
|
29
|
+
* Has a really **simple** API
|
|
30
|
+
* _authorize_action_ doesn't have _gazillion_ different API methods, separate DSL
|
|
31
|
+
or _"Getting Started"_ wiki pages. It just has a **few methods** to do all the heavy lifting.
|
|
32
|
+
And it's really easy to understand by looking at the code in your project,
|
|
33
|
+
who has access to which controller action.
|
|
34
|
+
|
|
35
|
+
* Is **framework-agnostic**
|
|
36
|
+
* Although _authorize_action_ is made to work with _Rails_ and _Sinatra_ by default,
|
|
37
|
+
it has an **easy** way to add support for whatever framework.
|
|
38
|
+
|
|
39
|
+
* No **database/ORM** dependencies
|
|
40
|
+
* _authorize_action_ **doesn't force** you to use any database/ORM - handle
|
|
41
|
+
your roles/permissions however you like.
|
|
42
|
+
|
|
43
|
+
* No **external** dependencies
|
|
44
|
+
* _authorize_action_ doesn't have any direct external dependencies, which
|
|
45
|
+
makes auditing its code and upgrading it really **painless**.
|
|
46
|
+
|
|
47
|
+
* Has a **lightweight** codebase
|
|
48
|
+
* Due to the simplicity of _authorize_action_ its codebase is really simple, clean and foolproof.
|
|
49
|
+
Entire codebase used in _production_ is under **100 lines**.
|
|
50
|
+
If you're not sure what exactly happens when you use _authorize_action_ in your project
|
|
51
|
+
then you can understand it all quickly by looking at [the code](https://github.com/jarmo/authorize_action/tree/master/lib).
|
|
52
|
+
|
|
53
|
+
* Is thoroughly **tested**
|
|
54
|
+
* _authorize_action_ has **100% code-coverage** and all tests are ran automatically
|
|
55
|
+
prior to each release making it impossible to ship a faulty version accidentally.
|
|
56
|
+
|
|
57
|
+
* Follows **Semantic Versioning**
|
|
58
|
+
* Although _authorize_action_ is a relatively new library for now,
|
|
59
|
+
it's going to follow [Semantic Versioning](http://semver.org/),
|
|
60
|
+
which adds some additional guarantees to developers.
|
|
61
|
+
|
|
62
|
+
## Installation
|
|
63
|
+
|
|
64
|
+
Add this line to your application's Gemfile:
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
gem 'authorize_action'
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
And then execute:
|
|
71
|
+
|
|
72
|
+
$ bundle
|
|
73
|
+
|
|
74
|
+
Or install it yourself as:
|
|
75
|
+
|
|
76
|
+
$ gem install authorize_action
|
|
77
|
+
|
|
78
|
+
## Usage
|
|
79
|
+
|
|
80
|
+
* Load _authorize_action_ gem
|
|
81
|
+
* Include framework specific module into base controller
|
|
82
|
+
* Define authorization rules for each controller/class
|
|
83
|
+
* Call `#authorize_action!` method before executing action
|
|
84
|
+
|
|
85
|
+
### Rails
|
|
86
|
+
|
|
87
|
+
```ruby
|
|
88
|
+
class ApplicationController < ActionController::Base
|
|
89
|
+
# Include authorize_action methods
|
|
90
|
+
include AuthorizeAction::Rails
|
|
91
|
+
|
|
92
|
+
# Check action authorization before action.
|
|
93
|
+
# By default all actions are forbidden!
|
|
94
|
+
before_action :authorize_action!
|
|
95
|
+
|
|
96
|
+
protected
|
|
97
|
+
|
|
98
|
+
# Helper method for administrator role
|
|
99
|
+
def admin?
|
|
100
|
+
# ...
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
class PostsController < ApplicationController
|
|
105
|
+
# Everyone has access to :index
|
|
106
|
+
def index
|
|
107
|
+
# ...
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Only post owner has access to :edit
|
|
111
|
+
def edit
|
|
112
|
+
# ...
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Authorization rules have to be defined for each controller
|
|
116
|
+
# action to specify the actual access rules.
|
|
117
|
+
self.authorization_rules = {
|
|
118
|
+
# Calling Proc object for :index action returns true
|
|
119
|
+
# thus everyone will have access to that action
|
|
120
|
+
index: -> { true },
|
|
121
|
+
|
|
122
|
+
# Calling Proc object for :edit action returns true only
|
|
123
|
+
# when Post owner matches current_user
|
|
124
|
+
edit: -> { Post.find(params[:id]).owner == current_user },
|
|
125
|
+
|
|
126
|
+
# Calling referenced #admin? method for :destroy action
|
|
127
|
+
# returns true only for administrators
|
|
128
|
+
destroy: -> :admin?
|
|
129
|
+
}
|
|
130
|
+
end
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Sinatra
|
|
134
|
+
|
|
135
|
+
```ruby
|
|
136
|
+
require "authorize_action"
|
|
137
|
+
|
|
138
|
+
class Blog < Sinatra::Base
|
|
139
|
+
# Include authorize_action methods
|
|
140
|
+
include AuthorizeAction::Sinatra
|
|
141
|
+
|
|
142
|
+
# Check action authorization before action.
|
|
143
|
+
# By default all actions are forbidden.
|
|
144
|
+
# Allow requests to `public/` directory if you want.
|
|
145
|
+
before { authorize_action! unless request.path_info.start_with?(settings.public_folder) }
|
|
146
|
+
|
|
147
|
+
# Everyone has access to reading posts
|
|
148
|
+
get "/posts" do
|
|
149
|
+
# ...
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Only post owner has access to editing post
|
|
153
|
+
get "/posts/:id/edit" do
|
|
154
|
+
# ...
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Only administrator can delete posts
|
|
158
|
+
delete "/posts/:id" do
|
|
159
|
+
# ...
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Authorization rules have to be defined for each route
|
|
163
|
+
# to specify the actual access rules.
|
|
164
|
+
self.authorization_rules = {
|
|
165
|
+
# Calling Proc object for `GET /posts` action returns true
|
|
166
|
+
# thus everyone will have access to that action
|
|
167
|
+
action(:get, "/posts") => -> { true },
|
|
168
|
+
|
|
169
|
+
# Calling Proc object for `GET /posts/:id/edit` action
|
|
170
|
+
# returns true only when Post owner matches current_user
|
|
171
|
+
action(:get, "/posts/:id/edit") => -> { Post.find(params[:id]).owner == current_user },
|
|
172
|
+
|
|
173
|
+
# Calling referenced #admin? method for `DELETE /posts/:id` action
|
|
174
|
+
# returns true only for administrators
|
|
175
|
+
action(:delete, "/posts/:id") => :admin?
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
# Helper method for administrator role
|
|
179
|
+
def admin?
|
|
180
|
+
# ...
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
end
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Other
|
|
187
|
+
|
|
188
|
+
```ruby
|
|
189
|
+
class BaseController
|
|
190
|
+
# Include authorize_action generic methods
|
|
191
|
+
include AuthorizeAction
|
|
192
|
+
|
|
193
|
+
# Call #authorize_action! before executing any controller actions.
|
|
194
|
+
before_any_action :authorize_action!
|
|
195
|
+
|
|
196
|
+
private
|
|
197
|
+
|
|
198
|
+
# Override AuthorizeAction#current_action_name to implement it.
|
|
199
|
+
# It is expected to return a Symbol/String object with an unique
|
|
200
|
+
# controller action identifier.
|
|
201
|
+
def current_action_name
|
|
202
|
+
# ...
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Override AuthorizeAction#forbid_action! to halt execution
|
|
206
|
+
# of controller action by rendering HTTP 403.
|
|
207
|
+
def forbid_action!
|
|
208
|
+
# ...
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Set authorization rules where keys are in the exact same format
|
|
212
|
+
# as returned by #current_action_name
|
|
213
|
+
# and values are Proc objects returning booleans.
|
|
214
|
+
self.authorization_rules = {
|
|
215
|
+
# ...
|
|
216
|
+
}
|
|
217
|
+
end
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Tips & Tricks
|
|
221
|
+
|
|
222
|
+
### Administrator Has Access to Every Action
|
|
223
|
+
|
|
224
|
+
There is no API for giving access to administrator for every possible action.
|
|
225
|
+
Nevertheless it can be achieved easily by just following
|
|
226
|
+
object-oriented programming principles.
|
|
227
|
+
|
|
228
|
+
Example below is based on Rails and Devise, but
|
|
229
|
+
the idea is the same for whatever framework:
|
|
230
|
+
|
|
231
|
+
```ruby
|
|
232
|
+
class ApplicationController < ActionController::Base
|
|
233
|
+
include AuthorizeAction::Rails
|
|
234
|
+
|
|
235
|
+
before_action :authorize_action!
|
|
236
|
+
|
|
237
|
+
# Override AuthorizeAction#authorize_action! to
|
|
238
|
+
# give access to administrator for every action or
|
|
239
|
+
# fall back to default AuthorizeAction behavior.
|
|
240
|
+
def authorize_action!
|
|
241
|
+
current_user.admin? || super
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Please make sure that you really-really need to do that!
|
|
247
|
+
|
|
248
|
+
### Protecting Your Views
|
|
249
|
+
|
|
250
|
+
You have protected your actions with _authorize_action_ and your models with [Strong Parameters](https://github.com/rails/strong_parameters) (or something similar), but what about views?
|
|
251
|
+
|
|
252
|
+
Again, there is no separate API for that, but why not use regular Ruby methods to do that?
|
|
253
|
+
|
|
254
|
+
Here's an example:
|
|
255
|
+
```ruby
|
|
256
|
+
# views/posts/edit.html.erb
|
|
257
|
+
|
|
258
|
+
<% if current_user.admin? %>
|
|
259
|
+
<%= link_to "Delete", @post, method: :delete
|
|
260
|
+
<% end %>
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Next step would be to create some helper class or module for roles.
|
|
264
|
+
|
|
265
|
+
## License
|
|
266
|
+
|
|
267
|
+
Copyright (c) Jarmo Pertman (jarmo.p@gmail.com). See [LICENSE](https://github.com/jarmo/authorize_action/blob/master/LICENSE.txt) for details.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "authorize_action"
|
|
7
|
+
spec.version = "1.0.0"
|
|
8
|
+
spec.authors = ["Jarmo Pertman"]
|
|
9
|
+
spec.email = ["jarmo.p@gmail.com"]
|
|
10
|
+
spec.summary = %q{Really secure and simple authorization library for your Rails, Sinatra or whatever web framework, which doesn't suck.}
|
|
11
|
+
spec.homepage = "https://github.com/jarmo/authorize_action"
|
|
12
|
+
spec.license = "MIT"
|
|
13
|
+
|
|
14
|
+
spec.files = `git ls-files -z`.split("\x0")
|
|
15
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
16
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
17
|
+
spec.require_paths = ["lib"]
|
|
18
|
+
|
|
19
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
|
20
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
|
21
|
+
spec.add_development_dependency "rspec", "~> 3.1.0"
|
|
22
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module AuthorizeAction
|
|
2
|
+
autoload :Rails, File.expand_path("authorize_action/rails", __dir__)
|
|
3
|
+
autoload :Sinatra, File.expand_path("authorize_action/sinatra", __dir__)
|
|
4
|
+
|
|
5
|
+
def authorize_action!
|
|
6
|
+
authorization_rule = self.class.authorization_rules && self.class.authorization_rules[current_action_name]
|
|
7
|
+
action_permitted = case authorization_rule
|
|
8
|
+
when Proc then instance_exec(&authorization_rule)
|
|
9
|
+
when Symbol then send(authorization_rule)
|
|
10
|
+
end
|
|
11
|
+
forbid_action! unless action_permitted
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.included(base)
|
|
15
|
+
base.extend(ClassMethods)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
module ClassMethods
|
|
19
|
+
attr_accessor :authorization_rules
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def forbid_action!
|
|
25
|
+
raise NotImplementedError
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def current_action_name
|
|
29
|
+
raise NotImplementedError
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module AuthorizeAction
|
|
2
|
+
module Rails
|
|
3
|
+
include AuthorizeAction
|
|
4
|
+
|
|
5
|
+
def self.included(base)
|
|
6
|
+
AuthorizeAction.included(base)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def forbid_action!
|
|
12
|
+
head(:forbidden)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def current_action_name
|
|
16
|
+
action_name.to_sym
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module AuthorizeAction
|
|
2
|
+
module Sinatra
|
|
3
|
+
include AuthorizeAction
|
|
4
|
+
|
|
5
|
+
def self.included(base)
|
|
6
|
+
AuthorizeAction.included(base)
|
|
7
|
+
base.extend(ClassMethods)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module ClassMethods
|
|
11
|
+
def action(request_method, request_path)
|
|
12
|
+
request_method = request_method.to_s.upcase
|
|
13
|
+
*_, route = routes[request_method].find {|pattern, _| request_path.match(pattern) }
|
|
14
|
+
route && route.instance_variable_get(:@route_name) && route.instance_variable_get(:@route_name).to_sym || "#{request_method} #{request_path}".to_sym
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def forbid_action!
|
|
21
|
+
halt(403)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def current_action_name
|
|
25
|
+
self.class.action(request.request_method, request.path_info)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe AuthorizeAction::Rails do
|
|
4
|
+
let(:authorizator) { Class.new { include AuthorizeAction::Rails }.new }
|
|
5
|
+
|
|
6
|
+
it "#authorize_action! delegates to AuthorizeAction#authorize_action!" do
|
|
7
|
+
expect_any_instance_of(AuthorizeAction).to receive(:authorize_action!)
|
|
8
|
+
|
|
9
|
+
authorizator.authorize_action!
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it "#forbid_action! calls Rails' #head(:forbidden)" do
|
|
13
|
+
expect(authorizator).to receive(:head).with(:forbidden).and_return(:forbidden_result)
|
|
14
|
+
|
|
15
|
+
expect(authorizator.send(:forbid_action!)).to eq(:forbidden_result)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "#current_action_name calls Rails' #action_name" do
|
|
19
|
+
expect(authorizator).to receive(:action_name).and_return("action-name")
|
|
20
|
+
|
|
21
|
+
expect(authorizator.send(:current_action_name)).to eq("action-name".to_sym)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe AuthorizeAction::Sinatra do
|
|
4
|
+
let(:authorizator) { Class.new { include AuthorizeAction::Sinatra }.new }
|
|
5
|
+
|
|
6
|
+
it "#authorize_action! delegates to AuthorizeAction#authorize_action!" do
|
|
7
|
+
expect_any_instance_of(AuthorizeAction).to receive(:authorize_action!)
|
|
8
|
+
|
|
9
|
+
authorizator.authorize_action!
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it "#forbid_action! calls Sintra's #halt(403)" do
|
|
13
|
+
expect(authorizator).to receive(:halt).with(403).and_return(:forbidden_result)
|
|
14
|
+
|
|
15
|
+
expect(authorizator.send(:forbid_action!)).to eq(:forbidden_result)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "#current_action_name finds Sinatra's action name from request" do
|
|
19
|
+
request = double("request", request_method: "GET", path_info: "/foo/bar")
|
|
20
|
+
allow(authorizator).to receive(:request).and_return(request)
|
|
21
|
+
expect(authorizator.class).to receive(:action).and_return("action-name".to_sym)
|
|
22
|
+
|
|
23
|
+
expect(authorizator.send(:current_action_name)).to eq("action-name".to_sym)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
context "#action" do
|
|
27
|
+
it "returns request path when no Sinatra route found" do
|
|
28
|
+
route_handler = -> {}
|
|
29
|
+
route_handler.instance_variable_set(:@route_name, "GET /foo/:bar")
|
|
30
|
+
routes = {
|
|
31
|
+
"GET" => [
|
|
32
|
+
# /foo/:bar
|
|
33
|
+
[/\A\/foo\/([^\/?#]+)\z/, ["bar"], [], route_handler]
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
expect(authorizator.class).to receive(:routes).and_return(routes)
|
|
37
|
+
|
|
38
|
+
expect(authorizator.class.action(:get, "/bar/baz")).to eq("GET /bar/baz".to_sym)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "returns request path when Sinatra route found, but no route name exists" do
|
|
42
|
+
route_handler = -> {}
|
|
43
|
+
route_handler.instance_variable_set(:@route_name, nil)
|
|
44
|
+
routes = {
|
|
45
|
+
"GET" => [
|
|
46
|
+
# /foo/:bar
|
|
47
|
+
[/\A\/foo\/([^\/?#]+)\z/, ["bar"], [], route_handler]
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
expect(authorizator.class).to receive(:routes).and_return(routes)
|
|
51
|
+
|
|
52
|
+
expect(authorizator.class.action(:get, "/foo/baz")).to eq("GET /foo/baz".to_sym)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "returns Sinatra's GET method route name" do
|
|
56
|
+
route_handler1 = -> {}
|
|
57
|
+
route_handler1.instance_variable_set(:@route_name, "GET /bar/:baz")
|
|
58
|
+
route_handler2 = -> {}
|
|
59
|
+
route_handler2.instance_variable_set(:@route_name, "GET /foo/:bar")
|
|
60
|
+
routes = {
|
|
61
|
+
"GET" => [
|
|
62
|
+
# /bar/:baz
|
|
63
|
+
[/\A\/bar\/([^\/?#]+)\z/, ["baz"], [], route_handler1],
|
|
64
|
+
# /foo/:bar
|
|
65
|
+
[/\A\/foo\/([^\/?#]+)\z/, ["bar"], [], route_handler2]
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
expect(authorizator.class).to receive(:routes).and_return(routes)
|
|
69
|
+
|
|
70
|
+
expect(authorizator.class.action(:get, "/foo/baz")).to eq("GET /foo/:bar".to_sym)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "returns Sinatra's POST method route name" do
|
|
74
|
+
route_handler1 = -> {}
|
|
75
|
+
route_handler1.instance_variable_set(:@route_name, "POST /bar/:baz")
|
|
76
|
+
route_handler2 = -> {}
|
|
77
|
+
route_handler2.instance_variable_set(:@route_name, "POST /foo/:bar")
|
|
78
|
+
routes = {
|
|
79
|
+
"POST" => [
|
|
80
|
+
# /bar/:baz
|
|
81
|
+
[/\A\/bar\/([^\/?#]+)\z/, ["baz"], [], route_handler1],
|
|
82
|
+
# /foo/:bar
|
|
83
|
+
[/\A\/foo\/([^\/?#]+)\z/, ["bar"], [], route_handler2]
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
expect(authorizator.class).to receive(:routes).and_return(routes)
|
|
87
|
+
|
|
88
|
+
expect(authorizator.class.action(:post, "/foo/baz")).to eq("POST /foo/:bar".to_sym)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe AuthorizeAction do
|
|
4
|
+
let(:authorizator) { Class.new { include AuthorizeAction }.new }
|
|
5
|
+
|
|
6
|
+
context "#authorize_action!" do
|
|
7
|
+
it "forbids action when authorization rules are not defined" do
|
|
8
|
+
allow(authorizator).to receive(:current_action_name).and_return(:action)
|
|
9
|
+
expect(authorizator).to receive(:forbid_action!)
|
|
10
|
+
|
|
11
|
+
authorizator.class.authorization_rules = nil
|
|
12
|
+
authorizator.authorize_action!
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "forbids action when authorization rules are empty" do
|
|
16
|
+
allow(authorizator).to receive(:current_action_name).and_return(:action)
|
|
17
|
+
expect(authorizator).to receive(:forbid_action!)
|
|
18
|
+
|
|
19
|
+
authorizator.class.authorization_rules = {}
|
|
20
|
+
authorizator.authorize_action!
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "forbids action when authorization rule is not defined for current action" do
|
|
24
|
+
allow(authorizator).to receive(:current_action_name).and_return(:action)
|
|
25
|
+
expect(authorizator).to receive(:forbid_action!)
|
|
26
|
+
|
|
27
|
+
authorizator.class.authorization_rules = {other_action: -> { false }}
|
|
28
|
+
authorizator.authorize_action!
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "forbids action when authorization rule returns false" do
|
|
32
|
+
allow(authorizator).to receive(:current_action_name).and_return(:action)
|
|
33
|
+
expect(authorizator).to receive(:forbid_action!)
|
|
34
|
+
|
|
35
|
+
authorizator.class.authorization_rules = {action: -> { false }}
|
|
36
|
+
authorizator.authorize_action!
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "allows action when authorization rule returns true" do
|
|
40
|
+
allow(authorizator).to receive(:current_action_name).and_return(:action)
|
|
41
|
+
expect(authorizator).to_not receive(:forbid_action!)
|
|
42
|
+
|
|
43
|
+
authorizator.class.authorization_rules = {action: -> { true }}
|
|
44
|
+
authorizator.authorize_action!
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "allows action when authorization rule returns truthy value" do
|
|
48
|
+
allow(authorizator).to receive(:current_action_name).and_return(:action)
|
|
49
|
+
expect(authorizator).to_not receive(:forbid_action!)
|
|
50
|
+
|
|
51
|
+
authorizator.class.authorization_rules = {action: -> { "truthy" }}
|
|
52
|
+
authorizator.authorize_action!
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "executes instance method if authorization rule is defined as Symbol" do
|
|
56
|
+
allow(authorizator).to receive(:current_action_name).and_return(:action)
|
|
57
|
+
expect(authorizator).to receive(:admin?).and_return(true)
|
|
58
|
+
expect(authorizator).to_not receive(:forbid_action!)
|
|
59
|
+
|
|
60
|
+
authorizator.class.authorization_rules = {action: :admin?}
|
|
61
|
+
authorizator.authorize_action!
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "#authorization_rules are not defined by default" do
|
|
66
|
+
expect(authorizator.class.authorization_rules).to be_nil
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it "#forbid_action! is not implemented by default" do
|
|
70
|
+
expect { authorizator.send(:forbid_action!) }.to raise_error(NotImplementedError)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "#current_action_name is not implemented by default" do
|
|
74
|
+
expect { authorizator.send(:current_action_name) }.to raise_error(NotImplementedError)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require "simplecov"
|
|
2
|
+
require "coveralls"
|
|
3
|
+
|
|
4
|
+
SimpleCov.formatter = Coveralls::SimpleCov::Formatter
|
|
5
|
+
SimpleCov.start
|
|
6
|
+
|
|
7
|
+
require File.expand_path("../lib/authorize_action", __dir__)
|
|
8
|
+
|
|
9
|
+
RSpec.configure do |config|
|
|
10
|
+
config.order = :random
|
|
11
|
+
config.color = true
|
|
12
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: authorize_action
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Jarmo Pertman
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2015-07-05 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.7'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.7'
|
|
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.1.0
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: 3.1.0
|
|
55
|
+
description:
|
|
56
|
+
email:
|
|
57
|
+
- jarmo.p@gmail.com
|
|
58
|
+
executables: []
|
|
59
|
+
extensions: []
|
|
60
|
+
extra_rdoc_files: []
|
|
61
|
+
files:
|
|
62
|
+
- ".gitignore"
|
|
63
|
+
- ".travis.yml"
|
|
64
|
+
- CHANGES.md
|
|
65
|
+
- Gemfile
|
|
66
|
+
- LICENSE.txt
|
|
67
|
+
- README.md
|
|
68
|
+
- Rakefile
|
|
69
|
+
- authorize_action.gemspec
|
|
70
|
+
- lib/authorize_action.rb
|
|
71
|
+
- lib/authorize_action/rails.rb
|
|
72
|
+
- lib/authorize_action/sinatra.rb
|
|
73
|
+
- spec/authorize_action/rails_spec.rb
|
|
74
|
+
- spec/authorize_action/sinatra_spec.rb
|
|
75
|
+
- spec/authorize_action_spec.rb
|
|
76
|
+
- spec/spec_helper.rb
|
|
77
|
+
homepage: https://github.com/jarmo/authorize_action
|
|
78
|
+
licenses:
|
|
79
|
+
- MIT
|
|
80
|
+
metadata: {}
|
|
81
|
+
post_install_message:
|
|
82
|
+
rdoc_options: []
|
|
83
|
+
require_paths:
|
|
84
|
+
- lib
|
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
|
+
requirements:
|
|
92
|
+
- - ">="
|
|
93
|
+
- !ruby/object:Gem::Version
|
|
94
|
+
version: '0'
|
|
95
|
+
requirements: []
|
|
96
|
+
rubyforge_project:
|
|
97
|
+
rubygems_version: 2.4.5
|
|
98
|
+
signing_key:
|
|
99
|
+
specification_version: 4
|
|
100
|
+
summary: Really secure and simple authorization library for your Rails, Sinatra or
|
|
101
|
+
whatever web framework, which doesn't suck.
|
|
102
|
+
test_files:
|
|
103
|
+
- spec/authorize_action/rails_spec.rb
|
|
104
|
+
- spec/authorize_action/sinatra_spec.rb
|
|
105
|
+
- spec/authorize_action_spec.rb
|
|
106
|
+
- spec/spec_helper.rb
|