blob: 07c7b01ca6fe99fe30de93e4c4e9319a9cd76777 [file] [log] [blame]
// 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<SupervisedUserRefreshTokenFetcher> 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<SupervisedUserRefreshTokenFetcher> 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<SupervisedUserSharedSettingsUpdate> password_update_;
base::WeakPtrFactory<SupervisedUserRegistrationUtilityImpl> 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>
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<SupervisedUserRefreshTokenFetcher> 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<SupervisedUserRefreshTokenFetcher> 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<SupervisedUserRefreshTokenFetcher> 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<base::Value>(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