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.
- data/LICENSE +21 -0
- data/README.rdoc +88 -0
- data/Rakefile +57 -0
- data/features/doorman.feature +99 -0
- data/features/step_definitions/common_steps.rb +25 -0
- data/features/step_definitions/webrat_steps.rb +115 -0
- data/features/support/authorized_matcher.rb +29 -0
- data/features/support/env.rb +13 -0
- data/features/support/paths.rb +19 -0
- data/features/support/unauthorized_matcher.rb +29 -0
- data/lib/rails_doorman/helpers.rb +17 -0
- data/lib/rails_doorman/rule.rb +59 -0
- data/lib/rails_doorman.rb +111 -0
- data/rails/init.rb +1 -0
- data/spec/fixtures/app/README +243 -0
- data/spec/fixtures/app/Rakefile +10 -0
- data/spec/fixtures/app/app/controllers/access_control_by_host_controller.rb +5 -0
- data/spec/fixtures/app/app/controllers/access_control_by_user_agent_controller.rb +4 -0
- data/spec/fixtures/app/app/controllers/allow_all_by_default_controller.rb +2 -0
- data/spec/fixtures/app/app/controllers/allowed_and_denied_roles_controller.rb +4 -0
- data/spec/fixtures/app/app/controllers/allowed_and_denied_users_controller.rb +4 -0
- data/spec/fixtures/app/app/controllers/allowed_role_controller.rb +3 -0
- data/spec/fixtures/app/app/controllers/allowed_role_with_only_controller.rb +3 -0
- data/spec/fixtures/app/app/controllers/allowed_user_controller.rb +3 -0
- data/spec/fixtures/app/app/controllers/application_controller.rb +36 -0
- data/spec/fixtures/app/app/controllers/denied_role_controller.rb +3 -0
- data/spec/fixtures/app/app/controllers/denied_user_controller.rb +4 -0
- data/spec/fixtures/app/app/controllers/deny_all_controller.rb +3 -0
- data/spec/fixtures/app/app/controllers/explicitly_allow_all_controller.rb +3 -0
- data/spec/fixtures/app/app/controllers/test_controller.rb +4 -0
- data/spec/fixtures/app/app/controllers/view_helpers_controller.rb +4 -0
- data/spec/fixtures/app/app/helpers/application_helper.rb +3 -0
- data/spec/fixtures/app/app/models/user.rb +7 -0
- data/spec/fixtures/app/app/views/layouts/application.html.erb +8 -0
- data/spec/fixtures/app/app/views/view_helpers/allow_via_role.html.erb +3 -0
- data/spec/fixtures/app/app/views/view_helpers/deny_via_role.html.erb +3 -0
- data/spec/fixtures/app/config/boot.rb +110 -0
- data/spec/fixtures/app/config/environment.rb +41 -0
- data/spec/fixtures/app/config/environments/development.rb +0 -0
- data/spec/fixtures/app/config/environments/production.rb +0 -0
- data/spec/fixtures/app/config/environments/test.rb +31 -0
- data/spec/fixtures/app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/fixtures/app/config/initializers/inflections.rb +10 -0
- data/spec/fixtures/app/config/initializers/mime_types.rb +5 -0
- data/spec/fixtures/app/config/initializers/new_rails_defaults.rb +19 -0
- data/spec/fixtures/app/config/initializers/session_store.rb +15 -0
- data/spec/fixtures/app/config/locales/en.yml +5 -0
- data/spec/fixtures/app/config/routes.rb +43 -0
- data/spec/fixtures/app/db/foo.txt +0 -0
- data/spec/fixtures/app/doc/README_FOR_APP +2 -0
- data/spec/fixtures/app/public/404.html +30 -0
- data/spec/fixtures/app/public/422.html +30 -0
- data/spec/fixtures/app/public/500.html +30 -0
- data/spec/fixtures/app/public/favicon.ico +0 -0
- data/spec/fixtures/app/public/images/rails.png +0 -0
- data/spec/fixtures/app/public/javascripts/application.js +2 -0
- data/spec/fixtures/app/public/javascripts/controls.js +963 -0
- data/spec/fixtures/app/public/javascripts/dragdrop.js +973 -0
- data/spec/fixtures/app/public/javascripts/effects.js +1128 -0
- data/spec/fixtures/app/public/javascripts/prototype.js +4320 -0
- data/spec/fixtures/app/public/robots.txt +5 -0
- data/spec/fixtures/app/script/about +4 -0
- data/spec/fixtures/app/script/console +3 -0
- data/spec/fixtures/app/script/dbconsole +3 -0
- data/spec/fixtures/app/script/destroy +3 -0
- data/spec/fixtures/app/script/generate +3 -0
- data/spec/fixtures/app/script/performance/benchmarker +3 -0
- data/spec/fixtures/app/script/performance/profiler +3 -0
- data/spec/fixtures/app/script/plugin +3 -0
- data/spec/fixtures/app/script/runner +3 -0
- data/spec/fixtures/app/script/server +3 -0
- data/spec/fixtures/app/test/performance/browsing_test.rb +9 -0
- data/spec/fixtures/app/test/test_helper.rb +38 -0
- data/spec/fixtures/app/vendor/plugins/doorman/init.rb +1 -0
- data/spec/rails_doorman/class_methods_spec.rb +49 -0
- data/spec/rails_doorman/rule_spec.rb +120 -0
- data/spec/spec_helper.rb +15 -0
- 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'
|