| // Copyright (c) 2012 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/chromeos/login/managed/locally_managed_user_creation_controller.h" |
| |
| #include "base/bind.h" |
| #include "base/file_util.h" |
| #include "base/files/file_path.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "base/sys_info.h" |
| #include "base/task_runner_util.h" |
| #include "base/threading/sequenced_worker_pool.h" |
| #include "base/values.h" |
| #include "chrome/browser/chromeos/login/managed/locally_managed_user_constants.h" |
| #include "chrome/browser/chromeos/login/mount_manager.h" |
| #include "chrome/browser/chromeos/login/supervised_user_manager.h" |
| #include "chrome/browser/chromeos/login/user.h" |
| #include "chrome/browser/chromeos/login/user_manager.h" |
| #include "chrome/browser/lifetime/application_lifetime.h" |
| #include "chrome/browser/sync/profile_sync_service.h" |
| #include "chrome/browser/sync/profile_sync_service_factory.h" |
| #include "chromeos/dbus/dbus_thread_manager.h" |
| #include "chromeos/dbus/session_manager_client.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/user_metrics.h" |
| #include "crypto/random.h" |
| #include "google_apis/gaia/google_service_auth_error.h" |
| |
| namespace chromeos { |
| |
| namespace { |
| |
| const int kMasterKeySize = 32; |
| const int kUserCreationTimeoutSeconds = 30; // 30 seconds. |
| |
| bool StoreManagedUserFiles(const std::string& token, |
| const base::FilePath& base_path) { |
| if (!base::SysInfo::IsRunningOnChromeOS()) { |
| // If running on desktop, cryptohome stub does not create home directory. |
| base::CreateDirectory(base_path); |
| } |
| base::FilePath token_file = base_path.Append(kManagedUserTokenFilename); |
| int bytes = file_util::WriteFile(token_file, token.c_str(), token.length()); |
| return bytes >= 0; |
| } |
| |
| } // namespace |
| |
| // static |
| const int LocallyManagedUserCreationController::kDummyAvatarIndex = -111; |
| |
| LocallyManagedUserCreationController::StatusConsumer::~StatusConsumer() {} |
| |
| LocallyManagedUserCreationController::UserCreationContext::UserCreationContext() |
| : avatar_index(kDummyAvatarIndex), |
| token_acquired(false), |
| token_succesfully_written(false), |
| creation_type(NEW_USER), |
| manager_profile(NULL) {} |
| |
| LocallyManagedUserCreationController::UserCreationContext:: |
| ~UserCreationContext() {} |
| |
| // static |
| LocallyManagedUserCreationController* |
| LocallyManagedUserCreationController::current_controller_ = NULL; |
| |
| LocallyManagedUserCreationController::LocallyManagedUserCreationController( |
| LocallyManagedUserCreationController::StatusConsumer* consumer, |
| const std::string& manager_id) |
| : consumer_(consumer), |
| weak_factory_(this) { |
| DCHECK(!current_controller_) << "More than one controller exist."; |
| current_controller_ = this; |
| creation_context_.reset( |
| new LocallyManagedUserCreationController::UserCreationContext()); |
| creation_context_->manager_id = manager_id; |
| } |
| |
| LocallyManagedUserCreationController::~LocallyManagedUserCreationController() { |
| current_controller_ = NULL; |
| } |
| |
| void LocallyManagedUserCreationController::SetUpCreation( |
| const base::string16& display_name, |
| const std::string& password, |
| int avatar_index) { |
| DCHECK(creation_context_); |
| creation_context_->display_name = display_name; |
| creation_context_->password = password; |
| creation_context_->avatar_index = avatar_index; |
| } |
| |
| void LocallyManagedUserCreationController::StartImport( |
| const base::string16& display_name, |
| const std::string& password, |
| int avatar_index, |
| const std::string& sync_id, |
| const std::string& master_key) { |
| DCHECK(creation_context_); |
| creation_context_->creation_type = USER_IMPORT; |
| creation_context_->display_name = display_name; |
| creation_context_->password = password; |
| creation_context_->avatar_index = avatar_index; |
| creation_context_->sync_user_id = sync_id; |
| creation_context_->master_key = master_key; |
| StartCreation(); |
| } |
| |
| void LocallyManagedUserCreationController::SetManagerProfile( |
| Profile* manager_profile) { |
| creation_context_->manager_profile = manager_profile; |
| } |
| |
| void LocallyManagedUserCreationController::StartCreation() { |
| DCHECK(creation_context_); |
| VLOG(1) << "Starting supervised user creation"; |
| |
| ProfileSyncService* sync_service = |
| ProfileSyncServiceFactory::GetInstance()->GetForProfile( |
| creation_context_->manager_profile); |
| ProfileSyncService::SyncStatusSummary status = |
| sync_service->QuerySyncStatusSummary(); |
| |
| if (status == ProfileSyncService::DATATYPES_NOT_INITIALIZED) |
| consumer_->OnLongCreationWarning(); |
| |
| timeout_timer_.Start( |
| FROM_HERE, base::TimeDelta::FromSeconds(kUserCreationTimeoutSeconds), |
| this, |
| &LocallyManagedUserCreationController::CreationTimedOut); |
| SupervisedUserManager* manager = |
| UserManager::Get()->GetSupervisedUserManager(); |
| manager->StartCreationTransaction(creation_context_->display_name); |
| |
| creation_context_->local_user_id = manager->GenerateUserId(); |
| |
| if (creation_context_->creation_type == NEW_USER) { |
| creation_context_->sync_user_id = |
| ManagedUserRegistrationUtility::GenerateNewManagedUserId(); |
| } |
| |
| manager->CreateUserRecord( |
| creation_context_->manager_id, |
| creation_context_->local_user_id, |
| creation_context_->sync_user_id, |
| creation_context_->display_name); |
| |
| manager->SetCreationTransactionUserId(creation_context_->local_user_id); |
| VLOG(1) << "Creating cryptohome"; |
| authenticator_ = new ManagedUserAuthenticator(this); |
| authenticator_->AuthenticateToCreate(creation_context_->local_user_id, |
| creation_context_->password); |
| } |
| |
| void LocallyManagedUserCreationController::OnAuthenticationFailure( |
| ManagedUserAuthenticator::AuthState error) { |
| timeout_timer_.Stop(); |
| ErrorCode code = NO_ERROR; |
| switch (error) { |
| case ManagedUserAuthenticator::NO_MOUNT: |
| code = CRYPTOHOME_NO_MOUNT; |
| break; |
| case ManagedUserAuthenticator::FAILED_MOUNT: |
| code = CRYPTOHOME_FAILED_MOUNT; |
| break; |
| case ManagedUserAuthenticator::FAILED_TPM: |
| code = CRYPTOHOME_FAILED_TPM; |
| break; |
| default: |
| NOTREACHED(); |
| } |
| if (consumer_) |
| consumer_->OnCreationError(code); |
| } |
| |
| void LocallyManagedUserCreationController::OnMountSuccess( |
| const std::string& mount_hash) { |
| creation_context_->mount_hash = mount_hash; |
| |
| if (creation_context_->creation_type == NEW_USER) { |
| // Generate master password. |
| char master_key_bytes[kMasterKeySize]; |
| crypto::RandBytes(&master_key_bytes, sizeof(master_key_bytes)); |
| creation_context_->master_key = StringToLowerASCII(base::HexEncode( |
| reinterpret_cast<const void*>(master_key_bytes), |
| sizeof(master_key_bytes))); |
| } |
| |
| VLOG(1) << "Adding master key"; |
| authenticator_->AddMasterKey(creation_context_->local_user_id, |
| creation_context_->password, |
| creation_context_->master_key); |
| } |
| |
| void LocallyManagedUserCreationController::OnAddKeySuccess() { |
| creation_context_->registration_utility = |
| ManagedUserRegistrationUtility::Create( |
| creation_context_->manager_profile); |
| |
| VLOG(1) << "Creating user on server"; |
| ManagedUserRegistrationInfo info(creation_context_->display_name, |
| creation_context_->avatar_index); |
| info.master_key = creation_context_->master_key; |
| timeout_timer_.Stop(); |
| creation_context_->registration_utility->Register( |
| creation_context_->sync_user_id, |
| info, |
| base::Bind(&LocallyManagedUserCreationController::RegistrationCallback, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void LocallyManagedUserCreationController::RegistrationCallback( |
| const GoogleServiceAuthError& error, |
| const std::string& token) { |
| if (error.state() == GoogleServiceAuthError::NONE) { |
| timeout_timer_.Start( |
| FROM_HERE, base::TimeDelta::FromSeconds(kUserCreationTimeoutSeconds), |
| this, |
| &LocallyManagedUserCreationController::CreationTimedOut); |
| TokenFetched(token); |
| } else { |
| LOG(ERROR) << "Managed user creation failed. Error code " << error.state(); |
| if (consumer_) |
| consumer_->OnCreationError(CLOUD_SERVER_ERROR); |
| } |
| } |
| |
| void LocallyManagedUserCreationController::CreationTimedOut() { |
| LOG(ERROR) << "Supervised user creation timed out."; |
| if (consumer_) |
| consumer_->OnCreationTimeout(); |
| } |
| |
| void LocallyManagedUserCreationController::FinishCreation() { |
| chrome::AttemptUserExit(); |
| } |
| |
| void LocallyManagedUserCreationController::CancelCreation() { |
| creation_context_->registration_utility.reset(); |
| chrome::AttemptUserExit(); |
| } |
| |
| std::string LocallyManagedUserCreationController::GetManagedUserId() { |
| DCHECK(creation_context_); |
| return creation_context_->local_user_id; |
| } |
| |
| void LocallyManagedUserCreationController::TokenFetched( |
| const std::string& token) { |
| creation_context_->token_acquired = true; |
| creation_context_->token = token; |
| |
| PostTaskAndReplyWithResult( |
| content::BrowserThread::GetBlockingPool(), |
| FROM_HERE, |
| base::Bind(&StoreManagedUserFiles, |
| creation_context_->token, |
| MountManager::GetHomeDir(creation_context_->mount_hash)), |
| base::Bind( |
| &LocallyManagedUserCreationController::OnManagedUserFilesStored, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void LocallyManagedUserCreationController::OnManagedUserFilesStored( |
| bool success) { |
| timeout_timer_.Stop(); |
| |
| content::RecordAction( |
| content::UserMetricsAction("ManagedMode_LocallyManagedUserCreated")); |
| |
| if (!success) { |
| if (consumer_) |
| consumer_->OnCreationError(TOKEN_WRITE_FAILED); |
| return; |
| } |
| // Assume that new token is valid. It will be automatically invalidated if |
| // sync service fails to use it. |
| UserManager::Get()->SaveUserOAuthStatus(creation_context_->local_user_id, |
| User::OAUTH2_TOKEN_STATUS_VALID); |
| UserManager::Get()->GetSupervisedUserManager()-> |
| CommitCreationTransaction(); |
| if (consumer_) |
| consumer_->OnCreationSuccess(); |
| } |
| |
| } // namespace chromeos |