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 @@ ...@@ -4,29 +4,100 @@
#include "chrome/browser/signin/easy_unlock_service_signin_chromeos.h" #include "chrome/browser/signin/easy_unlock_service_signin_chromeos.h"
#include "base/basictypes.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/command_line.h" #include "base/command_line.h"
#include "base/location.h"
#include "base/logging.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/easy_unlock/easy_unlock_key_manager.h"
#include "chrome/browser/chromeos/login/session/user_session_manager.h" #include "chrome/browser/chromeos/login/session/user_session_manager.h"
#include "chromeos/chromeos_switches.h" #include "chromeos/chromeos_switches.h"
#include "chromeos/login/auth/user_context.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) EasyUnlockServiceSignin::EasyUnlockServiceSignin(Profile* profile)
: EasyUnlockService(profile), : EasyUnlockService(profile),
allow_cryptohome_backoff_(true),
weak_ptr_factory_(this) { weak_ptr_factory_(this) {
} }
EasyUnlockServiceSignin::~EasyUnlockServiceSignin() { EasyUnlockServiceSignin::~EasyUnlockServiceSignin() {
} STLDeleteContainerPairSecondPointers(user_data_.begin(), user_data_.end());
void EasyUnlockServiceSignin::SetAssociatedUser(const std::string& user_id) {
if (user_id_ == user_id)
return;
user_id_ = user_id;
FetchCryptohomeKeys();
} }
EasyUnlockService::Type EasyUnlockServiceSignin::GetType() const { EasyUnlockService::Type EasyUnlockServiceSignin::GetType() const {
...@@ -42,7 +113,6 @@ void EasyUnlockServiceSignin::LaunchSetup() { ...@@ -42,7 +113,6 @@ void EasyUnlockServiceSignin::LaunchSetup() {
} }
const base::DictionaryValue* EasyUnlockServiceSignin::GetPermitAccess() const { const base::DictionaryValue* EasyUnlockServiceSignin::GetPermitAccess() const {
// TODO(tbarzic): Implement this (http://crbug.com/401634).
return NULL; return NULL;
} }
...@@ -56,7 +126,10 @@ void EasyUnlockServiceSignin::ClearPermitAccess() { ...@@ -56,7 +126,10 @@ void EasyUnlockServiceSignin::ClearPermitAccess() {
} }
const base::ListValue* EasyUnlockServiceSignin::GetRemoteDevices() const { 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( void EasyUnlockServiceSignin::SetRemoteDevices(
...@@ -82,52 +155,72 @@ EasyUnlockService::TurnOffFlowStatus ...@@ -82,52 +155,72 @@ EasyUnlockService::TurnOffFlowStatus
} }
std::string EasyUnlockServiceSignin::GetChallenge() const { std::string EasyUnlockServiceSignin::GetChallenge() const {
const UserData* data = FindLoadedDataForCurrentUser();
// TODO(xiyuan): Use correct remote device instead of hard coded first one. // TODO(xiyuan): Use correct remote device instead of hard coded first one.
size_t device_index = 0; uint32 device_index = 0;
return device_index < remote_devices_.size() if (!data || data->devices.size() <= device_index)
? remote_devices_[device_index].challenge return std::string();
: std::string(); return data->devices[device_index].challenge;
} }
void EasyUnlockServiceSignin::InitializeInternal() { void EasyUnlockServiceSignin::InitializeInternal() {
} }
bool EasyUnlockServiceSignin::IsAllowedInternal() { bool EasyUnlockServiceSignin::IsAllowedInternal() {
return !user_id_.empty() && !remote_devices_.empty() && return !user_id_.empty() &&
FindLoadedDataForCurrentUser() &&
CommandLine::ForCurrentProcess()->HasSwitch( CommandLine::ForCurrentProcess()->HasSwitch(
chromeos::switches::kEnableEasySignin); chromeos::switches::kEnableEasySignin);
} }
void EasyUnlockServiceSignin::FetchCryptohomeKeys() { void EasyUnlockServiceSignin::LoadCurrentUserDataIfNeeded() {
remote_devices_.clear(); if (user_id_.empty() ||
remote_devices_value_.reset(); !CommandLine::ForCurrentProcess()->HasSwitch(
if (user_id_.empty()) { chromeos::switches::kEnableEasySignin))
UpdateAppState();
return; return;
}
chromeos::EasyUnlockKeyManager* key_manager = std::map<std::string, UserData*>::iterator it = user_data_.find(user_id_);
chromeos::UserSessionManager::GetInstance()->GetEasyUnlockKeyManager(); if (it == user_data_.end())
DCHECK(key_manager); user_data_.insert(std::make_pair(user_id_, new UserData()));
key_manager->GetDeviceDataList(
chromeos::UserContext(user_id_), UserData* data = user_data_[user_id_];
base::Bind(&EasyUnlockServiceSignin::OnCryptohomeKeysFetched,
weak_ptr_factory_.GetWeakPtr())); 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, bool success,
const chromeos::EasyUnlockDeviceKeyDataList& devices) { const chromeos::EasyUnlockDeviceKeyDataList& devices) {
if (!success) { allow_cryptohome_backoff_ = false;
LOG(WARNING) << "Easy unlock cryptohome keys not found for user "
<< user_id_; UserData* data = user_data_[user_id_];
return; data->state = USER_DATA_STATE_LOADED;
if (success) {
data->devices = devices;
chromeos::EasyUnlockKeyManager::DeviceDataListToRemoteDeviceList(
user_id, devices, &data->remote_devices_value);
} }
}
remote_devices_ = devices; const EasyUnlockServiceSignin::UserData*
remote_devices_value_.reset(new base::ListValue); EasyUnlockServiceSignin::FindLoadedDataForCurrentUser() const {
chromeos::EasyUnlockKeyManager::DeviceDataListToRemoteDeviceList( if (user_id_.empty())
user_id_, remote_devices_, remote_devices_value_.get()); return NULL;
std::map<std::string, UserData*>::const_iterator it =
UpdateAppState(); 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 @@ ...@@ -5,28 +5,52 @@
#ifndef CHROME_BROWSER_SIGNIN_EASY_UNLOCK_SERVICE_SIGNIN_CHROMEOS_H_ #ifndef CHROME_BROWSER_SIGNIN_EASY_UNLOCK_SERVICE_SIGNIN_CHROMEOS_H_
#define CHROME_BROWSER_SIGNIN_EASY_UNLOCK_SERVICE_SIGNIN_CHROMEOS_H_ #define CHROME_BROWSER_SIGNIN_EASY_UNLOCK_SERVICE_SIGNIN_CHROMEOS_H_
#include <map>
#include <string> #include <string>
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/memory/weak_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/chromeos/login/easy_unlock/easy_unlock_types.h"
#include "chrome/browser/signin/easy_unlock_service.h" #include "chrome/browser/signin/easy_unlock_service.h"
namespace base {
class DictionaryValue;
class ListValue;
}
// EasyUnlockService instance that should be used for signin profile. // EasyUnlockService instance that should be used for signin profile.
class EasyUnlockServiceSignin : public EasyUnlockService { class EasyUnlockServiceSignin : public EasyUnlockService {
public: public:
explicit EasyUnlockServiceSignin(Profile* profile); explicit EasyUnlockServiceSignin(Profile* profile);
virtual ~EasyUnlockServiceSignin(); virtual ~EasyUnlockServiceSignin();
void SetAssociatedUser(const std::string& user_id);
private: 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: // EasyUnlockService implementation:
virtual EasyUnlockService::Type GetType() const OVERRIDE; virtual EasyUnlockService::Type GetType() const OVERRIDE;
virtual std::string GetUserEmail() const OVERRIDE; virtual std::string GetUserEmail() const OVERRIDE;
...@@ -44,20 +68,31 @@ class EasyUnlockServiceSignin : public EasyUnlockService { ...@@ -44,20 +68,31 @@ class EasyUnlockServiceSignin : public EasyUnlockService {
virtual bool IsAllowedInternal() OVERRIDE; virtual bool IsAllowedInternal() OVERRIDE;
virtual void InitializeInternal() OVERRIDE; virtual void InitializeInternal() OVERRIDE;
// Fetches |remote_devices_| info from cryptohome keys. // Loads the device data associated with the user's Easy unlock keys from
void FetchCryptohomeKeys(); // crypthome.
void LoadCurrentUserDataIfNeeded();
// Callback invoked when the cryptohome keys are fetched. // Callback invoked when the user's device data is loaded from cryptohome.
void OnCryptohomeKeysFetched( void OnUserDataLoaded(
const std::string& user_id,
bool success, 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_; std::string user_id_;
// Remote devices of the associated user. // Maps user ids to their fetched cryptohome key data.
chromeos::EasyUnlockDeviceKeyDataList remote_devices_; std::map<std::string, UserData*> user_data_;
scoped_ptr<base::ListValue> remote_devices_value_;
// 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_; 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