diff --git a/cloud_controller/Gemfile b/cloud_controller/Gemfile index 5e7442f..f6de485 100644 --- a/cloud_controller/Gemfile +++ b/cloud_controller/Gemfile @@ -9,7 +9,7 @@ gem 'nats', :require => 'nats/client' gem 'vcap_common', :require => ['vcap/common', 'vcap/component'], :git => 'https://github.com/cloudfoundry/vcap-common.git', :ref => 'fd6b6d91' gem 'vcap_logging', :require => ['vcap/logging'], :git => 'https://github.com/cloudfoundry/common.git', :ref => 'e36886a1' gem 'vcap_staging', '~> 0.1.62', :git => 'https://github.com/cloudfoundry/vcap-staging.git', :ref => '8d9da6cd' -gem 'cf-uaa-client', '~> 1.0', :git => 'https://github.com/cloudfoundry/uaa.git', :ref => 'b19eb89b' +gem 'cf-uaa-client', '~> 1.0', :git => 'https://github.com/cloudfoundry/uaa.git', :ref => 'dad29c90' # For queuing staging tasks gem 'em-hiredis' diff --git a/cloud_controller/Gemfile.lock b/cloud_controller/Gemfile.lock index 7e88bca..6ce601d 100644 --- a/cloud_controller/Gemfile.lock +++ b/cloud_controller/Gemfile.lock @@ -24,17 +24,16 @@ GIT GIT remote: https://github.com/cloudfoundry/uaa.git - revision: b19eb89b16a7646b6272556060e52ad09fc6707d - ref: b19eb89b + revision: dad29c9030f482c7a426c9c81b0e05d9715ccc11 + ref: dad29c90 specs: - cf-uaa-client (1.0.2) - em-http-request (>= 1.0.0.beta.3, <= 1.0.0.beta.3) + cf-uaa-client (1.1.1) + em-http-request eventmachine - interact - json_pure + highline launchy rest-client - thor + yajl-ruby GIT remote: https://github.com/cloudfoundry/vcap-common.git @@ -120,11 +119,11 @@ GEM eventmachine erubis (2.6.6) abstract (>= 1.0.0) + highline (1.6.13) hiredis (0.3.2) http_parser.rb (0.5.1) httpclient (2.2.7) i18n (0.5.0) - interact (0.4.3) json (1.7.3) json_pure (1.7.3) launchy (2.0.3) diff --git a/cloud_controller/app/models/uaa_token.rb b/cloud_controller/app/models/uaa_token.rb index b1eb4a9..b67ae24 100644 --- a/cloud_controller/app/models/uaa_token.rb +++ b/cloud_controller/app/models/uaa_token.rb @@ -1,5 +1,6 @@ require "uaa/token_coder" require "uaa/token_issuer" +require "uaa/misc" class UaaToken @@ -14,8 +15,13 @@ class UaaToken "vmc", nil) + @token_key_fetch_failure_count = 3 + + class << self + attr_accessor :token_key_fetch_failure_count + def is_uaa_token?(token) token.nil? || /\s+/.match(token.strip()).nil?? false : true end @@ -25,9 +31,27 @@ def decode_token(auth_token) return nil end - CloudController.logger.debug("uaa token coder #{@uaa_token_coder.inspect}") CloudController.logger.debug("Auth token is #{auth_token.inspect}") + # Try to fetch the token key (public key) from the UAA + if token_key_fetch_failure_count > 0 && !@token_key + begin + CF::UAA::Misc.async=true + @token_key ||= CF::UAA::Misc.validation_key(AppConfig[:uaa][:url], AppConfig[:uaa][:resource_id], AppConfig[:uaa][:client_secret]) + + if @token_key[:alg] == "SHA256withRSA" + CloudController.logger.debug("token key fetched from the uaa #{@token_key.inspect}") + @uaa_token_coder = CF::UAA::TokenCoder.new(AppConfig[:uaa][:resource_id], + AppConfig[:uaa][:token_secret], + @token_key[:value]) + end + rescue => e + self.token_key_fetch_failure_count = token_key_fetch_failure_count - 1 + CloudController.logger.warn("Failed to fetch the token key from the UAA token_key endpoint or recieved symmetric key instead") + CloudController.logger.debug("Request to uaa/token_key OR public key init failed. #{@token_key_fetch_failure_count} retries remain. #{e.message}") + end + end + token_information = nil begin token_information = @uaa_token_coder.decode(auth_token) @@ -52,7 +76,6 @@ def access_token if @access_token.nil? || expired?(@access_token) #Get a new one @token_issuer.async = true - @token_issuer.debug = true @token_issuer.logger = CloudController.logger @access_token = @token_issuer.client_credentials_grant().auth_header end @@ -62,7 +85,6 @@ def access_token def id_token(email, password) @id_token_issuer.async = true - @id_token_issuer.debug = true @id_token_issuer.logger = CloudController.logger id_token = @id_token_issuer.implicit_grant_with_creds(username: email, password: password).auth_header CloudController.logger.debug("id_token #{id_token}") diff --git a/cloud_controller/spec/controllers/users_controller_spec.rb b/cloud_controller/spec/controllers/users_controller_spec.rb index cc9a9b7..3d6cf03 100644 --- a/cloud_controller/spec/controllers/users_controller_spec.rb +++ b/cloud_controller/spec/controllers/users_controller_spec.rb @@ -188,4 +188,13 @@ def post_with_body(*args, &blk) it_should_behave_like "any request to the users controller" end + context "using jwt tokens with RSA keys" do + before :all do + CloudSpecHelpers.use_jwt_token_with_rsa_key = true + end + + it_should_behave_like "any request to the users controller" + end + + end diff --git a/cloud_controller/spec/requests/app_create_spec.rb b/cloud_controller/spec/requests/app_create_spec.rb index 311877c..1b3b4bf 100644 --- a/cloud_controller/spec/requests/app_create_spec.rb +++ b/cloud_controller/spec/requests/app_create_spec.rb @@ -30,4 +30,13 @@ it_should_behave_like "any request to create a new app" end + context "using jwt tokens with RSA keys" do + before :all do + CloudSpecHelpers.use_jwt_token_with_rsa_key = true + end + + it_should_behave_like "any request to create a new app" + end + + end diff --git a/cloud_controller/spec/requests/bulk_spec.rb b/cloud_controller/spec/requests/bulk_spec.rb index 8fb80d8..42e89c2 100644 --- a/cloud_controller/spec/requests/bulk_spec.rb +++ b/cloud_controller/spec/requests/bulk_spec.rb @@ -178,6 +178,15 @@ it_should_behave_like "any request to the bulk api" end + context "using jwt tokens with RSA keys" do + before :all do + CloudSpecHelpers.use_jwt_token_with_rsa_key = true + end + + it_should_behave_like "any request to the bulk api" + end + + def get_users(args=nil) get(bulk_users_url, args, @auth_header) end diff --git a/cloud_controller/spec/requests/info_spec.rb b/cloud_controller/spec/requests/info_spec.rb index 8ed0d3a..0cd3288 100644 --- a/cloud_controller/spec/requests/info_spec.rb +++ b/cloud_controller/spec/requests/info_spec.rb @@ -68,6 +68,15 @@ it_should_behave_like "any request" end + context "using jwt tokens with RSA keys" do + before :all do + CloudSpecHelpers.use_jwt_token_with_rsa_key = true + end + + it_should_behave_like "any request" + end + + end diff --git a/cloud_controller/spec/requests/proxy_user_spec.rb b/cloud_controller/spec/requests/proxy_user_spec.rb index 764a7b1..286ab5f 100644 --- a/cloud_controller/spec/requests/proxy_user_spec.rb +++ b/cloud_controller/spec/requests/proxy_user_spec.rb @@ -42,4 +42,13 @@ it_should_behave_like "any request to test a proxy user" end + context "using jwt tokens with RSA keys" do + before :all do + CloudSpecHelpers.use_jwt_token_with_rsa_key = true + end + + it_should_behave_like "any request to test a proxy user" + end + + end diff --git a/cloud_controller/spec/requests/user_tokens_spec.rb b/cloud_controller/spec/requests/user_tokens_spec.rb index 179a38c..a8047cd 100644 --- a/cloud_controller/spec/requests/user_tokens_spec.rb +++ b/cloud_controller/spec/requests/user_tokens_spec.rb @@ -34,6 +34,14 @@ it_should_behave_like "any request for a new user token" end + context "using jwt tokens with RSA keys" do + before :all do + CloudSpecHelpers.use_jwt_token_with_rsa_key = true + end + + it_should_behave_like "any request for a new user token" + end + context "When user_expire is specified" do before { UserToken.token_expire = 1.day } let :token do diff --git a/cloud_controller/spec/support/cloud_spec_helpers.rb b/cloud_controller/spec/support/cloud_spec_helpers.rb index 83ffc24..7568046 100644 --- a/cloud_controller/spec/support/cloud_spec_helpers.rb +++ b/cloud_controller/spec/support/cloud_spec_helpers.rb @@ -1,3 +1,5 @@ +require 'mocha' + module CloudSpecHelpers # Define test scenarios for https enforcement code HTTPS_ENFORCEMENT_SCENARIOS = [{:protocol => "http", :appconfig_enabled => [], :user => "user", :success => true}, @@ -18,6 +20,7 @@ module CloudSpecHelpers {:protocol => "https", :appconfig_enabled => [:https_required_for_admins], :user => "admin", :success => true}] @@use_jwt_token = false + @@use_jwt_token_with_rsa_key = false def self.use_jwt_token @@use_jwt_token @@ -27,6 +30,14 @@ def self.use_jwt_token=(use_jwt_token) @@use_jwt_token = use_jwt_token end + def self.use_jwt_token_with_rsa_key + @@use_jwt_token_with_rsa_key + end + + def self.use_jwt_token_with_rsa_key=(use_jwt_token_with_rsa_key) + @@use_jwt_token_with_rsa_key = use_jwt_token_with_rsa_key + end + # Generate a handy header Hash. # At minimum it requires a User or email as the first argument. # Optionally, you can pass a second User or email which will be the 'proxy user'. @@ -36,7 +47,18 @@ def headers_for(user, proxy_user = nil, raw_data = nil, https = false) headers = {} if user email = User === user ? user.email : user.to_s - if @@use_jwt_token + if @@use_jwt_token_with_rsa_key + UaaToken.stubs(:token_key_fetch_failure_count).returns(3) + CF::UAA::Misc.stubs(:validation_key).returns(public_key_from_uaa()) + token_body = {"resource_ids" => ["cloud_controller"], "foo" => "bar", "email" => email} + token_coder = CF::UAA::TokenCoder.new(AppConfig[:uaa][:resource_id], + AppConfig[:uaa][:token_secret], + private_key()) + + token = token_coder.encode(token_body, 'RS256') + AppConfig[:uaa][:enabled] = true + headers['HTTP_AUTHORIZATION'] = "bearer #{token}" + elsif @@use_jwt_token token_body = {"resource_ids" => ["cloud_controller"], "foo" => "bar", "email" => email} token_coder = CF::UAA::TokenCoder.new(AppConfig[:uaa][:resource_id], AppConfig[:uaa][:token_secret]) @@ -85,4 +107,30 @@ def build_admin_and_user def random_name(length = 7) Digest::SHA1.hexdigest("#{Time.now.nsec}-#{rand(1_000_000)}").slice(0,length) end + + def private_key + "-----BEGIN RSA PRIVATE KEY----- +MIIBywIBAAJhAOTeb4AZ+NwOtPh+ynIgGqa6UWNVe6JyJi+loPmPZdpHtzoqubnC +wEs6JSiSZ3rButEAw8ymgLV6iBY02hdjsl3h5Z0NWaxx8dzMZfXe4EpfB04ISoqq +hZCxchvuSDP4eQIDAQABAmEAqUuYsuuDWFRQrZgsbGsvC7G6zn3HLIy/jnM4NiJK +t0JhWNeN9skGsR7bqb1Sak2uWqW8ZqnqgAC32gxFRYHTavJEk6LTaHWovwDEhPqc +Zs+vXd6tZojJQ35chR/slUEBAjEA/sAd1oFLWb6PHkaz7r2NllwUBTvXL4VcMWTS +pN+5cU41i9fsZcHw6yZEl+ZCicDxAjEA5f3R+Bj42htNI7eylebew1+sUnFv1xT8 +jlzxSzwVkoZo+vef7OD6OcFLeInAHzAJAjEAs6izolK+3ETa1CRSwz0lPHQlnmdM +Y/QuR5tuPt6U/saEVuJpkn4LNRtg5qt6I4JRAjAgFRYTG7irBB/wmZFp47izXEc3 +gOdvA1hvq3tlWU5REDrYt24xpviA0fvrJpwMPbECMAKDKdiDi6Q4/iBkkzNMefA8 +7HX27b9LR33don/1u/yvzMUo+lrRdKAFJ+9GPE9XFA== +-----END RSA PRIVATE KEY-----" + end + + def public_key_from_uaa + {:alg => 'SHA256withRSA', + :value => "-----BEGIN RSA PUBLIC KEY----- +MGgCYQDk3m+AGfjcDrT4fspyIBqmulFjVXuiciYvpaD5j2XaR7c6Krm5wsBLOiUo +kmd6wbrRAMPMpoC1eogWNNoXY7Jd4eWdDVmscfHczGX13uBKXwdOCEqKqoWQsXIb +7kgz+HkCAwEAAQ== +-----END RSA PUBLIC KEY-----"} + end + + end diff --git a/health_manager/Gemfile b/health_manager/Gemfile index 2b7940b..edd9fd9 100644 --- a/health_manager/Gemfile +++ b/health_manager/Gemfile @@ -12,7 +12,7 @@ gem 'yajl-ruby', :require => ['yajl', 'yajl/json_gem'] gem 'vcap_common', '>= 1.0.10', :git => 'https://github.com/cloudfoundry/vcap-common.git', :ref => 'cbeb8a17' gem "vcap_logging", "~> 1.0.0", :git => 'https://github.com/cloudfoundry/common.git', :ref => 'e36886a1' -gem 'cf-uaa-client', '~> 1.0', :git => 'https://github.com/cloudfoundry/uaa.git', :ref => 'b19eb89b' +gem 'cf-uaa-client', '~> 1.0', :git => 'https://github.com/cloudfoundry/uaa.git', :ref => 'dad29c90' group :test do gem "rspec" diff --git a/health_manager/Gemfile.lock b/health_manager/Gemfile.lock index c4a6391..3cf9ec6 100644 --- a/health_manager/Gemfile.lock +++ b/health_manager/Gemfile.lock @@ -15,17 +15,16 @@ GIT GIT remote: https://github.com/cloudfoundry/uaa.git - revision: b19eb89b16a7646b6272556060e52ad09fc6707d - ref: b19eb89b + revision: dad29c9030f482c7a426c9c81b0e05d9715ccc11 + ref: dad29c90 specs: - cf-uaa-client (1.0.2) - em-http-request (>= 1.0.0.beta.3, <= 1.0.0.beta.3) + cf-uaa-client (1.1.1) + em-http-request eventmachine - interact - json_pure + highline launchy rest-client - thor + yajl-ruby GIT remote: https://github.com/cloudfoundry/vcap-common.git @@ -55,8 +54,8 @@ GEM http_parser.rb (>= 0.5.1) em-socksify (0.1.0) eventmachine + highline (1.6.13) http_parser.rb (0.5.3) - interact (0.4.3) json_pure (1.7.3) launchy (2.1.0) addressable (~> 2.2.6) @@ -84,7 +83,6 @@ GEM daemons (>= 1.0.9) eventmachine (>= 0.12.6) rack (>= 1.0.0) - thor (0.15.3) yajl-ruby (0.8.3) PLATFORMS