Skip to content

Commit

Permalink
Merge pull request #50 from sqrrrl/master
Browse files Browse the repository at this point in the history
Base version of 3LO support.
  • Loading branch information
tbetbetbe committed Nov 11, 2015
2 parents 5cae4f7 + d1bec63 commit 1e92446
Show file tree
Hide file tree
Showing 24 changed files with 1,636 additions and 63 deletions.
28 changes: 22 additions & 6 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
# This configuration was generated by `rubocop --auto-gen-config`
# on 2015-05-18 09:38:28 -0700 using RuboCop version 0.31.0.
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2015-10-14 13:50:41 -0700 using RuboCop version 0.34.2.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.

# Offense count: 3
# Offense count: 4
Metrics/AbcSize:
Max: 24
Max: 27

# Offense count: 10
# Offense count: 1
# Configuration parameters: CountComments.
Metrics/ClassLength:
Max: 109

# Offense count: 1
Metrics/CyclomaticComplexity:
Max: 7

# Offense count: 16
# Configuration parameters: CountComments.
Metrics/MethodLength:
Max: 13
Max: 22

# Offense count: 2
# Configuration parameters: EnforcedStyle, SupportedStyles.
Style/FormatString:
Exclude:
- 'lib/googleauth/user_authorizer.rb'
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ rvm:
- 1.9.3
- rbx-2
- jruby
matrix:
allow_failures:
- rbx-2 # See rubinius/rubinius#3485 - rubocop segfaults
script: "bundle exec rake"
addons:
apt:
Expand Down
18 changes: 18 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,21 @@ source 'https://rubygems.org'

# Specify your gem's dependencies in googleauth.gemspec
gemspec

group :development do
gem 'bundler', '~> 1.9'
gem 'simplecov', '~> 0.9'
gem 'coveralls', '~> 0.7'
gem 'fakefs', '~> 0.6'
gem 'rake', '~> 10.0'
gem 'rubocop', '~> 0.30'
gem 'rspec', '~> 3.0'
gem 'redis', '~> 3.2'
gem 'fakeredis', '~> 0.5'
gem 'webmock', '~> 1.21'
end

platforms :jruby do
group :development do
end
end
8 changes: 0 additions & 8 deletions googleauth.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,4 @@ Gem::Specification.new do |s|
s.add_dependency 'memoist', '~> 0.12'
s.add_dependency 'multi_json', '~> 1.11'
s.add_dependency 'signet', '~> 0.6'

s.add_development_dependency 'bundler', '~> 1.9'
s.add_development_dependency 'simplecov', '~> 0.9'
s.add_development_dependency 'coveralls', '~> 0.7'
s.add_development_dependency 'fakefs', '~> 0.6'
s.add_development_dependency 'rake', '~> 10.0'
s.add_development_dependency 'rubocop', '~> 0.30'
s.add_development_dependency 'rspec', '~> 3.0'
end
8 changes: 5 additions & 3 deletions lib/googleauth.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
require 'googleauth/compute_engine'
require 'googleauth/service_account'
require 'googleauth/user_refresh'
require 'googleauth/client_id'
require 'googleauth/user_authorizer'

module Google
# Module Auth provides classes that provide Google-specific authorization
Expand All @@ -56,11 +58,11 @@ def self.make_creds(options = {})
json_key_io, scope = options.values_at(:json_key_io, :scope)
if json_key_io
json_key, clz = determine_creds_class(json_key_io)
clz.new(json_key_io: StringIO.new(MultiJson.dump(json_key)),
scope: scope)
clz.make_creds(json_key_io: StringIO.new(MultiJson.dump(json_key)),
scope: scope)
else
clz = read_creds
clz.new(scope: scope)
clz.make_creds(scope: scope)
end
end

Expand Down
102 changes: 102 additions & 0 deletions lib/googleauth/client_id.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Copyright 2014, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

require 'multi_json'

module Google
module Auth
# Representation of an application's identity for user authorization
# flows.
class ClientId
INSTALLED_APP = 'installed'
WEB_APP = 'web'
CLIENT_ID = 'client_id'
CLIENT_SECRET = 'client_secret'
MISSING_TOP_LEVEL_ELEMENT_ERROR =
"Expected top level property 'installed' or 'web' to be present."

# Text identifier of the client ID
# @return [String]
attr_reader :id

# Secret associated with the client ID
# @return [String]
attr_reader :secret

class << self
attr_accessor :default
end

# Initialize the Client ID
#
# @param [String] id
# Text identifier of the client ID
# @param [String] secret
# Secret associated with the client ID
# @note Direction instantion is discouraged to avoid embedding IDs
# & secrets in source. See {#from_file} to load from
# `client_secrets.json` files.
def initialize(id, secret)
fail 'Client id can not be nil' if id.nil?
fail 'Client secret can not be nil' if secret.nil?
@id = id
@secret = secret
end

# Constructs a Client ID from a JSON file downloaed from the
# Google Developers Console.
#
# @param [String, File] file
# Path of file to read from
# @return [Google::Auth::ClientID]
def self.from_file(file)
fail 'File can not be nil.' if file.nil?
File.open(file.to_s) do |f|
json = f.read
config = MultiJson.load(json)
from_hash(config)
end
end

# Constructs a Client ID from a previously loaded JSON file. The hash
# structure should
# match the expected JSON format.
#
# @param [hash] config
# Parsed contents of the JSON file
# @return [Google::Auth::ClientID]
def self.from_hash(config)
fail 'Hash can not be nil.' if config.nil?
raw_detail = config[INSTALLED_APP] || config[WEB_APP]
fail MISSING_TOP_LEVEL_ELEMENT_ERROR if raw_detail.nil?
ClientId.new(raw_detail[CLIENT_ID], raw_detail[CLIENT_SECRET])
end
end
end
end
61 changes: 61 additions & 0 deletions lib/googleauth/scope_util.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

require 'googleauth/signet'
require 'googleauth/credentials_loader'
require 'multi_json'

module Google
module Auth
# Small utility for normalizing scopes into canonical form
module ScopeUtil
ALIASES = {
'email' => 'https://www.googleapis.com/auth/userinfo.email',
'profile' => 'https://www.googleapis.com/auth/userinfo.profile',
'openid' => 'https://www.googleapis.com/auth/plus.me'
}

def self.normalize(scope)
list = as_array(scope)
list.map { |item| ALIASES[item] || item }
end

def self.as_array(scope)
case scope
when Array
scope
when String
scope.split(' ')
else
fail 'Invalid scope value. Must be string or array'
end
end
end
end
end
41 changes: 23 additions & 18 deletions lib/googleauth/service_account.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,33 +49,37 @@ class ServiceAccountCredentials < Signet::OAuth2::Client
TOKEN_CRED_URI = 'https://www.googleapis.com/oauth2/v3/token'
extend CredentialsLoader

# Reads the private key and client email fields from the service account
# JSON key.
def self.read_json_key(json_key_io)
json_key = MultiJson.load(json_key_io.read)
fail 'missing client_email' unless json_key.key?('client_email')
fail 'missing private_key' unless json_key.key?('private_key')
[json_key['private_key'], json_key['client_email']]
end

# Initializes a ServiceAccountCredentials.
# Creates a ServiceAccountCredentials.
#
# @param json_key_io [IO] an IO from which the JSON key can be read
# @param scope [string|array|nil] the scope(s) to access
def initialize(options = {})
def self.make_creds(options = {})
json_key_io, scope = options.values_at(:json_key_io, :scope)
if json_key_io
private_key, client_email = self.class.read_json_key(json_key_io)
private_key, client_email = read_json_key(json_key_io)
else
private_key = ENV[CredentialsLoader::PRIVATE_KEY_VAR]
client_email = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
end

super(token_credential_uri: TOKEN_CRED_URI,
audience: TOKEN_CRED_URI,
scope: scope,
issuer: client_email,
signing_key: OpenSSL::PKey::RSA.new(private_key))
new(token_credential_uri: TOKEN_CRED_URI,
audience: TOKEN_CRED_URI,
scope: scope,
issuer: client_email,
signing_key: OpenSSL::PKey::RSA.new(private_key))
end

# Reads the private key and client email fields from the service account
# JSON key.
def self.read_json_key(json_key_io)
json_key = MultiJson.load(json_key_io.read)
fail 'missing client_email' unless json_key.key?('client_email')
fail 'missing private_key' unless json_key.key?('private_key')
[json_key['private_key'], json_key['client_email']]
end

def initialize(options = {})
super(options)
end

# Extends the base class.
Expand All @@ -97,7 +101,8 @@ def apply!(a_hash, opts = {})
client_email: @issuer
}
alt_clz = ServiceAccountJwtHeaderCredentials
alt = alt_clz.new(json_key_io: StringIO.new(MultiJson.dump(cred_json)))
key_io = StringIO.new(MultiJson.dump(cred_json))
alt = alt_clz.make_creds(json_key_io: key_io)
alt.apply!(a_hash)
end
end
Expand Down
19 changes: 19 additions & 0 deletions lib/googleauth/signet.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,25 @@ def apply(a_hash, opts = {})
def updater_proc
lambda(&method(:apply))
end

def on_refresh(&block)
@refresh_listeners ||= []
@refresh_listeners << block
end

alias_method :orig_fetch_access_token!, :fetch_access_token!
def fetch_access_token!(options)
info = orig_fetch_access_token!(options)
notify_refresh_listeners
info
end

def notify_refresh_listeners
listeners = @refresh_listeners || []
listeners.each do |block|
block.call(self)
end
end
end
end
end
Loading

0 comments on commit 1e92446

Please sign in to comment.