Skip to content

Commit

Permalink
Update get endpoints in apps controller to allow access to
Browse files Browse the repository at this point in the history
space_application_supporter

* #2210

Co-authored-by: Weyman Fung <[email protected]>
Co-authored-by: Mona Mohebbi <[email protected]>
  • Loading branch information
weymanf and monamohebbi committed May 12, 2021
1 parent 8248b39 commit 110157b
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 102 deletions.
7 changes: 4 additions & 3 deletions app/controllers/v3/apps_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ def index
dataset = if permission_queryer.can_read_globally?
AppListFetcher.fetch_all(message, eager_loaded_associations: Presenters::V3::AppPresenter.associated_resources)
else
AppListFetcher.fetch(message, permission_queryer.readable_space_guids, eager_loaded_associations: Presenters::V3::AppPresenter.associated_resources)
AppListFetcher.fetch(message, permission_queryer.readable_application_supporter_space_guids,
eager_loaded_associations: Presenters::V3::AppPresenter.associated_resources)
end

decorators = []
Expand All @@ -68,7 +69,7 @@ def show

app, space, org = AppFetcher.new.fetch(hashed_params[:guid])

app_not_found! unless app && permission_queryer.can_read_from_space?(space.guid, org.guid)
app_not_found! unless app && permission_queryer.untrusted_can_read_from_space?(space.guid, org.guid)

decorators = []
decorators << IncludeSpaceDecorator if IncludeSpaceDecorator.match?(message.include)
Expand Down Expand Up @@ -227,7 +228,7 @@ def builds
invalid_param!(message.errors.full_messages) unless message.valid?

app, space, org = AppFetcher.new.fetch(hashed_params[:guid])
app_not_found! unless app && permission_queryer.can_read_from_space?(space.guid, org.guid)
app_not_found! unless app && permission_queryer.untrusted_can_read_from_space?(space.guid, org.guid)

dataset = AppBuildsListFetcher.fetch_all(app.guid, message)
render status: :ok, json: Presenters::V3::PaginatedListPresenter.new(
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/v3/builds_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def index
dataset = if permission_queryer.can_read_globally?
BuildListFetcher.fetch_all(message, eager_loaded_associations: Presenters::V3::BuildPresenter.associated_resources)
else
BuildListFetcher.fetch_for_spaces(message, space_guids: permission_queryer.readable_space_guids,
BuildListFetcher.fetch_for_spaces(message, space_guids: permission_queryer.readable_space_application_supporter_guids,
eager_loaded_associations: Presenters::V3::BuildPresenter.associated_resources)
end

Expand Down
4 changes: 4 additions & 0 deletions lib/cloud_controller/membership.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ def member_guids(roles: [])
@space_auditor ||=
@user.audited_spaces_dataset.
association_join(:organization).map(&:guid)
when SPACE_APPLICATION_SUPPORTER
@space_application_supporter ||=
@user.application_supported_spaces_dataset.
association_join(:organization).map(&:guid)
when ORG_USER
@org_user ||=
@user.organizations_dataset.map(&:guid)
Expand Down
4 changes: 4 additions & 0 deletions lib/cloud_controller/permissions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ def can_read_from_space?(space_guid, org_guid)
can_read_globally? || membership.has_any_roles?(ROLES_FOR_SPACE_READING, space_guid, org_guid)
end

def untrusted_can_read_from_space?(space_guid, org_guid)
can_read_globally? || membership.has_any_roles?(ROLES_FOR_SPACE_APPLICATION_SUPPORTER_READING, space_guid, org_guid)
end

def can_read_secrets_in_space?(space_guid, org_guid)
can_read_secrets_globally? ||
membership.has_any_roles?(ROLES_FOR_SPACE_SECRETS_READING, space_guid, org_guid)
Expand Down
4 changes: 4 additions & 0 deletions lib/cloud_controller/permissions/queryer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ def can_read_from_space?(space_guid, org_guid)
end
end

def untrusted_can_read_from_space?(space_guid, org_guid)
db_permissions.untrusted_can_read_from_space?(space_guid, org_guid)
end

def can_read_secrets_in_space?(space_guid, org_guid)
science 'can_read_secrets_in_space' do |e|
e.context(space_guid: space_guid, org_guid: org_guid)
Expand Down
228 changes: 130 additions & 98 deletions spec/request/apps_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -471,11 +471,17 @@
app_model1_response_object,
] }

h['space_application_supporter'] = {
code: 200,
response_objects: [
app_model1_response_object,
] }

h['no_role'] = { code: 200, response_objects: [] }
h
end

it_behaves_like 'permissions for list endpoint', ALL_PERMISSIONS
it_behaves_like 'permissions for list endpoint', ALL_PERMISSIONS + ['space_application_supporter']
end

describe 'query list parameters' do
Expand Down Expand Up @@ -1310,7 +1316,7 @@
h
end

it_behaves_like 'permissions for single object endpoint', ALL_PERMISSIONS
it_behaves_like 'permissions for single object endpoint', ALL_PERMISSIONS + ['space_application_supporter']
end

context 'when the user has permission to view the app' do
Expand Down Expand Up @@ -1653,113 +1659,139 @@
)
}
let(:body) do
{ lifecycle: { type: 'buildpack', data: { buildpacks: ['http://github.com/myorg/awesome-buildpack'],
stack: 'cflinuxfs3' } } }
{
lifecycle: {
type: 'buildpack',
data: {
buildpacks: ['http://github.com/myorg/awesome-buildpack'],
stack: 'cflinuxfs3'
}
}
}
end
let(:staging_message) { VCAP::CloudController::BuildCreateMessage.new(body) }
let(:per_page) { 2 }
let(:order_by) { '-created_at' }
describe 'permissions' do
let(:api_call) do
lambda { |headers| get "/v3/apps/#{app_model.guid}/builds", nil, headers }
end
# TODO: be a more specific on the response content
let(:build_response_object) { anything }
let(:expected_codes_and_responses) do
h = Hash.new(code: 200, response_objects: build_response_object)
h['org_auditor'] = { code: 404 }
h['org_billing_manager'] = { code: 404 }
h['no_role'] = { code: 404 }
h
end

before do
space.organization.add_user(user)
space.add_developer(user)
VCAP::CloudController::BuildpackLifecycle.new(package, staging_message).create_lifecycle_data_model(build)
VCAP::CloudController::BuildpackLifecycle.new(package, staging_message).create_lifecycle_data_model(second_build)
build.update(state: droplet.state, error_description: droplet.error_description)
second_build.update(state: second_droplet.state, error_description: second_droplet.error_description)
it_behaves_like 'permissions for list endpoint', ALL_PERMISSIONS + ['space_application_supporter']
end

it 'lists the builds for app' do
get "v3/apps/#{app_model.guid}/builds?order_by=#{order_by}&per_page=#{per_page}", nil, user_header
describe 'as a developer' do
let(:staging_message) { VCAP::CloudController::BuildCreateMessage.new(body) }
let(:per_page) { 2 }
let(:order_by) { '-created_at' }

parsed_response = MultiJson.load(last_response.body)
before do
space.organization.add_user(user)
space.add_developer(user)
VCAP::CloudController::BuildpackLifecycle.new(package, staging_message).create_lifecycle_data_model(build)
VCAP::CloudController::BuildpackLifecycle.new(package, staging_message).create_lifecycle_data_model(second_build)
build.update(state: droplet.state, error_description: droplet.error_description)
second_build.update(state: second_droplet.state, error_description: second_droplet.error_description)
end

expect(last_response.status).to eq(200)
expect(parsed_response['resources']).to include(hash_including('guid' => build.guid))
expect(parsed_response['resources']).to include(hash_including('guid' => second_build.guid))
expect(parsed_response).to be_a_response_like({
'pagination' => {
'total_results' => 2,
'total_pages' => 1,
'first' => { 'href' => "#{link_prefix}/v3/apps/#{app_model.guid}/builds?order_by=#{order_by}&page=1&per_page=2" },
'last' => { 'href' => "#{link_prefix}/v3/apps/#{app_model.guid}/builds?order_by=#{order_by}&page=1&per_page=2" },
'next' => nil,
'previous' => nil,
},
'resources' => [
{
'guid' => build.guid,
'created_at' => iso8601,
'updated_at' => iso8601,
'state' => 'STAGED',
'error' => nil,
'lifecycle' => {
'type' => 'buildpack',
'data' => {
'buildpacks' => ['http://github.com/myorg/awesome-buildpack'],
'stack' => 'cflinuxfs3',
},
},
'package' => { 'guid' => package.guid, },
'droplet' => {
'guid' => droplet.guid
},
'relationships' => { 'app' => { 'data' => { 'guid' => app_model.guid } } },
'metadata' => { 'labels' => {}, 'annotations' => {} },
'links' => {
'self' => { 'href' => "#{link_prefix}/v3/builds/#{build.guid}", },
'app' => { 'href' => "#{link_prefix}/v3/apps/#{package.app.guid}", },
'droplet' => { 'href' => "#{link_prefix}/v3/droplets/#{droplet.guid}", }
},
'created_by' => { 'guid' => user.guid, 'name' => 'bob the builder', 'email' => '[email protected]', }
},
{
'guid' => second_build.guid,
'created_at' => iso8601,
'updated_at' => iso8601,
'state' => 'STAGED',
'error' => nil,
'lifecycle' => {
'type' => 'buildpack',
'data' => {
'buildpacks' => ['http://github.com/myorg/awesome-buildpack'],
'stack' => 'cflinuxfs3',
},
},
'package' => { 'guid' => package.guid, },
'droplet' => {
'guid' => second_droplet.guid,
},
'relationships' => { 'app' => { 'data' => { 'guid' => app_model.guid } } },
'metadata' => { 'labels' => {}, 'annotations' => {} },
'links' => {
'self' => { 'href' => "#{link_prefix}/v3/builds/#{second_build.guid}", },
'app' => { 'href' => "#{link_prefix}/v3/apps/#{package.app.guid}", },
'droplet' => { 'href' => "#{link_prefix}/v3/droplets/#{second_droplet.guid}", }
},
'created_by' => { 'guid' => user.guid, 'name' => 'bob the builder', 'email' => '[email protected]', }
},
]
})
end
it 'lists the builds for app' do
get "v3/apps/#{app_model.guid}/builds?order_by=#{order_by}&per_page=#{per_page}", nil, user_header

it_behaves_like 'list_endpoint_with_common_filters' do
let(:resource_klass) { VCAP::CloudController::BuildModel }
let(:additional_resource_params) { { app: app_model } }
let(:api_call) do
lambda { |headers, filters| get "/v3/apps/#{app_model.guid}/builds?#{filters}", nil, headers }
parsed_response = MultiJson.load(last_response.body)

expect(last_response.status).to eq(200)
expect(parsed_response['resources']).to include(hash_including('guid' => build.guid))
expect(parsed_response['resources']).to include(hash_including('guid' => second_build.guid))
expect(parsed_response).to be_a_response_like({
'pagination' => {
'total_results' => 2,
'total_pages' => 1,
'first' => { 'href' => "#{link_prefix}/v3/apps/#{app_model.guid}/builds?order_by=#{order_by}&page=1&per_page=2" },
'last' => { 'href' => "#{link_prefix}/v3/apps/#{app_model.guid}/builds?order_by=#{order_by}&page=1&per_page=2" },
'next' => nil,
'previous' => nil,
},
'resources' => [
{
'guid' => build.guid,
'created_at' => iso8601,
'updated_at' => iso8601,
'state' => 'STAGED',
'error' => nil,
'lifecycle' => {
'type' => 'buildpack',
'data' => {
'buildpacks' => ['http://github.com/myorg/awesome-buildpack'],
'stack' => 'cflinuxfs3',
},
},
'package' => { 'guid' => package.guid, },
'droplet' => {
'guid' => droplet.guid
},
'relationships' => { 'app' => { 'data' => { 'guid' => app_model.guid } } },
'metadata' => { 'labels' => {}, 'annotations' => {} },
'links' => {
'self' => { 'href' => "#{link_prefix}/v3/builds/#{build.guid}", },
'app' => { 'href' => "#{link_prefix}/v3/apps/#{package.app.guid}", },
'droplet' => { 'href' => "#{link_prefix}/v3/droplets/#{droplet.guid}", }
},
'created_by' => { 'guid' => user.guid, 'name' => 'bob the builder', 'email' => '[email protected]', }
},
{
'guid' => second_build.guid,
'created_at' => iso8601,
'updated_at' => iso8601,
'state' => 'STAGED',
'error' => nil,
'lifecycle' => {
'type' => 'buildpack',
'data' => {
'buildpacks' => ['http://github.com/myorg/awesome-buildpack'],
'stack' => 'cflinuxfs3',
},
},
'package' => { 'guid' => package.guid, },
'droplet' => {
'guid' => second_droplet.guid,
},
'relationships' => { 'app' => { 'data' => { 'guid' => app_model.guid } } },
'metadata' => { 'labels' => {}, 'annotations' => {} },
'links' => {
'self' => { 'href' => "#{link_prefix}/v3/builds/#{second_build.guid}", },
'app' => { 'href' => "#{link_prefix}/v3/apps/#{package.app.guid}", },
'droplet' => { 'href' => "#{link_prefix}/v3/droplets/#{second_droplet.guid}", }
},
'created_by' => { 'guid' => user.guid, 'name' => 'bob the builder', 'email' => '[email protected]', }
},
]
})
end
let(:headers) { admin_header }
end

it 'filters on label_selector' do
VCAP::CloudController::BuildLabelModel.make(key_name: 'fruit', value: 'strawberry', build: build)
it_behaves_like 'list_endpoint_with_common_filters' do
let(:resource_klass) { VCAP::CloudController::BuildModel }
let(:additional_resource_params) { { app: app_model } }
let(:api_call) do
lambda { |headers, filters| get "/v3/apps/#{app_model.guid}/builds?#{filters}", nil, headers }
end
let(:headers) { admin_header }
end

get "/v3/apps/#{app_model.guid}/builds?label_selector=fruit=strawberry", {}, user_header
it 'filters on label_selector' do
VCAP::CloudController::BuildLabelModel.make(key_name: 'fruit', value: 'strawberry', build: build)

expect(last_response.status).to eq(200)
expect(parsed_response['resources'].count).to eq(1)
expect(parsed_response['resources'][0]['guid']).to eq(build.guid)
get "/v3/apps/#{app_model.guid}/builds?label_selector=fruit=strawberry", {}, user_header

expect(last_response.status).to eq(200)
expect(parsed_response['resources'].count).to eq(1)
expect(parsed_response['resources'][0]['guid']).to eq(build.guid)
end
end
end

Expand Down

0 comments on commit 110157b

Please sign in to comment.