From 2d3967d969318f3041c766f377eed2ef88a613af Mon Sep 17 00:00:00 2001 From: Adolfo Victoria Date: Tue, 12 Jun 2018 16:28:36 -0700 Subject: libbrillo: policy: Add new update time restrictions policy Add a new device policy from chrome_device_policy.proto: DeviceAutoUpdateTimeRestrictions BUG=chromium:852860 TEST=libpolicy unit tests CQ-DEPEND=CL:1136538 Change-Id: Iaef8791f683af00668dcd4041282758cc26b8fbf Reviewed-on: https://chromium-review.googlesource.com/1101707 Commit-Ready: Adolfo Higueros Tested-by: Adolfo Higueros Reviewed-by: Dan Erat --- policy/device_policy.h | 24 +++++++++ policy/device_policy_impl.cc | 106 +++++++++++++++++++++++++++++++++++++ policy/device_policy_impl.h | 2 + policy/mock_device_policy.h | 3 +- policy/tests/libpolicy_unittest.cc | 17 ++++++ policy/tests/whitelist/policy_all | Bin 938 -> 1203 bytes 6 files changed, 151 insertions(+), 1 deletion(-) diff --git a/policy/device_policy.h b/policy/device_policy.h index b71d9de..52e73d5 100644 --- a/policy/device_policy.h +++ b/policy/device_policy.h @@ -12,6 +12,7 @@ #include #include +#include #pragma GCC visibility push(default) @@ -35,6 +36,22 @@ class DevicePolicy { uint16_t product_id; }; + // Time interval represented by two |day_of_week| and |time| pairs. The start + // of the interval is inclusive and the end is exclusive. The time represented + // by those pairs will be interpreted to be in the local timezone. Because of + // this, there exists the possibility of intervals being repeated or skipped + // in a day with daylight savings transitions, this is expected behavior. + struct WeeklyTimeInterval { + // Value is from 1 to 7 (1 = Monday, 2 = Tuesday, etc.). All values outside + // this range are invalid and will be discarded. + int start_day_of_week; + // Time since the start of the day. This value will be interpreted to be in + // the system's current timezone when used for range checking. + base::TimeDelta start_time; + int end_day_of_week; + base::TimeDelta end_time; + }; + DevicePolicy(); virtual ~DevicePolicy(); @@ -178,6 +195,13 @@ class DevicePolicy { // U2F or U2F_EXTENDED). Returns true on success. virtual bool GetSecondFactorAuthenticationMode(int* mode_out) const = 0; + // Writes the valid time intervals to |intervals_out|. These + // intervals are taken from the disallowed time intervals field in the + // AutoUpdateSettingsProto. Returns true if the intervals in the proto are + // valid. + virtual bool GetDisallowedTimeIntervals( + std::vector* intervals_out) const = 0; + private: // Verifies that the policy signature is correct. virtual bool VerifyPolicySignature() = 0; diff --git a/policy/device_policy_impl.cc b/policy/device_policy_impl.cc index 6189ca7..4d506ba 100644 --- a/policy/device_policy_impl.cc +++ b/policy/device_policy_impl.cc @@ -8,9 +8,12 @@ #include #include +#include #include #include #include +#include +#include #include #include @@ -98,6 +101,50 @@ std::string DecodeConnectionType(int type) { return kConnectionTypes[type]; } +// TODO(adokar): change type to base::Optional when available. +int ConvertDayOfWeekStringToInt(const std::string& day_of_week_str) { + if (day_of_week_str == "Sunday") return 0; + if (day_of_week_str == "Monday") return 1; + if (day_of_week_str == "Tuesday") return 2; + if (day_of_week_str == "Wednesday") return 3; + if (day_of_week_str == "Thursday") return 4; + if (day_of_week_str == "Friday") return 5; + if (day_of_week_str == "Saturday") return 6; + return -1; +} + +bool DecodeWeeklyTimeFromValue(const base::DictionaryValue& dict_value, + int* day_of_week_out, + base::TimeDelta* time_out) { + std::string day_of_week_str; + if (!dict_value.GetString("day_of_week", &day_of_week_str)) { + LOG(ERROR) << "Day of the week is absent."; + return false; + } + *day_of_week_out = ConvertDayOfWeekStringToInt(day_of_week_str); + if (*day_of_week_out == -1) { + LOG(ERROR) << "Undefined day of the week: " << day_of_week_str; + return false; + } + + int hours; + if (!dict_value.GetInteger("hours", &hours) || hours < 0 || hours > 23) { + LOG(ERROR) << "Hours are absent or are outside of the range [0, 24)."; + return false; + } + + int minutes; + if (!dict_value.GetInteger("minutes", &minutes) || minutes < 0 || + minutes > 59) { + LOG(ERROR) << "Minutes are absent or are outside the range [0, 60)"; + return false; + } + + *time_out = + base::TimeDelta::FromMinutes(minutes) + base::TimeDelta::FromHours(hours); + return true; +} + } // namespace DevicePolicyImpl::DevicePolicyImpl() @@ -510,6 +557,65 @@ bool DevicePolicyImpl::GetSecondFactorAuthenticationMode(int* mode_out) const { return true; } +bool DevicePolicyImpl::GetDisallowedTimeIntervals( + std::vector* intervals_out) const { + if (!device_policy_.has_auto_update_settings()) { + return false; + } + + const em::AutoUpdateSettingsProto& proto = + device_policy_.auto_update_settings(); + + if (!proto.has_disallowed_time_intervals()) { + return false; + } + + // Decode the JSON string + std::string error; + std::unique_ptr decoded_json = + base::JSONReader::ReadAndReturnError(proto.disallowed_time_intervals(), + base::JSON_ALLOW_TRAILING_COMMAS, + NULL, &error); + if (!decoded_json) { + LOG(ERROR) << "Invalid JSON string " << error; + return false; + } + + std::unique_ptr list_val = + base::ListValue::From(std::move(decoded_json)); + if (!list_val) { + LOG(ERROR) << "JSON string is not a list"; + return false; + } + + intervals_out->clear(); + + for (const auto& interval_value : *list_val) { + base::DictionaryValue* interval_dict; + if (!interval_value->GetAsDictionary(&interval_dict)) { + LOG(ERROR) << "Invalid JSON string given. Interval is not a dict."; + return false; + } + base::DictionaryValue* start; + base::DictionaryValue* end; + if (!interval_dict->GetDictionary("start", &start) || + !interval_dict->GetDictionary("end", &end)) { + LOG(ERROR) << "Interval is missing start/end."; + return false; + } + WeeklyTimeInterval weekly_interval; + if (!DecodeWeeklyTimeFromValue(*start, &weekly_interval.start_day_of_week, + &weekly_interval.start_time) || + !DecodeWeeklyTimeFromValue(*end, &weekly_interval.end_day_of_week, + &weekly_interval.end_time)) { + return false; + } + + intervals_out->push_back(weekly_interval); + } + return true; +} + bool DevicePolicyImpl::VerifyPolicyFile(const base::FilePath& policy_path) { if (!verify_root_ownership_) { return true; diff --git a/policy/device_policy_impl.h b/policy/device_policy_impl.h index 9ec39ac..ee00f0f 100644 --- a/policy/device_policy_impl.h +++ b/policy/device_policy_impl.h @@ -78,6 +78,8 @@ class DevicePolicyImpl : public DevicePolicy { bool GetAutoLaunchedKioskAppId(std::string* app_id_out) const override; bool IsEnterpriseManaged() const override; bool GetSecondFactorAuthenticationMode(int* mode_out) const override; + bool GetDisallowedTimeIntervals( + std::vector* intervals_out) const override; // Methods that can be used only for testing. void set_policy_data_for_testing( diff --git a/policy/mock_device_policy.h b/policy/mock_device_policy.h index eba80f4..2999a18 100644 --- a/policy/mock_device_policy.h +++ b/policy/mock_device_policy.h @@ -101,7 +101,8 @@ class MockDevicePolicy : public DevicePolicy { MOCK_CONST_METHOD1(GetAutoLaunchedKioskAppId, bool(std::string*)); MOCK_CONST_METHOD0(IsEnterpriseManaged, bool()); MOCK_CONST_METHOD1(GetSecondFactorAuthenticationMode, bool(int*)); - + MOCK_CONST_METHOD1(GetDisallowedTimeIntervals, + bool(std::vector*)); MOCK_METHOD0(VerifyPolicyFiles, bool(void)); MOCK_METHOD0(VerifyPolicySignature, bool(void)); }; diff --git a/policy/tests/libpolicy_unittest.cc b/policy/tests/libpolicy_unittest.cc index 384bd69..6fc7258 100644 --- a/policy/tests/libpolicy_unittest.cc +++ b/policy/tests/libpolicy_unittest.cc @@ -178,6 +178,21 @@ TEST(PolicyTest, DevicePolicyAllSetTest) { ASSERT_TRUE(policy.GetSecondFactorAuthenticationMode(&int_value)); EXPECT_EQ(2, int_value); + std::vector intervals; + ASSERT_TRUE(policy.GetDisallowedTimeIntervals(&intervals)); + ASSERT_EQ(2, intervals.size()); + EXPECT_EQ(4, intervals[0].start_day_of_week); + EXPECT_EQ(base::TimeDelta::FromMinutes(30) + base::TimeDelta::FromHours(12), + intervals[0].start_time); + EXPECT_EQ(6, intervals[0].end_day_of_week); + EXPECT_EQ(base::TimeDelta::FromMinutes(15) + base::TimeDelta::FromHours(3), + intervals[0].end_time); + EXPECT_EQ(1, intervals[1].start_day_of_week); + EXPECT_EQ(base::TimeDelta::FromMinutes(10) + base::TimeDelta::FromHours(20), + intervals[1].start_time); + EXPECT_EQ(3, intervals[1].end_day_of_week); + EXPECT_EQ(base::TimeDelta::FromMinutes(20), intervals[1].end_time); + // Reloading the protobuf should succeed. EXPECT_TRUE(provider.Reload()); } @@ -207,6 +222,7 @@ TEST(PolicyTest, DevicePolicyNoneSetTest) { bool bool_value; std::string string_value; std::vector list_device; + std::vector intervals; EXPECT_FALSE(policy.GetPolicyRefreshRate(&int_value)); EXPECT_FALSE(policy.GetUserWhitelist(&list_value)); @@ -235,6 +251,7 @@ TEST(PolicyTest, DevicePolicyNoneSetTest) { EXPECT_FALSE(policy.GetAllowKioskAppControlChromeVersion(&bool_value)); EXPECT_FALSE(policy.GetUsbDetachableWhitelist(&list_device)); EXPECT_FALSE(policy.GetSecondFactorAuthenticationMode(&int_value)); + EXPECT_FALSE(policy.GetDisallowedTimeIntervals(&intervals)); } // Verify that the library will correctly recognize and signal missing files. diff --git a/policy/tests/whitelist/policy_all b/policy/tests/whitelist/policy_all index 07999f6..c0dfaaf 100644 Binary files a/policy/tests/whitelist/policy_all and b/policy/tests/whitelist/policy_all differ -- cgit v1.2.3