// 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