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"
7 #include "base/memory/singleton.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "base/values.h"
12 #include "components/keyed_service/content/browser_context_dependency_manager.h"
13 #include "device/usb/usb_ids.h"
14 #include "extensions/browser/extension_host.h"
15 #include "extensions/browser/extension_prefs.h"
16 #include "extensions/browser/extensions_browser_client.h"
17 #include "extensions/browser/process_manager.h"
18 #include "extensions/browser/process_manager_factory.h"
19 #include "extensions/strings/grit/extensions_strings.h"
20 #include "ui/base/l10n/l10n_util.h"
22 namespace extensions
{
24 using content::BrowserContext
;
25 using device::UsbDevice
;
26 using extensions::APIPermission
;
27 using extensions::Extension
;
28 using extensions::ExtensionHost
;
29 using extensions::ExtensionPrefs
;
35 // The device that the app has permission to access.
36 const char kDevices
[] = "devices";
38 // The type of device saved.
39 const char kDeviceType
[] = "type";
41 // Type identifier for USB devices.
42 const char kDeviceTypeUsb
[] = "usb";
44 // The vendor ID of the device that the app had permission to access.
45 const char kDeviceVendorId
[] = "vendor_id";
47 // The product ID of the device that the app had permission to access.
48 const char kDeviceProductId
[] = "product_id";
50 // The serial number of the device that the app has permission to access.
51 const char kDeviceSerialNumber
[] = "serial_number";
53 // The manufacturer string read from the device that the app has permission to
55 const char kDeviceManufacturerString
[] = "manufacturer_string";
57 // The product string read from the device that the app has permission to
59 const char kDeviceProductString
[] = "product_string";
61 // Serialized timestamp of the last time when the device was opened by the app.
62 const char kDeviceLastUsed
[] = "last_used_time";
64 // Persists a DevicePermissionEntry in ExtensionPrefs.
65 void SaveDevicePermissionEntry(BrowserContext
* context
,
66 const std::string
& extension_id
,
67 scoped_refptr
<DevicePermissionEntry
> entry
) {
68 ExtensionPrefs
* prefs
= ExtensionPrefs::Get(context
);
69 ExtensionPrefs::ScopedListUpdate
update(prefs
, extension_id
, kDevices
);
70 base::ListValue
* devices
= update
.Get();
72 devices
= update
.Create();
75 scoped_ptr
<base::Value
> device_entry(entry
->ToValue());
76 DCHECK(devices
->Find(*device_entry
.get()) == devices
->end());
77 devices
->Append(device_entry
.release());
80 bool MatchesDevicePermissionEntry(const base::DictionaryValue
* value
,
81 scoped_refptr
<DevicePermissionEntry
> entry
) {
83 if (!value
->GetStringWithoutPathExpansion(kDeviceType
, &type
) ||
84 type
!= kDeviceTypeUsb
) {
88 if (!value
->GetIntegerWithoutPathExpansion(kDeviceVendorId
, &vendor_id
) ||
89 vendor_id
!= entry
->vendor_id()) {
93 if (!value
->GetIntegerWithoutPathExpansion(kDeviceProductId
, &product_id
) ||
94 product_id
!= entry
->product_id()) {
97 base::string16 serial_number
;
98 if (!value
->GetStringWithoutPathExpansion(kDeviceSerialNumber
,
100 serial_number
!= entry
->serial_number()) {
106 // Updates the timestamp stored in ExtensionPrefs for the given
107 // DevicePermissionEntry.
108 void UpdateDevicePermissionEntry(BrowserContext
* context
,
109 const std::string
& extension_id
,
110 scoped_refptr
<DevicePermissionEntry
> entry
) {
111 ExtensionPrefs
* prefs
= ExtensionPrefs::Get(context
);
112 ExtensionPrefs::ScopedListUpdate
update(prefs
, extension_id
, kDevices
);
113 base::ListValue
* devices
= update
.Get();
118 for (size_t i
= 0; i
< devices
->GetSize(); ++i
) {
119 base::DictionaryValue
* dict_value
;
120 if (!devices
->GetDictionary(i
, &dict_value
)) {
123 if (!MatchesDevicePermissionEntry(dict_value
, entry
)) {
126 devices
->Set(i
, entry
->ToValue().release());
131 // Removes the given DevicePermissionEntry from ExtensionPrefs.
132 void RemoveDevicePermissionEntry(BrowserContext
* context
,
133 const std::string
& extension_id
,
134 scoped_refptr
<DevicePermissionEntry
> entry
) {
135 ExtensionPrefs
* prefs
= ExtensionPrefs::Get(context
);
136 ExtensionPrefs::ScopedListUpdate
update(prefs
, extension_id
, kDevices
);
137 base::ListValue
* devices
= update
.Get();
142 for (size_t i
= 0; i
< devices
->GetSize(); ++i
) {
143 base::DictionaryValue
* dict_value
;
144 if (!devices
->GetDictionary(i
, &dict_value
)) {
147 if (!MatchesDevicePermissionEntry(dict_value
, entry
)) {
150 devices
->Remove(i
, nullptr);
155 // Clears all DevicePermissionEntries for the app from ExtensionPrefs.
156 void ClearDevicePermissionEntries(ExtensionPrefs
* prefs
,
157 const std::string
& extension_id
) {
158 prefs
->UpdateExtensionPref(extension_id
, kDevices
, NULL
);
161 // Returns all DevicePermissionEntries for the app.
162 std::set
<scoped_refptr
<DevicePermissionEntry
>> GetDevicePermissionEntries(
163 ExtensionPrefs
* prefs
,
164 const std::string
& extension_id
) {
165 std::set
<scoped_refptr
<DevicePermissionEntry
>> result
;
166 const base::ListValue
* devices
= NULL
;
167 if (!prefs
->ReadPrefAsList(extension_id
, kDevices
, &devices
)) {
171 for (const base::Value
* entry
: *devices
) {
172 const base::DictionaryValue
* entry_dict
;
173 if (!entry
->GetAsDictionary(&entry_dict
)) {
177 if (!entry_dict
->GetStringWithoutPathExpansion(kDeviceType
, &type
) ||
178 type
!= kDeviceTypeUsb
) {
182 if (!entry_dict
->GetIntegerWithoutPathExpansion(kDeviceVendorId
,
184 vendor_id
< 0 || vendor_id
> UINT16_MAX
) {
188 if (!entry_dict
->GetIntegerWithoutPathExpansion(kDeviceProductId
,
190 product_id
< 0 || product_id
> UINT16_MAX
) {
193 base::string16 serial_number
;
194 if (!entry_dict
->GetStringWithoutPathExpansion(kDeviceSerialNumber
,
199 base::string16 manufacturer_string
;
200 // Ignore failure as this string is optional.
201 entry_dict
->GetStringWithoutPathExpansion(kDeviceManufacturerString
,
202 &manufacturer_string
);
204 base::string16 product_string
;
205 // Ignore failure as this string is optional.
206 entry_dict
->GetStringWithoutPathExpansion(kDeviceProductString
,
209 // If a last used time is not stored in ExtensionPrefs last_used.is_null()
211 std::string last_used_str
;
212 int64 last_used_i64
= 0;
213 base::Time last_used
;
214 if (entry_dict
->GetStringWithoutPathExpansion(kDeviceLastUsed
,
216 base::StringToInt64(last_used_str
, &last_used_i64
)) {
217 last_used
= base::Time::FromInternalValue(last_used_i64
);
220 result
.insert(new DevicePermissionEntry(vendor_id
, product_id
,
221 serial_number
, manufacturer_string
,
222 product_string
, last_used
));
229 DevicePermissionEntry::DevicePermissionEntry(
230 scoped_refptr
<device::UsbDevice
> device
,
231 const base::string16
& serial_number
,
232 const base::string16
& manufacturer_string
,
233 const base::string16
& product_string
)
235 vendor_id_(device
->vendor_id()),
236 product_id_(device
->product_id()),
237 serial_number_(serial_number
),
238 manufacturer_string_(manufacturer_string
),
239 product_string_(product_string
) {
242 DevicePermissionEntry::DevicePermissionEntry(
245 const base::string16
& serial_number
,
246 const base::string16
& manufacturer_string
,
247 const base::string16
& product_string
,
248 const base::Time
& last_used
)
249 : vendor_id_(vendor_id
),
250 product_id_(product_id
),
251 serial_number_(serial_number
),
252 manufacturer_string_(manufacturer_string
),
253 product_string_(product_string
),
254 last_used_(last_used
) {
257 DevicePermissionEntry::~DevicePermissionEntry() {
260 bool DevicePermissionEntry::IsPersistent() const {
261 return !serial_number_
.empty();
264 scoped_ptr
<base::Value
> DevicePermissionEntry::ToValue() const {
265 if (!IsPersistent()) {
269 scoped_ptr
<base::DictionaryValue
> entry_dict(new base::DictionaryValue());
270 entry_dict
->SetStringWithoutPathExpansion(kDeviceType
, kDeviceTypeUsb
);
271 entry_dict
->SetIntegerWithoutPathExpansion(kDeviceVendorId
, vendor_id_
);
272 entry_dict
->SetIntegerWithoutPathExpansion(kDeviceProductId
, product_id_
);
273 DCHECK(!serial_number_
.empty());
274 entry_dict
->SetStringWithoutPathExpansion(kDeviceSerialNumber
,
276 if (!manufacturer_string_
.empty()) {
277 entry_dict
->SetStringWithoutPathExpansion(kDeviceManufacturerString
,
278 manufacturer_string_
);
280 if (!product_string_
.empty()) {
281 entry_dict
->SetStringWithoutPathExpansion(kDeviceProductString
,
284 if (!last_used_
.is_null()) {
285 entry_dict
->SetStringWithoutPathExpansion(
286 kDeviceLastUsed
, base::Int64ToString(last_used_
.ToInternalValue()));
289 return entry_dict
.Pass();
292 base::string16
DevicePermissionEntry::GetPermissionMessageString() const {
293 if (serial_number_
.empty()) {
294 return l10n_util::GetStringFUTF16(IDS_DEVICE_PERMISSIONS_DEVICE_NAME
,
295 GetProduct(), GetManufacturer());
297 return l10n_util::GetStringFUTF16(
298 IDS_EXTENSION_PROMPT_WARNING_USB_DEVICE_SERIAL
, GetProduct(),
299 GetManufacturer(), serial_number_
);
303 base::string16
DevicePermissionEntry::GetManufacturer() const {
304 if (manufacturer_string_
.empty()) {
305 const char* vendor_name
= device::UsbIds::GetVendorName(vendor_id_
);
307 return base::UTF8ToUTF16(vendor_name
);
309 return l10n_util::GetStringFUTF16(
310 IDS_DEVICE_UNKNOWN_VENDOR
,
311 base::ASCIIToUTF16(base::StringPrintf("0x%04x", vendor_id_
)));
314 return manufacturer_string_
;
318 base::string16
DevicePermissionEntry::GetProduct() const {
319 if (product_string_
.empty()) {
320 const char* product_name
=
321 device::UsbIds::GetProductName(vendor_id_
, product_id_
);
323 return base::UTF8ToUTF16(product_name
);
325 return l10n_util::GetStringFUTF16(
326 IDS_DEVICE_UNKNOWN_PRODUCT
,
327 base::ASCIIToUTF16(base::StringPrintf("0x%04x", product_id_
)));
330 return product_string_
;
334 DevicePermissions::~DevicePermissions() {
337 scoped_refptr
<DevicePermissionEntry
> DevicePermissions::FindEntry(
338 scoped_refptr
<device::UsbDevice
> device
) const {
339 const auto& ephemeral_device_entry
= ephemeral_devices_
.find(device
);
340 if (ephemeral_device_entry
!= ephemeral_devices_
.end()) {
341 return ephemeral_device_entry
->second
;
344 bool have_serial_number
= false;
345 base::string16 serial_number
;
346 for (const auto& entry
: entries_
) {
347 if (!entry
->IsPersistent()) {
350 if (entry
->vendor_id() != device
->vendor_id()) {
353 if (entry
->product_id() != device
->product_id()) {
356 if (!have_serial_number
) {
357 if (!device
->GetSerialNumber(&serial_number
)) {
360 have_serial_number
= true;
362 if (entry
->serial_number() != serial_number
) {
370 DevicePermissions::DevicePermissions(BrowserContext
* context
,
371 const std::string
& extension_id
) {
372 ExtensionPrefs
* prefs
= ExtensionPrefs::Get(context
);
373 entries_
= GetDevicePermissionEntries(prefs
, extension_id
);
376 DevicePermissions::DevicePermissions(const DevicePermissions
* original
)
377 : entries_(original
->entries_
),
378 ephemeral_devices_(original
->ephemeral_devices_
) {
382 DevicePermissionsManager
* DevicePermissionsManager::Get(
383 BrowserContext
* context
) {
384 return DevicePermissionsManagerFactory::GetForBrowserContext(context
);
387 scoped_ptr
<DevicePermissions
> DevicePermissionsManager::GetForExtension(
388 const std::string
& extension_id
) {
389 DCHECK(CalledOnValidThread());
390 return make_scoped_ptr(new DevicePermissions(GetOrInsert(extension_id
)));
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
= Get(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 const base::string16
& product_string
,
412 const base::string16
& manufacturer_string
,
413 const base::string16
& serial_number
) {
414 DCHECK(CalledOnValidThread());
415 DevicePermissions
* device_permissions
= GetOrInsert(extension_id
);
417 scoped_refptr
<DevicePermissionEntry
> device_entry(new DevicePermissionEntry(
418 device
, serial_number
, manufacturer_string
, product_string
));
420 if (device_entry
->IsPersistent()) {
421 for (const auto& entry
: device_permissions
->entries()) {
422 if (entry
->vendor_id() != device_entry
->vendor_id()) {
425 if (entry
->product_id() != device_entry
->product_id()) {
428 if (entry
->serial_number() == device_entry
->serial_number()) {
433 device_permissions
->entries_
.insert(device_entry
);
434 SaveDevicePermissionEntry(context_
, extension_id
, device_entry
);
435 } else if (!ContainsKey(device_permissions
->ephemeral_devices_
, device
)) {
436 // Non-persistent devices cannot be reliably identified when they are
437 // reconnected so such devices are only remembered until disconnect.
438 // Register an observer here so that this set doesn't grow undefinitely.
439 device_permissions
->entries_
.insert(device_entry
);
440 device_permissions
->ephemeral_devices_
[device
] = device_entry
;
441 device
->AddObserver(this);
445 void DevicePermissionsManager::UpdateLastUsed(
446 const std::string
& extension_id
,
447 scoped_refptr
<DevicePermissionEntry
> entry
) {
448 DCHECK(CalledOnValidThread());
449 entry
->set_last_used(base::Time::Now());
450 if (entry
->IsPersistent()) {
451 UpdateDevicePermissionEntry(context_
, extension_id
, entry
);
455 void DevicePermissionsManager::RemoveEntry(
456 const std::string
& extension_id
,
457 scoped_refptr
<DevicePermissionEntry
> entry
) {
458 DCHECK(CalledOnValidThread());
459 DevicePermissions
* device_permissions
= Get(extension_id
);
460 DCHECK(device_permissions
);
461 DCHECK(ContainsKey(device_permissions
->entries_
, entry
));
462 device_permissions
->entries_
.erase(entry
);
463 if (entry
->IsPersistent()) {
464 RemoveDevicePermissionEntry(context_
, extension_id
, entry
);
466 device_permissions
->ephemeral_devices_
.erase(entry
->device_
);
467 entry
->device_
->RemoveObserver(this);
471 void DevicePermissionsManager::Clear(const std::string
& extension_id
) {
472 DCHECK(CalledOnValidThread());
474 ClearDevicePermissionEntries(ExtensionPrefs::Get(context_
), extension_id
);
475 DevicePermissions
* device_permissions
= Get(extension_id
);
476 if (device_permissions
) {
477 for (const auto& device_entry
: device_permissions
->ephemeral_devices_
) {
478 device_entry
.first
->RemoveObserver(this);
480 extension_id_to_device_permissions_
.erase(extension_id
);
481 delete device_permissions
;
485 DevicePermissionsManager::DevicePermissionsManager(
486 content::BrowserContext
* context
)
487 : context_(context
), process_manager_observer_(this) {
488 process_manager_observer_
.Add(ProcessManager::Get(context
));
491 DevicePermissionsManager::~DevicePermissionsManager() {
492 for (const auto& map_entry
: extension_id_to_device_permissions_
) {
493 DevicePermissions
* device_permissions
= map_entry
.second
;
494 for (const auto& device_entry
: device_permissions
->ephemeral_devices_
) {
495 device_entry
.first
->RemoveObserver(this);
497 delete device_permissions
;
501 DevicePermissions
* DevicePermissionsManager::Get(
502 const std::string
& extension_id
) const {
503 std::map
<std::string
, DevicePermissions
*>::const_iterator it
=
504 extension_id_to_device_permissions_
.find(extension_id
);
505 if (it
!= extension_id_to_device_permissions_
.end()) {
512 DevicePermissions
* DevicePermissionsManager::GetOrInsert(
513 const std::string
& extension_id
) {
514 DevicePermissions
* device_permissions
= Get(extension_id
);
515 if (!device_permissions
) {
516 device_permissions
= new DevicePermissions(context_
, extension_id
);
517 extension_id_to_device_permissions_
[extension_id
] = device_permissions
;
520 return device_permissions
;
523 void DevicePermissionsManager::OnBackgroundHostClose(
524 const std::string
& extension_id
) {
525 DCHECK(CalledOnValidThread());
527 DevicePermissions
* device_permissions
= Get(extension_id
);
528 if (device_permissions
) {
529 // When all of the app's windows are closed and the background page is
530 // suspended all ephemeral device permissions are cleared.
531 for (const auto& map_entry
: device_permissions
->ephemeral_devices_
) {
532 map_entry
.first
->RemoveObserver(this);
533 device_permissions
->entries_
.erase(map_entry
.second
);
535 device_permissions
->ephemeral_devices_
.clear();
539 void DevicePermissionsManager::OnDisconnect(scoped_refptr
<UsbDevice
> device
) {
540 for (const auto& map_entry
: extension_id_to_device_permissions_
) {
541 // An ephemeral device cannot be identified if it is reconnected and so
542 // permission to access it is cleared on disconnect.
543 DevicePermissions
* device_permissions
= map_entry
.second
;
544 const auto& device_entry
=
545 device_permissions
->ephemeral_devices_
.find(device
);
546 DCHECK(device_entry
!= device_permissions
->ephemeral_devices_
.end());
547 device_permissions
->entries_
.erase(device_entry
->second
);
548 device_permissions
->ephemeral_devices_
.erase(device
);
549 device
->RemoveObserver(this);
554 DevicePermissionsManager
* DevicePermissionsManagerFactory::GetForBrowserContext(
555 content::BrowserContext
* context
) {
556 return static_cast<DevicePermissionsManager
*>(
557 GetInstance()->GetServiceForBrowserContext(context
, true));
561 DevicePermissionsManagerFactory
*
562 DevicePermissionsManagerFactory::GetInstance() {
563 return Singleton
<DevicePermissionsManagerFactory
>::get();
566 DevicePermissionsManagerFactory::DevicePermissionsManagerFactory()
567 : BrowserContextKeyedServiceFactory(
568 "DevicePermissionsManager",
569 BrowserContextDependencyManager::GetInstance()) {
570 DependsOn(ProcessManagerFactory::GetInstance());
573 DevicePermissionsManagerFactory::~DevicePermissionsManagerFactory() {
576 KeyedService
* DevicePermissionsManagerFactory::BuildServiceInstanceFor(
577 content::BrowserContext
* context
) const {
578 return new DevicePermissionsManager(context
);
581 BrowserContext
* DevicePermissionsManagerFactory::GetBrowserContextToUse(
582 BrowserContext
* context
) const {
583 // Return the original (possibly off-the-record) browser context so that a
584 // separate instance of the DevicePermissionsManager is used in incognito
585 // mode. The parent class's implemenation returns NULL.
589 } // namespace extensions