OLD | NEW |
| (Empty) |
1 // Copyright 2016 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 <stddef.h> | |
6 #include <stdint.h> | |
7 | |
8 #include <memory> | |
9 #include <utility> | |
10 | |
11 #include "base/bind.h" | |
12 #include "base/guid.h" | |
13 #include "base/macros.h" | |
14 #include "base/run_loop.h" | |
15 #include "base/test/test_suite.h" | |
16 #include "mojo/public/cpp/bindings/binding_set.h" | |
17 #include "services/shell/public/cpp/names.h" | |
18 #include "services/shell/public/cpp/service_test.h" | |
19 #include "services/shell/public/interfaces/service_manager.mojom.h" | |
20 #include "services/shell/tests/connect/connect_test.mojom.h" | |
21 | |
22 // Tests that multiple services can be packaged in a single service by | |
23 // implementing ServiceFactory; that these services can be specified by | |
24 // the package's manifest and are thus registered with the PackageManager. | |
25 | |
26 namespace shell { | |
27 | |
28 namespace { | |
29 | |
30 const char kTestPackageName[] = "service:connect_test_package"; | |
31 const char kTestAppName[] = "service:connect_test_app"; | |
32 const char kTestAppAName[] = "service:connect_test_a"; | |
33 const char kTestAppBName[] = "service:connect_test_b"; | |
34 const char kTestClassAppName[] = "service:connect_test_class_app"; | |
35 const char kTestSingletonAppName[] = "service:connect_test_singleton_app"; | |
36 const char kTestDriverName[] = "exe:connect_test_driver"; | |
37 | |
38 void ReceiveOneString(std::string* out_string, | |
39 base::RunLoop* loop, | |
40 const std::string& in_string) { | |
41 *out_string = in_string; | |
42 loop->Quit(); | |
43 } | |
44 | |
45 void ReceiveTwoStrings(std::string* out_string_1, | |
46 std::string* out_string_2, | |
47 base::RunLoop* loop, | |
48 const std::string& in_string_1, | |
49 const std::string& in_string_2) { | |
50 *out_string_1 = in_string_1; | |
51 *out_string_2 = in_string_2; | |
52 loop->Quit(); | |
53 } | |
54 | |
55 void ReceiveConnectionResult(mojom::ConnectResult* out_result, | |
56 Identity* out_target, | |
57 base::RunLoop* loop, | |
58 int32_t in_result, | |
59 const shell::Identity& in_identity) { | |
60 *out_result = static_cast<mojom::ConnectResult>(in_result); | |
61 *out_target = in_identity; | |
62 loop->Quit(); | |
63 } | |
64 | |
65 void QuitLoop(base::RunLoop* loop) { | |
66 loop->Quit(); | |
67 } | |
68 | |
69 } // namespace | |
70 | |
71 class ConnectTest : public test::ServiceTest, | |
72 public InterfaceFactory<test::mojom::ExposedInterface>, | |
73 public test::mojom::ExposedInterface { | |
74 public: | |
75 ConnectTest() : ServiceTest("service:connect_unittests") {} | |
76 ~ConnectTest() override {} | |
77 | |
78 protected: | |
79 std::unique_ptr<Connection> ConnectTo(Connector::ConnectParams* params) { | |
80 std::unique_ptr<Connection> connection = connector()->Connect(params); | |
81 base::RunLoop loop; | |
82 connection->AddConnectionCompletedClosure(base::Bind(&QuitLoop, &loop)); | |
83 loop.Run(); | |
84 return connection; | |
85 } | |
86 | |
87 void CompareConnectionState( | |
88 const std::string& connection_local_name, | |
89 const std::string& connection_remote_name, | |
90 const std::string& connection_remote_userid, | |
91 const std::string& initialize_local_name, | |
92 const std::string& initialize_userid) { | |
93 EXPECT_EQ(connection_remote_name, | |
94 connection_state_->connection_remote_name); | |
95 EXPECT_EQ(connection_remote_userid, | |
96 connection_state_->connection_remote_userid); | |
97 EXPECT_EQ(initialize_local_name, connection_state_->initialize_local_name); | |
98 EXPECT_EQ(initialize_userid, connection_state_->initialize_userid); | |
99 } | |
100 | |
101 private: | |
102 class TestService : public test::ServiceTestClient { | |
103 public: | |
104 explicit TestService(ConnectTest* connect_test) | |
105 : test::ServiceTestClient(connect_test), | |
106 connect_test_(connect_test) {} | |
107 ~TestService() override {} | |
108 | |
109 private: | |
110 bool OnConnect(const Identity& remote_identity, | |
111 InterfaceRegistry* registry) override { | |
112 registry->AddInterface<test::mojom::ExposedInterface>(connect_test_); | |
113 return true; | |
114 } | |
115 | |
116 ConnectTest* connect_test_; | |
117 | |
118 DISALLOW_COPY_AND_ASSIGN(TestService); | |
119 }; | |
120 | |
121 // test::ServiceTest: | |
122 void SetUp() override { | |
123 test::ServiceTest::SetUp(); | |
124 // We need to connect to the package first to force the shell to read the | |
125 // package app's manifest and register aliases for the applications it | |
126 // provides. | |
127 test::mojom::ConnectTestServicePtr root_service; | |
128 std::unique_ptr<Connection> connection = | |
129 connector()->Connect(kTestPackageName); | |
130 connection->GetInterface(&root_service); | |
131 base::RunLoop run_loop; | |
132 std::string root_name; | |
133 root_service->GetTitle( | |
134 base::Bind(&ReceiveOneString, &root_name, &run_loop)); | |
135 run_loop.Run(); | |
136 } | |
137 std::unique_ptr<Service> CreateService() override { | |
138 return base::MakeUnique<TestService>(this); | |
139 } | |
140 | |
141 // InterfaceFactory<test::mojom::ExposedInterface>: | |
142 void Create(const Identity& remote_identity, | |
143 test::mojom::ExposedInterfaceRequest request) override { | |
144 bindings_.AddBinding(this, std::move(request)); | |
145 } | |
146 | |
147 void ConnectionAccepted(test::mojom::ConnectionStatePtr state) override { | |
148 connection_state_ = std::move(state); | |
149 } | |
150 | |
151 test::mojom::ConnectionStatePtr connection_state_; | |
152 | |
153 mojo::BindingSet<test::mojom::ExposedInterface> bindings_; | |
154 | |
155 DISALLOW_COPY_AND_ASSIGN(ConnectTest); | |
156 }; | |
157 | |
158 // Ensure the connection was properly established and that a round trip | |
159 // method call/response is completed. | |
160 TEST_F(ConnectTest, Connect) { | |
161 std::unique_ptr<Connection> connection = connector()->Connect(kTestAppName); | |
162 test::mojom::ConnectTestServicePtr service; | |
163 connection->GetInterface(&service); | |
164 base::RunLoop run_loop; | |
165 std::string title; | |
166 service->GetTitle(base::Bind(&ReceiveOneString, &title, &run_loop)); | |
167 run_loop.Run(); | |
168 EXPECT_EQ("APP", title); | |
169 EXPECT_FALSE(connection->IsPending()); | |
170 EXPECT_EQ(connection->GetRemoteIdentity().name(), kTestAppName); | |
171 } | |
172 | |
173 TEST_F(ConnectTest, Instances) { | |
174 Connector::ConnectParams params_a( | |
175 Identity(kTestAppName, mojom::kInheritUserID, "A")); | |
176 std::unique_ptr<Connection> connection_a1 = ConnectTo(¶ms_a); | |
177 std::unique_ptr<Connection> connection_a2 = ConnectTo(¶ms_a); | |
178 std::string instance_a1, instance_a2; | |
179 test::mojom::ConnectTestServicePtr service_a1; | |
180 { | |
181 connection_a1->GetInterface(&service_a1); | |
182 base::RunLoop loop; | |
183 service_a1->GetInstance(base::Bind(&ReceiveOneString, &instance_a1, &loop)); | |
184 loop.Run(); | |
185 } | |
186 test::mojom::ConnectTestServicePtr service_a2; | |
187 { | |
188 connection_a2->GetInterface(&service_a2); | |
189 base::RunLoop loop; | |
190 service_a2->GetInstance(base::Bind(&ReceiveOneString, &instance_a2, &loop)); | |
191 loop.Run(); | |
192 } | |
193 EXPECT_EQ(instance_a1, instance_a2); | |
194 | |
195 Connector::ConnectParams params_b( | |
196 Identity(kTestAppName, mojom::kInheritUserID, "B")); | |
197 std::unique_ptr<Connection> connection_b = ConnectTo(¶ms_b); | |
198 std::string instance_b; | |
199 test::mojom::ConnectTestServicePtr service_b; | |
200 { | |
201 connection_b->GetInterface(&service_b); | |
202 base::RunLoop loop; | |
203 service_b->GetInstance(base::Bind(&ReceiveOneString, &instance_b, &loop)); | |
204 loop.Run(); | |
205 } | |
206 | |
207 EXPECT_NE(instance_a1, instance_b); | |
208 } | |
209 | |
210 // When both the unresolved and resolved instance names are their default | |
211 // values, the instance name from the unresolved name must be used. | |
212 // (The case where the instance names differ is covered by | |
213 // LifecycleTest.PackagedApp_CrashCrashesOtherProvidedApp). | |
214 TEST_F(ConnectTest, PreferUnresolvedDefaultInstanceName) { | |
215 // Connect to an app with no manifest-supplied instance name provided by a | |
216 // package, the instance name must be derived from the application instance | |
217 // name, not the package. | |
218 std::unique_ptr<Connection> connection = connector()->Connect(kTestAppName); | |
219 { | |
220 base::RunLoop loop; | |
221 connection->AddConnectionCompletedClosure(base::Bind(&QuitLoop, &loop)); | |
222 loop.Run(); | |
223 } | |
224 | |
225 std::string instance; | |
226 { | |
227 test::mojom::ConnectTestServicePtr service; | |
228 connection->GetInterface(&service); | |
229 base::RunLoop loop; | |
230 service->GetInstance(base::Bind(&ReceiveOneString, &instance, &loop)); | |
231 loop.Run(); | |
232 } | |
233 EXPECT_EQ(GetNamePath(kTestAppName), instance); | |
234 } | |
235 | |
236 // BlockedInterface should not be exposed to this application because it is not | |
237 // in our CapabilityFilter whitelist. | |
238 TEST_F(ConnectTest, BlockedInterface) { | |
239 std::unique_ptr<Connection> connection = connector()->Connect(kTestAppName); | |
240 base::RunLoop run_loop; | |
241 test::mojom::BlockedInterfacePtr blocked; | |
242 connection->GetInterface(&blocked); | |
243 blocked.set_connection_error_handler(base::Bind(&QuitLoop, &run_loop)); | |
244 std::string title = "unchanged"; | |
245 blocked->GetTitleBlocked(base::Bind(&ReceiveOneString, &title, &run_loop)); | |
246 run_loop.Run(); | |
247 EXPECT_EQ("unchanged", title); | |
248 } | |
249 | |
250 // Connects to an app provided by a package. | |
251 TEST_F(ConnectTest, PackagedApp) { | |
252 std::unique_ptr<Connection> connection = connector()->Connect(kTestAppAName); | |
253 test::mojom::ConnectTestServicePtr service_a; | |
254 connection->GetInterface(&service_a); | |
255 base::RunLoop run_loop; | |
256 std::string a_name; | |
257 service_a->GetTitle(base::Bind(&ReceiveOneString, &a_name, &run_loop)); | |
258 run_loop.Run(); | |
259 EXPECT_EQ("A", a_name); | |
260 EXPECT_FALSE(connection->IsPending()); | |
261 EXPECT_EQ(connection->GetRemoteIdentity().name(), kTestAppAName); | |
262 } | |
263 | |
264 // Ask the target application to attempt to connect to a third application | |
265 // provided by a package whose id is permitted by the primary target's | |
266 // CapabilityFilter but whose package is not. The connection should be | |
267 // allowed regardless of the target's CapabilityFilter with respect to the | |
268 // package. | |
269 TEST_F(ConnectTest, BlockedPackage) { | |
270 std::unique_ptr<Connection> connection = connector()->Connect(kTestAppName); | |
271 test::mojom::StandaloneAppPtr standalone_app; | |
272 connection->GetInterface(&standalone_app); | |
273 base::RunLoop run_loop; | |
274 std::string title; | |
275 standalone_app->ConnectToAllowedAppInBlockedPackage( | |
276 base::Bind(&ReceiveOneString, &title, &run_loop)); | |
277 run_loop.Run(); | |
278 EXPECT_EQ("A", title); | |
279 } | |
280 | |
281 // BlockedInterface should not be exposed to this application because it is not | |
282 // in our CapabilityFilter whitelist. | |
283 TEST_F(ConnectTest, PackagedApp_BlockedInterface) { | |
284 std::unique_ptr<Connection> connection = connector()->Connect(kTestAppAName); | |
285 base::RunLoop run_loop; | |
286 test::mojom::BlockedInterfacePtr blocked; | |
287 connection->GetInterface(&blocked); | |
288 blocked.set_connection_error_handler(base::Bind(&QuitLoop, &run_loop)); | |
289 run_loop.Run(); | |
290 } | |
291 | |
292 // Connection to another application provided by the same package, blocked | |
293 // because it's not in the capability filter whitelist. | |
294 TEST_F(ConnectTest, BlockedPackagedApplication) { | |
295 std::unique_ptr<Connection> connection = connector()->Connect(kTestAppBName); | |
296 test::mojom::ConnectTestServicePtr service_b; | |
297 connection->GetInterface(&service_b); | |
298 base::RunLoop run_loop; | |
299 connection->SetConnectionLostClosure(base::Bind(&QuitLoop, &run_loop)); | |
300 run_loop.Run(); | |
301 EXPECT_FALSE(connection->IsPending()); | |
302 EXPECT_EQ(mojom::ConnectResult::ACCESS_DENIED, connection->GetResult()); | |
303 } | |
304 | |
305 TEST_F(ConnectTest, CapabilityClasses) { | |
306 std::unique_ptr<Connection> connection = connector()->Connect(kTestAppName); | |
307 test::mojom::StandaloneAppPtr standalone_app; | |
308 connection->GetInterface(&standalone_app); | |
309 std::string string1, string2; | |
310 base::RunLoop loop; | |
311 standalone_app->ConnectToClassInterface( | |
312 base::Bind(&ReceiveTwoStrings, &string1, &string2, &loop)); | |
313 loop.Run(); | |
314 EXPECT_EQ("PONG", string1); | |
315 EXPECT_EQ("CLASS APP", string2); | |
316 } | |
317 | |
318 TEST_F(ConnectTest, ConnectWithoutExplicitClassBlocked) { | |
319 // We not be able to bind a ClassInterfacePtr since the connect_unittest app | |
320 // does not explicitly request the "class" capability from | |
321 // connect_test_class_app. This test will hang if it is bound. | |
322 std::unique_ptr<Connection> connection = | |
323 connector()->Connect(kTestClassAppName); | |
324 test::mojom::ClassInterfacePtr class_interface; | |
325 connection->GetInterface(&class_interface); | |
326 base::RunLoop loop; | |
327 class_interface.set_connection_error_handler(base::Bind(&QuitLoop, &loop)); | |
328 loop.Run(); | |
329 } | |
330 | |
331 TEST_F(ConnectTest, ConnectAsDifferentUser_Allowed) { | |
332 std::unique_ptr<Connection> connection = connector()->Connect(kTestAppName); | |
333 test::mojom::UserIdTestPtr user_id_test; | |
334 connection->GetInterface(&user_id_test); | |
335 mojom::ConnectResult result; | |
336 Identity target(kTestClassAppName, base::GenerateGUID()); | |
337 Identity result_identity; | |
338 { | |
339 base::RunLoop loop; | |
340 user_id_test->ConnectToClassAppAsDifferentUser( | |
341 target, | |
342 base::Bind(&ReceiveConnectionResult, &result, &result_identity, &loop)); | |
343 loop.Run(); | |
344 } | |
345 EXPECT_EQ(result, mojom::ConnectResult::SUCCEEDED); | |
346 EXPECT_EQ(target, result_identity); | |
347 } | |
348 | |
349 TEST_F(ConnectTest, ConnectAsDifferentUser_Blocked) { | |
350 std::unique_ptr<Connection> connection = connector()->Connect(kTestAppAName); | |
351 test::mojom::UserIdTestPtr user_id_test; | |
352 connection->GetInterface(&user_id_test); | |
353 mojom::ConnectResult result; | |
354 Identity target(kTestClassAppName, base::GenerateGUID()); | |
355 Identity result_identity; | |
356 { | |
357 base::RunLoop loop; | |
358 user_id_test->ConnectToClassAppAsDifferentUser( | |
359 target, | |
360 base::Bind(&ReceiveConnectionResult, &result, &result_identity, &loop)); | |
361 loop.Run(); | |
362 } | |
363 EXPECT_EQ(mojom::ConnectResult::ACCESS_DENIED, result); | |
364 EXPECT_FALSE(target == result_identity); | |
365 } | |
366 | |
367 // There are various other tests (shell, lifecycle) that test valid client | |
368 // process specifications. This is the only one for blocking. | |
369 TEST_F(ConnectTest, ConnectToClientProcess_Blocked) { | |
370 std::unique_ptr<Connection> connection = | |
371 connector()->Connect(kTestDriverName); | |
372 test::mojom::ClientProcessTestPtr client_process_test; | |
373 connection->GetInterface(&client_process_test); | |
374 mojom::ConnectResult result; | |
375 Identity result_identity; | |
376 { | |
377 base::RunLoop loop; | |
378 client_process_test->LaunchAndConnectToProcess( | |
379 base::Bind(&ReceiveConnectionResult, &result, &result_identity, &loop)); | |
380 loop.Run(); | |
381 } | |
382 EXPECT_EQ(mojom::ConnectResult::ACCESS_DENIED, result); | |
383 } | |
384 | |
385 // Verifies that a client with the "all_users" capability class can receive | |
386 // connections from clients run as other users. | |
387 TEST_F(ConnectTest, AllUsersSingleton) { | |
388 // Connect to an instance with an explicitly different user_id. This supplied | |
389 // user id should be ignored by the shell (which will generate its own | |
390 // synthetic user id for all-user singleton instances). | |
391 const std::string singleton_userid = base::GenerateGUID(); | |
392 Connector::ConnectParams params( | |
393 Identity(kTestSingletonAppName, singleton_userid)); | |
394 std::unique_ptr<Connection> connection = connector()->Connect(¶ms); | |
395 { | |
396 base::RunLoop loop; | |
397 connection->AddConnectionCompletedClosure(base::Bind(&QuitLoop, &loop)); | |
398 loop.Run(); | |
399 EXPECT_NE(connection->GetRemoteIdentity().user_id(), singleton_userid); | |
400 } | |
401 // This connects using the current client's user_id. It should be bound to the | |
402 // same service started above, with the same shell-generated user id. | |
403 std::unique_ptr<Connection> inherit_connection = | |
404 connector()->Connect(kTestSingletonAppName); | |
405 { | |
406 base::RunLoop loop; | |
407 inherit_connection->AddConnectionCompletedClosure( | |
408 base::Bind(&QuitLoop, &loop)); | |
409 loop.Run(); | |
410 EXPECT_EQ(inherit_connection->GetRemoteIdentity().user_id(), | |
411 connection->GetRemoteIdentity().user_id()); | |
412 } | |
413 } | |
414 | |
415 } // namespace shell | |
OLD | NEW |