OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012 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 "chrome/browser/signin/signin_manager_base.h" |
| 6 |
| 7 #include <string> |
| 8 #include <vector> |
| 9 |
| 10 #include "base/command_line.h" |
| 11 #include "base/memory/ref_counted.h" |
| 12 #include "base/prefs/pref_service.h" |
| 13 #include "base/string_util.h" |
| 14 #include "base/strings/string_split.h" |
| 15 #include "base/utf_string_conversions.h" |
| 16 #include "chrome/browser/browser_process.h" |
| 17 #include "chrome/browser/content_settings/cookie_settings.h" |
| 18 #include "chrome/browser/profiles/profile_info_cache.h" |
| 19 #include "chrome/browser/profiles/profile_io_data.h" |
| 20 #include "chrome/browser/profiles/profile_manager.h" |
| 21 #include "chrome/browser/signin/about_signin_internals.h" |
| 22 #include "chrome/browser/signin/about_signin_internals_factory.h" |
| 23 #include "chrome/browser/signin/signin_global_error.h" |
| 24 #include "chrome/browser/signin/signin_manager_cookie_helper.h" |
| 25 #include "chrome/browser/signin/token_service.h" |
| 26 #include "chrome/browser/signin/token_service_factory.h" |
| 27 #include "chrome/browser/sync/sync_prefs.h" |
| 28 #include "chrome/browser/ui/global_error/global_error_service.h" |
| 29 #include "chrome/browser/ui/global_error/global_error_service_factory.h" |
| 30 #include "chrome/common/chrome_notification_types.h" |
| 31 #include "chrome/common/chrome_switches.h" |
| 32 #include "chrome/common/pref_names.h" |
| 33 #include "content/public/browser/browser_thread.h" |
| 34 #include "google_apis/gaia/gaia_constants.h" |
| 35 #include "google_apis/gaia/gaia_urls.h" |
| 36 #include "third_party/icu/public/i18n/unicode/regex.h" |
| 37 |
| 38 using namespace signin_internals_util; |
| 39 |
| 40 using content::BrowserThread; |
| 41 |
| 42 // static |
| 43 bool SigninManagerBase::AreSigninCookiesAllowed(Profile* profile) { |
| 44 CookieSettings* cookie_settings = |
| 45 CookieSettings::Factory::GetForProfile(profile); |
| 46 return AreSigninCookiesAllowed(cookie_settings); |
| 47 } |
| 48 |
| 49 // static |
| 50 bool SigninManagerBase::AreSigninCookiesAllowed( |
| 51 CookieSettings* cookie_settings) { |
| 52 return cookie_settings && |
| 53 cookie_settings->IsSettingCookieAllowed( |
| 54 GURL(GaiaUrls::GetInstance()->gaia_origin_url()), |
| 55 GURL(GaiaUrls::GetInstance()->gaia_origin_url())); |
| 56 } |
| 57 |
| 58 // static |
| 59 bool SigninManagerBase::IsAllowedUsername(const std::string& username, |
| 60 const std::string& policy) { |
| 61 if (policy.empty()) |
| 62 return true; |
| 63 |
| 64 // Patterns like "*@foo.com" are not accepted by our regex engine (since they |
| 65 // are not valid regular expressions - they should instead be ".*@foo.com"). |
| 66 // For convenience, detect these patterns and insert a "." character at the |
| 67 // front. |
| 68 string16 pattern = UTF8ToUTF16(policy); |
| 69 if (pattern[0] == L'*') |
| 70 pattern.insert(pattern.begin(), L'.'); |
| 71 |
| 72 // See if the username matches the policy-provided pattern. |
| 73 UErrorCode status = U_ZERO_ERROR; |
| 74 const icu::UnicodeString icu_pattern(pattern.data(), pattern.length()); |
| 75 icu::RegexMatcher matcher(icu_pattern, UREGEX_CASE_INSENSITIVE, status); |
| 76 if (!U_SUCCESS(status)) { |
| 77 LOG(ERROR) << "Invalid login regex: " << pattern << ", status: " << status; |
| 78 // If an invalid pattern is provided, then prohibit *all* logins (better to |
| 79 // break signin than to quietly allow users to sign in). |
| 80 return false; |
| 81 } |
| 82 string16 username16 = UTF8ToUTF16(username); |
| 83 icu::UnicodeString icu_input(username16.data(), username16.length()); |
| 84 matcher.reset(icu_input); |
| 85 status = U_ZERO_ERROR; |
| 86 UBool match = matcher.matches(status); |
| 87 DCHECK(U_SUCCESS(status)); |
| 88 return !!match; // !! == convert from UBool to bool. |
| 89 } |
| 90 |
| 91 SigninManagerBase::SigninManagerBase() |
| 92 : profile_(NULL), |
| 93 weak_pointer_factory_(this) { |
| 94 } |
| 95 |
| 96 SigninManagerBase::~SigninManagerBase() { |
| 97 DCHECK(!signin_global_error_.get()) << |
| 98 "SigninManagerBase::Initialize called but not " |
| 99 "SigninManagerBase::Shutdown"; |
| 100 } |
| 101 |
| 102 void SigninManagerBase::Initialize(Profile* profile) { |
| 103 // Should never call Initialize() twice. |
| 104 DCHECK(!IsInitialized()); |
| 105 profile_ = profile; |
| 106 signin_global_error_.reset(new SigninGlobalError(this, profile)); |
| 107 GlobalErrorServiceFactory::GetForProfile(profile_)->AddGlobalError( |
| 108 signin_global_error_.get()); |
| 109 PrefService* local_state = g_browser_process->local_state(); |
| 110 // local_state can be null during unit tests. |
| 111 if (local_state) { |
| 112 local_state_pref_registrar_.Init(local_state); |
| 113 local_state_pref_registrar_.Add( |
| 114 prefs::kGoogleServicesUsernamePattern, |
| 115 base::Bind(&SigninManagerBase::OnGoogleServicesUsernamePatternChanged, |
| 116 weak_pointer_factory_.GetWeakPtr())); |
| 117 } |
| 118 signin_allowed_.Init(prefs::kSigninAllowed, profile_->GetPrefs(), |
| 119 base::Bind(&SigninManagerBase::OnSigninAllowedPrefChanged, |
| 120 base::Unretained(this))); |
| 121 |
| 122 // If the user is clearing the token service from the command line, then |
| 123 // clear their login info also (not valid to be logged in without any |
| 124 // tokens). |
| 125 CommandLine* cmd_line = CommandLine::ForCurrentProcess(); |
| 126 if (cmd_line->HasSwitch(switches::kClearTokenService)) |
| 127 profile->GetPrefs()->ClearPref(prefs::kGoogleServicesUsername); |
| 128 |
| 129 std::string user = profile_->GetPrefs()->GetString( |
| 130 prefs::kGoogleServicesUsername); |
| 131 if (!user.empty()) |
| 132 SetAuthenticatedUsername(user); |
| 133 |
| 134 InitTokenService(); |
| 135 |
| 136 if ((!user.empty() && !IsAllowedUsername(user)) || !IsSigninAllowed()) { |
| 137 // User is signed in, but the username is invalid - the administrator must |
| 138 // have changed the policy since the last signin, so sign out the user. |
| 139 SignOut(); |
| 140 } |
| 141 } |
| 142 |
| 143 void SigninManagerBase::InitTokenService() { |
| 144 // TokenService can be null for unit tests. |
| 145 TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); |
| 146 if (token_service) |
| 147 token_service->Initialize(GaiaConstants::kChromeSource, profile_); |
| 148 // Note: ChromeOS will kick off TokenService::LoadTokensFromDB from |
| 149 // OAuthLoginManager once the rest of the Profile is fully initialized. |
| 150 } |
| 151 |
| 152 bool SigninManagerBase::IsInitialized() const { |
| 153 return profile_ != NULL; |
| 154 } |
| 155 |
| 156 bool SigninManagerBase::IsAllowedUsername(const std::string& username) const { |
| 157 PrefService* local_state = g_browser_process->local_state(); |
| 158 if (!local_state) |
| 159 return true; // In a unit test with no local state - all names are allowed. |
| 160 |
| 161 std::string pattern = local_state->GetString( |
| 162 prefs::kGoogleServicesUsernamePattern); |
| 163 return IsAllowedUsername(username, pattern); |
| 164 } |
| 165 |
| 166 bool SigninManagerBase::IsSigninAllowed() const { |
| 167 return signin_allowed_.GetValue(); |
| 168 } |
| 169 |
| 170 // static |
| 171 bool SigninManagerBase::IsSigninAllowedOnIOThread(ProfileIOData* io_data) { |
| 172 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); |
| 173 return io_data->signin_allowed()->GetValue(); |
| 174 } |
| 175 |
| 176 const std::string& SigninManagerBase::GetAuthenticatedUsername() const { |
| 177 return authenticated_username_; |
| 178 } |
| 179 |
| 180 void SigninManagerBase::SetAuthenticatedUsername(const std::string& username) { |
| 181 if (!authenticated_username_.empty()) { |
| 182 DLOG_IF(ERROR, username != authenticated_username_) << |
| 183 "Tried to change the authenticated username to something different: " << |
| 184 "Current: " << authenticated_username_ << ", New: " << username; |
| 185 return; |
| 186 } |
| 187 authenticated_username_ = username; |
| 188 // TODO(tim): We could go further in ensuring kGoogleServicesUsername and |
| 189 // authenticated_username_ are consistent once established (e.g. remove |
| 190 // authenticated_username_ altogether). Bug 107160. |
| 191 |
| 192 NotifyDiagnosticsObservers(USERNAME, username); |
| 193 |
| 194 // Go ahead and update the last signed in username here as well. Once a |
| 195 // user is signed in the two preferences should match. Doing it here as |
| 196 // opposed to on signin allows us to catch the upgrade scenario. |
| 197 profile_->GetPrefs()->SetString(prefs::kGoogleServicesLastUsername, username); |
| 198 } |
| 199 |
| 200 void SigninManagerBase::SignOut() { |
| 201 DCHECK(IsInitialized()); |
| 202 |
| 203 if (authenticated_username_.empty() && !AuthInProgress()) |
| 204 return; |
| 205 |
| 206 GoogleServiceSignoutDetails details(authenticated_username_); |
| 207 authenticated_username_.clear(); |
| 208 profile_->GetPrefs()->ClearPref(prefs::kGoogleServicesUsername); |
| 209 |
| 210 // Erase (now) stale information from AboutSigninInternals. |
| 211 NotifyDiagnosticsObservers(USERNAME, ""); |
| 212 NotifyDiagnosticsObservers(LSID, ""); |
| 213 NotifyDiagnosticsObservers( |
| 214 signin_internals_util::SID, ""); |
| 215 |
| 216 TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); |
| 217 content::NotificationService::current()->Notify( |
| 218 chrome::NOTIFICATION_GOOGLE_SIGNED_OUT, |
| 219 content::Source<Profile>(profile_), |
| 220 content::Details<const GoogleServiceSignoutDetails>(&details)); |
| 221 token_service->ResetCredentialsInMemory(); |
| 222 token_service->EraseTokensFromDB(); |
| 223 } |
| 224 |
| 225 bool SigninManagerBase::AuthInProgress() const { |
| 226 // SigninManagerBase never kicks off auth processes itself. |
| 227 return false; |
| 228 } |
| 229 |
| 230 void SigninManagerBase::Shutdown() { |
| 231 if (signin_global_error_.get()) { |
| 232 GlobalErrorServiceFactory::GetForProfile(profile_)->RemoveGlobalError( |
| 233 signin_global_error_.get()); |
| 234 signin_global_error_.reset(); |
| 235 } |
| 236 } |
| 237 |
| 238 void SigninManagerBase::OnGoogleServicesUsernamePatternChanged() { |
| 239 if (!authenticated_username_.empty() && |
| 240 !IsAllowedUsername(authenticated_username_)) { |
| 241 // Signed in user is invalid according to the current policy so sign |
| 242 // the user out. |
| 243 SignOut(); |
| 244 } |
| 245 } |
| 246 |
| 247 void SigninManagerBase::OnSigninAllowedPrefChanged() { |
| 248 if (!IsSigninAllowed()) |
| 249 SignOut(); |
| 250 } |
| 251 |
| 252 void SigninManagerBase::AddSigninDiagnosticsObserver( |
| 253 SigninDiagnosticsObserver* observer) { |
| 254 signin_diagnostics_observers_.AddObserver(observer); |
| 255 } |
| 256 |
| 257 void SigninManagerBase::RemoveSigninDiagnosticsObserver( |
| 258 SigninDiagnosticsObserver* observer) { |
| 259 signin_diagnostics_observers_.RemoveObserver(observer); |
| 260 } |
| 261 |
| 262 void SigninManagerBase::NotifyDiagnosticsObservers( |
| 263 const UntimedSigninStatusField& field, |
| 264 const std::string& value) { |
| 265 FOR_EACH_OBSERVER(SigninDiagnosticsObserver, |
| 266 signin_diagnostics_observers_, |
| 267 NotifySigninValueChanged(field, value)); |
| 268 } |
| 269 |
| 270 void SigninManagerBase::NotifyDiagnosticsObservers( |
| 271 const TimedSigninStatusField& field, |
| 272 const std::string& value) { |
| 273 FOR_EACH_OBSERVER(SigninDiagnosticsObserver, |
| 274 signin_diagnostics_observers_, |
| 275 NotifySigninValueChanged(field, value)); |
| 276 } |
OLD | NEW |