Commit 60fc679e authored by Maxim Kolosovskiy's avatar Maxim Kolosovskiy Committed by Commit Bot

[Password Manager] Pressing "Change password" buttons launches CCT

Bug: 1086114, 1092444
Change-Id: I2906d756d0090734b23a711da27f497ec7296937
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2336595
Commit-Queue: Maxim Kolosovskiy  <kolos@chromium.org>
Reviewed-by: default avatarLukasz Suder <lsuder@chromium.org>
Reviewed-by: default avatarFriedrich [CET] <fhorschig@chromium.org>
Cr-Commit-Position: refs/heads/master@{#795390}
parent ced56e1f
...@@ -70,6 +70,7 @@ android_library("internal_java") { ...@@ -70,6 +70,7 @@ android_library("internal_java") {
"//third_party/android_deps:androidx_lifecycle_lifecycle_common_java", "//third_party/android_deps:androidx_lifecycle_lifecycle_common_java",
"//third_party/android_deps:androidx_preference_preference_java", "//third_party/android_deps:androidx_preference_preference_java",
"//third_party/android_deps:androidx_recyclerview_recyclerview_java", "//third_party/android_deps:androidx_recyclerview_recyclerview_java",
"//third_party/android_sdk/androidx_browser:androidx_browser_java",
"//ui/android:ui_full_java", "//ui/android:ui_full_java",
_public_target, _public_target,
] ]
......
...@@ -4,13 +4,21 @@ ...@@ -4,13 +4,21 @@
package org.chromium.chrome.browser.password_check; package org.chromium.chrome.browser.password_check;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.provider.Browser;
import android.view.MenuItem; import android.view.MenuItem;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.browser.customtabs.CustomTabsIntent;
import androidx.lifecycle.Lifecycle; import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent; import androidx.lifecycle.OnLifecycleEvent;
import org.chromium.base.IntentUtils;
import org.chromium.chrome.browser.IntentHandler;
import org.chromium.chrome.browser.LaunchIntentDispatcher;
import org.chromium.chrome.browser.help.HelpAndFeedback; import org.chromium.chrome.browser.help.HelpAndFeedback;
import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModel;
...@@ -21,8 +29,18 @@ import org.chromium.ui.modelutil.PropertyModelChangeProcessor; ...@@ -21,8 +29,18 @@ import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
* of the leaked password. * of the leaked password.
*/ */
class PasswordCheckCoordinator implements PasswordCheckComponentUi, LifecycleObserver { class PasswordCheckCoordinator implements PasswordCheckComponentUi, LifecycleObserver {
private static final String WELL_KNOWN_URL_PATH = "/.well-known/change-password";
private static final String AUTOFILL_ASSISTANT_PACKAGE =
"org.chromium.chrome.browser.autofill_assistant.";
private static final String AUTOFILL_ASSISTANT_ENABLED_KEY =
AUTOFILL_ASSISTANT_PACKAGE + "ENABLED";
private static final String PASSWORD_CHANGE_USERNAME_PARAMETER = "PASSWORD_CHANGE_USERNAME";
private static final String INTENT_PARAMETER = "INTENT";
private static final String INTENT = "PASSWORD_CHANGE";
private final PasswordCheckFragmentView mFragmentView; private final PasswordCheckFragmentView mFragmentView;
private final PasswordCheckMediator mMediator = new PasswordCheckMediator(); private final PasswordCheckMediator mMediator = new PasswordCheckMediator(
this::launchCctWithChangePasswordUrl, this::launchCctWithScript);
private PropertyModel mModel; private PropertyModel mModel;
/** /**
...@@ -95,4 +113,56 @@ class PasswordCheckCoordinator implements PasswordCheckComponentUi, LifecycleObs ...@@ -95,4 +113,56 @@ class PasswordCheckCoordinator implements PasswordCheckComponentUi, LifecycleObs
PropertyModelChangeProcessor.create( PropertyModelChangeProcessor.create(
model, view, PasswordCheckViewBinder::bindPasswordCheckView); model, view, PasswordCheckViewBinder::bindPasswordCheckView);
} }
/**
* Launches a CCT that points to the change password form or home page of |origin|.
* @param origin Origin of the site to be opened in a CCT.
*/
private void launchCctWithChangePasswordUrl(String origin) {
// TODO(crbug.com/1092444): Handle the case when an app should be opened. Consider to set
// |browser_fallback_url|, it is used in case of error while opening a CCT.
Intent intent = buildIntent(origin + WELL_KNOWN_URL_PATH);
IntentUtils.safeStartActivity(mFragmentView.getActivity(), intent);
}
/**
* Launches a CCT that starts a password change script for a {@link CompromisedCredential}.
* @param credential A {@link CompromisedCredential} to be changed with a script.
*/
private void launchCctWithScript(CompromisedCredential credential) {
Intent intent = buildIntent(credential.getOriginUrl());
populateAutofillAssistantExtras(intent, credential.getUsername());
IntentUtils.safeStartActivity(mFragmentView.getActivity(), intent);
}
/**
* Builds an intent to launch a CCT.
* @param initialUrl Initial URL to launch a CCT.
* @return {@link Intent} for CCT.
*/
private Intent buildIntent(String initialUrl) {
final Activity activity = mFragmentView.getActivity();
CustomTabsIntent customTabIntent =
new CustomTabsIntent.Builder().setShowTitle(true).build();
customTabIntent.intent.setData(Uri.parse(initialUrl));
Intent intent = LaunchIntentDispatcher.createCustomTabActivityIntent(
activity, customTabIntent.intent);
intent.setPackage(activity.getPackageName());
intent.putExtra(Browser.EXTRA_APPLICATION_ID, activity.getPackageName());
IntentHandler.addTrustedIntentExtras(intent);
return intent;
}
/**
* Populates intent extras for an Autofill Assistant script.
* @param intent An {@link Intent} to be populated.
* @param username A username for a password change script. One of extras to put.
*/
private void populateAutofillAssistantExtras(Intent intent, String username) {
intent.putExtra(AUTOFILL_ASSISTANT_ENABLED_KEY, /* value= */ true);
intent.putExtra(AUTOFILL_ASSISTANT_PACKAGE + PASSWORD_CHANGE_USERNAME_PARAMETER, username);
intent.putExtra(AUTOFILL_ASSISTANT_PACKAGE + INTENT_PARAMETER, INTENT);
// TODO(crbug.com/1086114): Also add the following parameters when server side changes is
// ready: CALLER, SOURCE. That would be useful for metrics.
}
} }
...@@ -9,6 +9,7 @@ import static org.chromium.chrome.browser.password_check.PasswordCheckProperties ...@@ -9,6 +9,7 @@ import static org.chromium.chrome.browser.password_check.PasswordCheckProperties
import static org.chromium.chrome.browser.password_check.PasswordCheckProperties.HeaderProperties.CHECK_STATUS; import static org.chromium.chrome.browser.password_check.PasswordCheckProperties.HeaderProperties.CHECK_STATUS;
import static org.chromium.chrome.browser.password_check.PasswordCheckProperties.ITEMS; import static org.chromium.chrome.browser.password_check.PasswordCheckProperties.ITEMS;
import org.chromium.base.Consumer;
import org.chromium.ui.modelutil.ListModel; import org.chromium.ui.modelutil.ListModel;
import org.chromium.ui.modelutil.MVCListAdapter.ListItem; import org.chromium.ui.modelutil.MVCListAdapter.ListItem;
import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModel;
...@@ -21,6 +22,14 @@ class PasswordCheckMediator ...@@ -21,6 +22,14 @@ class PasswordCheckMediator
implements PasswordCheckCoordinator.CredentialEventHandler, PasswordCheck.Observer { implements PasswordCheckCoordinator.CredentialEventHandler, PasswordCheck.Observer {
private PropertyModel mModel; private PropertyModel mModel;
private PasswordCheckComponentUi.Delegate mDelegate; private PasswordCheckComponentUi.Delegate mDelegate;
private final Consumer<String> mLaunchCctWithChangePasswordUrl;
private final Consumer<CompromisedCredential> mLaunchCctWithScript;
PasswordCheckMediator(Consumer<String> launchCctWithChangePasswordUrl,
Consumer<CompromisedCredential> launchCctWithScript) {
this.mLaunchCctWithChangePasswordUrl = launchCctWithChangePasswordUrl;
this.mLaunchCctWithScript = launchCctWithScript;
}
void initialize(PropertyModel model, PasswordCheckComponentUi.Delegate delegate) { void initialize(PropertyModel model, PasswordCheckComponentUi.Delegate delegate) {
mModel = model; mModel = model;
...@@ -96,12 +105,13 @@ class PasswordCheckMediator ...@@ -96,12 +105,13 @@ class PasswordCheckMediator
@Override @Override
public void onChangePasswordButtonClick(CompromisedCredential credential) { public void onChangePasswordButtonClick(CompromisedCredential credential) {
// TODO(crbug.com/1092444): Implement the action for the button. mLaunchCctWithChangePasswordUrl.accept(credential.getOriginUrl());
} }
@Override @Override
public void onChangePasswordWithScriptButtonClick(CompromisedCredential credential) { public void onChangePasswordWithScriptButtonClick(CompromisedCredential credential) {
// TODO(crbug.com/1086109): Implement the action for the button. assert credential.hasScript();
mLaunchCctWithScript.accept(credential);
} }
private PasswordCheck getPasswordCheck() { private PasswordCheck getPasswordCheck() {
......
...@@ -26,6 +26,7 @@ import org.junit.runner.RunWith; ...@@ -26,6 +26,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.chromium.base.Consumer;
import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.password_check.PasswordCheckProperties.ItemType; import org.chromium.chrome.browser.password_check.PasswordCheckProperties.ItemType;
...@@ -53,6 +54,10 @@ public class PasswordCheckControllerTest { ...@@ -53,6 +54,10 @@ public class PasswordCheckControllerTest {
@Mock @Mock
private PasswordCheckComponentUi.Delegate mDelegate; private PasswordCheckComponentUi.Delegate mDelegate;
@Mock @Mock
private Consumer<String> mLaunchCctWithChangePasswordUrlConsumer;
@Mock
private Consumer<CompromisedCredential> mLaunchCctWithScriptConsumer;
@Mock
private PasswordCheck mPasswordCheck; private PasswordCheck mPasswordCheck;
// DO NOT INITIALIZE HERE! The objects would be shared here which leaks state between tests. // DO NOT INITIALIZE HERE! The objects would be shared here which leaks state between tests.
...@@ -63,7 +68,8 @@ public class PasswordCheckControllerTest { ...@@ -63,7 +68,8 @@ public class PasswordCheckControllerTest {
public void setUp() { public void setUp() {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
mModel = PasswordCheckProperties.createDefaultModel(); mModel = PasswordCheckProperties.createDefaultModel();
mMediator = new PasswordCheckMediator(); mMediator = new PasswordCheckMediator(
mLaunchCctWithChangePasswordUrlConsumer, mLaunchCctWithScriptConsumer);
PasswordCheckFactory.setPasswordCheckForTesting(mPasswordCheck); PasswordCheckFactory.setPasswordCheckForTesting(mPasswordCheck);
mMediator.initialize(mModel, mDelegate); mMediator.initialize(mModel, mDelegate);
} }
...@@ -122,4 +128,16 @@ public class PasswordCheckControllerTest { ...@@ -122,4 +128,16 @@ public class PasswordCheckControllerTest {
mMediator.onRemove(ANA); mMediator.onRemove(ANA);
verify(mDelegate).removeCredential(eq(ANA)); verify(mDelegate).removeCredential(eq(ANA));
} }
@Test
public void testOnChangePasswordButtonClick() {
mMediator.onChangePasswordButtonClick(ANA);
verify(mLaunchCctWithChangePasswordUrlConsumer).accept(eq(ANA.getOriginUrl()));
}
@Test
public void testOnChangePasswordWithScriptButtonClick() {
mMediator.onChangePasswordWithScriptButtonClick(BOB);
verify(mLaunchCctWithScriptConsumer).accept(eq(BOB));
}
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment