blob: 0bf12bc6a797a0757ae2d55d772cefab6f033c43 [file] [log] [blame]
#pragma once
#include "logging.hpp"
#include "utility.hpp"
#include <boost/asio/ip/address.hpp>
#include <nlohmann/json.hpp>
#include <algorithm>
#include <csignal>
#include <cstdint>
#include <optional>
#include <random>
#include <vector>
namespace persistent_data
{
// entropy: 20 characters, 62 possibilities. log2(62^20) = 119 bits of
// entropy. OWASP recommends at least 64
// https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html#session-id-entropy
constexpr std::size_t sessionTokenSize = 20;
enum class PersistenceType : std::uint8_t
{
TIMEOUT, // User session times out after a predetermined amount of time
SINGLE_REQUEST // User times out once this request is completed.
};
struct UserSession
{
std::string uniqueId;
std::string sessionToken;
std::string username;
std::string csrfToken;
std::optional<std::string> clientId;
std::string clientIp;
std::chrono::time_point<std::chrono::steady_clock> lastUpdated;
PersistenceType persistence{PersistenceType::TIMEOUT};
bool cookieAuth = false;
bool isConfigureSelfOnly = false;
// There are two sources of truth for isConfigureSelfOnly:
// 1. When pamAuthenticateUser() returns PAM_NEW_AUTHTOK_REQD.
// 2. D-Bus User.Manager.GetUserInfo property UserPasswordExpired.
// These should be in sync, but the underlying condition can change at any
// time. For example, a password can expire or be changed outside of
// bmcweb. The value stored here is updated at the start of each
// operation and used as the truth within bmcweb.
/**
* @brief Fills object with data from UserSession's JSON representation
*
* This replaces nlohmann's from_json to ensure no-throw approach
*
* @param[in] j JSON object from which data should be loaded
*
* @return a shared pointer if data has been loaded properly, nullptr
* otherwise
*/
static std::shared_ptr<UserSession> fromJson(const nlohmann::json& j);
};
struct AuthConfigMethods
{
#ifdef BMCWEB_ENABLE_BASIC_AUTHENTICATION
bool basic = true;
#else
bool basic = false;
#endif
#ifdef BMCWEB_ENABLE_SESSION_AUTHENTICATION
bool sessionToken = true;
#else
bool sessionToken = false;
#endif
#ifdef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
bool xtoken = true;
#else
bool xtoken = false;
#endif
#ifdef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
bool cookie = true;
#else
bool cookie = false;
#endif
#ifdef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
bool tls = true;
#else
bool tls = false;
#endif
void fromJson(const nlohmann::json& j);
};
class SessionStore
{
public:
std::shared_ptr<UserSession> generateUserSession(
std::string_view username, const boost::asio::ip::address& clientIp,
const std::optional<std::string>& clientId,
PersistenceType persistence = PersistenceType::TIMEOUT,
bool isConfigureSelfOnly = false);
std::shared_ptr<UserSession> loginSessionByToken(std::string_view token);
std::shared_ptr<UserSession> getSessionByUid(std::string_view uid);
void removeSession(const std::shared_ptr<UserSession>& session);
std::vector<const std::string*> getUniqueIds(
bool getAll = true,
const PersistenceType& type = PersistenceType::SINGLE_REQUEST);
void removeSessionsByUsername(std::string_view username);
void updateAuthMethodsConfig(const AuthConfigMethods& config);
AuthConfigMethods& getAuthMethodsConfig()
{
return authMethodsConfig;
}
bool needsWrite() const
{
return needWrite;
}
int64_t getTimeoutInSeconds() const
{
return std::chrono::seconds(timeoutInSeconds).count();
}
void updateSessionTimeout(std::chrono::seconds newTimeoutInSeconds)
{
timeoutInSeconds = newTimeoutInSeconds;
needWrite = true;
}
static SessionStore& getInstance()
{
static SessionStore sessionStore;
return sessionStore;
}
void applySessionTimeouts();
SessionStore(const SessionStore&) = delete;
SessionStore& operator=(const SessionStore&) = delete;
SessionStore(SessionStore&&) = delete;
SessionStore& operator=(const SessionStore&&) = delete;
~SessionStore() = default;
std::unordered_map<std::string, std::shared_ptr<UserSession>,
std::hash<std::string>,
crow::utility::ConstantTimeCompare>
authTokens;
std::chrono::time_point<std::chrono::steady_clock> lastTimeoutUpdate;
bool needWrite{false};
std::chrono::seconds timeoutInSeconds;
AuthConfigMethods authMethodsConfig;
private:
SessionStore() : timeoutInSeconds(1800)
{}
};
} // namespace persistent_data