// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/supervised_user/supervised_user_registration_utility.h" #include "base/base64.h" #include "base/bind.h" #include "base/command_line.h" #include "base/memory/scoped_ptr.h" #include "base/prefs/pref_service.h" #include "base/rand_util.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" #include "chrome/browser/signin/signin_manager_factory.h" #include "chrome/browser/supervised_user/supervised_user_constants.h" #include "chrome/browser/supervised_user/supervised_user_refresh_token_fetcher.h" #include "chrome/browser/supervised_user/supervised_user_shared_settings_service.h" #include "chrome/browser/supervised_user/supervised_user_shared_settings_service_factory.h" #include "chrome/browser/supervised_user/supervised_user_shared_settings_update.h" #include "chrome/browser/supervised_user/supervised_user_sync_service.h" #include "chrome/browser/supervised_user/supervised_user_sync_service_factory.h" #include "chrome/browser/sync/glue/device_info.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" #include "components/signin/core/browser/profile_oauth2_token_service.h" #include "components/signin/core/browser/signin_manager.h" #include "google_apis/gaia/gaia_urls.h" #include "google_apis/gaia/google_service_auth_error.h" using base::DictionaryValue; namespace { SupervisedUserRegistrationUtility* g_instance_for_tests = NULL; // Actual implementation of SupervisedUserRegistrationUtility. class SupervisedUserRegistrationUtilityImpl : public SupervisedUserRegistrationUtility, public SupervisedUserSyncServiceObserver { public: SupervisedUserRegistrationUtilityImpl( PrefService* prefs, scoped_ptr token_fetcher, SupervisedUserSyncService* service, SupervisedUserSharedSettingsService* shared_settings_service); virtual ~SupervisedUserRegistrationUtilityImpl(); // Registers a new supervised user with the server. |supervised_user_id| is a // new unique ID for the new supervised user. If its value is the same as that // of one of the existing supervised users, then the same user will be created // on this machine (and if he has no avatar in sync, his avatar will be // updated). |info| contains necessary information like the display name of // the user and his avatar. |callback| is called with the result of the // registration. We use the info here and not the profile, because on Chrome // OS the profile of the supervised user does not yet exist. virtual void Register(const std::string& supervised_user_id, const SupervisedUserRegistrationInfo& info, const RegistrationCallback& callback) OVERRIDE; // SupervisedUserSyncServiceObserver: virtual void OnSupervisedUserAcknowledged( const std::string& supervised_user_id) OVERRIDE; virtual void OnSupervisedUsersSyncingStopped() OVERRIDE; virtual void OnSupervisedUsersChanged() OVERRIDE; private: // Fetches the supervised user token when we have the device name. void FetchToken(const std::string& client_name); // Called when we have received a token for the supervised user. void OnReceivedToken(const GoogleServiceAuthError& error, const std::string& token); // Dispatches the callback and cleans up if all the conditions have been met. void CompleteRegistrationIfReady(); // Aborts any registration currently in progress. If |run_callback| is true, // calls the callback specified in Register() with the given |error|. void AbortPendingRegistration(bool run_callback, const GoogleServiceAuthError& error); // If |run_callback| is true, dispatches the callback with the saved token // (which may be empty) and the given |error|. In any case, resets internal // variables to be ready for the next registration. void CompleteRegistration(bool run_callback, const GoogleServiceAuthError& error); // Cancels any registration currently in progress, without calling the // callback or reporting an error. void CancelPendingRegistration(); // SupervisedUserSharedSettingsUpdate acknowledgment callback for password // data in shared settings. void OnPasswordChangeAcknowledged(bool success); PrefService* prefs_; scoped_ptr token_fetcher_; // A |KeyedService| owned by the custodian profile. SupervisedUserSyncService* supervised_user_sync_service_; // A |KeyedService| owned by the custodian profile. SupervisedUserSharedSettingsService* supervised_user_shared_settings_service_; std::string pending_supervised_user_id_; std::string pending_supervised_user_token_; bool pending_supervised_user_acknowledged_; bool is_existing_supervised_user_; bool avatar_updated_; RegistrationCallback callback_; scoped_ptr password_update_; base::WeakPtrFactory weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(SupervisedUserRegistrationUtilityImpl); }; } // namespace SupervisedUserRegistrationInfo::SupervisedUserRegistrationInfo( const base::string16& name, int avatar_index) : avatar_index(avatar_index), name(name) { } SupervisedUserRegistrationInfo::~SupervisedUserRegistrationInfo() {} ScopedTestingSupervisedUserRegistrationUtility:: ScopedTestingSupervisedUserRegistrationUtility( SupervisedUserRegistrationUtility* instance) { SupervisedUserRegistrationUtility::SetUtilityForTests(instance); } ScopedTestingSupervisedUserRegistrationUtility:: ~ScopedTestingSupervisedUserRegistrationUtility() { SupervisedUserRegistrationUtility::SetUtilityForTests(NULL); } // static scoped_ptr SupervisedUserRegistrationUtility::Create(Profile* profile) { if (g_instance_for_tests) { SupervisedUserRegistrationUtility* result = g_instance_for_tests; g_instance_for_tests = NULL; return make_scoped_ptr(result); } ProfileOAuth2TokenService* token_service = ProfileOAuth2TokenServiceFactory::GetForProfile(profile); SigninManagerBase* signin_manager = SigninManagerFactory::GetForProfile(profile); scoped_ptr token_fetcher = SupervisedUserRefreshTokenFetcher::Create( token_service, signin_manager->GetAuthenticatedAccountId(), profile->GetRequestContext()); SupervisedUserSyncService* supervised_user_sync_service = SupervisedUserSyncServiceFactory::GetForProfile(profile); SupervisedUserSharedSettingsService* supervised_user_shared_settings_service = SupervisedUserSharedSettingsServiceFactory::GetForBrowserContext(profile); return make_scoped_ptr(SupervisedUserRegistrationUtility::CreateImpl( profile->GetPrefs(), token_fetcher.Pass(), supervised_user_sync_service, supervised_user_shared_settings_service)); } // static std::string SupervisedUserRegistrationUtility::GenerateNewSupervisedUserId() { std::string new_supervised_user_id; base::Base64Encode(base::RandBytesAsString(8), &new_supervised_user_id); return new_supervised_user_id; } // static void SupervisedUserRegistrationUtility::SetUtilityForTests( SupervisedUserRegistrationUtility* utility) { if (g_instance_for_tests) delete g_instance_for_tests; g_instance_for_tests = utility; } // static SupervisedUserRegistrationUtility* SupervisedUserRegistrationUtility::CreateImpl( PrefService* prefs, scoped_ptr token_fetcher, SupervisedUserSyncService* service, SupervisedUserSharedSettingsService* shared_settings_service) { return new SupervisedUserRegistrationUtilityImpl(prefs, token_fetcher.Pass(), service, shared_settings_service); } namespace { SupervisedUserRegistrationUtilityImpl::SupervisedUserRegistrationUtilityImpl( PrefService* prefs, scoped_ptr token_fetcher, SupervisedUserSyncService* service, SupervisedUserSharedSettingsService* shared_settings_service) : prefs_(prefs), token_fetcher_(token_fetcher.Pass()), supervised_user_sync_service_(service), supervised_user_shared_settings_service_(shared_settings_service), pending_supervised_user_acknowledged_(false), is_existing_supervised_user_(false), avatar_updated_(false), weak_ptr_factory_(this) { supervised_user_sync_service_->AddObserver(this); } SupervisedUserRegistrationUtilityImpl:: ~SupervisedUserRegistrationUtilityImpl() { supervised_user_sync_service_->RemoveObserver(this); CancelPendingRegistration(); } void SupervisedUserRegistrationUtilityImpl::Register( const std::string& supervised_user_id, const SupervisedUserRegistrationInfo& info, const RegistrationCallback& callback) { DCHECK(pending_supervised_user_id_.empty()); callback_ = callback; pending_supervised_user_id_ = supervised_user_id; bool need_password_update = !info.password_data.empty(); const base::DictionaryValue* dict = prefs_->GetDictionary(prefs::kSupervisedUsers); is_existing_supervised_user_ = dict->HasKey(supervised_user_id); if (!is_existing_supervised_user_) { supervised_user_sync_service_->AddSupervisedUser( pending_supervised_user_id_, base::UTF16ToUTF8(info.name), info.master_key, info.password_signature_key, info.password_encryption_key, info.avatar_index); } else { const base::DictionaryValue* value = NULL; bool success = dict->GetDictionaryWithoutPathExpansion(supervised_user_id, &value); DCHECK(success); std::string key; bool need_keys = !info.password_signature_key.empty() || !info.password_encryption_key.empty(); bool have_keys = value->GetString(SupervisedUserSyncService::kPasswordSignatureKey, &key) && !key.empty() && value->GetString(SupervisedUserSyncService::kPasswordEncryptionKey, &key) && !key.empty(); bool keys_need_update = need_keys && !have_keys; if (keys_need_update) { supervised_user_sync_service_->UpdateSupervisedUser( pending_supervised_user_id_, base::UTF16ToUTF8(info.name), info.master_key, info.password_signature_key, info.password_encryption_key, info.avatar_index); } else { // The user already exists and does not need to be updated. need_password_update = false; OnSupervisedUserAcknowledged(supervised_user_id); } avatar_updated_ = supervised_user_sync_service_->UpdateSupervisedUserAvatarIfNeeded( supervised_user_id, info.avatar_index); } #if defined(OS_CHROMEOS) const char* kAvatarKey = supervised_users::kChromeOSAvatarIndex; #else const char* kAvatarKey = supervised_users::kChromeAvatarIndex; #endif supervised_user_shared_settings_service_->SetValue( pending_supervised_user_id_, kAvatarKey, base::FundamentalValue(info.avatar_index)); if (need_password_update) { password_update_.reset(new SupervisedUserSharedSettingsUpdate( supervised_user_shared_settings_service_, pending_supervised_user_id_, supervised_users::kChromeOSPasswordData, scoped_ptr(info.password_data.DeepCopy()), base::Bind( &SupervisedUserRegistrationUtilityImpl:: OnPasswordChangeAcknowledged, weak_ptr_factory_.GetWeakPtr()))); } browser_sync::DeviceInfo::GetClientName( base::Bind(&SupervisedUserRegistrationUtilityImpl::FetchToken, weak_ptr_factory_.GetWeakPtr())); } void SupervisedUserRegistrationUtilityImpl::CancelPendingRegistration() { AbortPendingRegistration( false, // Don't run the callback. The error will be ignored. GoogleServiceAuthError(GoogleServiceAuthError::NONE)); } void SupervisedUserRegistrationUtilityImpl::OnSupervisedUserAcknowledged( const std::string& supervised_user_id) { DCHECK_EQ(pending_supervised_user_id_, supervised_user_id); DCHECK(!pending_supervised_user_acknowledged_); pending_supervised_user_acknowledged_ = true; CompleteRegistrationIfReady(); } void SupervisedUserRegistrationUtilityImpl::OnPasswordChangeAcknowledged( bool success) { DCHECK(password_update_); DCHECK(success); password_update_.reset(); CompleteRegistrationIfReady(); } void SupervisedUserRegistrationUtilityImpl::OnSupervisedUsersSyncingStopped() { AbortPendingRegistration( true, // Run the callback. GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED)); } void SupervisedUserRegistrationUtilityImpl::OnSupervisedUsersChanged() {} void SupervisedUserRegistrationUtilityImpl::FetchToken( const std::string& client_name) { token_fetcher_->Start( pending_supervised_user_id_, client_name, base::Bind(&SupervisedUserRegistrationUtilityImpl::OnReceivedToken, weak_ptr_factory_.GetWeakPtr())); } void SupervisedUserRegistrationUtilityImpl::OnReceivedToken( const GoogleServiceAuthError& error, const std::string& token) { if (error.state() != GoogleServiceAuthError::NONE) { CompleteRegistration(true, error); return; } DCHECK(!token.empty()); pending_supervised_user_token_ = token; CompleteRegistrationIfReady(); } void SupervisedUserRegistrationUtilityImpl::CompleteRegistrationIfReady() { bool skip_check = CommandLine::ForCurrentProcess()->HasSwitch( switches::kNoSupervisedUserAcknowledgmentCheck); if (!pending_supervised_user_acknowledged_ && !skip_check) return; if (password_update_ && !skip_check) return; if (pending_supervised_user_token_.empty()) return; GoogleServiceAuthError error(GoogleServiceAuthError::NONE); CompleteRegistration(true, error); } void SupervisedUserRegistrationUtilityImpl::AbortPendingRegistration( bool run_callback, const GoogleServiceAuthError& error) { pending_supervised_user_token_.clear(); CompleteRegistration(run_callback, error); } void SupervisedUserRegistrationUtilityImpl::CompleteRegistration( bool run_callback, const GoogleServiceAuthError& error) { if (callback_.is_null()) return; if (pending_supervised_user_token_.empty()) { DCHECK(!pending_supervised_user_id_.empty()); if (!is_existing_supervised_user_) { // Remove the pending supervised user if we weren't successful. // However, check that we are not importing a supervised user // before deleting it from sync to avoid accidental deletion of // existing supervised users by just canceling the registration for // example. supervised_user_sync_service_->DeleteSupervisedUser( pending_supervised_user_id_); } else if (avatar_updated_) { // Canceling (or failing) a supervised user import that did set the avatar // should undo this change. supervised_user_sync_service_->ClearSupervisedUserAvatar( pending_supervised_user_id_); } } if (run_callback) callback_.Run(error, pending_supervised_user_token_); callback_.Reset(); } } // namespace