152cc112dSEd Tanous #pragma once 252cc112dSEd Tanous 304e438cbSEd Tanous #include <app.hpp> 452cc112dSEd Tanous #include <boost/container/flat_map.hpp> 552cc112dSEd Tanous #include <boost/uuid/uuid.hpp> 652cc112dSEd Tanous #include <boost/uuid/uuid_generators.hpp> 752cc112dSEd Tanous #include <boost/uuid/uuid_io.hpp> 804e438cbSEd Tanous #include <http_request.hpp> 904e438cbSEd Tanous #include <http_response.hpp> 1052cc112dSEd Tanous #include <nlohmann/json.hpp> 1152cc112dSEd Tanous #include <pam_authenticate.hpp> 1252cc112dSEd Tanous #include <sessions.hpp> 1352cc112dSEd Tanous 1452cc112dSEd Tanous #include <filesystem> 1552cc112dSEd Tanous #include <fstream> 1652cc112dSEd Tanous #include <random> 1752cc112dSEd Tanous 1852cc112dSEd Tanous namespace persistent_data 1952cc112dSEd Tanous { 2052cc112dSEd Tanous 2152cc112dSEd Tanous class ConfigFile 2252cc112dSEd Tanous { 2352cc112dSEd Tanous uint64_t jsonRevision = 1; 2452cc112dSEd Tanous 2552cc112dSEd Tanous public: 2652cc112dSEd Tanous // todo(ed) should read this from a fixed location somewhere, not CWD 2752cc112dSEd Tanous static constexpr const char* filename = "bmcweb_persistent_data.json"; 2852cc112dSEd Tanous 2952cc112dSEd Tanous ConfigFile() 3052cc112dSEd Tanous { 3152cc112dSEd Tanous readData(); 3252cc112dSEd Tanous } 3352cc112dSEd Tanous 3452cc112dSEd Tanous ~ConfigFile() 3552cc112dSEd Tanous { 3652cc112dSEd Tanous if (persistent_data::SessionStore::getInstance().needsWrite()) 3752cc112dSEd Tanous { 3852cc112dSEd Tanous writeData(); 3952cc112dSEd Tanous } 4052cc112dSEd Tanous } 4152cc112dSEd Tanous 4252cc112dSEd Tanous // TODO(ed) this should really use protobuf, or some other serialization 4352cc112dSEd Tanous // library, but adding another dependency is somewhat outside the scope of 4452cc112dSEd Tanous // this application for the moment 4552cc112dSEd Tanous void readData() 4652cc112dSEd Tanous { 4752cc112dSEd Tanous std::ifstream persistentFile(filename); 4852cc112dSEd Tanous uint64_t fileRevision = 0; 4952cc112dSEd Tanous if (persistentFile.is_open()) 5052cc112dSEd Tanous { 5152cc112dSEd Tanous // call with exceptions disabled 5252cc112dSEd Tanous auto data = nlohmann::json::parse(persistentFile, nullptr, false); 5352cc112dSEd Tanous if (data.is_discarded()) 5452cc112dSEd Tanous { 5552cc112dSEd Tanous BMCWEB_LOG_ERROR 5652cc112dSEd Tanous << "Error parsing persistent data in json file."; 5752cc112dSEd Tanous } 5852cc112dSEd Tanous else 5952cc112dSEd Tanous { 6052cc112dSEd Tanous for (const auto& item : data.items()) 6152cc112dSEd Tanous { 6252cc112dSEd Tanous if (item.key() == "revision") 6352cc112dSEd Tanous { 6452cc112dSEd Tanous fileRevision = 0; 6552cc112dSEd Tanous 6652cc112dSEd Tanous const uint64_t* uintPtr = 6752cc112dSEd Tanous item.value().get_ptr<const uint64_t*>(); 6852cc112dSEd Tanous if (uintPtr == nullptr) 6952cc112dSEd Tanous { 7052cc112dSEd Tanous BMCWEB_LOG_ERROR << "Failed to read revision flag"; 7152cc112dSEd Tanous } 7252cc112dSEd Tanous else 7352cc112dSEd Tanous { 7452cc112dSEd Tanous fileRevision = *uintPtr; 7552cc112dSEd Tanous } 7652cc112dSEd Tanous } 7752cc112dSEd Tanous else if (item.key() == "system_uuid") 7852cc112dSEd Tanous { 7952cc112dSEd Tanous const std::string* jSystemUuid = 8052cc112dSEd Tanous item.value().get_ptr<const std::string*>(); 8152cc112dSEd Tanous if (jSystemUuid != nullptr) 8252cc112dSEd Tanous { 8352cc112dSEd Tanous systemUuid = *jSystemUuid; 8452cc112dSEd Tanous } 8552cc112dSEd Tanous } 8652cc112dSEd Tanous else if (item.key() == "auth_config") 8752cc112dSEd Tanous { 8852cc112dSEd Tanous SessionStore::getInstance() 8952cc112dSEd Tanous .getAuthMethodsConfig() 9052cc112dSEd Tanous .fromJson(item.value()); 9152cc112dSEd Tanous } 9252cc112dSEd Tanous else if (item.key() == "sessions") 9352cc112dSEd Tanous { 9452cc112dSEd Tanous for (const auto& elem : item.value()) 9552cc112dSEd Tanous { 9652cc112dSEd Tanous std::shared_ptr<UserSession> newSession = 9752cc112dSEd Tanous UserSession::fromJson(elem); 9852cc112dSEd Tanous 9952cc112dSEd Tanous if (newSession == nullptr) 10052cc112dSEd Tanous { 10152cc112dSEd Tanous BMCWEB_LOG_ERROR << "Problem reading session " 10252cc112dSEd Tanous "from persistent store"; 10352cc112dSEd Tanous continue; 10452cc112dSEd Tanous } 10552cc112dSEd Tanous 10652cc112dSEd Tanous BMCWEB_LOG_DEBUG 10752cc112dSEd Tanous << "Restored session: " << newSession->csrfToken 10852cc112dSEd Tanous << " " << newSession->uniqueId << " " 10952cc112dSEd Tanous << newSession->sessionToken; 11052cc112dSEd Tanous SessionStore::getInstance().authTokens.emplace( 11152cc112dSEd Tanous newSession->sessionToken, newSession); 11252cc112dSEd Tanous } 11352cc112dSEd Tanous } 114f2a4a606SManojkiran Eda else if (item.key() == "timeout") 115f2a4a606SManojkiran Eda { 116f2a4a606SManojkiran Eda const int64_t* jTimeout = 117f2a4a606SManojkiran Eda item.value().get_ptr<int64_t*>(); 118f2a4a606SManojkiran Eda if (jTimeout == nullptr) 119f2a4a606SManojkiran Eda { 120f2a4a606SManojkiran Eda BMCWEB_LOG_DEBUG 121f2a4a606SManojkiran Eda << "Problem reading session timeout value"; 122f2a4a606SManojkiran Eda continue; 123f2a4a606SManojkiran Eda } 124f2a4a606SManojkiran Eda std::chrono::seconds sessionTimeoutInseconds(*jTimeout); 125f2a4a606SManojkiran Eda BMCWEB_LOG_DEBUG << "Restored Session Timeout: " 126f2a4a606SManojkiran Eda << sessionTimeoutInseconds.count(); 127f2a4a606SManojkiran Eda SessionStore::getInstance().updateSessionTimeout( 128f2a4a606SManojkiran Eda sessionTimeoutInseconds); 129f2a4a606SManojkiran Eda } 13052cc112dSEd Tanous else 13152cc112dSEd Tanous { 13252cc112dSEd Tanous // Do nothing in the case of extra fields. We may have 13352cc112dSEd Tanous // cases where fields are added in the future, and we 13452cc112dSEd Tanous // want to at least attempt to gracefully support 13552cc112dSEd Tanous // downgrades in that case, even if we don't officially 13652cc112dSEd Tanous // support it 13752cc112dSEd Tanous } 13852cc112dSEd Tanous } 13952cc112dSEd Tanous } 14052cc112dSEd Tanous } 14152cc112dSEd Tanous bool needWrite = false; 14252cc112dSEd Tanous 14352cc112dSEd Tanous if (systemUuid.empty()) 14452cc112dSEd Tanous { 14552cc112dSEd Tanous systemUuid = 14652cc112dSEd Tanous boost::uuids::to_string(boost::uuids::random_generator()()); 14752cc112dSEd Tanous needWrite = true; 14852cc112dSEd Tanous } 14952cc112dSEd Tanous if (fileRevision < jsonRevision) 15052cc112dSEd Tanous { 15152cc112dSEd Tanous needWrite = true; 15252cc112dSEd Tanous } 15352cc112dSEd Tanous // write revision changes or system uuid changes immediately 15452cc112dSEd Tanous if (needWrite) 15552cc112dSEd Tanous { 15652cc112dSEd Tanous writeData(); 15752cc112dSEd Tanous } 15852cc112dSEd Tanous } 15952cc112dSEd Tanous 16052cc112dSEd Tanous void writeData() 16152cc112dSEd Tanous { 16252cc112dSEd Tanous std::ofstream persistentFile(filename); 16352cc112dSEd Tanous 16452cc112dSEd Tanous // set the permission of the file to 640 16552cc112dSEd Tanous std::filesystem::perms permission = 16652cc112dSEd Tanous std::filesystem::perms::owner_read | 16752cc112dSEd Tanous std::filesystem::perms::owner_write | 16852cc112dSEd Tanous std::filesystem::perms::group_read; 16952cc112dSEd Tanous std::filesystem::permissions(filename, permission); 1705fb91ba4SEd Tanous const auto& c = SessionStore::getInstance().getAuthMethodsConfig(); 171dc511aa7SEd Tanous nlohmann::json data{ 172dc511aa7SEd Tanous {"auth_config", 1735fb91ba4SEd Tanous {{"XToken", c.xtoken}, 1745fb91ba4SEd Tanous {"Cookie", c.cookie}, 1755fb91ba4SEd Tanous {"SessionToken", c.sessionToken}, 1765fb91ba4SEd Tanous {"BasicAuth", c.basic}, 1775fb91ba4SEd Tanous {"TLS", c.tls}} 17852cc112dSEd Tanous 1795fb91ba4SEd Tanous }, 18052cc112dSEd Tanous {"system_uuid", systemUuid}, 181dc511aa7SEd Tanous {"revision", jsonRevision}, 182dc511aa7SEd Tanous {"timeout", SessionStore::getInstance().getTimeoutInSeconds()}}; 1835fb91ba4SEd Tanous 1845fb91ba4SEd Tanous nlohmann::json& sessions = data["sessions"]; 1855fb91ba4SEd Tanous sessions = nlohmann::json::array(); 1865fb91ba4SEd Tanous for (const auto& p : SessionStore::getInstance().authTokens) 1875fb91ba4SEd Tanous { 1885fb91ba4SEd Tanous if (p.second->persistence != 1895fb91ba4SEd Tanous persistent_data::PersistenceType::SINGLE_REQUEST) 1905fb91ba4SEd Tanous { 1915fb91ba4SEd Tanous sessions.push_back({ 1925fb91ba4SEd Tanous {"unique_id", p.second->uniqueId}, 1935fb91ba4SEd Tanous {"session_token", p.second->sessionToken}, 1945fb91ba4SEd Tanous {"username", p.second->username}, 1955fb91ba4SEd Tanous {"csrf_token", p.second->csrfToken}, 196*c0ea7ae1SSunitha Harish {"client_ip", p.second->clientIp}, 1975fb91ba4SEd Tanous #ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE 1985fb91ba4SEd Tanous {"client_id", p.second->clientId}, 1995fb91ba4SEd Tanous #endif 2005fb91ba4SEd Tanous }); 2015fb91ba4SEd Tanous } 2025fb91ba4SEd Tanous } 20352cc112dSEd Tanous persistentFile << data; 20452cc112dSEd Tanous } 20552cc112dSEd Tanous 20652cc112dSEd Tanous std::string systemUuid{""}; 20752cc112dSEd Tanous }; 20852cc112dSEd Tanous 20952cc112dSEd Tanous inline ConfigFile& getConfig() 21052cc112dSEd Tanous { 21152cc112dSEd Tanous static ConfigFile f; 21252cc112dSEd Tanous return f; 21352cc112dSEd Tanous } 21452cc112dSEd Tanous 21552cc112dSEd Tanous } // namespace persistent_data 216