jrun-merb_doorman 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Michael D. Ivey <ivey@gweezlebur.com>
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 ADDED
@@ -0,0 +1,29 @@
1
+ merb_doorman
2
+ ============
3
+
4
+ *** Want to contribute? Some of the code isn't spec'd yet. ***
5
+
6
+
7
+ Merb plugin that provides an allow/deny DSL for controlling access
8
+
9
+
10
+ # mostly open:
11
+ # there is an implicit "allow :all" as the last rule
12
+ # rules continue to match until an allow is found, or we run out
13
+ # of rules
14
+
15
+ deny :host => "209.34.*"
16
+ deny :user => "bill" # calls current_user.login, but this is configurable
17
+ deny :user_agent => /MSIE/
18
+ deny {|c| c.params["arbitrary"] == "expressions"}
19
+
20
+
21
+ # mostly closed:
22
+ deny :all # removes implicit final allow :all
23
+ allow :host => "*.example.com"
24
+ allow :time => "8am-5pm" # not implemented yet
25
+
26
+ # store a block for repeated usage
27
+ Merb::Access.add_block :admin, {|c| c.current_user.admin?}
28
+
29
+ allow :admin
data/Rakefile ADDED
@@ -0,0 +1,67 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require 'spec/rake/spectask'
4
+
5
+ require 'merb-core'
6
+ require 'merb-core/tasks/merb'
7
+
8
+ GEM_NAME = "merb_doorman"
9
+ GEM_VERSION = "0.0.1"
10
+ AUTHOR = "Michael D. Ivey"
11
+ EMAIL = "ivey@gweezlebur.com"
12
+ HOMEPAGE = "http://github.com/ivey/merb_doorman/"
13
+ SUMMARY = "Merb plugin that provides an allow/deny DSL for controlling access"
14
+
15
+ spec = Gem::Specification.new do |s|
16
+ s.rubyforge_project = 'merb'
17
+ s.name = GEM_NAME
18
+ s.version = GEM_VERSION
19
+ s.platform = Gem::Platform::RUBY
20
+ s.has_rdoc = true
21
+ s.extra_rdoc_files = ["README", "LICENSE", 'TODO']
22
+ s.summary = SUMMARY
23
+ s.description = s.summary
24
+ s.author = AUTHOR
25
+ s.email = EMAIL
26
+ s.homepage = HOMEPAGE
27
+ s.add_dependency('merb-core', '>= 1.0')
28
+ s.require_path = 'lib'
29
+ s.files = %w(LICENSE README Rakefile TODO) + Dir.glob("{lib,spec}/**/*")
30
+ end
31
+
32
+ Rake::GemPackageTask.new(spec) do |pkg|
33
+ pkg.gem_spec = spec
34
+ end
35
+
36
+ desc "install the plugin as a gem"
37
+ task :install do
38
+ Merb::RakeHelper.install(GEM_NAME, :version => GEM_VERSION)
39
+ end
40
+
41
+ desc "Uninstall the gem"
42
+ task :uninstall do
43
+ Merb::RakeHelper.uninstall(GEM_NAME, :version => GEM_VERSION)
44
+ end
45
+
46
+ namespace :gem do
47
+ desc "Repackage, uninstall and install gem"
48
+ task :refresh do
49
+ Rake::Task[:spec].invoke
50
+ Rake::Task[:repackage].invoke
51
+ Rake::Task[:uninstall].invoke
52
+ Rake::Task[:install].invoke
53
+ end
54
+ end
55
+
56
+ desc "Create a gemspec file"
57
+ task :gemspec do
58
+ File.open("#{GEM_NAME}.gemspec", "w") do |file|
59
+ file.puts spec.to_ruby
60
+ end
61
+ end
62
+
63
+ desc "Run the examples"
64
+ Spec::Rake::SpecTask.new do |t|
65
+ t.spec_files = ["spec/**/*_spec.rb"]
66
+ t.spec_opts = %w[--color --format specdoc --diff]
67
+ end
data/TODO ADDED
@@ -0,0 +1,3 @@
1
+ * Multiple roles
2
+ * :only, :exclude, :if
3
+ * Something accessible from views
@@ -0,0 +1,118 @@
1
+ require 'merb_doorman/rule'
2
+ require 'merb_doorman/helpers'
3
+
4
+ module Merb
5
+ module Plugins
6
+ module Doorman
7
+ SUPPORTED_METHODS = [:block, :host, :role, :time, :user, :user_agent]
8
+
9
+ def self.supported_method?(method)
10
+ SUPPORTED_METHODS.include?(method)
11
+ end
12
+
13
+ def self.included(base)
14
+ base.extend(ClassMethods)
15
+ base.send(:include, InstanceMethods)
16
+ base.class_eval do
17
+ before :_doorman_check_acl
18
+ end
19
+ end
20
+
21
+ module ClassMethods
22
+ def _doorman_list
23
+ @_doorman_list ||= []
24
+ end
25
+
26
+ def _doorman_default
27
+ @_doorman_default ||= :allow
28
+ end
29
+
30
+ def deny(*args, &block)
31
+ _add_acl(:deny, args, block)
32
+ end
33
+
34
+ def allow(*args, &block)
35
+ _add_acl(:allow, args, block)
36
+ end
37
+
38
+ def _clear_acl_list
39
+ @_doorman_list = nil
40
+ end
41
+
42
+ def _add_acl(type, args, block)
43
+ opts = args.is_a?(Array) ? args.first : args
44
+ if opts == :all
45
+ @_doorman_default = type
46
+ return true
47
+ end
48
+ if block
49
+ _doorman_list << Rule.from_block(type, opts, &block)
50
+ else
51
+ _doorman_list << Rule.from_hash(type, opts)
52
+ end
53
+ end
54
+
55
+ def _doorman_user_block
56
+ Merb::Plugins.config[:merb_doorman][:user_block]
57
+ end
58
+
59
+ def _doorman_role_block
60
+ Merb::Plugins.config[:merb_doorman][:role_block]
61
+ end
62
+ end
63
+
64
+ module InstanceMethods
65
+ private
66
+ def _doorman_check_acl
67
+ allowed = false
68
+ self.class._doorman_list.each do |rule|
69
+ next unless rule.evaluate?(self.action_name)
70
+ if _check_rule(rule)
71
+ allowed = true
72
+ else
73
+ raise ::Merb::ControllerExceptions::Unauthorized
74
+ end
75
+ end
76
+ if self.class._doorman_default == :deny && !allowed
77
+ raise ::Merb::ControllerExceptions::Unauthorized
78
+ end
79
+ end
80
+
81
+ def _check_rule(rule)
82
+ match = case rule.method
83
+ when :block
84
+ rule.value.call(self)
85
+ when :host
86
+ request.host =~ Regexp.new(rule.value)
87
+ when :role
88
+ self.class._doorman_role_block.call(self, rule.value)
89
+ when :time
90
+ false #not implemented
91
+ when :user
92
+ self.class._doorman_user_block.call(self, rule.value)
93
+ when :user_agent
94
+ request.user_agent =~ Regexp.new(rule.value)
95
+ else
96
+ false
97
+ end
98
+ rule.deny? ? !match : match
99
+ end
100
+ end
101
+
102
+ end
103
+ end
104
+ end
105
+
106
+ if defined?(Merb::Plugins)
107
+ Merb::Plugins.config[:merb_doorman][:user_block] = proc do |c, login|
108
+ c.respond_to?(:current_user) &&
109
+ c.current_user.login == login.to_s
110
+ end
111
+ Merb::Plugins.config[:merb_doorman][:role_block] = proc do |c, role|
112
+ c.respond_to?(:current_user) &&
113
+ c.current_user.respond_to?(:has_role?) &&
114
+ c.current_user.has_role?(role)
115
+ end
116
+ Merb::Controller.send(:include, Merb::Plugins::Doorman)
117
+ Merb::GlobalHelpers.send(:include, Merb::Plugins::Doorman::Helpers)
118
+ end
@@ -0,0 +1,22 @@
1
+ module Merb
2
+ module Plugins
3
+ module Doorman
4
+
5
+ module Helpers
6
+ def allow(*args, &blk)
7
+ _capture_within_rule_context(:allow, args, &blk)
8
+ end
9
+
10
+ def deny(*args, &blk)
11
+ _capture_within_rule_context(:deny, args, &blk)
12
+ end
13
+
14
+ def _capture_within_rule_context(type, args, &blk)
15
+ _check_rule(Doorman::Rule.from_hash(type, args.first)) ? capture(&blk) : ""
16
+ end
17
+ private :_capture_within_rule_context
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,6 @@
1
+ namespace :merb_doorman do
2
+ desc "Do something for merb_doorman"
3
+ task :default do
4
+ puts "merb_doorman doesn't do anything"
5
+ end
6
+ end
@@ -0,0 +1,61 @@
1
+ module Merb
2
+ module Plugins
3
+ module Doorman
4
+ class InvalidRule < StandardError
5
+ def initialize(*args)
6
+ super "merb_doorman: invalid rule #{args.inspect}"
7
+ end
8
+ end
9
+
10
+ class Rule
11
+ def self.from_hash(type, opts)
12
+ rule = new(type)
13
+
14
+ h = opts.except(:only, :exclude)
15
+ raise InvalidRule.new(type, opts) if h.size > 1
16
+
17
+ rule.method = h.keys.first.to_sym
18
+ unless Doorman.supported_method?(rule.method)
19
+ raise InvalidRule.new(type, opts)
20
+ end
21
+ rule.value = h.values.first
22
+ rule.limits = extract_limits(opts)
23
+ rule
24
+ end
25
+
26
+ def self.from_block(type, opts = nil, &block)
27
+ raise InvalidRule.new(type, opts, block) unless block.arity == 1
28
+ opts ||= {}
29
+
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.only(:only, :exclude).inject({}) do |limits, kv|
39
+ limits.merge!(kv.first => Array(kv.last))
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)) &&
55
+ (!limits.key?(:exclude) || !limits[:exclude].include?(action_name))
56
+ end
57
+ end
58
+
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,16 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ class AllowAllByDefault < Merb::Plugins::Doorman::TestController; end
4
+
5
+ class ExplicitAllowAll < Merb::Plugins::Doorman::TestController
6
+ allow :all
7
+ end
8
+
9
+ [AllowAllByDefault, ExplicitAllowAll].each do |controller|
10
+ describe controller, 'request' do
11
+ it "should be authorized when explicitly denying all" do
12
+ response = request(url(:controller => controller.controller_name))
13
+ response.should be_authorized
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,46 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Merb::Plugins::Doorman::ClassMethods do
4
+ include Merb::Plugins::Doorman::ClassMethods
5
+
6
+ before(:each) do
7
+ _clear_acl_list
8
+ end
9
+
10
+ it "should store the ACL" do
11
+ _doorman_list.should be_an_instance_of(Array)
12
+ end
13
+
14
+ it "should have a deny method" do
15
+ lambda { deny :all }.should_not raise_error
16
+ end
17
+
18
+ it "should store a deny entry on the ACL" do
19
+ s = _doorman_list.size
20
+ deny :host => "192.168.*"
21
+ _doorman_list.should have(s + 1).elements
22
+ end
23
+
24
+ it "should have an allow method" do
25
+ lambda { allow :all }.should_not raise_error
26
+ end
27
+
28
+ it "should store an allow entry on the ACL" do
29
+ s = _doorman_list.size
30
+ allow :user_agent => /MSIE/
31
+ _doorman_list.should have(s + 1).elements
32
+ end
33
+
34
+ it "should allow valid ACL entries" do
35
+ lambda { deny :host => "192.168.*" }.should_not raise_error
36
+ lambda { deny :user => "bill" }.should_not raise_error
37
+ lambda { deny :user_agent => /MSIE/ }.should_not raise_error
38
+ lambda { deny :time => "8am-5pm" }.should_not raise_error
39
+ lambda { deny {|c| c.foo } }.should_not raise_error
40
+ end
41
+
42
+ it "should reject invalid ACL entries" do
43
+ lambda { deny :foo => "3" }.should raise_error
44
+ lambda { deny { foo } }.should raise_error
45
+ end
46
+ end
@@ -0,0 +1,12 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ class DenyAll < Merb::Plugins::Doorman::TestController
4
+ deny :all
5
+ end
6
+
7
+ describe DenyAll, 'request' do
8
+ it "should be unauthorized when explicitly denying all" do
9
+ response = request(url(:controller => DenyAll))
10
+ response.should be_unauthorized
11
+ end
12
+ end
@@ -0,0 +1,5 @@
1
+ class Exceptions < Merb::Controller
2
+ def unauthorized
3
+ render 'Unauthorized'
4
+ end
5
+ end
@@ -0,0 +1,17 @@
1
+ module Merb
2
+ module Plugins
3
+ module Doorman
4
+
5
+ class TestController < Merb::Controller
6
+ self._template_root = File.dirname(__FILE__) / ".." / "views"
7
+
8
+ class_inheritable_accessor :current_user
9
+ self.current_user ||= User.new
10
+
11
+ def show; render('Allowed Access') end
12
+ def index; render('Allowed Access') end
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,5 @@
1
+ class TestHelpers < Merb::Plugins::Doorman::TestController
2
+ def example_setup_is_ok; render end
3
+ def allow_via_role; render end
4
+ def deny_via_role; render end
5
+ end
@@ -0,0 +1,14 @@
1
+ module Merb
2
+ module Plugins
3
+ module Doorman
4
+
5
+ class User
6
+ attr_accessor :login, :roles
7
+ def initialize; reset end
8
+ def reset; @login, @roles = '', [] end
9
+ def has_role?(role); @roles.include?(role) end
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ <%= allow(:role => :admin) do %>
2
+ <h1>Allowed</h1>
3
+ <% end =%>
@@ -0,0 +1,3 @@
1
+ <%= deny(:role => :troll) do %>
2
+ <h1>Allowed</h1>
3
+ <% end =%>
@@ -0,0 +1 @@
1
+ <h1>Good to go</h1>
data/spec/host_spec.rb ADDED
@@ -0,0 +1,49 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ class AccessControllByHost < Merb::Plugins::Doorman::TestController
4
+ allow :all
5
+ allow :host => "allowed.example.org"
6
+ deny :host => "denied.example.org"
7
+ end
8
+ describe AccessControllByHost, 'request' do
9
+ it "should be unauthorized when the host is not explicitly allowed" do
10
+ response = request(url(:controller => AccessControllByHost))
11
+ response.should be_unauthorized
12
+ end
13
+
14
+ it "should be authorized when the host is allowed" do
15
+ response = request(url(:controller => AccessControllByHost), 'SERVER_NAME' => 'allowed.example.org')
16
+ response.should be_authorized
17
+ end
18
+
19
+ it "should be unauthorized when the host is denied" do
20
+ response = request(url(:controller => AccessControllByHost), 'SERVER_NAME' => 'denied.example.org')
21
+ response.should be_unauthorized
22
+ end
23
+ end
24
+
25
+ class AccessControllDenyAllExceptHost < Merb::Plugins::Doorman::TestController
26
+ deny :all
27
+ allow :host => "allowed.example.org"
28
+ end
29
+ describe AccessControllDenyAllExceptHost, 'request' do
30
+ it "should be authorized when the host is allowed" do
31
+ response = request(url(:controller => AccessControllDenyAllExceptHost))
32
+ response.should be_unauthorized
33
+ response = request(url(:controller => AccessControllDenyAllExceptHost), 'SERVER_NAME' => 'allowed.example.org')
34
+ response.should be_authorized
35
+ end
36
+ end
37
+
38
+ class AccessControllAllowAllExceptHost < Merb::Plugins::Doorman::TestController
39
+ allow :all
40
+ deny :host => "denied.example.org"
41
+ end
42
+ describe AccessControllDenyAllExceptHost, 'request' do
43
+ it "should be authorized when the host is allowed" do
44
+ response = request(url(:controller => AccessControllAllowAllExceptHost))
45
+ response.should be_authorized
46
+ response = request(url(:controller => AccessControllAllowAllExceptHost), 'SERVER_NAME' => 'denied.example.org')
47
+ response.should be_unauthorized
48
+ end
49
+ end
@@ -0,0 +1,19 @@
1
+ class BeAuthorized
2
+ def matches?(response)
3
+ @response = response
4
+ response.status == 200 && response.body == 'Allowed Access'
5
+ end
6
+
7
+ # ==== Returns
8
+ # String:: The failure message.
9
+ def failure_message
10
+ "expected the response from #{@response.url} #{$/} to have the status 200 Ok and body 'Allowed Access'" <<
11
+ "#{$/} but the status is #{@response.status} and the body is '#{@response.body}'"
12
+ end
13
+
14
+ # ==== Returns
15
+ # String:: The failure message to be displayed in negative matches.
16
+ def negative_failure_message
17
+ "expected the response from #{@response.url} #{$/} not to have the status 200 Ok and body 'Allowed Access'"
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ class BeUnauthorized
2
+ def matches?(response)
3
+ @response = response
4
+ response.status == 401 && response.body == 'Unauthorized'
5
+ end
6
+
7
+ # ==== Returns
8
+ # String:: The failure message.
9
+ def failure_message
10
+ "expected the response from #{@response.url} #{$/} to have the status 401 Unauthorized and body 'Unauthorized'" <<
11
+ "#{$/} but the status is #{@response.status} and the body is '#{@response.body}'"
12
+ end
13
+
14
+ # ==== Returns
15
+ # String:: The failure message to be displayed in negative matches.
16
+ def negative_failure_message
17
+ "expected the response from #{@response.url} #{$/} not to have the status 200 Ok and body 'Allowed Access'"
18
+ end
19
+ end
@@ -0,0 +1,43 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Merb::Plugins::Doorman::Helpers do
4
+ it "examples should be setup successfully" do
5
+ response = request(url(:controller => TestHelpers, :action => :example_setup_is_ok))
6
+ response.body.should == "<h1>Good to go</h1>"
7
+ end
8
+
9
+ describe "#allow" do
10
+ before(:each) do
11
+ TestHelpers.current_user.reset
12
+ end
13
+
14
+ it "should render the result of the block when allowed" do
15
+ TestHelpers.current_user.roles << :admin
16
+ response = request(url(:controller => TestHelpers, :action => :allow_via_role))
17
+ response.body.should == "\n <h1>Allowed</h1>\n"
18
+ end
19
+
20
+ it "should not return the result of the block when not allowed" do
21
+ response = request(url(:controller => TestHelpers, :action => :allow_via_role))
22
+ response.body.should == ""
23
+ end
24
+ end
25
+
26
+ describe "#deny" do
27
+ before(:each) do
28
+ TestHelpers.current_user.reset
29
+ end
30
+
31
+ it "should not return the result of the block when denied" do
32
+ TestHelpers.current_user.roles << :troll
33
+ response = request(url(:controller => TestHelpers, :action => :deny_via_role))
34
+ response.body.should == ""
35
+ end
36
+
37
+ it "should render the result of the block when not denied" do
38
+ TestHelpers.current_user.roles << :anything_but_troll
39
+ response = request(url(:controller => TestHelpers, :action => :deny_via_role))
40
+ response.body.should == "\n <h1>Allowed</h1>\n"
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,89 @@
1
+ require File.dirname(__FILE__) / '../spec_helper'
2
+
3
+ Rule = Merb::Plugins::Doorman::Rule
4
+
5
+ describe Merb::Plugins::Doorman::Rule do
6
+ describe '.from_hash' do
7
+ it "should set the method using the opts first key" do
8
+ rule = Rule.from_hash(:allow, :role => :whatever)
9
+ rule.method.should == :role
10
+ end
11
+
12
+ it "should set the value using the opts first value" do
13
+ rule = Rule.from_hash(:allow, :role => :whatever)
14
+ rule.value.should == :whatever
15
+ end
16
+
17
+ it "should move :only from options to limits" do
18
+ rule = Rule.from_hash(:allow, :role => :whatever, :only => :index)
19
+ rule.limits[:only].should_not be_nil
20
+ rule.limits[:role].should be_nil
21
+ end
22
+
23
+ it "should move :exclude from options to limits" do
24
+ rule = Rule.from_hash(:allow, :role => :whatever, :exclude => :index)
25
+ rule.limits[:exclude].should_not be_nil
26
+ rule.limits[:role].should be_nil
27
+ end
28
+
29
+ it "should convert a single :only value to an Array" do
30
+ rule = Rule.from_hash(:allow, :role => :admin, :only => :index)
31
+ rule.limits[:only].should == [:index]
32
+ end
33
+
34
+ it "should convert a single :exclude value to an Array" do
35
+ rule = Rule.from_hash(:allow, :role => :admin, :exclude => :index)
36
+ rule.limits[:exclude].should == [:index]
37
+ end
38
+
39
+ it "should raise an InvalidRule error when the options hash as too many values" do
40
+ lambda do
41
+ Rule.from_hash(:allow, :role => :admin, :user => :bob)
42
+ end.should raise_error(Merb::Plugins::Doorman::InvalidRule)
43
+ end
44
+
45
+ it "should raise an InvalidRule error when the method isn't supported" do
46
+ lambda do
47
+ Rule.from_hash(:allow, :not_suppoted => :whatever)
48
+ end.should raise_error(Merb::Plugins::Doorman::InvalidRule)
49
+ end
50
+ end
51
+
52
+ describe '.from_block' do
53
+ def block
54
+ proc {|ignored| true }
55
+ end
56
+
57
+ it "should set the method to :block" do
58
+ rule = Rule.from_block(:allow, &block)
59
+ rule.method.should == :block
60
+ end
61
+
62
+ it "should set the value to the block" do
63
+ rule = Rule.from_block(:allow, &block)
64
+ rule.value.should be_instance_of(Proc)
65
+ rule.value.call(:ignored).should be_true
66
+ end
67
+
68
+ it "should move :only from options to limits" do
69
+ rule = Rule.from_block(:allow, :only => :index, &block)
70
+ rule.limits[:only].should == [:index]
71
+ end
72
+
73
+ it "should move :exclude from options to limits" do
74
+ rule = Rule.from_block(:allow, :exclude => [:index, :show], &block)
75
+ rule.limits[:exclude].should == [:index, :show]
76
+ end
77
+
78
+ it "should convert a single :only value to an Array" do
79
+ rule = Rule.from_block(:allow, :exclude => :index, &block)
80
+ rule.limits[:exclude].should == [:index]
81
+ end
82
+
83
+ it "should raise an InvalidRule error when the blocks arity is not one" do
84
+ lambda do
85
+ Rule.from_block(:allow) {}
86
+ end.should raise_error(Merb::Plugins::Doorman::InvalidRule)
87
+ end
88
+ end
89
+ end
data/spec/role_spec.rb ADDED
@@ -0,0 +1,81 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ class AllowedRole < Merb::Plugins::Doorman::TestController
4
+ allow :role => :admin
5
+ end
6
+ describe AllowedRole, 'request' do
7
+ before(:each) do
8
+ AllowedRole.current_user.reset
9
+ end
10
+
11
+ it "should be authorized when the current user belongs to the allowed role" do
12
+ AllowedRole.current_user.roles << :admin
13
+ response = request(url(:controller => AllowedRole))
14
+ response.should be_authorized
15
+ end
16
+
17
+ it "should be unauthorized when the current user does not belong to the allowed role" do
18
+ response = request(url(:controller => AllowedRole))
19
+ response.should be_unauthorized
20
+
21
+ AllowedRole.current_user.roles << :not_admin
22
+ response = request(url(:controller => AllowedRole))
23
+ response.should be_unauthorized
24
+ end
25
+ end
26
+
27
+ class DeniedRole < Merb::Plugins::Doorman::TestController
28
+ deny :role => :troll
29
+ end
30
+ describe DeniedRole, 'request' do
31
+ before(:each) do
32
+ DeniedRole.current_user.reset
33
+ end
34
+
35
+ it "should be authorized when the current user does not belong to the denied role" do
36
+ response = request(url(:controller => DeniedRole))
37
+ response.should be_authorized
38
+ end
39
+
40
+ it "should be unauthorized when the current user belongs to the denied role" do
41
+ DeniedRole.current_user.roles << :troll
42
+ response = request(url(:controller => DeniedRole))
43
+ response.should be_unauthorized
44
+ end
45
+ end
46
+
47
+ class AllowedAndDeniedRoles < Merb::Plugins::Doorman::TestController
48
+ allow :role => :admin
49
+ deny :role => :troll
50
+ end
51
+ describe AllowedAndDeniedRoles, 'request' do
52
+ before(:each) do
53
+ AllowedAndDeniedRoles.current_user.reset
54
+ end
55
+
56
+ it "should be authorized when the current user belongs to the allowed role" do
57
+ AllowedAndDeniedRoles.current_user.roles << :admin
58
+ response = request(url(:controller => AllowedAndDeniedRoles))
59
+ response.should be_authorized
60
+ end
61
+
62
+ it "should be unauthorized when the current user belongs to the denied role" do
63
+ AllowedAndDeniedRoles.current_user.roles << :troll
64
+ response = request(url(:controller => AllowedAndDeniedRoles))
65
+ response.should be_unauthorized
66
+ end
67
+ end
68
+
69
+ class AllowedOnly < Merb::Plugins::Doorman::TestController
70
+ allow :role => :admin, :only => :show
71
+ end
72
+ describe AllowedAndDeniedRoles, 'request' do
73
+ before(:each) do
74
+ AllowedOnly.current_user.reset
75
+ end
76
+
77
+ it "should be authorized when the rule does not apply to the action" do
78
+ response = request(url(:controller => AllowedOnly, :action => :index))
79
+ response.should be_authorized
80
+ end
81
+ end
@@ -0,0 +1,42 @@
1
+ $TESTING=true
2
+ $:.push File.join(File.dirname(__FILE__), '..', 'lib')
3
+ require 'rubygems'
4
+ require 'merb-core'
5
+ require 'merb_doorman'
6
+
7
+ this_dir = File.dirname(__FILE__)
8
+
9
+ Merb.push_path(:matchers, this_dir / "matchers")
10
+ Merb.push_path(:fixture_model, this_dir / "fixtures/model")
11
+ Merb.push_path(:fixture_controller, this_dir / "fixtures/controllers")
12
+
13
+ Merb.start :environment => 'test', :adapter => 'runner'
14
+
15
+ def be_authorized
16
+ BeAuthorized.new
17
+ end
18
+
19
+ def be_unauthorized
20
+ BeUnauthorized.new
21
+ end
22
+
23
+ Spec::Runner.configure do |config|
24
+ config.include Merb::Test::RequestHelper
25
+ config.before(:all) do
26
+ Merb::Router.prepare { default_routes }
27
+ end
28
+ end
29
+
30
+ # # deny :user => "bill" # calls current_user.login, but this is configurable
31
+ # # deny {|c| c.params["arbitrary"] == "expressions"}
32
+ # #
33
+ # #
34
+ # # # mostly closed:
35
+ # # deny :all # removes implicit final allow :all
36
+ # # allow :host => "*.example.com"
37
+ # # allow :time => "8am-5pm"
38
+ # #
39
+ # # # store a block for repeated usage
40
+ # # Merb::Access.add_block :admin, {|c| c.current_user.admin?}
41
+ # #
42
+ # # allow :admin
@@ -0,0 +1,19 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ class AccessControllByUserAgent < Merb::Plugins::Doorman::TestController
4
+ deny :user_agent => /MSIE/
5
+ allow :user_agent => /iPhone/
6
+ end
7
+
8
+ describe AccessControllByUserAgent, 'request' do
9
+ it "should be authorized when the user agent is allowed" do
10
+ response = request(url(:controller => AccessControllByUserAgent.controller_name), 'HTTP_USER_AGENT' => 'Mozilla/5.0 (iPhone; U; CPU iPhone OS 2_1 like Mac OS X; en-us) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5F136 Safari/525.20')
11
+ response.should be_authorized
12
+ end
13
+
14
+ it "should be unauthorized when the user agent is denied" do
15
+ response = request(url(:controller => AccessControllByUserAgent.controller_name), 'HTTP_USER_AGENT' => 'Mozilla/4.0 (compatible; MSIE 5.5; Windows 98; Win 9x 4.90; MSIECrawler)')
16
+ response.should be_unauthorized
17
+ end
18
+ end
19
+
data/spec/user_spec.rb ADDED
@@ -0,0 +1,73 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ class AllowedUser < Merb::Plugins::Doorman::TestController
4
+ allow :user => :nancy
5
+ end
6
+ describe AllowedUser, 'request' do
7
+ before(:each) do
8
+ AllowedUser.current_user.reset
9
+ end
10
+
11
+ it "should be authorized when the current user has an allowed login" do
12
+ AllowedUser.current_user.login = 'nancy'
13
+ response = request(url(:controller => AllowedUser))
14
+ response.should be_authorized
15
+ end
16
+
17
+ it "should be unauthorized when the current user does not have an allowed login" do
18
+ response = request(url(:controller => AllowedUser))
19
+ response.should be_unauthorized
20
+
21
+ AllowedUser.current_user.login = 'jackie boy'
22
+ response = request(url(:controller => AllowedUser))
23
+ response.should be_unauthorized
24
+ end
25
+ end
26
+
27
+ class DeniedUser < Merb::Plugins::Doorman::TestController
28
+ deny :user => 'roark'
29
+ deny :user => 'kevin'
30
+ end
31
+ describe DeniedUser, 'request' do
32
+ before(:each) do
33
+ DeniedUser.current_user.reset
34
+ end
35
+
36
+ it "should be authorized when the current user does not have a denied login" do
37
+ DeniedUser.current_user.login = 'nancy'
38
+ response = request(url(:controller => DeniedUser))
39
+ response.should be_authorized
40
+ end
41
+
42
+ it "should be unauthorized when the current user has a denied login" do
43
+ DeniedUser.current_user.login = 'roark'
44
+ response = request(url(:controller => DeniedUser))
45
+ response.should be_unauthorized
46
+
47
+ DeniedUser.current_user.login = 'kevin'
48
+ response = request(url(:controller => DeniedUser))
49
+ response.should be_unauthorized
50
+ end
51
+ end
52
+
53
+ class AllowedAndDeniedUsers < Merb::Plugins::Doorman::TestController
54
+ allow :role => :admin
55
+ deny :role => :troll
56
+ end
57
+ describe AllowedAndDeniedUsers, 'request' do
58
+ before(:each) do
59
+ AllowedAndDeniedUsers.current_user.reset
60
+ end
61
+
62
+ it "should be authorized when the current user belongs to the allowed role" do
63
+ AllowedAndDeniedUsers.current_user.roles << :admin
64
+ response = request(url(:controller => AllowedAndDeniedUsers))
65
+ response.should be_authorized
66
+ end
67
+
68
+ it "should be unauthorized when the current user belongs to the denied role" do
69
+ AllowedAndDeniedUsers.current_user.roles << :troll
70
+ response = request(url(:controller => AllowedAndDeniedUsers))
71
+ response.should be_unauthorized
72
+ end
73
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jrun-merb_doorman
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Michael D. Ivey
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-11-16 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: merb-core
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: "1.0"
23
+ version:
24
+ description: Merb plugin that provides an allow/deny DSL for controlling access
25
+ email: ivey@gweezlebur.com
26
+ executables: []
27
+
28
+ extensions: []
29
+
30
+ extra_rdoc_files:
31
+ - README
32
+ - LICENSE
33
+ - TODO
34
+ files:
35
+ - LICENSE
36
+ - README
37
+ - Rakefile
38
+ - TODO
39
+ - lib/merb_doorman
40
+ - lib/merb_doorman/helpers.rb
41
+ - lib/merb_doorman/merbtasks.rb
42
+ - lib/merb_doorman/rule.rb
43
+ - lib/merb_doorman.rb
44
+ - spec/allow_all_spec.rb
45
+ - spec/class_methods_spec.rb
46
+ - spec/deny_all_spec.rb
47
+ - spec/fixtures
48
+ - spec/fixtures/controllers
49
+ - spec/fixtures/controllers/exceptions.rb
50
+ - spec/fixtures/controllers/test_controller.rb
51
+ - spec/fixtures/controllers/test_helpers.rb
52
+ - spec/fixtures/model
53
+ - spec/fixtures/model/user.rb
54
+ - spec/fixtures/views
55
+ - spec/fixtures/views/test_helpers
56
+ - spec/fixtures/views/test_helpers/allow_via_role.html.erb
57
+ - spec/fixtures/views/test_helpers/deny_via_role.html.erb
58
+ - spec/fixtures/views/test_helpers/example_setup_is_ok.html.erb
59
+ - spec/host_spec.rb
60
+ - spec/matchers
61
+ - spec/matchers/authorized_matcher.rb
62
+ - spec/matchers/unauthorized_matcher.rb
63
+ - spec/merb_doorman
64
+ - spec/merb_doorman/helpers_spec.rb
65
+ - spec/merb_doorman/rule_spec.rb
66
+ - spec/role_spec.rb
67
+ - spec/spec_helper.rb
68
+ - spec/user_agent_spec.rb
69
+ - spec/user_spec.rb
70
+ has_rdoc: true
71
+ homepage: http://github.com/ivey/merb_doorman/
72
+ post_install_message:
73
+ rdoc_options: []
74
+
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: "0"
82
+ version:
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: "0"
88
+ version:
89
+ requirements: []
90
+
91
+ rubyforge_project: merb
92
+ rubygems_version: 1.2.0
93
+ signing_key:
94
+ specification_version: 2
95
+ summary: Merb plugin that provides an allow/deny DSL for controlling access
96
+ test_files: []
97
+