Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / components / signin / core / browser / signin_manager.cc
blobf860777187eb4c59547b3e0b22760e6c020a152c
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.
5 #include "components/signin/core/browser/signin_manager.h"
7 #include <string>
8 #include <vector>
10 #include "base/metrics/histogram_macros.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/account_tracker_service.h"
17 #include "components/signin/core/browser/gaia_cookie_manager_service.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_constants.h"
25 #include "google_apis/gaia/gaia_urls.h"
26 #include "google_apis/gaia/google_service_auth_error.h"
27 #include "third_party/icu/source/i18n/unicode/regex.h"
29 using namespace signin_internals_util;
31 SigninManager::SigninManager(SigninClient* client,
32 ProfileOAuth2TokenService* token_service,
33 AccountTrackerService* account_tracker_service,
34 GaiaCookieManagerService* cookie_manager_service)
35 : SigninManagerBase(client, account_tracker_service),
36 prohibit_signout_(false),
37 type_(SIGNIN_TYPE_NONE),
38 client_(client),
39 token_service_(token_service),
40 cookie_manager_service_(cookie_manager_service),
41 signin_manager_signed_in_(false),
42 user_info_fetched_by_account_tracker_(false),
43 weak_pointer_factory_(this) {}
45 SigninManager::~SigninManager() {}
47 void SigninManager::InitTokenService() {
48 if (token_service_ && IsAuthenticated())
49 token_service_->LoadCredentials(GetAuthenticatedAccountId());
52 std::string SigninManager::SigninTypeToString(SigninManager::SigninType type) {
53 switch (type) {
54 case SIGNIN_TYPE_NONE:
55 return "No Signin";
56 case SIGNIN_TYPE_WITH_REFRESH_TOKEN:
57 return "With refresh token";
60 NOTREACHED();
61 return std::string();
64 bool SigninManager::PrepareForSignin(SigninType type,
65 const std::string& gaia_id,
66 const std::string& username,
67 const std::string& password) {
68 std::string account_id =
69 account_tracker_service()->PickAccountIdForAccount(gaia_id, username);
70 DCHECK(possibly_invalid_account_id_.empty() ||
71 possibly_invalid_account_id_ == account_id);
72 DCHECK(!account_id.empty());
74 if (!IsAllowedUsername(username)) {
75 // Account is not allowed by admin policy.
76 HandleAuthError(
77 GoogleServiceAuthError(GoogleServiceAuthError::ACCOUNT_DISABLED));
78 return false;
81 // This attempt is either 1) the user trying to establish initial sync, or
82 // 2) trying to refresh credentials for an existing username. If it is 2, we
83 // need to try again, but take care to leave state around tracking that the
84 // user has successfully signed in once before with this username, so that on
85 // restart we don't think sync setup has never completed.
86 ClearTransientSigninData();
87 type_ = type;
88 possibly_invalid_account_id_.assign(account_id);
89 possibly_invalid_gaia_id_.assign(gaia_id);
90 possibly_invalid_email_.assign(username);
91 password_.assign(password);
92 signin_manager_signed_in_ = false;
93 user_info_fetched_by_account_tracker_ = false;
94 NotifyDiagnosticsObservers(SIGNIN_STARTED, SigninTypeToString(type));
95 return true;
98 void SigninManager::StartSignInWithRefreshToken(
99 const std::string& refresh_token,
100 const std::string& gaia_id,
101 const std::string& username,
102 const std::string& password,
103 const OAuthTokenFetchedCallback& callback) {
104 DCHECK(!IsAuthenticated());
106 if (!PrepareForSignin(SIGNIN_TYPE_WITH_REFRESH_TOKEN, gaia_id, username,
107 password)) {
108 return;
111 // Store our token.
112 temp_refresh_token_ = refresh_token;
114 if (!callback.is_null() && !temp_refresh_token_.empty()) {
115 callback.Run(temp_refresh_token_);
116 } else {
117 // No oauth token or callback, so just complete our pending signin.
118 CompletePendingSignin();
122 void SigninManager::CopyCredentialsFrom(const SigninManager& source) {
123 DCHECK_NE(this, &source);
124 possibly_invalid_account_id_ = source.possibly_invalid_account_id_;
125 possibly_invalid_gaia_id_ = source.possibly_invalid_gaia_id_;
126 possibly_invalid_email_ = source.possibly_invalid_email_;
127 temp_refresh_token_ = source.temp_refresh_token_;
128 password_ = source.password_;
131 void SigninManager::ClearTransientSigninData() {
132 DCHECK(IsInitialized());
134 possibly_invalid_account_id_.clear();
135 possibly_invalid_gaia_id_.clear();
136 possibly_invalid_email_.clear();
137 password_.clear();
138 type_ = SIGNIN_TYPE_NONE;
139 temp_refresh_token_.clear();
142 void SigninManager::HandleAuthError(const GoogleServiceAuthError& error) {
143 ClearTransientSigninData();
145 FOR_EACH_OBSERVER(SigninManagerBase::Observer,
146 observer_list_,
147 GoogleSigninFailed(error));
150 void SigninManager::SignOut(
151 signin_metrics::ProfileSignout signout_source_metric) {
152 DCHECK(IsInitialized());
154 signin_metrics::LogSignout(signout_source_metric);
155 if (!IsAuthenticated()) {
156 if (AuthInProgress()) {
157 // If the user is in the process of signing in, then treat a call to
158 // SignOut as a cancellation request.
159 GoogleServiceAuthError error(GoogleServiceAuthError::REQUEST_CANCELED);
160 HandleAuthError(error);
161 } else {
162 // Clean up our transient data and exit if we aren't signed in.
163 // This avoids a perf regression from clearing out the TokenDB if
164 // SignOut() is invoked on startup to clean up any incomplete previous
165 // signin attempts.
166 ClearTransientSigninData();
168 return;
171 if (prohibit_signout_) {
172 DVLOG(1) << "Ignoring attempt to sign out while signout is prohibited";
173 return;
176 ClearTransientSigninData();
178 const std::string account_id = GetAuthenticatedAccountId();
179 const std::string username = GetAuthenticatedAccountInfo().email;
180 const base::Time signin_time =
181 base::Time::FromInternalValue(
182 client_->GetPrefs()->GetInt64(prefs::kSignedInTime));
183 clear_authenticated_user();
184 client_->GetPrefs()->ClearPref(prefs::kGoogleServicesHostedDomain);
185 client_->GetPrefs()->ClearPref(prefs::kGoogleServicesAccountId);
186 client_->GetPrefs()->ClearPref(prefs::kGoogleServicesUserAccountId);
187 client_->GetPrefs()->ClearPref(prefs::kSignedInTime);
188 client_->SignOut();
190 // Determine the duration the user was logged in and log that to UMA.
191 if (!signin_time.is_null()) {
192 base::TimeDelta signed_in_duration = base::Time::Now() - signin_time;
193 UMA_HISTOGRAM_COUNTS("Signin.SignedInDurationBeforeSignout",
194 signed_in_duration.InMinutes());
197 // Revoke all tokens before sending signed_out notification, because there
198 // may be components that don't listen for token service events when the
199 // profile is not connected to an account.
200 LOG(WARNING) << "Revoking refresh token on server. Reason: sign out, "
201 << "IsSigninAllowed: " << IsSigninAllowed();
202 token_service_->RevokeAllCredentials();
204 FOR_EACH_OBSERVER(SigninManagerBase::Observer,
205 observer_list_,
206 GoogleSignedOut(account_id, username));
209 void SigninManager::Initialize(PrefService* local_state) {
210 SigninManagerBase::Initialize(local_state);
212 // local_state can be null during unit tests.
213 if (local_state) {
214 local_state_pref_registrar_.Init(local_state);
215 local_state_pref_registrar_.Add(
216 prefs::kGoogleServicesUsernamePattern,
217 base::Bind(&SigninManager::OnGoogleServicesUsernamePatternChanged,
218 weak_pointer_factory_.GetWeakPtr()));
220 signin_allowed_.Init(prefs::kSigninAllowed,
221 client_->GetPrefs(),
222 base::Bind(&SigninManager::OnSigninAllowedPrefChanged,
223 base::Unretained(this)));
225 std::string account_id =
226 client_->GetPrefs()->GetString(prefs::kGoogleServicesAccountId);
227 std::string user = account_id.empty() ? std::string() :
228 account_tracker_service()->GetAccountInfo(account_id).email;
229 if ((!account_id.empty() && !IsAllowedUsername(user)) || !IsSigninAllowed()) {
230 // User is signed in, but the username is invalid - the administrator must
231 // have changed the policy since the last signin, so sign out the user.
232 SignOut(signin_metrics::SIGNIN_PREF_CHANGED_DURING_SIGNIN);
235 if (account_tracker_service()->GetMigrationState() ==
236 AccountTrackerService::MIGRATION_IN_PROGRESS) {
237 token_service_->AddObserver(this);
239 InitTokenService();
240 account_tracker_service()->AddObserver(this);
243 void SigninManager::Shutdown() {
244 account_tracker_service()->RemoveObserver(this);
245 local_state_pref_registrar_.RemoveAll();
246 SigninManagerBase::Shutdown();
249 void SigninManager::OnGoogleServicesUsernamePatternChanged() {
250 if (IsAuthenticated() &&
251 !IsAllowedUsername(GetAuthenticatedAccountInfo().email)) {
252 // Signed in user is invalid according to the current policy so sign
253 // the user out.
254 SignOut(signin_metrics::GOOGLE_SERVICE_NAME_PATTERN_CHANGED);
258 bool SigninManager::IsSigninAllowed() const {
259 return signin_allowed_.GetValue();
262 void SigninManager::OnSigninAllowedPrefChanged() {
263 if (!IsSigninAllowed())
264 SignOut(signin_metrics::SIGNOUT_PREF_CHANGED);
267 // static
268 bool SigninManager::IsUsernameAllowedByPolicy(const std::string& username,
269 const std::string& policy) {
270 if (policy.empty())
271 return true;
273 // Patterns like "*@foo.com" are not accepted by our regex engine (since they
274 // are not valid regular expressions - they should instead be ".*@foo.com").
275 // For convenience, detect these patterns and insert a "." character at the
276 // front.
277 base::string16 pattern = base::UTF8ToUTF16(policy);
278 if (pattern[0] == L'*')
279 pattern.insert(pattern.begin(), L'.');
281 // See if the username matches the policy-provided pattern.
282 UErrorCode status = U_ZERO_ERROR;
283 const icu::UnicodeString icu_pattern(pattern.data(), pattern.length());
284 icu::RegexMatcher matcher(icu_pattern, UREGEX_CASE_INSENSITIVE, status);
285 if (!U_SUCCESS(status)) {
286 LOG(ERROR) << "Invalid login regex: " << pattern << ", status: " << status;
287 // If an invalid pattern is provided, then prohibit *all* logins (better to
288 // break signin than to quietly allow users to sign in).
289 return false;
291 base::string16 username16 = base::UTF8ToUTF16(username);
292 icu::UnicodeString icu_input(username16.data(), username16.length());
293 matcher.reset(icu_input);
294 status = U_ZERO_ERROR;
295 UBool match = matcher.matches(status);
296 DCHECK(U_SUCCESS(status));
297 return !!match; // !! == convert from UBool to bool.
300 bool SigninManager::IsAllowedUsername(const std::string& username) const {
301 const PrefService* local_state = local_state_pref_registrar_.prefs();
302 if (!local_state)
303 return true; // In a unit test with no local state - all names are allowed.
305 std::string pattern =
306 local_state->GetString(prefs::kGoogleServicesUsernamePattern);
307 return IsUsernameAllowedByPolicy(username, pattern);
310 bool SigninManager::AuthInProgress() const {
311 return !possibly_invalid_account_id_.empty();
314 const std::string& SigninManager::GetAccountIdForAuthInProgress() const {
315 return possibly_invalid_account_id_;
318 const std::string& SigninManager::GetUsernameForAuthInProgress() const {
319 return possibly_invalid_email_;
322 void SigninManager::DisableOneClickSignIn(PrefService* prefs) {
323 prefs->SetBoolean(prefs::kReverseAutologinEnabled, false);
326 void SigninManager::MergeSigninCredentialIntoCookieJar() {
327 if (!client_->ShouldMergeSigninCredentialsIntoCookieJar())
328 return;
330 if (!IsAuthenticated())
331 return;
333 cookie_manager_service_->AddAccountToCookie(GetAuthenticatedAccountId());
336 void SigninManager::CompletePendingSignin() {
337 NotifyDiagnosticsObservers(SIGNIN_COMPLETED, "Successful");
338 DCHECK(!possibly_invalid_account_id_.empty());
339 OnSignedIn();
341 DCHECK(!temp_refresh_token_.empty());
342 DCHECK(IsAuthenticated());
344 std::string account_id = GetAuthenticatedAccountId();
345 token_service_->UpdateCredentials(account_id, temp_refresh_token_);
346 temp_refresh_token_.clear();
348 MergeSigninCredentialIntoCookieJar();
351 void SigninManager::OnExternalSigninCompleted(const std::string& username) {
352 AccountInfo info =
353 account_tracker_service()->FindAccountInfoByEmail(username);
354 DCHECK(!info.gaia.empty());
355 DCHECK(!info.email.empty());
356 possibly_invalid_account_id_ = info.account_id;
357 possibly_invalid_gaia_id_ = info.gaia;
358 possibly_invalid_email_ = info.email;
359 OnSignedIn();
362 void SigninManager::OnSignedIn() {
363 client_->GetPrefs()->SetInt64(prefs::kSignedInTime,
364 base::Time::Now().ToInternalValue());
365 SetAuthenticatedAccountInfo(possibly_invalid_gaia_id_,
366 possibly_invalid_email_);
367 const std::string gaia_id = possibly_invalid_gaia_id_;
369 possibly_invalid_account_id_.clear();
370 possibly_invalid_gaia_id_.clear();
371 possibly_invalid_email_.clear();
372 signin_manager_signed_in_ = true;
374 FOR_EACH_OBSERVER(
375 SigninManagerBase::Observer, observer_list_,
376 GoogleSigninSucceeded(GetAuthenticatedAccountId(),
377 GetAuthenticatedAccountInfo().email, password_));
379 client_->OnSignedIn(GetAuthenticatedAccountId(), gaia_id,
380 GetAuthenticatedAccountInfo().email, password_);
382 signin_metrics::LogSigninProfile(client_->IsFirstRun(),
383 client_->GetInstallDate());
385 DisableOneClickSignIn(client_->GetPrefs()); // Don't ever offer again.
387 PostSignedIn();
390 void SigninManager::PostSignedIn() {
391 if (!signin_manager_signed_in_ || !user_info_fetched_by_account_tracker_)
392 return;
394 client_->PostSignedIn(GetAuthenticatedAccountId(),
395 GetAuthenticatedAccountInfo().email, password_);
396 password_.clear();
399 void SigninManager::OnAccountUpdated(const AccountInfo& info) {
400 user_info_fetched_by_account_tracker_ = true;
401 PostSignedIn();
404 void SigninManager::OnAccountUpdateFailed(const std::string& account_id) {
405 user_info_fetched_by_account_tracker_ = true;
406 PostSignedIn();
409 void SigninManager::OnRefreshTokensLoaded() {
410 if (account_tracker_service()->GetMigrationState() ==
411 AccountTrackerService::MIGRATION_IN_PROGRESS) {
412 account_tracker_service()->SetMigrationDone();
413 token_service_->RemoveObserver(this);
417 void SigninManager::ProhibitSignout(bool prohibit_signout) {
418 prohibit_signout_ = prohibit_signout;
421 bool SigninManager::IsSignoutProhibited() const { return prohibit_signout_; }