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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #import "ios/chrome/browser/ui/authentication/authentication_flow.h"
6
7 #include "base/ios/weak_nsobject.h"
8 #include "base/logging.h"
9 #include "base/mac/objc_property_releaser.h"
10 #include "base/mac/scoped_block.h"
11 #include "base/mac/scoped_nsobject.h"
12 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
13 #include "ios/chrome/browser/signin/authentication_service.h"
14 #include "ios/chrome/browser/signin/authentication_service_factory.h"
15 #include "ios/chrome/browser/signin/constants.h"
16 #import "ios/chrome/browser/ui/authentication/authentication_flow_performer.h"
17 #include "ios/chrome/grit/ios_strings.h"
18 #import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
19 #import "ios/public/provider/chrome/browser/signin/chrome_identity.h"
20 #include "ios/public/provider/chrome/browser/signin/chrome_identity_service.h"
21 #include "ios/public/provider/chrome/browser/signin/signin_error_provider.h"
22 #include "ui/base/l10n/l10n_util.h"
23
24 using signin_ui::CompletionCallback;
25
26 namespace {
27
28 // The states of the sign-in flow state machine.
29 enum AuthenticationState {
30 BEGIN,
31 CHECK_SIGNIN_STEPS,
32 FETCH_MANAGED_STATUS,
33 CHECK_MERGE_CASE,
34 SHOW_MANAGED_CONFIRMATION,
35 SIGN_OUT_IF_NEEDED,
36 CLEAR_DATA,
37 SIGN_IN,
38 START_SYNC,
39 COMPLETE_WITH_SUCCESS,
40 COMPLETE_WITH_FAILURE,
41 CLEANUP_BEFORE_DONE,
42 DONE
43 };
44
45 NSError* IdentityMissingError() {
46 ios::SigninErrorProvider* provider =
47 ios::GetChromeBrowserProvider()->GetSigninErrorProvider();
48 return [NSError
49 errorWithDomain:provider->GetSigninErrorDomain()
50 code:provider->GetCode(ios::SigninError::MISSING_IDENTITY)
51 userInfo:nil];
52 }
53
54 } // namespace
55
56 @interface AuthenticationFlow ()
57
58 // Whether this flow is curently handling an error.
59 @property(nonatomic, assign) BOOL handlingError;
60
61 // Checks which sign-in steps to perform and updates member variables
62 // accordingly.
63 - (void)checkSigninSteps;
64
65 // Continues the sign-in state machine starting from |_state| and invokes
66 // |completion_| when finished.
67 - (void)continueSignin;
68
69 // Runs |completion_| asynchronously with |success| argument.
70 - (void)completeSignInWithSuccess:(BOOL)success;
71
72 // Cancels the current sign-in flow.
73 - (void)cancelFlow;
74
75 // Handles an authentication error and show an alert to the user.
76 - (void)handleAuthenticationError:(NSError*)error;
77
78 @end
79
80 @implementation AuthenticationFlow {
81 ShouldClearData _shouldClearData;
82 PostSignInAction _postSignInAction;
83 base::scoped_nsobject<UIViewController> _presentingViewController;
84 base::mac::ScopedBlock<CompletionCallback> _signInCompletion;
85 base::scoped_nsobject<AuthenticationFlowPerformer> _performer;
86
87 // State machine tracking.
88 AuthenticationState _state;
89 BOOL _didSignIn;
90 BOOL _failedOrCancelled;
91 BOOL _shouldSignIn;
92 BOOL _shouldSignOut;
93 BOOL _shouldShowManagedConfirmation;
94 BOOL _shouldStartSync;
95 ios::ChromeBrowserState* _browserState;
96 base::scoped_nsobject<ChromeIdentity> _browserStateIdentity;
97 base::scoped_nsobject<ChromeIdentity> _identityToSignIn;
98 base::scoped_nsobject<NSString> _identityToSignInHostedDomain;
99
100 // This AuthenticationFlow keeps a reference to |self| while a sign-in flow is
101 // is in progress to ensure it outlives any attempt to destroy it in
102 // |_signInCompletion|.
103 base::scoped_nsobject<AuthenticationFlow> _selfRetainer;
104
105 base::mac::ObjCPropertyReleaser _propertyReleaser_AuthenticationFlow;
106 }
107
108 @synthesize handlingError = _handlingError;
109
110 #pragma mark - Public methods
111
112 - (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState
113 identity:(ChromeIdentity*)identity
114 shouldClearData:(ShouldClearData)shouldClearData
115 postSignInAction:(PostSignInAction)postSignInAction
116 presentingViewController:
117 (UIViewController*)presentingViewController {
118 if ((self = [super init])) {
119 DCHECK(browserState);
120 DCHECK(presentingViewController);
121 _browserState = browserState;
122 _identityToSignIn.reset([identity retain]);
123 _shouldClearData = shouldClearData;
124 _postSignInAction = postSignInAction;
125 _presentingViewController.reset([presentingViewController retain]);
126 _state = BEGIN;
127 _propertyReleaser_AuthenticationFlow.Init(self, [AuthenticationFlow class]);
128 }
129 return self;
130 }
131
132 - (void)startSignInWithCompletion:(CompletionCallback)completion {
133 DCHECK_EQ(BEGIN, _state);
134 DCHECK(!_signInCompletion);
135 DCHECK(completion);
136 _signInCompletion.reset(completion, base::scoped_policy::RETAIN);
137 _selfRetainer.reset([self retain]);
138 // Kick off the state machine.
139 if (!_performer) {
140 _performer.reset(
141 [[AuthenticationFlowPerformer alloc] initWithDelegate:self]);
142 }
143 [self continueSignin];
144 }
145
146 - (void)cancelAndDismiss {
147 if (_state == DONE)
148 return;
149
150 [_performer cancelAndDismiss];
151 if (_state != DONE) {
152 // The performer might not have been able to continue the flow if it was
153 // waiting for a callback (e.g. waiting for AccountReconcilor). In this
154 // case, we force the flow to finish synchronously.
155 [self cancelFlow];
156 }
157
158 DCHECK_EQ(DONE, _state);
159 }
160
161 - (void)setPresentingViewController:
162 (UIViewController*)presentingViewController {
163 _presentingViewController.reset([presentingViewController retain]);
164 }
165
166 #pragma mark State machine management
167
168 - (AuthenticationState)nextStateFailedOrCancelled {
169 DCHECK(_failedOrCancelled);
170 switch (_state) {
171 case BEGIN:
172 case CHECK_SIGNIN_STEPS:
173 case FETCH_MANAGED_STATUS:
174 case CHECK_MERGE_CASE:
175 case SHOW_MANAGED_CONFIRMATION:
176 case SIGN_OUT_IF_NEEDED:
177 case CLEAR_DATA:
178 case SIGN_IN:
179 case START_SYNC:
180 return COMPLETE_WITH_FAILURE;
181 case COMPLETE_WITH_SUCCESS:
182 case COMPLETE_WITH_FAILURE:
183 return CLEANUP_BEFORE_DONE;
184 case CLEANUP_BEFORE_DONE:
185 case DONE:
186 return DONE;
187 }
188 }
189
190 - (AuthenticationState)nextState {
191 DCHECK(!self.handlingError);
192 if (_failedOrCancelled) {
193 return [self nextStateFailedOrCancelled];
194 }
195 DCHECK(!_failedOrCancelled);
196 switch (_state) {
197 case BEGIN:
198 return CHECK_SIGNIN_STEPS;
199 case CHECK_SIGNIN_STEPS:
200 if (_shouldSignIn)
201 return FETCH_MANAGED_STATUS;
202 else
203 return CHECK_MERGE_CASE;
204 case FETCH_MANAGED_STATUS:
205 return CHECK_MERGE_CASE;
206 case CHECK_MERGE_CASE:
207 if (_shouldShowManagedConfirmation)
208 return SHOW_MANAGED_CONFIRMATION;
209 else if (_shouldSignOut)
210 return SIGN_OUT_IF_NEEDED;
211 else if (_shouldClearData == SHOULD_CLEAR_DATA_CLEAR_DATA)
212 return CLEAR_DATA;
213 else if (_shouldSignIn)
214 return SIGN_IN;
215 else
216 return COMPLETE_WITH_SUCCESS;
217 case SHOW_MANAGED_CONFIRMATION:
218 if (_shouldSignOut)
219 return SIGN_OUT_IF_NEEDED;
220 else if (_shouldClearData == SHOULD_CLEAR_DATA_CLEAR_DATA)
221 return CLEAR_DATA;
222 else if (_shouldSignIn)
223 return SIGN_IN;
224 else
225 return COMPLETE_WITH_SUCCESS;
226 case SIGN_OUT_IF_NEEDED:
227 return _shouldClearData == SHOULD_CLEAR_DATA_CLEAR_DATA ? CLEAR_DATA
228 : SIGN_IN;
229 case CLEAR_DATA:
230 return SIGN_IN;
231 case SIGN_IN:
232 if (_shouldStartSync)
233 return START_SYNC;
234 else
235 return COMPLETE_WITH_SUCCESS;
236 case START_SYNC:
237 return COMPLETE_WITH_SUCCESS;
238 case COMPLETE_WITH_SUCCESS:
239 case COMPLETE_WITH_FAILURE:
240 return CLEANUP_BEFORE_DONE;
241 case CLEANUP_BEFORE_DONE:
242 case DONE:
243 return DONE;
244 }
245 }
246
247 - (void)continueSignin {
248 if (self.handlingError) {
249 // The flow should not continue while the error is being handled, e.g. while
250 // the user is being informed of an issue.
251 return;
252 }
253 _state = [self nextState];
254 switch (_state) {
255 case BEGIN:
256 NOTREACHED();
257 return;
258
259 case CHECK_SIGNIN_STEPS:
260 [self checkSigninSteps];
261 [self continueSignin];
262 return;
263
264 case FETCH_MANAGED_STATUS:
265 [_performer fetchManagedStatus:_browserState
266 forIdentity:_identityToSignIn];
267 return;
268
269 case CHECK_MERGE_CASE:
270 if ([_performer shouldHandleMergeCaseForIdentity:_identityToSignIn
271 browserState:_browserState]) {
272 if (_shouldClearData == SHOULD_CLEAR_DATA_USER_CHOICE) {
273 [_performer promptMergeCaseForIdentity:_identityToSignIn
274 browserState:_browserState
275 viewController:_presentingViewController];
276 return;
277 }
278 }
279 [self continueSignin];
280 return;
281
282 case SHOW_MANAGED_CONFIRMATION:
283 [_performer
284 showManagedConfirmationForHostedDomain:_identityToSignInHostedDomain
285 viewController:_presentingViewController];
286 return;
287
288 case SIGN_OUT_IF_NEEDED:
289 [_performer signOutBrowserState:_browserState];
290 return;
291
292 case CLEAR_DATA:
293 [_performer clearData:_browserState];
294 return;
295
296 case SIGN_IN:
297 [self signInIdentity:_identityToSignIn];
298 return;
299
300 case START_SYNC:
301 [_performer commitSyncForBrowserState:_browserState];
302 [self continueSignin];
303 return;
304
305 case COMPLETE_WITH_SUCCESS:
306 [self completeSignInWithSuccess:YES];
307 return;
308
309 case COMPLETE_WITH_FAILURE:
310 if (_didSignIn) {
311 [_performer signOutImmediatelyFromBrowserState:_browserState];
312 // Enabling/disabling sync does not take effect in the sync backend
313 // until committing changes.
314 [_performer commitSyncForBrowserState:_browserState];
315 }
316 [self completeSignInWithSuccess:NO];
317 return;
318 case CLEANUP_BEFORE_DONE:
319 // Clean up asynchronously to ensure that |self| does not die while
320 // the flow is running.
321 DCHECK([NSThread isMainThread]);
322 dispatch_async(dispatch_get_main_queue(), ^{
323 _selfRetainer.reset();
324 });
325 [self continueSignin];
326 return;
327 case DONE:
328 return;
329 }
330 NOTREACHED();
331 }
332
333 - (void)checkSigninSteps {
334 _browserStateIdentity.reset(
335 [AuthenticationServiceFactory::GetForBrowserState(_browserState)
336 ->GetAuthenticatedIdentity() retain]);
337 if (_browserStateIdentity)
338 _shouldSignOut = YES;
339
340 _shouldSignIn = YES;
341 _shouldStartSync = _postSignInAction == POST_SIGNIN_ACTION_START_SYNC;
342 }
343
344 - (void)signInIdentity:(ChromeIdentity*)identity {
345 if (ios::GetChromeBrowserProvider()
346 ->GetChromeIdentityService()
347 ->IsValidIdentity(identity)) {
348 [_performer signInIdentity:identity
349 withHostedDomain:_identityToSignInHostedDomain
350 toBrowserState:_browserState];
351 _didSignIn = YES;
352 [self continueSignin];
353 } else {
354 // Handle the case where the identity is no longer valid.
355 [self handleAuthenticationError:IdentityMissingError()];
356 }
357 }
358
359 - (void)completeSignInWithSuccess:(BOOL)success {
360 DCHECK(_signInCompletion)
361 << "|completeSignInWithSuccess| should not be called twice.";
362 _signInCompletion.get()(success);
363 _signInCompletion.reset();
364 [self continueSignin];
365 }
366
367 - (void)cancelFlow {
368 if (_failedOrCancelled) {
369 // Avoid double handling of cancel or error.
370 return;
371 }
372 _failedOrCancelled = YES;
373 [self continueSignin];
374 }
375
376 - (void)handleAuthenticationError:(NSError*)error {
377 if (_failedOrCancelled) {
378 // Avoid double handling of cancel or error.
379 return;
380 }
381 DCHECK(error);
382 _failedOrCancelled = YES;
383 self.handlingError = YES;
384 base::WeakNSObject<AuthenticationFlow> weakSelf(self);
385 [_performer showAuthenticationError:error
386 withCompletion:^{
387 base::scoped_nsobject<AuthenticationFlow> strongSelf(
388 [weakSelf retain]);
389 if (!strongSelf)
390 return;
391 [strongSelf setHandlingError:NO];
392 [strongSelf continueSignin];
393 }
394 viewController:_presentingViewController];
395 }
396
397 #pragma mark AuthenticationFlowPerformerDelegate
398
399 - (void)didSignOut {
400 [self continueSignin];
401 }
402
403 - (void)didClearData {
404 [self continueSignin];
405 }
406
407 - (void)didChooseClearDataPolicy:(ShouldClearData)shouldClearData {
408 DCHECK_NE(SHOULD_CLEAR_DATA_USER_CHOICE, shouldClearData);
409 _shouldSignOut = YES;
410 _shouldClearData = shouldClearData;
411 [self continueSignin];
412 }
413
414 - (void)didChooseCancel {
415 [self cancelFlow];
416 }
417
418 - (void)didFetchManagedStatus:(NSString*)hostedDomain {
419 DCHECK_EQ(FETCH_MANAGED_STATUS, _state);
420 _shouldShowManagedConfirmation = [hostedDomain length] > 0;
421 _identityToSignInHostedDomain.reset([hostedDomain retain]);
422 [self continueSignin];
423 }
424
425 - (void)didFailFetchManagedStatus:(NSError*)error {
426 DCHECK_EQ(FETCH_MANAGED_STATUS, _state);
427 NSError* flowError =
428 [NSError errorWithDomain:kAuthenticationErrorDomain
429 code:AUTHENTICATION_FLOW_ERROR
430 userInfo:@{
431 NSLocalizedDescriptionKey :
432 l10n_util::GetNSString(IDS_IOS_SIGN_IN_FAILED),
433 NSUnderlyingErrorKey : error
434 }];
435 [self handleAuthenticationError:flowError];
436 }
437
438 - (void)didAcceptManagedConfirmation {
439 [self continueSignin];
440 }
441
442 - (void)didCancelManagedConfirmation {
443 [self cancelFlow];
444 }
445
446 - (UIViewController*)presentingViewController {
447 return _presentingViewController;
448 }
449
450 #pragma mark - Used for testing
451
452 - (void)setPerformerForTesting:(AuthenticationFlowPerformer*)performer {
453 _performer.reset([performer retain]);
454 }
455
456 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698