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 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
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
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
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5.0
5
+ before_install: gem install bundler -v 1.16.1
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in fat_model_auth.gemspec
6
+ gemspec
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
+ [![Build Status](https://semaphoreci.com/api/v1/brentgreeff/fat_model_auth/branches/master/badge.svg)](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 'rake'
2
- require 'rake/testtask'
3
- require 'rake/rdoctask'
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
4
3
 
5
- desc 'Default: run unit tests.'
6
- task :default => :test
4
+ RSpec::Core::RakeTask.new(:spec)
7
5
 
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
6
+ task :default => :spec
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.0
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,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -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? :override_authority
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 = params[:controller].singularize
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
- render "#{Rails.root}/public/404.html", :status => 404
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
- auth_condition = params.pop[:if]
11
- methods = params
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
@@ -0,0 +1,3 @@
1
+ module FatModelAuth
2
+ VERSION = "4.0.0"
3
+ end
@@ -1,18 +1,11 @@
1
1
  module FatModelAuth
2
2
  module ViewHelpers
3
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}?"
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
@@ -1,9 +1,15 @@
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'
1
+ require 'action_pack'
2
+ require 'active_support'
6
3
 
7
- ActionController::Base.send(:include, FatModelAuth::ControllerHelpers)
8
- ActiveRecord::Base.send(:extend, FatModelAuth::ModelHelpers)
9
- ActionView::Base.send(:include, FatModelAuth::ViewHelpers)
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
- hash: 7
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: bin
9
+ bindir: exe
16
10
  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
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
- extra_rdoc_files:
29
- - MIT-LICENSE
30
- - README.rdoc
31
- files:
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.rdoc
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
- - 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
-
171
+ homepage: https://github.com/brentgreeff/fat_model_auth
172
+ licenses:
173
+ - MIT
174
+ metadata: {}
48
175
  post_install_message:
49
- rdoc_options:
50
- - --charset=UTF-8
51
- require_paths:
176
+ rdoc_options: []
177
+ require_paths:
52
178
  - lib
53
- required_ruby_version: !ruby/object:Gem::Requirement
54
- none: false
55
- requirements:
179
+ required_ruby_version: !ruby/object:Gem::Requirement
180
+ requirements:
56
181
  - - ">="
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:
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
- hash: 3
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: 1.3.7
191
+ rubygems_version: 2.7.3
75
192
  signing_key:
76
- specification_version: 3
77
- summary: Clean resource based Authorisation plugin for Rails.
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
@@ -1,8 +0,0 @@
1
- require 'test_helper'
2
-
3
- class FatModelAuthTest < ActiveSupport::TestCase
4
- # Replace this with your real tests.
5
- test "the truth" do
6
- assert true
7
- end
8
- end
data/test/test_helper.rb DELETED
@@ -1,3 +0,0 @@
1
- require 'rubygems'
2
- require 'active_support'
3
- require 'active_support/test_case'