google-api-client 0.5.0 → 0.6.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.
@@ -0,0 +1,115 @@
1
+ # Copyright 2010 Google Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'webrick'
16
+ require 'launchy'
17
+
18
+ module Google
19
+ class APIClient
20
+
21
+ # Small helper for the sample apps for performing OAuth 2.0 flows from the command
22
+ # line or in any other installed app environment.
23
+ #
24
+ # @example
25
+ #
26
+ # client = Google::APIClient.new
27
+ # flow = Google::APIClient::InstalledAppFlow.new(
28
+ # :client_id => '691380668085.apps.googleusercontent.com',
29
+ # :client_secret => '...,
30
+ # :scope => 'https://www.googleapis.com/auth/drive'
31
+ # )
32
+ # client.authorization = flow.authorize
33
+ #
34
+ class InstalledAppFlow
35
+
36
+ RESPONSE_BODY = <<-HTML
37
+ <html>
38
+ <head>
39
+ <script>
40
+ function closeWindow() {
41
+ window.open('', '_self', '');
42
+ window.close();
43
+ }
44
+ setTimeout(closeWindow, 10);
45
+ </script>
46
+ </head>
47
+ <body>You may close this window.</body>
48
+ </html>
49
+ HTML
50
+
51
+ ##
52
+ # Configure the flow
53
+ #
54
+ # @param [Hash] options The configuration parameters for the client.
55
+ # @option options [Fixnum] :port
56
+ # Port to run the embedded server on. Defaults to 9292
57
+ # @option options [String] :client_id
58
+ # A unique identifier issued to the client to identify itself to the
59
+ # authorization server.
60
+ # @option options [String] :client_secret
61
+ # A shared symmetric secret issued by the authorization server,
62
+ # which is used to authenticate the client.
63
+ # @option options [String] :scope
64
+ # The scope of the access request, expressed either as an Array
65
+ # or as a space-delimited String.
66
+ #
67
+ # @see Signet::OAuth2::Client
68
+ def initialize(options)
69
+ @port = options[:port] || 9292
70
+ @authorization = Signet::OAuth2::Client.new({
71
+ :authorization_uri => 'https://accounts.google.com/o/oauth2/auth',
72
+ :token_credential_uri => 'https://accounts.google.com/o/oauth2/token',
73
+ :redirect_uri => "http://localhost:#{@port}/"}.update(options)
74
+ )
75
+ end
76
+
77
+ ##
78
+ # Request authorization. Opens a browser and waits for response.
79
+ #
80
+ # @return [Signet::OAuth2::Client]
81
+ # Authorization instance, nil if user cancelled.
82
+ def authorize
83
+ auth = @authorization
84
+
85
+ server = WEBrick::HTTPServer.new(
86
+ :Port => @port,
87
+ :BindAddress =>"localhost",
88
+ :Logger => WEBrick::Log.new(STDOUT, 0),
89
+ :AccessLog => []
90
+ )
91
+ trap("INT") { server.shutdown }
92
+
93
+ server.mount_proc '/' do |req, res|
94
+ auth.code = req.query['code']
95
+ if auth.code
96
+ auth.fetch_access_token!
97
+ end
98
+ res.status = WEBrick::HTTPStatus::RC_ACCEPTED
99
+ res.body = RESPONSE_BODY
100
+ server.stop
101
+ end
102
+
103
+ Launchy.open(auth.authorization_uri.to_s)
104
+ server.start
105
+ if @authorization.access_token
106
+ return @authorization
107
+ else
108
+ return nil
109
+ end
110
+ end
111
+ end
112
+
113
+ end
114
+ end
115
+
@@ -22,17 +22,31 @@ module Google
22
22
  # Generates access tokens using the JWT assertion profile. Requires a
23
23
  # service account & access to the private key.
24
24
  #
25
- # @example
25
+ # @example Using Signet
26
+ #
27
+ # key = Google::APIClient::KeyUtils.load_from_pkcs12('client.p12', 'notasecret')
28
+ # client.authorization = Signet::OAuth2::Client.new(
29
+ # :token_credential_uri => 'https://accounts.google.com/o/oauth2/token',
30
+ # :audience => 'https://accounts.google.com/o/oauth2/token',
31
+ # :scope => 'https://www.googleapis.com/auth/prediction',
32
+ # :issuer => '123456-abcdef@developer.gserviceaccount.com',
33
+ # :signing_key => key)
34
+ # client.authorization.fetch_access_token!
35
+ # client.execute(...)
36
+ #
37
+ # @example Deprecated version
26
38
  #
27
39
  # client = Google::APIClient.new
28
40
  # key = Google::APIClient::PKCS12.load_key('client.p12', 'notasecret')
29
- # service_account = Google::APIClient::JWTAsserter(
30
- # '123456-abcdef@developer.gserviceaccount.com',
31
- # 'https://www.googleapis.com/auth/prediction',
32
- # key)
41
+ # service_account = Google::APIClient::JWTAsserter.new(
42
+ # '123456-abcdef@developer.gserviceaccount.com',
43
+ # 'https://www.googleapis.com/auth/prediction',
44
+ # key)
33
45
  # client.authorization = service_account.authorize
34
46
  # client.execute(...)
35
47
  #
48
+ # @deprecated
49
+ # Service accounts are now supported directly in Signet
36
50
  # @see https://developers.google.com/accounts/docs/OAuth2ServiceAccount
37
51
  class JWTAsserter
38
52
  # @return [String] ID/email of the issuing party
@@ -43,9 +57,11 @@ module Google
43
57
  attr_accessor :skew
44
58
  # @return [String] Scopes to authorize
45
59
  attr_reader :scope
46
- # @return [OpenSSL::PKey] key for signing assertions
60
+ # @return [String,OpenSSL::PKey] key for signing assertions
47
61
  attr_writer :key
48
-
62
+ # @return [String] Algorithm used for signing
63
+ attr_accessor :algorithm
64
+
49
65
  ##
50
66
  # Initializes the asserter for a service account.
51
67
  #
@@ -53,14 +69,18 @@ module Google
53
69
  # Name/ID of the client issuing the assertion
54
70
  # @param [String, Array] scope
55
71
  # Scopes to authorize. May be a space delimited string or array of strings
56
- # @param [OpenSSL::PKey] key
57
- # RSA private key for signing assertions
58
- def initialize(issuer, scope, key)
72
+ # @param [String,OpenSSL::PKey] key
73
+ # Key for signing assertions
74
+ # @param [String] algorithm
75
+ # Algorithm to use, either 'RS256' for RSA with SHA-256
76
+ # or 'HS256' for HMAC with SHA-256
77
+ def initialize(issuer, scope, key, algorithm = "RS256")
59
78
  self.issuer = issuer
60
79
  self.scope = scope
61
80
  self.expiry = 60 # 1 min default
62
81
  self.skew = 60
63
82
  self.key = key
83
+ self.algorithm = algorithm
64
84
  end
65
85
 
66
86
  ##
@@ -81,25 +101,6 @@ module Google
81
101
  end
82
102
  end
83
103
 
84
- ##
85
- # Builds & signs the assertion.
86
- #
87
- # @param [String] person
88
- # Email address of a user, if requesting a token to act on their behalf
89
- # @return [String] Encoded JWT
90
- def to_jwt(person=nil)
91
- now = Time.new
92
- assertion = {
93
- "iss" => @issuer,
94
- "scope" => self.scope,
95
- "aud" => "https://accounts.google.com/o/oauth2/token",
96
- "exp" => (now + expiry).to_i,
97
- "iat" => (now - skew).to_i
98
- }
99
- assertion['prn'] = person unless person.nil?
100
- return JWT.encode(assertion, @key, "RS256")
101
- end
102
-
103
104
  ##
104
105
  # Request a new access token.
105
106
  #
@@ -109,31 +110,28 @@ module Google
109
110
  # Pass through to Signet::OAuth2::Client.fetch_access_token
110
111
  # @return [Signet::OAuth2::Client] Access token
111
112
  #
112
- # @see Signet::OAuth2::Client.fetch_access_token
113
+ # @see Signet::OAuth2::Client.fetch_access_token!
113
114
  def authorize(person = nil, options={})
114
- assertion = self.to_jwt(person)
115
- authorization = Signet::OAuth2::Client.new(
116
- :token_credential_uri => 'https://accounts.google.com/o/oauth2/token'
117
- )
118
- authorization.grant_type = 'urn:ietf:params:oauth:grant-type:jwt-bearer'
119
- authorization.extension_parameters = { :assertion => assertion }
115
+ authorization = self.to_authorization
120
116
  authorization.fetch_access_token!(options)
121
- return JWTAuthorization.new(authorization, self, person)
122
- end
123
- end
124
-
125
- class JWTAuthorization < DelegateClass(Signet::OAuth2::Client)
126
- def initialize(authorization, asserter, person = nil)
127
- @asserter = asserter
128
- @person = person
129
- super(authorization)
117
+ return authorization
130
118
  end
131
119
 
132
- def fetch_access_token!(options={})
133
- new_authorization = @asserter.authorize(@person, options)
134
- __setobj__(new_authorization)
135
- self
136
- end
120
+ ##
121
+ # Builds a Signet OAuth2 client
122
+ #
123
+ # @return [Signet::OAuth2::Client] Access token
124
+ def to_authorization(person = nil)
125
+ return Signet::OAuth2::Client.new(
126
+ :token_credential_uri => 'https://accounts.google.com/o/oauth2/token',
127
+ :audience => 'https://accounts.google.com/o/oauth2/token',
128
+ :scope => self.scope,
129
+ :issuer => @issuer,
130
+ :signing_key => @key,
131
+ :signing_algorithm => @algorithm,
132
+ :person => person
133
+ )
134
+ end
137
135
  end
138
136
  end
139
137
  end
@@ -0,0 +1,93 @@
1
+ # Copyright 2010 Google Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Google
16
+ class APIClient
17
+ ##
18
+ # Helper for loading keys from the PKCS12 files downloaded when
19
+ # setting up service accounts at the APIs Console.
20
+ #
21
+ module KeyUtils
22
+ ##
23
+ # Loads a key from PKCS12 file, assuming a single private key
24
+ # is present.
25
+ #
26
+ # @param [String] keyfile
27
+ # Path of the PKCS12 file to load. If not a path to an actual file,
28
+ # assumes the string is the content of the file itself.
29
+ # @param [String] passphrase
30
+ # Passphrase for unlocking the private key
31
+ #
32
+ # @return [OpenSSL::PKey] The private key for signing assertions.
33
+ def self.load_from_pkcs12(keyfile, passphrase)
34
+ load_key(keyfile, passphrase) do |content, passphrase|
35
+ OpenSSL::PKCS12.new(content, passphrase).key
36
+ end
37
+ end
38
+
39
+
40
+ ##
41
+ # Loads a key from a PEM file.
42
+ #
43
+ # @param [String] keyfile
44
+ # Path of the PEM file to load. If not a path to an actual file,
45
+ # assumes the string is the content of the file itself.
46
+ # @param [String] passphrase
47
+ # Passphrase for unlocking the private key
48
+ #
49
+ # @return [OpenSSL::PKey] The private key for signing assertions.
50
+ #
51
+ def self.load_from_pem(keyfile, passphrase)
52
+ load_key(keyfile, passphrase) do | content, passphrase|
53
+ OpenSSL::PKey::RSA.new(content, passphrase)
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ ##
60
+ # Helper for loading keys from file or memory. Accepts a block
61
+ # to handle the specific file format.
62
+ #
63
+ # @param [String] keyfile
64
+ # Path of thefile to load. If not a path to an actual file,
65
+ # assumes the string is the content of the file itself.
66
+ # @param [String] passphrase
67
+ # Passphrase for unlocking the private key
68
+ #
69
+ # @yield [String, String]
70
+ # Key file & passphrase to extract key from
71
+ # @yieldparam [String] keyfile
72
+ # Contents of the file
73
+ # @yieldparam [String] passphrase
74
+ # Passphrase to unlock key
75
+ # @yieldreturn [OpenSSL::PKey]
76
+ # Private key
77
+ #
78
+ # @return [OpenSSL::PKey] The private key for signing assertions.
79
+ def self.load_key(keyfile, passphrase, &block)
80
+ begin
81
+ begin
82
+ content = File.open(keyfile, 'rb') { |io| io.read }
83
+ rescue
84
+ content = keyfile
85
+ end
86
+ block.call(content, passphrase)
87
+ rescue OpenSSL::OpenSSLError
88
+ raise ArgumentError.new("Invalid keyfile or passphrase")
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -12,6 +12,7 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ require 'google/api_client/auth/key_utils'
15
16
  module Google
16
17
  class APIClient
17
18
  ##
@@ -30,18 +31,10 @@ module Google
30
31
  # Passphrase for unlocking the private key
31
32
  #
32
33
  # @return [OpenSSL::PKey] The private key for signing assertions.
34
+ # @deprecated
35
+ # Use {Google::APIClient::KeyUtils} instead
33
36
  def self.load_key(keyfile, passphrase)
34
- begin
35
- if File.exists?(keyfile)
36
- content = File.read(keyfile)
37
- else
38
- content = keyfile
39
- end
40
- pkcs12 = OpenSSL::PKCS12.new(content, passphrase)
41
- return pkcs12.key
42
- rescue OpenSSL::PKCS12::PKCS12Error
43
- raise ArgumentError.new("Invalid keyfile or passphrase")
44
- end
37
+ KeyUtils.load_from_pkcs12(keyfile, passphrase)
45
38
  end
46
39
  end
47
40
  end
@@ -59,12 +59,11 @@ module Google
59
59
  # puts result.data
60
60
  # end
61
61
  #
62
- # batch.add(:api_method=>urlshortener.url.insert, :body_object => { 'longUrl' => 'http://example.com/foo' })
63
- # batch.add(:api_method=>urlshortener.url.insert, :body_object => { 'longUrl' => 'http://example.com/bar' })
62
+ # batch.add(:api_method => urlshortener.url.insert, :body_object => { 'longUrl' => 'http://example.com/foo' })
63
+ # batch.add(:api_method => urlshortener.url.insert, :body_object => { 'longUrl' => 'http://example.com/bar' })
64
64
  #
65
65
  # client.execute(batch)
66
66
  #
67
-
68
67
  class BatchRequest < Request
69
68
  BATCH_BOUNDARY = "-----------RubyApiBatchRequest".freeze
70
69
 
@@ -63,7 +63,6 @@ module Google
63
63
  end
64
64
  while filename == nil
65
65
  search_path ||= File.expand_path('.')
66
- puts search_path
67
66
  if File.exist?(File.join(search_path, 'client_secrets.json'))
68
67
  filename = File.join(search_path, 'client_secrets.json')
69
68
  elsif search_path == '/' || search_path =~ /[a-zA-Z]:[\/\\]/
@@ -14,7 +14,7 @@
14
14
 
15
15
 
16
16
  require 'addressable/uri'
17
-
17
+ require 'multi_json'
18
18
  require 'google/inflection'
19
19
  require 'google/api_client/discovery/resource'
20
20
  require 'google/api_client/discovery/method'
@@ -281,6 +281,20 @@ module Google
281
281
  "#<%s:%#0x ID:%s>", self.class.to_s, self.object_id, self.id
282
282
  )
283
283
  end
284
+
285
+ ##
286
+ # Marshalling support - serialize the API to a string (doc base + original
287
+ # discovery document).
288
+ def _dump(level)
289
+ MultiJson.dump([@document_base.to_s, @discovery_document])
290
+ end
291
+
292
+ ##
293
+ # Marshalling support - Restore an API instance from serialized form
294
+ def self._load(obj)
295
+ new(*MultiJson.load(obj))
296
+ end
297
+
284
298
  end
285
299
  end
286
300
  end
@@ -0,0 +1,32 @@
1
+ require 'logger'
2
+
3
+ module Google
4
+ class APIClient
5
+
6
+ class << self
7
+ ##
8
+ # Logger for the API client
9
+ #
10
+ # @return [Logger] logger instance.
11
+ attr_accessor :logger
12
+ end
13
+
14
+ self.logger = Logger.new(STDOUT)
15
+ self.logger.level = Logger::WARN
16
+
17
+ ##
18
+ # Module to make accessing the logger simpler
19
+ module Logging
20
+ ##
21
+ # Logger for the API client
22
+ #
23
+ # @return [Logger] logger instance.
24
+ def logger
25
+ Google::APIClient.logger
26
+ end
27
+ end
28
+
29
+ end
30
+
31
+
32
+ end