mirror of
https://github.com/electron/electron.git
synced 2026-02-19 03:14:51 -05:00
Compare commits
1 Commits
fix-shortc
...
perms
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8608d442e3 |
@@ -13,12 +13,14 @@
|
||||
#include "base/base_paths.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/containers/to_vector.h"
|
||||
#include "base/feature_list.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/no_destructor.h"
|
||||
#include "base/path_service.h"
|
||||
#include "base/strings/escape.h"
|
||||
#include "base/strings/string_number_conversions.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/common/chrome_paths.h"
|
||||
#include "chrome/common/pref_names.h"
|
||||
@@ -106,6 +108,34 @@ namespace electron {
|
||||
|
||||
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.
|
||||
media::mojom::CaptureHandlePtr CreateCaptureHandle(
|
||||
content::WebContents* capturer,
|
||||
@@ -863,6 +893,94 @@ bool ElectronBrowserContext::CheckDevicePermission(
|
||||
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
|
||||
ElectronBrowserContext* ElectronBrowserContext::From(
|
||||
const std::string& partition,
|
||||
|
||||
@@ -159,6 +159,24 @@ class ElectronBrowserContext : public content::BrowserContext {
|
||||
const base::Value& device,
|
||||
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:
|
||||
using DevicePermissionMap = std::map<
|
||||
blink::PermissionType,
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include "base/base_paths.h"
|
||||
#include "base/feature_list.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/json/values_util.h"
|
||||
@@ -32,6 +33,7 @@
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "gin/data_object_builder.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/web_contents_permission_helper.h"
|
||||
#include "shell/common/gin_converters/callback_converter.h"
|
||||
@@ -81,13 +83,21 @@ struct Converter<
|
||||
|
||||
namespace {
|
||||
|
||||
using BlockType = ChromeFileSystemAccessPermissionContext::BlockType;
|
||||
using HandleType = content::FileSystemAccessPermissionContext::HandleType;
|
||||
using GrantType = electron::FileSystemAccessPermissionContext::GrantType;
|
||||
using BlockType = ChromeFileSystemAccessPermissionContext::BlockType;
|
||||
using SensitiveEntryResult =
|
||||
ChromeFileSystemAccessPermissionContext::SensitiveEntryResult;
|
||||
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.
|
||||
// Schema (per origin):
|
||||
// {
|
||||
@@ -420,6 +430,10 @@ class FileSystemAccessPermissionContext::PermissionGrantImpl
|
||||
|
||||
if (status == blink::mojom::PermissionStatus::GRANTED) {
|
||||
SetStatus(PermissionStatus::GRANTED);
|
||||
// Save persistent grant when user grants permission
|
||||
if (context_) {
|
||||
context_->SavePersistedGrantsForOrigin(origin_);
|
||||
}
|
||||
std::move(callback).Run(PermissionRequestOutcome::kUserGranted);
|
||||
} else {
|
||||
SetStatus(PermissionStatus::DENIED);
|
||||
@@ -456,6 +470,9 @@ struct FileSystemAccessPermissionContext::OriginState {
|
||||
// PermissionGrantDestroyed().
|
||||
std::map<base::FilePath, PermissionGrantImpl*> read_grants;
|
||||
std::map<base::FilePath, PermissionGrantImpl*> write_grants;
|
||||
|
||||
// Persistent grant status for this origin
|
||||
PersistedGrantStatus persisted_grant_status = PersistedGrantStatus::kLoaded;
|
||||
};
|
||||
|
||||
FileSystemAccessPermissionContext::FileSystemAccessPermissionContext(
|
||||
@@ -496,7 +513,14 @@ FileSystemAccessPermissionContext::GetReadPermissionGrant(
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
// operator[] might insert a new OriginState in |active_permissions_map_|,
|
||||
// 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];
|
||||
|
||||
// Load persisted grants for new origins
|
||||
if (is_new_origin) {
|
||||
LoadPersistedGrantsForOrigin(origin);
|
||||
}
|
||||
|
||||
auto*& existing_grant = origin_state.read_grants[path_info.path];
|
||||
scoped_refptr<PermissionGrantImpl> grant;
|
||||
|
||||
@@ -523,6 +547,11 @@ FileSystemAccessPermissionContext::GetReadPermissionGrant(
|
||||
if (creating_new_grant &&
|
||||
AncestorHasActivePermission(origin, path_info.path, GrantType::kRead)) {
|
||||
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 {
|
||||
switch (user_action) {
|
||||
case UserAction::kOpen:
|
||||
@@ -554,7 +583,14 @@ FileSystemAccessPermissionContext::GetWritePermissionGrant(
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
// operator[] might insert a new OriginState in |active_permissions_map_|,
|
||||
// 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];
|
||||
|
||||
// Load persisted grants for new origins
|
||||
if (is_new_origin) {
|
||||
LoadPersistedGrantsForOrigin(origin);
|
||||
}
|
||||
|
||||
auto*& existing_grant = origin_state.write_grants[path_info.path];
|
||||
scoped_refptr<PermissionGrantImpl> grant;
|
||||
|
||||
@@ -581,6 +617,11 @@ FileSystemAccessPermissionContext::GetWritePermissionGrant(
|
||||
if (creating_new_grant &&
|
||||
AncestorHasActivePermission(origin, path_info.path, GrantType::kWrite)) {
|
||||
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 {
|
||||
switch (user_action) {
|
||||
case UserAction::kSave:
|
||||
@@ -830,7 +871,7 @@ content::PathInfo FileSystemAccessPermissionContext::GetLastPickedDirectory(
|
||||
}
|
||||
|
||||
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)
|
||||
? content::PathType::kExternal
|
||||
: content::PathType::kLocal;
|
||||
@@ -968,7 +1009,6 @@ bool FileSystemAccessPermissionContext::OriginHasReadAccess(
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FileSystemAccessPermissionContext::OriginHasWriteAccess(
|
||||
const url::Origin& origin) {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
@@ -992,6 +1032,11 @@ void FileSystemAccessPermissionContext::NavigatedAwayFromOrigin(
|
||||
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.
|
||||
if (!it->second.cleanup_timer) {
|
||||
it->second.cleanup_timer = std::make_unique<base::RetainingOneShotTimer>(
|
||||
@@ -1070,4 +1115,265 @@ FileSystemAccessPermissionContext::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 "chrome/browser/file_system_access/chrome_file_system_access_permission_context.h" // nogncheck
|
||||
#include "components/keyed_service/core/keyed_service.h"
|
||||
|
||||
class GURL;
|
||||
|
||||
namespace gin {
|
||||
@@ -37,12 +36,50 @@ class FileSystemURL;
|
||||
|
||||
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
|
||||
: public KeyedService,
|
||||
public content::FileSystemAccessPermissionContext {
|
||||
public:
|
||||
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(
|
||||
content::BrowserContext* browser_context,
|
||||
const base::Clock* clock = base::DefaultClock::GetInstance());
|
||||
@@ -122,6 +159,15 @@ class FileSystemAccessPermissionContext
|
||||
bool OriginHasReadAccess(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
|
||||
// navigated away from `origin` to some other origin.
|
||||
void NavigatedAwayFromOrigin(const url::Origin& origin);
|
||||
@@ -136,6 +182,24 @@ class FileSystemAccessPermissionContext
|
||||
|
||||
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(
|
||||
base::FilePath path,
|
||||
HandleType handle_type,
|
||||
|
||||
Reference in New Issue
Block a user