xref: /openbmc/bmcweb/include/sessions.hpp (revision 4d7b5ddb3a2b6cc42b7bbc0c710f297e6df4fd55)
140e9b92eSEd Tanous // SPDX-License-Identifier: Apache-2.0
240e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors
32b7981f6SKowalski, Kamil #pragma once
42b7981f6SKowalski, Kamil 
5d7857201SEd Tanous #include "bmcweb_config.h"
6d7857201SEd Tanous 
704e438cbSEd Tanous #include "logging.hpp"
82c6ffdb0SEd Tanous #include "ossl_random.hpp"
93ccb3adbSEd Tanous #include "utils/ip_utils.hpp"
10fc76b8acSEd Tanous 
11d7857201SEd Tanous #include <boost/asio/ip/address.hpp>
121abe55efSEd Tanous #include <nlohmann/json.hpp>
1312c04ef5SRatan Gupta 
14d7857201SEd Tanous #include <chrono>
151214b7e7SGunnar Mills #include <csignal>
16d7857201SEd Tanous #include <cstddef>
17d7857201SEd Tanous #include <cstdint>
18d7857201SEd Tanous #include <functional>
1989cda63dSEd Tanous #include <memory>
20bb759e3aSEd Tanous #include <optional>
21b7f3a82bSEd Tanous #include <string>
22d7857201SEd Tanous #include <string_view>
23d7857201SEd Tanous #include <unordered_map>
2489cda63dSEd Tanous #include <vector>
252b7981f6SKowalski, Kamil 
261abe55efSEd Tanous namespace persistent_data
271abe55efSEd Tanous {
282b7981f6SKowalski, Kamil 
2951dae67dSEd Tanous // entropy: 20 characters, 62 possibilities.  log2(62^20) = 119 bits of
3051dae67dSEd Tanous // entropy.  OWASP recommends at least 64
3151dae67dSEd Tanous // https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html#session-id-entropy
3251dae67dSEd Tanous constexpr std::size_t sessionTokenSize = 20;
3351dae67dSEd Tanous 
3489cda63dSEd Tanous enum class SessionType
351abe55efSEd Tanous {
3689cda63dSEd Tanous     None,
3789cda63dSEd Tanous     Basic,
3889cda63dSEd Tanous     Session,
3989cda63dSEd Tanous     Cookie,
4089cda63dSEd Tanous     MutualTLS
412b7981f6SKowalski, Kamil };
422b7981f6SKowalski, Kamil 
431abe55efSEd Tanous struct UserSession
441abe55efSEd Tanous {
4555c7b7a2SEd Tanous     std::string uniqueId;
4655c7b7a2SEd Tanous     std::string sessionToken;
472b7981f6SKowalski, Kamil     std::string username;
4855c7b7a2SEd Tanous     std::string csrfToken;
49bb759e3aSEd Tanous     std::optional<std::string> clientId;
5092f68223SSunitha Harish     std::string clientIp;
5155c7b7a2SEd Tanous     std::chrono::time_point<std::chrono::steady_clock> lastUpdated;
5289cda63dSEd Tanous     SessionType sessionType{SessionType::None};
537e9c08edSEd Tanous     bool cookieAuth = false;
543bf4e632SJoseph Reynolds     bool isConfigureSelfOnly = false;
5547f2934cSEd Tanous     std::string userRole;
5647f2934cSEd Tanous     std::vector<std::string> userGroups;
573bf4e632SJoseph Reynolds 
583bf4e632SJoseph Reynolds     // There are two sources of truth for isConfigureSelfOnly:
593bf4e632SJoseph Reynolds     //  1. When pamAuthenticateUser() returns PAM_NEW_AUTHTOK_REQD.
603bf4e632SJoseph Reynolds     //  2. D-Bus User.Manager.GetUserInfo property UserPasswordExpired.
613bf4e632SJoseph Reynolds     // These should be in sync, but the underlying condition can change at any
623bf4e632SJoseph Reynolds     // time.  For example, a password can expire or be changed outside of
633bf4e632SJoseph Reynolds     // bmcweb.  The value stored here is updated at the start of each
643bf4e632SJoseph Reynolds     // operation and used as the truth within bmcweb.
655cef0f7dSKowalski, Kamil 
665cef0f7dSKowalski, Kamil     /**
675cef0f7dSKowalski, Kamil      * @brief Fills object with data from UserSession's JSON representation
685cef0f7dSKowalski, Kamil      *
695cef0f7dSKowalski, Kamil      * This replaces nlohmann's from_json to ensure no-throw approach
705cef0f7dSKowalski, Kamil      *
715cef0f7dSKowalski, Kamil      * @param[in] j   JSON object from which data should be loaded
725cef0f7dSKowalski, Kamil      *
73a434f2bdSEd Tanous      * @return a shared pointer if data has been loaded properly, nullptr
74a434f2bdSEd Tanous      * otherwise
755cef0f7dSKowalski, Kamil      */
fromJsonpersistent_data::UserSession76504af5a0SPatrick Williams     static std::shared_ptr<UserSession> fromJson(
77504af5a0SPatrick Williams         const nlohmann::json::object_t& j)
781abe55efSEd Tanous     {
791abe55efSEd Tanous         std::shared_ptr<UserSession> userSession =
801abe55efSEd Tanous             std::make_shared<UserSession>();
810bdda665SEd Tanous         for (const auto& element : j)
821abe55efSEd Tanous         {
83e1ae5338SEd Tanous             const std::string* thisValue =
840bdda665SEd Tanous                 element.second.get_ptr<const std::string*>();
851abe55efSEd Tanous             if (thisValue == nullptr)
861abe55efSEd Tanous             {
8762598e31SEd Tanous                 BMCWEB_LOG_ERROR(
8862598e31SEd Tanous                     "Error reading persistent store.  Property {} was not of type string",
890bdda665SEd Tanous                     element.first);
90dc511aa7SEd Tanous                 continue;
91e1ae5338SEd Tanous             }
920bdda665SEd Tanous             if (element.first == "unique_id")
931abe55efSEd Tanous             {
9455c7b7a2SEd Tanous                 userSession->uniqueId = *thisValue;
951abe55efSEd Tanous             }
960bdda665SEd Tanous             else if (element.first == "session_token")
971abe55efSEd Tanous             {
9855c7b7a2SEd Tanous                 userSession->sessionToken = *thisValue;
991abe55efSEd Tanous             }
1000bdda665SEd Tanous             else if (element.first == "csrf_token")
1011abe55efSEd Tanous             {
10255c7b7a2SEd Tanous                 userSession->csrfToken = *thisValue;
1031abe55efSEd Tanous             }
1040bdda665SEd Tanous             else if (element.first == "username")
1051abe55efSEd Tanous             {
106e1ae5338SEd Tanous                 userSession->username = *thisValue;
1071abe55efSEd Tanous             }
1080bdda665SEd Tanous             else if (element.first == "client_id")
10908bdcc71SSunitha Harish             {
11008bdcc71SSunitha Harish                 userSession->clientId = *thisValue;
11108bdcc71SSunitha Harish             }
1120bdda665SEd Tanous             else if (element.first == "client_ip")
11392f68223SSunitha Harish             {
11492f68223SSunitha Harish                 userSession->clientIp = *thisValue;
11592f68223SSunitha Harish             }
11692f68223SSunitha Harish 
1171abe55efSEd Tanous             else
1181abe55efSEd Tanous             {
11962598e31SEd Tanous                 BMCWEB_LOG_ERROR(
12062598e31SEd Tanous                     "Got unexpected property reading persistent file: {}",
1210bdda665SEd Tanous                     element.first);
122dc511aa7SEd Tanous                 continue;
123e1ae5338SEd Tanous             }
1245cef0f7dSKowalski, Kamil         }
125dc511aa7SEd Tanous         // If any of these fields are missing, we can't restore the session, as
126dc511aa7SEd Tanous         // we don't have enough information.  These 4 fields have been present
127dc511aa7SEd Tanous         // in every version of this file in bmcwebs history, so any file, even
128dc511aa7SEd Tanous         // on upgrade, should have these present
129dc511aa7SEd Tanous         if (userSession->uniqueId.empty() || userSession->username.empty() ||
130dc511aa7SEd Tanous             userSession->sessionToken.empty() || userSession->csrfToken.empty())
131dc511aa7SEd Tanous         {
13262598e31SEd Tanous             BMCWEB_LOG_DEBUG("Session missing required security "
13362598e31SEd Tanous                              "information, refusing to restore");
134dc511aa7SEd Tanous             return nullptr;
135dc511aa7SEd Tanous         }
1365cef0f7dSKowalski, Kamil 
137e1ae5338SEd Tanous         // For now, sessions that were persisted through a reboot get their idle
1381abe55efSEd Tanous         // timer reset.  This could probably be overcome with a better
1391abe55efSEd Tanous         // understanding of wall clock time and steady timer time, possibly
1401abe55efSEd Tanous         // persisting values with wall clock time instead of steady timer, but
1411abe55efSEd Tanous         // the tradeoffs of all the corner cases involved are non-trivial, so
1421abe55efSEd Tanous         // this is done temporarily
14355c7b7a2SEd Tanous         userSession->lastUpdated = std::chrono::steady_clock::now();
14489cda63dSEd Tanous         userSession->sessionType = SessionType::Session;
1455cef0f7dSKowalski, Kamil 
146e1ae5338SEd Tanous         return userSession;
1475cef0f7dSKowalski, Kamil     }
1482b7981f6SKowalski, Kamil };
1492b7981f6SKowalski, Kamil 
1503ce3688aSEd Tanous enum class MTLSCommonNameParseMode
1513ce3688aSEd Tanous {
1523ce3688aSEd Tanous     Invalid = 0,
1533ce3688aSEd Tanous     // This section approximately matches Redfish AccountService
1543ce3688aSEd Tanous     // CertificateMappingAttribute,  plus bmcweb defined OEM ones.
1553ce3688aSEd Tanous     // Note, IDs in this enum must be maintained between versions, as they are
1563ce3688aSEd Tanous     // persisted to disk
1573ce3688aSEd Tanous     Whole = 1,
1583ce3688aSEd Tanous     CommonName = 2,
1593ce3688aSEd Tanous     UserPrincipalName = 3,
1603ce3688aSEd Tanous 
1613ce3688aSEd Tanous     // Intentional gap for future DMTF-defined enums
1623ce3688aSEd Tanous 
1633ce3688aSEd Tanous     // OEM parsing modes for various OEMs
1643ce3688aSEd Tanous     Meta = 100,
1653ce3688aSEd Tanous };
1663ce3688aSEd Tanous 
getMTLSCommonNameParseMode(std::string_view name)1673ce3688aSEd Tanous inline MTLSCommonNameParseMode getMTLSCommonNameParseMode(std::string_view name)
1683ce3688aSEd Tanous {
1693ce3688aSEd Tanous     if (name == "CommonName")
1703ce3688aSEd Tanous     {
1713ce3688aSEd Tanous         return MTLSCommonNameParseMode::CommonName;
1723ce3688aSEd Tanous     }
1733ce3688aSEd Tanous     if (name == "Whole")
1743ce3688aSEd Tanous     {
1753ce3688aSEd Tanous         // Not yet supported
1763ce3688aSEd Tanous         // return MTLSCommonNameParseMode::Whole;
1773ce3688aSEd Tanous     }
1783ce3688aSEd Tanous     if (name == "UserPrincipalName")
1793ce3688aSEd Tanous     {
180*4d7b5ddbSMalik Akbar Hashemi Rafsanjani         return MTLSCommonNameParseMode::UserPrincipalName;
1813ce3688aSEd Tanous     }
1823ce3688aSEd Tanous     if constexpr (BMCWEB_META_TLS_COMMON_NAME_PARSING)
1833ce3688aSEd Tanous     {
1843ce3688aSEd Tanous         if (name == "Meta")
1853ce3688aSEd Tanous         {
1863ce3688aSEd Tanous             return MTLSCommonNameParseMode::Meta;
1873ce3688aSEd Tanous         }
1883ce3688aSEd Tanous     }
1893ce3688aSEd Tanous     return MTLSCommonNameParseMode::Invalid;
1903ce3688aSEd Tanous }
1913ce3688aSEd Tanous 
19278158631SZbigniew Kurzynski struct AuthConfigMethods
19378158631SZbigniew Kurzynski {
1943281bcf1SEd Tanous     // Authentication paths
19525b54dbaSEd Tanous     bool basic = BMCWEB_BASIC_AUTH;
19625b54dbaSEd Tanous     bool sessionToken = BMCWEB_SESSION_AUTH;
19725b54dbaSEd Tanous     bool xtoken = BMCWEB_XTOKEN_AUTH;
19825b54dbaSEd Tanous     bool cookie = BMCWEB_COOKIE_AUTH;
19925b54dbaSEd Tanous     bool tls = BMCWEB_MUTUAL_TLS_AUTH;
20078158631SZbigniew Kurzynski 
2013281bcf1SEd Tanous     // Whether or not unauthenticated TLS should be accepted
2023281bcf1SEd Tanous     // true = reject connections if mutual tls is not provided
2033281bcf1SEd Tanous     // false = allow connection, and allow user to use other auth method
2043281bcf1SEd Tanous     // Always default to false, because root certificates will not
2053281bcf1SEd Tanous     // be provisioned at startup
2063281bcf1SEd Tanous     bool tlsStrict = false;
2073281bcf1SEd Tanous 
2083ce3688aSEd Tanous     MTLSCommonNameParseMode mTLSCommonNameParsingMode =
2093ce3688aSEd Tanous         getMTLSCommonNameParseMode(
2103ce3688aSEd Tanous             BMCWEB_MUTUAL_TLS_COMMON_NAME_PARSING_DEFAULT);
2113ce3688aSEd Tanous 
fromJsonpersistent_data::AuthConfigMethods2120bdda665SEd Tanous     void fromJson(const nlohmann::json::object_t& j)
21378158631SZbigniew Kurzynski     {
2140bdda665SEd Tanous         for (const auto& element : j)
21578158631SZbigniew Kurzynski         {
2160bdda665SEd Tanous             const bool* value = element.second.get_ptr<const bool*>();
2173ce3688aSEd Tanous             if (value != nullptr)
21878158631SZbigniew Kurzynski             {
2190bdda665SEd Tanous                 if (element.first == "XToken")
22078158631SZbigniew Kurzynski                 {
22178158631SZbigniew Kurzynski                     xtoken = *value;
22278158631SZbigniew Kurzynski                 }
2230bdda665SEd Tanous                 else if (element.first == "Cookie")
22478158631SZbigniew Kurzynski                 {
22578158631SZbigniew Kurzynski                     cookie = *value;
22678158631SZbigniew Kurzynski                 }
2270bdda665SEd Tanous                 else if (element.first == "SessionToken")
22878158631SZbigniew Kurzynski                 {
22978158631SZbigniew Kurzynski                     sessionToken = *value;
23078158631SZbigniew Kurzynski                 }
2310bdda665SEd Tanous                 else if (element.first == "BasicAuth")
23278158631SZbigniew Kurzynski                 {
23378158631SZbigniew Kurzynski                     basic = *value;
23478158631SZbigniew Kurzynski                 }
2350bdda665SEd Tanous                 else if (element.first == "TLS")
236501f1e58SZbigniew Kurzynski                 {
237501f1e58SZbigniew Kurzynski                     tls = *value;
238501f1e58SZbigniew Kurzynski                 }
2393281bcf1SEd Tanous                 else if (element.first == "TLSStrict")
2403281bcf1SEd Tanous                 {
2413281bcf1SEd Tanous                     tlsStrict = *value;
2423281bcf1SEd Tanous                 }
24378158631SZbigniew Kurzynski             }
2443ce3688aSEd Tanous             const uint64_t* intValue =
2453ce3688aSEd Tanous                 element.second.get_ptr<const uint64_t*>();
2463ce3688aSEd Tanous             if (intValue != nullptr)
2473ce3688aSEd Tanous             {
2483ce3688aSEd Tanous                 if (element.first == "MTLSCommonNameParseMode")
2493ce3688aSEd Tanous                 {
250*4d7b5ddbSMalik Akbar Hashemi Rafsanjani                     MTLSCommonNameParseMode tmpMTLSCommonNameParseMode =
2513ce3688aSEd Tanous                         static_cast<MTLSCommonNameParseMode>(*intValue);
252*4d7b5ddbSMalik Akbar Hashemi Rafsanjani                     if (tmpMTLSCommonNameParseMode <=
253*4d7b5ddbSMalik Akbar Hashemi Rafsanjani                             MTLSCommonNameParseMode::UserPrincipalName ||
254*4d7b5ddbSMalik Akbar Hashemi Rafsanjani                         tmpMTLSCommonNameParseMode ==
255*4d7b5ddbSMalik Akbar Hashemi Rafsanjani                             MTLSCommonNameParseMode::Meta)
256*4d7b5ddbSMalik Akbar Hashemi Rafsanjani                     {
257*4d7b5ddbSMalik Akbar Hashemi Rafsanjani                         mTLSCommonNameParsingMode = tmpMTLSCommonNameParseMode;
2583ce3688aSEd Tanous                     }
2593ce3688aSEd Tanous                     else
2603ce3688aSEd Tanous                     {
261*4d7b5ddbSMalik Akbar Hashemi Rafsanjani                         BMCWEB_LOG_WARNING(
2623ce3688aSEd Tanous                             "Json value of {} was out of range of the enum.  Ignoring",
2633ce3688aSEd Tanous                             *intValue);
2643ce3688aSEd Tanous                     }
2653ce3688aSEd Tanous                 }
2663ce3688aSEd Tanous             }
2673ce3688aSEd Tanous         }
26878158631SZbigniew Kurzynski     }
26978158631SZbigniew Kurzynski };
27078158631SZbigniew Kurzynski 
2711abe55efSEd Tanous class SessionStore
2721abe55efSEd Tanous {
2732b7981f6SKowalski, Kamil   public:
generateUserSession(std::string_view username,const boost::asio::ip::address & clientIp,const std::optional<std::string> & clientId,SessionType sessionType,bool isConfigureSelfOnly=false)27455c7b7a2SEd Tanous     std::shared_ptr<UserSession> generateUserSession(
27526ccae32SEd Tanous         std::string_view username, const boost::asio::ip::address& clientIp,
27689cda63dSEd Tanous         const std::optional<std::string>& clientId, SessionType sessionType,
277d3239224SSunitha Harish         bool isConfigureSelfOnly = false)
2781abe55efSEd Tanous     {
2792b7981f6SKowalski, Kamil         // Only need csrf tokens for cookie based auth, token doesn't matter
280b7f3a82bSEd Tanous         std::string sessionToken =
281b7f3a82bSEd Tanous             bmcweb::getRandomIdOfLength(sessionTokenSize);
282b7f3a82bSEd Tanous         std::string csrfToken = bmcweb::getRandomIdOfLength(sessionTokenSize);
283b7f3a82bSEd Tanous         std::string uniqueId = bmcweb::getRandomIdOfLength(10);
2842b7981f6SKowalski, Kamil 
285b7f3a82bSEd Tanous         //
286b7f3a82bSEd Tanous         if (sessionToken.empty() || csrfToken.empty() || uniqueId.empty())
2871abe55efSEd Tanous         {
288b7f3a82bSEd Tanous             BMCWEB_LOG_ERROR("Failed to generate session tokens");
289a68a8045SJames Feist             return nullptr;
290a68a8045SJames Feist         }
29141d61c82SJiaqing Zhao 
292bd79bce8SPatrick Williams         auto session = std::make_shared<UserSession>(UserSession{
293bd79bce8SPatrick Williams             uniqueId,
29447f2934cSEd Tanous             sessionToken,
29547f2934cSEd Tanous             std::string(username),
29647f2934cSEd Tanous             csrfToken,
29747f2934cSEd Tanous             clientId,
298bb759e3aSEd Tanous             redfish::ip_util::toString(clientIp),
29947f2934cSEd Tanous             std::chrono::steady_clock::now(),
30089cda63dSEd Tanous             sessionType,
30147f2934cSEd Tanous             false,
30247f2934cSEd Tanous             isConfigureSelfOnly,
30347f2934cSEd Tanous             "",
30447f2934cSEd Tanous             {}});
30541713dd7SPatrick Williams         auto it = authTokens.emplace(sessionToken, session);
3062b7981f6SKowalski, Kamil         // Only need to write to disk if session isn't about to be destroyed.
30789cda63dSEd Tanous         needWrite = sessionType != SessionType::Basic &&
30889cda63dSEd Tanous                     sessionType != SessionType::MutualTLS;
309e0d918bcSEd Tanous         return it.first->second;
3102b7981f6SKowalski, Kamil     }
3112b7981f6SKowalski, Kamil 
loginSessionByToken(std::string_view token)31226ccae32SEd Tanous     std::shared_ptr<UserSession> loginSessionByToken(std::string_view token)
3131abe55efSEd Tanous     {
31455c7b7a2SEd Tanous         applySessionTimeouts();
31551dae67dSEd Tanous         if (token.size() != sessionTokenSize)
31651dae67dSEd Tanous         {
31751dae67dSEd Tanous             return nullptr;
31851dae67dSEd Tanous         }
31955c7b7a2SEd Tanous         auto sessionIt = authTokens.find(std::string(token));
3201abe55efSEd Tanous         if (sessionIt == authTokens.end())
3211abe55efSEd Tanous         {
3222b7981f6SKowalski, Kamil             return nullptr;
3232b7981f6SKowalski, Kamil         }
32455c7b7a2SEd Tanous         std::shared_ptr<UserSession> userSession = sessionIt->second;
32555c7b7a2SEd Tanous         userSession->lastUpdated = std::chrono::steady_clock::now();
32655c7b7a2SEd Tanous         return userSession;
3272b7981f6SKowalski, Kamil     }
3282b7981f6SKowalski, Kamil 
getSessionByUid(std::string_view uid)32926ccae32SEd Tanous     std::shared_ptr<UserSession> getSessionByUid(std::string_view uid)
3301abe55efSEd Tanous     {
33155c7b7a2SEd Tanous         applySessionTimeouts();
3322b7981f6SKowalski, Kamil         // TODO(Ed) this is inefficient
33355c7b7a2SEd Tanous         auto sessionIt = authTokens.begin();
3341abe55efSEd Tanous         while (sessionIt != authTokens.end())
3351abe55efSEd Tanous         {
3361abe55efSEd Tanous             if (sessionIt->second->uniqueId == uid)
3371abe55efSEd Tanous             {
33855c7b7a2SEd Tanous                 return sessionIt->second;
3392b7981f6SKowalski, Kamil             }
34055c7b7a2SEd Tanous             sessionIt++;
3412b7981f6SKowalski, Kamil         }
3422b7981f6SKowalski, Kamil         return nullptr;
3432b7981f6SKowalski, Kamil     }
3442b7981f6SKowalski, Kamil 
removeSession(const std::shared_ptr<UserSession> & session)345b5a76932SEd Tanous     void removeSession(const std::shared_ptr<UserSession>& session)
3461abe55efSEd Tanous     {
34755c7b7a2SEd Tanous         authTokens.erase(session->sessionToken);
34855c7b7a2SEd Tanous         needWrite = true;
3492b7981f6SKowalski, Kamil     }
3502b7981f6SKowalski, Kamil 
getAllUniqueIds()35189cda63dSEd Tanous     std::vector<std::string> getAllUniqueIds()
3521abe55efSEd Tanous     {
35355c7b7a2SEd Tanous         applySessionTimeouts();
35489cda63dSEd Tanous         std::vector<std::string> ret;
35555c7b7a2SEd Tanous         ret.reserve(authTokens.size());
3561abe55efSEd Tanous         for (auto& session : authTokens)
3571abe55efSEd Tanous         {
35889cda63dSEd Tanous             ret.push_back(session.second->uniqueId);
35989cda63dSEd Tanous         }
36089cda63dSEd Tanous         return ret;
36189cda63dSEd Tanous     }
36289cda63dSEd Tanous 
getUniqueIdsBySessionType(SessionType type)36389cda63dSEd Tanous     std::vector<std::string> getUniqueIdsBySessionType(SessionType type)
3641abe55efSEd Tanous     {
36589cda63dSEd Tanous         applySessionTimeouts();
36689cda63dSEd Tanous 
36789cda63dSEd Tanous         std::vector<std::string> ret;
36889cda63dSEd Tanous         ret.reserve(authTokens.size());
36989cda63dSEd Tanous         for (auto& session : authTokens)
37089cda63dSEd Tanous         {
37189cda63dSEd Tanous             if (type == session.second->sessionType)
37289cda63dSEd Tanous             {
37389cda63dSEd Tanous                 ret.push_back(session.second->uniqueId);
3742b7981f6SKowalski, Kamil             }
3752b7981f6SKowalski, Kamil         }
3762b7981f6SKowalski, Kamil         return ret;
3772b7981f6SKowalski, Kamil     }
3782b7981f6SKowalski, Kamil 
getSessions()37989cda63dSEd Tanous     std::vector<std::shared_ptr<UserSession>> getSessions()
38089cda63dSEd Tanous     {
38189cda63dSEd Tanous         std::vector<std::shared_ptr<UserSession>> sessions;
38289cda63dSEd Tanous         sessions.reserve(authTokens.size());
38389cda63dSEd Tanous         for (auto& session : authTokens)
38489cda63dSEd Tanous         {
38589cda63dSEd Tanous             sessions.push_back(session.second);
38689cda63dSEd Tanous         }
38789cda63dSEd Tanous         return sessions;
38889cda63dSEd Tanous     }
38989cda63dSEd Tanous 
removeSessionsByUsername(std::string_view username)3909fa06f19SXie Ning     void removeSessionsByUsername(std::string_view username)
3919fa06f19SXie Ning     {
3929fa06f19SXie Ning         std::erase_if(authTokens, [username](const auto& value) {
3939fa06f19SXie Ning             if (value.second == nullptr)
3949fa06f19SXie Ning             {
3959fa06f19SXie Ning                 return false;
3969fa06f19SXie Ning             }
3979fa06f19SXie Ning             return value.second->username == username;
3989fa06f19SXie Ning         });
3999fa06f19SXie Ning     }
4009fa06f19SXie Ning 
removeSessionsByUsernameExceptSession(std::string_view username,const std::shared_ptr<UserSession> & session)401e518ef32SRavi Teja     void removeSessionsByUsernameExceptSession(
402e518ef32SRavi Teja         std::string_view username, const std::shared_ptr<UserSession>& session)
403e518ef32SRavi Teja     {
404e518ef32SRavi Teja         std::erase_if(authTokens, [username, session](const auto& value) {
405e518ef32SRavi Teja             if (value.second == nullptr)
406e518ef32SRavi Teja             {
407e518ef32SRavi Teja                 return false;
408e518ef32SRavi Teja             }
409e518ef32SRavi Teja 
410e518ef32SRavi Teja             return value.second->username == username &&
411e518ef32SRavi Teja                    value.second->uniqueId != session->uniqueId;
412e518ef32SRavi Teja         });
413e518ef32SRavi Teja     }
414e518ef32SRavi Teja 
updateAuthMethodsConfig(const AuthConfigMethods & config)41578158631SZbigniew Kurzynski     void updateAuthMethodsConfig(const AuthConfigMethods& config)
41678158631SZbigniew Kurzynski     {
417009c2a4dSZbigniew Kurzynski         bool isTLSchanged = (authMethodsConfig.tls != config.tls);
41878158631SZbigniew Kurzynski         authMethodsConfig = config;
41978158631SZbigniew Kurzynski         needWrite = true;
420009c2a4dSZbigniew Kurzynski         if (isTLSchanged)
421009c2a4dSZbigniew Kurzynski         {
422009c2a4dSZbigniew Kurzynski             // recreate socket connections with new settings
42392e11bf8SMyung Bae             // NOLINTNEXTLINE(misc-include-cleaner)
424009c2a4dSZbigniew Kurzynski             std::raise(SIGHUP);
425009c2a4dSZbigniew Kurzynski         }
42678158631SZbigniew Kurzynski     }
42778158631SZbigniew Kurzynski 
getAuthMethodsConfig()42878158631SZbigniew Kurzynski     AuthConfigMethods& getAuthMethodsConfig()
42978158631SZbigniew Kurzynski     {
43078158631SZbigniew Kurzynski         return authMethodsConfig;
43178158631SZbigniew Kurzynski     }
43278158631SZbigniew Kurzynski 
needsWrite() const4339eb808c1SEd Tanous     bool needsWrite() const
4341abe55efSEd Tanous     {
4351abe55efSEd Tanous         return needWrite;
4361abe55efSEd Tanous     }
getTimeoutInSeconds() const437271584abSEd Tanous     int64_t getTimeoutInSeconds() const
4381abe55efSEd Tanous     {
439f2a4a606SManojkiran Eda         return std::chrono::seconds(timeoutInSeconds).count();
440f2a4a606SManojkiran Eda     }
441f2a4a606SManojkiran Eda 
updateSessionTimeout(std::chrono::seconds newTimeoutInSeconds)442f2a4a606SManojkiran Eda     void updateSessionTimeout(std::chrono::seconds newTimeoutInSeconds)
443f2a4a606SManojkiran Eda     {
444f2a4a606SManojkiran Eda         timeoutInSeconds = newTimeoutInSeconds;
445f2a4a606SManojkiran Eda         needWrite = true;
44623a21a1cSEd Tanous     }
4472b7981f6SKowalski, Kamil 
getInstance()4481abe55efSEd Tanous     static SessionStore& getInstance()
4491abe55efSEd Tanous     {
4504b1b8683SBorawski.Lukasz         static SessionStore sessionStore;
4514b1b8683SBorawski.Lukasz         return sessionStore;
4524b1b8683SBorawski.Lukasz     }
4534b1b8683SBorawski.Lukasz 
applySessionTimeouts()4541abe55efSEd Tanous     void applySessionTimeouts()
4551abe55efSEd Tanous     {
45655c7b7a2SEd Tanous         auto timeNow = std::chrono::steady_clock::now();
457f2a4a606SManojkiran Eda         if (timeNow - lastTimeoutUpdate > std::chrono::seconds(1))
4581abe55efSEd Tanous         {
45955c7b7a2SEd Tanous             lastTimeoutUpdate = timeNow;
46055c7b7a2SEd Tanous             auto authTokensIt = authTokens.begin();
4611abe55efSEd Tanous             while (authTokensIt != authTokens.end())
4621abe55efSEd Tanous             {
4631abe55efSEd Tanous                 if (timeNow - authTokensIt->second->lastUpdated >=
464f2a4a606SManojkiran Eda                     timeoutInSeconds)
4651abe55efSEd Tanous                 {
46655c7b7a2SEd Tanous                     authTokensIt = authTokens.erase(authTokensIt);
46707386c67SRatan Gupta 
46855c7b7a2SEd Tanous                     needWrite = true;
4691abe55efSEd Tanous                 }
4701abe55efSEd Tanous                 else
4711abe55efSEd Tanous                 {
47255c7b7a2SEd Tanous                     authTokensIt++;
4732b7981f6SKowalski, Kamil                 }
4742b7981f6SKowalski, Kamil             }
4752b7981f6SKowalski, Kamil         }
4762b7981f6SKowalski, Kamil     }
47783cf8189SGunnar Mills 
47883cf8189SGunnar Mills     SessionStore(const SessionStore&) = delete;
47983cf8189SGunnar Mills     SessionStore& operator=(const SessionStore&) = delete;
480ecd6a3a2SEd Tanous     SessionStore(SessionStore&&) = delete;
481ecd6a3a2SEd Tanous     SessionStore& operator=(const SessionStore&&) = delete;
482ecd6a3a2SEd Tanous     ~SessionStore() = default;
48383cf8189SGunnar Mills 
48483cf8189SGunnar Mills     std::unordered_map<std::string, std::shared_ptr<UserSession>,
485724985ffSEd Tanous                        std::hash<std::string>, bmcweb::ConstantTimeCompare>
48683cf8189SGunnar Mills         authTokens;
48783cf8189SGunnar Mills 
48883cf8189SGunnar Mills     std::chrono::time_point<std::chrono::steady_clock> lastTimeoutUpdate;
48983cf8189SGunnar Mills     bool needWrite{false};
49083cf8189SGunnar Mills     std::chrono::seconds timeoutInSeconds;
49183cf8189SGunnar Mills     AuthConfigMethods authMethodsConfig;
49283cf8189SGunnar Mills 
49383cf8189SGunnar Mills   private:
SessionStore()49489492a15SPatrick Williams     SessionStore() : timeoutInSeconds(1800) {}
4952b7981f6SKowalski, Kamil };
4962b7981f6SKowalski, Kamil 
49755c7b7a2SEd Tanous } // namespace persistent_data
498