fix: persist permission granted to serial ports (#30209)
This commit is contained in:
parent
d2989de5d9
commit
461db8f1ab
8 changed files with 138 additions and 38 deletions
|
@ -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,
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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_;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue