chromium/chrome/browser/ash/child_accounts/time_limit_override.cc

// Copyright 2019 The Chromium Authors
// 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_limit_override.h"

#include <utility>

#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/values.h"

namespace ash {
namespace usage_time_limit {

namespace {

constexpr char kOverrideAction[] = "action";
constexpr char kOverrideActionCreatedAt[] = "created_at_millis";
constexpr char kOverrideActionDurationMins[] = "duration_mins";
constexpr char kOverrideActionLock[] = "LOCK";
constexpr char kOverrideActionUnlock[] = "UNLOCK";
constexpr char kOverrideActionSpecificData[] = "action_specific_data";
constexpr char kOverrideActionDuration[] = "duration_mins";

// Returns string containing |timestamp| int64_t value in milliseconds. This is
// how timestamp is sent in a policy.
std::string PolicyTimestamp(base::Time timestamp) {
  return base::NumberToString(
      (timestamp - base::Time::UnixEpoch()).InMilliseconds());
}

}  // namespace

// static
constexpr char TimeLimitOverride::kOverridesDictKey[];

// static
std::string TimeLimitOverride::ActionToString(Action action) {
  switch (action) {
    case Action::kLock:
      return kOverrideActionLock;
    case Action::kUnlock:
      return kOverrideActionUnlock;
  }
}

// static
std::optional<TimeLimitOverride> TimeLimitOverride::FromDictionary(
    const base::Value::Dict* dict) {
  if (!dict) {
    DLOG(ERROR) << "Override entry is not a dictionary";
    return std::nullopt;
  }

  const std::string* action_string = dict->FindString(kOverrideAction);
  if (!action_string || action_string->empty()) {
    DLOG(ERROR) << "Invalid override action.";
    return std::nullopt;
  }

  const std::string* creation_time_string =
      dict->FindString(kOverrideActionCreatedAt);
  int64_t creation_time_millis;
  if (!creation_time_string || creation_time_string->empty() ||
      !base::StringToInt64(*creation_time_string, &creation_time_millis)) {
    DLOG(ERROR) << "Invalid override creation time.";
    return std::nullopt;
  }

  Action action =
      *action_string == kOverrideActionLock ? Action::kLock : Action::kUnlock;

  base::Time creation_time =
      base::Time::UnixEpoch() + base::Milliseconds(creation_time_millis);

  const base::Value::Dict* action_dict =
      dict->FindDict(kOverrideActionSpecificData);
  const base::Value* duration_value =
      action_dict ? action_dict->Find(kOverrideActionDurationMins) : nullptr;
  std::optional<base::TimeDelta> duration =
      duration_value ? base::Minutes(duration_value->GetInt())
                     : std::optional<base::TimeDelta>();

  return TimeLimitOverride(action, creation_time, duration);
}

// static
std::optional<TimeLimitOverride> TimeLimitOverride::MostRecentFromList(
    const base::Value::List* list) {
  if (!list) {
    DLOG(ERROR) << "Override entries should be a list.";
    return std::nullopt;
  }

  // The most recent override created.
  std::optional<TimeLimitOverride> last_override;
  for (const base::Value& override_value : *list) {
    std::optional<TimeLimitOverride> current_override =
        FromDictionary(&override_value.GetDict());
    if (!current_override.has_value()) {
      DLOG(ERROR) << "Invalid override entry";
      continue;
    }

    if (!last_override.has_value() ||
        (current_override->created_at() > last_override->created_at())) {
      last_override = std::move(current_override);
    }
  }
  return last_override;
}

TimeLimitOverride::TimeLimitOverride(Action action,
                                     base::Time created_at,
                                     std::optional<base::TimeDelta> duration)
    : action_(action), created_at_(created_at), duration_(duration) {}

TimeLimitOverride::~TimeLimitOverride() = default;

TimeLimitOverride::TimeLimitOverride(TimeLimitOverride&&) = default;

TimeLimitOverride& TimeLimitOverride::operator=(TimeLimitOverride&&) = default;

bool TimeLimitOverride::operator==(const TimeLimitOverride& rhs) const {
  return action_ == rhs.action() && created_at_ == rhs.created_at() &&
         duration_ == rhs.duration();
}

bool TimeLimitOverride::IsLock() const {
  return action_ == Action::kLock;
}

base::Value::Dict TimeLimitOverride::ToDictionary() const {
  base::Value::Dict dict;
  dict.Set(kOverrideAction, base::Value(ActionToString(action_)));
  dict.Set(kOverrideActionCreatedAt, base::Value(PolicyTimestamp(created_at_)));
  if (duration_.has_value()) {
    base::Value::Dict duration_dict;
    duration_dict.Set(kOverrideActionDuration,
                      base::Value(duration_->InMinutes()));
    dict.Set(kOverrideActionSpecificData, std::move(duration_dict));
  }
  return dict;
}

}  // namespace usage_time_limit
}  // namespace ash