Chromium Code Reviews| Index: chrome/browser/chromeos/login/device_settings_service.cc |
| diff --git a/chrome/browser/chromeos/login/device_settings_service.cc b/chrome/browser/chromeos/login/device_settings_service.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..c505ea68f542d82945f3500f583d84ad9511ff40 |
| --- /dev/null |
| +++ b/chrome/browser/chromeos/login/device_settings_service.cc |
| @@ -0,0 +1,287 @@ |
| +// 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/device_settings_service.h" |
| + |
| +#include "base/bind.h" |
| +#include "base/file_util.h" |
| +#include "base/lazy_instance.h" |
| +#include "base/logging.h" |
| +#include "base/message_loop.h" |
| +#include "base/stl_util.h" |
| +#include "chrome/browser/chromeos/login/owner_key_util.h" |
| +#include "chrome/browser/chromeos/login/session_manager_operation.h" |
| +#include "chrome/browser/policy/proto/chrome_device_policy.pb.h" |
| +#include "chrome/browser/policy/proto/device_management_backend.pb.h" |
| +#include "chrome/common/chrome_notification_types.h" |
| +#include "content/public/browser/browser_thread.h" |
| +#include "content/public/browser/notification_service.h" |
| +#include "content/public/browser/notification_source.h" |
| +#include "crypto/rsa_private_key.h" |
| + |
| +namespace em = enterprise_management; |
| + |
| +namespace chromeos { |
| + |
| +static base::LazyInstance<DeviceSettingsService> g_device_settings_service = |
| + LAZY_INSTANCE_INITIALIZER; |
| + |
| +OwnerKey::OwnerKey(scoped_ptr<std::vector<uint8> > public_key, |
| + scoped_ptr<crypto::RSAPrivateKey> private_key) |
| + : public_key_(public_key.Pass()), |
| + private_key_(private_key.Pass()) {} |
| + |
| +OwnerKey::~OwnerKey() {} |
| + |
| +DeviceSettingsService::Observer::~Observer() {} |
| + |
| +DeviceSettingsService::DeviceSettingsService() |
| + : session_manager_client_(NULL), |
| + ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), |
| + store_status_(STORE_SUCCESS), |
| + ownership_status_(OWNERSHIP_UNKNOWN) {} |
| + |
| +DeviceSettingsService::~DeviceSettingsService() { |
| + STLDeleteContainerPointers(pending_operations_.begin(), |
| + pending_operations_.end()); |
| + pending_operations_.clear(); |
| +} |
| + |
| +// static |
| +DeviceSettingsService* DeviceSettingsService::Get() { |
| + return g_device_settings_service.Pointer(); |
| +} |
| + |
| +void DeviceSettingsService::Initialize( |
| + SessionManagerClient* session_manager_client, |
| + scoped_refptr<OwnerKeyUtil> owner_key_util) { |
| + DCHECK(session_manager_client); |
| + DCHECK(owner_key_util.get()); |
| + DCHECK(!session_manager_client_); |
| + DCHECK(!owner_key_util_.get()); |
| + |
| + session_manager_client_ = session_manager_client; |
| + owner_key_util_ = owner_key_util; |
| + |
| + session_manager_client_->AddObserver(this); |
| + |
| + StartNextOperation(); |
| +} |
| + |
| +void DeviceSettingsService::Shutdown() { |
| + session_manager_client_->RemoveObserver(this); |
| + session_manager_client_ = NULL; |
| + owner_key_util_ = NULL; |
| +} |
| + |
| +scoped_refptr<OwnerKey> DeviceSettingsService::GetOwnerKey() { |
| + return owner_key_; |
| +} |
| + |
| +void DeviceSettingsService::Load() { |
| + EnqueueLoad(false); |
| +} |
| + |
| +void DeviceSettingsService::SignAndStore( |
| + scoped_ptr<em::ChromeDeviceSettingsProto> settings, |
| + const base::Closure& callback) { |
| + Enqueue( |
| + new SignAndStoreSettingsOperation( |
| + base::Bind(&DeviceSettingsService::HandleCompletedOperation, |
| + weak_factory_.GetWeakPtr(), |
| + callback), |
| + settings.Pass(), |
| + username_)); |
| +} |
| + |
| +void DeviceSettingsService::Store(const std::string& policy_blob, |
| + const base::Closure& callback) { |
| + Enqueue( |
| + new StoreSettingsOperation( |
| + base::Bind(&DeviceSettingsService::HandleCompletedOperation, |
| + weak_factory_.GetWeakPtr(), |
| + callback), |
| + policy_blob)); |
| +} |
| + |
| +DeviceSettingsService::OwnershipStatus |
| + DeviceSettingsService::GetOwnershipStatus(bool blocking) { |
| + OwnershipStatus status = OWNERSHIP_UNKNOWN; |
| + bool is_owned = false; |
| + if (content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) { |
| + ownership_status_lock_.Acquire(); |
| + status = ownership_status_; |
| + ownership_status_lock_.Release(); |
| + if (status != OWNERSHIP_UNKNOWN || !blocking) |
| + return status; |
| + // Under common usage there is very short lapse of time when ownership |
| + // status is still unknown after constructing DeviceSettingsService. |
| + LOG(ERROR) << "Blocking on UI thread in DeviceSettingsService::GetStatus"; |
| + base::ThreadRestrictions::ScopedAllowIO allow_io; |
| + is_owned = owner_key_util_->IsPublicKeyPresent(); |
| + } else { |
| + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); |
| + is_owned = owner_key_util_->IsPublicKeyPresent(); |
| + } |
| + status = is_owned ? OWNERSHIP_TAKEN : OWNERSHIP_NONE; |
| + SetOwnershipStatus(status); |
| + return status; |
| +} |
| + |
| +void DeviceSettingsService::GetOwnershipStatusAsync( |
| + const OwnershipStatusCallback& callback) { |
| + if (owner_key_.get()) { |
| + // If there is a key, report status immediately. |
| + MessageLoop::current()->PostTask( |
| + FROM_HERE, |
| + base::Bind(callback, |
| + owner_key_->public_key() ? OWNERSHIP_TAKEN : OWNERSHIP_NONE, |
| + owner_key_->private_key())); |
| + } else { |
| + // If the key hasn't been loaded yet, trigger a policy load operation or |
| + // wait for an existing operation to finish. |
| + pending_ownership_status_callbacks_.push_back(callback); |
| + if (pending_operations_.empty()) |
| + EnqueueLoad(false); |
| + } |
| +} |
| + |
| +bool DeviceSettingsService::HasPrivateOwnerKey() { |
| + return owner_key_.get() && owner_key_->private_key(); |
| +} |
| + |
| +void DeviceSettingsService::SetUsername(const std::string& username) { |
| + username_ = username; |
| + |
| + // The private key may have become available, so force a key reload. |
| + OwnerKeySet(true); |
| +} |
| + |
| +const std::string& DeviceSettingsService::GetUsername() const { |
| + return username_; |
| +} |
| + |
| +void DeviceSettingsService::AddObserver(Observer* observer) { |
| + observers_.AddObserver(observer); |
| +} |
| + |
| +void DeviceSettingsService::RemoveObserver(Observer* observer) { |
| + observers_.RemoveObserver(observer); |
| +} |
| + |
| +void DeviceSettingsService::OwnerKeySet(bool success) { |
| + if (!success) { |
| + LOG(ERROR) << "Owner key change failed."; |
| + return; |
| + } |
| + |
| + owner_key_ = NULL; |
| + EnsureReload(true); |
| +} |
| + |
| +void DeviceSettingsService::PropertyChangeComplete(bool success) { |
| + if (!success) { |
| + LOG(ERROR) << "Policy update failed."; |
| + return; |
| + } |
| + |
| + EnsureReload(false); |
| +} |
| + |
| +void DeviceSettingsService::Enqueue(SessionManagerOperation* operation) { |
| + pending_operations_.push_back(operation); |
| + if (pending_operations_.front() == operation) |
| + StartNextOperation(); |
| +} |
| + |
| +void DeviceSettingsService::EnqueueLoad(bool force_key_load) { |
| + SessionManagerOperation* operation = |
| + new LoadSettingsOperation( |
| + base::Bind(&DeviceSettingsService::HandleCompletedOperation, |
| + weak_factory_.GetWeakPtr(), |
| + base::Closure())); |
| + operation->set_force_key_load(force_key_load); |
| + Enqueue(operation); |
| +} |
| + |
| +void DeviceSettingsService::EnsureReload(bool force_key_load) { |
| + if (!pending_operations_.empty()) |
| + pending_operations_.front()->RestartLoad(force_key_load); |
|
pastarmovj
2012/07/30 12:19:19
What if the operation is a store op?
Mattias Nissler (ping if slow)
2012/07/31 13:02:15
If the Store op is still storing data, it'll not b
|
| + else |
| + EnqueueLoad(force_key_load); |
| +} |
| + |
| +void DeviceSettingsService::StartNextOperation() { |
| + if (!pending_operations_.empty() && |
| + session_manager_client_ && |
| + owner_key_util_.get()) { |
| + pending_operations_.front()->Start(session_manager_client_, |
| + owner_key_util_, owner_key_); |
| + } |
| +} |
| + |
| +void DeviceSettingsService::HandleCompletedOperation( |
| + const base::Closure& callback, |
| + SessionManagerOperation* operation, |
| + Status status) { |
| + DCHECK_EQ(operation, pending_operations_.front()); |
| + store_status_ = status; |
| + |
| + OwnershipStatus ownership_status = OWNERSHIP_UNKNOWN; |
| + bool is_owner = false; |
| + scoped_refptr<OwnerKey> new_key(operation->owner_key()); |
| + if (new_key.get()) { |
| + ownership_status = |
| + new_key->public_key() ? OWNERSHIP_TAKEN : OWNERSHIP_NONE; |
| + is_owner = new_key->private_key(); |
|
pastarmovj
2012/07/30 12:19:19
I think this will be clearer if you wrote down the
Mattias Nissler (ping if slow)
2012/07/31 13:02:15
Done.
|
| + } else { |
| + NOTREACHED() << "Failed to determine key status."; |
| + } |
| + SetOwnershipStatus(ownership_status); |
| + |
| + if (owner_key_.get() != new_key.get()) { |
|
pastarmovj
2012/07/30 12:19:19
If I understand this right this code will now trig
Mattias Nissler (ping if slow)
2012/07/31 13:02:15
Yes, I verified key rotation thoroughly, and it st
|
| + owner_key_ = new_key; |
| + FOR_EACH_OBSERVER(Observer, observers_, OwnershipStatusChanged()); |
| + content::NotificationService::current()->Notify( |
| + chrome::NOTIFICATION_OWNERSHIP_STATUS_CHANGED, |
| + content::Source<DeviceSettingsService>(this), |
| + content::NotificationService::NoDetails()); |
| + } |
| + |
| + if (status == STORE_SUCCESS) { |
| + policy_data_ = operation->policy_data().Pass(); |
| + device_settings_ = operation->device_settings().Pass(); |
| + } else if (status != STORE_KEY_UNAVAILABLE) { |
| + LOG(ERROR) << "Session manager operation failed: " << status; |
| + } |
| + |
| + FOR_EACH_OBSERVER(Observer, observers_, DeviceSettingsUpdated()); |
| + |
| + std::vector<OwnershipStatusCallback> callbacks; |
| + callbacks.swap(pending_ownership_status_callbacks_); |
| + for (std::vector<OwnershipStatusCallback>::iterator iter(callbacks.begin()); |
| + iter != callbacks.end(); ++iter) { |
| + iter->Run(ownership_status, is_owner); |
| + } |
| + |
| + // The completion callback happens after the notification so clients can |
| + // filter self-triggered updates. |
| + if (!callback.is_null()) |
| + callback.Run(); |
| + |
| + // Only remove the pending operation here, so new operations triggered by any |
| + // of the callbacks above are queued up properly. |
| + pending_operations_.pop_front(); |
| + delete operation; |
| + |
| + StartNextOperation(); |
| +} |
| + |
| +void DeviceSettingsService::SetOwnershipStatus(OwnershipStatus new_status) { |
| + DCHECK(new_status == OWNERSHIP_TAKEN || new_status == OWNERSHIP_NONE); |
| + base::AutoLock lk(ownership_status_lock_); |
| + ownership_status_ = new_status; |
| +} |
| + |
| +} // namespace chromeos |