| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "mojo/shell/capability_filter_test.h" | |
| 6 | |
| 7 #include <utility> | |
| 8 | |
| 9 #include "base/macros.h" | |
| 10 #include "base/stl_util.h" | |
| 11 #include "base/strings/stringprintf.h" | |
| 12 #include "mojo/public/cpp/bindings/strong_binding.h" | |
| 13 #include "mojo/public/cpp/bindings/weak_binding_set.h" | |
| 14 #include "mojo/shell/application_loader.h" | |
| 15 #include "mojo/shell/public/cpp/connection.h" | |
| 16 #include "mojo/shell/public/cpp/interface_factory.h" | |
| 17 #include "mojo/shell/public/cpp/shell_connection.h" | |
| 18 | |
| 19 namespace mojo { | |
| 20 namespace shell { | |
| 21 namespace test { | |
| 22 | |
| 23 // Lives on the main thread of the test. | |
| 24 // Listens for interfaces exposed/blocked and for application connections being | |
| 25 // closed. Quits |loop| when all expectations are met. | |
| 26 class ConnectionValidator : public ApplicationLoader, | |
| 27 public ShellClient, | |
| 28 public InterfaceFactory<Validator>, | |
| 29 public Validator { | |
| 30 public: | |
| 31 ConnectionValidator(const std::set<std::string>& expectations, | |
| 32 base::MessageLoop* loop) | |
| 33 : app_(nullptr), | |
| 34 expectations_(expectations), | |
| 35 loop_(loop) {} | |
| 36 ~ConnectionValidator() override {} | |
| 37 | |
| 38 bool expectations_met() { | |
| 39 return unexpected_.empty() && expectations_.empty(); | |
| 40 } | |
| 41 | |
| 42 void PrintUnmetExpectations() { | |
| 43 for (auto expectation : expectations_) | |
| 44 ADD_FAILURE() << "Unmet: " << expectation; | |
| 45 for (auto unexpected : unexpected_) | |
| 46 ADD_FAILURE() << "Unexpected: " << unexpected; | |
| 47 } | |
| 48 | |
| 49 private: | |
| 50 // Overridden from ApplicationLoader: | |
| 51 void Load(const GURL& url, | |
| 52 InterfaceRequest<mojom::ShellClient> request) override { | |
| 53 app_.reset(new ShellConnection(this, std::move(request))); | |
| 54 } | |
| 55 | |
| 56 // Overridden from ShellClient: | |
| 57 bool AcceptConnection(Connection* connection) override { | |
| 58 connection->AddInterface<Validator>(this); | |
| 59 return true; | |
| 60 } | |
| 61 | |
| 62 // Overridden from InterfaceFactory<Validator>: | |
| 63 void Create(Connection* connection, | |
| 64 InterfaceRequest<Validator> request) override { | |
| 65 validator_bindings_.AddBinding(this, std::move(request)); | |
| 66 } | |
| 67 | |
| 68 // Overridden from Validator: | |
| 69 void AddInterfaceCalled(const String& app_url, | |
| 70 const String& service_url, | |
| 71 const String& name, | |
| 72 bool blocked) override { | |
| 73 Validate(base::StringPrintf("%s %s %s %s", | |
| 74 blocked ? "B" : "E", app_url.data(), service_url.data(), name.data())); | |
| 75 } | |
| 76 void ConnectionClosed(const String& app_url, | |
| 77 const String& service_url) override { | |
| 78 Validate(base::StringPrintf("C %s %s", app_url.data(), service_url.data())); | |
| 79 } | |
| 80 | |
| 81 void Validate(const std::string& result) { | |
| 82 DVLOG(1) << "Validate: " << result; | |
| 83 auto i = expectations_.find(result); | |
| 84 if (i != expectations_.end()) { | |
| 85 expectations_.erase(i); | |
| 86 if (expectations_.empty()) | |
| 87 loop_->QuitWhenIdle(); | |
| 88 } else { | |
| 89 // This is a test failure, and will result in PrintUnexpectedExpecations() | |
| 90 // being called. | |
| 91 unexpected_.insert(result); | |
| 92 loop_->QuitWhenIdle(); | |
| 93 } | |
| 94 } | |
| 95 | |
| 96 scoped_ptr<ShellConnection> app_; | |
| 97 std::set<std::string> expectations_; | |
| 98 std::set<std::string> unexpected_; | |
| 99 base::MessageLoop* loop_; | |
| 100 WeakBindingSet<Validator> validator_bindings_; | |
| 101 | |
| 102 DISALLOW_COPY_AND_ASSIGN(ConnectionValidator); | |
| 103 }; | |
| 104 | |
| 105 // This class models a system service that exposes two interfaces, Safe and | |
| 106 // Unsafe. The interface Unsafe is not to be exposed to untrusted applications. | |
| 107 class ServiceApplication : public ShellClient, | |
| 108 public InterfaceFactory<Safe>, | |
| 109 public InterfaceFactory<Unsafe>, | |
| 110 public Safe, | |
| 111 public Unsafe { | |
| 112 public: | |
| 113 ServiceApplication() : shell_(nullptr) {} | |
| 114 ~ServiceApplication() override {} | |
| 115 | |
| 116 private: | |
| 117 // Overridden from ShellClient: | |
| 118 void Initialize(Shell* shell, const std::string& url, uint32_t id) override { | |
| 119 shell_ = shell; | |
| 120 // ServiceApplications have no capability filter and can thus connect | |
| 121 // directly to the validator application. | |
| 122 shell_->ConnectToInterface("test:validator", &validator_); | |
| 123 } | |
| 124 bool AcceptConnection(Connection* connection) override { | |
| 125 AddInterface<Safe>(connection); | |
| 126 AddInterface<Unsafe>(connection); | |
| 127 return true; | |
| 128 } | |
| 129 | |
| 130 // Overridden from InterfaceFactory<Safe>: | |
| 131 void Create(Connection* connection, | |
| 132 InterfaceRequest<Safe> request) override { | |
| 133 safe_bindings_.AddBinding(this, std::move(request)); | |
| 134 } | |
| 135 | |
| 136 // Overridden from InterfaceFactory<Unsafe>: | |
| 137 void Create(Connection* connection, | |
| 138 InterfaceRequest<Unsafe> request) override { | |
| 139 unsafe_bindings_.AddBinding(this, std::move(request)); | |
| 140 } | |
| 141 | |
| 142 template <typename Interface> | |
| 143 void AddInterface(Connection* connection) { | |
| 144 validator_->AddInterfaceCalled(connection->GetRemoteApplicationURL(), | |
| 145 connection->GetConnectionURL(), | |
| 146 Interface::Name_, | |
| 147 !connection->AddInterface<Interface>(this)); | |
| 148 } | |
| 149 | |
| 150 Shell* shell_; | |
| 151 ValidatorPtr validator_; | |
| 152 WeakBindingSet<Safe> safe_bindings_; | |
| 153 WeakBindingSet<Unsafe> unsafe_bindings_; | |
| 154 | |
| 155 DISALLOW_COPY_AND_ASSIGN(ServiceApplication); | |
| 156 }; | |
| 157 | |
| 158 //////////////////////////////////////////////////////////////////////////////// | |
| 159 // TestApplication: | |
| 160 | |
| 161 TestApplication::TestApplication() : shell_(nullptr) {} | |
| 162 TestApplication::~TestApplication() {} | |
| 163 | |
| 164 void TestApplication::Initialize(Shell* shell, const std::string& url, | |
| 165 uint32_t id) { | |
| 166 shell_ = shell; | |
| 167 url_ = url; | |
| 168 } | |
| 169 bool TestApplication::AcceptConnection(Connection* connection) { | |
| 170 // TestApplications receive their Validator via the inbound connection. | |
| 171 connection->GetInterface(&validator_); | |
| 172 | |
| 173 connection1_ = shell_->Connect("test:service"); | |
| 174 connection1_->SetRemoteInterfaceProviderConnectionErrorHandler( | |
| 175 base::Bind(&TestApplication::ConnectionClosed, | |
| 176 base::Unretained(this), "test:service")); | |
| 177 | |
| 178 connection2_ = shell_->Connect("test:service2"); | |
| 179 connection2_->SetRemoteInterfaceProviderConnectionErrorHandler( | |
| 180 base::Bind(&TestApplication::ConnectionClosed, | |
| 181 base::Unretained(this), "test:service2")); | |
| 182 return true; | |
| 183 } | |
| 184 | |
| 185 void TestApplication::ConnectionClosed(const std::string& service_url) { | |
| 186 validator_->ConnectionClosed(url_, service_url); | |
| 187 } | |
| 188 | |
| 189 //////////////////////////////////////////////////////////////////////////////// | |
| 190 // TestLoader: | |
| 191 | |
| 192 TestLoader::TestLoader(ShellClient* delegate) : delegate_(delegate) {} | |
| 193 TestLoader::~TestLoader() {} | |
| 194 | |
| 195 void TestLoader::Load(const GURL& url, | |
| 196 InterfaceRequest<mojom::ShellClient> request) { | |
| 197 app_.reset(new ShellConnection(delegate_.get(), std::move(request))); | |
| 198 } | |
| 199 | |
| 200 //////////////////////////////////////////////////////////////////////////////// | |
| 201 // CapabilityFilterTest: | |
| 202 | |
| 203 CapabilityFilterTest::CapabilityFilterTest() : validator_(nullptr) {} | |
| 204 CapabilityFilterTest::~CapabilityFilterTest() {} | |
| 205 | |
| 206 void CapabilityFilterTest::RunBlockingTest() { | |
| 207 std::set<std::string> expectations; | |
| 208 expectations.insert("E test:trusted test:service mojo::shell::Safe"); | |
| 209 expectations.insert("E test:trusted test:service mojo::shell::Unsafe"); | |
| 210 expectations.insert("E test:trusted test:service2 mojo::shell::Safe"); | |
| 211 expectations.insert("E test:trusted test:service2 mojo::shell::Unsafe"); | |
| 212 expectations.insert("E test:untrusted test:service mojo::shell::Safe"); | |
| 213 expectations.insert("B test:untrusted test:service mojo::shell::Unsafe"); | |
| 214 expectations.insert("C test:untrusted test:service2"); | |
| 215 InitValidator(expectations); | |
| 216 | |
| 217 // This first application can only connect to test:service. Connections to | |
| 218 // test:service2 will be blocked. It also will only be able to see the | |
| 219 // "Safe" interface exposed by test:service. It will be blocked from seeing | |
| 220 // "Unsafe". | |
| 221 AllowedInterfaces interfaces; | |
| 222 interfaces.insert(Safe::Name_); | |
| 223 CapabilityFilter filter; | |
| 224 filter["test:service"] = interfaces; | |
| 225 RunApplication("test:untrusted", filter); | |
| 226 | |
| 227 // This second application can connect to both test:service and | |
| 228 // test:service2. It can connect to both "Safe" and "Unsafe" interfaces. | |
| 229 RunApplication("test:trusted", GetPermissiveCapabilityFilter()); | |
| 230 | |
| 231 RunTest(); | |
| 232 } | |
| 233 | |
| 234 void CapabilityFilterTest::RunWildcardTest() { | |
| 235 std::set<std::string> expectations; | |
| 236 expectations.insert("E test:wildcard test:service mojo::shell::Safe"); | |
| 237 expectations.insert("E test:wildcard test:service mojo::shell::Unsafe"); | |
| 238 expectations.insert("E test:wildcard test:service2 mojo::shell::Safe"); | |
| 239 expectations.insert("E test:wildcard test:service2 mojo::shell::Unsafe"); | |
| 240 expectations.insert("C test:blocked test:service"); | |
| 241 expectations.insert("C test:blocked test:service2"); | |
| 242 expectations.insert("B test:wildcard2 test:service mojo::shell::Safe"); | |
| 243 expectations.insert("B test:wildcard2 test:service mojo::shell::Unsafe"); | |
| 244 expectations.insert("B test:wildcard2 test:service2 mojo::shell::Safe"); | |
| 245 expectations.insert("B test:wildcard2 test:service2 mojo::shell::Unsafe"); | |
| 246 expectations.insert("E test:wildcard3 test:service mojo::shell::Safe"); | |
| 247 expectations.insert("E test:wildcard3 test:service mojo::shell::Unsafe"); | |
| 248 expectations.insert("E test:wildcard3 test:service2 mojo::shell::Safe"); | |
| 249 expectations.insert("B test:wildcard3 test:service2 mojo::shell::Unsafe"); | |
| 250 InitValidator(expectations); | |
| 251 | |
| 252 // This application is allowed to connect to any application because of a | |
| 253 // wildcard rule, and any interface exposed because of a wildcard rule in | |
| 254 // the interface array. | |
| 255 RunApplication("test:wildcard", GetPermissiveCapabilityFilter()); | |
| 256 | |
| 257 // This application is allowed to connect to no other applications because | |
| 258 // of an empty capability filter. | |
| 259 RunApplication("test:blocked", CapabilityFilter()); | |
| 260 | |
| 261 // This application is allowed to connect to any application because of a | |
| 262 // wildcard rule but may not connect to any interfaces because of an empty | |
| 263 // interface array. | |
| 264 CapabilityFilter filter1; | |
| 265 filter1["*"] = AllowedInterfaces(); | |
| 266 RunApplication("test:wildcard2", filter1); | |
| 267 | |
| 268 // This application is allowed to connect to both test:service and | |
| 269 // test:service2, and may see any interface exposed by test:service but only | |
| 270 // the Safe interface exposed by test:service2. | |
| 271 AllowedInterfaces interfaces2; | |
| 272 interfaces2.insert("*"); | |
| 273 CapabilityFilter filter2; | |
| 274 filter2["test:service"] = interfaces2; | |
| 275 AllowedInterfaces interfaces3; | |
| 276 interfaces3.insert(Safe::Name_); | |
| 277 filter2["test:service2"] = interfaces3; | |
| 278 RunApplication("test:wildcard3", filter2); | |
| 279 } | |
| 280 | |
| 281 | |
| 282 void CapabilityFilterTest::SetUp() { | |
| 283 application_manager_.reset(new ApplicationManager(true)); | |
| 284 CreateLoader<ServiceApplication>("test:service"); | |
| 285 CreateLoader<ServiceApplication>("test:service2"); | |
| 286 } | |
| 287 | |
| 288 void CapabilityFilterTest::TearDown() { | |
| 289 application_manager_.reset(); | |
| 290 } | |
| 291 | |
| 292 class InterfaceProviderImpl : public shell::mojom::InterfaceProvider { | |
| 293 public: | |
| 294 explicit InterfaceProviderImpl( | |
| 295 shell::mojom::InterfaceProviderRequest interfaces, | |
| 296 InterfaceFactory<Validator>* factory) | |
| 297 : binding_(this, std::move(interfaces)), | |
| 298 factory_(factory) {} | |
| 299 ~InterfaceProviderImpl() override {} | |
| 300 | |
| 301 private: | |
| 302 // shell::mojom::InterfaceProvider method. | |
| 303 void GetInterface(const mojo::String& interface_name, | |
| 304 ScopedMessagePipeHandle client_handle) override { | |
| 305 if (interface_name == Validator::Name_) { | |
| 306 factory_->Create(nullptr, | |
| 307 MakeRequest<Validator>(std::move(client_handle))); | |
| 308 } | |
| 309 } | |
| 310 | |
| 311 Binding<InterfaceProvider> binding_; | |
| 312 InterfaceFactory<Validator>* factory_; | |
| 313 | |
| 314 DISALLOW_COPY_AND_ASSIGN(InterfaceProviderImpl); | |
| 315 }; | |
| 316 | |
| 317 void CapabilityFilterTest::RunApplication(const std::string& url, | |
| 318 const CapabilityFilter& filter) { | |
| 319 shell::mojom::InterfaceProviderPtr remote_interfaces; | |
| 320 | |
| 321 // We expose Validator to the test application via ConnectToApplication | |
| 322 // because we don't allow the test application to connect to test:validator. | |
| 323 // Adding it to the CapabilityFilter would interfere with the test. | |
| 324 shell::mojom::InterfaceProviderPtr local_interfaces; | |
| 325 new InterfaceProviderImpl(GetProxy(&local_interfaces), validator_); | |
| 326 scoped_ptr<ConnectToApplicationParams> params( | |
| 327 new ConnectToApplicationParams); | |
| 328 params->SetTarget(Identity(GURL(url), std::string(), filter)); | |
| 329 params->set_remote_interfaces(GetProxy(&remote_interfaces)); | |
| 330 params->set_local_interfaces(std::move(local_interfaces)); | |
| 331 params->set_on_application_end(base::MessageLoop::QuitWhenIdleClosure()); | |
| 332 application_manager_->ConnectToApplication(std::move(params)); | |
| 333 } | |
| 334 | |
| 335 void CapabilityFilterTest::InitValidator( | |
| 336 const std::set<std::string>& expectations) { | |
| 337 validator_ = new ConnectionValidator(expectations, &loop_); | |
| 338 application_manager()->SetLoaderForURL(make_scoped_ptr(validator_), | |
| 339 GURL("test:validator")); | |
| 340 } | |
| 341 | |
| 342 void CapabilityFilterTest::RunTest() { | |
| 343 loop()->Run(); | |
| 344 EXPECT_TRUE(validator_->expectations_met()); | |
| 345 if (!validator_->expectations_met()) | |
| 346 validator_->PrintUnmetExpectations(); | |
| 347 } | |
| 348 | |
| 349 } // namespace test | |
| 350 } // namespace shell | |
| 351 } // namespace mojo | |
| OLD | NEW |