123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893 |
- // Copyright (c) 2024 Microsoft, GmbH
- // Use of this source code is governed by the MIT license that can be
- // found in the LICENSE file.
- #include "shell/browser/file_system_access/file_system_access_permission_context.h"
- #include <string>
- #include <utility>
- #include "base/base_paths.h"
- #include "base/files/file_path.h"
- #include "base/json/values_util.h"
- #include "base/path_service.h"
- #include "base/task/thread_pool.h"
- #include "base/time/time.h"
- #include "base/values.h"
- #include "chrome/browser/browser_process.h"
- #include "chrome/browser/file_system_access/chrome_file_system_access_permission_context.h" // nogncheck
- #include "chrome/browser/file_system_access/file_system_access_features.h"
- #include "chrome/common/chrome_paths.h"
- #include "chrome/grit/generated_resources.h"
- #include "content/public/browser/browser_context.h"
- #include "content/public/browser/browser_thread.h"
- #include "content/public/browser/disallow_activation_reason.h"
- #include "content/public/browser/render_frame_host.h"
- #include "content/public/browser/render_process_host.h"
- #include "content/public/browser/web_contents.h"
- #include "shell/browser/electron_permission_manager.h"
- #include "shell/browser/web_contents_permission_helper.h"
- #include "shell/common/gin_converters/file_path_converter.h"
- #include "third_party/blink/public/mojom/file_system_access/file_system_access_manager.mojom.h"
- #include "ui/base/l10n/l10n_util.h"
- #include "url/origin.h"
- namespace {
- using BlockType = ChromeFileSystemAccessPermissionContext::BlockType;
- using HandleType = content::FileSystemAccessPermissionContext::HandleType;
- using GrantType = electron::FileSystemAccessPermissionContext::GrantType;
- using blink::mojom::PermissionStatus;
- // Dictionary keys for the FILE_SYSTEM_LAST_PICKED_DIRECTORY website setting.
- // Schema (per origin):
- // {
- // ...
- // {
- // "default-id" : { "path" : <path> , "path-type" : <type>}
- // "custom-id-fruit" : { "path" : <path> , "path-type" : <type> }
- // "custom-id-flower" : { "path" : <path> , "path-type" : <type> }
- // ...
- // }
- // ...
- // }
- const char kDefaultLastPickedDirectoryKey[] = "default-id";
- const char kCustomLastPickedDirectoryKey[] = "custom-id";
- const char kPathKey[] = "path";
- const char kPathTypeKey[] = "path-type";
- const char kTimestampKey[] = "timestamp";
- #if BUILDFLAG(IS_WIN)
- [[nodiscard]] constexpr bool ContainsInvalidDNSCharacter(
- base::FilePath::StringType hostname) {
- return !base::ranges::all_of(hostname, [](base::FilePath::CharType c) {
- return (c >= L'A' && c <= L'Z') || (c >= L'a' && c <= L'z') ||
- (c >= L'0' && c <= L'9') || (c == L'.') || (c == L'-');
- });
- }
- bool MaybeIsLocalUNCPath(const base::FilePath& path) {
- if (!path.IsNetwork()) {
- return false;
- }
- const std::vector<base::FilePath::StringType> components =
- path.GetComponents();
- // Check for server name that could represent a local system. We only
- // check for a very short list, as it is impossible to cover all different
- // variants on Windows.
- if (components.size() >= 2 &&
- (base::FilePath::CompareEqualIgnoreCase(components[1],
- FILE_PATH_LITERAL("localhost")) ||
- components[1] == FILE_PATH_LITERAL("127.0.0.1") ||
- components[1] == FILE_PATH_LITERAL(".") ||
- components[1] == FILE_PATH_LITERAL("?") ||
- ContainsInvalidDNSCharacter(components[1]))) {
- return true;
- }
- // In case we missed the server name check above, we also check for shares
- // ending with '$' as they represent pre-defined shares, including the local
- // drives.
- for (size_t i = 2; i < components.size(); ++i) {
- if (components[i].back() == L'$') {
- return true;
- }
- }
- return false;
- }
- #endif // BUILDFLAG(IS_WIN)
- // Describes a rule for blocking a directory, which can be constructed
- // dynamically (based on state) or statically (from kBlockedPaths).
- struct BlockPathRule {
- base::FilePath path;
- BlockType type;
- };
- bool ShouldBlockAccessToPath(const base::FilePath& path,
- HandleType handle_type,
- std::vector<BlockPathRule> rules) {
- DCHECK(!path.empty());
- DCHECK(path.IsAbsolute());
- #if BUILDFLAG(IS_WIN)
- // On Windows, local UNC paths are rejected, as UNC path can be written in a
- // way that can bypass the blocklist.
- if (base::FeatureList::IsEnabled(
- features::kFileSystemAccessLocalUNCPathBlock) &&
- MaybeIsLocalUNCPath(path)) {
- return true;
- }
- #endif // BUILDFLAG(IS_WIN)
- // Add the hard-coded rules to the dynamic rules.
- for (auto const& [key, rule_path, type] :
- ChromeFileSystemAccessPermissionContext::kBlockedPaths) {
- if (key == ChromeFileSystemAccessPermissionContext::kNoBasePathKey) {
- rules.emplace_back(base::FilePath{rule_path}, type);
- } else if (base::FilePath path; base::PathService::Get(key, &path)) {
- rules.emplace_back(rule_path ? path.Append(rule_path) : path, type);
- }
- }
- base::FilePath nearest_ancestor;
- BlockType nearest_ancestor_block_type = BlockType::kDontBlockChildren;
- for (const auto& block : rules) {
- if (path == block.path || path.IsParent(block.path)) {
- DLOG(INFO) << "Blocking access to " << path
- << " because it is a parent of " << block.path;
- return true;
- }
- if (block.path.IsParent(path) &&
- (nearest_ancestor.empty() || nearest_ancestor.IsParent(block.path))) {
- nearest_ancestor = block.path;
- nearest_ancestor_block_type = block.type;
- }
- }
- // The path we're checking is not in a potentially blocked directory, or the
- // nearest ancestor does not block access to its children. Grant access.
- if (nearest_ancestor.empty() ||
- nearest_ancestor_block_type == BlockType::kDontBlockChildren) {
- return false;
- }
- // The path we're checking is a file, and the nearest ancestor only blocks
- // access to directories. Grant access.
- if (handle_type == HandleType::kFile &&
- nearest_ancestor_block_type == BlockType::kBlockNestedDirectories) {
- return false;
- }
- // The nearest ancestor blocks access to its children, so block access.
- DLOG(INFO) << "Blocking access to " << path << " because it is inside "
- << nearest_ancestor;
- return true;
- }
- std::string GenerateLastPickedDirectoryKey(const std::string& id) {
- return id.empty() ? kDefaultLastPickedDirectoryKey
- : base::StrCat({kCustomLastPickedDirectoryKey, "-", id});
- }
- } // namespace
- namespace electron {
- class FileSystemAccessPermissionContext::PermissionGrantImpl
- : public content::FileSystemAccessPermissionGrant {
- public:
- PermissionGrantImpl(base::WeakPtr<FileSystemAccessPermissionContext> context,
- const url::Origin& origin,
- const base::FilePath& path,
- HandleType handle_type,
- GrantType type,
- UserAction user_action)
- : context_{std::move(context)},
- origin_{origin},
- handle_type_{handle_type},
- type_{type},
- path_{path} {}
- // FileSystemAccessPermissionGrant:
- PermissionStatus GetStatus() override {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return status_;
- }
- base::FilePath GetPath() override {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return path_;
- }
- void RequestPermission(
- content::GlobalRenderFrameHostId frame_id,
- UserActivationState user_activation_state,
- base::OnceCallback<void(PermissionRequestOutcome)> callback) override {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // Check if a permission request has already been processed previously. This
- // check is done first because we don't want to reset the status of a
- // permission if it has already been granted.
- if (GetStatus() != PermissionStatus::ASK || !context_) {
- if (GetStatus() == PermissionStatus::GRANTED) {
- SetStatus(PermissionStatus::GRANTED);
- }
- std::move(callback).Run(PermissionRequestOutcome::kRequestAborted);
- return;
- }
- content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(frame_id);
- if (!rfh) {
- // Requested from a no longer valid RenderFrameHost.
- std::move(callback).Run(PermissionRequestOutcome::kInvalidFrame);
- return;
- }
- // Don't request permission for an inactive RenderFrameHost as the
- // page might not distinguish properly between user denying the permission
- // and automatic rejection.
- if (rfh->IsInactiveAndDisallowActivation(
- content::DisallowActivationReasonId::
- kFileSystemAccessPermissionRequest)) {
- std::move(callback).Run(PermissionRequestOutcome::kInvalidFrame);
- return;
- }
- // We don't allow file system access from fenced frames.
- if (rfh->IsNestedWithinFencedFrame()) {
- std::move(callback).Run(PermissionRequestOutcome::kInvalidFrame);
- return;
- }
- if (user_activation_state == UserActivationState::kRequired &&
- !rfh->HasTransientUserActivation()) {
- // No permission prompts without user activation.
- std::move(callback).Run(PermissionRequestOutcome::kNoUserActivation);
- return;
- }
- if (content::WebContents::FromRenderFrameHost(rfh) == nullptr) {
- std::move(callback).Run(PermissionRequestOutcome::kInvalidFrame);
- return;
- }
- auto origin = rfh->GetLastCommittedOrigin().GetURL();
- if (url::Origin::Create(origin) != origin_) {
- // Third party iframes are not allowed to request more permissions.
- std::move(callback).Run(PermissionRequestOutcome::kThirdPartyContext);
- return;
- }
- auto* permission_manager =
- static_cast<electron::ElectronPermissionManager*>(
- context_->browser_context()->GetPermissionControllerDelegate());
- if (!permission_manager) {
- std::move(callback).Run(PermissionRequestOutcome::kRequestAborted);
- return;
- }
- blink::PermissionType type = static_cast<blink::PermissionType>(
- electron::WebContentsPermissionHelper::PermissionType::FILE_SYSTEM);
- base::Value::Dict details;
- details.Set("filePath", base::FilePathToValue(path_));
- details.Set("isDirectory", handle_type_ == HandleType::kDirectory);
- details.Set("fileAccessType",
- type_ == GrantType::kWrite ? "writable" : "readable");
- permission_manager->RequestPermissionWithDetails(
- type, rfh, origin, false, std::move(details),
- base::BindOnce(&PermissionGrantImpl::OnPermissionRequestResult, this,
- std::move(callback)));
- }
- const url::Origin& origin() const {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return origin_;
- }
- HandleType handle_type() const {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return handle_type_;
- }
- GrantType type() const {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return type_;
- }
- void SetStatus(PermissionStatus new_status) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- auto permission_changed = status_ != new_status;
- status_ = new_status;
- if (permission_changed) {
- NotifyPermissionStatusChanged();
- }
- }
- static void UpdateGrantPath(
- std::map<base::FilePath, PermissionGrantImpl*>& grants,
- const base::FilePath& old_path,
- const base::FilePath& new_path) {
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- auto entry_it = base::ranges::find_if(
- grants,
- [&old_path](const auto& entry) { return entry.first == old_path; });
- if (entry_it == grants.end()) {
- // There must be an entry for an ancestor of this entry. Nothing to do
- // here.
- return;
- }
- DCHECK_EQ(entry_it->second->GetStatus(), PermissionStatus::GRANTED);
- auto* const grant_impl = entry_it->second;
- grant_impl->SetPath(new_path);
- // Update the permission grant's key in the map of active permissions.
- grants.erase(entry_it);
- grants.emplace(new_path, grant_impl);
- }
- protected:
- ~PermissionGrantImpl() override {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (context_) {
- context_->PermissionGrantDestroyed(this);
- }
- }
- private:
- void OnPermissionRequestResult(
- base::OnceCallback<void(PermissionRequestOutcome)> callback,
- blink::mojom::PermissionStatus status) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (status == blink::mojom::PermissionStatus::GRANTED) {
- SetStatus(PermissionStatus::GRANTED);
- std::move(callback).Run(PermissionRequestOutcome::kUserGranted);
- } else {
- SetStatus(PermissionStatus::DENIED);
- std::move(callback).Run(PermissionRequestOutcome::kUserDenied);
- }
- }
- void SetPath(const base::FilePath& new_path) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (path_ == new_path)
- return;
- path_ = new_path;
- NotifyPermissionStatusChanged();
- }
- SEQUENCE_CHECKER(sequence_checker_);
- base::WeakPtr<FileSystemAccessPermissionContext> const context_;
- const url::Origin origin_;
- const HandleType handle_type_;
- const GrantType type_;
- base::FilePath path_;
- // This member should only be updated via SetStatus().
- PermissionStatus status_ = PermissionStatus::ASK;
- };
- struct FileSystemAccessPermissionContext::OriginState {
- // Raw pointers, owned collectively by all the handles that reference this
- // grant. When last reference goes away this state is cleared as well by
- // PermissionGrantDestroyed().
- std::map<base::FilePath, PermissionGrantImpl*> read_grants;
- std::map<base::FilePath, PermissionGrantImpl*> write_grants;
- };
- FileSystemAccessPermissionContext::FileSystemAccessPermissionContext(
- content::BrowserContext* browser_context,
- const base::Clock* clock)
- : browser_context_(browser_context), clock_(clock) {
- DETACH_FROM_SEQUENCE(sequence_checker_);
- }
- FileSystemAccessPermissionContext::~FileSystemAccessPermissionContext() =
- default;
- scoped_refptr<content::FileSystemAccessPermissionGrant>
- FileSystemAccessPermissionContext::GetReadPermissionGrant(
- const url::Origin& origin,
- const base::FilePath& path,
- HandleType handle_type,
- UserAction user_action) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // operator[] might insert a new OriginState in |active_permissions_map_|,
- // but that is exactly what we want.
- auto& origin_state = active_permissions_map_[origin];
- auto*& existing_grant = origin_state.read_grants[path];
- scoped_refptr<PermissionGrantImpl> new_grant;
- if (existing_grant && existing_grant->handle_type() != handle_type) {
- // |path| changed from being a directory to being a file or vice versa,
- // don't just re-use the existing grant but revoke the old grant before
- // creating a new grant.
- existing_grant->SetStatus(PermissionStatus::DENIED);
- existing_grant = nullptr;
- }
- if (!existing_grant) {
- new_grant = base::MakeRefCounted<PermissionGrantImpl>(
- weak_factory_.GetWeakPtr(), origin, path, handle_type, GrantType::kRead,
- user_action);
- existing_grant = new_grant.get();
- }
- // If a parent directory is already readable this new grant should also be
- // readable.
- if (new_grant &&
- AncestorHasActivePermission(origin, path, GrantType::kRead)) {
- existing_grant->SetStatus(PermissionStatus::GRANTED);
- } else {
- switch (user_action) {
- case UserAction::kOpen:
- case UserAction::kSave:
- // Open and Save dialog only grant read access for individual files.
- if (handle_type == HandleType::kDirectory) {
- break;
- }
- [[fallthrough]];
- case UserAction::kDragAndDrop:
- // Drag&drop grants read access for all handles.
- existing_grant->SetStatus(PermissionStatus::GRANTED);
- break;
- case UserAction::kLoadFromStorage:
- case UserAction::kNone:
- break;
- }
- }
- return existing_grant;
- }
- scoped_refptr<content::FileSystemAccessPermissionGrant>
- FileSystemAccessPermissionContext::GetWritePermissionGrant(
- const url::Origin& origin,
- const base::FilePath& path,
- HandleType handle_type,
- UserAction user_action) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // operator[] might insert a new OriginState in |active_permissions_map_|,
- // but that is exactly what we want.
- auto& origin_state = active_permissions_map_[origin];
- auto*& existing_grant = origin_state.write_grants[path];
- scoped_refptr<PermissionGrantImpl> new_grant;
- if (existing_grant && existing_grant->handle_type() != handle_type) {
- // |path| changed from being a directory to being a file or vice versa,
- // don't just re-use the existing grant but revoke the old grant before
- // creating a new grant.
- existing_grant->SetStatus(PermissionStatus::DENIED);
- existing_grant = nullptr;
- }
- if (!existing_grant) {
- new_grant = base::MakeRefCounted<PermissionGrantImpl>(
- weak_factory_.GetWeakPtr(), origin, path, handle_type,
- GrantType::kWrite, user_action);
- existing_grant = new_grant.get();
- }
- // If a parent directory is already writable this new grant should also be
- // writable.
- if (new_grant &&
- AncestorHasActivePermission(origin, path, GrantType::kWrite)) {
- existing_grant->SetStatus(PermissionStatus::GRANTED);
- } else {
- switch (user_action) {
- case UserAction::kSave:
- // Only automatically grant write access for save dialogs.
- existing_grant->SetStatus(PermissionStatus::GRANTED);
- break;
- case UserAction::kOpen:
- case UserAction::kDragAndDrop:
- case UserAction::kLoadFromStorage:
- case UserAction::kNone:
- break;
- }
- }
- return existing_grant;
- }
- bool FileSystemAccessPermissionContext::CanObtainReadPermission(
- const url::Origin& origin) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return true;
- }
- bool FileSystemAccessPermissionContext::CanObtainWritePermission(
- const url::Origin& origin) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return true;
- }
- void FileSystemAccessPermissionContext::ConfirmSensitiveEntryAccess(
- const url::Origin& origin,
- PathType path_type,
- const base::FilePath& path,
- HandleType handle_type,
- UserAction user_action,
- content::GlobalRenderFrameHostId frame_id,
- base::OnceCallback<void(SensitiveEntryResult)> callback) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- auto after_blocklist_check_callback = base::BindOnce(
- &FileSystemAccessPermissionContext::DidCheckPathAgainstBlocklist,
- GetWeakPtr(), origin, path, handle_type, user_action, frame_id,
- std::move(callback));
- CheckPathAgainstBlocklist(path_type, path, handle_type,
- std::move(after_blocklist_check_callback));
- }
- void FileSystemAccessPermissionContext::CheckPathAgainstBlocklist(
- PathType path_type,
- const base::FilePath& path,
- HandleType handle_type,
- base::OnceCallback<void(bool)> callback) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // TODO(https://crbug.com/1009970): Figure out what external paths should be
- // blocked. We could resolve the external path to a local path, and check for
- // blocked directories based on that, but that doesn't work well. Instead we
- // should have a separate Chrome OS only code path to block for example the
- // root of certain external file systems.
- if (path_type == PathType::kExternal) {
- std::move(callback).Run(/*should_block=*/false);
- return;
- }
- std::vector<BlockPathRule> extra_rules;
- extra_rules.emplace_back(browser_context_->GetPath().DirName(),
- BlockType::kBlockAllChildren);
- base::ThreadPool::PostTaskAndReplyWithResult(
- FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
- base::BindOnce(&ShouldBlockAccessToPath, path, handle_type, extra_rules),
- std::move(callback));
- }
- void FileSystemAccessPermissionContext::PerformAfterWriteChecks(
- std::unique_ptr<content::FileSystemAccessWriteItem> item,
- content::GlobalRenderFrameHostId frame_id,
- base::OnceCallback<void(AfterWriteCheckResult)> callback) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- std::move(callback).Run(AfterWriteCheckResult::kAllow);
- }
- void FileSystemAccessPermissionContext::DidCheckPathAgainstBlocklist(
- const url::Origin& origin,
- const base::FilePath& path,
- HandleType handle_type,
- UserAction user_action,
- content::GlobalRenderFrameHostId frame_id,
- base::OnceCallback<void(SensitiveEntryResult)> callback,
- bool should_block) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (user_action == UserAction::kNone) {
- std::move(callback).Run(should_block ? SensitiveEntryResult::kAbort
- : SensitiveEntryResult::kAllowed);
- return;
- }
- // Chromium opens a dialog here, but in Electron's case we log and abort.
- if (should_block) {
- LOG(INFO) << path.value()
- << " is blocked by the blocklis and cannot be accessed";
- std::move(callback).Run(SensitiveEntryResult::kAbort);
- return;
- }
- std::move(callback).Run(SensitiveEntryResult::kAllowed);
- }
- void FileSystemAccessPermissionContext::MaybeEvictEntries(
- base::Value::Dict& dict) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- std::vector<std::pair<base::Time, std::string>> entries;
- entries.reserve(dict.size());
- for (auto entry : dict) {
- // Don't evict the default ID.
- if (entry.first == kDefaultLastPickedDirectoryKey) {
- continue;
- }
- // If the data is corrupted and `entry.second` is for some reason not a
- // dict, it should be first in line for eviction.
- auto timestamp = base::Time::Min();
- if (entry.second.is_dict()) {
- timestamp = base::ValueToTime(entry.second.GetDict().Find(kTimestampKey))
- .value_or(base::Time::Min());
- }
- entries.emplace_back(timestamp, entry.first);
- }
- if (entries.size() <= max_ids_per_origin_) {
- return;
- }
- base::ranges::sort(entries);
- size_t entries_to_remove = entries.size() - max_ids_per_origin_;
- for (size_t i = 0; i < entries_to_remove; ++i) {
- bool did_remove_entry = dict.Remove(entries[i].second);
- DCHECK(did_remove_entry);
- }
- }
- void FileSystemAccessPermissionContext::SetLastPickedDirectory(
- const url::Origin& origin,
- const std::string& id,
- const base::FilePath& path,
- const PathType type) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // Create an entry into the nested dictionary.
- base::Value::Dict entry;
- entry.Set(kPathKey, base::FilePathToValue(path));
- entry.Set(kPathTypeKey, static_cast<int>(type));
- entry.Set(kTimestampKey, base::TimeToValue(clock_->Now()));
- auto it = id_pathinfo_map_.find(origin);
- if (it != id_pathinfo_map_.end()) {
- base::Value::Dict& dict = it->second;
- dict.Set(GenerateLastPickedDirectoryKey(id), std::move(entry));
- MaybeEvictEntries(dict);
- } else {
- base::Value::Dict dict;
- dict.Set(GenerateLastPickedDirectoryKey(id), std::move(entry));
- MaybeEvictEntries(dict);
- id_pathinfo_map_.insert(std::make_pair(origin, std::move(dict)));
- }
- }
- FileSystemAccessPermissionContext::PathInfo
- FileSystemAccessPermissionContext::GetLastPickedDirectory(
- const url::Origin& origin,
- const std::string& id) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- auto it = id_pathinfo_map_.find(origin);
- PathInfo path_info;
- if (it == id_pathinfo_map_.end()) {
- return path_info;
- }
- auto* entry = it->second.FindDict(GenerateLastPickedDirectoryKey(id));
- if (!entry) {
- return path_info;
- }
- auto type_int =
- entry->FindInt(kPathTypeKey).value_or(static_cast<int>(PathType::kLocal));
- path_info.type = type_int == static_cast<int>(PathType::kExternal)
- ? PathType::kExternal
- : PathType::kLocal;
- path_info.path =
- base::ValueToFilePath(entry->Find(kPathKey)).value_or(base::FilePath());
- return path_info;
- }
- base::FilePath FileSystemAccessPermissionContext::GetWellKnownDirectoryPath(
- blink::mojom::WellKnownDirectory directory,
- const url::Origin& origin) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- int key = base::PATH_START;
- switch (directory) {
- case blink::mojom::WellKnownDirectory::kDirDesktop:
- key = base::DIR_USER_DESKTOP;
- break;
- case blink::mojom::WellKnownDirectory::kDirDocuments:
- key = chrome::DIR_USER_DOCUMENTS;
- break;
- case blink::mojom::WellKnownDirectory::kDirDownloads:
- key = chrome::DIR_DEFAULT_DOWNLOADS;
- break;
- case blink::mojom::WellKnownDirectory::kDirMusic:
- key = chrome::DIR_USER_MUSIC;
- break;
- case blink::mojom::WellKnownDirectory::kDirPictures:
- key = chrome::DIR_USER_PICTURES;
- break;
- case blink::mojom::WellKnownDirectory::kDirVideos:
- key = chrome::DIR_USER_VIDEOS;
- break;
- }
- base::FilePath directory_path;
- base::PathService::Get(key, &directory_path);
- return directory_path;
- }
- std::u16string FileSystemAccessPermissionContext::GetPickerTitle(
- const blink::mojom::FilePickerOptionsPtr& options) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // TODO(asully): Consider adding custom strings for invocations of the file
- // picker, as well. Returning the empty string will fall back to the platform
- // default for the given picker type.
- std::u16string title;
- switch (options->type_specific_options->which()) {
- case blink::mojom::TypeSpecificFilePickerOptionsUnion::Tag::
- kDirectoryPickerOptions:
- title = l10n_util::GetStringUTF16(
- options->type_specific_options->get_directory_picker_options()
- ->request_writable
- ? IDS_FILE_SYSTEM_ACCESS_CHOOSER_OPEN_WRITABLE_DIRECTORY_TITLE
- : IDS_FILE_SYSTEM_ACCESS_CHOOSER_OPEN_READABLE_DIRECTORY_TITLE);
- break;
- case blink::mojom::TypeSpecificFilePickerOptionsUnion::Tag::
- kSaveFilePickerOptions:
- title = l10n_util::GetStringUTF16(
- IDS_FILE_SYSTEM_ACCESS_CHOOSER_OPEN_SAVE_FILE_TITLE);
- break;
- case blink::mojom::TypeSpecificFilePickerOptionsUnion::Tag::
- kOpenFilePickerOptions:
- break;
- }
- return title;
- }
- void FileSystemAccessPermissionContext::NotifyEntryMoved(
- const url::Origin& origin,
- const base::FilePath& old_path,
- const base::FilePath& new_path) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (old_path == new_path) {
- return;
- }
- auto it = active_permissions_map_.find(origin);
- if (it != active_permissions_map_.end()) {
- PermissionGrantImpl::UpdateGrantPath(it->second.write_grants, old_path,
- new_path);
- PermissionGrantImpl::UpdateGrantPath(it->second.read_grants, old_path,
- new_path);
- }
- }
- void FileSystemAccessPermissionContext::OnFileCreatedFromShowSaveFilePicker(
- const GURL& file_picker_binding_context,
- const storage::FileSystemURL& url) {}
- void FileSystemAccessPermissionContext::CheckPathsAgainstEnterprisePolicy(
- std::vector<PathInfo> entries,
- content::GlobalRenderFrameHostId frame_id,
- EntriesAllowedByEnterprisePolicyCallback callback) {
- std::move(callback).Run(std::move(entries));
- }
- void FileSystemAccessPermissionContext::RevokeGrant(
- const url::Origin& origin,
- const base::FilePath& file_path) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- auto origin_it = active_permissions_map_.find(origin);
- if (origin_it != active_permissions_map_.end()) {
- OriginState& origin_state = origin_it->second;
- for (auto& grant : origin_state.read_grants) {
- if (file_path.empty() || grant.first == file_path) {
- grant.second->SetStatus(PermissionStatus::ASK);
- }
- }
- for (auto& grant : origin_state.write_grants) {
- if (file_path.empty() || grant.first == file_path) {
- grant.second->SetStatus(PermissionStatus::ASK);
- }
- }
- }
- }
- bool FileSystemAccessPermissionContext::OriginHasReadAccess(
- 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 base::ranges::any_of(it->second.read_grants, [&](const auto& grant) {
- return grant.second->GetStatus() == PermissionStatus::GRANTED;
- });
- }
- return false;
- }
- bool FileSystemAccessPermissionContext::OriginHasWriteAccess(
- 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 base::ranges::any_of(
- it->second.write_grants, [&](const auto& grant) {
- return grant.second->GetStatus() == PermissionStatus::GRANTED;
- });
- }
- return false;
- }
- void FileSystemAccessPermissionContext::CleanupPermissions(
- const url::Origin& origin) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- RevokeGrant(origin);
- }
- bool FileSystemAccessPermissionContext::AncestorHasActivePermission(
- const url::Origin& origin,
- const base::FilePath& path,
- GrantType grant_type) const {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- auto it = active_permissions_map_.find(origin);
- if (it == active_permissions_map_.end()) {
- return false;
- }
- const auto& relevant_grants = grant_type == GrantType::kWrite
- ? it->second.write_grants
- : it->second.read_grants;
- if (relevant_grants.empty()) {
- return false;
- }
- // Permissions are inherited from the closest ancestor.
- for (base::FilePath parent = path.DirName(); parent != parent.DirName();
- parent = parent.DirName()) {
- auto i = relevant_grants.find(parent);
- if (i != relevant_grants.end() && i->second &&
- i->second->GetStatus() == PermissionStatus::GRANTED) {
- return true;
- }
- }
- return false;
- }
- void FileSystemAccessPermissionContext::PermissionGrantDestroyed(
- PermissionGrantImpl* grant) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- auto it = active_permissions_map_.find(grant->origin());
- if (it == active_permissions_map_.end()) {
- return;
- }
- auto& grants = grant->type() == GrantType::kRead ? it->second.read_grants
- : it->second.write_grants;
- auto grant_it = grants.find(grant->GetPath());
- // Any non-denied permission grants should have still been in our grants
- // list. If this invariant is violated we would have permissions that might
- // be granted but won't be visible in any UI because the permission context
- // isn't tracking them anymore.
- if (grant_it == grants.end()) {
- DCHECK_EQ(PermissionStatus::DENIED, grant->GetStatus());
- return;
- }
- // The grant in |grants| for this path might have been replaced with a
- // different grant. Only erase if it actually matches the grant that was
- // destroyed.
- if (grant_it->second == grant) {
- grants.erase(grant_it);
- }
- }
- base::WeakPtr<FileSystemAccessPermissionContext>
- FileSystemAccessPermissionContext::GetWeakPtr() {
- return weak_factory_.GetWeakPtr();
- }
- } // namespace electron
|