Merge pull request #783 from tbetbetbe/grpc_ruby_switch_auth_to_google_auth_library_ruby

Grpc ruby switch auth to google auth library ruby
pull/400/merge
Michael Lumish 10 years ago
commit 886144544c
  1. 35
      src/ruby/bin/apis/pubsub_demo.rb
  2. 31
      src/ruby/bin/interop/interop_client.rb
  3. 1
      src/ruby/grpc.gemspec
  4. 2
      src/ruby/lib/grpc.rb
  5. 67
      src/ruby/lib/grpc/auth/compute_engine.rb
  6. 66
      src/ruby/lib/grpc/auth/service_account.rb
  7. 67
      src/ruby/lib/grpc/auth/signet.rb
  8. 163
      src/ruby/spec/auth/apply_auth_examples.rb
  9. 108
      src/ruby/spec/auth/compute_engine_spec.rb
  10. 75
      src/ruby/spec/auth/service_account_spec.rb
  11. 70
      src/ruby/spec/auth/signet_spec.rb
  12. 12
      tools/gce_setup/grpc_docker.sh

@ -31,10 +31,9 @@
# pubsub_demo demos accesses the Google PubSub API via its gRPC interface
#
# TODO: update the Usage once the usable auth gem is available
# $ SSL_CERT_FILE=<path/to/ssl/certs> \
# $ GOOGLE_APPLICATION_CREDENTIALS=<path_to_service_account_key_file> \
# SSL_CERT_FILE=<path/to/ssl/certs> \
# path/to/pubsub_demo.rb \
# --service_account_key_file=<path_to_service_account> \
# [--action=<chosen_demo_action> ]
#
# There are options related to the chosen action, see #parse_args below.
@ -49,6 +48,7 @@ $LOAD_PATH.unshift(this_dir) unless $LOAD_PATH.include?(this_dir)
require 'optparse'
require 'grpc'
require 'googleauth'
require 'google/protobuf'
require 'google/protobuf/empty'
@ -59,7 +59,9 @@ require 'tech/pubsub/proto/pubsub_services'
def load_prod_cert
fail 'could not find a production cert' if ENV['SSL_CERT_FILE'].nil?
p "loading prod certs from #{ENV['SSL_CERT_FILE']}"
File.open(ENV['SSL_CERT_FILE']).read
File.open(ENV['SSL_CERT_FILE']) do |f|
return f.read
end
end
# creates a SSL Credentials from the production certificates.
@ -68,14 +70,9 @@ def ssl_creds
end
# Builds the metadata authentication update proc.
#
# TODO: replace this once the ruby usable auth repo is available.
def auth_proc(opts)
if GRPC::Auth::GCECredentials.on_gce?
return GRPC::Auth::GCECredentials.new.updater_proc
end
fd = StringIO.new(File.read(opts.oauth_key_file))
GRPC::Auth::ServiceAccountCredentials.new(opts.oauth_scope, fd).updater_proc
auth_creds = Google::Auth.get_application_default(opts.oauth_scope)
return auth_creds.updater_proc
end
# Creates a stub for accessing the publisher service.
@ -216,14 +213,14 @@ class NamedActions
end
# Args is used to hold the command line info.
Args = Struct.new(:host, :oauth_scope, :oauth_key_file, :port, :action,
:project_id, :topic_name, :sub_name)
Args = Struct.new(:host, :oauth_scope, :port, :action, :project_id, :topic_name,
:sub_name)
# validates the the command line options, returning them as an Arg.
def parse_args
args = Args.new('pubsub-staging.googleapis.com',
'https://www.googleapis.com/auth/pubsub',
nil, 443, 'list_some_topics', 'stoked-keyword-656')
443, 'list_some_topics', 'stoked-keyword-656')
OptionParser.new do |opts|
opts.on('--oauth_scope scope',
'Scope for OAuth tokens') { |v| args['oauth_scope'] = v }
@ -233,10 +230,6 @@ def parse_args
opts.on('--server_port SERVER_PORT', 'server port') do |v|
args.port = v
end
opts.on('--service_account_key_file PATH',
'Path to the service account json key file') do |v|
args.oauth_key_file = v
end
# instance_methods(false) gives only the methods defined in that class.
scenes = NamedActions.instance_methods(false).map { |t| t.to_s }
@ -257,15 +250,11 @@ def parse_args
end
def _check_args(args)
%w(host port action).each do |a|
%w(host port action oauth_scope).each do |a|
if args[a].nil?
raise OptionParser::MissingArgument.new("please specify --#{a}")
end
end
if args['oauth_key_file'].nil? || args['oauth_scope'].nil?
fail(OptionParser::MissingArgument,
'please specify both of --service_account_key_file and --oauth_scope')
end
args
end

@ -48,6 +48,7 @@ require 'minitest'
require 'minitest/assertions'
require 'grpc'
require 'googleauth'
require 'google/protobuf'
require 'test/cpp/interop/test_services'
@ -56,7 +57,7 @@ require 'test/cpp/interop/empty'
require 'signet/ssl_config'
include GRPC::Auth
AUTH_ENV = Google::Auth::ServiceAccountCredentials::ENV_VAR
# loads the certificates used to access the test server securely.
def load_test_certs
@ -101,22 +102,14 @@ def create_stub(opts)
}
# Add service account creds if specified
if %w(all service_account_creds).include?(opts.test_case)
wants_creds = %w(all compute_engine_creds service_account_creds)
if wants_creds.include?(opts.test_case)
unless opts.oauth_scope.nil?
fd = StringIO.new(File.read(opts.oauth_key_file))
logger.info("loading oauth certs from #{opts.oauth_key_file}")
auth_creds = ServiceAccountCredentials.new(opts.oauth_scope, fd)
auth_creds = Google::Auth.get_application_default(opts.oauth_scope)
stub_opts[:update_metadata] = auth_creds.updater_proc
end
end
# Add compute engine creds if specified
if %w(all compute_engine_creds).include?(opts.test_case)
unless opts.oauth_scope.nil?
stub_opts[:update_metadata] = GCECredentials.new.update_proc
end
end
logger.info("... connecting securely to #{address}")
Grpc::Testing::TestService::Stub.new(address, **stub_opts)
else
@ -193,11 +186,11 @@ class NamedTests
def service_account_creds
# ignore this test if the oauth options are not set
if @args.oauth_scope.nil? || @args.oauth_key_file.nil?
if @args.oauth_scope.nil?
p 'NOT RUN: service_account_creds; no service_account settings'
return
end
json_key = File.read(@args.oauth_key_file)
json_key = File.read(ENV[AUTH_ENV])
wanted_email = MultiJson.load(json_key)['client_email']
resp = perform_large_unary(fill_username: true,
fill_oauth_scope: true)
@ -285,7 +278,7 @@ end
# Args is used to hold the command line info.
Args = Struct.new(:default_service_account, :host, :host_override,
:oauth_scope, :oauth_key_file, :port, :secure, :test_case,
:oauth_scope, :port, :secure, :test_case,
:use_test_ca)
# validates the the command line options, returning them as a Hash.
@ -302,10 +295,6 @@ def parse_args
'email address of the default service account') do |v|
args['default_service_account'] = v
end
opts.on('--service_account_key_file PATH',
'Path to the service account json key file') do |v|
args['oauth_key_file'] = v
end
opts.on('--server_host_override HOST_OVERRIDE',
'override host via a HTTP header') do |v|
args['host_override'] = v
@ -333,10 +322,6 @@ def _check_args(args)
fail(OptionParser::MissingArgument, "please specify --#{arg}")
end
end
if args['oauth_key_file'].nil? ^ args['oauth_scope'].nil?
fail(OptionParser::MissingArgument,
'please specify both of --service_account_key_file and --oauth_scope')
end
args
end

@ -22,6 +22,7 @@ Gem::Specification.new do |s|
s.add_dependency 'faraday', '~> 0.9'
s.add_dependency 'google-protobuf', '~> 3.0.0alpha.1.1'
s.add_dependency 'googleauth', '~> 0.1'
s.add_dependency 'logging', '~> 1.8'
s.add_dependency 'jwt', '~> 1.2.1'
s.add_dependency 'minitest', '~> 5.4' # reqd for interop tests

@ -27,8 +27,6 @@
# (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 'grpc/auth/compute_engine.rb'
require 'grpc/auth/service_account.rb'
require 'grpc/errors'
require 'grpc/grpc'
require 'grpc/logconfig'

@ -1,67 +0,0 @@
# 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 'faraday'
require 'grpc/auth/signet'
module GRPC
# Module Auth provides classes that provide Google-specific authentication
# used to access Google gRPC services.
module Auth
# Extends Signet::OAuth2::Client so that the auth token is obtained from
# the GCE metadata server.
class GCECredentials < Signet::OAuth2::Client
COMPUTE_AUTH_TOKEN_URI = 'http://metadata/computeMetadata/v1/'\
'instance/service-accounts/default/token'
COMPUTE_CHECK_URI = 'http://metadata.google.internal'
# Detect if this appear to be a GCE instance, by checking if metadata
# is available
def self.on_gce?(options = {})
c = options[:connection] || Faraday.default_connection
resp = c.get(COMPUTE_CHECK_URI)
return false unless resp.status == 200
return false unless resp.headers.key?('Metadata-Flavor')
return resp.headers['Metadata-Flavor'] == 'Google'
rescue Faraday::ConnectionFailed
return false
end
# Overrides the super class method to change how access tokens are
# fetched.
def fetch_access_token(options = {})
c = options[:connection] || Faraday.default_connection
c.headers = { 'Metadata-Flavor' => 'Google' }
resp = c.get(COMPUTE_AUTH_TOKEN_URI)
Signet::OAuth2.parse_credentials(resp.body,
resp.headers['content-type'])
end
end
end
end

@ -1,66 +0,0 @@
# 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 'grpc/auth/signet'
require 'multi_json'
require 'openssl'
# Reads the private key and client email fields from service account JSON key.
def 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
module GRPC
# Module Auth provides classes that provide Google-specific authentication
# used to access Google gRPC services.
module Auth
# Authenticates requests using Google's Service Account credentials.
# (cf https://developers.google.com/accounts/docs/OAuth2ServiceAccount)
class ServiceAccountCredentials < Signet::OAuth2::Client
TOKEN_CRED_URI = 'https://www.googleapis.com/oauth2/v3/token'
AUDIENCE = TOKEN_CRED_URI
# Initializes a ServiceAccountCredentials.
#
# @param scope [string|array] the scope(s) to access
# @param json_key_io [IO] an IO from which the JSON key can be read
def initialize(scope, json_key_io)
private_key, client_email = read_json_key(json_key_io)
super(token_credential_uri: TOKEN_CRED_URI,
audience: AUDIENCE,
scope: scope,
issuer: client_email,
signing_key: OpenSSL::PKey::RSA.new(private_key))
end
end
end
end

@ -1,67 +0,0 @@
# 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 'signet/oauth_2/client'
module Signet
# Signet::OAuth2 supports OAuth2 authentication.
module OAuth2
AUTH_METADATA_KEY = :Authorization
# Signet::OAuth2::Client creates an OAuth2 client
#
# Here client is re-opened to add the #apply and #apply! methods which
# update a hash map with the fetched authentication token
#
# Eventually, this change may be merged into signet itself, or some other
# package that provides Google-specific auth via signet, and this extension
# will be unnecessary.
class Client
# Updates a_hash updated with the authentication token
def apply!(a_hash, opts = {})
# fetch the access token there is currently not one, or if the client
# has expired
fetch_access_token!(opts) if access_token.nil? || expired?
a_hash[AUTH_METADATA_KEY] = "Bearer #{access_token}"
end
# Returns a clone of a_hash updated with the authentication token
def apply(a_hash, opts = {})
a_copy = a_hash.clone
apply!(a_copy, opts)
a_copy
end
# Returns a reference to the #apply method, suitable for passing as
# a closure
def updater_proc
lambda(&method(:apply))
end
end
end
end

@ -1,163 +0,0 @@
# 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.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.uniq!
require 'faraday'
require 'spec_helper'
def build_json_response(payload)
[200,
{ 'Content-Type' => 'application/json; charset=utf-8' },
MultiJson.dump(payload)]
end
WANTED_AUTH_KEY = :Authorization
shared_examples 'apply/apply! are OK' do
# tests that use these examples need to define
#
# @client which should be an auth client
#
# @make_auth_stubs, which should stub out the expected http behaviour of the
# auth client
describe '#fetch_access_token' do
it 'should set access_token to the fetched value' do
token = '1/abcdef1234567890'
stubs = make_auth_stubs with_access_token: token
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
@client.fetch_access_token!(connection: c)
expect(@client.access_token).to eq(token)
stubs.verify_stubbed_calls
end
end
describe '#apply!' do
it 'should update the target hash with fetched access token' do
token = '1/abcdef1234567890'
stubs = make_auth_stubs with_access_token: token
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
md = { foo: 'bar' }
@client.apply!(md, connection: c)
want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer #{token}" }
expect(md).to eq(want)
stubs.verify_stubbed_calls
end
end
describe 'updater_proc' do
it 'should provide a proc that updates a hash with the access token' do
token = '1/abcdef1234567890'
stubs = make_auth_stubs with_access_token: token
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
md = { foo: 'bar' }
the_proc = @client.updater_proc
got = the_proc.call(md, connection: c)
want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer #{token}" }
expect(got).to eq(want)
stubs.verify_stubbed_calls
end
end
describe '#apply' do
it 'should not update the original hash with the access token' do
token = '1/abcdef1234567890'
stubs = make_auth_stubs with_access_token: token
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
md = { foo: 'bar' }
@client.apply(md, connection: c)
want = { foo: 'bar' }
expect(md).to eq(want)
stubs.verify_stubbed_calls
end
it 'should add the token to the returned hash' do
token = '1/abcdef1234567890'
stubs = make_auth_stubs with_access_token: token
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
md = { foo: 'bar' }
got = @client.apply(md, connection: c)
want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer #{token}" }
expect(got).to eq(want)
stubs.verify_stubbed_calls
end
it 'should not fetch a new token if the current is not expired' do
token = '1/abcdef1234567890'
stubs = make_auth_stubs with_access_token: token
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
n = 5 # arbitrary
n.times do |_t|
md = { foo: 'bar' }
got = @client.apply(md, connection: c)
want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer #{token}" }
expect(got).to eq(want)
end
stubs.verify_stubbed_calls
end
it 'should fetch a new token if the current one is expired' do
token_1 = '1/abcdef1234567890'
token_2 = '2/abcdef1234567890'
[token_1, token_2].each do |t|
stubs = make_auth_stubs with_access_token: t
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
md = { foo: 'bar' }
got = @client.apply(md, connection: c)
want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer #{t}" }
expect(got).to eq(want)
stubs.verify_stubbed_calls
@client.expires_at -= 3601 # default is to expire in 1hr
end
end
end
end

@ -1,108 +0,0 @@
# 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.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.uniq!
require 'apply_auth_examples'
require 'faraday'
require 'grpc/auth/compute_engine'
require 'spec_helper'
describe GRPC::Auth::GCECredentials do
MD_URI = '/computeMetadata/v1/instance/service-accounts/default/token'
GCECredentials = GRPC::Auth::GCECredentials
before(:example) do
@client = GCECredentials.new
end
def make_auth_stubs(with_access_token: '')
Faraday::Adapter::Test::Stubs.new do |stub|
stub.get(MD_URI) do |env|
headers = env[:request_headers]
expect(headers['Metadata-Flavor']).to eq('Google')
build_json_response(
'access_token' => with_access_token,
'token_type' => 'Bearer',
'expires_in' => 3600)
end
end
end
it_behaves_like 'apply/apply! are OK'
describe '#on_gce?' do
it 'should be true when Metadata-Flavor is Google' do
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.get('/') do |_env|
[200,
{ 'Metadata-Flavor' => 'Google' },
'']
end
end
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
expect(GCECredentials.on_gce?(connection: c)).to eq(true)
stubs.verify_stubbed_calls
end
it 'should be false when Metadata-Flavor is not Google' do
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.get('/') do |_env|
[200,
{ 'Metadata-Flavor' => 'NotGoogle' },
'']
end
end
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
expect(GCECredentials.on_gce?(connection: c)).to eq(false)
stubs.verify_stubbed_calls
end
it 'should be false if the response is not 200' do
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.get('/') do |_env|
[404,
{ 'Metadata-Flavor' => 'Google' },
'']
end
end
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
expect(GCECredentials.on_gce?(connection: c)).to eq(false)
stubs.verify_stubbed_calls
end
end
end

@ -1,75 +0,0 @@
# 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.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.uniq!
require 'apply_auth_examples'
require 'grpc/auth/service_account'
require 'jwt'
require 'multi_json'
require 'openssl'
require 'spec_helper'
describe GRPC::Auth::ServiceAccountCredentials do
before(:example) do
@key = OpenSSL::PKey::RSA.new(2048)
cred_json = {
private_key_id: 'a_private_key_id',
private_key: @key.to_pem,
client_email: 'app@developer.gserviceaccount.com',
client_id: 'app.apps.googleusercontent.com',
type: 'service_account'
}
cred_json_text = MultiJson.dump(cred_json)
@client = GRPC::Auth::ServiceAccountCredentials.new(
'https://www.googleapis.com/auth/userinfo.profile',
StringIO.new(cred_json_text))
end
def make_auth_stubs(with_access_token: '')
Faraday::Adapter::Test::Stubs.new do |stub|
stub.post('/oauth2/v3/token') do |env|
params = Addressable::URI.form_unencode(env[:body])
_claim, _header = JWT.decode(params.assoc('assertion').last,
@key.public_key)
want = ['grant_type', 'urn:ietf:params:oauth:grant-type:jwt-bearer']
expect(params.assoc('grant_type')).to eq(want)
build_json_response(
'access_token' => with_access_token,
'token_type' => 'Bearer',
'expires_in' => 3600
)
end
end
end
it_behaves_like 'apply/apply! are OK'
end

@ -1,70 +0,0 @@
# 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.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.uniq!
require 'apply_auth_examples'
require 'grpc/auth/signet'
require 'jwt'
require 'openssl'
require 'spec_helper'
describe Signet::OAuth2::Client do
before(:example) do
@key = OpenSSL::PKey::RSA.new(2048)
@client = Signet::OAuth2::Client.new(
token_credential_uri: 'https://accounts.google.com/o/oauth2/token',
scope: 'https://www.googleapis.com/auth/userinfo.profile',
issuer: 'app@example.com',
audience: 'https://accounts.google.com/o/oauth2/token',
signing_key: @key
)
end
def make_auth_stubs(with_access_token: '')
Faraday::Adapter::Test::Stubs.new do |stub|
stub.post('/o/oauth2/token') do |env|
params = Addressable::URI.form_unencode(env[:body])
_claim, _header = JWT.decode(params.assoc('assertion').last,
@key.public_key)
want = ['grant_type', 'urn:ietf:params:oauth:grant-type:jwt-bearer']
expect(params.assoc('grant_type')).to eq(want)
build_json_response(
'access_token' => with_access_token,
'token_type' => 'Bearer',
'expires_in' => 3600
)
end
end
end
it_behaves_like 'apply/apply! are OK'
end

@ -957,9 +957,10 @@ grpc_cloud_prod_auth_service_account_creds_gen_ruby_cmd() {
local test_script="/var/local/git/grpc/src/ruby/bin/interop/interop_client.rb"
local test_script+=" --use_tls"
local gfe_flags=$(_grpc_prod_gfe_flags)
local added_gfe_flags=$(_grpc_svc_acc_test_flags)
local added_gfe_flags=$(_grpc_default_creds_test_flags)
local env_prefix="SSL_CERT_FILE=/cacerts/roots.pem"
local the_cmd="$cmd_prefix '$env_prefix ruby $test_script $gfe_flags $added_gfe_flag $@'"
env_prefix+=" GOOGLE_APPLICATION_CREDENTIALS=/service_account/stubbyCloudTestingTest-7dd63462c60c.json"
local the_cmd="$cmd_prefix '$env_prefix ruby $test_script $gfe_flags $added_gfe_flags $@'"
echo $the_cmd
}
@ -975,7 +976,7 @@ grpc_cloud_prod_auth_compute_engine_creds_gen_ruby_cmd() {
local gfe_flags=$(_grpc_prod_gfe_flags)
local added_gfe_flags=$(_grpc_gce_test_flags)
local env_prefix="SSL_CERT_FILE=/cacerts/roots.pem"
local the_cmd="$cmd_prefix '$env_prefix ruby $test_script $gfe_flags $added_gfe_flag $@'"
local the_cmd="$cmd_prefix '$env_prefix ruby $test_script $gfe_flags $added_gfe_flags $@'"
echo $the_cmd
}
@ -1153,6 +1154,11 @@ _grpc_svc_acc_test_flags() {
echo " --service_account_key_file=/service_account/stubbyCloudTestingTest-7dd63462c60c.json --oauth_scope=https://www.googleapis.com/auth/xapi.zoo"
}
# default credentials test flag
_grpc_default_creds_test_flags() {
echo " --oauth_scope=https://www.googleapis.com/auth/xapi.zoo"
}
# outputs the flags passed to the gcloud auth tests
_grpc_gce_test_flags() {
echo " --default_service_account=155450119199-r5aaqa2vqoa9g5mv2m6s3m1l293rlmel@developer.gserviceaccount.com --oauth_scope=https://www.googleapis.com/auth/xapi.zoo"

Loading…
Cancel
Save