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 "extensions/browser/api/device_permissions_manager.h"
8 #include "base/memory/singleton.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/values.h"
13 #include "components/keyed_service/content/browser_context_dependency_manager.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "device/core/device_client.h"
16 #include "device/usb/usb_device.h"
17 #include "device/usb/usb_ids.h"
18 #include "extensions/browser/extension_host.h"
19 #include "extensions/browser/extension_prefs.h"
20 #include "extensions/browser/extensions_browser_client.h"
21 #include "extensions/browser/process_manager.h"
22 #include "extensions/browser/process_manager_factory.h"
23 #include "extensions/strings/grit/extensions_strings.h"
24 #include "ui/base/l10n/l10n_util.h"
26 namespace extensions
{
28 using content::BrowserContext
;
29 using content::BrowserThread
;
30 using device::UsbDevice
;
31 using device::UsbService
;
32 using extensions::APIPermission
;
33 using extensions::Extension
;
34 using extensions::ExtensionHost
;
35 using extensions::ExtensionPrefs
;
41 // The device that the app has permission to access.
42 const char kDevices
[] = "devices";
44 // The type of device saved.
45 const char kDeviceType
[] = "type";
47 // Type identifier for USB devices.
48 const char kDeviceTypeUsb
[] = "usb";
50 // The vendor ID of the device that the app had permission to access.
51 const char kDeviceVendorId
[] = "vendor_id";
53 // The product ID of the device that the app had permission to access.
54 const char kDeviceProductId
[] = "product_id";
56 // The serial number of the device that the app has permission to access.
57 const char kDeviceSerialNumber
[] = "serial_number";
59 // The manufacturer string read from the device that the app has permission to
61 const char kDeviceManufacturerString
[] = "manufacturer_string";
63 // The product string read from the device that the app has permission to
65 const char kDeviceProductString
[] = "product_string";
67 // Serialized timestamp of the last time when the device was opened by the app.
68 const char kDeviceLastUsed
[] = "last_used_time";
70 // Persists a DevicePermissionEntry in ExtensionPrefs.
71 void SaveDevicePermissionEntry(BrowserContext
* context
,
72 const std::string
& extension_id
,
73 scoped_refptr
<DevicePermissionEntry
> entry
) {
74 ExtensionPrefs
* prefs
= ExtensionPrefs::Get(context
);
75 ExtensionPrefs::ScopedListUpdate
update(prefs
, extension_id
, kDevices
);
76 base::ListValue
* devices
= update
.Get();
78 devices
= update
.Create();
81 scoped_ptr
<base::Value
> device_entry(entry
->ToValue());
82 DCHECK(devices
->Find(*device_entry
.get()) == devices
->end());
83 devices
->Append(device_entry
.release());
86 bool MatchesDevicePermissionEntry(const base::DictionaryValue
* value
,
87 scoped_refptr
<DevicePermissionEntry
> entry
) {
89 if (!value
->GetStringWithoutPathExpansion(kDeviceType
, &type
) ||
90 type
!= kDeviceTypeUsb
) {
94 if (!value
->GetIntegerWithoutPathExpansion(kDeviceVendorId
, &vendor_id
) ||
95 vendor_id
!= entry
->vendor_id()) {
99 if (!value
->GetIntegerWithoutPathExpansion(kDeviceProductId
, &product_id
) ||
100 product_id
!= entry
->product_id()) {
103 base::string16 serial_number
;
104 if (!value
->GetStringWithoutPathExpansion(kDeviceSerialNumber
,
106 serial_number
!= entry
->serial_number()) {
112 // Updates the timestamp stored in ExtensionPrefs for the given
113 // DevicePermissionEntry.
114 void UpdateDevicePermissionEntry(BrowserContext
* context
,
115 const std::string
& extension_id
,
116 scoped_refptr
<DevicePermissionEntry
> entry
) {
117 ExtensionPrefs
* prefs
= ExtensionPrefs::Get(context
);
118 ExtensionPrefs::ScopedListUpdate
update(prefs
, extension_id
, kDevices
);
119 base::ListValue
* devices
= update
.Get();
124 for (size_t i
= 0; i
< devices
->GetSize(); ++i
) {
125 base::DictionaryValue
* dict_value
;
126 if (!devices
->GetDictionary(i
, &dict_value
)) {
129 if (!MatchesDevicePermissionEntry(dict_value
, entry
)) {
132 devices
->Set(i
, entry
->ToValue().release());
137 // Removes the given DevicePermissionEntry from ExtensionPrefs.
138 void RemoveDevicePermissionEntry(BrowserContext
* context
,
139 const std::string
& extension_id
,
140 scoped_refptr
<DevicePermissionEntry
> entry
) {
141 ExtensionPrefs
* prefs
= ExtensionPrefs::Get(context
);
142 ExtensionPrefs::ScopedListUpdate
update(prefs
, extension_id
, kDevices
);
143 base::ListValue
* devices
= update
.Get();
148 for (size_t i
= 0; i
< devices
->GetSize(); ++i
) {
149 base::DictionaryValue
* dict_value
;
150 if (!devices
->GetDictionary(i
, &dict_value
)) {
153 if (!MatchesDevicePermissionEntry(dict_value
, entry
)) {
156 devices
->Remove(i
, nullptr);
161 // Clears all DevicePermissionEntries for the app from ExtensionPrefs.
162 void ClearDevicePermissionEntries(ExtensionPrefs
* prefs
,
163 const std::string
& extension_id
) {
164 prefs
->UpdateExtensionPref(extension_id
, kDevices
, NULL
);
167 // Returns all DevicePermissionEntries for the app.
168 std::set
<scoped_refptr
<DevicePermissionEntry
>> GetDevicePermissionEntries(
169 ExtensionPrefs
* prefs
,
170 const std::string
& extension_id
) {
171 std::set
<scoped_refptr
<DevicePermissionEntry
>> result
;
172 const base::ListValue
* devices
= NULL
;
173 if (!prefs
->ReadPrefAsList(extension_id
, kDevices
, &devices
)) {
177 for (const base::Value
* entry
: *devices
) {
178 const base::DictionaryValue
* entry_dict
;
179 if (!entry
->GetAsDictionary(&entry_dict
)) {
183 if (!entry_dict
->GetStringWithoutPathExpansion(kDeviceType
, &type
) ||
184 type
!= kDeviceTypeUsb
) {
188 if (!entry_dict
->GetIntegerWithoutPathExpansion(kDeviceVendorId
,
190 vendor_id
< 0 || vendor_id
> UINT16_MAX
) {
194 if (!entry_dict
->GetIntegerWithoutPathExpansion(kDeviceProductId
,
196 product_id
< 0 || product_id
> UINT16_MAX
) {
199 base::string16 serial_number
;
200 if (!entry_dict
->GetStringWithoutPathExpansion(kDeviceSerialNumber
,
205 base::string16 manufacturer_string
;
206 // Ignore failure as this string is optional.
207 entry_dict
->GetStringWithoutPathExpansion(kDeviceManufacturerString
,
208 &manufacturer_string
);
210 base::string16 product_string
;
211 // Ignore failure as this string is optional.
212 entry_dict
->GetStringWithoutPathExpansion(kDeviceProductString
,
215 // If a last used time is not stored in ExtensionPrefs last_used.is_null()
217 std::string last_used_str
;
218 int64 last_used_i64
= 0;
219 base::Time last_used
;
220 if (entry_dict
->GetStringWithoutPathExpansion(kDeviceLastUsed
,
222 base::StringToInt64(last_used_str
, &last_used_i64
)) {
223 last_used
= base::Time::FromInternalValue(last_used_i64
);
226 result
.insert(new DevicePermissionEntry(vendor_id
, product_id
,
227 serial_number
, manufacturer_string
,
228 product_string
, last_used
));
235 DevicePermissionEntry::DevicePermissionEntry(
236 scoped_refptr
<device::UsbDevice
> device
)
238 vendor_id_(device
->vendor_id()),
239 product_id_(device
->product_id()),
240 serial_number_(device
->serial_number()),
241 manufacturer_string_(device
->manufacturer_string()),
242 product_string_(device
->product_string()) {
245 DevicePermissionEntry::DevicePermissionEntry(
248 const base::string16
& serial_number
,
249 const base::string16
& manufacturer_string
,
250 const base::string16
& product_string
,
251 const base::Time
& last_used
)
252 : vendor_id_(vendor_id
),
253 product_id_(product_id
),
254 serial_number_(serial_number
),
255 manufacturer_string_(manufacturer_string
),
256 product_string_(product_string
),
257 last_used_(last_used
) {
260 DevicePermissionEntry::~DevicePermissionEntry() {
263 bool DevicePermissionEntry::IsPersistent() const {
264 return !serial_number_
.empty();
267 scoped_ptr
<base::Value
> DevicePermissionEntry::ToValue() const {
268 if (!IsPersistent()) {
272 scoped_ptr
<base::DictionaryValue
> entry_dict(new base::DictionaryValue());
273 entry_dict
->SetStringWithoutPathExpansion(kDeviceType
, kDeviceTypeUsb
);
274 entry_dict
->SetIntegerWithoutPathExpansion(kDeviceVendorId
, vendor_id_
);
275 entry_dict
->SetIntegerWithoutPathExpansion(kDeviceProductId
, product_id_
);
276 DCHECK(!serial_number_
.empty());
277 entry_dict
->SetStringWithoutPathExpansion(kDeviceSerialNumber
,
279 if (!manufacturer_string_
.empty()) {
280 entry_dict
->SetStringWithoutPathExpansion(kDeviceManufacturerString
,
281 manufacturer_string_
);
283 if (!product_string_
.empty()) {
284 entry_dict
->SetStringWithoutPathExpansion(kDeviceProductString
,
287 if (!last_used_
.is_null()) {
288 entry_dict
->SetStringWithoutPathExpansion(
289 kDeviceLastUsed
, base::Int64ToString(last_used_
.ToInternalValue()));
292 return entry_dict
.Pass();
295 base::string16
DevicePermissionEntry::GetPermissionMessageString() const {
296 if (serial_number_
.empty()) {
297 return l10n_util::GetStringFUTF16(IDS_DEVICE_PERMISSIONS_DEVICE_NAME
,
298 GetProduct(), GetManufacturer());
300 return l10n_util::GetStringFUTF16(
301 IDS_EXTENSION_PROMPT_WARNING_USB_DEVICE_SERIAL
, GetProduct(),
302 GetManufacturer(), serial_number_
);
306 base::string16
DevicePermissionEntry::GetManufacturer() const {
307 if (manufacturer_string_
.empty()) {
308 const char* vendor_name
= device::UsbIds::GetVendorName(vendor_id_
);
310 return base::UTF8ToUTF16(vendor_name
);
312 return l10n_util::GetStringFUTF16(
313 IDS_DEVICE_UNKNOWN_VENDOR
,
314 base::ASCIIToUTF16(base::StringPrintf("0x%04x", vendor_id_
)));
317 return manufacturer_string_
;
321 base::string16
DevicePermissionEntry::GetProduct() const {
322 if (product_string_
.empty()) {
323 const char* product_name
=
324 device::UsbIds::GetProductName(vendor_id_
, product_id_
);
326 return base::UTF8ToUTF16(product_name
);
328 return l10n_util::GetStringFUTF16(
329 IDS_DEVICE_UNKNOWN_PRODUCT
,
330 base::ASCIIToUTF16(base::StringPrintf("0x%04x", product_id_
)));
333 return product_string_
;
337 DevicePermissions::~DevicePermissions() {
340 scoped_refptr
<DevicePermissionEntry
> DevicePermissions::FindEntry(
341 scoped_refptr
<device::UsbDevice
> device
) const {
342 const auto& ephemeral_device_entry
= ephemeral_devices_
.find(device
);
343 if (ephemeral_device_entry
!= ephemeral_devices_
.end()) {
344 return ephemeral_device_entry
->second
;
347 if (device
->serial_number().empty()) {
351 for (const auto& entry
: entries_
) {
352 if (!entry
->IsPersistent()) {
355 if (entry
->vendor_id() != device
->vendor_id()) {
358 if (entry
->product_id() != device
->product_id()) {
361 if (entry
->serial_number() != device
->serial_number()) {
369 DevicePermissions::DevicePermissions(BrowserContext
* context
,
370 const std::string
& extension_id
) {
371 ExtensionPrefs
* prefs
= ExtensionPrefs::Get(context
);
372 entries_
= GetDevicePermissionEntries(prefs
, extension_id
);
376 DevicePermissionsManager
* DevicePermissionsManager::Get(
377 BrowserContext
* context
) {
378 return DevicePermissionsManagerFactory::GetForBrowserContext(context
);
381 DevicePermissions
* DevicePermissionsManager::GetForExtension(
382 const std::string
& extension_id
) {
383 DCHECK(CalledOnValidThread());
384 DevicePermissions
* device_permissions
= GetInternal(extension_id
);
385 if (!device_permissions
) {
386 device_permissions
= new DevicePermissions(context_
, extension_id
);
387 extension_id_to_device_permissions_
[extension_id
] = device_permissions
;
390 return device_permissions
;
393 std::vector
<base::string16
>
394 DevicePermissionsManager::GetPermissionMessageStrings(
395 const std::string
& extension_id
) const {
396 DCHECK(CalledOnValidThread());
397 std::vector
<base::string16
> messages
;
398 const DevicePermissions
* device_permissions
= GetInternal(extension_id
);
399 if (device_permissions
) {
400 for (const scoped_refptr
<DevicePermissionEntry
>& entry
:
401 device_permissions
->entries()) {
402 messages
.push_back(entry
->GetPermissionMessageString());
408 void DevicePermissionsManager::AllowUsbDevice(
409 const std::string
& extension_id
,
410 scoped_refptr
<device::UsbDevice
> device
) {
411 DCHECK(CalledOnValidThread());
412 DevicePermissions
* device_permissions
= GetForExtension(extension_id
);
414 scoped_refptr
<DevicePermissionEntry
> device_entry(
415 new DevicePermissionEntry(device
));
417 if (device_entry
->IsPersistent()) {
418 for (const auto& entry
: device_permissions
->entries()) {
419 if (entry
->vendor_id() != device_entry
->vendor_id()) {
422 if (entry
->product_id() != device_entry
->product_id()) {
425 if (entry
->serial_number() == device_entry
->serial_number()) {
430 device_permissions
->entries_
.insert(device_entry
);
431 SaveDevicePermissionEntry(context_
, extension_id
, device_entry
);
432 } else if (!ContainsKey(device_permissions
->ephemeral_devices_
, device
)) {
433 // Non-persistent devices cannot be reliably identified when they are
434 // reconnected so such devices are only remembered until disconnect.
435 // Register an observer here so that this set doesn't grow undefinitely.
436 device_permissions
->entries_
.insert(device_entry
);
437 device_permissions
->ephemeral_devices_
[device
] = device_entry
;
439 // Only start observing when an ephemeral device has been added so that
440 // UsbService is not automatically initialized on profile creation (which it
441 // would be if this call were in the constructor).
442 UsbService
* usb_service
= device::DeviceClient::Get()->GetUsbService();
443 if (!usb_service_observer_
.IsObserving(usb_service
)) {
444 usb_service_observer_
.Add(usb_service
);
449 void DevicePermissionsManager::UpdateLastUsed(
450 const std::string
& extension_id
,
451 scoped_refptr
<DevicePermissionEntry
> entry
) {
452 DCHECK(CalledOnValidThread());
453 entry
->set_last_used(base::Time::Now());
454 if (entry
->IsPersistent()) {
455 UpdateDevicePermissionEntry(context_
, extension_id
, entry
);
459 void DevicePermissionsManager::RemoveEntry(
460 const std::string
& extension_id
,
461 scoped_refptr
<DevicePermissionEntry
> entry
) {
462 DCHECK(CalledOnValidThread());
463 DevicePermissions
* device_permissions
= GetInternal(extension_id
);
464 DCHECK(device_permissions
);
465 DCHECK(ContainsKey(device_permissions
->entries_
, entry
));
466 device_permissions
->entries_
.erase(entry
);
467 if (entry
->IsPersistent()) {
468 RemoveDevicePermissionEntry(context_
, extension_id
, entry
);
470 device_permissions
->ephemeral_devices_
.erase(entry
->device_
);
474 void DevicePermissionsManager::Clear(const std::string
& extension_id
) {
475 DCHECK(CalledOnValidThread());
477 ClearDevicePermissionEntries(ExtensionPrefs::Get(context_
), extension_id
);
478 DevicePermissions
* device_permissions
= GetInternal(extension_id
);
479 if (device_permissions
) {
480 extension_id_to_device_permissions_
.erase(extension_id
);
481 delete device_permissions
;
485 DevicePermissionsManager::DevicePermissionsManager(
486 content::BrowserContext
* context
)
488 process_manager_observer_(this),
489 usb_service_observer_(this) {
490 process_manager_observer_
.Add(ProcessManager::Get(context
));
493 DevicePermissionsManager::~DevicePermissionsManager() {
494 for (const auto& map_entry
: extension_id_to_device_permissions_
) {
495 DevicePermissions
* device_permissions
= map_entry
.second
;
496 delete device_permissions
;
500 DevicePermissions
* DevicePermissionsManager::GetInternal(
501 const std::string
& extension_id
) const {
502 std::map
<std::string
, DevicePermissions
*>::const_iterator it
=
503 extension_id_to_device_permissions_
.find(extension_id
);
504 if (it
!= extension_id_to_device_permissions_
.end()) {
511 void DevicePermissionsManager::OnBackgroundHostClose(
512 const std::string
& extension_id
) {
513 DCHECK(CalledOnValidThread());
515 DevicePermissions
* device_permissions
= GetInternal(extension_id
);
516 if (device_permissions
) {
517 // When all of the app's windows are closed and the background page is
518 // suspended all ephemeral device permissions are cleared.
519 for (const auto& map_entry
: device_permissions
->ephemeral_devices_
) {
520 device_permissions
->entries_
.erase(map_entry
.second
);
522 device_permissions
->ephemeral_devices_
.clear();
526 void DevicePermissionsManager::OnDeviceRemovedCleanup(
527 scoped_refptr
<UsbDevice
> device
) {
528 DCHECK(CalledOnValidThread());
529 for (const auto& map_entry
: extension_id_to_device_permissions_
) {
530 // An ephemeral device cannot be identified if it is reconnected and so
531 // permission to access it is cleared on disconnect.
532 DevicePermissions
* device_permissions
= map_entry
.second
;
533 const auto& device_entry
=
534 device_permissions
->ephemeral_devices_
.find(device
);
535 if (device_entry
!= device_permissions
->ephemeral_devices_
.end()) {
536 device_permissions
->entries_
.erase(device_entry
->second
);
537 device_permissions
->ephemeral_devices_
.erase(device
);
543 DevicePermissionsManager
* DevicePermissionsManagerFactory::GetForBrowserContext(
544 content::BrowserContext
* context
) {
545 return static_cast<DevicePermissionsManager
*>(
546 GetInstance()->GetServiceForBrowserContext(context
, true));
550 DevicePermissionsManagerFactory
*
551 DevicePermissionsManagerFactory::GetInstance() {
552 return Singleton
<DevicePermissionsManagerFactory
>::get();
555 DevicePermissionsManagerFactory::DevicePermissionsManagerFactory()
556 : BrowserContextKeyedServiceFactory(
557 "DevicePermissionsManager",
558 BrowserContextDependencyManager::GetInstance()) {
559 DependsOn(ProcessManagerFactory::GetInstance());
562 DevicePermissionsManagerFactory::~DevicePermissionsManagerFactory() {
565 KeyedService
* DevicePermissionsManagerFactory::BuildServiceInstanceFor(
566 content::BrowserContext
* context
) const {
567 return new DevicePermissionsManager(context
);
570 BrowserContext
* DevicePermissionsManagerFactory::GetBrowserContextToUse(
571 BrowserContext
* context
) const {
572 // Return the original (possibly off-the-record) browser context so that a
573 // separate instance of the DevicePermissionsManager is used in incognito
574 // mode. The parent class's implemenation returns NULL.
578 } // namespace extensions