1// Copyright 2015 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 <memory> 8 9#include "base/bind.h" 10#include "base/memory/ptr_util.h" 11#import "base/test/ios/wait_util.h" 12#include "components/pref_registry/pref_registry_syncable.h" 13#include "components/sync_preferences/pref_service_mock_factory.h" 14#include "components/sync_preferences/pref_service_syncable.h" 15#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h" 16#include "ios/chrome/browser/main/test_browser.h" 17#include "ios/chrome/browser/prefs/browser_prefs.h" 18#include "ios/chrome/browser/signin/authentication_service_factory.h" 19#import "ios/chrome/browser/signin/authentication_service_fake.h" 20#import "ios/chrome/browser/ui/authentication/authentication_flow_performer.h" 21#include "ios/public/provider/chrome/browser/signin/fake_chrome_identity_service.h" 22#include "ios/web/public/test/web_task_environment.h" 23#import "testing/gtest_mac.h" 24#import "testing/platform_test.h" 25#import "third_party/ocmock/gtest_support.h" 26#import "third_party/ocmock/ocmock_extensions.h" 27#include "ui/base/l10n/l10n_util.h" 28 29#if !defined(__has_feature) || !__has_feature(objc_arc) 30#error "This file requires ARC support." 31#endif 32 33namespace { 34 35class AuthenticationFlowTest : public PlatformTest { 36 protected: 37 void SetUp() override { 38 PlatformTest::SetUp(); 39 40 TestChromeBrowserState::Builder builder; 41 builder.AddTestingFactory( 42 AuthenticationServiceFactory::GetInstance(), 43 base::BindRepeating( 44 &AuthenticationServiceFake::CreateAuthenticationService)); 45 builder.SetPrefService(CreatePrefService()); 46 browser_state_ = builder.Build(); 47 WebStateList* web_state_list = nullptr; 48 browser_ = 49 std::make_unique<TestBrowser>(browser_state_.get(), web_state_list); 50 51 ios::FakeChromeIdentityService* identityService = 52 ios::FakeChromeIdentityService::GetInstanceFromChromeProvider(); 53 identityService->AddIdentities(@[ @"identity1", @"identity2" ]); 54 identity1_ = 55 [identityService->GetAllIdentitiesSortedForDisplay() objectAtIndex:0]; 56 identity2_ = 57 [identityService->GetAllIdentitiesSortedForDisplay() objectAtIndex:1]; 58 sign_in_completion_ = ^(BOOL success) { 59 finished_ = true; 60 signed_in_success_ = success; 61 }; 62 finished_ = false; 63 signed_in_success_ = false; 64 } 65 66 std::unique_ptr<sync_preferences::PrefServiceSyncable> CreatePrefService() { 67 sync_preferences::PrefServiceMockFactory factory; 68 scoped_refptr<user_prefs::PrefRegistrySyncable> registry( 69 new user_prefs::PrefRegistrySyncable); 70 std::unique_ptr<sync_preferences::PrefServiceSyncable> prefs = 71 factory.CreateSyncable(registry.get()); 72 RegisterBrowserStatePrefs(registry.get()); 73 return prefs; 74 } 75 76 AuthenticationFlowPerformer* GetAuthenticationFlowPerformer() { 77 return static_cast<AuthenticationFlowPerformer*>(performer_); 78 } 79 80 // Creates a new AuthenticationFlow with default values for fields that are 81 // not directly useful. 82 void CreateAuthenticationFlow(ShouldClearData shouldClearData, 83 PostSignInAction postSignInAction) { 84 ChromeIdentity* identity = identity1_; 85 view_controller_ = [OCMockObject niceMockForClass:[UIViewController class]]; 86 authentication_flow_ = 87 [[AuthenticationFlow alloc] initWithBrowser:browser_.get() 88 identity:identity 89 shouldClearData:shouldClearData 90 postSignInAction:postSignInAction 91 presentingViewController:view_controller_]; 92 performer_ = 93 [OCMockObject mockForClass:[AuthenticationFlowPerformer class]]; 94 [authentication_flow_ 95 setPerformerForTesting:GetAuthenticationFlowPerformer()]; 96 } 97 98 // Checks if the AuthenticationFlow operation has completed, and whether it 99 // was successful. 100 void CheckSignInCompletion(bool expectedSignedIn) { 101 base::test::ios::WaitUntilCondition(^bool { 102 return finished_; 103 }); 104 EXPECT_EQ(true, finished_); 105 EXPECT_EQ(expectedSignedIn, signed_in_success_); 106 [performer_ verify]; 107 } 108 109 web::WebTaskEnvironment task_environment_; 110 AuthenticationFlow* authentication_flow_; 111 std::unique_ptr<TestChromeBrowserState> browser_state_; 112 std::unique_ptr<Browser> browser_; 113 ChromeIdentity* identity1_; 114 ChromeIdentity* identity2_; 115 OCMockObject* performer_; 116 signin_ui::CompletionCallback sign_in_completion_; 117 UIViewController* view_controller_; 118 119 // State of the flow 120 bool finished_; 121 bool signed_in_success_; 122}; 123 124// Tests a Sign In of a normal account on the same profile, merging user data 125// and showing the sync settings. 126TEST_F(AuthenticationFlowTest, TestSignInSimple) { 127 CreateAuthenticationFlow(SHOULD_CLEAR_DATA_MERGE_DATA, 128 POST_SIGNIN_ACTION_START_SYNC); 129 130 [[[performer_ expect] andDo:^(NSInvocation*) { 131 [authentication_flow_ didFetchManagedStatus:nil]; 132 }] fetchManagedStatus:browser_state_.get() 133 forIdentity:identity1_]; 134 135 [[[performer_ expect] andReturnBool:NO] 136 shouldHandleMergeCaseForIdentity:identity1_ 137 browserState:browser_state_.get()]; 138 139 [[performer_ expect] signInIdentity:identity1_ 140 withHostedDomain:nil 141 toBrowserState:browser_state_.get()]; 142 143 [[performer_ expect] commitSyncForBrowserState:browser_state_.get()]; 144 145 [authentication_flow_ startSignInWithCompletion:sign_in_completion_]; 146 147 CheckSignInCompletion(true); 148} 149 150// Tests that signing in an already signed in account correctly signs it out 151// and back in. 152TEST_F(AuthenticationFlowTest, TestAlreadySignedIn) { 153 CreateAuthenticationFlow(SHOULD_CLEAR_DATA_MERGE_DATA, 154 POST_SIGNIN_ACTION_NONE); 155 156 [[[performer_ expect] andDo:^(NSInvocation*) { 157 [authentication_flow_ didFetchManagedStatus:nil]; 158 }] fetchManagedStatus:browser_state_.get() 159 forIdentity:identity1_]; 160 161 [[[performer_ expect] andReturnBool:NO] 162 shouldHandleMergeCaseForIdentity:identity1_ 163 browserState:browser_state_.get()]; 164 165 [[[performer_ expect] andDo:^(NSInvocation*) { 166 [authentication_flow_ didSignOut]; 167 }] signOutBrowserState:browser_state_.get()]; 168 169 [[performer_ expect] signInIdentity:identity1_ 170 withHostedDomain:nil 171 toBrowserState:browser_state_.get()]; 172 173 AuthenticationServiceFactory::GetForBrowserState(browser_state_.get()) 174 ->SignIn(identity1_); 175 [authentication_flow_ startSignInWithCompletion:sign_in_completion_]; 176 177 CheckSignInCompletion(true); 178} 179 180// Tests a Sign In of a different account, requiring a sign out of the already 181// signed in account, and asking the user whether data should be cleared or 182// merged. 183TEST_F(AuthenticationFlowTest, TestSignOutUserChoice) { 184 CreateAuthenticationFlow(SHOULD_CLEAR_DATA_USER_CHOICE, 185 POST_SIGNIN_ACTION_START_SYNC); 186 187 [[[performer_ expect] andDo:^(NSInvocation*) { 188 [authentication_flow_ didFetchManagedStatus:nil]; 189 }] fetchManagedStatus:browser_state_.get() 190 forIdentity:identity1_]; 191 192 [[[performer_ expect] andReturnBool:YES] 193 shouldHandleMergeCaseForIdentity:identity1_ 194 browserState:browser_state_.get()]; 195 196 [[[performer_ expect] andDo:^(NSInvocation*) { 197 [authentication_flow_ 198 didChooseClearDataPolicy:SHOULD_CLEAR_DATA_CLEAR_DATA]; 199 }] promptMergeCaseForIdentity:identity1_ 200 browser:browser_.get() 201 viewController:view_controller_]; 202 203 [[[performer_ expect] andDo:^(NSInvocation*) { 204 [authentication_flow_ didSignOut]; 205 }] signOutBrowserState:browser_state_.get()]; 206 207 [[[performer_ expect] andDo:^(NSInvocation*) { 208 [authentication_flow_ didClearData]; 209 }] clearDataFromBrowser:browser_.get() commandHandler:nil]; 210 211 [[performer_ expect] signInIdentity:identity1_ 212 withHostedDomain:nil 213 toBrowserState:browser_state_.get()]; 214 215 [[performer_ expect] commitSyncForBrowserState:browser_state_.get()]; 216 217 AuthenticationServiceFactory::GetForBrowserState(browser_state_.get()) 218 ->SignIn(identity2_); 219 [authentication_flow_ startSignInWithCompletion:sign_in_completion_]; 220 221 CheckSignInCompletion(true); 222} 223 224// Tests the cancelling of a Sign In. 225TEST_F(AuthenticationFlowTest, TestCancel) { 226 CreateAuthenticationFlow(SHOULD_CLEAR_DATA_USER_CHOICE, 227 POST_SIGNIN_ACTION_START_SYNC); 228 229 [[[performer_ expect] andDo:^(NSInvocation*) { 230 [authentication_flow_ didFetchManagedStatus:nil]; 231 }] fetchManagedStatus:browser_state_.get() 232 forIdentity:identity1_]; 233 234 [[[performer_ expect] andReturnBool:YES] 235 shouldHandleMergeCaseForIdentity:identity1_ 236 browserState:browser_state_.get()]; 237 238 [[[performer_ expect] andDo:^(NSInvocation*) { 239 [authentication_flow_ cancelAndDismissAnimated:NO]; 240 }] promptMergeCaseForIdentity:identity1_ 241 browser:browser_.get() 242 viewController:view_controller_]; 243 244 [[performer_ expect] cancelAndDismissAnimated:NO]; 245 246 [authentication_flow_ startSignInWithCompletion:sign_in_completion_]; 247 248 CheckSignInCompletion(false); 249} 250 251// Tests the fetch managed status failure case. 252TEST_F(AuthenticationFlowTest, TestFailFetchManagedStatus) { 253 CreateAuthenticationFlow(SHOULD_CLEAR_DATA_MERGE_DATA, 254 POST_SIGNIN_ACTION_START_SYNC); 255 256 NSError* error = [NSError errorWithDomain:@"foo" code:0 userInfo:nil]; 257 [[[performer_ expect] andDo:^(NSInvocation*) { 258 [authentication_flow_ didFailFetchManagedStatus:error]; 259 }] fetchManagedStatus:browser_state_.get() 260 forIdentity:identity1_]; 261 262 [[[performer_ expect] andDo:^(NSInvocation* invocation) { 263 __unsafe_unretained ProceduralBlock completionBlock; 264 [invocation getArgument:&completionBlock atIndex:3]; 265 completionBlock(); 266 }] showAuthenticationError:[OCMArg any] 267 withCompletion:[OCMArg any] 268 viewController:view_controller_ 269 browser:browser_.get()]; 270 271 [authentication_flow_ startSignInWithCompletion:sign_in_completion_]; 272 273 CheckSignInCompletion(false); 274} 275 276// Tests the managed sign in confirmation dialog is shown when signing in to 277// a managed identity. 278TEST_F(AuthenticationFlowTest, TestShowManagedConfirmation) { 279 CreateAuthenticationFlow(SHOULD_CLEAR_DATA_CLEAR_DATA, 280 POST_SIGNIN_ACTION_START_SYNC); 281 282 [[[performer_ expect] andDo:^(NSInvocation*) { 283 [authentication_flow_ didFetchManagedStatus:@"foo.com"]; 284 }] fetchManagedStatus:browser_state_.get() 285 forIdentity:identity1_]; 286 287 [[[performer_ expect] andReturnBool:NO] 288 shouldHandleMergeCaseForIdentity:identity1_ 289 browserState:browser_state_.get()]; 290 291 [[[performer_ expect] andDo:^(NSInvocation*) { 292 [authentication_flow_ didAcceptManagedConfirmation]; 293 }] showManagedConfirmationForHostedDomain:@"foo.com" 294 viewController:view_controller_ 295 browser:browser_.get()]; 296 297 [[[performer_ expect] andDo:^(NSInvocation*) { 298 [authentication_flow_ didClearData]; 299 }] clearDataFromBrowser:browser_.get() commandHandler:nil]; 300 301 [[performer_ expect] signInIdentity:identity1_ 302 withHostedDomain:@"foo.com" 303 toBrowserState:browser_state_.get()]; 304 305 [[performer_ expect] commitSyncForBrowserState:browser_state_.get()]; 306 307 [authentication_flow_ startSignInWithCompletion:sign_in_completion_]; 308 309 CheckSignInCompletion(true); 310} 311 312} // namespace 313