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.
5 #include "components/signin/core/browser/signin_manager.h"
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"
26 using namespace signin_internals_util;
30 const char kChromiumSyncService[] = "service=chromiumsync";
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";
40 bool SigninManager::IsWebBasedSigninFlowURL(const GURL& url) {
41 GURL effective(kChromeSigninEffectiveSite);
42 if (url.SchemeIs(effective.scheme().c_str()) &&
43 url.host() == effective.host()) {
47 GURL service_login(GaiaUrls::GetInstance()->service_login_url());
48 if (url.GetOrigin() != service_login.GetOrigin())
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;
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),
66 token_service_(token_service) {}
68 void SigninManager::AddMergeSessionObserver(
69 MergeSessionHelper::Observer* observer) {
70 if (merge_session_helper_)
71 merge_session_helper_->AddObserver(observer);
74 void SigninManager::RemoveMergeSessionObserver(
75 MergeSessionHelper::Observer* observer) {
76 if (merge_session_helper_)
77 merge_session_helper_->RemoveObserver(observer);
80 SigninManager::~SigninManager() {}
82 void SigninManager::InitTokenService() {
83 const std::string& account_id = GetAuthenticatedUsername();
84 if (token_service_ && !account_id.empty())
85 token_service_->LoadCredentials(account_id);
88 std::string SigninManager::SigninTypeToString(SigninManager::SigninType type) {
90 case SIGNIN_TYPE_NONE:
92 case SIGNIN_TYPE_WITH_REFRESH_TOKEN:
93 return "Signin with refresh token";
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());
107 if (!IsAllowedUsername(username)) {
108 // Account is not allowed by admin policy.
110 GoogleServiceAuthError(GoogleServiceAuthError::ACCOUNT_DISABLED));
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();
121 possibly_invalid_username_.assign(username);
122 password_.assign(password);
123 NotifyDiagnosticsObservers(SIGNIN_TYPE, SigninTypeToString(type));
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()));
135 if (!PrepareForSignin(SIGNIN_TYPE_WITH_REFRESH_TOKEN, username, password))
138 // Store our callback and token.
139 temp_refresh_token_ = refresh_token;
140 possibly_invalid_username_ = username;
142 NotifyDiagnosticsObservers(GET_USER_INFO_STATUS, "Successful");
144 if (!callback.is_null() && !temp_refresh_token_.empty()) {
145 callback.Run(temp_refresh_token_);
147 // No oauth token or callback, so just complete our pending signin.
148 CompletePendingSignin();
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_;
159 void SigninManager::ClearTransientSigninData() {
160 DCHECK(IsInitialized());
162 possibly_invalid_username_.clear();
164 type_ = SIGNIN_TYPE_NONE;
165 temp_refresh_token_.clear();
168 void SigninManager::HandleAuthError(const GoogleServiceAuthError& error) {
169 ClearTransientSigninData();
171 FOR_EACH_OBSERVER(Observer, observer_list_, GoogleSigninFailed(error));
174 void SigninManager::SignOut() {
175 DCHECK(IsInitialized());
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);
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
188 ClearTransientSigninData();
193 if (prohibit_signout_) {
194 DVLOG(1) << "Ignoring attempt to sign out while signout is prohibited";
198 ClearTransientSigninData();
200 const std::string username = GetAuthenticatedUsername();
201 clear_authenticated_username();
202 client_->GetPrefs()->ClearPref(prefs::kGoogleServicesUsername);
204 // Erase (now) stale information from AboutSigninInternals.
205 NotifyDiagnosticsObservers(USERNAME, "");
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();
214 FOR_EACH_OBSERVER(Observer, observer_list_, GoogleSignedOut(username));
217 void SigninManager::Initialize(PrefService* local_state) {
218 SigninManagerBase::Initialize(local_state);
220 // local_state can be null during unit tests.
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()));
228 signin_allowed_.Init(prefs::kSigninAllowed,
230 base::Bind(&SigninManager::OnSigninAllowedPrefChanged,
231 base::Unretained(this)));
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.
242 account_id_helper_.reset(
243 new SigninAccountIdHelper(client_, token_service_, this));
246 void SigninManager::Shutdown() {
247 if (merge_session_helper_)
248 merge_session_helper_->CancelAll();
250 local_state_pref_registrar_.RemoveAll();
251 account_id_helper_.reset();
252 SigninManagerBase::Shutdown();
255 void SigninManager::OnGoogleServicesUsernamePatternChanged() {
256 if (!GetAuthenticatedUsername().empty() &&
257 !IsAllowedUsername(GetAuthenticatedUsername())) {
258 // Signed in user is invalid according to the current policy so sign
264 bool SigninManager::IsSigninAllowed() const {
265 return signin_allowed_.GetValue();
268 void SigninManager::OnSigninAllowedPrefChanged() {
269 if (!IsSigninAllowed())
274 bool SigninManager::IsUsernameAllowedByPolicy(const std::string& username,
275 const std::string& policy) {
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
283 base::string16 pattern = base::UTF8ToUTF16(policy);
284 if (pattern[0] == L'*')
285 pattern.insert(pattern.begin(), L'.');
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).
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.
306 bool SigninManager::IsAllowedUsername(const std::string& username) const {
307 const PrefService* local_state = local_state_pref_registrar_.prefs();
309 return true; // In a unit test with no local state - all names are allowed.
311 std::string pattern =
312 local_state->GetString(prefs::kGoogleServicesUsernamePattern);
313 return IsUsernameAllowedByPolicy(username, pattern);
316 bool SigninManager::AuthInProgress() const {
317 return !possibly_invalid_username_.empty();
320 const std::string& SigninManager::GetUsernameForAuthInProgress() const {
321 return possibly_invalid_username_;
324 void SigninManager::DisableOneClickSignIn(PrefService* prefs) {
325 prefs->SetBoolean(prefs::kReverseAutologinEnabled, false);
328 void SigninManager::CompletePendingSignin() {
329 DCHECK(!possibly_invalid_username_.empty());
330 OnSignedIn(possibly_invalid_username_);
332 if (client_->ShouldMergeSigninCredentialsIntoCookieJar()) {
333 merge_session_helper_.reset(new MergeSessionHelper(
334 token_service_, client_->GetURLRequestContext(), NULL));
337 DCHECK(!temp_refresh_token_.empty());
338 DCHECK(!GetAuthenticatedUsername().empty());
339 token_service_->UpdateCredentials(GetAuthenticatedUsername(),
340 temp_refresh_token_);
341 temp_refresh_token_.clear();
343 if (client_->ShouldMergeSigninCredentialsIntoCookieJar())
344 merge_session_helper_->LogIn(GetAuthenticatedUsername());
347 void SigninManager::OnExternalSigninCompleted(const std::string& username) {
348 OnSignedIn(username);
351 void SigninManager::OnSignedIn(const std::string& username) {
352 SetAuthenticatedUsername(username);
353 possibly_invalid_username_.clear();
358 GoogleSigninSucceeded(GetAuthenticatedUsername(), password_));
360 client_->GoogleSigninSucceeded(GetAuthenticatedUsername(), password_);
362 password_.clear(); // Don't need it anymore.
363 DisableOneClickSignIn(client_->GetPrefs()); // Don't ever offer again.
366 void SigninManager::ProhibitSignout(bool prohibit_signout) {
367 prohibit_signout_ = prohibit_signout;
370 bool SigninManager::IsSignoutProhibited() const { return prohibit_signout_; }