Chromium Code Reviews| Index: mojo/shell/capability_filter_unittest.cc |
| diff --git a/mojo/shell/capability_filter_unittest.cc b/mojo/shell/capability_filter_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..52126ce92860381a0bc5cf91678f6b33c9fe7d32 |
| --- /dev/null |
| +++ b/mojo/shell/capability_filter_unittest.cc |
| @@ -0,0 +1,381 @@ |
| +// Copyright 2014 The Chromium Authors. All rights reserved. |
|
sky
2015/07/22 15:57:45
2015
|
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "base/at_exit.h" |
| +#include "base/bind.h" |
| +#include "base/macros.h" |
| +#include "base/message_loop/message_loop.h" |
| +#include "base/strings/stringprintf.h" |
| +#include "mojo/application/public/cpp/application_connection.h" |
| +#include "mojo/application/public/cpp/application_delegate.h" |
| +#include "mojo/application/public/cpp/application_impl.h" |
| +#include "mojo/application/public/cpp/connect.h" |
| +#include "mojo/application/public/cpp/interface_factory.h" |
| +#include "mojo/common/weak_binding_set.h" |
| +#include "mojo/public/cpp/bindings/strong_binding.h" |
| +#include "mojo/shell/application_loader.h" |
| +#include "mojo/shell/application_manager.h" |
| +#include "mojo/shell/test.mojom.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| + |
| +namespace mojo { |
| +namespace shell { |
| +namespace { |
| + |
| +// Quits |loop| when either: |
| +// - all of the |valid_calls| are received, or |
| +// - one of the |invalid_calls| is received. |
| +class CallValidator { |
| + public: |
| + CallValidator(base::MessageLoop* loop, |
| + const std::set<std::string>& valid_calls, |
| + const std::set<std::string>& invalid_calls) |
| + : loop_(loop), |
| + valid_calls_(valid_calls), |
| + invalid_calls_(invalid_calls) { |
| + } |
| + |
| + static std::string FormatCall(const std::string& function, |
| + const std::string& service_url, |
| + const std::string& remote_url) { |
| + return base::StringPrintf("%s %s %s", function.c_str(), service_url.c_str(), |
| + remote_url.c_str()); |
| + } |
| + |
| + void Call(const std::string& function, |
| + const std::string& service_url, |
| + const std::string& remote_url) { |
| + std::string call = FormatCall(function, service_url, remote_url); |
| + { |
| + auto i = invalid_calls_.find(call); |
| + if (i != invalid_calls_.end()) { |
| + loop_->Quit(); |
| + return; |
| + } |
| + } |
| + |
| + { |
| + auto i = valid_calls_.find(call); |
| + if (i != valid_calls_.end()) |
| + valid_calls_.erase(i); |
| + if (valid_calls_.empty()) |
| + loop_->Quit(); |
| + } |
| + } |
| + |
| + bool called_all_valid() const { return valid_calls_.empty(); } |
| + |
| + private: |
| + base::MessageLoop* loop_; |
| + std::set<std::string> valid_calls_; |
| + std::set<std::string> invalid_calls_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(CallValidator); |
| +}; |
| + |
| +// This class models an application who will use the shell to interact with a |
| +// system service. The shell may limit this application's visibility of the full |
| +// set of interfaces exposed by that service. |
| +class TestApplication : public ApplicationDelegate, |
| + public ApplicationLoader, |
| + public InterfaceFactory<Driver>, |
| + public Driver { |
| + public: |
| + explicit TestApplication(bool connect_to_test_service_2) |
| + : connect_to_test_service_2_(connect_to_test_service_2) {} |
| + ~TestApplication() override {} |
| + |
| + private: |
| + // Overridden from ApplicationDelegate: |
| + bool ConfigureIncomingConnection(ApplicationConnection* connection) override { |
| + connection->AddService<Driver>(this); |
| + return true; |
| + } |
| + |
| + // Overridden from ApplicationLoader: |
| + void Load(const GURL& url, InterfaceRequest<Application> request) override { |
| + app_.reset(new ApplicationImpl(this, request.Pass())); |
| + } |
| + |
| + // Overridden from InterfaceFactory<Driver>: |
| + void Create(ApplicationConnection* connection, |
| + InterfaceRequest<Driver> request) override { |
| + driver_bindings_.AddBinding(this, request.Pass()); |
| + } |
| + |
| + // Overridden from Driver: |
| + void Run() override { |
| + URLRequestPtr request(URLRequest::New()); |
| + request->url = String::From("test:service"); |
| + ApplicationConnection* connection = |
| + app_->ConnectToApplication(request.Pass()); |
| + connection->ConnectToService(&safe_); |
| + safe_->SafeMethod(); |
| + connection->ConnectToService(&unsafe_); |
| + unsafe_->UnsafeMethod(); |
| + |
| + if (connect_to_test_service_2_) { |
| + URLRequestPtr request2(URLRequest::New()); |
| + request2->url = String::From("test:service2"); |
| + ApplicationConnection* connection2 = |
| + app_->ConnectToApplication(request2.Pass()); |
| + connection2->ConnectToService(&safe_2_); |
| + safe_2_->SafeMethod(); |
| + connection2->ConnectToService(&unsafe_2_); |
| + unsafe_2_->UnsafeMethod(); |
| + |
| + } |
| + } |
| + |
| + bool connect_to_test_service_2_; |
| + scoped_ptr<ApplicationImpl> app_; |
| + WeakBindingSet<Driver> driver_bindings_; |
| + SafePtr safe_; |
| + UnsafePtr unsafe_; |
| + SafePtr safe_2_; |
| + UnsafePtr unsafe_2_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(TestApplication); |
| +}; |
| + |
| +class SafeImpl : public Safe { |
| + public: |
| + SafeImpl(CallValidator* validator, |
| + const std::string& service_url, |
| + const std::string& remote_url, |
| + InterfaceRequest<Safe> request) |
| + : validator_(validator), |
| + service_url_(service_url), |
| + remote_url_(remote_url), |
| + binding_(this, request.Pass()) {} |
| + ~SafeImpl() override {} |
| + |
| + private: |
| + // Overridden from Safe: |
| + virtual void SafeMethod() override { |
| + validator_->Call("SafeMethod", service_url_, remote_url_); |
| + } |
| + |
| + CallValidator* validator_; |
| + std::string service_url_; |
| + std::string remote_url_; |
| + StrongBinding<Safe> binding_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(SafeImpl); |
| +}; |
| + |
| +class UnsafeImpl : public Unsafe { |
| + public: |
| + UnsafeImpl(CallValidator* validator, |
| + const std::string& service_url, |
| + const std::string& remote_url, |
| + InterfaceRequest<Unsafe> request) |
| + : validator_(validator), |
| + service_url_(service_url), |
| + remote_url_(remote_url), |
| + binding_(this, request.Pass()) {} |
| + ~UnsafeImpl() override {} |
| + |
| + private: |
| + // Overridden from Unsafe: |
| + virtual void UnsafeMethod() override { |
| + validator_->Call("UnsafeMethod", service_url_, remote_url_); |
| + } |
| + |
| + CallValidator* validator_; |
| + std::string service_url_; |
| + std::string remote_url_; |
| + StrongBinding<Unsafe> binding_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(UnsafeImpl); |
| +}; |
| + |
| +// This class models a system service that exposes two interfaces, Safe and |
| +// Unsafe. The interface Unsafe is not to be exposed to untrusted applications. |
| +class ServiceApplication : public ApplicationDelegate, |
| + public ApplicationLoader, |
| + public InterfaceFactory<Safe>, |
| + public InterfaceFactory<Unsafe> { |
| + public: |
| + explicit ServiceApplication(CallValidator* validator) |
| + : validator_(validator) {} |
| + ~ServiceApplication() override {} |
| + |
| + private: |
| + // Overridden from ApplicationDelegate: |
| + bool ConfigureIncomingConnection(ApplicationConnection* connection) override { |
| + connection->AddService<Safe>(this); |
| + connection->AddService<Unsafe>(this); |
| + return true; |
| + } |
| + |
| + // Overridden from ApplicationLoader: |
| + void Load(const GURL& url, |
| + InterfaceRequest<Application> application_request) override { |
| + app_.reset(new ApplicationImpl(this, application_request.Pass())); |
| + } |
| + |
| + // Overridden from InterfaceFactory<Safe>: |
| + void Create(ApplicationConnection* connection, |
| + InterfaceRequest<Safe> request) override { |
| + new SafeImpl(validator_, app_->url(), connection->GetRemoteApplicationURL(), |
| + request.Pass()); |
| + } |
| + |
| + // Overridden from InterfaceFactory<Unsafe>: |
| + void Create(ApplicationConnection* connection, |
| + InterfaceRequest<Unsafe> request) override { |
| + new UnsafeImpl(validator_, app_->url(), |
| + connection->GetRemoteApplicationURL(), request.Pass()); |
| + } |
| + |
| + scoped_ptr<ApplicationImpl> app_; |
| + CallValidator* validator_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(ServiceApplication); |
| +}; |
| + |
| +class TestApplicationManagerDelegate : public ApplicationManager::Delegate { |
| + public: |
| + TestApplicationManagerDelegate() {} |
| + ~TestApplicationManagerDelegate() override {} |
| + |
| + private: |
| + // Overridden from ApplicationManager::Delegate: |
| + GURL ResolveMappings(const GURL& url) override { |
| + return url; |
| + } |
| + GURL ResolveMojoURL(const GURL& url) override { |
| + return url; |
| + } |
| + bool CreateFetcher(const GURL& url, |
| + const Fetcher::FetchCallback& loader_callback) override { |
| + return false; |
| + } |
| + |
| + DISALLOW_COPY_AND_ASSIGN(TestApplicationManagerDelegate); |
| +}; |
| + |
| +class CapabilityFilterTest : public testing::Test { |
| + public: |
| + CapabilityFilterTest() {} |
| + ~CapabilityFilterTest() override {} |
| + |
| + protected: |
| + void RunDriver(const std::string& url, CapabilityFilterPtr filter) { |
| + ServiceProviderPtr services; |
| + URLRequestPtr request(URLRequest::New()); |
| + request->url = String::From(url); |
| + application_manager_->ConnectToApplication( |
| + NULL, request.Pass(), std::string(), GURL(), GetProxy(&services), |
| + nullptr, filter.Pass(), base::MessageLoop::QuitWhenIdleClosure()); |
| + DriverPtr driver; |
| + ConnectToService(services.get(), &driver); |
| + driver->Run(); |
| + } |
| + |
| + base::MessageLoop* loop() { return &loop_; } |
| + ApplicationManager* application_manager() { |
| + return application_manager_.get(); |
| + } |
| + |
| + private: |
| + // Overridden from testing::Test: |
| + void SetUp() override { |
| + application_manager_.reset(new ApplicationManager(&test_delegate_)); |
| + } |
| + void TearDown() override { |
| + application_manager_.reset(); |
| + } |
| + |
| + base::ShadowingAtExitManager at_exit_; |
| + TestApplicationManagerDelegate test_delegate_; |
| + base::MessageLoop loop_; |
| + scoped_ptr<ApplicationManager> application_manager_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(CapabilityFilterTest); |
| +}; |
| + |
| +TEST_F(CapabilityFilterTest, RestrictedInterfaces) { |
| + std::set<std::string> valid_calls; |
| + valid_calls.insert("SafeMethod test:service test:untrusted_application"); |
| + valid_calls.insert("SafeMethod test:service test:trusted_application"); |
| + valid_calls.insert("UnsafeMethod test:service test:trusted_application"); |
| + std::set<std::string> invalid_calls; |
| + invalid_calls.insert("UnsafeMethod test:service test:untrusted_application"); |
| + CallValidator validator(loop(), valid_calls, invalid_calls); |
| + application_manager()->SetLoaderForURL( |
| + make_scoped_ptr(new TestApplication(false)), |
| + GURL("test:trusted_application")); |
| + application_manager()->SetLoaderForURL( |
| + make_scoped_ptr(new TestApplication(false)), |
| + GURL("test:untrusted_application")); |
| + application_manager()->SetLoaderForURL( |
| + make_scoped_ptr(new ServiceApplication(&validator)), |
| + GURL("test:service")); |
| + |
| + Array<String> interfaces(Array<String>::New(1)); |
| + interfaces[0] = String::From(std::string(Safe::Name_)); |
| + CapabilityFilterPtr filter(CapabilityFilter::New()); |
| + filter->filter.insert("test:service", interfaces.Pass()); |
| + |
| + // This first instance is restricted and will only be able to call the safe |
| + // method. |
| + RunDriver("test:untrusted_application", filter.Pass()); |
| + |
| + // This instance of the application should see both method calls. |
| + RunDriver("test:trusted_application", nullptr); |
| + |
| + loop()->Run(); |
| + |
| + EXPECT_TRUE(validator.called_all_valid()); |
| +} |
| + |
| +TEST_F(CapabilityFilterTest, RestrictedApplications) { |
| + std::set<std::string> valid_calls; |
| + valid_calls.insert("SafeMethod test:service test:trusted_application"); |
| + valid_calls.insert("SafeMethod test:service test:untrusted_application"); |
| + valid_calls.insert("SafeMethod test:service2 test:trusted_application"); |
| + valid_calls.insert("UnsafeMethod test:service test:trusted_application"); |
| + valid_calls.insert("UnsafeMethod test:service2 test:trusted_application"); |
| + |
| + std::set<std::string> invalid_calls; |
| + invalid_calls.insert("SafeMethod test:service2 test:untrusted_application"); |
| + invalid_calls.insert("UnsafeMethod test:service test:untrusted_application"); |
| + invalid_calls.insert("UnsafeMethod test:service2 test:untrusted_application"); |
| + |
| + CallValidator validator(loop(), valid_calls, invalid_calls); |
| + application_manager()->SetLoaderForURL( |
| + make_scoped_ptr(new TestApplication(true)), |
| + GURL("test:trusted_application")); |
| + application_manager()->SetLoaderForURL( |
| + make_scoped_ptr(new TestApplication(true)), |
| + GURL("test:untrusted_application")); |
| + application_manager()->SetLoaderForURL( |
| + make_scoped_ptr(new ServiceApplication(&validator)), |
| + GURL("test:service")); |
| + application_manager()->SetLoaderForURL( |
| + make_scoped_ptr(new ServiceApplication(&validator)), |
| + GURL("test:service2")); |
| + |
| + Array<String> interfaces(Array<String>::New(1)); |
| + interfaces[0] = String::From(std::string(Safe::Name_)); |
| + CapabilityFilterPtr filter(CapabilityFilter::New()); |
| + filter->filter.insert("test:service", interfaces.Pass()); |
| + |
| + // This first instance is restricted and will only be able to call the safe |
| + // method. |
| + RunDriver("test:untrusted_application", filter.Pass()); |
| + |
| + // This instance of the application should see both method calls. |
| + RunDriver("test:trusted_application", nullptr); |
| + |
| + loop()->Run(); |
| + |
| + EXPECT_TRUE(validator.called_all_valid()); |
| +} |
| + |
| +} // namespace |
| +} // namespace shell |
| +} // namespace mojo |