banken 0.0.1 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +2 -0
- data/.travis.yml +8 -0
- data/Gemfile +0 -2
- data/README.md +381 -17
- data/Rakefile +17 -1
- data/banken.gemspec +1 -0
- data/lib/banken.rb +27 -58
- data/lib/banken/error.rb +5 -5
- data/lib/banken/loyalty_finder.rb +28 -0
- data/lib/banken/version.rb +1 -1
- data/lib/generators/banken/install/USAGE +1 -1
- data/lib/generators/banken/install/install_generator.rb +2 -2
- data/lib/generators/banken/install/templates/{application_policy.rb → application_loyalty.rb} +2 -19
- data/lib/generators/banken/loyalty/USAGE +8 -0
- data/lib/generators/banken/loyalty/loyalty_generator.rb +13 -0
- data/lib/generators/banken/loyalty/templates/loyalty.rb +4 -0
- metadata +24 -8
- data/lib/banken/helper.rb +0 -8
- data/lib/banken/policy_finder.rb +0 -41
- data/lib/generators/banken/policy/USAGE +0 -8
- data/lib/generators/banken/policy/policy_generator.rb +0 -13
- data/lib/generators/banken/policy/templates/policy.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5fa23a2a65fb5297370655ef973516a3615be3d4
|
4
|
+
data.tar.gz: 5965509ce31efeb8243a1796191b4da0c416e429
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 763ee80874810e27d2d9edfa8dca6298e8f6694fa097ac558ac9857c23848b57d638b000410126e1bb427e183afb17ce4c37690a07d4c4701f4c3b9a50295d71
|
7
|
+
data.tar.gz: d8ea1e7142b0b3badd6bf538dc6a1498b921a858683e53d6b735bd5cd73bd6a02b848727d5f6fae4867fe7d69d894fa89f6458f8d2d634fa6a6c94e5083522cb
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,41 +1,405 @@
|
|
1
1
|
# Banken
|
2
2
|
|
3
|
-
|
3
|
+
[![Build Status](https://img.shields.io/travis/kyuden/banken/master.svg)](https://travis-ci.org/kyuden/banken)
|
4
|
+
[![Code Climate](https://codeclimate.com/github/kyuden/banken/badges/gpa.svg)](https://codeclimate.com/github/kyuden/banken)
|
5
|
+
[![Gem Version](https://badge.fury.io/rb/banken.svg)](https://badge.fury.io/rb/banken)
|
4
6
|
|
5
|
-
|
7
|
+
Banken provides a set of helpers which guide you in leveraging regular Ruby
|
8
|
+
classes and object oriented design patterns to build a simple, robust and
|
9
|
+
scaleable authorization system.
|
6
10
|
|
7
11
|
## Installation
|
8
12
|
|
9
|
-
|
13
|
+
``` ruby
|
14
|
+
gem "banken"
|
15
|
+
```
|
16
|
+
|
17
|
+
Include Banken in your application controller:
|
18
|
+
|
19
|
+
``` ruby
|
20
|
+
# app/controllers/application_controller.rb
|
21
|
+
class ApplicationController < ActionController::Base
|
22
|
+
include Banken
|
23
|
+
protect_from_forgery
|
24
|
+
end
|
25
|
+
```
|
26
|
+
|
27
|
+
Optionally, you can run the generator, which will set up an application loyalty
|
28
|
+
with some useful defaults for you:
|
29
|
+
|
30
|
+
``` sh
|
31
|
+
rails g banken:install
|
32
|
+
```
|
33
|
+
|
34
|
+
After generating your application loyalty, restart the Rails server so that Rails
|
35
|
+
can pick up any classes in the new `app/loyalties/` directory.
|
36
|
+
|
37
|
+
## Policies
|
38
|
+
|
39
|
+
Banken is focused around the notion of loyalty classes. We suggest that you put
|
40
|
+
these classes in `app/loyalties`. This is a simple example that allows updating
|
41
|
+
a post if the user is an admin, or if the post is unpublished:
|
42
|
+
|
43
|
+
``` ruby
|
44
|
+
# app/loyalties/posts_loyalty.rb
|
45
|
+
class PostsLoyalty
|
46
|
+
attr_reader :user, :post
|
47
|
+
|
48
|
+
def initialize(user, post)
|
49
|
+
@user = user
|
50
|
+
@post = post
|
51
|
+
end
|
52
|
+
|
53
|
+
def update?
|
54
|
+
user.admin? || post.unpublished?
|
55
|
+
end
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
As you can see, this is just a plain Ruby class. Banken makes the following
|
60
|
+
assumptions about this class:
|
61
|
+
|
62
|
+
- The class has the same name as some kind of controller class, only suffixed
|
63
|
+
with the word "Loyalty".
|
64
|
+
- The first argument is a user. In your controller, Banken will call the
|
65
|
+
`current_user` method to retrieve what to send into this argument
|
66
|
+
- The second argument is optional, whose authorization you want to check.
|
67
|
+
This does not need to be an ActiveRecord or even an ActiveModel object,
|
68
|
+
it can be anything really.
|
69
|
+
- The class implements some kind of query method, in this case `update?`.
|
70
|
+
Usually, this will map to the name of a particular controller action.
|
71
|
+
|
72
|
+
That's it really.
|
73
|
+
|
74
|
+
Usually you'll want to inherit from the application loyalty created by the
|
75
|
+
generator, or set up your own base class to inherit from:
|
76
|
+
|
77
|
+
``` ruby
|
78
|
+
# app/loyalties/posts_loyalty.rb
|
79
|
+
class PostsLoyalty < ApplicationLoyalty
|
80
|
+
def update?
|
81
|
+
user.admin? || record.unpublished?
|
82
|
+
end
|
83
|
+
end
|
84
|
+
```
|
85
|
+
|
86
|
+
In the generated `ApplicationLoyalty`, the optional object is called `record`.
|
87
|
+
|
88
|
+
Supposing that you are in PostsController, Banken now lets you do
|
89
|
+
this in your controller:
|
90
|
+
|
91
|
+
``` ruby
|
92
|
+
# app/controllers/posts_controller.rb
|
93
|
+
class PostsController < ApplicationController
|
94
|
+
def update
|
95
|
+
@post = Post.find(params[:id])
|
96
|
+
authorize! @post
|
97
|
+
if @post.update(post_params)
|
98
|
+
redirect_to @post
|
99
|
+
else
|
100
|
+
render :edit
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
```
|
105
|
+
|
106
|
+
The authorize method automatically infers from controller name that `Posts` will have a matching
|
107
|
+
`PostsLoyalty` class, and instantiates this class, handing in the current user
|
108
|
+
and the given optional object. It then infers from the action name, that it should call
|
109
|
+
`update?` on this instance of the loyalty. In this case, you can imagine that
|
110
|
+
`authorize!` would have done something like this:
|
111
|
+
|
112
|
+
``` ruby
|
113
|
+
raise "not authorized" unless PostsLoyalty.new(current_user, @post).update?
|
114
|
+
```
|
115
|
+
|
116
|
+
If you don't have an optional object for the first argument to `authorize!`, then you can pass
|
117
|
+
the class. For example:
|
118
|
+
|
119
|
+
Loyalty:
|
120
|
+
```ruby
|
121
|
+
# app/loyalties/posts_loyalty.rb
|
122
|
+
class PostsLoyalty < ApplicationLoyalty
|
123
|
+
def admin_list?
|
124
|
+
user.admin?
|
125
|
+
end
|
126
|
+
end
|
127
|
+
```
|
128
|
+
|
129
|
+
Controller:
|
130
|
+
```ruby
|
131
|
+
# app/controllers/posts_controller.rb
|
132
|
+
class PostsController < ApplicationController
|
133
|
+
def admin_list
|
134
|
+
authorize!
|
135
|
+
# Rest of controller action
|
136
|
+
end
|
137
|
+
end
|
138
|
+
```
|
139
|
+
|
140
|
+
You can easily get a hold of an instance of the loyalty through the `loyalty`
|
141
|
+
method in both the view and controller. This is especially useful for
|
142
|
+
conditionally showing links or buttons in the view:
|
143
|
+
|
144
|
+
``` erb
|
145
|
+
<% if loyalty(@post, :posts).update? %>
|
146
|
+
<%= link_to "Edit post", edit_post_path(@post) %>
|
147
|
+
<% end %>
|
148
|
+
```
|
149
|
+
|
150
|
+
## Ensuring loyalties are used
|
151
|
+
|
152
|
+
Banken adds a method called `verify_authorized` to your controllers. This
|
153
|
+
method will raise an exception if `authorize!` has not yet been called. You
|
154
|
+
should run this method in an `after_action` to ensure that you haven't
|
155
|
+
forgotten to authorize the action. For example:
|
156
|
+
|
157
|
+
``` ruby
|
158
|
+
# app/controllers/application_controller.rb
|
159
|
+
class ApplicationController < ActionController::Base
|
160
|
+
after_action :verify_authorized, except: :index
|
161
|
+
end
|
162
|
+
```
|
163
|
+
|
164
|
+
|
165
|
+
If you're using `verify_authorized` in your controllers but need to
|
166
|
+
conditionally bypass verification, you can use `skip_authorization`.
|
167
|
+
These are useful in circumstances where you don't want to disable verification for the
|
168
|
+
entire action, but have some cases where you intend to not authorize.
|
169
|
+
|
170
|
+
```ruby
|
171
|
+
# app/controllers/posts_controller.rb
|
172
|
+
class PostsController < ApplicationController
|
173
|
+
def show
|
174
|
+
record = Record.find_by(attribute: "value")
|
175
|
+
if record.present?
|
176
|
+
authorize record
|
177
|
+
else
|
178
|
+
skip_authorization
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
```
|
183
|
+
|
184
|
+
If you need to perform some more sophisticated logic or you want to raise a custom
|
185
|
+
exception you can use the two lower level method `banken_loyalty_authorized?` which
|
186
|
+
return `true` or `false` depending on whether `authorize!` have been called, respectively.
|
187
|
+
|
188
|
+
## Just plain old Ruby
|
189
|
+
|
190
|
+
As you can see, Banken doesn't do anything you couldn't have easily done
|
191
|
+
yourself. It's a very small library, it just provides a few neat helpers.
|
192
|
+
Together these give you the power of building a well structured, fully working
|
193
|
+
authorization system without using any special DSLs or funky syntax or
|
194
|
+
anything.
|
195
|
+
|
196
|
+
Remember that all of the loyalty is just plain Ruby classes,
|
197
|
+
which means you can use the same mechanisms you always use to DRY things up.
|
198
|
+
Encapsulate a set of permissions into a module and include them in multiple
|
199
|
+
loyalties. Use `alias_method` to make some permissions behave the same as
|
200
|
+
others. Inherit from a base set of permissions. Use metaprogramming if you
|
201
|
+
really have to.
|
202
|
+
|
203
|
+
## Generator
|
204
|
+
|
205
|
+
Use the supplied generator to generate loyalties:
|
206
|
+
|
207
|
+
``` sh
|
208
|
+
rails g banken:loyalty posts
|
209
|
+
```
|
210
|
+
|
211
|
+
## Closed systems
|
212
|
+
|
213
|
+
In many applications, only logged in users are really able to do anything. If
|
214
|
+
you're building such a system, it can be kind of cumbersome to check that the
|
215
|
+
user in a loyalty isn't `nil` for every single permission.
|
216
|
+
|
217
|
+
We suggest that you define a filter that redirects unauthenticated users to the
|
218
|
+
login page. As a secondary defence, if you've defined an ApplicationLoyalty, it
|
219
|
+
might be a good idea to raise an exception if somehow an unauthenticated user
|
220
|
+
got through. This way you can fail more gracefully.
|
221
|
+
|
222
|
+
``` ruby
|
223
|
+
# app/loyalties/application_loyalty.rb
|
224
|
+
class ApplicationLoyalty
|
225
|
+
def initialize(user, record)
|
226
|
+
raise Banken::NotAuthorizedError, "must be logged in" unless user
|
227
|
+
@user = user
|
228
|
+
@record = record
|
229
|
+
end
|
230
|
+
end
|
231
|
+
```
|
232
|
+
|
233
|
+
## Rescuing a denied Authorization in Rails
|
234
|
+
|
235
|
+
Banken raises a `Banken::NotAuthorizedError` you can
|
236
|
+
[rescue_from](http://guides.rubyonrails.org/action_controller_overview.html#rescue-from)
|
237
|
+
in your `ApplicationController`. You can customize the `user_not_authorized`
|
238
|
+
method in every controller.
|
239
|
+
|
240
|
+
```ruby
|
241
|
+
# app/controllers/application_controller.rb
|
242
|
+
class ApplicationController < ActionController::Base
|
243
|
+
protect_from_forgery
|
244
|
+
include Banken
|
245
|
+
|
246
|
+
rescue_from Banken::NotAuthorizedError, with: :user_not_authorized
|
247
|
+
|
248
|
+
private
|
249
|
+
|
250
|
+
def user_not_authorized
|
251
|
+
flash[:alert] = "You are not authorized to perform this action."
|
252
|
+
redirect_to(request.referrer || root_path)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
```
|
256
|
+
|
257
|
+
## Creating custom error messages
|
258
|
+
|
259
|
+
`NotAuthorizedError`s provide information on what query (e.g. `:create?`), what
|
260
|
+
controller (e.g. `PostsController`), and what loyalty (e.g. an instance of
|
261
|
+
`PostsLoyalty`) caused the error to be raised.
|
262
|
+
|
263
|
+
One way to use these `query`, `record`, and `loyalty` properties is to connect
|
264
|
+
them with `I18n` to generate error messages. Here's how you might go about doing
|
265
|
+
that.
|
10
266
|
|
11
267
|
```ruby
|
12
|
-
|
268
|
+
# app/controllers/application_controller.rb
|
269
|
+
class ApplicationController < ActionController::Base
|
270
|
+
rescue_from Banken::NotAuthorizedError, with: :user_not_authorized
|
271
|
+
|
272
|
+
private
|
273
|
+
|
274
|
+
def user_not_authorized(exception)
|
275
|
+
loyalty_name = exception.loyalty.class.to_s.underscore
|
276
|
+
|
277
|
+
flash[:error] = t "#{loyalty_name}.#{exception.query}", scope: "banken", default: :default
|
278
|
+
redirect_to(request.referrer || root_path)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
```
|
282
|
+
|
283
|
+
```yaml
|
284
|
+
en:
|
285
|
+
banken:
|
286
|
+
default: 'You cannot perform this action.'
|
287
|
+
posts_loyalty:
|
288
|
+
update?: 'You cannot edit this post!'
|
289
|
+
create?: 'You cannot create posts!'
|
13
290
|
```
|
14
291
|
|
15
|
-
|
292
|
+
Of course, this is just an example. Banken is agnostic as to how you implement
|
293
|
+
your error messaging.
|
16
294
|
|
17
|
-
|
295
|
+
## Customize Banken user
|
18
296
|
|
19
|
-
|
297
|
+
In some cases your controller might not have access to `current_user`, or your
|
298
|
+
`current_user` is not the method that should be invoked by Banken. Simply
|
299
|
+
define a method in your controller called `banken_user`.
|
20
300
|
|
21
|
-
|
301
|
+
```ruby
|
302
|
+
def banken_user
|
303
|
+
User.find_by_other_means
|
304
|
+
end
|
305
|
+
```
|
306
|
+
|
307
|
+
## Additional context
|
22
308
|
|
23
|
-
|
309
|
+
Banken strongly encourages you to model your application in such a way that the
|
310
|
+
only context you need for authorization is a user object and a domain model that
|
311
|
+
you want to check authorization for. If you find yourself needing more context than
|
312
|
+
that, consider whether you are authorizing the right domain model, maybe another
|
313
|
+
domain model (or a wrapper around multiple domain models) can provide the context
|
314
|
+
you need.
|
24
315
|
|
25
|
-
|
316
|
+
Banken does not allow you to pass additional arguments to loyalties for precisely
|
317
|
+
this reason.
|
26
318
|
|
27
|
-
|
319
|
+
However, in very rare cases, you might need to authorize based on more context than just
|
320
|
+
the currently authenticated user. Suppose for example that authorization is dependent
|
321
|
+
on IP address in addition to the authenticated user. In that case, one option is to
|
322
|
+
create a special class which wraps up both user and IP and passes it to the loyalty.
|
28
323
|
|
29
|
-
|
324
|
+
``` ruby
|
325
|
+
class UserContext
|
326
|
+
attr_reader :user, :ip
|
30
327
|
|
31
|
-
|
328
|
+
def initialize(user, ip)
|
329
|
+
@user = user
|
330
|
+
@ip = ip
|
331
|
+
end
|
332
|
+
end
|
32
333
|
|
33
|
-
|
334
|
+
# app/controllers/application_controller.rb
|
335
|
+
class ApplicationController
|
336
|
+
include Banken
|
34
337
|
|
35
|
-
|
338
|
+
def banken_user
|
339
|
+
UserContext.new(current_user, request.ip)
|
340
|
+
end
|
341
|
+
end
|
342
|
+
```
|
343
|
+
|
344
|
+
## Strong parameters
|
36
345
|
|
346
|
+
In Rails 4 (or Rails 3.2 with the
|
347
|
+
[strong_parameters](https://github.com/rails/strong_parameters) gem),
|
348
|
+
mass-assignment protection is handled in the controller. With Banken you can
|
349
|
+
control which attributes a user has access to update via your loyalties. You can
|
350
|
+
set up a `permitted_attributes` method in your loyalty like this:
|
37
351
|
|
38
|
-
|
352
|
+
```ruby
|
353
|
+
# app/loyalties/posts_loyalty.rb
|
354
|
+
class PostsLoyalty < ApplicationLoyalty
|
355
|
+
def permitted_attributes
|
356
|
+
if user.admin? || user.owner_of?(post)
|
357
|
+
[:title, :body, :tag_list]
|
358
|
+
else
|
359
|
+
[:tag_list]
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|
363
|
+
```
|
364
|
+
|
365
|
+
You can now retrieve these attributes from the loyalty:
|
366
|
+
|
367
|
+
```ruby
|
368
|
+
# app/controllers/posts_controller.rb
|
369
|
+
class PostsController < ApplicationController
|
370
|
+
def update
|
371
|
+
@post = Post.find(params[:id])
|
372
|
+
if @post.update_attributes(post_params)
|
373
|
+
redirect_to @post
|
374
|
+
else
|
375
|
+
render :edit
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
private
|
380
|
+
|
381
|
+
def post_params
|
382
|
+
params.require(:post).permit(loyalty(@post).permitted_attributes)
|
383
|
+
end
|
384
|
+
end
|
385
|
+
```
|
386
|
+
|
387
|
+
However, this is a bit cumbersome, so Banken provides a convenient helper method:
|
388
|
+
|
389
|
+
```ruby
|
390
|
+
# app/controllers/posts_controller.rb
|
391
|
+
class PostsController < ApplicationController
|
392
|
+
def update
|
393
|
+
@post = Post.find(params[:id])
|
394
|
+
if @post.update_attributes(permitted_attributes(@post))
|
395
|
+
redirect_to @post
|
396
|
+
else
|
397
|
+
render :edit
|
398
|
+
end
|
399
|
+
end
|
400
|
+
end
|
401
|
+
```
|
39
402
|
|
40
|
-
|
403
|
+
# License
|
41
404
|
|
405
|
+
Licensed under the MIT license, see the separate LICENSE.txt file.
|
data/Rakefile
CHANGED
@@ -1 +1,17 @@
|
|
1
|
-
require
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
require 'yard'
|
5
|
+
|
6
|
+
desc "Run all examples"
|
7
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
8
|
+
#t.rspec_path = 'bin/rspec'
|
9
|
+
t.rspec_opts = %w[--color]
|
10
|
+
end
|
11
|
+
|
12
|
+
YARD::Rake::YardocTask.new do |t|
|
13
|
+
t.files = ['lib/**/*.rb']
|
14
|
+
#t.options = ['--any', '--extra', '--opts'] # optional
|
15
|
+
end
|
16
|
+
|
17
|
+
task :default => [:spec]
|
data/banken.gemspec
CHANGED
data/lib/banken.rb
CHANGED
@@ -4,67 +4,52 @@ require "active_support/core_ext/object/blank"
|
|
4
4
|
require "active_support/core_ext/module/introspection"
|
5
5
|
require "banken/version"
|
6
6
|
require "banken/error"
|
7
|
-
require "banken/
|
8
|
-
require "banken/policy_finder"
|
7
|
+
require "banken/loyalty_finder"
|
9
8
|
|
10
9
|
module Banken
|
11
10
|
extend ActiveSupport::Concern
|
12
11
|
|
13
12
|
included do
|
14
|
-
# TODO
|
15
|
-
# helper Helper if respond_to?(:helper)
|
16
13
|
if respond_to?(:helper_method)
|
17
|
-
|
18
|
-
# helper_method :banken_policy_scope
|
14
|
+
helper_method :loyalty
|
19
15
|
helper_method :banken_user
|
20
16
|
end
|
17
|
+
|
21
18
|
if respond_to?(:hide_action)
|
22
|
-
hide_action :policy_scope
|
23
19
|
hide_action :permitted_attributes
|
24
|
-
hide_action :
|
20
|
+
hide_action :loyalty
|
25
21
|
hide_action :banken_user
|
26
22
|
hide_action :skip_authorization
|
27
|
-
hide_action :skip_policy_scope
|
28
23
|
hide_action :verify_authorized
|
29
|
-
hide_action :
|
30
|
-
hide_action :policies
|
31
|
-
hide_action :policy_scopes
|
24
|
+
hide_action :loyalties
|
32
25
|
end
|
33
26
|
end
|
34
27
|
|
35
28
|
class << self
|
36
|
-
def
|
37
|
-
|
38
|
-
end
|
39
|
-
|
40
|
-
def policy!(controller, user, record)
|
41
|
-
PolicyFinder.new(controller).policy!.new(user, record)
|
29
|
+
def loyalty!(controller_name, user, record=nil)
|
30
|
+
LoyaltyFinder.new(controller_name).loyalty!.new(user, record)
|
42
31
|
end
|
43
32
|
end
|
44
33
|
|
45
34
|
def authorize!(record=nil)
|
46
|
-
@
|
35
|
+
@_banken_loyalty_authorized = true
|
47
36
|
|
48
|
-
|
49
|
-
unless
|
50
|
-
raise NotAuthorizedError.new(controller: banken_controller_name,
|
37
|
+
loyalty = loyalty(record)
|
38
|
+
unless loyalty.public_send(banken_query_name)
|
39
|
+
raise NotAuthorizedError.new(controller: banken_controller_name, query: banken_query_name, loyalty: loyalty)
|
51
40
|
end
|
52
41
|
|
53
42
|
true
|
54
43
|
end
|
55
44
|
|
56
|
-
def policy_scope(scope)
|
57
|
-
@_banken_policy_scoped = true
|
58
|
-
banken_policy_scope(scope)
|
59
|
-
end
|
60
|
-
|
61
45
|
def permitted_attributes(record)
|
62
46
|
name = record.class.to_s.demodulize.underscore
|
63
|
-
params.require(name).permit(
|
47
|
+
params.require(name).permit(loyalty(record).permitted_attributes)
|
64
48
|
end
|
65
49
|
|
66
|
-
def
|
67
|
-
|
50
|
+
def loyalty(record=nil, controller_name=nil)
|
51
|
+
controller_name = banken_controller_name unless controller_name
|
52
|
+
loyalties[controller_name.to_s] ||= Banken.loyalty!(controller_name, banken_user, record)
|
68
53
|
end
|
69
54
|
|
70
55
|
def banken_user
|
@@ -72,48 +57,32 @@ module Banken
|
|
72
57
|
end
|
73
58
|
|
74
59
|
def skip_authorization
|
75
|
-
@
|
76
|
-
end
|
77
|
-
|
78
|
-
def skip_policy_scope
|
79
|
-
@_banken_policy_scoped = true
|
60
|
+
@_banken_loyalty_authorized = true
|
80
61
|
end
|
81
62
|
|
82
63
|
def verify_authorized
|
83
|
-
raise AuthorizationNotPerformedError unless
|
84
|
-
end
|
85
|
-
|
86
|
-
def verify_policy_scoped
|
87
|
-
raise PolicyScopingNotPerformedError unless banken_policy_scoped?
|
64
|
+
raise AuthorizationNotPerformedError unless banken_loyalty_authorized?
|
88
65
|
end
|
89
66
|
|
90
|
-
def
|
91
|
-
!!@
|
67
|
+
def banken_loyalty_authorized?
|
68
|
+
!!@_banken_loyalty_authorized
|
92
69
|
end
|
93
70
|
|
94
|
-
def
|
95
|
-
|
96
|
-
end
|
97
|
-
|
98
|
-
def policies
|
99
|
-
@_banken_policies ||= {}
|
100
|
-
end
|
101
|
-
|
102
|
-
def policy_scopes
|
103
|
-
@_banken_policy_scopes ||= {}
|
71
|
+
def loyalties
|
72
|
+
@_banken_loyalties ||= {}
|
104
73
|
end
|
105
74
|
|
106
75
|
private
|
107
76
|
|
108
|
-
def banken_policy_scope(scope)
|
109
|
-
policy_scopes[scope] ||= Banken.policy_scope!(banken_controller_name, banken_user, scope)
|
110
|
-
end
|
111
|
-
|
112
77
|
def banken_action_name
|
113
|
-
params[:action]
|
78
|
+
params[:action]
|
114
79
|
end
|
115
80
|
|
116
81
|
def banken_controller_name
|
117
|
-
params[:controller]
|
82
|
+
params[:controller]
|
83
|
+
end
|
84
|
+
|
85
|
+
def banken_query_name
|
86
|
+
"#{banken_action_name}?"
|
118
87
|
end
|
119
88
|
end
|
data/lib/banken/error.rb
CHANGED
@@ -2,17 +2,17 @@ module Banken
|
|
2
2
|
class Error < StandardError; end
|
3
3
|
|
4
4
|
class NotAuthorizedError < Error
|
5
|
-
attr_reader :controller, :
|
5
|
+
attr_reader :controller, :query, :loyalty
|
6
6
|
|
7
7
|
def initialize(options={})
|
8
8
|
if options.is_a? String
|
9
9
|
message = options
|
10
10
|
else
|
11
11
|
@controller = options[:controller]
|
12
|
-
@
|
13
|
-
@
|
12
|
+
@query = options[:query]
|
13
|
+
@loyalty = options[:loyalty]
|
14
14
|
|
15
|
-
message = options.fetch(:message) { "not allowed to #{
|
15
|
+
message = options.fetch(:message) { "not allowed to #{query} of #{controller} by #{loyalty.inspect}" }
|
16
16
|
end
|
17
17
|
|
18
18
|
super(message)
|
@@ -22,5 +22,5 @@ module Banken
|
|
22
22
|
class NotDefinedError < Error; end
|
23
23
|
class AuthorizationNotPerformedError < Error; end
|
24
24
|
|
25
|
-
class
|
25
|
+
class LoyaltyScopingNotPerformedError < AuthorizationNotPerformedError; end
|
26
26
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Banken
|
2
|
+
class LoyaltyFinder
|
3
|
+
SUFFIX = "Loyalty"
|
4
|
+
|
5
|
+
attr_reader :controller
|
6
|
+
|
7
|
+
def initialize(controller)
|
8
|
+
@controller = controller.to_s
|
9
|
+
end
|
10
|
+
|
11
|
+
def loyalty
|
12
|
+
loyalty_name.constantize
|
13
|
+
rescue NameError
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def loyalty!
|
18
|
+
raise NotDefinedError, "unable to find loyalty of nil" unless controller
|
19
|
+
loyalty || raise(NotDefinedError, "unable to find loyalty `#{loyalty_name}` for `#{controller}`")
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def loyalty_name
|
25
|
+
"#{controller.camelize}#{SUFFIX}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/banken/version.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
Description:
|
2
|
-
Generates an application
|
2
|
+
Generates an application loyalty as a starting point for your application.
|
@@ -3,8 +3,8 @@ module Banken
|
|
3
3
|
class InstallGenerator < ::Rails::Generators::Base
|
4
4
|
source_root File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
|
5
5
|
|
6
|
-
def
|
7
|
-
template '
|
6
|
+
def copy_application_loyalty
|
7
|
+
template 'application_loyalty.rb', 'app/loyalties/application_loyalty.rb'
|
8
8
|
end
|
9
9
|
end
|
10
10
|
end
|
data/lib/generators/banken/install/templates/{application_policy.rb → application_loyalty.rb}
RENAMED
@@ -1,4 +1,4 @@
|
|
1
|
-
class
|
1
|
+
class ApplicationLoyalty
|
2
2
|
attr_reader :user, :record
|
3
3
|
|
4
4
|
def initialize(user, record)
|
@@ -11,7 +11,7 @@ class ApplicationPolicy
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def show?
|
14
|
-
|
14
|
+
false
|
15
15
|
end
|
16
16
|
|
17
17
|
def create?
|
@@ -33,21 +33,4 @@ class ApplicationPolicy
|
|
33
33
|
def destroy?
|
34
34
|
false
|
35
35
|
end
|
36
|
-
|
37
|
-
def scope
|
38
|
-
Scope.new(user, record.class).resolve
|
39
|
-
end
|
40
|
-
|
41
|
-
class Scope
|
42
|
-
attr_reader :user, :scope
|
43
|
-
|
44
|
-
def initialize(user, scope)
|
45
|
-
@user = user
|
46
|
-
@scope = scope
|
47
|
-
end
|
48
|
-
|
49
|
-
def resolve
|
50
|
-
scope
|
51
|
-
end
|
52
|
-
end
|
53
36
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Banken
|
2
|
+
module Generators
|
3
|
+
class LoyaltyGenerator < ::Rails::Generators::NamedBase
|
4
|
+
source_root File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
|
5
|
+
|
6
|
+
def create_loyalty
|
7
|
+
template 'loyalty.rb', File.join('app/loyalties', class_path, "#{file_name}_loyalty.rb")
|
8
|
+
end
|
9
|
+
|
10
|
+
hook_for :test_framework
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: banken
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- kyuden
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-11-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -108,6 +108,20 @@ dependencies:
|
|
108
108
|
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: yard
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
111
125
|
description: Object oriented authorization like pundit for Rails applications.
|
112
126
|
email:
|
113
127
|
- msmsms.um@gmail.com
|
@@ -116,6 +130,8 @@ extensions: []
|
|
116
130
|
extra_rdoc_files: []
|
117
131
|
files:
|
118
132
|
- ".gitignore"
|
133
|
+
- ".rspec"
|
134
|
+
- ".travis.yml"
|
119
135
|
- CODE_OF_CONDUCT.md
|
120
136
|
- Gemfile
|
121
137
|
- LICENSE.txt
|
@@ -124,15 +140,14 @@ files:
|
|
124
140
|
- banken.gemspec
|
125
141
|
- lib/banken.rb
|
126
142
|
- lib/banken/error.rb
|
127
|
-
- lib/banken/
|
128
|
-
- lib/banken/policy_finder.rb
|
143
|
+
- lib/banken/loyalty_finder.rb
|
129
144
|
- lib/banken/version.rb
|
130
145
|
- lib/generators/banken/install/USAGE
|
131
146
|
- lib/generators/banken/install/install_generator.rb
|
132
|
-
- lib/generators/banken/install/templates/
|
133
|
-
- lib/generators/banken/
|
134
|
-
- lib/generators/banken/
|
135
|
-
- lib/generators/banken/
|
147
|
+
- lib/generators/banken/install/templates/application_loyalty.rb
|
148
|
+
- lib/generators/banken/loyalty/USAGE
|
149
|
+
- lib/generators/banken/loyalty/loyalty_generator.rb
|
150
|
+
- lib/generators/banken/loyalty/templates/loyalty.rb
|
136
151
|
homepage: https://github.com/kyuden/banken
|
137
152
|
licenses:
|
138
153
|
- MIT
|
@@ -158,3 +173,4 @@ signing_key:
|
|
158
173
|
specification_version: 4
|
159
174
|
summary: OO authorization for Rails.
|
160
175
|
test_files: []
|
176
|
+
has_rdoc:
|
data/lib/banken/helper.rb
DELETED
data/lib/banken/policy_finder.rb
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
module Banken
|
2
|
-
class PolicyFinder
|
3
|
-
SUFFIX = "Policy"
|
4
|
-
|
5
|
-
attr_reader :controller
|
6
|
-
|
7
|
-
def initialize(controller)
|
8
|
-
@controller = controller
|
9
|
-
end
|
10
|
-
|
11
|
-
def scope
|
12
|
-
policy::Scope if policy
|
13
|
-
rescue NameError
|
14
|
-
nil
|
15
|
-
end
|
16
|
-
|
17
|
-
def policy
|
18
|
-
klass = find
|
19
|
-
klass = klass.constantize if klass.is_a?(String)
|
20
|
-
klass
|
21
|
-
rescue NameError
|
22
|
-
nil
|
23
|
-
end
|
24
|
-
|
25
|
-
def scope!
|
26
|
-
raise NotDefinedError, "unable to find policy scope of nil" unless controller
|
27
|
-
scope || raise(NotDefinedError, "unable to find scope `#{find}::Scope` for `#{controller}`")
|
28
|
-
end
|
29
|
-
|
30
|
-
def policy!
|
31
|
-
raise NotDefinedError, "unable to find policy of nil" unless controller
|
32
|
-
policy || raise(NotDefinedError, "unable to find policy `#{find}` for `#{controller}`")
|
33
|
-
end
|
34
|
-
|
35
|
-
private
|
36
|
-
|
37
|
-
def find
|
38
|
-
"#{controller.camelize}#{SUFFIX}"
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
@@ -1,13 +0,0 @@
|
|
1
|
-
module Banken
|
2
|
-
module Generators
|
3
|
-
class PolicyGenerator < ::Rails::Generators::NamedBase
|
4
|
-
source_root File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
|
5
|
-
|
6
|
-
def create_policy
|
7
|
-
template 'policy.rb', File.join('app/policies', class_path, "#{file_name}_policy.rb")
|
8
|
-
end
|
9
|
-
|
10
|
-
hook_for :test_framework
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|