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.
Files changed (30) hide show
  1. data/ChangeLog +5 -0
  2. data/README +1 -4
  3. data/Rakefile +2 -2
  4. data/lib/ads_common/api.rb +106 -16
  5. data/lib/ads_common/api_config.rb +2 -3
  6. data/lib/ads_common/auth/base_handler.rb +22 -3
  7. data/lib/ads_common/auth/client_login_handler.rb +27 -32
  8. data/lib/ads_common/auth/oauth_handler.rb +260 -0
  9. data/lib/ads_common/build/savon_abstract_generator.rb +12 -11
  10. data/lib/ads_common/build/savon_generator.rb +31 -27
  11. data/lib/ads_common/build/savon_registry.rb +46 -23
  12. data/lib/ads_common/build/savon_registry_generator.rb +23 -10
  13. data/lib/ads_common/build/savon_service_generator.rb +17 -3
  14. data/lib/ads_common/config.rb +1 -1
  15. data/lib/ads_common/credential_handler.rb +3 -7
  16. data/lib/ads_common/errors.rb +18 -6
  17. data/lib/ads_common/savon_headers/base_header_handler.rb +80 -0
  18. data/lib/ads_common/{soap4r_logger.rb → savon_headers/httpi_request_proxy.rb} +27 -20
  19. data/lib/ads_common/savon_headers/oauth_header_handler.rb +92 -0
  20. data/lib/ads_common/savon_headers/simple_header_handler.rb +17 -49
  21. data/lib/ads_common/savon_service.rb +129 -41
  22. data/test/test_savon_service.rb +9 -4
  23. metadata +39 -43
  24. data/lib/ads_common/build/rake_common.rb +0 -343
  25. data/lib/ads_common/build/soap4r_generator.rb +0 -565
  26. data/lib/ads_common/savon_headers/client_login_header_handler.rb +0 -60
  27. data/lib/ads_common/soap4r_headers/nested_header_handler.rb +0 -50
  28. data/lib/ads_common/soap4r_headers/single_header_handler.rb +0 -44
  29. data/lib/ads_common/soap4r_patches.rb +0 -210
  30. data/lib/ads_common/soap4r_response_handler.rb +0 -80
data/ChangeLog CHANGED
@@ -1,3 +1,8 @@
1
+ 0.5.0:
2
+ - Now support Savon 0.9.6.
3
+ - No longer depend on Soap4r.
4
+ - Support for OAuth authentication method.
5
+
1
6
  0.4.0:
2
7
  - Extracted soap4r specific code from main path.
3
8
  - APIs with Savon backend are now ruby1.9 - compatible.
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
- - soap4r v1.5.8 (only for soap4r-based libraries);
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', '~> 0.9.1')
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'
@@ -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
- @wrappers = Hash.new
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 create an authentication handler. Needs to be
136
- # implemented on the specific API because of the different nesting models
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
- raise NotImplementedError, 'create_auth_handler not overriden.'
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 wrapper. Needs to be implemented on
151
- # specific API level.
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. Must be a symbol.
155
- # - service: name for the intended service. Must be a symbol.
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 service.
224
+ # - a simplified wrapper generated for the driver
159
225
  #
160
226
  def prepare_wrapper(version, service)
161
- raise NotImplementedError, 'prepare_wrapper not overriden.'
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.4.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 2010, Google Inc. All Rights Reserved.
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.sgomes@gmail.com (Sérgio Gomes)
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
- @token = config.read('authentication.auth_token')
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
- case prop
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
- result << :authToken
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 email and password.
70
+ # except for ignored fields.
78
71
  def headers(credentials)
79
- @token = generate_token(credentials) if @token == nil
80
- result = {}
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 logging via
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
- if results.include?(:Error)
151
- error_message += " Error: %s." % results[:Error]
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, error_message
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