Add oauth helper for installed apps, update CLI
This commit is contained in:
parent
1d7315ee9b
commit
8ce4d052fe
174
bin/google-api
174
bin/google-api
|
@ -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,35 +246,15 @@ 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!
|
||||
oauth_client = flow.authorize
|
||||
if oauth_client
|
||||
config = {
|
||||
"mechanism" => "oauth_2",
|
||||
"scope" => options[:scope],
|
||||
|
@ -404,6 +265,7 @@ HTML
|
|||
}
|
||||
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
|
||||
|
Loading…
Reference in New Issue