Save encrypted auth token in profile prefs. (#32596)
* Save encrypted auth token in profile prefs. * Using async os crypt.
This commit is contained in:
@@ -45,6 +45,7 @@
|
||||
#include "brave/components/containers/buildflags/buildflags.h"
|
||||
#include "brave/components/de_amp/common/pref_names.h"
|
||||
#include "brave/components/debounce/core/browser/debounce_service.h"
|
||||
#include "brave/components/email_aliases/email_aliases_service.h"
|
||||
#include "brave/components/global_privacy_control/pref_names.h"
|
||||
#include "brave/components/ipfs/ipfs_prefs.h"
|
||||
#include "brave/components/ntp_background_images/browser/view_counter_service.h"
|
||||
@@ -571,6 +572,8 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
|
||||
containers::RegisterProfilePrefs(registry);
|
||||
#endif
|
||||
|
||||
email_aliases::EmailAliasesService::RegisterProfilePrefs(registry);
|
||||
|
||||
OverrideDefaultPrefValues(registry);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,10 @@ static_library("email_aliases") {
|
||||
"//base",
|
||||
"//brave/components/email_aliases:features",
|
||||
"//brave/components/email_aliases:service",
|
||||
"//chrome/browser:browser_process",
|
||||
"//chrome/browser/profiles:profile",
|
||||
"//components/keyed_service/content",
|
||||
"//components/user_prefs",
|
||||
"//content/public/browser",
|
||||
"//mojo/public/cpp/bindings",
|
||||
]
|
||||
@@ -54,6 +56,7 @@ if (toolkit_views) {
|
||||
"//brave/components/email_aliases:email_aliases_api_types",
|
||||
"//brave/components/email_aliases:features",
|
||||
"//brave/components/email_aliases:service",
|
||||
"//brave/components/email_aliases:test_utils",
|
||||
"//chrome/browser",
|
||||
"//chrome/browser/ui/views/bubble",
|
||||
"//chrome/test:test_support",
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
#include "base/json/json_reader.h"
|
||||
#include "base/path_service.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "base/test/run_until.h"
|
||||
#include "base/time/time.h"
|
||||
#include "brave/browser/email_aliases/email_aliases_service_factory.h"
|
||||
@@ -15,16 +14,19 @@
|
||||
#include "brave/browser/ui/webui/brave_settings_ui.h"
|
||||
#include "brave/components/constants/brave_paths.h"
|
||||
#include "brave/components/email_aliases/email_aliases_api.h"
|
||||
#include "brave/components/email_aliases/email_aliases_auth.h"
|
||||
#include "brave/components/email_aliases/email_aliases_service.h"
|
||||
#include "brave/components/email_aliases/features.h"
|
||||
#include "brave/components/email_aliases/test_utils.h"
|
||||
#include "chrome/browser/browser_process.h"
|
||||
#include "chrome/browser/profiles/profile.h"
|
||||
#include "chrome/browser/renderer_context_menu/render_view_context_menu.h"
|
||||
#include "chrome/browser/renderer_context_menu/render_view_context_menu_browsertest_util.h"
|
||||
#include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
|
||||
#include "chrome/browser/ui/browser.h"
|
||||
#include "chrome/browser/ui/browser_commands.h"
|
||||
#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
|
||||
#include "chrome/browser/ui/views/bubble/webui_bubble_manager.h"
|
||||
#include "chrome/browser/ui/views/frame/browser_view.h"
|
||||
#include "chrome/test/base/in_process_browser_test.h"
|
||||
#include "chrome/test/base/ui_test_utils.h"
|
||||
#include "content/public/browser/render_frame_host.h"
|
||||
@@ -398,11 +400,13 @@ IN_PROC_BROWSER_TEST_F(EmailAliasesBrowserTest, ContextMenuNotAuthorized) {
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(EmailAliasesBrowserTest, ContextMenuAuthorized) {
|
||||
email_aliases_service()->RequestAuthentication("success@domain.com",
|
||||
base::DoNothing());
|
||||
ASSERT_TRUE(base::test::RunUntil([&]() {
|
||||
return !email_aliases_service()->GetAuthTokenForTesting().empty();
|
||||
}));
|
||||
auto* service = email_aliases_service();
|
||||
{
|
||||
auto initilized = test::AuthStateObserver::Setup(service, true);
|
||||
}
|
||||
service->RequestAuthentication("success@domain.com", base::DoNothing());
|
||||
ASSERT_TRUE(base::test::RunUntil(
|
||||
[&]() { return !service->GetAuthTokenForTesting().empty(); }));
|
||||
|
||||
Navigate(GURL("https://a.test/email_aliases/inputs.html"));
|
||||
InjectHelpers(ActiveWebContents());
|
||||
@@ -499,4 +503,26 @@ IN_PROC_BROWSER_TEST_F(EmailAliasesBrowserTest, ContextMenuAuthorizedCancel) {
|
||||
|
||||
EXPECT_TRUE(AwaitText("#type-email", "")); // text not changed
|
||||
}
|
||||
IN_PROC_BROWSER_TEST_F(EmailAliasesBrowserTest, LogInLogOut) {
|
||||
// Prepare auth token
|
||||
EmailAliasesAuth auth(
|
||||
browser()->profile()->GetPrefs(),
|
||||
test::GetEncryptor(g_browser_process->os_crypt_async()));
|
||||
auth.SetAuthEmail(kSuccessEmail);
|
||||
auth.SetAuthToken("success_token");
|
||||
|
||||
// Settings in logged-in state
|
||||
Navigate(GURL("chrome://settings/email-aliases"));
|
||||
InjectHelpers(ActiveWebContents());
|
||||
Wait("#create-new-item-button");
|
||||
|
||||
// Reset token
|
||||
auth.SetAuthToken({});
|
||||
Wait("#get-login-link-button"); // Settings in sing-in state.
|
||||
|
||||
// Logged-in again
|
||||
auth.SetAuthToken("success_token");
|
||||
Wait("#create-new-item-button"); // Logged-in state.
|
||||
}
|
||||
|
||||
} // namespace email_aliases
|
||||
|
||||
@@ -9,8 +9,10 @@
|
||||
|
||||
#include "brave/components/email_aliases/email_aliases_service.h"
|
||||
#include "brave/components/email_aliases/features.h"
|
||||
#include "chrome/browser/browser_process.h"
|
||||
#include "chrome/browser/profiles/profile.h"
|
||||
#include "chrome/browser/profiles/profile_selections.h"
|
||||
#include "components/user_prefs/user_prefs.h"
|
||||
#include "content/public/browser/storage_partition.h"
|
||||
|
||||
namespace email_aliases {
|
||||
@@ -53,7 +55,8 @@ EmailAliasesServiceFactory::BuildServiceInstanceForBrowserContext(
|
||||
content::BrowserContext* context) const {
|
||||
return std::make_unique<EmailAliasesService>(
|
||||
context->GetDefaultStoragePartition()
|
||||
->GetURLLoaderFactoryForBrowserProcess());
|
||||
->GetURLLoaderFactoryForBrowserProcess(),
|
||||
user_prefs::UserPrefs::Get(context), g_browser_process->os_crypt_async());
|
||||
}
|
||||
|
||||
} // namespace email_aliases
|
||||
|
||||
@@ -25,6 +25,11 @@ class EmailAliasesPage extends HTMLElement {
|
||||
import('/email_aliases.bundle.js' as any).then(({ mount }) => {
|
||||
mount(subpage)
|
||||
})
|
||||
|
||||
if (loadTimeData.getBoolean('shouldExposeElementsForTesting')) {
|
||||
;(window as any).testing = (window as any).testing || {}
|
||||
;(window as any).testing[`emailAliases`] = this.shadowRoot
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -206,6 +206,7 @@ brave_chrome_browser_deps = [
|
||||
"//brave/components/debounce/content/browser",
|
||||
"//brave/components/email_aliases:features",
|
||||
"//brave/components/email_aliases:mojom",
|
||||
"//brave/components/email_aliases:service",
|
||||
"//brave/components/global_privacy_control",
|
||||
"//brave/components/google_sign_in_permission",
|
||||
"//brave/components/https_upgrade_exceptions/browser",
|
||||
|
||||
@@ -33,6 +33,20 @@ static_library("features") {
|
||||
public_deps = [ "//base" ]
|
||||
}
|
||||
|
||||
source_set("auth") {
|
||||
sources = [
|
||||
"email_aliases_auth.cc",
|
||||
"email_aliases_auth.h",
|
||||
]
|
||||
|
||||
deps = [
|
||||
"//base",
|
||||
"//components/os_crypt/async/common",
|
||||
"//components/prefs",
|
||||
"//services/preferences/public/cpp",
|
||||
]
|
||||
}
|
||||
|
||||
static_library("service") {
|
||||
sources = [
|
||||
"email_aliases_service.cc",
|
||||
@@ -47,22 +61,45 @@ static_library("service") {
|
||||
"//brave/components/email_aliases:features",
|
||||
"//brave/components/resources:strings_grit",
|
||||
"//components/keyed_service/core",
|
||||
"//components/os_crypt/async/browser",
|
||||
"//components/os_crypt/async/common",
|
||||
"//mojo/public/cpp/bindings",
|
||||
"//net",
|
||||
"//services/network/public/cpp",
|
||||
"//ui/base",
|
||||
]
|
||||
|
||||
public_deps = [ ":auth" ]
|
||||
}
|
||||
|
||||
source_set("test_utils") {
|
||||
testonly = true
|
||||
|
||||
sources = [
|
||||
"test_utils.cc",
|
||||
"test_utils.h",
|
||||
]
|
||||
|
||||
deps = [
|
||||
":service",
|
||||
"//base/test:test_support",
|
||||
"//brave/components/email_aliases:mojom",
|
||||
"//components/os_crypt/async/browser",
|
||||
"//mojo/public/cpp/bindings",
|
||||
]
|
||||
}
|
||||
|
||||
source_set("unit_tests") {
|
||||
testonly = true
|
||||
sources = [ "email_aliases_service_unittest.cc" ]
|
||||
deps = [
|
||||
":features",
|
||||
":service",
|
||||
":test_utils",
|
||||
"//base/test:test_support",
|
||||
"//brave/components/constants",
|
||||
"//brave/components/email_aliases:features",
|
||||
"//brave/components/email_aliases:service",
|
||||
"//brave/components/resources:strings_grit",
|
||||
"//components/os_crypt/async/browser:test_support",
|
||||
"//components/prefs:test_support",
|
||||
"//services/network:test_support",
|
||||
"//services/network/public/cpp",
|
||||
|
||||
@@ -2,6 +2,15 @@ include_rules = [
|
||||
"+absl/strings/str_format.h",
|
||||
"+components/keyed_service/core",
|
||||
"+components/grit/brave_components_strings.h",
|
||||
"+components/os_crypt/async",
|
||||
"+components/prefs",
|
||||
"+services/network/public/cpp",
|
||||
"+services/network/test",
|
||||
"+services/preferences/public/cpp",
|
||||
]
|
||||
|
||||
specific_include_rules = {
|
||||
"test_utils.*": [
|
||||
"+gtest"
|
||||
],
|
||||
}
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
/* Copyright (c) 2025 The Brave Authors. All rights reserved.
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "brave/components/email_aliases/email_aliases_auth.h"
|
||||
|
||||
#include "base/auto_reset.h"
|
||||
#include "base/base64.h"
|
||||
#include "base/values.h"
|
||||
#include "components/prefs/pref_registry_simple.h"
|
||||
#include "components/prefs/pref_service.h"
|
||||
#include "services/preferences/public/cpp/dictionary_value_update.h"
|
||||
#include "services/preferences/public/cpp/scoped_pref_update.h"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char kEmailField[] = "email";
|
||||
constexpr char kTokenField[] = "token";
|
||||
|
||||
bool Encrypt(const os_crypt_async::Encryptor& encryptor,
|
||||
const std::string& plain_text,
|
||||
std::string& out) {
|
||||
if (plain_text.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto encrypted = encryptor.EncryptString(plain_text);
|
||||
if (!encrypted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
out = base::Base64Encode(encrypted.value());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Decrypt(const os_crypt_async::Encryptor& encryptor,
|
||||
const std::string& base64,
|
||||
std::string& out) {
|
||||
if (base64.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto encrypted = base::Base64Decode(base64);
|
||||
if (!encrypted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto decrypted = encryptor.DecryptData(encrypted.value());
|
||||
if (!decrypted) {
|
||||
return false;
|
||||
}
|
||||
out = std::string(decrypted->begin(), decrypted->end());
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace email_aliases {
|
||||
|
||||
EmailAliasesAuth::EmailAliasesAuth(PrefService* prefs_service,
|
||||
os_crypt_async::Encryptor encryptor,
|
||||
OnChangedCallback on_changed)
|
||||
: prefs_service_(prefs_service),
|
||||
encryptor_(std::move(encryptor)),
|
||||
on_changed_(std::move(on_changed)) {
|
||||
CHECK(prefs_service_);
|
||||
CHECK(on_changed_);
|
||||
|
||||
pref_change_registrar_.Init(prefs_service_);
|
||||
pref_change_registrar_.Add(
|
||||
prefs::kAuth, base::BindRepeating(&EmailAliasesAuth::OnPrefChanged,
|
||||
base::Unretained(this)));
|
||||
|
||||
auth_email_ = GetAuthEmail();
|
||||
is_authenticated_ = !CheckAndGetAuthToken().empty() && !auth_email_.empty();
|
||||
}
|
||||
|
||||
EmailAliasesAuth::~EmailAliasesAuth() = default;
|
||||
|
||||
// static
|
||||
void EmailAliasesAuth::RegisterProfilePrefs(PrefRegistrySimple* registry) {
|
||||
registry->RegisterDictionaryPref(prefs::kAuth);
|
||||
}
|
||||
|
||||
bool EmailAliasesAuth::IsAuthenticated() const {
|
||||
return is_authenticated_;
|
||||
}
|
||||
|
||||
void EmailAliasesAuth::SetAuthEmail(const std::string& email) {
|
||||
if (GetAuthEmail() != email) {
|
||||
::prefs::ScopedDictionaryPrefUpdate update(prefs_service_, prefs::kAuth);
|
||||
update->SetString(kEmailField, email);
|
||||
update->SetString(kTokenField, std::string_view{});
|
||||
}
|
||||
}
|
||||
|
||||
void EmailAliasesAuth::SetAuthToken(const std::string& auth_token) {
|
||||
::prefs::ScopedDictionaryPrefUpdate update(prefs_service_, prefs::kAuth);
|
||||
|
||||
std::string encrypted;
|
||||
if (auth_token.empty() || !Encrypt(encryptor_, auth_token, encrypted)) {
|
||||
update->SetString(kTokenField, std::string_view{});
|
||||
} else {
|
||||
update->SetString(kTokenField, encrypted);
|
||||
}
|
||||
}
|
||||
|
||||
std::string EmailAliasesAuth::GetAuthEmail() const {
|
||||
const base::Value::Dict& auth = prefs_service_->GetDict(prefs::kAuth);
|
||||
if (const auto* email = auth.FindString(kEmailField)) {
|
||||
return *email;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string EmailAliasesAuth::CheckAndGetAuthToken() {
|
||||
const base::Value::Dict& auth = prefs_service_->GetDict(prefs::kAuth);
|
||||
if (const auto* encrypted_token = auth.FindString(kTokenField)) {
|
||||
if (encrypted_token->empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string token;
|
||||
if (!Decrypt(encryptor_, *encrypted_token, token)) {
|
||||
// Failed to decrypt token -> reset.
|
||||
SetAuthToken({});
|
||||
return {};
|
||||
}
|
||||
return token;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void EmailAliasesAuth::OnPrefChanged(const std::string& pref_name) {
|
||||
if (!notify_) {
|
||||
return;
|
||||
}
|
||||
|
||||
base::AutoReset reenter(¬ify_, false);
|
||||
|
||||
auto auth_email = GetAuthEmail();
|
||||
if (auth_email != auth_email_) {
|
||||
SetAuthToken({});
|
||||
auth_email_ = std::move(auth_email);
|
||||
}
|
||||
|
||||
is_authenticated_ = !CheckAndGetAuthToken().empty() && !auth_email_.empty();
|
||||
on_changed_.Run();
|
||||
}
|
||||
|
||||
} // namespace email_aliases
|
||||
@@ -0,0 +1,62 @@
|
||||
/* Copyright (c) 2025 The Brave Authors. All rights reserved.
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef BRAVE_COMPONENTS_EMAIL_ALIASES_EMAIL_ALIASES_AUTH_H_
|
||||
#define BRAVE_COMPONENTS_EMAIL_ALIASES_EMAIL_ALIASES_AUTH_H_
|
||||
|
||||
#include "components/os_crypt/async/common/encryptor.h"
|
||||
#include "components/prefs/pref_change_registrar.h"
|
||||
#include "components/prefs/pref_member.h"
|
||||
|
||||
class PrefRegistrySimple;
|
||||
class PrefService;
|
||||
|
||||
namespace email_aliases {
|
||||
|
||||
namespace prefs {
|
||||
|
||||
inline constexpr char kAuth[] = "brave.email_aliases.auth";
|
||||
|
||||
} // namespace prefs
|
||||
|
||||
class EmailAliasesAuth {
|
||||
public:
|
||||
using OnChangedCallback = base::RepeatingClosure;
|
||||
|
||||
explicit EmailAliasesAuth(PrefService* prefs_service,
|
||||
os_crypt_async::Encryptor encryptor,
|
||||
OnChangedCallback on_changed = base::DoNothing());
|
||||
~EmailAliasesAuth();
|
||||
|
||||
static void RegisterProfilePrefs(PrefRegistrySimple* registry);
|
||||
|
||||
bool IsAuthenticated() const;
|
||||
|
||||
void SetAuthEmail(const std::string& email);
|
||||
void SetAuthToken(const std::string& auth_token);
|
||||
|
||||
std::string GetAuthEmail() const;
|
||||
std::string CheckAndGetAuthToken();
|
||||
|
||||
private:
|
||||
void OnPrefChanged(const std::string& pref_name);
|
||||
|
||||
const raw_ptr<PrefService> prefs_service_ = nullptr;
|
||||
|
||||
os_crypt_async::Encryptor encryptor_;
|
||||
|
||||
PrefChangeRegistrar pref_change_registrar_;
|
||||
|
||||
OnChangedCallback on_changed_;
|
||||
|
||||
bool notify_ = true;
|
||||
|
||||
std::string auth_email_;
|
||||
bool is_authenticated_ = false;
|
||||
};
|
||||
|
||||
} // namespace email_aliases
|
||||
|
||||
#endif // BRAVE_COMPONENTS_EMAIL_ALIASES_EMAIL_ALIASES_AUTH_H_
|
||||
@@ -23,6 +23,8 @@
|
||||
#include "brave/components/email_aliases/email_aliases_api.h"
|
||||
#include "brave/components/email_aliases/features.h"
|
||||
#include "components/grit/brave_components_strings.h"
|
||||
#include "components/os_crypt/async/browser/os_crypt_async.h"
|
||||
#include "components/os_crypt/async/common/encryptor.h"
|
||||
#include "mojo/public/cpp/bindings/callback_helpers.h"
|
||||
#include "mojo/public/cpp/bindings/pending_remote.h"
|
||||
#include "net/traffic_annotation/network_traffic_annotation.h"
|
||||
@@ -120,16 +122,28 @@ GURL EmailAliasesService::GetEmailAliasesServiceURL() {
|
||||
}
|
||||
|
||||
EmailAliasesService::EmailAliasesService(
|
||||
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
|
||||
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
|
||||
PrefService* pref_service,
|
||||
os_crypt_async::OSCryptAsync* os_crypt_async)
|
||||
: url_loader_factory_(url_loader_factory),
|
||||
pref_service_(pref_service),
|
||||
verify_init_url_(GetAccountsServiceVerifyInitURL()),
|
||||
verify_result_url_(GetAccountsServiceVerifyResultURL()),
|
||||
email_aliases_service_base_url_(GetEmailAliasesServiceURL()) {
|
||||
CHECK(base::FeatureList::IsEnabled(email_aliases::features::kEmailAliases));
|
||||
CHECK(pref_service_);
|
||||
|
||||
os_crypt_async->GetInstance(base::BindOnce(
|
||||
&EmailAliasesService::OnEncryptorReady, weak_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
EmailAliasesService::~EmailAliasesService() = default;
|
||||
|
||||
// static
|
||||
void EmailAliasesService::RegisterProfilePrefs(PrefRegistrySimple* registry) {
|
||||
EmailAliasesAuth::RegisterProfilePrefs(registry);
|
||||
}
|
||||
|
||||
void EmailAliasesService::Shutdown() {
|
||||
receivers_.Clear();
|
||||
observers_.Clear();
|
||||
@@ -143,24 +157,27 @@ void EmailAliasesService::BindInterface(
|
||||
void EmailAliasesService::NotifyObserversAuthStateChanged(
|
||||
mojom::AuthenticationStatus status,
|
||||
const std::optional<std::string>& error_message) {
|
||||
const auto email = auth_ ? auth_->GetAuthEmail() : std::string();
|
||||
for (auto& observer : observers_) {
|
||||
observer->OnAuthStateChanged(
|
||||
mojom::AuthState::New(status, auth_email_, error_message));
|
||||
mojom::AuthState::New(status, email, error_message));
|
||||
}
|
||||
}
|
||||
|
||||
void EmailAliasesService::ResetVerificationFlow() {
|
||||
CHECK(auth_);
|
||||
verification_simple_url_loader_.reset();
|
||||
session_request_timer_.Stop();
|
||||
verification_token_.clear();
|
||||
auth_token_.clear();
|
||||
auth_->SetAuthToken({});
|
||||
}
|
||||
|
||||
void EmailAliasesService::RequestAuthentication(
|
||||
const std::string& auth_email,
|
||||
RequestAuthenticationCallback callback) {
|
||||
CHECK(auth_);
|
||||
ResetVerificationFlow();
|
||||
auth_email_ = auth_email;
|
||||
auth_->SetAuthEmail(auth_email);
|
||||
if (auth_email.empty()) {
|
||||
std::move(callback).Run(base::unexpected(
|
||||
l10n_util::GetStringUTF8(IDS_EMAIL_ALIASES_ERROR_NO_EMAIL_PROVIDED)));
|
||||
@@ -198,6 +215,44 @@ void EmailAliasesService::RequestAuthentication(
|
||||
kMaxResponseLength.InBytesUnsigned());
|
||||
}
|
||||
|
||||
void EmailAliasesService::OnEncryptorReady(
|
||||
os_crypt_async::Encryptor encryptor) {
|
||||
CHECK(!auth_);
|
||||
auth_.emplace(pref_service_.get(), std::move(encryptor),
|
||||
base::BindRepeating(&EmailAliasesService::OnAuthChanged,
|
||||
weak_factory_.GetWeakPtr()));
|
||||
OnAuthChanged();
|
||||
}
|
||||
|
||||
std::string EmailAliasesService::GetAuthEmail() const {
|
||||
if (!auth_) {
|
||||
return {};
|
||||
}
|
||||
return auth_->GetAuthEmail();
|
||||
}
|
||||
|
||||
std::string EmailAliasesService::GetAuthToken() {
|
||||
if (!auth_) {
|
||||
return {};
|
||||
}
|
||||
return auth_->CheckAndGetAuthToken();
|
||||
}
|
||||
|
||||
mojom::AuthenticationStatus EmailAliasesService::GetCurrentStatus() {
|
||||
if (!auth_) {
|
||||
return mojom::AuthenticationStatus::kStartup;
|
||||
} else if (IsAuthenticated()) {
|
||||
return mojom::AuthenticationStatus::kAuthenticated;
|
||||
} else if (!verification_token_.empty()) {
|
||||
return mojom::AuthenticationStatus::kAuthenticating;
|
||||
}
|
||||
return mojom::AuthenticationStatus::kUnauthenticated;
|
||||
}
|
||||
|
||||
void EmailAliasesService::OnAuthChanged() {
|
||||
NotifyObserversAuthStateChanged(GetCurrentStatus());
|
||||
}
|
||||
|
||||
void EmailAliasesService::OnRequestAuthenticationResponse(
|
||||
RequestAuthenticationCallback callback,
|
||||
std::optional<std::string> response_body) {
|
||||
@@ -307,9 +362,8 @@ void EmailAliasesService::OnRequestSessionResponse(
|
||||
return;
|
||||
}
|
||||
// Success; set the auth token and notify observers.
|
||||
auth_token_ = *parsed_session->auth_token;
|
||||
auth_->SetAuthToken(*parsed_session->auth_token);
|
||||
session_poll_elapsed_timer_.reset();
|
||||
NotifyObserversAuthStateChanged(mojom::AuthenticationStatus::kAuthenticated);
|
||||
// Kick off an initial aliases refresh on successful authentication.
|
||||
RefreshAliases();
|
||||
}
|
||||
@@ -347,10 +401,12 @@ void EmailAliasesService::CancelAuthenticationOrLogout(
|
||||
|
||||
void EmailAliasesService::GenerateAlias(GenerateAliasCallback callback) {
|
||||
base::Value::Dict body_value; // empty JSON object required by the API
|
||||
auto wrapper = mojo::WrapCallbackWithDefaultInvokeIfNotRun(
|
||||
std::move(callback), base::unexpected(std::string()));
|
||||
ApiFetch(email_aliases_service_base_url_,
|
||||
net::HttpRequestHeaders::kPostMethod, body_value,
|
||||
base::BindOnce(&EmailAliasesService::OnGenerateAliasResponse,
|
||||
weak_factory_.GetWeakPtr(), std::move(callback)));
|
||||
weak_factory_.GetWeakPtr(), std::move(wrapper)));
|
||||
}
|
||||
|
||||
void EmailAliasesService::UpdateAlias(
|
||||
@@ -366,10 +422,12 @@ void EmailAliasesService::UpdateAlias(
|
||||
// For now, we only support active aliases.
|
||||
request.status = "active";
|
||||
auto body_value = request.ToValue();
|
||||
auto wrapper = mojo::WrapCallbackWithDefaultInvokeIfNotRun(
|
||||
std::move(callback), base::unexpected(std::string()));
|
||||
ApiFetch(email_aliases_service_base_url_, net::HttpRequestHeaders::kPutMethod,
|
||||
body_value,
|
||||
base::BindOnce(&EmailAliasesService::OnEditAliasResponse,
|
||||
weak_factory_.GetWeakPtr(), std::move(callback),
|
||||
weak_factory_.GetWeakPtr(), std::move(wrapper),
|
||||
/*update_expected=*/true));
|
||||
}
|
||||
|
||||
@@ -378,10 +436,12 @@ void EmailAliasesService::DeleteAlias(const std::string& alias_email,
|
||||
DeleteAliasRequest request;
|
||||
request.alias = alias_email;
|
||||
auto body_value = request.ToValue();
|
||||
auto wrapper = mojo::WrapCallbackWithDefaultInvokeIfNotRun(
|
||||
std::move(callback), base::unexpected(std::string()));
|
||||
ApiFetch(email_aliases_service_base_url_,
|
||||
net::HttpRequestHeaders::kDeleteMethod, body_value,
|
||||
base::BindOnce(&EmailAliasesService::OnEditAliasResponse,
|
||||
weak_factory_.GetWeakPtr(), std::move(callback),
|
||||
weak_factory_.GetWeakPtr(), std::move(wrapper),
|
||||
/*update_expected=*/false));
|
||||
}
|
||||
|
||||
@@ -391,17 +451,17 @@ void EmailAliasesService::AddObserver(
|
||||
auto* remote = observers_.Get(id);
|
||||
if (remote) {
|
||||
remote->OnAuthStateChanged(
|
||||
mojom::AuthState::New(mojom::AuthenticationStatus::kUnauthenticated,
|
||||
/*email=*/"", /*error_message=*/std::nullopt));
|
||||
mojom::AuthState::New(GetCurrentStatus(), GetAuthEmail(),
|
||||
/*error_message=*/std::nullopt));
|
||||
}
|
||||
}
|
||||
|
||||
bool EmailAliasesService::IsAuthenticated() const {
|
||||
return !auth_email_.empty() && !auth_token_.empty();
|
||||
return auth_ && auth_->IsAuthenticated();
|
||||
}
|
||||
|
||||
std::string EmailAliasesService::GetAuthTokenForTesting() const {
|
||||
return auth_token_;
|
||||
std::string EmailAliasesService::GetAuthTokenForTesting() {
|
||||
return GetAuthToken();
|
||||
}
|
||||
|
||||
void EmailAliasesService::ApiFetch(const GURL& url,
|
||||
@@ -430,11 +490,16 @@ void EmailAliasesService::ApiFetchInternal(
|
||||
const std::string_view method,
|
||||
std::optional<std::string> serialized_body,
|
||||
BodyAsStringCallback callback) {
|
||||
const auto auth_token = GetAuthToken();
|
||||
if (auth_token.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto resource_request = std::make_unique<network::ResourceRequest>();
|
||||
resource_request->url = url;
|
||||
resource_request->method = method;
|
||||
resource_request->headers.SetHeader("Authorization",
|
||||
std::string("Bearer ") + auth_token_);
|
||||
std::string("Bearer ") + auth_token);
|
||||
resource_request->headers.SetHeader("X-API-key",
|
||||
BUILDFLAG(BRAVE_SERVICES_KEY));
|
||||
auto simple_url_loader = network::SimpleURLLoader::Create(
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "base/timer/timer.h"
|
||||
#include "base/values.h"
|
||||
#include "brave/components/email_aliases/email_aliases.mojom.h"
|
||||
#include "brave/components/email_aliases/email_aliases_auth.h"
|
||||
#include "components/keyed_service/core/keyed_service.h"
|
||||
#include "mojo/public/cpp/bindings/pending_receiver.h"
|
||||
#include "mojo/public/cpp/bindings/receiver_set.h"
|
||||
@@ -24,6 +25,11 @@
|
||||
#include "net/traffic_annotation/network_traffic_annotation.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
namespace os_crypt_async {
|
||||
class Encryptor;
|
||||
class OSCryptAsync;
|
||||
} // namespace os_crypt_async
|
||||
|
||||
namespace network {
|
||||
class SharedURLLoaderFactory;
|
||||
class SimpleURLLoader;
|
||||
@@ -42,9 +48,13 @@ class EmailAliasesService : public KeyedService,
|
||||
public mojom::EmailAliasesService {
|
||||
public:
|
||||
EmailAliasesService(
|
||||
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
|
||||
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
|
||||
PrefService* pref_service,
|
||||
os_crypt_async::OSCryptAsync* os_crypt_async);
|
||||
~EmailAliasesService() override;
|
||||
|
||||
static void RegisterProfilePrefs(PrefRegistrySimple* registry);
|
||||
|
||||
// KeyedService:
|
||||
// Called when the owning profile context is shutting down. Releases
|
||||
// interface bindings and observers.
|
||||
@@ -88,7 +98,7 @@ class EmailAliasesService : public KeyedService,
|
||||
mojo::PendingReceiver<mojom::EmailAliasesService> receiver);
|
||||
|
||||
// Returns the current auth token for tests. Empty when unauthenticated.
|
||||
std::string GetAuthTokenForTesting() const;
|
||||
std::string GetAuthTokenForTesting();
|
||||
|
||||
// Build the fully-qualified Brave Accounts verification URLs.
|
||||
static GURL GetAccountsServiceVerifyInitURL();
|
||||
@@ -102,6 +112,15 @@ class EmailAliasesService : public KeyedService,
|
||||
using BodyAsStringCallback =
|
||||
base::OnceCallback<void(std::optional<std::string> response_body)>;
|
||||
|
||||
void OnEncryptorReady(os_crypt_async::Encryptor encryptor);
|
||||
|
||||
std::string GetAuthEmail() const;
|
||||
std::string GetAuthToken();
|
||||
|
||||
mojom::AuthenticationStatus GetCurrentStatus();
|
||||
|
||||
void OnAuthChanged();
|
||||
|
||||
// Handles the response to the verify/init request. Parses a verification
|
||||
// token and, if present, proceeds to poll the session endpoint. Invokes
|
||||
// |callback| with an optional error message.
|
||||
@@ -176,15 +195,13 @@ class EmailAliasesService : public KeyedService,
|
||||
// Temporary token returned by verify/init and used to authorize polling.
|
||||
std::string verification_token_;
|
||||
|
||||
// Long-lived token returned by verify/result upon successful authentication.
|
||||
std::string auth_token_;
|
||||
|
||||
// The email address used for the current authentication attempt.
|
||||
std::string auth_email_;
|
||||
std::optional<EmailAliasesAuth> auth_;
|
||||
|
||||
// URL loader factory used to issue network requests to Brave Accounts.
|
||||
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
|
||||
|
||||
const raw_ptr<PrefService> pref_service_ = nullptr;
|
||||
|
||||
// Single SimpleURLLoader instance used for both verify/init and
|
||||
// verify/result requests. Recreated for each new request.
|
||||
std::unique_ptr<network::SimpleURLLoader> verification_simple_url_loader_;
|
||||
|
||||
@@ -20,7 +20,10 @@
|
||||
#include "base/types/expected.h"
|
||||
#include "brave/components/constants/brave_services_key.h"
|
||||
#include "brave/components/email_aliases/features.h"
|
||||
#include "brave/components/email_aliases/test_utils.h"
|
||||
#include "components/grit/brave_components_strings.h"
|
||||
#include "components/os_crypt/async/browser/test_utils.h"
|
||||
#include "components/prefs/testing_pref_service.h"
|
||||
#include "net/base/net_errors.h"
|
||||
#include "net/http/http_status_code.h"
|
||||
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
|
||||
@@ -34,45 +37,22 @@ namespace email_aliases {
|
||||
|
||||
using AuthenticationStatus = email_aliases::mojom::AuthenticationStatus;
|
||||
|
||||
namespace {
|
||||
|
||||
// Test observer for authentication state changes
|
||||
class TestObserver : public email_aliases::mojom::EmailAliasesServiceObserver {
|
||||
public:
|
||||
void OnAuthStateChanged(email_aliases::mojom::AuthStatePtr state) override {
|
||||
last_state = state->status;
|
||||
}
|
||||
bool WaitFor(AuthenticationStatus expected) {
|
||||
return base::test::RunUntil([&]() { return last_state == expected; });
|
||||
}
|
||||
void OnAliasesUpdated(std::vector<email_aliases::mojom::AliasPtr>) override {}
|
||||
AuthenticationStatus last_state = AuthenticationStatus::kUnauthenticated;
|
||||
mojo::Receiver<email_aliases::mojom::EmailAliasesServiceObserver> receiver_{
|
||||
this};
|
||||
void BindReceiver(
|
||||
mojo::PendingReceiver<email_aliases::mojom::EmailAliasesServiceObserver>
|
||||
pending) {
|
||||
receiver_.Bind(std::move(pending));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
class EmailAliasesServiceTest : public ::testing::Test {
|
||||
protected:
|
||||
EmailAliasesServiceTest() {
|
||||
feature_list_.InitAndEnableFeature(email_aliases::features::kEmailAliases);
|
||||
EmailAliasesService::RegisterProfilePrefs(prefs_.registry());
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
os_crypt_ = os_crypt_async::GetTestOSCryptAsyncForTesting();
|
||||
keyed_service_ = std::make_unique<EmailAliasesService>(
|
||||
test_url_loader_factory_.GetSafeWeakWrapper());
|
||||
test_url_loader_factory_.GetSafeWeakWrapper(), &prefs_,
|
||||
os_crypt_.get());
|
||||
|
||||
keyed_service_->BindInterface(service_.BindNewPipeAndPassReceiver());
|
||||
observer_ = std::make_unique<TestObserver>();
|
||||
mojo::PendingRemote<email_aliases::mojom::EmailAliasesServiceObserver>
|
||||
remote;
|
||||
observer_->BindReceiver(remote.InitWithNewPipeAndPassReceiver());
|
||||
service_->AddObserver(std::move(remote));
|
||||
observer_ = email_aliases::test::AuthStateObserver::Setup(
|
||||
keyed_service_.get(), true);
|
||||
}
|
||||
|
||||
// Make authentication request and wait for the response.
|
||||
@@ -135,10 +115,12 @@ class EmailAliasesServiceTest : public ::testing::Test {
|
||||
|
||||
base::test::ScopedFeatureList feature_list_;
|
||||
network::TestURLLoaderFactory test_url_loader_factory_;
|
||||
TestingPrefServiceSimple prefs_;
|
||||
std::unique_ptr<os_crypt_async::OSCryptAsync> os_crypt_;
|
||||
std::unique_ptr<EmailAliasesService> keyed_service_;
|
||||
mojo::Remote<mojom::EmailAliasesService> service_;
|
||||
base::test::TaskEnvironment task_environment_;
|
||||
std::unique_ptr<TestObserver> observer_;
|
||||
std::unique_ptr<test::AuthStateObserver> observer_;
|
||||
};
|
||||
|
||||
TEST_F(EmailAliasesServiceTest, RequestAuthentication_EmptyEmail) {
|
||||
@@ -185,6 +167,57 @@ TEST_F(EmailAliasesServiceTest, RequestAuthentication_Success_MultipleCalls) {
|
||||
EXPECT_TRUE(result3.Get().has_value());
|
||||
}
|
||||
|
||||
TEST_F(EmailAliasesServiceTest, Auth) {
|
||||
EmailAliasesAuth auth(&prefs_, test::GetEncryptor(os_crypt_.get()),
|
||||
base::BindLambdaForTesting([&]() {}));
|
||||
|
||||
auth.SetAuthEmail("test@domain.com");
|
||||
EXPECT_EQ(auth.GetAuthEmail(), "test@domain.com");
|
||||
|
||||
auth.SetAuthToken("token");
|
||||
EXPECT_EQ(auth.CheckAndGetAuthToken(), "token");
|
||||
|
||||
auth.SetAuthEmail({});
|
||||
EXPECT_EQ(auth.GetAuthEmail(), "");
|
||||
EXPECT_EQ(auth.CheckAndGetAuthToken(), "");
|
||||
|
||||
auth.SetAuthEmail("test@domain.com");
|
||||
auth.SetAuthToken("token");
|
||||
|
||||
{
|
||||
// set the same email
|
||||
prefs_.SetDict(
|
||||
prefs::kAuth,
|
||||
base::Value::Dict()
|
||||
.Set("email", "test@domain.com")
|
||||
.Set("token", *prefs_.GetDict(prefs::kAuth).FindString("token")));
|
||||
EXPECT_EQ(auth.GetAuthEmail(), "test@domain.com");
|
||||
EXPECT_EQ(auth.CheckAndGetAuthToken(), "token");
|
||||
}
|
||||
|
||||
{
|
||||
// set new email
|
||||
prefs_.SetDict(
|
||||
prefs::kAuth,
|
||||
base::Value::Dict()
|
||||
.Set("email", "new@domain.com")
|
||||
.Set("token", *prefs_.GetDict(prefs::kAuth).FindString("token")));
|
||||
EXPECT_EQ(auth.GetAuthEmail(), "new@domain.com");
|
||||
EXPECT_EQ(auth.CheckAndGetAuthToken(), ""); // token reset
|
||||
}
|
||||
{
|
||||
// token becomes invalid
|
||||
auth.SetAuthToken("token");
|
||||
EXPECT_EQ(auth.GetAuthEmail(), "new@domain.com");
|
||||
EXPECT_EQ(auth.CheckAndGetAuthToken(), "token");
|
||||
prefs_.SetDict(prefs::kAuth, base::Value::Dict()
|
||||
.Set("email", "new@domain.com")
|
||||
.Set("token", "invalid"));
|
||||
EXPECT_EQ(auth.GetAuthEmail(), "new@domain.com");
|
||||
EXPECT_EQ(auth.CheckAndGetAuthToken(), ""); // token reset
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(EmailAliasesServiceTest, RequestSession_Success) {
|
||||
RunRequestSessionTest({"{\"authToken\":\"auth456\", \"verified\":true, "
|
||||
"\"service\":\"email-aliases\"}"},
|
||||
@@ -192,6 +225,35 @@ TEST_F(EmailAliasesServiceTest, RequestSession_Success) {
|
||||
EXPECT_EQ(keyed_service_->GetAuthTokenForTesting(), "auth456");
|
||||
}
|
||||
|
||||
TEST_F(EmailAliasesServiceTest, SessionPreserved) {
|
||||
RunRequestSessionTest({"{\"authToken\":\"auth456\", \"verified\":true, "
|
||||
"\"service\":\"email-aliases\"}"},
|
||||
AuthenticationStatus::kAuthenticated);
|
||||
// Simulate next start.
|
||||
keyed_service_ = std::make_unique<EmailAliasesService>(
|
||||
test_url_loader_factory_.GetSafeWeakWrapper(), &prefs_, os_crypt_.get());
|
||||
{
|
||||
auto initialized =
|
||||
test::AuthStateObserver::Setup(keyed_service_.get(), true);
|
||||
}
|
||||
EXPECT_TRUE(keyed_service_->IsAuthenticated());
|
||||
EXPECT_EQ("auth456", keyed_service_->GetAuthTokenForTesting());
|
||||
|
||||
// New Observer is notified.
|
||||
auto observer = test::AuthStateObserver::Setup(keyed_service_.get());
|
||||
EXPECT_TRUE(observer->WaitFor(AuthenticationStatus::kAuthenticated));
|
||||
|
||||
// Prefs contain values.
|
||||
EmailAliasesAuth auth(&prefs_, test::GetEncryptor(os_crypt_.get()));
|
||||
EXPECT_EQ("test@example.com", auth.GetAuthEmail());
|
||||
EXPECT_EQ("auth456", auth.CheckAndGetAuthToken());
|
||||
|
||||
const auto& pref_value = prefs_.GetDict(prefs::kAuth);
|
||||
EXPECT_EQ("test@example.com", *pref_value.FindString("email"));
|
||||
EXPECT_FALSE(pref_value.FindString("token")->empty()); // token saved
|
||||
EXPECT_NE("auth456", *pref_value.FindString("token")); // token encrypted
|
||||
}
|
||||
|
||||
TEST_F(EmailAliasesServiceTest, RequestSession_InvalidJson) {
|
||||
RunRequestSessionTest({"not a json"}, AuthenticationStatus::kAuthenticating);
|
||||
}
|
||||
@@ -207,7 +269,7 @@ TEST_F(EmailAliasesServiceTest, RequestSession_RetryOnMissingAuthToken) {
|
||||
email_aliases::mojom::AuthenticationStatus::kAuthenticated);
|
||||
EXPECT_EQ(keyed_service_->GetAuthTokenForTesting(), "auth456");
|
||||
// unauthenticated, authenticating, authenticated
|
||||
EXPECT_EQ(observer_->last_state,
|
||||
EXPECT_EQ(observer_->GetStatus().status,
|
||||
email_aliases::mojom::AuthenticationStatus::kAuthenticated);
|
||||
}
|
||||
|
||||
@@ -251,13 +313,12 @@ class EmailAliasesServiceTimingTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
feature_list_.InitAndEnableFeature(email_aliases::features::kEmailAliases);
|
||||
EmailAliasesService::RegisterProfilePrefs(prefs_.registry());
|
||||
os_crypt_ = os_crypt_async::GetTestOSCryptAsyncForTesting();
|
||||
service_ = std::make_unique<EmailAliasesService>(
|
||||
url_loader_factory_.GetSafeWeakWrapper());
|
||||
observer_ = std::make_unique<TestObserver>();
|
||||
mojo::PendingRemote<email_aliases::mojom::EmailAliasesServiceObserver>
|
||||
remote;
|
||||
observer_->BindReceiver(remote.InitWithNewPipeAndPassReceiver());
|
||||
service_->AddObserver(std::move(remote));
|
||||
url_loader_factory_.GetSafeWeakWrapper(), &prefs_, os_crypt_.get());
|
||||
observer_ =
|
||||
email_aliases::test::AuthStateObserver::Setup(service_.get(), true);
|
||||
}
|
||||
|
||||
// Starts auth and captures verify/result request times via interceptor.
|
||||
@@ -290,8 +351,10 @@ class EmailAliasesServiceTimingTest : public ::testing::Test {
|
||||
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
|
||||
base::test::ScopedFeatureList feature_list_;
|
||||
network::TestURLLoaderFactory url_loader_factory_;
|
||||
std::unique_ptr<os_crypt_async::OSCryptAsync> os_crypt_;
|
||||
TestingPrefServiceSimple prefs_;
|
||||
std::unique_ptr<EmailAliasesService> service_;
|
||||
std::unique_ptr<TestObserver> observer_;
|
||||
std::unique_ptr<test::AuthStateObserver> observer_;
|
||||
std::vector<base::TimeTicks> verify_result_request_times_;
|
||||
};
|
||||
|
||||
@@ -335,7 +398,7 @@ TEST_F(EmailAliasesServiceTimingTest, VerifyResult_StopsAfterMaxDuration) {
|
||||
|
||||
EXPECT_EQ(verify_result_request_times_.size(), expected_requests);
|
||||
|
||||
EXPECT_EQ(observer_->last_state,
|
||||
EXPECT_EQ(observer_->GetStatus().status,
|
||||
email_aliases::mojom::AuthenticationStatus::kUnauthenticated);
|
||||
}
|
||||
|
||||
@@ -441,10 +504,25 @@ class EmailAliasesAPITest : public ::testing::Test {
|
||||
return result_out;
|
||||
}
|
||||
|
||||
void SetupAuth(bool auth = true) {
|
||||
EmailAliasesAuth settings(&prefs_, test::GetEncryptor(os_crypt_.get()));
|
||||
settings.SetAuthEmail("test@example.com");
|
||||
if (auth) {
|
||||
settings.SetAuthToken("token456");
|
||||
} else {
|
||||
settings.SetAuthEmail({});
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void SetUp() override {
|
||||
EmailAliasesService::RegisterProfilePrefs(prefs_.registry());
|
||||
os_crypt_ = os_crypt_async::GetTestOSCryptAsyncForTesting();
|
||||
SetupAuth();
|
||||
|
||||
service_ = std::make_unique<EmailAliasesService>(
|
||||
url_loader_factory_.GetSafeWeakWrapper());
|
||||
url_loader_factory_.GetSafeWeakWrapper(), &prefs_, os_crypt_.get());
|
||||
email_aliases::test::AuthStateObserver::Setup(service_.get(), true);
|
||||
|
||||
mojo::PendingRemote<mojom::EmailAliasesServiceObserver> remote;
|
||||
observer_.BindReceiver(remote.InitWithNewPipeAndPassReceiver());
|
||||
@@ -454,6 +532,8 @@ class EmailAliasesAPITest : public ::testing::Test {
|
||||
base::test::ScopedFeatureList feature_list_{features::kEmailAliases};
|
||||
base::test::TaskEnvironment task_environment_;
|
||||
network::TestURLLoaderFactory url_loader_factory_;
|
||||
std::unique_ptr<os_crypt_async::OSCryptAsync> os_crypt_;
|
||||
TestingPrefServiceSimple prefs_;
|
||||
std::unique_ptr<EmailAliasesService> service_;
|
||||
AliasObserver observer_;
|
||||
};
|
||||
|
||||
@@ -62,6 +62,7 @@ export const ListIntroduction = ({
|
||||
</Description>
|
||||
</Col>
|
||||
<Button
|
||||
id='create-new-item-button'
|
||||
isDisabled={aliasesCount >= MAX_ALIASES}
|
||||
kind='filled'
|
||||
size='small'
|
||||
|
||||
@@ -83,6 +83,7 @@ const BeforeSendingEmailForm = ({
|
||||
value={email}
|
||||
/>
|
||||
<Button
|
||||
id='get-login-link-button'
|
||||
onClick={requestAuthentication}
|
||||
type='submit'
|
||||
kind='filled'
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) 2025 The Brave Authors. All rights reserved.
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include "brave/components/email_aliases/test_utils.h"
|
||||
|
||||
#include "base/test/run_until.h"
|
||||
#include "base/test/test_future.h"
|
||||
#include "components/os_crypt/async/browser/os_crypt_async.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace email_aliases::test {
|
||||
|
||||
AuthStateObserver::AuthStateObserver(
|
||||
mojo::PendingReceiver<email_aliases::mojom::EmailAliasesServiceObserver>
|
||||
pending) {
|
||||
receiver_.Bind(std::move(pending));
|
||||
}
|
||||
|
||||
AuthStateObserver::~AuthStateObserver() = default;
|
||||
|
||||
// static
|
||||
std::unique_ptr<AuthStateObserver> AuthStateObserver::Setup(
|
||||
EmailAliasesService* service,
|
||||
bool wait_initialized) {
|
||||
mojo::PendingRemote<email_aliases::mojom::EmailAliasesServiceObserver> remote;
|
||||
auto observer = base::WrapUnique(
|
||||
new AuthStateObserver(remote.InitWithNewPipeAndPassReceiver()));
|
||||
service->AddObserver(std::move(remote));
|
||||
|
||||
if (wait_initialized) {
|
||||
EXPECT_TRUE(observer->WaitInitialized());
|
||||
}
|
||||
return observer;
|
||||
}
|
||||
|
||||
const mojom::AuthState& AuthStateObserver::GetStatus() const {
|
||||
return *last_status_;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool AuthStateObserver::WaitFor(
|
||||
mojom::AuthenticationStatus status) {
|
||||
return base::test::RunUntil(
|
||||
[status, this]() { return GetStatus().status == status; });
|
||||
}
|
||||
|
||||
[[nodiscard]] bool AuthStateObserver::WaitInitialized() {
|
||||
return base::test::RunUntil([this]() {
|
||||
return GetStatus().status != mojom::AuthenticationStatus::kStartup;
|
||||
});
|
||||
}
|
||||
|
||||
void AuthStateObserver::OnAuthStateChanged(
|
||||
email_aliases::mojom::AuthStatePtr state) {
|
||||
last_status_ = std::move(state);
|
||||
}
|
||||
|
||||
void AuthStateObserver::OnAliasesUpdated(
|
||||
std::vector<email_aliases::mojom::AliasPtr>) {}
|
||||
|
||||
os_crypt_async::Encryptor GetEncryptor(os_crypt_async::OSCryptAsync* os_crypt) {
|
||||
base::test::TestFuture<os_crypt_async::Encryptor> result;
|
||||
os_crypt->GetInstance(result.GetCallback());
|
||||
return result.Take();
|
||||
}
|
||||
|
||||
} // namespace email_aliases::test
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright (c) 2025 The Brave Authors. All rights reserved.
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef BRAVE_COMPONENTS_EMAIL_ALIASES_TEST_UTILS_H_
|
||||
#define BRAVE_COMPONENTS_EMAIL_ALIASES_TEST_UTILS_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "brave/components/email_aliases/email_aliases.mojom.h"
|
||||
#include "brave/components/email_aliases/email_aliases_auth.h"
|
||||
#include "brave/components/email_aliases/email_aliases_service.h"
|
||||
#include "mojo/public/cpp/bindings/pending_receiver.h"
|
||||
#include "mojo/public/cpp/bindings/receiver.h"
|
||||
|
||||
namespace email_aliases::test {
|
||||
|
||||
// Test observer for authentication state changes
|
||||
class AuthStateObserver
|
||||
: public email_aliases::mojom::EmailAliasesServiceObserver {
|
||||
public:
|
||||
~AuthStateObserver() override;
|
||||
|
||||
static std::unique_ptr<AuthStateObserver> Setup(
|
||||
EmailAliasesService* service,
|
||||
bool wait_initialized = false);
|
||||
|
||||
const mojom::AuthState& GetStatus() const;
|
||||
[[nodiscard]] bool WaitFor(mojom::AuthenticationStatus status);
|
||||
[[nodiscard]] bool WaitInitialized();
|
||||
|
||||
private:
|
||||
explicit AuthStateObserver(
|
||||
mojo::PendingReceiver<email_aliases::mojom::EmailAliasesServiceObserver>
|
||||
pending);
|
||||
|
||||
void OnAuthStateChanged(email_aliases::mojom::AuthStatePtr state) override;
|
||||
void OnAliasesUpdated(std::vector<email_aliases::mojom::AliasPtr>) override;
|
||||
|
||||
mojom::AuthStatePtr last_status_ =
|
||||
mojom::AuthState::New(mojom::AuthenticationStatus::kStartup,
|
||||
std::string(),
|
||||
std::nullopt);
|
||||
|
||||
mojo::Receiver<email_aliases::mojom::EmailAliasesServiceObserver> receiver_{
|
||||
this};
|
||||
};
|
||||
|
||||
os_crypt_async::Encryptor GetEncryptor(os_crypt_async::OSCryptAsync* os_crypt);
|
||||
|
||||
} // namespace email_aliases::test
|
||||
|
||||
#endif // BRAVE_COMPONENTS_EMAIL_ALIASES_TEST_UTILS_H_
|
||||
@@ -10,6 +10,7 @@ brave_components_os_crypt_visibility = [
|
||||
"//brave/components/brave_account",
|
||||
"//brave/components/brave_rewards/content",
|
||||
"//brave/components/brave_sync:prefs",
|
||||
"//brave/components/email_aliases:auth",
|
||||
"//brave/components/sync/service:unit_tests",
|
||||
"//chrome/utility",
|
||||
"//components/os_crypt/sync",
|
||||
|
||||
Reference in New Issue
Block a user