signet 0.3.4 → 0.4.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.
- data/CHANGELOG.md +5 -0
- data/Gemfile +5 -5
- data/Gemfile.lock +12 -7
- data/README.md +1 -0
- data/Rakefile +3 -11
- data/lib/signet/errors.rb +8 -1
- data/lib/signet/oauth_1/client.rb +18 -10
- data/lib/signet/oauth_1/server.rb +505 -0
- data/lib/signet/oauth_2/client.rb +13 -5
- data/lib/signet/version.rb +2 -2
- data/spec/signet/oauth_1/client_spec.rb +26 -14
- data/spec/signet/oauth_1/credential_spec.rb +4 -0
- data/spec/signet/oauth_1/server_spec.rb +846 -0
- data/spec/signet/oauth_1/services/google_spec.rb +5 -1
- data/spec/signet/oauth_1/signature_methods/hmac_sha1_spec.rb +4 -0
- data/spec/signet/oauth_1_spec.rb +4 -0
- data/spec/signet/oauth_2/client_spec.rb +13 -9
- data/spec/signet/oauth_2_spec.rb +4 -0
- data/spec/signet_spec.rb +4 -0
- data/spec/spec.opts +1 -1
- data/spec/spec_helper.rb +2 -0
- data/tasks/gem.rake +6 -6
- data/tasks/spec.rake +26 -45
- data/tasks/yard.rake +16 -22
- metadata +129 -75
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
source :rubygems
|
|
2
2
|
|
|
3
3
|
gem 'addressable', '>= 2.2.3'
|
|
4
|
-
gem 'faraday', '~> 0.
|
|
5
|
-
gem 'multi_json', '>= 1.
|
|
6
|
-
gem 'jwt', '>= 0.1.
|
|
4
|
+
gem 'faraday', '~> 0.8.1'
|
|
5
|
+
gem 'multi_json', '>= 1.0.0'
|
|
6
|
+
gem 'jwt', '>= 0.1.5'
|
|
7
7
|
gem 'extlib', '>= 0.9.15'
|
|
8
8
|
gem 'jruby-openssl', :platforms => :jruby
|
|
9
9
|
|
|
10
10
|
group :development do
|
|
11
|
-
gem 'launchy'
|
|
11
|
+
gem 'launchy', '>= 2.0.0'
|
|
12
12
|
gem 'yard'
|
|
13
13
|
gem 'redcarpet'
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
group :test, :development do
|
|
17
17
|
gem 'rake', '>= 0.9.0'
|
|
18
|
-
gem 'rspec', '
|
|
18
|
+
gem 'rspec', '>= 2.11.0'
|
|
19
19
|
gem 'rcov', '>= 0.9.9', :platform => :mri_18
|
|
20
20
|
end
|
data/Gemfile.lock
CHANGED
|
@@ -3,11 +3,10 @@ GEM
|
|
|
3
3
|
specs:
|
|
4
4
|
addressable (2.2.8)
|
|
5
5
|
bouncy-castle-java (1.5.0146.1)
|
|
6
|
+
diff-lcs (1.1.3)
|
|
6
7
|
extlib (0.9.15)
|
|
7
|
-
faraday (0.
|
|
8
|
-
addressable (~> 2.2)
|
|
8
|
+
faraday (0.8.1)
|
|
9
9
|
multipart-post (~> 1.1)
|
|
10
|
-
rack (~> 1.1)
|
|
11
10
|
ffi (1.0.11-java)
|
|
12
11
|
jruby-openssl (0.7.7)
|
|
13
12
|
bouncy-castle-java (>= 1.5.0146.1)
|
|
@@ -23,11 +22,17 @@ GEM
|
|
|
23
22
|
spoon (~> 0.0.1)
|
|
24
23
|
multi_json (1.3.5)
|
|
25
24
|
multipart-post (1.1.5)
|
|
26
|
-
rack (1.4.1)
|
|
27
25
|
rake (0.9.2.2)
|
|
28
26
|
rcov (1.0.0)
|
|
29
27
|
redcarpet (2.1.1)
|
|
30
|
-
rspec (
|
|
28
|
+
rspec (2.10.0)
|
|
29
|
+
rspec-core (~> 2.10.0)
|
|
30
|
+
rspec-expectations (~> 2.10.0)
|
|
31
|
+
rspec-mocks (~> 2.10.0)
|
|
32
|
+
rspec-core (2.10.0)
|
|
33
|
+
rspec-expectations (2.10.0)
|
|
34
|
+
diff-lcs (~> 1.1.3)
|
|
35
|
+
rspec-mocks (2.10.0)
|
|
31
36
|
spoon (0.0.1)
|
|
32
37
|
yard (0.8.1)
|
|
33
38
|
|
|
@@ -38,7 +43,7 @@ PLATFORMS
|
|
|
38
43
|
DEPENDENCIES
|
|
39
44
|
addressable (>= 2.2.3)
|
|
40
45
|
extlib (>= 0.9.15)
|
|
41
|
-
faraday (~> 0.
|
|
46
|
+
faraday (~> 0.8.1)
|
|
42
47
|
jruby-openssl
|
|
43
48
|
jwt (>= 0.1.4)
|
|
44
49
|
launchy
|
|
@@ -46,5 +51,5 @@ DEPENDENCIES
|
|
|
46
51
|
rake (>= 0.9.0)
|
|
47
52
|
rcov (>= 0.9.9)
|
|
48
53
|
redcarpet
|
|
49
|
-
rspec (~>
|
|
54
|
+
rspec (~> 2.10.0)
|
|
50
55
|
yard
|
data/README.md
CHANGED
data/Rakefile
CHANGED
|
@@ -5,14 +5,6 @@ $:.uniq!
|
|
|
5
5
|
require 'rubygems'
|
|
6
6
|
require 'rake'
|
|
7
7
|
|
|
8
|
-
begin
|
|
9
|
-
require 'spec/rake/spectask'
|
|
10
|
-
rescue LoadError
|
|
11
|
-
STDERR.puts "Please install rspec:"
|
|
12
|
-
STDERR.puts "sudo gem install rspec"
|
|
13
|
-
exit(1)
|
|
14
|
-
end
|
|
15
|
-
|
|
16
8
|
require File.join(File.dirname(__FILE__), 'lib/signet', 'version')
|
|
17
9
|
|
|
18
10
|
PKG_DISPLAY_NAME = 'Signet'
|
|
@@ -36,11 +28,11 @@ PKG_FILES = FileList[
|
|
|
36
28
|
"[A-Z]*", "Rakefile"
|
|
37
29
|
].exclude(/database\.yml/).exclude(/[_\.]git$/)
|
|
38
30
|
|
|
39
|
-
RCOV_ENABLED = !!(RUBY_PLATFORM !=
|
|
31
|
+
RCOV_ENABLED = !!(RUBY_PLATFORM != 'java' && RUBY_VERSION =~ /^1\.8/)
|
|
40
32
|
if RCOV_ENABLED
|
|
41
|
-
task :default =>
|
|
33
|
+
task :default => 'spec:rcov'
|
|
42
34
|
else
|
|
43
|
-
task :default =>
|
|
35
|
+
task :default => 'spec:normal'
|
|
44
36
|
end
|
|
45
37
|
|
|
46
38
|
WINDOWS = (RUBY_PLATFORM =~ /mswin|win32|mingw|bccwin|cygwin/) rescue false
|
data/lib/signet/errors.rb
CHANGED
|
@@ -27,7 +27,14 @@ module Signet
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
##
|
|
30
|
-
# An error indicating the server
|
|
30
|
+
# An error indicating that the server considers the Authorization header to
|
|
31
|
+
# be malformed(missing/unsupported/invalid parameters), and the request
|
|
32
|
+
# should be considered invalid.
|
|
33
|
+
class MalformedAuthorizationError < StandardError
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
##
|
|
37
|
+
# An error indicating the remote server refused to authorize the client.
|
|
31
38
|
class AuthorizationError < StandardError
|
|
32
39
|
##
|
|
33
40
|
# Creates a new authentication error.
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
-
gem 'faraday', '~> 0.
|
|
15
|
+
gem 'faraday', '~> 0.8.1'
|
|
16
16
|
require 'faraday'
|
|
17
17
|
require 'faraday/utils'
|
|
18
18
|
|
|
@@ -538,7 +538,8 @@ module Signet
|
|
|
538
538
|
options = {
|
|
539
539
|
:signature_method => 'HMAC-SHA1',
|
|
540
540
|
:additional_parameters => [],
|
|
541
|
-
:realm => nil
|
|
541
|
+
:realm => nil,
|
|
542
|
+
:connection => Faraday.default_connection
|
|
542
543
|
}.merge(options)
|
|
543
544
|
method = :post
|
|
544
545
|
parameters = ::Signet::OAuth1.unsigned_temporary_credential_parameters(
|
|
@@ -565,8 +566,10 @@ module Signet
|
|
|
565
566
|
headers << ['Content-Type', 'application/x-www-form-urlencoded']
|
|
566
567
|
headers << ['Content-Length', '0']
|
|
567
568
|
end
|
|
568
|
-
return
|
|
569
|
-
req.url(Addressable::URI.parse(
|
|
569
|
+
return options[:connection].build_request(method.to_s.downcase.to_sym) do |req|
|
|
570
|
+
req.url(Addressable::URI.parse(
|
|
571
|
+
self.temporary_credential_uri.to_str
|
|
572
|
+
).normalize.to_s)
|
|
570
573
|
req.headers = Faraday::Utils::Headers.new(headers)
|
|
571
574
|
end
|
|
572
575
|
end
|
|
@@ -603,6 +606,7 @@ module Signet
|
|
|
603
606
|
options[:connection] ||= Faraday.default_connection
|
|
604
607
|
request = self.generate_temporary_credential_request(options)
|
|
605
608
|
request_env = request.to_env(options[:connection])
|
|
609
|
+
request_env[:request] ||= request
|
|
606
610
|
response = options[:connection].app.call(request_env)
|
|
607
611
|
if response.status.to_i == 200
|
|
608
612
|
return ::Signet::OAuth1.parse_form_encoded_credentials(response.body)
|
|
@@ -689,7 +693,8 @@ module Signet
|
|
|
689
693
|
end
|
|
690
694
|
options = {
|
|
691
695
|
:signature_method => 'HMAC-SHA1',
|
|
692
|
-
:realm => nil
|
|
696
|
+
:realm => nil,
|
|
697
|
+
:connection => Faraday.default_connection
|
|
693
698
|
}.merge(options)
|
|
694
699
|
method = :post
|
|
695
700
|
parameters = ::Signet::OAuth1.unsigned_token_credential_parameters(
|
|
@@ -718,8 +723,10 @@ module Signet
|
|
|
718
723
|
headers << ['Content-Type', 'application/x-www-form-urlencoded']
|
|
719
724
|
headers << ['Content-Length', '0']
|
|
720
725
|
end
|
|
721
|
-
return
|
|
722
|
-
req.url(Addressable::URI.parse(
|
|
726
|
+
return options[:connection].build_request(method.to_s.downcase.to_sym) do |req|
|
|
727
|
+
req.url(Addressable::URI.parse(
|
|
728
|
+
self.token_credential_uri.to_str
|
|
729
|
+
).normalize.to_s)
|
|
723
730
|
req.headers = Faraday::Utils::Headers.new(headers)
|
|
724
731
|
end
|
|
725
732
|
end
|
|
@@ -754,6 +761,7 @@ module Signet
|
|
|
754
761
|
options[:connection] ||= Faraday.default_connection
|
|
755
762
|
request = self.generate_token_credential_request(options)
|
|
756
763
|
request_env = request.to_env(options[:connection])
|
|
764
|
+
request_env[:request] ||= request
|
|
757
765
|
response = options[:connection].app.call(request_env)
|
|
758
766
|
if response.status.to_i == 200
|
|
759
767
|
return ::Signet::OAuth1.parse_form_encoded_credentials(response.body)
|
|
@@ -889,9 +897,8 @@ module Signet
|
|
|
889
897
|
raise TypeError, "Expected String, got #{body.class}."
|
|
890
898
|
end
|
|
891
899
|
method = method.to_s.downcase.to_sym
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
req.url(Addressable::URI.parse(uri))
|
|
900
|
+
request = options[:connection].build_request(method) do |req|
|
|
901
|
+
req.url(Addressable::URI.parse(uri).normalize.to_s)
|
|
895
902
|
req.headers = Faraday::Utils::Headers.new(headers)
|
|
896
903
|
req.body = body
|
|
897
904
|
end
|
|
@@ -978,6 +985,7 @@ module Signet
|
|
|
978
985
|
options[:connection] ||= Faraday.default_connection
|
|
979
986
|
request = self.generate_authenticated_request(options)
|
|
980
987
|
request_env = request.to_env(options[:connection])
|
|
988
|
+
request_env[:request] ||= request
|
|
981
989
|
response = options[:connection].app.call(request_env)
|
|
982
990
|
if response.status.to_i == 401
|
|
983
991
|
# When accessing a protected resource, we only want to raise an
|
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
# Copyright (C) 2011 The Yakima Herald-Republic.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
#
|
|
15
|
+
require 'faraday'
|
|
16
|
+
|
|
17
|
+
require 'stringio'
|
|
18
|
+
require 'addressable/uri'
|
|
19
|
+
require 'signet'
|
|
20
|
+
require 'signet/errors'
|
|
21
|
+
require 'signet/oauth_1'
|
|
22
|
+
require 'signet/oauth_1/credential'
|
|
23
|
+
|
|
24
|
+
module Signet
|
|
25
|
+
module OAuth1
|
|
26
|
+
class Server
|
|
27
|
+
|
|
28
|
+
# @return [Proc] lookup the value from this Proc.
|
|
29
|
+
attr_accessor :nonce_timestamp, :client_credential, :token_credential,
|
|
30
|
+
:temporary_credential, :verifier
|
|
31
|
+
|
|
32
|
+
##
|
|
33
|
+
# Creates an OAuth 1.0 server.
|
|
34
|
+
# @overload initialize(options)
|
|
35
|
+
# @param [Proc] nonce_timestamp verify a nonce/timestamp pair.
|
|
36
|
+
# @param [Proc] client_credential find a client credential.
|
|
37
|
+
# @param [Proc] token_credential find a token credential.
|
|
38
|
+
# @param [Proc] temporary_credential find a temporary credential.
|
|
39
|
+
# @param [Proc] verifier validate a verifier value.
|
|
40
|
+
#
|
|
41
|
+
# @example
|
|
42
|
+
# server = Signet::OAuth1::Server.new(
|
|
43
|
+
# :nonce_timestamp =>
|
|
44
|
+
# lambda { |n,t| OauthNonce.remember(n,t) },
|
|
45
|
+
# :client_credential =>
|
|
46
|
+
# lambda { |key| ClientCredential.find_by_key(key).to_hash },
|
|
47
|
+
# :token_credential =>
|
|
48
|
+
# lambda { |key| TokenCredential.find_by_key(key).to_hash },
|
|
49
|
+
# :temporary_credential =>
|
|
50
|
+
# lambda { |key| TemporaryCredential.find_by_key(key).to_hash },
|
|
51
|
+
# :verifier =>
|
|
52
|
+
# lambda {|verifier| Verifier.find_by_verifier(verifier).active? }
|
|
53
|
+
# )
|
|
54
|
+
def initialize(options={})
|
|
55
|
+
[:nonce_timestamp, :client_credential, :token_credential,
|
|
56
|
+
:temporary_credential, :verifier].each do |attr|
|
|
57
|
+
instance_variable_set("@#{attr}", options[attr])
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
##
|
|
62
|
+
# Determine if the supplied nonce/timestamp pair is valid by calling
|
|
63
|
+
# the {#nonce_timestamp} Proc.
|
|
64
|
+
#
|
|
65
|
+
# @param [String, #to_str] nonce value from the request
|
|
66
|
+
# @param [String, #to_str] timestamp value from the request
|
|
67
|
+
# @return [Boolean] if the nonce/timestamp pair is valid.
|
|
68
|
+
def validate_nonce_timestamp(nonce, timestamp)
|
|
69
|
+
nonce =
|
|
70
|
+
@nonce_timestamp.call(nonce, timestamp) if
|
|
71
|
+
@nonce_timestamp.respond_to?(:call)
|
|
72
|
+
nonce ? true : false
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
##
|
|
76
|
+
# Find the appropriate client credential by calling
|
|
77
|
+
# the {#client_credential} Proc.
|
|
78
|
+
#
|
|
79
|
+
# @param [String] key provided to the {#client_credential} Proc.
|
|
80
|
+
# @return [Signet::OAuth1::Credential] The client credential.
|
|
81
|
+
def find_client_credential(key)
|
|
82
|
+
call_credential_lookup(@client_credential, key)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
##
|
|
86
|
+
# Find the appropriate client credential by calling
|
|
87
|
+
# the {#token_credential} Proc.
|
|
88
|
+
#
|
|
89
|
+
# @param [String] key provided to the {#token_credential} Proc.
|
|
90
|
+
# @return [Signet::OAuth1::Credential] if the credential is found.
|
|
91
|
+
def find_token_credential(key)
|
|
92
|
+
call_credential_lookup(@token_credential, key)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
##
|
|
96
|
+
# Find the appropriate client credential by calling
|
|
97
|
+
# the {#temporary_credential} Proc.
|
|
98
|
+
#
|
|
99
|
+
# @param [String] key provided to the {#temporary_credential} Proc.
|
|
100
|
+
# @return [Signet::OAuth1::Credential] if the credential is found.
|
|
101
|
+
def find_temporary_credential(key)
|
|
102
|
+
call_credential_lookup(@temporary_credential, key)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
##
|
|
106
|
+
# Call a credential lookup, and cast the result to a proper Credential.
|
|
107
|
+
#
|
|
108
|
+
# @param [Proc] credential to call.
|
|
109
|
+
# @param [String] key provided to the Proc in <code>credential</code>
|
|
110
|
+
# @return [Signet::OAuth1::Credential] credential provided by
|
|
111
|
+
# <code>credential</code> (if any).
|
|
112
|
+
def call_credential_lookup(credential, key)
|
|
113
|
+
cred = credential.call(key) if
|
|
114
|
+
credential.respond_to?(:call)
|
|
115
|
+
return nil if cred.nil?
|
|
116
|
+
return nil unless (cred.respond_to?(:to_str) ||
|
|
117
|
+
cred.respond_to?(:to_ary) ||
|
|
118
|
+
cred.respond_to?(:to_hash) )
|
|
119
|
+
if(cred.instance_of?(::Signet::OAuth1::Credential))
|
|
120
|
+
cred
|
|
121
|
+
else
|
|
122
|
+
::Signet::OAuth1::Credential.new(cred)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
##
|
|
127
|
+
# Determine if the verifier is valid by calling the Proc in {#verifier}.
|
|
128
|
+
#
|
|
129
|
+
# @param [String] Key provided to the {#verifier} Proc.
|
|
130
|
+
# @return [Boolean] if the verifier Proc returns anything other than
|
|
131
|
+
# <code>nil</code> or <code>false</code>.
|
|
132
|
+
def find_verifier(verifier)
|
|
133
|
+
verified = @verifier.call(verifier) if @verifier.respond_to?(:call)
|
|
134
|
+
verified ? true : false
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
##
|
|
139
|
+
# Validate and normalize the components from an HTTP request.
|
|
140
|
+
# @overload verify_request_components(options)
|
|
141
|
+
# @param [Faraday::Request] request A pre-constructed request to verify.
|
|
142
|
+
# @param [String] method the HTTP method , defaults to <code>GET</code>
|
|
143
|
+
# @param [Addressable::URI, String] uri the URI .
|
|
144
|
+
# @param [Hash, Array] headers the HTTP headers.
|
|
145
|
+
# @param [StringIO, String] body The HTTP body.
|
|
146
|
+
# @param [HTTPAdapter] adapter The HTTP adapter(optional).
|
|
147
|
+
# @return [Hash] normalized request components
|
|
148
|
+
def verify_request_components(options={})
|
|
149
|
+
if options[:request]
|
|
150
|
+
if options[:request].kind_of?(Faraday::Request) || options[:request].kind_of?(Array)
|
|
151
|
+
request = options[:request]
|
|
152
|
+
elsif options[:adapter]
|
|
153
|
+
request = options[:adapter].adapt_request(options[:request])
|
|
154
|
+
end
|
|
155
|
+
method = request.method
|
|
156
|
+
uri = request.path
|
|
157
|
+
headers = request.headers
|
|
158
|
+
body = request.body
|
|
159
|
+
else
|
|
160
|
+
method = options[:method] || :get
|
|
161
|
+
uri = options[:uri]
|
|
162
|
+
headers = options[:headers] || []
|
|
163
|
+
body = options[:body] || ''
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
headers = headers.to_a if headers.kind_of?(Hash)
|
|
167
|
+
method = method.to_s.upcase
|
|
168
|
+
|
|
169
|
+
request_components = {
|
|
170
|
+
:method => method,
|
|
171
|
+
:uri => uri,
|
|
172
|
+
:headers => headers
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
# Verify that we have all the pieces required to validate the HTTP request
|
|
176
|
+
request_components.each do |(key, value)|
|
|
177
|
+
unless value
|
|
178
|
+
raise ArgumentError, "Missing :#{key} parameter."
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
request_components[:body] = body
|
|
182
|
+
request_components
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
##
|
|
186
|
+
# Validate and normalize the HTTP Authorization header.
|
|
187
|
+
#
|
|
188
|
+
# @param [Array] headers from HTTP request.
|
|
189
|
+
# @return [Hash] Hash of Authorization header.
|
|
190
|
+
def verify_auth_header_components(headers)
|
|
191
|
+
auth_header = headers.find{|x| x[0] == 'Authorization'}
|
|
192
|
+
if(auth_header.nil? || auth_header[1] == '')
|
|
193
|
+
raise MalformedAuthorizationError.new('Authorization header is missing')
|
|
194
|
+
end
|
|
195
|
+
auth_hash = ::Signet::OAuth1.parse_authorization_header(
|
|
196
|
+
auth_header[1] ).inject({}) {|acc, (key,val)| acc[key.downcase] = val; acc}
|
|
197
|
+
|
|
198
|
+
auth_hash
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
##
|
|
203
|
+
# @overload request_realm(options)
|
|
204
|
+
# @param [Hash] request A pre-constructed request to verify.
|
|
205
|
+
# @param [String] method the HTTP method , defaults to <code>GET</code>
|
|
206
|
+
# @param [Addressable::URI, String] uri the URI .
|
|
207
|
+
# @param [Hash, Array] headers the HTTP headers.
|
|
208
|
+
# @param [StringIO, String] body The HTTP body.
|
|
209
|
+
# @param [HTTPAdapter] adapter The HTTP adapter(optional).
|
|
210
|
+
# @return [String] The Authorization realm(see RFC 2617) of the request.
|
|
211
|
+
def request_realm(options={})
|
|
212
|
+
if(options[:request])
|
|
213
|
+
request_components = verify_request_components(
|
|
214
|
+
:request=>options[:request],
|
|
215
|
+
:adapter=>options[:adapter] )
|
|
216
|
+
else
|
|
217
|
+
request_components = verify_request_components(
|
|
218
|
+
:method=>options[:method],
|
|
219
|
+
:uri=>options[:uri],
|
|
220
|
+
:headers=>options[:headers],
|
|
221
|
+
:body=>options[:body] )
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
auth_header = request_components[:headers].find{|x| x[0] == 'Authorization'}
|
|
225
|
+
if(auth_header.nil? || auth_header[1] == '')
|
|
226
|
+
raise MalformedAuthorizationError.new('Authorization header is missing')
|
|
227
|
+
end
|
|
228
|
+
auth_hash = ::Signet::OAuth1.parse_authorization_header(
|
|
229
|
+
auth_header[1] ).inject({}) {|acc, (key,val)| acc[key.downcase] = val; acc}
|
|
230
|
+
auth_hash['realm']
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
##
|
|
234
|
+
# Authenticates a temporary credential request. If no oauth_callback is
|
|
235
|
+
# present in the request, <code>oob</code> will be returned.
|
|
236
|
+
#
|
|
237
|
+
# @overload authenticate_temporary_credential_request(options)
|
|
238
|
+
# @param [Hash] request The configuration parameters for the request.
|
|
239
|
+
# @param [String] method the HTTP method , defaults to <code>GET</code>
|
|
240
|
+
# @param [Addressable::URI, String] uri the URI .
|
|
241
|
+
# @param [Hash, Array] headers the HTTP headers.
|
|
242
|
+
# @param [StringIO, String] body The HTTP body.
|
|
243
|
+
# @param [HTTPAdapter] adapter The HTTP adapter(optional).
|
|
244
|
+
# @return [String] The oauth_callback value, or <code>false</code> if not valid.
|
|
245
|
+
def authenticate_temporary_credential_request(options={})
|
|
246
|
+
verifications = {
|
|
247
|
+
:client_credential =>
|
|
248
|
+
lambda { |x| ::Signet::OAuth1::Credential.new('Client credential key',
|
|
249
|
+
'Client credential secret'
|
|
250
|
+
)
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
verifications.each do |(key, value)|
|
|
254
|
+
raise ArgumentError, "#{key} was not set." unless self.send(key)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
if(options[:request])
|
|
258
|
+
request_components = verify_request_components(
|
|
259
|
+
:request=>options[:request],
|
|
260
|
+
:adapter=>options[:adapter] )
|
|
261
|
+
else
|
|
262
|
+
request_components = verify_request_components(
|
|
263
|
+
:method=>options[:method],
|
|
264
|
+
:uri=>options[:uri],
|
|
265
|
+
:headers=>options[:headers] )
|
|
266
|
+
end
|
|
267
|
+
# body should be blank; we don't care in any case.
|
|
268
|
+
method = request_components[:method]
|
|
269
|
+
uri = request_components[:uri]
|
|
270
|
+
headers = request_components[:headers]
|
|
271
|
+
|
|
272
|
+
auth_hash = verify_auth_header_components(headers)
|
|
273
|
+
return false unless(client_credential = find_client_credential(
|
|
274
|
+
auth_hash['oauth_consumer_key']) )
|
|
275
|
+
|
|
276
|
+
return false unless validate_nonce_timestamp(auth_hash['oauth_nonce'],
|
|
277
|
+
auth_hash['oauth_timestamp'])
|
|
278
|
+
client_credential_secret = client_credential.secret if client_credential
|
|
279
|
+
|
|
280
|
+
computed_signature = ::Signet::OAuth1.sign_parameters(
|
|
281
|
+
method,
|
|
282
|
+
uri,
|
|
283
|
+
# Realm isn't used, and will throw the signature off.
|
|
284
|
+
auth_hash.reject{|k,v| k=='realm'}.to_a,
|
|
285
|
+
client_credential_secret,
|
|
286
|
+
nil
|
|
287
|
+
)
|
|
288
|
+
if(computed_signature == auth_hash['oauth_signature'])
|
|
289
|
+
if(auth_hash.fetch('oauth_callback', 'oob').empty?)
|
|
290
|
+
'oob'
|
|
291
|
+
else
|
|
292
|
+
auth_hash.fetch('oauth_callback')
|
|
293
|
+
end
|
|
294
|
+
else
|
|
295
|
+
false
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
##
|
|
301
|
+
# Authenticates a token credential request.
|
|
302
|
+
# @overload authenticate_token_credential_request(options)
|
|
303
|
+
# @param [Hash] request The configuration parameters for the request.
|
|
304
|
+
# @param [String] method the HTTP method , defaults to <code>GET</code>
|
|
305
|
+
# @param [Addressable::URI, String] uri the URI .
|
|
306
|
+
# @param [Hash, Array] headers the HTTP headers.
|
|
307
|
+
# @param [StringIO, String] body The HTTP body.
|
|
308
|
+
# @param [HTTPAdapter] adapter The HTTP adapter(optional).
|
|
309
|
+
# @return [Hash] A hash of credentials and realm for a valid request,
|
|
310
|
+
# or <code>nil</code> if not valid.
|
|
311
|
+
def authenticate_token_credential_request(options={})
|
|
312
|
+
verifications = {
|
|
313
|
+
:client_credential =>
|
|
314
|
+
lambda {|x| ::Signet::OAuth1::Credential.new('Client credential key',
|
|
315
|
+
'Client credential secret')
|
|
316
|
+
},
|
|
317
|
+
:temporary_credential =>
|
|
318
|
+
lambda {|x| ::Signet::OAuth1::Credential.new('Temporary credential key',
|
|
319
|
+
'Temporary credential secret')
|
|
320
|
+
},
|
|
321
|
+
:verifier =>
|
|
322
|
+
lambda {|x| 'Verifier' }
|
|
323
|
+
}
|
|
324
|
+
verifications.each do |(key, value)|
|
|
325
|
+
unless self.send(key)
|
|
326
|
+
raise ArgumentError, "#{key} was not set."
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
if(options[:request])
|
|
330
|
+
request_components = verify_request_components(
|
|
331
|
+
:request=>options[:request],
|
|
332
|
+
:adapter=>options[:adapter]
|
|
333
|
+
)
|
|
334
|
+
else
|
|
335
|
+
request_components = verify_request_components(
|
|
336
|
+
:method=>options[:method],
|
|
337
|
+
:uri=>options[:uri],
|
|
338
|
+
:headers=>options[:headers],
|
|
339
|
+
:body=>options[:body]
|
|
340
|
+
)
|
|
341
|
+
end
|
|
342
|
+
# body should be blank; we don't care in any case.
|
|
343
|
+
method = request_components[:method]
|
|
344
|
+
uri = request_components[:uri]
|
|
345
|
+
headers = request_components[:headers]
|
|
346
|
+
|
|
347
|
+
auth_hash = verify_auth_header_components(headers)
|
|
348
|
+
return false unless(
|
|
349
|
+
client_credential = find_client_credential(auth_hash['oauth_consumer_key'])
|
|
350
|
+
)
|
|
351
|
+
return false unless(
|
|
352
|
+
temporary_credential = find_temporary_credential(auth_hash['oauth_token'])
|
|
353
|
+
)
|
|
354
|
+
return false unless validate_nonce_timestamp(
|
|
355
|
+
auth_hash['oauth_nonce'], auth_hash['oauth_timestamp'])
|
|
356
|
+
|
|
357
|
+
computed_signature = ::Signet::OAuth1.sign_parameters(
|
|
358
|
+
method,
|
|
359
|
+
uri,
|
|
360
|
+
# Realm isn't used, and will throw the signature off.
|
|
361
|
+
auth_hash.reject{|k,v| k=='realm'}.to_a,
|
|
362
|
+
client_credential.secret,
|
|
363
|
+
temporary_credential.secret
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
if(computed_signature == auth_hash['oauth_signature'])
|
|
367
|
+
{:client_credential=>client_credential,
|
|
368
|
+
:temporary_credential=>temporary_credential,
|
|
369
|
+
:realm=>auth_hash['realm']
|
|
370
|
+
}
|
|
371
|
+
else
|
|
372
|
+
nil
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
##
|
|
377
|
+
# Authenticates a request for a protected resource.
|
|
378
|
+
# @overload authenticate_resource_request(options)
|
|
379
|
+
# @param [Hash] request The configuration parameters for the request.
|
|
380
|
+
# @param [String] method the HTTP method , defaults to <code>GET</code>
|
|
381
|
+
# @param [Addressable::URI, String] uri the URI .
|
|
382
|
+
# @param [Hash, Array] headers the HTTP headers.
|
|
383
|
+
# @param [StringIO, String] body The HTTP body.
|
|
384
|
+
# @param [Boolean] two_legged skip the token_credential lookup?
|
|
385
|
+
# @param [HTTPAdapter] adapter The HTTP adapter(optional).
|
|
386
|
+
#
|
|
387
|
+
# @return [Hash] A hash of the credentials and realm for a valid request,
|
|
388
|
+
# or <code>nil</code> if not valid.
|
|
389
|
+
def authenticate_resource_request(options={})
|
|
390
|
+
verifications = {
|
|
391
|
+
:client_credential =>
|
|
392
|
+
lambda do |x|
|
|
393
|
+
::Signet::OAuth1::Credential.new('Client credential key',
|
|
394
|
+
'Client credential secret')
|
|
395
|
+
end
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
unless(options[:two_legged] == true)
|
|
399
|
+
verifications.update(
|
|
400
|
+
:token_credential =>
|
|
401
|
+
lambda do |x|
|
|
402
|
+
::Signet::OAuth1::Credential.new('Token credential key',
|
|
403
|
+
'Token credential secret')
|
|
404
|
+
end
|
|
405
|
+
)
|
|
406
|
+
end
|
|
407
|
+
# Make sure all required state is set
|
|
408
|
+
verifications.each do |(key, value)|
|
|
409
|
+
unless self.send(key)
|
|
410
|
+
raise ArgumentError, "#{key} was not set."
|
|
411
|
+
end
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
if(options[:request])
|
|
415
|
+
request_components = verify_request_components(
|
|
416
|
+
:request=>options[:request],
|
|
417
|
+
:adapter=>options[:adapter] )
|
|
418
|
+
else
|
|
419
|
+
request_components = verify_request_components(
|
|
420
|
+
:method=>options[:method],
|
|
421
|
+
:uri=>options[:uri],
|
|
422
|
+
:headers=>options[:headers],
|
|
423
|
+
:body=>options[:body] )
|
|
424
|
+
end
|
|
425
|
+
method = request_components[:method]
|
|
426
|
+
uri = request_components[:uri]
|
|
427
|
+
headers = request_components[:headers]
|
|
428
|
+
body = request_components[:body]
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
if !body.kind_of?(String) && body.respond_to?(:each)
|
|
432
|
+
# Just in case we get a chunked body
|
|
433
|
+
merged_body = StringIO.new
|
|
434
|
+
body.each do |chunk|
|
|
435
|
+
merged_body.write(chunk)
|
|
436
|
+
end
|
|
437
|
+
body = merged_body.string
|
|
438
|
+
end
|
|
439
|
+
if !body.kind_of?(String)
|
|
440
|
+
raise TypeError, "Expected String, got #{body.class}."
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
media_type = nil
|
|
444
|
+
headers.each do |(header, value)|
|
|
445
|
+
if header.downcase == 'Content-Type'.downcase
|
|
446
|
+
media_type = value.gsub(/^([^;]+)(;.*?)?$/, '\1')
|
|
447
|
+
end
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
auth_hash = verify_auth_header_components(headers)
|
|
451
|
+
|
|
452
|
+
auth_token = auth_hash['oauth_token']
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
unless(options[:two_legged])
|
|
456
|
+
return nil if(auth_token.nil?)
|
|
457
|
+
return nil unless(token_credential = find_token_credential(auth_token))
|
|
458
|
+
token_credential_secret = token_credential.secret if token_credential
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
return nil unless(client_credential =
|
|
462
|
+
find_client_credential(auth_hash['oauth_consumer_key']))
|
|
463
|
+
|
|
464
|
+
return nil unless validate_nonce_timestamp(auth_hash['oauth_nonce'],
|
|
465
|
+
auth_hash['oauth_timestamp'])
|
|
466
|
+
|
|
467
|
+
if(method == ('POST' || 'PUT') &&
|
|
468
|
+
media_type == 'application/x-www-form-urlencoded')
|
|
469
|
+
request_components[:body] = body
|
|
470
|
+
post_parameters = Addressable::URI.form_unencode(body)
|
|
471
|
+
post_parameters.each {|param| param[1] = "" if param[1].nil?}
|
|
472
|
+
# If the auth header doesn't have the same params as the body, it
|
|
473
|
+
# can't have been signed correctly(5849#3.4.1.3)
|
|
474
|
+
unless(post_parameters.sort == auth_hash.reject{|k,v| k.index('oauth_')}.to_a.sort)
|
|
475
|
+
raise MalformedAuthorizationError.new(
|
|
476
|
+
'Request is of type application/x-www-form-urlencoded ' +
|
|
477
|
+
'but Authentication header did not include form values'
|
|
478
|
+
)
|
|
479
|
+
end
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
client_credential_secret = client_credential.secret if client_credential
|
|
483
|
+
|
|
484
|
+
computed_signature = ::Signet::OAuth1.sign_parameters(
|
|
485
|
+
method,
|
|
486
|
+
uri,
|
|
487
|
+
# Realm isn't used, and will throw the signature off.
|
|
488
|
+
auth_hash.reject{|k,v| k=='realm'}.to_a,
|
|
489
|
+
client_credential_secret,
|
|
490
|
+
token_credential_secret
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
if(computed_signature == auth_hash['oauth_signature'])
|
|
494
|
+
{:client_credential=>client_credential,
|
|
495
|
+
:token_credential=>token_credential,
|
|
496
|
+
:realm=>auth_hash['realm']
|
|
497
|
+
}
|
|
498
|
+
else
|
|
499
|
+
nil
|
|
500
|
+
end
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
end
|
|
504
|
+
end
|
|
505
|
+
end
|