Skip to content

Commit

Permalink
Adds IsConnectedToCloudAuthenticator method to the passwordsPrivate API.
Browse files Browse the repository at this point in the history
This methods enables the Password Manager settings UI to check whether the chrome client is connected to the Cloud Authenticator and if the disconnect UI should be displayed (offered).

Bug: 338959659
Change-Id: I8430f24a9982f17bc7e4c954b6239af610d74b3f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5522536
Reviewed-by: Adam Langley <agl@chromium.org>
Reviewed-by: Demetrios Papadopoulos <dpapad@chromium.org>
Reviewed-by: Viktor Semeniuk <vsemeniuk@google.com>
Reviewed-by: Tim <tjudkins@chromium.org>
Commit-Queue: Markus Heintz <markusheintz@google.com>
Reviewed-by: Justin Lulejian <jlulejian@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1298316}
  • Loading branch information
Markus Heintz authored and Chromium LUCI CQ committed May 8, 2024
1 parent 8ed8908 commit c0a6328
Show file tree
Hide file tree
Showing 18 changed files with 110 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -697,4 +697,14 @@ void PasswordsPrivateDisconnectCloudAuthenticatorFunction::
Respond(WithArguments(success));
}

// PasswordsPrivateIsConnectedToCloudAuthenticatorFunction
ResponseAction PasswordsPrivateIsConnectedToCloudAuthenticatorFunction::Run() {
if (auto delegate = GetDelegate(browser_context())) {
return RespondNow(WithArguments(
delegate->IsConnectedToCloudAuthenticator(GetSenderWebContents())));
}

return RespondNow(Error(kNoDelegateError));
}

} // namespace extensions
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,19 @@ class PasswordsPrivateDisconnectCloudAuthenticatorFunction
void OnDisconnectCloudAuthenticatorCompleted(bool success);
};

class PasswordsPrivateIsConnectedToCloudAuthenticatorFunction
: public ExtensionFunction {
public:
DECLARE_EXTENSION_FUNCTION("passwordsPrivate.isConnectedToCloudAuthenticator",
PASSWORDSPRIVATE_ISCONNECTEDTOCLOUDAUTHENTICATOR)

protected:
~PasswordsPrivateIsConnectedToCloudAuthenticatorFunction() override = default;

// ExtensionFunction overrides.
ResponseAction Run() override;
};

} // namespace extensions

#endif // CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_PASSWORDS_PRIVATE_API_H_
Original file line number Diff line number Diff line change
Expand Up @@ -453,4 +453,9 @@ IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, DisconnectCloudAuthenticator) {
EXPECT_TRUE(get_disconnect_cloud_authenticator_called());
}

IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest,
IsConnectedToCloudAuthenticator) {
EXPECT_TRUE(RunPasswordsSubtest("isConnectedToCloudAuthenticator"));
}

} // namespace extensions
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,9 @@ class PasswordsPrivateDelegate
content::WebContents* web_contents,
base::OnceCallback<void(bool)> success_callback) = 0;

virtual bool IsConnectedToCloudAuthenticator(
content::WebContents* web_contents) = 0;

virtual base::WeakPtr<PasswordsPrivateDelegate> AsWeakPtr() = 0;

protected:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -967,6 +967,19 @@ void PasswordsPrivateDelegateImpl::DisconnectCloudAuthenticator(
}
}

bool PasswordsPrivateDelegateImpl::IsConnectedToCloudAuthenticator(
content::WebContents* web_contents) {
EnclaveManager* enclave_manager =
EnclaveManagerFactory::GetAsEnclaveManagerForProfile(
Profile::FromBrowserContext(web_contents->GetBrowserContext()));

if (!enclave_manager) {
return false;
}

return enclave_manager->is_registered();
}

base::WeakPtr<PasswordsPrivateDelegate>
PasswordsPrivateDelegateImpl::AsWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ class PasswordsPrivateDelegateImpl
void DisconnectCloudAuthenticator(
content::WebContents* web_contents,
base::OnceCallback<void(bool)> success_callback) override;
bool IsConnectedToCloudAuthenticator(
content::WebContents* web_contents) override;

base::WeakPtr<PasswordsPrivateDelegate> AsWeakPtr() override;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ class MockEnclaveManager : public EnclaveManagerInterface {
MockEnclaveManager(const EnclaveManager&&) = delete;

MOCK_METHOD(void, Unenroll, (Callback), (override));
MOCK_METHOD(bool, is_registered, (), (const override));
};

// static
Expand Down Expand Up @@ -1909,6 +1910,17 @@ TEST_F(PasswordsPrivateDelegateImplTest, DisconnectCloudAuthenticator) {
base::BindLambdaForTesting([](bool success) { EXPECT_TRUE(success); }));
}

TEST_F(PasswordsPrivateDelegateImplTest, IsConnecetdToCloudAuthenticator) {
auto delegate = CreateDelegate();
std::unique_ptr<content::WebContents> web_contents = CreateWebContents();

MockEnclaveManager* enclave_manager_mock = static_cast<MockEnclaveManager*>(
EnclaveManagerFactory::GetForProfile(profile()));
EXPECT_CALL(*enclave_manager_mock, is_registered).Times(1);

delegate->IsConnectedToCloudAuthenticator(web_contents.get());
}

#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS)
class PasswordsPrivateDelegateImplMockTaskEnvironmentTest
: public testing::Test {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,11 @@ void TestPasswordsPrivateDelegate::DisconnectCloudAuthenticator(
std::move(success_callback).Run(false);
}

bool TestPasswordsPrivateDelegate::IsConnectedToCloudAuthenticator(
content::WebContents* web_contents) {
return false;
}

base::WeakPtr<PasswordsPrivateDelegate>
TestPasswordsPrivateDelegate::AsWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,12 @@ class TestPasswordsPrivateDelegate : public PasswordsPrivateDelegate {
void ChangePasswordManagerPin(
content::WebContents* web_contents,
base::OnceCallback<void(bool)> success_callback) override;
bool IsPasswordManagerPinAvailable(
content::WebContents* web_contents) override;
void DisconnectCloudAuthenticator(
content::WebContents* web_contents,
base::OnceCallback<void(bool)> success_callback) override;
bool IsPasswordManagerPinAvailable(
bool IsConnectedToCloudAuthenticator(
content::WebContents* web_contents) override;

base::WeakPtr<PasswordsPrivateDelegate> AsWeakPtr() override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -384,14 +384,20 @@ export interface PasswordManagerProxy {
/** Starts the flow for changing Password Manager PIN. */
changePasswordManagerPin(): Promise<boolean>;

/** Checks whether changing the Password Manager PIN is possible. */
isPasswordManagerPinAvailable(): Promise<boolean>;

/**
* Starts the flow for disconnecting the Cloud Authenticator
* (Passkeys Enclave).
*/
disconnectCloudAuthenticator(): Promise<boolean>;

/** Checks whether changing the Password Manager PIN is possible. */
isPasswordManagerPinAvailable(): Promise<boolean>;
/**
* Checks whether the Chrome client is connected to the Cloud Authenticator
* (Passkeys Enclave).
*/
isConnectedToCloudAuthenticator(): Promise<boolean>;
}

/**
Expand Down Expand Up @@ -625,12 +631,16 @@ export class PasswordManagerImpl implements PasswordManagerProxy {
return chrome.passwordsPrivate.changePasswordManagerPin();
}

isPasswordManagerPinAvailable() {
return chrome.passwordsPrivate.isPasswordManagerPinAvailable();
}

disconnectCloudAuthenticator() {
return chrome.passwordsPrivate.disconnectCloudAuthenticator();
}

isPasswordManagerPinAvailable() {
return chrome.passwordsPrivate.isPasswordManagerPinAvailable();
isConnectedToCloudAuthenticator() {
return chrome.passwordsPrivate.isConnectedToCloudAuthenticator();
}

static getInstance(): PasswordManagerProxy {
Expand Down
2 changes: 1 addition & 1 deletion chrome/browser/webauthn/enclave_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class EnclaveManager : public EnclaveManagerInterface {
// else the loading failed and an empty state is being used.)
bool is_loaded() const;
// Returns true if the current user has been registered with the enclave.
bool is_registered() const;
bool is_registered() const override;
// Returns true if `StoreKeys` has been called and thus `AddDeviceToAccount`
// or `AddDeviceAndPINToAccount` can be called.
bool has_pending_keys() const;
Expand Down
3 changes: 3 additions & 0 deletions chrome/browser/webauthn/enclave_manager_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ class EnclaveManagerInterface : public KeyedService {
EnclaveManagerInterface& operator=(const EnclaveManagerInterface&) = delete;
~EnclaveManagerInterface() override = default;

// Returns true if the current user has been registered with the enclave.
virtual bool is_registered() const = 0;

// Send a request to the enclave to delete the registration for the current
// user, erase local keys, and erase local state for the user. Safe to call in
// any state and is a no-op if no registration exists.
Expand Down
6 changes: 6 additions & 0 deletions chrome/common/extensions/api/passwords_private.idl
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ namespace passwordsPrivate {
callback PasswordManagerPinAvailableCallback = void(boolean available);
callback PasswordManagerPinChangedCallback = void(boolean success);
callback DisconnectCloudAuthenticatorCallback = void(boolean success);
callback IsConnectedToCloudAuthenticatorCallback = void(boolean connected);

interface Functions {
// Function that logs that the Passwords page was accessed from the Chrome
Expand Down Expand Up @@ -564,6 +565,11 @@ namespace passwordsPrivate {
// Disconnects the Chrome client from the cloud authenticator.
static void disconnectCloudAuthenticator(
DisconnectCloudAuthenticatorCallback callback);

// Checks whether the Chrome client is registered with/connected to
// the cloud authenticator.
static void isConnectedToCloudAuthenticator(
IsConnectedToCloudAuthenticatorCallback callback);
};

interface Events {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,15 @@ var availableTests = [
chrome.test.assertNoLastError();
chrome.test.succeed();
});
},

function isConnectedToCloudAuthenticator() {
var callback = function(connected) {
chrome.test.assertFalse(connected);
chrome.test.succeed();
};

chrome.passwordsPrivate.isConnectedToCloudAuthenticator(callback);
}
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export class TestPasswordManagerProxy extends TestBrowserProxy implements
isPasswordManagerPinAvailable: boolean,
changePasswordManagerPinSuccesful: boolean|null,
disconnectCloudAuthenticatorSuccessful: boolean|null,
isConnectedToCloudAuthenticator: boolean,
};

listeners: {
Expand Down Expand Up @@ -72,6 +73,7 @@ export class TestPasswordManagerProxy extends TestBrowserProxy implements
'getUrlCollection',
'importPasswords',
'isAccountStoreDefault',
'isConnectedToCloudAuthenticator',
'isOptedInForAccountStorage',
'isPasswordManagerPinAvailable',
'movePasswordsToAccount',
Expand Down Expand Up @@ -108,6 +110,7 @@ export class TestPasswordManagerProxy extends TestBrowserProxy implements
isPasswordManagerPinAvailable: false,
changePasswordManagerPinSuccesful: null,
disconnectCloudAuthenticatorSuccessful: null,
isConnectedToCloudAuthenticator: false,
};

// Holds listeners so they can be called when needed.
Expand Down Expand Up @@ -406,4 +409,9 @@ export class TestPasswordManagerProxy extends TestBrowserProxy implements
}
return Promise.reject(new Error());
}

isConnectedToCloudAuthenticator(): Promise<boolean> {
this.methodCalled('isConnectedToCloudAuthenticator');
return Promise.resolve(this.data.isConnectedToCloudAuthenticator);
}
}
1 change: 1 addition & 0 deletions extensions/browser/extension_function_histogram_value.h
Original file line number Diff line number Diff line change
Expand Up @@ -1947,6 +1947,7 @@ enum HistogramValue {
WEBSTOREPRIVATE_GETFULLCHROMEVERSION = 1885,
ODFSCONFIGPRIVATE_ISFILESYSTEMPROVIDERCONTENTCACHEENABLED = 1886,
PASSWORDSPRIVATE_DISCONNECTCLOUDAUTHENTICATOR = 1887,
PASSWORDSPRIVATE_ISCONNECTEDTOCLOUDAUTHENTICATOR = 1888,
// Last entry: Add new entries above, then run:
// tools/metrics/histograms/update_extension_histograms.py
ENUM_BOUNDARY
Expand Down
1 change: 1 addition & 0 deletions tools/metrics/histograms/metadata/extensions/enums.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2742,6 +2742,7 @@ Called by update_extension_histograms.py.-->
<int value="1886"
label="ODFSCONFIGPRIVATE_ISFILESYSTEMPROVIDERCONTENTCACHEENABLED"/>
<int value="1887" label="PASSWORDSPRIVATE_DISCONNECTCLOUDAUTHENTICATOR"/>
<int value="1888" label="PASSWORDSPRIVATE_ISCONNECTEDTOCLOUDAUTHENTICATOR"/>
</enum>

<enum name="ExtensionInstallationCrxInstallError">
Expand Down
1 change: 1 addition & 0 deletions tools/typescript/definitions/passwords_private.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ declare global {
export function changePasswordManagerPin(): Promise<boolean>;
export function isPasswordManagerPinAvailable(): Promise<boolean>;
export function disconnectCloudAuthenticator(): Promise<boolean>;
export function isConnectedToCloudAuthenticator(): Promise<boolean>;

export const onSavedPasswordsListChanged:
ChromeEvent<(entries: PasswordUiEntry[]) => void>;
Expand Down

0 comments on commit c0a6328

Please sign in to comment.