fat_model_auth 2.0.1
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.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +172 -0
- data/Rakefile +23 -0
- data/VERSION +1 -0
- data/lib/fat_model_auth.rb +9 -0
- data/lib/fat_model_auth/canned_gate_keeper.rb +40 -0
- data/lib/fat_model_auth/controller_helpers.rb +45 -0
- data/lib/fat_model_auth/gate_keeper.rb +33 -0
- data/lib/fat_model_auth/model_helpers.rb +20 -0
- data/lib/fat_model_auth/view_helpers.rb +18 -0
- data/test/fat_model_auth_test.rb +8 -0
- data/test/test_helper.rb +3 -0
- metadata +79 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 [Brent Greeff]
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,172 @@
|
|
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/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
desc 'Test the fat_model_auth plugin.'
|
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
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.0.0
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'fat_model_auth/canned_gate_keeper'
|
2
|
+
require 'fat_model_auth/controller_helpers'
|
3
|
+
require 'fat_model_auth/gate_keeper'
|
4
|
+
require 'fat_model_auth/model_helpers'
|
5
|
+
require 'fat_model_auth/view_helpers'
|
6
|
+
|
7
|
+
ActionController::Base.send(:include, FatModelAuth::ControllerHelpers)
|
8
|
+
ActiveRecord::Base.send(:extend, FatModelAuth::ModelHelpers)
|
9
|
+
ActionView::Base.send(:include, FatModelAuth::ViewHelpers)
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module FatModelAuth
|
2
|
+
class CannedGateKeeper
|
3
|
+
|
4
|
+
def self.allows(method)
|
5
|
+
self.new(method => true)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.denies(method)
|
9
|
+
self.new(method => false)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.build(params)
|
13
|
+
self.new(params)
|
14
|
+
end
|
15
|
+
|
16
|
+
def allows(user)
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(params)
|
21
|
+
@map = {}
|
22
|
+
add_rules(params)
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_rules(params)
|
26
|
+
for param in params
|
27
|
+
response = param.pop
|
28
|
+
@map["to_#{param.pop}?".to_sym] = lambda { response }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def method_missing(method, *args)
|
33
|
+
unless @map.has_key? method
|
34
|
+
raise NoMethodError, "undefined method allows(user).#{method} for #{self.class}"
|
35
|
+
end
|
36
|
+
|
37
|
+
@map[method].call
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module FatModelAuth
|
2
|
+
class AuthException < Exception; end
|
3
|
+
|
4
|
+
module ControllerHelpers
|
5
|
+
protected
|
6
|
+
|
7
|
+
def auth_required
|
8
|
+
authority = get_authority
|
9
|
+
|
10
|
+
access_granted = authority.allows(current_user).send "to_#{params[:action]}?"
|
11
|
+
respond_with_404_page unless access_granted
|
12
|
+
end
|
13
|
+
|
14
|
+
def access_denied?
|
15
|
+
authority = get_authority
|
16
|
+
|
17
|
+
access_granted = authority.allows(current_user).send "to_#{params[:action]}?"
|
18
|
+
if access_granted
|
19
|
+
false
|
20
|
+
else
|
21
|
+
respond_with_404_page
|
22
|
+
true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def get_authority
|
29
|
+
if self.respond_to? :override_authority
|
30
|
+
authority = override_authority
|
31
|
+
raise FatModelAuth::AuthException, "override_authority defined but nil" if authority.nil?
|
32
|
+
else
|
33
|
+
authority_name = params[:controller].singularize
|
34
|
+
authority = instance_variable_get("@#{authority_name}")
|
35
|
+
raise FatModelAuth::AuthException, "#{authority_name} is nil" if authority.nil?
|
36
|
+
end
|
37
|
+
|
38
|
+
return authority
|
39
|
+
end
|
40
|
+
|
41
|
+
def respond_with_404_page
|
42
|
+
render "#{RAILS_ROOT}/public/404.html", :status => 404
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module FatModelAuth
|
2
|
+
class GateKeeper
|
3
|
+
|
4
|
+
def initialize(params)
|
5
|
+
@map = {}
|
6
|
+
add_rules(params)
|
7
|
+
end
|
8
|
+
|
9
|
+
def add_rules(params)
|
10
|
+
auth_condition = params.pop[:if]
|
11
|
+
methods = params
|
12
|
+
|
13
|
+
for method in methods
|
14
|
+
@map["to_#{method}?".to_sym] = auth_condition
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def check(model, user)
|
19
|
+
@model = model
|
20
|
+
@user = user
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing(method, *args)
|
25
|
+
unless @map.has_key? method
|
26
|
+
raise NoMethodError, "undefined method allows(user).#{method} for #{@model.inspect}"
|
27
|
+
end
|
28
|
+
return false if @user.nil?
|
29
|
+
|
30
|
+
@map[method].call(@model, @user)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module FatModelAuth
|
2
|
+
module ModelHelpers
|
3
|
+
def allows(*params)
|
4
|
+
if self.respond_to? :gate_keeper
|
5
|
+
class_eval do
|
6
|
+
self.gate_keeper.add_rules(params)
|
7
|
+
end
|
8
|
+
else
|
9
|
+
class_eval do
|
10
|
+
cattr_accessor :gate_keeper
|
11
|
+
self.gate_keeper = FatModelAuth::GateKeeper.new(params)
|
12
|
+
|
13
|
+
define_method "allows" do |user|
|
14
|
+
self.gate_keeper.check(self, user)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module FatModelAuth
|
2
|
+
module ViewHelpers
|
3
|
+
def allowed_to?(options)
|
4
|
+
authority = options.values.first
|
5
|
+
|
6
|
+
get_actions(options).each do |action|
|
7
|
+
return true if authority.allows(current_user).send "to_#{action}?"
|
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
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fat_model_auth
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 13
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 2.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Brent Greeff
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-10-30 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: Clean resource based Authorisation plugin for Rails.
|
23
|
+
email: email@brentgreeff.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files:
|
29
|
+
- MIT-LICENSE
|
30
|
+
- README.rdoc
|
31
|
+
files:
|
32
|
+
- MIT-LICENSE
|
33
|
+
- README.rdoc
|
34
|
+
- Rakefile
|
35
|
+
- VERSION
|
36
|
+
- lib/fat_model_auth.rb
|
37
|
+
- lib/fat_model_auth/canned_gate_keeper.rb
|
38
|
+
- lib/fat_model_auth/controller_helpers.rb
|
39
|
+
- lib/fat_model_auth/gate_keeper.rb
|
40
|
+
- lib/fat_model_auth/model_helpers.rb
|
41
|
+
- lib/fat_model_auth/view_helpers.rb
|
42
|
+
- test/fat_model_auth_test.rb
|
43
|
+
- test/test_helper.rb
|
44
|
+
has_rdoc: true
|
45
|
+
homepage: http://github.com/brentgreeff/fat_model_auth
|
46
|
+
licenses: []
|
47
|
+
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options:
|
50
|
+
- --charset=UTF-8
|
51
|
+
require_paths:
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
hash: 3
|
59
|
+
segments:
|
60
|
+
- 0
|
61
|
+
version: "0"
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
hash: 3
|
68
|
+
segments:
|
69
|
+
- 0
|
70
|
+
version: "0"
|
71
|
+
requirements: []
|
72
|
+
|
73
|
+
rubyforge_project:
|
74
|
+
rubygems_version: 1.3.7
|
75
|
+
signing_key:
|
76
|
+
specification_version: 3
|
77
|
+
summary: Clean resource based Authorisation plugin for Rails.
|
78
|
+
test_files: []
|
79
|
+
|