blob: 919f122b970ff62bb9440928b6086df2e7c78c43 [file] [log] [blame]
// Copyright 2019 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/ash/child_accounts/time_limits/app_time_policy_helpers.h"
#include <utility>
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/browser/ash/child_accounts/time_limits/app_types.h"
namespace ash {
namespace app_time {
namespace policy {
const char kUrlList[] = "url_list";
const char kAppList[] = "app_list";
const char kAppId[] = "app_id";
const char kAppType[] = "app_type";
const char kAppLimitsArray[] = "app_limits";
const char kAppInfoDict[] = "app_info";
const char kRestrictionEnum[] = "restriction";
const char kDailyLimitInt[] = "daily_limit_mins";
const char kLastUpdatedString[] = "last_updated_millis";
const char kResetAtDict[] = "reset_at";
const char kHourInt[] = "hour";
const char kMinInt[] = "minute";
const char kActivityReportingEnabled[] = "activity_reporting_enabled";
apps::mojom::AppType PolicyStringToAppType(const std::string& app_type) {
if (app_type == "ARC")
return apps::mojom::AppType::kArc;
if (app_type == "BOREALIS")
return apps::mojom::AppType::kBorealis;
if (app_type == "BUILT-IN")
return apps::mojom::AppType::kBuiltIn;
if (app_type == "CROSTINI")
return apps::mojom::AppType::kCrostini;
if (app_type == "EXTENSION")
return apps::mojom::AppType::kExtension;
if (app_type == "PLUGIN-VM")
return apps::mojom::AppType::kPluginVm;
if (app_type == "WEB")
return apps::mojom::AppType::kWeb;
NOTREACHED();
return apps::mojom::AppType::kUnknown;
}
std::string AppTypeToPolicyString(apps::mojom::AppType app_type) {
switch (app_type) {
case apps::mojom::AppType::kArc:
return "ARC";
case apps::mojom::AppType::kBorealis:
return "BOREALIS";
case apps::mojom::AppType::kBuiltIn:
return "BUILT-IN";
case apps::mojom::AppType::kCrostini:
return "CROSTINI";
case apps::mojom::AppType::kExtension:
return "EXTENSION";
case apps::mojom::AppType::kPluginVm:
return "PLUGIN-VM";
case apps::mojom::AppType::kWeb:
return "WEB";
default:
NOTREACHED();
return "";
}
}
AppRestriction PolicyStringToAppRestriction(const std::string& restriction) {
if (restriction == "BLOCK")
return AppRestriction::kBlocked;
if (restriction == "TIME_LIMIT")
return AppRestriction::kTimeLimit;
NOTREACHED();
return AppRestriction::kUnknown;
}
std::string AppRestrictionToPolicyString(const AppRestriction& restriction) {
switch (restriction) {
case AppRestriction::kBlocked:
return "BLOCK";
case AppRestriction::kTimeLimit:
return "TIME_LIMIT";
default:
NOTREACHED();
return "";
}
}
absl::optional<AppId> AppIdFromDict(const base::Value& dict) {
if (!dict.is_dict())
return absl::nullopt;
const std::string* id = dict.FindStringKey(kAppId);
if (!id || id->empty()) {
DLOG(ERROR) << "Invalid id.";
return absl::nullopt;
}
const std::string* type_string = dict.FindStringKey(kAppType);
if (!type_string || type_string->empty()) {
DLOG(ERROR) << "Invalid type.";
return absl::nullopt;
}
return AppId(PolicyStringToAppType(*type_string), *id);
}
base::Value AppIdToDict(const AppId& app_id) {
base::Value value(base::Value::Type::DICTIONARY);
value.SetKey(kAppId, base::Value(app_id.app_id()));
value.SetKey(kAppType, base::Value(AppTypeToPolicyString(app_id.app_type())));
return value;
}
absl::optional<AppId> AppIdFromAppInfoDict(const base::Value& dict) {
if (!dict.is_dict())
return absl::nullopt;
const base::Value* app_info = dict.FindKey(kAppInfoDict);
if (!app_info || !app_info->is_dict()) {
DLOG(ERROR) << "Invalid app info dictionary.";
return absl::nullopt;
}
return AppIdFromDict(*app_info);
}
absl::optional<AppLimit> AppLimitFromDict(const base::Value& dict) {
if (!dict.is_dict())
return absl::nullopt;
const std::string* restriction_string = dict.FindStringKey(kRestrictionEnum);
if (!restriction_string || restriction_string->empty()) {
DLOG(ERROR) << "Invalid restriction.";
return absl::nullopt;
}
const AppRestriction restriction =
PolicyStringToAppRestriction(*restriction_string);
absl::optional<int> daily_limit_mins = dict.FindIntKey(kDailyLimitInt);
if ((restriction == AppRestriction::kTimeLimit && !daily_limit_mins) ||
(restriction == AppRestriction::kBlocked && daily_limit_mins)) {
DLOG(ERROR) << "Invalid restriction.";
return absl::nullopt;
}
absl::optional<base::TimeDelta> daily_limit;
if (daily_limit_mins) {
daily_limit = base::TimeDelta::FromMinutes(*daily_limit_mins);
if (daily_limit && (*daily_limit < base::TimeDelta::FromHours(0) ||
*daily_limit > base::TimeDelta::FromHours(24))) {
DLOG(ERROR) << "Invalid daily limit.";
return absl::nullopt;
}
}
const std::string* last_updated_string =
dict.FindStringKey(kLastUpdatedString);
int64_t last_updated_millis;
if (!last_updated_string || last_updated_string->empty() ||
!base::StringToInt64(*last_updated_string, &last_updated_millis)) {
DLOG(ERROR) << "Invalid last updated time.";
return absl::nullopt;
}
const base::Time last_updated =
base::Time::UnixEpoch() +
base::TimeDelta::FromMilliseconds(last_updated_millis);
return AppLimit(restriction, daily_limit, last_updated);
}
base::Value AppLimitToDict(const AppLimit& limit) {
base::Value value(base::Value::Type::DICTIONARY);
value.SetKey(kRestrictionEnum,
base::Value(AppRestrictionToPolicyString(limit.restriction())));
if (limit.daily_limit())
value.SetKey(kDailyLimitInt, base::Value(limit.daily_limit()->InMinutes()));
const std::string last_updated_string = base::NumberToString(
(limit.last_updated() - base::Time::UnixEpoch()).InMilliseconds());
value.SetKey(kLastUpdatedString, base::Value(last_updated_string));
return value;
}
absl::optional<base::TimeDelta> ResetTimeFromDict(const base::Value& dict) {
if (!dict.is_dict())
return absl::nullopt;
const base::Value* reset_dict = dict.FindKey(kResetAtDict);
if (!reset_dict || !reset_dict->is_dict()) {
DLOG(ERROR) << "Invalid reset time dictionary.";
return absl::nullopt;
}
absl::optional<int> hour = reset_dict->FindIntKey(kHourInt);
if (!hour) {
DLOG(ERROR) << "Invalid reset hour.";
return absl::nullopt;
}
absl::optional<int> minutes = reset_dict->FindIntKey(kMinInt);
if (!minutes) {
DLOG(ERROR) << "Invalid reset minutes.";
return absl::nullopt;
}
const int hour_in_mins = base::TimeDelta::FromHours(1).InMinutes();
return base::TimeDelta::FromMinutes(hour.value() * hour_in_mins +
minutes.value());
}
base::Value ResetTimeToDict(int hour, int minutes) {
base::Value value(base::Value::Type::DICTIONARY);
value.SetKey(kHourInt, base::Value(hour));
value.SetKey(kMinInt, base::Value(minutes));
return value;
}
absl::optional<bool> ActivityReportingEnabledFromDict(const base::Value& dict) {
if (!dict.is_dict())
return absl::nullopt;
return dict.FindBoolPath(kActivityReportingEnabled);
}
std::map<AppId, AppLimit> AppLimitsFromDict(const base::Value& dict) {
std::map<AppId, AppLimit> app_limits;
const base::Value* limits_array = dict.FindListKey(kAppLimitsArray);
if (!limits_array) {
DLOG(ERROR) << "Invalid app limits list.";
return app_limits;
}
base::Value::ConstListView list_view = limits_array->GetList();
for (const base::Value& dict : list_view) {
if (!dict.is_dict()) {
DLOG(ERROR) << "Invalid app limits entry. ";
continue;
}
absl::optional<AppId> app_id = AppIdFromAppInfoDict(dict);
if (!app_id) {
DLOG(ERROR) << "Invalid app id.";
continue;
}
absl::optional<AppLimit> app_limit = AppLimitFromDict(dict);
if (!app_limit) {
DLOG(ERROR) << "Invalid app limit.";
continue;
}
app_limits.emplace(*app_id, *app_limit);
}
return app_limits;
}
} // namespace policy
} // namespace app_time
} // namespace ash