Supervised user import: Listen for profile creation/deletion
[chromium-blink-merge.git] / extensions / browser / api / device_permissions_manager.cc
blobd64c0271c43742c30a0fb3a96245e7739a4fa93d
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/bind.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;
37 namespace {
39 // Preference keys
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
60 // access.
61 const char kDeviceManufacturerString[] = "manufacturer_string";
63 // The product string read from the device that the app has permission to
64 // access.
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();
77 if (!devices) {
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) {
88 std::string type;
89 if (!value->GetStringWithoutPathExpansion(kDeviceType, &type) ||
90 type != kDeviceTypeUsb) {
91 return false;
93 int vendor_id;
94 if (!value->GetIntegerWithoutPathExpansion(kDeviceVendorId, &vendor_id) ||
95 vendor_id != entry->vendor_id()) {
96 return false;
98 int product_id;
99 if (!value->GetIntegerWithoutPathExpansion(kDeviceProductId, &product_id) ||
100 product_id != entry->product_id()) {
101 return false;
103 base::string16 serial_number;
104 if (!value->GetStringWithoutPathExpansion(kDeviceSerialNumber,
105 &serial_number) ||
106 serial_number != entry->serial_number()) {
107 return false;
109 return true;
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();
120 if (!devices) {
121 return;
124 for (size_t i = 0; i < devices->GetSize(); ++i) {
125 base::DictionaryValue* dict_value;
126 if (!devices->GetDictionary(i, &dict_value)) {
127 continue;
129 if (!MatchesDevicePermissionEntry(dict_value, entry)) {
130 continue;
132 devices->Set(i, entry->ToValue().release());
133 break;
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();
144 if (!devices) {
145 return;
148 for (size_t i = 0; i < devices->GetSize(); ++i) {
149 base::DictionaryValue* dict_value;
150 if (!devices->GetDictionary(i, &dict_value)) {
151 continue;
153 if (!MatchesDevicePermissionEntry(dict_value, entry)) {
154 continue;
156 devices->Remove(i, nullptr);
157 break;
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)) {
174 return result;
177 for (const base::Value* entry : *devices) {
178 const base::DictionaryValue* entry_dict;
179 if (!entry->GetAsDictionary(&entry_dict)) {
180 continue;
182 std::string type;
183 if (!entry_dict->GetStringWithoutPathExpansion(kDeviceType, &type) ||
184 type != kDeviceTypeUsb) {
185 continue;
187 int vendor_id;
188 if (!entry_dict->GetIntegerWithoutPathExpansion(kDeviceVendorId,
189 &vendor_id) ||
190 vendor_id < 0 || vendor_id > UINT16_MAX) {
191 continue;
193 int product_id;
194 if (!entry_dict->GetIntegerWithoutPathExpansion(kDeviceProductId,
195 &product_id) ||
196 product_id < 0 || product_id > UINT16_MAX) {
197 continue;
199 base::string16 serial_number;
200 if (!entry_dict->GetStringWithoutPathExpansion(kDeviceSerialNumber,
201 &serial_number)) {
202 continue;
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,
213 &product_string);
215 // If a last used time is not stored in ExtensionPrefs last_used.is_null()
216 // will be true.
217 std::string last_used_str;
218 int64 last_used_i64 = 0;
219 base::Time last_used;
220 if (entry_dict->GetStringWithoutPathExpansion(kDeviceLastUsed,
221 &last_used_str) &&
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));
230 return result;
233 } // namespace
235 DevicePermissionEntry::DevicePermissionEntry(
236 scoped_refptr<device::UsbDevice> device)
237 : device_(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(
246 uint16_t vendor_id,
247 uint16_t product_id,
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()) {
269 return nullptr;
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,
278 serial_number_);
279 if (!manufacturer_string_.empty()) {
280 entry_dict->SetStringWithoutPathExpansion(kDeviceManufacturerString,
281 manufacturer_string_);
283 if (!product_string_.empty()) {
284 entry_dict->SetStringWithoutPathExpansion(kDeviceProductString,
285 product_string_);
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());
299 } else {
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_);
309 if (vendor_name) {
310 return base::UTF8ToUTF16(vendor_name);
311 } else {
312 return l10n_util::GetStringFUTF16(
313 IDS_DEVICE_UNKNOWN_VENDOR,
314 base::ASCIIToUTF16(base::StringPrintf("0x%04x", vendor_id_)));
316 } else {
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_);
325 if (product_name) {
326 return base::UTF8ToUTF16(product_name);
327 } else {
328 return l10n_util::GetStringFUTF16(
329 IDS_DEVICE_UNKNOWN_PRODUCT,
330 base::ASCIIToUTF16(base::StringPrintf("0x%04x", product_id_)));
332 } else {
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()) {
348 return nullptr;
351 for (const auto& entry : entries_) {
352 if (!entry->IsPersistent()) {
353 continue;
355 if (entry->vendor_id() != device->vendor_id()) {
356 continue;
358 if (entry->product_id() != device->product_id()) {
359 continue;
361 if (entry->serial_number() != device->serial_number()) {
362 continue;
364 return entry;
366 return nullptr;
369 DevicePermissions::DevicePermissions(BrowserContext* context,
370 const std::string& extension_id) {
371 ExtensionPrefs* prefs = ExtensionPrefs::Get(context);
372 entries_ = GetDevicePermissionEntries(prefs, extension_id);
375 // static
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());
405 return messages;
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()) {
420 continue;
422 if (entry->product_id() != device_entry->product_id()) {
423 continue;
425 if (entry->serial_number() == device_entry->serial_number()) {
426 return;
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);
469 } else {
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)
487 : context_(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()) {
505 return it->second;
508 return NULL;
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);
542 // static
543 DevicePermissionsManager* DevicePermissionsManagerFactory::GetForBrowserContext(
544 content::BrowserContext* context) {
545 return static_cast<DevicePermissionsManager*>(
546 GetInstance()->GetServiceForBrowserContext(context, true));
549 // static
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.
575 return context;
578 } // namespace extensions