1 // Copyright (c) 2012 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 #include "chrome/browser/signin/signin_tracker.h" 6 7 #include "chrome/browser/chrome_notification_types.h" 8 #include "chrome/browser/profiles/profile.h" 9 #include "chrome/browser/signin/signin_manager.h" 10 #include "chrome/browser/signin/signin_manager_factory.h" 11 #include "chrome/browser/signin/token_service.h" 12 #include "chrome/browser/signin/token_service_factory.h" 13 #include "content/public/browser/notification_details.h" 14 #include "content/public/browser/notification_source.h" 15 #include "google_apis/gaia/gaia_constants.h" 16 17 static const char* kSignedInServices[] = { 18 GaiaConstants::kSyncService, 19 GaiaConstants::kGaiaOAuth2LoginRefreshToken 20 }; 21 static const int kNumSignedInServices = 22 arraysize(kSignedInServices); 23 24 // Helper to check if the given token service is relevant for sync. 25 SigninTracker::SigninTracker(Profile* profile, Observer* observer) 26 : state_(WAITING_FOR_GAIA_VALIDATION), 27 profile_(profile), 28 observer_(observer), 29 credentials_valid_(false) { 30 Initialize(); 31 } 32 33 SigninTracker::~SigninTracker() { 34 } 35 36 void SigninTracker::Initialize() { 37 DCHECK(observer_); 38 // Register for notifications from the SigninManager. 39 registrar_.Add(this, 40 chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL, 41 content::Source<Profile>(profile_)); 42 registrar_.Add(this, 43 chrome::NOTIFICATION_GOOGLE_SIGNIN_FAILED, 44 content::Source<Profile>(profile_)); 45 TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); 46 registrar_.Add(this, 47 chrome::NOTIFICATION_TOKEN_AVAILABLE, 48 content::Source<TokenService>(token_service)); 49 registrar_.Add(this, 50 chrome::NOTIFICATION_TOKEN_REQUEST_FAILED, 51 content::Source<TokenService>(token_service)); 52 53 if (state_ == SERVICES_INITIALIZING) 54 HandleServiceStateChange(); 55 } 56 57 void SigninTracker::Observe(int type, 58 const content::NotificationSource& source, 59 const content::NotificationDetails& details) { 60 // We should not get more than one of these notifications. 61 switch (type) { 62 case chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL: 63 DCHECK_EQ(state_, WAITING_FOR_GAIA_VALIDATION); 64 state_ = SERVICES_INITIALIZING; 65 // If our services are already signed in, see if it's possible to 66 // transition to the SIGNIN_COMPLETE state. 67 HandleServiceStateChange(); 68 break; 69 case chrome::NOTIFICATION_GOOGLE_SIGNIN_FAILED: { 70 DCHECK_EQ(state_, WAITING_FOR_GAIA_VALIDATION); 71 const GoogleServiceAuthError& error = 72 *(content::Details<const GoogleServiceAuthError>(details).ptr()); 73 observer_->SigninFailed(error); 74 break; 75 } 76 case chrome::NOTIFICATION_TOKEN_AVAILABLE: 77 // A new token is available - check to see if we're all signed in now. 78 HandleServiceStateChange(); 79 break; 80 case chrome::NOTIFICATION_TOKEN_REQUEST_FAILED: 81 if (state_ == SERVICES_INITIALIZING) { 82 const TokenService::TokenRequestFailedDetails& token_details = 83 *(content::Details<const TokenService::TokenRequestFailedDetails>( 84 details).ptr()); 85 for (int i = 0; i < kNumSignedInServices; ++i) { 86 if (token_details.service() == kSignedInServices[i]) { 87 // We got an error loading one of our tokens, so notify our 88 // observer. 89 state_ = WAITING_FOR_GAIA_VALIDATION; 90 observer_->SigninFailed(token_details.error()); 91 } 92 } 93 } 94 break; 95 default: 96 NOTREACHED(); 97 } 98 } 99 100 void SigninTracker::HandleServiceStateChange() { 101 if (state_ != SERVICES_INITIALIZING) { 102 // Ignore service updates until after our GAIA credentials are validated. 103 return; 104 } 105 106 GoogleServiceAuthError error(GoogleServiceAuthError::NONE); 107 state_ = GetSigninState(profile_, &error); 108 switch (state_) { 109 case WAITING_FOR_GAIA_VALIDATION: 110 observer_->SigninFailed(error); 111 break; 112 case SIGNIN_COMPLETE: 113 observer_->SigninSuccess(); 114 break; 115 default: 116 // State has not changed, nothing to do. 117 DCHECK_EQ(state_, SERVICES_INITIALIZING); 118 break; 119 } 120 } 121 122 // static 123 bool SigninTracker::AreServiceTokensLoaded(Profile* profile) { 124 // See if we have all of the tokens required. 125 TokenService* token_service = TokenServiceFactory::GetForProfile(profile); 126 for (int i = 0; i < kNumSignedInServices; ++i) { 127 if (!token_service->HasTokenForService(kSignedInServices[i])) { 128 // Don't have a token for one of our signed-in services. 129 return false; 130 } 131 } 132 return true; 133 } 134 135 // static 136 SigninTracker::LoginState SigninTracker::GetSigninState( 137 Profile* profile, 138 GoogleServiceAuthError* error) { 139 SigninManagerBase* signin = SigninManagerFactory::GetForProfile(profile); 140 if (signin->GetAuthenticatedUsername().empty()) { 141 // User is signed out, trigger a signin failure. 142 if (error) 143 *error = GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED); 144 return WAITING_FOR_GAIA_VALIDATION; 145 } 146 147 // If we haven't loaded all our service tokens yet, just exit (we'll be called 148 // again when another token is loaded, or will transition to SigninFailed if 149 // the loading fails). 150 if (!AreServiceTokensLoaded(profile)) 151 return SERVICES_INITIALIZING; 152 153 return SIGNIN_COMPLETE; 154 } 155