Commit 138a5a2f authored by Henrique Grandinetti's avatar Henrique Grandinetti Committed by Commit Bot

Add converters for the UsageTimeLimit policy dictionary.

Before using the UsageTimeLimit on the TimeLimitProcessor we convert this to higher level structs, that allow the processor to work better. I was going to submit all together, but it was too big, so I split into two cls.

These structs here didn't need to have their interfaces public, since its going to be used solely by the TimeLimitProcessor. However, I exposed its interface on the internal namespace in order to make it testable. Usually, we don't want to test private code, but these converters have a complex logic, and I thought it was better to have this explicitly tested.

Bug: 823536
Change-Id: Ia90c5e83b5d0f149ae93024a1671b93850584f91
Reviewed-on: https://chromium-review.googlesource.com/1071687
Commit-Queue: Rahul Chaturvedi <rkc@chromium.org>
Reviewed-by: default avatarRahul Chaturvedi <rkc@chromium.org>
Reviewed-by: default avatarJacob Dufault <jdufault@chromium.org>
Cr-Commit-Position: refs/heads/master@{#565036}
parent a2089b42
...@@ -1907,6 +1907,7 @@ source_set("unit_tests") { ...@@ -1907,6 +1907,7 @@ source_set("unit_tests") {
"authpolicy/auth_policy_credentials_manager_unittest.cc", "authpolicy/auth_policy_credentials_manager_unittest.cc",
"base/file_flusher_unittest.cc", "base/file_flusher_unittest.cc",
"certificate_provider/certificate_provider_service_unittest.cc", "certificate_provider/certificate_provider_service_unittest.cc",
"child_accounts/usage_time_limit_processor_unittest.cc",
"crostini/crostini_manager_unittest.cc", "crostini/crostini_manager_unittest.cc",
"customization/customization_document_unittest.cc", "customization/customization_document_unittest.cc",
"drive/download_handler_unittest.cc", "drive/download_handler_unittest.cc",
......
...@@ -4,7 +4,202 @@ ...@@ -4,7 +4,202 @@
#include "usage_time_limit_processor.h" #include "usage_time_limit_processor.h"
#include "base/strings/string_number_conversions.h"
namespace chromeos { namespace chromeos {
namespace internal {
namespace {
constexpr char kOverrideAction[] = "action";
constexpr char kOverrideActionCreatedAt[] = "created_at_millis";
constexpr char kOverrideActionDurationMins[] = "duration_mins";
constexpr char kOverrideActionLock[] = "LOCK";
constexpr char kOverrideActionSpecificData[] = "action_specific_data";
constexpr char kTimeLimitLastUpdatedAt[] = "last_updated_millis";
constexpr char kUsageLimitResetAt[] = "reset_at";
constexpr char kUsageLimitUsageQuota[] = "usage_quota_mins";
constexpr char kWindowLimitEntries[] = "entries";
constexpr char kWindowLimitEntryEffectiveDay[] = "effective_day";
constexpr char kWindowLimitEntryEndsAt[] = "ends_at";
constexpr char kWindowLimitEntryStartsAt[] = "starts_at";
constexpr char kWindowLimitEntryTimeHour[] = "hour";
constexpr char kWindowLimitEntryTimeMinute[] = "minute";
constexpr const char* kTimeLimitWeekdays[] = {
"sunday", "monday", "tuesday", "wednesday",
"thursday", "friday", "saturday"};
} // namespace
// Transforms the time dictionary sent on the UsageTimeLimit policy to a
// TimeDelta, that represents the distance from midnight.
base::TimeDelta ValueToTimeDelta(const base::Value* policy_time) {
int hour = policy_time->FindKey(kWindowLimitEntryTimeHour)->GetInt();
int minute = policy_time->FindKey(kWindowLimitEntryTimeMinute)->GetInt();
return base::TimeDelta::FromMinutes(hour * 60 + minute);
}
// Transforms weekday strings into the Weekday enum.
Weekday GetWeekday(std::string weekday) {
std::transform(weekday.begin(), weekday.end(), weekday.begin(), ::tolower);
for (int i = 0; i < static_cast<int>(Weekday::kCount); i++) {
if (weekday == kTimeLimitWeekdays[i]) {
return static_cast<Weekday>(i);
}
}
LOG(ERROR) << "Unexpected weekday " << weekday;
return Weekday::kSunday;
}
TimeWindowLimitEntry::TimeWindowLimitEntry() = default;
TimeWindowLimitEntry::TimeWindowLimitEntry(TimeWindowLimitEntry&&) = default;
TimeWindowLimitEntry& TimeWindowLimitEntry::operator=(TimeWindowLimitEntry&&) =
default;
bool TimeWindowLimitEntry::IsOvernight() const {
return ends_at < starts_at;
}
TimeWindowLimit::TimeWindowLimit(const base::Value& window_limit_dict) {
if (!window_limit_dict.FindKey(kWindowLimitEntries))
return;
for (const base::Value& entry_dict :
window_limit_dict.FindKey(kWindowLimitEntries)->GetList()) {
const base::Value* effective_day =
entry_dict.FindKey(kWindowLimitEntryEffectiveDay);
const base::Value* starts_at =
entry_dict.FindKey(kWindowLimitEntryStartsAt);
const base::Value* ends_at = entry_dict.FindKey(kWindowLimitEntryEndsAt);
const base::Value* last_updated_value =
entry_dict.FindKey(kTimeLimitLastUpdatedAt);
if (!effective_day || !starts_at || !ends_at || !last_updated_value) {
// Missing information, so this entry will be ignored.
continue;
}
int64_t last_updated;
if (!base::StringToInt64(last_updated_value->GetString(), &last_updated)) {
// Cannot process entry without a valid last updated.
continue;
}
TimeWindowLimitEntry entry;
entry.starts_at = ValueToTimeDelta(starts_at);
entry.ends_at = ValueToTimeDelta(ends_at);
entry.last_updated = base::Time::UnixEpoch() +
base::TimeDelta::FromMilliseconds(last_updated);
Weekday weekday = GetWeekday(effective_day->GetString());
// We only support one time_limit_window per day. If more than one is sent
// we only use the latest updated.
if (!entries[weekday] ||
entries[weekday].value().last_updated < entry.last_updated) {
entries[weekday] = std::move(entry);
}
}
}
TimeWindowLimit::~TimeWindowLimit() = default;
TimeWindowLimit::TimeWindowLimit(TimeWindowLimit&&) = default;
TimeWindowLimit& TimeWindowLimit::operator=(TimeWindowLimit&&) = default;
TimeUsageLimitEntry::TimeUsageLimitEntry() = default;
TimeUsageLimitEntry::TimeUsageLimitEntry(TimeUsageLimitEntry&&) = default;
TimeUsageLimitEntry& TimeUsageLimitEntry::operator=(TimeUsageLimitEntry&&) =
default;
TimeUsageLimit::TimeUsageLimit(const base::Value& usage_limit_dict)
// Default reset time is midnight.
: reset_at(base::TimeDelta::FromMinutes(0)) {
const base::Value* reset_at_value =
usage_limit_dict.FindKey(kUsageLimitResetAt);
if (reset_at_value) {
reset_at = ValueToTimeDelta(reset_at_value);
}
for (const std::string& weekday_key : kTimeLimitWeekdays) {
if (!usage_limit_dict.FindKey(weekday_key))
continue;
const base::Value* entry_dict = usage_limit_dict.FindKey(weekday_key);
const base::Value* usage_quota = entry_dict->FindKey(kUsageLimitUsageQuota);
const base::Value* last_updated_value =
entry_dict->FindKey(kTimeLimitLastUpdatedAt);
int64_t last_updated;
if (!base::StringToInt64(last_updated_value->GetString(), &last_updated)) {
// Cannot process entry without a valid last updated.
continue;
}
Weekday weekday = GetWeekday(weekday_key);
TimeUsageLimitEntry entry;
entry.usage_quota = base::TimeDelta::FromMinutes(usage_quota->GetInt());
entry.last_updated = base::Time::UnixEpoch() +
base::TimeDelta::FromMilliseconds(last_updated);
entries[weekday] = std::move(entry);
}
}
TimeUsageLimit::~TimeUsageLimit() = default;
TimeUsageLimit::TimeUsageLimit(TimeUsageLimit&&) = default;
TimeUsageLimit& TimeUsageLimit::operator=(TimeUsageLimit&&) = default;
Override::Override(const base::Value& override_dict) {
const base::Value* action_value = override_dict.FindKey(kOverrideAction);
const base::Value* created_at_value =
override_dict.FindKey(kOverrideActionCreatedAt);
if (!action_value || !created_at_value)
return;
int64_t created_at_millis;
if (!base::StringToInt64(created_at_value->GetString(), &created_at_millis)) {
// Cannot process entry without a valid creation time.
return;
}
action = action_value->GetString() == kOverrideActionLock ? Action::kLock
: Action::kUnlock;
created_at = base::Time::UnixEpoch() +
base::TimeDelta::FromMilliseconds(created_at_millis);
const base::Value* duration_value = override_dict.FindPath(
{kOverrideActionSpecificData, kOverrideActionDurationMins});
if (duration_value)
duration = base::TimeDelta::FromMinutes(duration_value->GetInt());
}
Override::~Override() = default;
Override::Override(Override&&) = default;
Override& Override::operator=(Override&&) = default;
Weekday GetWeekday(base::Time time) {
base::Time::Exploded exploded;
time.LocalExplode(&exploded);
return static_cast<Weekday>(exploded.day_of_week);
}
Weekday WeekdayShift(Weekday current_day, int shift) {
return static_cast<Weekday>(static_cast<int>(current_day) +
shift % static_cast<int>(Weekday::kCount));
}
} // namespace internal
namespace usage_time_limit { namespace usage_time_limit {
State GetState(const std::unique_ptr<base::DictionaryValue>& time_limit, State GetState(const std::unique_ptr<base::DictionaryValue>& time_limit,
...@@ -24,4 +219,4 @@ base::Time GetExpectedResetTime( ...@@ -24,4 +219,4 @@ base::Time GetExpectedResetTime(
} }
} // namespace usage_time_limit } // namespace usage_time_limit
} // namespace chromeos } // namespace chromeos
\ No newline at end of file
...@@ -9,13 +9,108 @@ ...@@ -9,13 +9,108 @@
#ifndef CHROME_BROWSER_CHROMEOS_CHILD_ACCOUNTS_USAGE_TIME_LIMIT_PROCESSOR_H_ #ifndef CHROME_BROWSER_CHROMEOS_CHILD_ACCOUNTS_USAGE_TIME_LIMIT_PROCESSOR_H_
#define CHROME_BROWSER_CHROMEOS_CHILD_ACCOUNTS_USAGE_TIME_LIMIT_PROCESSOR_H_ #define CHROME_BROWSER_CHROMEOS_CHILD_ACCOUNTS_USAGE_TIME_LIMIT_PROCESSOR_H_
#include <vector>
#include "base/optional.h" #include "base/optional.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "base/values.h" #include "base/values.h"
namespace chromeos { namespace chromeos {
namespace internal {
enum class Weekday {
kSunday = 0,
kMonday,
kTuesday,
kWednesday,
kThursday,
kFriday,
kSaturday,
kCount,
};
class TimeWindowLimitEntry {
public:
TimeWindowLimitEntry();
TimeWindowLimitEntry(TimeWindowLimitEntry&&);
TimeWindowLimitEntry& operator=(TimeWindowLimitEntry&&);
bool IsOvernight() const;
// Start time of time window limit. This is the distance from midnight.
base::TimeDelta starts_at;
// End time of time window limit. This is the distance from midnight.
base::TimeDelta ends_at;
// Last time this entry was updated.
base::Time last_updated;
private:
DISALLOW_COPY_AND_ASSIGN(TimeWindowLimitEntry);
};
class TimeWindowLimit {
public:
TimeWindowLimit(const base::Value& window_limit_dict);
~TimeWindowLimit();
TimeWindowLimit(TimeWindowLimit&&);
TimeWindowLimit& operator=(TimeWindowLimit&&);
std::unordered_map<Weekday, base::Optional<TimeWindowLimitEntry>> entries;
private:
DISALLOW_COPY_AND_ASSIGN(TimeWindowLimit);
};
class TimeUsageLimitEntry {
public:
TimeUsageLimitEntry();
TimeUsageLimitEntry(TimeUsageLimitEntry&&);
TimeUsageLimitEntry& operator=(TimeUsageLimitEntry&&);
base::TimeDelta usage_quota;
base::Time last_updated;
private:
DISALLOW_COPY_AND_ASSIGN(TimeUsageLimitEntry);
};
class TimeUsageLimit {
public:
TimeUsageLimit(const base::Value& usage_limit_dict);
~TimeUsageLimit();
TimeUsageLimit(TimeUsageLimit&&);
TimeUsageLimit& operator=(TimeUsageLimit&&);
std::unordered_map<Weekday, base::Optional<TimeUsageLimitEntry>> entries;
base::TimeDelta reset_at;
private:
DISALLOW_COPY_AND_ASSIGN(TimeUsageLimit);
};
class Override {
public:
enum class Action { kLock, kUnlock };
Override(const base::Value& override_dict);
~Override();
Override(Override&&);
Override& operator=(Override&&);
Action action;
base::Time created_at;
base::Optional<base::TimeDelta> duration;
private:
DISALLOW_COPY_AND_ASSIGN(Override);
};
// Retrieves the weekday from a time.
Weekday GetWeekday(base::Time time);
// Shifts the current weekday, if the value is po
Weekday WeekdayShift(Weekday current_day, int shift);
} // namespace internal
namespace usage_time_limit { namespace usage_time_limit {
enum class ActivePolicies { enum class ActivePolicies {
......
// Copyright 2018 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 "usage_time_limit_processor.h"
#include <memory>
#include "base/values.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromeos {
namespace internal {
class UsageTimeLimitProcessorTest : public testing::Test {
public:
UsageTimeLimitProcessorTest() {}
~UsageTimeLimitProcessorTest() override = default;
base::Value CreateTime(int hour, int minute) {
base::Value time(base::Value::Type::DICTIONARY);
time.SetKey("hour", base::Value(hour));
time.SetKey("minute", base::Value(minute));
return time;
}
base::Value CreateTimeWindow(base::Value day,
base::Value start,
base::Value end,
base::Value last_updated) {
base::Value time_window(base::Value::Type::DICTIONARY);
time_window.SetKey("effective_day", std::move(day));
time_window.SetKey("starts_at", std::move(start));
time_window.SetKey("ends_at", std::move(end));
time_window.SetKey("last_updated_millis", std::move(last_updated));
return time_window;
}
base::Value CreateTimeUsage(base::Value usage_quota,
base::Value last_updated) {
base::Value time_usage(base::Value::Type::DICTIONARY);
time_usage.SetKey("usage_quota_mins", std::move(usage_quota));
time_usage.SetKey("last_updated_millis", std::move(last_updated));
return time_usage;
}
std::string CreatePolicyTimestamp(const char* time_string) {
base::Time time;
if (!base::Time::FromUTCString(time_string, &time)) {
LOG(ERROR) << "Wrong time string format.";
}
return std::to_string(
base::TimeDelta(time - base::Time::UnixEpoch()).InMilliseconds());
}
};
// Validates that a well formed dictionary containing the time_window_limit
// information from the UsageTimeLimit policy is converted to its intermediate
// representation correctly.
TEST_F(UsageTimeLimitProcessorTest, TimeLimitWindowValid) {
// Create dictionary containing the policy information.
std::string last_updated_millis =
CreatePolicyTimestamp("1 Jan 1970 00:00:00");
base::Value monday_time_limit =
CreateTimeWindow(base::Value("MONDAY"), CreateTime(22, 30),
CreateTime(7, 30), base::Value(last_updated_millis));
base::Value friday_time_limit =
CreateTimeWindow(base::Value("FRIDAY"), CreateTime(23, 0),
CreateTime(8, 20), base::Value(last_updated_millis));
base::Value window_limit_entries(base::Value::Type::LIST);
window_limit_entries.GetList().push_back(std::move(monday_time_limit));
window_limit_entries.GetList().push_back(std::move(friday_time_limit));
base::Value time_window_limit = base::Value(base::Value::Type::DICTIONARY);
time_window_limit.SetKey("entries", std::move(window_limit_entries));
// Call tested function.
TimeWindowLimit window_limit_struct(time_window_limit);
ASSERT_TRUE(window_limit_struct.entries[Weekday::kMonday]);
ASSERT_EQ(window_limit_struct.entries[Weekday::kMonday]
.value()
.starts_at.InMinutes(),
22 * 60 + 30);
ASSERT_EQ(
window_limit_struct.entries[Weekday::kMonday].value().ends_at.InMinutes(),
7 * 60 + 30);
ASSERT_EQ(window_limit_struct.entries[Weekday::kMonday].value().last_updated,
base::Time::UnixEpoch());
ASSERT_TRUE(window_limit_struct.entries[Weekday::kFriday]);
ASSERT_EQ(window_limit_struct.entries[Weekday::kFriday]
.value()
.starts_at.InMinutes(),
23 * 60);
ASSERT_EQ(
window_limit_struct.entries[Weekday::kFriday].value().ends_at.InMinutes(),
8 * 60 + 20);
ASSERT_EQ(window_limit_struct.entries[Weekday::kFriday].value().last_updated,
base::Time::UnixEpoch());
// Assert that weekdays without time_window_limits are not set.
ASSERT_FALSE(window_limit_struct.entries[Weekday::kTuesday]);
ASSERT_FALSE(window_limit_struct.entries[Weekday::kWednesday]);
ASSERT_FALSE(window_limit_struct.entries[Weekday::kThursday]);
ASSERT_FALSE(window_limit_struct.entries[Weekday::kSaturday]);
ASSERT_FALSE(window_limit_struct.entries[Weekday::kSunday]);
}
// Validates that a well formed dictionary containing the time_usage_limit
// information from the UsageTimeLimit policy is converted to its intermediate
// representation correctly.
TEST_F(UsageTimeLimitProcessorTest, TimeUsageWindowValid) {
// Create dictionary containing the policy information.
std::string last_updated_millis_one =
CreatePolicyTimestamp("1 Jan 2018 10:00:00");
std::string last_updated_millis_two =
CreatePolicyTimestamp("1 Jan 2018 11:00:00");
base::Value tuesday_time_usage =
CreateTimeUsage(base::Value(120), base::Value(last_updated_millis_one));
base::Value thursday_time_usage =
CreateTimeUsage(base::Value(80), base::Value(last_updated_millis_two));
base::Value time_usage_limit = base::Value(base::Value::Type::DICTIONARY);
time_usage_limit.SetKey("tuesday", std::move(tuesday_time_usage));
time_usage_limit.SetKey("thursday", std::move(thursday_time_usage));
time_usage_limit.SetKey("reset_at", CreateTime(8, 0));
// Call tested functions.
TimeUsageLimit usage_limit_struct(time_usage_limit);
ASSERT_EQ(usage_limit_struct.reset_at.InMinutes(), 8 * 60);
ASSERT_EQ(usage_limit_struct.entries[Weekday::kTuesday]
.value()
.usage_quota.InMinutes(),
120);
ASSERT_EQ(usage_limit_struct.entries[Weekday::kTuesday].value().last_updated,
base::Time::FromDoubleT(1514800800));
ASSERT_EQ(usage_limit_struct.entries[Weekday::kThursday]
.value()
.usage_quota.InMinutes(),
80);
ASSERT_EQ(usage_limit_struct.entries[Weekday::kThursday].value().last_updated,
base::Time::FromDoubleT(1514804400));
// Assert that weekdays without time_usage_limits are not set.
ASSERT_FALSE(usage_limit_struct.entries[Weekday::kMonday]);
ASSERT_FALSE(usage_limit_struct.entries[Weekday::kWednesday]);
ASSERT_FALSE(usage_limit_struct.entries[Weekday::kFriday]);
ASSERT_FALSE(usage_limit_struct.entries[Weekday::kSaturday]);
ASSERT_FALSE(usage_limit_struct.entries[Weekday::kSunday]);
}
// Validates that a well formed dictionary containing the override information
// from the UsageTimeLimit policy is converted to its intermediate
// representation correctly.
TEST_F(UsageTimeLimitProcessorTest, OverrideValid) {
// Create policy information.
std::string created_at_millis = CreatePolicyTimestamp("1 Jan 2018 10:00:00");
base::Value override = base::Value(base::Value::Type::DICTIONARY);
override.SetKey("action", base::Value("UNLOCK"));
override.SetKey("created_at_millis", base::Value(created_at_millis));
// Call tested functions.
Override override_struct(override);
// Assert right fields are set.
ASSERT_EQ(override_struct.action, Override::Action::kUnlock);
ASSERT_EQ(override_struct.created_at, base::Time::FromDoubleT(1514800800));
ASSERT_FALSE(override_struct.duration);
}
} // namespace internal
} // namespace chromeos
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