Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / components / signin / core / browser / signin_manager.cc
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.
4
5 #include "components/signin/core/browser/signin_manager.h"
6
7 #include <string>
8 #include <vector>
9
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"
28
29 using namespace signin_internals_util;
30
31 namespace {
32
33 const char kChromiumSyncService[] = "service=chromiumsync";
34
35 }  // namespace
36
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";
41
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;
48   }
49
50   GURL service_login(GaiaUrls::GetInstance()->service_login_url());
51   if (url.GetOrigin() != service_login.GetOrigin())
52     return false;
53
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;
60 }
61
62 SigninManager::SigninManager(SigninClient* client,
63                              ProfileOAuth2TokenService* token_service)
64     : SigninManagerBase(client),
65       prohibit_signout_(false),
66       type_(SIGNIN_TYPE_NONE),
67       client_(client),
68       token_service_(token_service),
69       weak_pointer_factory_(this) {}
70
71 void SigninManager::AddMergeSessionObserver(
72     MergeSessionHelper::Observer* observer) {
73   if (merge_session_helper_)
74     merge_session_helper_->AddObserver(observer);
75 }
76
77 void SigninManager::RemoveMergeSessionObserver(
78     MergeSessionHelper::Observer* observer) {
79   if (merge_session_helper_)
80     merge_session_helper_->RemoveObserver(observer);
81 }
82
83 SigninManager::~SigninManager() {}
84
85 void SigninManager::InitTokenService() {
86   if (token_service_ && IsAuthenticated())
87     token_service_->LoadCredentials(GetAuthenticatedAccountId());
88 }
89
90 std::string SigninManager::SigninTypeToString(SigninManager::SigninType type) {
91   switch (type) {
92     case SIGNIN_TYPE_NONE:
93       return "No Signin";
94     case SIGNIN_TYPE_WITH_REFRESH_TOKEN:
95       return "Signin with refresh token";
96   }
97
98   NOTREACHED();
99   return std::string();
100 }
101
102 bool SigninManager::PrepareForSignin(SigninType type,
103                                      const std::string& username,
104                                      const std::string& password) {
105   DCHECK(possibly_invalid_username_.empty() ||
106          possibly_invalid_username_ == username);
107   DCHECK(!username.empty());
108
109   if (!IsAllowedUsername(username)) {
110     // Account is not allowed by admin policy.
111     HandleAuthError(
112         GoogleServiceAuthError(GoogleServiceAuthError::ACCOUNT_DISABLED));
113     return false;
114   }
115
116   // This attempt is either 1) the user trying to establish initial sync, or
117   // 2) trying to refresh credentials for an existing username.  If it is 2, we
118   // need to try again, but take care to leave state around tracking that the
119   // user has successfully signed in once before with this username, so that on
120   // restart we don't think sync setup has never completed.
121   ClearTransientSigninData();
122   type_ = type;
123   possibly_invalid_username_.assign(username);
124   password_.assign(password);
125   NotifyDiagnosticsObservers(SIGNIN_TYPE, SigninTypeToString(type));
126   return true;
127 }
128
129 void SigninManager::StartSignInWithRefreshToken(
130     const std::string& refresh_token,
131     const std::string& username,
132     const std::string& password,
133     const OAuthTokenFetchedCallback& callback) {
134   DCHECK(!IsAuthenticated() ||
135          gaia::AreEmailsSame(username, GetAuthenticatedUsername()));
136
137   if (!PrepareForSignin(SIGNIN_TYPE_WITH_REFRESH_TOKEN, username, password))
138     return;
139
140   // Store our callback and token.
141   temp_refresh_token_ = refresh_token;
142   possibly_invalid_username_ = username;
143
144   NotifyDiagnosticsObservers(GET_USER_INFO_STATUS, "Successful");
145
146   if (!callback.is_null() && !temp_refresh_token_.empty()) {
147     callback.Run(temp_refresh_token_);
148   } else {
149     // No oauth token or callback, so just complete our pending signin.
150     CompletePendingSignin();
151   }
152 }
153
154 void SigninManager::CopyCredentialsFrom(const SigninManager& source) {
155   DCHECK_NE(this, &source);
156   possibly_invalid_username_ = source.possibly_invalid_username_;
157   temp_refresh_token_ = source.temp_refresh_token_;
158   password_ = source.password_;
159 }
160
161 void SigninManager::ClearTransientSigninData() {
162   DCHECK(IsInitialized());
163
164   possibly_invalid_username_.clear();
165   password_.clear();
166   type_ = SIGNIN_TYPE_NONE;
167   temp_refresh_token_.clear();
168 }
169
170 void SigninManager::HandleAuthError(const GoogleServiceAuthError& error) {
171   ClearTransientSigninData();
172
173   FOR_EACH_OBSERVER(Observer, observer_list_, GoogleSigninFailed(error));
174 }
175
176 void SigninManager::SignOut(
177     signin_metrics::ProfileSignout signout_source_metric) {
178   DCHECK(IsInitialized());
179
180   signin_metrics::LogSignout(signout_source_metric);
181   if (!IsAuthenticated()) {
182     if (AuthInProgress()) {
183       // If the user is in the process of signing in, then treat a call to
184       // SignOut as a cancellation request.
185       GoogleServiceAuthError error(GoogleServiceAuthError::REQUEST_CANCELED);
186       HandleAuthError(error);
187     } else {
188       // Clean up our transient data and exit if we aren't signed in.
189       // This avoids a perf regression from clearing out the TokenDB if
190       // SignOut() is invoked on startup to clean up any incomplete previous
191       // signin attempts.
192       ClearTransientSigninData();
193     }
194     return;
195   }
196
197   if (prohibit_signout_) {
198     DVLOG(1) << "Ignoring attempt to sign out while signout is prohibited";
199     return;
200   }
201
202   ClearTransientSigninData();
203
204   const std::string account_id = GetAuthenticatedAccountId();
205   const std::string username = GetAuthenticatedUsername();
206   const base::Time signin_time =
207       base::Time::FromInternalValue(
208           client_->GetPrefs()->GetInt64(prefs::kSignedInTime));
209   ClearAuthenticatedUsername();
210   client_->GetPrefs()->ClearPref(prefs::kGoogleServicesHostedDomain);
211   client_->GetPrefs()->ClearPref(prefs::kGoogleServicesUsername);
212   client_->GetPrefs()->ClearPref(prefs::kSignedInTime);
213   client_->OnSignedOut();
214
215   // Erase (now) stale information from AboutSigninInternals.
216   NotifyDiagnosticsObservers(USERNAME, "");
217
218   // Determine the duration the user was logged in and log that to UMA.
219   if (!signin_time.is_null()) {
220     base::TimeDelta signed_in_duration = base::Time::Now() - signin_time;
221     UMA_HISTOGRAM_COUNTS("Signin.SignedInDurationBeforeSignout",
222                          signed_in_duration.InMinutes());
223   }
224
225   // Revoke all tokens before sending signed_out notification, because there
226   // may be components that don't listen for token service events when the
227   // profile is not connected to an account.
228   LOG(WARNING) << "Revoking refresh token on server. Reason: sign out, "
229                << "IsSigninAllowed: " << IsSigninAllowed();
230   token_service_->RevokeAllCredentials();
231
232   FOR_EACH_OBSERVER(Observer,
233                     observer_list_,
234                     GoogleSignedOut(account_id, username));
235 }
236
237 void SigninManager::Initialize(PrefService* local_state) {
238   SigninManagerBase::Initialize(local_state);
239
240   // local_state can be null during unit tests.
241   if (local_state) {
242     local_state_pref_registrar_.Init(local_state);
243     local_state_pref_registrar_.Add(
244         prefs::kGoogleServicesUsernamePattern,
245         base::Bind(&SigninManager::OnGoogleServicesUsernamePatternChanged,
246                    weak_pointer_factory_.GetWeakPtr()));
247   }
248   signin_allowed_.Init(prefs::kSigninAllowed,
249                        client_->GetPrefs(),
250                        base::Bind(&SigninManager::OnSigninAllowedPrefChanged,
251                                   base::Unretained(this)));
252
253   std::string user =
254       client_->GetPrefs()->GetString(prefs::kGoogleServicesUsername);
255   if ((!user.empty() && !IsAllowedUsername(user)) || !IsSigninAllowed()) {
256     // User is signed in, but the username is invalid - the administrator must
257     // have changed the policy since the last signin, so sign out the user.
258     SignOut(signin_metrics::SIGNIN_PREF_CHANGED_DURING_SIGNIN);
259   }
260
261   InitTokenService();
262   account_id_helper_.reset(
263       new SigninAccountIdHelper(client_, token_service_, this));
264 }
265
266 void SigninManager::Shutdown() {
267   if (merge_session_helper_)
268     merge_session_helper_->CancelAll();
269
270   local_state_pref_registrar_.RemoveAll();
271   account_id_helper_.reset();
272   SigninManagerBase::Shutdown();
273 }
274
275 void SigninManager::OnGoogleServicesUsernamePatternChanged() {
276   if (IsAuthenticated() &&
277       !IsAllowedUsername(GetAuthenticatedUsername())) {
278     // Signed in user is invalid according to the current policy so sign
279     // the user out.
280     SignOut(signin_metrics::GOOGLE_SERVICE_NAME_PATTERN_CHANGED);
281   }
282 }
283
284 bool SigninManager::IsSigninAllowed() const {
285   return signin_allowed_.GetValue();
286 }
287
288 void SigninManager::OnSigninAllowedPrefChanged() {
289   if (!IsSigninAllowed())
290     SignOut(signin_metrics::SIGNOUT_PREF_CHANGED);
291 }
292
293 // static
294 bool SigninManager::IsUsernameAllowedByPolicy(const std::string& username,
295                                               const std::string& policy) {
296   if (policy.empty())
297     return true;
298
299   // Patterns like "*@foo.com" are not accepted by our regex engine (since they
300   // are not valid regular expressions - they should instead be ".*@foo.com").
301   // For convenience, detect these patterns and insert a "." character at the
302   // front.
303   base::string16 pattern = base::UTF8ToUTF16(policy);
304   if (pattern[0] == L'*')
305     pattern.insert(pattern.begin(), L'.');
306
307   // See if the username matches the policy-provided pattern.
308   UErrorCode status = U_ZERO_ERROR;
309   const icu::UnicodeString icu_pattern(pattern.data(), pattern.length());
310   icu::RegexMatcher matcher(icu_pattern, UREGEX_CASE_INSENSITIVE, status);
311   if (!U_SUCCESS(status)) {
312     LOG(ERROR) << "Invalid login regex: " << pattern << ", status: " << status;
313     // If an invalid pattern is provided, then prohibit *all* logins (better to
314     // break signin than to quietly allow users to sign in).
315     return false;
316   }
317   base::string16 username16 = base::UTF8ToUTF16(username);
318   icu::UnicodeString icu_input(username16.data(), username16.length());
319   matcher.reset(icu_input);
320   status = U_ZERO_ERROR;
321   UBool match = matcher.matches(status);
322   DCHECK(U_SUCCESS(status));
323   return !!match;  // !! == convert from UBool to bool.
324 }
325
326 bool SigninManager::IsAllowedUsername(const std::string& username) const {
327   const PrefService* local_state = local_state_pref_registrar_.prefs();
328   if (!local_state)
329     return true;  // In a unit test with no local state - all names are allowed.
330
331   std::string pattern =
332       local_state->GetString(prefs::kGoogleServicesUsernamePattern);
333   return IsUsernameAllowedByPolicy(username, pattern);
334 }
335
336 bool SigninManager::AuthInProgress() const {
337   return !possibly_invalid_username_.empty();
338 }
339
340 const std::string& SigninManager::GetUsernameForAuthInProgress() const {
341   return possibly_invalid_username_;
342 }
343
344 void SigninManager::DisableOneClickSignIn(PrefService* prefs) {
345   prefs->SetBoolean(prefs::kReverseAutologinEnabled, false);
346 }
347
348 void SigninManager::CompletePendingSignin() {
349   DCHECK(!possibly_invalid_username_.empty());
350   OnSignedIn(possibly_invalid_username_);
351
352   if (client_->ShouldMergeSigninCredentialsIntoCookieJar()) {
353     merge_session_helper_.reset(new MergeSessionHelper(
354         token_service_, GaiaConstants::kChromeSource,
355         client_->GetURLRequestContext(), NULL));
356   }
357
358   DCHECK(!temp_refresh_token_.empty());
359   DCHECK(IsAuthenticated());
360   std::string account_id = GetAuthenticatedAccountId();
361   token_service_->UpdateCredentials(account_id, temp_refresh_token_);
362   temp_refresh_token_.clear();
363
364   if (client_->ShouldMergeSigninCredentialsIntoCookieJar())
365     merge_session_helper_->LogIn(account_id);
366 }
367
368 void SigninManager::OnExternalSigninCompleted(const std::string& username) {
369   OnSignedIn(username);
370 }
371
372 void SigninManager::OnSignedIn(const std::string& username) {
373   client_->GetPrefs()->SetInt64(prefs::kSignedInTime,
374                                 base::Time::Now().ToInternalValue());
375   SetAuthenticatedUsername(username);
376   possibly_invalid_username_.clear();
377
378   FOR_EACH_OBSERVER(
379       Observer,
380       observer_list_,
381       GoogleSigninSucceeded(GetAuthenticatedAccountId(),
382                             GetAuthenticatedUsername(),
383                             password_));
384
385   client_->GoogleSigninSucceeded(GetAuthenticatedAccountId(),
386                                  GetAuthenticatedUsername(),
387                                  password_);
388
389   signin_metrics::LogSigninProfile(client_->IsFirstRun(),
390                                    client_->GetInstallDate());
391
392   password_.clear();                           // Don't need it anymore.
393   DisableOneClickSignIn(client_->GetPrefs());  // Don't ever offer again.
394 }
395
396 void SigninManager::ProhibitSignout(bool prohibit_signout) {
397   prohibit_signout_ = prohibit_signout;
398 }
399
400 bool SigninManager::IsSignoutProhibited() const { return prohibit_signout_; }