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