Skip to content
Merged
6 changes: 6 additions & 0 deletions app/controllers/v3/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ class ApplicationController < ActionController::Base
rescue_from CloudController::Errors::CompoundError, with: :handle_compound_error
rescue_from ActionDispatch::Http::Parameters::ParseError, with: :handle_invalid_request_body
rescue_from Sequel::DatabaseConnectionError, Sequel::DatabaseDisconnectError, with: :handle_db_connection_error
rescue_from OpenSSL::Cipher::CipherError, with: :handle_key_derivation_error

def configuration
Config.config
Expand Down Expand Up @@ -219,6 +220,11 @@ def handle_db_connection_error(_)
handle_api_error(error)
end

def handle_key_derivation_error(_)
error = CloudController::Errors::V3::ApiError.new_from_details('InternalServerError', 'Failed to decrypt credentials')
Comment thread
kathap marked this conversation as resolved.
Outdated
handle_api_error(error)
end

def handle_exception(error)
presenter = ErrorPresenter.new(error, Rails.env.test?, V3ErrorHasher.new(error))
logger.info(presenter.log_message)
Expand Down
5 changes: 5 additions & 0 deletions errors/v3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,8 @@
name: UaaRateLimited
http_code: 429
message: "The UAA is currently rate limited. Please try again later"

10001:
name: InternalServerError
http_code: 500
message: "%s"
32 changes: 32 additions & 0 deletions spec/request/apps_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3398,6 +3398,38 @@
end
end
end

context 'when the encryption_key_label is invalid' do
let(:instance) { VCAP::CloudController::ManagedServiceInstance.make(space:) }
let(:app_binding) do
VCAP::CloudController::ServiceBinding.make(
app: app_model,
service_instance_guid: instance.guid,
credentials: { key: 'value' },
syslog_drain_url: 'syslog-url',
volume_mounts: %w[volume1 volume2]
)
end

before do
VCAP::CloudController::Encryptor.database_encryption_keys = {
encryption_key_0: 'somevalidkeyvalue',
foo: 'fooencryptionkey',
death: 'headbangingdeathmetalkey', 'invalid-key-label': 'fakekey'
}
allow_any_instance_of(ErrorPresenter).to receive(:raise_500?).and_return(false)
end

it 'fails to decrypt the environment variables and returns a 500 error' do
app_binding.class.db[:service_bindings].where(id: app_binding.id).update(encryption_key_label: 'invalid-key-label')

allow(VCAP::CloudController::Encryptor).to receive(:run_cipher).and_raise(OpenSSL::Cipher::CipherError)
Comment thread
kathap marked this conversation as resolved.
api_call.call(admin_headers)

expect(last_response).to have_status_code(500)
expect(parsed_response['errors'].first['detail']).to match(/Failed/i)
end
end
end

describe 'GET /v3/apps/:guid/permissions' do
Expand Down
33 changes: 33 additions & 0 deletions spec/request/service_brokers_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -899,6 +899,39 @@ def expect_empty_list(user_headers)
expect(response).to include('detail' => 'Service broker not found')
end
end

context 'when updating credentials and the encryption_key_label is invalid' do
let(:broker) { VCAP::CloudController::ServiceBroker.make }
let(:api_call) do
lambda { |headers|
patch "/v3/service_brokers/#{broker.guid}", { authentication: {
type: 'basic',
credentials: {
username: 'your-username',
password: 'your-password'
}
} }.to_json, headers
}
end

before do
VCAP::CloudController::Encryptor.database_encryption_keys = {
encryption_key_0: 'somevalidkeyvalue',
foo: 'fooencryptionkey',
death: 'headbangingdeathmetalkey', 'invalid-key-label': 'fakekey'
}
broker.class.db[:service_brokers].where(id: broker.id).update(encryption_key_label: 'invalid-key-label')
allow(VCAP::CloudController::Encryptor).to receive(:run_cipher).and_raise(OpenSSL::Cipher::CipherError)
allow_any_instance_of(ErrorPresenter).to receive(:raise_500?).and_return(false)
end

it 'fails to decrypt the broker data and returns a 500 error' do
api_call.call(admin_headers)

expect(last_response).to have_status_code(500)
expect(parsed_response['errors'].first['detail']).to match(/Failed/i)
end
end
end

describe 'POST /v3/service_brokers' do
Expand Down
21 changes: 21 additions & 0 deletions spec/request/service_credential_bindings_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,27 @@ def check_filtered_bindings(*bindings)
}
end

context 'when the encryption_key_label is invalid' do
before do
VCAP::CloudController::Encryptor.database_encryption_keys = {
encryption_key_0: 'somevalidkeyvalue',
foo: 'fooencryptionkey',
death: 'headbangingdeathmetalkey', 'invalid-key-label': 'fakekey'
}
allow_any_instance_of(ErrorPresenter).to receive(:raise_500?).and_return(false)
end

it 'fails to decrypt the credentials and returns a 500 error' do
app_binding.class.db[:service_bindings].where(id: app_binding.id).update(encryption_key_label: 'invalid-key-label')

allow(VCAP::CloudController::Encryptor).to receive(:run_cipher).and_raise(OpenSSL::Cipher::CipherError)
api_call.call(admin_headers)

expect(last_response).to have_status_code(500)
expect(parsed_response['errors'].first['detail']).to match(/Failed/i)
end
end

context "last binding operation is in 'create succeeded' state" do
before do
app_binding.save_with_attributes_and_new_operation({}, { type: 'create', state: 'succeeded' })
Expand Down
25 changes: 23 additions & 2 deletions spec/unit/controllers/v3/application_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,18 @@ def not_found
raise CloudController::Errors::NotFound.new_from_details('NotFound')
end

def db_connection_error
raise Sequel::DatabaseConnectionError.new
def key_derivation_error
raise OpenSSL::Cipher::CipherError
end

def db_disconnect_error
raise Sequel::DatabaseDisconnectError.new
end

def db_connection_error
raise Sequel::DatabaseConnectionError.new
end

def warnings_is_nil
add_warning_headers(nil)
render status: :ok, json: {}
Expand Down Expand Up @@ -320,6 +324,23 @@ def warnings_incorrect_type
end
end

describe '#handle_key_derivation_error' do
let!(:user) { set_current_user(VCAP::CloudController::User.make) }

before do
allow_any_instance_of(ErrorPresenter).to receive(:raise_500?).and_return(false)
Comment thread
kathap marked this conversation as resolved.
routes.draw do
get 'key_derivation_error' => 'anonymous#key_derivation_error'
end
end

it 'rescues from OpenSSL::Cipher::CipherError and renders an error presenter' do
get :key_derivation_error
expect(response).to have_http_status(:internal_server_error)
expect(response).to have_error_message(/Failed to decrypt credentials/)
end
end

describe '#add_warning_headers' do
let!(:user) { set_current_user(VCAP::CloudController::User.make) }

Expand Down