jrun-rails_doorman 0.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.
Files changed (78) hide show
  1. data/LICENSE +21 -0
  2. data/README.rdoc +88 -0
  3. data/Rakefile +57 -0
  4. data/features/doorman.feature +99 -0
  5. data/features/step_definitions/common_steps.rb +25 -0
  6. data/features/step_definitions/webrat_steps.rb +115 -0
  7. data/features/support/authorized_matcher.rb +29 -0
  8. data/features/support/env.rb +13 -0
  9. data/features/support/paths.rb +19 -0
  10. data/features/support/unauthorized_matcher.rb +29 -0
  11. data/lib/rails_doorman/helpers.rb +17 -0
  12. data/lib/rails_doorman/rule.rb +59 -0
  13. data/lib/rails_doorman.rb +111 -0
  14. data/rails/init.rb +1 -0
  15. data/spec/fixtures/app/README +243 -0
  16. data/spec/fixtures/app/Rakefile +10 -0
  17. data/spec/fixtures/app/app/controllers/access_control_by_host_controller.rb +5 -0
  18. data/spec/fixtures/app/app/controllers/access_control_by_user_agent_controller.rb +4 -0
  19. data/spec/fixtures/app/app/controllers/allow_all_by_default_controller.rb +2 -0
  20. data/spec/fixtures/app/app/controllers/allowed_and_denied_roles_controller.rb +4 -0
  21. data/spec/fixtures/app/app/controllers/allowed_and_denied_users_controller.rb +4 -0
  22. data/spec/fixtures/app/app/controllers/allowed_role_controller.rb +3 -0
  23. data/spec/fixtures/app/app/controllers/allowed_role_with_only_controller.rb +3 -0
  24. data/spec/fixtures/app/app/controllers/allowed_user_controller.rb +3 -0
  25. data/spec/fixtures/app/app/controllers/application_controller.rb +36 -0
  26. data/spec/fixtures/app/app/controllers/denied_role_controller.rb +3 -0
  27. data/spec/fixtures/app/app/controllers/denied_user_controller.rb +4 -0
  28. data/spec/fixtures/app/app/controllers/deny_all_controller.rb +3 -0
  29. data/spec/fixtures/app/app/controllers/explicitly_allow_all_controller.rb +3 -0
  30. data/spec/fixtures/app/app/controllers/test_controller.rb +4 -0
  31. data/spec/fixtures/app/app/controllers/view_helpers_controller.rb +4 -0
  32. data/spec/fixtures/app/app/helpers/application_helper.rb +3 -0
  33. data/spec/fixtures/app/app/models/user.rb +7 -0
  34. data/spec/fixtures/app/app/views/layouts/application.html.erb +8 -0
  35. data/spec/fixtures/app/app/views/view_helpers/allow_via_role.html.erb +3 -0
  36. data/spec/fixtures/app/app/views/view_helpers/deny_via_role.html.erb +3 -0
  37. data/spec/fixtures/app/config/boot.rb +110 -0
  38. data/spec/fixtures/app/config/environment.rb +41 -0
  39. data/spec/fixtures/app/config/environments/development.rb +0 -0
  40. data/spec/fixtures/app/config/environments/production.rb +0 -0
  41. data/spec/fixtures/app/config/environments/test.rb +31 -0
  42. data/spec/fixtures/app/config/initializers/backtrace_silencers.rb +7 -0
  43. data/spec/fixtures/app/config/initializers/inflections.rb +10 -0
  44. data/spec/fixtures/app/config/initializers/mime_types.rb +5 -0
  45. data/spec/fixtures/app/config/initializers/new_rails_defaults.rb +19 -0
  46. data/spec/fixtures/app/config/initializers/session_store.rb +15 -0
  47. data/spec/fixtures/app/config/locales/en.yml +5 -0
  48. data/spec/fixtures/app/config/routes.rb +43 -0
  49. data/spec/fixtures/app/db/foo.txt +0 -0
  50. data/spec/fixtures/app/doc/README_FOR_APP +2 -0
  51. data/spec/fixtures/app/public/404.html +30 -0
  52. data/spec/fixtures/app/public/422.html +30 -0
  53. data/spec/fixtures/app/public/500.html +30 -0
  54. data/spec/fixtures/app/public/favicon.ico +0 -0
  55. data/spec/fixtures/app/public/images/rails.png +0 -0
  56. data/spec/fixtures/app/public/javascripts/application.js +2 -0
  57. data/spec/fixtures/app/public/javascripts/controls.js +963 -0
  58. data/spec/fixtures/app/public/javascripts/dragdrop.js +973 -0
  59. data/spec/fixtures/app/public/javascripts/effects.js +1128 -0
  60. data/spec/fixtures/app/public/javascripts/prototype.js +4320 -0
  61. data/spec/fixtures/app/public/robots.txt +5 -0
  62. data/spec/fixtures/app/script/about +4 -0
  63. data/spec/fixtures/app/script/console +3 -0
  64. data/spec/fixtures/app/script/dbconsole +3 -0
  65. data/spec/fixtures/app/script/destroy +3 -0
  66. data/spec/fixtures/app/script/generate +3 -0
  67. data/spec/fixtures/app/script/performance/benchmarker +3 -0
  68. data/spec/fixtures/app/script/performance/profiler +3 -0
  69. data/spec/fixtures/app/script/plugin +3 -0
  70. data/spec/fixtures/app/script/runner +3 -0
  71. data/spec/fixtures/app/script/server +3 -0
  72. data/spec/fixtures/app/test/performance/browsing_test.rb +9 -0
  73. data/spec/fixtures/app/test/test_helper.rb +38 -0
  74. data/spec/fixtures/app/vendor/plugins/doorman/init.rb +1 -0
  75. data/spec/rails_doorman/class_methods_spec.rb +49 -0
  76. data/spec/rails_doorman/rule_spec.rb +120 -0
  77. data/spec/spec_helper.rb +15 -0
  78. metadata +176 -0
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2008 Michael D. Ivey <ivey@gweezlebur.com>
2
+ Copyright (c) 2009 Jeremy Burks <jeremy.burks@gmail.com>
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,88 @@
1
+ == rails_doorman
2
+
3
+ +rails_doorman+ is an authorization plugin for Ruby on Rails
4
+ applications.
5
+
6
+ This code was orignally written by Michael D. Ivey for Merb.
7
+
8
+ == Resources
9
+
10
+ Development
11
+
12
+ * http://github.com/jrun/rails_doorman
13
+
14
+ Source
15
+
16
+ * git://github.com/jrun/rails_doorman.git
17
+
18
+ Bugs
19
+
20
+ * http://github.com/jrun/rails_doorman/issues
21
+
22
+ == Usage
23
+
24
+ === Controllers
25
+
26
+ class Admin::ArticlesController < AdminController
27
+ allow :role => :admin
28
+ end
29
+
30
+ allow :role => :admin # current_user.has_role?(:admin)
31
+ deny :role => :troll
32
+ allow :role => :admin, :exclude => :show
33
+ allow :role => :troll, :only => :index
34
+
35
+ +rails_doorman+ supports more than roles.
36
+
37
+ allow :user => :nancy # current_user.login.to_sym == 'nancy'.to_sym
38
+ allow :host => 'allowed.example.org' # request.host =~ Regexp.new('allowed.example.org')
39
+ deny :host => 'denied.example.org'
40
+ deny :user_agent => /MSIE/ # request.user_agent =~ Regexp.new(/MSIE/)
41
+
42
+ === Views
43
+
44
+ All rules can be used in views.
45
+
46
+ <% allow(:role => :admin) do %>
47
+ <h1>Allowed</h1>
48
+ <% end %>
49
+
50
+ <% deny(:role => :troll) do %>
51
+ <h1>Allowed</h1>
52
+ <% end %>
53
+
54
+ === Unauthorized
55
+
56
+ When a rule fails a Doorman::Unauthorized error is raised. The error
57
+ can be caught in ApplicationController#rescue_action_in_public.
58
+
59
+ class ApplicationController < ActionController::Base
60
+ private
61
+ def rescue_action_in_public(exception)
62
+ case exception
63
+ when Doorman::InvalidRule
64
+ render :text => 'Invalid Rule', :status => '500 Internal Server Error'
65
+ when Doorman::Unauthorized
66
+ render :text => 'Unauthorized', :status => '401 Unauthorized'
67
+ else
68
+ super(exception)
69
+ end
70
+ end
71
+ end
72
+
73
+ == TODO
74
+
75
+ === Rule inheritance
76
+
77
+ Controller subclasses do not inherit its parents rules. This is more
78
+ like a bug than a todo.
79
+
80
+ === More configurable
81
+
82
+ +rails_doorman+ should be configurable. For example, the :user rule
83
+ uses +login+ for comparison. Often times +username+ is used instead of
84
+ +login+.
85
+
86
+ This type of configuration is partially implementd but it is half
87
+ baked and not tested.
88
+
data/Rakefile ADDED
@@ -0,0 +1,57 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require 'spec/rake/spectask'
4
+
5
+ GEM_NAME = "rails_doorman"
6
+ GEM_VERSION = "0.0.1"
7
+ AUTHOR = "Jeremy Burks"
8
+ EMAIL = "jeremy.burks@gmail.com"
9
+ HOMEPAGE = "http://github.com/jrun/rails_doorman/"
10
+ SUMMARY = "Rails plugin that provides an allow/deny DSL for controlling access"
11
+
12
+ spec = Gem::Specification.new do |s|
13
+ s.name = GEM_NAME
14
+ s.version = GEM_VERSION
15
+ s.platform = Gem::Platform::RUBY
16
+ s.has_rdoc = false
17
+ s.summary = SUMMARY
18
+ s.description = s.summary
19
+ s.author = AUTHOR
20
+ s.email = EMAIL
21
+ s.homepage = HOMEPAGE
22
+ s.add_dependency('rails', '>= 2.3.2')
23
+ s.require_path = 'lib'
24
+ s.files = %w(LICENSE README.rdoc Rakefile) + Dir.glob("{rails,lib,spec,features}/**/*")
25
+ end
26
+
27
+ Rake::GemPackageTask.new(spec) do |pkg|
28
+ pkg.gem_spec = spec
29
+ end
30
+
31
+ namespace :gem do
32
+ desc "Repackage, uninstall and install gem"
33
+ task :refresh do
34
+ Rake::Task[:spec].invoke
35
+ Rake::Task[:repackage].invoke
36
+ end
37
+ end
38
+
39
+ desc "Create a gemspec file"
40
+ task :gemspec do
41
+ File.open("#{GEM_NAME}.gemspec", "w") do |file|
42
+ file.puts spec.to_ruby
43
+ end
44
+ end
45
+
46
+ desc "Run the examples"
47
+ Spec::Rake::SpecTask.new do |t|
48
+ t.spec_files = ["spec/**/*_spec.rb"]
49
+ t.spec_opts = %w[--color --format specdoc --diff]
50
+ end
51
+
52
+ require 'cucumber/rake/task'
53
+ Cucumber::Rake::Task.new(:features) do |t|
54
+ t.cucumber_opts = "--format pretty"
55
+ end
56
+
57
+ task :default => [:spec, :features]
@@ -0,0 +1,99 @@
1
+ Feature: Manage Access
2
+ In order to
3
+ As a
4
+ I want to
5
+
6
+ Scenario: Allow all by default
7
+ When I go to /allow_all_by_default
8
+ Then I should be authorized
9
+
10
+ Scenario: Explicitly allow all
11
+ When I go to /explicitly_allow_all
12
+ Then I should be authorized
13
+
14
+ Scenario: Unauthorized when explicitly denying all
15
+ When I go to /deny_all
16
+ Then I should not be authorized
17
+
18
+ Scenario: Authorized when the user is allowed
19
+ Given I am Nancy
20
+ When I go to /allowed_user
21
+ Then I should be authorized
22
+
23
+ Scenario: Unauthorized when the user is not allowed
24
+ Given I am Jackie Boy
25
+ When I go to /allowed_user
26
+ Then I should not be authorized
27
+
28
+ Scenario: Authorized when the user belongs to the allowed rule
29
+ Given I have the role admin
30
+ When I go to /allowed_role
31
+ Then I should be authorized
32
+
33
+ Scenario: Unauthorized when the user does not belong to the rule
34
+ Given I have the role not_admin
35
+ When I go to /allowed_role
36
+ Then I should not be authorized
37
+
38
+ Scenario: Authorized when the user does not belong to the denied role
39
+ When I go to /denied_role
40
+ Then I should be authorized
41
+
42
+ Scenario: Unauthorized when the user belongs to the denied role
43
+ Given I have the role troll
44
+ When I go to /denied_role
45
+ Then I should not be authorized
46
+
47
+ Scenario: Authorized when the user belongs to an allowed role
48
+ Given I have the role admin
49
+ When I go to /allowed_and_denied_roles
50
+ Then I should be authorized
51
+
52
+ Scenario: Unauthorized when the user belongs to the denied role
53
+ Given I have the role troll
54
+ When I go to /allowed_and_denied_roles
55
+ Then I should not be authorized
56
+
57
+ Scenario: Unauthorized when the user does not belong to the role
58
+ Given I have no roles
59
+ When I go to /allowed_role_with_only/show
60
+ Then I should not be authorized
61
+
62
+ Scenario: Authorized when he user belongs to the role
63
+ Given I have the role admin
64
+ When I go to /allowed_role_with_only/show
65
+ Then I should be authorized
66
+ Given I have no roles
67
+ When I go to /allowed_role_with_only
68
+ Then I should be authorized
69
+
70
+ Scenario: Authorized when the rule does not apply to the action
71
+ Given I have the role admin
72
+ When I go to /allowed_role_with_only
73
+ Then I should be authorized
74
+
75
+ Scenario: Unauthorized when the host is not explicitly allowed
76
+ When I go to /access_control_by_host
77
+ Then I should not be authorized
78
+
79
+ Scenario: Unauthorized when authorizing via role and there is not acurrent user
80
+ When There is no current user
81
+ And I go to /allowed_role
82
+ Then I should not be authorized
83
+
84
+
85
+ Scenario: View Helpers - Allow
86
+ Given I have the role admin
87
+ When I go to /view_helpers/allow_via_role
88
+ Then I should see "Allowed"
89
+ Given I have no roles
90
+ When I go to /view_helpers/allow_via_role
91
+ Then I should not see "Allowed"
92
+
93
+ Scenario: View Helpers - Deny
94
+ Given I have no roles
95
+ When I go to /view_helpers/deny_via_role
96
+ Then I should see "Allowed"
97
+ Given I have the role troll
98
+ When I go to /view_helpers/deny_via_role
99
+ Then I should not see "Allowed"
@@ -0,0 +1,25 @@
1
+ Given /^I am (.*)$/ do |name|
2
+ ApplicationController.reset_current_user
3
+ ApplicationController.current_user.login = name.downcase
4
+ end
5
+
6
+ Given /^I have no roles$/ do
7
+ ApplicationController.reset_current_user
8
+ end
9
+
10
+ Given /^I have the role (.*)$/ do |role|
11
+ Given "I have no roles"
12
+ ApplicationController.current_user.roles << role.to_sym
13
+ end
14
+
15
+ Then /^I should be authorized$/ do
16
+ response.should be_authorized
17
+ end
18
+
19
+ Then /^I should not be authorized$/ do
20
+ response.should be_unauthorized
21
+ end
22
+
23
+ When /^There is no current user$/ do
24
+ ApplicationController.nil_current_user
25
+ end
@@ -0,0 +1,115 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "paths"))
2
+
3
+ # Commonly used webrat steps
4
+ # http://github.com/brynary/webrat
5
+
6
+ Given /^I am on (.+)$/ do |page_name|
7
+ visit path_to(page_name)
8
+ end
9
+
10
+ When /^I go to (.+)$/ do |page_name|
11
+ visit path_to(page_name)
12
+ end
13
+
14
+ When /^I press "([^\"]*)"$/ do |button|
15
+ click_button(button)
16
+ end
17
+
18
+ When /^I follow "([^\"]*)"$/ do |link|
19
+ click_link(link)
20
+ end
21
+
22
+ When /^I fill in "([^\"]*)" with "([^\"]*)"$/ do |field, value|
23
+ fill_in(field, :with => value)
24
+ end
25
+
26
+ When /^I select "([^\"]*)" from "([^\"]*)"$/ do |value, field|
27
+ select(value, :from => field)
28
+ end
29
+
30
+ # Use this step in conjunction with Rail's datetime_select helper. For example:
31
+ # When I select "December 25, 2008 10:00" as the date and time
32
+ When /^I select "([^\"]*)" as the date and time$/ do |time|
33
+ select_datetime(time)
34
+ end
35
+
36
+ # Use this step when using multiple datetime_select helpers on a page or
37
+ # you want to specify which datetime to select. Given the following view:
38
+ # <%= f.label :preferred %><br />
39
+ # <%= f.datetime_select :preferred %>
40
+ # <%= f.label :alternative %><br />
41
+ # <%= f.datetime_select :alternative %>
42
+ # The following steps would fill out the form:
43
+ # When I select "November 23, 2004 11:20" as the "Preferred" data and time
44
+ # And I select "November 25, 2004 10:30" as the "Alternative" data and time
45
+ When /^I select "([^\"]*)" as the "([^\"]*)" date and time$/ do |datetime, datetime_label|
46
+ select_datetime(datetime, :from => datetime_label)
47
+ end
48
+
49
+ # Use this step in conjunction with Rail's time_select helper. For example:
50
+ # When I select "2:20PM" as the time
51
+ # Note: Rail's default time helper provides 24-hour time-- not 12 hour time. Webrat
52
+ # will convert the 2:20PM to 14:20 and then select it.
53
+ When /^I select "([^\"]*)" as the time$/ do |time|
54
+ select_time(time)
55
+ end
56
+
57
+ # Use this step when using multiple time_select helpers on a page or you want to
58
+ # specify the name of the time on the form. For example:
59
+ # When I select "7:30AM" as the "Gym" time
60
+ When /^I select "([^\"]*)" as the "([^\"]*)" time$/ do |time, time_label|
61
+ select_time(time, :from => time_label)
62
+ end
63
+
64
+ # Use this step in conjunction with Rail's date_select helper. For example:
65
+ # When I select "February 20, 1981" as the date
66
+ When /^I select "([^\"]*)" as the date$/ do |date|
67
+ select_date(date)
68
+ end
69
+
70
+ # Use this step when using multiple date_select helpers on one page or
71
+ # you want to specify the name of the date on the form. For example:
72
+ # When I select "April 26, 1982" as the "Date of Birth" date
73
+ When /^I select "([^\"]*)" as the "([^\"]*)" date$/ do |date, date_label|
74
+ select_date(date, :from => date_label)
75
+ end
76
+
77
+ When /^I check "([^\"]*)"$/ do |field|
78
+ check(field)
79
+ end
80
+
81
+ When /^I uncheck "([^\"]*)"$/ do |field|
82
+ uncheck(field)
83
+ end
84
+
85
+ When /^I choose "([^\"]*)"$/ do |field|
86
+ choose(field)
87
+ end
88
+
89
+ When /^I attach the file at "([^\"]*)" to "([^\"]*)"$/ do |path, field|
90
+ attach_file(field, path)
91
+ end
92
+
93
+ Then /^I should see "([^\"]*)"$/ do |text|
94
+ response.should contain(text)
95
+ end
96
+
97
+ Then /^I should not see "([^\"]*)"$/ do |text|
98
+ response.should_not contain(text)
99
+ end
100
+
101
+ Then /^the "([^\"]*)" field should contain "([^\"]*)"$/ do |field, value|
102
+ field_labeled(field).value.should =~ /#{value}/
103
+ end
104
+
105
+ Then /^the "([^\"]*)" field should not contain "([^\"]*)"$/ do |field, value|
106
+ field_labeled(field).value.should_not =~ /#{value}/
107
+ end
108
+
109
+ Then /^the "([^\"]*)" checkbox should be checked$/ do |label|
110
+ field_labeled(label).should be_checked
111
+ end
112
+
113
+ Then /^I should be on (.+)$/ do |page_name|
114
+ URI.parse(current_url).path.should == path_to(page_name)
115
+ end
@@ -0,0 +1,29 @@
1
+ class BeAuthorized
2
+ def matches?(response)
3
+ @response = response
4
+ @response.status == "200 OK" && @response.body == 'Allowed Access'
5
+ end
6
+
7
+ # ==== Returns
8
+ # String:: The failure message.
9
+ def failure_message
10
+ <<-EOS
11
+ expected the response from #{@response.request.url}
12
+ to have the status 200 OK and body 'Allowed Access'"
13
+ but the status is #{@response.status} and the body is '#{@response.body}'"
14
+ EOS
15
+ end
16
+
17
+ # ==== Returns
18
+ # String:: The failure message to be displayed in negative matches.
19
+ def negative_failure_message
20
+ <<-EOS
21
+ expected the response from #{@response.request.url}
22
+ not to have the status 200 Ok and body 'Allowed Access'"
23
+ EOS
24
+ end
25
+ end
26
+
27
+ def be_authorized
28
+ BeAuthorized.new
29
+ end
@@ -0,0 +1,13 @@
1
+ ENV["RAILS_ENV"] ||= "test"
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec/fixtures/app/config/environment')
4
+ require 'cucumber/rails/world'
5
+ require 'cucumber/rails/rspec'
6
+ require 'cucumber/formatter/unicode'
7
+
8
+ require 'webrat'
9
+ require 'webrat/core/matchers'
10
+
11
+ Webrat.configure do |config|
12
+ config.mode = :rails
13
+ end
@@ -0,0 +1,19 @@
1
+ module NavigationHelpers
2
+ # Maps a static name to a static route.
3
+ #
4
+ # This method is *not* designed to map from a dynamic name to a
5
+ # dynamic route like <tt>post_comments_path(post)</tt>. For dynamic
6
+ # routes like this you should *not* rely on #path_to, but write
7
+ # your own step definitions instead. Example:
8
+ #
9
+ # Given /I am on the comments page for the "(.+)" post/ |name|
10
+ # post = Post.find_by_name(name)
11
+ # visit post_comments_path(post)
12
+ # end
13
+ #
14
+ def path_to(page_name)
15
+ page_name
16
+ end
17
+ end
18
+
19
+ World(NavigationHelpers)
@@ -0,0 +1,29 @@
1
+ class BeUnauthorized
2
+ def matches?(response)
3
+ @response = response
4
+ @response.status == '401 Unauthorized' && @response.body == 'Unauthorized'
5
+ end
6
+
7
+ # ==== Returns
8
+ # String:: The failure message.
9
+ def failure_message
10
+ <<-EOS
11
+ expected the response from #{@response.request.url}
12
+ to have the status 401 Unauthorized and body 'Unauthorized'"
13
+ but the status is #{@response.status} and the body is '#{@response.body}'"
14
+ EOS
15
+ end
16
+
17
+ # ==== Returns
18
+ # String:: The failure message to be displayed in negative matches.
19
+ def negative_failure_message
20
+ <<-EOS
21
+ expected the response from #{@response.request.url}
22
+ not to have the status 200 Ok and body 'Allowed Access'"
23
+ EOS
24
+ end
25
+ end
26
+
27
+ def be_unauthorized
28
+ BeUnauthorized.new
29
+ end
@@ -0,0 +1,17 @@
1
+
2
+ module Doorman
3
+ module Helpers
4
+ def allow(*args, &blk)
5
+ _capture_within_rule_context(:allow, args, &blk)
6
+ end
7
+
8
+ def deny(*args, &blk)
9
+ _capture_within_rule_context(:deny, args, &blk)
10
+ end
11
+
12
+ def _capture_within_rule_context(type, args, &blk)
13
+ _check_rule(Doorman::Rule.from_hash(type, args.first)) ? blk.call : ""
14
+ end
15
+ private :_capture_within_rule_context
16
+ end
17
+ end
@@ -0,0 +1,59 @@
1
+
2
+ module Doorman
3
+ class InvalidRule < StandardError
4
+ def initialize(*args)
5
+ super "rails_doorman: invalid rule #{args.inspect}"
6
+ end
7
+ end
8
+
9
+ class Rule
10
+ def self.from_hash(type, opts)
11
+ rule = new(type)
12
+ h = opts.except(:only, :exclude)
13
+ if h.size > 1
14
+ raise InvalidRule.new(type, opts)
15
+ end
16
+ rule.method = h.keys.first.to_sym
17
+ unless Doorman.supported_method?(rule.method)
18
+ raise InvalidRule.new(type, opts)
19
+ end
20
+ rule.value = h.values.first
21
+ rule.limits = extract_limits(opts)
22
+ rule
23
+ end
24
+
25
+ def self.from_block(type, opts = nil, &block)
26
+ unless block.arity == 1
27
+ raise InvalidRule.new(type, opts, block)
28
+ end
29
+ opts ||= {}
30
+ rule = new(type)
31
+ rule.method = :block
32
+ rule.value = block
33
+ rule.limits = extract_limits(opts)
34
+ rule
35
+ end
36
+
37
+ def self.extract_limits(h)
38
+ h.slice(:only, :exclude).inject({}) do |limits, kv|
39
+ limits.merge!(kv.first => Array(kv.last).map {|limit| limit.to_sym })
40
+ end
41
+ end
42
+
43
+ attr_accessor :type, :method, :value, :limits
44
+
45
+ def initialize(type)
46
+ @type, @limits = type, {}
47
+ end
48
+
49
+ def deny?
50
+ type == :deny
51
+ end
52
+
53
+ def evaluate?(action_name)
54
+ (!limits.key?(:only) || limits[:only].include?(action_name.to_sym)) &&
55
+ (!limits.key?(:exclude) || !limits[:exclude].include?(action_name.to_sym))
56
+ end
57
+ end
58
+
59
+ end
@@ -0,0 +1,111 @@
1
+ require 'rails_doorman/rule'
2
+ require 'rails_doorman/helpers'
3
+
4
+ module Doorman
5
+ class Unauthorized < StandardError; end
6
+
7
+ class << self
8
+ def options
9
+ @options ||= {
10
+ :user_identifier_method => :login,
11
+ :has_role_method => :has_role?
12
+ }
13
+ end
14
+
15
+ SUPPORTED_METHODS = [:block, :host, :role, :user, :user_agent].freeze
16
+
17
+ def supported_method?(method)
18
+ SUPPORTED_METHODS.include?(method)
19
+ end
20
+
21
+ def included(base)
22
+ base.extend(ClassMethods)
23
+ base.class_eval do
24
+ include InstanceMethods
25
+ include SharedMethods
26
+ helper Doorman::SharedMethods, Doorman::Helpers
27
+ before_filter :_doorman_check_acl
28
+ end
29
+ end
30
+ end
31
+
32
+ module ClassMethods
33
+ def _doorman_list
34
+ @_doorman_list ||= []
35
+ end
36
+
37
+ def _doorman_default
38
+ @_doorman_default ||= :allow
39
+ end
40
+
41
+ def deny(*args, &block)
42
+ _add_acl(:deny, args, block)
43
+ end
44
+
45
+ def allow(*args, &block)
46
+ _add_acl(:allow, args, block)
47
+ end
48
+
49
+ def _clear_acl_list
50
+ @_doorman_list = nil
51
+ end
52
+
53
+ def _add_acl(type, args, block)
54
+ opts = args.is_a?(Array) ? args.first : args
55
+ if opts == :all
56
+ @_doorman_default = type
57
+ return true
58
+ end
59
+ if block
60
+ _doorman_list << Rule.from_block(type, opts, &block)
61
+ else
62
+ _doorman_list << Rule.from_hash(type, opts)
63
+ end
64
+ end
65
+ end
66
+
67
+ module InstanceMethods
68
+ private
69
+ def _doorman_check_acl
70
+ allowed = false
71
+ self.class._doorman_list.each do |rule|
72
+ next unless rule.evaluate?(self.action_name)
73
+ if _check_rule(rule)
74
+ allowed = true
75
+ else
76
+ raise Unauthorized
77
+ end
78
+ end
79
+ if self.class._doorman_default == :deny && !allowed
80
+ raise Unauthorized
81
+ end
82
+ end
83
+ end
84
+
85
+ module SharedMethods
86
+ # self can either be in a controller or view context.
87
+ private
88
+ def _check_rule(rule)
89
+ match = case rule.method
90
+ when :block
91
+ rule.value.call(self)
92
+ when :host
93
+ request.host =~ Regexp.new(rule.value)
94
+ when :role
95
+ current_user && current_user.send(Doorman.options[:has_role_method], rule.value)
96
+ when :user
97
+ current_user && current_user.send(Doorman.options[:user_identifier_method]).to_sym == rule.value.to_sym
98
+ when :user_agent
99
+ request.user_agent =~ Regexp.new(rule.value)
100
+ else
101
+ false
102
+ end
103
+ rule.deny? ? !match : match
104
+ end
105
+ end
106
+
107
+ end
108
+
109
+ ActionController::Base.class_eval do
110
+ include Doorman
111
+ end
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'rails_doorman'