| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef CHROME_BROWSER_ASH_POLICY_DLP_FILES_POLICY_NOTIFICATION_MANAGER_H_ |
| #define CHROME_BROWSER_ASH_POLICY_DLP_FILES_POLICY_NOTIFICATION_MANAGER_H_ |
| |
| #include <memory> |
| #include <optional> |
| #include <queue> |
| |
| #include "base/files/file_path.h" |
| #include "base/functional/callback_forward.h" |
| #include "base/memory/raw_ptr.h" |
| #include "chrome/browser/ash/browser_delegate/browser_controller.h" |
| #include "chrome/browser/ash/file_manager/io_task.h" |
| #include "chrome/browser/ash/file_manager/io_task_controller.h" |
| #include "chrome/browser/ash/policy/dlp/dialogs/files_policy_dialog.h" |
| #include "chrome/browser/chromeos/policy/dlp/dialogs/policy_dialog_base.h" |
| #include "chrome/browser/chromeos/policy/dlp/dlp_confidential_file.h" |
| #include "chrome/browser/chromeos/policy/dlp/dlp_file_destination.h" |
| #include "chrome/browser/chromeos/policy/dlp/dlp_files_utils.h" |
| #include "components/keyed_service/core/keyed_service.h" |
| #include "content/public/browser/browser_context.h" |
| #include "ui/gfx/native_ui_types.h" |
| #include "ui/views/widget/widget_observer.h" |
| |
| namespace content { |
| class BrowserContext; |
| } // namespace content |
| |
| namespace policy { |
| |
| // The type of policy notification. |
| enum class NotificationType { |
| kError, |
| kWarning, |
| }; |
| |
| // The policy notification button index. |
| enum NotificationButton { |
| CANCEL = 0, |
| OK, |
| }; |
| |
| // FilesPolicyNotificationManager is responsible for showing block and warning |
| // notifications/dialogs for files because of DLP and enterprise connectors |
| // policies. |
| class FilesPolicyNotificationManager |
| : public KeyedService, |
| public ash::BrowserController::Observer, |
| public file_manager::io_task::IOTaskController::Observer { |
| public: |
| explicit FilesPolicyNotificationManager(content::BrowserContext* context); |
| |
| FilesPolicyNotificationManager(const FilesPolicyNotificationManager&) = |
| delete; |
| FilesPolicyNotificationManager& operator=( |
| const FilesPolicyNotificationManager&) = delete; |
| |
| ~FilesPolicyNotificationManager() override; |
| |
| // KeyedService overrides: |
| void Shutdown() override; |
| |
| // Show DLP block UI. If `task_id` is set, the corresponding IOTask will be |
| // updated with the blocked files. Otherwise a desktop notification will be |
| // shown. |
| virtual void ShowDlpBlockedFiles( |
| std::optional<file_manager::io_task::IOTaskId> task_id, |
| std::vector<base::FilePath> blocked_files, |
| dlp::FileAction action); |
| |
| // Associates `dialog_info` to the given enterprise connectors `reason` in the |
| // information corresponding to the IOTask with `task_id`. This will replace |
| // previously stored dialog info with the same `reason`. |
| virtual void SetConnectorsBlockedFiles( |
| file_manager::io_task::IOTaskId task_id, |
| dlp::FileAction action, |
| FilesPolicyDialog::BlockReason reason, |
| FilesPolicyDialog::Info dialog_info); |
| |
| // Shows DLP Warning UI. If `task_id` is set, the corresponding IOTask |
| // will be paused. Otherwise a desktop notification will be shown. Virtual |
| // to allow overrides in tests. |
| virtual void ShowDlpWarning( |
| WarningWithJustificationCallback callback, |
| std::optional<file_manager::io_task::IOTaskId> task_id, |
| std::vector<base::FilePath> warning_files, |
| const DlpFileDestination& destination, |
| dlp::FileAction action); |
| |
| // Shows Connectors Warning UI and pauses the corresponding IOTask. |
| // Virtual to allow overrides in tests. |
| virtual void ShowConnectorsWarning(WarningWithJustificationCallback callback, |
| file_manager::io_task::IOTaskId task_id, |
| dlp::FileAction action, |
| FilesPolicyDialog::Info dialog_info); |
| |
| // Shows a Files Policy warning or error desktop notification with |
| // `notification_id` based on `status`. Used for IO tasks. |
| virtual void ShowFilesPolicyNotification( |
| const std::string& notification_id, |
| const file_manager::io_task::ProgressStatus& status); |
| |
| // Shows a policy dialog of type `type` for task identified by `task_id`. |
| // Used for copy and move operations. |
| virtual void ShowDialog(file_manager::io_task::IOTaskId task_id, |
| FilesDialogType type); |
| |
| // Shows a DLP warning timeout notification for `action`. `notification_id` |
| // should have value for IO tasks. When it doesn't have a value, i.e. for non |
| // IO tasks, computes a new unique id for the notification. |
| void ShowDlpWarningTimeoutNotification( |
| dlp::FileAction action, |
| std::optional<std::string> notification_id = std::nullopt); |
| |
| // Returns whether IO task is being tracked. |
| bool HasIOTask(file_manager::io_task::IOTaskId task_id) const; |
| |
| // Runs warning callback for the corresponding IOTask with should_proceed set |
| // to true. |
| virtual void OnIOTaskResumed(file_manager::io_task::IOTaskId task_id); |
| |
| // Force shows a desktop notification for all tracked IO tasks with blocked |
| // files. |
| void ShowBlockedNotifications(); |
| |
| // Clears any info stored about the task with `task_id`. |
| virtual void OnErrorItemDismissed(file_manager::io_task::IOTaskId task_id); |
| |
| std::map<FilesPolicyDialog::BlockReason, FilesPolicyDialog::Info> |
| GetIOTaskDialogInfoMapForTesting( |
| file_manager::io_task::IOTaskId task_id) const; |
| // Returns whether IO task has a warning timeout timer. |
| bool HasWarningTimerForTesting(file_manager::io_task::IOTaskId task_id) const; |
| |
| // Used in tests to set the test task runner. |
| void SetTaskRunnerForTesting(scoped_refptr<base::SequencedTaskRunner>); |
| |
| protected: |
| // The number of notifications shown so far. Used to calculate a unique |
| // notification ID. Only applies to non IOTasks operations (upload, download, |
| // etc.) as notifications for IOTasks are shown based on the task state from |
| // the SystemNotificationManager. |
| size_t notification_count_ = 0; |
| |
| private: |
| // Holds all information related to file task warning. Any extra information |
| // needed for custom messaging should be added here. |
| struct WarningInfo { |
| WarningInfo() = delete; |
| WarningInfo(Policy warning_reason, |
| WarningWithJustificationCallback warning_callback, |
| WarningWithJustificationCallback dialog_callback, |
| FilesPolicyDialog::Info dialog_info); |
| WarningInfo(WarningInfo&& other); |
| ~WarningInfo(); |
| |
| // Warning reason. There should be only one policy per warning as mixed |
| // warnings aren't supported. |
| Policy warning_reason; |
| // Warning callback. |
| WarningWithJustificationCallback warning_callback; |
| // Invoked by clicking on dialog's buttons. Wrapper around `callback` as it |
| // performs additional actions before running `callback` with the same |
| // `should_proceed` parameter. |
| WarningWithJustificationCallback dialog_callback; |
| // Holds warning dialog info such as the warned files, the warning message, |
| // an optional custom learn more URL or whether bypassing the warning |
| // requires a user justification that should be used when displaying the |
| // dialog. |
| FilesPolicyDialog::Info dialog_info; |
| |
| // A potentially saved justification for bypassing a warning. |
| std::optional<std::u16string> user_justification; |
| }; |
| |
| // Holds needed information for each tracked file task. |
| class FileTaskInfo : public views::WidgetObserver { |
| public: |
| explicit FileTaskInfo(dlp::FileAction action); |
| FileTaskInfo(FileTaskInfo&& other); |
| ~FileTaskInfo() override; |
| |
| // Starts observing `widget`. Should be called when the warning/error dialog |
| // is created. |
| void AddWidget(views::Widget* widget); |
| // Closes `widget_` if it's not nullptr. |
| void CloseWidget(); |
| views::Widget* widget() const { return widget_; } |
| |
| // Sets `warning_info_`. |
| void SetWarningInfo(WarningInfo warning_info); |
| // Resets `warning_info_`. |
| void ResetWarningInfo(); |
| // Returns a pointer to WarningInfo if it exists. Otherwise, it returns |
| // nullptr. |
| WarningInfo* GetWarningInfo(); |
| // Returns true if `warning_info_` has value. |
| bool HasWarningInfo() const; |
| |
| const std::map<FilesPolicyDialog::BlockReason, FilesPolicyDialog::Info>& |
| block_info_map() const { |
| return block_info_map_; |
| } |
| |
| // Stores `dialog_info` associated with the given `reason`. |
| // Calling this method a second time with the same `reason` will overwrite |
| // previously stored `files` and `dialog_settings`. |
| void SetBlockedFiles(FilesPolicyDialog::BlockReason reason, |
| FilesPolicyDialog::Info dialog_info); |
| |
| // Returns the overall number of blocked files irrespective of their block |
| // reason. |
| size_t GetBlockedFilesSize() const; |
| |
| dlp::FileAction action() const { return action_; } |
| |
| private: |
| // views::WidgetObserver overrides: |
| void OnWidgetDestroying(views::Widget* widget) override; |
| |
| // Should have value only if there's warning. |
| std::optional<WarningInfo> warning_info_; |
| // A map of files and dialog settings blocked for certain block reasons. |
| std::map<FilesPolicyDialog::BlockReason, FilesPolicyDialog::Info> |
| block_info_map_; |
| // The action that's restricted. |
| dlp::FileAction action_; |
| // Warning/Error dialog widget. Each FileTask is expected to have only one |
| // open dialog at a time. |
| raw_ptr<views::Widget> widget_ = nullptr; |
| // Warning/Error dialog widget observation. |
| base::ScopedObservation<views::Widget, views::WidgetObserver> |
| widget_observation_{this}; |
| }; |
| |
| // Callback to show the dialog. Invoked with a Files App window when |
| // successfully opened, or null if opening the Files App times out. |
| using ShowDialogCallback = base::OnceCallback<void(gfx::NativeWindow)>; |
| |
| // Holds information for showing a Files Policy dialog. |
| struct DialogInfo { |
| DialogInfo() = delete; |
| DialogInfo(ShowDialogCallback callback, |
| file_manager::io_task::IOTaskId task_id, |
| base::OnceClosure timeout_callback); |
| DialogInfo(ShowDialogCallback callback, |
| std::string notification_id, |
| base::OnceClosure timeout_callback); |
| ~DialogInfo(); |
| |
| // Id of the task for which dialog is being shown. Used for Copy and Move |
| // IOTasks. |
| std::optional<file_manager::io_task::IOTaskId> task_id; |
| // Id of the notification for which dialog is being shown. Used for non IO |
| // tasks. |
| std::optional<std::string> notification_id; |
| // Callback to show the dialog. |
| ShowDialogCallback dialog_callback; |
| // Callback to stop waiting for the Files app. |
| base::OnceClosure timeout_callback; |
| base::TimeTicks created_at; |
| base::OneShotTimer timeout_timer; |
| }; |
| |
| // Shows a Files Policy warning or error desktop notification with |
| // `notification_id` for an IOTask with `task_id`. |
| void ShowFilesPolicyNotification(const std::string& notification_id, |
| file_manager::io_task::IOTaskId task_id); |
| |
| // Click handler for Data Leak Prevention or Enterprise Connectors policy |
| // warning notifications. |
| void HandleFilesPolicyWarningNotificationClick( |
| file_manager::io_task::IOTaskId task_id, |
| std::string notification_id, |
| std::optional<int> button_index); |
| |
| // Click handler for Data Leak Prevention or Enterprise Connectors policy |
| // error notifications. |
| void HandleFilesPolicyErrorNotificationClick( |
| file_manager::io_task::IOTaskId task_id, |
| std::string notification_id, |
| std::optional<int> button_index); |
| |
| // Click handler for DLP warning notifications. Used for non IO tasks. |
| void HandleDlpWarningNotificationClick(std::string notification_id, |
| std::optional<int> button_index); |
| |
| // Click handler for DLP error notifications. Used for non IO tasks. |
| void HandleDlpErrorNotificationClick(std::string notification_id, |
| std::vector<base::FilePath> files, |
| dlp::FileAction action, |
| std::optional<int> button_index); |
| |
| // Shows a FilesPolicyDialog of `type` for task with `task_id`. |
| void ShowDialogForIOTask(file_manager::io_task::IOTaskId task_id, |
| FilesDialogType type, |
| gfx::NativeWindow modal_parent); |
| |
| // Shows a FilesPolicyDialog of `type` for non-IO task associated with |
| // `notification_id`. |
| void ShowDialogForNonIOTask(std::string notification_id, |
| FilesDialogType type, |
| gfx::NativeWindow modal_parent); |
| |
| // Shows a FilesPolicyDialog of `type` based on `info`. |
| void ShowFilesPolicyDialog(FileTaskInfo& info, |
| FilesDialogType type, |
| gfx::NativeWindow modal_parent); |
| |
| // Starts tracking IO task with `task_id`. |
| void AddIOTask(file_manager::io_task::IOTaskId task_id, |
| dlp::FileAction action); |
| |
| // Launches the Files App in default directory and appends `dialog_info` to |
| // the queue of pending dialogs in order to show the dialog over it. |
| void LaunchFilesApp(std::unique_ptr<DialogInfo> dialog_info); |
| |
| // ash::BrowserController::Observer overrides: |
| // Called when opening a new Files App window to use as the modal parent for a |
| // FilesPolicyDialog. |
| void OnBrowserCreated(ash::BrowserDelegate* browser) override; |
| |
| // file_manager::io_task::IOTaskController::Observer overrides: |
| void OnIOTaskStatus( |
| const file_manager::io_task::ProgressStatus& status) override; |
| |
| // Returns whether IO task has any blocked file. |
| bool HasBlockedFiles(file_manager::io_task::IOTaskId task_id) const; |
| |
| // Returns whether IO task has a warning. |
| bool HasWarning(file_manager::io_task::IOTaskId task_id) const; |
| |
| // Returns whether non IO task is being tracked. |
| bool HasNonIOTask(const std::string& notification_id) const; |
| |
| // Returns whether non IO task has any blocked file. |
| bool HasBlockedFiles(const std::string& notification_id) const; |
| |
| // Returns whether non IO task has a warning. |
| bool HasWarning(const std::string& notification_id) const; |
| |
| // Called when the user clicks on one of the warning dialog's buttons. |
| // Resumes/cancels the task with `task_id` based on the value of |
| // `should_proceed`. Used for Copy and Move IOTasks. |
| void OnIOTaskWarningDialogClicked( |
| file_manager::io_task::IOTaskId task_id, |
| Policy warning_reason, |
| std::optional<std::u16string> user_justification, |
| bool should_proceed); |
| |
| // Called when the user clicks on one of the warning dialog's buttons. |
| // associated with `notification_id`. Resumes/cancels the operation based on |
| // the value of `should_proceed`. |
| void OnNonIOTaskWarningDialogClicked( |
| const std::string& notification_id, |
| std::optional<std::u16string> user_justification, |
| bool should_proceed); |
| |
| // Opens DLP Learn more link and closes the notification having |
| // `notification_id`. |
| void OnDlpLearnMoreButtonClicked(const std::string& notification_id, |
| std::optional<int> button_index); |
| |
| // Calls the IOTaskController to resume the task with `task_id`. |
| void Resume(file_manager::io_task::IOTaskId task_id); |
| |
| // Calls the IOTaskController to cancel the task with `task_id`. |
| void Cancel(file_manager::io_task::IOTaskId task_id); |
| |
| // Shows DLP block desktop notification. |
| void ShowDlpBlockNotification(std::vector<base::FilePath> blocked_files, |
| dlp::FileAction action); |
| |
| // Shows DLP warning desktop notification. |
| void ShowDlpWarningNotification(WarningWithJustificationCallback callback, |
| std::vector<base::FilePath> warning_files, |
| const DlpFileDestination& destination, |
| dlp::FileAction action); |
| |
| // Pauses IO task due to `warning_reason`. |
| void PauseIOTask(file_manager::io_task::IOTaskId task_id, |
| WarningWithJustificationCallback callback, |
| dlp::FileAction action, |
| Policy warning_reason, |
| FilesPolicyDialog::Info dialog_info); |
| |
| // Called after opening the Files App times out. |
| // Stops waiting for the app and shows a dialog for `task_id` without a modal |
| // parent (i.e. as a system modal). |
| void OnIOTaskAppLaunchTimedOut(file_manager::io_task::IOTaskId task_id); |
| |
| // Called after opening the Files App times out. |
| // Stops waiting for the app and shows a dialog for `notification_id` without |
| // a modal parent (i.e. as a system modal). |
| void OnNonIOTaskAppLaunchTimedOut(std::string notification_id); |
| |
| // Helper method that pops the oldest entry from `pending_dialogs_` and |
| // creates a dialog with with `modal_parent`. No-op if the list is empty. |
| void ShowPendingDialog(gfx::NativeWindow modal_parent); |
| |
| // Called when the warning times out. Stops waiting for the user input, |
| // cancels the task, and runs the warning callback with should_proceed set to |
| // false. |
| void OnIOTaskWarningTimedOut(const file_manager::io_task::IOTaskId& task_id); |
| |
| // Called when the warning times out. Stops waiting for the user input, and |
| // runs the warning callback with should_proceed set to false. |
| void OnNonIOTaskWarningTimedOut(const std::string& notification_id); |
| |
| // Callback to show a policy dialog after waiting to open a Files App window. |
| base::OnceCallback<void(gfx::NativeWindow)> pending_callback_; |
| |
| // Context for which the FPNM is created. |
| raw_ptr<content::BrowserContext, DanglingUntriaged> context_; |
| |
| // A map from tracked IO tasks ids to their info. |
| std::map<file_manager::io_task::IOTaskId, FileTaskInfo> io_tasks_; |
| |
| // A map from notification ids to related task info for non IO operations. |
| std::map<std::string, FileTaskInfo> non_io_tasks_; |
| |
| // Callbacks to show a policy dialog after waiting to open a Files App window. |
| std::queue<std::unique_ptr<DialogInfo>> pending_dialogs_; |
| |
| // Used to fallack to system modal if opening the Files App times out. |
| scoped_refptr<base::SequencedTaskRunner> task_runner_; |
| |
| // Active timers for IOTasks warnings. |
| base::flat_map<file_manager::io_task::IOTaskId, |
| std::unique_ptr<base::OneShotTimer>> |
| io_tasks_warning_timers_; |
| |
| // Active timers for non-IOTasks warnings. |
| base::flat_map<std::string, std::unique_ptr<base::OneShotTimer>> |
| non_io_tasks_warning_timers_; |
| |
| base::WeakPtrFactory<FilesPolicyNotificationManager> weak_factory_{this}; |
| }; |
| |
| } // namespace policy |
| |
| #endif // CHROME_BROWSER_ASH_POLICY_DLP_FILES_POLICY_NOTIFICATION_MANAGER_H_ |