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