OLD | NEW |
(Empty) | |
| 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. |
| 4 |
| 5 #include "extensions/browser/api/device_permissions_manager.h" |
| 6 |
| 7 #include "base/memory/singleton.h" |
| 8 #include "base/strings/stringprintf.h" |
| 9 #include "base/strings/utf_string_conversions.h" |
| 10 #include "base/values.h" |
| 11 #include "components/keyed_service/content/browser_context_dependency_manager.h" |
| 12 #include "content/public/browser/notification_service.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/notification_types.h" |
| 17 #include "extensions/strings/grit/extensions_strings.h" |
| 18 #include "ui/base/l10n/l10n_util.h" |
| 19 |
| 20 namespace extensions { |
| 21 |
| 22 using content::BrowserContext; |
| 23 using device::UsbDevice; |
| 24 using extensions::APIPermission; |
| 25 using extensions::Extension; |
| 26 using extensions::ExtensionHost; |
| 27 using extensions::ExtensionPrefs; |
| 28 |
| 29 namespace { |
| 30 |
| 31 // Preference keys |
| 32 |
| 33 // The device that the app has permission to access. |
| 34 const char kDevices[] = "devices"; |
| 35 |
| 36 // The type of device saved. |
| 37 const char kDeviceType[] = "type"; |
| 38 |
| 39 // Type identifier for USB devices. |
| 40 const char kDeviceTypeUsb[] = "usb"; |
| 41 |
| 42 // The vendor ID of the device that the app had permission to access. |
| 43 const char kDeviceVendorId[] = "vendor_id"; |
| 44 |
| 45 // The product ID of the device that the app had permission to access. |
| 46 const char kDeviceProductId[] = "product_id"; |
| 47 |
| 48 // The serial number of the device that the app has permission to access. |
| 49 const char kDeviceSerialNumber[] = "serial_number"; |
| 50 |
| 51 // Persists a DevicePermissionEntry in ExtensionPrefs. |
| 52 void SaveDevicePermissionEntry(BrowserContext* context, |
| 53 const std::string& extension_id, |
| 54 const DevicePermissionEntry& device) { |
| 55 ExtensionPrefs* prefs = ExtensionPrefs::Get(context); |
| 56 ExtensionPrefs::ScopedListUpdate update(prefs, extension_id, kDevices); |
| 57 base::ListValue* devices = update.Get(); |
| 58 if (!devices) { |
| 59 devices = update.Create(); |
| 60 } |
| 61 |
| 62 base::Value* device_entry = device.ToValue(); |
| 63 DCHECK(devices->Find(*device_entry) == devices->end()); |
| 64 devices->Append(device_entry); |
| 65 } |
| 66 |
| 67 // Clears all DevicePermissionEntries for the app from ExtensionPrefs. |
| 68 void ClearDevicePermissionEntries(ExtensionPrefs* prefs, |
| 69 const std::string& extension_id) { |
| 70 prefs->UpdateExtensionPref(extension_id, kDevices, NULL); |
| 71 } |
| 72 |
| 73 // Returns all DevicePermissionEntries for the app. |
| 74 std::vector<DevicePermissionEntry> GetDevicePermissionEntries( |
| 75 ExtensionPrefs* prefs, |
| 76 const std::string& extension_id) { |
| 77 std::vector<DevicePermissionEntry> result; |
| 78 const base::ListValue* devices = NULL; |
| 79 if (!prefs->ReadPrefAsList(extension_id, kDevices, &devices)) { |
| 80 return result; |
| 81 } |
| 82 |
| 83 for (base::ListValue::const_iterator it = devices->begin(); |
| 84 it != devices->end(); |
| 85 ++it) { |
| 86 const base::DictionaryValue* device_entry = NULL; |
| 87 if (!(*it)->GetAsDictionary(&device_entry)) { |
| 88 continue; |
| 89 } |
| 90 int vendor_id; |
| 91 if (!device_entry->GetIntegerWithoutPathExpansion(kDeviceVendorId, |
| 92 &vendor_id) || |
| 93 vendor_id < 0 || vendor_id > UINT16_MAX) { |
| 94 continue; |
| 95 } |
| 96 int product_id; |
| 97 if (!device_entry->GetIntegerWithoutPathExpansion(kDeviceProductId, |
| 98 &product_id) || |
| 99 product_id < 0 || product_id > UINT16_MAX) { |
| 100 continue; |
| 101 } |
| 102 base::string16 serial_number; |
| 103 if (!device_entry->GetStringWithoutPathExpansion(kDeviceSerialNumber, |
| 104 &serial_number)) { |
| 105 continue; |
| 106 } |
| 107 |
| 108 result.push_back( |
| 109 DevicePermissionEntry(vendor_id, product_id, serial_number)); |
| 110 } |
| 111 return result; |
| 112 } |
| 113 } |
| 114 |
| 115 DevicePermissionEntry::DevicePermissionEntry( |
| 116 uint16_t vendor_id, |
| 117 uint16_t product_id, |
| 118 const base::string16& serial_number) |
| 119 : vendor_id(vendor_id), |
| 120 product_id(product_id), |
| 121 serial_number(serial_number) { |
| 122 } |
| 123 |
| 124 base::Value* DevicePermissionEntry::ToValue() const { |
| 125 base::DictionaryValue* device_entry_dict = new base::DictionaryValue(); |
| 126 device_entry_dict->SetStringWithoutPathExpansion(kDeviceType, kDeviceTypeUsb); |
| 127 device_entry_dict->SetIntegerWithoutPathExpansion(kDeviceVendorId, vendor_id); |
| 128 device_entry_dict->SetIntegerWithoutPathExpansion(kDeviceProductId, |
| 129 product_id); |
| 130 device_entry_dict->SetStringWithoutPathExpansion(kDeviceSerialNumber, |
| 131 serial_number); |
| 132 return device_entry_dict; |
| 133 } |
| 134 |
| 135 DevicePermissions::~DevicePermissions() { |
| 136 } |
| 137 |
| 138 bool DevicePermissions::CheckUsbDevice( |
| 139 scoped_refptr<device::UsbDevice> device) const { |
| 140 if (ephemeral_devices_.find(device) != ephemeral_devices_.end()) { |
| 141 return true; |
| 142 } |
| 143 |
| 144 bool have_serial_number = false; |
| 145 base::string16 serial_number; |
| 146 for (const auto& entry : permission_entries_) { |
| 147 if (entry.vendor_id != device->vendor_id()) { |
| 148 continue; |
| 149 } |
| 150 if (entry.product_id != device->product_id()) { |
| 151 continue; |
| 152 } |
| 153 if (!have_serial_number) { |
| 154 if (!device->GetSerialNumber(&serial_number)) { |
| 155 break; |
| 156 } |
| 157 have_serial_number = true; |
| 158 } |
| 159 if (entry.serial_number != serial_number) { |
| 160 continue; |
| 161 } |
| 162 return true; |
| 163 } |
| 164 return false; |
| 165 } |
| 166 |
| 167 DevicePermissions::DevicePermissions(BrowserContext* context, |
| 168 const std::string& extension_id) { |
| 169 ExtensionPrefs* prefs = ExtensionPrefs::Get(context); |
| 170 permission_entries_ = GetDevicePermissionEntries(prefs, extension_id); |
| 171 } |
| 172 |
| 173 DevicePermissions::DevicePermissions( |
| 174 const std::vector<DevicePermissionEntry>& permission_entries, |
| 175 const std::set<scoped_refptr<device::UsbDevice>>& ephemeral_devices) |
| 176 : permission_entries_(permission_entries), |
| 177 ephemeral_devices_(ephemeral_devices) { |
| 178 } |
| 179 |
| 180 std::vector<DevicePermissionEntry>& DevicePermissions::permission_entries() { |
| 181 return permission_entries_; |
| 182 } |
| 183 |
| 184 std::set<scoped_refptr<device::UsbDevice>>& |
| 185 DevicePermissions::ephemeral_devices() { |
| 186 return ephemeral_devices_; |
| 187 } |
| 188 |
| 189 // static |
| 190 DevicePermissionsManager* DevicePermissionsManager::Get( |
| 191 BrowserContext* context) { |
| 192 return DevicePermissionsManagerFactory::GetForBrowserContext(context); |
| 193 } |
| 194 |
| 195 scoped_ptr<DevicePermissions> DevicePermissionsManager::GetForExtension( |
| 196 const std::string& extension_id) { |
| 197 DCHECK(CalledOnValidThread()); |
| 198 |
| 199 DevicePermissions* device_permissions = GetOrInsert(extension_id); |
| 200 return make_scoped_ptr( |
| 201 new DevicePermissions(device_permissions->permission_entries(), |
| 202 device_permissions->ephemeral_devices())); |
| 203 } |
| 204 |
| 205 std::vector<base::string16> |
| 206 DevicePermissionsManager::GetPermissionMessageStrings( |
| 207 const std::string& extension_id) { |
| 208 DCHECK(CalledOnValidThread()); |
| 209 |
| 210 std::vector<base::string16> messages; |
| 211 DevicePermissions* device_permissions = Get(extension_id); |
| 212 if (!device_permissions) { |
| 213 return messages; |
| 214 } |
| 215 |
| 216 for (const auto& entry : device_permissions->permission_entries()) { |
| 217 const char* vendorName = device::UsbIds::GetVendorName(entry.vendor_id); |
| 218 const char* productName = |
| 219 device::UsbIds::GetProductName(entry.vendor_id, entry.product_id); |
| 220 if (vendorName) { |
| 221 if (productName) { |
| 222 messages.push_back(l10n_util::GetStringFUTF16( |
| 223 IDS_EXTENSION_PROMPT_WARNING_USB_DEVICE_SERIAL, |
| 224 base::UTF8ToUTF16(vendorName), |
| 225 base::UTF8ToUTF16(productName), |
| 226 entry.serial_number)); |
| 227 } else { |
| 228 messages.push_back(l10n_util::GetStringFUTF16( |
| 229 IDS_EXTENSION_PROMPT_WARNING_USB_DEVICE_PID_SERIAL, |
| 230 base::UTF8ToUTF16(vendorName), |
| 231 base::ASCIIToUTF16(base::StringPrintf("0x%04X", entry.product_id)), |
| 232 entry.serial_number)); |
| 233 } |
| 234 } else { |
| 235 messages.push_back(l10n_util::GetStringFUTF16( |
| 236 IDS_EXTENSION_PROMPT_WARNING_USB_DEVICE_VID_PID_SERIAL, |
| 237 base::ASCIIToUTF16(base::StringPrintf("0x%04X", entry.vendor_id)), |
| 238 base::ASCIIToUTF16(base::StringPrintf("0x%04X", entry.product_id)), |
| 239 entry.serial_number)); |
| 240 } |
| 241 } |
| 242 return messages; |
| 243 } |
| 244 |
| 245 void DevicePermissionsManager::AllowUsbDevice( |
| 246 const std::string& extension_id, |
| 247 scoped_refptr<device::UsbDevice> device, |
| 248 const base::string16& serial_number) { |
| 249 DCHECK(CalledOnValidThread()); |
| 250 DevicePermissions* device_permissions = GetOrInsert(extension_id); |
| 251 |
| 252 if (!serial_number.empty()) { |
| 253 for (const auto& entry : device_permissions->permission_entries()) { |
| 254 if (entry.vendor_id != device->vendor_id()) { |
| 255 continue; |
| 256 } |
| 257 if (entry.product_id != device->product_id()) { |
| 258 continue; |
| 259 } |
| 260 if (entry.serial_number == serial_number) { |
| 261 return; |
| 262 } |
| 263 } |
| 264 |
| 265 DevicePermissionEntry device_entry = DevicePermissionEntry( |
| 266 device->vendor_id(), device->product_id(), serial_number); |
| 267 device_permissions->permission_entries().push_back(device_entry); |
| 268 SaveDevicePermissionEntry(context_, extension_id, device_entry); |
| 269 } else { |
| 270 // Without a serial number a device cannot be reliably identified when it |
| 271 // is reconnected so such devices are only remembered until disconnect. |
| 272 // Register an observer here so that this set doesn't grow undefinitely. |
| 273 device_permissions->ephemeral_devices().insert(device); |
| 274 device->AddObserver(this); |
| 275 } |
| 276 } |
| 277 |
| 278 void DevicePermissionsManager::Clear(const std::string& extension_id) { |
| 279 DCHECK(CalledOnValidThread()); |
| 280 |
| 281 ClearDevicePermissionEntries(ExtensionPrefs::Get(context_), extension_id); |
| 282 std::map<std::string, DevicePermissions*>::iterator it = |
| 283 extension_id_to_device_permissions_.find(extension_id); |
| 284 if (it != extension_id_to_device_permissions_.end()) { |
| 285 delete it->second; |
| 286 extension_id_to_device_permissions_.erase(it); |
| 287 } |
| 288 } |
| 289 |
| 290 DevicePermissionsManager::DevicePermissionsManager( |
| 291 content::BrowserContext* context) |
| 292 : context_(context) { |
| 293 registrar_.Add(this, |
| 294 extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED, |
| 295 content::NotificationService::AllSources()); |
| 296 } |
| 297 |
| 298 DevicePermissionsManager::~DevicePermissionsManager() { |
| 299 for (const auto& map_entry : extension_id_to_device_permissions_) { |
| 300 delete map_entry.second; |
| 301 } |
| 302 } |
| 303 |
| 304 DevicePermissions* DevicePermissionsManager::Get( |
| 305 const std::string& extension_id) const { |
| 306 std::map<std::string, DevicePermissions*>::const_iterator it = |
| 307 extension_id_to_device_permissions_.find(extension_id); |
| 308 if (it != extension_id_to_device_permissions_.end()) { |
| 309 return it->second; |
| 310 } |
| 311 |
| 312 return NULL; |
| 313 } |
| 314 |
| 315 DevicePermissions* DevicePermissionsManager::GetOrInsert( |
| 316 const std::string& extension_id) { |
| 317 DevicePermissions* device_permissions = Get(extension_id); |
| 318 if (!device_permissions) { |
| 319 device_permissions = new DevicePermissions(context_, extension_id); |
| 320 extension_id_to_device_permissions_[extension_id] = device_permissions; |
| 321 } |
| 322 |
| 323 return device_permissions; |
| 324 } |
| 325 |
| 326 void DevicePermissionsManager::Observe( |
| 327 int type, |
| 328 const content::NotificationSource& source, |
| 329 const content::NotificationDetails& details) { |
| 330 DCHECK(CalledOnValidThread()); |
| 331 DCHECK_EQ(extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED, type); |
| 332 |
| 333 ExtensionHost* host = content::Details<ExtensionHost>(details).ptr(); |
| 334 DevicePermissions* device_permissions = Get(host->extension_id()); |
| 335 if (device_permissions) { |
| 336 // When the extension is unloaded all ephemeral device permissions are |
| 337 // cleared. |
| 338 for (std::set<scoped_refptr<UsbDevice>>::iterator it = |
| 339 device_permissions->ephemeral_devices().begin(); |
| 340 it != device_permissions->ephemeral_devices().end(); |
| 341 ++it) { |
| 342 (*it)->RemoveObserver(this); |
| 343 } |
| 344 device_permissions->ephemeral_devices().clear(); |
| 345 } |
| 346 } |
| 347 |
| 348 void DevicePermissionsManager::OnDisconnect(scoped_refptr<UsbDevice> device) { |
| 349 for (const auto& map_entry : extension_id_to_device_permissions_) { |
| 350 // An ephemeral device cannot be identified if it is reconnected and so |
| 351 // permission to access it is cleared on disconnect. |
| 352 map_entry.second->ephemeral_devices().erase(device); |
| 353 device->RemoveObserver(this); |
| 354 } |
| 355 } |
| 356 |
| 357 // static |
| 358 DevicePermissionsManager* DevicePermissionsManagerFactory::GetForBrowserContext( |
| 359 content::BrowserContext* context) { |
| 360 return static_cast<DevicePermissionsManager*>( |
| 361 GetInstance()->GetServiceForBrowserContext(context, true)); |
| 362 } |
| 363 |
| 364 // static |
| 365 DevicePermissionsManagerFactory* |
| 366 DevicePermissionsManagerFactory::GetInstance() { |
| 367 return Singleton<DevicePermissionsManagerFactory>::get(); |
| 368 } |
| 369 |
| 370 DevicePermissionsManagerFactory::DevicePermissionsManagerFactory() |
| 371 : BrowserContextKeyedServiceFactory( |
| 372 "DevicePermissionsManager", |
| 373 BrowserContextDependencyManager::GetInstance()) { |
| 374 } |
| 375 |
| 376 DevicePermissionsManagerFactory::~DevicePermissionsManagerFactory() { |
| 377 } |
| 378 |
| 379 KeyedService* DevicePermissionsManagerFactory::BuildServiceInstanceFor( |
| 380 content::BrowserContext* context) const { |
| 381 return new DevicePermissionsManager(context); |
| 382 } |
| 383 |
| 384 } // namespace extensions |
OLD | NEW |