[iOS][MIM2] Use AuthenticationFlow in the account menu
Switching the account menu to use AuthenticationFlow since it now supports profile switching. This patch also update AuthenticationFlow to set the account switching flag from AuthenticationService. In a later patch, AuthenticationService needs to send a notification when the flag is reset. TODO: http://crbug.com/397194707. Bug: 375604649 Change-Id: I06c691e7863aa3f14dcda3cbcf263dbb9c6a906e Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6275964 Auto-Submit: Jérôme Lebel <jlebel@chromium.org> Commit-Queue: Jérôme Lebel <jlebel@chromium.org> Reviewed-by: Marc Treib <treib@chromium.org> Cr-Commit-Position: refs/heads/main@{#1421131}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
694c756660
commit
1d9c8d59ae
ios/chrome/browser/authentication/ui_bundled
@@ -65,62 +65,6 @@
|
|||||||
#import "ios/chrome/grit/ios_strings.h"
|
#import "ios/chrome/grit/ios_strings.h"
|
||||||
#import "ui/base/l10n/l10n_util.h"
|
#import "ui/base/l10n/l10n_util.h"
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// First part of a switch-account-after-switching-profile continuation: Sign out
|
|
||||||
// the current account if it's different from the desired one.
|
|
||||||
void ChangeProfileSignOutIfMismatchContinuation(
|
|
||||||
id<SystemIdentity> expected_identity,
|
|
||||||
SceneState* scene_state,
|
|
||||||
base::OnceClosure closure) {
|
|
||||||
Browser* browser =
|
|
||||||
scene_state.browserProviderInterface.mainBrowserProvider.browser;
|
|
||||||
AuthenticationService* authentication_service =
|
|
||||||
AuthenticationServiceFactory::GetForProfile(browser->GetProfile());
|
|
||||||
|
|
||||||
id<SystemIdentity> existing_identity =
|
|
||||||
authentication_service->GetPrimaryIdentity(signin::ConsentLevel::kSignin);
|
|
||||||
if (!existing_identity || existing_identity == expected_identity) {
|
|
||||||
// No need to sign-out as either the correct identity is signed-in or
|
|
||||||
// no identity is signed-in.
|
|
||||||
std::move(closure).Run();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Signing out is only allowed in the personal profile. Phrased another way,
|
|
||||||
// we shouldn't try to a work profile with a non-matching account.
|
|
||||||
// TODO(crbug.com/375605174): AuthenticationService should probably enforce
|
|
||||||
// this internally.
|
|
||||||
CHECK_EQ(browser->GetProfile()->GetProfileName(),
|
|
||||||
GetApplicationContext()
|
|
||||||
->GetAccountProfileMapper()
|
|
||||||
->GetPersonalProfileName());
|
|
||||||
|
|
||||||
authentication_service->SignOut(
|
|
||||||
signin_metrics::ProfileSignout::kChangeAccountInAccountMenu,
|
|
||||||
base::CallbackToBlock(std::move(closure)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Second part of a switch-account-after-switching-profile continuation: Sign in
|
|
||||||
// the desired account if it's not already signed in.
|
|
||||||
void ChangeProfileSignInContinuation(id<SystemIdentity> identity,
|
|
||||||
SceneState* scene_state,
|
|
||||||
base::OnceClosure closure) {
|
|
||||||
Browser* browser =
|
|
||||||
scene_state.browserProviderInterface.mainBrowserProvider.browser;
|
|
||||||
// TODO(crbug.com/375604649): This should probably go through
|
|
||||||
// AuthenticationFlow rather than using AuthenticationService directly, so
|
|
||||||
// that the snackbar gets shown, and also the enterprise onboarding screen if
|
|
||||||
// necessary.
|
|
||||||
AuthenticationService* authentication_service =
|
|
||||||
AuthenticationServiceFactory::GetForProfile(browser->GetProfile());
|
|
||||||
authentication_service->SignIn(identity,
|
|
||||||
signin_metrics::AccessPoint::kAccountMenu);
|
|
||||||
std::move(closure).Run();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // anonymous namespace
|
|
||||||
|
|
||||||
@interface AccountMenuCoordinator () <AccountMenuMediatorDelegate,
|
@interface AccountMenuCoordinator () <AccountMenuMediatorDelegate,
|
||||||
ManageAccountsCoordinatorDelegate,
|
ManageAccountsCoordinatorDelegate,
|
||||||
UIAdaptivePresentationControllerDelegate>
|
UIAdaptivePresentationControllerDelegate>
|
||||||
@@ -149,7 +93,6 @@ void ChangeProfileSignInContinuation(id<SystemIdentity> identity,
|
|||||||
SyncEncryptionPassphraseTableViewController*
|
SyncEncryptionPassphraseTableViewController*
|
||||||
_syncEncryptionPassphraseTableViewController;
|
_syncEncryptionPassphraseTableViewController;
|
||||||
id<ApplicationCommands> _applicationHandler;
|
id<ApplicationCommands> _applicationHandler;
|
||||||
id<ChangeProfileCommands> _changeProfileHandler;
|
|
||||||
raw_ptr<ChromeAccountManagerService> _accountManagerService;
|
raw_ptr<ChromeAccountManagerService> _accountManagerService;
|
||||||
// Callback to hide the activity overlay.
|
// Callback to hide the activity overlay.
|
||||||
base::ScopedClosureRunner _activityOverlayCallback;
|
base::ScopedClosureRunner _activityOverlayCallback;
|
||||||
@@ -182,9 +125,6 @@ void ChangeProfileSignInContinuation(id<SystemIdentity> identity,
|
|||||||
_prefService = profile->GetPrefs();
|
_prefService = profile->GetPrefs();
|
||||||
_applicationHandler = HandlerForProtocol(self.browser->GetCommandDispatcher(),
|
_applicationHandler = HandlerForProtocol(self.browser->GetCommandDispatcher(),
|
||||||
ApplicationCommands);
|
ApplicationCommands);
|
||||||
_changeProfileHandler = HandlerForProtocol(
|
|
||||||
self.browser->GetSceneState().profileState.appState.appCommandDispatcher,
|
|
||||||
ChangeProfileCommands);
|
|
||||||
|
|
||||||
_viewController = [[AccountMenuViewController alloc] init];
|
_viewController = [[AccountMenuViewController alloc] init];
|
||||||
|
|
||||||
@@ -323,20 +263,6 @@ void ChangeProfileSignInContinuation(id<SystemIdentity> identity,
|
|||||||
[_signoutActionSheetCoordinator start];
|
[_signoutActionSheetCoordinator start];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)triggerProfileSwitchToProfileNamed:(std::string_view)profileName
|
|
||||||
andSigninWithSystemIdentity:(id<SystemIdentity>)identity {
|
|
||||||
CHECK(AreSeparateProfilesForManagedAccountsEnabled());
|
|
||||||
SceneState* sceneState = self.browser->GetSceneState();
|
|
||||||
|
|
||||||
ChangeProfileContinuation continuation = ChainChangeProfileContinuations(
|
|
||||||
base::BindOnce(&ChangeProfileSignOutIfMismatchContinuation, identity),
|
|
||||||
base::BindOnce(&ChangeProfileSignInContinuation, identity));
|
|
||||||
|
|
||||||
[_changeProfileHandler changeProfile:profileName
|
|
||||||
forScene:sceneState
|
|
||||||
continuation:std::move(continuation)];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)didTapAddAccountWithCompletion:
|
- (void)didTapAddAccountWithCompletion:
|
||||||
(SigninCoordinatorCompletionCallback)completion {
|
(SigninCoordinatorCompletionCallback)completion {
|
||||||
[self openAddAccountWithBaseViewController:_navigationController
|
[self openAddAccountWithBaseViewController:_navigationController
|
||||||
|
@@ -18,6 +18,7 @@
|
|||||||
#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_data_source.h"
|
#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_data_source.h"
|
||||||
#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mediator_delegate.h"
|
#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mediator_delegate.h"
|
||||||
#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_view_controller.h"
|
#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_view_controller.h"
|
||||||
|
#import "ios/chrome/browser/authentication/ui_bundled/authentication_flow/authentication_flow.h"
|
||||||
#import "ios/chrome/browser/authentication/ui_bundled/cells/table_view_account_item.h"
|
#import "ios/chrome/browser/authentication/ui_bundled/cells/table_view_account_item.h"
|
||||||
#import "ios/chrome/browser/authentication/ui_bundled/enterprise/enterprise_utils.h"
|
#import "ios/chrome/browser/authentication/ui_bundled/enterprise/enterprise_utils.h"
|
||||||
#import "ios/chrome/browser/authentication/ui_bundled/signin/signin_constants.h"
|
#import "ios/chrome/browser/authentication/ui_bundled/signin/signin_constants.h"
|
||||||
@@ -325,25 +326,22 @@
|
|||||||
_blockUpdates = YES;
|
_blockUpdates = YES;
|
||||||
self.userInteractionsBlocked = YES;
|
self.userInteractionsBlocked = YES;
|
||||||
|
|
||||||
|
__weak __typeof(self) weakSelf = self;
|
||||||
|
id<SystemIdentity> fromIdentity = _primaryIdentity;
|
||||||
|
// TODO(crbug.com/375604649): Need to use AuthenticationFlow in both cases.
|
||||||
if (AreSeparateProfilesForManagedAccountsEnabled()) {
|
if (AreSeparateProfilesForManagedAccountsEnabled()) {
|
||||||
std::optional<std::string> profileName =
|
_authenticationFlow = [self.delegate
|
||||||
GetApplicationContext()
|
triggerSigninWithSystemIdentity:newIdentity
|
||||||
->GetAccountProfileMapper()
|
completion:^(SigninCoordinatorResult result) {
|
||||||
->FindProfileNameForGaiaID(GaiaId(gaiaID));
|
[weakSelf signinEndedWithResult:result
|
||||||
if (profileName &&
|
fromIdentity:fromIdentity
|
||||||
*profileName != _accountManagerService->GetProfileName()) {
|
toIdentity:newIdentity];
|
||||||
// TODO(crbug.com/375604649): Unblock the UI (and show some error?) if
|
}];
|
||||||
// switching failed.
|
return;
|
||||||
[self.delegate triggerProfileSwitchToProfileNamed:*profileName
|
|
||||||
andSigninWithSystemIdentity:newIdentity];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_accountSwitchInProgress =
|
_accountSwitchInProgress =
|
||||||
_authenticationService->DeclareAccountSwitchInProgress();
|
_authenticationService->DeclareAccountSwitchInProgress();
|
||||||
__weak __typeof(self) weakSelf = self;
|
|
||||||
id<SystemIdentity> fromIdentity = _primaryIdentity;
|
|
||||||
[self.delegate signOutFromTargetRect:targetRect
|
[self.delegate signOutFromTargetRect:targetRect
|
||||||
forSwitch:YES
|
forSwitch:YES
|
||||||
completion:^(BOOL success) {
|
completion:^(BOOL success) {
|
||||||
|
@@ -40,10 +40,6 @@
|
|||||||
forSwitch:(BOOL)forSwith
|
forSwitch:(BOOL)forSwith
|
||||||
completion:(void (^)(BOOL))completion;
|
completion:(void (^)(BOOL))completion;
|
||||||
|
|
||||||
// Requests a switch to the profile with the given `profileName`.
|
|
||||||
- (void)triggerProfileSwitchToProfileNamed:(std::string_view)profileName
|
|
||||||
andSigninWithSystemIdentity:(id<SystemIdentity>)identity;
|
|
||||||
|
|
||||||
// Shows https://myaccount.google.com/ for the account currently signed-in
|
// Shows https://myaccount.google.com/ for the account currently signed-in
|
||||||
// to Chrome. The content is displayed in a new view in the stack, i.e.
|
// to Chrome. The content is displayed in a new view in the stack, i.e.
|
||||||
// it doesn't close the current view.
|
// it doesn't close the current view.
|
||||||
|
@@ -344,24 +344,52 @@ TEST_P(AccountMenuMediatorTest, TestAccountTapedSignoutFailed) {
|
|||||||
// This variable will contain the callback that should be executed once
|
// This variable will contain the callback that should be executed once
|
||||||
// sign-out ends.
|
// sign-out ends.
|
||||||
__block signin_ui::SignoutCompletionCallback signoutCallback = nil;
|
__block signin_ui::SignoutCompletionCallback signoutCallback = nil;
|
||||||
|
__block signin_ui::SigninCompletionCallback signinCallback = nil;
|
||||||
const CGRect target = CGRect();
|
const CGRect target = CGRect();
|
||||||
OCMExpect([delegate_mock_
|
|
||||||
signOutFromTargetRect:target
|
|
||||||
forSwitch:YES
|
|
||||||
completion:[OCMArg checkWithBlock:^BOOL(id value) {
|
|
||||||
signoutCallback = value;
|
|
||||||
return true;
|
|
||||||
}]]);
|
|
||||||
OCMExpect([consumer_mock_ switchingStarted]);
|
OCMExpect([consumer_mock_ switchingStarted]);
|
||||||
OCMExpect([consumer_mock_ setUserInteractionsEnabled:NO]);
|
OCMExpect([consumer_mock_ setUserInteractionsEnabled:NO]);
|
||||||
|
switch (GetParam()) {
|
||||||
|
case kOldApiWithoutSeparateProfiles:
|
||||||
|
case kNewApiWithoutSeparateProfiles:
|
||||||
|
OCMExpect([delegate_mock_
|
||||||
|
signOutFromTargetRect:target
|
||||||
|
forSwitch:YES
|
||||||
|
completion:[OCMArg checkWithBlock:^BOOL(id value) {
|
||||||
|
signoutCallback = value;
|
||||||
|
return true;
|
||||||
|
}]]);
|
||||||
|
break;
|
||||||
|
case kNewApiWithSeparateProfiles:
|
||||||
|
OCMExpect(
|
||||||
|
[delegate_mock_
|
||||||
|
triggerSigninWithSystemIdentity:kSecondaryIdentity
|
||||||
|
completion:[OCMArg checkWithBlock:^BOOL(
|
||||||
|
id value) {
|
||||||
|
signinCallback = value;
|
||||||
|
return true;
|
||||||
|
}]])
|
||||||
|
.andReturn(authentication_flow_mock_);
|
||||||
|
break;
|
||||||
|
}
|
||||||
[mediator_ accountTappedWithGaiaID:kSecondaryIdentity.gaiaID
|
[mediator_ accountTappedWithGaiaID:kSecondaryIdentity.gaiaID
|
||||||
targetRect:target];
|
targetRect:target];
|
||||||
VerifyMock();
|
VerifyMock();
|
||||||
|
|
||||||
OCMExpect([consumer_mock_ switchingStopped]);
|
OCMExpect([consumer_mock_ switchingStopped]);
|
||||||
OCMExpect([consumer_mock_ setUserInteractionsEnabled:YES]);
|
OCMExpect([consumer_mock_ setUserInteractionsEnabled:YES]);
|
||||||
// Simulate a sign-out failure
|
switch (GetParam()) {
|
||||||
signoutCallback(false);
|
case kOldApiWithoutSeparateProfiles:
|
||||||
|
case kNewApiWithoutSeparateProfiles:
|
||||||
|
// Simulate a sign-out failure.
|
||||||
|
signoutCallback(false);
|
||||||
|
EXPECT_EQ(signinCallback, nil);
|
||||||
|
break;
|
||||||
|
case kNewApiWithSeparateProfiles:
|
||||||
|
// Simulate AuthenticationFlow failure.
|
||||||
|
signinCallback(SigninCoordinatorResultCanceledByUser);
|
||||||
|
EXPECT_EQ(signoutCallback, nil);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests the result of accountTappedWithGaiaID:targetRect:
|
// Tests the result of accountTappedWithGaiaID:targetRect:
|
||||||
@@ -375,26 +403,35 @@ TEST_P(AccountMenuMediatorTest, TestAccountTapedSignInFailed) {
|
|||||||
// This variable will contain the callback that should be executed once
|
// This variable will contain the callback that should be executed once
|
||||||
// sign-out ends.
|
// sign-out ends.
|
||||||
__block signin_ui::SignoutCompletionCallback signoutCallback = nil;
|
__block signin_ui::SignoutCompletionCallback signoutCallback = nil;
|
||||||
|
__block signin_ui::SigninCompletionCallback signinCallback = nil;
|
||||||
const CGRect target = CGRect();
|
const CGRect target = CGRect();
|
||||||
OCMExpect([delegate_mock_
|
|
||||||
signOutFromTargetRect:target
|
|
||||||
forSwitch:YES
|
|
||||||
completion:[OCMArg checkWithBlock:^BOOL(id value) {
|
|
||||||
signoutCallback = value;
|
|
||||||
// Actually sign-out, in order to test next step.
|
|
||||||
SignOut();
|
|
||||||
return true;
|
|
||||||
}]]);
|
|
||||||
OCMExpect([consumer_mock_ switchingStarted]);
|
OCMExpect([consumer_mock_ switchingStarted]);
|
||||||
OCMExpect([consumer_mock_ setUserInteractionsEnabled:NO]);
|
OCMExpect([consumer_mock_ setUserInteractionsEnabled:NO]);
|
||||||
[mediator_ accountTappedWithGaiaID:kSecondaryIdentity.gaiaID
|
switch (GetParam()) {
|
||||||
targetRect:target];
|
case kOldApiWithoutSeparateProfiles:
|
||||||
VerifyMock();
|
case kNewApiWithoutSeparateProfiles:
|
||||||
|
OCMExpect([delegate_mock_
|
||||||
|
signOutFromTargetRect:target
|
||||||
|
forSwitch:YES
|
||||||
|
completion:[OCMArg checkWithBlock:^BOOL(id value) {
|
||||||
|
signoutCallback = value;
|
||||||
|
// Actually sign-out, in order to test next step.
|
||||||
|
SignOut();
|
||||||
|
return true;
|
||||||
|
}]]);
|
||||||
|
[mediator_ accountTappedWithGaiaID:kSecondaryIdentity.gaiaID
|
||||||
|
targetRect:target];
|
||||||
|
VerifyMock();
|
||||||
|
break;
|
||||||
|
case kNewApiWithSeparateProfiles:
|
||||||
|
// Sign-out is not done by the mediator but by AuthenticationFlow.
|
||||||
|
// This step can be skipped.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Simulate a sign-out success.
|
// Simulate a sign-out success.
|
||||||
// This variable will contain the callback that should be executed once
|
// This variable will contain the callback that should be executed once
|
||||||
// sign-in ended.
|
// sign-in ended.
|
||||||
__block signin_ui::SigninCompletionCallback signinCallback = nil;
|
|
||||||
OCMExpect([delegate_mock_
|
OCMExpect([delegate_mock_
|
||||||
triggerSigninWithSystemIdentity:kSecondaryIdentity
|
triggerSigninWithSystemIdentity:kSecondaryIdentity
|
||||||
completion:[OCMArg checkWithBlock:^BOOL(
|
completion:[OCMArg checkWithBlock:^BOOL(
|
||||||
@@ -403,12 +440,23 @@ TEST_P(AccountMenuMediatorTest, TestAccountTapedSignInFailed) {
|
|||||||
return true;
|
return true;
|
||||||
}]])
|
}]])
|
||||||
.andReturn(authentication_flow_mock_);
|
.andReturn(authentication_flow_mock_);
|
||||||
signoutCallback(true);
|
switch (GetParam()) {
|
||||||
|
case kOldApiWithoutSeparateProfiles:
|
||||||
|
case kNewApiWithoutSeparateProfiles:
|
||||||
|
signoutCallback(true);
|
||||||
|
// Testing the sign-in callback.
|
||||||
|
// The delegate should not receive any message. The mediator directly sign
|
||||||
|
// the user back in the previous account.
|
||||||
|
OCMExpect([consumer_mock_ updatePrimaryAccount]);
|
||||||
|
break;
|
||||||
|
case kNewApiWithSeparateProfiles:
|
||||||
|
// Simulate account switching.
|
||||||
|
[mediator_ accountTappedWithGaiaID:kSecondaryIdentity.gaiaID
|
||||||
|
targetRect:target];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Testing the sign-in callback.
|
// Expect that the consumer unlocks the UI.
|
||||||
// The delegate should not receive any message. The mediator directly sign the
|
|
||||||
// user back in the previous account.
|
|
||||||
OCMExpect([consumer_mock_ updatePrimaryAccount]);
|
|
||||||
OCMExpect([consumer_mock_ switchingStopped]);
|
OCMExpect([consumer_mock_ switchingStopped]);
|
||||||
OCMExpect([consumer_mock_ setUserInteractionsEnabled:YES]);
|
OCMExpect([consumer_mock_ setUserInteractionsEnabled:YES]);
|
||||||
signinCallback(SigninCoordinatorResult::SigninCoordinatorResultInterrupted);
|
signinCallback(SigninCoordinatorResult::SigninCoordinatorResultInterrupted);
|
||||||
@@ -429,33 +477,52 @@ TEST_P(AccountMenuMediatorTest, TestAccountTapedWithSuccessfulSwitch) {
|
|||||||
// This variable will contain the callback that should be executed once
|
// This variable will contain the callback that should be executed once
|
||||||
// sign-out ends.
|
// sign-out ends.
|
||||||
__block signin_ui::SignoutCompletionCallback signoutCallback = nil;
|
__block signin_ui::SignoutCompletionCallback signoutCallback = nil;
|
||||||
|
__block signin_ui::SigninCompletionCallback signinCallback = nil;
|
||||||
const CGRect target = CGRect();
|
const CGRect target = CGRect();
|
||||||
OCMExpect([delegate_mock_
|
|
||||||
signOutFromTargetRect:target
|
|
||||||
forSwitch:YES
|
|
||||||
completion:[OCMArg checkWithBlock:^BOOL(id value) {
|
|
||||||
signoutCallback = value;
|
|
||||||
return true;
|
|
||||||
}]]);
|
|
||||||
OCMExpect([consumer_mock_ switchingStarted]);
|
OCMExpect([consumer_mock_ switchingStarted]);
|
||||||
OCMExpect([consumer_mock_ setUserInteractionsEnabled:NO]);
|
OCMExpect([consumer_mock_ setUserInteractionsEnabled:NO]);
|
||||||
[mediator_ accountTappedWithGaiaID:kSecondaryIdentity.gaiaID
|
switch (GetParam()) {
|
||||||
targetRect:target];
|
case kOldApiWithoutSeparateProfiles:
|
||||||
VerifyMock();
|
case kNewApiWithoutSeparateProfiles:
|
||||||
|
OCMExpect([delegate_mock_
|
||||||
// Simulate a sign-out success.
|
signOutFromTargetRect:target
|
||||||
// This variable will contain the callback that should be executed once
|
forSwitch:YES
|
||||||
// sign-in ends.
|
completion:[OCMArg checkWithBlock:^BOOL(id value) {
|
||||||
__block signin_ui::SigninCompletionCallback signinCallback = nil;
|
signoutCallback = value;
|
||||||
OCMExpect([delegate_mock_
|
return true;
|
||||||
triggerSigninWithSystemIdentity:kSecondaryIdentity
|
}]]);
|
||||||
completion:[OCMArg checkWithBlock:^BOOL(
|
[mediator_ accountTappedWithGaiaID:kSecondaryIdentity.gaiaID
|
||||||
id value) {
|
targetRect:target];
|
||||||
signinCallback = value;
|
VerifyMock();
|
||||||
return true;
|
// Simulate a sign-out success.
|
||||||
}]])
|
// This variable will contain the callback that should be executed once
|
||||||
.andReturn(authentication_flow_mock_);
|
// sign-in ends.
|
||||||
signoutCallback(true);
|
OCMExpect(
|
||||||
|
[delegate_mock_
|
||||||
|
triggerSigninWithSystemIdentity:kSecondaryIdentity
|
||||||
|
completion:[OCMArg checkWithBlock:^BOOL(
|
||||||
|
id value) {
|
||||||
|
signinCallback = value;
|
||||||
|
return true;
|
||||||
|
}]])
|
||||||
|
.andReturn(authentication_flow_mock_);
|
||||||
|
signoutCallback(true);
|
||||||
|
break;
|
||||||
|
case kNewApiWithSeparateProfiles:
|
||||||
|
// Simulate account switching.
|
||||||
|
OCMExpect(
|
||||||
|
[delegate_mock_
|
||||||
|
triggerSigninWithSystemIdentity:kSecondaryIdentity
|
||||||
|
completion:[OCMArg checkWithBlock:^BOOL(
|
||||||
|
id value) {
|
||||||
|
signinCallback = value;
|
||||||
|
return true;
|
||||||
|
}]])
|
||||||
|
.andReturn(authentication_flow_mock_);
|
||||||
|
[mediator_ accountTappedWithGaiaID:kSecondaryIdentity.gaiaID
|
||||||
|
targetRect:target];
|
||||||
|
break;
|
||||||
|
}
|
||||||
VerifyMock();
|
VerifyMock();
|
||||||
|
|
||||||
OCMExpect([delegate_mock_
|
OCMExpect([delegate_mock_
|
||||||
|
@@ -246,6 +246,7 @@ void RecordIOSIdentityAvailableInProfile(
|
|||||||
|
|
||||||
// `YES` if the profile switching is done.
|
// `YES` if the profile switching is done.
|
||||||
BOOL _didSwitchProfile;
|
BOOL _didSwitchProfile;
|
||||||
|
base::ScopedClosureRunner _accountSwitchInProgress;
|
||||||
}
|
}
|
||||||
|
|
||||||
@synthesize handlingError = _handlingError;
|
@synthesize handlingError = _handlingError;
|
||||||
@@ -626,14 +627,18 @@ void RecordIOSIdentityAvailableInProfile(
|
|||||||
// Otherwise, this step does nothing and the flow continues to the next step.
|
// Otherwise, this step does nothing and the flow continues to the next step.
|
||||||
- (void)signOutIfNeededStep {
|
- (void)signOutIfNeededStep {
|
||||||
ProfileIOS* profile = [self originalProfile];
|
ProfileIOS* profile = [self originalProfile];
|
||||||
|
AuthenticationService* authenticationService =
|
||||||
|
AuthenticationServiceFactory::GetForProfile(profile);
|
||||||
id<SystemIdentity> currentIdentity =
|
id<SystemIdentity> currentIdentity =
|
||||||
AuthenticationServiceFactory::GetForProfile(profile)->GetPrimaryIdentity(
|
authenticationService->GetPrimaryIdentity(signin::ConsentLevel::kSignin);
|
||||||
signin::ConsentLevel::kSignin);
|
if (!currentIdentity || [currentIdentity isEqual:_identityToSignIn]) {
|
||||||
if (currentIdentity && ![currentIdentity isEqual:_identityToSignIn]) {
|
// No need to sign out.
|
||||||
[_performer signOutProfile:profile];
|
[self continueFlow];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
[self continueFlow];
|
_accountSwitchInProgress =
|
||||||
|
authenticationService->DeclareAccountSwitchInProgress();
|
||||||
|
[_performer signOutProfile:profile];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets the primary identity for the current profile.
|
// Sets the primary identity for the current profile.
|
||||||
@@ -691,6 +696,7 @@ void RecordIOSIdentityAvailableInProfile(
|
|||||||
- (void)completeWithSuccessStep {
|
- (void)completeWithSuccessStep {
|
||||||
DCHECK(_signInCompletion)
|
DCHECK(_signInCompletion)
|
||||||
<< "`completeSignInWithResult` should not be called twice.";
|
<< "`completeSignInWithResult` should not be called twice.";
|
||||||
|
_accountSwitchInProgress.RunAndReset();
|
||||||
signin_metrics::SigninAccountType accountType =
|
signin_metrics::SigninAccountType accountType =
|
||||||
(_identityToSignInHostedDomain.length > 0)
|
(_identityToSignInHostedDomain.length > 0)
|
||||||
? signin_metrics::SigninAccountType::kManaged
|
? signin_metrics::SigninAccountType::kManaged
|
||||||
@@ -713,6 +719,7 @@ void RecordIOSIdentityAvailableInProfile(
|
|||||||
ProfileIOS* profile = [self originalProfile];
|
ProfileIOS* profile = [self originalProfile];
|
||||||
[_performer signOutImmediatelyFromProfile:profile];
|
[_performer signOutImmediatelyFromProfile:profile];
|
||||||
}
|
}
|
||||||
|
_accountSwitchInProgress.RunAndReset();
|
||||||
SigninCoordinatorResult result;
|
SigninCoordinatorResult result;
|
||||||
switch (_cancelationReason) {
|
switch (_cancelationReason) {
|
||||||
case CancelationReason::kFailed:
|
case CancelationReason::kFailed:
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
#import "base/strings/sys_string_conversions.h"
|
#import "base/strings/sys_string_conversions.h"
|
||||||
|
#import "base/test/ios/wait_util.h"
|
||||||
#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_constants.h"
|
#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_constants.h"
|
||||||
#import "ios/chrome/browser/authentication/ui_bundled/signin/signin_constants.h"
|
#import "ios/chrome/browser/authentication/ui_bundled/signin/signin_constants.h"
|
||||||
#import "ios/chrome/browser/authentication/ui_bundled/signin_earl_grey.h"
|
#import "ios/chrome/browser/authentication/ui_bundled/signin_earl_grey.h"
|
||||||
@@ -125,12 +126,27 @@ id<GREYMatcher> ContinueButtonWithIdentityMatcher(
|
|||||||
[[EarlGrey selectElementWithMatcher:grey_accessibilityID(
|
[[EarlGrey selectElementWithMatcher:grey_accessibilityID(
|
||||||
kAccountMenuSecondaryAccountButtonId)]
|
kAccountMenuSecondaryAccountButtonId)]
|
||||||
performAction:grey_tap()];
|
performAction:grey_tap()];
|
||||||
// TODO(crbug.com/375604649): The enterprise onboarding screen should show up
|
// Wait for the enterprise onboarding screen.
|
||||||
// at this point.
|
ConditionBlock enterpriseOnboardingCondition = ^{
|
||||||
|
NSError* error;
|
||||||
|
[[EarlGrey selectElementWithMatcher:ManagedProfileCreationScreenMatcher()]
|
||||||
|
assertWithMatcher:grey_sufficientlyVisible()
|
||||||
|
error:&error];
|
||||||
|
|
||||||
// Wait for the new profile to finish loading.
|
return error == nil;
|
||||||
// TODO(crbug.com/331783685): Find a better way to wait for this.
|
};
|
||||||
GREYWaitForAppToIdle(@"App failed to idle");
|
GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(
|
||||||
|
base::test::ios::kWaitForUIElementTimeout,
|
||||||
|
enterpriseOnboardingCondition),
|
||||||
|
@"Enterprise onboarding didn't appear.");
|
||||||
|
// Confirm the enterprise onboarding screen.
|
||||||
|
[[EarlGrey selectElementWithMatcher:
|
||||||
|
chrome_test_util::PromoStylePrimaryActionButtonMatcher()]
|
||||||
|
performAction:grey_tap()];
|
||||||
|
|
||||||
|
// Ensure the enterprise onboarding screen did disapepar.
|
||||||
|
[[EarlGrey selectElementWithMatcher:IdentityDiscMatcher()]
|
||||||
|
assertWithMatcher:grey_sufficientlyVisible()];
|
||||||
|
|
||||||
[SigninEarlGrey verifySignedInWithFakeIdentity:managedIdentity];
|
[SigninEarlGrey verifySignedInWithFakeIdentity:managedIdentity];
|
||||||
|
|
||||||
@@ -158,6 +174,64 @@ id<GREYMatcher> ContinueButtonWithIdentityMatcher(
|
|||||||
@"Profile should have been switched");
|
@"Profile should have been switched");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests switching to a managed account and refuse the enterprise onboard
|
||||||
|
// screen.
|
||||||
|
- (void)testRefuseToSwitchToManageAccount {
|
||||||
|
// Separate profiles are only available in iOS 17+.
|
||||||
|
if (!@available(iOS 17, *)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString* personalProfileName = [ChromeEarlGrey currentProfileName];
|
||||||
|
|
||||||
|
// Setup: There's 1 personal and 1 managed account. The personal account is
|
||||||
|
// signed in.
|
||||||
|
FakeSystemIdentity* const personalIdentity =
|
||||||
|
[FakeSystemIdentity fakeIdentity1];
|
||||||
|
[SigninEarlGrey addFakeIdentity:personalIdentity];
|
||||||
|
|
||||||
|
FakeSystemIdentity* const managedIdentity =
|
||||||
|
[FakeSystemIdentity fakeManagedIdentity];
|
||||||
|
[SigninEarlGrey addFakeIdentity:managedIdentity];
|
||||||
|
|
||||||
|
[SigninEarlGreyUI signinWithFakeIdentity:personalIdentity];
|
||||||
|
|
||||||
|
// Switch to the managed account, which triggers a switch to a new managed
|
||||||
|
// profile.
|
||||||
|
OpenAccountMenu();
|
||||||
|
[[EarlGrey selectElementWithMatcher:grey_accessibilityID(
|
||||||
|
kAccountMenuSecondaryAccountButtonId)]
|
||||||
|
performAction:grey_tap()];
|
||||||
|
// Wait for the enterprise onboarding screen.
|
||||||
|
ConditionBlock enterpriseOnboardingCondition = ^{
|
||||||
|
NSError* error;
|
||||||
|
[[EarlGrey selectElementWithMatcher:ManagedProfileCreationScreenMatcher()]
|
||||||
|
assertWithMatcher:grey_sufficientlyVisible()
|
||||||
|
error:&error];
|
||||||
|
|
||||||
|
return error == nil;
|
||||||
|
};
|
||||||
|
GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(
|
||||||
|
base::test::ios::kWaitForUIElementTimeout,
|
||||||
|
enterpriseOnboardingCondition),
|
||||||
|
@"Enterprise onboarding didn't appear.");
|
||||||
|
// Refuse the enterprise onboarding screen.
|
||||||
|
[[EarlGrey selectElementWithMatcher:
|
||||||
|
chrome_test_util::PromoStyleSecondaryActionButtonMatcher()]
|
||||||
|
performAction:grey_tap()];
|
||||||
|
|
||||||
|
// Wait for the new profile to finish loading.
|
||||||
|
// TODO(crbug.com/331783685): Find a better way to wait for this.
|
||||||
|
GREYWaitForAppToIdle(@"App failed to idle");
|
||||||
|
|
||||||
|
[SigninEarlGrey verifySignedInWithFakeIdentity:personalIdentity];
|
||||||
|
|
||||||
|
// Verify that the profile was actually switched back.
|
||||||
|
GREYAssert(
|
||||||
|
[[ChromeEarlGrey currentProfileName] isEqualToString:personalProfileName],
|
||||||
|
@"Profile should have been switched");
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface SeparateProfilesFRETestCase : ChromeTestCase
|
@interface SeparateProfilesFRETestCase : ChromeTestCase
|
||||||
|
Reference in New Issue
Block a user