| #pragma once | |
| #include <algorithm> | |
| #include <csignal> | |
| #include <cstdint> | |
| #include <optional> | |
| #include <random> | |
| #include <vector> | |
| #include "boost/asio/ip/address.hpp" // NOLINT | |
| #include "utility.hpp" | |
| #include <nlohmann/json.hpp> | |
| 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 |