blob: 058283ba29492513bf2a2df668a32790807872bc [file] [log] [blame]
// Copyright 2019 The Chromium OS 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 "kerberos/kerberos_metrics.h"
#include <utility>
#include <base/files/file_util.h>
#include <base/strings/stringprintf.h>
#include <base/time/clock.h>
#include "base/time/default_clock.h"
namespace kerberos {
namespace {
// Prefix for all UMA stats.
constexpr char kKerberos[] = "Kerberos.";
// Prefix for Kerberos.Result.<method name> stats.
constexpr char kResult[] = "Result.";
// Stat for the result of a ValidateConfig call.
constexpr char kValidateConfigErrorCode[] = "ValidateConfigErrorCode";
// Stat for the encryption types used on Kerberos TGT creation.
constexpr char kEncryptionTypesAcquireKerberosTgt[] =
"EncryptionTypesAcquireKerberosTgt";
// Stat for counting user types, see |UserType|.
constexpr char kDailyActiveUsers[] = "DailyActiveUsers";
// Prefix for UMA stats that are counting accounts.
constexpr char kNumberOfAccounts[] = "NumberOfAccounts.";
// Stat names for counting accounts, prefixed by "Kerberos.NumberOfAccounts.".
constexpr char kTotal[] = "Total";
constexpr char kManaged[] = "Managed";
constexpr char kUnmanaged[] = "Unmanaged";
constexpr char kRememberedPassword[] = "RememberedPassword";
constexpr char kUseLoginPassword[] = "UseLoginPassword";
// Max number of accounts for UMA stats.
constexpr int kMaxAccounts = 10;
// Used to rate limit some UMA stats to once a day.
constexpr char kDailyReportTimeFile[] = "daily_report_timestamp";
// User type to be sent to Kerberos.DailyActiveUsers. These values (except
// UserType::kCount, which should be last) are persisted to logs. Entries should
// not be renumbered and numeric values should never be reused.
enum class UserType { kManaged = 0, kUnmanaged = 1, kCount = 2 };
} // namespace
KerberosMetrics::KerberosMetrics(const base::FilePath& storage_dir)
: kerberos_(kKerberos),
acquire_tgt_timer_(kerberos_ + "AcquireKerberosTgtTime",
1 /* min 1 millisecond */,
20000 /* max 20 seconds */,
50 /* bucket count */),
daily_report_time_path_(storage_dir.Append(kDailyReportTimeFile)),
clock_(std::make_unique<base::DefaultClock>()) {
chromeos_metrics::TimerReporter::set_metrics_lib(&metrics_lib_);
}
KerberosMetrics::~KerberosMetrics() {
chromeos_metrics::TimerReporter::set_metrics_lib(nullptr);
}
void KerberosMetrics::StartAcquireTgtTimer() {
DCHECK(!acquire_tgt_timer_.HasStarted());
acquire_tgt_timer_.Start();
}
void KerberosMetrics::StopAcquireTgtTimerAndReport() {
DCHECK(acquire_tgt_timer_.HasStarted());
acquire_tgt_timer_.Stop();
acquire_tgt_timer_.ReportMilliseconds();
}
void KerberosMetrics::ReportDBusCallResult(const std::string& method_name,
ErrorType error) {
metrics_lib_.SendEnumToUMA(kerberos_ + kResult + method_name,
static_cast<int>(error),
static_cast<int>(ERROR_COUNT));
}
void KerberosMetrics::ReportValidateConfigErrorCode(ConfigErrorCode code) {
metrics_lib_.SendEnumToUMA(kerberos_ + kValidateConfigErrorCode,
static_cast<int>(code),
static_cast<int>(CONFIG_ERROR_COUNT));
}
void KerberosMetrics::ReportKerberosEncryptionTypes(
KerberosEncryptionTypes types) {
metrics_lib_.SendEnumToUMA(kerberos_ + kEncryptionTypesAcquireKerberosTgt,
static_cast<int>(types),
static_cast<int>(KerberosEncryptionTypes::kCount));
}
bool KerberosMetrics::ShouldReportDailyUsageStats() {
const base::Time now = clock_->Now();
base::File::Info info;
if (!base::GetFileInfo(daily_report_time_path_, &info)) {
// Create the file. Don't skew stats if something goes wrong. Note that
// base::TouchFile bails if the file doesn't exist!
const bool res =
base::WriteFile(daily_report_time_path_, nullptr, 0) == 0 &&
base::TouchFile(daily_report_time_path_, now, now);
if (!res)
LOG(WARNING) << "Failed to touch " << daily_report_time_path_.value();
return res;
}
// Be sure to gracefully handle the case when the clock is moved backwards.
const base::Time last_file_time = info.last_modified;
int days_elapsed = (now - last_file_time).InDays();
if (days_elapsed == 0)
return false;
// Don't set the new file time to |now|. This would result in an average
// frequency of less than one day.
base::Time new_time =
last_file_time + days_elapsed * base::TimeDelta::FromDays(1);
const bool res = base::TouchFile(daily_report_time_path_, new_time, new_time);
if (!res)
LOG(WARNING) << "Failed to touch " << daily_report_time_path_.value();
// Don't report if time goes backwards (but do reset the file time!).
return days_elapsed > 0;
}
void KerberosMetrics::ReportDailyUsageStats(int total_count,
int managed_count,
int unmanaged_count,
int remembered_password_count,
int use_login_password_count) {
// TODO(https://crbug.com/984552): Send the proper user type once unmanaged
// users can use this feature.
metrics_lib_.SendEnumToUMA(kerberos_ + kDailyActiveUsers,
static_cast<int>(UserType::kManaged),
static_cast<int>(UserType::kCount));
SendAccountCount(kTotal, total_count);
SendAccountCount(kManaged, managed_count);
SendAccountCount(kUnmanaged, unmanaged_count);
SendAccountCount(kRememberedPassword, remembered_password_count);
SendAccountCount(kUseLoginPassword, use_login_password_count);
}
void KerberosMetrics::SetClockForTesting(std::unique_ptr<base::Clock> clock) {
clock_ = std::move(clock);
}
void KerberosMetrics::SendAccountCount(const char* name, int count) {
metrics_lib_.SendToUMA(kerberos_ + kNumberOfAccounts + name, count, 1,
kMaxAccounts, kMaxAccounts + 1);
}
} // namespace kerberos