warden 0.2.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 +20 -0
- data/README.textile +1 -0
- data/Rakefile +57 -0
- data/TODO.textile +2 -0
- data/lib/warden.rb +14 -0
- data/lib/warden/authentication/hooks.rb +125 -0
- data/lib/warden/authentication/strategies.rb +58 -0
- data/lib/warden/authentication/strategy_base.rb +124 -0
- data/lib/warden/errors.rb +70 -0
- data/lib/warden/manager.rb +134 -0
- data/lib/warden/mixins/common.rb +25 -0
- data/lib/warden/proxy.rb +200 -0
- data/spec/helpers/request_helper.rb +51 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/warden/authenticated_data_store_spec.rb +111 -0
- data/spec/warden/errors_spec.rb +46 -0
- data/spec/warden/hooks_spec.rb +103 -0
- data/spec/warden/manager_spec.rb +158 -0
- data/spec/warden/proxy_spec.rb +218 -0
- data/spec/warden/strategies/failz.rb +9 -0
- data/spec/warden/strategies/invalid.rb +7 -0
- data/spec/warden/strategies/pass.rb +7 -0
- data/spec/warden/strategies/pass_without_user.rb +7 -0
- data/spec/warden/strategies/password.rb +12 -0
- data/spec/warden/strategies_spec.rb +78 -0
- data/spec/warden/strategy_base_spec.rb +259 -0
- data/spec/warden_spec.rb +4 -0
- metadata +83 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Daniel Neighman
|
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.textile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Please see the "Warden Wiki":http://wiki.github.com/hassox/warden for overview documentation.
|
data/Rakefile
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
require 'rubygems/specification'
|
4
|
+
require 'date'
|
5
|
+
require 'spec/rake/spectask'
|
6
|
+
|
7
|
+
GEM = "warden"
|
8
|
+
GEM_VERSION = "0.2.1"
|
9
|
+
AUTHOR = "Daniel Neighman"
|
10
|
+
EMAIL = "has.sox@gmail.com"
|
11
|
+
HOMEPAGE = "http://github.com/hassox/warden"
|
12
|
+
SUMMARY = "Rack middleware that provides authentication for rack applications"
|
13
|
+
|
14
|
+
spec = Gem::Specification.new do |s|
|
15
|
+
s.name = GEM
|
16
|
+
s.version = GEM_VERSION
|
17
|
+
s.platform = Gem::Platform::RUBY
|
18
|
+
s.has_rdoc = true
|
19
|
+
s.extra_rdoc_files = ["README.textile", "LICENSE", 'TODO.textile']
|
20
|
+
s.summary = SUMMARY
|
21
|
+
s.description = s.summary
|
22
|
+
s.author = AUTHOR
|
23
|
+
s.email = EMAIL
|
24
|
+
s.homepage = HOMEPAGE
|
25
|
+
|
26
|
+
# Uncomment this to add a dependency
|
27
|
+
# s.add_dependency "foo"
|
28
|
+
|
29
|
+
s.require_path = 'lib'
|
30
|
+
s.autorequire = GEM
|
31
|
+
s.files = %w(LICENSE README.textile Rakefile TODO.textile) + Dir.glob("{lib,spec}/**/*")
|
32
|
+
end
|
33
|
+
|
34
|
+
task :default => :spec
|
35
|
+
|
36
|
+
desc "Run specs"
|
37
|
+
Spec::Rake::SpecTask.new do |t|
|
38
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
39
|
+
t.spec_opts = %w(-fs --color)
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
44
|
+
pkg.gem_spec = spec
|
45
|
+
end
|
46
|
+
|
47
|
+
desc "install the gem locally"
|
48
|
+
task :install => [:package] do
|
49
|
+
sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}}
|
50
|
+
end
|
51
|
+
|
52
|
+
desc "create a gemspec file"
|
53
|
+
task :make_spec do
|
54
|
+
File.open("#{GEM}.gemspec", "w") do |file|
|
55
|
+
file.puts spec.to_ruby
|
56
|
+
end
|
57
|
+
end
|
data/TODO.textile
ADDED
data/lib/warden.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
$:.unshift File.join(File.dirname(__FILE__))
|
3
|
+
require 'warden/mixins/common'
|
4
|
+
require 'warden/proxy'
|
5
|
+
require 'warden/manager'
|
6
|
+
require 'warden/errors'
|
7
|
+
require 'warden/authentication/hooks'
|
8
|
+
require 'warden/authentication/strategy_base'
|
9
|
+
require 'warden/authentication/strategies'
|
10
|
+
|
11
|
+
|
12
|
+
module Warden
|
13
|
+
class NotAuthenticated < StandardError; end
|
14
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
module Warden
|
2
|
+
class Manager
|
3
|
+
|
4
|
+
class << self
|
5
|
+
# A callback hook set to run every time after a user is set.
|
6
|
+
# This will happen the first time the user is either authenticated, accessed or manually set
|
7
|
+
# during a request. You can supply as many hooks as you like, and they will be run in order of decleration
|
8
|
+
#
|
9
|
+
# Parameters:
|
10
|
+
# <block> A block where you can set arbitrary logic to run every time a user is set
|
11
|
+
# Block Parameters: |user, auth, opts|
|
12
|
+
# user - The user object that is being set
|
13
|
+
# auth - The raw authentication proxy object.
|
14
|
+
# opts - any options passed into the set_user call includeing :scope
|
15
|
+
#
|
16
|
+
# Example:
|
17
|
+
# Warden::Manager.after_set_user do |user,auth,opts|
|
18
|
+
# scope = opts[:scope]
|
19
|
+
# if auth.session["#{scope}.last_access"].to_i > (Time.now - 5.minutes)
|
20
|
+
# auth.logout(scope)
|
21
|
+
# throw(:warden, :scope => scope, :reason => "Times Up")
|
22
|
+
# end
|
23
|
+
# auth.session["#{scope}.last_access"] = Time.now
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# :api: public
|
27
|
+
def after_set_user(&block)
|
28
|
+
raise BlockNotGiven unless block_given?
|
29
|
+
_after_set_user << block
|
30
|
+
end
|
31
|
+
|
32
|
+
# Provides access to the array of after_set_user blocks to run
|
33
|
+
# :api: private
|
34
|
+
def _after_set_user # :nodoc:
|
35
|
+
@_after_set_user ||= []
|
36
|
+
end
|
37
|
+
|
38
|
+
# A callback hook set to run after the first authentiation of a session.
|
39
|
+
# This will only happenwhen the session is first authenticated
|
40
|
+
#
|
41
|
+
# Parameters:
|
42
|
+
# <block> A block to contain logic for the callback
|
43
|
+
# Block Parameters: |user, auth, opts|
|
44
|
+
# user - The user object that is being set
|
45
|
+
# auth - The raw authentication proxy object.
|
46
|
+
# opts - any options passed into the authenticate call includeing :scope
|
47
|
+
#
|
48
|
+
# Example:
|
49
|
+
#
|
50
|
+
# Warden::Manager.after_authentication do |user, auth, opts|
|
51
|
+
# throw(:warden, opts) unless user.active?
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# :api: public
|
55
|
+
def after_authentication(&block)
|
56
|
+
raise BlockNotGiven unless block_given?
|
57
|
+
_after_authentication << block
|
58
|
+
end
|
59
|
+
|
60
|
+
# Provides access to the array of after_authentication blocks
|
61
|
+
# :api: private
|
62
|
+
def _after_authentication
|
63
|
+
@_after_authentication ||= []
|
64
|
+
end
|
65
|
+
|
66
|
+
# A callback that runs just prior to the failur application being called.
|
67
|
+
# This callback occurs after PATH_INFO has been modified for the failure (default /unauthenticated)
|
68
|
+
# In this callback you can mutate the environment as required by the failure application
|
69
|
+
# If a Rails controller were used for the failure_app for example, you would need to set request[:params][:action] = :unauthenticated
|
70
|
+
#
|
71
|
+
# Parameters:
|
72
|
+
# <block> A block to contain logic for the callback
|
73
|
+
# Block Parameters: |user, auth, opts|
|
74
|
+
# env - The rack env hash
|
75
|
+
# opts - any options passed into the authenticate call includeing :scope
|
76
|
+
#
|
77
|
+
# Example:
|
78
|
+
# Warden::Manager.before_failure do |env, opts|
|
79
|
+
# params = Rack::Request.new(env).params
|
80
|
+
# params[:action] = :unauthenticated
|
81
|
+
# params[:warden_failure] = opts
|
82
|
+
# end
|
83
|
+
#
|
84
|
+
# :api: public
|
85
|
+
def before_failure(&block)
|
86
|
+
_before_failure << block
|
87
|
+
end
|
88
|
+
|
89
|
+
# Provides access to the callback array for before_failure
|
90
|
+
# :api: private
|
91
|
+
def _before_failure
|
92
|
+
@_before_failure ||= []
|
93
|
+
end
|
94
|
+
|
95
|
+
# A callback that runs just after to the failur application being called.
|
96
|
+
# This callback is primarily included for Rails 2.3 since Rails 2.3 controllers are not pure Rack Applications
|
97
|
+
# Return whatever you want to be returned for the actual rack response array
|
98
|
+
#
|
99
|
+
# Parameters:
|
100
|
+
# <block> A block to contain logic for the callback
|
101
|
+
# Block Parameters: |user, auth, opts|
|
102
|
+
# result - The result of the rack application
|
103
|
+
# opts - any options passed into the authenticate call includeing :scope
|
104
|
+
#
|
105
|
+
# Example:
|
106
|
+
# # Rails 2.3 after_failure
|
107
|
+
# Warden::Manager.after_failure do |result|
|
108
|
+
# result.to_a
|
109
|
+
# end
|
110
|
+
#
|
111
|
+
# :api: public
|
112
|
+
def after_failure(&block)
|
113
|
+
_after_failure << block
|
114
|
+
end
|
115
|
+
|
116
|
+
# Provides access to the callback array for after_failure
|
117
|
+
# :api: private
|
118
|
+
def _after_failure
|
119
|
+
@_after_failure ||= []
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
end # Manager
|
125
|
+
end # Warden
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Warden
|
2
|
+
module Strategies
|
3
|
+
class << self
|
4
|
+
|
5
|
+
# Adds a strategy to the grab-bag of strategies available to use.
|
6
|
+
# A strategy is a place where you can put logic related to authentication.
|
7
|
+
# A strategy inherits from Warden::Strategies::Base. The _add_ method provides a clean way
|
8
|
+
# to declare your strategies.
|
9
|
+
# You _must_ declare an @authenticate!@ method.
|
10
|
+
# You _may_ provide a @valid?@ method.
|
11
|
+
# The valid method should return true or false depending on if the strategy is a valid one for the request.
|
12
|
+
#
|
13
|
+
# Parameters:
|
14
|
+
# <label: Symbol> The label is the name given to a strategy. Use the label to refer to the strategy when authenticating
|
15
|
+
# <strategy: Class|nil> The optional stragtegy argument if set _must_ be a class that inherits from Warden::Strategies::Base and _must_
|
16
|
+
# implement an @authenticate!@ method
|
17
|
+
# <block> The block acts as a convinient way to declare your strategy. Inside is the class definition of a strategy.
|
18
|
+
#
|
19
|
+
# Examples:
|
20
|
+
#
|
21
|
+
# Block Declared Strategy:
|
22
|
+
# Warden::Strategies.add(:foo) do
|
23
|
+
# def authenticate!
|
24
|
+
# # authentication logic
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# Class Declared Strategy:
|
29
|
+
# Warden::Strategies.add(:foo, MyStrategy)
|
30
|
+
#
|
31
|
+
# :api: public
|
32
|
+
def add(label, strategy = nil, &blk)
|
33
|
+
strategy = strategy.nil? ? Class.new(Warden::Strategies::Base, &blk) : strategy
|
34
|
+
raise NoMethodError, "authenticate! is not declared in the #{label} strategy" if !strategy.method_defined?(:authenticate!)
|
35
|
+
raise "#{label.inspect} is Not a Warden::Strategy::Base" if !strategy.ancestors.include?(Warden::Strategies::Base)
|
36
|
+
_strategies[label] = strategy
|
37
|
+
end
|
38
|
+
|
39
|
+
# Provides access to declared strategies by label
|
40
|
+
# :api: public
|
41
|
+
def [](label)
|
42
|
+
_strategies[label]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Clears all declared middleware.
|
46
|
+
# :api: public
|
47
|
+
def clear!
|
48
|
+
@strategies = {}
|
49
|
+
end
|
50
|
+
|
51
|
+
# :api: private
|
52
|
+
def _strategies
|
53
|
+
@strategies ||= {}
|
54
|
+
end
|
55
|
+
end # << self
|
56
|
+
|
57
|
+
end # Strategies
|
58
|
+
end # Warden
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module Warden
|
2
|
+
module Strategies
|
3
|
+
class Base
|
4
|
+
# :api: public
|
5
|
+
attr_accessor :user, :message
|
6
|
+
|
7
|
+
#:api: private
|
8
|
+
attr_accessor :result, :custom_response
|
9
|
+
|
10
|
+
# Setup for redirection
|
11
|
+
# :api: private
|
12
|
+
attr_reader :_status
|
13
|
+
|
14
|
+
# Accessor for the rack env
|
15
|
+
# :api: public
|
16
|
+
attr_reader :env
|
17
|
+
include ::Warden::Mixins::Common
|
18
|
+
|
19
|
+
# :api: private
|
20
|
+
def initialize(env, config = {}) # :nodoc:
|
21
|
+
@config = config
|
22
|
+
@env, @_status, @headers = env, nil, {}
|
23
|
+
@halted = false
|
24
|
+
end
|
25
|
+
|
26
|
+
# The method that is called from above. This method calls the underlying authetniate! method
|
27
|
+
# :api: private
|
28
|
+
def _run! # :nodoc:
|
29
|
+
result = authenticate!
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
# Acts as a guarding method for the strategy.
|
34
|
+
# If #valid? responds false, the strategy will not be executed
|
35
|
+
# Overwrite with your own logic
|
36
|
+
# :api: overwritable
|
37
|
+
def valid?; true; end
|
38
|
+
|
39
|
+
# Provides access to the headers hash for setting custom headers
|
40
|
+
# :api: public
|
41
|
+
def headers(header = {})
|
42
|
+
@headers ||= {}
|
43
|
+
@headers.merge! header
|
44
|
+
@headers
|
45
|
+
end
|
46
|
+
|
47
|
+
# Access to the errors object.
|
48
|
+
# :api: public
|
49
|
+
def errors
|
50
|
+
@env['warden.errors']
|
51
|
+
end
|
52
|
+
|
53
|
+
# Cause the processing of the strategies to stop and cascade no further
|
54
|
+
# :api: public
|
55
|
+
def halt!
|
56
|
+
@halted = true
|
57
|
+
end
|
58
|
+
|
59
|
+
# Checks to see if a strategy was halted
|
60
|
+
# :api: public
|
61
|
+
def halted?
|
62
|
+
!!@halted
|
63
|
+
end
|
64
|
+
|
65
|
+
# A simple method to return from authenticate! if you want to ignore this strategy
|
66
|
+
# :api: public
|
67
|
+
def pass; end
|
68
|
+
|
69
|
+
# Whenever you want to provide a user object as "authenticated" use the +success!+ method.
|
70
|
+
# This will halt the strategy, and set the user in the approprieate scope.
|
71
|
+
# It is the "login" method
|
72
|
+
#
|
73
|
+
# Parameters:
|
74
|
+
# user - The user object to login. This object can be anything you have setup to serialize in and out of the session
|
75
|
+
#
|
76
|
+
# :api: public
|
77
|
+
def success!(user)
|
78
|
+
halt!
|
79
|
+
@user = user
|
80
|
+
@result = :success
|
81
|
+
end
|
82
|
+
|
83
|
+
# This causes the strategy to fail. It does not throw an :warden symbol to drop the request out to the failure application
|
84
|
+
# You must throw an :warden symbol somewhere in the application to enforce this
|
85
|
+
# :api: public
|
86
|
+
def fail!(message = "Failed to Login")
|
87
|
+
halt!
|
88
|
+
@message = message
|
89
|
+
@result = :failure
|
90
|
+
end
|
91
|
+
|
92
|
+
# Causes the authentication to redirect. An :warden symbol must be thrown to actually execute this redirect
|
93
|
+
#
|
94
|
+
# Parameters:
|
95
|
+
# url <String> - The string representing the URL to be redirected to
|
96
|
+
# pararms <Hash> - Any parameters to encode into the URL
|
97
|
+
# opts <Hash> - Any options to recirect with.
|
98
|
+
# available options: permanent => (true || false)
|
99
|
+
#
|
100
|
+
# :api: public
|
101
|
+
def redirect!(url, params = {}, opts = {})
|
102
|
+
halt!
|
103
|
+
@_status = opts[:permanent] ? 301 : 302
|
104
|
+
headers["Location"] = url
|
105
|
+
headers["Location"] << "?" << Rack::Utils.build_query(params) unless params.empty?
|
106
|
+
|
107
|
+
@message = opts[:message].nil? ? "You are being redirected to #{headers["Location"]}" : opts[:message]
|
108
|
+
|
109
|
+
@result = :redirect
|
110
|
+
|
111
|
+
headers["Location"]
|
112
|
+
end
|
113
|
+
|
114
|
+
# Return a custom rack array. You must throw an :warden symbol to activate this
|
115
|
+
# :api: public
|
116
|
+
def custom!(response)
|
117
|
+
halt!
|
118
|
+
@custom_response = response
|
119
|
+
@result = :custom
|
120
|
+
end
|
121
|
+
|
122
|
+
end # Base
|
123
|
+
end # Strategies
|
124
|
+
end # Warden
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Warden
|
2
|
+
class Proxy
|
3
|
+
# :api: public
|
4
|
+
def errors
|
5
|
+
@env['warden.errors'] ||= Errors.new
|
6
|
+
end
|
7
|
+
|
8
|
+
# Lifted from DataMapper's dm-validations plugin :)
|
9
|
+
# @author Guy van den Berg
|
10
|
+
# @since DM 0.9
|
11
|
+
class Errors
|
12
|
+
|
13
|
+
include Enumerable
|
14
|
+
|
15
|
+
# Clear existing authentication errors.
|
16
|
+
def clear!
|
17
|
+
errors.clear
|
18
|
+
end
|
19
|
+
|
20
|
+
# Add a authentication error. Use the field_name :general if the errors does
|
21
|
+
# not apply to a specific field of the Resource.
|
22
|
+
#
|
23
|
+
# @param <Symbol> field_name the name of the field that caused the error
|
24
|
+
# @param <String> message the message to add
|
25
|
+
def add(field_name, message)
|
26
|
+
(errors[field_name] ||= []) << message
|
27
|
+
end
|
28
|
+
|
29
|
+
# Collect all errors into a single list.
|
30
|
+
def full_messages
|
31
|
+
errors.inject([]) do |list,pair|
|
32
|
+
list += pair.last
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Return authentication errors for a particular field_name.
|
37
|
+
#
|
38
|
+
# @param <Symbol> field_name the name of the field you want an error for
|
39
|
+
def on(field_name)
|
40
|
+
errors_for_field = errors[field_name]
|
41
|
+
blank?(errors_for_field) ? nil : errors_for_field
|
42
|
+
end
|
43
|
+
|
44
|
+
def each
|
45
|
+
errors.map.each do |k,v|
|
46
|
+
next if blank?(v)
|
47
|
+
yield(v)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def empty?
|
52
|
+
entries.empty?
|
53
|
+
end
|
54
|
+
|
55
|
+
def method_missing(meth, *args, &block)
|
56
|
+
errors.send(meth, *args, &block)
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
def errors
|
61
|
+
@errors ||= {}
|
62
|
+
end
|
63
|
+
|
64
|
+
def blank?(thing)
|
65
|
+
thing.nil? || thing == "" || (thing.respond_to?(:empty?) && thing.empty?)
|
66
|
+
end
|
67
|
+
|
68
|
+
end # class Errors
|
69
|
+
end # Proxy
|
70
|
+
end # Warden
|