Import refactored library. See MIGRATING.MD for details

This commit is contained in:
Steven Bazyl committed 2015-06-23 15:15:14 -07:00
1 parent 9b7809174d
commit f584a2cec8
373 files changed
+255715 -474

No files matched your search

+126
View File
@@ -0,0 +1,126 @@
# 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.
#
# @param [Google::APIClient::Storage] storage
# Optional object that responds to :write_credentials, used to serialize
# the OAuth 2 credentials after completing the flow.
#
# @return [Signet::OAuth2::Client]
# Authorization instance, nil if user cancelled.
def authorize(storage=nil)
auth = @authorization
server = WEBrick::HTTPServer.new(
:Port => @port,
:BindAddress =>"localhost",
:Logger => WEBrick::Log.new(STDOUT, 0),
:AccessLog => []
)
begin
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
ensure
server.shutdown
end
if @authorization.access_token
if storage.respond_to?(:write_credentials)
storage.write_credentials(@authorization)
end
return @authorization
else
return nil
end
end
end
end
end
+102
View File
@@ -0,0 +1,102 @@
# Copyright 2013 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 'signet/oauth_2/client'
module Google
class APIClient
##
# Represents cached OAuth 2 tokens stored on local disk in a
# JSON serialized file. Meant to resemble the serialized format
# http://google-api-python-client.googlecode.com/hg/docs/epy/oauth2client.file.Storage-class.html
#
class Storage
AUTHORIZATION_URI = 'https://accounts.google.com/o/oauth2/auth'
TOKEN_CREDENTIAL_URI = 'https://accounts.google.com/o/oauth2/token'
# @return [Object] Storage object.
attr_accessor :store
# @return [Signet::OAuth2::Client]
attr_reader :authorization
##
# Initializes the Storage object.
#
# @params [Object] Storage object
def initialize(store)
@store= store
@authorization = nil
end
##
# Write the credentials to the specified store.
#
# @params [Signet::OAuth2::Client] authorization
# Optional authorization instance. If not provided, the authorization
# already associated with this instance will be written.
def write_credentials(authorization=nil)
@authorization = authorization if authorization
if @authorization.respond_to?(:refresh_token) && @authorization.refresh_token
store.write_credentials(credentials_hash)
end
end
##
# Loads credentials and authorizes an client.
# @return [Object] Signet::OAuth2::Client or NIL
def authorize
@authorization = nil
cached_credentials = load_credentials
if cached_credentials && cached_credentials.size > 0
@authorization = Signet::OAuth2::Client.new(cached_credentials)
@authorization.issued_at = Time.at(cached_credentials['issued_at'].to_i)
self.refresh_authorization if @authorization.expired?
end
return @authorization
end
##
# refresh credentials and save them to store
def refresh_authorization
authorization.refresh!
self.write_credentials
end
private
##
# Attempt to read in credentials from the specified store.
def load_credentials
store.load_credentials
end
##
# @return [Hash] with credentials
def credentials_hash
{
:access_token => authorization.access_token,
:authorization_uri => AUTHORIZATION_URI,
:client_id => authorization.client_id,
:client_secret => authorization.client_secret,
:expires_in => authorization.expires_in,
:refresh_token => authorization.refresh_token,
:token_credential_uri => TOKEN_CREDENTIAL_URI,
:issued_at => authorization.issued_at.to_i
}
end
end
end
end
@@ -0,0 +1,58 @@
# Copyright 2013 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 'json'
module Google
class APIClient
##
# Represents cached OAuth 2 tokens stored on local disk in a
# JSON serialized file. Meant to resemble the serialized format
# http://google-api-python-client.googlecode.com/hg/docs/epy/oauth2client.file.Storage-class.html
#
class FileStore
attr_accessor :path
##
# Initializes the FileStorage object.
#
# @param [String] path
# Path to the credentials file.
def initialize(path)
@path= path
end
##
# Attempt to read in credentials from the specified file.
def load_credentials
open(path, 'r') { |f| JSON.parse(f.read) }
rescue
nil
end
##
# Write the credentials to the specified file.
#
# @param [Signet::OAuth2::Client] authorization
# Optional authorization instance. If not provided, the authorization
# already associated with this instance will be written.
def write_credentials(credentials_hash)
open(self.path, 'w+') do |f|
f.write(credentials_hash.to_json)
end
end
end
end
end
@@ -0,0 +1,54 @@
# Copyright 2013 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 'json'
module Google
class APIClient
class RedisStore
DEFAULT_REDIS_CREDENTIALS_KEY = "google_api_credentials"
attr_accessor :redis
##
# Initializes the RedisStore object.
#
# @params [Object] Redis instance
def initialize(redis, key = nil)
@redis= redis
@redis_credentials_key = key
end
##
# Attempt to read in credentials from redis.
def load_credentials
credentials = redis.get redis_credentials_key
JSON.parse(credentials) if credentials
end
def redis_credentials_key
@redis_credentials_key || DEFAULT_REDIS_CREDENTIALS_KEY
end
##
# Write the credentials to redis.
#
# @params [Hash] credentials
def write_credentials(credentials_hash)
redis.set(redis_credentials_key, credentials_hash.to_json)
end
end
end
end
+179
View File
@@ -0,0 +1,179 @@
# 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 'compat/multi_json'
module Google
class APIClient
##
# Manages the persistence of client configuration data and secrets. Format
# inspired by the Google API Python client.
#
# @see https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
#
# @example
# {
# "web": {
# "client_id": "asdfjasdljfasdkjf",
# "client_secret": "1912308409123890",
# "redirect_uris": ["https://www.example.com/oauth2callback"],
# "auth_uri": "https://accounts.google.com/o/oauth2/auth",
# "token_uri": "https://accounts.google.com/o/oauth2/token"
# }
# }
#
# @example
# {
# "installed": {
# "client_id": "837647042410-75ifg...usercontent.com",
# "client_secret":"asdlkfjaskd",
# "redirect_uris": ["http://localhost", "urn:ietf:oauth:2.0:oob"],
# "auth_uri": "https://accounts.google.com/o/oauth2/auth",
# "token_uri": "https://accounts.google.com/o/oauth2/token"
# }
# }
class ClientSecrets
##
# Reads client configuration from a file
#
# @param [String] filename
# Path to file to load
#
# @return [Google::APIClient::ClientSecrets]
# OAuth client settings
def self.load(filename=nil)
if filename && File.directory?(filename)
search_path = File.expand_path(filename)
filename = nil
end
while filename == nil
search_path ||= File.expand_path('.')
if File.exists?(File.join(search_path, 'client_secrets.json'))
filename = File.join(search_path, 'client_secrets.json')
elsif search_path == '/' || search_path =~ /[a-zA-Z]:[\/\\]/
raise ArgumentError,
'No client_secrets.json filename supplied ' +
'and/or could not be found in search path.'
else
search_path = File.expand_path(File.join(search_path, '..'))
end
end
data = File.open(filename, 'r') { |file| MultiJson.load(file.read) }
return self.new(data)
end
##
# Intialize OAuth client settings.
#
# @param [Hash] options
# Parsed client secrets files
def initialize(options={})
# Client auth configuration
@flow = options[:flow] || options.keys.first.to_s || 'web'
fdata = options[@flow]
@client_id = fdata[:client_id] || fdata["client_id"]
@client_secret = fdata[:client_secret] || fdata["client_secret"]
@redirect_uris = fdata[:redirect_uris] || fdata["redirect_uris"]
@redirect_uris ||= [fdata[:redirect_uri] || fdata["redirect_uri"]].compact
@javascript_origins = (
fdata[:javascript_origins] ||
fdata["javascript_origins"]
)
@javascript_origins ||= [fdata[:javascript_origin] || fdata["javascript_origin"]].compact
@authorization_uri = fdata[:auth_uri] || fdata["auth_uri"]
@authorization_uri ||= fdata[:authorization_uri]
@token_credential_uri = fdata[:token_uri] || fdata["token_uri"]
@token_credential_uri ||= fdata[:token_credential_uri]
# Associated token info
@access_token = fdata[:access_token] || fdata["access_token"]
@refresh_token = fdata[:refresh_token] || fdata["refresh_token"]
@id_token = fdata[:id_token] || fdata["id_token"]
@expires_in = fdata[:expires_in] || fdata["expires_in"]
@expires_at = fdata[:expires_at] || fdata["expires_at"]
@issued_at = fdata[:issued_at] || fdata["issued_at"]
end
attr_reader(
:flow, :client_id, :client_secret, :redirect_uris, :javascript_origins,
:authorization_uri, :token_credential_uri, :access_token,
:refresh_token, :id_token, :expires_in, :expires_at, :issued_at
)
##
# Serialize back to the original JSON form
#
# @return [String]
# JSON
def to_json
return MultiJson.dump(to_hash)
end
def to_hash
{
self.flow => ({
'client_id' => self.client_id,
'client_secret' => self.client_secret,
'redirect_uris' => self.redirect_uris,
'javascript_origins' => self.javascript_origins,
'auth_uri' => self.authorization_uri,
'token_uri' => self.token_credential_uri,
'access_token' => self.access_token,
'refresh_token' => self.refresh_token,
'id_token' => self.id_token,
'expires_in' => self.expires_in,
'expires_at' => self.expires_at,
'issued_at' => self.issued_at
}).inject({}) do |accu, (k, v)|
# Prunes empty values from JSON output.
unless v == nil || (v.respond_to?(:empty?) && v.empty?)
accu[k] = v
end
accu
end
}
end
def to_authorization
gem 'signet', '>= 0.4.0'
require 'signet/oauth_2/client'
# NOTE: Do not rely on this default value, as it may change
new_authorization = Signet::OAuth2::Client.new
new_authorization.client_id = self.client_id
new_authorization.client_secret = self.client_secret
new_authorization.authorization_uri = (
self.authorization_uri ||
'https://accounts.google.com/o/oauth2/auth'
)
new_authorization.token_credential_uri = (
self.token_credential_uri ||
'https://accounts.google.com/o/oauth2/token'
)
new_authorization.redirect_uri = self.redirect_uris.first
# These are supported, but unlikely.
new_authorization.access_token = self.access_token
new_authorization.refresh_token = self.refresh_token
new_authorization.id_token = self.id_token
new_authorization.expires_in = self.expires_in
new_authorization.issued_at = self.issued_at if self.issued_at
new_authorization.expires_at = self.expires_at if self.expires_at
return new_authorization
end
end
end
end