file_system_access_permission_context.cc 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799
  1. // Copyright (c) 2024 Microsoft, GmbH
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #include "shell/browser/file_system_access/file_system_access_permission_context.h"
  5. #include <string>
  6. #include <utility>
  7. #include "base/base_paths.h"
  8. #include "base/files/file_path.h"
  9. #include "base/json/values_util.h"
  10. #include "base/path_service.h"
  11. #include "base/task/thread_pool.h"
  12. #include "base/values.h"
  13. #include "chrome/browser/browser_process.h"
  14. #include "chrome/browser/file_system_access/chrome_file_system_access_permission_context.h" // nogncheck
  15. #include "chrome/browser/file_system_access/file_system_access_features.h"
  16. #include "chrome/common/chrome_paths.h"
  17. #include "chrome/grit/generated_resources.h"
  18. #include "content/public/browser/browser_context.h"
  19. #include "content/public/browser/browser_thread.h"
  20. #include "content/public/browser/disallow_activation_reason.h"
  21. #include "content/public/browser/render_frame_host.h"
  22. #include "content/public/browser/render_process_host.h"
  23. #include "content/public/browser/web_contents.h"
  24. #include "shell/browser/electron_permission_manager.h"
  25. #include "shell/browser/web_contents_permission_helper.h"
  26. #include "shell/common/gin_converters/file_path_converter.h"
  27. #include "third_party/blink/public/mojom/file_system_access/file_system_access_manager.mojom.h"
  28. #include "ui/base/l10n/l10n_util.h"
  29. #include "url/origin.h"
  30. namespace {
  31. using BlockType = ChromeFileSystemAccessPermissionContext::BlockType;
  32. using HandleType = content::FileSystemAccessPermissionContext::HandleType;
  33. using GrantType = electron::FileSystemAccessPermissionContext::GrantType;
  34. using blink::mojom::PermissionStatus;
  35. #if BUILDFLAG(IS_WIN)
  36. [[nodiscard]] constexpr bool ContainsInvalidDNSCharacter(
  37. base::FilePath::StringType hostname) {
  38. return !base::ranges::all_of(hostname, [](base::FilePath::CharType c) {
  39. return (c >= L'A' && c <= L'Z') || (c >= L'a' && c <= L'z') ||
  40. (c >= L'0' && c <= L'9') || (c == L'.') || (c == L'-');
  41. });
  42. }
  43. bool MaybeIsLocalUNCPath(const base::FilePath& path) {
  44. if (!path.IsNetwork()) {
  45. return false;
  46. }
  47. const std::vector<base::FilePath::StringType> components =
  48. path.GetComponents();
  49. // Check for server name that could represent a local system. We only
  50. // check for a very short list, as it is impossible to cover all different
  51. // variants on Windows.
  52. if (components.size() >= 2 &&
  53. (base::FilePath::CompareEqualIgnoreCase(components[1],
  54. FILE_PATH_LITERAL("localhost")) ||
  55. components[1] == FILE_PATH_LITERAL("127.0.0.1") ||
  56. components[1] == FILE_PATH_LITERAL(".") ||
  57. components[1] == FILE_PATH_LITERAL("?") ||
  58. ContainsInvalidDNSCharacter(components[1]))) {
  59. return true;
  60. }
  61. // In case we missed the server name check above, we also check for shares
  62. // ending with '$' as they represent pre-defined shares, including the local
  63. // drives.
  64. for (size_t i = 2; i < components.size(); ++i) {
  65. if (components[i].back() == L'$') {
  66. return true;
  67. }
  68. }
  69. return false;
  70. }
  71. #endif
  72. // Describes a rule for blocking a directory, which can be constructed
  73. // dynamically (based on state) or statically (from kBlockedPaths).
  74. struct BlockPathRule {
  75. base::FilePath path;
  76. BlockType type;
  77. };
  78. bool ShouldBlockAccessToPath(const base::FilePath& path,
  79. HandleType handle_type,
  80. std::vector<BlockPathRule> rules) {
  81. DCHECK(!path.empty());
  82. DCHECK(path.IsAbsolute());
  83. #if BUILDFLAG(IS_WIN)
  84. // On Windows, local UNC paths are rejected, as UNC path can be written in a
  85. // way that can bypass the blocklist.
  86. if (base::FeatureList::IsEnabled(
  87. features::kFileSystemAccessLocalUNCPathBlock) &&
  88. MaybeIsLocalUNCPath(path)) {
  89. return true;
  90. }
  91. #endif
  92. // Add the hard-coded rules to the dynamic rules.
  93. for (auto const& [key, rule_path, type] :
  94. ChromeFileSystemAccessPermissionContext::kBlockedPaths) {
  95. if (key == ChromeFileSystemAccessPermissionContext::kNoBasePathKey) {
  96. rules.emplace_back(base::FilePath{rule_path}, type);
  97. } else if (base::FilePath path; base::PathService::Get(key, &path)) {
  98. rules.emplace_back(rule_path ? path.Append(rule_path) : path, type);
  99. }
  100. }
  101. base::FilePath nearest_ancestor;
  102. BlockType nearest_ancestor_block_type = BlockType::kDontBlockChildren;
  103. for (const auto& block : rules) {
  104. if (path == block.path || path.IsParent(block.path)) {
  105. DLOG(INFO) << "Blocking access to " << path
  106. << " because it is a parent of " << block.path;
  107. return true;
  108. }
  109. if (block.path.IsParent(path) &&
  110. (nearest_ancestor.empty() || nearest_ancestor.IsParent(block.path))) {
  111. nearest_ancestor = block.path;
  112. nearest_ancestor_block_type = block.type;
  113. }
  114. }
  115. // The path we're checking is not in a potentially blocked directory, or the
  116. // nearest ancestor does not block access to its children. Grant access.
  117. if (nearest_ancestor.empty() ||
  118. nearest_ancestor_block_type == BlockType::kDontBlockChildren) {
  119. return false;
  120. }
  121. // The path we're checking is a file, and the nearest ancestor only blocks
  122. // access to directories. Grant access.
  123. if (handle_type == HandleType::kFile &&
  124. nearest_ancestor_block_type == BlockType::kBlockNestedDirectories) {
  125. return false;
  126. }
  127. // The nearest ancestor blocks access to its children, so block access.
  128. DLOG(INFO) << "Blocking access to " << path << " because it is inside "
  129. << nearest_ancestor;
  130. return true;
  131. }
  132. } // namespace
  133. namespace electron {
  134. class FileSystemAccessPermissionContext::PermissionGrantImpl
  135. : public content::FileSystemAccessPermissionGrant {
  136. public:
  137. PermissionGrantImpl(base::WeakPtr<FileSystemAccessPermissionContext> context,
  138. const url::Origin& origin,
  139. const base::FilePath& path,
  140. HandleType handle_type,
  141. GrantType type,
  142. UserAction user_action)
  143. : context_{std::move(context)},
  144. origin_{origin},
  145. handle_type_{handle_type},
  146. type_{type},
  147. path_{path} {}
  148. // FileSystemAccessPermissionGrant:
  149. PermissionStatus GetStatus() override {
  150. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  151. return status_;
  152. }
  153. base::FilePath GetPath() override {
  154. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  155. return path_;
  156. }
  157. void RequestPermission(
  158. content::GlobalRenderFrameHostId frame_id,
  159. UserActivationState user_activation_state,
  160. base::OnceCallback<void(PermissionRequestOutcome)> callback) override {
  161. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  162. // Check if a permission request has already been processed previously. This
  163. // check is done first because we don't want to reset the status of a
  164. // permission if it has already been granted.
  165. if (GetStatus() != PermissionStatus::ASK || !context_) {
  166. if (GetStatus() == PermissionStatus::GRANTED) {
  167. SetStatus(PermissionStatus::GRANTED);
  168. }
  169. std::move(callback).Run(PermissionRequestOutcome::kRequestAborted);
  170. return;
  171. }
  172. content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(frame_id);
  173. if (!rfh) {
  174. // Requested from a no longer valid RenderFrameHost.
  175. std::move(callback).Run(PermissionRequestOutcome::kInvalidFrame);
  176. return;
  177. }
  178. // Don't request permission for an inactive RenderFrameHost as the
  179. // page might not distinguish properly between user denying the permission
  180. // and automatic rejection.
  181. if (rfh->IsInactiveAndDisallowActivation(
  182. content::DisallowActivationReasonId::
  183. kFileSystemAccessPermissionRequest)) {
  184. std::move(callback).Run(PermissionRequestOutcome::kInvalidFrame);
  185. return;
  186. }
  187. // We don't allow file system access from fenced frames.
  188. if (rfh->IsNestedWithinFencedFrame()) {
  189. std::move(callback).Run(PermissionRequestOutcome::kInvalidFrame);
  190. return;
  191. }
  192. if (user_activation_state == UserActivationState::kRequired &&
  193. !rfh->HasTransientUserActivation()) {
  194. // No permission prompts without user activation.
  195. std::move(callback).Run(PermissionRequestOutcome::kNoUserActivation);
  196. return;
  197. }
  198. if (content::WebContents::FromRenderFrameHost(rfh) == nullptr) {
  199. std::move(callback).Run(PermissionRequestOutcome::kInvalidFrame);
  200. return;
  201. }
  202. auto origin = rfh->GetLastCommittedOrigin().GetURL();
  203. if (url::Origin::Create(origin) != origin_) {
  204. // Third party iframes are not allowed to request more permissions.
  205. std::move(callback).Run(PermissionRequestOutcome::kThirdPartyContext);
  206. return;
  207. }
  208. auto* permission_manager =
  209. static_cast<electron::ElectronPermissionManager*>(
  210. context_->browser_context()->GetPermissionControllerDelegate());
  211. if (!permission_manager) {
  212. std::move(callback).Run(PermissionRequestOutcome::kRequestAborted);
  213. return;
  214. }
  215. blink::PermissionType type = static_cast<blink::PermissionType>(
  216. electron::WebContentsPermissionHelper::PermissionType::FILE_SYSTEM);
  217. base::Value::Dict details;
  218. details.Set("filePath", base::FilePathToValue(path_));
  219. details.Set("isDirectory", handle_type_ == HandleType::kDirectory);
  220. details.Set("fileAccessType",
  221. type_ == GrantType::kWrite ? "writable" : "readable");
  222. permission_manager->RequestPermissionWithDetails(
  223. type, rfh, origin, false, std::move(details),
  224. base::BindOnce(&PermissionGrantImpl::OnPermissionRequestResult, this,
  225. std::move(callback)));
  226. }
  227. const url::Origin& origin() const {
  228. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  229. return origin_;
  230. }
  231. HandleType handle_type() const {
  232. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  233. return handle_type_;
  234. }
  235. GrantType type() const {
  236. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  237. return type_;
  238. }
  239. void SetStatus(PermissionStatus new_status) {
  240. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  241. auto permission_changed = status_ != new_status;
  242. status_ = new_status;
  243. if (permission_changed) {
  244. NotifyPermissionStatusChanged();
  245. }
  246. }
  247. static void UpdateGrantPath(
  248. std::map<base::FilePath, PermissionGrantImpl*>& grants,
  249. const base::FilePath& old_path,
  250. const base::FilePath& new_path) {
  251. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  252. auto entry_it = base::ranges::find_if(
  253. grants,
  254. [&old_path](const auto& entry) { return entry.first == old_path; });
  255. if (entry_it == grants.end()) {
  256. // There must be an entry for an ancestor of this entry. Nothing to do
  257. // here.
  258. return;
  259. }
  260. DCHECK_EQ(entry_it->second->GetStatus(), PermissionStatus::GRANTED);
  261. auto* const grant_impl = entry_it->second;
  262. grant_impl->SetPath(new_path);
  263. // Update the permission grant's key in the map of active permissions.
  264. grants.erase(entry_it);
  265. grants.emplace(new_path, grant_impl);
  266. }
  267. protected:
  268. ~PermissionGrantImpl() override {
  269. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  270. if (context_) {
  271. context_->PermissionGrantDestroyed(this);
  272. }
  273. }
  274. private:
  275. void OnPermissionRequestResult(
  276. base::OnceCallback<void(PermissionRequestOutcome)> callback,
  277. blink::mojom::PermissionStatus status) {
  278. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  279. if (status == blink::mojom::PermissionStatus::GRANTED) {
  280. SetStatus(PermissionStatus::GRANTED);
  281. std::move(callback).Run(PermissionRequestOutcome::kUserGranted);
  282. } else {
  283. SetStatus(PermissionStatus::DENIED);
  284. std::move(callback).Run(PermissionRequestOutcome::kUserDenied);
  285. }
  286. }
  287. void SetPath(const base::FilePath& new_path) {
  288. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  289. if (path_ == new_path)
  290. return;
  291. path_ = new_path;
  292. NotifyPermissionStatusChanged();
  293. }
  294. SEQUENCE_CHECKER(sequence_checker_);
  295. base::WeakPtr<FileSystemAccessPermissionContext> const context_;
  296. const url::Origin origin_;
  297. const HandleType handle_type_;
  298. const GrantType type_;
  299. base::FilePath path_;
  300. // This member should only be updated via SetStatus().
  301. PermissionStatus status_ = PermissionStatus::ASK;
  302. };
  303. struct FileSystemAccessPermissionContext::OriginState {
  304. // Raw pointers, owned collectively by all the handles that reference this
  305. // grant. When last reference goes away this state is cleared as well by
  306. // PermissionGrantDestroyed().
  307. std::map<base::FilePath, PermissionGrantImpl*> read_grants;
  308. std::map<base::FilePath, PermissionGrantImpl*> write_grants;
  309. };
  310. FileSystemAccessPermissionContext::FileSystemAccessPermissionContext(
  311. content::BrowserContext* browser_context)
  312. : browser_context_(browser_context) {
  313. DETACH_FROM_SEQUENCE(sequence_checker_);
  314. }
  315. FileSystemAccessPermissionContext::~FileSystemAccessPermissionContext() =
  316. default;
  317. scoped_refptr<content::FileSystemAccessPermissionGrant>
  318. FileSystemAccessPermissionContext::GetReadPermissionGrant(
  319. const url::Origin& origin,
  320. const base::FilePath& path,
  321. HandleType handle_type,
  322. UserAction user_action) {
  323. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  324. // operator[] might insert a new OriginState in |active_permissions_map_|,
  325. // but that is exactly what we want.
  326. auto& origin_state = active_permissions_map_[origin];
  327. auto*& existing_grant = origin_state.read_grants[path];
  328. scoped_refptr<PermissionGrantImpl> new_grant;
  329. if (existing_grant && existing_grant->handle_type() != handle_type) {
  330. // |path| changed from being a directory to being a file or vice versa,
  331. // don't just re-use the existing grant but revoke the old grant before
  332. // creating a new grant.
  333. existing_grant->SetStatus(PermissionStatus::DENIED);
  334. existing_grant = nullptr;
  335. }
  336. if (!existing_grant) {
  337. new_grant = base::MakeRefCounted<PermissionGrantImpl>(
  338. weak_factory_.GetWeakPtr(), origin, path, handle_type, GrantType::kRead,
  339. user_action);
  340. existing_grant = new_grant.get();
  341. }
  342. // If a parent directory is already readable this new grant should also be
  343. // readable.
  344. if (new_grant &&
  345. AncestorHasActivePermission(origin, path, GrantType::kRead)) {
  346. existing_grant->SetStatus(PermissionStatus::GRANTED);
  347. } else {
  348. switch (user_action) {
  349. case UserAction::kOpen:
  350. case UserAction::kSave:
  351. // Open and Save dialog only grant read access for individual files.
  352. if (handle_type == HandleType::kDirectory) {
  353. break;
  354. }
  355. [[fallthrough]];
  356. case UserAction::kDragAndDrop:
  357. // Drag&drop grants read access for all handles.
  358. existing_grant->SetStatus(PermissionStatus::GRANTED);
  359. break;
  360. case UserAction::kLoadFromStorage:
  361. case UserAction::kNone:
  362. break;
  363. }
  364. }
  365. return existing_grant;
  366. }
  367. scoped_refptr<content::FileSystemAccessPermissionGrant>
  368. FileSystemAccessPermissionContext::GetWritePermissionGrant(
  369. const url::Origin& origin,
  370. const base::FilePath& path,
  371. HandleType handle_type,
  372. UserAction user_action) {
  373. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  374. // operator[] might insert a new OriginState in |active_permissions_map_|,
  375. // but that is exactly what we want.
  376. auto& origin_state = active_permissions_map_[origin];
  377. auto*& existing_grant = origin_state.write_grants[path];
  378. scoped_refptr<PermissionGrantImpl> new_grant;
  379. if (existing_grant && existing_grant->handle_type() != handle_type) {
  380. // |path| changed from being a directory to being a file or vice versa,
  381. // don't just re-use the existing grant but revoke the old grant before
  382. // creating a new grant.
  383. existing_grant->SetStatus(PermissionStatus::DENIED);
  384. existing_grant = nullptr;
  385. }
  386. if (!existing_grant) {
  387. new_grant = base::MakeRefCounted<PermissionGrantImpl>(
  388. weak_factory_.GetWeakPtr(), origin, path, handle_type,
  389. GrantType::kWrite, user_action);
  390. existing_grant = new_grant.get();
  391. }
  392. // If a parent directory is already writable this new grant should also be
  393. // writable.
  394. if (new_grant &&
  395. AncestorHasActivePermission(origin, path, GrantType::kWrite)) {
  396. existing_grant->SetStatus(PermissionStatus::GRANTED);
  397. } else {
  398. switch (user_action) {
  399. case UserAction::kSave:
  400. // Only automatically grant write access for save dialogs.
  401. existing_grant->SetStatus(PermissionStatus::GRANTED);
  402. break;
  403. case UserAction::kOpen:
  404. case UserAction::kDragAndDrop:
  405. case UserAction::kLoadFromStorage:
  406. case UserAction::kNone:
  407. break;
  408. }
  409. }
  410. return existing_grant;
  411. }
  412. bool FileSystemAccessPermissionContext::CanObtainReadPermission(
  413. const url::Origin& origin) {
  414. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  415. return true;
  416. }
  417. bool FileSystemAccessPermissionContext::CanObtainWritePermission(
  418. const url::Origin& origin) {
  419. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  420. return true;
  421. }
  422. void FileSystemAccessPermissionContext::ConfirmSensitiveEntryAccess(
  423. const url::Origin& origin,
  424. PathType path_type,
  425. const base::FilePath& path,
  426. HandleType handle_type,
  427. UserAction user_action,
  428. content::GlobalRenderFrameHostId frame_id,
  429. base::OnceCallback<void(SensitiveEntryResult)> callback) {
  430. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  431. auto after_blocklist_check_callback = base::BindOnce(
  432. &FileSystemAccessPermissionContext::DidCheckPathAgainstBlocklist,
  433. GetWeakPtr(), origin, path, handle_type, user_action, frame_id,
  434. std::move(callback));
  435. CheckPathAgainstBlocklist(path_type, path, handle_type,
  436. std::move(after_blocklist_check_callback));
  437. }
  438. void FileSystemAccessPermissionContext::CheckPathAgainstBlocklist(
  439. PathType path_type,
  440. const base::FilePath& path,
  441. HandleType handle_type,
  442. base::OnceCallback<void(bool)> callback) {
  443. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  444. // TODO(https://crbug.com/1009970): Figure out what external paths should be
  445. // blocked. We could resolve the external path to a local path, and check for
  446. // blocked directories based on that, but that doesn't work well. Instead we
  447. // should have a separate Chrome OS only code path to block for example the
  448. // root of certain external file systems.
  449. if (path_type == PathType::kExternal) {
  450. std::move(callback).Run(/*should_block=*/false);
  451. return;
  452. }
  453. std::vector<BlockPathRule> extra_rules;
  454. extra_rules.emplace_back(browser_context_->GetPath().DirName(),
  455. BlockType::kBlockAllChildren);
  456. base::ThreadPool::PostTaskAndReplyWithResult(
  457. FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
  458. base::BindOnce(&ShouldBlockAccessToPath, path, handle_type, extra_rules),
  459. std::move(callback));
  460. }
  461. void FileSystemAccessPermissionContext::PerformAfterWriteChecks(
  462. std::unique_ptr<content::FileSystemAccessWriteItem> item,
  463. content::GlobalRenderFrameHostId frame_id,
  464. base::OnceCallback<void(AfterWriteCheckResult)> callback) {
  465. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  466. std::move(callback).Run(AfterWriteCheckResult::kAllow);
  467. }
  468. void FileSystemAccessPermissionContext::DidCheckPathAgainstBlocklist(
  469. const url::Origin& origin,
  470. const base::FilePath& path,
  471. HandleType handle_type,
  472. UserAction user_action,
  473. content::GlobalRenderFrameHostId frame_id,
  474. base::OnceCallback<void(SensitiveEntryResult)> callback,
  475. bool should_block) {
  476. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  477. if (user_action == UserAction::kNone) {
  478. std::move(callback).Run(should_block ? SensitiveEntryResult::kAbort
  479. : SensitiveEntryResult::kAllowed);
  480. return;
  481. }
  482. // Chromium opens a dialog here, but in Electron's case we log and abort.
  483. if (should_block) {
  484. LOG(INFO) << path.value()
  485. << " is blocked by the blocklis and cannot be accessed";
  486. std::move(callback).Run(SensitiveEntryResult::kAbort);
  487. return;
  488. }
  489. std::move(callback).Run(SensitiveEntryResult::kAllowed);
  490. }
  491. void FileSystemAccessPermissionContext::SetLastPickedDirectory(
  492. const url::Origin& origin,
  493. const std::string& id,
  494. const base::FilePath& path,
  495. const PathType type) {
  496. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  497. LOG(INFO) << "NOTIMPLEMENTED SetLastPickedDirectory: " << path.value();
  498. }
  499. FileSystemAccessPermissionContext::PathInfo
  500. FileSystemAccessPermissionContext::GetLastPickedDirectory(
  501. const url::Origin& origin,
  502. const std::string& id) {
  503. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  504. LOG(INFO) << "NOTIMPLEMENTED GetLastPickedDirectory";
  505. return PathInfo();
  506. }
  507. base::FilePath FileSystemAccessPermissionContext::GetWellKnownDirectoryPath(
  508. blink::mojom::WellKnownDirectory directory,
  509. const url::Origin& origin) {
  510. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  511. int key = base::PATH_START;
  512. switch (directory) {
  513. case blink::mojom::WellKnownDirectory::kDirDesktop:
  514. key = base::DIR_USER_DESKTOP;
  515. break;
  516. case blink::mojom::WellKnownDirectory::kDirDocuments:
  517. key = chrome::DIR_USER_DOCUMENTS;
  518. break;
  519. case blink::mojom::WellKnownDirectory::kDirDownloads:
  520. key = chrome::DIR_DEFAULT_DOWNLOADS;
  521. break;
  522. case blink::mojom::WellKnownDirectory::kDirMusic:
  523. key = chrome::DIR_USER_MUSIC;
  524. break;
  525. case blink::mojom::WellKnownDirectory::kDirPictures:
  526. key = chrome::DIR_USER_PICTURES;
  527. break;
  528. case blink::mojom::WellKnownDirectory::kDirVideos:
  529. key = chrome::DIR_USER_VIDEOS;
  530. break;
  531. }
  532. base::FilePath directory_path;
  533. base::PathService::Get(key, &directory_path);
  534. return directory_path;
  535. }
  536. std::u16string FileSystemAccessPermissionContext::GetPickerTitle(
  537. const blink::mojom::FilePickerOptionsPtr& options) {
  538. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  539. // TODO(asully): Consider adding custom strings for invocations of the file
  540. // picker, as well. Returning the empty string will fall back to the platform
  541. // default for the given picker type.
  542. std::u16string title;
  543. switch (options->type_specific_options->which()) {
  544. case blink::mojom::TypeSpecificFilePickerOptionsUnion::Tag::
  545. kDirectoryPickerOptions:
  546. title = l10n_util::GetStringUTF16(
  547. options->type_specific_options->get_directory_picker_options()
  548. ->request_writable
  549. ? IDS_FILE_SYSTEM_ACCESS_CHOOSER_OPEN_WRITABLE_DIRECTORY_TITLE
  550. : IDS_FILE_SYSTEM_ACCESS_CHOOSER_OPEN_READABLE_DIRECTORY_TITLE);
  551. break;
  552. case blink::mojom::TypeSpecificFilePickerOptionsUnion::Tag::
  553. kSaveFilePickerOptions:
  554. title = l10n_util::GetStringUTF16(
  555. IDS_FILE_SYSTEM_ACCESS_CHOOSER_OPEN_SAVE_FILE_TITLE);
  556. break;
  557. case blink::mojom::TypeSpecificFilePickerOptionsUnion::Tag::
  558. kOpenFilePickerOptions:
  559. break;
  560. }
  561. return title;
  562. }
  563. void FileSystemAccessPermissionContext::NotifyEntryMoved(
  564. const url::Origin& origin,
  565. const base::FilePath& old_path,
  566. const base::FilePath& new_path) {
  567. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  568. if (old_path == new_path) {
  569. return;
  570. }
  571. auto it = active_permissions_map_.find(origin);
  572. if (it != active_permissions_map_.end()) {
  573. PermissionGrantImpl::UpdateGrantPath(it->second.write_grants, old_path,
  574. new_path);
  575. PermissionGrantImpl::UpdateGrantPath(it->second.read_grants, old_path,
  576. new_path);
  577. }
  578. }
  579. void FileSystemAccessPermissionContext::OnFileCreatedFromShowSaveFilePicker(
  580. const GURL& file_picker_binding_context,
  581. const storage::FileSystemURL& url) {}
  582. void FileSystemAccessPermissionContext::CheckPathsAgainstEnterprisePolicy(
  583. std::vector<PathInfo> entries,
  584. content::GlobalRenderFrameHostId frame_id,
  585. EntriesAllowedByEnterprisePolicyCallback callback) {
  586. std::move(callback).Run(std::move(entries));
  587. }
  588. void FileSystemAccessPermissionContext::RevokeGrant(
  589. const url::Origin& origin,
  590. const base::FilePath& file_path) {
  591. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  592. auto origin_it = active_permissions_map_.find(origin);
  593. if (origin_it != active_permissions_map_.end()) {
  594. OriginState& origin_state = origin_it->second;
  595. for (auto& grant : origin_state.read_grants) {
  596. if (file_path.empty() || grant.first == file_path) {
  597. grant.second->SetStatus(PermissionStatus::ASK);
  598. }
  599. }
  600. for (auto& grant : origin_state.write_grants) {
  601. if (file_path.empty() || grant.first == file_path) {
  602. grant.second->SetStatus(PermissionStatus::ASK);
  603. }
  604. }
  605. }
  606. }
  607. bool FileSystemAccessPermissionContext::OriginHasReadAccess(
  608. const url::Origin& origin) {
  609. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  610. auto it = active_permissions_map_.find(origin);
  611. if (it != active_permissions_map_.end()) {
  612. return base::ranges::any_of(it->second.read_grants, [&](const auto& grant) {
  613. return grant.second->GetStatus() == PermissionStatus::GRANTED;
  614. });
  615. }
  616. return false;
  617. }
  618. bool FileSystemAccessPermissionContext::OriginHasWriteAccess(
  619. const url::Origin& origin) {
  620. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  621. auto it = active_permissions_map_.find(origin);
  622. if (it != active_permissions_map_.end()) {
  623. return base::ranges::any_of(
  624. it->second.write_grants, [&](const auto& grant) {
  625. return grant.second->GetStatus() == PermissionStatus::GRANTED;
  626. });
  627. }
  628. return false;
  629. }
  630. void FileSystemAccessPermissionContext::CleanupPermissions(
  631. const url::Origin& origin) {
  632. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  633. RevokeGrant(origin);
  634. }
  635. bool FileSystemAccessPermissionContext::AncestorHasActivePermission(
  636. const url::Origin& origin,
  637. const base::FilePath& path,
  638. GrantType grant_type) const {
  639. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  640. auto it = active_permissions_map_.find(origin);
  641. if (it == active_permissions_map_.end()) {
  642. return false;
  643. }
  644. const auto& relevant_grants = grant_type == GrantType::kWrite
  645. ? it->second.write_grants
  646. : it->second.read_grants;
  647. if (relevant_grants.empty()) {
  648. return false;
  649. }
  650. // Permissions are inherited from the closest ancestor.
  651. for (base::FilePath parent = path.DirName(); parent != parent.DirName();
  652. parent = parent.DirName()) {
  653. auto i = relevant_grants.find(parent);
  654. if (i != relevant_grants.end() && i->second &&
  655. i->second->GetStatus() == PermissionStatus::GRANTED) {
  656. return true;
  657. }
  658. }
  659. return false;
  660. }
  661. void FileSystemAccessPermissionContext::PermissionGrantDestroyed(
  662. PermissionGrantImpl* grant) {
  663. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  664. auto it = active_permissions_map_.find(grant->origin());
  665. if (it == active_permissions_map_.end()) {
  666. return;
  667. }
  668. auto& grants = grant->type() == GrantType::kRead ? it->second.read_grants
  669. : it->second.write_grants;
  670. auto grant_it = grants.find(grant->GetPath());
  671. // Any non-denied permission grants should have still been in our grants
  672. // list. If this invariant is violated we would have permissions that might
  673. // be granted but won't be visible in any UI because the permission context
  674. // isn't tracking them anymore.
  675. if (grant_it == grants.end()) {
  676. DCHECK_EQ(PermissionStatus::DENIED, grant->GetStatus());
  677. return;
  678. }
  679. // The grant in |grants| for this path might have been replaced with a
  680. // different grant. Only erase if it actually matches the grant that was
  681. // destroyed.
  682. if (grant_it->second == grant) {
  683. grants.erase(grant_it);
  684. }
  685. }
  686. base::WeakPtr<FileSystemAccessPermissionContext>
  687. FileSystemAccessPermissionContext::GetWeakPtr() {
  688. return weak_factory_.GetWeakPtr();
  689. }
  690. } // namespace electron