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 #include "components/signin/core/browser/signin_manager.h" 6 7 #include <string> 8 #include <vector> 9 10 #include "base/metrics/histogram.h" 11 #include "base/prefs/pref_service.h" 12 #include "base/strings/string_split.h" 13 #include "base/strings/string_util.h" 14 #include "base/strings/utf_string_conversions.h" 15 #include "base/time/time.h" 16 #include "components/signin/core/browser/profile_oauth2_token_service.h" 17 #include "components/signin/core/browser/signin_account_id_helper.h" 18 #include "components/signin/core/browser/signin_client.h" 19 #include "components/signin/core/browser/signin_internals_util.h" 20 #include "components/signin/core/browser/signin_manager_cookie_helper.h" 21 #include "components/signin/core/browser/signin_metrics.h" 22 #include "components/signin/core/common/signin_pref_names.h" 23 #include "google_apis/gaia/gaia_auth_util.h" 24 #include "google_apis/gaia/gaia_urls.h" 25 #include "net/base/escape.h" 26 #include "third_party/icu/source/i18n/unicode/regex.h" 27 28 using namespace signin_internals_util; 29 30 namespace { 31 32 const char kChromiumSyncService[] = "service=chromiumsync"; 33 34 } // namespace 35 36 // Under the covers, we use a dummy chrome-extension ID to serve the purposes 37 // outlined in the .h file comment for this string. 38 const char SigninManager::kChromeSigninEffectiveSite[] = 39 "chrome-extension://acfccoigjajmmgbhpfbjnpckhjjegnih"; 40 41 // static 42 bool SigninManager::IsWebBasedSigninFlowURL(const GURL& url) { 43 GURL effective(kChromeSigninEffectiveSite); 44 if (url.SchemeIs(effective.scheme().c_str()) && 45 url.host() == effective.host()) { 46 return true; 47 } 48 49 GURL service_login(GaiaUrls::GetInstance()->service_login_url()); 50 if (url.GetOrigin() != service_login.GetOrigin()) 51 return false; 52 53 // Any login UI URLs with signin=chromiumsync should be considered a web 54 // URL (relies on GAIA keeping the "service=chromiumsync" query string 55 // fragment present even when embedding inside a "continue" parameter). 56 return net::UnescapeURLComponent(url.query(), 57 net::UnescapeRule::URL_SPECIAL_CHARS) 58 .find(kChromiumSyncService) != std::string::npos; 59 } 60 61 SigninManager::SigninManager(SigninClient* client, 62 ProfileOAuth2TokenService* token_service) 63 : SigninManagerBase(client), 64 prohibit_signout_(false), 65 type_(SIGNIN_TYPE_NONE), 66 client_(client), 67 token_service_(token_service), 68 weak_pointer_factory_(this) {} 69 70 void SigninManager::AddMergeSessionObserver( 71 MergeSessionHelper::Observer* observer) { 72 if (merge_session_helper_) 73 merge_session_helper_->AddObserver(observer); 74 } 75 76 void SigninManager::RemoveMergeSessionObserver( 77 MergeSessionHelper::Observer* observer) { 78 if (merge_session_helper_) 79 merge_session_helper_->RemoveObserver(observer); 80 } 81 82 SigninManager::~SigninManager() {} 83 84 void SigninManager::InitTokenService() { 85 const std::string& account_id = GetAuthenticatedUsername(); 86 if (token_service_ && !account_id.empty()) 87 token_service_->LoadCredentials(account_id); 88 } 89 90 std::string SigninManager::SigninTypeToString(SigninManager::SigninType type) { 91 switch (type) { 92 case SIGNIN_TYPE_NONE: 93 return "No Signin"; 94 case SIGNIN_TYPE_WITH_REFRESH_TOKEN: 95 return "Signin with refresh token"; 96 } 97 98 NOTREACHED(); 99 return std::string(); 100 } 101 102 bool SigninManager::PrepareForSignin(SigninType type, 103 const std::string& username, 104 const std::string& password) { 105 DCHECK(possibly_invalid_username_.empty() || 106 possibly_invalid_username_ == username); 107 DCHECK(!username.empty()); 108 109 if (!IsAllowedUsername(username)) { 110 // Account is not allowed by admin policy. 111 HandleAuthError( 112 GoogleServiceAuthError(GoogleServiceAuthError::ACCOUNT_DISABLED)); 113 return false; 114 } 115 116 // This attempt is either 1) the user trying to establish initial sync, or 117 // 2) trying to refresh credentials for an existing username. If it is 2, we 118 // need to try again, but take care to leave state around tracking that the 119 // user has successfully signed in once before with this username, so that on 120 // restart we don't think sync setup has never completed. 121 ClearTransientSigninData(); 122 type_ = type; 123 possibly_invalid_username_.assign(username); 124 password_.assign(password); 125 NotifyDiagnosticsObservers(SIGNIN_TYPE, SigninTypeToString(type)); 126 return true; 127 } 128 129 void SigninManager::StartSignInWithRefreshToken( 130 const std::string& refresh_token, 131 const std::string& username, 132 const std::string& password, 133 const OAuthTokenFetchedCallback& callback) { 134 DCHECK(!IsAuthenticated() || 135 gaia::AreEmailsSame(username, GetAuthenticatedUsername())); 136 137 if (!PrepareForSignin(SIGNIN_TYPE_WITH_REFRESH_TOKEN, username, password)) 138 return; 139 140 // Store our callback and token. 141 temp_refresh_token_ = refresh_token; 142 possibly_invalid_username_ = username; 143 144 NotifyDiagnosticsObservers(GET_USER_INFO_STATUS, "Successful"); 145 146 if (!callback.is_null() && !temp_refresh_token_.empty()) { 147 callback.Run(temp_refresh_token_); 148 } else { 149 // No oauth token or callback, so just complete our pending signin. 150 CompletePendingSignin(); 151 } 152 } 153 154 void SigninManager::CopyCredentialsFrom(const SigninManager& source) { 155 DCHECK_NE(this, &source); 156 possibly_invalid_username_ = source.possibly_invalid_username_; 157 temp_refresh_token_ = source.temp_refresh_token_; 158 password_ = source.password_; 159 } 160 161 void SigninManager::ClearTransientSigninData() { 162 DCHECK(IsInitialized()); 163 164 possibly_invalid_username_.clear(); 165 password_.clear(); 166 type_ = SIGNIN_TYPE_NONE; 167 temp_refresh_token_.clear(); 168 } 169 170 void SigninManager::HandleAuthError(const GoogleServiceAuthError& error) { 171 ClearTransientSigninData(); 172 173 FOR_EACH_OBSERVER(Observer, observer_list_, GoogleSigninFailed(error)); 174 } 175 176 void SigninManager::SignOut( 177 signin_metrics::ProfileSignout signout_source_metric) { 178 DCHECK(IsInitialized()); 179 180 signin_metrics::LogSignout(signout_source_metric); 181 if (!IsAuthenticated()) { 182 if (AuthInProgress()) { 183 // If the user is in the process of signing in, then treat a call to 184 // SignOut as a cancellation request. 185 GoogleServiceAuthError error(GoogleServiceAuthError::REQUEST_CANCELED); 186 HandleAuthError(error); 187 } else { 188 // Clean up our transient data and exit if we aren't signed in. 189 // This avoids a perf regression from clearing out the TokenDB if 190 // SignOut() is invoked on startup to clean up any incomplete previous 191 // signin attempts. 192 ClearTransientSigninData(); 193 } 194 return; 195 } 196 197 if (prohibit_signout_) { 198 DVLOG(1) << "Ignoring attempt to sign out while signout is prohibited"; 199 return; 200 } 201 202 ClearTransientSigninData(); 203 204 const std::string account_id = GetAuthenticatedAccountId(); 205 const std::string username = GetAuthenticatedUsername(); 206 const base::Time signin_time = 207 base::Time::FromInternalValue( 208 client_->GetPrefs()->GetInt64(prefs::kSignedInTime)); 209 clear_authenticated_username(); 210 client_->GetPrefs()->ClearPref(prefs::kGoogleServicesUsername); 211 client_->GetPrefs()->ClearPref(prefs::kSignedInTime); 212 client_->ClearSigninScopedDeviceId(); 213 214 // Erase (now) stale information from AboutSigninInternals. 215 NotifyDiagnosticsObservers(USERNAME, ""); 216 217 // Determine the duration the user was logged in and log that to UMA. 218 if (!signin_time.is_null()) { 219 base::TimeDelta signed_in_duration = base::Time::Now() - signin_time; 220 UMA_HISTOGRAM_COUNTS("Signin.SignedInDurationBeforeSignout", 221 signed_in_duration.InMinutes()); 222 } 223 224 // Revoke all tokens before sending signed_out notification, because there 225 // may be components that don't listen for token service events when the 226 // profile is not connected to an account. 227 LOG(WARNING) << "Revoking refresh token on server. Reason: sign out, " 228 << "IsSigninAllowed: " << IsSigninAllowed(); 229 token_service_->RevokeAllCredentials(); 230 231 FOR_EACH_OBSERVER(Observer, 232 observer_list_, 233 GoogleSignedOut(account_id, username)); 234 } 235 236 void SigninManager::Initialize(PrefService* local_state) { 237 SigninManagerBase::Initialize(local_state); 238 239 // local_state can be null during unit tests. 240 if (local_state) { 241 local_state_pref_registrar_.Init(local_state); 242 local_state_pref_registrar_.Add( 243 prefs::kGoogleServicesUsernamePattern, 244 base::Bind(&SigninManager::OnGoogleServicesUsernamePatternChanged, 245 weak_pointer_factory_.GetWeakPtr())); 246 } 247 signin_allowed_.Init(prefs::kSigninAllowed, 248 client_->GetPrefs(), 249 base::Bind(&SigninManager::OnSigninAllowedPrefChanged, 250 base::Unretained(this))); 251 252 std::string user = 253 client_->GetPrefs()->GetString(prefs::kGoogleServicesUsername); 254 if ((!user.empty() && !IsAllowedUsername(user)) || !IsSigninAllowed()) { 255 // User is signed in, but the username is invalid - the administrator must 256 // have changed the policy since the last signin, so sign out the user. 257 SignOut(signin_metrics::SIGNIN_PREF_CHANGED_DURING_SIGNIN); 258 } 259 260 InitTokenService(); 261 account_id_helper_.reset( 262 new SigninAccountIdHelper(client_, token_service_, this)); 263 } 264 265 void SigninManager::Shutdown() { 266 if (merge_session_helper_) 267 merge_session_helper_->CancelAll(); 268 269 local_state_pref_registrar_.RemoveAll(); 270 account_id_helper_.reset(); 271 SigninManagerBase::Shutdown(); 272 } 273 274 void SigninManager::OnGoogleServicesUsernamePatternChanged() { 275 if (IsAuthenticated() && 276 !IsAllowedUsername(GetAuthenticatedUsername())) { 277 // Signed in user is invalid according to the current policy so sign 278 // the user out. 279 SignOut(signin_metrics::GOOGLE_SERVICE_NAME_PATTERN_CHANGED); 280 } 281 } 282 283 bool SigninManager::IsSigninAllowed() const { 284 return signin_allowed_.GetValue(); 285 } 286 287 void SigninManager::OnSigninAllowedPrefChanged() { 288 if (!IsSigninAllowed()) 289 SignOut(signin_metrics::SIGNOUT_PREF_CHANGED); 290 } 291 292 // static 293 bool SigninManager::IsUsernameAllowedByPolicy(const std::string& username, 294 const std::string& policy) { 295 if (policy.empty()) 296 return true; 297 298 // Patterns like "*@foo.com" are not accepted by our regex engine (since they 299 // are not valid regular expressions - they should instead be ".*@foo.com"). 300 // For convenience, detect these patterns and insert a "." character at the 301 // front. 302 base::string16 pattern = base::UTF8ToUTF16(policy); 303 if (pattern[0] == L'*') 304 pattern.insert(pattern.begin(), L'.'); 305 306 // See if the username matches the policy-provided pattern. 307 UErrorCode status = U_ZERO_ERROR; 308 const icu::UnicodeString icu_pattern(pattern.data(), pattern.length()); 309 icu::RegexMatcher matcher(icu_pattern, UREGEX_CASE_INSENSITIVE, status); 310 if (!U_SUCCESS(status)) { 311 LOG(ERROR) << "Invalid login regex: " << pattern << ", status: " << status; 312 // If an invalid pattern is provided, then prohibit *all* logins (better to 313 // break signin than to quietly allow users to sign in). 314 return false; 315 } 316 base::string16 username16 = base::UTF8ToUTF16(username); 317 icu::UnicodeString icu_input(username16.data(), username16.length()); 318 matcher.reset(icu_input); 319 status = U_ZERO_ERROR; 320 UBool match = matcher.matches(status); 321 DCHECK(U_SUCCESS(status)); 322 return !!match; // !! == convert from UBool to bool. 323 } 324 325 bool SigninManager::IsAllowedUsername(const std::string& username) const { 326 const PrefService* local_state = local_state_pref_registrar_.prefs(); 327 if (!local_state) 328 return true; // In a unit test with no local state - all names are allowed. 329 330 std::string pattern = 331 local_state->GetString(prefs::kGoogleServicesUsernamePattern); 332 return IsUsernameAllowedByPolicy(username, pattern); 333 } 334 335 bool SigninManager::AuthInProgress() const { 336 return !possibly_invalid_username_.empty(); 337 } 338 339 const std::string& SigninManager::GetUsernameForAuthInProgress() const { 340 return possibly_invalid_username_; 341 } 342 343 void SigninManager::DisableOneClickSignIn(PrefService* prefs) { 344 prefs->SetBoolean(prefs::kReverseAutologinEnabled, false); 345 } 346 347 void SigninManager::CompletePendingSignin() { 348 DCHECK(!possibly_invalid_username_.empty()); 349 OnSignedIn(possibly_invalid_username_); 350 351 if (client_->ShouldMergeSigninCredentialsIntoCookieJar()) { 352 merge_session_helper_.reset(new MergeSessionHelper( 353 token_service_, client_->GetURLRequestContext(), NULL)); 354 } 355 356 DCHECK(!temp_refresh_token_.empty()); 357 DCHECK(IsAuthenticated()); 358 token_service_->UpdateCredentials(GetAuthenticatedUsername(), 359 temp_refresh_token_); 360 temp_refresh_token_.clear(); 361 362 if (client_->ShouldMergeSigninCredentialsIntoCookieJar()) 363 merge_session_helper_->LogIn(GetAuthenticatedUsername()); 364 } 365 366 void SigninManager::OnExternalSigninCompleted(const std::string& username) { 367 OnSignedIn(username); 368 } 369 370 void SigninManager::OnSignedIn(const std::string& username) { 371 client_->GetPrefs()->SetInt64(prefs::kSignedInTime, 372 base::Time::Now().ToInternalValue()); 373 SetAuthenticatedUsername(username); 374 possibly_invalid_username_.clear(); 375 376 FOR_EACH_OBSERVER( 377 Observer, 378 observer_list_, 379 GoogleSigninSucceeded(GetAuthenticatedAccountId(), 380 GetAuthenticatedUsername(), 381 password_)); 382 383 client_->GoogleSigninSucceeded(GetAuthenticatedAccountId(), 384 GetAuthenticatedUsername(), 385 password_); 386 387 signin_metrics::LogSigninProfile(client_->IsFirstRun(), 388 client_->GetInstallDate()); 389 390 password_.clear(); // Don't need it anymore. 391 DisableOneClickSignIn(client_->GetPrefs()); // Don't ever offer again. 392 } 393 394 void SigninManager::ProhibitSignout(bool prohibit_signout) { 395 prohibit_signout_ = prohibit_signout; 396 } 397 398 bool SigninManager::IsSignoutProhibited() const { return prohibit_signout_; } 399