google-ads-common 0.8.2 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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