Permalink
Browse files

Add oauth helper for installed apps, update CLI

  • Loading branch information...
1 parent 1d7315e commit 8ce4d052feaf61c272a50afe2ff703e35cac2a64 @sqrrrl sqrrrl committed Jan 5, 2013
Showing with 139 additions and 172 deletions.
  1. +24 −172 bin/google-api
  2. +115 −0 lib/google/api_client/auth/installed_app.rb
View
@@ -15,45 +15,13 @@ require 'faraday/utils'
require 'webrick'
require 'google/api_client/version'
require 'google/api_client'
+require 'google/api_client/auth/installed_app'
ARGV.unshift('--help') if ARGV.empty?
module Google
class APIClient
class CLI
- # Used for oauth login
- class OAuthVerifierServlet < WEBrick::HTTPServlet::AbstractServlet
- attr_reader :verifier
-
- def do_GET(request, response)
- $verifier ||= Addressable::URI.unencode_component(
- request.request_uri.to_s[/\?.*oauth_verifier=([^&$]+)(&|$)/, 1] ||
- request.request_uri.to_s[/\?.*code=([^&$]+)(&|$)/, 1]
- )
- response.status = WEBrick::HTTPStatus::RC_ACCEPTED
- # This javascript will auto-close the tab after the
- # verifier is obtained.
- response.body = <<-HTML
-<html>
- <head>
- <script>
- function closeWindow() {
- window.open('', '_self', '');
- window.close();
- }
- setTimeout(closeWindow, 10);
- </script>
- </head>
- <body>
- You may close this window.
- </body>
-</html>
-HTML
- # Eww, hack!
- server = self.instance_variable_get('@server')
- server.stop if server
- end
- end
# Initialize with default parameter values
def initialize(argv)
@@ -164,8 +132,7 @@ HTML
opts.separator(
"\nAvailable commands:\n" +
- " oauth-1-login Log a user into an API with OAuth 1.0a\n" +
- " oauth-2-login Log a user into an API with OAuth 2.0 d10\n" +
+ " oauth-2-login Log a user into an API with OAuth 2.0\n" +
" list List the methods available for an API\n" +
" execute Execute a method on the API\n" +
" irb Start an interactive client session"
@@ -184,9 +151,7 @@ HTML
end
def client
- require 'signet/oauth_1/client'
require 'yaml'
- require 'irb'
config_file = File.expand_path('~/.google-api.yaml')
authorization = nil
if File.exist?(config_file)
@@ -198,19 +163,14 @@ HTML
authorization = config["mechanism"].to_sym
end
- client = Google::APIClient.new(:authorization => authorization)
+ client = Google::APIClient.new(
+ :application_name => 'Ruby CLI',
+ :application_version => Google::APIClient::VERSION::STRING,
+ :authorization => authorization)
case authorization
when :oauth_1
- if client.authorization &&
- !client.authorization.kind_of?(Signet::OAuth1::Client)
- STDERR.puts(
- "Unexpected authorization mechanism: " +
- "#{client.authorization.class}"
- )
- exit(1)
- end
- config = open(config_file, 'r') { |file| YAML.load(file.read) }
+ STDERR.puts('OAuth 1 is deprecated. Please reauthorize with OAuth 2.')
client.authorization.client_credential_key =
config["client_credential_key"]
client.authorization.client_credential_secret =
@@ -220,15 +180,6 @@ HTML
client.authorization.token_credential_secret =
config["token_credential_secret"]
when :oauth_2
- if client.authorization &&
- !client.authorization.kind_of?(Signet::OAuth2::Client)
- STDERR.puts(
- "Unexpected authorization mechanism: " +
- "#{client.authorization.class}"
- )
- exit(1)
- end
- config = open(config_file, 'r') { |file| YAML.load(file.read) }
client.authorization.scope = options[:scope]
client.authorization.client_id = config["client_id"]
client.authorization.client_secret = config["client_secret"]
@@ -268,84 +219,14 @@ HTML
end
COMMANDS = [
- :oauth_1_login,
:oauth_2_login,
:list,
:execute,
:irb,
- :fuzz
]
- def oauth_1_login
- require 'signet/oauth_1/client'
- require 'launchy'
- require 'yaml'
- if options[:client_credential_key] &&
- options[:client_credential_secret]
- config = {
- "mechanism" => "oauth_1",
- "scope" => options[:scope],
- "client_credential_key" => options[:client_credential_key],
- "client_credential_secret" => options[:client_credential_secret],
- "token_credential_key" => nil,
- "token_credential_secret" => nil
- }
- config_file = File.expand_path('~/.google-api.yaml')
- open(config_file, 'w') { |file| file.write(YAML.dump(config)) }
- exit(0)
- else
- $verifier = nil
- server = WEBrick::HTTPServer.new(
- :Port => OAUTH_SERVER_PORT,
- :Logger => WEBrick::Log.new,
- :AccessLog => WEBrick::Log.new
- )
- server.logger.level = 0
- trap("INT") { server.shutdown }
-
- server.mount("/", OAuthVerifierServlet)
-
- oauth_client = Signet::OAuth1::Client.new(
- :temporary_credential_uri =>
- 'https://www.google.com/accounts/OAuthGetRequestToken',
- :authorization_uri =>
- 'https://www.google.com/accounts/OAuthAuthorizeToken',
- :token_credential_uri =>
- 'https://www.google.com/accounts/OAuthGetAccessToken',
- :client_credential_key => 'anonymous',
- :client_credential_secret => 'anonymous',
- :callback => "http://localhost:#{OAUTH_SERVER_PORT}/"
- )
- oauth_client.fetch_temporary_credential!(:additional_parameters => {
- :scope => options[:scope],
- :xoauth_displayname => 'Google API Client'
- })
-
- # Launch browser
- Launchy::Browser.run(oauth_client.authorization_uri.to_s)
-
- server.start
- oauth_client.fetch_token_credential!(:verifier => $verifier)
- config = {
- "scope" => options[:scope],
- "client_credential_key" =>
- oauth_client.client_credential_key,
- "client_credential_secret" =>
- oauth_client.client_credential_secret,
- "token_credential_key" =>
- oauth_client.token_credential_key,
- "token_credential_secret" =>
- oauth_client.token_credential_secret
- }
- config_file = File.expand_path('~/.google-api.yaml')
- open(config_file, 'w') { |file| file.write(YAML.dump(config)) }
- exit(0)
- end
- end
-
def oauth_2_login
require 'signet/oauth_2/client'
- require 'launchy'
require 'yaml'
if !options[:client_credential_key] ||
!options[:client_credential_secret]
@@ -365,45 +246,26 @@ HTML
open(config_file, 'w') { |file| file.write(YAML.dump(config)) }
exit(0)
else
- $verifier = nil
- logger = WEBrick::Log.new
- logger.level = 0
- server = WEBrick::HTTPServer.new(
- :Port => OAUTH_SERVER_PORT,
- :Logger => logger,
- :AccessLog => logger
- )
- trap("INT") { server.shutdown }
-
- server.mount("/", OAuthVerifierServlet)
-
- oauth_client = Signet::OAuth2::Client.new(
- :authorization_uri =>
- 'https://www.google.com/accounts/o8/oauth2/authorization',
- :token_credential_uri =>
- 'https://www.google.com/accounts/o8/oauth2/token',
+ flow = Google::APIClient::InstalledAppFlow.new(
+ :port => OAUTH_SERVER_PORT,
:client_id => options[:client_credential_key],
:client_secret => options[:client_credential_secret],
- :redirect_uri => "http://localhost:#{OAUTH_SERVER_PORT}/",
:scope => options[:scope]
)
-
- # Launch browser
- Launchy.open(oauth_client.authorization_uri.to_s)
-
- server.start
- oauth_client.code = $verifier
- oauth_client.fetch_access_token!
- config = {
- "mechanism" => "oauth_2",
- "scope" => options[:scope],
- "client_id" => oauth_client.client_id,
- "client_secret" => oauth_client.client_secret,
- "access_token" => oauth_client.access_token,
- "refresh_token" => oauth_client.refresh_token
- }
- config_file = File.expand_path('~/.google-api.yaml')
- open(config_file, 'w') { |file| file.write(YAML.dump(config)) }
+
+ oauth_client = flow.authorize
+ if oauth_client
+ config = {
+ "mechanism" => "oauth_2",
+ "scope" => options[:scope],
+ "client_id" => oauth_client.client_id,
+ "client_secret" => oauth_client.client_secret,
+ "access_token" => oauth_client.access_token,
+ "refresh_token" => oauth_client.refresh_token
+ }
+ config_file = File.expand_path('~/.google-api.yaml')
+ open(config_file, 'w') { |file| file.write(YAML.dump(config)) }
+ end
exit(0)
end
end
@@ -414,7 +276,7 @@ HTML
STDERR.puts('No API name supplied.')
exit(1)
end
- client = Google::APIClient.new(:authorization => nil)
+ #client = Google::APIClient.new(:authorization => nil)
if options[:discovery_uri]
if options[:api] && options[:version]
client.register_discovery_uri(
@@ -517,16 +379,6 @@ HTML
IRB.start(__FILE__)
end
- def fuzz
- STDERR.puts('API fuzzing not yet supported.')
- if self.rpcname
- # Fuzz just one method
- else
- # Fuzz the entire API
- end
- exit(1)
- end
-
def help
puts self.parser
exit(0)
@@ -0,0 +1,115 @@
+# Copyright 2010 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require 'webrick'
+require 'launchy'
+
+module Google
+ class APIClient
+
+ # Small helper for the sample apps for performing OAuth 2.0 flows from the command
+ # line or in any other installed app environment.
+ #
+ # @example
+ #
+ # client = Google::APIClient.new
+ # flow = Google::APIClient::InstalledAppFlow.new(
+ # :client_id => '691380668085.apps.googleusercontent.com',
+ # :client_secret => '...,
+ # :scope => 'https://www.googleapis.com/auth/drive'
+ # )
+ # client.authorization = flow.authorize
+ #
+ class InstalledAppFlow
+
+ RESPONSE_BODY = <<-HTML
+ <html>
+ <head>
+ <script>
+ function closeWindow() {
+ window.open('', '_self', '');
+ window.close();
+ }
+ setTimeout(closeWindow, 10);
+ </script>
+ </head>
+ <body>You may close this window.</body>
+ </html>
+ HTML
+
+ ##
+ # Configure the flow
+ #
+ # @param [Hash] options The configuration parameters for the client.
+ # @option options [Fixnum] :port
+ # Port to run the embedded server on. Defaults to 9292
+ # @option options [String] :client_id
+ # A unique identifier issued to the client to identify itself to the
+ # authorization server.
+ # @option options [String] :client_secret
+ # A shared symmetric secret issued by the authorization server,
+ # which is used to authenticate the client.
+ # @option options [String] :scope
+ # The scope of the access request, expressed either as an Array
+ # or as a space-delimited String.
+ #
+ # @see Signet::OAuth2::Client
+ def initialize(options)
+ @port = options[:port] || 9292
+ @authorization = Signet::OAuth2::Client.new({
+ :authorization_uri => 'https://accounts.google.com/o/oauth2/auth',
+ :token_credential_uri => 'https://accounts.google.com/o/oauth2/token',
+ :redirect_uri => "http://localhost:#{@port}/"}.update(options)
+ )
+ end
+
+ ##
+ # Request authorization. Opens a browser and waits for response.
+ #
+ # @return [Signet::OAuth2::Client]
+ # Authorization instance, nil if user cancelled.
+ def authorize
+ auth = @authorization
+
+ server = WEBrick::HTTPServer.new(
+ :Port => @port,
+ :BindAddress =>"localhost",
+ :Logger => WEBrick::Log.new(STDOUT, 0),
+ :AccessLog => []
+ )
+ trap("INT") { server.shutdown }
+
+ server.mount_proc '/' do |req, res|
+ auth.code = req.query['code']
+ if auth.code
+ auth.fetch_access_token!
+ end
+ res.status = WEBrick::HTTPStatus::RC_ACCEPTED
+ res.body = RESPONSE_BODY
+ server.stop
+ end
+
+ Launchy.open(auth.authorization_uri.to_s)
+ server.start
+ if @authorization.access_token
+ return @authorization
+ else
+ return nil
+ end
+ end
+ end
+
+ end
+end
+

0 comments on commit 8ce4d05

Please sign in to comment.