Add abhijeet.k@samsung.com to AUTHORS list.
[chromium-blink-merge.git] / components / signin / core / browser / signin_manager.cc
blob391030583ca3df2a784c58a6a66887c1a2216f87
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/gaia_cookie_manager_service.h"
17 #include "components/signin/core/browser/profile_oauth2_token_service.h"
18 #include "components/signin/core/browser/signin_account_id_helper.h"
19 #include "components/signin/core/browser/signin_client.h"
20 #include "components/signin/core/browser/signin_internals_util.h"
21 #include "components/signin/core/browser/signin_manager_cookie_helper.h"
22 #include "components/signin/core/browser/signin_metrics.h"
23 #include "components/signin/core/common/signin_pref_names.h"
24 #include "google_apis/gaia/gaia_auth_util.h"
25 #include "google_apis/gaia/gaia_constants.h"
26 #include "google_apis/gaia/gaia_urls.h"
27 #include "google_apis/gaia/google_service_auth_error.h"
28 #include "third_party/icu/source/i18n/unicode/regex.h"
30 using namespace signin_internals_util;
32 SigninManager::SigninManager(SigninClient* client,
33 ProfileOAuth2TokenService* token_service,
34 AccountTrackerService* account_tracker_service,
35 GaiaCookieManagerService* cookie_manager_service)
36 : SigninManagerBase(client),
37 prohibit_signout_(false),
38 type_(SIGNIN_TYPE_NONE),
39 client_(client),
40 token_service_(token_service),
41 account_tracker_service_(account_tracker_service),
42 cookie_manager_service_(cookie_manager_service),
43 signin_manager_signed_in_(false),
44 user_info_fetched_by_account_tracker_(false),
45 weak_pointer_factory_(this) {}
47 SigninManager::~SigninManager() {}
49 void SigninManager::InitTokenService() {
50 if (token_service_ && IsAuthenticated())
51 token_service_->LoadCredentials(GetAuthenticatedAccountId());
54 std::string SigninManager::SigninTypeToString(SigninManager::SigninType type) {
55 switch (type) {
56 case SIGNIN_TYPE_NONE:
57 return "No Signin";
58 case SIGNIN_TYPE_WITH_REFRESH_TOKEN:
59 return "With refresh token";
62 NOTREACHED();
63 return std::string();
66 bool SigninManager::PrepareForSignin(SigninType type,
67 const std::string& username,
68 const std::string& password) {
69 DCHECK(possibly_invalid_username_.empty() ||
70 possibly_invalid_username_ == username);
71 DCHECK(!username.empty());
73 if (!IsAllowedUsername(username)) {
74 // Account is not allowed by admin policy.
75 HandleAuthError(
76 GoogleServiceAuthError(GoogleServiceAuthError::ACCOUNT_DISABLED));
77 return false;
80 // This attempt is either 1) the user trying to establish initial sync, or
81 // 2) trying to refresh credentials for an existing username. If it is 2, we
82 // need to try again, but take care to leave state around tracking that the
83 // user has successfully signed in once before with this username, so that on
84 // restart we don't think sync setup has never completed.
85 ClearTransientSigninData();
86 type_ = type;
87 possibly_invalid_username_.assign(username);
88 password_.assign(password);
89 signin_manager_signed_in_ = false;
90 user_info_fetched_by_account_tracker_ = false;
91 NotifyDiagnosticsObservers(SIGNIN_STARTED, SigninTypeToString(type));
92 return true;
95 void SigninManager::StartSignInWithRefreshToken(
96 const std::string& refresh_token,
97 const std::string& username,
98 const std::string& password,
99 const OAuthTokenFetchedCallback& callback) {
100 DCHECK(!IsAuthenticated() ||
101 gaia::AreEmailsSame(username, GetAuthenticatedUsername()));
103 if (!PrepareForSignin(SIGNIN_TYPE_WITH_REFRESH_TOKEN, username, password))
104 return;
106 // Store our callback and token.
107 temp_refresh_token_ = refresh_token;
108 possibly_invalid_username_ = username;
110 if (!callback.is_null() && !temp_refresh_token_.empty()) {
111 callback.Run(temp_refresh_token_);
112 } else {
113 // No oauth token or callback, so just complete our pending signin.
114 CompletePendingSignin();
118 void SigninManager::CopyCredentialsFrom(const SigninManager& source) {
119 DCHECK_NE(this, &source);
120 possibly_invalid_username_ = source.possibly_invalid_username_;
121 temp_refresh_token_ = source.temp_refresh_token_;
122 password_ = source.password_;
125 void SigninManager::ClearTransientSigninData() {
126 DCHECK(IsInitialized());
128 possibly_invalid_username_.clear();
129 password_.clear();
130 type_ = SIGNIN_TYPE_NONE;
131 temp_refresh_token_.clear();
134 void SigninManager::HandleAuthError(const GoogleServiceAuthError& error) {
135 ClearTransientSigninData();
137 FOR_EACH_OBSERVER(SigninManagerBase::Observer,
138 observer_list_,
139 GoogleSigninFailed(error));
142 void SigninManager::SignOut(
143 signin_metrics::ProfileSignout signout_source_metric) {
144 DCHECK(IsInitialized());
146 signin_metrics::LogSignout(signout_source_metric);
147 if (!IsAuthenticated()) {
148 if (AuthInProgress()) {
149 // If the user is in the process of signing in, then treat a call to
150 // SignOut as a cancellation request.
151 GoogleServiceAuthError error(GoogleServiceAuthError::REQUEST_CANCELED);
152 HandleAuthError(error);
153 } else {
154 // Clean up our transient data and exit if we aren't signed in.
155 // This avoids a perf regression from clearing out the TokenDB if
156 // SignOut() is invoked on startup to clean up any incomplete previous
157 // signin attempts.
158 ClearTransientSigninData();
160 return;
163 if (prohibit_signout_) {
164 DVLOG(1) << "Ignoring attempt to sign out while signout is prohibited";
165 return;
168 ClearTransientSigninData();
170 const std::string account_id = GetAuthenticatedAccountId();
171 const std::string username = GetAuthenticatedUsername();
172 const base::Time signin_time =
173 base::Time::FromInternalValue(
174 client_->GetPrefs()->GetInt64(prefs::kSignedInTime));
175 ClearAuthenticatedUsername();
176 client_->GetPrefs()->ClearPref(prefs::kGoogleServicesHostedDomain);
177 client_->GetPrefs()->ClearPref(prefs::kGoogleServicesUsername);
178 client_->GetPrefs()->ClearPref(prefs::kSignedInTime);
179 client_->OnSignedOut();
181 // Determine the duration the user was logged in and log that to UMA.
182 if (!signin_time.is_null()) {
183 base::TimeDelta signed_in_duration = base::Time::Now() - signin_time;
184 UMA_HISTOGRAM_COUNTS("Signin.SignedInDurationBeforeSignout",
185 signed_in_duration.InMinutes());
188 // Revoke all tokens before sending signed_out notification, because there
189 // may be components that don't listen for token service events when the
190 // profile is not connected to an account.
191 LOG(WARNING) << "Revoking refresh token on server. Reason: sign out, "
192 << "IsSigninAllowed: " << IsSigninAllowed();
193 token_service_->RevokeAllCredentials();
195 FOR_EACH_OBSERVER(SigninManagerBase::Observer,
196 observer_list_,
197 GoogleSignedOut(account_id, username));
200 void SigninManager::Initialize(PrefService* local_state) {
201 SigninManagerBase::Initialize(local_state);
203 // local_state can be null during unit tests.
204 if (local_state) {
205 local_state_pref_registrar_.Init(local_state);
206 local_state_pref_registrar_.Add(
207 prefs::kGoogleServicesUsernamePattern,
208 base::Bind(&SigninManager::OnGoogleServicesUsernamePatternChanged,
209 weak_pointer_factory_.GetWeakPtr()));
211 signin_allowed_.Init(prefs::kSigninAllowed,
212 client_->GetPrefs(),
213 base::Bind(&SigninManager::OnSigninAllowedPrefChanged,
214 base::Unretained(this)));
216 std::string user =
217 client_->GetPrefs()->GetString(prefs::kGoogleServicesUsername);
218 if ((!user.empty() && !IsAllowedUsername(user)) || !IsSigninAllowed()) {
219 // User is signed in, but the username is invalid - the administrator must
220 // have changed the policy since the last signin, so sign out the user.
221 SignOut(signin_metrics::SIGNIN_PREF_CHANGED_DURING_SIGNIN);
224 InitTokenService();
225 account_id_helper_.reset(
226 new SigninAccountIdHelper(client_, token_service_, this));
228 account_tracker_service_->AddObserver(this);
231 void SigninManager::Shutdown() {
232 account_tracker_service_->RemoveObserver(this);
233 local_state_pref_registrar_.RemoveAll();
234 account_id_helper_.reset();
235 SigninManagerBase::Shutdown();
238 void SigninManager::OnGoogleServicesUsernamePatternChanged() {
239 if (IsAuthenticated() &&
240 !IsAllowedUsername(GetAuthenticatedUsername())) {
241 // Signed in user is invalid according to the current policy so sign
242 // the user out.
243 SignOut(signin_metrics::GOOGLE_SERVICE_NAME_PATTERN_CHANGED);
247 bool SigninManager::IsSigninAllowed() const {
248 return signin_allowed_.GetValue();
251 void SigninManager::OnSigninAllowedPrefChanged() {
252 if (!IsSigninAllowed())
253 SignOut(signin_metrics::SIGNOUT_PREF_CHANGED);
256 // static
257 bool SigninManager::IsUsernameAllowedByPolicy(const std::string& username,
258 const std::string& policy) {
259 if (policy.empty())
260 return true;
262 // Patterns like "*@foo.com" are not accepted by our regex engine (since they
263 // are not valid regular expressions - they should instead be ".*@foo.com").
264 // For convenience, detect these patterns and insert a "." character at the
265 // front.
266 base::string16 pattern = base::UTF8ToUTF16(policy);
267 if (pattern[0] == L'*')
268 pattern.insert(pattern.begin(), L'.');
270 // See if the username matches the policy-provided pattern.
271 UErrorCode status = U_ZERO_ERROR;
272 const icu::UnicodeString icu_pattern(pattern.data(), pattern.length());
273 icu::RegexMatcher matcher(icu_pattern, UREGEX_CASE_INSENSITIVE, status);
274 if (!U_SUCCESS(status)) {
275 LOG(ERROR) << "Invalid login regex: " << pattern << ", status: " << status;
276 // If an invalid pattern is provided, then prohibit *all* logins (better to
277 // break signin than to quietly allow users to sign in).
278 return false;
280 base::string16 username16 = base::UTF8ToUTF16(username);
281 icu::UnicodeString icu_input(username16.data(), username16.length());
282 matcher.reset(icu_input);
283 status = U_ZERO_ERROR;
284 UBool match = matcher.matches(status);
285 DCHECK(U_SUCCESS(status));
286 return !!match; // !! == convert from UBool to bool.
289 bool SigninManager::IsAllowedUsername(const std::string& username) const {
290 const PrefService* local_state = local_state_pref_registrar_.prefs();
291 if (!local_state)
292 return true; // In a unit test with no local state - all names are allowed.
294 std::string pattern =
295 local_state->GetString(prefs::kGoogleServicesUsernamePattern);
296 return IsUsernameAllowedByPolicy(username, pattern);
299 bool SigninManager::AuthInProgress() const {
300 return !possibly_invalid_username_.empty();
303 const std::string& SigninManager::GetUsernameForAuthInProgress() const {
304 return possibly_invalid_username_;
307 void SigninManager::DisableOneClickSignIn(PrefService* prefs) {
308 prefs->SetBoolean(prefs::kReverseAutologinEnabled, false);
311 void SigninManager::MergeSigninCredentialIntoCookieJar() {
312 if (!client_->ShouldMergeSigninCredentialsIntoCookieJar())
313 return;
315 if (!IsAuthenticated())
316 return;
318 cookie_manager_service_->AddAccountToCookie(GetAuthenticatedAccountId());
321 void SigninManager::CompletePendingSignin() {
322 NotifyDiagnosticsObservers(SIGNIN_COMPLETED, "Successful");
324 DCHECK(!possibly_invalid_username_.empty());
325 OnSignedIn(possibly_invalid_username_);
327 DCHECK(!temp_refresh_token_.empty());
328 DCHECK(IsAuthenticated());
329 token_service_->UpdateCredentials(GetAuthenticatedAccountId(),
330 temp_refresh_token_);
331 temp_refresh_token_.clear();
333 MergeSigninCredentialIntoCookieJar();
336 void SigninManager::OnExternalSigninCompleted(const std::string& username) {
337 OnSignedIn(username);
340 void SigninManager::OnSignedIn(const std::string& username) {
341 client_->GetPrefs()->SetInt64(prefs::kSignedInTime,
342 base::Time::Now().ToInternalValue());
343 SetAuthenticatedUsername(username);
344 possibly_invalid_username_.clear();
345 signin_manager_signed_in_ = true;
347 FOR_EACH_OBSERVER(
348 SigninManagerBase::Observer,
349 observer_list_,
350 GoogleSigninSucceeded(GetAuthenticatedAccountId(),
351 GetAuthenticatedUsername(),
352 password_));
354 client_->OnSignedIn(GetAuthenticatedAccountId(),
355 GetAuthenticatedUsername(),
356 password_);
358 signin_metrics::LogSigninProfile(client_->IsFirstRun(),
359 client_->GetInstallDate());
361 DisableOneClickSignIn(client_->GetPrefs()); // Don't ever offer again.
363 PostSignedIn();
366 void SigninManager::PostSignedIn() {
367 if (!signin_manager_signed_in_ || !user_info_fetched_by_account_tracker_)
368 return;
370 client_->PostSignedIn(GetAuthenticatedAccountId(),
371 GetAuthenticatedUsername(),
372 password_);
373 password_.clear();
376 void SigninManager::OnAccountUpdated(
377 const AccountTrackerService::AccountInfo& info) {
378 user_info_fetched_by_account_tracker_ = true;
379 PostSignedIn();
382 void SigninManager::OnAccountUpdateFailed(const std::string& account_id) {
383 user_info_fetched_by_account_tracker_ = true;
384 PostSignedIn();
387 void SigninManager::ProhibitSignout(bool prohibit_signout) {
388 prohibit_signout_ = prohibit_signout;
391 bool SigninManager::IsSignoutProhibited() const { return prohibit_signout_; }