Explicitly add python-numpy dependency to install-build-deps.
[chromium-blink-merge.git] / extensions / browser / api / device_permissions_manager.cc
blob87f52f081fdb5f74df53a9008e846cf1542f2d42
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;
31 namespace {
33 // Preference keys
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
54 // access.
55 const char kDeviceManufacturerString[] = "manufacturer_string";
57 // The product string read from the device that the app has permission to
58 // access.
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();
71 if (!devices) {
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) {
82 std::string type;
83 if (!value->GetStringWithoutPathExpansion(kDeviceType, &type) ||
84 type != kDeviceTypeUsb) {
85 return false;
87 int vendor_id;
88 if (!value->GetIntegerWithoutPathExpansion(kDeviceVendorId, &vendor_id) ||
89 vendor_id != entry->vendor_id()) {
90 return false;
92 int product_id;
93 if (!value->GetIntegerWithoutPathExpansion(kDeviceProductId, &product_id) ||
94 product_id != entry->product_id()) {
95 return false;
97 base::string16 serial_number;
98 if (!value->GetStringWithoutPathExpansion(kDeviceSerialNumber,
99 &serial_number) ||
100 serial_number != entry->serial_number()) {
101 return false;
103 return true;
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();
114 if (!devices) {
115 return;
118 for (size_t i = 0; i < devices->GetSize(); ++i) {
119 base::DictionaryValue* dict_value;
120 if (!devices->GetDictionary(i, &dict_value)) {
121 continue;
123 if (!MatchesDevicePermissionEntry(dict_value, entry)) {
124 continue;
126 devices->Set(i, entry->ToValue().release());
127 break;
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();
138 if (!devices) {
139 return;
142 for (size_t i = 0; i < devices->GetSize(); ++i) {
143 base::DictionaryValue* dict_value;
144 if (!devices->GetDictionary(i, &dict_value)) {
145 continue;
147 if (!MatchesDevicePermissionEntry(dict_value, entry)) {
148 continue;
150 devices->Remove(i, nullptr);
151 break;
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)) {
168 return result;
171 for (const base::Value* entry : *devices) {
172 const base::DictionaryValue* entry_dict;
173 if (!entry->GetAsDictionary(&entry_dict)) {
174 continue;
176 std::string type;
177 if (!entry_dict->GetStringWithoutPathExpansion(kDeviceType, &type) ||
178 type != kDeviceTypeUsb) {
179 continue;
181 int vendor_id;
182 if (!entry_dict->GetIntegerWithoutPathExpansion(kDeviceVendorId,
183 &vendor_id) ||
184 vendor_id < 0 || vendor_id > UINT16_MAX) {
185 continue;
187 int product_id;
188 if (!entry_dict->GetIntegerWithoutPathExpansion(kDeviceProductId,
189 &product_id) ||
190 product_id < 0 || product_id > UINT16_MAX) {
191 continue;
193 base::string16 serial_number;
194 if (!entry_dict->GetStringWithoutPathExpansion(kDeviceSerialNumber,
195 &serial_number)) {
196 continue;
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,
207 &product_string);
209 // If a last used time is not stored in ExtensionPrefs last_used.is_null()
210 // will be true.
211 std::string last_used_str;
212 int64 last_used_i64 = 0;
213 base::Time last_used;
214 if (entry_dict->GetStringWithoutPathExpansion(kDeviceLastUsed,
215 &last_used_str) &&
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));
224 return result;
227 } // namespace
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)
234 : device_(device),
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(
243 uint16_t vendor_id,
244 uint16_t product_id,
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()) {
266 return nullptr;
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,
275 serial_number_);
276 if (!manufacturer_string_.empty()) {
277 entry_dict->SetStringWithoutPathExpansion(kDeviceManufacturerString,
278 manufacturer_string_);
280 if (!product_string_.empty()) {
281 entry_dict->SetStringWithoutPathExpansion(kDeviceProductString,
282 product_string_);
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());
296 } else {
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_);
306 if (vendor_name) {
307 return base::UTF8ToUTF16(vendor_name);
308 } else {
309 return l10n_util::GetStringFUTF16(
310 IDS_DEVICE_UNKNOWN_VENDOR,
311 base::ASCIIToUTF16(base::StringPrintf("0x%04x", vendor_id_)));
313 } else {
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_);
322 if (product_name) {
323 return base::UTF8ToUTF16(product_name);
324 } else {
325 return l10n_util::GetStringFUTF16(
326 IDS_DEVICE_UNKNOWN_PRODUCT,
327 base::ASCIIToUTF16(base::StringPrintf("0x%04x", product_id_)));
329 } else {
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()) {
348 continue;
350 if (entry->vendor_id() != device->vendor_id()) {
351 continue;
353 if (entry->product_id() != device->product_id()) {
354 continue;
356 if (!have_serial_number) {
357 if (!device->GetSerialNumber(&serial_number)) {
358 break;
360 have_serial_number = true;
362 if (entry->serial_number() != serial_number) {
363 continue;
365 return entry;
367 return nullptr;
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_) {
381 // static
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());
405 return messages;
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()) {
423 continue;
425 if (entry->product_id() != device_entry->product_id()) {
426 continue;
428 if (entry->serial_number() == device_entry->serial_number()) {
429 return;
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);
465 } else {
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()) {
506 return it->second;
509 return NULL;
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);
553 // static
554 DevicePermissionsManager* DevicePermissionsManagerFactory::GetForBrowserContext(
555 content::BrowserContext* context) {
556 return static_cast<DevicePermissionsManager*>(
557 GetInstance()->GetServiceForBrowserContext(context, true));
560 // static
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.
586 return context;
589 } // namespace extensions