manager.rb 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. # encoding: utf-8
  2. require 'warden/hooks'
  3. require 'warden/config'
  4. module Warden
  5. # The middleware for Rack Authentication
  6. # The middlware requires that there is a session upstream
  7. # The middleware injects an authentication object into
  8. # the rack environment hash
  9. class Manager
  10. extend Warden::Hooks
  11. attr_accessor :config
  12. # Initialize the middleware. If a block is given, a Warden::Config is yielded so you can properly
  13. # configure the Warden::Manager.
  14. # :api: public
  15. def initialize(app, options={})
  16. default_strategies = options.delete(:default_strategies)
  17. @app, @config = app, Warden::Config.new(options)
  18. @config.default_strategies *default_strategies if default_strategies
  19. yield @config if block_given?
  20. self
  21. end
  22. # Invoke the application guarding for throw :warden.
  23. # If this is downstream from another warden instance, don't do anything.
  24. # :api: private
  25. def call(env) # :nodoc:
  26. return @app.call(env) if env['warden'] && env['warden'].manager != self
  27. env['warden'] = Proxy.new(env, self)
  28. result = catch(:warden) do
  29. @app.call(env)
  30. end
  31. result ||= {}
  32. case result
  33. when Array
  34. if result.first == 401 && intercept_401?(env)
  35. process_unauthenticated(env)
  36. else
  37. result
  38. end
  39. when Hash
  40. process_unauthenticated(env, result)
  41. end
  42. end
  43. # :api: private
  44. def _run_callbacks(*args) #:nodoc:
  45. self.class._run_callbacks(*args)
  46. end
  47. class << self
  48. # Prepares the user to serialize into the session.
  49. # Any object that can be serialized into the session in some way can be used as a "user" object
  50. # Generally however complex object should not be stored in the session.
  51. # If possible store only a "key" of the user object that will allow you to reconstitute it.
  52. #
  53. # You can supply different methods of serialization for different scopes by passing a scope symbol
  54. #
  55. # Example:
  56. # Warden::Manager.serialize_into_session{ |user| user.id }
  57. # # With Scope:
  58. # Warden::Manager.serialize_into_session(:admin) { |user| user.id }
  59. #
  60. # :api: public
  61. def serialize_into_session(scope = nil, &block)
  62. method_name = scope.nil? ? :serialize : "#{scope}_serialize"
  63. Warden::SessionSerializer.send :define_method, method_name, &block
  64. end
  65. # Reconstitues the user from the session.
  66. # Use the results of user_session_key to reconstitue the user from the session on requests after the initial login
  67. # You can supply different methods of de-serialization for different scopes by passing a scope symbol
  68. #
  69. # Example:
  70. # Warden::Manager.serialize_from_session{ |id| User.get(id) }
  71. # # With Scope:
  72. # Warden::Manager.serialize_from_session(:admin) { |id| AdminUser.get(id) }
  73. #
  74. # :api: public
  75. def serialize_from_session(scope = nil, &block)
  76. method_name = scope.nil? ? :deserialize : "#{scope}_deserialize"
  77. Warden::SessionSerializer.send :define_method, method_name, &block
  78. end
  79. end
  80. private
  81. def intercept_401?(env)
  82. config[:intercept_401] && !env['warden'].custom_failure?
  83. end
  84. # When a request is unauthenticated, here's where the processing occurs.
  85. # It looks at the result of the proxy to see if it's been executed and what action to take.
  86. # :api: private
  87. def process_unauthenticated(env, options={})
  88. options[:action] ||= begin
  89. opts = config[:scope_defaults][config.default_scope] || {}
  90. opts[:action] || 'unauthenticated'
  91. end
  92. proxy = env['warden']
  93. result = options[:result] || proxy.result
  94. case result
  95. when :redirect
  96. body = proxy.message || "You are being redirected to #{proxy.headers['Location']}"
  97. [proxy.status, proxy.headers, [body]]
  98. when :custom
  99. proxy.custom_response
  100. else
  101. call_failure_app(env, options)
  102. end
  103. end
  104. # Calls the failure app.
  105. # The before_failure hooks are run on each failure
  106. # :api: private
  107. def call_failure_app(env, options = {})
  108. if config.failure_app
  109. options.merge!(:attempted_path => ::Rack::Request.new(env).fullpath)
  110. env["PATH_INFO"] = "/#{options[:action]}"
  111. env["warden.options"] = options
  112. _run_callbacks(:before_failure, env, options)
  113. config.failure_app.call(env).to_a
  114. else
  115. raise "No Failure App provided"
  116. end
  117. end # call_failure_app
  118. end
  119. end # Warden