diff options
author | Tim Emiola <temiola@google.com> | 2015-01-30 18:50:30 -0800 |
---|---|---|
committer | Tim Emiola <temiola@google.com> | 2015-01-31 18:11:56 -0800 |
commit | 4694df3c4e8fb68c3b327070d25dcc4524b6badb (patch) | |
tree | 2d745392d39f9e219d91bb67dfa5b2760579b904 | |
parent | 720bc81c899cc9c75f73984a8e2a2498b31a1604 (diff) |
Adds compute engine auth implementation that extends the signet auth class
- also, corrects the content of the authorization header
-rw-r--r-- | src/ruby/lib/grpc/auth/compute_engine.rb | 69 | ||||
-rw-r--r-- | src/ruby/lib/grpc/auth/signet.rb | 2 | ||||
-rw-r--r-- | src/ruby/spec/auth/apply_auth_examples.rb | 25 | ||||
-rw-r--r-- | src/ruby/spec/auth/compute_engine_spec.rb | 108 |
4 files changed, 199 insertions, 5 deletions
diff --git a/src/ruby/lib/grpc/auth/compute_engine.rb b/src/ruby/lib/grpc/auth/compute_engine.rb new file mode 100644 index 0000000000..9004bef46e --- /dev/null +++ b/src/ruby/lib/grpc/auth/compute_engine.rb @@ -0,0 +1,69 @@ +# 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 Google + module RPC + # 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 +end diff --git a/src/ruby/lib/grpc/auth/signet.rb b/src/ruby/lib/grpc/auth/signet.rb index b46af1696a..ba1a3a8d8f 100644 --- a/src/ruby/lib/grpc/auth/signet.rb +++ b/src/ruby/lib/grpc/auth/signet.rb @@ -46,7 +46,7 @@ module Signet # 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}" + a_hash[AUTH_METADATA_KEY] = "Bearer #{access_token}" end # Returns a clone of a_hash updated with the authentication token diff --git a/src/ruby/spec/auth/apply_auth_examples.rb b/src/ruby/spec/auth/apply_auth_examples.rb index af1f6df04a..f626c54410 100644 --- a/src/ruby/spec/auth/apply_auth_examples.rb +++ b/src/ruby/spec/auth/apply_auth_examples.rb @@ -74,12 +74,29 @@ shared_examples 'apply/apply! are OK' do md = { foo: 'bar' } @client.apply!(md, connection: c) - want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer: #{token}" } + 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' @@ -104,7 +121,7 @@ shared_examples 'apply/apply! are OK' do md = { foo: 'bar' } got = @client.apply(md, connection: c) - want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer: #{token}" } + want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer #{token}" } expect(got).to eq(want) stubs.verify_stubbed_calls end @@ -120,7 +137,7 @@ shared_examples 'apply/apply! are OK' do n.times do |_t| md = { foo: 'bar' } got = @client.apply(md, connection: c) - want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer: #{token}" } + want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer #{token}" } expect(got).to eq(want) end stubs.verify_stubbed_calls @@ -137,7 +154,7 @@ shared_examples 'apply/apply! are OK' do end md = { foo: 'bar' } got = @client.apply(md, connection: c) - want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer: #{t}" } + 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 diff --git a/src/ruby/spec/auth/compute_engine_spec.rb b/src/ruby/spec/auth/compute_engine_spec.rb new file mode 100644 index 0000000000..9e0b4660fa --- /dev/null +++ b/src/ruby/spec/auth/compute_engine_spec.rb @@ -0,0 +1,108 @@ +# 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 Google::RPC::Auth::GCECredentials do + MD_URI = '/computeMetadata/v1/instance/service-accounts/default/token' + GCECredentials = Google::RPC::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 |