Skip to content

Commit

Permalink
space application supporter can access specific "process" GET endpoints.
Browse files Browse the repository at this point in the history
- add some information about redacted info in the docs

#2211

Co-authored-by: Weyman Fung <weymanf@vmware.com>
Co-authored-by: Merric de Launey <mdelauney@pivotal.io>
  • Loading branch information
weymanf and MerricdeLauney committed Jun 11, 2021
1 parent 4b770eb commit 9711c69
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 63 deletions.
8 changes: 4 additions & 4 deletions app/controllers/v3/processes_controller.rb
Expand Up @@ -26,14 +26,14 @@ def index

if app_nested?
app, dataset = ProcessListFetcher.fetch_for_app(message, eager_loaded_associations: Presenters::V3::ProcessPresenter.associated_resources)
app_not_found! unless app && permission_queryer.can_read_from_space?(app.space.guid, app.organization.guid)
app_not_found! unless app && permission_queryer.untrusted_can_read_from_space?(app.space.guid, app.organization.guid)
else
dataset = if permission_queryer.can_read_globally?
ProcessListFetcher.fetch_all(message, eager_loaded_associations: Presenters::V3::ProcessPresenter.associated_resources)
else
ProcessListFetcher.fetch_for_spaces(
message,
space_guids: permission_queryer.readable_space_guids,
space_guids: permission_queryer.readable_application_supporter_space_guids,
eager_loaded_associations: Presenters::V3::ProcessPresenter.associated_resources
)
end
Expand Down Expand Up @@ -109,11 +109,11 @@ def stats
def find_process_and_space
if app_nested?
@process, app, @space, org = ProcessFetcher.fetch_for_app_by_type(app_guid: hashed_params[:app_guid], process_type: hashed_params[:type])
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)
process_not_found! unless @process
else
@process, @space, org = ProcessFetcher.fetch(process_guid: hashed_params[:process_guid])
process_not_found! unless @process && permission_queryer.can_read_from_space?(@space.guid, org.guid)
process_not_found! unless @process && permission_queryer.untrusted_can_read_from_space?(@space.guid, org.guid)
end
end

Expand Down
11 changes: 6 additions & 5 deletions docs/v3/source/includes/resources/processes/_get.md.erb
Expand Up @@ -26,12 +26,13 @@ Content-Type: application/json
`GET /v3/apps/:guid/processes/:type`

#### Permitted roles
|
Role | Notes |
--- | ---
Admin |
Admin Read-Only |
Global Auditor |
Org Manager |
Space Auditor |
Global Auditor | Some fields are redacted
Org Manager | Some fields are redacted
Space Application Supporter | Some fields are redacted. Experimental |
Space Auditor | Some fields are redacted
Space Developer |
Space Manager |
Space Manager | Some fields are redacted
Expand Up @@ -40,12 +40,13 @@ Name | Type | Description
**updated_ats** (*experimental*)| _[timestamp](#timestamps)_ | Timestamp to filter by. When filtering on equality, several comma-delimited timestamps may be passed. Also supports filtering with [relational operators](#relational-operators-experimental)

#### Permitted roles
|
Roles | Notes |
--- | ---
Admin |
Admin Read-Only |
Global Auditor |
Org Manager |
Space Auditor |
Space Developer |
Space Application Developer | Experimental
Space Manager |
11 changes: 6 additions & 5 deletions docs/v3/source/includes/resources/processes/_stats.md.erb
Expand Up @@ -26,12 +26,13 @@ Content-Type: application/json
`GET /v3/apps/:guid/processes/:type/stats`

#### Permitted roles
|
Role | Notes |
--- | ---
Admin |
Admin Read-Only |
Global Auditor |
Org Manager |
Space Auditor |
Global Auditor | Some fields are redacted
Org Manager | Some fields are redacted
Space Application Supporter | Some fields are redacted. Experimental |
Space Auditor | Some fields are redacted
Space Developer |
Space Manager |
Space Manager | Some fields are redacted
125 changes: 102 additions & 23 deletions spec/request/processes_spec.rb
Expand Up @@ -2,7 +2,8 @@
require 'request_spec_shared_examples'

RSpec.describe 'Processes' do
let(:space) { VCAP::CloudController::Space.make }
let(:org) { VCAP::CloudController::Organization.make }
let(:space) { VCAP::CloudController::Space.make(organization: org) }
let(:app_model) { VCAP::CloudController::AppModel.make(space: space, name: 'my_app', droplet: droplet) }
let(:droplet) { VCAP::CloudController::DropletModel.make }
let(:developer) { make_developer_for_space(space) }
Expand Down Expand Up @@ -51,8 +52,6 @@
)
}

before { VCAP::CloudController::ProcessModel.make(:process, app: app_model) }

it_behaves_like 'list query endpoint' do
let(:message) { VCAP::CloudController::ProcessesListMessage }
let(:request) { '/v3/processes' }
Expand Down Expand Up @@ -94,11 +93,11 @@

expected_response = {
'pagination' => {
'total_results' => 3,
'total_pages' => 2,
'total_results' => 2,
'total_pages' => 1,
'first' => { 'href' => "#{link_prefix}/v3/processes?page=1&per_page=2" },
'last' => { 'href' => "#{link_prefix}/v3/processes?page=2&per_page=2" },
'next' => { 'href' => "#{link_prefix}/v3/processes?page=2&per_page=2" },
'last' => { 'href' => "#{link_prefix}/v3/processes?page=1&per_page=2" },
'next' => nil,
'previous' => nil,
},
'resources' => [
Expand Down Expand Up @@ -357,12 +356,26 @@
end
end
end

context 'permissions' do
let(:api_call) { lambda { |user_headers| get '/v3/processes', nil, user_headers } }

let(:expected_codes_and_responses) do
h = Hash.new(code: 200, response_guids: [web_process.guid, worker_process.guid])
h['org_auditor'] = { code: 200, response_guids: [] }
h['org_billing_manager'] = { code: 200, response_guids: [] }
h['no_role'] = { code: 200, response_objects: [] }
h
end

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

describe 'GET /v3/processes/:guid' do
it 'retrieves the process' do
revision = VCAP::CloudController::RevisionModel.make
process = VCAP::CloudController::ProcessModel.make(
let(:revision) { VCAP::CloudController::RevisionModel.make }
let(:process) do
VCAP::CloudController::ProcessModel.make(
:process,
app: app_model,
revision: revision,
Expand All @@ -372,10 +385,9 @@
disk_quota: 1024,
command: 'rackup',
)

get "/v3/processes/#{process.guid}", nil, developer_headers

expected_response = {
end
let(:expected_response) do
{
'guid' => process.guid,
'type' => 'web',
'relationships' => {
Expand Down Expand Up @@ -404,6 +416,10 @@
'stats' => { 'href' => "#{link_prefix}/v3/processes/#{process.guid}/stats" },
},
}
end

it 'retrieves the process' do
get "/v3/processes/#{process.guid}", nil, developer_headers

parsed_response = MultiJson.load(last_response.body)

Expand All @@ -412,8 +428,6 @@
end

it 'redacts information for auditors' do
process = VCAP::CloudController::ProcessModel.make(:process, app: app_model, command: 'rackup')

auditor = VCAP::CloudController::User.make
space.organization.add_user(auditor)
space.add_auditor(auditor)
Expand All @@ -425,6 +439,23 @@
expect(last_response.status).to eq(200)
expect(parsed_response['command']).to eq('[PRIVATE DATA HIDDEN]')
end

context 'permissions' do
let(:api_call) { lambda { |user_headers| get "/v3/processes/#{process.guid}", nil, user_headers } }

let(:expected_codes_and_responses) do
h = Hash.new(code: 200, response_object: expected_response.merge({ 'command' => '[PRIVATE DATA HIDDEN]' }))
h['space_developer'] = { code: 200, response_object: expected_response }
h['admin'] = { code: 200, response_object: expected_response }
h['admin_read_only'] = { code: 200, response_object: expected_response }
h['org_auditor'] = { code: 404 }
h['org_billing_manager'] = { code: 404, response_object: nil }
h['no_role'] = { code: 404, response_object: nil }
h
end

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

describe 'GET stats' do
Expand Down Expand Up @@ -543,6 +574,21 @@
expect(parsed_response).to be_a_response_like(expected_response)
end
end

context 'permissions' do
let(:api_call) { lambda { |user_headers| get "/v3/processes/#{process.guid}/stats", nil, user_headers } }

let(:expected_codes_and_responses) do
h = Hash.new(code: 200, response_object: expected_response)
h['org_auditor'] = { code: 404 }
h['org_billing_manager'] = { code: 404, response_object: [] }
h['no_role'] = { code: 404, response_object: [] }
h
end

# while this endpoint returns a list, it's easier to test as a single object since it doesn't paginate
it_behaves_like 'permissions for single object endpoint', ALL_PERMISSIONS + ['space_application_supporter']
end
end

describe 'PATCH /v3/processes/:guid' do
Expand Down Expand Up @@ -1050,12 +1096,26 @@
end
end
end

context 'permissions' do
let(:api_call) { lambda { |user_headers| get "/v3/apps/#{app_model.guid}/processes", nil, user_headers } }
let(:expected_guids) { [process1.guid, process2.guid, process3.guid, deployment_process.guid] }

let(:expected_codes_and_responses) do
h = Hash.new(code: 200, response_guids: expected_guids)
h['org_auditor'] = { code: 404 }
h['org_billing_manager'] = { code: 404, response_guids: nil }
h['no_role'] = { code: 404, response_guids: nil }
h
end

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

describe 'GET /v3/apps/:guid/processes/:type' do
it 'retrieves the process for an app with the requested type' do
revision = VCAP::CloudController::RevisionModel.make
process = VCAP::CloudController::ProcessModel.make(
let!(:process) {
VCAP::CloudController::ProcessModel.make(
:process,
app: app_model,
revision: revision,
Expand All @@ -1065,10 +1125,10 @@
disk_quota: 1024,
command: 'rackup',
)

get "/v3/apps/#{app_model.guid}/processes/web", nil, developer_headers

expected_response = {
}
let!(:revision) { VCAP::CloudController::RevisionModel.make }
let(:expected_response) do
{
'guid' => process.guid,
'relationships' => {
'app' => { 'data' => { 'guid' => app_model.guid } },
Expand Down Expand Up @@ -1097,6 +1157,9 @@
'stats' => { 'href' => "#{link_prefix}/v3/processes/#{process.guid}/stats" },
},
}
end
it 'retrieves the process for an app with the requested type' do
get "/v3/apps/#{app_model.guid}/processes/web", nil, developer_headers

parsed_response = MultiJson.load(last_response.body)

Expand All @@ -1118,6 +1181,22 @@
expect(last_response.status).to eq(200)
expect(parsed_response['command']).to eq('[PRIVATE DATA HIDDEN]')
end
context 'permissions' do
let(:api_call) { lambda { |user_headers| get "/v3/apps/#{app_model.guid}/processes/web", nil, user_headers } }

let(:expected_codes_and_responses) do
h = Hash.new(code: 200, response_object: expected_response.merge({ 'command' => '[PRIVATE DATA HIDDEN]' }))
h['space_developer'] = { code: 200, response_object: expected_response }
h['admin'] = { code: 200, response_object: expected_response }
h['admin_read_only'] = { code: 200, response_object: expected_response }
h['org_billing_manager'] = { code: 404, response_object: nil }
h['org_auditor'] = { code: 404, response_object: nil }
h['no_role'] = { code: 404, response_object: nil }
h
end

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

describe 'PATCH /v3/apps/:guid/processes/:type' do
Expand Down
2 changes: 1 addition & 1 deletion spec/request_spec_shared_examples.rb
Expand Up @@ -85,7 +85,7 @@ def expect_filtered_resources(endpoint, filter, list)

expected_response_guids = expected_codes_and_responses[role][:response_guids]
if expected_response_guids
expect(parsed_response['resources'].map { |space| space['guid'] }).to match_array(expected_response_guids)
expect(parsed_response['resources'].map { |resource| resource['guid'] }).to match_array(expected_response_guids)
end
end
end
Expand Down
24 changes: 0 additions & 24 deletions spec/unit/controllers/v3/processes_controller_spec.rb
Expand Up @@ -11,31 +11,7 @@
allow_user_read_access_for(user, spaces: [space])
end

it 'returns 200 and lists the processes' do
process1 = VCAP::CloudController::ProcessModel.make(:process, app: app)
process2 = VCAP::CloudController::ProcessModel.make(:process, app: app)
VCAP::CloudController::ProcessModel.make

get :index

response_guids = parsed_body['resources'].map { |r| r['guid'] }
expect(response.status).to eq(200)
expect(response_guids).to match_array([process1.guid, process2.guid])
end

context 'when accessed as an app subresource' do
it 'uses the app as a filter' do
process1 = VCAP::CloudController::ProcessModel.make(:process, app: app)
process2 = VCAP::CloudController::ProcessModel.make(:process, app: app)
VCAP::CloudController::ProcessModel.make(:process)

get :index, params: { app_guid: app.guid }

expect(response.status).to eq(200)
response_guids = parsed_body['resources'].map { |r| r['guid'] }
expect(response_guids).to match_array([process1.guid, process2.guid])
end

it 'eager loads associated resources that the presenter specifies' do
expect(VCAP::CloudController::ProcessListFetcher).to receive(:fetch_for_app).with(
an_instance_of(VCAP::CloudController::ProcessesListMessage),
Expand Down

0 comments on commit 9711c69

Please sign in to comment.