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