• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_urls.h"
25 #include "net/base/escape.h"
26 #include "third_party/icu/source/i18n/unicode/regex.h"
27 
28 using namespace signin_internals_util;
29 
30 namespace {
31 
32 const char kChromiumSyncService[] = "service=chromiumsync";
33 
34 }  // namespace
35 
36 // Under the covers, we use a dummy chrome-extension ID to serve the purposes
37 // outlined in the .h file comment for this string.
38 const char SigninManager::kChromeSigninEffectiveSite[] =
39     "chrome-extension://acfccoigjajmmgbhpfbjnpckhjjegnih";
40 
41 // static
IsWebBasedSigninFlowURL(const GURL & url)42 bool SigninManager::IsWebBasedSigninFlowURL(const GURL& url) {
43   GURL effective(kChromeSigninEffectiveSite);
44   if (url.SchemeIs(effective.scheme().c_str()) &&
45       url.host() == effective.host()) {
46     return true;
47   }
48 
49   GURL service_login(GaiaUrls::GetInstance()->service_login_url());
50   if (url.GetOrigin() != service_login.GetOrigin())
51     return false;
52 
53   // Any login UI URLs with signin=chromiumsync should be considered a web
54   // URL (relies on GAIA keeping the "service=chromiumsync" query string
55   // fragment present even when embedding inside a "continue" parameter).
56   return net::UnescapeURLComponent(url.query(),
57                                    net::UnescapeRule::URL_SPECIAL_CHARS)
58              .find(kChromiumSyncService) != std::string::npos;
59 }
60 
SigninManager(SigninClient * client,ProfileOAuth2TokenService * token_service)61 SigninManager::SigninManager(SigninClient* client,
62                              ProfileOAuth2TokenService* token_service)
63     : SigninManagerBase(client),
64       prohibit_signout_(false),
65       type_(SIGNIN_TYPE_NONE),
66       client_(client),
67       token_service_(token_service),
68       weak_pointer_factory_(this) {}
69 
AddMergeSessionObserver(MergeSessionHelper::Observer * observer)70 void SigninManager::AddMergeSessionObserver(
71     MergeSessionHelper::Observer* observer) {
72   if (merge_session_helper_)
73     merge_session_helper_->AddObserver(observer);
74 }
75 
RemoveMergeSessionObserver(MergeSessionHelper::Observer * observer)76 void SigninManager::RemoveMergeSessionObserver(
77     MergeSessionHelper::Observer* observer) {
78   if (merge_session_helper_)
79     merge_session_helper_->RemoveObserver(observer);
80 }
81 
~SigninManager()82 SigninManager::~SigninManager() {}
83 
InitTokenService()84 void SigninManager::InitTokenService() {
85   const std::string& account_id = GetAuthenticatedUsername();
86   if (token_service_ && !account_id.empty())
87     token_service_->LoadCredentials(account_id);
88 }
89 
SigninTypeToString(SigninManager::SigninType type)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 
PrepareForSignin(SigninType type,const std::string & username,const std::string & password)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 
StartSignInWithRefreshToken(const std::string & refresh_token,const std::string & username,const std::string & password,const OAuthTokenFetchedCallback & callback)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 
CopyCredentialsFrom(const SigninManager & source)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 
ClearTransientSigninData()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 
HandleAuthError(const GoogleServiceAuthError & error)170 void SigninManager::HandleAuthError(const GoogleServiceAuthError& error) {
171   ClearTransientSigninData();
172 
173   FOR_EACH_OBSERVER(Observer, observer_list_, GoogleSigninFailed(error));
174 }
175 
SignOut(signin_metrics::ProfileSignout signout_source_metric)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   clear_authenticated_username();
210   client_->GetPrefs()->ClearPref(prefs::kGoogleServicesUsername);
211   client_->GetPrefs()->ClearPref(prefs::kSignedInTime);
212   client_->ClearSigninScopedDeviceId();
213 
214   // Erase (now) stale information from AboutSigninInternals.
215   NotifyDiagnosticsObservers(USERNAME, "");
216 
217   // Determine the duration the user was logged in and log that to UMA.
218   if (!signin_time.is_null()) {
219     base::TimeDelta signed_in_duration = base::Time::Now() - signin_time;
220     UMA_HISTOGRAM_COUNTS("Signin.SignedInDurationBeforeSignout",
221                          signed_in_duration.InMinutes());
222   }
223 
224   // Revoke all tokens before sending signed_out notification, because there
225   // may be components that don't listen for token service events when the
226   // profile is not connected to an account.
227   LOG(WARNING) << "Revoking refresh token on server. Reason: sign out, "
228                << "IsSigninAllowed: " << IsSigninAllowed();
229   token_service_->RevokeAllCredentials();
230 
231   FOR_EACH_OBSERVER(Observer,
232                     observer_list_,
233                     GoogleSignedOut(account_id, username));
234 }
235 
Initialize(PrefService * local_state)236 void SigninManager::Initialize(PrefService* local_state) {
237   SigninManagerBase::Initialize(local_state);
238 
239   // local_state can be null during unit tests.
240   if (local_state) {
241     local_state_pref_registrar_.Init(local_state);
242     local_state_pref_registrar_.Add(
243         prefs::kGoogleServicesUsernamePattern,
244         base::Bind(&SigninManager::OnGoogleServicesUsernamePatternChanged,
245                    weak_pointer_factory_.GetWeakPtr()));
246   }
247   signin_allowed_.Init(prefs::kSigninAllowed,
248                        client_->GetPrefs(),
249                        base::Bind(&SigninManager::OnSigninAllowedPrefChanged,
250                                   base::Unretained(this)));
251 
252   std::string user =
253       client_->GetPrefs()->GetString(prefs::kGoogleServicesUsername);
254   if ((!user.empty() && !IsAllowedUsername(user)) || !IsSigninAllowed()) {
255     // User is signed in, but the username is invalid - the administrator must
256     // have changed the policy since the last signin, so sign out the user.
257     SignOut(signin_metrics::SIGNIN_PREF_CHANGED_DURING_SIGNIN);
258   }
259 
260   InitTokenService();
261   account_id_helper_.reset(
262       new SigninAccountIdHelper(client_, token_service_, this));
263 }
264 
Shutdown()265 void SigninManager::Shutdown() {
266   if (merge_session_helper_)
267     merge_session_helper_->CancelAll();
268 
269   local_state_pref_registrar_.RemoveAll();
270   account_id_helper_.reset();
271   SigninManagerBase::Shutdown();
272 }
273 
OnGoogleServicesUsernamePatternChanged()274 void SigninManager::OnGoogleServicesUsernamePatternChanged() {
275   if (IsAuthenticated() &&
276       !IsAllowedUsername(GetAuthenticatedUsername())) {
277     // Signed in user is invalid according to the current policy so sign
278     // the user out.
279     SignOut(signin_metrics::GOOGLE_SERVICE_NAME_PATTERN_CHANGED);
280   }
281 }
282 
IsSigninAllowed() const283 bool SigninManager::IsSigninAllowed() const {
284   return signin_allowed_.GetValue();
285 }
286 
OnSigninAllowedPrefChanged()287 void SigninManager::OnSigninAllowedPrefChanged() {
288   if (!IsSigninAllowed())
289     SignOut(signin_metrics::SIGNOUT_PREF_CHANGED);
290 }
291 
292 // static
IsUsernameAllowedByPolicy(const std::string & username,const std::string & policy)293 bool SigninManager::IsUsernameAllowedByPolicy(const std::string& username,
294                                               const std::string& policy) {
295   if (policy.empty())
296     return true;
297 
298   // Patterns like "*@foo.com" are not accepted by our regex engine (since they
299   // are not valid regular expressions - they should instead be ".*@foo.com").
300   // For convenience, detect these patterns and insert a "." character at the
301   // front.
302   base::string16 pattern = base::UTF8ToUTF16(policy);
303   if (pattern[0] == L'*')
304     pattern.insert(pattern.begin(), L'.');
305 
306   // See if the username matches the policy-provided pattern.
307   UErrorCode status = U_ZERO_ERROR;
308   const icu::UnicodeString icu_pattern(pattern.data(), pattern.length());
309   icu::RegexMatcher matcher(icu_pattern, UREGEX_CASE_INSENSITIVE, status);
310   if (!U_SUCCESS(status)) {
311     LOG(ERROR) << "Invalid login regex: " << pattern << ", status: " << status;
312     // If an invalid pattern is provided, then prohibit *all* logins (better to
313     // break signin than to quietly allow users to sign in).
314     return false;
315   }
316   base::string16 username16 = base::UTF8ToUTF16(username);
317   icu::UnicodeString icu_input(username16.data(), username16.length());
318   matcher.reset(icu_input);
319   status = U_ZERO_ERROR;
320   UBool match = matcher.matches(status);
321   DCHECK(U_SUCCESS(status));
322   return !!match;  // !! == convert from UBool to bool.
323 }
324 
IsAllowedUsername(const std::string & username) const325 bool SigninManager::IsAllowedUsername(const std::string& username) const {
326   const PrefService* local_state = local_state_pref_registrar_.prefs();
327   if (!local_state)
328     return true;  // In a unit test with no local state - all names are allowed.
329 
330   std::string pattern =
331       local_state->GetString(prefs::kGoogleServicesUsernamePattern);
332   return IsUsernameAllowedByPolicy(username, pattern);
333 }
334 
AuthInProgress() const335 bool SigninManager::AuthInProgress() const {
336   return !possibly_invalid_username_.empty();
337 }
338 
GetUsernameForAuthInProgress() const339 const std::string& SigninManager::GetUsernameForAuthInProgress() const {
340   return possibly_invalid_username_;
341 }
342 
DisableOneClickSignIn(PrefService * prefs)343 void SigninManager::DisableOneClickSignIn(PrefService* prefs) {
344   prefs->SetBoolean(prefs::kReverseAutologinEnabled, false);
345 }
346 
CompletePendingSignin()347 void SigninManager::CompletePendingSignin() {
348   DCHECK(!possibly_invalid_username_.empty());
349   OnSignedIn(possibly_invalid_username_);
350 
351   if (client_->ShouldMergeSigninCredentialsIntoCookieJar()) {
352     merge_session_helper_.reset(new MergeSessionHelper(
353         token_service_, client_->GetURLRequestContext(), NULL));
354   }
355 
356   DCHECK(!temp_refresh_token_.empty());
357   DCHECK(IsAuthenticated());
358   token_service_->UpdateCredentials(GetAuthenticatedUsername(),
359                                     temp_refresh_token_);
360   temp_refresh_token_.clear();
361 
362   if (client_->ShouldMergeSigninCredentialsIntoCookieJar())
363     merge_session_helper_->LogIn(GetAuthenticatedUsername());
364 }
365 
OnExternalSigninCompleted(const std::string & username)366 void SigninManager::OnExternalSigninCompleted(const std::string& username) {
367   OnSignedIn(username);
368 }
369 
OnSignedIn(const std::string & username)370 void SigninManager::OnSignedIn(const std::string& username) {
371   client_->GetPrefs()->SetInt64(prefs::kSignedInTime,
372                                 base::Time::Now().ToInternalValue());
373   SetAuthenticatedUsername(username);
374   possibly_invalid_username_.clear();
375 
376   FOR_EACH_OBSERVER(
377       Observer,
378       observer_list_,
379       GoogleSigninSucceeded(GetAuthenticatedAccountId(),
380                             GetAuthenticatedUsername(),
381                             password_));
382 
383   client_->GoogleSigninSucceeded(GetAuthenticatedAccountId(),
384                                  GetAuthenticatedUsername(),
385                                  password_);
386 
387   signin_metrics::LogSigninProfile(client_->IsFirstRun(),
388                                    client_->GetInstallDate());
389 
390   password_.clear();                           // Don't need it anymore.
391   DisableOneClickSignIn(client_->GetPrefs());  // Don't ever offer again.
392 }
393 
ProhibitSignout(bool prohibit_signout)394 void SigninManager::ProhibitSignout(bool prohibit_signout) {
395   prohibit_signout_ = prohibit_signout;
396 }
397 
IsSignoutProhibited() const398 bool SigninManager::IsSignoutProhibited() const { return prohibit_signout_; }
399