Skip to content
This repository was archived by the owner on Jun 2, 2021. It is now read-only.

Commit 557bc26

Browse files
authored
v3(services): Allow bind from shared spaces (cloudfoundry#1948)
* v3(services): Allow bind from shared spaces - Updated permission on bind to make sure users in shared spaces can bind to apps - Added tests for bind and unbind from shared spaces [#175641070](https://www.pivotaltracker.com/story/show/175641070) * Fixed indentation
1 parent 09168bc commit 557bc26

2 files changed

Lines changed: 101 additions & 27 deletions

File tree

app/controllers/v3/service_credential_bindings_controller.rb

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def create
4444
unprocessable!(message.errors.full_messages) unless message.valid?
4545

4646
service_instance = VCAP::CloudController::ServiceInstance.first(guid: message.service_instance_guid)
47-
resource_not_accessible!('service instance', message.service_instance_guid) unless can_access_resource?(service_instance)
47+
resource_not_accessible!('service instance', message.service_instance_guid) unless can_read_service_instance?(service_instance)
4848

4949
app = VCAP::CloudController::AppModel.first(guid: message.app_guid)
5050
resource_not_accessible!('app', message.app_guid) unless can_access_resource?(app)
@@ -152,6 +152,16 @@ def can_access_resource?(resource)
152152
resource.present? && can_read_from_space?(resource.space)
153153
end
154154

155+
def can_read_service_instance?(service_instance)
156+
if service_instance.present?
157+
readable_spaces = service_instance.shared_spaces + [service_instance.space]
158+
159+
readable_spaces.any? do |space|
160+
permission_queryer.can_read_from_space?(space.guid, space.organization_guid)
161+
end
162+
end
163+
end
164+
155165
def can_write_to_space?(space)
156166
permission_queryer.can_write_to_space?(space.guid)
157167
end

spec/request/service_credential_bindings_spec.rb

Lines changed: 90 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,14 +1058,40 @@ def check_filtered_bindings(*bindings)
10581058
let(:audit) { VCAP::CloudController::Event.last }
10591059
let(:job) { VCAP::CloudController::PollableJobModel.last }
10601060

1061-
it_behaves_like 'permissions for single object endpoint', ALL_PERMISSIONS do
1062-
let(:expected_codes_and_responses) do
1063-
Hash.new(code: 403).tap do |h|
1064-
h['space_developer'] = { code: 202 }
1065-
h['admin'] = { code: 202 }
1066-
h['org_billing_manager'] = { code: 422 }
1067-
h['org_auditor'] = { code: 422 }
1068-
h['no_role'] = { code: 422 }
1061+
context 'permissions' do
1062+
context 'users in the originating service instance space' do
1063+
it_behaves_like 'permissions for single object endpoint', ALL_PERMISSIONS do
1064+
let(:expected_codes_and_responses) do
1065+
Hash.new(code: 403).tap do |h|
1066+
h['space_developer'] = { code: 202 }
1067+
h['admin'] = { code: 202 }
1068+
h['org_billing_manager'] = { code: 422 }
1069+
h['org_auditor'] = { code: 422 }
1070+
h['no_role'] = { code: 422 }
1071+
end
1072+
end
1073+
end
1074+
end
1075+
1076+
context 'users in the space where the SI has been shared to' do
1077+
let(:orginal_org) { VCAP::CloudController::Organization.make }
1078+
let(:original_space) { VCAP::CloudController::Space.make(organization: orginal_org) }
1079+
let(:service_instance) { VCAP::CloudController::ManagedServiceInstance.make(space: original_space, service_plan: plan) }
1080+
1081+
before do
1082+
service_instance.add_shared_space(space)
1083+
end
1084+
1085+
it_behaves_like 'permissions for single object endpoint', ALL_PERMISSIONS do
1086+
let(:expected_codes_and_responses) do
1087+
Hash.new(code: 403).tap do |h|
1088+
h['space_developer'] = { code: 202 }
1089+
h['admin'] = { code: 202 }
1090+
h['org_billing_manager'] = { code: 422 }
1091+
h['org_auditor'] = { code: 422 }
1092+
h['no_role'] = { code: 422 }
1093+
end
1094+
end
10691095
end
10701096
end
10711097
end
@@ -1452,6 +1478,24 @@ def check_filtered_bindings(*bindings)
14521478
let(:service_instance) { VCAP::CloudController::UserProvidedServiceInstance.make(**service_instance_details) }
14531479

14541480
context 'user provided services' do
1481+
context 'permissions' do
1482+
let(:db_check) {
1483+
lambda {
1484+
get "/v3/service_credential_bindings/#{guid}", {}, admin_headers
1485+
expect(last_response).to have_status_code(404)
1486+
}
1487+
}
1488+
1489+
let(:expected_codes_and_responses) do
1490+
Hash.new(code: 403).tap do |h|
1491+
h['admin'] = h['space_developer'] = { code: 204 }
1492+
h['org_billing_manager'] = h['org_auditor'] = h['no_role'] = { code: 404 }
1493+
end
1494+
end
1495+
1496+
it_behaves_like 'permissions for delete endpoint', ALL_PERMISSIONS
1497+
end
1498+
14551499
context 'app bindings' do
14561500
it 'can successfully delete the record' do
14571501
api_call.call(admin_headers)
@@ -1499,6 +1543,44 @@ def check_filtered_bindings(*bindings)
14991543
}
15001544
end
15011545

1546+
context 'permissions' do
1547+
let(:db_check) {
1548+
lambda {
1549+
expect(last_response.headers['Location']).to match(%r(http.+/v3/jobs/[a-fA-F0-9-]+))
1550+
}
1551+
}
1552+
1553+
context 'users in the originating service instance space' do
1554+
it_behaves_like 'permissions for delete endpoint', ALL_PERMISSIONS do
1555+
let(:expected_codes_and_responses) do
1556+
Hash.new(code: 403).tap do |h|
1557+
h['admin'] = h['space_developer'] = { code: 202 }
1558+
h['org_billing_manager'] = h['org_auditor'] = h['no_role'] = { code: 404 }
1559+
end
1560+
end
1561+
end
1562+
end
1563+
1564+
context 'users in the space where the SI has been shared to' do
1565+
let(:orginal_org) { VCAP::CloudController::Organization.make }
1566+
let(:original_space) { VCAP::CloudController::Space.make(organization: orginal_org) }
1567+
let(:service_instance) { VCAP::CloudController::ManagedServiceInstance.make(space: original_space) }
1568+
1569+
before do
1570+
service_instance.add_shared_space(space)
1571+
end
1572+
1573+
it_behaves_like 'permissions for delete endpoint', ALL_PERMISSIONS do
1574+
let(:expected_codes_and_responses) do
1575+
Hash.new(code: 403).tap do |h|
1576+
h['admin'] = h['space_developer'] = { code: 202 }
1577+
h['org_billing_manager'] = h['org_auditor'] = h['no_role'] = { code: 404 }
1578+
end
1579+
end
1580+
end
1581+
end
1582+
end
1583+
15021584
context 'app binding' do
15031585
it 'responds with a job resource' do
15041586
api_call.call(space_dev_headers)
@@ -1788,24 +1870,6 @@ def check_filtered_bindings(*bindings)
17881870
end
17891871
end
17901872

1791-
context 'permissions' do
1792-
let(:db_check) {
1793-
lambda {
1794-
get "/v3/service_credential_bindings/#{guid}", {}, admin_headers
1795-
expect(last_response).to have_status_code(404)
1796-
}
1797-
}
1798-
1799-
let(:expected_codes_and_responses) do
1800-
Hash.new(code: 403).tap do |h|
1801-
h['admin'] = h['space_developer'] = { code: 204 }
1802-
h['org_billing_manager'] = h['org_auditor'] = h['no_role'] = { code: 404 }
1803-
end
1804-
end
1805-
1806-
it_behaves_like 'permissions for delete endpoint', ALL_PERMISSIONS
1807-
end
1808-
18091873
describe 'invalid requests' do
18101874
context 'when the binding does not exist' do
18111875
let(:guid) { 'fake-binding' }

0 commit comments

Comments
 (0)