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