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"
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
),
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
) {
54 case SIGNIN_TYPE_NONE
:
56 case SIGNIN_TYPE_WITH_REFRESH_TOKEN
:
57 return "With refresh token";
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.
77 GoogleServiceAuthError(GoogleServiceAuthError::ACCOUNT_DISABLED
));
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();
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
));
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
,
112 temp_refresh_token_
= refresh_token
;
114 if (!callback
.is_null() && !temp_refresh_token_
.empty()) {
115 callback
.Run(temp_refresh_token_
);
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();
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
,
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
);
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
166 ClearTransientSigninData();
171 if (prohibit_signout_
) {
172 DVLOG(1) << "Ignoring attempt to sign out while signout is prohibited";
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
);
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
,
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.
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
,
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);
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
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
);
268 bool SigninManager::IsUsernameAllowedByPolicy(const std::string
& username
,
269 const std::string
& policy
) {
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
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).
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();
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())
330 if (!IsAuthenticated())
333 cookie_manager_service_
->AddAccountToCookie(GetAuthenticatedAccountId());
336 void SigninManager::CompletePendingSignin() {
337 NotifyDiagnosticsObservers(SIGNIN_COMPLETED
, "Successful");
338 DCHECK(!possibly_invalid_account_id_
.empty());
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
) {
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
;
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;
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.
390 void SigninManager::PostSignedIn() {
391 if (!signin_manager_signed_in_
|| !user_info_fetched_by_account_tracker_
)
394 client_
->PostSignedIn(GetAuthenticatedAccountId(),
395 GetAuthenticatedAccountInfo().email
, password_
);
399 void SigninManager::OnAccountUpdated(const AccountInfo
& info
) {
400 user_info_fetched_by_account_tracker_
= true;
404 void SigninManager::OnAccountUpdateFailed(const std::string
& account_id
) {
405 user_info_fetched_by_account_tracker_
= true;
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_
; }