Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(328)

Unified Diff: ios/chrome/browser/ui/authentication/authentication_flow.mm

Issue 2586993002: Upstream Chrome on iOS source code [3/11]. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ios/chrome/browser/ui/authentication/authentication_flow.mm
diff --git a/ios/chrome/browser/ui/authentication/authentication_flow.mm b/ios/chrome/browser/ui/authentication/authentication_flow.mm
new file mode 100644
index 0000000000000000000000000000000000000000..ca6c1dddd09746f0a6c8d280449f9e6787877ae0
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/authentication_flow.mm
@@ -0,0 +1,456 @@
+// 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.h"
+
+#include "base/ios/weak_nsobject.h"
+#include "base/logging.h"
+#include "base/mac/objc_property_releaser.h"
+#include "base/mac/scoped_block.h"
+#include "base/mac/scoped_nsobject.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/signin/authentication_service.h"
+#include "ios/chrome/browser/signin/authentication_service_factory.h"
+#include "ios/chrome/browser/signin/constants.h"
+#import "ios/chrome/browser/ui/authentication/authentication_flow_performer.h"
+#include "ios/chrome/grit/ios_strings.h"
+#import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
+#import "ios/public/provider/chrome/browser/signin/chrome_identity.h"
+#include "ios/public/provider/chrome/browser/signin/chrome_identity_service.h"
+#include "ios/public/provider/chrome/browser/signin/signin_error_provider.h"
+#include "ui/base/l10n/l10n_util.h"
+
+using signin_ui::CompletionCallback;
+
+namespace {
+
+// The states of the sign-in flow state machine.
+enum AuthenticationState {
+ BEGIN,
+ CHECK_SIGNIN_STEPS,
+ FETCH_MANAGED_STATUS,
+ CHECK_MERGE_CASE,
+ SHOW_MANAGED_CONFIRMATION,
+ SIGN_OUT_IF_NEEDED,
+ CLEAR_DATA,
+ SIGN_IN,
+ START_SYNC,
+ COMPLETE_WITH_SUCCESS,
+ COMPLETE_WITH_FAILURE,
+ CLEANUP_BEFORE_DONE,
+ DONE
+};
+
+NSError* IdentityMissingError() {
+ ios::SigninErrorProvider* provider =
+ ios::GetChromeBrowserProvider()->GetSigninErrorProvider();
+ return [NSError
+ errorWithDomain:provider->GetSigninErrorDomain()
+ code:provider->GetCode(ios::SigninError::MISSING_IDENTITY)
+ userInfo:nil];
+}
+
+} // namespace
+
+@interface AuthenticationFlow ()
+
+// Whether this flow is curently handling an error.
+@property(nonatomic, assign) BOOL handlingError;
+
+// Checks which sign-in steps to perform and updates member variables
+// accordingly.
+- (void)checkSigninSteps;
+
+// Continues the sign-in state machine starting from |_state| and invokes
+// |completion_| when finished.
+- (void)continueSignin;
+
+// Runs |completion_| asynchronously with |success| argument.
+- (void)completeSignInWithSuccess:(BOOL)success;
+
+// Cancels the current sign-in flow.
+- (void)cancelFlow;
+
+// Handles an authentication error and show an alert to the user.
+- (void)handleAuthenticationError:(NSError*)error;
+
+@end
+
+@implementation AuthenticationFlow {
+ ShouldClearData _shouldClearData;
+ PostSignInAction _postSignInAction;
+ base::scoped_nsobject<UIViewController> _presentingViewController;
+ base::mac::ScopedBlock<CompletionCallback> _signInCompletion;
+ base::scoped_nsobject<AuthenticationFlowPerformer> _performer;
+
+ // State machine tracking.
+ AuthenticationState _state;
+ BOOL _didSignIn;
+ BOOL _failedOrCancelled;
+ BOOL _shouldSignIn;
+ BOOL _shouldSignOut;
+ BOOL _shouldShowManagedConfirmation;
+ BOOL _shouldStartSync;
+ ios::ChromeBrowserState* _browserState;
+ base::scoped_nsobject<ChromeIdentity> _browserStateIdentity;
+ base::scoped_nsobject<ChromeIdentity> _identityToSignIn;
+ base::scoped_nsobject<NSString> _identityToSignInHostedDomain;
+
+ // This AuthenticationFlow keeps a reference to |self| while a sign-in flow is
+ // is in progress to ensure it outlives any attempt to destroy it in
+ // |_signInCompletion|.
+ base::scoped_nsobject<AuthenticationFlow> _selfRetainer;
+
+ base::mac::ObjCPropertyReleaser _propertyReleaser_AuthenticationFlow;
+}
+
+@synthesize handlingError = _handlingError;
+
+#pragma mark - Public methods
+
+- (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState
+ identity:(ChromeIdentity*)identity
+ shouldClearData:(ShouldClearData)shouldClearData
+ postSignInAction:(PostSignInAction)postSignInAction
+ presentingViewController:
+ (UIViewController*)presentingViewController {
+ if ((self = [super init])) {
+ DCHECK(browserState);
+ DCHECK(presentingViewController);
+ _browserState = browserState;
+ _identityToSignIn.reset([identity retain]);
+ _shouldClearData = shouldClearData;
+ _postSignInAction = postSignInAction;
+ _presentingViewController.reset([presentingViewController retain]);
+ _state = BEGIN;
+ _propertyReleaser_AuthenticationFlow.Init(self, [AuthenticationFlow class]);
+ }
+ return self;
+}
+
+- (void)startSignInWithCompletion:(CompletionCallback)completion {
+ DCHECK_EQ(BEGIN, _state);
+ DCHECK(!_signInCompletion);
+ DCHECK(completion);
+ _signInCompletion.reset(completion, base::scoped_policy::RETAIN);
+ _selfRetainer.reset([self retain]);
+ // Kick off the state machine.
+ if (!_performer) {
+ _performer.reset(
+ [[AuthenticationFlowPerformer alloc] initWithDelegate:self]);
+ }
+ [self continueSignin];
+}
+
+- (void)cancelAndDismiss {
+ if (_state == DONE)
+ return;
+
+ [_performer cancelAndDismiss];
+ if (_state != DONE) {
+ // The performer might not have been able to continue the flow if it was
+ // waiting for a callback (e.g. waiting for AccountReconcilor). In this
+ // case, we force the flow to finish synchronously.
+ [self cancelFlow];
+ }
+
+ DCHECK_EQ(DONE, _state);
+}
+
+- (void)setPresentingViewController:
+ (UIViewController*)presentingViewController {
+ _presentingViewController.reset([presentingViewController retain]);
+}
+
+#pragma mark State machine management
+
+- (AuthenticationState)nextStateFailedOrCancelled {
+ DCHECK(_failedOrCancelled);
+ switch (_state) {
+ case BEGIN:
+ case CHECK_SIGNIN_STEPS:
+ case FETCH_MANAGED_STATUS:
+ case CHECK_MERGE_CASE:
+ case SHOW_MANAGED_CONFIRMATION:
+ case SIGN_OUT_IF_NEEDED:
+ case CLEAR_DATA:
+ case SIGN_IN:
+ case START_SYNC:
+ return COMPLETE_WITH_FAILURE;
+ case COMPLETE_WITH_SUCCESS:
+ case COMPLETE_WITH_FAILURE:
+ return CLEANUP_BEFORE_DONE;
+ case CLEANUP_BEFORE_DONE:
+ case DONE:
+ return DONE;
+ }
+}
+
+- (AuthenticationState)nextState {
+ DCHECK(!self.handlingError);
+ if (_failedOrCancelled) {
+ return [self nextStateFailedOrCancelled];
+ }
+ DCHECK(!_failedOrCancelled);
+ switch (_state) {
+ case BEGIN:
+ return CHECK_SIGNIN_STEPS;
+ case CHECK_SIGNIN_STEPS:
+ if (_shouldSignIn)
+ return FETCH_MANAGED_STATUS;
+ else
+ return CHECK_MERGE_CASE;
+ case FETCH_MANAGED_STATUS:
+ return CHECK_MERGE_CASE;
+ case CHECK_MERGE_CASE:
+ if (_shouldShowManagedConfirmation)
+ return SHOW_MANAGED_CONFIRMATION;
+ else if (_shouldSignOut)
+ return SIGN_OUT_IF_NEEDED;
+ else if (_shouldClearData == SHOULD_CLEAR_DATA_CLEAR_DATA)
+ return CLEAR_DATA;
+ else if (_shouldSignIn)
+ return SIGN_IN;
+ else
+ return COMPLETE_WITH_SUCCESS;
+ case SHOW_MANAGED_CONFIRMATION:
+ if (_shouldSignOut)
+ return SIGN_OUT_IF_NEEDED;
+ else if (_shouldClearData == SHOULD_CLEAR_DATA_CLEAR_DATA)
+ return CLEAR_DATA;
+ else if (_shouldSignIn)
+ return SIGN_IN;
+ else
+ return COMPLETE_WITH_SUCCESS;
+ case SIGN_OUT_IF_NEEDED:
+ return _shouldClearData == SHOULD_CLEAR_DATA_CLEAR_DATA ? CLEAR_DATA
+ : SIGN_IN;
+ case CLEAR_DATA:
+ return SIGN_IN;
+ case SIGN_IN:
+ if (_shouldStartSync)
+ return START_SYNC;
+ else
+ return COMPLETE_WITH_SUCCESS;
+ case START_SYNC:
+ return COMPLETE_WITH_SUCCESS;
+ case COMPLETE_WITH_SUCCESS:
+ case COMPLETE_WITH_FAILURE:
+ return CLEANUP_BEFORE_DONE;
+ case CLEANUP_BEFORE_DONE:
+ case DONE:
+ return DONE;
+ }
+}
+
+- (void)continueSignin {
+ if (self.handlingError) {
+ // The flow should not continue while the error is being handled, e.g. while
+ // the user is being informed of an issue.
+ return;
+ }
+ _state = [self nextState];
+ switch (_state) {
+ case BEGIN:
+ NOTREACHED();
+ return;
+
+ case CHECK_SIGNIN_STEPS:
+ [self checkSigninSteps];
+ [self continueSignin];
+ return;
+
+ case FETCH_MANAGED_STATUS:
+ [_performer fetchManagedStatus:_browserState
+ forIdentity:_identityToSignIn];
+ return;
+
+ case CHECK_MERGE_CASE:
+ if ([_performer shouldHandleMergeCaseForIdentity:_identityToSignIn
+ browserState:_browserState]) {
+ if (_shouldClearData == SHOULD_CLEAR_DATA_USER_CHOICE) {
+ [_performer promptMergeCaseForIdentity:_identityToSignIn
+ browserState:_browserState
+ viewController:_presentingViewController];
+ return;
+ }
+ }
+ [self continueSignin];
+ return;
+
+ case SHOW_MANAGED_CONFIRMATION:
+ [_performer
+ showManagedConfirmationForHostedDomain:_identityToSignInHostedDomain
+ viewController:_presentingViewController];
+ return;
+
+ case SIGN_OUT_IF_NEEDED:
+ [_performer signOutBrowserState:_browserState];
+ return;
+
+ case CLEAR_DATA:
+ [_performer clearData:_browserState];
+ return;
+
+ case SIGN_IN:
+ [self signInIdentity:_identityToSignIn];
+ return;
+
+ case START_SYNC:
+ [_performer commitSyncForBrowserState:_browserState];
+ [self continueSignin];
+ return;
+
+ case COMPLETE_WITH_SUCCESS:
+ [self completeSignInWithSuccess:YES];
+ return;
+
+ case COMPLETE_WITH_FAILURE:
+ if (_didSignIn) {
+ [_performer signOutImmediatelyFromBrowserState:_browserState];
+ // Enabling/disabling sync does not take effect in the sync backend
+ // until committing changes.
+ [_performer commitSyncForBrowserState:_browserState];
+ }
+ [self completeSignInWithSuccess:NO];
+ return;
+ case CLEANUP_BEFORE_DONE:
+ // Clean up asynchronously to ensure that |self| does not die while
+ // the flow is running.
+ DCHECK([NSThread isMainThread]);
+ dispatch_async(dispatch_get_main_queue(), ^{
+ _selfRetainer.reset();
+ });
+ [self continueSignin];
+ return;
+ case DONE:
+ return;
+ }
+ NOTREACHED();
+}
+
+- (void)checkSigninSteps {
+ _browserStateIdentity.reset(
+ [AuthenticationServiceFactory::GetForBrowserState(_browserState)
+ ->GetAuthenticatedIdentity() retain]);
+ if (_browserStateIdentity)
+ _shouldSignOut = YES;
+
+ _shouldSignIn = YES;
+ _shouldStartSync = _postSignInAction == POST_SIGNIN_ACTION_START_SYNC;
+}
+
+- (void)signInIdentity:(ChromeIdentity*)identity {
+ if (ios::GetChromeBrowserProvider()
+ ->GetChromeIdentityService()
+ ->IsValidIdentity(identity)) {
+ [_performer signInIdentity:identity
+ withHostedDomain:_identityToSignInHostedDomain
+ toBrowserState:_browserState];
+ _didSignIn = YES;
+ [self continueSignin];
+ } else {
+ // Handle the case where the identity is no longer valid.
+ [self handleAuthenticationError:IdentityMissingError()];
+ }
+}
+
+- (void)completeSignInWithSuccess:(BOOL)success {
+ DCHECK(_signInCompletion)
+ << "|completeSignInWithSuccess| should not be called twice.";
+ _signInCompletion.get()(success);
+ _signInCompletion.reset();
+ [self continueSignin];
+}
+
+- (void)cancelFlow {
+ if (_failedOrCancelled) {
+ // Avoid double handling of cancel or error.
+ return;
+ }
+ _failedOrCancelled = YES;
+ [self continueSignin];
+}
+
+- (void)handleAuthenticationError:(NSError*)error {
+ if (_failedOrCancelled) {
+ // Avoid double handling of cancel or error.
+ return;
+ }
+ DCHECK(error);
+ _failedOrCancelled = YES;
+ self.handlingError = YES;
+ base::WeakNSObject<AuthenticationFlow> weakSelf(self);
+ [_performer showAuthenticationError:error
+ withCompletion:^{
+ base::scoped_nsobject<AuthenticationFlow> strongSelf(
+ [weakSelf retain]);
+ if (!strongSelf)
+ return;
+ [strongSelf setHandlingError:NO];
+ [strongSelf continueSignin];
+ }
+ viewController:_presentingViewController];
+}
+
+#pragma mark AuthenticationFlowPerformerDelegate
+
+- (void)didSignOut {
+ [self continueSignin];
+}
+
+- (void)didClearData {
+ [self continueSignin];
+}
+
+- (void)didChooseClearDataPolicy:(ShouldClearData)shouldClearData {
+ DCHECK_NE(SHOULD_CLEAR_DATA_USER_CHOICE, shouldClearData);
+ _shouldSignOut = YES;
+ _shouldClearData = shouldClearData;
+ [self continueSignin];
+}
+
+- (void)didChooseCancel {
+ [self cancelFlow];
+}
+
+- (void)didFetchManagedStatus:(NSString*)hostedDomain {
+ DCHECK_EQ(FETCH_MANAGED_STATUS, _state);
+ _shouldShowManagedConfirmation = [hostedDomain length] > 0;
+ _identityToSignInHostedDomain.reset([hostedDomain retain]);
+ [self continueSignin];
+}
+
+- (void)didFailFetchManagedStatus:(NSError*)error {
+ DCHECK_EQ(FETCH_MANAGED_STATUS, _state);
+ NSError* flowError =
+ [NSError errorWithDomain:kAuthenticationErrorDomain
+ code:AUTHENTICATION_FLOW_ERROR
+ userInfo:@{
+ NSLocalizedDescriptionKey :
+ l10n_util::GetNSString(IDS_IOS_SIGN_IN_FAILED),
+ NSUnderlyingErrorKey : error
+ }];
+ [self handleAuthenticationError:flowError];
+}
+
+- (void)didAcceptManagedConfirmation {
+ [self continueSignin];
+}
+
+- (void)didCancelManagedConfirmation {
+ [self cancelFlow];
+}
+
+- (UIViewController*)presentingViewController {
+ return _presentingViewController;
+}
+
+#pragma mark - Used for testing
+
+- (void)setPerformerForTesting:(AuthenticationFlowPerformer*)performer {
+ _performer.reset([performer retain]);
+}
+
+@end

Powered by Google App Engine
This is Rietveld 408576698