mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
1 Commits
refactor/a
...
perms
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8608d442e3 |
@@ -13,12 +13,14 @@
|
|||||||
#include "base/base_paths.h"
|
#include "base/base_paths.h"
|
||||||
#include "base/command_line.h"
|
#include "base/command_line.h"
|
||||||
#include "base/containers/to_vector.h"
|
#include "base/containers/to_vector.h"
|
||||||
|
#include "base/feature_list.h"
|
||||||
#include "base/files/file_path.h"
|
#include "base/files/file_path.h"
|
||||||
#include "base/no_destructor.h"
|
#include "base/no_destructor.h"
|
||||||
#include "base/path_service.h"
|
#include "base/path_service.h"
|
||||||
#include "base/strings/escape.h"
|
#include "base/strings/escape.h"
|
||||||
#include "base/strings/string_number_conversions.h"
|
#include "base/strings/string_number_conversions.h"
|
||||||
#include "base/strings/string_util.h"
|
#include "base/strings/string_util.h"
|
||||||
|
#include "chrome/browser/file_system_access/file_system_access_features.h"
|
||||||
#include "chrome/browser/predictors/preconnect_manager.h"
|
#include "chrome/browser/predictors/preconnect_manager.h"
|
||||||
#include "chrome/common/chrome_paths.h"
|
#include "chrome/common/chrome_paths.h"
|
||||||
#include "chrome/common/pref_names.h"
|
#include "chrome/common/pref_names.h"
|
||||||
@@ -106,6 +108,34 @@ namespace electron {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
bool DoesFileSystemAccessPermissionMatch(const base::Value& permission_grant,
|
||||||
|
const base::Value& grant_to_compare) {
|
||||||
|
if (!permission_grant.is_dict() || !grant_to_compare.is_dict()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& grant_dict = permission_grant.GetDict();
|
||||||
|
const auto& compare_dict = grant_to_compare.GetDict();
|
||||||
|
|
||||||
|
// Compare file path
|
||||||
|
const auto* grant_path = grant_dict.FindString("path");
|
||||||
|
const auto* compare_path = compare_dict.FindString("path");
|
||||||
|
if (!grant_path || !compare_path || *grant_path != *compare_path) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare permission type (readable/writable)
|
||||||
|
const auto grant_readable = grant_dict.FindBool("readable").value_or(false);
|
||||||
|
const auto compare_readable =
|
||||||
|
compare_dict.FindBool("readable").value_or(false);
|
||||||
|
const auto grant_writable = grant_dict.FindBool("writable").value_or(false);
|
||||||
|
const auto compare_writable =
|
||||||
|
compare_dict.FindBool("writable").value_or(false);
|
||||||
|
|
||||||
|
return grant_readable == compare_readable &&
|
||||||
|
grant_writable == compare_writable;
|
||||||
|
}
|
||||||
|
|
||||||
// Copied from chrome/browser/media/webrtc/desktop_capture_devices_util.cc.
|
// Copied from chrome/browser/media/webrtc/desktop_capture_devices_util.cc.
|
||||||
media::mojom::CaptureHandlePtr CreateCaptureHandle(
|
media::mojom::CaptureHandlePtr CreateCaptureHandle(
|
||||||
content::WebContents* capturer,
|
content::WebContents* capturer,
|
||||||
@@ -863,6 +893,94 @@ bool ElectronBrowserContext::CheckDevicePermission(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ElectronBrowserContext::GrantFileSystemAccessPermission(
|
||||||
|
const url::Origin& origin,
|
||||||
|
const base::Value& permission_grant,
|
||||||
|
blink::PermissionType permission_type) {
|
||||||
|
if (!base::FeatureList::IsEnabled(
|
||||||
|
features::kFileSystemAccessPersistentPermissions)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
granted_devices_[permission_type][origin].push_back(
|
||||||
|
std::make_unique<base::Value>(permission_grant.Clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ElectronBrowserContext::RevokeFileSystemAccessPermission(
|
||||||
|
const url::Origin& origin,
|
||||||
|
const base::Value& permission_grant,
|
||||||
|
blink::PermissionType permission_type) {
|
||||||
|
if (!base::FeatureList::IsEnabled(
|
||||||
|
features::kFileSystemAccessPersistentPermissions)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto& current_permissions_it = granted_devices_.find(permission_type);
|
||||||
|
if (current_permissions_it == granted_devices_.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto& origin_permissions_it =
|
||||||
|
current_permissions_it->second.find(origin);
|
||||||
|
if (origin_permissions_it == current_permissions_it->second.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::erase_if(
|
||||||
|
origin_permissions_it->second, [&permission_grant](auto const& val) {
|
||||||
|
return DoesFileSystemAccessPermissionMatch(permission_grant, *val);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ElectronBrowserContext::CheckFileSystemAccessPermission(
|
||||||
|
const url::Origin& origin,
|
||||||
|
const base::Value& permission_grant,
|
||||||
|
blink::PermissionType permission_type) {
|
||||||
|
if (!base::FeatureList::IsEnabled(
|
||||||
|
features::kFileSystemAccessPersistentPermissions)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto& current_permissions_it = granted_devices_.find(permission_type);
|
||||||
|
if (current_permissions_it == granted_devices_.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const auto& origin_permissions_it =
|
||||||
|
current_permissions_it->second.find(origin);
|
||||||
|
if (origin_permissions_it == current_permissions_it->second.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (const auto& grant_to_compare : origin_permissions_it->second) {
|
||||||
|
if (DoesFileSystemAccessPermissionMatch(permission_grant,
|
||||||
|
*grant_to_compare))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<base::Value>
|
||||||
|
ElectronBrowserContext::GetFileSystemAccessGrantsForOrigin(
|
||||||
|
const url::Origin& origin,
|
||||||
|
blink::PermissionType permission_type) {
|
||||||
|
std::vector<base::Value> grants;
|
||||||
|
|
||||||
|
if (!base::FeatureList::IsEnabled(
|
||||||
|
features::kFileSystemAccessPersistentPermissions)) {
|
||||||
|
return grants;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& current_permissions_it = granted_devices_.find(permission_type);
|
||||||
|
if (current_permissions_it == granted_devices_.end())
|
||||||
|
return grants;
|
||||||
|
|
||||||
|
const auto& origin_permissions_it =
|
||||||
|
current_permissions_it->second.find(origin);
|
||||||
|
if (origin_permissions_it == current_permissions_it->second.end())
|
||||||
|
return grants;
|
||||||
|
|
||||||
|
for (const auto& grant : origin_permissions_it->second) {
|
||||||
|
grants.push_back(grant->Clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
return grants;
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
ElectronBrowserContext* ElectronBrowserContext::From(
|
ElectronBrowserContext* ElectronBrowserContext::From(
|
||||||
const std::string& partition,
|
const std::string& partition,
|
||||||
|
|||||||
@@ -159,6 +159,24 @@ class ElectronBrowserContext : public content::BrowserContext {
|
|||||||
const base::Value& device,
|
const base::Value& device,
|
||||||
blink::PermissionType permissionType);
|
blink::PermissionType permissionType);
|
||||||
|
|
||||||
|
// File System Access persistent permissions
|
||||||
|
void GrantFileSystemAccessPermission(const url::Origin& origin,
|
||||||
|
const base::Value& permission_grant,
|
||||||
|
blink::PermissionType permission_type);
|
||||||
|
|
||||||
|
void RevokeFileSystemAccessPermission(const url::Origin& origin,
|
||||||
|
const base::Value& permission_grant,
|
||||||
|
blink::PermissionType permission_type);
|
||||||
|
|
||||||
|
bool CheckFileSystemAccessPermission(const url::Origin& origin,
|
||||||
|
const base::Value& permission_grant,
|
||||||
|
blink::PermissionType permission_type);
|
||||||
|
|
||||||
|
// Get all file system access grants for an origin
|
||||||
|
std::vector<base::Value> GetFileSystemAccessGrantsForOrigin(
|
||||||
|
const url::Origin& origin,
|
||||||
|
blink::PermissionType permission_type);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using DevicePermissionMap = std::map<
|
using DevicePermissionMap = std::map<
|
||||||
blink::PermissionType,
|
blink::PermissionType,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "base/base_paths.h"
|
#include "base/base_paths.h"
|
||||||
|
#include "base/feature_list.h"
|
||||||
#include "base/files/file_path.h"
|
#include "base/files/file_path.h"
|
||||||
#include "base/files/file_util.h"
|
#include "base/files/file_util.h"
|
||||||
#include "base/json/values_util.h"
|
#include "base/json/values_util.h"
|
||||||
@@ -32,6 +33,7 @@
|
|||||||
#include "content/public/browser/web_contents.h"
|
#include "content/public/browser/web_contents.h"
|
||||||
#include "gin/data_object_builder.h"
|
#include "gin/data_object_builder.h"
|
||||||
#include "shell/browser/api/electron_api_session.h"
|
#include "shell/browser/api/electron_api_session.h"
|
||||||
|
#include "shell/browser/electron_browser_context.h"
|
||||||
#include "shell/browser/electron_permission_manager.h"
|
#include "shell/browser/electron_permission_manager.h"
|
||||||
#include "shell/browser/web_contents_permission_helper.h"
|
#include "shell/browser/web_contents_permission_helper.h"
|
||||||
#include "shell/common/gin_converters/callback_converter.h"
|
#include "shell/common/gin_converters/callback_converter.h"
|
||||||
@@ -81,13 +83,21 @@ struct Converter<
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using BlockType = ChromeFileSystemAccessPermissionContext::BlockType;
|
|
||||||
using HandleType = content::FileSystemAccessPermissionContext::HandleType;
|
using HandleType = content::FileSystemAccessPermissionContext::HandleType;
|
||||||
using GrantType = electron::FileSystemAccessPermissionContext::GrantType;
|
using GrantType = electron::FileSystemAccessPermissionContext::GrantType;
|
||||||
|
using BlockType = ChromeFileSystemAccessPermissionContext::BlockType;
|
||||||
using SensitiveEntryResult =
|
using SensitiveEntryResult =
|
||||||
ChromeFileSystemAccessPermissionContext::SensitiveEntryResult;
|
ChromeFileSystemAccessPermissionContext::SensitiveEntryResult;
|
||||||
using blink::mojom::PermissionStatus;
|
using blink::mojom::PermissionStatus;
|
||||||
|
|
||||||
|
// Preference keys for persistent file system access grants
|
||||||
|
const char kPermissionPathKey[] = "path";
|
||||||
|
const char kPermissionDisplayNameKey[] = "display-name";
|
||||||
|
const char kPermissionIsDirectoryKey[] = "is-directory";
|
||||||
|
const char kPermissionWritableKey[] = "writable";
|
||||||
|
const char kPermissionReadableKey[] = "readable";
|
||||||
|
const char kPermissionGrantTimeKey[] = "grant-time";
|
||||||
|
|
||||||
// Dictionary keys for the FILE_SYSTEM_LAST_PICKED_DIRECTORY website setting.
|
// Dictionary keys for the FILE_SYSTEM_LAST_PICKED_DIRECTORY website setting.
|
||||||
// Schema (per origin):
|
// Schema (per origin):
|
||||||
// {
|
// {
|
||||||
@@ -420,6 +430,10 @@ class FileSystemAccessPermissionContext::PermissionGrantImpl
|
|||||||
|
|
||||||
if (status == blink::mojom::PermissionStatus::GRANTED) {
|
if (status == blink::mojom::PermissionStatus::GRANTED) {
|
||||||
SetStatus(PermissionStatus::GRANTED);
|
SetStatus(PermissionStatus::GRANTED);
|
||||||
|
// Save persistent grant when user grants permission
|
||||||
|
if (context_) {
|
||||||
|
context_->SavePersistedGrantsForOrigin(origin_);
|
||||||
|
}
|
||||||
std::move(callback).Run(PermissionRequestOutcome::kUserGranted);
|
std::move(callback).Run(PermissionRequestOutcome::kUserGranted);
|
||||||
} else {
|
} else {
|
||||||
SetStatus(PermissionStatus::DENIED);
|
SetStatus(PermissionStatus::DENIED);
|
||||||
@@ -456,6 +470,9 @@ struct FileSystemAccessPermissionContext::OriginState {
|
|||||||
// PermissionGrantDestroyed().
|
// PermissionGrantDestroyed().
|
||||||
std::map<base::FilePath, PermissionGrantImpl*> read_grants;
|
std::map<base::FilePath, PermissionGrantImpl*> read_grants;
|
||||||
std::map<base::FilePath, PermissionGrantImpl*> write_grants;
|
std::map<base::FilePath, PermissionGrantImpl*> write_grants;
|
||||||
|
|
||||||
|
// Persistent grant status for this origin
|
||||||
|
PersistedGrantStatus persisted_grant_status = PersistedGrantStatus::kLoaded;
|
||||||
};
|
};
|
||||||
|
|
||||||
FileSystemAccessPermissionContext::FileSystemAccessPermissionContext(
|
FileSystemAccessPermissionContext::FileSystemAccessPermissionContext(
|
||||||
@@ -496,7 +513,14 @@ FileSystemAccessPermissionContext::GetReadPermissionGrant(
|
|||||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||||
// operator[] might insert a new OriginState in |active_permissions_map_|,
|
// operator[] might insert a new OriginState in |active_permissions_map_|,
|
||||||
// but that is exactly what we want.
|
// but that is exactly what we want.
|
||||||
|
bool is_new_origin = active_permissions_map_.find(origin) == active_permissions_map_.end();
|
||||||
auto& origin_state = active_permissions_map_[origin];
|
auto& origin_state = active_permissions_map_[origin];
|
||||||
|
|
||||||
|
// Load persisted grants for new origins
|
||||||
|
if (is_new_origin) {
|
||||||
|
LoadPersistedGrantsForOrigin(origin);
|
||||||
|
}
|
||||||
|
|
||||||
auto*& existing_grant = origin_state.read_grants[path_info.path];
|
auto*& existing_grant = origin_state.read_grants[path_info.path];
|
||||||
scoped_refptr<PermissionGrantImpl> grant;
|
scoped_refptr<PermissionGrantImpl> grant;
|
||||||
|
|
||||||
@@ -523,6 +547,11 @@ FileSystemAccessPermissionContext::GetReadPermissionGrant(
|
|||||||
if (creating_new_grant &&
|
if (creating_new_grant &&
|
||||||
AncestorHasActivePermission(origin, path_info.path, GrantType::kRead)) {
|
AncestorHasActivePermission(origin, path_info.path, GrantType::kRead)) {
|
||||||
grant->SetStatus(PermissionStatus::GRANTED);
|
grant->SetStatus(PermissionStatus::GRANTED);
|
||||||
|
} else if (creating_new_grant &&
|
||||||
|
CanAutoGrantViaPersistentPermission(origin, path_info.path,
|
||||||
|
handle_type, GrantType::kRead)) {
|
||||||
|
// Check if we can auto-grant via persistent permissions
|
||||||
|
grant->SetStatus(PermissionStatus::GRANTED);
|
||||||
} else {
|
} else {
|
||||||
switch (user_action) {
|
switch (user_action) {
|
||||||
case UserAction::kOpen:
|
case UserAction::kOpen:
|
||||||
@@ -554,7 +583,14 @@ FileSystemAccessPermissionContext::GetWritePermissionGrant(
|
|||||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||||
// operator[] might insert a new OriginState in |active_permissions_map_|,
|
// operator[] might insert a new OriginState in |active_permissions_map_|,
|
||||||
// but that is exactly what we want.
|
// but that is exactly what we want.
|
||||||
|
bool is_new_origin = active_permissions_map_.find(origin) == active_permissions_map_.end();
|
||||||
auto& origin_state = active_permissions_map_[origin];
|
auto& origin_state = active_permissions_map_[origin];
|
||||||
|
|
||||||
|
// Load persisted grants for new origins
|
||||||
|
if (is_new_origin) {
|
||||||
|
LoadPersistedGrantsForOrigin(origin);
|
||||||
|
}
|
||||||
|
|
||||||
auto*& existing_grant = origin_state.write_grants[path_info.path];
|
auto*& existing_grant = origin_state.write_grants[path_info.path];
|
||||||
scoped_refptr<PermissionGrantImpl> grant;
|
scoped_refptr<PermissionGrantImpl> grant;
|
||||||
|
|
||||||
@@ -581,6 +617,11 @@ FileSystemAccessPermissionContext::GetWritePermissionGrant(
|
|||||||
if (creating_new_grant &&
|
if (creating_new_grant &&
|
||||||
AncestorHasActivePermission(origin, path_info.path, GrantType::kWrite)) {
|
AncestorHasActivePermission(origin, path_info.path, GrantType::kWrite)) {
|
||||||
grant->SetStatus(PermissionStatus::GRANTED);
|
grant->SetStatus(PermissionStatus::GRANTED);
|
||||||
|
} else if (creating_new_grant &&
|
||||||
|
CanAutoGrantViaPersistentPermission(origin, path_info.path,
|
||||||
|
handle_type, GrantType::kWrite)) {
|
||||||
|
// Check if we can auto-grant via persistent permissions
|
||||||
|
grant->SetStatus(PermissionStatus::GRANTED);
|
||||||
} else {
|
} else {
|
||||||
switch (user_action) {
|
switch (user_action) {
|
||||||
case UserAction::kSave:
|
case UserAction::kSave:
|
||||||
@@ -830,7 +871,7 @@ content::PathInfo FileSystemAccessPermissionContext::GetLastPickedDirectory(
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto type_int = entry->FindInt(kPathTypeKey)
|
auto type_int = entry->FindInt(kPathTypeKey)
|
||||||
.value_or(static_cast<int>(content::PathType::kLocal));
|
.value_or(static_cast<int>(content::PathType::kExternal));
|
||||||
path_info.type = type_int == static_cast<int>(content::PathType::kExternal)
|
path_info.type = type_int == static_cast<int>(content::PathType::kExternal)
|
||||||
? content::PathType::kExternal
|
? content::PathType::kExternal
|
||||||
: content::PathType::kLocal;
|
: content::PathType::kLocal;
|
||||||
@@ -968,7 +1009,6 @@ bool FileSystemAccessPermissionContext::OriginHasReadAccess(
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileSystemAccessPermissionContext::OriginHasWriteAccess(
|
bool FileSystemAccessPermissionContext::OriginHasWriteAccess(
|
||||||
const url::Origin& origin) {
|
const url::Origin& origin) {
|
||||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||||
@@ -992,6 +1032,11 @@ void FileSystemAccessPermissionContext::NavigatedAwayFromOrigin(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mark persistent grants as backgrounded when navigating away
|
||||||
|
if (it->second.persisted_grant_status == PersistedGrantStatus::kCurrent) {
|
||||||
|
SetPersistedGrantStatus(origin, PersistedGrantStatus::kBackgrounded);
|
||||||
|
}
|
||||||
|
|
||||||
// Start a timer to possibly clean up permissions for this origin.
|
// Start a timer to possibly clean up permissions for this origin.
|
||||||
if (!it->second.cleanup_timer) {
|
if (!it->second.cleanup_timer) {
|
||||||
it->second.cleanup_timer = std::make_unique<base::RetainingOneShotTimer>(
|
it->second.cleanup_timer = std::make_unique<base::RetainingOneShotTimer>(
|
||||||
@@ -1070,4 +1115,265 @@ FileSystemAccessPermissionContext::GetWeakPtr() {
|
|||||||
return weak_factory_.GetWeakPtr();
|
return weak_factory_.GetWeakPtr();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace electron
|
// ============================================================================
|
||||||
|
// Persistent Permissions Implementation
|
||||||
|
//
|
||||||
|
// This implementation provides persistent file system access permissions that
|
||||||
|
// survive browser sessions. Unlike Chrome's ObjectPermissionContextBase
|
||||||
|
// approach, this uses Electron's preference system for storage.
|
||||||
|
//
|
||||||
|
// Key concepts:
|
||||||
|
// - PersistedGrantType: Represents the current state of persistent grants
|
||||||
|
// - kNone: No persistent grants exist
|
||||||
|
// - kDormant: Grants exist but are backgrounded
|
||||||
|
// - kActive: Grants are currently active
|
||||||
|
//
|
||||||
|
// - PersistedGrantStatus: Tracks origin-specific persistent state
|
||||||
|
// - kLoaded: Origin state loaded from preferences
|
||||||
|
// - kCurrent: Grants are active in current session
|
||||||
|
// - kBackgrounded: Grants exist but origin is backgrounded
|
||||||
|
//
|
||||||
|
// The implementation integrates with existing grant logic by:
|
||||||
|
// 1. Loading persisted grants when an origin first requests access
|
||||||
|
// 2. Checking persistent grants during auto-grant decisions
|
||||||
|
// 3. Saving grants when users approve permissions
|
||||||
|
// 4. Managing grant lifecycle during navigation events
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
FileSystemAccessPermissionContext::PersistedGrantType
|
||||||
|
FileSystemAccessPermissionContext::GetPersistedGrantType(
|
||||||
|
const url::Origin& origin) {
|
||||||
|
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||||
|
|
||||||
|
auto it = active_permissions_map_.find(origin);
|
||||||
|
if (it == active_permissions_map_.end()) {
|
||||||
|
return PersistedGrantType::kNone;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (it->second.persisted_grant_status) {
|
||||||
|
case PersistedGrantStatus::kBackgrounded:
|
||||||
|
return PersistedGrantType::kDormant;
|
||||||
|
case PersistedGrantStatus::kLoaded:
|
||||||
|
case PersistedGrantStatus::kCurrent:
|
||||||
|
return PersistedGrantType::kActive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSystemAccessPermissionContext::PersistedGrantStatus
|
||||||
|
FileSystemAccessPermissionContext::GetPersistedGrantStatus(
|
||||||
|
const url::Origin& origin) const {
|
||||||
|
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||||
|
|
||||||
|
auto it = active_permissions_map_.find(origin);
|
||||||
|
if (it == active_permissions_map_.end()) {
|
||||||
|
return PersistedGrantStatus::kLoaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
return it->second.persisted_grant_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemAccessPermissionContext::SetPersistedGrantStatus(
|
||||||
|
const url::Origin& origin,
|
||||||
|
PersistedGrantStatus status) {
|
||||||
|
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||||
|
|
||||||
|
auto& origin_state = active_permissions_map_[origin];
|
||||||
|
origin_state.persisted_grant_status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemAccessPermissionContext::LoadPersistedGrantsForOrigin(
|
||||||
|
const url::Origin& origin) {
|
||||||
|
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||||
|
|
||||||
|
auto* browser_context = static_cast<ElectronBrowserContext*>(browser_context_);
|
||||||
|
|
||||||
|
// Load all file system grants for this origin
|
||||||
|
auto grants = browser_context->GetFileSystemAccessGrantsForOrigin(
|
||||||
|
origin, blink::PermissionType::FILE_SYSTEM);
|
||||||
|
|
||||||
|
// Create permission grants from persisted data
|
||||||
|
auto& origin_state = active_permissions_map_[origin];
|
||||||
|
|
||||||
|
for (const auto& grant_data : grants) {
|
||||||
|
if (auto* path_str = grant_data.GetDict().FindString(kPermissionPathKey)) {
|
||||||
|
base::FilePath path = base::FilePath::FromUTF8Unsafe(*path_str);
|
||||||
|
std::string display_name;
|
||||||
|
if (auto* display_name_val = grant_data.GetDict().FindString(kPermissionDisplayNameKey))
|
||||||
|
display_name = *display_name_val;
|
||||||
|
auto is_directory = grant_data.GetDict().FindBool(kPermissionIsDirectoryKey).value_or(false);
|
||||||
|
auto readable = grant_data.GetDict().FindBool(kPermissionReadableKey).value_or(false);
|
||||||
|
auto writable = grant_data.GetDict().FindBool(kPermissionWritableKey).value_or(false);
|
||||||
|
|
||||||
|
content::PathInfo path_info;
|
||||||
|
path_info.path = path;
|
||||||
|
path_info.display_name = display_name;
|
||||||
|
path_info.type = content::PathType::kLocal;
|
||||||
|
|
||||||
|
HandleType handle_type = is_directory ? HandleType::kDirectory : HandleType::kFile;
|
||||||
|
|
||||||
|
// Create read grant if readable
|
||||||
|
if (readable && origin_state.read_grants.find(path) == origin_state.read_grants.end()) {
|
||||||
|
auto grant = base::MakeRefCounted<PermissionGrantImpl>(
|
||||||
|
weak_factory_.GetWeakPtr(), origin, path_info, handle_type,
|
||||||
|
GrantType::kRead, UserAction::kLoadFromStorage);
|
||||||
|
grant->SetStatus(PermissionStatus::GRANTED);
|
||||||
|
origin_state.read_grants[path] = grant.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create write grant if writable
|
||||||
|
if (writable && origin_state.write_grants.find(path) == origin_state.write_grants.end()) {
|
||||||
|
auto grant = base::MakeRefCounted<PermissionGrantImpl>(
|
||||||
|
weak_factory_.GetWeakPtr(), origin, path_info, handle_type,
|
||||||
|
GrantType::kWrite, UserAction::kLoadFromStorage);
|
||||||
|
grant->SetStatus(PermissionStatus::GRANTED);
|
||||||
|
origin_state.write_grants[path] = grant.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SetPersistedGrantStatus(origin, PersistedGrantStatus::kLoaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemAccessPermissionContext::SavePersistedGrantsForOrigin(
|
||||||
|
const url::Origin& origin) {
|
||||||
|
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||||
|
|
||||||
|
auto* browser_context = static_cast<ElectronBrowserContext*>(browser_context_);
|
||||||
|
auto it = active_permissions_map_.find(origin);
|
||||||
|
if (it == active_permissions_map_.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& origin_state = it->second;
|
||||||
|
|
||||||
|
// Collect all unique paths and their permissions
|
||||||
|
std::map<base::FilePath, std::pair<bool, bool>> path_permissions; // path -> (readable, writable)
|
||||||
|
|
||||||
|
// Add read grants
|
||||||
|
for (const auto& [path, grant] : origin_state.read_grants) {
|
||||||
|
if (grant->GetStatus() == PermissionStatus::GRANTED) {
|
||||||
|
path_permissions[path].first = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add write grants
|
||||||
|
for (const auto& [path, grant] : origin_state.write_grants) {
|
||||||
|
if (grant->GetStatus() == PermissionStatus::GRANTED) {
|
||||||
|
path_permissions[path].second = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save combined grants
|
||||||
|
for (const auto& [path, permissions] : path_permissions) {
|
||||||
|
bool readable = permissions.first;
|
||||||
|
bool writable = permissions.second;
|
||||||
|
|
||||||
|
if (readable || writable) {
|
||||||
|
// Get grant info from either read or write grant
|
||||||
|
PermissionGrantImpl* grant = nullptr;
|
||||||
|
if (readable && origin_state.read_grants.count(path)) {
|
||||||
|
grant = origin_state.read_grants.at(path);
|
||||||
|
} else if (writable && origin_state.write_grants.count(path)) {
|
||||||
|
grant = origin_state.write_grants.at(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (grant) {
|
||||||
|
base::Value::Dict grant_data;
|
||||||
|
grant_data.Set(kPermissionPathKey, path.AsUTF8Unsafe());
|
||||||
|
grant_data.Set(kPermissionDisplayNameKey, grant->GetDisplayName());
|
||||||
|
grant_data.Set(kPermissionIsDirectoryKey, grant->handle_type() == HandleType::kDirectory);
|
||||||
|
grant_data.Set(kPermissionReadableKey, readable);
|
||||||
|
grant_data.Set(kPermissionWritableKey, writable);
|
||||||
|
grant_data.Set(kPermissionGrantTimeKey, base::TimeToValue(clock_->Now()));
|
||||||
|
|
||||||
|
browser_context->GrantFileSystemAccessPermission(
|
||||||
|
origin, base::Value(std::move(grant_data)),
|
||||||
|
blink::PermissionType::FILE_SYSTEM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SetPersistedGrantStatus(origin, PersistedGrantStatus::kCurrent);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileSystemAccessPermissionContext::CanAutoGrantViaPersistentPermission(
|
||||||
|
const url::Origin& origin,
|
||||||
|
const base::FilePath& path,
|
||||||
|
HandleType handle_type,
|
||||||
|
GrantType grant_type) {
|
||||||
|
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||||
|
|
||||||
|
// Check if we have an active persistent permission for this path
|
||||||
|
auto persisted_grant_type = GetPersistedGrantType(origin);
|
||||||
|
if (persisted_grant_type == PersistedGrantType::kNone) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* browser_context = static_cast<ElectronBrowserContext*>(browser_context_);
|
||||||
|
|
||||||
|
// Get all grants for this origin
|
||||||
|
auto grants = browser_context->GetFileSystemAccessGrantsForOrigin(
|
||||||
|
origin, blink::PermissionType::FILE_SYSTEM);
|
||||||
|
|
||||||
|
// Check if any grant matches our path and permission type
|
||||||
|
for (const auto& grant_data : grants) {
|
||||||
|
const auto& dict = grant_data.GetDict();
|
||||||
|
auto* stored_path = dict.FindString("path");
|
||||||
|
if (!stored_path || *stored_path != path.AsUTF8Unsafe()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto stored_is_directory = dict.FindBool("is-directory").value_or(false);
|
||||||
|
bool handle_type_matches = (stored_is_directory && handle_type == HandleType::kDirectory) ||
|
||||||
|
(!stored_is_directory && handle_type == HandleType::kFile);
|
||||||
|
if (!handle_type_matches) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool readable = dict.FindBool("readable").value_or(false);
|
||||||
|
bool writable = dict.FindBool("writable").value_or(false);
|
||||||
|
|
||||||
|
if ((grant_type == GrantType::kRead && readable) ||
|
||||||
|
(grant_type == GrantType::kWrite && writable)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemAccessPermissionContext::EnablePersistentPermissions(
|
||||||
|
const url::Origin& origin) {
|
||||||
|
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||||
|
|
||||||
|
SetPersistedGrantStatus(origin, PersistedGrantStatus::kCurrent);
|
||||||
|
SavePersistedGrantsForOrigin(origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemAccessPermissionContext::DisablePersistentPermissions(
|
||||||
|
const url::Origin& origin) {
|
||||||
|
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||||
|
|
||||||
|
auto* browser_context = static_cast<ElectronBrowserContext*>(browser_context_);
|
||||||
|
|
||||||
|
// Get all existing grants and revoke them
|
||||||
|
auto grants = browser_context->GetFileSystemAccessGrantsForOrigin(
|
||||||
|
origin, blink::PermissionType::FILE_SYSTEM);
|
||||||
|
|
||||||
|
for (const auto& grant : grants) {
|
||||||
|
browser_context->RevokeFileSystemAccessPermission(
|
||||||
|
origin, grant, blink::PermissionType::FILE_SYSTEM);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetPersistedGrantStatus(origin, PersistedGrantStatus::kLoaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileSystemAccessPermissionContext::HasPersistentPermissions(
|
||||||
|
const url::Origin& origin) {
|
||||||
|
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||||
|
|
||||||
|
auto persisted_grant_type = GetPersistedGrantType(origin);
|
||||||
|
return persisted_grant_type != PersistedGrantType::kNone;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -20,7 +20,6 @@
|
|||||||
#include "base/values.h"
|
#include "base/values.h"
|
||||||
#include "chrome/browser/file_system_access/chrome_file_system_access_permission_context.h" // nogncheck
|
#include "chrome/browser/file_system_access/chrome_file_system_access_permission_context.h" // nogncheck
|
||||||
#include "components/keyed_service/core/keyed_service.h"
|
#include "components/keyed_service/core/keyed_service.h"
|
||||||
|
|
||||||
class GURL;
|
class GURL;
|
||||||
|
|
||||||
namespace gin {
|
namespace gin {
|
||||||
@@ -37,12 +36,50 @@ class FileSystemURL;
|
|||||||
|
|
||||||
namespace electron {
|
namespace electron {
|
||||||
|
|
||||||
|
// FileSystemAccessPermissionContext provides file system access permission
|
||||||
|
// management for Electron applications. This implementation includes support
|
||||||
|
// for persistent permissions that survive browser sessions.
|
||||||
|
//
|
||||||
|
// Unlike Chrome's implementation which uses ObjectPermissionContextBase, this
|
||||||
|
// uses Electron's preference system for persistent storage, making it compatible
|
||||||
|
// with Electron's architecture.
|
||||||
|
//
|
||||||
|
// Key features:
|
||||||
|
// - Session-based grants for temporary permissions
|
||||||
|
// - Persistent grants that survive application restarts
|
||||||
|
// - Integration with Electron's permission system
|
||||||
|
// - Path blocklist checking and security restrictions
|
||||||
|
//
|
||||||
|
// Usage:
|
||||||
|
// - Call EnablePersistentPermissions() to enable persistent storage for an origin
|
||||||
|
// - Grants are automatically saved when users approve permissions
|
||||||
|
// - Grants are automatically loaded when origins access the file system
|
||||||
class FileSystemAccessPermissionContext
|
class FileSystemAccessPermissionContext
|
||||||
: public KeyedService,
|
: public KeyedService,
|
||||||
public content::FileSystemAccessPermissionContext {
|
public content::FileSystemAccessPermissionContext {
|
||||||
public:
|
public:
|
||||||
enum class GrantType { kRead, kWrite };
|
enum class GrantType { kRead, kWrite };
|
||||||
|
|
||||||
|
// Represents persistent grant types for permissions
|
||||||
|
enum class PersistedGrantType {
|
||||||
|
// No persisted grants exist for this origin
|
||||||
|
kNone,
|
||||||
|
// Origin has persisted grants that are currently dormant (backgrounded)
|
||||||
|
kDormant,
|
||||||
|
// Origin has active persistent grants
|
||||||
|
kActive,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Represents the origin-scoped state for persistent grants
|
||||||
|
enum class PersistedGrantStatus {
|
||||||
|
// Origin state has been loaded from preferences
|
||||||
|
kLoaded,
|
||||||
|
// Persisted grants are active for this session
|
||||||
|
kCurrent,
|
||||||
|
// Persisted grants are dormant due to being backgrounded
|
||||||
|
kBackgrounded
|
||||||
|
};
|
||||||
|
|
||||||
explicit FileSystemAccessPermissionContext(
|
explicit FileSystemAccessPermissionContext(
|
||||||
content::BrowserContext* browser_context,
|
content::BrowserContext* browser_context,
|
||||||
const base::Clock* clock = base::DefaultClock::GetInstance());
|
const base::Clock* clock = base::DefaultClock::GetInstance());
|
||||||
@@ -122,6 +159,15 @@ class FileSystemAccessPermissionContext
|
|||||||
bool OriginHasReadAccess(const url::Origin& origin);
|
bool OriginHasReadAccess(const url::Origin& origin);
|
||||||
bool OriginHasWriteAccess(const url::Origin& origin);
|
bool OriginHasWriteAccess(const url::Origin& origin);
|
||||||
|
|
||||||
|
// Enable persistent permissions for an origin
|
||||||
|
void EnablePersistentPermissions(const url::Origin& origin);
|
||||||
|
|
||||||
|
// Disable persistent permissions for an origin
|
||||||
|
void DisablePersistentPermissions(const url::Origin& origin);
|
||||||
|
|
||||||
|
// Check if an origin has persistent permissions enabled
|
||||||
|
bool HasPersistentPermissions(const url::Origin& origin);
|
||||||
|
|
||||||
// Called by FileSystemAccessWebContentsHelper when a top-level frame was
|
// Called by FileSystemAccessWebContentsHelper when a top-level frame was
|
||||||
// navigated away from `origin` to some other origin.
|
// navigated away from `origin` to some other origin.
|
||||||
void NavigatedAwayFromOrigin(const url::Origin& origin);
|
void NavigatedAwayFromOrigin(const url::Origin& origin);
|
||||||
@@ -136,6 +182,24 @@ class FileSystemAccessPermissionContext
|
|||||||
|
|
||||||
void PermissionGrantDestroyed(PermissionGrantImpl* grant);
|
void PermissionGrantDestroyed(PermissionGrantImpl* grant);
|
||||||
|
|
||||||
|
// Persistent permissions methods
|
||||||
|
PersistedGrantType GetPersistedGrantType(const url::Origin& origin);
|
||||||
|
PersistedGrantStatus GetPersistedGrantStatus(const url::Origin& origin) const;
|
||||||
|
void SetPersistedGrantStatus(const url::Origin& origin,
|
||||||
|
PersistedGrantStatus status);
|
||||||
|
|
||||||
|
// Load persisted grants from preferences for an origin
|
||||||
|
void LoadPersistedGrantsForOrigin(const url::Origin& origin);
|
||||||
|
|
||||||
|
// Save current grants to preferences for an origin
|
||||||
|
void SavePersistedGrantsForOrigin(const url::Origin& origin);
|
||||||
|
|
||||||
|
// Check if a path can be auto-granted via persistent permissions
|
||||||
|
bool CanAutoGrantViaPersistentPermission(const url::Origin& origin,
|
||||||
|
const base::FilePath& path,
|
||||||
|
HandleType handle_type,
|
||||||
|
GrantType grant_type);
|
||||||
|
|
||||||
void CheckShouldBlockAccessToPathAndReply(
|
void CheckShouldBlockAccessToPathAndReply(
|
||||||
base::FilePath path,
|
base::FilePath path,
|
||||||
HandleType handle_type,
|
HandleType handle_type,
|
||||||
|
|||||||
Reference in New Issue
Block a user