diff options
author | reddaly <reddaly@chromium.org> | 2015-03-27 16:18:25 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-03-27 23:19:03 +0000 |
commit | 68ea490084597d5d4640e782989c0a6a094dcd21 (patch) | |
tree | 4bc54f7d81b00de793c9a1ed804813d8f294558f /chrome/browser/extensions/api | |
parent | 5f681cc59082ad1e8a05f6921595661e6dc1bdf9 (diff) | |
download | chromium_src-68ea490084597d5d4640e782989c0a6a094dcd21.zip chromium_src-68ea490084597d5d4640e782989c0a6a094dcd21.tar.gz chromium_src-68ea490084597d5d4640e782989c0a6a094dcd21.tar.bz2 |
Allows all packaged apps to use the chrome.mdns API, and removes whitelist that restricts the set of service type that can be discovered.
R=mkwst@chromium.org,mfoltz@chromium.org
BUG=426500
Review URL: https://codereview.chromium.org/668983003
Cr-Commit-Position: refs/heads/master@{#322671}
Diffstat (limited to 'chrome/browser/extensions/api')
-rw-r--r-- | chrome/browser/extensions/api/mdns/mdns_api.cc | 40 | ||||
-rw-r--r-- | chrome/browser/extensions/api/mdns/mdns_api_unittest.cc | 217 | ||||
-rw-r--r-- | chrome/browser/extensions/api/mdns/mdns_apitest.cc | 2 |
3 files changed, 245 insertions, 14 deletions
diff --git a/chrome/browser/extensions/api/mdns/mdns_api.cc b/chrome/browser/extensions/api/mdns/mdns_api.cc index 9bc7fb3..2a7c41d 100644 --- a/chrome/browser/extensions/api/mdns/mdns_api.cc +++ b/chrome/browser/extensions/api/mdns/mdns_api.cc @@ -9,6 +9,7 @@ #include "base/lazy_instance.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/common/extensions/api/mdns.h" +#include "extensions/browser/extension_registry.h" namespace extensions { @@ -31,8 +32,9 @@ bool IsServiceTypeWhitelisted(const std::string& service_type) { MDnsAPI::MDnsAPI(content::BrowserContext* context) : browser_context_(context) { DCHECK(browser_context_); - EventRouter::Get(context) - ->RegisterObserver(this, mdns::OnServiceList::kEventName); + extensions::EventRouter* event_router = EventRouter::Get(context); + DCHECK(event_router); + event_router->RegisterObserver(this, mdns::OnServiceList::kEventName); } MDnsAPI::~MDnsAPI() { @@ -57,6 +59,8 @@ BrowserContextKeyedAPIFactory<MDnsAPI>* MDnsAPI::GetFactoryInstance() { void MDnsAPI::SetDnsSdRegistryForTesting( scoped_ptr<DnsSdRegistry> dns_sd_registry) { dns_sd_registry_ = dns_sd_registry.Pass(); + if (dns_sd_registry_.get()) + dns_sd_registry_.get()->AddObserver(this); } DnsSdRegistry* MDnsAPI::dns_sd_registry() { @@ -81,7 +85,7 @@ void MDnsAPI::OnListenerRemoved(const EventListenerInfo& details) { void MDnsAPI::UpdateMDnsListeners(const EventListenerInfo& details) { std::set<std::string> new_service_types; - // Check all listeners for service type filers. + // Check all listeners for service type filters. const EventListenerMap::ListenerList& listeners = extensions::EventRouter::Get(browser_context_) ->listeners() @@ -94,6 +98,19 @@ void MDnsAPI::UpdateMDnsListeners(const EventListenerInfo& details) { filter->GetStringASCII(kEventFilterServiceTypeKey, &filter_value); if (filter_value.empty()) continue; + + const Extension* extension = ExtensionRegistry::Get(browser_context_)-> + enabled_extensions().GetByID((*it)->extension_id()); + // Don't listen for services associated only with disabled extensions. + if (!extension) + continue; + + // Platform apps may query for all services; other types of extensions are + // restricted to a whitelist. + if (!extension->is_platform_app() && + !IsServiceTypeWhitelisted(filter_value)) + continue; + new_service_types.insert(filter_value); } @@ -107,17 +124,12 @@ void MDnsAPI::UpdateMDnsListeners(const EventListenerInfo& details) { // Update the registry. DnsSdRegistry* registry = dns_sd_registry(); - for (std::set<std::string>::iterator it = added_service_types.begin(); - it != added_service_types.end(); ++it) { - if (IsServiceTypeWhitelisted(*it)) - registry->RegisterDnsSdListener(*it); + for (const auto& srv : added_service_types) { + registry->RegisterDnsSdListener(srv); } - for (std::set<std::string>::iterator it = removed_service_types.begin(); - it != removed_service_types.end(); ++it) { - if (IsServiceTypeWhitelisted(*it)) - registry->UnregisterDnsSdListener(*it); + for (const auto& srv : removed_service_types) { + registry->UnregisterDnsSdListener(srv); } - service_types_ = new_service_types; } @@ -143,10 +155,10 @@ void MDnsAPI::OnDnsSdEvent(const std::string& service_type, event->restrict_to_browser_context = browser_context_; event->filter_info.SetServiceType(service_type); - VLOG(1) << "Broadcasting OnServiceList event: " << event.get(); - // TODO(justinlin): To avoid having listeners without filters getting all // events, modify API to have this event require filters. + // TODO(reddaly): If event isn't on whitelist, ensure it does not get + // broadcast to extensions. extensions::EventRouter::Get(browser_context_)->BroadcastEvent(event.Pass()); } diff --git a/chrome/browser/extensions/api/mdns/mdns_api_unittest.cc b/chrome/browser/extensions/api/mdns/mdns_api_unittest.cc new file mode 100644 index 0000000..ede2a2b --- /dev/null +++ b/chrome/browser/extensions/api/mdns/mdns_api_unittest.cc @@ -0,0 +1,217 @@ +// Copyright 2015 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 "chrome/browser/extensions/api/mdns/mdns_api.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_service_test_base.h" +#include "chrome/browser/extensions/test_extension_system.h" +#include "chrome/common/extensions/api/mdns.h" +#include "content/public/browser/browser_context.h" +#include "content/public/test/mock_render_process_host.h" +#include "extensions/browser/extension_prefs_factory.h" +#include "extensions/browser/extension_registry.h" +#include "extensions/common/manifest_constants.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace extensions { + +namespace { + +KeyedService* MDnsAPITestingFactoryFunction(content::BrowserContext* context) { + return new MDnsAPI(context); +} + +// For ExtensionService interface when it requires a path that is not used. +base::FilePath bogus_file_pathname(const std::string& name) { + return base::FilePath(FILE_PATH_LITERAL("//foobar_nonexistent")) + .AppendASCII(name); +} + +class MockDnsSdRegistry : public DnsSdRegistry { + public: + explicit MockDnsSdRegistry(extensions::MDnsAPI* api) : api_(api) {} + virtual ~MockDnsSdRegistry() {} + + MOCK_METHOD1(AddObserver, void(DnsSdObserver* observer)); + MOCK_METHOD1(RemoveObserver, void(DnsSdObserver* observer)); + MOCK_METHOD1(RegisterDnsSdListener, void(std::string service_type)); + MOCK_METHOD1(UnregisterDnsSdListener, void(std::string service_type)); + + void DispatchMDnsEvent(const std::string& service_type, + const DnsSdServiceList& services) { + api_->OnDnsSdEvent(service_type, services); + } + + private: + extensions::DnsSdRegistry::DnsSdObserver* api_; +}; + +class MDnsAPITest : public extensions::ExtensionServiceTestBase { + public: + void SetUp() override { + extensions::ExtensionServiceTestBase::SetUp(); + + // Set up browser_context(). + InitializeEmptyExtensionService(); + + // A custom TestingFactoryFunction is required for an MDnsAPI to actually be + // constructed. + MDnsAPI::GetFactoryInstance()->SetTestingFactory( + browser_context(), + MDnsAPITestingFactoryFunction); + + // Create an event router and associate it with the context. + extensions::EventRouter* event_router = new extensions::EventRouter( + browser_context(), + ExtensionPrefsFactory::GetInstance()->GetForBrowserContext( + browser_context())); + static_cast<TestExtensionSystem*>( + ExtensionSystem::Get(browser_context()))->SetEventRouter( + scoped_ptr<extensions::EventRouter>(event_router)); + + // Do some sanity checking + ASSERT_EQ(event_router, EventRouter::Get(browser_context())); + ASSERT_TRUE(MDnsAPI::Get(browser_context())); // constructs MDnsAPI + + registry_ = new MockDnsSdRegistry(MDnsAPI::Get(browser_context())); + EXPECT_CALL(*dns_sd_registry(), + AddObserver(MDnsAPI::Get(browser_context()))) + .Times(1); + MDnsAPI::Get(browser_context())->SetDnsSdRegistryForTesting( + scoped_ptr<DnsSdRegistry>(registry_)); + + render_process_host_.reset( + new content::MockRenderProcessHost(browser_context())); + } + + void TearDown() override { + EXPECT_CALL(*dns_sd_registry(), + RemoveObserver(MDnsAPI::Get(browser_context()))) + .Times(1); + render_process_host_.reset(); + extensions::ExtensionServiceTestBase::TearDown(); + MDnsAPI::GetFactoryInstance()->SetTestingFactory( + browser_context(), + nullptr); + + registry_ = nullptr; + } + + virtual MockDnsSdRegistry* dns_sd_registry() { + return registry_; + } + + // Constructs an extension according to the parameters that matter most to + // MDnsAPI the local unit tests. + const scoped_refptr<extensions::Extension> CreateExtension( + std::string name, + bool is_platform_app, + std::string extension_id) { + base::DictionaryValue manifest; + manifest.SetString(extensions::manifest_keys::kVersion, "1.0.0.0"); + manifest.SetString(extensions::manifest_keys::kName, name); + if (is_platform_app) { + // Setting app.background.page = "background.html" is sufficient to make + // the extension type TYPE_PLATFORM_APP. + manifest.Set(extensions::manifest_keys::kPlatformAppBackgroundPage, + new base::StringValue("background.html")); + } + + std::string error; + return extensions::Extension::Create( + bogus_file_pathname(name), + extensions::Manifest::INVALID_LOCATION, + manifest, + Extension::NO_FLAGS, + extension_id, + &error); + } + + content::RenderProcessHost* render_process_host() const { + return render_process_host_.get(); + } + + private: + // The registry is owned by MDnsAPI, but MDnsAPI does not have an accessor + // for it, so use a private member. + MockDnsSdRegistry* registry_; + + scoped_ptr<content::RenderProcessHost> render_process_host_; + +}; + +TEST_F(MDnsAPITest, ExtensionRespectsWhitelist) { + const std::string ext_id("mbflcebpggnecokmikipoihdbecnjfoj"); + scoped_refptr<extensions::Extension> extension = + CreateExtension("Dinosaur networker", false, ext_id); + ExtensionRegistry::Get(browser_context())->AddEnabled(extension); + ASSERT_EQ(Manifest::TYPE_EXTENSION, extension.get()->GetType()); + + // There is a whitelist of mdns service types extensions may access, which + // includes "_testing._tcp.local" and exludes "_trex._tcp.local" + { + base::DictionaryValue filter; + filter.SetString(kEventFilterServiceTypeKey, "_trex._tcp.local"); + + ASSERT_TRUE(dns_sd_registry()); + // Test that the extension is able to listen to a non-whitelisted service + EXPECT_CALL(*dns_sd_registry(), RegisterDnsSdListener("_trex._tcp.local")) + .Times(0); + EventRouter::Get(browser_context())->AddFilteredEventListener( + api::mdns::OnServiceList::kEventName, render_process_host(), ext_id, + filter, false); + + EXPECT_CALL(*dns_sd_registry(), UnregisterDnsSdListener("_trex._tcp.local")) + .Times(0); + EventRouter::Get(browser_context())->RemoveFilteredEventListener( + api::mdns::OnServiceList::kEventName, render_process_host(), ext_id, + filter, false); + } + { + base::DictionaryValue filter; + filter.SetString(kEventFilterServiceTypeKey, "_testing._tcp.local"); + + ASSERT_TRUE(dns_sd_registry()); + // Test that the extension is able to listen to a whitelisted service + EXPECT_CALL(*dns_sd_registry(), + RegisterDnsSdListener("_testing._tcp.local")); + EventRouter::Get(browser_context())->AddFilteredEventListener( + api::mdns::OnServiceList::kEventName, render_process_host(), ext_id, + filter, false); + + EXPECT_CALL(*dns_sd_registry(), + UnregisterDnsSdListener("_testing._tcp.local")); + EventRouter::Get(browser_context())->RemoveFilteredEventListener( + api::mdns::OnServiceList::kEventName, render_process_host(), + ext_id, filter, false); + } +} + +TEST_F(MDnsAPITest, PlatformAppsNotSubjectToWhitelist) { + const std::string ext_id("mbflcebpggnecokmikipoihdbecnjfoj"); + scoped_refptr<extensions::Extension> extension = + CreateExtension("Dinosaur networker", true, ext_id); + ExtensionRegistry::Get(browser_context())->AddEnabled(extension); + ASSERT_TRUE(extension.get()->is_platform_app()); + + base::DictionaryValue filter; + filter.SetString(kEventFilterServiceTypeKey, "_trex._tcp.local"); + + ASSERT_TRUE(dns_sd_registry()); + // Test that the extension is able to listen to a non-whitelisted service + EXPECT_CALL(*dns_sd_registry(), RegisterDnsSdListener("_trex._tcp.local")); + EventRouter::Get(browser_context())->AddFilteredEventListener( + api::mdns::OnServiceList::kEventName, render_process_host(), ext_id, + filter, false); + + EXPECT_CALL(*dns_sd_registry(), UnregisterDnsSdListener("_trex._tcp.local")); + EventRouter::Get(browser_context())->RemoveFilteredEventListener( + api::mdns::OnServiceList::kEventName, render_process_host(), ext_id, + filter, false); +} + +} // empty namespace + +} // namespace extensions diff --git a/chrome/browser/extensions/api/mdns/mdns_apitest.cc b/chrome/browser/extensions/api/mdns/mdns_apitest.cc index 5365518..61d10d3 100644 --- a/chrome/browser/extensions/api/mdns/mdns_apitest.cc +++ b/chrome/browser/extensions/api/mdns/mdns_apitest.cc @@ -52,6 +52,8 @@ class MDnsAPITest : public ExtensionApiTest { void SetUpTestDnsSdRegistry() { extensions::MDnsAPI* api = extensions::MDnsAPI::Get(profile()); dns_sd_registry_ = new MockDnsSdRegistry(api); + EXPECT_CALL(*dns_sd_registry_, AddObserver(api)) + .Times(1); // Transfers ownership of the registry instance. api->SetDnsSdRegistryForTesting( make_scoped_ptr<DnsSdRegistry>(dns_sd_registry_).Pass()); |