blob: b016ad20811b18b223a5c68bd6a5c3b151419157 [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cryptohome/user_session/user_session_map.h"
#include <memory>
#include <utility>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "cryptohome/mock_credential_verifier.h"
#include "cryptohome/user_session/mock_user_session.h"
#include "cryptohome/user_session/user_session.h"
namespace cryptohome {
namespace {
using ::testing::IsNull;
using ::testing::IsTrue;
using ::testing::Not;
using ::testing::Pair;
using ::testing::UnorderedElementsAre;
// Helper utility for making a stub verifier with a given label. Returns a
// "unique_ptr, ptr" pair so that tests that need to hand over ownership but
// hold on to a pointer can easily do so. Usually used as:
//
// auto [verifier, ptr] = MakeTestVerifier("label")
std::pair<std::unique_ptr<CredentialVerifier>, CredentialVerifier*>
MakeTestVerifier(std::string label) {
auto owned_ptr = std::make_unique<MockCredentialVerifier>(
AuthFactorType::kPassword, std::move(label),
AuthFactorMetadata{.metadata = PasswordAuthFactorMetadata()});
auto* unowned_ptr = owned_ptr.get();
return {std::move(owned_ptr), unowned_ptr};
}
std::vector<std::pair<Username, const UserSession*>> GetSessionItems(
const UserSessionMap& session_map) {
std::vector<std::pair<Username, const UserSession*>> items;
for (const auto& [account_id, session] : session_map) {
items.emplace_back(account_id, &session);
}
return items;
}
class UserSessionMapTest : public testing::Test {
protected:
// Constants for use in testing. These are non-static because Username cannot
// be safely used as constexpr, but they are logically global constants.
const Username kUsername1{"foo1@bar.com"};
const Username kUsername2{"foo2@bar.com"};
// Returns a const-ref to the test object. Used for testing const methods.
const UserSessionMap& const_session_map() const { return session_map_; }
UserSessionMap session_map_;
};
TEST_F(UserSessionMapTest, InitialEmpty) {
EXPECT_TRUE(session_map_.empty());
EXPECT_EQ(session_map_.size(), 0);
EXPECT_EQ(session_map_.begin(), session_map_.end());
EXPECT_EQ(session_map_.Find(kUsername1), nullptr);
EXPECT_EQ(session_map_.Find(kUsername2), nullptr);
EXPECT_EQ(const_session_map().Find(kUsername1), nullptr);
EXPECT_EQ(const_session_map().Find(kUsername2), nullptr);
}
TEST_F(UserSessionMapTest, AddOne) {
auto session = std::make_unique<MockUserSession>();
const UserSession* session_ptr = session.get();
EXPECT_TRUE(session_map_.Add(kUsername1, std::move(session)));
EXPECT_FALSE(session_map_.empty());
EXPECT_EQ(session_map_.size(), 1);
EXPECT_THAT(GetSessionItems(session_map_),
UnorderedElementsAre(Pair(kUsername1, session_ptr)));
EXPECT_EQ(session_map_.Find(kUsername1), session_ptr);
EXPECT_EQ(session_map_.Find(kUsername2), nullptr);
EXPECT_EQ(const_session_map().Find(kUsername1), session_ptr);
EXPECT_EQ(const_session_map().Find(kUsername2), nullptr);
}
TEST_F(UserSessionMapTest, AddTwo) {
auto session1 = std::make_unique<MockUserSession>();
const UserSession* session1_ptr = session1.get();
auto session2 = std::make_unique<MockUserSession>();
const UserSession* session2_ptr = session2.get();
EXPECT_TRUE(session_map_.Add(kUsername1, std::move(session1)));
EXPECT_TRUE(session_map_.Add(kUsername2, std::move(session2)));
EXPECT_FALSE(session_map_.empty());
EXPECT_EQ(session_map_.size(), 2);
EXPECT_THAT(GetSessionItems(session_map_),
UnorderedElementsAre(Pair(kUsername1, session1_ptr),
Pair(kUsername2, session2_ptr)));
EXPECT_EQ(session_map_.Find(kUsername1), session1_ptr);
EXPECT_EQ(session_map_.Find(kUsername2), session2_ptr);
EXPECT_EQ(const_session_map().Find(kUsername1), session1_ptr);
EXPECT_EQ(const_session_map().Find(kUsername2), session2_ptr);
}
TEST_F(UserSessionMapTest, AddDuplicate) {
auto session1 = std::make_unique<MockUserSession>();
const UserSession* session1_ptr = session1.get();
EXPECT_TRUE(session_map_.Add(kUsername1, std::move(session1)));
EXPECT_FALSE(
session_map_.Add(kUsername1, std::make_unique<MockUserSession>()));
EXPECT_EQ(session_map_.size(), 1);
EXPECT_EQ(session_map_.Find(kUsername1), session1_ptr);
}
TEST_F(UserSessionMapTest, RemoveSingle) {
EXPECT_TRUE(
session_map_.Add(kUsername1, std::make_unique<MockUserSession>()));
EXPECT_TRUE(session_map_.Remove(kUsername1));
EXPECT_EQ(session_map_.size(), 0);
EXPECT_EQ(session_map_.Find(kUsername1), nullptr);
EXPECT_EQ(const_session_map().Find(kUsername1), nullptr);
}
TEST_F(UserSessionMapTest, RemoveWhenEmpty) {
EXPECT_FALSE(session_map_.Remove(kUsername1));
EXPECT_EQ(session_map_.size(), 0);
EXPECT_EQ(session_map_.Find(kUsername1), nullptr);
}
TEST_F(UserSessionMapTest, RemoveNonExisting) {
auto session = std::make_unique<MockUserSession>();
const UserSession* session_ptr = session.get();
EXPECT_TRUE(session_map_.Add(kUsername1, std::move(session)));
EXPECT_FALSE(session_map_.Remove(kUsername2));
EXPECT_EQ(session_map_.size(), 1);
EXPECT_EQ(session_map_.Find(kUsername1), session_ptr);
EXPECT_EQ(session_map_.Find(kUsername2), nullptr);
}
TEST_F(UserSessionMapTest, RemoveTwice) {
EXPECT_TRUE(
session_map_.Add(kUsername1, std::make_unique<MockUserSession>()));
EXPECT_TRUE(session_map_.Remove(kUsername1));
EXPECT_FALSE(session_map_.Remove(kUsername1));
EXPECT_EQ(session_map_.size(), 0);
EXPECT_EQ(session_map_.Find(kUsername1), nullptr);
}
// Use VerifierForwarder to add multiple verifiers before a user session gets
// created and check that they get picked up by the session after it's created.
TEST_F(UserSessionMapTest, AddVerifiersBeforeSession) {
static constexpr char kLabel1[] = "primary-pass";
static constexpr char kLabel2[] = "secondary-pass";
auto [verifier1, ptr1] = MakeTestVerifier(kLabel1);
auto [verifier2, ptr2] = MakeTestVerifier(kLabel2);
auto [verifier3, ptr3] = MakeTestVerifier(kLabel1); // For second user.
// Create forwarders and give them verifiers.
auto forwarder1 = std::make_unique<UserSessionMap::VerifierForwarder>(
kUsername1, &session_map_);
auto forwarder2 = std::make_unique<UserSessionMap::VerifierForwarder>(
kUsername2, &session_map_);
forwarder1->AddVerifier(std::move(verifier1));
forwarder1->AddVerifier(std::move(verifier2));
forwarder2->AddVerifier(std::move(verifier3));
// Confirm that the user sessions don't exist.
ASSERT_THAT(session_map_.Find(kUsername1), IsNull());
ASSERT_THAT(session_map_.Find(kUsername2), IsNull());
// Create the users, they should get the verifiers from the forwarders.
EXPECT_THAT(session_map_.Add(kUsername1, std::make_unique<MockUserSession>()),
IsTrue());
EXPECT_THAT(session_map_.Add(kUsername2, std::make_unique<MockUserSession>()),
IsTrue());
auto* user1 = session_map_.Find(kUsername1);
ASSERT_THAT(user1, Not(IsNull()));
EXPECT_THAT(user1->GetCredentialVerifiers(),
UnorderedElementsAre(ptr1, ptr2));
auto* user2 = session_map_.Find(kUsername2);
ASSERT_THAT(user2, Not(IsNull()));
EXPECT_THAT(user2->GetCredentialVerifiers(), UnorderedElementsAre(ptr3));
// Deleting the forwarders should do nothing.
forwarder1 = nullptr;
forwarder2 = nullptr;
EXPECT_THAT(user1->GetCredentialVerifiers(),
UnorderedElementsAre(ptr1, ptr2));
EXPECT_THAT(user2->GetCredentialVerifiers(), UnorderedElementsAre(ptr3));
}
// Use VerifierForwarder to add multiple verifiers after a user session gets
// created and check that they go immediately to the session.
TEST_F(UserSessionMapTest, AddVerifiersAfterSession) {
static constexpr char kLabel1[] = "primary-pass";
static constexpr char kLabel2[] = "secondary-pass";
auto [verifier1, ptr1] = MakeTestVerifier(kLabel1);
auto [verifier2, ptr2] = MakeTestVerifier(kLabel2);
auto [verifier3, ptr3] = MakeTestVerifier(kLabel1); // For second user.
// Create the users.
EXPECT_THAT(session_map_.Add(kUsername1, std::make_unique<MockUserSession>()),
IsTrue());
EXPECT_THAT(session_map_.Add(kUsername2, std::make_unique<MockUserSession>()),
IsTrue());
auto* user1 = session_map_.Find(kUsername1);
ASSERT_THAT(user1, Not(IsNull()));
auto* user2 = session_map_.Find(kUsername2);
ASSERT_THAT(user2, Not(IsNull()));
// Create forwarders and give them verifiers.
auto forwarder1 = std::make_unique<UserSessionMap::VerifierForwarder>(
kUsername1, &session_map_);
auto forwarder2 = std::make_unique<UserSessionMap::VerifierForwarder>(
kUsername2, &session_map_);
forwarder1->AddVerifier(std::move(verifier1));
forwarder1->AddVerifier(std::move(verifier2));
forwarder2->AddVerifier(std::move(verifier3));
// The sessions should have the verifiers.
EXPECT_THAT(user1->GetCredentialVerifiers(),
UnorderedElementsAre(ptr1, ptr2));
EXPECT_THAT(user2->GetCredentialVerifiers(), UnorderedElementsAre(ptr3));
// Deleting the forwarders should do nothing.
forwarder1 = nullptr;
forwarder2 = nullptr;
EXPECT_THAT(user1->GetCredentialVerifiers(),
UnorderedElementsAre(ptr1, ptr2));
EXPECT_THAT(user2->GetCredentialVerifiers(), UnorderedElementsAre(ptr3));
}
// Use VerifierForwarder to add multiple verifiers for the same user after the
// session ends. In particular, this test checks that forwarders don't have
// dangling pointers on the session.
TEST_F(UserSessionMapTest, AddVerifiersAfterSessionEndViaCoincidingForwarders) {
constexpr char kLabel1[] = "primary-pass";
constexpr char kLabel2[] = "secondary-pass";
// Arrange: create session, forwarders and then destroy the session.
EXPECT_THAT(session_map_.Add(kUsername1, std::make_unique<MockUserSession>()),
IsTrue());
UserSessionMap::VerifierForwarder forwarder1(kUsername1, &session_map_);
UserSessionMap::VerifierForwarder forwarder2(kUsername1, &session_map_);
EXPECT_THAT(session_map_.Remove(kUsername1), IsTrue());
// Act: add verifiers and then create new session.
auto [verifier1, ptr1] = MakeTestVerifier(kLabel1);
forwarder1.AddVerifier(std::move(verifier1));
auto [verifier2, ptr2] = MakeTestVerifier(kLabel2);
forwarder2.AddVerifier(std::move(verifier2));
EXPECT_THAT(session_map_.Add(kUsername1, std::make_unique<MockUserSession>()),
IsTrue());
// Assert: the session should have verifiers from both forwarders.
auto* user = session_map_.Find(kUsername1);
ASSERT_THAT(user, Not(IsNull()));
EXPECT_THAT(user->GetCredentialVerifiers(), UnorderedElementsAre(ptr1, ptr2));
}
// Use VerifierForwarder to add verifiers both before and after a session is
// created.
TEST_F(UserSessionMapTest, AddVerifiersBeforeAndAfterSession) {
static constexpr char kLabel1[] = "primary-pass";
static constexpr char kLabel2[] = "secondary-pass";
auto [verifier1, ptr1] = MakeTestVerifier(kLabel1);
auto [verifier2, ptr2] = MakeTestVerifier(kLabel2);
// Create a forwarder and give it a verifier.
auto forwarder = std::make_unique<UserSessionMap::VerifierForwarder>(
kUsername1, &session_map_);
forwarder->AddVerifier(std::move(verifier1));
// Confirm that the user session doesn't exist.
ASSERT_THAT(session_map_.Find(kUsername1), IsNull());
// Create the user and add the other verifier. It should end up with both.
EXPECT_THAT(session_map_.Add(kUsername1, std::make_unique<MockUserSession>()),
IsTrue());
auto* user = session_map_.Find(kUsername1);
ASSERT_THAT(user, Not(IsNull()));
forwarder->AddVerifier(std::move(verifier2));
EXPECT_THAT(user->GetCredentialVerifiers(), UnorderedElementsAre(ptr1, ptr2));
// Deleting the forwarder should do nothing.
forwarder = nullptr;
EXPECT_THAT(user->GetCredentialVerifiers(), UnorderedElementsAre(ptr1, ptr2));
}
// Use VerifierForwarder to add verifiers both before and after a session is
// created.
TEST_F(UserSessionMapTest, DetachingVerifiersShouldLoseOldVerifiers) {
static constexpr char kLabel1[] = "primary-pass";
static constexpr char kLabel2[] = "secondary-pass";
auto [verifier1, ptr1] = MakeTestVerifier(kLabel1);
auto [verifier2, ptr2] = MakeTestVerifier(kLabel2);
// Create a forwarder and give it a verifier.
auto forwarder = std::make_unique<UserSessionMap::VerifierForwarder>(
kUsername1, &session_map_);
forwarder->AddVerifier(std::move(verifier1));
// Confirm that the user session doesn't exist.
ASSERT_THAT(session_map_.Find(kUsername1), IsNull());
// Create the user. It should have the verifier.
EXPECT_THAT(session_map_.Add(kUsername1, std::make_unique<MockUserSession>()),
IsTrue());
auto* user = session_map_.Find(kUsername1);
ASSERT_THAT(user, Not(IsNull()));
EXPECT_THAT(user->GetCredentialVerifiers(), UnorderedElementsAre(ptr1));
// Delete the user, which should detach the verifier.
EXPECT_THAT(session_map_.Remove(kUsername1), IsTrue());
ASSERT_THAT(session_map_.Find(kUsername1), IsNull());
// Add another verifier to the forwarder, and then re-create the user. It
// should have the new verifier but not the old one which should be gone.
forwarder->AddVerifier(std::move(verifier2));
EXPECT_THAT(session_map_.Add(kUsername1, std::make_unique<MockUserSession>()),
IsTrue());
user = session_map_.Find(kUsername1);
ASSERT_THAT(user, Not(IsNull()));
EXPECT_THAT(user->GetCredentialVerifiers(), UnorderedElementsAre(ptr2));
// Deleting the forwarder should do nothing.
forwarder = nullptr;
EXPECT_THAT(user->GetCredentialVerifiers(), UnorderedElementsAre(ptr2));
}
// Check that deleting a forwarder drops any un-forwarded verifiers.
TEST_F(UserSessionMapTest, DeletedForwardersDropsUnforwardedVerifiers) {
static constexpr char kLabel1[] = "primary-pass";
static constexpr char kLabel2[] = "secondary-pass";
auto [verifier1, ptr1] = MakeTestVerifier(kLabel1);
auto [verifier2, ptr2] = MakeTestVerifier(kLabel2);
// Create a forwarder and give it a verifier.
auto forwarder = std::make_unique<UserSessionMap::VerifierForwarder>(
kUsername1, &session_map_);
forwarder->AddVerifier(std::move(verifier1));
// Destroy the forwarder and replace it with a fresh one.
forwarder = nullptr;
forwarder = std::make_unique<UserSessionMap::VerifierForwarder>(
kUsername1, &session_map_);
forwarder->AddVerifier(std::move(verifier2));
// Confirm that the user session doesn't exist.
ASSERT_THAT(session_map_.Find(kUsername1), IsNull());
// Create the user, it should get just the verifier from the second forwarder.
EXPECT_THAT(session_map_.Add(kUsername1, std::make_unique<MockUserSession>()),
IsTrue());
auto* user = session_map_.Find(kUsername1);
ASSERT_THAT(user, Not(IsNull()));
EXPECT_THAT(user->GetCredentialVerifiers(), UnorderedElementsAre(ptr2));
// Deleting the forwarder should do nothing.
forwarder = nullptr;
EXPECT_THAT(user->GetCredentialVerifiers(), UnorderedElementsAre(ptr2));
}
} // namespace
} // namespace cryptohome