warden 0.5.3 → 0.6.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.
Files changed (35) hide show
  1. data/History.rdoc +8 -5
  2. data/README.textile +1 -1
  3. data/lib/warden.rb +7 -4
  4. data/lib/warden/declarable.rb +43 -0
  5. data/lib/warden/hooks.rb +121 -0
  6. data/lib/warden/manager.rb +52 -21
  7. data/lib/warden/mixins/common.rb +11 -2
  8. data/lib/warden/proxy.rb +88 -32
  9. data/lib/warden/serializers.rb +20 -0
  10. data/lib/warden/serializers/base.rb +38 -0
  11. data/lib/warden/serializers/cookie.rb +34 -0
  12. data/lib/warden/serializers/session.rb +30 -0
  13. data/lib/warden/strategies.rb +18 -0
  14. data/lib/warden/{authentication/strategy_base.rb → strategies/base.rb} +27 -2
  15. data/lib/warden/version.rb +1 -1
  16. data/spec/helpers/request_helper.rb +14 -12
  17. data/spec/{warden → helpers}/strategies/failz.rb +0 -0
  18. data/spec/{warden → helpers}/strategies/invalid.rb +0 -0
  19. data/spec/{warden → helpers}/strategies/pass.rb +0 -0
  20. data/spec/{warden → helpers}/strategies/pass_without_user.rb +0 -0
  21. data/spec/{warden → helpers}/strategies/password.rb +0 -0
  22. data/spec/spec_helper.rb +1 -1
  23. data/spec/warden/authenticated_data_store_spec.rb +4 -4
  24. data/spec/warden/manager_spec.rb +0 -8
  25. data/spec/warden/proxy_spec.rb +61 -10
  26. data/spec/warden/serializers/cookie_spec.rb +60 -0
  27. data/spec/warden/serializers/session_spec.rb +47 -0
  28. data/spec/warden/serializers_spec.rb +96 -0
  29. data/spec/warden/{strategy_base_spec.rb → strategies/base_spec.rb} +1 -1
  30. data/spec/warden/strategies_spec.rb +19 -15
  31. data/warden.gemspec +28 -18
  32. metadata +28 -18
  33. data/VERSION +0 -1
  34. data/lib/warden/authentication/hooks.rb +0 -124
  35. data/lib/warden/authentication/strategies.rb +0 -59
@@ -1,8 +1,11 @@
1
- === Version 0.5.3
2
- * bug fixes
3
- * authenticated? and unauthenticated? should return true or false, not the user or false.
1
+ * enhancements
2
+ * added serializers, including session serializer (set by default) and a cookie serializer (josevalim)
3
+
4
+ * deprecation
5
+ * serializer_into_session and serializer_from_session are deprecated, overwrite serialize and deserializer in Warden::Serializers::Session instead (josevalim)
6
+
7
+ == Version 0.5.2 / 2009-11-09
4
8
 
5
- === Version 0.5.2
6
9
  * enhancements
7
10
  * authenticated? always try to serialize the user from session (josevalim)
8
11
  * stored_in_session? checks if user information is stored in session, without serializing (josevalim)
@@ -22,7 +25,7 @@
22
25
  * Make scope available to strategies (josevalim)
23
26
 
24
27
  * bug fixes
25
- * Do not consume opts twice, otherwise just the first will parse the scope. (josevalim)
28
+ * Do not consume opts twice, otherwise just the first will parse the scope (josevalim)
26
29
 
27
30
  === Version 0.3.2 / 2009-09-15
28
31
 
@@ -7,5 +7,5 @@ I'm going to try and keep a list of all the contributors to this project. If I'
7
7
  * Daniel Neighman (hassox)
8
8
  * Mick Staugaard (staugaard)
9
9
  * José Valim (josevalim)
10
- * Carlo Santoniodasilva (carlosantoniodasilva)
10
+ * Carlos Antonio da Silva (carlosantoniodasilva)
11
11
  * Justin Smestad (jsmestad)
@@ -1,14 +1,17 @@
1
1
  # encoding: utf-8
2
2
  require 'forwardable'
3
3
  $:.unshift File.join(File.dirname(__FILE__))
4
+
4
5
  require 'warden/mixins/common'
5
6
  require 'warden/proxy'
6
7
  require 'warden/manager'
7
8
  require 'warden/errors'
8
- require 'warden/authentication/hooks'
9
- require 'warden/authentication/strategy_base'
10
- require 'warden/authentication/strategies'
11
-
9
+ require 'warden/strategies'
10
+ require 'warden/strategies/base'
11
+ require 'warden/serializers'
12
+ require 'warden/serializers/base'
13
+ require 'warden/serializers/cookie'
14
+ require 'warden/serializers/session'
12
15
 
13
16
  module Warden
14
17
  class NotAuthenticated < StandardError; end
@@ -0,0 +1,43 @@
1
+ # encoding: utf-8
2
+ module Warden
3
+ module Declarable
4
+
5
+ # Add a declaration and store it in a hash.
6
+ def add(label, declaration = nil, &block)
7
+ base = self.const_get(:Base)
8
+
9
+ declaration ||= Class.new(base)
10
+ declaration.class_eval(&block) if block_given?
11
+
12
+ check_validity!(label, declaration)
13
+ raise "#{label.inspect} is not a #{base}" unless declaration.ancestors.include?(base)
14
+
15
+ _declarations[label] = declaration
16
+ end
17
+
18
+ # Update a previously given declaration.
19
+ def update(label, &block)
20
+ declaration = _declarations[label]
21
+ raise "Unknown declaration #{label.inspect}" unless declaration
22
+ add(label, declaration, &block)
23
+ end
24
+
25
+ # Provides access to declarations by label
26
+ # :api: public
27
+ def [](label)
28
+ _declarations[label]
29
+ end
30
+
31
+ # Clears all declared.
32
+ # :api: public
33
+ def clear!
34
+ _declarations.clear
35
+ end
36
+
37
+ # :api: private
38
+ def _declarations
39
+ @declarations ||= {}
40
+ end
41
+
42
+ end # Declarable
43
+ end # Warden
@@ -0,0 +1,121 @@
1
+ # encoding: utf-8
2
+ module Warden
3
+ module Hooks
4
+
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 prior to the logout of each scope.
96
+ #
97
+ # Parameters:
98
+ # <block> A block to contain logic for the callback
99
+ # Block Parameters: |user, auth, scope|
100
+ # user - The authenticated user for the current scope
101
+ # auth - The warden proxy object
102
+ # scope - current logout scope
103
+ #
104
+ # Example:
105
+ # Warden::Manager.before_logout do |user, auth, scope|
106
+ # user.forget_me!
107
+ # end
108
+ #
109
+ # :api: public
110
+ def before_logout(&block)
111
+ _before_logout << block
112
+ end
113
+
114
+ # Provides access to the callback array for before_logout
115
+ # :api: private
116
+ def _before_logout
117
+ @_before_logout ||= []
118
+ end
119
+
120
+ end # Hooks
121
+ end # Warden
@@ -1,10 +1,14 @@
1
1
  # encoding: utf-8
2
+ require 'warden/hooks'
3
+
2
4
  module Warden
3
5
  # The middleware for Rack Authentication
4
6
  # The middlware requires that there is a session upstream
5
7
  # The middleware injects an authentication object into
6
8
  # the rack environment hash
7
9
  class Manager
10
+ extend Warden::Hooks
11
+
8
12
  attr_accessor :config, :failure_app
9
13
 
10
14
  # initialize the middleware.
@@ -19,6 +23,11 @@ module Warden
19
23
  # Should ensure there is a failure application defined.
20
24
  @failure_app = config[:failure_app] if config[:failure_app]
21
25
  raise "No Failure App provided" unless @failure_app
26
+
27
+ # Set default configuration values.
28
+ @config[:default_strategies] ||= []
29
+ @config[:default_serializers] ||= [ :session ]
30
+
22
31
  self
23
32
  end
24
33
 
@@ -28,6 +37,12 @@ module Warden
28
37
  @config[:silence_missing_strategies] = true
29
38
  end
30
39
 
40
+ # Do not raise an error if a missing serializer is given by default.
41
+ # :api: plugin
42
+ def silence_missing_serializers!
43
+ @config[:silence_missing_serializers] = true
44
+ end
45
+
31
46
  # Set the default strategies to use.
32
47
  # :api: public
33
48
  def default_strategies(*strategies)
@@ -38,6 +53,16 @@ module Warden
38
53
  end
39
54
  end
40
55
 
56
+ # Set the default serializers to use. By default, only session is enabled.
57
+ # :api: public
58
+ def default_serializers(*serializers)
59
+ if serializers.empty?
60
+ @config[:default_serializers]
61
+ else
62
+ @config[:default_serializers] = serializers.flatten
63
+ end
64
+ end
65
+
41
66
  # :api: private
42
67
  def call(env) # :nodoc:
43
68
  # if this is downstream from another warden instance, don't do anything.
@@ -64,21 +89,6 @@ module Warden
64
89
 
65
90
  class << self
66
91
 
67
- # Does the work of storing the user in the session
68
- # :api: private
69
- def _store_user(user, session, scope = :default) # :nodoc:
70
- return nil unless user
71
- session["warden.user.#{scope}.key"] = serialize_into_session.call(user)
72
- end
73
-
74
- # Does the work of fetching the user from the session
75
- # :api: private
76
- def _fetch_user(session, scope = :default) # :nodoc:
77
- key = session["warden.user.#{scope}.key"]
78
- return nil unless key
79
- serialize_from_session.call(key)
80
- end
81
-
82
92
  # Prepares the user to serialize into the session.
83
93
  # Any object that can be serialized into the session in some way can be used as a "user" object
84
94
  # Generally however complex object should not be stored in the session.
@@ -87,10 +97,21 @@ module Warden
87
97
  # Example:
88
98
  # Warden::Manager.serialize_into_session{ |user| user.id }
89
99
  #
100
+ # Deprecation:
101
+ # This method was deprecated in favor of serializer in Session. You can set it while setting the middleware:
102
+ #
103
+ # use Warden::Manager do |manager|
104
+ # manager.update(:session) do
105
+ # def serialize(user)
106
+ # user.id
107
+ # end
108
+ # end
109
+ # end
110
+ #
90
111
  # :api: public
91
112
  def serialize_into_session(&block)
92
- @serialize_into_session = block if block_given?
93
- @serialize_into_session ||= lambda{|user| user}
113
+ warn "[DEPRECATION] serialize_into_session is deprecated. Please overwrite the serialize method in Warden::Serializers::Session."
114
+ Warden::Serializers::Session.send :define_method, :serialize, &block
94
115
  end
95
116
 
96
117
  # Reconstitues the user from the session.
@@ -99,10 +120,21 @@ module Warden
99
120
  # Example:
100
121
  # Warden::Manager.serialize_from_session{ |id| User.get(id) }
101
122
  #
123
+ # Deprecation:
124
+ # This method was deprecated in favor of serializer in Session. You can set it while setting the middleware:
125
+ #
126
+ # use Warden::Manager do |manager|
127
+ # manager.update(:session) do
128
+ # def deserialize(user)
129
+ # User.get(id)
130
+ # end
131
+ # end
132
+ # end
133
+ #
102
134
  # :api: public
103
- def serialize_from_session(&blk)
104
- @serialize_from_session = blk if block_given?
105
- @serialize_from_session ||= lambda{|key| key}
135
+ def serialize_from_session(&block)
136
+ warn "[DEPRECATION] serialize_from_session is deprecated. Please overwrite the deserialize method in Warden::Serializers::Session."
137
+ Warden::Serializers::Session.send :define_method, :deserialize, &block
106
138
  end
107
139
  end
108
140
 
@@ -135,7 +167,6 @@ module Warden
135
167
 
136
168
  # Call the before failure callbacks
137
169
  Warden::Manager._before_failure.each{|hook| hook.call(env,opts)}
138
-
139
170
  @failure_app.call(env).to_a
140
171
  end
141
172
  end # call_failure_app
@@ -8,14 +8,23 @@ module Warden
8
8
  def session
9
9
  env['rack.session']
10
10
  end # session
11
- alias_method :raw_session, :session
12
11
 
13
- # Convenience method to access the rack request
12
+ # Alias :session to :raw_session since the former will be user API for storing scoped data.
13
+ alias :raw_session :session
14
+
15
+ # Convenience method to access the rack request.
14
16
  # :api: public
15
17
  def request
16
18
  @request ||= Rack::Request.new(@env)
17
19
  end # request
18
20
 
21
+ # Convenience method to access the rack response. This should be replaced by the
22
+ # actual response returned to the client.
23
+ # :api: public
24
+ def response
25
+ @response ||= Rack::Response.new(@env)
26
+ end # response
27
+
19
28
  # Convenience method to access the rack request params
20
29
  # :api: public
21
30
  def params
@@ -3,6 +3,7 @@ module Warden
3
3
  class UserNotSet < RuntimeError; end
4
4
 
5
5
  class Proxy
6
+ # An accessor to the wining strategy
6
7
  # :api: private
7
8
  attr_accessor :winning_strategy
8
9
 
@@ -16,7 +17,7 @@ module Warden
16
17
  # :api: private
17
18
  def_delegators :winning_strategy, :headers, :_status, :custom_response
18
19
 
19
- def initialize(env, config = {}) # :nodoc:
20
+ def initialize(env, config = {}) #:nodoc:
20
21
  @env = env
21
22
  @config = config
22
23
  @strategies = @config.fetch(:default_strategies, [])
@@ -34,14 +35,16 @@ module Warden
34
35
  #
35
36
  # Example:
36
37
  # env['warden'].authenticated?(:admin)
38
+ #
37
39
  # :api: public
38
40
  def authenticated?(scope = :default)
39
- result = !!user(scope)
41
+ result = user(scope) || false
40
42
  yield if block_given? && result
41
43
  result
42
44
  end
43
45
 
44
46
  # Same API as authenticated, but returns false when authenticated.
47
+ # :api: public
45
48
  def unauthenticated?(scope = :default)
46
49
  result = !authenticated?(scope)
47
50
  yield if block_given? && result
@@ -59,6 +62,7 @@ module Warden
59
62
  #
60
63
  # Example:
61
64
  # env['auth'].authenticate(:password, :basic, :scope => :sudo)
65
+ #
62
66
  # :api: public
63
67
  def authenticate(*args)
64
68
  scope, opts = _perform_authentication(*args)
@@ -83,11 +87,18 @@ module Warden
83
87
  #
84
88
  # Example
85
89
  # env['warden'].set_user(@user)
86
- # env['warden'].stored_in_session? #=> true
90
+ # env['warden'].stored? #=> true
91
+ # env['warden'].stored?(:default) #=> true
92
+ # env['warden'].stored?(:default, :session) #=> true
93
+ # env['warden'].stored?(:default, :cookie) #=> false
87
94
  #
88
95
  # :api: public
89
- def stored_in_session?(scope = :default)
90
- !!raw_session["warden.user.#{scope}.key"]
96
+ def stored?(scope = :default, serializer = nil)
97
+ if serializer
98
+ find_serializer(serializer).stored?(scope)
99
+ else
100
+ serializers.any? { |s| s.stored?(scope) }
101
+ end
91
102
  end
92
103
 
93
104
  # Manually set the user into the session and auth proxy
@@ -95,15 +106,15 @@ module Warden
95
106
  # Parameters:
96
107
  # user - An object that has been setup to serialize into and out of the session.
97
108
  # opts - An options hash. Use the :scope option to set the scope of the user, set the :store option to false to skip serializing into the session.
109
+ #
98
110
  # :api: public
99
111
  def set_user(user, opts = {})
100
112
  scope = (opts[:scope] ||= :default)
101
- Warden::Manager._store_user(user, raw_session, scope) unless opts[:store] == false# Get the user into the session
113
+ _store_user(user, scope) unless opts[:store] == false
102
114
 
103
115
  # Run the after hooks for setting the user
104
- Warden::Manager._after_set_user.each{|hook| hook.call(user, self, opts)}
105
-
106
- @users[scope] = user # Store the user in the proxy user object
116
+ Warden::Manager._after_set_user.each{ |hook| hook.call(user, self, opts) }
117
+ @users[scope] = user
107
118
  end
108
119
 
109
120
  # Provides acccess to the user object in a given scope for a request.
@@ -118,7 +129,7 @@ module Warden
118
129
  #
119
130
  # :api: public
120
131
  def user(scope = :default)
121
- @users[scope] ||= lookup_user_from_session(scope)
132
+ @users[scope] ||= set_user(_fetch_user(scope), :scope => scope)
122
133
  end
123
134
 
124
135
  # Provides a scoped session data for authenticated users.
@@ -155,22 +166,20 @@ module Warden
155
166
  #
156
167
  # :api: public
157
168
  def logout(*scopes)
158
- # Run before_logout hooks for each scoped user
159
- @users.each do |scope, user|
160
- next unless scopes.empty? || scopes.include?(scope)
161
- Warden::Manager._before_logout.each { |hook| hook.call(user, self, scope) }
169
+ if scopes.empty?
170
+ scopes = @users.keys
171
+ reset_session = true
162
172
  end
163
173
 
164
- if scopes.empty?
165
- reset_session!
166
- @users.clear
167
- else
168
- scopes.each do |s|
169
- raw_session["warden.user.#{s}.key"] = nil
170
- raw_session["warden.user.#{s}.session"] = nil
171
- @users.delete(s)
172
- end
174
+ scopes.each do |scope|
175
+ user = @users.delete(scope)
176
+ Warden::Manager._before_logout.each { |hook| hook.call(user, self, scope) }
177
+
178
+ raw_session.delete("warden.user.#{scope}.session")
179
+ _delete_user(user, scope)
173
180
  end
181
+
182
+ reset_session! if reset_session
174
183
  end
175
184
 
176
185
  # proxy methods through to the winning strategy
@@ -198,7 +207,24 @@ module Warden
198
207
  !!@custom_failure
199
208
  end
200
209
 
210
+ # Retrieve and initializer serializers.
211
+ # :api: private
212
+ def serializers # :nodoc:
213
+ @serializers ||= begin
214
+ array = []
215
+ @config[:default_serializers].each do |s|
216
+ unless Warden::Serializers[s]
217
+ raise "Invalid serializer #{s}" unless silence_missing_serializers?
218
+ next
219
+ end
220
+ array << Warden::Serializers[s].new(@env)
221
+ end
222
+ array
223
+ end
224
+ end
225
+
201
226
  private
227
+
202
228
  # :api: private
203
229
  def _perform_authentication(*args)
204
230
  scope = scope_from_args(args)
@@ -213,11 +239,8 @@ module Warden
213
239
 
214
240
  strategies.each do |s|
215
241
  unless Warden::Strategies[s]
216
- if args.empty? && @config[:silence_missing_strategies]
217
- next
218
- else
219
- raise "Invalid strategy #{s}"
220
- end
242
+ raise "Invalid strategy #{s}" unless args.empty? && silence_missing_strategies?
243
+ next
221
244
  end
222
245
 
223
246
  strategy = Warden::Strategies[s].new(@env, scope, @conf)
@@ -239,18 +262,51 @@ module Warden
239
262
  end
240
263
 
241
264
  # :api: private
242
- def scope_from_args(args)
265
+ def scope_from_args(args) # :nodoc:
243
266
  Hash === args.last ? args.last.fetch(:scope, :default) : :default
244
267
  end
245
268
 
246
269
  # :api: private
247
- def opts_from_args(args)
270
+ def opts_from_args(args) # :nodoc:
248
271
  Hash === args.last ? args.pop : {}
249
272
  end
250
273
 
251
274
  # :api: private
252
- def lookup_user_from_session(scope)
253
- set_user(Warden::Manager._fetch_user(raw_session, scope), :scope => scope)
275
+ def silence_missing_strategies? # :nodoc:
276
+ @config[:silence_missing_strategies]
277
+ end
278
+
279
+ # :api: private
280
+ def silence_missing_serializers? # :nodoc:
281
+ @config[:silence_missing_serializers]
282
+ end
283
+
284
+ # Does the work of storing the user in stores.
285
+ # :api: private
286
+ def _store_user(user, scope = :default) # :nodoc:
287
+ return unless user
288
+ serializers.each { |s| s.store(user, scope) }
289
+ end
290
+
291
+ # Does the work of fetching the user from the first store.
292
+ # :api: private
293
+ def _fetch_user(scope = :default) # :nodoc:
294
+ serializers.each do |s|
295
+ user = s.fetch(scope)
296
+ return user if user
297
+ end
298
+ nil
299
+ end
300
+
301
+ # Does the work of deleteing the user in all stores.
302
+ # :api: private
303
+ def _delete_user(user, scope = :default) # :nodoc:
304
+ serializers.each { |s| s.delete(scope, user) }
305
+ end
306
+
307
+ # :api: private
308
+ def find_serializer(name) # :nodoc:
309
+ serializers.find { |s| s.class == ::Warden::Serializers[name] }
254
310
  end
255
311
  end # Proxy
256
312
  end # Warden