omniauth 2.1.3 → 2.2.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.
@@ -0,0 +1,57 @@
1
+ module OmniAuth
2
+ module Strategy
3
+ # Builds the auth hash from the uid, info, credentials, and extra
4
+ # blocks a strategy declares with the class-level fetcher DSL.
5
+ module Fetchers
6
+ def uid
7
+ self.class.uid_stack(self).last
8
+ end
9
+
10
+ def info
11
+ merge_stack(self.class.info_stack(self))
12
+ end
13
+
14
+ def credentials
15
+ merge_stack(self.class.credentials_stack(self))
16
+ end
17
+
18
+ def extra
19
+ merge_stack(self.class.extra_stack(self))
20
+ end
21
+
22
+ def auth_hash
23
+ credentials_data = credentials
24
+ extra_data = extra
25
+ AuthHash.new(:provider => name, :uid => uid).tap do |auth|
26
+ auth.info = info unless skip_info?
27
+ auth.credentials = credentials_data if credentials_data
28
+ auth.extra = extra_data if extra_data
29
+ end
30
+ end
31
+
32
+ # Determines whether or not user info should be retrieved. This
33
+ # allows some strategies to save a call to an external API service
34
+ # for existing users. You can use it either by setting the `:skip_info`
35
+ # to true or by setting `:skip_info` to a Proc that takes a uid and
36
+ # evaluates to true when you would like to skip info.
37
+ #
38
+ # @example
39
+ #
40
+ # use MyStrategy, :skip_info => lambda{|uid| User.find_by_uid(uid)}
41
+ def skip_info?
42
+ return false unless options.skip_info?
43
+ return true unless options.skip_info.respond_to?(:call)
44
+
45
+ options.skip_info.call(uid)
46
+ end
47
+
48
+ protected
49
+
50
+ def merge_stack(stack)
51
+ merged = {} #: Hash[untyped, untyped]
52
+ stack.each { |hash| merged.merge!(hash) }
53
+ merged
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,48 @@
1
+ module OmniAuth
2
+ module Strategy
3
+ # Guards against forged hosts. Unless a full_host is set, callback URLs
4
+ # use the request's host, which comes from the Host or X-Forwarded-Host
5
+ # header, so a forged one would send the provider a redirect_uri on
6
+ # another host.
7
+ module Hosts
8
+ protected
9
+
10
+ # Whether callback URLs may use this request's host. They always may
11
+ # when a full_host is set, since then the request's host isn't used.
12
+ # Otherwise, if OmniAuth.config.allowed_hosts is set, the host must
13
+ # match one of its entries: a string matches the whole host, ignoring
14
+ # case, and a regexp must match the whole host.
15
+ def allowed_host?
16
+ return true if full_host_set?
17
+
18
+ allowed_hosts = OmniAuth.config.allowed_hosts
19
+ return true unless allowed_hosts
20
+
21
+ host = request.host
22
+ allowed_hosts.any? do |allowed|
23
+ allowed.instance_of?(Regexp) ? /\A(?:#{allowed})\z/ === host : allowed.casecmp?(host)
24
+ end
25
+ end
26
+
27
+ def full_host_set?
28
+ !(options[:full_host] || OmniAuth.config.full_host).nil?
29
+ end
30
+
31
+ def warn_if_using_request_host
32
+ return if full_host_set? || OmniAuth.config.allowed_hosts
33
+
34
+ log :warn, <<-WARN
35
+ OmniAuth is building callback URLs from the request's host, which comes
36
+ from the Host or X-Forwarded-Host header and can be forged unless your
37
+ app or a proxy in front of it checks it. To fix this, set either:
38
+ OmniAuth.config.full_host = 'https://example.com'
39
+ OmniAuth.config.allowed_hosts = ['example.com']
40
+ WARN
41
+ end
42
+
43
+ def invalid_host!
44
+ fail!(:invalid_host, Error.new('The request host is not in OmniAuth.config.allowed_hosts'))
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,140 @@
1
+ module OmniAuth
2
+ module Strategy
3
+ # Works out the request, callback, and setup paths for a strategy and
4
+ # whether the current request is on one of them, plus the full URLs
5
+ # built from them.
6
+ module Paths
7
+ # Returns true if the environment recognizes either the
8
+ # request or callback path.
9
+ def on_auth_path?
10
+ on_request_path? || on_callback_path?
11
+ end
12
+
13
+ def on_request_path?
14
+ if options[:request_path].respond_to?(:call)
15
+ options.fetch(:request_path).call(env)
16
+ else
17
+ on_path?(request_path)
18
+ end
19
+ end
20
+
21
+ def on_callback_path?
22
+ on_path?(callback_path) || current_path.sub(FORMAT_EXTENSION_REGEX, EMPTY_STRING).casecmp(callback_path).zero?
23
+ end
24
+
25
+ def on_path?(path)
26
+ current_path.casecmp(path).zero?
27
+ end
28
+
29
+ def path_prefix
30
+ options[:path_prefix] || OmniAuth.config.path_prefix
31
+ end
32
+
33
+ def custom_path(kind)
34
+ if options[kind].respond_to?(:call)
35
+ result = options.fetch(kind).call(env)
36
+ return nil unless result.is_a?(String)
37
+
38
+ result
39
+ else
40
+ options[kind]
41
+ end
42
+ end
43
+
44
+ def request_path
45
+ @request_path ||=
46
+ if options[:request_path].is_a?(String)
47
+ options.fetch(:request_path)
48
+ else
49
+ "#{script_name}#{path_prefix}/#{name}"
50
+ end
51
+ end
52
+
53
+ def callback_path
54
+ @callback_path ||= callback_path_option || custom_path(:request_path) || "#{script_name}#{path_prefix}/#{name}/callback"
55
+ end
56
+
57
+ def setup_path
58
+ options[:setup_path] || "#{path_prefix}/#{name}/setup"
59
+ end
60
+
61
+ CURRENT_PATH_REGEX = %r{/$}.freeze
62
+ FORMAT_EXTENSION_REGEX = /\.\w+\z/.freeze
63
+ EMPTY_STRING = ''.freeze
64
+ # Repeated slashes are collapsed, as Rails routing does, so a proxy
65
+ # that sends //auth/:provider still reaches the strategy.
66
+ def current_path
67
+ @current_path ||= request.path.downcase.squeeze('/').sub(CURRENT_PATH_REGEX, EMPTY_STRING)
68
+ end
69
+
70
+ def query_string
71
+ request.query_string.empty? ? '' : "?#{request.query_string}"
72
+ end
73
+
74
+ # The scheme and host for URLs such as callback_url: the strategy's
75
+ # :full_host option, else OmniAuth.config.full_host, else the
76
+ # request's. Either setting can be a string or a proc taking the env.
77
+ def full_host
78
+ host = options[:full_host] || OmniAuth.config.full_host
79
+ case host
80
+ when String then host
81
+ when Proc then host.call(env)
82
+ else full_host_from_request
83
+ end
84
+ end
85
+
86
+ def callback_url
87
+ full_host + callback_path + query_string
88
+ end
89
+
90
+ def script_name
91
+ current_env = @env
92
+ return '' if current_env.nil?
93
+
94
+ current_env['SCRIPT_NAME'] || ''
95
+ end
96
+
97
+ protected
98
+
99
+ # The :callback_path option: a string, or a lambda that returns true
100
+ # when the current path is the callback path.
101
+ def callback_path_option
102
+ option = options[:callback_path]
103
+ return option if option.is_a?(String)
104
+
105
+ current_path if option.respond_to?(:call) && option.call(env)
106
+ end
107
+
108
+ # The scheme, host, and port of the current request, or an empty
109
+ # string if the request URL isn't absolute.
110
+ def full_host_from_request
111
+ # in Rack 1.3.x, request.url explodes if scheme is nil
112
+ return '' unless request.scheme && URI.parse(request.url).absolute?
113
+
114
+ uri = URI.parse(request.url.sub(/\?.*/, ''))
115
+ uri.path = ''
116
+ # sometimes the url is actually showing http inside rails because the
117
+ # other layers (like nginx) have handled the ssl termination.
118
+ uri.scheme = 'https' if ssl?
119
+ String(uri)
120
+ end
121
+
122
+ # Rack env values that mean the request came in over HTTPS.
123
+ SSL_ENV_VALUES = {
124
+ 'HTTPS' => 'on',
125
+ 'HTTP_X_FORWARDED_SSL' => 'on',
126
+ 'HTTP_X_FORWARDED_SCHEME' => 'https',
127
+ 'rack.url_scheme' => 'https'
128
+ }.freeze
129
+
130
+ # Reads request.env rather than env, so full_host and callback_url also
131
+ # work outside a Rack call, as when a strategy's tests stub #request.
132
+ def ssl?
133
+ request_env = request.env
134
+ # A proxy chain can list several protocols; the first is the client's.
135
+ forwarded_proto = request_env['HTTP_X_FORWARDED_PROTO'].to_s.split(',').first
136
+ forwarded_proto.eql?('https') || SSL_ENV_VALUES.any? { |key, value| request_env[key].eql?(value) }
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,53 @@
1
+ module OmniAuth
2
+ module Strategy
3
+ # Runs the request and callback phases against OmniAuth.config.mock_auth
4
+ # instead of the provider when OmniAuth.config.test_mode is enabled.
5
+ module TestMode
6
+ # This is called in lieu of the normal request process
7
+ # in the event that OmniAuth has been configured to be
8
+ # in test mode.
9
+ def mock_call!(*)
10
+ if request_call?
11
+ mock_request_call
12
+ elsif on_callback_path?
13
+ mock_callback_call
14
+ else
15
+ call_app!
16
+ end
17
+ rescue StandardError => e
18
+ handle_phase_error(e)
19
+ end
20
+
21
+ def mock_request_call
22
+ setup_phase
23
+
24
+ run_hook(:request_validation_phase)
25
+
26
+ session['omniauth.params'] = omniauth_params
27
+
28
+ run_hook(:before_request_phase)
29
+
30
+ store_origin if options.origin_param
31
+
32
+ result = redirect(callback_url)
33
+
34
+ run_hook(:after_request_phase)
35
+ result
36
+ end
37
+
38
+ def mock_callback_call
39
+ setup_phase
40
+
41
+ origin = session.delete('omniauth.origin')
42
+ env['omniauth.origin'] ||= origin
43
+ env['omniauth.origin'] = nil if env.fetch('omniauth.origin').eql?('')
44
+ env['omniauth.params'] = session.delete('omniauth.params') || {}
45
+
46
+ mocked_auth = OmniAuth.mock_auth_for(name)
47
+ env['omniauth.auth'] = mocked_auth unless mocked_auth.instance_of?(Symbol)
48
+ run_hook(:before_callback_phase)
49
+ mocked_auth.instance_of?(Symbol) ? fail!(mocked_auth) : call_app!
50
+ end
51
+ end
52
+ end
53
+ end