| Index: ios/chrome/browser/ui/authentication/authentication_flow_performer.mm
|
| diff --git a/ios/chrome/browser/ui/authentication/authentication_flow_performer.mm b/ios/chrome/browser/ui/authentication/authentication_flow_performer.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..6600833e43476a5922757b831fea273eb706f2dc
|
| --- /dev/null
|
| +++ b/ios/chrome/browser/ui/authentication/authentication_flow_performer.mm
|
| @@ -0,0 +1,452 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#import "ios/chrome/browser/ui/authentication/authentication_flow_performer.h"
|
| +
|
| +#include <memory>
|
| +
|
| +#include "base/ios/block_types.h"
|
| +#include "base/ios/weak_nsobject.h"
|
| +#include "base/logging.h"
|
| +#include "base/mac/bind_objc_block.h"
|
| +#include "base/mac/scoped_nsobject.h"
|
| +#include "base/metrics/user_metrics.h"
|
| +#include "base/strings/sys_string_conversions.h"
|
| +#include "base/time/time.h"
|
| +#include "base/timer/timer.h"
|
| +#include "components/prefs/pref_service.h"
|
| +#include "components/signin/core/browser/account_tracker_service.h"
|
| +#include "components/signin/core/browser/signin_manager.h"
|
| +#include "components/signin/core/common/signin_pref_names.h"
|
| +#include "components/strings/grit/components_strings.h"
|
| +#include "google_apis/gaia/gaia_auth_util.h"
|
| +#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
|
| +#include "ios/chrome/browser/experimental_flags.h"
|
| +#include "ios/chrome/browser/signin/account_tracker_service_factory.h"
|
| +#include "ios/chrome/browser/signin/authentication_service.h"
|
| +#include "ios/chrome/browser/signin/authentication_service_factory.h"
|
| +#import "ios/chrome/browser/signin/browser_state_data_remover.h"
|
| +#import "ios/chrome/browser/signin/constants.h"
|
| +#include "ios/chrome/browser/signin/signin_manager_factory.h"
|
| +#include "ios/chrome/browser/sync/sync_setup_service.h"
|
| +#include "ios/chrome/browser/sync/sync_setup_service_factory.h"
|
| +#include "ios/chrome/browser/ui/alert_coordinator/alert_coordinator.h"
|
| +#import "ios/chrome/browser/ui/authentication/authentication_ui_util.h"
|
| +#import "ios/chrome/browser/ui/settings/import_data_collection_view_controller.h"
|
| +#import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
|
| +#include "ios/chrome/grit/ios_chromium_strings.h"
|
| +#include "ios/chrome/grit/ios_strings.h"
|
| +#include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
|
| +#import "ios/public/provider/chrome/browser/signin/chrome_identity.h"
|
| +#include "ui/base/l10n/l10n_util.h"
|
| +
|
| +using signin_ui::CompletionCallback;
|
| +
|
| +namespace {
|
| +
|
| +const int64_t kAuthenticationFlowTimeoutSeconds = 10;
|
| +
|
| +} // namespace
|
| +
|
| +@interface AuthenticationFlowPerformer ()<ImportDataControllerDelegate,
|
| + SettingsNavigationControllerDelegate>
|
| +
|
| +// Starts the watchdog timer with a timeout of
|
| +// |kAuthenticationFlowTimeoutSeconds| for the fetching managed status
|
| +// operation. It will notify |_delegate| of the failure unless
|
| +// |stopWatchdogTimer| is called before it times out.
|
| +- (void)startWatchdogTimerForManagedStatus;
|
| +
|
| +// Stops the watchdog timer, and doesn't call the |timeoutDelegateSelector|.
|
| +// Returns whether the watchdog was actually running.
|
| +- (BOOL)stopWatchdogTimer;
|
| +
|
| +// Callback for when the alert is dismissed.
|
| +- (void)alertControllerDidDisappear:(AlertCoordinator*)alertCoordinator;
|
| +
|
| +@end
|
| +
|
| +@implementation AuthenticationFlowPerformer {
|
| + base::WeakNSProtocol<id<AuthenticationFlowPerformerDelegate>> _delegate;
|
| + base::scoped_nsobject<AlertCoordinator> _alertCoordinator;
|
| + base::scoped_nsobject<SettingsNavigationController> _navigationController;
|
| + std::unique_ptr<base::Timer> _watchdogTimer;
|
| +}
|
| +
|
| +- (id<AuthenticationFlowPerformerDelegate>)delegate {
|
| + return _delegate.get();
|
| +}
|
| +
|
| +- (instancetype)initWithDelegate:
|
| + (id<AuthenticationFlowPerformerDelegate>)delegate {
|
| + self = [super init];
|
| + if (self)
|
| + _delegate.reset(delegate);
|
| + return self;
|
| +}
|
| +
|
| +- (void)cancelAndDismiss {
|
| + [_alertCoordinator executeCancelHandler];
|
| + [_alertCoordinator stop];
|
| + if (_navigationController) {
|
| + [_navigationController settingsWillBeDismissed];
|
| + _navigationController.reset();
|
| + [[_delegate presentingViewController] dismissViewControllerAnimated:NO
|
| + completion:nil];
|
| + }
|
| + [self stopWatchdogTimer];
|
| +}
|
| +
|
| +- (void)commitSyncForBrowserState:(ios::ChromeBrowserState*)browserState {
|
| + SyncSetupServiceFactory::GetForBrowserState(browserState)->CommitChanges();
|
| +}
|
| +
|
| +- (void)startWatchdogTimerForManagedStatus {
|
| + base::WeakNSObject<AuthenticationFlowPerformer> weakSelf(self);
|
| + ProceduralBlock onTimeout = ^{
|
| + base::scoped_nsobject<AuthenticationFlowPerformer> strongSelf(
|
| + [weakSelf retain]);
|
| + if (!strongSelf)
|
| + return;
|
| + [strongSelf stopWatchdogTimer];
|
| + NSError* error = [NSError errorWithDomain:kAuthenticationErrorDomain
|
| + code:TIMED_OUT_FETCH_POLICY
|
| + userInfo:nil];
|
| + [strongSelf.get()->_delegate didFailFetchManagedStatus:error];
|
| + };
|
| + _watchdogTimer.reset(new base::Timer(false, false));
|
| + _watchdogTimer->Start(FROM_HERE, base::TimeDelta::FromSeconds(
|
| + kAuthenticationFlowTimeoutSeconds),
|
| + base::BindBlock(onTimeout));
|
| +}
|
| +
|
| +- (BOOL)stopWatchdogTimer {
|
| + if (_watchdogTimer) {
|
| + _watchdogTimer->Stop();
|
| + _watchdogTimer.reset();
|
| + return YES;
|
| + }
|
| + return NO;
|
| +}
|
| +
|
| +- (void)fetchManagedStatus:(ios::ChromeBrowserState*)browserState
|
| + forIdentity:(ChromeIdentity*)identity {
|
| + if (!experimental_flags::IsMDMIntegrationEnabled()) {
|
| + [_delegate didFetchManagedStatus:nil];
|
| + return;
|
| + }
|
| + if (gaia::ExtractDomainName(gaia::CanonicalizeEmail(
|
| + base::SysNSStringToUTF8(identity.userEmail))) == "gmail.com") {
|
| + // Do nothing for @gmail.com addresses as they can't have a hosted domain.
|
| + // This avoids waiting for this step to complete (and a network call).
|
| + [_delegate didFetchManagedStatus:nil];
|
| + return;
|
| + }
|
| +
|
| + [self startWatchdogTimerForManagedStatus];
|
| + base::WeakNSObject<AuthenticationFlowPerformer> weakSelf(self);
|
| + ios::GetChromeBrowserProvider()
|
| + ->GetChromeIdentityService()
|
| + ->GetHostedDomainForIdentity(
|
| + identity, ^(NSString* hosted_domain, NSError* error) {
|
| + [weakSelf handleGetHostedDomain:hosted_domain
|
| + error:error
|
| + browserState:browserState];
|
| + });
|
| +}
|
| +
|
| +- (void)handleGetHostedDomain:(NSString*)hostedDomain
|
| + error:(NSError*)error
|
| + browserState:(ios::ChromeBrowserState*)browserState {
|
| + if (![self stopWatchdogTimer]) {
|
| + // Watchdog timer has already fired, don't notify the delegate.
|
| + return;
|
| + }
|
| + if (error) {
|
| + [_delegate didFailFetchManagedStatus:error];
|
| + return;
|
| + }
|
| + [_delegate didFetchManagedStatus:hostedDomain];
|
| +}
|
| +
|
| +- (void)signInIdentity:(ChromeIdentity*)identity
|
| + withHostedDomain:(NSString*)hostedDomain
|
| + toBrowserState:(ios::ChromeBrowserState*)browserState {
|
| + AuthenticationServiceFactory::GetForBrowserState(browserState)
|
| + ->SignIn(identity, base::SysNSStringToUTF8(hostedDomain));
|
| +}
|
| +
|
| +- (void)signOutBrowserState:(ios::ChromeBrowserState*)browserState {
|
| + AuthenticationServiceFactory::GetForBrowserState(browserState)
|
| + ->SignOut(signin_metrics::USER_CLICKED_SIGNOUT_SETTINGS, ^{
|
| + [_delegate didSignOut];
|
| + });
|
| +}
|
| +
|
| +- (void)signOutImmediatelyFromBrowserState:
|
| + (ios::ChromeBrowserState*)browserState {
|
| + AuthenticationServiceFactory::GetForBrowserState(browserState)
|
| + ->SignOut(signin_metrics::ABORT_SIGNIN, nil);
|
| +}
|
| +
|
| +- (void)promptSwitchFromManagedEmail:(NSString*)managedEmail
|
| + withHostedDomain:(NSString*)hostedDomain
|
| + toEmail:(NSString*)toEmail
|
| + viewController:(UIViewController*)viewController {
|
| + DCHECK(!_alertCoordinator);
|
| + NSString* title = l10n_util::GetNSString(IDS_IOS_MANAGED_SWITCH_TITLE);
|
| + NSString* subtitle = l10n_util::GetNSStringF(
|
| + IDS_IOS_MANAGED_SWITCH_SUBTITLE, base::SysNSStringToUTF16(managedEmail),
|
| + base::SysNSStringToUTF16(toEmail),
|
| + base::SysNSStringToUTF16(hostedDomain));
|
| + NSString* acceptLabel =
|
| + l10n_util::GetNSString(IDS_IOS_MANAGED_SWITCH_ACCEPT_BUTTON);
|
| + NSString* cancelLabel = l10n_util::GetNSString(IDS_CANCEL);
|
| +
|
| + _alertCoordinator.reset([[AlertCoordinator alloc]
|
| + initWithBaseViewController:viewController
|
| + title:title
|
| + message:subtitle]);
|
| +
|
| + base::WeakNSObject<AuthenticationFlowPerformer> weakSelf(self);
|
| + base::WeakNSObject<AlertCoordinator> weakAlert(_alertCoordinator);
|
| + ProceduralBlock acceptBlock = ^{
|
| + base::scoped_nsobject<AuthenticationFlowPerformer> strongSelf(
|
| + [weakSelf retain]);
|
| + if (!strongSelf)
|
| + return;
|
| + [[strongSelf delegate]
|
| + didChooseClearDataPolicy:SHOULD_CLEAR_DATA_CLEAR_DATA];
|
| + [strongSelf alertControllerDidDisappear:weakAlert];
|
| + };
|
| + ProceduralBlock cancelBlock = ^{
|
| + base::scoped_nsobject<AuthenticationFlowPerformer> strongSelf(
|
| + [weakSelf retain]);
|
| + if (!strongSelf)
|
| + return;
|
| + [[strongSelf delegate] didChooseCancel];
|
| + [strongSelf alertControllerDidDisappear:weakAlert];
|
| + };
|
| +
|
| + [_alertCoordinator addItemWithTitle:cancelLabel
|
| + action:cancelBlock
|
| + style:UIAlertActionStyleCancel];
|
| + [_alertCoordinator addItemWithTitle:acceptLabel
|
| + action:acceptBlock
|
| + style:UIAlertActionStyleDefault];
|
| + [_alertCoordinator setCancelAction:cancelBlock];
|
| + [_alertCoordinator start];
|
| +}
|
| +
|
| +- (void)promptMergeCaseForIdentity:(ChromeIdentity*)identity
|
| + browserState:(ios::ChromeBrowserState*)browserState
|
| + viewController:(UIViewController*)viewController {
|
| + BOOL isSignedIn = YES;
|
| + NSString* lastSignedInEmail =
|
| + [AuthenticationServiceFactory::GetForBrowserState(browserState)
|
| + ->GetAuthenticatedIdentity() userEmail];
|
| + if (!lastSignedInEmail) {
|
| + lastSignedInEmail =
|
| + base::SysUTF8ToNSString(browserState->GetPrefs()->GetString(
|
| + prefs::kGoogleServicesLastUsername));
|
| + isSignedIn = NO;
|
| + }
|
| +
|
| + if (AuthenticationServiceFactory::GetForBrowserState(browserState)
|
| + ->IsAuthenticatedIdentityManaged()) {
|
| + NSString* hostedDomain = base::SysUTF8ToNSString(
|
| + ios::SigninManagerFactory::GetForBrowserState(browserState)
|
| + ->GetAuthenticatedAccountInfo()
|
| + .hosted_domain);
|
| + [self promptSwitchFromManagedEmail:lastSignedInEmail
|
| + withHostedDomain:hostedDomain
|
| + toEmail:[identity userEmail]
|
| + viewController:viewController];
|
| + return;
|
| + }
|
| + _navigationController.reset([SettingsNavigationController
|
| + newImportDataController:browserState
|
| + delegate:self
|
| + importDataDelegate:self
|
| + fromEmail:lastSignedInEmail
|
| + toEmail:[identity userEmail]
|
| + isSignedIn:isSignedIn]);
|
| + [_navigationController setShouldCommitSyncChangesOnDismissal:NO];
|
| + [[_delegate presentingViewController]
|
| + presentViewController:_navigationController
|
| + animated:YES
|
| + completion:nil];
|
| +}
|
| +
|
| +- (void)clearData:(ios::ChromeBrowserState*)browserState {
|
| + DCHECK(!AuthenticationServiceFactory::GetForBrowserState(browserState)
|
| + ->GetAuthenticatedUserEmail());
|
| + BrowserStateDataRemover::ClearData(browserState, ^{
|
| + [_delegate didClearData];
|
| + });
|
| +}
|
| +
|
| +- (BOOL)shouldHandleMergeCaseForIdentity:(ChromeIdentity*)identity
|
| + browserState:
|
| + (ios::ChromeBrowserState*)browserState {
|
| + std::string lastSignedInAccountId =
|
| + browserState->GetPrefs()->GetString(prefs::kGoogleServicesLastAccountId);
|
| + std::string currentSignedInAccountId =
|
| + ios::AccountTrackerServiceFactory::GetForBrowserState(browserState)
|
| + ->PickAccountIdForAccount(
|
| + base::SysNSStringToUTF8([identity gaiaID]),
|
| + base::SysNSStringToUTF8([identity userEmail]));
|
| + if (!lastSignedInAccountId.empty()) {
|
| + // Merge case exists if the id of the previously signed in account is
|
| + // different from the one of the account being signed in.
|
| + return lastSignedInAccountId != currentSignedInAccountId;
|
| + }
|
| +
|
| + // kGoogleServicesLastAccountId pref might not have been populated yet,
|
| + // check the old kGoogleServicesLastUsername pref.
|
| + std::string lastSignedInEmail =
|
| + browserState->GetPrefs()->GetString(prefs::kGoogleServicesLastUsername);
|
| + std::string currentSignedInEmail =
|
| + base::SysNSStringToUTF8([identity userEmail]);
|
| + return !lastSignedInEmail.empty() &&
|
| + !gaia::AreEmailsSame(currentSignedInEmail, lastSignedInEmail);
|
| +}
|
| +
|
| +- (void)showManagedConfirmationForHostedDomain:(NSString*)hostedDomain
|
| + viewController:
|
| + (UIViewController*)viewController {
|
| + DCHECK(!_alertCoordinator);
|
| + NSString* title = l10n_util::GetNSString(IDS_IOS_MANAGED_SIGNIN_TITLE);
|
| + NSString* subtitle = l10n_util::GetNSStringF(
|
| + IDS_IOS_MANAGED_SIGNIN_SUBTITLE, base::SysNSStringToUTF16(hostedDomain));
|
| + NSString* acceptLabel =
|
| + l10n_util::GetNSString(IDS_IOS_MANAGED_SIGNIN_ACCEPT_BUTTON);
|
| + NSString* cancelLabel = l10n_util::GetNSString(IDS_CANCEL);
|
| +
|
| + _alertCoordinator.reset([[AlertCoordinator alloc]
|
| + initWithBaseViewController:viewController
|
| + title:title
|
| + message:subtitle]);
|
| +
|
| + base::WeakNSObject<AuthenticationFlowPerformer> weakSelf(self);
|
| + base::WeakNSObject<AlertCoordinator> weakAlert(_alertCoordinator);
|
| + ProceduralBlock acceptBlock = ^{
|
| + base::scoped_nsobject<AuthenticationFlowPerformer> strongSelf(
|
| + [weakSelf retain]);
|
| + if (!strongSelf)
|
| + return;
|
| + [[strongSelf delegate] didAcceptManagedConfirmation];
|
| + [strongSelf alertControllerDidDisappear:weakAlert];
|
| + };
|
| + ProceduralBlock cancelBlock = ^{
|
| + base::scoped_nsobject<AuthenticationFlowPerformer> strongSelf(
|
| + [weakSelf retain]);
|
| + if (!strongSelf)
|
| + return;
|
| + [[strongSelf delegate] didCancelManagedConfirmation];
|
| + [strongSelf alertControllerDidDisappear:weakAlert];
|
| + };
|
| +
|
| + [_alertCoordinator addItemWithTitle:cancelLabel
|
| + action:cancelBlock
|
| + style:UIAlertActionStyleCancel];
|
| + [_alertCoordinator addItemWithTitle:acceptLabel
|
| + action:acceptBlock
|
| + style:UIAlertActionStyleDefault];
|
| + [_alertCoordinator setCancelAction:cancelBlock];
|
| + [_alertCoordinator start];
|
| +}
|
| +
|
| +- (void)showAuthenticationError:(NSError*)error
|
| + withCompletion:(ProceduralBlock)callback
|
| + viewController:(UIViewController*)viewController {
|
| + DCHECK(!_alertCoordinator);
|
| +
|
| + _alertCoordinator.reset(
|
| + [ios_internal::ErrorCoordinatorNoItem(error, viewController) retain]);
|
| +
|
| + base::WeakNSObject<AuthenticationFlowPerformer> weakSelf(self);
|
| + base::WeakNSObject<AlertCoordinator> weakAlert(_alertCoordinator);
|
| + ProceduralBlock dismissAction = ^{
|
| + if (callback)
|
| + callback();
|
| + [weakSelf alertControllerDidDisappear:weakAlert];
|
| + };
|
| +
|
| + NSString* okButtonLabel = l10n_util::GetNSString(IDS_OK);
|
| + [_alertCoordinator addItemWithTitle:okButtonLabel
|
| + action:dismissAction
|
| + style:UIAlertActionStyleDefault];
|
| +
|
| + [_alertCoordinator setCancelAction:dismissAction];
|
| +
|
| + [_alertCoordinator start];
|
| +}
|
| +
|
| +- (void)alertControllerDidDisappear:(AlertCoordinator*)alertCoordinator {
|
| + if (_alertCoordinator.get() != alertCoordinator) {
|
| + // Do not reset the |_alertCoordinator| if it has changed. This typically
|
| + // happens when the user taps on any of the actions on "Clear Data Before
|
| + // Syncing?" dialog, as the sign-in confirmation dialog is created before
|
| + // the "Clear Data Before Syncing?" dialog is dismissed.
|
| + return;
|
| + }
|
| + _alertCoordinator.reset();
|
| +}
|
| +
|
| +#pragma mark - ImportDataControllerDelegate
|
| +
|
| +- (void)didChooseClearDataPolicy:(ImportDataCollectionViewController*)controller
|
| + shouldClearData:(ShouldClearData)shouldClearData {
|
| + DCHECK_NE(SHOULD_CLEAR_DATA_USER_CHOICE, shouldClearData);
|
| + if (shouldClearData == SHOULD_CLEAR_DATA_CLEAR_DATA) {
|
| + base::RecordAction(
|
| + base::UserMetricsAction("Signin_ImportDataPrompt_DontImport"));
|
| + } else {
|
| + base::RecordAction(
|
| + base::UserMetricsAction("Signin_ImportDataPrompt_ImportData"));
|
| + }
|
| +
|
| + base::WeakNSObject<AuthenticationFlowPerformer> weakSelf(self);
|
| + ProceduralBlock block = ^{
|
| + base::scoped_nsobject<AuthenticationFlowPerformer> strongSelf(
|
| + [weakSelf retain]);
|
| + if (!strongSelf)
|
| + return;
|
| + strongSelf.get()->_navigationController.reset();
|
| + [[strongSelf delegate] didChooseClearDataPolicy:shouldClearData];
|
| + };
|
| + [_navigationController settingsWillBeDismissed];
|
| + [[_delegate presentingViewController] dismissViewControllerAnimated:YES
|
| + completion:block];
|
| +}
|
| +
|
| +#pragma mark - SettingsNavigationControllerDelegate
|
| +
|
| +- (void)closeSettings {
|
| + base::RecordAction(base::UserMetricsAction("Signin_ImportDataPrompt_Cancel"));
|
| +
|
| + base::WeakNSObject<AuthenticationFlowPerformer> weakSelf(self);
|
| + ProceduralBlock block = ^{
|
| + base::scoped_nsobject<AuthenticationFlowPerformer> strongSelf(
|
| + [weakSelf retain]);
|
| + if (!strongSelf)
|
| + return;
|
| + strongSelf.get()->_navigationController.reset();
|
| + [[strongSelf delegate] didChooseCancel];
|
| + };
|
| + [_navigationController settingsWillBeDismissed];
|
| + [[_delegate presentingViewController] dismissViewControllerAnimated:YES
|
| + completion:block];
|
| +}
|
| +
|
| +- (void)closeSettingsAndOpenNewIncognitoTab {
|
| + NOTREACHED();
|
| +}
|
| +
|
| +- (void)closeSettingsAndOpenUrl:(OpenUrlCommand*)command {
|
| + NOTREACHED();
|
| +}
|
| +
|
| +@end
|
|
|