Commit 0eb86d1c authored by Yusuf Sengul's avatar Yusuf Sengul Committed by Commit Bot

Resubmit - Add support for fetching experiments

Bug: 1143836
Change-Id: I8bd0f67da63c49432091fb28d7fba65f41ae2738
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2548351
Commit-Queue: Yusuf Sengul <yusufsn@google.com>
Reviewed-by: default avatarRobin Lewis <wrlewis@google.com>
Cr-Commit-Position: refs/heads/master@{#829341}
parent bba87382
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
#include "chrome/credential_provider/extension/os_service_manager.h" #include "chrome/credential_provider/extension/os_service_manager.h"
#include "chrome/credential_provider/extension/service.h" #include "chrome/credential_provider/extension/service.h"
#include "chrome/credential_provider/extension/task_manager.h" #include "chrome/credential_provider/extension/task_manager.h"
#include "chrome/credential_provider/gaiacp/experiments_fetcher.h"
#include "chrome/credential_provider/gaiacp/experiments_manager.h"
#include "chrome/credential_provider/gaiacp/gem_device_details_manager.h" #include "chrome/credential_provider/gaiacp/gem_device_details_manager.h"
#include "chrome/credential_provider/gaiacp/logging.h" #include "chrome/credential_provider/gaiacp/logging.h"
#include "chrome/credential_provider/gaiacp/reg_utils.h" #include "chrome/credential_provider/gaiacp/reg_utils.h"
...@@ -38,6 +40,13 @@ void RegisterAllTasks() { ...@@ -38,6 +40,13 @@ void RegisterAllTasks() {
"UploadDeviceDetails", credential_provider::GemDeviceDetailsManager:: "UploadDeviceDetails", credential_provider::GemDeviceDetailsManager::
UploadDeviceDetailsTaskCreator()); UploadDeviceDetailsTaskCreator());
} }
// Task to fetch experiments for all GCPW users.
if (credential_provider::ExperimentsManager::Get()->ExperimentsEnabled()) {
credential_provider::extension::TaskManager::Get()->RegisterTask(
"FetchExperiments", credential_provider::ExperimentsFetcher::
GetFetchExperimentsTaskCreator());
}
} }
int APIENTRY wWinMain(HINSTANCE hInstance, int APIENTRY wWinMain(HINSTANCE hInstance,
......
...@@ -16,6 +16,10 @@ import("//tools/grit/grit_rule.gni") ...@@ -16,6 +16,10 @@ import("//tools/grit/grit_rule.gni")
# This static library is shared with the setup program. # This static library is shared with the setup program.
source_set("common") { source_set("common") {
sources = [ sources = [
"experiments_fetcher.cc",
"experiments_fetcher.h",
"experiments_manager.cc",
"experiments_manager.h",
"gcp_crash_reporter_client.cc", "gcp_crash_reporter_client.cc",
"gcp_crash_reporter_client.h", "gcp_crash_reporter_client.h",
"gcp_crash_reporting_utils.cc", "gcp_crash_reporting_utils.cc",
......
// Copyright 2020 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/credential_provider/gaiacp/experiments_fetcher.h"
#include <windows.h>
#include "base/bind.h"
#include "base/files/file.h"
#include "base/json/json_writer.h"
#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
#include "chrome/common/chrome_version.h"
#include "chrome/credential_provider/gaiacp/experiments_manager.h"
#include "chrome/credential_provider/gaiacp/gcp_utils.h"
#include "chrome/credential_provider/gaiacp/logging.h"
#include "chrome/credential_provider/gaiacp/reg_utils.h"
namespace credential_provider {
namespace {
// HTTP endpoint on the GCPW service to fetch experiments.
const wchar_t kGcpwServiceFetchExperimentsPath[] = L"/v1/experiments";
// Default timeout when trying to make requests to the GCPW service.
const base::TimeDelta kDefaultFetchExperimentsRequestTimeout =
base::TimeDelta::FromMilliseconds(5000);
// The period of refreshing experiments.
const base::TimeDelta kExperimentsRefreshExecutionPeriod =
base::TimeDelta::FromHours(3);
// Maximum number of retries if a HTTP call to the backend fails.
constexpr unsigned int kMaxNumHttpRetries = 1;
// HTTP query parameters for fetch experiments RPC.
const char kObfuscatedUserIdKey[] = "obfuscated_gaia_id";
const char kGcpwVersionKey[] = "gcpw_version";
const char kDmTokenKey[] = "dm_token";
const char kDeviceResourceIdKey[] = "device_resource_id";
const char kFeaturesKey[] = "feature";
// Defines a task that is called by the ESA to perform the experiments fetch
// operation.
class ExperimentsFetchTask : public extension::Task {
public:
static std::unique_ptr<extension::Task> Create() {
std::unique_ptr<extension::Task> esa_task(new ExperimentsFetchTask());
return esa_task;
}
// ESA calls this to retrieve a configuration for the task execution. Return
// a default config for now.
extension::Config GetConfig() final {
extension::Config config;
config.execution_period = kExperimentsRefreshExecutionPeriod;
return config;
}
// ESA calls this to set all the user-device contexts for the execution of the
// task.
HRESULT SetContext(const std::vector<extension::UserDeviceContext>& c) final {
context_ = c;
return S_OK;
}
// ESA calls execute function to perform the actual task.
HRESULT Execute() final {
HRESULT task_status = S_OK;
for (const auto& c : context_) {
HRESULT hr = ExperimentsFetcher::Get()->FetchAndStoreExperiments(c);
if (FAILED(hr)) {
LOGFN(ERROR) << "Failed fetching experiments for " << c.user_sid
<< ". hr=" << putHR(hr);
task_status = hr;
}
}
return task_status;
}
private:
std::vector<extension::UserDeviceContext> context_;
};
// Builds the request dictionary to fetch experiments from the backend. If
// |dm_token| is empty, it isn't added into request. If user id isn't found for
// the given |sid|, returns an empty dictionary.
std::unique_ptr<base::DictionaryValue> GetExperimentsRequestDict(
const base::string16& sid,
const base::string16& device_resource_id,
const base::string16& dm_token) {
std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
base::string16 user_id;
HRESULT status = GetIdFromSid(sid.c_str(), &user_id);
if (FAILED(status)) {
LOGFN(ERROR) << "Could not get user id from sid " << sid;
return nullptr;
}
dict->SetString(kObfuscatedUserIdKey, user_id);
if (!dm_token.empty()) {
dict->SetString(kDmTokenKey, dm_token);
}
dict->SetString(kDeviceResourceIdKey, device_resource_id);
dict->SetString(kGcpwVersionKey, TEXT(CHROME_VERSION_STRING));
auto keys = std::make_unique<base::ListValue>();
for (auto& experiment : ExperimentsManager::Get()->GetExperimentsList())
keys->AppendString(experiment);
dict->Set(kFeaturesKey, std::move(keys));
return dict;
}
} // namespace
GURL ExperimentsFetcher::GetExperimentsUrl() {
GURL gcpw_service_url = GetGcpwServiceUrl();
return gcpw_service_url.Resolve(kGcpwServiceFetchExperimentsPath);
}
// static
ExperimentsFetcher* ExperimentsFetcher::Get() {
return *GetInstanceStorage();
}
// static
ExperimentsFetcher** ExperimentsFetcher::GetInstanceStorage() {
static ExperimentsFetcher instance;
static ExperimentsFetcher* instance_storage = &instance;
return &instance_storage;
}
// static
extension::TaskCreator ExperimentsFetcher::GetFetchExperimentsTaskCreator() {
return base::BindRepeating(&ExperimentsFetchTask::Create);
}
ExperimentsFetcher::ExperimentsFetcher() {}
ExperimentsFetcher::~ExperimentsFetcher() = default;
HRESULT ExperimentsFetcher::FetchAndStoreExperiments(
const extension::UserDeviceContext& context) {
return FetchAndStoreExperimentsInternal(
context.user_sid, /* access_token= */ "",
GetExperimentsRequestDict(context.user_sid, context.device_resource_id,
context.dm_token));
}
HRESULT ExperimentsFetcher::FetchAndStoreExperiments(
const base::string16& sid,
const std::string& access_token) {
HRESULT hr = FetchAndStoreExperimentsInternal(
sid, access_token,
GetExperimentsRequestDict(sid, GetUserDeviceResourceId(sid),
/* dm_token= */ L""));
return hr;
}
HRESULT ExperimentsFetcher::FetchAndStoreExperimentsInternal(
const base::string16& sid,
const std::string& access_token,
std::unique_ptr<base::DictionaryValue> request_dict) {
if (!request_dict) {
LOGFN(ERROR) << "Request dictionary is null";
return E_FAIL;
}
// Make the fetch experiments HTTP request.
base::Optional<base::Value> request_result;
HRESULT hr = WinHttpUrlFetcher::BuildRequestAndFetchResultFromHttpService(
GetExperimentsUrl(), access_token, {}, *request_dict.get(),
kDefaultFetchExperimentsRequestTimeout, kMaxNumHttpRetries,
&request_result);
if (FAILED(hr)) {
LOGFN(ERROR) << "BuildRequestAndFetchResultFromHttpService hr="
<< putHR(hr);
return hr;
}
std::string experiments_data;
if (request_result && request_result->is_dict()) {
if (!base::JSONWriter::Write(*request_result, &experiments_data)) {
LOGFN(ERROR) << "base::JSONWriter::Write failed";
return E_FAIL;
}
} else {
LOGFN(ERROR) << "Failed to parse experiments response!";
return E_FAIL;
}
uint32_t open_flags = base::File::FLAG_CREATE_ALWAYS |
base::File::FLAG_WRITE |
base::File::FLAG_EXCLUSIVE_WRITE;
std::unique_ptr<base::File> experiments_file = GetOpenedFileForUser(
sid, open_flags, kGcpwExperimentsDirectory, kGcpwUserExperimentsFileName);
if (!experiments_file) {
LOGFN(ERROR) << "Failed to open " << kGcpwUserExperimentsFileName
<< " file.";
return E_FAIL;
}
int num_bytes_written = experiments_file->Write(0, experiments_data.c_str(),
experiments_data.size());
experiments_file.reset();
if (size_t(num_bytes_written) != experiments_data.size()) {
LOGFN(ERROR) << "Failed writing experiments data to file! Only "
<< num_bytes_written << " bytes written out of "
<< experiments_data.size();
return E_FAIL;
}
base::Time fetch_time = base::Time::Now();
base::string16 fetch_time_millis = base::NumberToString16(
fetch_time.ToDeltaSinceWindowsEpoch().InMilliseconds());
if (!ExperimentsManager::Get()->ReloadExperiments(sid)) {
LOGFN(ERROR) << "Error when loading experiments for user with sid " << sid;
}
// Store the fetch time so we know whether a refresh is needed.
SetUserProperty(sid, kLastUserExperimentsRefreshTimeRegKey,
fetch_time_millis);
return S_OK;
}
} // namespace credential_provider
// Copyright 2020 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.
#ifndef CHROME_CREDENTIAL_PROVIDER_GAIACP_EXPERIMENTS_FETCHER_H_
#define CHROME_CREDENTIAL_PROVIDER_GAIACP_EXPERIMENTS_FETCHER_H_
#include "base/values.h"
#include "base/win/windows_types.h"
#include "chrome/credential_provider/extension/task_manager.h"
#include "url/gurl.h"
namespace credential_provider {
// Fetcher used to fetch experiments from GCPW backends.
class ExperimentsFetcher {
public:
// Gets the singleton instance.
static ExperimentsFetcher* Get();
// Provides the GCPW extension with a TaskCreator which can be used to create
// a task for fetching experiments.
static extension::TaskCreator GetFetchExperimentsTaskCreator();
// Fetches the experiments for the user implied with the |access_token| from
// the GCPW backend and saves it in file storage replacing any previously
// fetched versions.
HRESULT FetchAndStoreExperiments(const base::string16& sid,
const std::string& access_token);
// Fetches the experiments for the user-device |context| provided by the GCPW
// extension service from the GCPW backend and saves it in file storage
// replacing any previously fetched versions.
HRESULT FetchAndStoreExperiments(const extension::UserDeviceContext& context);
// Returns the experiments fetch endpoint.
GURL GetExperimentsUrl();
private:
// Returns the storage used for the instance pointer.
static ExperimentsFetcher** GetInstanceStorage();
ExperimentsFetcher();
virtual ~ExperimentsFetcher();
// Fetches experiments with the provided |request_dict|. |access_token| needs
// to be present if the experiments are being fetched in the presence of an
// oauth token. Otherwise |request_dict| should be carrying the obfuscated
// user id as well as DM token.
HRESULT FetchAndStoreExperimentsInternal(
const base::string16& sid,
const std::string& access_token,
std::unique_ptr<base::DictionaryValue> request_dict);
};
} // namespace credential_provider
#endif // CHROME_CREDENTIAL_PROVIDER_GAIACP_EXPERIMENTS_FETCHER_H_
// Copyright 2020 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/credential_provider/gaiacp/experiments_manager.h"
#include <vector>
#include "base/files/file.h"
#include "base/json/json_reader.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/credential_provider/gaiacp/gcp_utils.h"
#include "chrome/credential_provider/gaiacp/logging.h"
#include "chrome/credential_provider/gaiacp/reg_utils.h"
namespace credential_provider {
namespace {
// Registry key to control whether experiments feature is enabled.
const wchar_t kExperimentsEnabledRegKey[] = L"experiments_enabled";
// Name of the keys found in the experiments server response.
const char kResponseExperimentsKeyName[] = "experiments";
const char kResponseFeatureKeyName[] = "feature";
const char kResponseValueKeyName[] = "value";
} // namespace
// static
ExperimentsManager* ExperimentsManager::Get() {
return *GetInstanceStorage();
}
// static
ExperimentsManager** ExperimentsManager::GetInstanceStorage() {
static ExperimentsManager instance;
static ExperimentsManager* instance_storage = &instance;
return &instance_storage;
}
ExperimentsManager::ExperimentsManager() {
if (ExperimentsEnabled()) {
RegisterExperiments();
ReloadAllExperiments();
}
}
ExperimentsManager::~ExperimentsManager() = default;
void ExperimentsManager::RegisterExperiments() {
experiments_to_values_.clear();
for (ExperimentMetadata em : experiments) {
// Experiment name to default value mapping is created prior to reading
// experiments file.
experiments_to_values_[em.name].first = em.default_value;
}
}
// Reloads the experiment values from fetched experiments file for the given
// |sid|. Loads the fetched experiments values into |experiments_to_values_|.
bool ExperimentsManager::ReloadExperiments(const base::string16& sid) {
uint32_t open_flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
std::unique_ptr<base::File> experiments_file = GetOpenedFileForUser(
sid, open_flags, kGcpwExperimentsDirectory, kGcpwUserExperimentsFileName);
if (!experiments_file) {
return false;
}
std::vector<char> buffer(experiments_file->GetLength());
experiments_file->Read(0, buffer.data(), buffer.size());
experiments_file.reset();
base::Optional<base::Value> experiments_data =
base::JSONReader::Read(base::StringPiece(buffer.data(), buffer.size()),
base::JSON_ALLOW_TRAILING_COMMAS);
if (!experiments_data || !experiments_data->is_dict()) {
LOGFN(ERROR) << "Failed to read experiments data from file!";
return false;
}
const base::Value* experiments =
experiments_data->FindListKey(kResponseExperimentsKeyName);
if (!experiments) {
LOGFN(ERROR) << "User experiments not found!";
return false;
}
if (experiments->is_list()) {
for (const auto& item : experiments->GetList()) {
auto* f = item.FindStringKey(kResponseFeatureKeyName);
auto* v = item.FindStringKey(kResponseValueKeyName);
if (!f || !v) {
LOGFN(WARNING) << "Either feature or value are not found!";
}
experiments_to_values_[*f].second[base::UTF16ToUTF8(sid)] = *v;
}
}
return true;
}
// TODO(crbug.com/1143829): Reload experiments if they were fetched by ESA.
void ExperimentsManager::ReloadAllExperiments() {
std::map<base::string16, UserTokenHandleInfo> sid_to_gaia_id;
HRESULT hr = GetUserTokenHandles(&sid_to_gaia_id);
if (FAILED(hr)) {
LOGFN(ERROR) << "Unable to get the list of associated users";
return;
}
for (auto& sid : sid_to_gaia_id) {
if (!ReloadExperiments(sid.first)) {
LOGFN(ERROR) << "Error when loading experiments for user with sid "
<< sid.first;
}
}
}
std::string ExperimentsManager::GetExperimentForUser(const std::string& sid,
Experiment experiment) {
std::string experiment_name = experiments[experiment].name;
// There shouldn't be a case where a registered experiment can't be found, but
// just in case we return an empty string.
if (experiments_to_values_.find(experiment_name) ==
experiments_to_values_.end())
return "";
auto& sid_to_experiment_values =
experiments_to_values_[experiment_name].second;
// If the experiment value doesn't exist for the given sid, return the default
// value.
if (sid_to_experiment_values.find(sid) == sid_to_experiment_values.end()) {
return experiments_to_values_[experiment_name].first;
}
return sid_to_experiment_values[sid];
}
bool ExperimentsManager::GetExperimentForUserAsBool(const std::string& sid,
Experiment experiment) {
return base::ToLowerASCII(GetExperimentForUser(sid, experiment)) == "true";
}
bool ExperimentsManager::ExperimentsEnabled() const {
return GetGlobalFlagOrDefault(kExperimentsEnabledRegKey, 0);
}
std::vector<std::string> ExperimentsManager::GetExperimentsList() const {
std::vector<std::string> experiment_names;
for (auto& experiment : experiments)
experiment_names.push_back(experiment.name);
return experiment_names;
}
} // namespace credential_provider
// Copyright 2020 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.
#ifndef CHROME_CREDENTIAL_PROVIDER_GAIACP_EXPERIMENTS_MANAGER_H_
#define CHROME_CREDENTIAL_PROVIDER_GAIACP_EXPERIMENTS_MANAGER_H_
#include <map>
#include <vector>
#include "base/strings/string16.h"
namespace credential_provider {
// List of experiments which must be kept in sync with the metadata for the
// experiment below.
enum Experiment { TEST_CLIENT_FLAG, TEST_CLIENT_FLAG2 };
class ExperimentsManager {
public:
// Gets the singleton instance.
static ExperimentsManager* Get();
// Called as the first thing when ExperimentsManager is getting constructed.
// Registers all the experiments used in GCPW and ESA with their default
// values. If fetching experiments happen to fail, defaults set by this
// function are used.
void RegisterExperiments();
// Reloads the experiments for the given |sid|.
bool ReloadExperiments(const base::string16& sid);
// Returns the experiment value for the provided |sid| and |experiment|.
std::string GetExperimentForUser(const std::string& sid,
Experiment experiment);
// Returns the experiment value as a boolean for the provided |sid| and
// |experiment|.
bool GetExperimentForUserAsBool(const std::string& sid,
Experiment experiment);
// Return true if experiments feature is enabled.
bool ExperimentsEnabled() const;
// Returns the list of experiments to fetch from the backend.
std::vector<std::string> GetExperimentsList() const;
private:
// Returns the storage used for the instance pointer.
static ExperimentsManager** GetInstanceStorage();
ExperimentsManager();
virtual ~ExperimentsManager();
// Updates the cached values of experiments with the recent fetch.
void ReloadAllExperiments();
// sid to experiment value mapping.
typedef std::map<std::string, std::string> PerUserValues;
// default value and per user experiment value pair.
typedef std::pair<std::string, PerUserValues> ExperimentValue;
// Map of features to <default value, per user value mapping>
std::map<std::string, ExperimentValue> experiments_to_values_;
// Struct which contains an individual experiment name and default value.
struct ExperimentMetadata {
Experiment experiment;
std::string name;
std::string default_value;
};
// Metadata for list of supported experiments.
std::vector<ExperimentMetadata> experiments = {
// TODO(crbug.com/1147522): Clean up the test experiments when actual
// experiments are introduced. These were added for e2e testing.
{TEST_CLIENT_FLAG, "test_client_flag", "false"},
{TEST_CLIENT_FLAG2, "test_client_flag2", "false"}};
};
} // namespace credential_provider
#endif // CHROME_CREDENTIAL_PROVIDER_GAIACP_EXPERIMENTS_MANAGER_H_
// Copyright 2020 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 "base/strings/utf_string_conversions.h"
#include "chrome/credential_provider/extension/task.h"
#include "chrome/credential_provider/gaiacp/experiments_fetcher.h"
#include "chrome/credential_provider/gaiacp/experiments_manager.h"
#include "chrome/credential_provider/gaiacp/gcp_utils.h"
#include "chrome/credential_provider/gaiacp/reg_utils.h"
#include "chrome/credential_provider/test/gcp_fakes.h"
#include "chrome/credential_provider/test/gls_runner_test_base.h"
#include "chrome/credential_provider/test/test_credential.h"
#include "google_apis/gaia/gaia_urls.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace credential_provider {
namespace testing {
class ExperimentsManagerTest : public GlsRunnerTestBase {
protected:
void SetUp() override;
};
void ExperimentsManagerTest::SetUp() {
GlsRunnerTestBase::SetUp();
ASSERT_EQ(S_OK,
SetGlobalFlagForTesting(L"experiments_enabled", 1)); // IN-TEST
FakesForTesting fakes;
fakes.fake_win_http_url_fetcher_creator =
fake_http_url_fetcher_factory()->GetCreatorCallback();
WinHttpUrlFetcher::SetCreatorForTesting(
fakes.fake_win_http_url_fetcher_creator);
// Set token result a valid access token.
fake_http_url_fetcher_factory()->SetFakeResponse(
GURL(GaiaUrls::GetInstance()->oauth2_token_url().spec().c_str()),
FakeWinHttpUrlFetcher::Headers(), "{\"access_token\": \"dummy_token\"}");
}
TEST_F(ExperimentsManagerTest, DefaultValues) {
EXPECT_EQ("false", ExperimentsManager::Get()->GetExperimentForUser(
"test_sid", Experiment::TEST_CLIENT_FLAG));
EXPECT_EQ("false", ExperimentsManager::Get()->GetExperimentForUser(
"test_sid", Experiment::TEST_CLIENT_FLAG2));
EXPECT_FALSE(ExperimentsManager::Get()->GetExperimentForUserAsBool(
"test_sid", Experiment::TEST_CLIENT_FLAG));
EXPECT_FALSE(ExperimentsManager::Get()->GetExperimentForUserAsBool(
"test_sid", Experiment::TEST_CLIENT_FLAG2));
}
// Tests different outcomes of fetching experiments through GCPW:
// 0 - Experiments are fetched successfully.
// 1 - Failed fetching experiments.
// 2 - Response in fetching experiments contains malformed payload.
class ExperimentsManagerGcpwE2ETest
: public ExperimentsManagerTest,
public ::testing::WithParamInterface<int> {};
TEST_P(ExperimentsManagerGcpwE2ETest, FetchingExperiments) {
int experiment_fetch_status = GetParam();
// Create a fake user that has the same gaia id as the test gaia id.
CComBSTR sid;
ASSERT_EQ(S_OK,
fake_os_user_manager()->CreateTestOSUser(
L"foo", L"password", L"Full Name", L"comment",
base::UTF8ToUTF16(kDefaultGaiaId), L"user@company.com", &sid));
base::string16 device_resource_id = L"foo_resource_id";
ASSERT_EQ(S_OK, SetUserProperty(OLE2W(sid), L"device_resource_id",
device_resource_id));
// Re-registering effectively clears the experiment values from the previous
// fetches.
ExperimentsManager::Get()->RegisterExperiments();
GURL url = ExperimentsFetcher::Get()->GetExperimentsUrl();
if (experiment_fetch_status == 0) {
fake_http_url_fetcher_factory()->SetFakeResponse(
url, FakeWinHttpUrlFetcher::Headers(),
"{\"experiments\": [{\"feature\": \"test_client_flag\", \"value\": "
"\"abc\"}, {\"feature\": \"test_client_flag2\", \"value\": \"def\"} ] "
"}");
} else if (experiment_fetch_status == 1) {
fake_http_url_fetcher_factory()->SetFakeFailedResponse(url, E_FAIL);
} else {
fake_http_url_fetcher_factory()->SetFakeResponse(
url, FakeWinHttpUrlFetcher::Headers(), "{\"bad_experiments\": [ ] }");
}
// Create provider and start logon.
Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred;
ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred));
Microsoft::WRL::ComPtr<ITestCredential> test;
ASSERT_EQ(S_OK, cred.As(&test));
ASSERT_EQ(S_OK, StartLogonProcessAndWait());
// Email should be the same as the default one.
EXPECT_EQ(test->GetFinalEmail(), kDefaultEmail);
std::string experiment1_value = "false";
std::string experiment2_value = "false";
if (experiment_fetch_status == 0) {
experiment1_value = "abc";
experiment2_value = "def";
}
EXPECT_EQ(experiment1_value,
ExperimentsManager::Get()->GetExperimentForUser(
base::UTF16ToUTF8(OLE2W(sid)), Experiment::TEST_CLIENT_FLAG));
EXPECT_EQ(experiment2_value,
ExperimentsManager::Get()->GetExperimentForUser(
base::UTF16ToUTF8(OLE2W(sid)), Experiment::TEST_CLIENT_FLAG2));
}
INSTANTIATE_TEST_SUITE_P(All,
ExperimentsManagerGcpwE2ETest,
::testing::Values(0, 1, 2));
// Tests different outcomes of fetching experiments through ESA:
// 0 - Experiments are fetched successfully.
// 1 - Failed fetching experiments.
// 2 - Response in fetching experiments contains malformed payload.
class ExperimentsManagerESAE2ETest : public ExperimentsManagerTest,
public ::testing::WithParamInterface<int> {
};
TEST_P(ExperimentsManagerESAE2ETest, FetchingExperiments) {
int experiment_fetch_status = GetParam();
// Create a fake user that has the same gaia id as the test gaia id.
CComBSTR sid;
ASSERT_EQ(S_OK,
fake_os_user_manager()->CreateTestOSUser(
L"foo", L"password", L"Full Name", L"comment",
base::UTF8ToUTF16(kDefaultGaiaId), L"user@company.com", &sid));
ASSERT_EQ(S_OK, GenerateGCPWDmToken((BSTR)sid));
base::string16 device_resource_id = L"foo_resource_id";
ASSERT_EQ(S_OK, SetUserProperty(OLE2W(sid), L"device_resource_id",
device_resource_id));
// Re-registering effectively clears the experiment values from the previous
// fetches.
ExperimentsManager::Get()->RegisterExperiments();
GURL url = ExperimentsFetcher::Get()->GetExperimentsUrl();
if (experiment_fetch_status == 0) {
fake_http_url_fetcher_factory()->SetFakeResponse(
url, FakeWinHttpUrlFetcher::Headers(),
"{\"experiments\": [{\"feature\": \"test_client_flag\", \"value\": "
"\"abc\"}, {\"feature\": \"test_client_flag2\", \"value\": \"def\"} ] "
"}");
} else if (experiment_fetch_status == 1) {
fake_http_url_fetcher_factory()->SetFakeFailedResponse(url, E_FAIL);
} else {
fake_http_url_fetcher_factory()->SetFakeResponse(
url, FakeWinHttpUrlFetcher::Headers(), "{\"bad_experiments\": [ ] }");
}
base::string16 dm_token;
ASSERT_EQ(S_OK, GetGCPWDmToken((BSTR)sid, &dm_token));
std::unique_ptr<extension::Task> task(
ExperimentsFetcher::GetFetchExperimentsTaskCreator().Run());
task->SetContext({{device_resource_id, L"", L"", OLE2W(sid), dm_token}});
task->Execute();
std::string experiment1_value = "false";
std::string experiment2_value = "false";
if (experiment_fetch_status == 0) {
experiment1_value = "abc";
experiment2_value = "def";
}
EXPECT_EQ(experiment1_value,
ExperimentsManager::Get()->GetExperimentForUser(
base::UTF16ToUTF8(OLE2W(sid)), Experiment::TEST_CLIENT_FLAG));
EXPECT_EQ(experiment2_value,
ExperimentsManager::Get()->GetExperimentForUser(
base::UTF16ToUTF8(OLE2W(sid)), Experiment::TEST_CLIENT_FLAG2));
}
INSTANTIATE_TEST_SUITE_P(All,
ExperimentsManagerESAE2ETest,
::testing::Values(0, 1, 2));
} // namespace testing
} // namespace credential_provider
...@@ -40,6 +40,8 @@ ...@@ -40,6 +40,8 @@
#include "chrome/credential_provider/gaiacp/auth_utils.h" #include "chrome/credential_provider/gaiacp/auth_utils.h"
#include "chrome/credential_provider/gaiacp/device_policies_manager.h" #include "chrome/credential_provider/gaiacp/device_policies_manager.h"
#include "chrome/credential_provider/gaiacp/event_logs_upload_manager.h" #include "chrome/credential_provider/gaiacp/event_logs_upload_manager.h"
#include "chrome/credential_provider/gaiacp/experiments_fetcher.h"
#include "chrome/credential_provider/gaiacp/experiments_manager.h"
#include "chrome/credential_provider/gaiacp/gaia_credential_provider.h" #include "chrome/credential_provider/gaiacp/gaia_credential_provider.h"
#include "chrome/credential_provider/gaiacp/gaia_credential_provider_i.h" #include "chrome/credential_provider/gaiacp/gaia_credential_provider_i.h"
#include "chrome/credential_provider/gaiacp/gaia_resources.h" #include "chrome/credential_provider/gaiacp/gaia_resources.h"
...@@ -810,6 +812,44 @@ HRESULT CreateNewUser(OSUserManager* manager, ...@@ -810,6 +812,44 @@ HRESULT CreateNewUser(OSUserManager* manager,
return HRESULT_FROM_WIN32(NERR_UserExists); return HRESULT_FROM_WIN32(NERR_UserExists);
} }
// If GCPW user policies or experiments are stale, make sure to fetch them
// before proceeding with the login.
void GetUserConfigsIfStale(const base::string16& sid,
const base::string16& gaia_id,
const base::string16& access_token) {
if (UserPoliciesManager::Get()->CloudPoliciesEnabled() &&
UserPoliciesManager::Get()->IsUserPolicyStaleOrMissing(sid)) {
// Save gaia id since it is needed for the cloud policies server request.
HRESULT hr = SetUserProperty(sid, kUserId, gaia_id);
if (FAILED(hr)) {
LOGFN(ERROR) << "SetUserProperty(id) hr=" << putHR(hr);
}
hr = UserPoliciesManager::Get()->FetchAndStoreCloudUserPolicies(
sid, base::UTF16ToUTF8(access_token));
if (FAILED(hr)) {
LOGFN(ERROR) << "Failed fetching user policies for user " << sid
<< " Error: " << putHR(hr);
}
}
if (ExperimentsManager::Get()->ExperimentsEnabled() &&
GetTimeDeltaSinceLastFetch(sid, kLastUserExperimentsRefreshTimeRegKey) >
kMaxTimeDeltaSinceLastExperimentsFetch) {
HRESULT hr = SetUserProperty(sid, kUserId, gaia_id);
if (FAILED(hr)) {
LOGFN(ERROR) << "SetUserProperty(id) hr=" << putHR(hr);
}
hr = ExperimentsFetcher::Get()->FetchAndStoreExperiments(
sid, base::UTF16ToUTF8(access_token));
if (FAILED(hr)) {
LOGFN(ERROR) << "Failed fetching experiments for user " << sid
<< " Error: " << putHR(hr);
}
}
}
} // namespace } // namespace
CGaiaCredentialBase::UIProcessInfo::UIProcessInfo() {} CGaiaCredentialBase::UIProcessInfo::UIProcessInfo() {}
...@@ -2492,27 +2532,12 @@ HRESULT CGaiaCredentialBase::OnUserAuthenticated(BSTR authentication_info, ...@@ -2492,27 +2532,12 @@ HRESULT CGaiaCredentialBase::OnUserAuthenticated(BSTR authentication_info,
: "false")); : "false"));
} }
base::string16 sid = OLE2CW(user_sid_); base::string16 gaia_id = GetDictString(*authentication_results_, kKeyId);
if (UserPoliciesManager::Get()->CloudPoliciesEnabled() && // TODO(crbug.com/976744) Use downscoped token here.
UserPoliciesManager::Get()->IsUserPolicyStaleOrMissing(sid)) { base::string16 access_token =
// Save gaia id since it is needed for the cloud policies server request. GetDictString(*authentication_results_, kKeyAccessToken);
base::string16 gaia_id = GetDictString(*authentication_results_, kKeyId); GetUserConfigsIfStale(OLE2CW(user_sid_), gaia_id, access_token);
HRESULT hr = SetUserProperty(sid, kUserId, gaia_id); SecurelyClearString(access_token);
if (FAILED(hr)) {
LOGFN(ERROR) << "SetUserProperty(id) hr=" << putHR(hr);
}
// TODO(crbug.com/976744) Use downscoped token here.
base::string16 access_token =
GetDictString(*authentication_results_, kKeyAccessToken);
hr = UserPoliciesManager::Get()->FetchAndStoreCloudUserPolicies(
sid, base::UTF16ToUTF8(access_token));
SecurelyClearString(access_token);
if (FAILED(hr)) {
LOGFN(ERROR) << "Failed fetching user policies for user " << sid
<< " Error: " << putHR(hr);
}
}
base::string16 local_password = base::string16 local_password =
GetDictString(*authentication_results_, kKeyPassword); GetDictString(*authentication_results_, kKeyPassword);
......
...@@ -3596,7 +3596,7 @@ TEST_P(GcpGaiaCredentialBaseFetchCloudPoliciesTest, FetchAndStore) { ...@@ -3596,7 +3596,7 @@ TEST_P(GcpGaiaCredentialBaseFetchCloudPoliciesTest, FetchAndStore) {
ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0)); ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0));
base::TimeDelta time_since_last_fetch = base::TimeDelta time_since_last_fetch =
UserPoliciesManager::Get()->GetTimeDeltaSinceLastPolicyFetch(sid); GetTimeDeltaSinceLastFetch(sid, kLastUserPolicyRefreshTimeRegKey);
if (cloud_policies_enabled && !policy_refreshed_recently) { if (cloud_policies_enabled && !policy_refreshed_recently) {
ASSERT_EQ(1, fake_user_policies_manager.GetNumTimesFetchAndStoreCalled()); ASSERT_EQ(1, fake_user_policies_manager.GetNumTimesFetchAndStoreCalled());
......
...@@ -92,6 +92,12 @@ constexpr wchar_t kDefaultMdmUrl[] = ...@@ -92,6 +92,12 @@ constexpr wchar_t kDefaultMdmUrl[] =
constexpr int kMaxNumConsecutiveUploadDeviceFailures = 3; constexpr int kMaxNumConsecutiveUploadDeviceFailures = 3;
const base::TimeDelta kMaxTimeDeltaSinceLastUserPolicyRefresh = const base::TimeDelta kMaxTimeDeltaSinceLastUserPolicyRefresh =
base::TimeDelta::FromDays(1); base::TimeDelta::FromDays(1);
const base::TimeDelta kMaxTimeDeltaSinceLastExperimentsFetch =
base::TimeDelta::FromDays(1);
// Path elements for the path where the experiments are stored on disk.
const wchar_t kGcpwExperimentsDirectory[] = L"Experiments";
const wchar_t kGcpwUserExperimentsFileName[] = L"ExperimentsFetchResponse";
namespace { namespace {
...@@ -109,6 +115,7 @@ constexpr base::win::i18n::LanguageSelector::LangToOffset ...@@ -109,6 +115,7 @@ constexpr base::win::i18n::LanguageSelector::LangToOffset
#define HANDLE_LANGUAGE(l_, o_) {L## #l_, o_}, #define HANDLE_LANGUAGE(l_, o_) {L## #l_, o_},
DO_LANGUAGES DO_LANGUAGES
#undef HANDLE_LANGUAGE #undef HANDLE_LANGUAGE
}; };
base::FilePath GetStartupSentinelLocation(const base::string16& version) { base::FilePath GetStartupSentinelLocation(const base::string16& version) {
...@@ -233,6 +240,23 @@ HRESULT GetGCPWDmTokenInternal(const base::string16& sid, ...@@ -233,6 +240,23 @@ HRESULT GetGCPWDmTokenInternal(const base::string16& sid,
return S_OK; return S_OK;
} }
// Get the path to the directory under DIR_COMMON_APP_DATA with the given |sid|
// and |file_dir|.
base::FilePath GetDirectoryFilePath(const base::string16& sid,
const base::string16& file_dir) {
base::FilePath path;
if (!base::PathService::Get(base::DIR_COMMON_APP_DATA, &path)) {
HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
LOGFN(ERROR) << "PathService::Get(DIR_COMMON_APP_DATA) hr=" << putHR(hr);
return base::FilePath();
}
path = path.Append(GetInstallParentDirectoryName())
.Append(kCredentialProviderFolder)
.Append(file_dir)
.Append(sid);
return path;
}
} // namespace } // namespace
// GoogleRegistrationDataForTesting ////////////////////////////////////////// // GoogleRegistrationDataForTesting //////////////////////////////////////////
...@@ -1255,4 +1279,62 @@ base::string16 GetDevelopmentUrl(const base::string16& url, ...@@ -1255,4 +1279,62 @@ base::string16 GetDevelopmentUrl(const base::string16& url,
return url; return url;
} }
std::unique_ptr<base::File> GetOpenedFileForUser(
const base::string16& sid,
uint32_t open_flags,
const base::string16& file_dir,
const base::string16& file_name) {
base::FilePath experiments_dir = GetDirectoryFilePath(sid, file_dir);
if (!base::DirectoryExists(experiments_dir)) {
base::File::Error error;
if (!CreateDirectoryAndGetError(experiments_dir, &error)) {
LOGFN(ERROR) << "Experiments data directory could not be created for "
<< sid << " Error: " << error;
return nullptr;
}
}
base::FilePath experiments_file_path = experiments_dir.Append(file_name);
std::unique_ptr<base::File> experiments_file(
new base::File(experiments_file_path, open_flags));
if (!experiments_file->IsValid()) {
LOGFN(ERROR) << "Error opening experiments file for user " << sid
<< " with flags " << open_flags
<< " Error: " << experiments_file->error_details();
return nullptr;
}
base::File::Error lock_error =
experiments_file->Lock(base::File::LockMode::kExclusive);
if (lock_error != base::File::FILE_OK) {
LOGFN(ERROR)
<< "Failed to obtain exclusive lock on experiments file! Error: "
<< lock_error;
return nullptr;
}
return experiments_file;
}
base::TimeDelta GetTimeDeltaSinceLastFetch(const base::string16& sid,
const base::string16& flag) {
wchar_t last_fetch_millis[512];
ULONG last_fetch_size = base::size(last_fetch_millis);
HRESULT hr = GetUserProperty(sid, flag, last_fetch_millis, &last_fetch_size);
if (FAILED(hr)) {
return base::TimeDelta::Max();
}
int64_t last_fetch_millis_int64;
base::StringToInt64(last_fetch_millis, &last_fetch_millis_int64);
int64_t time_delta_from_last_fetch_ms =
base::Time::Now().ToDeltaSinceWindowsEpoch().InMilliseconds() -
last_fetch_millis_int64;
return base::TimeDelta::FromMilliseconds(time_delta_from_last_fetch_ms);
}
} // namespace credential_provider } // namespace credential_provider
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <string> #include <string>
#include "base/callback.h" #include "base/callback.h"
#include "base/files/file.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "base/values.h" #include "base/values.h"
...@@ -75,6 +76,14 @@ extern const int kMaxNumConsecutiveUploadDeviceFailures; ...@@ -75,6 +76,14 @@ extern const int kMaxNumConsecutiveUploadDeviceFailures;
// again. // again.
extern const base::TimeDelta kMaxTimeDeltaSinceLastUserPolicyRefresh; extern const base::TimeDelta kMaxTimeDeltaSinceLastUserPolicyRefresh;
// Maximum allowed time delta after which experiments should be fetched
// again.
extern const base::TimeDelta kMaxTimeDeltaSinceLastExperimentsFetch;
// Path elements for the path where the experiments are stored on disk.
extern const wchar_t kGcpwExperimentsDirectory[];
extern const wchar_t kGcpwUserExperimentsFileName[];
// Because of some strange dependency problems with windows header files, // Because of some strange dependency problems with windows header files,
// define STATUS_SUCCESS here instead of including ntstatus.h or SubAuth.h // define STATUS_SUCCESS here instead of including ntstatus.h or SubAuth.h
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) #define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
...@@ -404,6 +413,20 @@ GURL GetGcpwServiceUrl(); ...@@ -404,6 +413,20 @@ GURL GetGcpwServiceUrl();
base::string16 GetDevelopmentUrl(const base::string16& url, base::string16 GetDevelopmentUrl(const base::string16& url,
const base::string16& dev); const base::string16& dev);
// Returns a handle to a file which is stored under DIR_COMMON_APP_DATA > |sid|
// > |file_dir| > |file_name|. The file is opened with the provided
// |open_flags|.
std::unique_ptr<base::File> GetOpenedFileForUser(
const base::string16& sid,
uint32_t open_flags,
const base::string16& file_dir,
const base::string16& file_name);
// Returns the time delta since the last fetch for the given |sid|. |flag|
// stores the last fetch time.
base::TimeDelta GetTimeDeltaSinceLastFetch(const base::string16& sid,
const base::string16& flag);
} // namespace credential_provider } // namespace credential_provider
#endif // CHROME_CREDENTIAL_PROVIDER_GAIACP_GCP_UTILS_H_ #endif // CHROME_CREDENTIAL_PROVIDER_GAIACP_GCP_UTILS_H_
...@@ -65,6 +65,10 @@ constexpr wchar_t kRegUseShorterAccountName[] = L"use_shorter_account_name"; ...@@ -65,6 +65,10 @@ constexpr wchar_t kRegUseShorterAccountName[] = L"use_shorter_account_name";
constexpr wchar_t kEmailDomainsKey[] = L"ed"; // deprecated. constexpr wchar_t kEmailDomainsKey[] = L"ed"; // deprecated.
constexpr wchar_t kEmailDomainsKeyNew[] = L"domains_allowed_to_login"; constexpr wchar_t kEmailDomainsKeyNew[] = L"domains_allowed_to_login";
const wchar_t kLastUserPolicyRefreshTimeRegKey[] = L"last_policy_refresh_time";
const wchar_t kLastUserExperimentsRefreshTimeRegKey[] =
L"last_experiments_refresh_time";
namespace { namespace {
constexpr wchar_t kAccountPicturesRootRegKey[] = constexpr wchar_t kAccountPicturesRootRegKey[] =
......
...@@ -83,6 +83,14 @@ extern const wchar_t kRegUseShorterAccountName[]; ...@@ -83,6 +83,14 @@ extern const wchar_t kRegUseShorterAccountName[];
extern const wchar_t kEmailDomainsKey[]; // Older deprecated key. extern const wchar_t kEmailDomainsKey[]; // Older deprecated key.
extern const wchar_t kEmailDomainsKeyNew[]; extern const wchar_t kEmailDomainsKeyNew[];
// Registry key where the the last time the policy is refreshed for the user is
// stored.
extern const wchar_t kLastUserPolicyRefreshTimeRegKey[];
// Registry key where the the last time the experiments is refreshed for the
// user is stored.
extern const wchar_t kLastUserExperimentsRefreshTimeRegKey[];
// Gets any HKLM registry key on the system. // Gets any HKLM registry key on the system.
HRESULT GetMachineRegDWORD(const base::string16& key_name, HRESULT GetMachineRegDWORD(const base::string16& key_name,
const base::string16& name, const base::string16& name,
......
...@@ -41,13 +41,8 @@ const base::TimeDelta kDefaultFetchPoliciesRequestTimeout = ...@@ -41,13 +41,8 @@ const base::TimeDelta kDefaultFetchPoliciesRequestTimeout =
base::TimeDelta::FromMilliseconds(5000); base::TimeDelta::FromMilliseconds(5000);
// Path elements for the path where the policies are stored on disk. // Path elements for the path where the policies are stored on disk.
constexpr base::FilePath::CharType kGcpwPoliciesDirectory[] = L"Policies"; constexpr wchar_t kGcpwPoliciesDirectory[] = L"Policies";
constexpr base::FilePath::CharType kGcpwUserPolicyFileName[] = constexpr wchar_t kGcpwUserPolicyFileName[] = L"PolicyFetchResponse";
L"PolicyFetchResponse";
// Registry key where the the last time the policy is refreshed for the user is
// stored.
const wchar_t kLastUserPolicyRefreshTimeRegKey[] = L"last_policy_refresh_time";
// Maximum number of retries if a HTTP call to the backend fails. // Maximum number of retries if a HTTP call to the backend fails.
constexpr unsigned int kMaxNumHttpRetries = 1; constexpr unsigned int kMaxNumHttpRetries = 1;
...@@ -66,57 +61,6 @@ const base::TimeDelta kCloudPoliciesExecutionPeriod = ...@@ -66,57 +61,6 @@ const base::TimeDelta kCloudPoliciesExecutionPeriod =
// True when cloud policies feature is enabled. // True when cloud policies feature is enabled.
bool g_cloud_policies_enabled = false; bool g_cloud_policies_enabled = false;
// Get the path to the directory where the policies will be stored for the user
// with |sid|.
base::FilePath GetUserPolicyDirectoryFilePath(const base::string16& sid) {
base::FilePath path;
if (!base::PathService::Get(base::DIR_COMMON_APP_DATA, &path)) {
HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
LOGFN(ERROR) << "PathService::Get(DIR_COMMON_APP_DATA) hr=" << putHR(hr);
return base::FilePath();
}
path = path.Append(GetInstallParentDirectoryName())
.Append(kCredentialProviderFolder)
.Append(kGcpwPoliciesDirectory)
.Append(sid);
return path;
}
std::unique_ptr<base::File> GetOpenedPolicyFileForUser(
const base::string16& sid,
uint32_t open_flags) {
base::FilePath policy_dir = GetUserPolicyDirectoryFilePath(sid);
if (!base::DirectoryExists(policy_dir)) {
base::File::Error error;
if (!CreateDirectoryAndGetError(policy_dir, &error)) {
LOGFN(ERROR) << "Policy data directory could not be created for " << sid
<< " Error: " << error;
return nullptr;
}
}
base::FilePath policy_file_path = policy_dir.Append(kGcpwUserPolicyFileName);
std::unique_ptr<base::File> policy_file(
new base::File(policy_file_path, open_flags));
if (!policy_file->IsValid()) {
LOGFN(ERROR) << "Error opening policy file for user " << sid
<< " with flags " << open_flags
<< " Error: " << policy_file->error_details();
return nullptr;
}
base::File::Error lock_error =
policy_file->Lock(base::File::LockMode::kExclusive);
if (lock_error != base::File::FILE_OK) {
LOGFN(ERROR) << "Failed to obtain exclusive lock on policy file! Error: "
<< lock_error;
return nullptr;
}
return policy_file;
}
// Creates the URL used to fetch the policies from the backend based on the // Creates the URL used to fetch the policies from the backend based on the
// credential present (OAuth vs DM token) for authentication. // credential present (OAuth vs DM token) for authentication.
GURL GetFetchUserPoliciesUrl(const base::string16& sid, GURL GetFetchUserPoliciesUrl(const base::string16& sid,
...@@ -302,8 +246,8 @@ HRESULT UserPoliciesManager::FetchAndStorePolicies( ...@@ -302,8 +246,8 @@ HRESULT UserPoliciesManager::FetchAndStorePolicies(
uint32_t open_flags = base::File::FLAG_CREATE_ALWAYS | uint32_t open_flags = base::File::FLAG_CREATE_ALWAYS |
base::File::FLAG_WRITE | base::File::FLAG_WRITE |
base::File::FLAG_EXCLUSIVE_WRITE; base::File::FLAG_EXCLUSIVE_WRITE;
std::unique_ptr<base::File> policy_file = std::unique_ptr<base::File> policy_file = GetOpenedFileForUser(
GetOpenedPolicyFileForUser(sid, open_flags); sid, open_flags, kGcpwPoliciesDirectory, kGcpwUserPolicyFileName);
if (!policy_file) { if (!policy_file) {
return (fetch_status_ = E_FAIL); return (fetch_status_ = E_FAIL);
} }
...@@ -330,35 +274,14 @@ HRESULT UserPoliciesManager::FetchAndStorePolicies( ...@@ -330,35 +274,14 @@ HRESULT UserPoliciesManager::FetchAndStorePolicies(
return (fetch_status_ = S_OK); return (fetch_status_ = S_OK);
} }
base::TimeDelta UserPoliciesManager::GetTimeDeltaSinceLastPolicyFetch(
const base::string16& sid) const {
wchar_t last_fetch_millis[512];
ULONG last_fetch_size = base::size(last_fetch_millis);
HRESULT hr = GetUserProperty(sid, kLastUserPolicyRefreshTimeRegKey,
last_fetch_millis, &last_fetch_size);
if (FAILED(hr)) {
// The policy was never fetched before.
return base::TimeDelta::Max();
}
int64_t last_fetch_millis_int64;
base::StringToInt64(last_fetch_millis, &last_fetch_millis_int64);
int64_t time_delta_from_last_fetch_ms =
base::Time::Now().ToDeltaSinceWindowsEpoch().InMilliseconds() -
last_fetch_millis_int64;
return base::TimeDelta::FromMilliseconds(time_delta_from_last_fetch_ms);
}
bool UserPoliciesManager::GetUserPolicies(const base::string16& sid, bool UserPoliciesManager::GetUserPolicies(const base::string16& sid,
UserPolicies* user_policies) const { UserPolicies* user_policies) const {
DCHECK(user_policies); DCHECK(user_policies);
uint32_t open_flags = base::File::FLAG_OPEN | base::File::FLAG_READ; uint32_t open_flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
std::unique_ptr<base::File> policy_file = std::unique_ptr<base::File> policy_file = GetOpenedFileForUser(
GetOpenedPolicyFileForUser(sid, open_flags); sid, open_flags, kGcpwPoliciesDirectory, kGcpwUserPolicyFileName);
if (!policy_file) { if (!policy_file) {
return false; return false;
} }
...@@ -395,7 +318,7 @@ bool UserPoliciesManager::IsUserPolicyStaleOrMissing( ...@@ -395,7 +318,7 @@ bool UserPoliciesManager::IsUserPolicyStaleOrMissing(
return true; return true;
} }
if (GetTimeDeltaSinceLastPolicyFetch(sid) > if (GetTimeDeltaSinceLastFetch(sid, kLastUserPolicyRefreshTimeRegKey) >
kMaxTimeDeltaSinceLastUserPolicyRefresh) { kMaxTimeDeltaSinceLastUserPolicyRefresh) {
return true; return true;
} }
......
...@@ -42,11 +42,6 @@ class COMPONENT_EXPORT(GCPW_POLICIES) UserPoliciesManager { ...@@ -42,11 +42,6 @@ class COMPONENT_EXPORT(GCPW_POLICIES) UserPoliciesManager {
virtual HRESULT FetchAndStoreCloudUserPolicies( virtual HRESULT FetchAndStoreCloudUserPolicies(
const extension::UserDeviceContext& context); const extension::UserDeviceContext& context);
// Return the elapsed time delta since the last time the policies were
// successfully fetched for the user with |sid|.
base::TimeDelta GetTimeDeltaSinceLastPolicyFetch(
const base::string16& sid) const;
// Get the URL of GCPW service for HTTP request for fetching user policies // Get the URL of GCPW service for HTTP request for fetching user policies
// when the caller has a valid OAuth token for authentication. // when the caller has a valid OAuth token for authentication.
GURL GetGcpwServiceUserPoliciesUrl(const base::string16& sid); GURL GetGcpwServiceUserPoliciesUrl(const base::string16& sid);
......
...@@ -11,6 +11,7 @@ test("gcp_unittests") { ...@@ -11,6 +11,7 @@ test("gcp_unittests") {
"../extension/task_manager_unittests.cc", "../extension/task_manager_unittests.cc",
"../gaiacp/associated_user_validator_unittests.cc", "../gaiacp/associated_user_validator_unittests.cc",
"../gaiacp/device_policies_manager_unittests.cc", "../gaiacp/device_policies_manager_unittests.cc",
"../gaiacp/experiments_manager_unittests.cc",
"../gaiacp/gaia_credential_base_unittests.cc", "../gaiacp/gaia_credential_base_unittests.cc",
"../gaiacp/gaia_credential_other_user_unittests.cc", "../gaiacp/gaia_credential_other_user_unittests.cc",
"../gaiacp/gaia_credential_provider_unittests.cc", "../gaiacp/gaia_credential_provider_unittests.cc",
......
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