google-ads-common 0.4.0 → 0.5.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 +5 -0
- data/README +1 -4
- data/Rakefile +2 -2
- data/lib/ads_common/api.rb +106 -16
- data/lib/ads_common/api_config.rb +2 -3
- data/lib/ads_common/auth/base_handler.rb +22 -3
- data/lib/ads_common/auth/client_login_handler.rb +27 -32
- data/lib/ads_common/auth/oauth_handler.rb +260 -0
- data/lib/ads_common/build/savon_abstract_generator.rb +12 -11
- data/lib/ads_common/build/savon_generator.rb +31 -27
- data/lib/ads_common/build/savon_registry.rb +46 -23
- data/lib/ads_common/build/savon_registry_generator.rb +23 -10
- data/lib/ads_common/build/savon_service_generator.rb +17 -3
- data/lib/ads_common/config.rb +1 -1
- data/lib/ads_common/credential_handler.rb +3 -7
- data/lib/ads_common/errors.rb +18 -6
- data/lib/ads_common/savon_headers/base_header_handler.rb +80 -0
- data/lib/ads_common/{soap4r_logger.rb → savon_headers/httpi_request_proxy.rb} +27 -20
- data/lib/ads_common/savon_headers/oauth_header_handler.rb +92 -0
- data/lib/ads_common/savon_headers/simple_header_handler.rb +17 -49
- data/lib/ads_common/savon_service.rb +129 -41
- data/test/test_savon_service.rb +9 -4
- metadata +39 -43
- data/lib/ads_common/build/rake_common.rb +0 -343
- data/lib/ads_common/build/soap4r_generator.rb +0 -565
- data/lib/ads_common/savon_headers/client_login_header_handler.rb +0 -60
- data/lib/ads_common/soap4r_headers/nested_header_handler.rb +0 -50
- data/lib/ads_common/soap4r_headers/single_header_handler.rb +0 -44
- data/lib/ads_common/soap4r_patches.rb +0 -210
- data/lib/ads_common/soap4r_response_handler.rb +0 -80
data/ChangeLog
CHANGED
data/README
CHANGED
@@ -17,8 +17,7 @@ Install it using the gem install command.
|
|
17
17
|
$ gem install --local google-ads-common-VERSION.gem
|
18
18
|
|
19
19
|
The following gem libraries are required:
|
20
|
-
-
|
21
|
-
- savon v0.9.1 or greater (for savon-enabled libraries);
|
20
|
+
- savon v0.9.6;
|
22
21
|
- httpi v0.9.2 or greater;
|
23
22
|
- httpclient v2.1.6 or greater.
|
24
23
|
|
@@ -33,8 +32,6 @@ The following gem libraries are required:
|
|
33
32
|
build-time tasks for client libraries (not for google-ads-common).
|
34
33
|
- +savon_headers/+: Contains classes that handle header injection into savon
|
35
34
|
requests.
|
36
|
-
- +soap4r_headers/+: Contains classes that handle header injection into soap4r
|
37
|
-
requests.
|
38
35
|
- +test/+: Contains the unit tests for the library.
|
39
36
|
|
40
37
|
== 2 - Commands
|
data/Rakefile
CHANGED
@@ -47,10 +47,10 @@ spec = Gem::Specification.new do |s|
|
|
47
47
|
s.test_files = tests
|
48
48
|
s.has_rdoc = true
|
49
49
|
s.extra_rdoc_files = docs
|
50
|
-
s.add_dependency('savon', '
|
51
|
-
s.add_dependency('soap4r', '= 1.5.8')
|
50
|
+
s.add_dependency('savon', '= 0.9.6')
|
52
51
|
s.add_dependency('httpclient', '>= 2.1.6')
|
53
52
|
s.add_dependency('httpi', '~> 0.9.2')
|
53
|
+
s.add_dependency('oauth', '~> 0.4.5')
|
54
54
|
end
|
55
55
|
|
56
56
|
desc 'Default target - build'
|
data/lib/ads_common/api.rb
CHANGED
@@ -21,9 +21,10 @@
|
|
21
21
|
|
22
22
|
require 'logger'
|
23
23
|
|
24
|
+
require 'ads_common/config'
|
24
25
|
require 'ads_common/errors'
|
25
|
-
require 'ads_common/auth/base_handler'
|
26
26
|
require 'ads_common/auth/client_login_handler'
|
27
|
+
require 'ads_common/auth/oauth_handler'
|
27
28
|
|
28
29
|
module AdsCommon
|
29
30
|
class Api
|
@@ -46,17 +47,18 @@ module AdsCommon
|
|
46
47
|
create_default_logger() : provided_logger
|
47
48
|
|
48
49
|
# Check for valid environment.
|
49
|
-
env_string = config.read('service.environment')
|
50
|
+
env_string = @config.read('service.environment')
|
50
51
|
environment = (env_string.nil?) ? api_config.default_environment :
|
51
52
|
env_string.to_s.upcase.to_sym
|
52
53
|
if api_config.environments.include?(environment)
|
53
|
-
config.set('service.environment', environment)
|
54
|
+
@config.set('service.environment', environment)
|
54
55
|
else
|
55
56
|
raise AdsCommon::Errors::Error,
|
56
57
|
"Unknown or unspecified environment: \"%s\"" % env_string
|
57
58
|
end
|
58
59
|
|
59
|
-
|
60
|
+
# Service wrappers.
|
61
|
+
@wrappers = {}
|
60
62
|
end
|
61
63
|
|
62
64
|
# Sets the logger to use.
|
@@ -112,6 +114,43 @@ module AdsCommon
|
|
112
114
|
return wrapper
|
113
115
|
end
|
114
116
|
|
117
|
+
# Authorize with specified authentication method.
|
118
|
+
#
|
119
|
+
# Args:
|
120
|
+
# - parameters - hash of credentials to add to configuration
|
121
|
+
# - block - code block to handle auth login url
|
122
|
+
#
|
123
|
+
# Returns:
|
124
|
+
# - Auth token for the method
|
125
|
+
#
|
126
|
+
# Throws:
|
127
|
+
# - AdsCommon::Errors::AuthError or derived if authetication error has
|
128
|
+
# occured
|
129
|
+
#
|
130
|
+
def authorize(parameters = {}, &block)
|
131
|
+
parameters.each_pair do |key, value|
|
132
|
+
@credential_handler.set_credential(key, value)
|
133
|
+
end
|
134
|
+
@auth_handler = get_auth_handler(@config.read('service.environment'))
|
135
|
+
|
136
|
+
# Token might still be valid, if not ask for a new one.
|
137
|
+
token = @auth_handler.get_token() ||
|
138
|
+
begin
|
139
|
+
@auth_handler.get_token(@credential_handler.credentials)
|
140
|
+
rescue AdsCommon::Errors::OAuthVerificationRequired => e
|
141
|
+
verification_code = (block_given?) ? yield(e.oauth_url) : nil
|
142
|
+
# Retry with verification code if one provided.
|
143
|
+
if verification_code
|
144
|
+
@credential_handler.set_credential(
|
145
|
+
:oauth_verification_code, verification_code)
|
146
|
+
retry
|
147
|
+
else
|
148
|
+
raise e
|
149
|
+
end
|
150
|
+
end
|
151
|
+
return token
|
152
|
+
end
|
153
|
+
|
115
154
|
private
|
116
155
|
|
117
156
|
# Retrieve the SOAP header handlers to plug into the drivers. Needs to
|
@@ -132,33 +171,77 @@ module AdsCommon
|
|
132
171
|
raise NotImplementedError, 'soap_header_handlers not overriden.'
|
133
172
|
end
|
134
173
|
|
135
|
-
# Auxiliary method to
|
136
|
-
#
|
137
|
-
# used in different APIs and API versions.
|
174
|
+
# Auxiliary method to get an authentication handler. Creates a new one if
|
175
|
+
# the handler has not been initialized yet.
|
138
176
|
#
|
139
177
|
# Args:
|
140
178
|
# - environment: the current working environment (production, sandbox, etc.)
|
141
|
-
# - version: intended API version
|
179
|
+
# - version: intended API version, must be a symbol, optional
|
180
|
+
#
|
181
|
+
# Returns:
|
182
|
+
# - auth handler
|
183
|
+
#
|
184
|
+
def get_auth_handler(environment, version = nil)
|
185
|
+
@auth_handler ||= create_auth_handler(environment, version)
|
186
|
+
return @auth_handler
|
187
|
+
end
|
188
|
+
|
189
|
+
# Auxiliary method to create an authentication handler.
|
190
|
+
#
|
191
|
+
# Args:
|
192
|
+
# - environment: the current working environment (production, sandbox, etc.)
|
193
|
+
# - version: intended API version, must be a symbol, optional
|
142
194
|
#
|
143
195
|
# Returns:
|
144
196
|
# - auth handler
|
145
197
|
#
|
146
|
-
def create_auth_handler(environment, version)
|
147
|
-
|
198
|
+
def create_auth_handler(environment, version = nil)
|
199
|
+
auth_method_str = @config.read('authentication.method', 'ClientLogin')
|
200
|
+
auth_method = auth_method_str.to_s.upcase.to_sym
|
201
|
+
return case auth_method
|
202
|
+
when :CLIENTLOGIN
|
203
|
+
auth_server = api_config.auth_server(environment)
|
204
|
+
AdsCommon::Auth::ClientLoginHandler.new(config, auth_server,
|
205
|
+
api_config.headers_config[:LOGIN_SERVICE_NAME])
|
206
|
+
when :OAUTH
|
207
|
+
scope = api_config.environment_config()[environment][:oauth_scope]
|
208
|
+
AdsCommon::Auth::OAuthHandler.new(config, scope)
|
209
|
+
else
|
210
|
+
raise AdsCommon::Errors::Error,
|
211
|
+
"Unknown authentication method '%s'." % auth_method_str
|
212
|
+
end
|
148
213
|
end
|
149
214
|
|
150
|
-
# Handle loading of a single service
|
151
|
-
#
|
215
|
+
# Handle loading of a single service.
|
216
|
+
# Creates the driver, sets up handlers, declares the appropriate wrapper
|
217
|
+
# class and creates an instance of it.
|
152
218
|
#
|
153
219
|
# Args:
|
154
|
-
# - version: intended API version
|
155
|
-
# - service: name for the intended service
|
220
|
+
# - version: intended API version, must be a symbol
|
221
|
+
# - service: name for the intended service
|
156
222
|
#
|
157
223
|
# Returns:
|
158
|
-
# - a wrapper generated for the
|
224
|
+
# - a simplified wrapper generated for the driver
|
159
225
|
#
|
160
226
|
def prepare_wrapper(version, service)
|
161
|
-
|
227
|
+
environment = config.read('service.environment')
|
228
|
+
api_config.do_require(version, service)
|
229
|
+
endpoint = api_config.endpoint(environment, version, service)
|
230
|
+
interface_class_name = api_config.interface_name(version, service)
|
231
|
+
endpoint_url = endpoint.nil? ? nil : endpoint + service.to_s
|
232
|
+
wrapper = class_for_path(interface_class_name).new(self, endpoint_url)
|
233
|
+
|
234
|
+
auth_handler = get_auth_handler(environment, version)
|
235
|
+
header_list =
|
236
|
+
auth_handler.header_list(@credential_handler.credentials(version))
|
237
|
+
|
238
|
+
soap_handlers = soap_header_handlers(auth_handler, header_list,
|
239
|
+
version, wrapper.namespace)
|
240
|
+
soap_handlers.each do |handler|
|
241
|
+
wrapper.headerhandler << handler
|
242
|
+
end
|
243
|
+
|
244
|
+
return wrapper
|
162
245
|
end
|
163
246
|
|
164
247
|
# Auxiliary method to create a default Logger.
|
@@ -196,5 +279,12 @@ module AdsCommon
|
|
196
279
|
end
|
197
280
|
return result
|
198
281
|
end
|
282
|
+
|
283
|
+
# Converts complete class path into class object.
|
284
|
+
def class_for_path(path)
|
285
|
+
path.split('::').inject(Kernel) do |scope, const_name|
|
286
|
+
scope.const_get(const_name)
|
287
|
+
end
|
288
|
+
end
|
199
289
|
end
|
200
290
|
end
|
@@ -26,7 +26,7 @@ module AdsCommon
|
|
26
26
|
# Contains helper methods for loading and managing the available services.
|
27
27
|
# This module is meant to be imported into API-specific modules.
|
28
28
|
module ApiConfig
|
29
|
-
ADS_COMMON_VERSION = '0.
|
29
|
+
ADS_COMMON_VERSION = '0.5.0'
|
30
30
|
|
31
31
|
# Get the available API versions.
|
32
32
|
#
|
@@ -156,8 +156,7 @@ module AdsCommon
|
|
156
156
|
def auth_server(environment)
|
157
157
|
auth_server_url =
|
158
158
|
ENV['ADSAPI_AUTH_URL'] ||
|
159
|
-
auth_server_config[environment]
|
160
|
-
auth_server_config[default_environment()]
|
159
|
+
auth_server_config[environment]
|
161
160
|
return auth_server_url
|
162
161
|
end
|
163
162
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
#
|
3
3
|
# Authors:: api.sgomes@gmail.com (Sérgio Gomes)
|
4
4
|
#
|
5
|
-
# Copyright:: Copyright
|
5
|
+
# Copyright:: Copyright 2011, Google Inc. All Rights Reserved.
|
6
6
|
#
|
7
7
|
# License:: Licensed under the Apache License, Version 2.0 (the "License");
|
8
8
|
# you may not use this file except in compliance with the License.
|
@@ -24,6 +24,13 @@
|
|
24
24
|
module AdsCommon
|
25
25
|
module Auth
|
26
26
|
class BaseHandler
|
27
|
+
# Default initializer.
|
28
|
+
def initialize(config)
|
29
|
+
@config = config
|
30
|
+
@logger = @config.read('library.logger')
|
31
|
+
@token = nil
|
32
|
+
end
|
33
|
+
|
27
34
|
# Callback to be used by CredentialHandlers to notify the auth handler of
|
28
35
|
# a change in one of the credentials. Useful for e.g. invalidating a
|
29
36
|
# token. The generic method does nothing.
|
@@ -40,13 +47,25 @@ module AdsCommon
|
|
40
47
|
# This method returns the set of fields to be included in the header.
|
41
48
|
# The generic method simply returns everything passed to it.
|
42
49
|
def header_list(credentials)
|
43
|
-
return credentials.keys
|
50
|
+
return credentials.keys.dup()
|
44
51
|
end
|
45
52
|
|
46
53
|
# This method returns the key value pairs to be included in the header.
|
47
54
|
# The generic method simply returns everything passed to it.
|
48
55
|
def headers(credentials)
|
49
|
-
return credentials
|
56
|
+
return credentials.dup()
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns authorization token of some kind. Attempts to create a new one
|
60
|
+
# if the token has not yet been created and credentials present.
|
61
|
+
def get_token(credentials = nil)
|
62
|
+
@token = create_token(credentials) if @token.nil? and credentials
|
63
|
+
return @token
|
64
|
+
end
|
65
|
+
|
66
|
+
# Creates authorization token. Needs to be overriden.
|
67
|
+
def create_token(credentials)
|
68
|
+
raise NotImplementedError, 'create_token not overriden.'
|
50
69
|
end
|
51
70
|
end
|
52
71
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
#!/usr/bin/ruby
|
2
2
|
#
|
3
|
-
# Authors:: api.
|
4
|
-
# api.dklimkin@gmail.com (Danial Klimkin)
|
3
|
+
# Authors:: api.dklimkin@gmail.com (Danial Klimkin)
|
5
4
|
#
|
6
5
|
# Copyright:: Copyright 2011, Google Inc. All Rights Reserved.
|
7
6
|
#
|
@@ -32,15 +31,13 @@ module AdsCommon
|
|
32
31
|
|
33
32
|
# Credentials class to handle ClientLogin authentication.
|
34
33
|
class ClientLoginHandler < AdsCommon::Auth::BaseHandler
|
35
|
-
|
36
34
|
ACCOUNT_TYPE = 'GOOGLE'
|
37
35
|
AUTH_PATH = '/accounts/ClientLogin'
|
38
36
|
IGNORED_FIELDS = [:email, :password, :auth_token]
|
39
37
|
|
40
38
|
# Initializes the ClientLoginHandler with all the necessary details.
|
41
39
|
def initialize(config, server, service_name)
|
42
|
-
|
43
|
-
@config = config
|
40
|
+
super(config)
|
44
41
|
@server = server
|
45
42
|
@service_name = service_name
|
46
43
|
end
|
@@ -48,10 +45,7 @@ module AdsCommon
|
|
48
45
|
# Invalidates the stored token if the email, password or provided auth
|
49
46
|
# token have changed.
|
50
47
|
def property_changed(prop, value)
|
51
|
-
|
52
|
-
when :auth_token
|
53
|
-
@token = config.read('authentication.auth_token')
|
54
|
-
when :email, :password
|
48
|
+
if [:auth_token, :email, :password].include?(prop)
|
55
49
|
@token = nil
|
56
50
|
end
|
57
51
|
end
|
@@ -65,23 +59,20 @@ module AdsCommon
|
|
65
59
|
|
66
60
|
# Returns all of the fields that this auth handler will fill.
|
67
61
|
def header_list(credentials)
|
68
|
-
result =
|
69
|
-
|
70
|
-
credentials.each do |p, v|
|
71
|
-
result << p unless IGNORED_FIELDS.include?(p)
|
62
|
+
result = credentials.keys.map.reject do |field|
|
63
|
+
IGNORED_FIELDS.include?(field)
|
72
64
|
end
|
65
|
+
result << :authToken
|
73
66
|
return result
|
74
67
|
end
|
75
68
|
|
76
69
|
# Returns all of the credentials received from the CredentialHandler,
|
77
|
-
# except for
|
70
|
+
# except for ignored fields.
|
78
71
|
def headers(credentials)
|
79
|
-
|
80
|
-
|
81
|
-
result[:authToken] = @token
|
82
|
-
credentials.each do |p, v|
|
83
|
-
result[p] = v unless IGNORED_FIELDS.include?(p)
|
72
|
+
result = credentials.reject do |field, value|
|
73
|
+
IGNORED_FIELDS.include?(field)
|
84
74
|
end
|
75
|
+
result[:authToken] = get_token(credentials)
|
85
76
|
return result
|
86
77
|
end
|
87
78
|
|
@@ -93,9 +84,8 @@ module AdsCommon
|
|
93
84
|
# - credentials: a hash with the credentials for the account being
|
94
85
|
# accessed
|
95
86
|
#
|
96
|
-
#
|
97
87
|
# Raises:
|
98
|
-
# AdsCommon::Errors::AuthError if validation fails
|
88
|
+
# - AdsCommon::Errors::AuthError if validation fails
|
99
89
|
#
|
100
90
|
def validate_credentials(credentials)
|
101
91
|
if credentials.nil?
|
@@ -114,7 +104,7 @@ module AdsCommon
|
|
114
104
|
end
|
115
105
|
end
|
116
106
|
|
117
|
-
# Auxiliary method to generate an authentication token for
|
107
|
+
# Auxiliary method to generate an authentication token for login in via
|
118
108
|
# the ClientLogin API.
|
119
109
|
#
|
120
110
|
# Args:
|
@@ -122,11 +112,18 @@ module AdsCommon
|
|
122
112
|
# accessed
|
123
113
|
#
|
124
114
|
# Returns:
|
125
|
-
# The auth token for the account (as a string)
|
115
|
+
# - The auth token for the account (as a string)
|
126
116
|
#
|
127
117
|
# Raises:
|
128
|
-
# AdsCommon::Errors::AuthError if authentication fails
|
118
|
+
# - AdsCommon::Errors::AuthError if authentication fails
|
129
119
|
#
|
120
|
+
def create_token(credentials)
|
121
|
+
token = @config.read('authentication.auth_token') ||
|
122
|
+
generate_token(credentials)
|
123
|
+
return token
|
124
|
+
end
|
125
|
+
|
126
|
+
# Generates new client login token based on credentials.
|
130
127
|
def generate_token(credentials)
|
131
128
|
validate_credentials(credentials)
|
132
129
|
|
@@ -147,25 +144,23 @@ module AdsCommon
|
|
147
144
|
else
|
148
145
|
error_message = "Login failed for email %s: HTTP code %d." %
|
149
146
|
[credentials[:email], response.code]
|
150
|
-
|
151
|
-
|
152
|
-
else
|
153
|
-
error_message += " Raw error: %s." % response.body
|
154
|
-
end
|
147
|
+
error_str = results[:Error] || response.body
|
148
|
+
error_message += " Error: %s." % error_str if error_str
|
155
149
|
if results.include?(:Info)
|
156
150
|
error_message += " Info: %s." % results[:Info]
|
157
151
|
end
|
158
|
-
raise AdsCommon::Errors::AuthError,
|
152
|
+
raise AdsCommon::Errors::AuthError.new(error_message, error_str,
|
153
|
+
results[:Info])
|
159
154
|
end
|
160
155
|
end
|
161
156
|
|
162
157
|
# Extracts key-value pairs from ClientLogin server response.
|
163
158
|
#
|
164
159
|
# Args:
|
165
|
-
# - text: server response string
|
160
|
+
# - text: server response string
|
166
161
|
#
|
167
162
|
# Returns:
|
168
|
-
# Hash of key-value pairs
|
163
|
+
# Hash of key-value pairs
|
169
164
|
#
|
170
165
|
def parse_token_text(text)
|
171
166
|
result = {}
|
@@ -0,0 +1,260 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
#
|
3
|
+
# Authors:: api.dklimkin@gmail.com (Danial Klimkin)
|
4
|
+
#
|
5
|
+
# Copyright:: Copyright 2011, Google Inc. All Rights Reserved.
|
6
|
+
#
|
7
|
+
# License:: Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
16
|
+
# implied.
|
17
|
+
# See the License for the specific language governing permissions and
|
18
|
+
# limitations under the License.
|
19
|
+
#
|
20
|
+
# This module manages OAuth authentication.
|
21
|
+
|
22
|
+
require 'oauth'
|
23
|
+
|
24
|
+
require 'ads_common/auth/base_handler'
|
25
|
+
require 'ads_common/errors'
|
26
|
+
|
27
|
+
module AdsCommon
|
28
|
+
module Auth
|
29
|
+
|
30
|
+
# Credentials class to handle OAuth authentication.
|
31
|
+
class OAuthHandler < AdsCommon::Auth::BaseHandler
|
32
|
+
IGNORED_FIELDS = [
|
33
|
+
:email, :password, :auth_token,
|
34
|
+
:oauth_verification_code, :oauth_consumer_secret, :oauth_consumer_key
|
35
|
+
]
|
36
|
+
|
37
|
+
OAUTH_CONFIG = {
|
38
|
+
:site => "https://www.google.com",
|
39
|
+
:request_token_path => "/accounts/OAuthGetRequestToken",
|
40
|
+
:access_token_path => "/accounts/OAuthGetAccessToken",
|
41
|
+
:authorize_path => "/accounts/OAuthAuthorizeToken"
|
42
|
+
}
|
43
|
+
|
44
|
+
DEFAULT_CALLBACK = 'oob'
|
45
|
+
DEFAULT_METHOD = 'HMAC-SHA1'
|
46
|
+
|
47
|
+
# Initializes the OAuthHandler with all the necessary details.
|
48
|
+
#
|
49
|
+
# Args:
|
50
|
+
# - config: Config object with library configuration
|
51
|
+
# - scope: OAuth authorization scope
|
52
|
+
#
|
53
|
+
def initialize(config, scope)
|
54
|
+
super(config)
|
55
|
+
@scope = scope
|
56
|
+
end
|
57
|
+
|
58
|
+
def handle_error(error)
|
59
|
+
# TODO: Add support.
|
60
|
+
raise error
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns all of the fields that this auth handler will fill.
|
64
|
+
#
|
65
|
+
# Args:
|
66
|
+
# - credentials: request credentials
|
67
|
+
#
|
68
|
+
# Returns:
|
69
|
+
# - array with header names
|
70
|
+
#
|
71
|
+
def header_list(credentials)
|
72
|
+
result = credentials.keys.map.reject do |field|
|
73
|
+
IGNORED_FIELDS.include?(field)
|
74
|
+
end
|
75
|
+
result << :access_token
|
76
|
+
return result
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns all of the credentials received from the CredentialHandler,
|
80
|
+
# except for ignored fields.
|
81
|
+
#
|
82
|
+
# Args:
|
83
|
+
# - credentials: request credentials
|
84
|
+
#
|
85
|
+
# Returns:
|
86
|
+
# - hash with header names and values
|
87
|
+
#
|
88
|
+
def headers(credentials)
|
89
|
+
result = credentials.reject do |field, value|
|
90
|
+
IGNORED_FIELDS.include?(field)
|
91
|
+
end
|
92
|
+
result[:access_token] = get_token(credentials)
|
93
|
+
return result
|
94
|
+
end
|
95
|
+
|
96
|
+
# Returns OAuth-specific Consumer object.
|
97
|
+
def get_oauth_consumer()
|
98
|
+
return @consumer
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
# Auxiliary method to validate the credentials for token generation.
|
104
|
+
#
|
105
|
+
# Args:
|
106
|
+
# - credentials: a hash with the credentials for the account being
|
107
|
+
# accessed
|
108
|
+
#
|
109
|
+
# Raises:
|
110
|
+
# - AdsCommon::Errors::AuthError if validation fails
|
111
|
+
#
|
112
|
+
def validate_credentials(credentials)
|
113
|
+
if credentials.nil?
|
114
|
+
raise AdsCommon::Errors::AuthError, 'No credentials supplied.'
|
115
|
+
end
|
116
|
+
|
117
|
+
if credentials[:oauth_consumer_key].nil?
|
118
|
+
raise AdsCommon::Errors::AuthError,
|
119
|
+
'Consumer key not included in credentials.'
|
120
|
+
end
|
121
|
+
|
122
|
+
if credentials[:oauth_consumer_secret].nil?
|
123
|
+
raise AdsCommon::Errors::AuthError,
|
124
|
+
'Consumer secret not included in credentials.'
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Auxiliary method to generate an authentication token for logging via
|
129
|
+
# the OAuth API.
|
130
|
+
#
|
131
|
+
# Args:
|
132
|
+
# - credentials: a hash with the credentials for the account being
|
133
|
+
# accessed
|
134
|
+
#
|
135
|
+
# Returns:
|
136
|
+
# - The auth token for the account (as an AccessToken)
|
137
|
+
#
|
138
|
+
# Raises:
|
139
|
+
# - AdsCommon::Errors::AuthError if authentication fails
|
140
|
+
# - AdsCommon::Errors::OAuthVerificationRequired if OAuth verification
|
141
|
+
# code required
|
142
|
+
#
|
143
|
+
def create_token(credentials)
|
144
|
+
validate_credentials(credentials)
|
145
|
+
if @consumer.nil?
|
146
|
+
oauth_config = OAUTH_CONFIG.merge({:scope => @scope})
|
147
|
+
@consumer = OAuth::Consumer.new(credentials[:oauth_consumer_key],
|
148
|
+
credentials[:oauth_consumer_secret], oauth_config)
|
149
|
+
end
|
150
|
+
return create_token_from_credentials(credentials) ||
|
151
|
+
generate_access_token(credentials)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Creates access token based on data from credentials.
|
155
|
+
#
|
156
|
+
# Args:
|
157
|
+
# - credentials: a hash with the credentials for the account being
|
158
|
+
# accessed
|
159
|
+
#
|
160
|
+
# Returns:
|
161
|
+
# - The auth token for the account (as an AccessToken)
|
162
|
+
#
|
163
|
+
def create_token_from_credentials(credentials)
|
164
|
+
access_token = nil
|
165
|
+
|
166
|
+
token = credentials[:oauth_token]
|
167
|
+
if !token.nil? and !token.empty?
|
168
|
+
method = credentials[:oauth_method] || DEFAULT_METHOD
|
169
|
+
access_token = case method
|
170
|
+
when 'RSA-SHA1'
|
171
|
+
OAuth::AccessToken.from_hash(@consumer, {:oauth_token => token})
|
172
|
+
when 'HMAC-SHA1'
|
173
|
+
token_secret = credentials[:oauth_token_secret]
|
174
|
+
if token_secret.nil? or token_secret.empty?
|
175
|
+
@logger.warn(("The 'token' specified for method %s but " +
|
176
|
+
"'token secret' is not available, ignoring token") % method)
|
177
|
+
nil
|
178
|
+
else
|
179
|
+
OAuth::AccessToken.from_hash(@consumer, {
|
180
|
+
:oauth_token => token, :oauth_token_secret => token_secret})
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
return access_token
|
185
|
+
end
|
186
|
+
|
187
|
+
# Generates new request tokens and authorizes it to get access token.
|
188
|
+
#
|
189
|
+
# Args:
|
190
|
+
# - credentials: a hash with the credentials for the account being
|
191
|
+
# accessed
|
192
|
+
#
|
193
|
+
# Returns:
|
194
|
+
# - The auth token for the account (as an AccessToken)
|
195
|
+
#
|
196
|
+
def generate_access_token(credentials)
|
197
|
+
token = nil
|
198
|
+
callback = credentials[:oauth_callback] || DEFAULT_CALLBACK
|
199
|
+
begin
|
200
|
+
if @request_token.nil?
|
201
|
+
@request_token = @consumer.get_request_token(
|
202
|
+
{:oauth_callback => callback}, {:scope => @scope})
|
203
|
+
raise_oauth_verification_error(@request_token, callback)
|
204
|
+
end
|
205
|
+
verification_code = credentials[:oauth_verification_code]
|
206
|
+
if verification_code.nil? || verification_code.empty?
|
207
|
+
raise_oauth_verification_error(@request_token, callback)
|
208
|
+
else
|
209
|
+
token = @request_token.get_access_token(
|
210
|
+
{:oauth_verifier => verification_code})
|
211
|
+
@request_token = nil
|
212
|
+
end
|
213
|
+
rescue OAuth::Unauthorized => e
|
214
|
+
if @request_token
|
215
|
+
raise_oauth_verification_error(@request_token, callback)
|
216
|
+
else
|
217
|
+
raise AdsCommon::Errors::AuthError,
|
218
|
+
"Authorization error occured: %s" % e
|
219
|
+
end
|
220
|
+
end
|
221
|
+
return token
|
222
|
+
end
|
223
|
+
|
224
|
+
# Raises a OAuthVerificationRequired error with auth URL for given
|
225
|
+
# request token.
|
226
|
+
#
|
227
|
+
# Args:
|
228
|
+
# - request_token: an initialized OAuth request token
|
229
|
+
# - callback: OAuth callback URL
|
230
|
+
#
|
231
|
+
# Returns:
|
232
|
+
# - never returns
|
233
|
+
#
|
234
|
+
# Raises:
|
235
|
+
# - AdsCommon::Errors::OAuthVerificationRequired in all cases
|
236
|
+
#
|
237
|
+
def raise_oauth_verification_error(request_token, callback)
|
238
|
+
oauth_url = request_token.authorize_url({:oauth_callback => callback})
|
239
|
+
raise AdsCommon::Errors::OAuthVerificationRequired, oauth_url
|
240
|
+
end
|
241
|
+
|
242
|
+
# Extracts key-value pairs from OAuth server response.
|
243
|
+
#
|
244
|
+
# Args:
|
245
|
+
# - text: server response string
|
246
|
+
#
|
247
|
+
# Returns:
|
248
|
+
# - Hash of key-value pairs
|
249
|
+
#
|
250
|
+
def parse_token_text(text)
|
251
|
+
result = {}
|
252
|
+
text.split('&').each do |line|
|
253
|
+
key, value = line.split("=")
|
254
|
+
result[key.to_sym] = value
|
255
|
+
end
|
256
|
+
return result
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|