Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions app/access/service_instance_access.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ def create?(service_instance, params=nil)
return true if admin_user?
FeatureFlag.raise_unless_enabled!(:service_instance_creation)
return false if service_instance.in_suspended_org?
service_instance.space.has_developer?(context.user) && allowed?(service_instance)
service_instance.space&.has_developer?(context.user) && allowed?(service_instance)
end

def read_for_update?(service_instance, params=nil)
return true if admin_user?
return false if service_instance.in_suspended_org?
service_instance.space.has_developer?(context.user)
service_instance.space&.has_developer?(context.user)
end

def update?(service_instance, params=nil)
Expand All @@ -20,21 +20,20 @@ def update?(service_instance, params=nil)
def delete?(service_instance)
return true if admin_user?
return false if service_instance.in_suspended_org?
service_instance.space.has_developer?(context.user)
service_instance.space&.has_developer?(context.user)
end

def manage_permissions?(service_instance)
return true if admin_user?
service_instance.space.has_developer?(context.user)
service_instance.space&.has_developer?(context.user)
end

def manage_permissions_with_token?(service_instance)
read_with_token?(service_instance) || has_read_permissions_scope?
end

def read_permissions?(service_instance)
return true if admin_user? || admin_read_only_user?
service_instance.space.has_member?(context.user) || service_instance.space.organization.managers.include?(context.user)
admin_user? || admin_read_only_user? || object_is_visible_to_user?(service_instance, context.user)
end

def read_permissions_with_token?(service_instance)
Expand All @@ -43,7 +42,7 @@ def read_permissions_with_token?(service_instance)

def read_env?(service_instance)
return true if admin_user? || admin_read_only_user?
service_instance.space.has_developer?(context.user)
service_instance.space&.has_developer?(context.user)
end

def read_env_with_token?(service_instance)
Expand All @@ -64,7 +63,7 @@ def allowed?(service_instance)
end

def purge?(service_instance)
admin_user? || (service_instance.space.has_developer?(context.user) && service_instance.service_broker.private?)
admin_user? || (service_instance.space&.has_developer?(context.user) && service_instance.service_broker.private?)
end

def purge_with_token?(instance)
Expand Down
16 changes: 7 additions & 9 deletions app/controllers/runtime/spaces_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -138,20 +138,18 @@ def enumerate_services(guid)
def enumerate_service_instances(guid)
space = find_guid_and_validate_access(:read, guid)

if params['return_user_provided_service_instances'] == 'true'
model_class = ServiceInstance
relation_name = :service_instances
else
model_class = ManagedServiceInstance
relation_name = :managed_service_instances
end
model_class = params['return_user_provided_service_instances'] == 'true' ? ServiceInstance : ManagedServiceInstance

service_instances = Query.filtered_dataset_from_query_params(
model_class,
space.user_visible_relationship_dataset(relation_name, @access_context.user, @access_context.admin_override),
model_class.user_visible(@access_context.user, @access_context.admin_override),
ServiceInstancesController.query_parameters,
@opts)
service_instances.filter(space: space)

service_instances = service_instances.filter(Sequel.or([
[:space, space],
[:shared_spaces, space]
]))

collection_renderer.render_json(
ServiceInstancesController,
Expand Down
65 changes: 65 additions & 0 deletions app/controllers/services/service_instances_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
require 'controllers/services/lifecycle/service_instance_deprovisioner'
require 'controllers/services/lifecycle/service_instance_purger'
require 'fetchers/service_instance_fetcher'
require 'fetchers/service_binding_list_fetcher'
require 'presenters/v2/service_instance_shared_to_presenter'
require 'presenters/v2/service_instance_shared_from_presenter'

module VCAP::CloudController
class ServiceInstancesController < RestController::ModelController
Expand Down Expand Up @@ -205,6 +208,46 @@ def permissions(guid)
end
end

get '/v2/service_instances/:guid/shared_from', :shared_from_information
def shared_from_information(guid)
service_instance = find_guid_and_validate_access(:read, guid, ManagedServiceInstance)

if service_instance.shared?
[HTTP::OK, {}, JSON.generate(CloudController::Presenters::V2::ServiceInstanceSharedFromPresenter.new.to_hash(service_instance.space))]
else
[HTTP::NO_CONTENT, {}, '']
end
rescue CloudController::Errors::ApiError => e
if e.name == 'NotAuthorized'
HTTP::NOT_FOUND
else
raise e
end
end

get '/v2/service_instances/:guid/shared_to', :enumerate_shared_to_information
def enumerate_shared_to_information(guid)
service_instance = find_guid_and_validate_access(:read, guid, ManagedServiceInstance)
validate_access(:read, service_instance.space)

associated_controller = VCAP::CloudController::SpacesController
associated_path = "#{self.class.url_for_guid(guid)}/shared_to"

create_paginated_collection_renderer(service_instance).render_json(
associated_controller,
service_instance.shared_spaces_dataset,
associated_path,
@opts,
{},
)
rescue CloudController::Errors::ApiError => e
if e.name == 'NotAuthorized'
HTTP::NOT_FOUND
else
raise e
end
end

def self.url_for_guid(guid)
object = ServiceInstance.where(guid: guid).first

Expand Down Expand Up @@ -303,6 +346,28 @@ def unbind_route(route_guid, instance_guid)

private

class ServiceInstanceSharedToSerializer
def initialize(service_instance)
@service_instance = service_instance
end

def serialize(controller, space, opts, orphans=nil)
bound_app_count = ServiceBindingListFetcher.fetch_service_instance_bindings_in_space(@service_instance.guid, space.guid).count
CloudController::Presenters::V2::ServiceInstanceSharedToPresenter.new.to_hash(space, bound_app_count)
end
end

def create_paginated_collection_renderer(service_instance)
VCAP::CloudController::RestController::PaginatedCollectionRenderer.new(
VCAP::CloudController::RestController::SecureEagerLoader.new,
ServiceInstanceSharedToSerializer.new(service_instance),
{
max_results_per_page: config.get(:renderer, :max_results_per_page),
default_results_per_page: config.get(:renderer, :default_results_per_page),
max_inline_relations_depth: config.get(:renderer, :max_inline_relations_depth),
})
end

def route_services_enabled?
@config.get(:route_services_enabled)
end
Expand Down
19 changes: 19 additions & 0 deletions app/controllers/v3/service_instances_controller.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
require 'messages/to_many_relationship_message'
require 'messages/service_instances/service_instances_list_message'

require 'presenters/v3/relationship_presenter'
require 'presenters/v3/to_many_relationship_presenter'
require 'presenters/v3/paginated_list_presenter'
require 'actions/service_instance_share'
require 'actions/service_instance_unshare'
require 'fetchers/service_instance_list_fetcher'

class ServiceInstancesV3Controller < ApplicationController
def index
message = ServiceInstancesListMessage.from_params(query_params)
invalid_param!(message.errors.full_messages) unless message.valid?

dataset = if can_read_globally?
ServiceInstanceListFetcher.new.fetch_all(message: message)
else
ServiceInstanceListFetcher.new.fetch(message: message, space_guids: readable_space_guids)
end

render status: :ok, json: Presenters::V3::PaginatedListPresenter.new(
dataset: dataset,
path: '/v3/service_instances',
message: message)
end

def share_service_instance
FeatureFlag.raise_unless_enabled!(:service_instance_sharing)

Expand Down
7 changes: 7 additions & 0 deletions app/fetchers/service_binding_list_fetcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ def fetch_all
filter(dataset)
end

def self.fetch_service_instance_bindings_in_space(service_instance_guid, space_guid)
ServiceBinding.select_all(ServiceBinding.table_name).
join(:apps, guid: :app_guid).
where(apps__space_guid: space_guid).
where(service_bindings__service_instance_guid: service_instance_guid)
end

private

def filter(dataset)
Expand Down
30 changes: 30 additions & 0 deletions app/fetchers/service_instance_list_fetcher.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module VCAP::CloudController
class ServiceInstanceListFetcher
def fetch(message:, space_guids:)
source_space_instance_dataset = ServiceInstance.select_all(ServiceInstance.table_name).
join(Space.table_name, id: :space_id, guid: space_guids)

shared_instance_dataset = ServiceInstance.select_all(ServiceInstance.table_name).
join(:service_instance_shares, service_instance_guid: :guid, target_space_guid: space_guids)

dataset = source_space_instance_dataset.union(shared_instance_dataset, alias: :service_instances)

filter(dataset, message)
end

def fetch_all(message:)
dataset = ServiceInstance.dataset
filter(dataset, message)
end

private

def filter(dataset, message)
if message.requested?(:names)
dataset = dataset.where(service_instances__name: message.names)
end

dataset
end
end
end
32 changes: 32 additions & 0 deletions app/messages/service_instances/service_instances_list_message.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
require 'messages/list_message'

module VCAP::CloudController
class ServiceInstancesListMessage < ListMessage
ALLOWED_KEYS = [:page, :per_page, :order_by, :names].freeze

attr_accessor(*ALLOWED_KEYS)

validates_with NoAdditionalParamsValidator
validates :names, array: true, allow_nil: true

def initialize(params={})
super(params.symbolize_keys)
end

def self.from_params(params)
opts = params.dup
to_array! opts, 'names'
new(opts.symbolize_keys)
end

def valid_order_by_values
super << :name
end

private

def allowed_keys
ALLOWED_KEYS
end
end
end
10 changes: 9 additions & 1 deletion app/models/services/service_instance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ def self.user_visibility_filter(user)
[:space, user.spaces_dataset],
[:space, user.audited_spaces_dataset],
[:space, user.managed_spaces_dataset],
[:shared_spaces, user.spaces_dataset],
[:shared_spaces, user.managed_spaces_dataset],
[:shared_spaces, user.audited_spaces_dataset],
[:shared_spaces, managed_organizations_spaces_dataset(user.managed_organizations_dataset)],
])
end

Expand Down Expand Up @@ -134,7 +138,7 @@ def credentials_with_serialization
alias_method_chain :credentials, 'serialization'

def in_suspended_org?
space.in_suspended_org?
space&.in_suspended_org?
end

def after_create
Expand Down Expand Up @@ -171,6 +175,10 @@ def volume_service?
false
end

def shared?
shared_spaces.any?
end

def self.managed_organizations_spaces_dataset(managed_organizations_dataset)
VCAP::CloudController::Space.dataset.filter({ organization_id: managed_organizations_dataset.select(:organization_id) })
end
Expand Down
2 changes: 2 additions & 0 deletions app/presenters/v2/service_instance_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ def entity_hash(controller, obj, opts, depth, parents, orphans=nil)
obj_hash['service_plan_guid'] = service_plan.guid
obj_hash['service_guid'] = service_plan.service.guid
rel_hash['service_url'] = "/v2/services/#{service_plan.service.guid}"
rel_hash['shared_from_url'] = "/v2/service_instances/#{obj.guid}/shared_from"
rel_hash['shared_to_url'] = "/v2/service_instances/#{obj.guid}/shared_to"
end

obj_hash.merge!(rel_hash)
Expand Down
14 changes: 14 additions & 0 deletions app/presenters/v2/service_instance_shared_from_presenter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module CloudController
module Presenters
module V2
class ServiceInstanceSharedFromPresenter
def to_hash(space)
{
'space_name' => space.name,
'organization_name' => space.organization.name
}
end
end
end
end
end
13 changes: 13 additions & 0 deletions app/presenters/v2/service_instance_shared_to_presenter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
require 'presenters/v2/service_instance_shared_from_presenter'

module CloudController
module Presenters
module V2
class ServiceInstanceSharedToPresenter < ServiceInstanceSharedFromPresenter
def to_hash(space, bound_app_count)
super(space).merge({ 'bound_app_count' => bound_app_count })
end
end
end
end
end
4 changes: 3 additions & 1 deletion app/presenters/v3/paginated_list_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
require 'presenters/v3/process_presenter'
require 'presenters/v3/route_mapping_presenter'
require 'presenters/v3/service_binding_presenter'
require 'presenters/v3/service_instance_presenter'
require 'presenters/v3/task_presenter'
require 'presenters/v3/organization_presenter'
require 'presenters/v3/space_presenter'
Expand All @@ -24,7 +25,8 @@ class PaginatedListPresenter
'PackageModel' => VCAP::CloudController::Presenters::V3::PackagePresenter,
'RouteMappingModel' => VCAP::CloudController::Presenters::V3::RouteMappingPresenter,
'ServiceBinding' => VCAP::CloudController::Presenters::V3::ServiceBindingPresenter,
'TaskModel' => VCAP::CloudController::Presenters::V3::TaskPresenter,
'ManagedServiceInstance' => VCAP::CloudController::Presenters::V3::ServiceInstancePresenter,
'TaskModel' => VCAP::CloudController::Presenters::V3::TaskPresenter,
}.freeze

def initialize(dataset:, path:, message: nil, show_secrets: false)
Expand Down
24 changes: 24 additions & 0 deletions app/presenters/v3/service_instance_presenter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
require 'presenters/v3/base_presenter'

module VCAP::CloudController
module Presenters
module V3
class ServiceInstancePresenter < BasePresenter
def to_hash
{
guid: service_instance.guid,
created_at: service_instance.created_at,
updated_at: service_instance.updated_at,
name: service_instance.name
}
end

private

def service_instance
@resource
end
end
end
end
end
Loading