blob: 759b529306dd720a28fd832193a679072572f137 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// TODO(vardhan): Make entity references secure (no introspection allowed).
#include "peridot/bin/sessionmgr/entity_provider_runner/entity_provider_runner.h"
#include <utility>
#include <lib/fsl/types/type_converters.h>
#include <lib/fsl/vmo/strings.h>
#include <lib/fxl/macros.h>
#include <lib/fxl/strings/join_strings.h>
#include <lib/fxl/type_converter.h>
#include "peridot/bin/sessionmgr/entity_provider_runner/entity_provider_controller.h"
#include "peridot/bin/sessionmgr/entity_provider_runner/entity_provider_launcher.h"
#include "peridot/lib/fidl/json_xdr.h"
#include "peridot/lib/util/string_escape.h"
namespace modular {
namespace {
constexpr char kEntityReferencePrefix[] = "EntityRef";
constexpr char kEntityDataReferencePrefix[] = "EntityData";
constexpr char kStoryEntityReferencePrefix[] = "Story";
using StringMap = std::map<std::string, std::string>;
constexpr XdrFilterType<StringMap> XdrStringMap[] = {
XdrFilter<StringMap>,
nullptr,
};
// Given a |entity_namespace|, |provider_uri| and a |cookie|, encodes it into an
// entity reference.
std::string EncodeEntityReference(const std::string& entity_namespace,
const std::string& provider_uri,
const std::string& cookie) {
std::vector<std::string> parts(3);
parts[0] = entity_namespace;
parts[1] = StringEscape(provider_uri, "/");
parts[2] = StringEscape(cookie, "/");
return fxl::JoinStrings(parts, "/");
}
// Returns an entity reference for an entity associated with the given
// |story_id| and |cookie|.
std::string EncodeStoryEntityReference(const std::string& story_id,
const std::string& cookie) {
return EncodeEntityReference(kStoryEntityReferencePrefix, story_id, cookie);
}
// Returns an entity reference for an entity associated with the given
// |agent_url| and |cookie|.
std::string EncodeAgentEntityReference(const std::string& agent_url,
const std::string& cookie) {
return EncodeEntityReference(kEntityReferencePrefix, agent_url, cookie);
}
// Inverse of EncodeEntityReference.
bool DecodeEntityReference(const std::string& entity_reference,
std::string* const prefix,
std::string* const provider_uri,
std::string* const cookie) {
auto parts = SplitEscapedString(entity_reference, '/');
if (parts.size() != 3) {
return false;
}
*prefix = StringUnescape(parts[0].ToString());
*provider_uri = StringUnescape(parts[1].ToString());
*cookie = StringUnescape(parts[2].ToString());
return true;
}
bool DecodeEntityDataReference(const std::string& entity_reference,
std::map<std::string, std::string>* data) {
auto parts = SplitEscapedString(entity_reference, '/');
if (parts.size() != 2 ||
StringUnescape(parts[0]) != kEntityDataReferencePrefix) {
return false;
}
return XdrRead(StringUnescape(parts[1].ToString()), data, XdrStringMap);
}
} // namespace
class EntityProviderRunner::EntityReferenceFactoryImpl
: fuchsia::modular::EntityReferenceFactory {
public:
// Creates an entity reference factory for the given |provider_uri|.
//
// |entity_provider_runner| exposes the concrete implementations for encoding
// entity references.
EntityReferenceFactoryImpl(const std::string& agent_url,
EntityProviderRunner* const entity_provider_runner)
: agent_url_(agent_url),
entity_provider_runner_(entity_provider_runner) {}
void AddBinding(
fidl::InterfaceRequest<fuchsia::modular::EntityReferenceFactory>
request) {
bindings_.AddBinding(this, std::move(request));
}
void set_empty_set_handler(const std::function<void()>& handler) {
bindings_.set_empty_set_handler(handler);
}
private:
// |fuchsia::modular::EntityReferenceFactory|
void CreateReference(std::string cookie,
CreateReferenceCallback callback) override {
entity_provider_runner_->CreateReference(agent_url_, cookie, callback);
}
// The agent url if the entity reference factory produces references to
// entities backed by agents, otherwise the story id of the story entity
// provider.
const std::string agent_url_;
EntityProviderRunner* const entity_provider_runner_;
fidl::BindingSet<fuchsia::modular::EntityReferenceFactory> bindings_;
FXL_DISALLOW_COPY_AND_ASSIGN(EntityReferenceFactoryImpl);
};
// This class provides |fuchsia::modular::Entity| implementations for a given
// data entity reference.
class EntityProviderRunner::DataEntity : fuchsia::modular::Entity {
public:
DataEntity(EntityProviderRunner* const provider,
const std::string& entity_reference,
std::map<std::string, std::string> data) {
for (const auto& kv : data) {
types_.push_back(kv.first);
}
data_ = std::move(data);
bindings_.set_empty_set_handler([provider, entity_reference] {
provider->OnDataEntityFinished(entity_reference);
});
};
void AddBinding(fidl::InterfaceRequest<fuchsia::modular::Entity> request) {
bindings_.AddBinding(this, std::move(request));
}
private:
// |fuchsia::modular::Entity|
void GetTypes(GetTypesCallback result) override {
result(types_);
}
// |fuchsia::modular::Entity|
void GetData(std::string type, GetDataCallback result) override {
auto it = data_.find(type);
if (it != data_.end()) {
fsl::SizedVmo vmo;
FXL_CHECK(fsl::VmoFromString(it->second, &vmo));
auto vmo_ptr =
std::make_unique<fuchsia::mem::Buffer>(std::move(vmo).ToTransport());
result(std::move(vmo_ptr));
} else {
result(nullptr);
}
}
// |fuchsia::modular::Entity|
void WriteData(std::string type, fuchsia::mem::Buffer data,
WriteDataCallback callback) override {
// TODO(MI4-1301)
callback(fuchsia::modular::EntityWriteStatus::READ_ONLY);
}
// |fuchsia::modular::Entity|
void GetReference(GetReferenceCallback callback) override {
// TODO(MI4-1301)
FXL_NOTIMPLEMENTED();
}
// |fuchsia::modular::Entity|
void Watch(
std::string type,
fidl::InterfaceHandle<fuchsia::modular::EntityWatcher> watcher) override {
// TODO(MI4-1301)
FXL_NOTIMPLEMENTED();
}
std::vector<std::string> types_;
std::map<std::string, std::string> data_;
fidl::BindingSet<fuchsia::modular::Entity> bindings_;
FXL_DISALLOW_COPY_AND_ASSIGN(DataEntity);
};
EntityProviderRunner::EntityProviderRunner(
EntityProviderLauncher* const entity_provider_launcher)
: entity_provider_launcher_(entity_provider_launcher) {}
EntityProviderRunner::~EntityProviderRunner() = default;
void EntityProviderRunner::ConnectEntityReferenceFactory(
const std::string& agent_url,
fidl::InterfaceRequest<fuchsia::modular::EntityReferenceFactory> request) {
auto it = entity_reference_factory_bindings_.find(agent_url);
if (it == entity_reference_factory_bindings_.end()) {
bool inserted;
std::tie(it, inserted) = entity_reference_factory_bindings_.emplace(
std::make_pair(agent_url, std::make_unique<EntityReferenceFactoryImpl>(
agent_url, this)));
FXL_DCHECK(inserted);
it->second->set_empty_set_handler([this, agent_url] {
entity_reference_factory_bindings_.erase(agent_url);
});
}
it->second->AddBinding(std::move(request));
}
std::string EntityProviderRunner::CreateStoryEntityReference(
const std::string& story_id, const std::string& cookie) {
return EncodeStoryEntityReference(story_id, cookie);
}
void EntityProviderRunner::ConnectEntityResolver(
fidl::InterfaceRequest<fuchsia::modular::EntityResolver> request) {
entity_resolver_bindings_.AddBinding(this, std::move(request));
}
void EntityProviderRunner::OnEntityProviderFinished(
const std::string agent_url) {
entity_provider_controllers_.erase(agent_url);
}
std::string EntityProviderRunner::CreateReferenceFromData(
std::map<std::string, std::string> type_to_data) {
// TODO(rosswang): Several of these heap allocations are unnecessary but this
// code is only temporary.
std::string encoded;
XdrWrite(&encoded, &type_to_data, XdrStringMap);
std::vector<std::string> parts(2);
parts[0] = kEntityDataReferencePrefix;
parts[1] = StringEscape(encoded, "/");
std::string joined = fxl::JoinStrings(parts, "/");
if (joined.length() > ZX_CHANNEL_MAX_MSG_BYTES) {
FXL_LOG(ERROR) << "data entity reference size exceeds FIDL channel message "
"size limits";
}
return joined;
}
void EntityProviderRunner::CreateReference(
const std::string& agent_url, const std::string& cookie,
fuchsia::modular::EntityReferenceFactory::CreateReferenceCallback
callback) {
auto entity_ref = EncodeAgentEntityReference(agent_url, cookie);
callback(entity_ref);
}
void EntityProviderRunner::ResolveDataEntity(
fidl::StringPtr entity_reference,
fidl::InterfaceRequest<fuchsia::modular::Entity> entity_request) {
std::map<std::string, std::string> entity_data;
if (!DecodeEntityDataReference(entity_reference, &entity_data)) {
FXL_LOG(INFO) << "Could not decode entity reference: " << entity_reference;
return;
// |entity_request| closes here.
}
auto inserted =
data_entities_.emplace(std::make_pair(entity_reference.get(), nullptr));
if (inserted.second) {
// This is a new entity.
inserted.first->second = std::make_unique<DataEntity>(
this, entity_reference.get(), std::move(entity_data));
}
inserted.first->second->AddBinding(std::move(entity_request));
}
void EntityProviderRunner::OnDataEntityFinished(
const std::string& entity_reference) {
data_entities_.erase(entity_reference);
}
void EntityProviderRunner::ResolveEntity(
std::string entity_reference,
fidl::InterfaceRequest<fuchsia::modular::Entity> entity_request) {
if (entity_reference.find(kEntityDataReferencePrefix) == 0ul) {
ResolveDataEntity(entity_reference, std::move(entity_request));
return;
}
std::string provider_uri;
std::string cookie;
std::string prefix;
fuchsia::modular::EntityProviderPtr entity_provider;
fuchsia::modular::AgentControllerPtr agent_controller;
if (!DecodeEntityReference(entity_reference, &prefix, &provider_uri,
&cookie)) {
return;
// |entity_request| is closed here.
}
bool is_story_entity = prefix == kStoryEntityReferencePrefix;
if (is_story_entity) {
entity_provider_launcher_->ConnectToStoryEntityProvider(
provider_uri, entity_provider.NewRequest());
} else if (prefix == kEntityReferencePrefix) {
entity_provider_launcher_->ConnectToEntityProvider(
provider_uri, entity_provider.NewRequest(),
agent_controller.NewRequest());
} else {
return;
// |entity_request| is closed here.
}
// Connect to the EntityProviderController managing this entity.
auto it = entity_provider_controllers_.find(provider_uri);
if (it == entity_provider_controllers_.end()) {
bool inserted;
std::tie(it, inserted) =
entity_provider_controllers_.emplace(std::make_pair(
provider_uri,
std::make_unique<EntityProviderController>(
std::move(entity_provider), std::move(agent_controller),
[this, is_story_entity, provider_uri] {
if (!is_story_entity) {
OnEntityProviderFinished(provider_uri);
}
})));
FXL_DCHECK(inserted);
}
it->second->ProvideEntity(cookie, entity_reference,
std::move(entity_request));
}
} // namespace modular