warden 0.5.3 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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