Skip to content

Commit

Permalink
cros: Add 'auth-events' crash key
Browse files Browse the repository at this point in the history
This permanent crash key will keep the list of the last user auth
events.

Bug: b:278694020, b:267769914
Change-Id: Id440703a793d05fcff12bf793b4c409a7cd3bb8e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4517364
Reviewed-by: Maksim Ivanov <emaxx@chromium.org>
Reviewed-by: Denis Kuznetsov <antrim@chromium.org>
Commit-Queue: Anastasiia N <anastasiian@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1150088}
  • Loading branch information
Anastasiia N authored and Chromium LUCI CQ committed May 27, 2023
1 parent c0bcb26 commit d8b4cd0
Show file tree
Hide file tree
Showing 8 changed files with 229 additions and 43 deletions.
3 changes: 2 additions & 1 deletion ash/login/ui/lock_contents_view.cc
Expand Up @@ -652,6 +652,7 @@ void LockContentsView::OnUsersChanged(const std::vector<LoginUserInfo>& users) {
LOG(WARNING)
<< "LockContentsView::OnUsersChanged called during Authentication.";
}
AuthEventsRecorder::Get()->OnLockContentsViewUpdate();
// The debug view will potentially call this method many times. Make sure to
// invalidate any child references.
primary_big_view_ = nullptr;
Expand Down Expand Up @@ -2515,7 +2516,7 @@ void LockContentsView::SetKioskLicenseModeForTesting(
void LockContentsView::RecordAndResetPasswordAttempts(
AuthEventsRecorder::AuthenticationOutcome outcome,
AccountId account_id) {
AuthEventsRecorder::Get()->OnExistingUserLoginExit(
AuthEventsRecorder::Get()->OnExistingUserLoginScreenExit(
outcome, unlock_attempt_by_user_[account_id]);
unlock_attempt_by_user_[account_id] = 0;
}
Expand Down
1 change: 1 addition & 0 deletions ash/login/ui/lock_screen.cc
Expand Up @@ -49,6 +49,7 @@ void RecordScreenType(LockScreen::ScreenType type) {
screen_type = AuthEventsRecorder::AuthenticationSurface::kLock;
break;
}
AuthEventsRecorder::Get()->ResetLoginData();
AuthEventsRecorder::Get()->OnAuthenticationSurfaceChange(screen_type);
}

Expand Down
2 changes: 2 additions & 0 deletions ash/login/ui/login_auth_user_view.cc
Expand Up @@ -47,6 +47,7 @@
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chromeos/ash/components/login/auth/auth_events_recorder.h"
#include "chromeos/constants/chromeos_features.h"
#include "components/user_manager/known_user.h"
#include "components/user_manager/user.h"
Expand Down Expand Up @@ -1369,6 +1370,7 @@ void LoginAuthUserView::OnGestureEvent(ui::GestureEvent* event) {
}

void LoginAuthUserView::OnAuthSubmit(const std::u16string& password) {
AuthEventsRecorder::Get()->OnAuthSubmit();
LOG(WARNING) << "crbug.com/1339004 : AuthSubmit "
<< password_view_->IsReadOnly() << " / "
<< pin_input_view_->IsReadOnly();
Expand Down
2 changes: 2 additions & 0 deletions ash/login/ui/login_pin_input_view.cc
Expand Up @@ -14,6 +14,7 @@
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "chromeos/ash/components/login/auth/auth_events_recorder.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/accessibility/ax_enums.mojom-shared.h"
#include "ui/accessibility/ax_node_data.h"
Expand Down Expand Up @@ -94,6 +95,7 @@ void LoginPinInput::OnModified(bool last_field_active, bool complete) {
absl::optional<std::string> user_input = GetCode();
DCHECK(on_submit_);
LOG(WARNING) << "crbug.com/1339004 : Submitting PIN " << IsReadOnly();
AuthEventsRecorder::Get()->OnPinSubmit();
SetReadOnly(true);
on_submit_.Run(base::UTF8ToUTF16(user_input.value_or(std::string())));
}
Expand Down
4 changes: 0 additions & 4 deletions chrome/browser/ash/login/ui/login_display_host_common.cc
Expand Up @@ -60,7 +60,6 @@
#include "chrome/common/pref_names.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
#include "chromeos/ash/components/login/auth/auth_events_recorder.h"
#include "chromeos/ash/components/login/auth/auth_performer.h"
#include "components/keep_alive_registry/keep_alive_types.h"
#include "components/strings/grit/components_strings.h"
Expand Down Expand Up @@ -204,9 +203,6 @@ LoginDisplayHostCommon::LoginDisplayHostCommon()
browser_shutdown::AddAppTerminatingCallback(base::BindOnce(
&LoginDisplayHostCommon::OnAppTerminating, base::Unretained(this)));
BrowserList::AddObserver(this);
AuthEventsRecorder::Get()->ResetLoginData();
AuthEventsRecorder::Get()->OnAuthenticationSurfaceChange(
AuthEventsRecorder::AuthenticationSurface::kLogin);
}

LoginDisplayHostCommon::~LoginDisplayHostCommon() {
Expand Down
128 changes: 113 additions & 15 deletions chromeos/ash/components/login/auth/auth_events_recorder.cc
Expand Up @@ -4,6 +4,7 @@

#include "chromeos/ash/components/login/auth/auth_events_recorder.h"

#include <numeric>
#include <vector>

#include "base/check_is_test.h"
Expand All @@ -23,12 +24,19 @@
namespace ash {
namespace {

constexpr int kMaxSessionStateCrashKeyLength = 32;
constexpr char kSessionStateCrashKey[] = "session-state";

using AuthenticationSurface = AuthEventsRecorder::AuthenticationSurface;
using AuthenticationOutcome = AuthEventsRecorder::AuthenticationOutcome;
using CryptohomeRecoveryResult = AuthEventsRecorder::CryptohomeRecoveryResult;
using UserLoginType = AuthEventsRecorder::UserLoginType;

// Constants for crash keys:
constexpr int kMaxSessionStateCrashKeyLength = 32;
constexpr int kMaxAuthEventsCrashKeyLength = 1024;
constexpr char kAuthEventSeparator = ',';

// Names of the crash keys:
constexpr char kAuthEventsCrashKey[] = "auth-events";
constexpr char kSessionStateCrashKey[] = "session-state";

// Histogram for tracking the reason of auth failure
constexpr char kFailureReasonHistogramName[] = "Login.FailureReason";
Expand Down Expand Up @@ -101,7 +109,7 @@ std::string UserCountSuffix(int user_count) {
// Suffix for grouping by screen type. Should match suffixes of the
// Ash.OSAuth.{Login,Lock}.NbPasswordAttempts.{UntilFailure,UntilSuccess}
// metrics in metadata/ash/histograms.xml
std::string GetAuthenticationSurfaceSuffix(AuthenticationSurface screen) {
std::string GetAuthenticationSurfaceName(AuthenticationSurface screen) {
switch (screen) {
case AuthenticationSurface::kLock:
return "Lock";
Expand Down Expand Up @@ -141,7 +149,7 @@ std::string GetNbPasswordAttemptsHistogramName(
AuthenticationSurface screen,
AuthenticationOutcome exit_type) {
return base::StringPrintf(kNbPasswordAttemptsHistogramName,
GetAuthenticationSurfaceSuffix(screen).c_str(),
GetAuthenticationSurfaceName(screen).c_str(),
GetAuthenticationOutcomeSuffix(exit_type).c_str());
}

Expand Down Expand Up @@ -209,6 +217,39 @@ std::string GetSessionStateCrashKeyValue(session_manager::SessionState state) {
}
}

std::string GetUserLoginTypeName(AuthEventsRecorder::UserLoginType type) {
switch (type) {
case UserLoginType::kOnlineNew:
return "online_new";
case UserLoginType::kOnlineExisting:
return "online_existing";
case UserLoginType::kOffline:
return "offline";
case UserLoginType::kEphemeral:
return "ephemeral";
}
NOTREACHED();
return "";
}

std::string GetAuthenticationOutcomeName(AuthenticationOutcome exit_type) {
switch (exit_type) {
case AuthenticationOutcome::kSuccess:
return "success";
case AuthenticationOutcome::kFailure:
return "failure";
case AuthenticationOutcome::kRecovery:
return "recovery";
}
NOTREACHED();
return "";
}

std::string GetCrashKeyStringWithStatus(const std::string& event_name,
bool success) {
return event_name + (success ? "_success" : "_failure");
}

} // namespace

// static
Expand Down Expand Up @@ -258,6 +299,7 @@ void AuthEventsRecorder::OnAuthFailure(
base::RecordAction(base::UserMetricsAction("Login_Failure"));
UMA_HISTOGRAM_ENUMERATION(kFailureReasonHistogramName, reason,
AuthFailure::NUM_FAILURE_REASONS);
AddAuthEvent(GetCrashKeyStringWithStatus("login", /*success=*/false));
}

void AuthEventsRecorder::OnLoginSuccess(const SuccessReason& reason,
Expand All @@ -267,11 +309,12 @@ void AuthEventsRecorder::OnLoginSuccess(const SuccessReason& reason,
base::RecordAction(base::UserMetricsAction("Login_Success"));
UMA_HISTOGRAM_ENUMERATION(kSuccessReasonHistogramName, reason,
SuccessReason::NUM_SUCCESS_REASONS);
MaybeUpdateUserLoginType(is_new_user, is_login_offline, is_ephemeral);
UpdateUserLoginType(is_new_user, is_login_offline, is_ephemeral);
}

void AuthEventsRecorder::OnGuestLoginSuccess() {
base::RecordAction(base::UserMetricsAction("Login_GuestLoginSuccess"));
AddAuthEvent(GetCrashKeyStringWithStatus("guest_login", /*success=*/true));
}

void AuthEventsRecorder::OnUserCount(int user_count) {
Expand All @@ -287,11 +330,12 @@ void AuthEventsRecorder::OnShowUsersOnSignin(bool show_users_on_signin) {
void AuthEventsRecorder::OnAuthenticationSurfaceChange(
AuthenticationSurface surface) {
auth_surface_ = surface;
AddAuthEvent("auth_surface_change_" + GetAuthenticationSurfaceName(surface));
}

void AuthEventsRecorder::OnExistingUserLoginExit(
void AuthEventsRecorder::OnExistingUserLoginScreenExit(
AuthenticationOutcome exit_type,
int num_login_attempts) const {
int num_login_attempts) {
CHECK(auth_surface_);
CHECK_GE(num_login_attempts, 0);
if (exit_type == AuthenticationOutcome::kFailure) {
Expand All @@ -301,6 +345,7 @@ void AuthEventsRecorder::OnExistingUserLoginExit(
base::UmaHistogramCounts100(
GetNbPasswordAttemptsHistogramName(auth_surface_.value(), exit_type),
num_login_attempts);
AddAuthEvent("login_screen_exit_" + GetAuthenticationOutcomeName(exit_type));
}

void AuthEventsRecorder::RecordUserAuthFactors(
Expand All @@ -322,6 +367,20 @@ void AuthEventsRecorder::OnRecoveryDone(CryptohomeRecoveryResult result,
const base::TimeDelta& time) {
base::UmaHistogramMediumTimes(GetRecoveryDurationHistogramName(result), time);
base::UmaHistogramEnumeration(kRecoveryResultHistogramName, result);
AddAuthEvent(GetCrashKeyStringWithStatus(
"recovery_done", result == CryptohomeRecoveryResult::kSucceeded));
}

void AuthEventsRecorder::OnAuthSubmit() {
AddAuthEvent("auth_submit");
}

void AuthEventsRecorder::OnPinSubmit() {
AddAuthEvent("pin_submit");
}

void AuthEventsRecorder::OnLockContentsViewUpdate() {
AddAuthEvent("update_lock_screen_view");
}

void AuthEventsRecorder::OnSessionStateChanged() {
Expand All @@ -332,21 +391,22 @@ void AuthEventsRecorder::OnSessionStateChanged() {
key.Set(GetSessionStateCrashKeyValue(session_state));
}

void AuthEventsRecorder::MaybeUpdateUserLoginType(bool is_new_user,
bool is_login_offline,
bool is_ephemeral) {
void AuthEventsRecorder::UpdateUserLoginType(bool is_new_user,
bool is_login_offline,
bool is_ephemeral) {
if (is_login_offline) {
user_login_type_ = AuthEventsRecorder::kOffline;
user_login_type_ = UserLoginType::kOffline;
} else if (!is_new_user) {
// The rest 3 online login types are with either existing user and new users
user_login_type_ = AuthEventsRecorder::kOnlineExisting;
user_login_type_ = UserLoginType::kOnlineExisting;
} else if (is_ephemeral) {
// The rest 2 new user login types are either ephemeral or new online users
user_login_type_ = AuthEventsRecorder::kEphemeral;
user_login_type_ = UserLoginType::kEphemeral;
} else {
user_login_type_ = AuthEventsRecorder::kOnlineNew;
user_login_type_ = UserLoginType::kOnlineNew;
}

AddAuthEvent("login_" + GetUserLoginTypeName(user_login_type_.value()));
MaybeReportFlowMetrics();
}

Expand All @@ -362,6 +422,44 @@ void AuthEventsRecorder::MaybeReportFlowMetrics() {
user_login_type_.value());
}

void AuthEventsRecorder::AddAuthEvent(const std::string& event_name) {
events_.push_back(event_name);
UpdateAuthEventsCrashKey();
}

void AuthEventsRecorder::UpdateAuthEventsCrashKey() {
if (events_.size() == 0) {
return;
}

// Preallocate the space needed for all the events combined.
const size_t events_string_length =
std::accumulate(events_.begin(), events_.end(), 0,
[](const size_t sum, const std::string& event) {
return sum + event.length() + 1;
});
std::string crash_key_string;
crash_key_string.reserve(events_string_length);

// Put new events at the front, so that we keep the most recent & relevant
// ones.
for (const std::string& event : events_) {
crash_key_string += event;
crash_key_string += kAuthEventSeparator;
}
DCHECK_EQ(crash_key_string.length(), events_string_length);

if (crash_key_string.length() > kMaxAuthEventsCrashKeyLength) {
crash_key_string = crash_key_string.substr(crash_key_string.length() -
kMaxAuthEventsCrashKeyLength);
}

// Note: the string will be truncated to `kMaxAuthEventsCrashKeyLength`.
static crash_reporter::CrashKeyString<kMaxAuthEventsCrashKeyLength> key(
kAuthEventsCrashKey);
key.Set(crash_key_string);
}

void AuthEventsRecorder::Reset() {
user_count_ = absl::nullopt;
show_users_on_signin_ = absl::nullopt;
Expand Down
37 changes: 30 additions & 7 deletions chromeos/ash/components/login/auth/auth_events_recorder.h
Expand Up @@ -5,8 +5,10 @@
#ifndef CHROMEOS_ASH_COMPONENTS_LOGIN_AUTH_AUTH_EVENTS_RECORDER_H_
#define CHROMEOS_ASH_COMPONENTS_LOGIN_AUTH_AUTH_EVENTS_RECORDER_H_

#include <string>
#include <vector>

#include "base/containers/circular_deque.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "chromeos/ash/components/cryptohome/auth_factor.h"
Expand All @@ -26,12 +28,12 @@ class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_LOGIN_AUTH) AuthEventsRecorder
public:
// Enum used for UMA. Do NOT reorder or remove entry. Don't forget to
// update LoginFlowUserLoginType enum in enums.xml when adding new entries.
enum UserLoginType {
enum class UserLoginType {
kOnlineNew = 0,
kOnlineExisting = 1,
kOffline = 2,
kEphemeral = 3,
kMaxValue
kMaxValue = kEphemeral
};

enum class AuthenticationSurface { kLogin, kLock };
Expand Down Expand Up @@ -116,8 +118,8 @@ class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_LOGIN_AUTH) AuthEventsRecorder
// `OnAuthenticationSurfaceChange` must be called before this method.
// Nothing will be recorded if the `exit_type` is `kFailure` and
// `num_login_attempts` is 0.
void OnExistingUserLoginExit(AuthenticationOutcome exit_type,
int num_login_attempts) const;
void OnExistingUserLoginScreenExit(AuthenticationOutcome exit_type,
int num_login_attempts);

// Report which auth factors the user has configured.
void RecordUserAuthFactors(
Expand All @@ -127,6 +129,15 @@ class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_LOGIN_AUTH) AuthEventsRecorder
void OnRecoveryDone(CryptohomeRecoveryResult result,
const base::TimeDelta& time);

// Report that the user submitted an auth method.
void OnAuthSubmit();

// Report that the user submitted the pin input field.
void OnPinSubmit();

// Report that `LockContentsView` layout was updated.
void OnLockContentsViewUpdate();

int knowledge_factor_auth_failure_count() {
return knowledge_factor_auth_failure_count_;
}
Expand All @@ -145,17 +156,29 @@ class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_LOGIN_AUTH) AuthEventsRecorder

// Determine the user login type from the provided information.
// Call `MaybeReportFlowMetrics`.
void MaybeUpdateUserLoginType(bool is_new_user,
bool is_login_offline,
bool is_ephemeral);
void UpdateUserLoginType(bool is_new_user,
bool is_login_offline,
bool is_ephemeral);

// Report the user login type in association with policy and total user count
// if 3 information are available: user_count_, show_users_on_signin_,
// user_login_type_.
void MaybeReportFlowMetrics();

// Append a new auth event to the list and call `UpdateAuthEventsCrashKey`.
void AddAuthEvent(const std::string& event_name);

// Update the `kAuthEventsCrashKey`.
// Note: The maximum total length of the crash key value that is saved is
// `kMaxAuthEventsCrashKeyLength`. The newest events will be kept when the
// string is too long.
void UpdateAuthEventsCrashKey();

void Reset();

// List of auth event, newer events are at the end.
base::circular_deque<std::string> events_;

base::ScopedObservation<session_manager::SessionManager,
session_manager::SessionManagerObserver>
session_observation_{this};
Expand Down

0 comments on commit d8b4cd0

Please sign in to comment.