diff options
author | ben <ben@chromium.org> | 2016-03-09 17:21:58 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-03-10 01:23:41 +0000 |
commit | d1faec7945458e2500203bd9a7d37cd462646447 (patch) | |
tree | de8557ee265788379e526345d4232aef1828100b /mojo | |
parent | cbe35bac4f296de200b2821d8406d921b83956ba (diff) | |
download | chromium_src-d1faec7945458e2500203bd9a7d37cd462646447.zip chromium_src-d1faec7945458e2500203bd9a7d37cd462646447.tar.gz chromium_src-d1faec7945458e2500203bd9a7d37cd462646447.tar.bz2 |
Morph CapabilityFilter into caps::Capabilities, which supports capability classes (yet unimplemented).
R=sky@chromium.org
BUG=
Review URL: https://codereview.chromium.org/1775113003
Cr-Commit-Position: refs/heads/master@{#380285}
Diffstat (limited to 'mojo')
24 files changed, 462 insertions, 156 deletions
diff --git a/mojo/mojo_base.gyp b/mojo/mojo_base.gyp index a3f6ba6..69f970a 100644 --- a/mojo/mojo_base.gyp +++ b/mojo/mojo_base.gyp @@ -126,6 +126,7 @@ 'mojom_files': [ 'services/catalog/public/interfaces/catalog.mojom', 'services/catalog/public/interfaces/resolver.mojom', + 'shell/public/interfaces/capabilities.mojom', 'shell/public/interfaces/connector.mojom', 'shell/public/interfaces/interface_provider.mojom', 'shell/public/interfaces/shell.mojom', @@ -142,6 +143,7 @@ 'type': 'static_library', 'sources': [ 'shell/public/cpp/application_runner.h', + 'shell/public/cpp/capabilities.h', 'shell/public/cpp/connect.h', 'shell/public/cpp/connection.h', 'shell/public/cpp/connector.h', @@ -153,6 +155,7 @@ 'shell/public/cpp/interface_factory_impl.h', 'shell/public/cpp/interface_registry.h', 'shell/public/cpp/lib/application_runner.cc', + 'shell/public/cpp/lib/capabilities.cc', 'shell/public/cpp/lib/connection_impl.cc', 'shell/public/cpp/lib/connection_impl.h', 'shell/public/cpp/lib/connector_impl.cc', diff --git a/mojo/services/catalog/builder.cc b/mojo/services/catalog/builder.cc index cd011120..9d7d5b7 100644 --- a/mojo/services/catalog/builder.cc +++ b/mojo/services/catalog/builder.cc @@ -6,30 +6,106 @@ #include "base/values.h" #include "mojo/services/catalog/store.h" +#include "mojo/shell/public/cpp/capabilities.h" #include "mojo/shell/public/cpp/names.h" +// TODO(beng): this code should do better error handling instead of CHECKing so +// much. + namespace catalog { -CapabilityFilter BuildCapabilityFilter(const base::DictionaryValue& value) { - CapabilityFilter filter; +mojo::CapabilitySpec BuildCapabilitiesV0( + const base::DictionaryValue& value) { + mojo::CapabilitySpec capabilities; base::DictionaryValue::Iterator it(value); for (; !it.IsAtEnd(); it.Advance()) { const base::ListValue* values = nullptr; CHECK(it.value().GetAsList(&values)); - AllowedInterfaces interfaces; + mojo::CapabilityRequest spec; for (auto i = values->begin(); i != values->end(); ++i) { - std::string iface_name; + mojo::Interface interface_name; const base::Value* v = *i; - CHECK(v->GetAsString(&iface_name)); - interfaces.insert(iface_name); + CHECK(v->GetAsString(&interface_name)); + spec.interfaces.insert(interface_name); + } + capabilities.required[it.key()] = spec; + } + return capabilities; +} + +void ReadStringSet(const base::ListValue& list_value, + std::set<std::string>* string_set) { + DCHECK(string_set); + for (auto i = list_value.begin(); i != list_value.end(); ++i) { + std::string value; + const base::Value* value_value = *i; + CHECK(value_value->GetAsString(&value)); + string_set->insert(value); + } +} + +void ReadStringSetFromValue(const base::Value& value, + std::set<std::string>* string_set) { + const base::ListValue* list_value = nullptr; + CHECK(value.GetAsList(&list_value)); + ReadStringSet(*list_value, string_set); +} + +void ReadStringSetFromDictionary(const base::DictionaryValue& dictionary, + const std::string& key, + std::set<std::string>* string_set) { + const base::ListValue* list_value = nullptr; + if (dictionary.HasKey(key)) + CHECK(dictionary.GetList(key, &list_value)); + if (list_value) + ReadStringSet(*list_value, string_set); +} + +mojo::CapabilitySpec BuildCapabilitiesV1( + const base::DictionaryValue& value) { + mojo::CapabilitySpec capabilities; + + const base::DictionaryValue* provided_value = nullptr; + if (value.HasKey(Store::kCapabilities_ProvidedKey)) { + CHECK(value.GetDictionary(Store::kCapabilities_ProvidedKey, + &provided_value)); + } + if (provided_value) { + mojo::CapabilityRequest provided; + base::DictionaryValue::Iterator it(*provided_value); + for(; !it.IsAtEnd(); it.Advance()) { + mojo::Interfaces interfaces; + ReadStringSetFromValue(it.value(), &interfaces); + capabilities.provided[it.key()] = interfaces; + } + } + + const base::DictionaryValue* required_value = nullptr; + if (value.HasKey(Store::kCapabilities_RequiredKey)) { + CHECK(value.GetDictionary(Store::kCapabilities_RequiredKey, + &required_value)); + } + if (required_value) { + base::DictionaryValue::Iterator it(*required_value); + for (; !it.IsAtEnd(); it.Advance()) { + mojo::CapabilityRequest spec; + const base::DictionaryValue* entry_value = nullptr; + CHECK(it.value().GetAsDictionary(&entry_value)); + ReadStringSetFromDictionary( + *entry_value, Store::kCapabilities_ClassesKey, &spec.classes); + ReadStringSetFromDictionary( + *entry_value, Store::kCapabilities_InterfacesKey, &spec.interfaces); + capabilities.required[it.key()] = spec; } - filter[it.key()] = interfaces; } - return filter; + return capabilities; } Entry BuildEntry(const base::DictionaryValue& value) { Entry entry; + int manifest_version = 0; + if (value.HasKey(Store::kManifestVersionKey)) + CHECK(value.GetInteger(Store::kManifestVersionKey, &manifest_version)); std::string name_string; CHECK(value.GetString(Store::kNameKey, &name_string)); CHECK(mojo::IsValidName(name_string)) << "Invalid Name: " << name_string; @@ -42,23 +118,47 @@ Entry BuildEntry(const base::DictionaryValue& value) { CHECK(value.GetString(Store::kDisplayNameKey, &entry.display_name)); const base::DictionaryValue* capabilities = nullptr; CHECK(value.GetDictionary(Store::kCapabilitiesKey, &capabilities)); - entry.capabilities = BuildCapabilityFilter(*capabilities); - + if (manifest_version == 0) + entry.capabilities = BuildCapabilitiesV0(*capabilities); + else + entry.capabilities = BuildCapabilitiesV1(*capabilities); return entry; } -void SerializeEntry(const Entry& entry, base::DictionaryValue** value) { - *value = new base::DictionaryValue; - (*value)->SetString(Store::kNameKey, entry.name); - (*value)->SetString(Store::kDisplayNameKey, entry.display_name); - base::DictionaryValue* capabilities = new base::DictionaryValue; - for (const auto& pair : entry.capabilities) { +scoped_ptr<base::DictionaryValue> SerializeEntry(const Entry& entry) { + scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue); + value->SetInteger(Store::kManifestVersionKey, 1); + value->SetString(Store::kNameKey, entry.name); + value->SetString(Store::kDisplayNameKey, entry.display_name); + value->SetString(Store::kQualifierKey, entry.qualifier); + scoped_ptr<base::DictionaryValue> spec(new base::DictionaryValue); + + scoped_ptr<base::DictionaryValue> provided(new base::DictionaryValue); + for (const auto& i : entry.capabilities.provided) { + scoped_ptr<base::ListValue> interfaces(new base::ListValue); + for (const auto& interface_name : i.second) + interfaces->AppendString(interface_name); + provided->Set(i.first, std::move(interfaces)); + } + spec->Set(Store::kCapabilities_ProvidedKey, std::move(provided)); + + scoped_ptr<base::DictionaryValue> required(new base::DictionaryValue); + for (const auto& i : entry.capabilities.required) { + scoped_ptr<base::DictionaryValue> request(new base::DictionaryValue); + scoped_ptr<base::ListValue> classes(new base::ListValue); + for (const auto& class_name : i.second.classes) + classes->AppendString(class_name); + request->Set(Store::kCapabilities_ClassesKey, std::move(classes)); scoped_ptr<base::ListValue> interfaces(new base::ListValue); - for (const auto& iface_name : pair.second) - interfaces->AppendString(iface_name); - capabilities->Set(pair.first, std::move(interfaces)); + for (const auto& interface_name : i.second.interfaces) + interfaces->AppendString(interface_name); + request->Set(Store::kCapabilities_InterfacesKey, std::move(interfaces)); + required->Set(i.first, std::move(request)); } - (*value)->Set(Store::kCapabilitiesKey, make_scoped_ptr(capabilities)); + spec->Set(Store::kCapabilities_RequiredKey, std::move(required)); + + value->Set(Store::kCapabilitiesKey, std::move(spec)); + return value; } } // namespace catalog diff --git a/mojo/services/catalog/builder.h b/mojo/services/catalog/builder.h index 2f3e1aa..71eebf1 100644 --- a/mojo/services/catalog/builder.h +++ b/mojo/services/catalog/builder.h @@ -15,7 +15,7 @@ namespace catalog { Entry BuildEntry(const base::DictionaryValue& value); -void SerializeEntry(const Entry& entry, base::DictionaryValue** value); +scoped_ptr<base::DictionaryValue> SerializeEntry(const Entry& entry); } // namespace catalog diff --git a/mojo/services/catalog/builder_unittest.cc b/mojo/services/catalog/builder_unittest.cc index 4290a2f..1705be8 100644 --- a/mojo/services/catalog/builder_unittest.cc +++ b/mojo/services/catalog/builder_unittest.cc @@ -9,15 +9,27 @@ #include "base/macros.h" #include "base/path_service.h" #include "base/values.h" +#include "mojo/shell/public/cpp/capabilities.h" #include "mojo/shell/public/cpp/names.h" #include "testing/gtest/include/gtest/gtest.h" +namespace catalog { + class BuilderTest : public testing::Test { public: BuilderTest() {} ~BuilderTest() override {} protected: + scoped_ptr<base::Value> ReadEntry(const std::string& manifest, Entry* entry) { + DCHECK(entry); + scoped_ptr<base::Value> value = ReadManifest(manifest); + base::DictionaryValue* dictionary = nullptr; + CHECK(value->GetAsDictionary(&dictionary)); + *entry = BuildEntry(*dictionary); + return value; + } + scoped_ptr<base::Value> ReadManifest(const std::string& manifest) { base::FilePath manifest_path; PathService::Get(base::DIR_SOURCE_ROOT, &manifest_path); @@ -40,13 +52,9 @@ class BuilderTest : public testing::Test { DISALLOW_COPY_AND_ASSIGN(BuilderTest); }; -namespace catalog { - TEST_F(BuilderTest, Simple) { - scoped_ptr<base::Value> value = ReadManifest("simple"); - base::DictionaryValue* dictionary = nullptr; - CHECK(value->GetAsDictionary(&dictionary)); - Entry entry = BuildEntry(*dictionary); + Entry entry; + ReadEntry("simple", &entry); EXPECT_EQ("mojo:foo", entry.name); EXPECT_EQ(mojo::GetNamePath(entry.name), entry.qualifier); @@ -54,10 +62,8 @@ TEST_F(BuilderTest, Simple) { } TEST_F(BuilderTest, Instance) { - scoped_ptr<base::Value> value = ReadManifest("instance"); - base::DictionaryValue* dictionary = nullptr; - CHECK(value->GetAsDictionary(&dictionary)); - Entry entry = BuildEntry(*dictionary); + Entry entry; + ReadEntry("instance", &entry); EXPECT_EQ("mojo:foo", entry.name); EXPECT_EQ("bar", entry.qualifier); @@ -65,19 +71,30 @@ TEST_F(BuilderTest, Instance) { } TEST_F(BuilderTest, Capabilities) { - scoped_ptr<base::Value> value = ReadManifest("capabilities"); - base::DictionaryValue* dictionary = nullptr; - CHECK(value->GetAsDictionary(&dictionary)); - Entry entry = BuildEntry(*dictionary); + Entry entry; + ReadEntry("capabilities", &entry); EXPECT_EQ("mojo:foo", entry.name); EXPECT_EQ("bar", entry.qualifier); EXPECT_EQ("Foo", entry.display_name); - CapabilityFilter filter; - AllowedInterfaces interfaces; - interfaces.insert("mojo::Bar"); - filter["mojo:bar"] = interfaces; - EXPECT_EQ(filter, entry.capabilities); + mojo::CapabilitySpec spec; + mojo::CapabilityRequest request; + request.interfaces.insert("mojo::Bar"); + spec.required["mojo:bar"] = request; + EXPECT_EQ(spec, entry.capabilities); +} + +TEST_F(BuilderTest, Serialization) { + Entry entry; + scoped_ptr<base::Value> value = ReadEntry("serialization", &entry); + + scoped_ptr<base::DictionaryValue> serialized(SerializeEntry(entry)); + + // We can't just compare values, since during deserialization some of the + // lists get converted to std::sets, which are sorted, so Value::Equals will + // fail. + Entry reconstituted = BuildEntry(*serialized.get()); + EXPECT_EQ(entry, reconstituted); } TEST_F(BuilderTest, Malformed) { diff --git a/mojo/services/catalog/catalog.cc b/mojo/services/catalog/catalog.cc index 6d5518c..661ec21 100644 --- a/mojo/services/catalog/catalog.cc +++ b/mojo/services/catalog/catalog.cc @@ -148,16 +148,10 @@ void Catalog::CompleteResolveMojoName( mojo::GetNamePath(resolved_name) + extension); } - mojo::shell::mojom::CapabilityFilterPtr filter( - mojo::shell::mojom::CapabilityFilter::New()); - filter->filter = mojo::Map<mojo::String, mojo::Array<mojo::String>>(); - for (const auto& entry : entry_iter->second.capabilities) { - mojo::Array<mojo::String> interfaces; - for (auto interface_name : entry.second) - interfaces.push_back(interface_name); - filter->filter.insert(entry.first, std::move(interfaces)); - } - callback.Run(resolved_name, qualifier, std::move(filter), + mojo::shell::mojom::CapabilitySpecPtr capabilities_ptr = + mojo::shell::mojom::CapabilitySpec::From(entry_iter->second.capabilities); + + callback.Run(resolved_name, qualifier, std::move(capabilities_ptr), file_url.spec()); } @@ -203,11 +197,8 @@ void Catalog::DeserializeCatalog() { void Catalog::SerializeCatalog() { scoped_ptr<base::ListValue> catalog(new base::ListValue); - for (const auto& entry : catalog_) { - base::DictionaryValue* dictionary = nullptr; - SerializeEntry(entry.second, &dictionary); - catalog->Append(make_scoped_ptr(dictionary)); - } + for (const auto& entry : catalog_) + catalog->Append(SerializeEntry(entry.second)); if (store_) store_->UpdateStore(std::move(catalog)); } diff --git a/mojo/services/catalog/data/serialization b/mojo/services/catalog/data/serialization new file mode 100644 index 0000000..4605702 --- /dev/null +++ b/mojo/services/catalog/data/serialization @@ -0,0 +1,18 @@ +{ + "manifest_version": 1, + "name": "mojo:foo", + "display_name": "Foo", + "process-group": "bar", + "capabilities": { + "provided": { + "foo": ["mojo::Bar", "mojo::Baz"], + "bar": ["mojo::Bork"] + }, + "required": { + "mojo:bar": { + "classes": ["a", "b"], + "interfaces": ["mojo::Blork", "mojo::Blark"] + } + } + } +} diff --git a/mojo/services/catalog/entry.cc b/mojo/services/catalog/entry.cc index 9ed445a..9aec915 100644 --- a/mojo/services/catalog/entry.cc +++ b/mojo/services/catalog/entry.cc @@ -10,4 +10,10 @@ Entry::Entry() {} Entry::Entry(const Entry& other) = default; Entry::~Entry() {} +bool Entry::operator==(const Entry& other) const { + return other.name == name && other.qualifier == qualifier && + other.display_name == display_name && + other.capabilities == capabilities; +} + } // catalog diff --git a/mojo/services/catalog/entry.h b/mojo/services/catalog/entry.h index 4f2f64a..4b7ffa8 100644 --- a/mojo/services/catalog/entry.h +++ b/mojo/services/catalog/entry.h @@ -5,17 +5,11 @@ #ifndef MOJO_SERVICES_CATALOG_ENTRY_H_ #define MOJO_SERVICES_CATALOG_ENTRY_H_ -#include <map> -#include <set> #include <string> -namespace catalog { +#include "mojo/shell/public/cpp/capabilities.h" -// A set of names of interfaces that may be exposed to an application. -using AllowedInterfaces = std::set<std::string>; -// A map of allowed applications to allowed interface sets. See shell.mojom for -// more details. -using CapabilityFilter = std::map<std::string, AllowedInterfaces>; +namespace catalog { // Static information about an application package known to the Catalog. struct Entry { @@ -23,10 +17,12 @@ struct Entry { Entry(const Entry& other); ~Entry(); + bool operator==(const Entry& other) const; + std::string name; std::string qualifier; std::string display_name; - CapabilityFilter capabilities; + mojo::CapabilitySpec capabilities; }; } // namespace catalog diff --git a/mojo/services/catalog/store.cc b/mojo/services/catalog/store.cc index 3bce188..c48aa72 100644 --- a/mojo/services/catalog/store.cc +++ b/mojo/services/catalog/store.cc @@ -7,6 +7,8 @@ namespace catalog { // static +const char Store::kManifestVersionKey[] = "manifest_version"; +// static const char Store::kNameKey[] = "name"; // static const char Store::kQualifierKey[] = "process-group"; @@ -14,5 +16,13 @@ const char Store::kQualifierKey[] = "process-group"; const char Store::kDisplayNameKey[] = "display_name"; // static const char Store::kCapabilitiesKey[] = "capabilities"; +// static +const char Store::kCapabilities_ProvidedKey[] = "provided"; +// static +const char Store::kCapabilities_RequiredKey[] = "required"; +// static +const char Store::kCapabilities_ClassesKey[] = "classes"; +// static +const char Store::kCapabilities_InterfacesKey[] = "interfaces"; } // namespace catalog diff --git a/mojo/services/catalog/store.h b/mojo/services/catalog/store.h index 74309b6..cb763d5 100644 --- a/mojo/services/catalog/store.h +++ b/mojo/services/catalog/store.h @@ -14,15 +14,24 @@ namespace catalog { // contents of the store, so no one else must modify its contents. class Store { public: + // Value is an integer. + static const char kManifestVersionKey[]; // Value is a string. static const char kNameKey[]; // Value is a string. static const char kQualifierKey[]; // Value is a string. static const char kDisplayNameKey[]; - // Value is a dictionary that maps from the filter to a list of string - // interfaces. + // Value is a dictionary. static const char kCapabilitiesKey[]; + // Value is a dictionary. + static const char kCapabilities_ProvidedKey[]; + // Value is a dictionary. + static const char kCapabilities_RequiredKey[]; + // Value is a list. + static const char kCapabilities_ClassesKey[]; + // Value is a list. + static const char kCapabilities_InterfacesKey[]; virtual ~Store() {} diff --git a/mojo/shell/public/cpp/BUILD.gn b/mojo/shell/public/cpp/BUILD.gn index b1f0345..53318b1 100644 --- a/mojo/shell/public/cpp/BUILD.gn +++ b/mojo/shell/public/cpp/BUILD.gn @@ -21,6 +21,7 @@ source_set("cpp_for_chromium") { source_set("sources") { sources = [ "application_runner.h", + "capabilities.h", "connect.h", "connection.h", "connector.h", @@ -31,6 +32,7 @@ source_set("sources") { "interface_factory_impl.h", "interface_registry.h", "lib/application_runner.cc", + "lib/capabilities.cc", "lib/connection_impl.cc", "lib/connection_impl.h", "lib/connector_impl.cc", diff --git a/mojo/shell/public/cpp/capabilities.h b/mojo/shell/public/cpp/capabilities.h new file mode 100644 index 0000000..39bc387 --- /dev/null +++ b/mojo/shell/public/cpp/capabilities.h @@ -0,0 +1,65 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_SHELL_PUBLIC_CPP_CAPABILITIES_H_ +#define MOJO_SHELL_PUBLIC_CPP_CAPABILITIES_H_ + +#include <map> +#include <set> +#include <string> + +#include "mojo/shell/public/interfaces/shell_resolver.mojom.h" + +namespace mojo { +using Class = std::string; +using Classes = std::set<std::string>; +using Interface = std::string; +using Interfaces = std::set<std::string>; +using Name = std::string; + +// See comments in mojo/shell/public/interfaces/capabilities.mojom for a +// description of CapabilityRequest and CapabilitySpec. + +struct CapabilityRequest { + CapabilityRequest(); + ~CapabilityRequest(); + bool operator==(const CapabilityRequest& other) const; + Classes classes; + Interfaces interfaces; +}; + +struct CapabilitySpec { + CapabilitySpec(); + ~CapabilitySpec(); + bool operator==(const CapabilitySpec& other) const; + std::map<Class, Interfaces> provided; + std::map<Name, CapabilityRequest> required; +}; + +template <> +struct TypeConverter<shell::mojom::CapabilitySpecPtr, CapabilitySpec> { + static shell::mojom::CapabilitySpecPtr Convert( + const CapabilitySpec& input); +}; +template <> +struct TypeConverter<CapabilitySpec, shell::mojom::CapabilitySpecPtr> { + static CapabilitySpec Convert( + const shell::mojom::CapabilitySpecPtr& input); +}; + +template <> +struct TypeConverter<shell::mojom::CapabilityRequestPtr, + CapabilityRequest> { + static shell::mojom::CapabilityRequestPtr Convert( + const CapabilityRequest& input); +}; +template <> +struct TypeConverter<CapabilityRequest, + shell::mojom::CapabilityRequestPtr> { + static CapabilityRequest Convert( + const shell::mojom::CapabilityRequestPtr& input); +}; +} // namespace mojo + +#endif // MOJO_SHELL_PUBLIC_CPP_CAPABILITIES_H_ diff --git a/mojo/shell/public/cpp/lib/application_test_base.cc b/mojo/shell/public/cpp/lib/application_test_base.cc index dcd7dc3..c895a24 100644 --- a/mojo/shell/public/cpp/lib/application_test_base.cc +++ b/mojo/shell/public/cpp/lib/application_test_base.cc @@ -57,7 +57,7 @@ class ShellGrabber : public shell::mojom::ShellClient { uint32_t source_id, shell::mojom::InterfaceProviderRequest local_interfaces, shell::mojom::InterfaceProviderPtr remote_interfaces, - Array<String> allowed_interfaces, + shell::mojom::CapabilityRequestPtr capability_spec, const String& name) override { CHECK(false); } diff --git a/mojo/shell/public/cpp/lib/capabilities.cc b/mojo/shell/public/cpp/lib/capabilities.cc new file mode 100644 index 0000000..cc3fc18 --- /dev/null +++ b/mojo/shell/public/cpp/lib/capabilities.cc @@ -0,0 +1,70 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/shell/public/cpp/capabilities.h" + +namespace mojo { + +CapabilityRequest::CapabilityRequest() {} +CapabilityRequest::~CapabilityRequest() {} + +bool CapabilityRequest::operator==(const CapabilityRequest& other) const { + return other.classes == classes && other.interfaces == interfaces; +} + +CapabilitySpec::CapabilitySpec() {} +CapabilitySpec::~CapabilitySpec() {} + +bool CapabilitySpec::operator==(const CapabilitySpec& other) const { + return other.provided == provided && other.required == required; +} + +// static +shell::mojom::CapabilitySpecPtr +TypeConverter<shell::mojom::CapabilitySpecPtr, CapabilitySpec>::Convert( + const CapabilitySpec& input) { + shell::mojom::CapabilitySpecPtr spec(shell::mojom::CapabilitySpec::New()); + spec->provided = + mojo::Map<mojo::String, mojo::Array<mojo::String>>::From(input.provided); + spec->required = + mojo::Map<mojo::String, shell::mojom::CapabilityRequestPtr>::From( + input.required); + return spec; +} + +// static +CapabilitySpec +TypeConverter<CapabilitySpec, shell::mojom::CapabilitySpecPtr>::Convert( + const shell::mojom::CapabilitySpecPtr& input) { + CapabilitySpec spec; + spec.provided = input->provided.To<std::map<Class, Interfaces>>(); + spec.required = + input->required.To<std::map<Name, CapabilityRequest>>(); + return spec; +} + +// static +shell::mojom::CapabilityRequestPtr +TypeConverter<shell::mojom::CapabilityRequestPtr, + CapabilityRequest>::Convert( + const CapabilityRequest& input) { + shell::mojom::CapabilityRequestPtr request( + shell::mojom::CapabilityRequest::New()); + request->classes = mojo::Array<mojo::String>::From(input.classes); + request->interfaces = mojo::Array<mojo::String>::From(input.interfaces); + return request; +} + +// static +CapabilityRequest +TypeConverter<CapabilityRequest, + shell::mojom::CapabilityRequestPtr>::Convert( + const shell::mojom::CapabilityRequestPtr& input) { + CapabilityRequest request; + request.classes = input->classes.To<std::set<std::string>>(); + request.interfaces = input->interfaces.To<std::set<std::string>>(); + return request; +} + +} // namespace mojo diff --git a/mojo/shell/public/cpp/lib/shell_connection.cc b/mojo/shell/public/cpp/lib/shell_connection.cc index 8ee729d..2e2a7ec 100644 --- a/mojo/shell/public/cpp/lib/shell_connection.cc +++ b/mojo/shell/public/cpp/lib/shell_connection.cc @@ -6,6 +6,7 @@ #include "base/logging.h" #include "base/message_loop/message_loop.h" #include "mojo/public/cpp/bindings/interface_ptr.h" +#include "mojo/shell/public/cpp/capabilities.h" #include "mojo/shell/public/cpp/connector.h" #include "mojo/shell/public/cpp/lib/connection_impl.h" #include "mojo/shell/public/cpp/lib/connector_impl.h" @@ -49,12 +50,12 @@ void ShellConnection::AcceptConnection( uint32_t source_id, shell::mojom::InterfaceProviderRequest local_interfaces, shell::mojom::InterfaceProviderPtr remote_interfaces, - Array<String> allowed_interfaces, + shell::mojom::CapabilityRequestPtr allowed_capabilities, const String& name) { scoped_ptr<Connection> registry(new internal::ConnectionImpl( name, source.To<Identity>(), source_id, std::move(remote_interfaces), std::move(local_interfaces), - allowed_interfaces.To<std::set<std::string>>(), + allowed_capabilities->interfaces.To<std::set<std::string>>(), Connection::State::CONNECTED)); if (!client_->AcceptConnection(registry.get())) return; diff --git a/mojo/shell/public/cpp/shell_connection.h b/mojo/shell/public/cpp/shell_connection.h index 7ec245b..e0b1eb1 100644 --- a/mojo/shell/public/cpp/shell_connection.h +++ b/mojo/shell/public/cpp/shell_connection.h @@ -66,7 +66,7 @@ class ShellConnection : public shell::mojom::ShellClient { uint32_t source_id, shell::mojom::InterfaceProviderRequest remote_interfaces, shell::mojom::InterfaceProviderPtr local_interfaces, - Array<String> allowed_interfaces, + shell::mojom::CapabilityRequestPtr allowed_capabilities, const String& name) override; void OnConnectionError(); diff --git a/mojo/shell/public/interfaces/BUILD.gn b/mojo/shell/public/interfaces/BUILD.gn index 683e502..0eeac14 100644 --- a/mojo/shell/public/interfaces/BUILD.gn +++ b/mojo/shell/public/interfaces/BUILD.gn @@ -7,6 +7,7 @@ import("//mojo/public/tools/bindings/mojom.gni") # GYP version: mojo/mojo_base.gyp:mojo_application_bindings mojom("interfaces") { sources = [ + "capabilities.mojom", "connector.mojom", "interface_provider.mojom", "shell.mojom", diff --git a/mojo/shell/public/interfaces/capabilities.mojom b/mojo/shell/public/interfaces/capabilities.mojom new file mode 100644 index 0000000..563b88c --- /dev/null +++ b/mojo/shell/public/interfaces/capabilities.mojom @@ -0,0 +1,47 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module mojo.shell.mojom; + +// Mojo Capabilities ----------------------------------------------------------- +// +// Mojo applications expose interfaces and capability classes to one another. +// +// An interface is just a Mojo interface, defined in a mojom file like this one. +// In a CapabilitySpec, an interface is represented by its fully qualified name, +// which is serialized based on the interface name and module: +// module::path::InterfaceName. +// +// A class is an alias to something, either a set of interface names granted +// with that class, or some behavior specific to the application that provides +// it. + +// Describes the set of classes and interfaces required by an application. +// Note that there may be overlap between the interfaces implied by the +// resolution of classes and those specified in |interfaces|. The set of +// interfaces granted to the requestor is the union of these sets. +struct CapabilityRequest { + // An array of class names required. + array<string> classes; + // An array of interface names required. + array<string> interfaces; +}; + +// Describes the capabilities offered and requested by an application. +// This struct is populated from the application manifest. +struct CapabilitySpec { + // The classes offered by this application, and for each class an array of + // interfaces. If no interfaces are granted with a class, the array will be + // empty. + // A map of class name -> array of interfaces. The array can be empty, + // non-empty, or ["*"], which means allow access to all interfaces. + map<string, array<string>> provided; + + // The applications this application needs to speak to, and the classes and + // interfaces it requests. + // A map of application name -> spec. "*" is also supported as the key, which + // supplies a CapabilityRequest for all applications in addition to specific + // ones specified. + map<string, CapabilityRequest> required; +}; diff --git a/mojo/shell/public/interfaces/shell.mojom b/mojo/shell/public/interfaces/shell.mojom index 5e0469f..04dcd1d 100644 --- a/mojo/shell/public/interfaces/shell.mojom +++ b/mojo/shell/public/interfaces/shell.mojom @@ -5,7 +5,6 @@ module mojo.shell.mojom; import "mojo/shell/public/interfaces/connector.mojom"; -import "mojo/shell/public/interfaces/shell_client_factory.mojom"; struct InstanceInfo { uint32 id; diff --git a/mojo/shell/public/interfaces/shell_client.mojom b/mojo/shell/public/interfaces/shell_client.mojom index d6a223e..260bb2c 100644 --- a/mojo/shell/public/interfaces/shell_client.mojom +++ b/mojo/shell/public/interfaces/shell_client.mojom @@ -4,6 +4,7 @@ module mojo.shell.mojom; +import "mojo/shell/public/interfaces/capabilities.mojom"; import "mojo/shell/public/interfaces/connector.mojom"; import "mojo/shell/public/interfaces/interface_provider.mojom"; @@ -69,6 +70,6 @@ interface ShellClient { uint32 source_id, InterfaceProvider&? local_interfaces, InterfaceProvider? remote_interfaces, - array<string> allowed_interfaces, + CapabilityRequest allowed_capabilities, string resolved_name); }; diff --git a/mojo/shell/public/interfaces/shell_resolver.mojom b/mojo/shell/public/interfaces/shell_resolver.mojom index b5e6d29..090bf83 100644 --- a/mojo/shell/public/interfaces/shell_resolver.mojom +++ b/mojo/shell/public/interfaces/shell_resolver.mojom @@ -4,19 +4,7 @@ module mojo.shell.mojom; -// Specifies a whitelist of applications and services an application can connect -// to. Connections to applications not explicitly specified here as a key are -// rejected. Connections to services not specified in an application's allowed -// interfaces value are not made. -// A "*" value as the only key in an otherwise empty map means the application -// may connect to any other application. -// A "*" value as the only string in an otherwise empty array of interface names -// means the application may connect to any service in that application. -// An empty interface name array means the application may not connect to any -// services exposed by the application it is connecting to. -struct CapabilityFilter { - map<string, array<string>> filter; -}; +import "mojo/shell/public/interfaces/capabilities.mojom"; // Implemented exclusively for the Mojo Shell's use in resolving mojo: names // and reading static manifest information. @@ -46,6 +34,6 @@ interface ShellResolver { ResolveMojoName(string mojo_name) => (string resolved_mojo_name, string qualifier, - CapabilityFilter? filter, + CapabilitySpec? capability_spec, string? mojo_file_url); }; diff --git a/mojo/shell/shell.cc b/mojo/shell/shell.cc index 8dac715..1daa93a 100644 --- a/mojo/shell/shell.cc +++ b/mojo/shell/shell.cc @@ -38,7 +38,7 @@ const char kCatalogName[] = "mojo:catalog"; void EmptyResolverCallback(const String& resolved_name, const String& resolved_instance, - mojom::CapabilityFilterPtr base_filter, + mojom::CapabilitySpecPtr capabilities, const String& file_url) {} } @@ -47,28 +47,28 @@ Identity CreateShellIdentity() { return Identity("mojo:shell", mojom::kRootUserID); } -CapabilityFilter GetPermissiveCapabilityFilter() { - CapabilityFilter filter; - AllowedInterfaces interfaces; - interfaces.insert("*"); - filter["*"] = interfaces; - return filter; +CapabilitySpec GetPermissiveCapabilities() { + CapabilitySpec capabilities; + CapabilityRequest spec; + spec.interfaces.insert("*"); + capabilities.required["*"] = spec; + return capabilities; } -AllowedInterfaces GetAllowedInterfaces(const CapabilityFilter& filter, - const Identity& identity) { - // Start by looking for interfaces specific to the supplied identity. - auto it = filter.find(identity.name()); - if (it != filter.end()) +CapabilityRequest GetCapabilityRequest(const CapabilitySpec& spec, + const Identity& identity) { + // Start by looking for specs specific to the supplied identity. + auto it = spec.required.find(identity.name()); + if (it != spec.required.end()) return it->second; // Fall back to looking for a wildcard rule. - it = filter.find("*"); - if (filter.size() == 1 && it != filter.end()) + it = spec.required.find("*"); + if (spec.required.size() == 1 && it != spec.required.end()) return it->second; // Finally, nothing is allowed. - return AllowedInterfaces(); + return CapabilityRequest(); } // Encapsulates a connection to an instance of an application, tracked by the @@ -82,12 +82,13 @@ class Shell::Instance : public mojom::Connector, Instance(mojom::ShellClientPtr shell_client, mojo::shell::Shell* shell, const Identity& identity, - const CapabilityFilter& filter) + const CapabilitySpec& capabilities) : shell_(shell), id_(GenerateUniqueID()), identity_(identity), - filter_(filter), - allow_any_application_(filter.size() == 1 && filter.count("*") == 1), + capabilities_(capabilities), + allow_any_application_(capabilities.required.size() == 1 && + capabilities.required.count("*") == 1), shell_client_(std::move(shell_client)), pid_receiver_binding_(this), weak_factory_(this) { @@ -112,17 +113,17 @@ class Shell::Instance : public mojom::Connector, params->connect_callback().Run(mojom::ConnectResult::SUCCEEDED, identity_.user_id(), id_); uint32_t source_id = mojom::kInvalidInstanceID; - AllowedInterfaces interfaces; - interfaces.insert("*"); + CapabilityRequest spec; + spec.interfaces.insert("*"); Instance* source = shell_->GetExistingInstance(params->source()); if (source) { - interfaces = GetAllowedInterfaces(source->filter_, identity_); + spec = GetCapabilityRequest(source->capabilities_, identity_); source_id = source->id(); } shell_client_->AcceptConnection( mojom::Identity::From(params->source()), source_id, params->TakeRemoteInterfaces(), params->TakeLocalInterfaces(), - Array<String>::From(interfaces), params->target().name()); + mojom::CapabilityRequest::From(spec), params->target().name()); } void StartWithClientProcessConnection( @@ -185,7 +186,7 @@ class Shell::Instance : public mojom::Connector, // - a user id other than its own, kInheritUserID or kRootUserID. // - a non-empty instance name. // - a non-null client_process_connection. - if (!ValidateCapabilityFilter(target, callback)) + if (!ValidateCapabilities(target, callback)) return; scoped_ptr<ConnectParams> params(new ConnectParams); @@ -263,13 +264,14 @@ class Shell::Instance : public mojom::Connector, return true; } - bool ValidateCapabilityFilter(const Identity& target, - const ConnectCallback& callback) { + bool ValidateCapabilities(const Identity& target, + const ConnectCallback& callback) { if (allow_any_application_ || - filter_.find(target.name()) != filter_.end()) { + capabilities_.required.find(target.name()) != + capabilities_.required.end()) { return true; } - LOG(ERROR) << "CapabilityFilter prevented connection from: " << + LOG(ERROR) << "Capabilities prevented connection from: " << identity_.name() << " to: " << target.name(); callback.Run(mojom::ConnectResult::ACCESS_DENIED, mojom::kInheritUserID, mojom::kInvalidInstanceID); @@ -294,7 +296,7 @@ class Shell::Instance : public mojom::Connector, // process is launched. const uint32_t id_; const Identity identity_; - const CapabilityFilter filter_; + const CapabilitySpec capabilities_; const bool allow_any_application_; mojom::ShellClientPtr shell_client_; Binding<mojom::PIDReceiver> pid_receiver_binding_; @@ -330,8 +332,7 @@ Shell::Shell(scoped_ptr<NativeRunnerFactory> native_runner_factory, native_runner_factory_(std::move(native_runner_factory)), weak_ptr_factory_(this) { mojom::ShellClientRequest request; - CreateInstance(CreateShellIdentity(), GetPermissiveCapabilityFilter(), - &request); + CreateInstance(CreateShellIdentity(), GetPermissiveCapabilities(), &request); shell_connection_.reset(new ShellConnection(this, std::move(request))); InitCatalog(std::move(catalog_store)); @@ -385,8 +386,8 @@ mojom::ShellClientRequest Shell::InitInstanceForEmbedder( DCHECK(!GetExistingInstance(target)); mojom::ShellClientRequest request; - embedder_instance_ = CreateInstance( - target, GetPermissiveCapabilityFilter(), &request); + embedder_instance_ = + CreateInstance(target, GetPermissiveCapabilities(), &request); DCHECK(embedder_instance_); return request; @@ -433,7 +434,7 @@ void Shell::InitCatalog(scoped_ptr<catalog::Store> store) { // TODO(beng): Does the catalog actually have to be run with a permissive // filter? Identity identity(name, mojom::kRootUserID); - CreateInstance(identity, GetPermissiveCapabilityFilter(), &request); + CreateInstance(identity, GetPermissiveCapabilities(), &request); loader_raw->Load(name, std::move(request)); ConnectToInterface(this, CreateShellIdentity(), name, &shell_resolver_); @@ -495,13 +496,13 @@ bool Shell::ConnectToExistingInstance(scoped_ptr<ConnectParams>* params) { } Shell::Instance* Shell::CreateInstance(const Identity& target_id, - const CapabilityFilter& filter, + const CapabilitySpec& capabilities, mojom::ShellClientRequest* request) { CHECK(target_id.user_id() != mojom::kInheritUserID); mojom::ShellClientPtr shell_client; *request = GetProxy(&shell_client); Instance* instance = - new Instance(std::move(shell_client), this, target_id, filter); + new Instance(std::move(shell_client), this, target_id, capabilities); DCHECK(identity_to_instance_.find(target_id) == identity_to_instance_.end()); identity_to_instance_[target_id] = instance; @@ -563,7 +564,7 @@ void Shell::OnShellClientFactoryLost(const Identity& which) { void Shell::OnGotResolvedName(scoped_ptr<ConnectParams> params, const String& resolved_name, const String& resolved_instance, - mojom::CapabilityFilterPtr base_filter, + mojom::CapabilitySpecPtr capabilities_ptr, const String& file_url) { std::string instance_name = params->target().instance(); if (instance_name == GetNamePath(params->target().name()) && @@ -581,22 +582,22 @@ void Shell::OnGotResolvedName(scoped_ptr<ConnectParams> params, return; Identity source = params->source(); - // |base_filter| can be null when there is no manifest, e.g. for URL types - // not resolvable by the resolver. - CapabilityFilter filter = GetPermissiveCapabilityFilter(); - if (!base_filter.is_null()) - filter = base_filter->filter.To<CapabilityFilter>(); + // |capabilities_ptr| can be null when there is no manifest, e.g. for URL + // types not resolvable by the resolver. + CapabilitySpec capabilities = GetPermissiveCapabilities(); + if (!capabilities_ptr.is_null()) + capabilities = capabilities_ptr.To<CapabilitySpec>(); mojom::ClientProcessConnectionPtr client_process_connection = params->TakeClientProcessConnection(); mojom::ShellClientRequest request; - Instance* instance = CreateInstance(target, filter, &request); + Instance* instance = CreateInstance(target, capabilities, &request); instance->ConnectToClient(std::move(params)); if (LoadWithLoader(target, &request)) return; - CHECK(!file_url.is_null() && !base_filter.is_null()); + CHECK(!file_url.is_null() && !capabilities_ptr.is_null()); if (target.name() != resolved_name) { // In cases where a package alias is resolved, we have to use the instance diff --git a/mojo/shell/shell.h b/mojo/shell/shell.h index 7169ec1..2d66593 100644 --- a/mojo/shell/shell.h +++ b/mojo/shell/shell.h @@ -17,6 +17,7 @@ #include "mojo/shell/connect_params.h" #include "mojo/shell/loader.h" #include "mojo/shell/native_runner.h" +#include "mojo/shell/public/cpp/capabilities.h" #include "mojo/shell/public/cpp/identity.h" #include "mojo/shell/public/cpp/interface_factory.h" #include "mojo/shell/public/cpp/shell_client.h" @@ -36,25 +37,10 @@ namespace mojo { class ShellConnection; namespace shell { -// A set of names of interfaces that may be exposed to an application. -using AllowedInterfaces = std::set<std::string>; -// A map of allowed applications to allowed interface sets. See shell.mojom for -// more details. -using CapabilityFilter = std::map<std::string, AllowedInterfaces>; - // Creates an identity for the Shell, used when the Shell connects to // applications. Identity CreateShellIdentity(); -// Returns a capability filter that allows an application to connect to any -// other application and any service exposed by other applications. -CapabilityFilter GetPermissiveCapabilityFilter(); - -// Returns the set of interfaces that an application instance with |filter| is -// allowed to see from an instance with |identity|. -AllowedInterfaces GetAllowedInterfaces(const CapabilityFilter& filter, - const Identity& identity); - class Shell : public ShellClient { public: // API for testing. @@ -141,17 +127,9 @@ class Shell : public ShellClient { bool ConnectToExistingInstance(scoped_ptr<ConnectParams>* params); Instance* CreateInstance(const Identity& target_id, - const CapabilityFilter& filter, + const CapabilitySpec& capabilities, mojom::ShellClientRequest* request); - // Called from the instance implementing mojom::Shell. |user_id| must be - // resolved by the instance (i.e. must not be mojom::kInheritUserID). - void CreateInstanceForFactory( - mojom::ShellClientFactoryPtr factory, - const std::string& name, - const std::string& user_id, - mojom::CapabilityFilterPtr filter, - mojom::PIDReceiverRequest pid_receiver); // Called from the instance implementing mojom::Shell. void AddInstanceListener(mojom::InstanceListenerPtr listener); @@ -171,12 +149,12 @@ class Shell : public ShellClient { // |resolved_name| is the mojo: name identifying the physical package // application. // |file_url| is the resolved file:// URL of the physical package. - // |base_filter| is the CapabilityFilter the requested application should be + // |base_filter| is the CapabilitySpecPtr the requested application should be // run with, from its manifest. void OnGotResolvedName(scoped_ptr<ConnectParams> params, const String& resolved_name, const String& resolved_instance, - mojom::CapabilityFilterPtr base_filter, + mojom::CapabilitySpecPtr capabilities, const String& file_url); // Tries to load |target| with an Loader. Returns true if one was registered diff --git a/mojo/shell/tests/connect/connect_test_app_manifest.json b/mojo/shell/tests/connect/connect_test_app_manifest.json index 39099fb..ae25a53 100644 --- a/mojo/shell/tests/connect/connect_test_app_manifest.json +++ b/mojo/shell/tests/connect/connect_test_app_manifest.json @@ -1,7 +1,10 @@ { + "manifest_version": 1, "name": "mojo:connect_test_app", "display_name": "Connect Test App", "capabilities": { - "mojo:connect_test_a": ["*"] + "required": { + "mojo:connect_test_a": { "interfaces": ["*"] } + } } } |