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