Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(633)

Unified Diff: chrome/browser/usb/web_usb_permission_store.cc

Issue 1382783002: Store USB device permissions in website settings. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/usb/web_usb_permission_store.cc
diff --git a/chrome/browser/usb/web_usb_permission_store.cc b/chrome/browser/usb/web_usb_permission_store.cc
new file mode 100644
index 0000000000000000000000000000000000000000..3f5d85041d018e87785c16214b8d2a8cf2c42028
--- /dev/null
+++ b/chrome/browser/usb/web_usb_permission_store.cc
@@ -0,0 +1,261 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/singleton.h"
+#include "base/stl_util.h"
+#include "base/values.h"
+#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
+#include "chrome/browser/profiles/incognito_helpers.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/usb/web_usb_permission_store.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/content_settings/core/common/content_settings_pattern.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+#include "device/core/device_client.h"
+#include "device/usb/usb_device.h"
+
+using content::BrowserContext;
+using device::UsbDevice;
+
+namespace {
+
+const char* const kDeviceListKey = "devices";
+const char* const kDeviceNameKey = "name";
+const char* const kVendorIdKey = "vendor-id";
+const char* const kProductIdKey = "product-id";
+const char* const kSerialNumberKey = "serial-number";
+
+class WebUSBPermissionStoreFactory : public BrowserContextKeyedServiceFactory {
+ public:
+ static WebUSBPermissionStore* GetForBrowserContext(BrowserContext* context) {
+ return static_cast<WebUSBPermissionStore*>(
+ GetInstance()->GetServiceForBrowserContext(context, true));
+ }
+
+ static WebUSBPermissionStoreFactory* GetInstance() {
Bernhard Bauer 2015/10/01 08:31:25 You will need to add a (visible from outside) meth
+ return base::Singleton<WebUSBPermissionStoreFactory>::get();
+ }
+
+ private:
+ friend struct base::DefaultSingletonTraits<WebUSBPermissionStoreFactory>;
+
+ WebUSBPermissionStoreFactory()
+ : BrowserContextKeyedServiceFactory(
+ "WebUSBPermissionStore",
+ BrowserContextDependencyManager::GetInstance()) {}
+ ~WebUSBPermissionStoreFactory() override {}
+
+ BrowserContext* GetBrowserContextToUse(
+ BrowserContext* context) const override {
+ return chrome::GetBrowserContextOwnInstanceInIncognito(context);
+ }
+
+ KeyedService* BuildServiceInstanceFor(
+ BrowserContext* context) const override {
+ return new WebUSBPermissionStore(context);
+ }
Bernhard Bauer 2015/10/01 08:31:25 DISALLOW_COPY_AND_ASSIGN
+};
+
+bool CanStorePersistentEntry(const scoped_refptr<UsbDevice>& device) {
+ return !device->serial_number().empty();
+}
+
+class USBDeviceEntry {
+ public:
+ static scoped_ptr<USBDeviceEntry> FindForDevice(
+ const base::DictionaryValue& value,
+ scoped_refptr<UsbDevice> device) {
+ const base::ListValue* device_list;
+ if (!value.GetList(kDeviceListKey, &device_list))
+ return nullptr;
+
+ scoped_ptr<USBDeviceEntry> entry(new USBDeviceEntry());
+ for (size_t i = 0; i < device_list->GetSize(); ++i) {
+ const base::DictionaryValue* device_dict;
+ if (!device_list->GetDictionary(i, &device_dict))
+ continue;
+
+ if (!device_dict->GetString(kDeviceNameKey, &entry->device_name_) ||
+ !device_dict->GetInteger(kVendorIdKey, &entry->vendor_id_) ||
+ !device_dict->GetInteger(kProductIdKey, &entry->product_id_) ||
+ !device_dict->GetString(kSerialNumberKey, &entry->serial_number_))
+ continue;
+
+ if (entry->device_name_.empty())
+ continue;
+
+ if (entry->vendor_id_ != device->vendor_id() ||
+ entry->product_id_ != device->product_id() ||
+ entry->serial_number_ != device->serial_number())
+ continue;
+
+ entry->found_in_list_ = true;
+ entry->list_index_ = i;
+ return entry.Pass();
+ }
+ return nullptr;
+ }
+
+ USBDeviceEntry() {}
+
+ explicit USBDeviceEntry(scoped_refptr<UsbDevice> device) {
Ken Rockot(use gerrit already) 2015/10/01 01:26:32 nit: const scoped_refptr<UsbDevice>& here and else
+ device_name_ = device->product_string();
+ vendor_id_ = device->vendor_id();
+ product_id_ = device->product_id();
+ serial_number_ = device->serial_number();
+ }
+
+ ~USBDeviceEntry() {}
+
+ void AddToDeviceList(base::DictionaryValue* value) {
+ DCHECK(!found_in_list_);
+ scoped_ptr<base::DictionaryValue> device_dict(new base::DictionaryValue());
+ device_dict->SetString(kDeviceNameKey, device_name_);
+ device_dict->SetInteger(kVendorIdKey, vendor_id_);
+ device_dict->SetInteger(kProductIdKey, product_id_);
+ device_dict->SetString(kSerialNumberKey, serial_number_);
+
+ base::ListValue* device_list;
+ if (!value->GetList(kDeviceListKey, &device_list)) {
+ device_list = new base::ListValue();
+ value->Set(kDeviceListKey, device_list);
+ }
+
+ device_list->Append(device_dict.Pass());
+ }
+
+ void RemoveFromDeviceList(base::DictionaryValue* value) {
+ DCHECK(found_in_list_);
+ base::ListValue* device_list;
Bernhard Bauer 2015/10/01 08:31:25 Initialize to nullptr? Otherwise you might derefer
+ if (!value->GetList(kDeviceListKey, &device_list))
+ NOTREACHED();
Bernhard Bauer 2015/10/01 08:31:25 Instead of `if (...) NOTREACHED()`, store a succes
+
+ bool removed = device_list->Remove(list_index_, nullptr);
+ DCHECK(removed);
+ }
+
+ private:
+ base::string16 device_name_;
+ int vendor_id_;
+ int product_id_;
+ base::string16 serial_number_;
+ bool found_in_list_ = false;
+ size_t list_index_;
Bernhard Bauer 2015/10/01 08:31:25 DISALLOW_COPY_AND_ASSIGN
+};
+
+scoped_ptr<base::DictionaryValue> GetDictionaryForOrigin(
+ HostContentSettingsMap* settings,
+ const GURL& origin_url) {
+ if (!settings)
+ return nullptr;
+
+ scoped_ptr<base::DictionaryValue> value =
+ base::DictionaryValue::From(settings->GetWebsiteSetting(
+ origin_url, origin_url, CONTENT_SETTINGS_TYPE_USB, std::string(),
+ nullptr));
+ if (!value) {
Bernhard Bauer 2015/10/01 08:31:25 Nit: Braces are optional for single-line bodies, a
+ return make_scoped_ptr(new base::DictionaryValue());
+ }
+
+ return value.Pass();
+}
+
+void SetDictionaryForOrigin(HostContentSettingsMap* settings,
+ const GURL& origin_url,
+ scoped_ptr<base::Value> value) {
+ if (!settings)
+ return;
+
+ ContentSettingsPattern pattern(
+ ContentSettingsPattern::FromURLNoWildcard(origin_url));
+ if (!pattern.IsValid())
+ return;
+
+ settings->SetWebsiteSetting(pattern, ContentSettingsPattern::Wildcard(),
+ CONTENT_SETTINGS_TYPE_USB, std::string(),
+ value.release());
+}
+
+} // namespace
+
+// static
+WebUSBPermissionStore* WebUSBPermissionStore::Get(BrowserContext* context) {
+ return WebUSBPermissionStoreFactory::GetForBrowserContext(context);
+}
+
+WebUSBPermissionStore::WebUSBPermissionStore(BrowserContext* context)
+ : observer_(this) {
+ usb_service_ = device::DeviceClient::Get()->GetUsbService();
+ if (usb_service_)
+ observer_.Add(usb_service_);
+ Profile* profile = Profile::FromBrowserContext(context);
+ host_content_settings_map_ =
+ HostContentSettingsMapFactory::GetForProfile(profile);
+}
+
+WebUSBPermissionStore::~WebUSBPermissionStore() {}
+
+void WebUSBPermissionStore::GrantDevicePermission(const GURL& origin,
+ const std::string& guid) {
+ DCHECK_EQ(origin, origin.GetOrigin());
+ scoped_refptr<UsbDevice> device = usb_service_->GetDevice(guid);
+ if (!device)
+ return;
+
+ if (CanStorePersistentEntry(device)) {
+ USBDeviceEntry entry(device);
+ scoped_ptr<base::DictionaryValue> value =
+ GetDictionaryForOrigin(host_content_settings_map_, origin);
+ entry.AddToDeviceList(value.get());
+ SetDictionaryForOrigin(host_content_settings_map_, origin, value.Pass());
+ } else {
+ ephemeral_devices_[origin].insert(guid);
+ }
+}
+
+void WebUSBPermissionStore::RevokeDevicePermission(const GURL& origin,
+ const std::string& guid) {
+ DCHECK_EQ(origin, origin.GetOrigin());
+ auto it = ephemeral_devices_.find(origin);
+ if (it != ephemeral_devices_.end()) {
+ it->second.erase(guid);
+ if (it->second.empty())
+ ephemeral_devices_.erase(it);
+ }
+
+ scoped_refptr<UsbDevice> device = usb_service_->GetDevice(guid);
+ if (!device)
+ return;
+
+ scoped_ptr<base::DictionaryValue> value =
+ GetDictionaryForOrigin(host_content_settings_map_, origin);
+ scoped_ptr<USBDeviceEntry> entry =
+ USBDeviceEntry::FindForDevice(*value, device);
+ if (entry) {
+ entry->RemoveFromDeviceList(value.get());
+ SetDictionaryForOrigin(host_content_settings_map_, origin, value.Pass());
+ }
+}
+
+bool WebUSBPermissionStore::HasDevicePermission(const GURL& origin,
+ const std::string& guid) {
+ DCHECK_EQ(origin, origin.GetOrigin());
+ auto it = ephemeral_devices_.find(origin);
+ if (it != ephemeral_devices_.end())
+ return ContainsValue(it->second, guid);
+
+ scoped_refptr<UsbDevice> device = usb_service_->GetDevice(guid);
+ if (!device)
+ return false;
+
+ scoped_ptr<base::DictionaryValue> value =
+ GetDictionaryForOrigin(host_content_settings_map_, origin);
+ return USBDeviceEntry::FindForDevice(*value, device);
+}
+
+void WebUSBPermissionStore::OnDeviceRemoved(scoped_refptr<UsbDevice> device) {
+ for (auto& map_entry : ephemeral_devices_)
+ map_entry.second.erase(device->guid());
+}

Powered by Google App Engine
This is Rietveld 408576698