152cc112dSEd Tanous #pragma once 252cc112dSEd Tanous 304e438cbSEd Tanous #include <app.hpp> 4601c71aeSEd Tanous #include <boost/beast/http/fields.hpp> 552cc112dSEd Tanous #include <boost/container/flat_map.hpp> 652cc112dSEd Tanous #include <boost/uuid/uuid.hpp> 752cc112dSEd Tanous #include <boost/uuid/uuid_generators.hpp> 852cc112dSEd Tanous #include <boost/uuid/uuid_io.hpp> 928afb49cSJunLin Chen #include <event_service_store.hpp> 1004e438cbSEd Tanous #include <http_request.hpp> 1104e438cbSEd Tanous #include <http_response.hpp> 1252cc112dSEd Tanous #include <nlohmann/json.hpp> 1352cc112dSEd Tanous #include <pam_authenticate.hpp> 1452cc112dSEd Tanous #include <sessions.hpp> 1552cc112dSEd Tanous 1652cc112dSEd Tanous #include <filesystem> 1752cc112dSEd Tanous #include <fstream> 1852cc112dSEd Tanous #include <random> 1952cc112dSEd Tanous 2052cc112dSEd Tanous namespace persistent_data 2152cc112dSEd Tanous { 2252cc112dSEd Tanous 2352cc112dSEd Tanous class ConfigFile 2452cc112dSEd Tanous { 2552cc112dSEd Tanous uint64_t jsonRevision = 1; 2652cc112dSEd Tanous 2752cc112dSEd Tanous public: 2852cc112dSEd Tanous // todo(ed) should read this from a fixed location somewhere, not CWD 2952cc112dSEd Tanous static constexpr const char* filename = "bmcweb_persistent_data.json"; 3052cc112dSEd Tanous 3152cc112dSEd Tanous ConfigFile() 3252cc112dSEd Tanous { 3352cc112dSEd Tanous readData(); 3452cc112dSEd Tanous } 3552cc112dSEd Tanous 3652cc112dSEd Tanous ~ConfigFile() 3752cc112dSEd Tanous { 3883cf8189SGunnar Mills // Make sure we aren't writing stale sessions 3983cf8189SGunnar Mills persistent_data::SessionStore::getInstance().applySessionTimeouts(); 4052cc112dSEd Tanous if (persistent_data::SessionStore::getInstance().needsWrite()) 4152cc112dSEd Tanous { 4252cc112dSEd Tanous writeData(); 4352cc112dSEd Tanous } 4452cc112dSEd Tanous } 4552cc112dSEd Tanous 46*ecd6a3a2SEd Tanous ConfigFile(const ConfigFile&) = delete; 47*ecd6a3a2SEd Tanous ConfigFile(ConfigFile&&) = delete; 48*ecd6a3a2SEd Tanous ConfigFile& operator=(const ConfigFile&) = delete; 49*ecd6a3a2SEd Tanous ConfigFile& operator=(ConfigFile&&) = delete; 50*ecd6a3a2SEd Tanous 5152cc112dSEd Tanous // TODO(ed) this should really use protobuf, or some other serialization 5252cc112dSEd Tanous // library, but adding another dependency is somewhat outside the scope of 5352cc112dSEd Tanous // this application for the moment 5452cc112dSEd Tanous void readData() 5552cc112dSEd Tanous { 5652cc112dSEd Tanous std::ifstream persistentFile(filename); 5752cc112dSEd Tanous uint64_t fileRevision = 0; 5852cc112dSEd Tanous if (persistentFile.is_open()) 5952cc112dSEd Tanous { 6052cc112dSEd Tanous // call with exceptions disabled 6152cc112dSEd Tanous auto data = nlohmann::json::parse(persistentFile, nullptr, false); 6252cc112dSEd Tanous if (data.is_discarded()) 6352cc112dSEd Tanous { 6452cc112dSEd Tanous BMCWEB_LOG_ERROR 6552cc112dSEd Tanous << "Error parsing persistent data in json file."; 6652cc112dSEd Tanous } 6752cc112dSEd Tanous else 6852cc112dSEd Tanous { 6952cc112dSEd Tanous for (const auto& item : data.items()) 7052cc112dSEd Tanous { 7152cc112dSEd Tanous if (item.key() == "revision") 7252cc112dSEd Tanous { 7352cc112dSEd Tanous fileRevision = 0; 7452cc112dSEd Tanous 7552cc112dSEd Tanous const uint64_t* uintPtr = 7652cc112dSEd Tanous item.value().get_ptr<const uint64_t*>(); 7752cc112dSEd Tanous if (uintPtr == nullptr) 7852cc112dSEd Tanous { 7952cc112dSEd Tanous BMCWEB_LOG_ERROR << "Failed to read revision flag"; 8052cc112dSEd Tanous } 8152cc112dSEd Tanous else 8252cc112dSEd Tanous { 8352cc112dSEd Tanous fileRevision = *uintPtr; 8452cc112dSEd Tanous } 8552cc112dSEd Tanous } 8652cc112dSEd Tanous else if (item.key() == "system_uuid") 8752cc112dSEd Tanous { 8852cc112dSEd Tanous const std::string* jSystemUuid = 8952cc112dSEd Tanous item.value().get_ptr<const std::string*>(); 9052cc112dSEd Tanous if (jSystemUuid != nullptr) 9152cc112dSEd Tanous { 9252cc112dSEd Tanous systemUuid = *jSystemUuid; 9352cc112dSEd Tanous } 9452cc112dSEd Tanous } 9552cc112dSEd Tanous else if (item.key() == "auth_config") 9652cc112dSEd Tanous { 9752cc112dSEd Tanous SessionStore::getInstance() 9852cc112dSEd Tanous .getAuthMethodsConfig() 9952cc112dSEd Tanous .fromJson(item.value()); 10052cc112dSEd Tanous } 10152cc112dSEd Tanous else if (item.key() == "sessions") 10252cc112dSEd Tanous { 10352cc112dSEd Tanous for (const auto& elem : item.value()) 10452cc112dSEd Tanous { 10552cc112dSEd Tanous std::shared_ptr<UserSession> newSession = 10652cc112dSEd Tanous UserSession::fromJson(elem); 10752cc112dSEd Tanous 10852cc112dSEd Tanous if (newSession == nullptr) 10952cc112dSEd Tanous { 11052cc112dSEd Tanous BMCWEB_LOG_ERROR << "Problem reading session " 11152cc112dSEd Tanous "from persistent store"; 11252cc112dSEd Tanous continue; 11352cc112dSEd Tanous } 11452cc112dSEd Tanous 11552cc112dSEd Tanous BMCWEB_LOG_DEBUG 11652cc112dSEd Tanous << "Restored session: " << newSession->csrfToken 11752cc112dSEd Tanous << " " << newSession->uniqueId << " " 11852cc112dSEd Tanous << newSession->sessionToken; 11952cc112dSEd Tanous SessionStore::getInstance().authTokens.emplace( 12052cc112dSEd Tanous newSession->sessionToken, newSession); 12152cc112dSEd Tanous } 12252cc112dSEd Tanous } 123f2a4a606SManojkiran Eda else if (item.key() == "timeout") 124f2a4a606SManojkiran Eda { 125f2a4a606SManojkiran Eda const int64_t* jTimeout = 126f2a4a606SManojkiran Eda item.value().get_ptr<int64_t*>(); 127f2a4a606SManojkiran Eda if (jTimeout == nullptr) 128f2a4a606SManojkiran Eda { 129f2a4a606SManojkiran Eda BMCWEB_LOG_DEBUG 130f2a4a606SManojkiran Eda << "Problem reading session timeout value"; 131f2a4a606SManojkiran Eda continue; 132f2a4a606SManojkiran Eda } 133f2a4a606SManojkiran Eda std::chrono::seconds sessionTimeoutInseconds(*jTimeout); 134f2a4a606SManojkiran Eda BMCWEB_LOG_DEBUG << "Restored Session Timeout: " 135f2a4a606SManojkiran Eda << sessionTimeoutInseconds.count(); 136f2a4a606SManojkiran Eda SessionStore::getInstance().updateSessionTimeout( 137f2a4a606SManojkiran Eda sessionTimeoutInseconds); 138f2a4a606SManojkiran Eda } 13928afb49cSJunLin Chen else if (item.key() == "eventservice_config") 14028afb49cSJunLin Chen { 14128afb49cSJunLin Chen EventServiceStore::getInstance() 14228afb49cSJunLin Chen .getEventServiceConfig() 14328afb49cSJunLin Chen .fromJson(item.value()); 14428afb49cSJunLin Chen } 14528afb49cSJunLin Chen else if (item.key() == "subscriptions") 14628afb49cSJunLin Chen { 14728afb49cSJunLin Chen for (const auto& elem : item.value()) 14828afb49cSJunLin Chen { 14928afb49cSJunLin Chen std::shared_ptr<UserSubscription> newSubscription = 15028afb49cSJunLin Chen UserSubscription::fromJson(elem); 15128afb49cSJunLin Chen 15228afb49cSJunLin Chen if (newSubscription == nullptr) 15328afb49cSJunLin Chen { 15428afb49cSJunLin Chen BMCWEB_LOG_ERROR 15528afb49cSJunLin Chen << "Problem reading subscription " 15628afb49cSJunLin Chen "from persistent store"; 15728afb49cSJunLin Chen continue; 15828afb49cSJunLin Chen } 15928afb49cSJunLin Chen 16028afb49cSJunLin Chen BMCWEB_LOG_DEBUG << "Restored subscription: " 16128afb49cSJunLin Chen << newSubscription->id << " " 16228afb49cSJunLin Chen << newSubscription->customText; 16328afb49cSJunLin Chen EventServiceStore::getInstance() 16428afb49cSJunLin Chen .subscriptionsConfigMap.emplace( 16528afb49cSJunLin Chen newSubscription->id, newSubscription); 16628afb49cSJunLin Chen } 16728afb49cSJunLin Chen } 16852cc112dSEd Tanous else 16952cc112dSEd Tanous { 17052cc112dSEd Tanous // Do nothing in the case of extra fields. We may have 17152cc112dSEd Tanous // cases where fields are added in the future, and we 17252cc112dSEd Tanous // want to at least attempt to gracefully support 17352cc112dSEd Tanous // downgrades in that case, even if we don't officially 17452cc112dSEd Tanous // support it 17552cc112dSEd Tanous } 17652cc112dSEd Tanous } 17752cc112dSEd Tanous } 17852cc112dSEd Tanous } 17952cc112dSEd Tanous bool needWrite = false; 18052cc112dSEd Tanous 18152cc112dSEd Tanous if (systemUuid.empty()) 18252cc112dSEd Tanous { 18352cc112dSEd Tanous systemUuid = 18452cc112dSEd Tanous boost::uuids::to_string(boost::uuids::random_generator()()); 18552cc112dSEd Tanous needWrite = true; 18652cc112dSEd Tanous } 18752cc112dSEd Tanous if (fileRevision < jsonRevision) 18852cc112dSEd Tanous { 18952cc112dSEd Tanous needWrite = true; 19052cc112dSEd Tanous } 19152cc112dSEd Tanous // write revision changes or system uuid changes immediately 19252cc112dSEd Tanous if (needWrite) 19352cc112dSEd Tanous { 19452cc112dSEd Tanous writeData(); 19552cc112dSEd Tanous } 19652cc112dSEd Tanous } 19752cc112dSEd Tanous 19852cc112dSEd Tanous void writeData() 19952cc112dSEd Tanous { 20052cc112dSEd Tanous std::ofstream persistentFile(filename); 20152cc112dSEd Tanous 20252cc112dSEd Tanous // set the permission of the file to 640 20352cc112dSEd Tanous std::filesystem::perms permission = 20452cc112dSEd Tanous std::filesystem::perms::owner_read | 20552cc112dSEd Tanous std::filesystem::perms::owner_write | 20652cc112dSEd Tanous std::filesystem::perms::group_read; 20752cc112dSEd Tanous std::filesystem::permissions(filename, permission); 2085fb91ba4SEd Tanous const auto& c = SessionStore::getInstance().getAuthMethodsConfig(); 20928afb49cSJunLin Chen const auto& eventServiceConfig = 21028afb49cSJunLin Chen EventServiceStore::getInstance().getEventServiceConfig(); 211dc511aa7SEd Tanous nlohmann::json data{ 212dc511aa7SEd Tanous {"auth_config", 2135fb91ba4SEd Tanous {{"XToken", c.xtoken}, 2145fb91ba4SEd Tanous {"Cookie", c.cookie}, 2155fb91ba4SEd Tanous {"SessionToken", c.sessionToken}, 2165fb91ba4SEd Tanous {"BasicAuth", c.basic}, 2175fb91ba4SEd Tanous {"TLS", c.tls}} 21852cc112dSEd Tanous 2195fb91ba4SEd Tanous }, 22028afb49cSJunLin Chen {"eventservice_config", 22128afb49cSJunLin Chen {{"ServiceEnabled", eventServiceConfig.enabled}, 22228afb49cSJunLin Chen {"DeliveryRetryAttempts", eventServiceConfig.retryAttempts}, 22328afb49cSJunLin Chen {"DeliveryRetryIntervalSeconds", 22428afb49cSJunLin Chen eventServiceConfig.retryTimeoutInterval}} 22528afb49cSJunLin Chen 22628afb49cSJunLin Chen }, 22752cc112dSEd Tanous {"system_uuid", systemUuid}, 228dc511aa7SEd Tanous {"revision", jsonRevision}, 229dc511aa7SEd Tanous {"timeout", SessionStore::getInstance().getTimeoutInSeconds()}}; 2305fb91ba4SEd Tanous 2315fb91ba4SEd Tanous nlohmann::json& sessions = data["sessions"]; 2325fb91ba4SEd Tanous sessions = nlohmann::json::array(); 2335fb91ba4SEd Tanous for (const auto& p : SessionStore::getInstance().authTokens) 2345fb91ba4SEd Tanous { 2355fb91ba4SEd Tanous if (p.second->persistence != 2365fb91ba4SEd Tanous persistent_data::PersistenceType::SINGLE_REQUEST) 2375fb91ba4SEd Tanous { 2385fb91ba4SEd Tanous sessions.push_back({ 2395fb91ba4SEd Tanous {"unique_id", p.second->uniqueId}, 2405fb91ba4SEd Tanous {"session_token", p.second->sessionToken}, 2415fb91ba4SEd Tanous {"username", p.second->username}, 2425fb91ba4SEd Tanous {"csrf_token", p.second->csrfToken}, 243c0ea7ae1SSunitha Harish {"client_ip", p.second->clientIp}, 2445fb91ba4SEd Tanous #ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE 2455fb91ba4SEd Tanous {"client_id", p.second->clientId}, 2465fb91ba4SEd Tanous #endif 2475fb91ba4SEd Tanous }); 2485fb91ba4SEd Tanous } 2495fb91ba4SEd Tanous } 25028afb49cSJunLin Chen nlohmann::json& subscriptions = data["subscriptions"]; 25128afb49cSJunLin Chen subscriptions = nlohmann::json::array(); 25228afb49cSJunLin Chen for (const auto& it : 25328afb49cSJunLin Chen EventServiceStore::getInstance().subscriptionsConfigMap) 25428afb49cSJunLin Chen { 25528afb49cSJunLin Chen std::shared_ptr<UserSubscription> subValue = it.second; 25628afb49cSJunLin Chen if (subValue->subscriptionType == "SSE") 25728afb49cSJunLin Chen { 25828afb49cSJunLin Chen BMCWEB_LOG_DEBUG 25928afb49cSJunLin Chen << "The subscription type is SSE, so skipping."; 26028afb49cSJunLin Chen continue; 26128afb49cSJunLin Chen } 262601c71aeSEd Tanous nlohmann::json::object_t headers; 263601c71aeSEd Tanous for (const boost::beast::http::fields::value_type& header : 264601c71aeSEd Tanous subValue->httpHeaders) 265601c71aeSEd Tanous { 266601c71aeSEd Tanous // Note, these are technically copies because nlohmann doesn't 267601c71aeSEd Tanous // support key lookup by std::string_view. At least the 268601c71aeSEd Tanous // following code can use move 269601c71aeSEd Tanous // https://github.com/nlohmann/json/issues/1529 270601c71aeSEd Tanous std::string name(header.name_string()); 271601c71aeSEd Tanous headers[std::move(name)] = header.value(); 272601c71aeSEd Tanous } 273601c71aeSEd Tanous 27428afb49cSJunLin Chen subscriptions.push_back({ 27528afb49cSJunLin Chen {"Id", subValue->id}, 27628afb49cSJunLin Chen {"Context", subValue->customText}, 27728afb49cSJunLin Chen {"DeliveryRetryPolicy", subValue->retryPolicy}, 27828afb49cSJunLin Chen {"Destination", subValue->destinationUrl}, 27928afb49cSJunLin Chen {"EventFormatType", subValue->eventFormatType}, 280601c71aeSEd Tanous {"HttpHeaders", std::move(headers)}, 28128afb49cSJunLin Chen {"MessageIds", subValue->registryMsgIds}, 28228afb49cSJunLin Chen {"Protocol", subValue->protocol}, 28328afb49cSJunLin Chen {"RegistryPrefixes", subValue->registryPrefixes}, 28428afb49cSJunLin Chen {"ResourceTypes", subValue->resourceTypes}, 28528afb49cSJunLin Chen {"SubscriptionType", subValue->subscriptionType}, 28628afb49cSJunLin Chen {"MetricReportDefinitions", subValue->metricReportDefinitions}, 28728afb49cSJunLin Chen }); 28828afb49cSJunLin Chen } 28952cc112dSEd Tanous persistentFile << data; 29052cc112dSEd Tanous } 29152cc112dSEd Tanous 29252cc112dSEd Tanous std::string systemUuid{""}; 29352cc112dSEd Tanous }; 29452cc112dSEd Tanous 29552cc112dSEd Tanous inline ConfigFile& getConfig() 29652cc112dSEd Tanous { 29752cc112dSEd Tanous static ConfigFile f; 29852cc112dSEd Tanous return f; 29952cc112dSEd Tanous } 30052cc112dSEd Tanous 30152cc112dSEd Tanous } // namespace persistent_data 302