GN + Android: extract android_standalone_library rule.
[chromium-blink-merge.git] / components / signin / core / browser / signin_manager.cc
blob163204d4a4bcd9340d7fc85d48b5f3240e4fc1b2
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.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/profile_oauth2_token_service.h"
17 #include "components/signin/core/browser/signin_account_id_helper.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 "net/base/escape.h"
27 #include "third_party/icu/source/i18n/unicode/regex.h"
29 using namespace signin_internals_util;
31 namespace {
33 const char kChromiumSyncService[] = "service=chromiumsync";
35 } // namespace
37 // Under the covers, we use a dummy chrome-extension ID to serve the purposes
38 // outlined in the .h file comment for this string.
39 const char SigninManager::kChromeSigninEffectiveSite[] =
40 "chrome-extension://acfccoigjajmmgbhpfbjnpckhjjegnih";
42 // static
43 bool SigninManager::IsWebBasedSigninFlowURL(const GURL& url) {
44 GURL effective(kChromeSigninEffectiveSite);
45 if (url.SchemeIs(effective.scheme().c_str()) &&
46 url.host() == effective.host()) {
47 return true;
50 GURL service_login(GaiaUrls::GetInstance()->service_login_url());
51 if (url.GetOrigin() != service_login.GetOrigin())
52 return false;
54 // Any login UI URLs with signin=chromiumsync should be considered a web
55 // URL (relies on GAIA keeping the "service=chromiumsync" query string
56 // fragment present even when embedding inside a "continue" parameter).
57 return net::UnescapeURLComponent(url.query(),
58 net::UnescapeRule::URL_SPECIAL_CHARS)
59 .find(kChromiumSyncService) != std::string::npos;
62 SigninManager::SigninManager(SigninClient* client,
63 ProfileOAuth2TokenService* token_service,
64 AccountTrackerService* account_tracker_service)
65 : SigninManagerBase(client),
66 prohibit_signout_(false),
67 type_(SIGNIN_TYPE_NONE),
68 client_(client),
69 token_service_(token_service),
70 account_tracker_service_(account_tracker_service),
71 weak_pointer_factory_(this),
72 signin_manager_signed_in_(false),
73 user_info_fetched_by_account_tracker_(false) {}
75 void SigninManager::AddMergeSessionObserver(
76 MergeSessionHelper::Observer* observer) {
77 if (merge_session_helper_)
78 merge_session_helper_->AddObserver(observer);
81 void SigninManager::RemoveMergeSessionObserver(
82 MergeSessionHelper::Observer* observer) {
83 if (merge_session_helper_)
84 merge_session_helper_->RemoveObserver(observer);
87 SigninManager::~SigninManager() {}
89 void SigninManager::InitTokenService() {
90 if (token_service_ && IsAuthenticated())
91 token_service_->LoadCredentials(GetAuthenticatedAccountId());
94 std::string SigninManager::SigninTypeToString(SigninManager::SigninType type) {
95 switch (type) {
96 case SIGNIN_TYPE_NONE:
97 return "No Signin";
98 case SIGNIN_TYPE_WITH_REFRESH_TOKEN:
99 return "Signin with refresh token";
102 NOTREACHED();
103 return std::string();
106 bool SigninManager::PrepareForSignin(SigninType type,
107 const std::string& username,
108 const std::string& password) {
109 DCHECK(possibly_invalid_username_.empty() ||
110 possibly_invalid_username_ == username);
111 DCHECK(!username.empty());
113 if (!IsAllowedUsername(username)) {
114 // Account is not allowed by admin policy.
115 HandleAuthError(
116 GoogleServiceAuthError(GoogleServiceAuthError::ACCOUNT_DISABLED));
117 return false;
120 // This attempt is either 1) the user trying to establish initial sync, or
121 // 2) trying to refresh credentials for an existing username. If it is 2, we
122 // need to try again, but take care to leave state around tracking that the
123 // user has successfully signed in once before with this username, so that on
124 // restart we don't think sync setup has never completed.
125 ClearTransientSigninData();
126 type_ = type;
127 possibly_invalid_username_.assign(username);
128 password_.assign(password);
129 signin_manager_signed_in_ = false;
130 user_info_fetched_by_account_tracker_ = false;
131 NotifyDiagnosticsObservers(SIGNIN_TYPE, SigninTypeToString(type));
132 return true;
135 void SigninManager::StartSignInWithRefreshToken(
136 const std::string& refresh_token,
137 const std::string& username,
138 const std::string& password,
139 const OAuthTokenFetchedCallback& callback) {
140 DCHECK(!IsAuthenticated() ||
141 gaia::AreEmailsSame(username, GetAuthenticatedUsername()));
143 if (!PrepareForSignin(SIGNIN_TYPE_WITH_REFRESH_TOKEN, username, password))
144 return;
146 // Store our callback and token.
147 temp_refresh_token_ = refresh_token;
148 possibly_invalid_username_ = username;
150 NotifyDiagnosticsObservers(GET_USER_INFO_STATUS, "Successful");
152 if (!callback.is_null() && !temp_refresh_token_.empty()) {
153 callback.Run(temp_refresh_token_);
154 } else {
155 // No oauth token or callback, so just complete our pending signin.
156 CompletePendingSignin();
160 void SigninManager::CopyCredentialsFrom(const SigninManager& source) {
161 DCHECK_NE(this, &source);
162 possibly_invalid_username_ = source.possibly_invalid_username_;
163 temp_refresh_token_ = source.temp_refresh_token_;
164 password_ = source.password_;
167 void SigninManager::ClearTransientSigninData() {
168 DCHECK(IsInitialized());
170 possibly_invalid_username_.clear();
171 password_.clear();
172 type_ = SIGNIN_TYPE_NONE;
173 temp_refresh_token_.clear();
176 void SigninManager::HandleAuthError(const GoogleServiceAuthError& error) {
177 ClearTransientSigninData();
179 FOR_EACH_OBSERVER(SigninManagerBase::Observer,
180 observer_list_,
181 GoogleSigninFailed(error));
184 void SigninManager::SignOut(
185 signin_metrics::ProfileSignout signout_source_metric) {
186 DCHECK(IsInitialized());
188 signin_metrics::LogSignout(signout_source_metric);
189 if (!IsAuthenticated()) {
190 if (AuthInProgress()) {
191 // If the user is in the process of signing in, then treat a call to
192 // SignOut as a cancellation request.
193 GoogleServiceAuthError error(GoogleServiceAuthError::REQUEST_CANCELED);
194 HandleAuthError(error);
195 } else {
196 // Clean up our transient data and exit if we aren't signed in.
197 // This avoids a perf regression from clearing out the TokenDB if
198 // SignOut() is invoked on startup to clean up any incomplete previous
199 // signin attempts.
200 ClearTransientSigninData();
202 return;
205 if (prohibit_signout_) {
206 DVLOG(1) << "Ignoring attempt to sign out while signout is prohibited";
207 return;
210 ClearTransientSigninData();
212 const std::string account_id = GetAuthenticatedAccountId();
213 const std::string username = GetAuthenticatedUsername();
214 const base::Time signin_time =
215 base::Time::FromInternalValue(
216 client_->GetPrefs()->GetInt64(prefs::kSignedInTime));
217 ClearAuthenticatedUsername();
218 client_->GetPrefs()->ClearPref(prefs::kGoogleServicesHostedDomain);
219 client_->GetPrefs()->ClearPref(prefs::kGoogleServicesUsername);
220 client_->GetPrefs()->ClearPref(prefs::kSignedInTime);
221 client_->OnSignedOut();
223 // Erase (now) stale information from AboutSigninInternals.
224 NotifyDiagnosticsObservers(USERNAME, "");
226 // Determine the duration the user was logged in and log that to UMA.
227 if (!signin_time.is_null()) {
228 base::TimeDelta signed_in_duration = base::Time::Now() - signin_time;
229 UMA_HISTOGRAM_COUNTS("Signin.SignedInDurationBeforeSignout",
230 signed_in_duration.InMinutes());
233 // Revoke all tokens before sending signed_out notification, because there
234 // may be components that don't listen for token service events when the
235 // profile is not connected to an account.
236 LOG(WARNING) << "Revoking refresh token on server. Reason: sign out, "
237 << "IsSigninAllowed: " << IsSigninAllowed();
238 token_service_->RevokeAllCredentials();
240 FOR_EACH_OBSERVER(SigninManagerBase::Observer,
241 observer_list_,
242 GoogleSignedOut(account_id, username));
245 void SigninManager::Initialize(PrefService* local_state) {
246 SigninManagerBase::Initialize(local_state);
248 // local_state can be null during unit tests.
249 if (local_state) {
250 local_state_pref_registrar_.Init(local_state);
251 local_state_pref_registrar_.Add(
252 prefs::kGoogleServicesUsernamePattern,
253 base::Bind(&SigninManager::OnGoogleServicesUsernamePatternChanged,
254 weak_pointer_factory_.GetWeakPtr()));
256 signin_allowed_.Init(prefs::kSigninAllowed,
257 client_->GetPrefs(),
258 base::Bind(&SigninManager::OnSigninAllowedPrefChanged,
259 base::Unretained(this)));
261 std::string user =
262 client_->GetPrefs()->GetString(prefs::kGoogleServicesUsername);
263 if ((!user.empty() && !IsAllowedUsername(user)) || !IsSigninAllowed()) {
264 // User is signed in, but the username is invalid - the administrator must
265 // have changed the policy since the last signin, so sign out the user.
266 SignOut(signin_metrics::SIGNIN_PREF_CHANGED_DURING_SIGNIN);
269 InitTokenService();
270 account_id_helper_.reset(
271 new SigninAccountIdHelper(client_, token_service_, this));
273 account_tracker_service_->AddObserver(this);
276 void SigninManager::Shutdown() {
277 account_tracker_service_->RemoveObserver(this);
278 if (merge_session_helper_)
279 merge_session_helper_->CancelAll();
281 local_state_pref_registrar_.RemoveAll();
282 account_id_helper_.reset();
283 SigninManagerBase::Shutdown();
286 void SigninManager::OnGoogleServicesUsernamePatternChanged() {
287 if (IsAuthenticated() &&
288 !IsAllowedUsername(GetAuthenticatedUsername())) {
289 // Signed in user is invalid according to the current policy so sign
290 // the user out.
291 SignOut(signin_metrics::GOOGLE_SERVICE_NAME_PATTERN_CHANGED);
295 bool SigninManager::IsSigninAllowed() const {
296 return signin_allowed_.GetValue();
299 void SigninManager::OnSigninAllowedPrefChanged() {
300 if (!IsSigninAllowed())
301 SignOut(signin_metrics::SIGNOUT_PREF_CHANGED);
304 // static
305 bool SigninManager::IsUsernameAllowedByPolicy(const std::string& username,
306 const std::string& policy) {
307 if (policy.empty())
308 return true;
310 // Patterns like "*@foo.com" are not accepted by our regex engine (since they
311 // are not valid regular expressions - they should instead be ".*@foo.com").
312 // For convenience, detect these patterns and insert a "." character at the
313 // front.
314 base::string16 pattern = base::UTF8ToUTF16(policy);
315 if (pattern[0] == L'*')
316 pattern.insert(pattern.begin(), L'.');
318 // See if the username matches the policy-provided pattern.
319 UErrorCode status = U_ZERO_ERROR;
320 const icu::UnicodeString icu_pattern(pattern.data(), pattern.length());
321 icu::RegexMatcher matcher(icu_pattern, UREGEX_CASE_INSENSITIVE, status);
322 if (!U_SUCCESS(status)) {
323 LOG(ERROR) << "Invalid login regex: " << pattern << ", status: " << status;
324 // If an invalid pattern is provided, then prohibit *all* logins (better to
325 // break signin than to quietly allow users to sign in).
326 return false;
328 base::string16 username16 = base::UTF8ToUTF16(username);
329 icu::UnicodeString icu_input(username16.data(), username16.length());
330 matcher.reset(icu_input);
331 status = U_ZERO_ERROR;
332 UBool match = matcher.matches(status);
333 DCHECK(U_SUCCESS(status));
334 return !!match; // !! == convert from UBool to bool.
337 bool SigninManager::IsAllowedUsername(const std::string& username) const {
338 const PrefService* local_state = local_state_pref_registrar_.prefs();
339 if (!local_state)
340 return true; // In a unit test with no local state - all names are allowed.
342 std::string pattern =
343 local_state->GetString(prefs::kGoogleServicesUsernamePattern);
344 return IsUsernameAllowedByPolicy(username, pattern);
347 bool SigninManager::AuthInProgress() const {
348 return !possibly_invalid_username_.empty();
351 const std::string& SigninManager::GetUsernameForAuthInProgress() const {
352 return possibly_invalid_username_;
355 void SigninManager::DisableOneClickSignIn(PrefService* prefs) {
356 prefs->SetBoolean(prefs::kReverseAutologinEnabled, false);
359 void SigninManager::CompletePendingSignin() {
360 DCHECK(!possibly_invalid_username_.empty());
361 OnSignedIn(possibly_invalid_username_);
363 if (client_->ShouldMergeSigninCredentialsIntoCookieJar()) {
364 merge_session_helper_.reset(new MergeSessionHelper(
365 token_service_, GaiaConstants::kChromeSource,
366 client_->GetURLRequestContext(), NULL));
369 DCHECK(!temp_refresh_token_.empty());
370 DCHECK(IsAuthenticated());
371 std::string account_id = GetAuthenticatedAccountId();
372 token_service_->UpdateCredentials(account_id, temp_refresh_token_);
373 temp_refresh_token_.clear();
375 if (client_->ShouldMergeSigninCredentialsIntoCookieJar())
376 merge_session_helper_->LogIn(account_id);
379 void SigninManager::OnExternalSigninCompleted(const std::string& username) {
380 OnSignedIn(username);
383 void SigninManager::OnSignedIn(const std::string& username) {
384 client_->GetPrefs()->SetInt64(prefs::kSignedInTime,
385 base::Time::Now().ToInternalValue());
386 SetAuthenticatedUsername(username);
387 possibly_invalid_username_.clear();
388 signin_manager_signed_in_ = true;
390 FOR_EACH_OBSERVER(
391 SigninManagerBase::Observer,
392 observer_list_,
393 GoogleSigninSucceeded(GetAuthenticatedAccountId(),
394 GetAuthenticatedUsername(),
395 password_));
397 client_->OnSignedIn(GetAuthenticatedAccountId(),
398 GetAuthenticatedUsername(),
399 password_);
401 signin_metrics::LogSigninProfile(client_->IsFirstRun(),
402 client_->GetInstallDate());
404 DisableOneClickSignIn(client_->GetPrefs()); // Don't ever offer again.
406 PostSignedIn();
409 void SigninManager::PostSignedIn() {
410 if (!signin_manager_signed_in_ || !user_info_fetched_by_account_tracker_)
411 return;
413 client_->PostSignedIn(GetAuthenticatedAccountId(),
414 GetAuthenticatedUsername(),
415 password_);
416 password_.clear();
419 void SigninManager::OnAccountUpdated(
420 const AccountTrackerService::AccountInfo& info) {
421 user_info_fetched_by_account_tracker_ = true;
422 PostSignedIn();
425 void SigninManager::OnAccountUpdateFailed(const std::string& account_id) {
426 user_info_fetched_by_account_tracker_ = true;
427 PostSignedIn();
430 void SigninManager::ProhibitSignout(bool prohibit_signout) {
431 prohibit_signout_ = prohibit_signout;
434 bool SigninManager::IsSignoutProhibited() const { return prohibit_signout_; }