zuul 0.1.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.
- data/.document +5 -0
- data/.gitignore +23 -0
- data/LICENSE +20 -0
- data/README.rdoc +60 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/lib/zuul.rb +8 -0
- data/lib/zuul/restrict_access.rb +106 -0
- data/lib/zuul/valid_roles.rb +37 -0
- data/spec/rails_root/app/controllers/application_controller.rb +2 -0
- data/spec/rails_root/app/models/user.rb +8 -0
- data/spec/rails_root/config/boot.rb +110 -0
- data/spec/rails_root/config/database.yml +5 -0
- data/spec/rails_root/config/environment.rb +7 -0
- data/spec/rails_root/config/environments/test.rb +7 -0
- data/spec/rails_root/config/initializers/session_store.rb +15 -0
- data/spec/rails_root/config/routes.rb +4 -0
- data/spec/rails_root/spec/controllers/require_user_spec.rb +138 -0
- data/spec/rails_root/spec/controllers/restrict_access_spec.rb +64 -0
- data/spec/rails_root/spec/models/user_spec.rb +37 -0
- data/spec/rails_root/spec/spec_helper.rb +34 -0
- data/zuul.gemspec +78 -0
- metadata +87 -0
data/.document
ADDED
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
## MAC OS
|
2
|
+
.DS_Store
|
3
|
+
|
4
|
+
## TEXTMATE
|
5
|
+
*.tmproj
|
6
|
+
tmtags
|
7
|
+
|
8
|
+
## EMACS
|
9
|
+
*~
|
10
|
+
\#*
|
11
|
+
.\#*
|
12
|
+
|
13
|
+
## VIM
|
14
|
+
*.swp
|
15
|
+
|
16
|
+
## PROJECT::GENERAL
|
17
|
+
coverage
|
18
|
+
rdoc
|
19
|
+
pkg
|
20
|
+
|
21
|
+
## PROJECT::SPECIFIC
|
22
|
+
spec/rails_root/db/test.sqlite3
|
23
|
+
spec/rails_root/log/*
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Wes Gibbs
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
=Zuul
|
2
|
+
|
3
|
+
Zuul provides a simple role-based authorization framework for Rails apps.
|
4
|
+
|
5
|
+
==Quick Start
|
6
|
+
|
7
|
+
Add a +role+ to your +users+ table
|
8
|
+
|
9
|
+
add_column :users, :role, :string
|
10
|
+
|
11
|
+
In your +User+ model, specify the valid roles
|
12
|
+
|
13
|
+
valid_roles :guest, :member, :admin
|
14
|
+
|
15
|
+
In your +ApplicationController+, enable access restrictions
|
16
|
+
|
17
|
+
include Zuul::RestrictAccess
|
18
|
+
restrict_access
|
19
|
+
|
20
|
+
In your controllers, specify which roles are allowed for which actions.
|
21
|
+
|
22
|
+
require_user :guest, :admin, :only => :index, :show
|
23
|
+
|
24
|
+
==Examples and Options
|
25
|
+
|
26
|
+
You can pass +restrict_access+ some options
|
27
|
+
* +access_denied_message+ - The string that will be added to the flash[:notice] if the user has been denied access to an action. Defaults to "You must be logged in to access this page".
|
28
|
+
* +require_no_user_message+ - The string that will be added to the flash[:notice] if the requested action requires there be NO user signed in and there is one. Defaults to "You must be logged out to access this page".
|
29
|
+
* +unauthorized_redirect_path+ - The name of a method, as a symbol, that will be called to determine where to redirect someone when they have been denied access. The method is expected to return a string. The default is :unauthorized_path which returns "/".
|
30
|
+
|
31
|
+
You can pass +require_user+ a list of roles and also indicate which actions to apply the restriction to using <code>:only</code> and <code>:except</code>. Some examples:
|
32
|
+
* Restrict access to all actions for a specific role.
|
33
|
+
|
34
|
+
<code>require_user :admin</code>
|
35
|
+
|
36
|
+
* Restrict access to specific actions for specific roles.
|
37
|
+
|
38
|
+
<code>require_user :guest, :admin, :only => :index, :show</code>
|
39
|
+
|
40
|
+
* Require a user but don't care about the role.
|
41
|
+
|
42
|
+
<code>require_user :only => :show</code>
|
43
|
+
|
44
|
+
* Don't allow access to edit or update if there is a user.
|
45
|
+
|
46
|
+
<code>require_no_user :only => :edit, :update</code>
|
47
|
+
|
48
|
+
|
49
|
+
== Note on Patches/Pull Requests
|
50
|
+
|
51
|
+
* Fork the project.
|
52
|
+
* Make your feature addition or bug fix.
|
53
|
+
* Add tests for it. This is important so I don't break it in a
|
54
|
+
future version unintentionally.
|
55
|
+
* Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
56
|
+
* Send me a pull request. Bonus points for topic branches.
|
57
|
+
|
58
|
+
== Copyright
|
59
|
+
|
60
|
+
Copyright (c) 2009 Wes Gibbs. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "zuul"
|
8
|
+
gem.summary = %Q{Simple Rails Authorization}
|
9
|
+
gem.description = %Q{A simple authorization solution for Rails apps.}
|
10
|
+
gem.email = "wes@hashrocket.com"
|
11
|
+
gem.homepage = "http://github.com/wgibbs/zuul"
|
12
|
+
gem.authors = ["Wes Gibbs"]
|
13
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
14
|
+
gem.test_files = ["spec/**/*"]
|
15
|
+
gem.files.exclude "spec/rails_root/**/*"
|
16
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
17
|
+
end
|
18
|
+
Jeweler::GemcutterTasks.new
|
19
|
+
rescue LoadError
|
20
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
21
|
+
end
|
22
|
+
|
23
|
+
require 'rake/testtask'
|
24
|
+
Rake::TestTask.new(:test) do |test|
|
25
|
+
test.libs << 'lib' << 'test'
|
26
|
+
test.pattern = 'teset/rails_root/test/**/test_*.rb'
|
27
|
+
test.verbose = true
|
28
|
+
end
|
29
|
+
|
30
|
+
require 'spec/rake/spectask'
|
31
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
32
|
+
spec.libs << 'lib' << 'spec'
|
33
|
+
spec.spec_files = FileList['spec/rails_root/spec/**/*_spec.rb']
|
34
|
+
end
|
35
|
+
|
36
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
37
|
+
spec.libs << 'lib' << 'spec'
|
38
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
39
|
+
spec.rcov = true
|
40
|
+
end
|
41
|
+
|
42
|
+
task :spec => :check_dependencies
|
43
|
+
|
44
|
+
task :default => :spec
|
45
|
+
|
46
|
+
require 'rake/rdoctask'
|
47
|
+
Rake::RDocTask.new do |rdoc|
|
48
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
49
|
+
|
50
|
+
rdoc.rdoc_dir = 'rdoc'
|
51
|
+
rdoc.title = "zuul #{version}"
|
52
|
+
rdoc.rdoc_files.include('README*')
|
53
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
54
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/lib/zuul.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
module Zuul
|
2
|
+
module RestrictAccess
|
3
|
+
def self.included(controller)
|
4
|
+
controller.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def self.extended(klass)
|
9
|
+
klass.cattr_accessor :access_denied_message
|
10
|
+
klass.cattr_accessor :require_no_user_message
|
11
|
+
klass.cattr_accessor :unauthorized_redirect_path
|
12
|
+
end
|
13
|
+
|
14
|
+
# Meant to be called from your controllers. This is
|
15
|
+
# where you define which roles have access to which actions in the
|
16
|
+
# controller. Examples:
|
17
|
+
# * <code>require_user :admin</code>: Restrict access to all actions for a specific role.
|
18
|
+
# * <code>require_user :guest, :admin, :only => :index, :show</code>: Restrict access to specific actions for specific roles.
|
19
|
+
# * <code>require_user :only => :show</code>: Require a user but don't care about the role.
|
20
|
+
def require_user(*roles)
|
21
|
+
options = roles.extract_options!
|
22
|
+
self.before_filter options do |controller|
|
23
|
+
controller.send(:require_user, roles)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Tells its controller to check that there is no user
|
28
|
+
# before allowing someone into an action. For example:
|
29
|
+
# * <code>require_no_user :only => :edit, :update</code>: Don't allow access to the edit action
|
30
|
+
# if there is a user.
|
31
|
+
def require_no_user(options = {})
|
32
|
+
self.before_filter options do |controller|
|
33
|
+
controller.send(:require_no_user)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Intended to be called by ApplicationController. It mixes
|
38
|
+
# in a set of instance methods that manage conferring or denying access to actions.
|
39
|
+
# You can customize the behavior when a user is denied access with these
|
40
|
+
# options:
|
41
|
+
# * +access_denied_message+: The string that will be added to the
|
42
|
+
# flash[:notice] if the user has been denied access to an action.
|
43
|
+
# Defaults to "You must be logged in to access this page".
|
44
|
+
# * +require_no_user_message+: The string that will be added to the
|
45
|
+
# flash[:notice] if the requested action requires there be NO user signed
|
46
|
+
# in and there is one. Defaults to "You must be logged out to access this
|
47
|
+
# page.".
|
48
|
+
# * +unauthorized_redirect_path+: The name of a method, as a symbol, that
|
49
|
+
# will be called to determine where to redirect someone when they have
|
50
|
+
# been denied access. The method is expected to return a string. The
|
51
|
+
# default is :unauthorized_path which returns "/".
|
52
|
+
def restrict_access(options = {})
|
53
|
+
self.access_denied_message = options[:access_denied_message] || "You must be logged in to access this page"
|
54
|
+
self.require_no_user_message = options[:require_no_user_message] || "You must be logged out to access this page"
|
55
|
+
self.unauthorized_redirect_path = options[:unauthorized_redirect_path] || :unauthorized_path
|
56
|
+
include ApplicationController::InstanceMethods
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
module ApplicationController
|
61
|
+
module InstanceMethods
|
62
|
+
def require_user(*roles)
|
63
|
+
roles.flatten!
|
64
|
+
return true if current_user && roles.empty?
|
65
|
+
deny_access unless roles.any? do |role|
|
66
|
+
method = (role.to_s + "?").to_sym
|
67
|
+
if current_user && current_user.respond_to?(method)
|
68
|
+
current_user.send(method)
|
69
|
+
else
|
70
|
+
false
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
private :require_user
|
75
|
+
|
76
|
+
def require_no_user
|
77
|
+
if current_user
|
78
|
+
store_location
|
79
|
+
flash[:notice] = self.class.require_no_user_message
|
80
|
+
redirect_to send(self.class.unauthorized_redirect_path)
|
81
|
+
return false
|
82
|
+
end
|
83
|
+
end
|
84
|
+
private :require_no_user
|
85
|
+
|
86
|
+
def deny_access
|
87
|
+
store_location
|
88
|
+
flash[:notice] = self.class.access_denied_message
|
89
|
+
redirect_to send(self.class.unauthorized_redirect_path)
|
90
|
+
return false
|
91
|
+
end
|
92
|
+
private :deny_access
|
93
|
+
|
94
|
+
def store_location
|
95
|
+
session[:return_to] = request.request_uri
|
96
|
+
end
|
97
|
+
private :store_location
|
98
|
+
|
99
|
+
def unauthorized_path
|
100
|
+
"/"
|
101
|
+
end
|
102
|
+
private :unauthorized_path
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Zuul
|
2
|
+
module ValidRoles
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def valid_roles(*roles)
|
9
|
+
attr_protected :role
|
10
|
+
write_inheritable_attribute(:roles, roles)
|
11
|
+
include InstanceMethods
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module InstanceMethods
|
16
|
+
def self.included(base)
|
17
|
+
base.read_inheritable_attribute(:roles).each do |role|
|
18
|
+
class_eval <<-CODE
|
19
|
+
def #{role}?
|
20
|
+
self.role.to_sym == :#{role}
|
21
|
+
end
|
22
|
+
CODE
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def role
|
27
|
+
role_name = read_attribute(:role)
|
28
|
+
role_name && role_name.to_sym
|
29
|
+
end
|
30
|
+
|
31
|
+
def role=(role)
|
32
|
+
return unless self.class.read_inheritable_attribute(:roles).include?(role.to_sym)
|
33
|
+
write_attribute(:role, role.to_s)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# Don't change this file!
|
2
|
+
# Configure your app in config/environment.rb and config/environments/*.rb
|
3
|
+
|
4
|
+
RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
|
5
|
+
|
6
|
+
module Rails
|
7
|
+
class << self
|
8
|
+
def boot!
|
9
|
+
unless booted?
|
10
|
+
preinitialize
|
11
|
+
pick_boot.run
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def booted?
|
16
|
+
defined? Rails::Initializer
|
17
|
+
end
|
18
|
+
|
19
|
+
def pick_boot
|
20
|
+
(vendor_rails? ? VendorBoot : GemBoot).new
|
21
|
+
end
|
22
|
+
|
23
|
+
def vendor_rails?
|
24
|
+
File.exist?("#{RAILS_ROOT}/vendor/rails")
|
25
|
+
end
|
26
|
+
|
27
|
+
def preinitialize
|
28
|
+
load(preinitializer_path) if File.exist?(preinitializer_path)
|
29
|
+
end
|
30
|
+
|
31
|
+
def preinitializer_path
|
32
|
+
"#{RAILS_ROOT}/config/preinitializer.rb"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Boot
|
37
|
+
def run
|
38
|
+
load_initializer
|
39
|
+
Rails::Initializer.run(:set_load_path)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class VendorBoot < Boot
|
44
|
+
def load_initializer
|
45
|
+
require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
|
46
|
+
Rails::Initializer.run(:install_gem_spec_stubs)
|
47
|
+
Rails::GemDependency.add_frozen_gem_path
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class GemBoot < Boot
|
52
|
+
def load_initializer
|
53
|
+
self.class.load_rubygems
|
54
|
+
load_rails_gem
|
55
|
+
require 'initializer'
|
56
|
+
end
|
57
|
+
|
58
|
+
def load_rails_gem
|
59
|
+
if version = self.class.gem_version
|
60
|
+
gem 'rails', version
|
61
|
+
else
|
62
|
+
gem 'rails'
|
63
|
+
end
|
64
|
+
rescue Gem::LoadError => load_error
|
65
|
+
$stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)
|
66
|
+
exit 1
|
67
|
+
end
|
68
|
+
|
69
|
+
class << self
|
70
|
+
def rubygems_version
|
71
|
+
Gem::RubyGemsVersion rescue nil
|
72
|
+
end
|
73
|
+
|
74
|
+
def gem_version
|
75
|
+
if defined? RAILS_GEM_VERSION
|
76
|
+
RAILS_GEM_VERSION
|
77
|
+
elsif ENV.include?('RAILS_GEM_VERSION')
|
78
|
+
ENV['RAILS_GEM_VERSION']
|
79
|
+
else
|
80
|
+
parse_gem_version(read_environment_rb)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def load_rubygems
|
85
|
+
require 'rubygems'
|
86
|
+
min_version = '1.3.1'
|
87
|
+
unless rubygems_version >= min_version
|
88
|
+
$stderr.puts %Q(Rails requires RubyGems >= #{min_version} (you have #{rubygems_version}). Please `gem update --system` and try again.)
|
89
|
+
exit 1
|
90
|
+
end
|
91
|
+
|
92
|
+
rescue LoadError
|
93
|
+
$stderr.puts %Q(Rails requires RubyGems >= #{min_version}. Please install RubyGems and try again: http://rubygems.rubyforge.org)
|
94
|
+
exit 1
|
95
|
+
end
|
96
|
+
|
97
|
+
def parse_gem_version(text)
|
98
|
+
$1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*["']([!~<>=]*\s*[\d.]+)["']/
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
def read_environment_rb
|
103
|
+
File.read("#{RAILS_ROOT}/config/environment.rb")
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# All that for this:
|
110
|
+
Rails.boot!
|
@@ -0,0 +1,7 @@
|
|
1
|
+
config.cache_classes = true
|
2
|
+
config.whiny_nils = true
|
3
|
+
config.action_controller.consider_all_requests_local = true
|
4
|
+
config.action_controller.perform_caching = false
|
5
|
+
config.action_view.cache_template_loading = true
|
6
|
+
config.action_controller.allow_forgery_protection = false
|
7
|
+
config.action_mailer.delivery_method = :test
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# Be sure to restart your server when you modify this file.
|
2
|
+
|
3
|
+
# Your secret key for verifying cookie session data integrity.
|
4
|
+
# If you change this key, all old sessions will become invalid!
|
5
|
+
# Make sure the secret is at least 30 characters and all random,
|
6
|
+
# no regular words or you'll be exposed to dictionary attacks.
|
7
|
+
ActionController::Base.session = {
|
8
|
+
:key => '_session',
|
9
|
+
:secret => 'weebleswobblebuttheydontfalldown'
|
10
|
+
}
|
11
|
+
|
12
|
+
# Use the database for sessions instead of the cookie-based default,
|
13
|
+
# which shouldn't be used to store highly confidential information
|
14
|
+
# (create the session table with "rake db:sessions:create")
|
15
|
+
# ActionController::Base.session_store = :active_record_store
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
class ApplicationController
|
4
|
+
include Zuul::RestrictAccess
|
5
|
+
restrict_access
|
6
|
+
end
|
7
|
+
|
8
|
+
context "one role required for all actions" do
|
9
|
+
class Stock1Controller < ApplicationController
|
10
|
+
require_user :member
|
11
|
+
def index; render :text => 'index'; end
|
12
|
+
def show; render :text => 'show'; end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe Stock1Controller do
|
16
|
+
before do
|
17
|
+
controller.stubs(:current_user).returns(@user = stub('user'))
|
18
|
+
end
|
19
|
+
|
20
|
+
it "denies someone without that role" do
|
21
|
+
@user.stubs(:member?).returns(false)
|
22
|
+
get :index
|
23
|
+
response.should redirect_to('/')
|
24
|
+
end
|
25
|
+
it "allows someone with that role" do
|
26
|
+
@user.stubs(:member?).returns(true)
|
27
|
+
get :index
|
28
|
+
response.body.should == 'index'
|
29
|
+
end
|
30
|
+
it "controls access to all actions in the controller" do
|
31
|
+
@user.stubs(:member?).returns(false)
|
32
|
+
get :index
|
33
|
+
response.should redirect_to('/')
|
34
|
+
get :show
|
35
|
+
response.should redirect_to('/')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "one role required for only one action" do
|
41
|
+
class Stock2Controller < ApplicationController
|
42
|
+
require_user :member, :only => :show
|
43
|
+
def index; render :text => 'index'; end
|
44
|
+
def show; render :text => 'show'; end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe Stock2Controller do
|
48
|
+
before do
|
49
|
+
controller.stubs(:current_user).returns(@user = stub('user'))
|
50
|
+
end
|
51
|
+
|
52
|
+
it "denies someone without that role from the protected action" do
|
53
|
+
@user.stubs(:member?).returns(false)
|
54
|
+
get :show
|
55
|
+
response.should redirect_to('/')
|
56
|
+
end
|
57
|
+
it "allows someone with that role into the protected action" do
|
58
|
+
@user.stubs(:member?).returns(true)
|
59
|
+
get :show
|
60
|
+
response.body.should == 'show'
|
61
|
+
end
|
62
|
+
it "allows anyone into the unprotected action" do
|
63
|
+
@user.stubs(:member?).returns(false)
|
64
|
+
get :index
|
65
|
+
response.body.should == 'index'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "user with no specific role required for all actions" do
|
71
|
+
class Stock3Controller < ApplicationController
|
72
|
+
require_user
|
73
|
+
def index; render :text => 'index'; end
|
74
|
+
def show; render :text => 'show'; end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe Stock3Controller do
|
78
|
+
before do
|
79
|
+
controller.stubs(:current_user).returns(@user = stub('user'))
|
80
|
+
end
|
81
|
+
|
82
|
+
it "denies access if there is no user" do
|
83
|
+
controller.stubs(:current_user).returns(nil)
|
84
|
+
get :show
|
85
|
+
response.should redirect_to('/')
|
86
|
+
end
|
87
|
+
it "allows access to an admin user" do
|
88
|
+
@user.stubs(:admin?).returns(true)
|
89
|
+
get :show
|
90
|
+
response.body.should == 'show'
|
91
|
+
end
|
92
|
+
it "allows access to a guest user" do
|
93
|
+
@user.stubs(:guest?).returns(true)
|
94
|
+
get :index
|
95
|
+
response.body.should == 'index'
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context "user with no specific role required for all but one action" do
|
101
|
+
class Stock4Controller < ApplicationController
|
102
|
+
require_user :except => :show
|
103
|
+
def index; render :text => 'index'; end
|
104
|
+
def show; render :text => 'show'; end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe Stock4Controller do
|
108
|
+
before do
|
109
|
+
controller.stubs(:current_user).returns(@user = stub('user'))
|
110
|
+
end
|
111
|
+
|
112
|
+
it "denies access if there is no user" do
|
113
|
+
controller.stubs(:current_user).returns(nil)
|
114
|
+
get :index
|
115
|
+
response.should redirect_to('/')
|
116
|
+
end
|
117
|
+
it "allows access to the unprotected action" do
|
118
|
+
controller.stubs(:current_user).returns(nil)
|
119
|
+
get :show
|
120
|
+
response.body.should == 'show'
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
context "cannot access the actions if there is a user" do
|
126
|
+
class Stock5Controller < ApplicationController
|
127
|
+
require_no_user
|
128
|
+
def index; render :text => 'index'; end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe Stock5Controller do
|
132
|
+
it "denies access if there is a user" do
|
133
|
+
controller.stubs(:current_user).returns(@user = stub('user'))
|
134
|
+
get :index
|
135
|
+
response.should redirect_to('/')
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
context "specifying a custom 'access denied' flash message" do
|
4
|
+
class ApplicationController1 < ActionController::Base
|
5
|
+
include Zuul::RestrictAccess
|
6
|
+
restrict_access :access_denied_message => "You shall not pass"
|
7
|
+
end
|
8
|
+
|
9
|
+
class StockController1 < ApplicationController1
|
10
|
+
require_user
|
11
|
+
def index; render :text => 'index'; end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe StockController1 do
|
15
|
+
it "uses the custom message" do
|
16
|
+
controller.stubs(:current_user).returns(nil)
|
17
|
+
get :index
|
18
|
+
flash[:notice].should == "You shall not pass"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "specifying a custom 'access denied' redirect path" do
|
24
|
+
class ApplicationController2 < ActionController::Base
|
25
|
+
include Zuul::RestrictAccess
|
26
|
+
restrict_access :unauthorized_redirect_path => :signin_path
|
27
|
+
def signin_path
|
28
|
+
'/signup'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class StockController2 < ApplicationController2
|
33
|
+
require_user
|
34
|
+
def index; render :text => 'index'; end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe StockController2 do
|
38
|
+
it "uses the custom message" do
|
39
|
+
controller.stubs(:current_user).returns(nil)
|
40
|
+
get :index
|
41
|
+
response.should redirect_to('/signup')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context "specifying a custom 'cannot have a user' message" do
|
47
|
+
class ApplicationController3 < ActionController::Base
|
48
|
+
include Zuul::RestrictAccess
|
49
|
+
restrict_access :require_no_user_message => "You can't do this with a user"
|
50
|
+
end
|
51
|
+
|
52
|
+
class StockController3 < ApplicationController3
|
53
|
+
require_no_user
|
54
|
+
def index; render :text => 'index'; end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe StockController3 do
|
58
|
+
it "uses the custom message" do
|
59
|
+
controller.stubs(:current_user).returns(stub('user'))
|
60
|
+
get :index
|
61
|
+
flash[:notice].should == "You can't do this with a user"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe User do
|
4
|
+
before do
|
5
|
+
@user = User.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it "knows its role" do
|
9
|
+
@user.role = 'admin'
|
10
|
+
@user.admin?.should be_true
|
11
|
+
end
|
12
|
+
|
13
|
+
it "returns its role as a symbol" do
|
14
|
+
@user.role = 'admin'
|
15
|
+
@user.role.should == :admin
|
16
|
+
end
|
17
|
+
|
18
|
+
it "assigns the role if it is in the list of valid roles" do
|
19
|
+
@user.role = :member
|
20
|
+
@user.role.should == :member
|
21
|
+
end
|
22
|
+
|
23
|
+
it "does not assign the role if it is not in the list of valid roles" do
|
24
|
+
@user.role = 'admin'
|
25
|
+
@user.role = :superuser
|
26
|
+
@user.role.should == :admin
|
27
|
+
end
|
28
|
+
|
29
|
+
it "does not allow the role to be mass-assigned" do
|
30
|
+
begin
|
31
|
+
@user.update_attributes(:role => 'admin')
|
32
|
+
rescue Exception => e
|
33
|
+
ensure
|
34
|
+
@user.role.should be_nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
ENV["RAILS_ENV"] = "test"
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
|
3
|
+
require 'spec'
|
4
|
+
require 'spec/rails'
|
5
|
+
|
6
|
+
Spec::Runner.configure do |config|
|
7
|
+
config.use_transactional_fixtures = true
|
8
|
+
config.use_instantiated_fixtures = false
|
9
|
+
config.fixture_path = RAILS_ROOT + '/spec/fixtures/'
|
10
|
+
config.global_fixtures = :all
|
11
|
+
config.mock_with :mocha
|
12
|
+
end
|
13
|
+
|
14
|
+
ActiveRecord::Base.establish_connection(
|
15
|
+
:adapter => 'sqlite3',
|
16
|
+
:database => File.join(File.dirname(__FILE__), '../db/test.sqlite3')
|
17
|
+
)
|
18
|
+
|
19
|
+
class CreateSchema < ActiveRecord::Migration
|
20
|
+
def self.up
|
21
|
+
create_table :users, :force => true do |t|
|
22
|
+
t.string :first_name
|
23
|
+
t.string :last_name
|
24
|
+
t.string :email
|
25
|
+
t.string :username
|
26
|
+
t.string :role
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
CreateSchema.suppress_messages { CreateSchema.migrate(:up) }
|
32
|
+
|
33
|
+
class ActiveSupport::TestCase
|
34
|
+
end
|
data/zuul.gemspec
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{zuul}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Wes Gibbs"]
|
12
|
+
s.date = %q{2009-11-01}
|
13
|
+
s.description = %q{A simple authorization solution for Rails apps.}
|
14
|
+
s.email = %q{wes@hashrocket.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"lib/zuul.rb",
|
27
|
+
"lib/zuul/restrict_access.rb",
|
28
|
+
"lib/zuul/valid_roles.rb",
|
29
|
+
"zuul.gemspec"
|
30
|
+
]
|
31
|
+
s.homepage = %q{http://github.com/wgibbs/zuul}
|
32
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
33
|
+
s.require_paths = ["lib"]
|
34
|
+
s.rubygems_version = %q{1.3.5}
|
35
|
+
s.summary = %q{Simple Rails Authorization}
|
36
|
+
s.test_files = [
|
37
|
+
"spec/rails_root",
|
38
|
+
"spec/rails_root/app",
|
39
|
+
"spec/rails_root/app/controllers",
|
40
|
+
"spec/rails_root/app/controllers/application_controller.rb",
|
41
|
+
"spec/rails_root/app/models",
|
42
|
+
"spec/rails_root/app/models/user.rb",
|
43
|
+
"spec/rails_root/config",
|
44
|
+
"spec/rails_root/config/boot.rb",
|
45
|
+
"spec/rails_root/config/database.yml",
|
46
|
+
"spec/rails_root/config/environment.rb",
|
47
|
+
"spec/rails_root/config/environments",
|
48
|
+
"spec/rails_root/config/environments/test.rb",
|
49
|
+
"spec/rails_root/config/initializers",
|
50
|
+
"spec/rails_root/config/initializers/session_store.rb",
|
51
|
+
"spec/rails_root/config/routes.rb",
|
52
|
+
"spec/rails_root/db",
|
53
|
+
"spec/rails_root/db/test.sqlite3",
|
54
|
+
"spec/rails_root/log",
|
55
|
+
"spec/rails_root/log/test.log",
|
56
|
+
"spec/rails_root/spec",
|
57
|
+
"spec/rails_root/spec/controllers",
|
58
|
+
"spec/rails_root/spec/controllers/require_user_spec.rb",
|
59
|
+
"spec/rails_root/spec/controllers/restrict_access_spec.rb",
|
60
|
+
"spec/rails_root/spec/models",
|
61
|
+
"spec/rails_root/spec/models/user_spec.rb",
|
62
|
+
"spec/rails_root/spec/spec_helper.rb"
|
63
|
+
]
|
64
|
+
|
65
|
+
if s.respond_to? :specification_version then
|
66
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
67
|
+
s.specification_version = 3
|
68
|
+
|
69
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
70
|
+
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
71
|
+
else
|
72
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
73
|
+
end
|
74
|
+
else
|
75
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: zuul
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Wes Gibbs
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-11-01 00:00:00 -04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rspec
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.2.9
|
24
|
+
version:
|
25
|
+
description: A simple authorization solution for Rails apps.
|
26
|
+
email: wes@hashrocket.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- LICENSE
|
33
|
+
- README.rdoc
|
34
|
+
files:
|
35
|
+
- .document
|
36
|
+
- .gitignore
|
37
|
+
- LICENSE
|
38
|
+
- README.rdoc
|
39
|
+
- Rakefile
|
40
|
+
- VERSION
|
41
|
+
- lib/zuul.rb
|
42
|
+
- lib/zuul/restrict_access.rb
|
43
|
+
- lib/zuul/valid_roles.rb
|
44
|
+
- zuul.gemspec
|
45
|
+
has_rdoc: true
|
46
|
+
homepage: http://github.com/wgibbs/zuul
|
47
|
+
licenses: []
|
48
|
+
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options:
|
51
|
+
- --charset=UTF-8
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: "0"
|
59
|
+
version:
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: "0"
|
65
|
+
version:
|
66
|
+
requirements: []
|
67
|
+
|
68
|
+
rubyforge_project:
|
69
|
+
rubygems_version: 1.3.5
|
70
|
+
signing_key:
|
71
|
+
specification_version: 3
|
72
|
+
summary: Simple Rails Authorization
|
73
|
+
test_files:
|
74
|
+
- spec/rails_root/app/controllers/application_controller.rb
|
75
|
+
- spec/rails_root/app/models/user.rb
|
76
|
+
- spec/rails_root/config/boot.rb
|
77
|
+
- spec/rails_root/config/database.yml
|
78
|
+
- spec/rails_root/config/environment.rb
|
79
|
+
- spec/rails_root/config/environments/test.rb
|
80
|
+
- spec/rails_root/config/initializers/session_store.rb
|
81
|
+
- spec/rails_root/config/routes.rb
|
82
|
+
- spec/rails_root/db/test.sqlite3
|
83
|
+
- spec/rails_root/log/test.log
|
84
|
+
- spec/rails_root/spec/controllers/require_user_spec.rb
|
85
|
+
- spec/rails_root/spec/controllers/restrict_access_spec.rb
|
86
|
+
- spec/rails_root/spec/models/user_spec.rb
|
87
|
+
- spec/rails_root/spec/spec_helper.rb
|