fat_model_auth 3.0.0 → 4.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/.DS_Store +0 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +133 -0
- data/Guardfile +72 -0
- data/LICENSE.txt +21 -0
- data/README.md +230 -0
- data/Rakefile +4 -21
- data/VERSION +1 -1
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/fat_model_auth.gemspec +36 -0
- data/lib/fat_model_auth/canned_gate_keeper.rb +6 -8
- data/lib/fat_model_auth/controller_helpers.rb +16 -12
- data/lib/fat_model_auth/gate_keeper.rb +15 -8
- data/lib/fat_model_auth/model_helpers.rb +2 -1
- data/lib/fat_model_auth/railtie.rb +19 -0
- data/lib/fat_model_auth/version.rb +3 -0
- data/lib/fat_model_auth/view_helpers.rb +4 -11
- data/lib/fat_model_auth.rb +14 -8
- metadata +171 -55
- data/README.rdoc +0 -172
- data/test/fat_model_auth_test.rb +0 -8
- data/test/test_helper.rb +0 -3
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 60e63b266772142d56d536dc3985199f38ef424804a6ee4fd57918eec9cc326a
|
|
4
|
+
data.tar.gz: '009ed5f678f7fd34af4cfaf04cde49c70ccd159be5cc80e3a12d29a13c9ab06a'
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: c2f13ff755861acee8831c829bcbfe708b0c21049e5c489b49b42d08a8d1ed719aba07dc10e2e7be30bb9bd18f3edd0b46e4bfcd40e98a01b7903b12b73fdff4
|
|
7
|
+
data.tar.gz: 31906b600814c68081032784a2291004f5ef7e8a835018de8de243c4cdbe819ef3393b66dbb3def6d8c669948ae46f70605dbdc7c847bed311d42dec8b63d2ad
|
data/.DS_Store
ADDED
|
Binary file
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-gemset
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
fat_model_auth
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.5.0
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
fat_model_auth (4.0.0)
|
|
5
|
+
|
|
6
|
+
GEM
|
|
7
|
+
remote: https://rubygems.org/
|
|
8
|
+
specs:
|
|
9
|
+
actionpack (5.1.4)
|
|
10
|
+
actionview (= 5.1.4)
|
|
11
|
+
activesupport (= 5.1.4)
|
|
12
|
+
rack (~> 2.0)
|
|
13
|
+
rack-test (>= 0.6.3)
|
|
14
|
+
rails-dom-testing (~> 2.0)
|
|
15
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
|
16
|
+
actionview (5.1.4)
|
|
17
|
+
activesupport (= 5.1.4)
|
|
18
|
+
builder (~> 3.1)
|
|
19
|
+
erubi (~> 1.4)
|
|
20
|
+
rails-dom-testing (~> 2.0)
|
|
21
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
|
22
|
+
activemodel (5.1.4)
|
|
23
|
+
activesupport (= 5.1.4)
|
|
24
|
+
activerecord (5.1.4)
|
|
25
|
+
activemodel (= 5.1.4)
|
|
26
|
+
activesupport (= 5.1.4)
|
|
27
|
+
arel (~> 8.0)
|
|
28
|
+
activesupport (5.1.4)
|
|
29
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
30
|
+
i18n (~> 0.7)
|
|
31
|
+
minitest (~> 5.1)
|
|
32
|
+
tzinfo (~> 1.1)
|
|
33
|
+
arel (8.0.0)
|
|
34
|
+
awesome_print (1.8.0)
|
|
35
|
+
builder (3.2.3)
|
|
36
|
+
coderay (1.1.2)
|
|
37
|
+
concurrent-ruby (1.0.5)
|
|
38
|
+
crass (1.0.3)
|
|
39
|
+
diff-lcs (1.3)
|
|
40
|
+
erubi (1.7.0)
|
|
41
|
+
ffi (1.9.18)
|
|
42
|
+
ffi (1.9.18-x86-mingw32)
|
|
43
|
+
formatador (0.2.5)
|
|
44
|
+
guard (2.14.2)
|
|
45
|
+
formatador (>= 0.2.4)
|
|
46
|
+
listen (>= 2.7, < 4.0)
|
|
47
|
+
lumberjack (>= 1.0.12, < 2.0)
|
|
48
|
+
nenv (~> 0.1)
|
|
49
|
+
notiffany (~> 0.0)
|
|
50
|
+
pry (>= 0.9.12)
|
|
51
|
+
shellany (~> 0.0)
|
|
52
|
+
thor (>= 0.18.1)
|
|
53
|
+
guard-compat (1.2.1)
|
|
54
|
+
guard-rspec (4.7.3)
|
|
55
|
+
guard (~> 2.1)
|
|
56
|
+
guard-compat (~> 1.1)
|
|
57
|
+
rspec (>= 2.99.0, < 4.0)
|
|
58
|
+
i18n (0.9.1)
|
|
59
|
+
concurrent-ruby (~> 1.0)
|
|
60
|
+
listen (3.1.5)
|
|
61
|
+
rb-fsevent (~> 0.9, >= 0.9.4)
|
|
62
|
+
rb-inotify (~> 0.9, >= 0.9.7)
|
|
63
|
+
ruby_dep (~> 1.2)
|
|
64
|
+
loofah (2.1.1)
|
|
65
|
+
crass (~> 1.0.2)
|
|
66
|
+
nokogiri (>= 1.5.9)
|
|
67
|
+
lumberjack (1.0.12)
|
|
68
|
+
method_source (0.9.0)
|
|
69
|
+
mini_portile2 (2.3.0)
|
|
70
|
+
minitest (5.11.1)
|
|
71
|
+
nenv (0.3.0)
|
|
72
|
+
nokogiri (1.8.1)
|
|
73
|
+
mini_portile2 (~> 2.3.0)
|
|
74
|
+
nokogiri (1.8.1-x86-mingw32)
|
|
75
|
+
mini_portile2 (~> 2.3.0)
|
|
76
|
+
notiffany (0.1.1)
|
|
77
|
+
nenv (~> 0.1)
|
|
78
|
+
shellany (~> 0.0)
|
|
79
|
+
pry (0.11.3)
|
|
80
|
+
coderay (~> 1.1.0)
|
|
81
|
+
method_source (~> 0.9.0)
|
|
82
|
+
rack (2.0.3)
|
|
83
|
+
rack-test (0.8.2)
|
|
84
|
+
rack (>= 1.0, < 3)
|
|
85
|
+
rails-dom-testing (2.0.3)
|
|
86
|
+
activesupport (>= 4.2.0)
|
|
87
|
+
nokogiri (>= 1.6)
|
|
88
|
+
rails-html-sanitizer (1.0.3)
|
|
89
|
+
loofah (~> 2.0)
|
|
90
|
+
rake (10.3.2)
|
|
91
|
+
rb-fsevent (0.10.2)
|
|
92
|
+
rb-inotify (0.9.10)
|
|
93
|
+
ffi (>= 0.5.0, < 2)
|
|
94
|
+
rspec (3.7.0)
|
|
95
|
+
rspec-core (~> 3.7.0)
|
|
96
|
+
rspec-expectations (~> 3.7.0)
|
|
97
|
+
rspec-mocks (~> 3.7.0)
|
|
98
|
+
rspec-core (3.7.1)
|
|
99
|
+
rspec-support (~> 3.7.0)
|
|
100
|
+
rspec-expectations (3.7.0)
|
|
101
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
102
|
+
rspec-support (~> 3.7.0)
|
|
103
|
+
rspec-mocks (3.7.0)
|
|
104
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
105
|
+
rspec-support (~> 3.7.0)
|
|
106
|
+
rspec-support (3.7.0)
|
|
107
|
+
ruby_dep (1.5.0)
|
|
108
|
+
shellany (0.0.1)
|
|
109
|
+
sqlite3 (1.3.13)
|
|
110
|
+
sqlite3 (1.3.13-x86-mingw32)
|
|
111
|
+
thor (0.20.0)
|
|
112
|
+
thread_safe (0.3.6)
|
|
113
|
+
tzinfo (1.2.4)
|
|
114
|
+
thread_safe (~> 0.1)
|
|
115
|
+
|
|
116
|
+
PLATFORMS
|
|
117
|
+
ruby
|
|
118
|
+
x86-mingw32
|
|
119
|
+
|
|
120
|
+
DEPENDENCIES
|
|
121
|
+
actionpack (~> 5.1)
|
|
122
|
+
activerecord (~> 5.1)
|
|
123
|
+
activesupport (~> 5.1)
|
|
124
|
+
awesome_print
|
|
125
|
+
bundler
|
|
126
|
+
fat_model_auth!
|
|
127
|
+
guard-rspec
|
|
128
|
+
rake (~> 10.0)
|
|
129
|
+
rspec
|
|
130
|
+
sqlite3
|
|
131
|
+
|
|
132
|
+
BUNDLED WITH
|
|
133
|
+
1.16.1
|
data/Guardfile
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# A sample Guardfile
|
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
|
3
|
+
|
|
4
|
+
## Uncomment and set this to only include directories you want to watch
|
|
5
|
+
# directories %w(app lib config test spec features) \
|
|
6
|
+
# .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
|
|
7
|
+
|
|
8
|
+
## Note: if you are using the `directories` clause above and you are not
|
|
9
|
+
## watching the project directory ('.'), then you will want to move
|
|
10
|
+
## the Guardfile to a watched dir and symlink it back, e.g.
|
|
11
|
+
#
|
|
12
|
+
# $ mkdir config
|
|
13
|
+
# $ mv Guardfile config/
|
|
14
|
+
# $ ln -s config/Guardfile .
|
|
15
|
+
#
|
|
16
|
+
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"
|
|
17
|
+
|
|
18
|
+
# Note: The cmd option is now required due to the increasing number of ways
|
|
19
|
+
# rspec may be run, below are examples of the most common uses.
|
|
20
|
+
# * bundler: 'bundle exec rspec'
|
|
21
|
+
# * bundler binstubs: 'bin/rspec'
|
|
22
|
+
# * spring: 'bin/rspec' (This will use spring if running and you have
|
|
23
|
+
# installed the spring binstubs per the docs)
|
|
24
|
+
# * zeus: 'zeus rspec' (requires the server to be started separately)
|
|
25
|
+
# * 'just' rspec: 'rspec'
|
|
26
|
+
|
|
27
|
+
guard :rspec, cmd: "bundle exec rspec" do
|
|
28
|
+
require "guard/rspec/dsl"
|
|
29
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
|
30
|
+
|
|
31
|
+
# Feel free to open issues for suggestions and improvements
|
|
32
|
+
|
|
33
|
+
clearing :on
|
|
34
|
+
|
|
35
|
+
# RSpec files
|
|
36
|
+
rspec = dsl.rspec
|
|
37
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
|
38
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
|
39
|
+
watch(rspec.spec_files)
|
|
40
|
+
|
|
41
|
+
# Ruby files
|
|
42
|
+
ruby = dsl.ruby
|
|
43
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
|
44
|
+
|
|
45
|
+
# Rails files
|
|
46
|
+
rails = dsl.rails(view_extensions: %w(erb haml slim))
|
|
47
|
+
dsl.watch_spec_files_for(rails.app_files)
|
|
48
|
+
dsl.watch_spec_files_for(rails.views)
|
|
49
|
+
|
|
50
|
+
watch(rails.controllers) do |m|
|
|
51
|
+
[
|
|
52
|
+
rspec.spec.call("routing/#{m[1]}_routing"),
|
|
53
|
+
rspec.spec.call("controllers/#{m[1]}_controller"),
|
|
54
|
+
rspec.spec.call("acceptance/#{m[1]}")
|
|
55
|
+
]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Rails config changes
|
|
59
|
+
watch(rails.spec_helper) { rspec.spec_dir }
|
|
60
|
+
watch(rails.routes) { "#{rspec.spec_dir}/routing" }
|
|
61
|
+
watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" }
|
|
62
|
+
|
|
63
|
+
# Capybara features specs
|
|
64
|
+
watch(rails.view_dirs) { |m| rspec.spec.call("features/#{m[1]}") }
|
|
65
|
+
watch(rails.layouts) { |m| rspec.spec.call("features/#{m[1]}") }
|
|
66
|
+
|
|
67
|
+
# Turnip features and steps
|
|
68
|
+
watch(%r{^spec/acceptance/(.+)\.feature$})
|
|
69
|
+
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m|
|
|
70
|
+
Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance"
|
|
71
|
+
end
|
|
72
|
+
end
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018 Brent
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
[](https://semaphoreci.com/brentgreeff/fat_model_auth)
|
|
2
|
+
|
|
3
|
+
# FatModelAuth
|
|
4
|
+
|
|
5
|
+
Wikipedia defines Authorization as:
|
|
6
|
+
|
|
7
|
+
> “the function of specifying access rights to resources”
|
|
8
|
+
|
|
9
|
+
Fat Model Auth allows the resources themselves to define these rights.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
Add to Gemfile
|
|
14
|
+
|
|
15
|
+
$ gem fat_model_auth
|
|
16
|
+
|
|
17
|
+
## Fat Model Auth is a simple, clean authorization system for Rails
|
|
18
|
+
|
|
19
|
+
How simple?
|
|
20
|
+
|
|
21
|
+
## ArticlesController
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
before_action :load_article
|
|
25
|
+
|
|
26
|
+
def edit
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def update
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def load_article
|
|
35
|
+
Article.find(params[:id])
|
|
36
|
+
end
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
We want to ensure only the Article's author can view the edit page or update the article.
|
|
40
|
+
|
|
41
|
+
### Add a before filter to the articles_controller:
|
|
42
|
+
|
|
43
|
+
`before_action :auth_required, only: [:edit, :update]`
|
|
44
|
+
|
|
45
|
+
Since this is the article controller, the resource in question is the @article.
|
|
46
|
+
|
|
47
|
+
auth_required must be called after the resource is already loaded.
|
|
48
|
+
|
|
49
|
+
Like this:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
before_action :load_article, only: [:edit, :update]
|
|
53
|
+
before_action :auth_required, only: [:edit, :update]
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
auth_required will infer the name of the resource from the controller. In the case of articles_controller, it will look for an @article instance variable.
|
|
57
|
+
|
|
58
|
+
Try and view the articles#edit page from a browser, or event better: re-run the spec.
|
|
59
|
+
|
|
60
|
+
You should get an exception:
|
|
61
|
+
|
|
62
|
+
`undefined method 'allows' for #<Article:0x204a8d8>`
|
|
63
|
+
|
|
64
|
+
This means the gem is working correctly.
|
|
65
|
+
|
|
66
|
+
fat_model_auth has generated a call to the @article model:
|
|
67
|
+
|
|
68
|
+
`@article.allows(current_user).to_edit?`
|
|
69
|
+
|
|
70
|
+
You need to define a `current_user` method in the application_controller, so that the current user is passed in for evaluation.
|
|
71
|
+
|
|
72
|
+
If `current_user` is `nil`, the controller will always return access_denied.
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
## In the Article Model
|
|
76
|
+
|
|
77
|
+
* Add the following:
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
allows :edit, :update,
|
|
81
|
+
if: -> (article, user) { article.author == user }
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The article model now supports the allows instance method, with 2 chains:
|
|
85
|
+
|
|
86
|
+
`@article.allows(current_user).to_edit?`
|
|
87
|
+
|
|
88
|
+
- called from the #edit action when using the auth_required before_action.
|
|
89
|
+
|
|
90
|
+
`@article.allows(current_user).to_update?`
|
|
91
|
+
|
|
92
|
+
- called on the update action.
|
|
93
|
+
|
|
94
|
+
### Access Denied is 404
|
|
95
|
+
|
|
96
|
+
Trying to access a resource without permission returns 404.
|
|
97
|
+
|
|
98
|
+
By returning 403 (Forbidden) you might be revealing potentially sensitive information.
|
|
99
|
+
|
|
100
|
+
404 = that doesn't exist.
|
|
101
|
+
|
|
102
|
+
403 = Yes that does exist, but you need to try harder to get access to it.
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
### New & Create
|
|
106
|
+
|
|
107
|
+
When dealing with the #new & #create actions we need a slightly different approach.
|
|
108
|
+
|
|
109
|
+
Quite often we will build the new object in the action.
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
def create
|
|
113
|
+
@article = current_user.articles.build(params[:article])
|
|
114
|
+
return if access_denied?
|
|
115
|
+
end
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
When you call access_denied? fat_model_auth will ask the @article if access is allowed.
|
|
119
|
+
|
|
120
|
+
The following call is generated for you:
|
|
121
|
+
|
|
122
|
+
`@article.allows(current_user).to_create?`
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
### What if you need different rules for different actions?
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
allows :edit,
|
|
129
|
+
if: -> (article, user) { article.author.can_edit? }
|
|
130
|
+
|
|
131
|
+
allows :update,
|
|
132
|
+
if: -> (article, user) { article.allows_updating? }
|
|
133
|
+
|
|
134
|
+
allows :delete,
|
|
135
|
+
unless: -> (article, user) { user.can_delete?(article) }
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Both if: and unless: symbols are supported.
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
### What if you are loading an @article in the StoriesController
|
|
142
|
+
|
|
143
|
+
We need to tell the controller which object will act as the authority.
|
|
144
|
+
|
|
145
|
+
class StoriesController < ApplicationController
|
|
146
|
+
def override_authority
|
|
147
|
+
@article
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
## View (templates)
|
|
153
|
+
|
|
154
|
+
Control which links, buttons or controls are displayed to a user:
|
|
155
|
+
|
|
156
|
+
`<%= link_to('EDIT', edit_article_path(article)) if allowed_to? edit: article -%>`
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
Control which blocks of html are accessible:
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
<% if allowed_to? edit_or_destroy: article -%>
|
|
163
|
+
<funky>html</funky>
|
|
164
|
+
<% end %>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
## Test First
|
|
169
|
+
|
|
170
|
+
Before adding unit tests, its best to start with a request_spec or another kind of integration test which is focussed on user interaction.
|
|
171
|
+
|
|
172
|
+
If the integration test covers the logic in the model then that might be sufficient.
|
|
173
|
+
|
|
174
|
+
## Request specs
|
|
175
|
+
```
|
|
176
|
+
login_as no_good_user
|
|
177
|
+
|
|
178
|
+
get "/articles/#{article.to_param}/edit"
|
|
179
|
+
|
|
180
|
+
expect( response ).to have_http_status(404)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
If you are using a mock user, you can stub the response
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
let(:yes) { FatModelAuth::CannedGateKeeper.allows(:edit) }
|
|
187
|
+
|
|
188
|
+
it "allows"
|
|
189
|
+
expect(article).to receive(:allows).and_return(yes)
|
|
190
|
+
end
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
or
|
|
194
|
+
|
|
195
|
+
```
|
|
196
|
+
let(:no) { FatModelAuth::CannedGateKeeper.denies(:edit) }
|
|
197
|
+
|
|
198
|
+
it "does not allow"
|
|
199
|
+
expect(article).to receive(:allows).and_returns(no)
|
|
200
|
+
end
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Model specs
|
|
204
|
+
|
|
205
|
+
### EDIT
|
|
206
|
+
|
|
207
|
+
`expect( article.allows( peon ).to_edit? ).to be false`
|
|
208
|
+
|
|
209
|
+
`expect( article.allows( admin ).to_edit? ).to be true`
|
|
210
|
+
|
|
211
|
+
### UPDATE
|
|
212
|
+
|
|
213
|
+
`expect( article.allows( peon ).to_update? ).to be false`
|
|
214
|
+
|
|
215
|
+
`expect( article.allows( admin ).to_update? ).to be true`
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
## Development
|
|
219
|
+
|
|
220
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
221
|
+
|
|
222
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
223
|
+
|
|
224
|
+
## Contributing
|
|
225
|
+
|
|
226
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/brentgreeff/fat_model_auth.
|
|
227
|
+
|
|
228
|
+
## License
|
|
229
|
+
|
|
230
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
CHANGED
|
@@ -1,23 +1,6 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
3
|
-
require 'rake/rdoctask'
|
|
1
|
+
require "bundler/gem_tasks"
|
|
2
|
+
require "rspec/core/rake_task"
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
task :default => :test
|
|
4
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
Rake::TestTask.new(:test) do |t|
|
|
10
|
-
t.libs << 'lib'
|
|
11
|
-
t.libs << 'test'
|
|
12
|
-
t.pattern = 'test/**/*_test.rb'
|
|
13
|
-
t.verbose = true
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
desc 'Generate documentation for the fat_model_auth plugin.'
|
|
17
|
-
Rake::RDocTask.new(:rdoc) do |rdoc|
|
|
18
|
-
rdoc.rdoc_dir = 'rdoc'
|
|
19
|
-
rdoc.title = 'FatModelAuth'
|
|
20
|
-
rdoc.options << '--line-numbers' << '--inline-source'
|
|
21
|
-
rdoc.rdoc_files.include('README')
|
|
22
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
23
|
-
end
|
|
6
|
+
task :default => :spec
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
3.0.0
|
data/bin/console
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "bundler/setup"
|
|
4
|
+
require "fat_model_auth"
|
|
5
|
+
|
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
8
|
+
|
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
10
|
+
# require "pry"
|
|
11
|
+
# Pry.start
|
|
12
|
+
|
|
13
|
+
require "irb"
|
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
|
+
require "fat_model_auth/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "fat_model_auth"
|
|
7
|
+
spec.version = FatModelAuth::VERSION
|
|
8
|
+
spec.authors = ["Brent Greeff"]
|
|
9
|
+
spec.email = ["email@brentgreeff.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = %q{Clean resource based Authorisation system for Rails.}
|
|
12
|
+
spec.description = %q{Define the rules for accessing resources through a simple DSL.}
|
|
13
|
+
spec.homepage = "https://github.com/brentgreeff/fat_model_auth"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
|
17
|
+
f.match(%r{^(test|spec|features)/})
|
|
18
|
+
end
|
|
19
|
+
spec.bindir = "exe"
|
|
20
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
21
|
+
spec.require_paths = ["lib"]
|
|
22
|
+
|
|
23
|
+
spec.add_development_dependency "activesupport", "~> 5.1"
|
|
24
|
+
spec.add_development_dependency "activerecord", "~> 5.1"
|
|
25
|
+
spec.add_development_dependency "actionpack", "~> 5.1"
|
|
26
|
+
spec.add_development_dependency "sqlite3"
|
|
27
|
+
|
|
28
|
+
spec.add_development_dependency "bundler"
|
|
29
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
|
30
|
+
spec.add_development_dependency "rspec"
|
|
31
|
+
|
|
32
|
+
spec.add_development_dependency "guard-rspec"
|
|
33
|
+
# spec.add_development_dependency "cucumber"
|
|
34
|
+
# spec.add_development_dependency "aruba"
|
|
35
|
+
spec.add_development_dependency "awesome_print"
|
|
36
|
+
end
|
|
@@ -1,39 +1,37 @@
|
|
|
1
1
|
module FatModelAuth
|
|
2
2
|
class CannedGateKeeper
|
|
3
|
-
|
|
4
3
|
def self.allows(method)
|
|
5
4
|
self.new(method => true)
|
|
6
5
|
end
|
|
7
|
-
|
|
6
|
+
|
|
8
7
|
def self.denies(method)
|
|
9
8
|
self.new(method => false)
|
|
10
9
|
end
|
|
11
|
-
|
|
10
|
+
|
|
12
11
|
def self.build(params)
|
|
13
12
|
self.new(params)
|
|
14
13
|
end
|
|
15
|
-
|
|
14
|
+
|
|
16
15
|
def allows(user)
|
|
17
16
|
self
|
|
18
17
|
end
|
|
19
|
-
|
|
18
|
+
|
|
20
19
|
def initialize(params)
|
|
21
20
|
@map = {}
|
|
22
21
|
add_rules(params)
|
|
23
22
|
end
|
|
24
|
-
|
|
23
|
+
|
|
25
24
|
def add_rules(params)
|
|
26
25
|
for param in params
|
|
27
26
|
response = param.pop
|
|
28
27
|
@map["to_#{param.pop}?".to_sym] = lambda { response }
|
|
29
28
|
end
|
|
30
29
|
end
|
|
31
|
-
|
|
30
|
+
|
|
32
31
|
def method_missing(method, *args)
|
|
33
32
|
unless @map.has_key? method
|
|
34
33
|
raise NoMethodError, "undefined method allows(user).#{method} for #{self.class}"
|
|
35
34
|
end
|
|
36
|
-
|
|
37
35
|
@map[method].call
|
|
38
36
|
end
|
|
39
37
|
end
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
module FatModelAuth
|
|
2
2
|
class AuthException < Exception; end
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
module ControllerHelpers
|
|
5
5
|
protected
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
def auth_required
|
|
8
8
|
authority = get_authority
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
access_granted = authority.allows(current_user).send "to_#{params[:action]}?"
|
|
11
11
|
respond_with_404_page unless access_granted
|
|
12
12
|
end
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
def access_denied?
|
|
15
15
|
authority = get_authority
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
access_granted = authority.allows(current_user).send "to_#{params[:action]}?"
|
|
18
18
|
if access_granted
|
|
19
19
|
false
|
|
@@ -22,24 +22,28 @@ module FatModelAuth
|
|
|
22
22
|
true
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
private
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
def get_authority
|
|
29
|
-
if self.respond_to?
|
|
29
|
+
if self.respond_to?(:override_authority, true)
|
|
30
30
|
authority = override_authority
|
|
31
31
|
raise FatModelAuth::AuthException, "override_authority defined but nil" if authority.nil?
|
|
32
32
|
else
|
|
33
|
-
authority_name =
|
|
33
|
+
authority_name = controller_name.singularize
|
|
34
34
|
authority = instance_variable_get("@#{authority_name}")
|
|
35
35
|
raise FatModelAuth::AuthException, "#{authority_name} is nil" if authority.nil?
|
|
36
36
|
end
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
return authority
|
|
39
39
|
end
|
|
40
|
-
|
|
40
|
+
|
|
41
41
|
def respond_with_404_page
|
|
42
|
-
|
|
42
|
+
if defined?(Rails)
|
|
43
|
+
render file: "#{Rails.root}/public/404.html", status: 404, layout: false
|
|
44
|
+
else
|
|
45
|
+
render nothing: true, status: 404, layout: false
|
|
46
|
+
end
|
|
43
47
|
end
|
|
44
48
|
end
|
|
45
49
|
end
|
|
@@ -1,33 +1,40 @@
|
|
|
1
1
|
module FatModelAuth
|
|
2
2
|
class GateKeeper
|
|
3
|
-
|
|
4
3
|
def initialize(params)
|
|
5
4
|
@map = {}
|
|
6
5
|
add_rules(params)
|
|
7
6
|
end
|
|
8
|
-
|
|
7
|
+
|
|
9
8
|
def add_rules(params)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
*methods, options = params
|
|
10
|
+
auth_condition = options[:if] || negate(options[:unless])
|
|
11
|
+
|
|
13
12
|
for method in methods
|
|
14
13
|
@map["to_#{method}?".to_sym] = auth_condition
|
|
15
14
|
end
|
|
16
15
|
end
|
|
17
|
-
|
|
16
|
+
|
|
18
17
|
def check(model, user)
|
|
19
18
|
@model = model
|
|
20
19
|
@user = user
|
|
21
20
|
self
|
|
22
21
|
end
|
|
23
|
-
|
|
22
|
+
|
|
24
23
|
def method_missing(method, *args)
|
|
25
24
|
unless @map.has_key? method
|
|
26
25
|
raise NoMethodError, "undefined method allows(user).#{method} for #{@model.inspect}"
|
|
27
26
|
end
|
|
28
27
|
return false if @user.nil?
|
|
29
|
-
|
|
28
|
+
|
|
30
29
|
@map[method].call(@model, @user)
|
|
31
30
|
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def negate(predicate)
|
|
35
|
+
proc do |*args|
|
|
36
|
+
!predicate.call(*args)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
32
39
|
end
|
|
33
40
|
end
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
module FatModelAuth
|
|
2
2
|
module ModelHelpers
|
|
3
|
+
|
|
3
4
|
def allows(*params)
|
|
4
5
|
if self.respond_to? :gate_keeper
|
|
5
6
|
class_eval do
|
|
@@ -9,7 +10,7 @@ module FatModelAuth
|
|
|
9
10
|
class_eval do
|
|
10
11
|
cattr_accessor :gate_keeper
|
|
11
12
|
self.gate_keeper = FatModelAuth::GateKeeper.new(params)
|
|
12
|
-
|
|
13
|
+
|
|
13
14
|
define_method "allows" do |user|
|
|
14
15
|
self.gate_keeper.check(self, user)
|
|
15
16
|
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'rails/railtie'
|
|
2
|
+
|
|
3
|
+
module FatModelAuth
|
|
4
|
+
class Railtie < Rails::Railtie
|
|
5
|
+
initializer "fat_model_auth.initialize" do |app|
|
|
6
|
+
ActiveSupport.on_load :action_controller do
|
|
7
|
+
include FatModelAuth::ControllerHelpers
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
ActiveSupport.on_load :active_record do
|
|
11
|
+
extend FatModelAuth::ModelHelpers
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
ActiveSupport.on_load :action_view do
|
|
15
|
+
include FatModelAuth::ViewHelpers
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
module FatModelAuth
|
|
2
2
|
module ViewHelpers
|
|
3
3
|
def allowed_to?(options)
|
|
4
|
-
authority = options.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
actions, authority = options.first
|
|
5
|
+
|
|
6
|
+
actions.to_s.split('_or_').any? do |action|
|
|
7
|
+
authority.allows(current_user).send "to_#{action}?"
|
|
8
8
|
end
|
|
9
|
-
return false
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
private
|
|
13
|
-
|
|
14
|
-
def get_actions(options)
|
|
15
|
-
return options.keys.first.to_s.split('_or_')
|
|
16
9
|
end
|
|
17
10
|
end
|
|
18
11
|
end
|
data/lib/fat_model_auth.rb
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
require '
|
|
2
|
-
require '
|
|
3
|
-
require 'fat_model_auth/gate_keeper'
|
|
4
|
-
require 'fat_model_auth/model_helpers'
|
|
5
|
-
require 'fat_model_auth/view_helpers'
|
|
1
|
+
require 'action_pack'
|
|
2
|
+
require 'active_support'
|
|
6
3
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
require "fat_model_auth/version"
|
|
5
|
+
|
|
6
|
+
require 'fat_model_auth/railtie' if defined?(Rails)
|
|
7
|
+
|
|
8
|
+
module FatModelAuth
|
|
9
|
+
extend ActiveSupport::Autoload
|
|
10
|
+
autoload :ControllerHelpers, 'fat_model_auth/controller_helpers.rb'
|
|
11
|
+
autoload :ModelHelpers, 'fat_model_auth/model_helpers.rb'
|
|
12
|
+
autoload :ViewHelpers, 'fat_model_auth/view_helpers.rb'
|
|
13
|
+
autoload :GateKeeper, 'fat_model_auth/gate_keeper.rb'
|
|
14
|
+
autoload :CannedGateKeeper, 'fat_model_auth/canned_gate_keeper.rb'
|
|
15
|
+
end
|
metadata
CHANGED
|
@@ -1,79 +1,195 @@
|
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fat_model_auth
|
|
3
|
-
version: !ruby/object:Gem::Version
|
|
4
|
-
|
|
5
|
-
prerelease: false
|
|
6
|
-
segments:
|
|
7
|
-
- 3
|
|
8
|
-
- 0
|
|
9
|
-
- 0
|
|
10
|
-
version: 3.0.0
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 4.0.0
|
|
11
5
|
platform: ruby
|
|
12
|
-
authors:
|
|
6
|
+
authors:
|
|
13
7
|
- Brent Greeff
|
|
14
8
|
autorequire:
|
|
15
|
-
bindir:
|
|
9
|
+
bindir: exe
|
|
16
10
|
cert_chain: []
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
11
|
+
date: 2018-01-25 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: activesupport
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '5.1'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '5.1'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: activerecord
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '5.1'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '5.1'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: actionpack
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '5.1'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '5.1'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: sqlite3
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: bundler
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: rake
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '10.0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '10.0'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: rspec
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0'
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: guard-rspec
|
|
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'
|
|
125
|
+
- !ruby/object:Gem::Dependency
|
|
126
|
+
name: awesome_print
|
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - ">="
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: '0'
|
|
132
|
+
type: :development
|
|
133
|
+
prerelease: false
|
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - ">="
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: '0'
|
|
139
|
+
description: Define the rules for accessing resources through a simple DSL.
|
|
140
|
+
email:
|
|
141
|
+
- email@brentgreeff.com
|
|
24
142
|
executables: []
|
|
25
|
-
|
|
26
143
|
extensions: []
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
|
|
144
|
+
extra_rdoc_files: []
|
|
145
|
+
files:
|
|
146
|
+
- ".DS_Store"
|
|
147
|
+
- ".gitignore"
|
|
148
|
+
- ".rspec"
|
|
149
|
+
- ".ruby-gemset"
|
|
150
|
+
- ".ruby-version"
|
|
151
|
+
- ".travis.yml"
|
|
152
|
+
- Gemfile
|
|
153
|
+
- Gemfile.lock
|
|
154
|
+
- Guardfile
|
|
155
|
+
- LICENSE.txt
|
|
32
156
|
- MIT-LICENSE
|
|
33
|
-
- README.
|
|
157
|
+
- README.md
|
|
34
158
|
- Rakefile
|
|
35
159
|
- VERSION
|
|
160
|
+
- bin/console
|
|
161
|
+
- bin/setup
|
|
162
|
+
- fat_model_auth.gemspec
|
|
36
163
|
- lib/fat_model_auth.rb
|
|
37
164
|
- lib/fat_model_auth/canned_gate_keeper.rb
|
|
38
165
|
- lib/fat_model_auth/controller_helpers.rb
|
|
39
166
|
- lib/fat_model_auth/gate_keeper.rb
|
|
40
167
|
- lib/fat_model_auth/model_helpers.rb
|
|
168
|
+
- lib/fat_model_auth/railtie.rb
|
|
169
|
+
- lib/fat_model_auth/version.rb
|
|
41
170
|
- lib/fat_model_auth/view_helpers.rb
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
licenses: []
|
|
47
|
-
|
|
171
|
+
homepage: https://github.com/brentgreeff/fat_model_auth
|
|
172
|
+
licenses:
|
|
173
|
+
- MIT
|
|
174
|
+
metadata: {}
|
|
48
175
|
post_install_message:
|
|
49
|
-
rdoc_options:
|
|
50
|
-
|
|
51
|
-
require_paths:
|
|
176
|
+
rdoc_options: []
|
|
177
|
+
require_paths:
|
|
52
178
|
- lib
|
|
53
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
|
54
|
-
|
|
55
|
-
requirements:
|
|
179
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
180
|
+
requirements:
|
|
56
181
|
- - ">="
|
|
57
|
-
- !ruby/object:Gem::Version
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
version: "0"
|
|
62
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
63
|
-
none: false
|
|
64
|
-
requirements:
|
|
182
|
+
- !ruby/object:Gem::Version
|
|
183
|
+
version: '0'
|
|
184
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
185
|
+
requirements:
|
|
65
186
|
- - ">="
|
|
66
|
-
- !ruby/object:Gem::Version
|
|
67
|
-
|
|
68
|
-
segments:
|
|
69
|
-
- 0
|
|
70
|
-
version: "0"
|
|
187
|
+
- !ruby/object:Gem::Version
|
|
188
|
+
version: '0'
|
|
71
189
|
requirements: []
|
|
72
|
-
|
|
73
190
|
rubyforge_project:
|
|
74
|
-
rubygems_version:
|
|
191
|
+
rubygems_version: 2.7.3
|
|
75
192
|
signing_key:
|
|
76
|
-
specification_version:
|
|
77
|
-
summary: Clean resource based Authorisation
|
|
193
|
+
specification_version: 4
|
|
194
|
+
summary: Clean resource based Authorisation system for Rails.
|
|
78
195
|
test_files: []
|
|
79
|
-
|
data/README.rdoc
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
= Fat Model Auth
|
|
2
|
-
|
|
3
|
-
Wikipedia defines Authorization as 'the function of specifying access rights to resources'.
|
|
4
|
-
Fat Model Auth allows the resources themselves to define these rights.
|
|
5
|
-
|
|
6
|
-
== Fat Model Auth is a simple, clean authorization system for Rails
|
|
7
|
-
|
|
8
|
-
* How simple?
|
|
9
|
-
|
|
10
|
-
== Controller
|
|
11
|
-
|
|
12
|
-
* Imagine you have a controller for Articles:
|
|
13
|
-
|
|
14
|
-
before_filter :load_article
|
|
15
|
-
|
|
16
|
-
def edit
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def update
|
|
20
|
-
# Update shazam
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
private
|
|
24
|
-
|
|
25
|
-
def load_article
|
|
26
|
-
Article.find(params[:id])
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
* We want to ensure only the Articles Author can view the edit page or update
|
|
30
|
-
* an Article.
|
|
31
|
-
|
|
32
|
-
* Just Add a before filter:
|
|
33
|
-
|
|
34
|
-
before_filter :auth_required, :only => [:edit, :update]
|
|
35
|
-
|
|
36
|
-
* Add it AFTER you load the article
|
|
37
|
-
|
|
38
|
-
Go ahead and try and view the article
|
|
39
|
-
|
|
40
|
-
* We are missing something:
|
|
41
|
-
|
|
42
|
-
undefined method 'allows' for #<Article:0x204a8d8>
|
|
43
|
-
|
|
44
|
-
== Model
|
|
45
|
-
|
|
46
|
-
* Just add the following to your Article model:
|
|
47
|
-
|
|
48
|
-
allows :edit, :update,
|
|
49
|
-
:if => proc {|article, user| article.author == user}
|
|
50
|
-
|
|
51
|
-
* Need different rules for different actions:
|
|
52
|
-
|
|
53
|
-
allows :edit,
|
|
54
|
-
:if => proc {|article, user| article.author.name.eql? 'Jeff'}
|
|
55
|
-
|
|
56
|
-
allows :update,
|
|
57
|
-
:if => proc {|article, user| article.allows_updating?}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
Control which functions are displayed to a user
|
|
61
|
-
|
|
62
|
-
== View
|
|
63
|
-
|
|
64
|
-
<%= link_to('EDIT', edit_article_path(article)) if allowed_to? :edit => article -%>
|
|
65
|
-
|
|
66
|
-
* What about groups of controls:
|
|
67
|
-
|
|
68
|
-
<% if allowed_to? :edit_or_destroy => article -%>
|
|
69
|
-
<funky>html</funky>
|
|
70
|
-
<% end %>
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
Thats it.
|
|
74
|
-
|
|
75
|
-
== Test First
|
|
76
|
-
|
|
77
|
-
The rules that define access control, are defined in the model.
|
|
78
|
-
If you are testing first, start with a unit test:
|
|
79
|
-
|
|
80
|
-
* EDIT
|
|
81
|
-
|
|
82
|
-
assert @article.allows(@article.author).to_edit?
|
|
83
|
-
|
|
84
|
-
deny @article.allows(@someone_else).to_edit?
|
|
85
|
-
|
|
86
|
-
* UPDATE
|
|
87
|
-
|
|
88
|
-
assert @article.allows(@jeff).to_update?
|
|
89
|
-
|
|
90
|
-
deny @article.allows(@sammy).to_update?
|
|
91
|
-
|
|
92
|
-
These magic methods are created when you define 'allows' on your model.
|
|
93
|
-
|
|
94
|
-
When you say 'auth_required' in a before filter:
|
|
95
|
-
The plugin looks for an object based on the name of the controller:
|
|
96
|
-
|
|
97
|
-
So if you put the before filter in the 'articles_controller'
|
|
98
|
-
and you call the 'edit' action,
|
|
99
|
-
|
|
100
|
-
it generates the following call:
|
|
101
|
-
|
|
102
|
-
@article.allows(current_user).to_edit?
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
What happens if I am editing articles but I am not in the articles_controller?
|
|
106
|
-
|
|
107
|
-
* Just add this to the controller
|
|
108
|
-
|
|
109
|
-
class RestlessController < ApplicationController
|
|
110
|
-
def override_authority
|
|
111
|
-
@article
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
== Remember
|
|
116
|
-
|
|
117
|
-
1. A nil current_user will always return access_denied.
|
|
118
|
-
|
|
119
|
-
== Access Denied is 404
|
|
120
|
-
|
|
121
|
-
Thats right deal with it or fork it.
|
|
122
|
-
|
|
123
|
-
Trying to access a resource without permission returns 404
|
|
124
|
-
In other words:
|
|
125
|
-
|
|
126
|
-
"Say What?"
|
|
127
|
-
|
|
128
|
-
== New & Create and complex conditons
|
|
129
|
-
|
|
130
|
-
If you have complex conditions or when creating a new object
|
|
131
|
-
it may not have the information you need in a before filter.
|
|
132
|
-
|
|
133
|
-
You can always get the same result by being explicit in the method:
|
|
134
|
-
|
|
135
|
-
# Articles controller
|
|
136
|
-
|
|
137
|
-
def create
|
|
138
|
-
@article = current_user.articles.build(params[:article])
|
|
139
|
-
return if access_denied?
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
== Testing my controllers
|
|
143
|
-
|
|
144
|
-
login_as @no_good_user
|
|
145
|
-
get :edit, :id => @article.id
|
|
146
|
-
|
|
147
|
-
assert_response :not_found
|
|
148
|
-
assert_template 'public/404'
|
|
149
|
-
|
|
150
|
-
If you are using mock user, you may want to stub the response
|
|
151
|
-
|
|
152
|
-
@article.expects(:allows).returns(FatModelAuth::CannedGateKeeper.allows(:edit))
|
|
153
|
-
|
|
154
|
-
or
|
|
155
|
-
|
|
156
|
-
@article.expects(:allows).returns(FatModelAuth::CannedGateKeeper.denies(:edit))
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
== Testing my views
|
|
160
|
-
|
|
161
|
-
Who does that?
|
|
162
|
-
|
|
163
|
-
step 1. Install should_pricot
|
|
164
|
-
|
|
165
|
-
login_as @no_good_user
|
|
166
|
-
get :edit, :id => @article.id
|
|
167
|
-
|
|
168
|
-
element('#power_user a[@href="/create/havok"]').should_be_missing
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
Copyright (c) 2009 [Brent Greeff], released under the MIT license
|
data/test/fat_model_auth_test.rb
DELETED
data/test/test_helper.rb
DELETED