google-ads-common 0.7.2 → 0.7.3

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog CHANGED
@@ -1,3 +1,9 @@
1
+ 0.7.3:
2
+ - Compatibility with Savon -> 1.0.0 (not backward-compatible).
3
+ - Now require HTTPI ~> 1.0.0.
4
+ - HttpClient is no longer explicitely required.
5
+ - Support for OAuth2.0 authorization method.
6
+
1
7
  0.7.2:
2
8
  - Compatibility with savon-0.9.10 (not backward-compatible).
3
9
 
data/README CHANGED
@@ -4,9 +4,6 @@ This gem is a dependency for the new generation of Ruby Google Ads client
4
4
  libraries. It contains common code shared among all of these libraries, such as
5
5
  authentication, SOAP stub generation, error handling, logging, etc.
6
6
 
7
- This is an early preview release, so a lot of things are still missing!
8
-
9
-
10
7
  = Docs for Users
11
8
 
12
9
  == 1 - Installation:
@@ -14,13 +11,12 @@ This is an early preview release, so a lot of things are still missing!
14
11
  google-ads-common is a Ruby gem. See http://docs.rubygems.org/read/book/1
15
12
 
16
13
  Install it using the gem install command.
17
- $ gem install --local google-ads-common-VERSION.gem
14
+ $ gem install google-ads-common
18
15
 
19
16
  The following gem libraries are required:
20
- - savon v0.9.9 or greater;
21
- - httpi v0.9.7 or greater;
22
- - httpclient v2.2.3 or greater;
23
- - oauth v0.4.5 or greater.
17
+ - savon
18
+ - httpi
19
+ - oauth
24
20
 
25
21
  = Docs for Developers
26
22
 
@@ -26,6 +26,7 @@ require 'ads_common/errors'
26
26
  require 'ads_common/utils'
27
27
  require 'ads_common/auth/client_login_handler'
28
28
  require 'ads_common/auth/oauth_handler'
29
+ require 'ads_common/auth/oauth2_handler'
29
30
 
30
31
  module AdsCommon
31
32
  class Api
@@ -110,12 +111,15 @@ module AdsCommon
110
111
  begin
111
112
  credentials = @credential_handler.credentials
112
113
  token = @auth_handler.get_token(credentials)
113
- rescue AdsCommon::Errors::OAuthVerificationRequired => e
114
+ rescue AdsCommon::Errors::OAuthVerificationRequired,
115
+ AdsCommon::Errors::OAuth2VerificationRequired => e
114
116
  verification_code = (block_given?) ? yield(e.oauth_url) : nil
115
117
  # Retry with verification code if one provided.
116
118
  if verification_code
117
- @credential_handler.set_credential(
118
- :oauth_verification_code, verification_code)
119
+ code_symbol =
120
+ e.kind_of?(AdsCommon::Errors::OAuthVerificationRequired) ?
121
+ :oauth_verification_code : :oauth2_verification_code
122
+ @credential_handler.set_credential(code_symbol, verification_code)
119
123
  retry
120
124
  else
121
125
  raise e
@@ -193,6 +197,13 @@ module AdsCommon
193
197
  @config,
194
198
  api_config.environment_config(environment, :oauth_scope)
195
199
  )
200
+ when :OAUTH2
201
+ environment = @config.read('service.environment',
202
+ api_config.default_environment())
203
+ AdsCommon::Auth::OAuth2Handler.new(
204
+ @config,
205
+ api_config.environment_config(environment, :oauth_scope)
206
+ )
196
207
  else
197
208
  raise AdsCommon::Errors::Error,
198
209
  "Unknown authentication method '%s'" % auth_method
@@ -0,0 +1,219 @@
1
+ # Encoding: utf-8
2
+ #
3
+ # Authors:: api.dklimkin@gmail.com (Danial Klimkin)
4
+ #
5
+ # Copyright:: Copyright 2012, 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 OAuth2.0 authentication.
21
+
22
+ require 'oauth2'
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 OAuth2.0 authentication.
31
+ class OAuth2Handler < AdsCommon::Auth::BaseHandler
32
+ OAUTH2_CONFIG = {
33
+ :site => 'https://accounts.google.com',
34
+ :authorize_url => '/o/oauth2/auth',
35
+ :token_url => '/o/oauth2/token'
36
+ }
37
+ OAUTH2_HEADER = 'Bearer %s'
38
+ DEFAULT_CALLBACK = 'urn:ietf:wg:oauth:2.0:oob'
39
+
40
+ # Initializes the OAuthHandler2 with all the necessary details.
41
+ #
42
+ # Args:
43
+ # - config: Config object with library configuration
44
+ # - scope: OAuth authorization scope
45
+ #
46
+ def initialize(config, scope)
47
+ super(config)
48
+ @scope = scope
49
+ end
50
+
51
+ # Invalidates the stored token if the required credential has changed.
52
+ def property_changed(prop, value)
53
+ oauth2_keys = [:oauth2_client_id, :oauth2_client_secret, :oauth2_token]
54
+ if oauth2_keys.include?(prop)
55
+ @token, @client = nil, nil
56
+ end
57
+ end
58
+
59
+ def handle_error(error)
60
+ # TODO: Add support.
61
+ get_logger().error(error)
62
+ raise error
63
+ end
64
+
65
+ # Returns authorization string.
66
+ def auth_string(credentials, request = nil)
67
+ return generate_oauth2_parameters_string(credentials)
68
+ end
69
+
70
+ # Overrides base get_token method to account for the token expiration.
71
+ def get_token(credentials = nil)
72
+ refresh_token! if !@token.nil? && @token.expired?
73
+ return super(credentials)
74
+ end
75
+
76
+ # Refreshes access token from refresh token.
77
+ def refresh_token!()
78
+ return nil if @token.nil? or @token.refresh_token.nil?
79
+ @token = @token.refresh!
80
+ return @token
81
+ end
82
+
83
+ private
84
+
85
+ # Generates auth string for OAuth2.0 method of authentication.
86
+ #
87
+ # Args:
88
+ # - credentials: credentials set for authorization
89
+ #
90
+ # Returns:
91
+ # - Authentication string
92
+ #
93
+ def generate_oauth2_parameters_string(credentials)
94
+ token = get_token(credentials)
95
+ return OAUTH2_HEADER % token.token
96
+ end
97
+
98
+ # Auxiliary method to validate the credentials for token generation.
99
+ #
100
+ # Args:
101
+ # - credentials: a hash with the credentials for the account being
102
+ # accessed
103
+ #
104
+ # Raises:
105
+ # - AdsCommon::Errors::AuthError if validation fails
106
+ #
107
+ def validate_credentials(credentials)
108
+ if @scope.nil?
109
+ raise AdsCommon::Errors::AuthError, 'Scope is not specified.'
110
+ end
111
+
112
+ if credentials.nil?
113
+ raise AdsCommon::Errors::AuthError, 'No credentials supplied.'
114
+ end
115
+
116
+ if credentials[:oauth2_client_id].nil?
117
+ raise AdsCommon::Errors::AuthError,
118
+ 'Client id is not included in the credentials.'
119
+ end
120
+
121
+ if credentials[:oauth2_client_secret].nil?
122
+ raise AdsCommon::Errors::AuthError,
123
+ 'Client secret is not included in the credentials.'
124
+ end
125
+ end
126
+
127
+ # Auxiliary method to generate an authentication token for logging via
128
+ # the OAuth2.0 API.
129
+ #
130
+ # Args:
131
+ # - credentials: a hash with the credentials for the account being
132
+ # accessed
133
+ #
134
+ # Returns:
135
+ # - The auth token for the account (as an AccessToken)
136
+ #
137
+ # Raises:
138
+ # - AdsCommon::Errors::AuthError if authentication fails
139
+ # - AdsCommon::Errors::OAuthVerificationRequired if OAuth verification
140
+ # code required
141
+ #
142
+ def create_token(credentials)
143
+ validate_credentials(credentials)
144
+ @client ||= create_client(credentials)
145
+ return create_token_from_credentials(credentials, @client) ||
146
+ generate_access_token(credentials, @client)
147
+ end
148
+
149
+ def create_client(credentials)
150
+ oauth2_config = OAUTH2_CONFIG.dup()
151
+ proxy = @config.read('connection.proxy')
152
+ unless proxy.nil?
153
+ oauth2_config.merge!({:connection_opts => {:proxy => proxy}})
154
+ end
155
+ client = OAuth2::Client.new(credentials[:oauth2_client_id],
156
+ credentials[:oauth2_client_secret],
157
+ oauth2_config)
158
+ return client
159
+ end
160
+
161
+ # Creates access token based on data from credentials.
162
+ #
163
+ # Args:
164
+ # - credentials: a hash with the credentials for the account being
165
+ # accessed. Has to include :oauth2_token key with a hash value that
166
+ # represents a stored OAuth2 access token
167
+ # - client: OAuth2 client for the current configuration
168
+ #
169
+ # Returns:
170
+ # - The auth token for the account (as an AccessToken)
171
+ #
172
+ def create_token_from_credentials(credentials, client)
173
+ access_token = nil
174
+ oauth2_token_hash = credentials[:oauth2_token]
175
+ if !oauth2_token_hash.nil? && oauth2_token_hash.kind_of?(Hash)
176
+ access_token = OAuth2::AccessToken.new(
177
+ client, {:access_token => oauth2_token_hash})
178
+ end
179
+ return access_token
180
+ end
181
+
182
+ # Generates new request tokens and authorizes it to get access token.
183
+ #
184
+ # Args:
185
+ # - credentials: a hash with the credentials for the account being
186
+ # accessed
187
+ # - client: OAuth2 client for the current configuration
188
+ #
189
+ # Returns:
190
+ # - The auth token for the account (as an AccessToken)
191
+ #
192
+ def generate_access_token(credentials, client)
193
+ token = nil
194
+ begin
195
+ callback = credentials[:oauth2_callback] || DEFAULT_CALLBACK
196
+ verification_code = credentials[:oauth2_verification_code]
197
+ if verification_code.nil? || verification_code.empty?
198
+ auth_options = {
199
+ :redirect_uri => callback,
200
+ :scope => @scope,
201
+ :state => credentials[:oauth2_state],
202
+ :access_type => credentials[:oauth2_access_type],
203
+ :approval_prompt => credentials[:oauth2_approval_prompt]
204
+ }.reject {|k, v| v.nil?}
205
+ auth_url = client.auth_code.authorize_url(auth_options)
206
+ raise AdsCommon::Errors::OAuth2VerificationRequired.new(auth_url)
207
+ else
208
+ token = @client.auth_code.get_token(verification_code,
209
+ {:redirect_uri => callback})
210
+ end
211
+ rescue OAuth::Unauthorized => e
212
+ raise AdsCommon::Errors::AuthError,
213
+ 'Authorization error occured: %s' % e
214
+ end
215
+ return token
216
+ end
217
+ end
218
+ end
219
+ end
@@ -17,7 +17,7 @@
17
17
  # See the License for the specific language governing permissions and
18
18
  # limitations under the License.
19
19
  #
20
- # This module manages OAuth authentication.
20
+ # This module manages OAuth1.0a authentication.
21
21
 
22
22
  require 'oauth'
23
23
 
@@ -63,11 +63,6 @@ module AdsCommon
63
63
  raise error
64
64
  end
65
65
 
66
- # Returns OAuth-specific Consumer object.
67
- def get_oauth_consumer()
68
- return @consumer
69
- end
70
-
71
66
  # Returns authorization string.
72
67
  def auth_string(credentials, request)
73
68
  if request.nil?
@@ -257,23 +252,6 @@ module AdsCommon
257
252
  oauth_url, request_token)
258
253
  raise error
259
254
  end
260
-
261
- # Extracts key-value pairs from OAuth server response.
262
- #
263
- # Args:
264
- # - text: server response string
265
- #
266
- # Returns:
267
- # - Hash of key-value pairs
268
- #
269
- def parse_token_text(text)
270
- result = {}
271
- text.split('&').each do |line|
272
- key, value = line.split("=")
273
- result[key.to_sym] = value
274
- end
275
- return result
276
- end
277
255
  end
278
256
  end
279
257
  end
@@ -23,6 +23,8 @@
23
23
  require 'savon'
24
24
  require 'rexml/document'
25
25
 
26
+ require 'ads_common/utils'
27
+
26
28
  module AdsCommon
27
29
  module Build
28
30
  # Contains the methods that extracts WSDL data.
@@ -19,6 +19,8 @@
19
19
  #
20
20
  # Generic class to handle credentials across client libraries.
21
21
 
22
+ require 'ads_common/api_config'
23
+
22
24
  module AdsCommon
23
25
  class CredentialHandler
24
26
 
@@ -84,7 +86,7 @@ module AdsCommon
84
86
  agent_app ||= $0
85
87
  agent_data = extra_ids
86
88
  agent_data << "Common-Ruby/%s" % AdsCommon::ApiConfig::CLIENT_LIB_VERSION
87
- agent_data << "Savon/%s" % Savon::Version
89
+ agent_data << "Savon/%s" % Savon::VERSION
88
90
  ruby_engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'
89
91
  agent_data << [ruby_engine, RUBY_VERSION].join('/')
90
92
  agent_data << "HTTPI/%s" % HTTPI::VERSION
@@ -39,7 +39,7 @@ module AdsCommon
39
39
  end
40
40
  end
41
41
 
42
- # Raised when OAuth access token is required.
42
+ # Raised when OAuth1.0a access token is required.
43
43
  class OAuthVerificationRequired < AuthError
44
44
  attr_reader :oauth_url, :request_token
45
45
  def initialize(oauth_url, request_token)
@@ -48,6 +48,15 @@ module AdsCommon
48
48
  end
49
49
  end
50
50
 
51
+ # Raised when OAuth2.0 access token is required.
52
+ class OAuth2VerificationRequired < AuthError
53
+ attr_reader :oauth_url
54
+ def initialize(oauth_url)
55
+ super()
56
+ @oauth_url = oauth_url
57
+ end
58
+ end
59
+
51
60
  # Raised when ClientLogin Captcha challenge occurs.
52
61
  class CaptchaRequiredError < AuthError
53
62
  attr_reader :error, :captcha_token
@@ -21,6 +21,6 @@
21
21
 
22
22
  module AdsCommon
23
23
  module ApiConfig
24
- CLIENT_LIB_VERSION = '0.7.2'
24
+ CLIENT_LIB_VERSION = '0.7.3'
25
25
  end
26
26
  end
@@ -52,4 +52,11 @@ class TestCredentialHandler < Test::Unit::TestCase
52
52
  assert_equal(42, credentials[:client_customer_id])
53
53
  assert_equal('bar', credentials[:foo])
54
54
  end
55
+
56
+ def test_generate_user_agent_simple()
57
+ result1 = @handler.generate_http_user_agent()
58
+ assert_kind_of(String, result1)
59
+ result2 = @handler.generate_soap_user_agent()
60
+ assert_kind_of(String, result2)
61
+ end
55
62
  end
metadata CHANGED
@@ -1,88 +1,97 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: google-ads-common
3
- version: !ruby/object:Gem::Version
4
- version: 0.7.2
3
+ version: !ruby/object:Gem::Version
4
+ hash: 5
5
5
  prerelease:
6
+ segments:
7
+ - 0
8
+ - 7
9
+ - 3
10
+ version: 0.7.3
6
11
  platform: ruby
7
- authors:
12
+ authors:
8
13
  - Sergio Gomes
9
14
  - Danial Klimkin
10
15
  autorequire:
11
16
  bindir: bin
12
17
  cert_chain: []
13
- date: 2012-06-08 00:00:00.000000000 Z
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
18
+
19
+ date: 2012-06-21 00:00:00 Z
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
16
22
  name: savon
17
- requirement: !ruby/object:Gem::Requirement
18
- none: false
19
- requirements:
20
- - - ~>
21
- - !ruby/object:Gem::Version
22
- version: 0.9.10
23
- type: :runtime
24
23
  prerelease: false
25
- version_requirements: !ruby/object:Gem::Requirement
26
- none: false
27
- requirements:
28
- - - ~>
29
- - !ruby/object:Gem::Version
30
- version: 0.9.10
31
- - !ruby/object:Gem::Dependency
32
- name: httpclient
33
- requirement: !ruby/object:Gem::Requirement
24
+ requirement: &id001 !ruby/object:Gem::Requirement
34
25
  none: false
35
- requirements:
26
+ requirements:
36
27
  - - ~>
37
- - !ruby/object:Gem::Version
38
- version: 2.2.3
28
+ - !ruby/object:Gem::Version
29
+ hash: 23
30
+ segments:
31
+ - 1
32
+ - 0
33
+ - 0
34
+ version: 1.0.0
39
35
  type: :runtime
40
- prerelease: false
41
- version_requirements: !ruby/object:Gem::Requirement
42
- none: false
43
- requirements:
44
- - - ~>
45
- - !ruby/object:Gem::Version
46
- version: 2.2.3
47
- - !ruby/object:Gem::Dependency
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
48
38
  name: httpi
49
- requirement: !ruby/object:Gem::Requirement
50
- none: false
51
- requirements:
52
- - - ~>
53
- - !ruby/object:Gem::Version
54
- version: 0.9.7
55
- type: :runtime
56
39
  prerelease: false
57
- version_requirements: !ruby/object:Gem::Requirement
40
+ requirement: &id002 !ruby/object:Gem::Requirement
58
41
  none: false
59
- requirements:
42
+ requirements:
60
43
  - - ~>
61
- - !ruby/object:Gem::Version
62
- version: 0.9.7
63
- - !ruby/object:Gem::Dependency
44
+ - !ruby/object:Gem::Version
45
+ hash: 23
46
+ segments:
47
+ - 1
48
+ - 0
49
+ - 0
50
+ version: 1.0.0
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
64
54
  name: oauth
65
- requirement: !ruby/object:Gem::Requirement
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
66
57
  none: false
67
- requirements:
58
+ requirements:
68
59
  - - ~>
69
- - !ruby/object:Gem::Version
60
+ - !ruby/object:Gem::Version
61
+ hash: 5
62
+ segments:
63
+ - 0
64
+ - 4
65
+ - 5
70
66
  version: 0.4.5
71
67
  type: :runtime
68
+ version_requirements: *id003
69
+ - !ruby/object:Gem::Dependency
70
+ name: oauth2
72
71
  prerelease: false
73
- version_requirements: !ruby/object:Gem::Requirement
72
+ requirement: &id004 !ruby/object:Gem::Requirement
74
73
  none: false
75
- requirements:
74
+ requirements:
76
75
  - - ~>
77
- - !ruby/object:Gem::Version
78
- version: 0.4.5
76
+ - !ruby/object:Gem::Version
77
+ hash: 1
78
+ segments:
79
+ - 0
80
+ - 7
81
+ - 1
82
+ version: 0.7.1
83
+ type: :runtime
84
+ version_requirements: *id004
79
85
  description: Essential utilities shared by all Ads Ruby client libraries
80
- email:
86
+ email:
81
87
  - api.dklimkin@gmail.com
82
88
  executables: []
89
+
83
90
  extensions: []
91
+
84
92
  extra_rdoc_files: []
85
- files:
93
+
94
+ files:
86
95
  - lib/ads_common/api.rb
87
96
  - lib/ads_common/utils.rb
88
97
  - lib/ads_common/version.rb
@@ -90,6 +99,7 @@ files:
90
99
  - lib/ads_common/savon_service.rb
91
100
  - lib/ads_common/api_config.rb
92
101
  - lib/ads_common/auth/client_login_handler.rb
102
+ - lib/ads_common/auth/oauth2_handler.rb
93
103
  - lib/ads_common/auth/oauth_handler.rb
94
104
  - lib/ads_common/auth/base_handler.rb
95
105
  - lib/ads_common/http.rb
@@ -120,29 +130,40 @@ files:
120
130
  - ChangeLog
121
131
  homepage: http://code.google.com/p/google-api-ads-ruby/
122
132
  licenses: []
133
+
123
134
  post_install_message:
124
135
  rdoc_options: []
125
- require_paths:
136
+
137
+ require_paths:
126
138
  - lib
127
- required_ruby_version: !ruby/object:Gem::Requirement
139
+ required_ruby_version: !ruby/object:Gem::Requirement
128
140
  none: false
129
- requirements:
130
- - - ! '>='
131
- - !ruby/object:Gem::Version
132
- version: '0'
133
- required_rubygems_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ hash: 3
145
+ segments:
146
+ - 0
147
+ version: "0"
148
+ required_rubygems_version: !ruby/object:Gem::Requirement
134
149
  none: false
135
- requirements:
136
- - - ! '>='
137
- - !ruby/object:Gem::Version
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ hash: 23
154
+ segments:
155
+ - 1
156
+ - 3
157
+ - 6
138
158
  version: 1.3.6
139
159
  requirements: []
160
+
140
161
  rubyforge_project: google-ads-common
141
162
  rubygems_version: 1.8.24
142
163
  signing_key:
143
164
  specification_version: 3
144
165
  summary: Common code for Google Ads APIs
145
- test_files:
166
+ test_files:
146
167
  - test/test_savon_service.rb
147
168
  - test/test_results_extractor.rb
148
169
  - test/test_credential_handler.rb