1 // Copyright 2014 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.
5 #include "base/run_loop.h"
6 #include "base/strings/utf_string_conversions.h"
7 #include "base/test/values_test_util.h"
8 #include "chrome/browser/extensions/test_extension_environment.h"
9 #include "chrome/test/base/testing_profile.h"
10 #include "device/core/device_client.h"
11 #include "device/usb/usb_device.h"
12 #include "device/usb/usb_device_handle.h"
13 #include "extensions/browser/api/device_permissions_manager.h"
14 #include "extensions/browser/extension_prefs.h"
15 #include "extensions/common/extension.h"
16 #include "testing/gmock/include/gmock/gmock.h"
17 #include "testing/gtest/include/gtest/gtest.h"
19 namespace extensions
{
23 using device::UsbDevice
;
24 using device::UsbDeviceHandle
;
25 using device::UsbService
;
28 using testing::Return
;
29 using testing::SetArgPointee
;
33 class MockUsbService
: public UsbService
{
35 MockUsbService() : mock_device_client(this) {}
37 MOCK_METHOD1(GetDeviceById
, scoped_refptr
<UsbDevice
>(uint32
));
38 MOCK_METHOD1(GetDevices
, void(std::vector
<scoped_refptr
<UsbDevice
>>*));
40 // Public wrapper for the protected NotifyDeviceRemove function.
41 void NotifyDeviceRemoved(scoped_refptr
<UsbDevice
> device
) {
42 UsbService::NotifyDeviceRemoved(device
);
46 class MockDeviceClient
: device::DeviceClient
{
48 explicit MockDeviceClient(UsbService
* usb_service
)
49 : usb_service_(usb_service
) {}
51 UsbService
* GetUsbService() override
{ return usb_service_
; }
54 UsbService
* usb_service_
;
57 MockDeviceClient mock_device_client
;
60 class MockUsbDevice
: public UsbDevice
{
62 explicit MockUsbDevice(const std::string
& serial_number
)
63 : UsbDevice(0, 0, next_id
++) {
64 if (serial_number
.empty()) {
65 EXPECT_CALL(*this, GetSerialNumber(_
)).WillRepeatedly(Return(false));
67 EXPECT_CALL(*this, GetSerialNumber(_
))
69 DoAll(SetArgPointee
<0>(base::ASCIIToUTF16(serial_number
)),
73 EXPECT_CALL(*this, GetProduct(_
))
75 DoAll(SetArgPointee
<0>(base::ASCIIToUTF16("Test Product")),
77 EXPECT_CALL(*this, GetManufacturer(_
))
79 DoAll(SetArgPointee
<0>(base::ASCIIToUTF16("Test Manufacturer")),
83 MOCK_METHOD0(Open
, scoped_refptr
<UsbDeviceHandle
>());
84 MOCK_METHOD1(Close
, bool(scoped_refptr
<UsbDeviceHandle
>));
85 MOCK_METHOD0(GetConfiguration
, const device::UsbConfigDescriptor
*());
86 MOCK_METHOD1(GetManufacturer
, bool(base::string16
*));
87 MOCK_METHOD1(GetProduct
, bool(base::string16
*));
88 MOCK_METHOD1(GetSerialNumber
, bool(base::string16
*));
91 virtual ~MockUsbDevice() {}
94 void AllowUsbDevice(DevicePermissionsManager
* manager
,
95 const Extension
* extension
,
96 scoped_refptr
<UsbDevice
> device
) {
97 // If the device cannot provide any of these strings they will simply by
99 base::string16 product
;
100 device
->GetProduct(&product
);
101 base::string16 manufacturer
;
102 device
->GetManufacturer(&manufacturer
);
103 base::string16 serial_number
;
104 device
->GetSerialNumber(&serial_number
);
106 manager
->AllowUsbDevice(
107 extension
->id(), device
, product
, manufacturer
, serial_number
);
110 scoped_refptr
<DevicePermissionEntry
> FindEntry(
111 DevicePermissions
* device_permissions
,
112 scoped_refptr
<UsbDevice
> device
) {
113 base::string16 serial_number
;
114 device
->GetSerialNumber(&serial_number
);
116 return device_permissions
->FindEntry(device
, serial_number
);
121 class DevicePermissionsManagerTest
: public testing::Test
{
123 void SetUp() override
{
124 testing::Test::SetUp();
125 env_
.GetExtensionPrefs(); // Force creation before adding extensions.
126 extension_
= env_
.MakeExtension(*base::test::ParseJson(
130 " \"scripts\": [\"background.js\"]"
133 " \"permissions\": ["
137 device0_
= new MockUsbDevice("ABCDE");
138 device1_
= new MockUsbDevice("");
139 device2_
= new MockUsbDevice("12345");
140 device3_
= new MockUsbDevice("");
141 usb_service_
= new MockUsbService();
142 UsbService::SetInstanceForTest(usb_service_
);
145 extensions::TestExtensionEnvironment env_
;
146 const extensions::Extension
* extension_
;
147 MockUsbService
* usb_service_
;
148 scoped_refptr
<MockUsbDevice
> device0_
;
149 scoped_refptr
<MockUsbDevice
> device1_
;
150 scoped_refptr
<MockUsbDevice
> device2_
;
151 scoped_refptr
<MockUsbDevice
> device3_
;
154 TEST_F(DevicePermissionsManagerTest
, AllowAndClearDevices
) {
155 DevicePermissionsManager
* manager
=
156 DevicePermissionsManager::Get(env_
.profile());
157 AllowUsbDevice(manager
, extension_
, device0_
);
158 AllowUsbDevice(manager
, extension_
, device1_
);
160 scoped_ptr
<DevicePermissions
> device_permissions
=
161 manager
->GetForExtension(extension_
->id());
162 scoped_refptr
<DevicePermissionEntry
> device0_entry
=
163 FindEntry(device_permissions
.get(), device0_
);
164 ASSERT_TRUE(device0_entry
.get());
165 scoped_refptr
<DevicePermissionEntry
> device1_entry
=
166 FindEntry(device_permissions
.get(), device1_
);
167 ASSERT_TRUE(device1_entry
.get());
168 ASSERT_FALSE(FindEntry(device_permissions
.get(), device2_
).get());
169 ASSERT_FALSE(FindEntry(device_permissions
.get(), device3_
).get());
170 ASSERT_EQ(2U, device_permissions
->entries().size());
172 ASSERT_EQ(base::ASCIIToUTF16(
173 "Test Product from Test Manufacturer (serial number ABCDE)"),
174 device0_entry
->GetPermissionMessageString());
175 ASSERT_EQ(base::ASCIIToUTF16("Test Product from Test Manufacturer"),
176 device1_entry
->GetPermissionMessageString());
178 manager
->Clear(extension_
->id());
180 device_permissions
= manager
->GetForExtension(extension_
->id());
181 ASSERT_FALSE(FindEntry(device_permissions
.get(), device0_
).get());
182 ASSERT_FALSE(FindEntry(device_permissions
.get(), device1_
).get());
183 ASSERT_FALSE(FindEntry(device_permissions
.get(), device2_
).get());
184 ASSERT_FALSE(FindEntry(device_permissions
.get(), device3_
).get());
185 ASSERT_EQ(0U, device_permissions
->entries().size());
187 // After clearing device it should be possible to grant permission again.
188 AllowUsbDevice(manager
, extension_
, device0_
);
189 AllowUsbDevice(manager
, extension_
, device1_
);
191 device_permissions
= manager
->GetForExtension(extension_
->id());
192 ASSERT_TRUE(FindEntry(device_permissions
.get(), device0_
).get());
193 ASSERT_TRUE(FindEntry(device_permissions
.get(), device1_
).get());
194 ASSERT_FALSE(FindEntry(device_permissions
.get(), device2_
).get());
195 ASSERT_FALSE(FindEntry(device_permissions
.get(), device3_
).get());
198 TEST_F(DevicePermissionsManagerTest
, SuspendExtension
) {
199 DevicePermissionsManager
* manager
=
200 DevicePermissionsManager::Get(env_
.profile());
201 AllowUsbDevice(manager
, extension_
, device0_
);
202 AllowUsbDevice(manager
, extension_
, device1_
);
204 scoped_ptr
<DevicePermissions
> device_permissions
=
205 manager
->GetForExtension(extension_
->id());
206 ASSERT_TRUE(FindEntry(device_permissions
.get(), device0_
).get());
207 ASSERT_TRUE(FindEntry(device_permissions
.get(), device1_
).get());
208 ASSERT_FALSE(FindEntry(device_permissions
.get(), device2_
).get());
209 ASSERT_FALSE(FindEntry(device_permissions
.get(), device3_
).get());
211 manager
->OnBackgroundHostClose(extension_
->id());
213 device_permissions
= manager
->GetForExtension(extension_
->id());
214 // Device 0 is still registered because its serial number has been stored in
215 // ExtensionPrefs, it is "persistent".
216 ASSERT_TRUE(FindEntry(device_permissions
.get(), device0_
).get());
217 // Device 1 does not have uniquely identifying traits and so permission to
218 // open it has been dropped when the app's windows have closed and the
219 // background page has been suspended.
220 ASSERT_FALSE(FindEntry(device_permissions
.get(), device1_
).get());
221 ASSERT_FALSE(FindEntry(device_permissions
.get(), device2_
).get());
222 ASSERT_FALSE(FindEntry(device_permissions
.get(), device3_
).get());
225 // TODO(reillyg): Until crbug.com/427985 is resolved device removal
226 // notifications are delivered asynchronously and so this test must be disabled.
227 TEST_F(DevicePermissionsManagerTest
, DISABLED_DisconnectDevice
) {
228 DevicePermissionsManager
* manager
=
229 DevicePermissionsManager::Get(env_
.profile());
230 AllowUsbDevice(manager
, extension_
, device0_
);
231 AllowUsbDevice(manager
, extension_
, device1_
);
233 scoped_ptr
<DevicePermissions
> device_permissions
=
234 manager
->GetForExtension(extension_
->id());
235 ASSERT_TRUE(FindEntry(device_permissions
.get(), device0_
).get());
236 ASSERT_TRUE(FindEntry(device_permissions
.get(), device1_
).get());
237 ASSERT_FALSE(FindEntry(device_permissions
.get(), device2_
).get());
238 ASSERT_FALSE(FindEntry(device_permissions
.get(), device3_
).get());
240 usb_service_
->NotifyDeviceRemoved(device0_
);
241 usb_service_
->NotifyDeviceRemoved(device1_
);
243 device_permissions
= manager
->GetForExtension(extension_
->id());
244 // Device 0 will be accessible when it is reconnected because it can be
245 // recognized by its serial number.
246 ASSERT_TRUE(FindEntry(device_permissions
.get(), device0_
).get());
247 // Device 1 does not have a serial number and cannot be distinguished from
248 // any other device of the same model so the app must request permission again
249 // when it is reconnected.
250 ASSERT_FALSE(FindEntry(device_permissions
.get(), device1_
).get());
251 ASSERT_FALSE(FindEntry(device_permissions
.get(), device2_
).get());
252 ASSERT_FALSE(FindEntry(device_permissions
.get(), device3_
).get());
255 TEST_F(DevicePermissionsManagerTest
, RevokeAndRegrantAccess
) {
256 DevicePermissionsManager
* manager
=
257 DevicePermissionsManager::Get(env_
.profile());
258 AllowUsbDevice(manager
, extension_
, device0_
);
259 AllowUsbDevice(manager
, extension_
, device1_
);
261 scoped_ptr
<DevicePermissions
> device_permissions
=
262 manager
->GetForExtension(extension_
->id());
263 scoped_refptr
<DevicePermissionEntry
> device0_entry
=
264 FindEntry(device_permissions
.get(), device0_
);
265 ASSERT_TRUE(device0_entry
.get());
266 scoped_refptr
<DevicePermissionEntry
> device1_entry
=
267 FindEntry(device_permissions
.get(), device1_
);
268 ASSERT_TRUE(device1_entry
.get());
270 manager
->RemoveEntry(extension_
->id(), device0_entry
);
271 device_permissions
= manager
->GetForExtension(extension_
->id());
272 ASSERT_FALSE(FindEntry(device_permissions
.get(), device0_
).get());
273 ASSERT_TRUE(FindEntry(device_permissions
.get(), device1_
).get());
275 AllowUsbDevice(manager
, extension_
, device0_
);
276 device_permissions
= manager
->GetForExtension(extension_
->id());
277 ASSERT_TRUE(FindEntry(device_permissions
.get(), device0_
).get());
278 ASSERT_TRUE(FindEntry(device_permissions
.get(), device1_
).get());
280 manager
->RemoveEntry(extension_
->id(), device1_entry
);
281 device_permissions
= manager
->GetForExtension(extension_
->id());
282 ASSERT_TRUE(FindEntry(device_permissions
.get(), device0_
).get());
283 ASSERT_FALSE(FindEntry(device_permissions
.get(), device1_
).get());
285 AllowUsbDevice(manager
, extension_
, device1_
);
286 device_permissions
= manager
->GetForExtension(extension_
->id());
287 ASSERT_TRUE(FindEntry(device_permissions
.get(), device0_
).get());
288 ASSERT_TRUE(FindEntry(device_permissions
.get(), device1_
).get());
291 TEST_F(DevicePermissionsManagerTest
, UpdateLastUsed
) {
292 DevicePermissionsManager
* manager
=
293 DevicePermissionsManager::Get(env_
.profile());
294 AllowUsbDevice(manager
, extension_
, device0_
);
296 scoped_ptr
<DevicePermissions
> device_permissions
=
297 manager
->GetForExtension(extension_
->id());
298 scoped_refptr
<DevicePermissionEntry
> device0_entry
=
299 FindEntry(device_permissions
.get(), device0_
);
300 ASSERT_TRUE(device0_entry
->last_used().is_null());
302 manager
->UpdateLastUsed(extension_
->id(), device0_entry
);
303 device_permissions
= manager
->GetForExtension(extension_
->id());
304 device0_entry
= FindEntry(device_permissions
.get(), device0_
);
305 ASSERT_FALSE(device0_entry
->last_used().is_null());
308 TEST_F(DevicePermissionsManagerTest
, LoadPrefs
) {
309 scoped_ptr
<base::Value
> prefs_value
= base::test::ParseJson(
312 " \"product_id\": 0,"
313 " \"serial_number\": \"ABCDE\","
314 " \"type\": \"usb\","
318 env_
.GetExtensionPrefs()->UpdateExtensionPref(extension_
->id(), "devices",
319 prefs_value
.release());
321 DevicePermissionsManager
* manager
=
322 DevicePermissionsManager::Get(env_
.profile());
323 scoped_ptr
<DevicePermissions
> device_permissions
=
324 manager
->GetForExtension(extension_
->id());
325 ASSERT_TRUE(FindEntry(device_permissions
.get(), device0_
).get());
326 ASSERT_FALSE(FindEntry(device_permissions
.get(), device1_
).get());
327 ASSERT_FALSE(FindEntry(device_permissions
.get(), device2_
).get());
328 ASSERT_FALSE(FindEntry(device_permissions
.get(), device3_
).get());
331 } // namespace extensions