// Copyright (c) 2011 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/policy/user_policy_identity_strategy.h"
#include "base/file_util.h"
#include "chrome/browser/browser_signin.h"
#include "chrome/browser/net/gaia/token_service.h"
#include "chrome/browser/policy/proto/device_management_backend.pb.h"
#include "chrome/browser/policy/proto/device_management_constants.h"
#include "chrome/browser/policy/proto/device_management_local.pb.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/guid.h"
#include "chrome/common/net/gaia/gaia_constants.h"
#include "content/browser/browser_thread.h"
#include "content/common/notification_details.h"
#include "content/common/notification_service.h"
#include "content/common/notification_source.h"
#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/login/user_manager.h"
#endif
namespace policy {
namespace em = enterprise_management;
// Responsible for managing the on-disk token cache.
class UserPolicyIdentityStrategy::TokenCache
: public base::RefCountedThreadSafe<
UserPolicyIdentityStrategy::TokenCache> {
public:
TokenCache(const base::WeakPtr<UserPolicyIdentityStrategy>& identity_strategy,
const FilePath& cache_file);
void Load();
void Store(const std::string& token, const std::string& device_id);
private:
friend class base::RefCountedThreadSafe<
UserPolicyIdentityStrategy::TokenCache>;
~TokenCache() {}
void LoadOnFileThread();
void NotifyOnUIThread(const std::string& token,
const std::string& device_id);
void StoreOnFileThread(const std::string& token,
const std::string& device_id);
const base::WeakPtr<UserPolicyIdentityStrategy> identity_strategy_;
const FilePath cache_file_;
DISALLOW_COPY_AND_ASSIGN(TokenCache);
};
UserPolicyIdentityStrategy::TokenCache::TokenCache(
const base::WeakPtr<UserPolicyIdentityStrategy>& identity_strategy,
const FilePath& cache_file)
: identity_strategy_(identity_strategy),
cache_file_(cache_file) {}
void UserPolicyIdentityStrategy::TokenCache::Load() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
NewRunnableMethod(
this, &UserPolicyIdentityStrategy::TokenCache::LoadOnFileThread));
}
void UserPolicyIdentityStrategy::TokenCache::Store(
const std::string& token,
const std::string& device_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
NewRunnableMethod(
this,
&UserPolicyIdentityStrategy::TokenCache::StoreOnFileThread,
token,
device_id));
}
void UserPolicyIdentityStrategy::TokenCache::LoadOnFileThread() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
std::string device_token;
std::string device_id;
if (file_util::PathExists(cache_file_)) {
std::string data;
em::DeviceCredentials device_credentials;
if (file_util::ReadFileToString(cache_file_, &data) &&
device_credentials.ParseFromArray(data.c_str(), data.size())) {
device_token = device_credentials.device_token();
device_id = device_credentials.device_id();
}
}
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
NewRunnableMethod(
this,
&UserPolicyIdentityStrategy::TokenCache::NotifyOnUIThread,
device_token,
device_id));
}
void UserPolicyIdentityStrategy::TokenCache::NotifyOnUIThread(
const std::string& token,
const std::string& device_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (identity_strategy_.get())
identity_strategy_->OnCacheLoaded(token, device_id);
}
void UserPolicyIdentityStrategy::TokenCache::StoreOnFileThread(
const std::string& token,
const std::string& device_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
em::DeviceCredentials device_credentials;
device_credentials.set_device_token(token);
device_credentials.set_device_id(device_id);
std::string data;
bool success = device_credentials.SerializeToString(&data);
if (!success) {
LOG(WARNING) << "Failed serialize device token data, will not write "
<< cache_file_.value();
return;
}
file_util::WriteFile(cache_file_, data.c_str(), data.length());
}
UserPolicyIdentityStrategy::UserPolicyIdentityStrategy(
Profile* profile,
const FilePath& cache_file)
: profile_(profile),
ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
cache_ = new TokenCache(weak_ptr_factory_.GetWeakPtr(), cache_file);
registrar_.Add(this,
NotificationType::TOKEN_AVAILABLE,
Source<TokenService>(profile->GetTokenService()));
// Register for the event of user login. The device management token won't
// be fetched until we know the domain of the currently logged in user.
#if defined(OS_CHROMEOS)
registrar_.Add(this,
NotificationType::LOGIN_USER_CHANGED,
NotificationService::AllSources());
#else
registrar_.Add(this,
NotificationType::GOOGLE_SIGNIN_SUCCESSFUL,
Source<Profile>(profile_));
#endif
cache_->Load();
}
UserPolicyIdentityStrategy::~UserPolicyIdentityStrategy() {}
std::string UserPolicyIdentityStrategy::GetDeviceToken() {
return device_token_;
}
std::string UserPolicyIdentityStrategy::GetDeviceID() {
return device_id_;
}
std::string UserPolicyIdentityStrategy::GetMachineID() {
return std::string();
}
std::string UserPolicyIdentityStrategy::GetMachineModel() {
return std::string();
}
em::DeviceRegisterRequest_Type
UserPolicyIdentityStrategy::GetPolicyRegisterType() {
return em::DeviceRegisterRequest::USER;
}
std::string UserPolicyIdentityStrategy::GetPolicyType() {
return kChromeUserPolicyType;
}
bool UserPolicyIdentityStrategy::GetCredentials(std::string* username,
std::string* auth_token) {
*username = GetCurrentUser();
*auth_token = profile_->GetTokenService()->GetTokenForService(
GaiaConstants::kDeviceManagementService);
return !username->empty() && !auth_token->empty() && !device_id_.empty();
}
void UserPolicyIdentityStrategy::OnDeviceTokenAvailable(
const std::string& token) {
DCHECK(!device_id_.empty());
device_token_ = token;
cache_->Store(device_token_, device_id_);
NotifyDeviceTokenChanged();
}
std::string UserPolicyIdentityStrategy::GetCurrentUser() {
#if defined(OS_CHROMEOS)
// TODO(mnissler) On CrOS it seems impossible to figure out what user belongs
// to a profile. Revisit after multi-profile support landed.
return chromeos::UserManager::Get()->logged_in_user().email();
#else
return profile_->GetBrowserSignin()->GetSignedInUsername();
#endif
}
void UserPolicyIdentityStrategy::CheckAndTriggerFetch() {
if (!GetCurrentUser().empty() &&
profile_->GetTokenService()->HasTokenForService(
GaiaConstants::kDeviceManagementService)) {
// For user tokens, there is no actual identifier. We generate a random
// identifier instead each time we ask for the token.
device_id_ = guid::GenerateGUID();
NotifyAuthChanged();
}
}
void UserPolicyIdentityStrategy::OnCacheLoaded(const std::string& token,
const std::string& device_id) {
if (!token.empty() && !device_id.empty()) {
device_token_ = token;
device_id_ = device_id;
NotifyDeviceTokenChanged();
} else {
CheckAndTriggerFetch();
}
}
void UserPolicyIdentityStrategy::Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (type == NotificationType::TOKEN_AVAILABLE) {
if (Source<TokenService>(source).ptr() == profile_->GetTokenService()) {
const TokenService::TokenAvailableDetails* token_details =
Details<const TokenService::TokenAvailableDetails>(details).ptr();
if (token_details->service() == GaiaConstants::kDeviceManagementService)
if (device_token_.empty()) {
// Request a new device management server token, but only in case we
// don't already have it.
CheckAndTriggerFetch();
}
}
#if defined(OS_CHROMEOS)
} else if (type == NotificationType::LOGIN_USER_CHANGED) {
CheckAndTriggerFetch();
#else
} else if (type == NotificationType::GOOGLE_SIGNIN_SUCCESSFUL) {
if (profile_ == Source<Profile>(source).ptr())
CheckAndTriggerFetch();
#endif
} else {
NOTREACHED();
}
}
} // namespace policy