Stack sampling profiler: add fire-and-forget interface
[chromium-blink-merge.git] / components / signin / core / browser / signin_manager.cc
blob409a713c7d54da7bd2a8257b4911a61133ab37c3
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 = GetAuthenticatedUsername();
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() && !IsAllowedUsername(GetAuthenticatedUsername())) {
251 // Signed in user is invalid according to the current policy so sign
252 // the user out.
253 SignOut(signin_metrics::GOOGLE_SERVICE_NAME_PATTERN_CHANGED);
257 bool SigninManager::IsSigninAllowed() const {
258 return signin_allowed_.GetValue();
261 void SigninManager::OnSigninAllowedPrefChanged() {
262 if (!IsSigninAllowed())
263 SignOut(signin_metrics::SIGNOUT_PREF_CHANGED);
266 // static
267 bool SigninManager::IsUsernameAllowedByPolicy(const std::string& username,
268 const std::string& policy) {
269 if (policy.empty())
270 return true;
272 // Patterns like "*@foo.com" are not accepted by our regex engine (since they
273 // are not valid regular expressions - they should instead be ".*@foo.com").
274 // For convenience, detect these patterns and insert a "." character at the
275 // front.
276 base::string16 pattern = base::UTF8ToUTF16(policy);
277 if (pattern[0] == L'*')
278 pattern.insert(pattern.begin(), L'.');
280 // See if the username matches the policy-provided pattern.
281 UErrorCode status = U_ZERO_ERROR;
282 const icu::UnicodeString icu_pattern(pattern.data(), pattern.length());
283 icu::RegexMatcher matcher(icu_pattern, UREGEX_CASE_INSENSITIVE, status);
284 if (!U_SUCCESS(status)) {
285 LOG(ERROR) << "Invalid login regex: " << pattern << ", status: " << status;
286 // If an invalid pattern is provided, then prohibit *all* logins (better to
287 // break signin than to quietly allow users to sign in).
288 return false;
290 base::string16 username16 = base::UTF8ToUTF16(username);
291 icu::UnicodeString icu_input(username16.data(), username16.length());
292 matcher.reset(icu_input);
293 status = U_ZERO_ERROR;
294 UBool match = matcher.matches(status);
295 DCHECK(U_SUCCESS(status));
296 return !!match; // !! == convert from UBool to bool.
299 bool SigninManager::IsAllowedUsername(const std::string& username) const {
300 const PrefService* local_state = local_state_pref_registrar_.prefs();
301 if (!local_state)
302 return true; // In a unit test with no local state - all names are allowed.
304 std::string pattern =
305 local_state->GetString(prefs::kGoogleServicesUsernamePattern);
306 return IsUsernameAllowedByPolicy(username, pattern);
309 bool SigninManager::AuthInProgress() const {
310 return !possibly_invalid_account_id_.empty();
313 const std::string& SigninManager::GetAccountIdForAuthInProgress() const {
314 return possibly_invalid_account_id_;
317 const std::string& SigninManager::GetUsernameForAuthInProgress() const {
318 return possibly_invalid_email_;
321 void SigninManager::DisableOneClickSignIn(PrefService* prefs) {
322 prefs->SetBoolean(prefs::kReverseAutologinEnabled, false);
325 void SigninManager::MergeSigninCredentialIntoCookieJar() {
326 if (!client_->ShouldMergeSigninCredentialsIntoCookieJar())
327 return;
329 if (!IsAuthenticated())
330 return;
332 cookie_manager_service_->AddAccountToCookie(GetAuthenticatedAccountId());
335 void SigninManager::CompletePendingSignin() {
336 NotifyDiagnosticsObservers(SIGNIN_COMPLETED, "Successful");
337 DCHECK(!possibly_invalid_account_id_.empty());
338 OnSignedIn();
340 DCHECK(!temp_refresh_token_.empty());
341 DCHECK(IsAuthenticated());
343 std::string account_id = GetAuthenticatedAccountId();
344 token_service_->UpdateCredentials(account_id, temp_refresh_token_);
345 temp_refresh_token_.clear();
347 MergeSigninCredentialIntoCookieJar();
350 void SigninManager::OnExternalSigninCompleted(const std::string& username) {
351 AccountTrackerService::AccountInfo info =
352 account_tracker_service()->FindAccountInfoByEmail(username);
353 DCHECK(!info.gaia.empty());
354 DCHECK(!info.email.empty());
355 possibly_invalid_account_id_ = info.account_id;
356 possibly_invalid_gaia_id_ = info.gaia;
357 possibly_invalid_email_ = info.email;
358 OnSignedIn();
361 void SigninManager::OnSignedIn() {
362 client_->GetPrefs()->SetInt64(prefs::kSignedInTime,
363 base::Time::Now().ToInternalValue());
364 SetAuthenticatedAccountInfo(possibly_invalid_gaia_id_,
365 possibly_invalid_email_);
366 const std::string gaia_id = possibly_invalid_gaia_id_;
368 possibly_invalid_account_id_.clear();
369 possibly_invalid_gaia_id_.clear();
370 possibly_invalid_email_.clear();
371 signin_manager_signed_in_ = true;
373 FOR_EACH_OBSERVER(
374 SigninManagerBase::Observer,
375 observer_list_,
376 GoogleSigninSucceeded(GetAuthenticatedAccountId(),
377 GetAuthenticatedUsername(),
378 password_));
380 client_->OnSignedIn(GetAuthenticatedAccountId(),
381 gaia_id,
382 GetAuthenticatedUsername(),
383 password_);
385 signin_metrics::LogSigninProfile(client_->IsFirstRun(),
386 client_->GetInstallDate());
388 DisableOneClickSignIn(client_->GetPrefs()); // Don't ever offer again.
390 PostSignedIn();
393 void SigninManager::PostSignedIn() {
394 if (!signin_manager_signed_in_ || !user_info_fetched_by_account_tracker_)
395 return;
397 client_->PostSignedIn(GetAuthenticatedAccountId(),
398 GetAuthenticatedUsername(),
399 password_);
400 password_.clear();
403 void SigninManager::OnAccountUpdated(
404 const AccountTrackerService::AccountInfo& info) {
405 user_info_fetched_by_account_tracker_ = true;
406 PostSignedIn();
409 void SigninManager::OnAccountUpdateFailed(const std::string& account_id) {
410 user_info_fetched_by_account_tracker_ = true;
411 PostSignedIn();
414 void SigninManager::OnRefreshTokensLoaded() {
415 if (account_tracker_service()->GetMigrationState() ==
416 AccountTrackerService::MIGRATION_IN_PROGRESS) {
417 account_tracker_service()->SetMigrationDone();
418 token_service_->RemoveObserver(this);
422 void SigninManager::ProhibitSignout(bool prohibit_signout) {
423 prohibit_signout_ = prohibit_signout;
426 bool SigninManager::IsSignoutProhibited() const { return prohibit_signout_; }