fix: persist permission granted to serial ports (#30209)

This commit is contained in:
John Kleinschmidt 2021-07-26 13:10:57 -04:00 committed by GitHub
parent d2989de5d9
commit 461db8f1ab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 138 additions and 38 deletions

View file

@ -8,6 +8,8 @@
#include <utility>
#include <vector>
#include "base/barrier_closure.h"
#include "base/base_paths.h"
#include "base/command_line.h"
@ -93,6 +95,9 @@ std::string MakePartitionName(const std::string& input) {
} // namespace
const char kSerialGrantedDevicesPref[] =
"profile.content_settings.exceptions.serial-chooser-data";
// static
ElectronBrowserContext::BrowserContextMap&
ElectronBrowserContext::browser_context_map() {
@ -189,6 +194,7 @@ void ElectronBrowserContext::InitPrefs() {
registry->RegisterFilePathPref(prefs::kDownloadDefaultDirectory,
download_dir);
registry->RegisterDictionaryPref(prefs::kDevToolsFileSystemPaths);
registry->RegisterDictionaryPref(kSerialGrantedDevicesPref);
InspectableWebContents::RegisterPrefs(registry.get());
MediaDeviceIDSalt::RegisterPrefs(registry.get());
ZoomLevelDelegate::RegisterPrefs(registry.get());
@ -409,6 +415,54 @@ void ElectronBrowserContext::SetSSLConfigClient(
ssl_config_client_ = std::move(client);
}
void ElectronBrowserContext::GrantObjectPermission(
const url::Origin& origin,
base::Value object,
const std::string& pref_key) {
std::string origin_string = origin.Serialize();
DictionaryPrefUpdate update(prefs(), pref_key);
base::Value* const current_objects = update.Get();
if (!current_objects || !current_objects->is_dict()) {
base::ListValue objects_for_origin;
objects_for_origin.Append(std::move(object));
base::DictionaryValue objects_by_origin;
objects_by_origin.SetPath(origin_string, std::move(objects_for_origin));
prefs()->Set(pref_key, std::move(objects_by_origin));
} else {
base::Value* const objects_mutable =
current_objects->FindListKey(origin_string);
if (objects_mutable) {
base::Value::ListStorage objects = std::move(*objects_mutable).TakeList();
objects.push_back(std::move(object));
*objects_mutable = base::Value(std::move(objects));
} else {
base::Value new_objects(base::Value::Type::LIST);
new_objects.Append(std::move(object));
current_objects->SetKey(origin_string, std::move(new_objects));
}
}
}
std::vector<std::unique_ptr<base::Value>>
ElectronBrowserContext::GetGrantedObjects(const url::Origin& origin,
const std::string& pref_key) {
auto* current_objects = prefs()->Get(pref_key);
if (!current_objects || !current_objects->is_dict()) {
return {};
}
const base::Value* objects_for_origin =
current_objects->FindPath(origin.Serialize());
if (!objects_for_origin)
return {};
std::vector<std::unique_ptr<base::Value>> results;
for (const auto& object : objects_for_origin->GetList())
results.push_back(std::make_unique<base::Value>(object.Clone()));
return results;
}
// static
ElectronBrowserContext* ElectronBrowserContext::From(
const std::string& partition,

View file

@ -8,6 +8,7 @@
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "base/memory/weak_ptr.h"
#include "chrome/browser/predictors/preconnect_manager.h"
@ -45,6 +46,9 @@ class ResolveProxyHelper;
class WebViewManager;
class ProtocolRegistry;
// Preference keys for device apis
extern const char kSerialGrantedDevicesPref[];
class ElectronBrowserContext : public content::BrowserContext {
public:
// partition_id => browser_context
@ -139,6 +143,19 @@ class ElectronBrowserContext : public content::BrowserContext {
network::mojom::SSLConfigPtr GetSSLConfig();
void SetSSLConfigClient(mojo::Remote<network::mojom::SSLConfigClient> client);
// Grants |origin| access to |object| by writing it into the browser context.
// To be used in place of ObjectPermissionContextBase::GrantObjectPermission.
void GrantObjectPermission(const url::Origin& origin,
base::Value object,
const std::string& pref_key);
// Returns the list of objects that |origin| has been granted permission to
// access. To be used in place of
// ObjectPermissionContextBase::GetGrantedObjects.
std::vector<std::unique_ptr<base::Value>> GetGrantedObjects(
const url::Origin& origin,
const std::string& pref_key);
~ElectronBrowserContext() override;
private:

View file

@ -71,7 +71,6 @@ bool ElectronSerialDelegate::HasPortPermission(
auto* chooser_context =
SerialChooserContextFactory::GetForBrowserContext(browser_context);
return chooser_context->HasPortPermission(
frame->GetLastCommittedOrigin(),
web_contents->GetMainFrame()->GetLastCommittedOrigin(), port);
}

View file

@ -4,6 +4,7 @@
#include "shell/browser/serial/serial_chooser_context.h"
#include <memory>
#include <string>
#include <utility>
@ -46,7 +47,7 @@ base::UnguessableToken DecodeToken(base::StringPiece input) {
return base::UnguessableToken();
}
const auto* data = reinterpret_cast<const uint64_t*>(buffer.data());
const uint64_t* data = reinterpret_cast<const uint64_t*>(buffer.data());
return base::UnguessableToken::Deserialize(data[0], data[1]);
}
@ -82,30 +83,73 @@ base::Value PortInfoToValue(const device::mojom::SerialPortInfo& port) {
return value;
}
SerialChooserContext::SerialChooserContext() = default;
SerialChooserContext::SerialChooserContext(
ElectronBrowserContext* browser_context)
: browser_context_(browser_context) {}
SerialChooserContext::~SerialChooserContext() = default;
void SerialChooserContext::GrantPortPermission(
const url::Origin& requesting_origin,
const url::Origin& embedding_origin,
const url::Origin& origin,
const device::mojom::SerialPortInfo& port) {
base::Value value = PortInfoToValue(port);
port_info_.insert({port.token, value.Clone()});
ephemeral_ports_[{requesting_origin, embedding_origin}].insert(port.token);
if (CanStorePersistentEntry(port)) {
browser_context_->GrantObjectPermission(origin, std::move(value),
kSerialGrantedDevicesPref);
return;
}
ephemeral_ports_[origin].insert(port.token);
}
bool SerialChooserContext::HasPortPermission(
const url::Origin& requesting_origin,
const url::Origin& embedding_origin,
const url::Origin& origin,
const device::mojom::SerialPortInfo& port) {
auto it = ephemeral_ports_.find({requesting_origin, embedding_origin});
auto it = ephemeral_ports_.find(origin);
if (it != ephemeral_ports_.end()) {
const std::set<base::UnguessableToken> ports = it->second;
if (base::Contains(ports, port.token))
return true;
}
if (!CanStorePersistentEntry(port)) {
return false;
}
std::vector<std::unique_ptr<base::Value>> object_list =
browser_context_->GetGrantedObjects(origin, kSerialGrantedDevicesPref);
for (const auto& device : object_list) {
#if defined(OS_WIN)
const std::string& device_instance_id =
*device->FindStringKey(kDeviceInstanceIdKey);
if (port.device_instance_id == device_instance_id)
return true;
#else
const int vendor_id = *device->FindIntKey(kVendorIdKey);
const int product_id = *device->FindIntKey(kProductIdKey);
const std::string& serial_number = *device->FindStringKey(kSerialNumberKey);
// Guaranteed by the CanStorePersistentEntry) check above.
DCHECK(port.has_vendor_id);
DCHECK(port.has_product_id);
DCHECK(port.serial_number && !port.serial_number->empty());
if (port.vendor_id != vendor_id || port.product_id != product_id ||
port.serial_number != serial_number) {
continue;
}
#if defined(OS_MAC)
const std::string& usb_driver_name = *device->FindStringKey(kUsbDriverKey);
if (port.usb_driver_name != usb_driver_name) {
continue;
}
#endif // defined(OS_MAC)
return true;
#endif // defined(OS_WIN)
}
return false;
}
@ -170,14 +214,6 @@ void SerialChooserContext::OnPortRemoved(
for (auto& observer : port_observer_list_)
observer.OnPortRemoved(*port);
std::vector<std::pair<url::Origin, url::Origin>> revoked_url_pairs;
for (auto& map_entry : ephemeral_ports_) {
std::set<base::UnguessableToken>& ports = map_entry.second;
if (ports.erase(port->token) > 0) {
revoked_url_pairs.push_back(map_entry.first);
}
}
port_info_.erase(port->token);
}

View file

@ -18,6 +18,7 @@
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/device/public/mojom/serial.mojom-forward.h"
#include "shell/browser/electron_browser_context.h"
#include "third_party/blink/public/mojom/serial/serial.mojom.h"
#include "url/gurl.h"
#include "url/origin.h"
@ -33,15 +34,13 @@ class SerialChooserContext : public KeyedService,
public:
using PortObserver = content::SerialDelegate::Observer;
SerialChooserContext();
explicit SerialChooserContext(ElectronBrowserContext* browser_context);
~SerialChooserContext() override;
// Serial-specific interface for granting and checking permissions.
void GrantPortPermission(const url::Origin& requesting_origin,
const url::Origin& embedding_origin,
void GrantPortPermission(const url::Origin& origin,
const device::mojom::SerialPortInfo& port);
bool HasPortPermission(const url::Origin& requesting_origin,
const url::Origin& embedding_origin,
bool HasPortPermission(const url::Origin& origin,
const device::mojom::SerialPortInfo& port);
static bool CanStorePersistentEntry(
const device::mojom::SerialPortInfo& port);
@ -62,16 +61,11 @@ class SerialChooserContext : public KeyedService,
void SetUpPortManagerConnection(
mojo::PendingRemote<device::mojom::SerialPortManager> manager);
void OnPortManagerConnectionError();
void OnGetPorts(const url::Origin& requesting_origin,
const url::Origin& embedding_origin,
blink::mojom::SerialService::GetPortsCallback callback,
std::vector<device::mojom::SerialPortInfoPtr> ports);
// Tracks the set of ports to which an origin (potentially embedded in another
// origin) has access to. Key is (requesting_origin, embedding_origin).
std::map<std::pair<url::Origin, url::Origin>,
std::set<base::UnguessableToken>>
ephemeral_ports_;
ElectronBrowserContext* browser_context_;
// Tracks the set of ports to which an origin has access to.
std::map<url::Origin, std::set<base::UnguessableToken>> ephemeral_ports_;
// Holds information about ports in |ephemeral_ports_|.
std::map<base::UnguessableToken, base::Value> port_info_;

View file

@ -5,6 +5,7 @@
#include "shell/browser/serial/serial_chooser_context_factory.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "shell/browser/electron_browser_context.h"
#include "shell/browser/serial/serial_chooser_context.h"
namespace electron {
@ -18,7 +19,9 @@ SerialChooserContextFactory::~SerialChooserContextFactory() = default;
KeyedService* SerialChooserContextFactory::BuildServiceInstanceFor(
content::BrowserContext* context) const {
return new SerialChooserContext();
auto* browser_context =
static_cast<electron::ElectronBrowserContext*>(context);
return new SerialChooserContext(browser_context);
}
// static

View file

@ -68,8 +68,7 @@ SerialChooserController::SerialChooserController(
filters_(std::move(filters)),
callback_(std::move(callback)),
serial_delegate_(serial_delegate) {
requesting_origin_ = render_frame_host->GetLastCommittedOrigin();
embedding_origin_ = web_contents->GetMainFrame()->GetLastCommittedOrigin();
origin_ = web_contents->GetMainFrame()->GetLastCommittedOrigin();
chooser_context_ = SerialChooserContextFactory::GetForBrowserContext(
web_contents->GetBrowserContext())
@ -125,8 +124,7 @@ void SerialChooserController::OnDeviceChosen(const std::string& port_id) {
return ptr->token.ToString() == port_id;
});
if (it != ports_.end()) {
chooser_context_->GrantPortPermission(requesting_origin_,
embedding_origin_, *it->get());
chooser_context_->GrantPortPermission(origin_, *it->get());
RunCallback(it->Clone());
} else {
RunCallback(/*port=*/nullptr);

View file

@ -53,8 +53,7 @@ class SerialChooserController final : public SerialChooserContext::PortObserver,
std::vector<blink::mojom::SerialPortFilterPtr> filters_;
content::SerialChooser::Callback callback_;
url::Origin requesting_origin_;
url::Origin embedding_origin_;
url::Origin origin_;
base::WeakPtr<SerialChooserContext> chooser_context_;