Commit 5f2d92c5 authored by tbarzic's avatar tbarzic Committed by Commit bot

[Easy signin] Update the logic for getting user device list from cryptohome

* Add retry logic if initial loads fail (to handle the case cryptohome
service has not yet started)
* Cache the loaded results.

BUG=401634

Review URL: https://codereview.chromium.org/583403002

Cr-Commit-Position: refs/heads/master@{#296056}
parent 4c929f39
......@@ -4,29 +4,100 @@
#include "chrome/browser/signin/easy_unlock_service_signin_chromeos.h"
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/values.h"
#include "base/stl_util.h"
#include "base/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_key_manager.h"
#include "chrome/browser/chromeos/login/session/user_session_manager.h"
#include "chromeos/chromeos_switches.h"
#include "chromeos/login/auth/user_context.h"
namespace {
// The maximum allowed backoff interval when waiting for cryptohome to start.
uint32 kMaxCryptohomeBackoffIntervalMs = 10000u;
// If the data load fails, the initial interval after which the load will be
// retried. Further intervals will exponentially increas by factor 2.
uint32 kInitialCryptohomeBackoffIntervalMs = 200u;
// Calculates the backoff interval that should be used next.
// |backoff| The last backoff interval used.
uint32 GetNextBackoffInterval(uint32 backoff) {
if (backoff == 0u)
return kInitialCryptohomeBackoffIntervalMs;
return backoff * 2;
}
void LoadDataForUser(
const std::string& user_id,
uint32 backoff_ms,
const chromeos::EasyUnlockKeyManager::GetDeviceDataListCallback& callback);
// Callback passed to |LoadDataForUser()|.
// If |LoadDataForUser| function succeeded, it invokes |callback| with the
// results.
// If |LoadDataForUser| failed and further retries are allowed, schedules new
// |LoadDataForUser| call with some backoff. If no further retires are allowed,
// it invokes |callback| with the |LoadDataForUser| results.
void RetryDataLoadOnError(
const std::string& user_id,
uint32 backoff_ms,
const chromeos::EasyUnlockKeyManager::GetDeviceDataListCallback& callback,
bool success,
const chromeos::EasyUnlockDeviceKeyDataList& data_list) {
if (success) {
callback.Run(success, data_list);
return;
}
uint32 next_backoff_ms = GetNextBackoffInterval(backoff_ms);
if (next_backoff_ms > kMaxCryptohomeBackoffIntervalMs) {
callback.Run(false, data_list);
return;
}
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::Bind(&LoadDataForUser, user_id, next_backoff_ms, callback),
base::TimeDelta::FromMilliseconds(next_backoff_ms));
}
// Loads device data list associated with the user's Easy unlock keys.
void LoadDataForUser(
const std::string& user_id,
uint32 backoff_ms,
const chromeos::EasyUnlockKeyManager::GetDeviceDataListCallback& callback) {
chromeos::EasyUnlockKeyManager* key_manager =
chromeos::UserSessionManager::GetInstance()->GetEasyUnlockKeyManager();
DCHECK(key_manager);
key_manager->GetDeviceDataList(
chromeos::UserContext(user_id),
base::Bind(&RetryDataLoadOnError, user_id, backoff_ms, callback));
}
} // namespace
EasyUnlockServiceSignin::UserData::UserData()
: state(EasyUnlockServiceSignin::USER_DATA_STATE_INITIAL) {
}
EasyUnlockServiceSignin::UserData::~UserData() {}
EasyUnlockServiceSignin::EasyUnlockServiceSignin(Profile* profile)
: EasyUnlockService(profile),
allow_cryptohome_backoff_(true),
weak_ptr_factory_(this) {
}
EasyUnlockServiceSignin::~EasyUnlockServiceSignin() {
}
void EasyUnlockServiceSignin::SetAssociatedUser(const std::string& user_id) {
if (user_id_ == user_id)
return;
user_id_ = user_id;
FetchCryptohomeKeys();
STLDeleteContainerPairSecondPointers(user_data_.begin(), user_data_.end());
}
EasyUnlockService::Type EasyUnlockServiceSignin::GetType() const {
......@@ -42,7 +113,6 @@ void EasyUnlockServiceSignin::LaunchSetup() {
}
const base::DictionaryValue* EasyUnlockServiceSignin::GetPermitAccess() const {
// TODO(tbarzic): Implement this (http://crbug.com/401634).
return NULL;
}
......@@ -56,7 +126,10 @@ void EasyUnlockServiceSignin::ClearPermitAccess() {
}
const base::ListValue* EasyUnlockServiceSignin::GetRemoteDevices() const {
return remote_devices_value_.get();
const UserData* data = FindLoadedDataForCurrentUser();
if (!data)
return NULL;
return &data->remote_devices_value;
}
void EasyUnlockServiceSignin::SetRemoteDevices(
......@@ -82,52 +155,72 @@ EasyUnlockService::TurnOffFlowStatus
}
std::string EasyUnlockServiceSignin::GetChallenge() const {
const UserData* data = FindLoadedDataForCurrentUser();
// TODO(xiyuan): Use correct remote device instead of hard coded first one.
size_t device_index = 0;
return device_index < remote_devices_.size()
? remote_devices_[device_index].challenge
: std::string();
uint32 device_index = 0;
if (!data || data->devices.size() <= device_index)
return std::string();
return data->devices[device_index].challenge;
}
void EasyUnlockServiceSignin::InitializeInternal() {
}
bool EasyUnlockServiceSignin::IsAllowedInternal() {
return !user_id_.empty() && !remote_devices_.empty() &&
return !user_id_.empty() &&
FindLoadedDataForCurrentUser() &&
CommandLine::ForCurrentProcess()->HasSwitch(
chromeos::switches::kEnableEasySignin);
}
void EasyUnlockServiceSignin::FetchCryptohomeKeys() {
remote_devices_.clear();
remote_devices_value_.reset();
if (user_id_.empty()) {
UpdateAppState();
void EasyUnlockServiceSignin::LoadCurrentUserDataIfNeeded() {
if (user_id_.empty() ||
!CommandLine::ForCurrentProcess()->HasSwitch(
chromeos::switches::kEnableEasySignin))
return;
}
chromeos::EasyUnlockKeyManager* key_manager =
chromeos::UserSessionManager::GetInstance()->GetEasyUnlockKeyManager();
DCHECK(key_manager);
key_manager->GetDeviceDataList(
chromeos::UserContext(user_id_),
base::Bind(&EasyUnlockServiceSignin::OnCryptohomeKeysFetched,
weak_ptr_factory_.GetWeakPtr()));
std::map<std::string, UserData*>::iterator it = user_data_.find(user_id_);
if (it == user_data_.end())
user_data_.insert(std::make_pair(user_id_, new UserData()));
UserData* data = user_data_[user_id_];
if (data->state != USER_DATA_STATE_INITIAL)
return;
data->state = USER_DATA_STATE_LOADING;
LoadDataForUser(
user_id_,
allow_cryptohome_backoff_ ? 0u : kMaxCryptohomeBackoffIntervalMs,
base::Bind(&EasyUnlockServiceSignin::OnUserDataLoaded,
weak_ptr_factory_.GetWeakPtr(),
user_id_));
}
void EasyUnlockServiceSignin::OnCryptohomeKeysFetched(
void EasyUnlockServiceSignin::OnUserDataLoaded(
const std::string& user_id,
bool success,
const chromeos::EasyUnlockDeviceKeyDataList& devices) {
if (!success) {
LOG(WARNING) << "Easy unlock cryptohome keys not found for user "
<< user_id_;
return;
allow_cryptohome_backoff_ = false;
UserData* data = user_data_[user_id_];
data->state = USER_DATA_STATE_LOADED;
if (success) {
data->devices = devices;
chromeos::EasyUnlockKeyManager::DeviceDataListToRemoteDeviceList(
user_id, devices, &data->remote_devices_value);
}
}
remote_devices_ = devices;
remote_devices_value_.reset(new base::ListValue);
chromeos::EasyUnlockKeyManager::DeviceDataListToRemoteDeviceList(
user_id_, remote_devices_, remote_devices_value_.get());
UpdateAppState();
const EasyUnlockServiceSignin::UserData*
EasyUnlockServiceSignin::FindLoadedDataForCurrentUser() const {
if (user_id_.empty())
return NULL;
std::map<std::string, UserData*>::const_iterator it =
user_data_.find(user_id_);
if (it == user_data_.end())
return NULL;
if (it->second->state != USER_DATA_STATE_LOADED)
return NULL;
return it->second;
}
......@@ -5,28 +5,52 @@
#ifndef CHROME_BROWSER_SIGNIN_EASY_UNLOCK_SERVICE_SIGNIN_CHROMEOS_H_
#define CHROME_BROWSER_SIGNIN_EASY_UNLOCK_SERVICE_SIGNIN_CHROMEOS_H_
#include <map>
#include <string>
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/values.h"
#include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_types.h"
#include "chrome/browser/signin/easy_unlock_service.h"
namespace base {
class DictionaryValue;
class ListValue;
}
// EasyUnlockService instance that should be used for signin profile.
class EasyUnlockServiceSignin : public EasyUnlockService {
public:
explicit EasyUnlockServiceSignin(Profile* profile);
virtual ~EasyUnlockServiceSignin();
void SetAssociatedUser(const std::string& user_id);
private:
// The load state of a user's cryptohome key data.
enum UserDataState {
// Initial state, the key data is empty and not being loaded.
USER_DATA_STATE_INITIAL,
// The key data is empty, but being loaded.
USER_DATA_STATE_LOADING,
// The key data has been loaded.
USER_DATA_STATE_LOADED
};
// Structure containing a user's key data loaded from cryptohome.
struct UserData {
UserData();
~UserData();
// The loading state of the data.
UserDataState state;
// The data as returned from cryptohome.
chromeos::EasyUnlockDeviceKeyDataList devices;
// The list of remote device dictionaries understood by Easy unlock app.
// This will be returned by |GetRemoteDevices| method.
base::ListValue remote_devices_value;
private:
DISALLOW_COPY_AND_ASSIGN(UserData);
};
// EasyUnlockService implementation:
virtual EasyUnlockService::Type GetType() const OVERRIDE;
virtual std::string GetUserEmail() const OVERRIDE;
......@@ -44,20 +68,31 @@ class EasyUnlockServiceSignin : public EasyUnlockService {
virtual bool IsAllowedInternal() OVERRIDE;
virtual void InitializeInternal() OVERRIDE;
// Fetches |remote_devices_| info from cryptohome keys.
void FetchCryptohomeKeys();
// Loads the device data associated with the user's Easy unlock keys from
// crypthome.
void LoadCurrentUserDataIfNeeded();
// Callback invoked when the cryptohome keys are fetched.
void OnCryptohomeKeysFetched(
// Callback invoked when the user's device data is loaded from cryptohome.
void OnUserDataLoaded(
const std::string& user_id,
bool success,
const chromeos::EasyUnlockDeviceKeyDataList& devices);
const chromeos::EasyUnlockDeviceKeyDataList& data);
// User id of the associated user.
// If the device data has been loaded for the current user, returns is.
// Otherwise, returns NULL.
const UserData* FindLoadedDataForCurrentUser() const;
// User id of the user currently associated with the service.
std::string user_id_;
// Remote devices of the associated user.
chromeos::EasyUnlockDeviceKeyDataList remote_devices_;
scoped_ptr<base::ListValue> remote_devices_value_;
// Maps user ids to their fetched cryptohome key data.
std::map<std::string, UserData*> user_data_;
// Whether failed attempts to load user data should be retried.
// This is to handle case where cryptohome daemon is not started in time the
// service attempts to load some data. Retries will be allowed only until the
// first data load finishes (even if it fails).
bool allow_cryptohome_backoff_;
base::WeakPtrFactory<EasyUnlockServiceSignin> weak_ptr_factory_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment