blob: 999abe70c7157bc803551f882bbb9d7c8ec5961f [file] [log] [blame] [edit]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "secagentd/plugins.h"
#include <memory>
#include "base/functional/callback_forward.h"
#include "base/memory/scoped_refptr.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "cryptohome/proto_bindings/auth_factor.pb.h"
#include "dbus/mock_bus.h"
#include "dbus/mock_object_proxy.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "secagentd/device_user.h"
#include "secagentd/proto/security_xdr_events.pb.h"
#include "secagentd/test/mock_batch_sender.h"
#include "secagentd/test/mock_device_user.h"
#include "secagentd/test/mock_message_sender.h"
#include "secagentd/test/mock_policies_features_broker.h"
#include "secagentd/test/mock_process_cache.h"
#include "secagentd/test/test_utils.h"
#include "user_data_auth-client-test/user_data_auth/dbus-proxy-mocks.h"
namespace secagentd::testing {
namespace pb = cros_xdr::reporting;
using ::testing::_;
using ::testing::Eq;
using ::testing::Invoke;
using ::testing::Return;
using ::testing::WithArg;
using ::testing::WithArgs;
using BatchSenderType =
::testing::StrictMock<MockBatchSender<std::monostate,
pb::XdrUserEvent,
pb::UserEventAtomicVariant>>;
constexpr char kDeviceUser[] = "deviceUser@email.com";
constexpr char kSanitized[] = "943cebc444e3e19da9a2dbf9c8a473bc7cc16d9d";
class AuthenticationPluginTestFixture : public ::testing::Test {
protected:
void SetUp() override {
message_sender_ = base::MakeRefCounted<MockMessageSender>();
device_user_ = base::MakeRefCounted<MockDeviceUser>();
// Unused in authentication plugin.
scoped_refptr<MockProcessCache> process_cache =
base::MakeRefCounted<MockProcessCache>();
policies_features_broker_ =
base::MakeRefCounted<MockPoliciesFeaturesBroker>();
plugin_factory_ = std::make_unique<PluginFactory>();
plugin_ = plugin_factory_->Create(
Types::Plugin::kAuthenticate, message_sender_, process_cache,
policies_features_broker_, device_user_, 1);
EXPECT_NE(nullptr, plugin_);
auto batch_sender = std::make_unique<BatchSenderType>();
batch_sender_ = batch_sender.get();
auth_plugin_ = static_cast<AuthenticationPlugin*>(plugin_.get());
auth_plugin_->SetBatchSenderForTesting(std::move(batch_sender));
}
void SaveRegisterLockingCbs() {
EXPECT_CALL(*device_user_, RegisterScreenLockedHandler)
.WillOnce(WithArg<0>(
Invoke([this](const base::RepeatingCallback<void()>& cb) {
locked_cb_ = cb;
})));
EXPECT_CALL(*device_user_, RegisterScreenUnlockedHandler)
.WillOnce(WithArg<0>(
Invoke([this](const base::RepeatingCallback<void()>& cb) {
unlocked_cb_ = cb;
})));
}
void SaveSessionStateChangeCb() {
EXPECT_CALL(*device_user_, RegisterSessionChangeListener)
.WillOnce(WithArg<0>(Invoke([this](const base::RepeatingCallback<void(
const std::string& state)>& cb) {
state_changed_cb_ = cb;
})));
}
void SetupObjectProxies() {
EXPECT_CALL(*batch_sender_, Start());
cryptohome_proxy_ =
std::make_unique<org::chromium::UserDataAuthInterfaceProxyMock>();
cryptohome_object_proxy_ = new dbus::MockObjectProxy(
bus_.get(), user_data_auth::kUserDataAuthServiceName,
dbus::ObjectPath(user_data_auth::kUserDataAuthServicePath));
EXPECT_CALL(*cryptohome_proxy_, GetObjectProxy)
.WillRepeatedly(Return(cryptohome_object_proxy_.get()));
EXPECT_CALL(*cryptohome_object_proxy_, DoWaitForServiceToBeAvailable(_))
.WillRepeatedly(
WithArg<0>(Invoke([](base::OnceCallback<void(bool)>* cb) mutable {
std::move(*cb).Run(true);
})));
EXPECT_CALL(*cryptohome_proxy_,
DoRegisterAuthenticateAuthFactorCompletedSignalHandler)
.WillOnce(WithArg<0>(Invoke(
[this](base::RepeatingCallback<void(
const user_data_auth::AuthenticateAuthFactorCompleted&)>
cb) { auth_factor_cb_ = cb; })));
auth_plugin_->cryptohome_proxy_ = std::move(cryptohome_proxy_);
}
AuthFactorType GetAuthFactor() { return auth_plugin_->auth_factor_type_; }
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
scoped_refptr<MockMessageSender> message_sender_;
scoped_refptr<MockPoliciesFeaturesBroker> policies_features_broker_;
scoped_refptr<MockDeviceUser> device_user_;
scoped_refptr<dbus::MockBus> bus_;
scoped_refptr<dbus::MockObjectProxy> cryptohome_object_proxy_;
std::unique_ptr<org::chromium::UserDataAuthInterfaceProxyMock>
cryptohome_proxy_;
std::unique_ptr<PluginFactory> plugin_factory_;
std::unique_ptr<PluginInterface> plugin_;
AuthenticationPlugin* auth_plugin_;
BatchSenderType* batch_sender_;
base::RepeatingCallback<void(
const user_data_auth::AuthenticateAuthFactorCompleted&)>
auth_factor_cb_;
base::RepeatingCallback<void()> locked_cb_;
base::RepeatingCallback<void()> unlocked_cb_;
base::RepeatingCallback<void(const std::string& state)> state_changed_cb_;
};
TEST_F(AuthenticationPluginTestFixture, TestGetName) {
ASSERT_EQ("Authentication", plugin_->GetName());
}
TEST_F(AuthenticationPluginTestFixture, TestScreenLockToUnlock) {
SetupObjectProxies();
SaveRegisterLockingCbs();
SaveSessionStateChangeCb();
EXPECT_CALL(*device_user_, GetDeviceUserAsync)
.Times(2)
.WillRepeatedly(WithArg<0>(Invoke(
[](base::OnceCallback<void(const std::string& device_user)> cb) {
std::move(cb).Run(kDeviceUser);
})));
user_data_auth::AuthenticateAuthFactorCompleted completed;
completed.set_auth_factor_type(user_data_auth::AUTH_FACTOR_TYPE_PIN);
// batch_sender_ will be called twice. Once for lock, once for unlock.
auto lock_event = std::make_unique<pb::UserEventAtomicVariant>();
auto unlock_event = std::make_unique<pb::UserEventAtomicVariant>();
EXPECT_CALL(*batch_sender_, Enqueue(_))
.Times(2)
.WillOnce(WithArg<0>(
Invoke([&lock_event](
std::unique_ptr<google::protobuf::MessageLite> message) {
lock_event->ParseFromString(
std::move(message->SerializeAsString()));
})))
.WillOnce(WithArg<0>(
Invoke([&unlock_event](
std::unique_ptr<google::protobuf::MessageLite> message) {
unlock_event->ParseFromString(
std::move(message->SerializeAsString()));
})));
EXPECT_TRUE(plugin_->Activate().ok());
auth_factor_cb_.Run(completed);
locked_cb_.Run();
auto expected_event = std::make_unique<pb::UserEventAtomicVariant>();
expected_event->mutable_common();
expected_event->mutable_lock();
expected_event->mutable_common()->set_device_user(kDeviceUser);
expected_event->mutable_common()->set_create_timestamp_us(
lock_event->common().create_timestamp_us());
EXPECT_THAT(*expected_event, EqualsProto(*lock_event));
// Unlock.
expected_event->Clear();
unlocked_cb_.Run();
expected_event->mutable_unlock()->mutable_authentication()->add_auth_factor(
AuthFactorType::Authentication_AuthenticationType_AUTH_PIN);
expected_event->mutable_common()->set_create_timestamp_us(
unlock_event->common().create_timestamp_us());
expected_event->mutable_common()->set_device_user(kDeviceUser);
EXPECT_THAT(*expected_event, EqualsProto(*unlock_event));
EXPECT_EQ(AuthFactorType::Authentication_AuthenticationType_AUTH_TYPE_UNKNOWN,
GetAuthFactor());
}
TEST_F(AuthenticationPluginTestFixture, TestScreenLoginToLogout) {
SetupObjectProxies();
SaveRegisterLockingCbs();
SaveSessionStateChangeCb();
EXPECT_CALL(*device_user_, GetDeviceUserAsync)
.WillOnce(WithArg<0>(Invoke(
[](base::OnceCallback<void(const std::string& device_user)> cb) {
std::move(cb).Run(kDeviceUser);
})))
.WillOnce(WithArg<0>(Invoke(
[](base::OnceCallback<void(const std::string& device_user)> cb) {
std::move(cb).Run(device_user::kEmpty);
})));
user_data_auth::AuthenticateAuthFactorCompleted completed;
completed.set_auth_factor_type(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
// batch_sender_ will be called twice. Once for login, once for logout.
auto logout_event = std::make_unique<pb::UserEventAtomicVariant>();
auto login_event = std::make_unique<pb::UserEventAtomicVariant>();
EXPECT_CALL(*batch_sender_, Enqueue(_))
.Times(2)
.WillOnce(WithArg<0>(
Invoke([&login_event](
std::unique_ptr<google::protobuf::MessageLite> message) {
login_event->ParseFromString(
std::move(message->SerializeAsString()));
})))
.WillOnce(WithArg<0>(
Invoke([&logout_event](
std::unique_ptr<google::protobuf::MessageLite> message) {
logout_event->ParseFromString(
std::move(message->SerializeAsString()));
})));
EXPECT_TRUE(plugin_->Activate().ok());
auth_factor_cb_.Run(completed);
state_changed_cb_.Run(kStarted);
auto expected_event = std::make_unique<pb::UserEventAtomicVariant>();
expected_event->mutable_common();
expected_event->mutable_logon()->mutable_authentication()->add_auth_factor(
AuthFactorType::Authentication_AuthenticationType_AUTH_PASSWORD);
expected_event->mutable_common()->set_device_user(kDeviceUser);
expected_event->mutable_common()->set_create_timestamp_us(
login_event->common().create_timestamp_us());
EXPECT_THAT(*expected_event, EqualsProto(*login_event));
EXPECT_EQ(AuthFactorType::Authentication_AuthenticationType_AUTH_TYPE_UNKNOWN,
GetAuthFactor());
// Logoff.
expected_event->Clear();
state_changed_cb_.Run(kStopped);
expected_event->mutable_logoff();
expected_event->mutable_common()->set_create_timestamp_us(
logout_event->common().create_timestamp_us());
expected_event->mutable_common()->set_device_user(kDeviceUser);
EXPECT_THAT(*expected_event, EqualsProto(*logout_event));
}
TEST_F(AuthenticationPluginTestFixture, LateAuthFactor) {
SetupObjectProxies();
SaveRegisterLockingCbs();
SaveSessionStateChangeCb();
EXPECT_CALL(*device_user_, GetDeviceUserAsync)
.Times(2)
.WillRepeatedly(WithArg<0>(Invoke(
[](base::OnceCallback<void(const std::string& device_user)> cb) {
std::move(cb).Run(kDeviceUser);
})));
user_data_auth::AuthenticateAuthFactorCompleted completed;
completed.set_auth_factor_type(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
auto login_event = std::make_unique<pb::UserEventAtomicVariant>();
EXPECT_CALL(*batch_sender_, Enqueue(_))
.Times(1)
.WillOnce(WithArg<0>(
Invoke([&login_event](
std::unique_ptr<google::protobuf::MessageLite> message) {
login_event->ParseFromString(
std::move(message->SerializeAsString()));
})));
EXPECT_TRUE(plugin_->Activate().ok());
// Have the state change cb run first to simulate late signal.
state_changed_cb_.Run(kStarted);
auth_factor_cb_.Run(completed);
task_environment_.FastForwardBy(kWaitForAuthFactorS);
ASSERT_EQ(1, login_event->logon().authentication().auth_factor_size());
EXPECT_EQ(AuthFactorType::Authentication_AuthenticationType_AUTH_PASSWORD,
login_event->logon().authentication().auth_factor()[0]);
EXPECT_EQ(AuthFactorType::Authentication_AuthenticationType_AUTH_TYPE_UNKNOWN,
GetAuthFactor());
EXPECT_EQ(kDeviceUser, login_event->common().device_user());
// Unlock.
auto unlock_event = std::make_unique<pb::UserEventAtomicVariant>();
EXPECT_CALL(*batch_sender_, Enqueue(_))
.Times(1)
.WillOnce(WithArg<0>(
Invoke([&unlock_event](
std::unique_ptr<google::protobuf::MessageLite> message) {
unlock_event->ParseFromString(
std::move(message->SerializeAsString()));
})));
// Have the state change cb run first to simulate late signal.
unlocked_cb_.Run();
auth_factor_cb_.Run(completed);
task_environment_.FastForwardBy(kWaitForAuthFactorS);
ASSERT_EQ(1, unlock_event->unlock().authentication().auth_factor_size());
EXPECT_EQ(AuthFactorType::Authentication_AuthenticationType_AUTH_PASSWORD,
unlock_event->unlock().authentication().auth_factor()[0]);
EXPECT_EQ(AuthFactorType::Authentication_AuthenticationType_AUTH_TYPE_UNKNOWN,
GetAuthFactor());
EXPECT_EQ(kDeviceUser, unlock_event->common().device_user());
}
TEST_F(AuthenticationPluginTestFixture, FailedLoginThenSuccess) {
SetupObjectProxies();
SaveRegisterLockingCbs();
SaveSessionStateChangeCb();
EXPECT_CALL(*device_user_, GetDeviceUserAsync)
.WillOnce(WithArg<0>(Invoke(
[](base::OnceCallback<void(const std::string& device_user)> cb) {
std::move(cb).Run(kDeviceUser);
})));
user_data_auth::AuthenticateAuthFactorCompleted failure;
failure.mutable_error_info();
failure.set_auth_factor_type(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
failure.set_username(kDeviceUser);
failure.set_sanitized_username(kSanitized);
auto failure_event = std::make_unique<pb::UserEventAtomicVariant>();
auto login_event = std::make_unique<pb::UserEventAtomicVariant>();
EXPECT_CALL(*batch_sender_, Enqueue(_))
.Times(2)
.WillOnce(WithArg<0>(
Invoke([&failure_event](
std::unique_ptr<google::protobuf::MessageLite> message) {
failure_event->ParseFromString(
std::move(message->SerializeAsString()));
})))
.WillOnce(WithArg<0>(
Invoke([&login_event](
std::unique_ptr<google::protobuf::MessageLite> message) {
login_event->ParseFromString(
std::move(message->SerializeAsString()));
})));
EXPECT_CALL(*batch_sender_,
Visit(Eq(pb::UserEventAtomicVariant::kFailure), _, _))
.WillOnce(Return(false))
.WillOnce([&failure_event](auto unused_type, const auto& unused_key,
BatchSenderType::VisitCallback cb) {
std::move(cb).Run(failure_event.get());
return true;
});
EXPECT_CALL(*device_user_,
GetUsernameBasedOnAffiliation(kDeviceUser, kSanitized))
.WillOnce(Return(kDeviceUser));
EXPECT_TRUE(plugin_->Activate().ok());
// 2 Failures.
auth_factor_cb_.Run(failure);
auth_factor_cb_.Run(failure);
// Success.
user_data_auth::AuthenticateAuthFactorCompleted completed;
completed.set_auth_factor_type(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
auth_factor_cb_.Run(completed);
state_changed_cb_.Run(kStarted);
// Failure.
ASSERT_EQ(1, failure_event->failure().authentication().auth_factor_size());
EXPECT_EQ(AuthFactorType::Authentication_AuthenticationType_AUTH_PASSWORD,
failure_event->failure().authentication().auth_factor()[0]);
EXPECT_EQ(2, failure_event->failure().authentication().num_failed_attempts());
EXPECT_EQ(kDeviceUser, failure_event->common().device_user());
// Success.
ASSERT_EQ(1, login_event->logon().authentication().auth_factor_size());
EXPECT_EQ(AuthFactorType::Authentication_AuthenticationType_AUTH_PASSWORD,
login_event->logon().authentication().auth_factor()[0]);
EXPECT_EQ(AuthFactorType::Authentication_AuthenticationType_AUTH_TYPE_UNKNOWN,
GetAuthFactor());
EXPECT_EQ(kDeviceUser, login_event->common().device_user());
}
// When a pin is incorrectly entered two Auth signals are sent on the
// lockscreen. One trying the pin and one trying the password. In this case
// ignore the password and keep the auth_factor as pin.
// TODO(b:305093271): Update logic to handle if password is actually used.
TEST_F(AuthenticationPluginTestFixture, FailedLockscreenLogin) {
SetupObjectProxies();
SaveRegisterLockingCbs();
SaveSessionStateChangeCb();
EXPECT_CALL(*device_user_, GetDeviceUserAsync)
.WillOnce(WithArg<0>(Invoke(
[](base::OnceCallback<void(const std::string& device_user)> cb) {
std::move(cb).Run(kDeviceUser);
})));
user_data_auth::AuthenticateAuthFactorCompleted failure_pin;
failure_pin.mutable_error_info();
failure_pin.set_auth_factor_type(user_data_auth::AUTH_FACTOR_TYPE_PIN);
failure_pin.set_username(kDeviceUser);
failure_pin.set_sanitized_username(kSanitized);
user_data_auth::AuthenticateAuthFactorCompleted failure_pass;
failure_pass.mutable_error_info();
failure_pass.set_auth_factor_type(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
failure_pass.set_username(kDeviceUser);
failure_pass.set_sanitized_username(kSanitized);
auto failure_event_1 = std::make_unique<pb::UserEventAtomicVariant>();
auto failure_event_2 = std::make_unique<pb::UserEventAtomicVariant>();
auto login_event = std::make_unique<pb::UserEventAtomicVariant>();
EXPECT_CALL(*batch_sender_, Enqueue(_))
.Times(3)
.WillOnce(WithArg<0>(
Invoke([&failure_event_1](
std::unique_ptr<google::protobuf::MessageLite> message) {
failure_event_1->ParseFromString(
std::move(message->SerializeAsString()));
})))
.WillOnce(WithArg<0>(
Invoke([&failure_event_2](
std::unique_ptr<google::protobuf::MessageLite> message) {
failure_event_2->ParseFromString(
std::move(message->SerializeAsString()));
})))
.WillOnce(WithArg<0>(
Invoke([&login_event](
std::unique_ptr<google::protobuf::MessageLite> message) {
login_event->ParseFromString(
std::move(message->SerializeAsString()));
})));
EXPECT_CALL(*batch_sender_,
Visit(Eq(pb::UserEventAtomicVariant::kFailure), _, _))
.WillOnce(Return(false))
.WillOnce([&failure_event_1](auto unused_type, const auto& unused_key,
BatchSenderType::VisitCallback cb) {
std::move(cb).Run(failure_event_1.get());
return true;
})
.WillOnce(Return(false))
.WillRepeatedly([&failure_event_2](auto unused_type,
const auto& unused_key,
BatchSenderType::VisitCallback cb) {
std::move(cb).Run(failure_event_2.get());
return true;
});
EXPECT_CALL(*device_user_,
GetUsernameBasedOnAffiliation(kDeviceUser, kSanitized))
.Times(2)
.WillRepeatedly(Return(kDeviceUser));
EXPECT_TRUE(plugin_->Activate().ok());
// 2 Failures for pin and password.
auth_factor_cb_.Run(failure_pin);
auth_factor_cb_.Run(failure_pass);
auth_factor_cb_.Run(failure_pin);
auth_factor_cb_.Run(failure_pass);
// 3 more password failures for when pin entry is disabled.
auth_factor_cb_.Run(failure_pass);
auth_factor_cb_.Run(failure_pass);
auth_factor_cb_.Run(failure_pass);
// Success.
user_data_auth::AuthenticateAuthFactorCompleted completed;
completed.set_auth_factor_type(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
auth_factor_cb_.Run(completed);
state_changed_cb_.Run(kStarted);
// Failure 1.
ASSERT_EQ(1, failure_event_1->failure().authentication().auth_factor_size());
EXPECT_EQ(AuthFactorType::Authentication_AuthenticationType_AUTH_PIN,
failure_event_1->failure().authentication().auth_factor()[0]);
EXPECT_EQ(2,
failure_event_1->failure().authentication().num_failed_attempts());
EXPECT_EQ(kDeviceUser, failure_event_1->common().device_user());
// Failure 2.
ASSERT_EQ(1, failure_event_2->failure().authentication().auth_factor_size());
EXPECT_EQ(AuthFactorType::Authentication_AuthenticationType_AUTH_PASSWORD,
failure_event_2->failure().authentication().auth_factor()[0]);
EXPECT_EQ(3,
failure_event_2->failure().authentication().num_failed_attempts());
EXPECT_EQ(kDeviceUser, failure_event_2->common().device_user());
// Success.
ASSERT_EQ(1, login_event->logon().authentication().auth_factor_size());
EXPECT_EQ(AuthFactorType::Authentication_AuthenticationType_AUTH_PASSWORD,
login_event->logon().authentication().auth_factor()[0]);
EXPECT_EQ(AuthFactorType::Authentication_AuthenticationType_AUTH_TYPE_UNKNOWN,
GetAuthFactor());
EXPECT_EQ(kDeviceUser, login_event->common().device_user());
}
TEST_F(AuthenticationPluginTestFixture, FailedLockscreenDelayBetweenAttempts) {
SetupObjectProxies();
SaveRegisterLockingCbs();
SaveSessionStateChangeCb();
user_data_auth::AuthenticateAuthFactorCompleted failure_pin;
failure_pin.mutable_error_info();
failure_pin.set_auth_factor_type(user_data_auth::AUTH_FACTOR_TYPE_PIN);
failure_pin.set_username(kDeviceUser);
failure_pin.set_sanitized_username(kSanitized);
user_data_auth::AuthenticateAuthFactorCompleted failure_pass;
failure_pass.mutable_error_info();
failure_pass.set_auth_factor_type(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
failure_pass.set_username(kDeviceUser);
failure_pass.set_sanitized_username(kSanitized);
auto failure_event_1 = std::make_unique<pb::UserEventAtomicVariant>();
auto failure_event_2 = std::make_unique<pb::UserEventAtomicVariant>();
EXPECT_CALL(*batch_sender_, Enqueue(_))
.Times(2)
.WillOnce(WithArg<0>(
Invoke([&failure_event_1](
std::unique_ptr<google::protobuf::MessageLite> message) {
failure_event_1->ParseFromString(
std::move(message->SerializeAsString()));
})))
.WillOnce(WithArg<0>(
Invoke([&failure_event_2](
std::unique_ptr<google::protobuf::MessageLite> message) {
failure_event_2->ParseFromString(
std::move(message->SerializeAsString()));
})));
EXPECT_CALL(*batch_sender_,
Visit(Eq(pb::UserEventAtomicVariant::kFailure), _, _))
.WillOnce(Return(false))
.WillOnce([&failure_event_1](auto unused_type, const auto& unused_key,
BatchSenderType::VisitCallback cb) {
std::move(cb).Run(failure_event_1.get());
return true;
})
.WillOnce(Return(false))
.WillRepeatedly([&failure_event_2](auto unused_type,
const auto& unused_key,
BatchSenderType::VisitCallback cb) {
std::move(cb).Run(failure_event_2.get());
return true;
});
EXPECT_CALL(*device_user_,
GetUsernameBasedOnAffiliation(kDeviceUser, kSanitized))
.Times(2)
.WillRepeatedly(Return(kDeviceUser));
EXPECT_TRUE(plugin_->Activate().ok());
// 2 pin failures for first event.
auth_factor_cb_.Run(failure_pin);
auth_factor_cb_.Run(failure_pin);
// 3 password failures for second event after delay.
task_environment_.FastForwardBy(
base::Seconds(kMaxDelayForLockscreenAttemptsS + 1));
auth_factor_cb_.Run(failure_pass);
auth_factor_cb_.Run(failure_pass);
auth_factor_cb_.Run(failure_pass);
// Failure 1.
ASSERT_EQ(1, failure_event_1->failure().authentication().auth_factor_size());
EXPECT_EQ(AuthFactorType::Authentication_AuthenticationType_AUTH_PIN,
failure_event_1->failure().authentication().auth_factor()[0]);
EXPECT_EQ(2,
failure_event_1->failure().authentication().num_failed_attempts());
EXPECT_EQ(kDeviceUser, failure_event_1->common().device_user());
// Failure 2.
ASSERT_EQ(1, failure_event_2->failure().authentication().auth_factor_size());
EXPECT_EQ(AuthFactorType::Authentication_AuthenticationType_AUTH_PASSWORD,
failure_event_2->failure().authentication().auth_factor()[0]);
EXPECT_EQ(3,
failure_event_2->failure().authentication().num_failed_attempts());
EXPECT_EQ(kDeviceUser, failure_event_2->common().device_user());
}
TEST_F(AuthenticationPluginTestFixture, FailedLoginAfterBatchTimeout) {
SetupObjectProxies();
SaveRegisterLockingCbs();
SaveSessionStateChangeCb();
user_data_auth::AuthenticateAuthFactorCompleted failure;
failure.mutable_error_info();
failure.set_auth_factor_type(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
failure.set_username(kDeviceUser);
failure.set_sanitized_username(kSanitized);
auto failure_event = std::make_unique<pb::UserEventAtomicVariant>();
failure_event->mutable_common()->set_create_timestamp_us(5);
EXPECT_CALL(*batch_sender_, Enqueue(_))
.Times(1)
.WillOnce(WithArg<0>(
Invoke([&failure_event](
std::unique_ptr<google::protobuf::MessageLite> message) {
failure_event->ParseFromString(
std::move(message->SerializeAsString()));
})));
EXPECT_CALL(*batch_sender_,
Visit(Eq(pb::UserEventAtomicVariant::kFailure), _, _))
.WillOnce(Return(false))
.WillRepeatedly([&failure_event](auto unused_type, const auto& unused_key,
BatchSenderType::VisitCallback cb) {
std::move(cb).Run(failure_event.get());
return true;
});
EXPECT_CALL(*device_user_,
GetUsernameBasedOnAffiliation(kDeviceUser, kSanitized))
.WillOnce(Return(kDeviceUser));
EXPECT_TRUE(plugin_->Activate().ok());
// 3 Failures.
auth_factor_cb_.Run(failure);
auth_factor_cb_.Run(failure);
auth_factor_cb_.Run(failure);
// Failure.
ASSERT_EQ(1, failure_event->failure().authentication().auth_factor_size());
EXPECT_EQ(AuthFactorType::Authentication_AuthenticationType_AUTH_PASSWORD,
failure_event->failure().authentication().auth_factor()[0]);
EXPECT_EQ(3, failure_event->failure().authentication().num_failed_attempts());
EXPECT_EQ(kDeviceUser, failure_event->common().device_user());
}
TEST_F(AuthenticationPluginTestFixture, FailedLoginCreateTimestampSquashing) {
SetupObjectProxies();
SaveRegisterLockingCbs();
SaveSessionStateChangeCb();
EXPECT_CALL(*device_user_, GetDeviceUserAsync)
.WillOnce(WithArg<0>(Invoke(
[](base::OnceCallback<void(const std::string& device_user)> cb) {
std::move(cb).Run(kDeviceUser);
})));
user_data_auth::AuthenticateAuthFactorCompleted failure;
failure.mutable_error_info();
failure.set_auth_factor_type(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
failure.set_username(kDeviceUser);
failure.set_sanitized_username(kSanitized);
auto failure_event_1 = std::make_unique<pb::UserEventAtomicVariant>();
auto failure_event_2 = std::make_unique<pb::UserEventAtomicVariant>();
auto login_event = std::make_unique<pb::UserEventAtomicVariant>();
EXPECT_CALL(*batch_sender_, Enqueue(_))
.Times(3)
.WillOnce(WithArg<0>(
Invoke([&failure_event_1](
std::unique_ptr<google::protobuf::MessageLite> message) {
failure_event_1->ParseFromString(
std::move(message->SerializeAsString()));
})))
.WillOnce(WithArg<0>(
Invoke([&login_event](
std::unique_ptr<google::protobuf::MessageLite> message) {
login_event->ParseFromString(
std::move(message->SerializeAsString()));
})))
.WillOnce(WithArg<0>(
Invoke([&failure_event_2](
std::unique_ptr<google::protobuf::MessageLite> message) {
failure_event_2->ParseFromString(
std::move(message->SerializeAsString()));
})));
EXPECT_CALL(*batch_sender_,
Visit(Eq(pb::UserEventAtomicVariant::kFailure), _, _))
.Times(3)
.WillOnce(Return(false))
.WillOnce([&failure_event_1](auto unused_type, const auto& unused_key,
BatchSenderType::VisitCallback cb) {
return std::move(cb).Run(failure_event_1.get());
})
.WillOnce([&failure_event_2](auto unused_type, const auto& unused_key,
BatchSenderType::VisitCallback cb) {
return std::move(cb).Run(failure_event_2.get());
});
EXPECT_CALL(*device_user_,
GetUsernameBasedOnAffiliation(kDeviceUser, kSanitized))
.Times(2)
.WillRepeatedly(Return(kDeviceUser));
EXPECT_TRUE(plugin_->Activate().ok());
// 2 Failures.
auth_factor_cb_.Run(failure);
auth_factor_cb_.Run(failure);
// Successful login.
user_data_auth::AuthenticateAuthFactorCompleted completed;
completed.set_auth_factor_type(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
auth_factor_cb_.Run(completed);
state_changed_cb_.Run(kStarted);
// Another failure but timestamp is after successful login.
auth_factor_cb_.Run(failure);
// Failure 1.
ASSERT_EQ(1, failure_event_1->failure().authentication().auth_factor_size());
EXPECT_EQ(AuthFactorType::Authentication_AuthenticationType_AUTH_PASSWORD,
failure_event_1->failure().authentication().auth_factor()[0]);
EXPECT_EQ(2,
failure_event_1->failure().authentication().num_failed_attempts());
EXPECT_EQ(kDeviceUser, failure_event_1->common().device_user());
// Success.
ASSERT_EQ(1, login_event->logon().authentication().auth_factor_size());
EXPECT_EQ(AuthFactorType::Authentication_AuthenticationType_AUTH_PASSWORD,
login_event->logon().authentication().auth_factor()[0]);
EXPECT_EQ(kDeviceUser, login_event->common().device_user());
// Failure 2.
ASSERT_EQ(1, failure_event_2->failure().authentication().auth_factor_size());
EXPECT_EQ(AuthFactorType::Authentication_AuthenticationType_AUTH_PASSWORD,
failure_event_2->failure().authentication().auth_factor()[0]);
EXPECT_EQ(1,
failure_event_2->failure().authentication().num_failed_attempts());
EXPECT_EQ(kDeviceUser, failure_event_2->common().device_user());
}
TEST_F(AuthenticationPluginTestFixture, FailedLoginAuthFactorSquashing) {
SetupObjectProxies();
SaveRegisterLockingCbs();
SaveSessionStateChangeCb();
user_data_auth::AuthenticateAuthFactorCompleted failure;
failure.mutable_error_info();
failure.set_auth_factor_type(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
failure.set_username(kDeviceUser);
failure.set_sanitized_username(kSanitized);
auto failure_event_1 = std::make_unique<pb::UserEventAtomicVariant>();
auto failure_event_2 = std::make_unique<pb::UserEventAtomicVariant>();
EXPECT_CALL(*batch_sender_, Enqueue(_))
.Times(2)
.WillOnce(WithArg<0>(
Invoke([&failure_event_1](
std::unique_ptr<google::protobuf::MessageLite> message) {
failure_event_1->ParseFromString(
std::move(message->SerializeAsString()));
})))
.WillOnce(WithArg<0>(
Invoke([&failure_event_2](
std::unique_ptr<google::protobuf::MessageLite> message) {
failure_event_2->ParseFromString(
std::move(message->SerializeAsString()));
})));
EXPECT_CALL(*batch_sender_,
Visit(Eq(pb::UserEventAtomicVariant::kFailure), _, _))
.Times(3)
.WillOnce(Return(false))
.WillRepeatedly([&failure_event_1](auto unused_type,
const auto& unused_key,
BatchSenderType::VisitCallback cb) {
return std::move(cb).Run(failure_event_1.get());
});
EXPECT_CALL(*device_user_,
GetUsernameBasedOnAffiliation(kDeviceUser, kSanitized))
.Times(2)
.WillRepeatedly(Return(kDeviceUser));
EXPECT_TRUE(plugin_->Activate().ok());
// 2 Failures of same type.
auth_factor_cb_.Run(failure);
auth_factor_cb_.Run(failure);
// 1 failure with different auth type.
failure.set_auth_factor_type(user_data_auth::AUTH_FACTOR_TYPE_PIN);
auth_factor_cb_.Run(failure);
// Failure 1.
ASSERT_EQ(1, failure_event_1->failure().authentication().auth_factor_size());
EXPECT_EQ(AuthFactorType::Authentication_AuthenticationType_AUTH_PASSWORD,
failure_event_1->failure().authentication().auth_factor()[0]);
EXPECT_EQ(2,
failure_event_1->failure().authentication().num_failed_attempts());
EXPECT_EQ(kDeviceUser, failure_event_1->common().device_user());
// Failure 2.
ASSERT_EQ(1, failure_event_2->failure().authentication().auth_factor_size());
EXPECT_EQ(AuthFactorType::Authentication_AuthenticationType_AUTH_PIN,
failure_event_2->failure().authentication().auth_factor()[0]);
EXPECT_EQ(1,
failure_event_2->failure().authentication().num_failed_attempts());
EXPECT_EQ(kDeviceUser, failure_event_2->common().device_user());
}
TEST_F(AuthenticationPluginTestFixture, TestSecagentdRestart) {
SetupObjectProxies();
SaveRegisterLockingCbs();
SaveSessionStateChangeCb();
EXPECT_CALL(*device_user_, GetDeviceUserAsync)
.Times(3)
.WillOnce(WithArg<0>(Invoke(
[](base::OnceCallback<void(const std::string& device_user)> cb) {
std::move(cb).Run(device_user::kEmpty);
})))
.WillRepeatedly(WithArg<0>(Invoke(
[](base::OnceCallback<void(const std::string& device_user)> cb) {
std::move(cb).Run(kDeviceUser);
})));
// batch_sender_ will be called twice. Once for login, once for logout.
auto login_event = std::make_unique<pb::UserEventAtomicVariant>();
EXPECT_CALL(*batch_sender_, Enqueue(_))
.Times(1)
.WillOnce(WithArg<0>(
Invoke([&login_event](
std::unique_ptr<google::protobuf::MessageLite> message) {
login_event->ParseFromString(
std::move(message->SerializeAsString()));
})));
EXPECT_TRUE(plugin_->Activate().ok());
// First one should do nothing because device user is empty.
state_changed_cb_.Run(kInit);
state_changed_cb_.Run(kInit);
task_environment_.FastForwardBy(kWaitForAuthFactorS);
auto expected_event = std::make_unique<pb::UserEventAtomicVariant>();
expected_event->mutable_common();
expected_event->mutable_logon()->mutable_authentication()->add_auth_factor(
AuthFactorType::Authentication_AuthenticationType_AUTH_TYPE_UNKNOWN);
expected_event->mutable_common()->set_device_user(kDeviceUser);
expected_event->mutable_common()->set_create_timestamp_us(
login_event->common().create_timestamp_us());
EXPECT_THAT(*expected_event, EqualsProto(*login_event));
EXPECT_EQ(AuthFactorType::Authentication_AuthenticationType_AUTH_TYPE_UNKNOWN,
GetAuthFactor());
}
TEST_F(AuthenticationPluginTestFixture, TestUserCreation) {
SetupObjectProxies();
SaveRegisterLockingCbs();
SaveSessionStateChangeCb();
EXPECT_CALL(*device_user_, GetDeviceUserAsync)
.Times(1)
.WillOnce(WithArg<0>(Invoke(
[](base::OnceCallback<void(const std::string& device_user)> cb) {
std::move(cb).Run(kDeviceUser);
})));
user_data_auth::AuthenticateAuthFactorCompleted completed;
completed.set_auth_factor_type(user_data_auth::AUTH_FACTOR_TYPE_UNSPECIFIED);
completed.set_user_creation(true);
auto login_event = std::make_unique<pb::UserEventAtomicVariant>();
EXPECT_CALL(*batch_sender_, Enqueue(_))
.Times(1)
.WillOnce(WithArg<0>(
Invoke([&login_event](
std::unique_ptr<google::protobuf::MessageLite> message) {
login_event->ParseFromString(
std::move(message->SerializeAsString()));
})));
EXPECT_TRUE(plugin_->Activate().ok());
auth_factor_cb_.Run(completed);
state_changed_cb_.Run(kStarted);
auto expected_event = std::make_unique<pb::UserEventAtomicVariant>();
expected_event->mutable_common();
expected_event->mutable_logon()->mutable_authentication()->add_auth_factor(
AuthFactorType::Authentication_AuthenticationType_AUTH_NEW_USER);
expected_event->mutable_common()->set_device_user(kDeviceUser);
expected_event->mutable_common()->set_create_timestamp_us(
login_event->common().create_timestamp_us());
EXPECT_THAT(*expected_event, EqualsProto(*login_event));
EXPECT_EQ(AuthFactorType::Authentication_AuthenticationType_AUTH_TYPE_UNKNOWN,
GetAuthFactor());
}
} // namespace secagentd::testing