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/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_; }
    374