google-ads-common 0.8.2 → 0.9.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 CHANGED
@@ -1,3 +1,8 @@
1
+ 0.9.0:
2
+ - Switched to Signet OAuth implementation.
3
+ - Removed deprecated OAuth1.0a scheme, please use OAuth2.0 instead.
4
+ - Support for JWT authorization (OAuth2 service accounts).
5
+
1
6
  0.8.2:
2
7
  - Fixed issue #89.
3
8
 
data/README CHANGED
@@ -16,8 +16,7 @@ Install it using the gem install command.
16
16
  The following gem libraries are required:
17
17
  - savon
18
18
  - httpi
19
- - oauth
20
- - oauth2
19
+ - signet
21
20
 
22
21
  = Docs for Developers
23
22
 
@@ -25,8 +25,8 @@ require 'ads_common/config'
25
25
  require 'ads_common/errors'
26
26
  require 'ads_common/utils'
27
27
  require 'ads_common/auth/client_login_handler'
28
- require 'ads_common/auth/oauth_handler'
29
28
  require 'ads_common/auth/oauth2_handler'
29
+ require 'ads_common/auth/oauth2_jwt_handler'
30
30
 
31
31
  module AdsCommon
32
32
  class Api
@@ -111,15 +111,12 @@ module AdsCommon
111
111
  begin
112
112
  credentials = @credential_handler.credentials
113
113
  token = @auth_handler.get_token(credentials)
114
- rescue AdsCommon::Errors::OAuthVerificationRequired,
115
- AdsCommon::Errors::OAuth2VerificationRequired => e
114
+ rescue AdsCommon::Errors::OAuth2VerificationRequired => e
116
115
  verification_code = (block_given?) ? yield(e.oauth_url) : nil
117
116
  # Retry with verification code if one provided.
118
117
  if 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)
118
+ @credential_handler.set_credential(
119
+ :oauth2_verification_code, verification_code)
123
120
  retry
124
121
  else
125
122
  raise e
@@ -195,16 +192,19 @@ module AdsCommon
195
192
  api_config.client_login_config(:LOGIN_SERVICE_NAME)
196
193
  )
197
194
  when :OAUTH
195
+ raise AdsCommon::Errors::Error,
196
+ 'OAuth authorization method is deprecated, use OAuth2 instead.'
197
+ when :OAUTH2
198
198
  environment = @config.read('service.environment',
199
199
  api_config.default_environment())
200
- AdsCommon::Auth::OAuthHandler.new(
200
+ AdsCommon::Auth::OAuth2Handler.new(
201
201
  @config,
202
202
  api_config.environment_config(environment, :oauth_scope)
203
203
  )
204
- when :OAUTH2
204
+ when :OAUTH2_JWT
205
205
  environment = @config.read('service.environment',
206
206
  api_config.default_environment())
207
- AdsCommon::Auth::OAuth2Handler.new(
207
+ AdsCommon::Auth::OAuth2JwtHandler.new(
208
208
  @config,
209
209
  api_config.environment_config(environment, :oauth_scope)
210
210
  )
@@ -52,7 +52,7 @@ module AdsCommon
52
52
  end
53
53
 
54
54
  # Returns authorization string. Needs to be overridden.
55
- def auth_string(credentials, request)
55
+ def auth_string(credentials)
56
56
  raise NotImplementedError, 'auth_string not overridden.'
57
57
  end
58
58
 
@@ -63,7 +63,7 @@ module AdsCommon
63
63
  end
64
64
 
65
65
  # Returns authorization string.
66
- def auth_string(credentials, request)
66
+ def auth_string(credentials)
67
67
  return [AUTH_PREFIX, get_token(credentials)].join
68
68
  end
69
69
 
@@ -19,7 +19,8 @@
19
19
  #
20
20
  # This module manages OAuth2.0 authentication.
21
21
 
22
- require 'oauth2'
22
+ require 'faraday'
23
+ require 'signet/oauth_2/client'
23
24
 
24
25
  require 'ads_common/auth/base_handler'
25
26
  require 'ads_common/errors'
@@ -30,11 +31,11 @@ module AdsCommon
30
31
  # Credentials class to handle OAuth2.0 authentication.
31
32
  class OAuth2Handler < AdsCommon::Auth::BaseHandler
32
33
  OAUTH2_CONFIG = {
33
- :site => 'https://accounts.google.com',
34
- :authorize_url => '/o/oauth2/auth',
35
- :token_url => '/o/oauth2/token'
34
+ :authorization_uri =>
35
+ 'https://accounts.google.com/o/oauth2/auth',
36
+ :token_credential_uri =>
37
+ 'https://accounts.google.com/o/oauth2/token'
36
38
  }
37
- OAUTH2_HEADER = 'Bearer %s'
38
39
  DEFAULT_CALLBACK = 'urn:ietf:wg:oauth:2.0:oob'
39
40
 
40
41
  # Initializes the OAuthHandler2 with all the necessary details.
@@ -45,7 +46,7 @@ module AdsCommon
45
46
  #
46
47
  def initialize(config, scope)
47
48
  super(config)
48
- @scope = scope
49
+ @scope, @client = scope, nil
49
50
  end
50
51
 
51
52
  # Invalidates the stored token if the required credential has changed.
@@ -62,40 +63,37 @@ module AdsCommon
62
63
  raise error
63
64
  end
64
65
 
65
- # Returns authorization string.
66
- def auth_string(credentials, request = nil)
67
- return generate_oauth2_parameters_string(credentials)
66
+ # Generates auth string for OAuth2.0 method of authentication.
67
+ #
68
+ # Args:
69
+ # - credentials: credentials set for authorization
70
+ #
71
+ # Returns:
72
+ # - Authentication string
73
+ #
74
+ def auth_string(credentials)
75
+ token = get_token(credentials)
76
+ return ::Signet::OAuth2.generate_bearer_authorization_header(
77
+ token[:access_token])
68
78
  end
69
79
 
70
80
  # Overrides base get_token method to account for the token expiration.
71
81
  def get_token(credentials = nil)
72
82
  token = super(credentials)
73
- token = refresh_token! if !token.nil? && token.expired?
74
- return oauth_token_to_hash(token)
83
+ token = refresh_token! if !@client.nil? && @client.expired?
84
+ return token
75
85
  end
76
86
 
77
87
  # Refreshes access token from refresh token.
78
88
  def refresh_token!()
79
- return nil if @token.nil? or @token.refresh_token.nil?
80
- @token = @token.refresh!
89
+ return nil if @token.nil? or @token[:refresh_token].nil?
90
+ @client.refresh!
91
+ @token = token_from_client(@client)
81
92
  return @token
82
93
  end
83
94
 
84
95
  private
85
96
 
86
- # Generates auth string for OAuth2.0 method of authentication.
87
- #
88
- # Args:
89
- # - credentials: credentials set for authorization
90
- #
91
- # Returns:
92
- # - Authentication string
93
- #
94
- def generate_oauth2_parameters_string(credentials)
95
- token = get_token(credentials)
96
- return OAUTH2_HEADER % token[:access_token]
97
- end
98
-
99
97
  # Auxiliary method to validate the credentials for token generation.
100
98
  #
101
99
  # Args:
@@ -154,15 +152,14 @@ module AdsCommon
154
152
  end
155
153
 
156
154
  def create_client(credentials)
157
- oauth2_config = OAUTH2_CONFIG.dup()
158
- proxy = @config.read('connection.proxy')
159
- unless proxy.nil?
160
- oauth2_config.merge!({:connection_opts => {:proxy => proxy}})
161
- end
162
- client = OAuth2::Client.new(credentials[:oauth2_client_id],
163
- credentials[:oauth2_client_secret],
164
- oauth2_config)
165
- return client
155
+ oauth_options = OAUTH2_CONFIG.merge({
156
+ :client_id => credentials[:oauth2_client_id],
157
+ :client_secret => credentials[:oauth2_client_secret],
158
+ :scope => @scope,
159
+ :redirect_uri => credentials[:oauth2_callback] || DEFAULT_CALLBACK,
160
+ :state => credentials[:oauth2_state]
161
+ }).reject {|k, v| v.nil?}
162
+ return Signet::OAuth2::Client.new(oauth_options)
166
163
  end
167
164
 
168
165
  # Creates access token based on data from credentials.
@@ -177,13 +174,12 @@ module AdsCommon
177
174
  # - The auth token for the account (as an AccessToken)
178
175
  #
179
176
  def create_token_from_credentials(credentials, client)
180
- access_token = nil
181
177
  oauth2_token_hash = credentials[:oauth2_token]
182
178
  if !oauth2_token_hash.nil? && oauth2_token_hash.kind_of?(Hash)
183
- token_data = oauth2_token_hash.dup()
184
- access_token = OAuth2::AccessToken.from_hash(client, token_data)
179
+ token_data = AdsCommon::Utils.hash_keys_to_str(oauth2_token_hash)
180
+ client.update_token!(token_data)
185
181
  end
186
- return access_token
182
+ return token_from_client(client)
187
183
  end
188
184
 
189
185
  # Generates new request tokens and authorizes it to get access token.
@@ -194,45 +190,43 @@ module AdsCommon
194
190
  # - client: OAuth2 client for the current configuration
195
191
  #
196
192
  # Returns:
197
- # - The auth token for the account (as an AccessToken)
193
+ # - The auth token for the account (as Hash)
198
194
  #
199
195
  def generate_access_token(credentials, client)
200
196
  token = nil
201
197
  begin
202
- callback = credentials[:oauth2_callback] || DEFAULT_CALLBACK
203
198
  verification_code = credentials[:oauth2_verification_code]
204
199
  if verification_code.nil? || verification_code.empty?
205
- auth_options = {
206
- :redirect_uri => callback,
207
- :scope => @scope,
208
- :state => credentials[:oauth2_state],
209
- :access_type => credentials[:oauth2_access_type],
210
- :approval_prompt => credentials[:oauth2_approval_prompt]
200
+ uri_options = {
201
+ :access_type => credentials[:oauth2_access_type],
202
+ :approval_prompt => credentials[:oauth2_approval_prompt]
211
203
  }.reject {|k, v| v.nil?}
212
- auth_url = client.auth_code.authorize_url(auth_options)
213
- raise AdsCommon::Errors::OAuth2VerificationRequired.new(auth_url)
204
+ oauth_url = client.authorization_uri(uri_options)
205
+ raise AdsCommon::Errors::OAuth2VerificationRequired.new(oauth_url)
214
206
  else
215
- token = @client.auth_code.get_token(verification_code,
216
- {:redirect_uri => callback})
207
+ client.code = verification_code
208
+ proxy = @config.read('connection.proxy')
209
+ connection = (proxy.nil?) ? nil : Faraday.new(:proxy => proxy)
210
+ token = AdsCommon::Utils.hash_keys_to_sym(
211
+ client.fetch_access_token!(:connection => connection))
217
212
  end
218
- rescue OAuth::Unauthorized => e
213
+ rescue Signet::AuthorizationError => e
219
214
  raise AdsCommon::Errors::AuthError,
220
215
  'Authorization error occured: %s' % e
221
216
  end
222
217
  return token
223
218
  end
224
219
 
225
- # Converts OAuth token object into hash structure.
226
- def oauth_token_to_hash(token)
227
- return token.nil? ? nil :
228
- {
229
- :access_token => token.token,
230
- :refresh_token => token.refresh_token,
231
- :expires_in => token.expires_in,
232
- :expires_at => token.expires_at,
233
- :params => token.params,
234
- :options => token.options
235
- }
220
+ # Create a token Hash from a client.
221
+ def token_from_client(client)
222
+ return nil if client.refresh_token.nil? && client.access_token.nil?
223
+ return {
224
+ :access_token => client.access_token,
225
+ :refresh_token => client.refresh_token,
226
+ :issued_at => client.issued_at,
227
+ :expires_in => client.expires_in,
228
+ :id_token => client.id_token
229
+ }
236
230
  end
237
231
  end
238
232
  end
@@ -0,0 +1,201 @@
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 JWT authentication.
21
+
22
+ require 'faraday'
23
+ require 'signet/oauth_2/client'
24
+
25
+ require 'ads_common/auth/base_handler'
26
+ require 'ads_common/errors'
27
+
28
+ module AdsCommon
29
+ module Auth
30
+
31
+ # Credentials class to handle OAuth2.0 authentication.
32
+ class OAuth2JwtHandler < AdsCommon::Auth::BaseHandler
33
+
34
+ OAUTH2_CONFIG = {
35
+ :token_credential_uri => 'https://accounts.google.com/o/oauth2/token',
36
+ :audience => 'https://accounts.google.com/o/oauth2/token'
37
+ }
38
+
39
+ # Initializes the OAuthHandler2 with all the necessary details.
40
+ #
41
+ # Args:
42
+ # - config: Config object with library configuration
43
+ # - scope: OAuth authorization scope
44
+ #
45
+ def initialize(config, scope)
46
+ super(config)
47
+ @scope, @client = scope, nil
48
+ end
49
+
50
+ # Invalidates the stored token if the required credential has changed.
51
+ def property_changed(prop, value)
52
+ oauth2_keys =
53
+ [:oauth2_issuer, :oauth2_secret, :oauth2_keyfile, :oauth2_key]
54
+ @client = nil if oauth2_keys.include?(prop)
55
+ end
56
+
57
+ def handle_error(error)
58
+ # TODO: Add support.
59
+ get_logger().error(error)
60
+ raise error
61
+ end
62
+
63
+ # Generates auth string for OAuth2.0 JWT method of authentication.
64
+ #
65
+ # Args:
66
+ # - credentials: credentials set for authorization
67
+ #
68
+ # Returns:
69
+ # - Authentication string
70
+ #
71
+ def auth_string(credentials)
72
+ token = get_token(credentials)
73
+ return ::Signet::OAuth2.generate_bearer_authorization_header(
74
+ token[:access_token])
75
+ end
76
+
77
+ # Overrides base get_token method to account for the token expiration.
78
+ def get_token(credentials = nil)
79
+ token = super(credentials)
80
+ token = refresh_token! if !@client.nil? && @client.expired?
81
+ return token
82
+ end
83
+
84
+ # Refreshes access token from refresh token.
85
+ def refresh_token!()
86
+ return nil if @token.nil? or @token[:refresh_token].nil?
87
+ @client.refresh!
88
+ @token = token_from_client(@client)
89
+ return @token
90
+ end
91
+
92
+ private
93
+
94
+ # Auxiliary method to validate the credentials for JWT authentication.
95
+ #
96
+ # Args:
97
+ # - credentials: a hash with the credentials for the account being
98
+ # accessed
99
+ #
100
+ # Raises:
101
+ # - AdsCommon::Errors::AuthError if validation fails
102
+ #
103
+ def validate_credentials(credentials)
104
+ if @scope.nil?
105
+ raise AdsCommon::Errors::AuthError, 'Scope is not specified.'
106
+ end
107
+
108
+ if credentials.nil?
109
+ raise AdsCommon::Errors::AuthError, 'No credentials supplied.'
110
+ end
111
+
112
+ if credentials[:oauth2_issuer].nil?
113
+ raise AdsCommon::Errors::AuthError,
114
+ 'Issuer is not included in the credentials.'
115
+ end
116
+
117
+ if credentials[:oauth2_secret].nil?
118
+ raise AdsCommon::Errors::AuthError,
119
+ 'Key secret is not included in the credentials.'
120
+ end
121
+
122
+ if credentials[:oauth2_key].nil? && credentials[:oauth2_keyfile].nil?
123
+ raise AdsCommon::Errors::AuthError,
124
+ 'Either key or key file must be provided for OAuth2 JWT.'
125
+ end
126
+
127
+ if credentials[:oauth2_key] && credentials[:oauth2_keyfile]
128
+ raise AdsCommon::Errors::AuthError,
129
+ 'Both JWT key and key file provided, only one can be used.'
130
+ end
131
+
132
+ if credentials[:oauth2_key] &&
133
+ !credentials[:oauth2_key].kind_of?(OpenSSL::PKey::RSA)
134
+ raise AdsCommon::Errors::AuthError,
135
+ 'OAuth2 JWT key provided must be of type OpenSSL::PKey::RSA.'
136
+ end
137
+
138
+ if credentials[:oauth2_keyfile] &&
139
+ !File.file?(credentials[:oauth2_keyfile])
140
+ raise AdsCommon::Errors::AuthError,
141
+ "Key file '%s' does not exist or not a file."
142
+ end
143
+ end
144
+
145
+ # Auxiliary method to generate an authentication token for logging via
146
+ # the OAuth2.0 API.
147
+ #
148
+ # Args:
149
+ # - credentials: a hash with the credentials for the account being
150
+ # accessed
151
+ #
152
+ # Returns:
153
+ # - The auth token for the account (as an AccessToken)
154
+ #
155
+ # Raises:
156
+ # - AdsCommon::Errors::AuthError if authentication fails
157
+ # - AdsCommon::Errors::OAuthVerificationRequired if OAuth verification
158
+ # code required
159
+ #
160
+ def create_token(credentials)
161
+ validate_credentials(credentials)
162
+ @client ||= create_client(credentials)
163
+ @client.fetch_access_token!()
164
+ return token_from_client(@client)
165
+ end
166
+
167
+ # Creates a Signet client based on credentials.
168
+ def create_client(credentials)
169
+ credentials = load_oauth2_jwt_credentials(credentials)
170
+ oauth_options = OAUTH2_CONFIG.merge({
171
+ :issuer => credentials[:oauth2_issuer],
172
+ :signing_key => credentials[:oauth2_key],
173
+ :scope => @scope,
174
+ })
175
+ return Signet::OAuth2::Client.new(oauth_options)
176
+ end
177
+
178
+ # Loads JWT key if configured with a filename.
179
+ def load_oauth2_jwt_credentials(credentials)
180
+ return credentials unless credentials.include?(:oauth2_keyfile)
181
+ key_file = File.read(credentials[:oauth2_keyfile])
182
+ key_secret = credentials[:oauth2_secret]
183
+ key = OpenSSL::PKCS12.new(key_file, key_secret).key
184
+ result = credentials.merge({:oauth2_key => key})
185
+ result.delete(:oauth2_keyfile)
186
+ return result
187
+ end
188
+
189
+ # Create a token Hash from a client.
190
+ def token_from_client(client)
191
+ return nil if client.access_token.nil?
192
+ return {
193
+ :access_token => client.access_token,
194
+ :issued_at => client.issued_at,
195
+ :expires_in => client.expires_in,
196
+ :id_token => client.id_token
197
+ }
198
+ end
199
+ end
200
+ end
201
+ end
@@ -39,15 +39,6 @@ module AdsCommon
39
39
  end
40
40
  end
41
41
 
42
- # Raised when OAuth1.0a access token is required.
43
- class OAuthVerificationRequired < AuthError
44
- attr_reader :oauth_url, :request_token
45
- def initialize(oauth_url, request_token)
46
- super()
47
- @oauth_url, @request_token = oauth_url, request_token
48
- end
49
- end
50
-
51
42
  # Raised when OAuth2.0 access token is required.
52
43
  class OAuth2VerificationRequired < AuthError
53
44
  attr_reader :oauth_url
@@ -20,7 +20,6 @@
20
20
  # Handles SOAP headers and namespaces definition for OAuth type header.
21
21
 
22
22
  require 'ads_common/savon_headers/base_header_handler'
23
- require 'ads_common/savon_headers/httpi_request_proxy'
24
23
 
25
24
  module AdsCommon
26
25
  module SavonHeaders
@@ -43,7 +42,7 @@ module AdsCommon
43
42
  credentials = @credential_handler.credentials
44
43
  request.url = soap.endpoint
45
44
  request.headers['Authorization'] =
46
- @auth_handler.auth_string(credentials, request)
45
+ @auth_handler.auth_string(credentials)
47
46
  end
48
47
  end
49
48
  end
@@ -31,6 +31,24 @@ module AdsCommon
31
31
  return result
32
32
  end
33
33
  end
34
+
35
+ # Converts all hash keys to strings.
36
+ def self.hash_keys_to_str(data)
37
+ return nil if data.nil?
38
+ return data.inject({}) do |result, (k, v)|
39
+ result[k.to_s] = v
40
+ result
41
+ end
42
+ end
43
+
44
+ # Converts all hash keys to symbols.
45
+ def self.hash_keys_to_sym(data)
46
+ return nil if data.nil?
47
+ return data.inject({}) do |result, (k, v)|
48
+ result[k.to_sym] = v
49
+ result
50
+ end
51
+ end
34
52
  end
35
53
  end
36
54
 
@@ -21,6 +21,6 @@
21
21
 
22
22
  module AdsCommon
23
23
  module ApiConfig
24
- CLIENT_LIB_VERSION = '0.8.2'
24
+ CLIENT_LIB_VERSION = '0.9.0'
25
25
  end
26
26
  end
@@ -54,4 +54,25 @@ class TestUtils < Test::Unit::TestCase
54
54
  assert_not_same(str, result)
55
55
  assert_equal('str_snake', str)
56
56
  end
57
+
58
+ def test_hash_keys_to_str()
59
+ data = {:a => 'aa', :b5 => 43, 'xyz' => :abc}
60
+ result = AdsCommon::Utils.hash_keys_to_str(data)
61
+ assert_equal('aa', result['a'])
62
+ assert_equal(43, result['b5'])
63
+ assert_equal(:abc, result['xyz'])
64
+ assert_equal(3, result.size)
65
+ assert_not_same(result, data)
66
+ end
67
+
68
+ def test_hash_keys_to_sym()
69
+ data = {:a => 'aa', :b5 => 43, 'xyz' => :abc, 'f5' => :xyz}
70
+ result = AdsCommon::Utils.hash_keys_to_sym(data)
71
+ assert_equal('aa', result[:a])
72
+ assert_equal(43, result[:b5])
73
+ assert_equal(:abc, result[:xyz])
74
+ assert_equal(:xyz, result[:f5])
75
+ assert_equal(4, result.size)
76
+ assert_not_same(result, data)
77
+ end
57
78
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: google-ads-common
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.2
4
+ version: 0.9.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-10-29 00:00:00.000000000 Z
13
+ date: 2013-01-08 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: savon
@@ -45,13 +45,13 @@ dependencies:
45
45
  - !ruby/object:Gem::Version
46
46
  version: 1.1.0
47
47
  - !ruby/object:Gem::Dependency
48
- name: oauth
48
+ name: signet
49
49
  requirement: !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
53
53
  - !ruby/object:Gem::Version
54
- version: 0.4.5
54
+ version: 0.4.4
55
55
  type: :runtime
56
56
  prerelease: false
57
57
  version_requirements: !ruby/object:Gem::Requirement
@@ -59,23 +59,7 @@ dependencies:
59
59
  requirements:
60
60
  - - ~>
61
61
  - !ruby/object:Gem::Version
62
- version: 0.4.5
63
- - !ruby/object:Gem::Dependency
64
- name: oauth2
65
- requirement: !ruby/object:Gem::Requirement
66
- none: false
67
- requirements:
68
- - - ~>
69
- - !ruby/object:Gem::Version
70
- version: 0.8.0
71
- type: :runtime
72
- prerelease: false
73
- version_requirements: !ruby/object:Gem::Requirement
74
- none: false
75
- requirements:
76
- - - ~>
77
- - !ruby/object:Gem::Version
78
- version: 0.8.0
62
+ version: 0.4.4
79
63
  description: Essential utilities shared by all Ads Ruby client libraries
80
64
  email:
81
65
  - api.dklimkin@gmail.com
@@ -91,14 +75,13 @@ files:
91
75
  - lib/ads_common/api_config.rb
92
76
  - lib/ads_common/auth/client_login_handler.rb
93
77
  - lib/ads_common/auth/oauth2_handler.rb
94
- - lib/ads_common/auth/oauth_handler.rb
95
78
  - lib/ads_common/auth/base_handler.rb
79
+ - lib/ads_common/auth/oauth2_jwt_handler.rb
96
80
  - lib/ads_common/http.rb
97
81
  - lib/ads_common/errors.rb
98
82
  - lib/ads_common/parameters_validator.rb
99
83
  - lib/ads_common/savon_headers/oauth_header_handler.rb
100
84
  - lib/ads_common/savon_headers/base_header_handler.rb
101
- - lib/ads_common/savon_headers/httpi_request_proxy.rb
102
85
  - lib/ads_common/results_extractor.rb
103
86
  - lib/ads_common/build/savon_abstract_generator.rb
104
87
  - lib/ads_common/build/savon_service_generator.rb
@@ -1,257 +0,0 @@
1
- # Encoding: utf-8
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 OAuth1.0a 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
- OAUTH_CONFIG = {
33
- :site => 'https://www.google.com',
34
- :request_token_path => '/accounts/OAuthGetRequestToken',
35
- :access_token_path => '/accounts/OAuthGetAccessToken',
36
- :authorize_path => '/accounts/OAuthAuthorizeToken'
37
- }
38
-
39
- DEFAULT_CALLBACK = 'oob'
40
- DEFAULT_METHOD = 'HMAC-SHA1'
41
-
42
- # Initializes the OAuthHandler with all the necessary details.
43
- #
44
- # Args:
45
- # - config: Config object with library configuration
46
- # - scope: OAuth authorization scope
47
- #
48
- def initialize(config, scope)
49
- super(config)
50
- @scope = scope
51
- end
52
-
53
- # Invalidates the stored token if the required credential has changed.
54
- def property_changed(prop, value)
55
- if [:oauth_consumer_key, :oauth_consumer_secret].include?(prop)
56
- @consumer, @token, @request_token = nil, nil, nil
57
- end
58
- end
59
-
60
- def handle_error(error)
61
- # TODO: Add support.
62
- get_logger().error(error)
63
- raise error
64
- end
65
-
66
- # Returns authorization string.
67
- def auth_string(credentials, request)
68
- if request.nil?
69
- raise AdsCommon::Errors::AuthError,
70
- 'Request is required for OAuth generator.'
71
- end
72
- return generate_oauth_parameters_string(credentials, request)
73
- end
74
-
75
- private
76
-
77
- # Generates auth string for OAuth method of authentication.
78
- #
79
- # Args:
80
- # - credentials: credentials set for authorization
81
- # - request: a HTTPI Request to generate headers for
82
- #
83
- # Returns:
84
- # - Authentication string
85
- #
86
- def generate_oauth_parameters_string(credentials, request)
87
- # get_token() ensures @consumer is initialized.
88
- token = get_token(credentials)
89
- oauth_params = {
90
- :consumer => @consumer,
91
- :token => token
92
- }
93
- oauth_helper = OAuth::Client::Helper.new(request, oauth_params)
94
- return oauth_helper.header
95
- end
96
-
97
- private
98
-
99
- # Auxiliary method to validate the credentials for token generation.
100
- #
101
- # Args:
102
- # - credentials: a hash with the credentials for the account being
103
- # accessed
104
- #
105
- # Raises:
106
- # - AdsCommon::Errors::AuthError if validation fails
107
- #
108
- def validate_credentials(credentials)
109
- if @scope.nil?
110
- raise AdsCommon::Errors::AuthError, 'Scope is not specified.'
111
- end
112
-
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
-
127
- # TODO: add checks for both methods.
128
- end
129
-
130
- # Auxiliary method to generate an authentication token for logging via
131
- # the OAuth API.
132
- #
133
- # Args:
134
- # - credentials: a hash with the credentials for the account being
135
- # accessed
136
- #
137
- # Returns:
138
- # - The auth token for the account (as an AccessToken)
139
- #
140
- # Raises:
141
- # - AdsCommon::Errors::AuthError if authentication fails
142
- # - AdsCommon::Errors::OAuthVerificationRequired if OAuth verification
143
- # code required
144
- #
145
- def create_token(credentials)
146
- validate_credentials(credentials)
147
- @consumer ||= create_consumer(credentials)
148
- return create_token_from_credentials(credentials, @consumer) ||
149
- generate_access_token(credentials, @consumer)
150
- end
151
-
152
- def create_consumer(credentials)
153
- oauth_config = OAUTH_CONFIG.merge({:scope => @scope})
154
- proxy = @config.read('connection.proxy')
155
- oauth_config[:proxy] = proxy unless proxy.nil?
156
- return OAuth::Consumer.new(
157
- credentials[:oauth_consumer_key],
158
- credentials[:oauth_consumer_secret],
159
- oauth_config)
160
- end
161
-
162
- # Creates access token based on data from credentials.
163
- #
164
- # Args:
165
- # - credentials: a hash with the credentials for the account being
166
- # accessed
167
- # - consumer: OAuth consumer 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, consumer)
173
- token = credentials[:oauth_token]
174
- if token.nil? or token.empty?
175
- return nil
176
- end
177
-
178
- method = credentials[:oauth_method] || DEFAULT_METHOD
179
- access_token = case method
180
- when 'RSA-SHA1'
181
- OAuth::AccessToken.from_hash(consumer, {:oauth_token => token})
182
- when 'HMAC-SHA1'
183
- token_secret = credentials[:oauth_token_secret]
184
- if token_secret.nil? or token_secret.empty?
185
- get_logger().warn(("The 'token' specified for method %s but " +
186
- "'token secret' is not available, ignoring token") % method)
187
- nil
188
- else
189
- OAuth::AccessToken.from_hash(consumer, {
190
- :oauth_token => token, :oauth_token_secret => token_secret})
191
- end
192
- end
193
- return access_token
194
- end
195
-
196
- # Generates new request tokens and authorizes it to get access token.
197
- #
198
- # Args:
199
- # - credentials: a hash with the credentials for the account being
200
- # accessed
201
- # - consumer: OAuth consumer for the current configuration
202
- #
203
- # Returns:
204
- # - The auth token for the account (as an AccessToken)
205
- #
206
- def generate_access_token(credentials, consumer)
207
- token = nil
208
- callback = credentials[:oauth_callback] || DEFAULT_CALLBACK
209
- begin
210
- if @request_token.nil?
211
- @request_token = credentials[:oauth_request_token] ||
212
- consumer.get_request_token(
213
- {:oauth_callback => callback},
214
- {:scope => @scope}
215
- )
216
- end
217
- verification_code = credentials[:oauth_verification_code]
218
- if verification_code.nil? || verification_code.empty?
219
- raise_oauth_verification_error(@request_token, callback)
220
- else
221
- token = @request_token.get_access_token(
222
- {:oauth_verifier => verification_code})
223
- @request_token = nil
224
- end
225
- rescue OAuth::Unauthorized => e
226
- if @request_token
227
- raise_oauth_verification_error(@request_token, callback)
228
- else
229
- raise AdsCommon::Errors::AuthError,
230
- "Authorization error occured: %s" % e
231
- end
232
- end
233
- return token
234
- end
235
-
236
- # Raises a OAuthVerificationRequired error with auth URL for given
237
- # request token.
238
- #
239
- # Args:
240
- # - request_token: an initialized OAuth request token
241
- # - callback: OAuth callback URL
242
- #
243
- # Returns:
244
- # - never returns
245
- #
246
- # Raises:
247
- # - AdsCommon::Errors::OAuthVerificationRequired in all cases
248
- #
249
- def raise_oauth_verification_error(request_token, callback)
250
- oauth_url = request_token.authorize_url({:oauth_callback => callback})
251
- error = AdsCommon::Errors::OAuthVerificationRequired.new(
252
- oauth_url, request_token)
253
- raise error
254
- end
255
- end
256
- end
257
- end
@@ -1,51 +0,0 @@
1
- # Encoding: utf-8
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
- # OAuth request proxy for HTTPI::Request.
21
-
22
- require 'oauth/request_proxy/base'
23
- require 'httpi/request'
24
-
25
- module OAuth
26
- module RequestProxy
27
- class HTTPIRequest < OAuth::RequestProxy::Base
28
- proxies HTTPI::Request
29
-
30
- # HTTP method to use.
31
- def method
32
- return 'POST'
33
- end
34
-
35
- # Request URL.
36
- def uri
37
- request.url.to_s
38
- end
39
-
40
- # Query parameters.
41
- def parameters
42
- options[:parameters]
43
- end
44
-
45
- # Request body.
46
- def body
47
- request.body
48
- end
49
- end
50
- end
51
- end