Home | History | Annotate | Download | only in browser
      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
     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 
     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 
     70 void SigninManager::AddMergeSessionObserver(
     71     MergeSessionHelper::Observer* observer) {
     72   if (merge_session_helper_)
     73     merge_session_helper_->AddObserver(observer);
     74 }
     75 
     76 void SigninManager::RemoveMergeSessionObserver(
     77     MergeSessionHelper::Observer* observer) {
     78   if (merge_session_helper_)
     79     merge_session_helper_->RemoveObserver(observer);
     80 }
     81 
     82 SigninManager::~SigninManager() {}
     83 
     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 
     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   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 
    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 
    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 
    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 
    283 bool SigninManager::IsSigninAllowed() const {
    284   return signin_allowed_.GetValue();
    285 }
    286 
    287 void SigninManager::OnSigninAllowedPrefChanged() {
    288   if (!IsSigninAllowed())
    289     SignOut(signin_metrics::SIGNOUT_PREF_CHANGED);
    290 }
    291 
    292 // static
    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 
    325 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 
    335 bool SigninManager::AuthInProgress() const {
    336   return !possibly_invalid_username_.empty();
    337 }
    338 
    339 const std::string& SigninManager::GetUsernameForAuthInProgress() const {
    340   return possibly_invalid_username_;
    341 }
    342 
    343 void SigninManager::DisableOneClickSignIn(PrefService* prefs) {
    344   prefs->SetBoolean(prefs::kReverseAutologinEnabled, false);
    345 }
    346 
    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 
    366 void SigninManager::OnExternalSigninCompleted(const std::string& username) {
    367   OnSignedIn(username);
    368 }
    369 
    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 
    394 void SigninManager::ProhibitSignout(bool prohibit_signout) {
    395   prohibit_signout_ = prohibit_signout;
    396 }
    397 
    398 bool SigninManager::IsSignoutProhibited() const { return prohibit_signout_; }
    399