152cc112dSEd Tanous #pragma once 252cc112dSEd Tanous 33ccb3adbSEd Tanous #include "event_service_store.hpp" 43ccb3adbSEd Tanous #include "http_request.hpp" 53ccb3adbSEd Tanous #include "http_response.hpp" 62c6ffdb0SEd Tanous #include "ossl_random.hpp" 73ccb3adbSEd Tanous #include "sessions.hpp" 83ccb3adbSEd Tanous 9601c71aeSEd Tanous #include <boost/beast/http/fields.hpp> 1052cc112dSEd Tanous #include <nlohmann/json.hpp> 1152cc112dSEd Tanous 1252cc112dSEd Tanous #include <filesystem> 1352cc112dSEd Tanous #include <fstream> 1452cc112dSEd Tanous #include <random> 1552cc112dSEd Tanous 1652cc112dSEd Tanous namespace persistent_data 1752cc112dSEd Tanous { 1852cc112dSEd Tanous 1952cc112dSEd Tanous class ConfigFile 2052cc112dSEd Tanous { 2152cc112dSEd Tanous uint64_t jsonRevision = 1; 2252cc112dSEd Tanous 2352cc112dSEd Tanous public: 2452cc112dSEd Tanous // todo(ed) should read this from a fixed location somewhere, not CWD 2552cc112dSEd Tanous static constexpr const char* filename = "bmcweb_persistent_data.json"; 2652cc112dSEd Tanous 2752cc112dSEd Tanous ConfigFile() 2852cc112dSEd Tanous { 2952cc112dSEd Tanous readData(); 3052cc112dSEd Tanous } 3152cc112dSEd Tanous 3252cc112dSEd Tanous ~ConfigFile() 3352cc112dSEd Tanous { 3483cf8189SGunnar Mills // Make sure we aren't writing stale sessions 3583cf8189SGunnar Mills persistent_data::SessionStore::getInstance().applySessionTimeouts(); 3652cc112dSEd Tanous if (persistent_data::SessionStore::getInstance().needsWrite()) 3752cc112dSEd Tanous { 3852cc112dSEd Tanous writeData(); 3952cc112dSEd Tanous } 4052cc112dSEd Tanous } 4152cc112dSEd Tanous 42ecd6a3a2SEd Tanous ConfigFile(const ConfigFile&) = delete; 43ecd6a3a2SEd Tanous ConfigFile(ConfigFile&&) = delete; 44ecd6a3a2SEd Tanous ConfigFile& operator=(const ConfigFile&) = delete; 45ecd6a3a2SEd Tanous ConfigFile& operator=(ConfigFile&&) = delete; 46ecd6a3a2SEd Tanous 4752cc112dSEd Tanous // TODO(ed) this should really use protobuf, or some other serialization 4852cc112dSEd Tanous // library, but adding another dependency is somewhat outside the scope of 4952cc112dSEd Tanous // this application for the moment 5052cc112dSEd Tanous void readData() 5152cc112dSEd Tanous { 5252cc112dSEd Tanous std::ifstream persistentFile(filename); 5352cc112dSEd Tanous uint64_t fileRevision = 0; 5452cc112dSEd Tanous if (persistentFile.is_open()) 5552cc112dSEd Tanous { 5652cc112dSEd Tanous // call with exceptions disabled 5752cc112dSEd Tanous auto data = nlohmann::json::parse(persistentFile, nullptr, false); 5852cc112dSEd Tanous if (data.is_discarded()) 5952cc112dSEd Tanous { 60*62598e31SEd Tanous BMCWEB_LOG_ERROR("Error parsing persistent data in json file."); 6152cc112dSEd Tanous } 6252cc112dSEd Tanous else 6352cc112dSEd Tanous { 6452cc112dSEd Tanous for (const auto& item : data.items()) 6552cc112dSEd Tanous { 6652cc112dSEd Tanous if (item.key() == "revision") 6752cc112dSEd Tanous { 6852cc112dSEd Tanous fileRevision = 0; 6952cc112dSEd Tanous 7052cc112dSEd Tanous const uint64_t* uintPtr = 7152cc112dSEd Tanous item.value().get_ptr<const uint64_t*>(); 7252cc112dSEd Tanous if (uintPtr == nullptr) 7352cc112dSEd Tanous { 74*62598e31SEd Tanous BMCWEB_LOG_ERROR("Failed to read revision flag"); 7552cc112dSEd Tanous } 7652cc112dSEd Tanous else 7752cc112dSEd Tanous { 7852cc112dSEd Tanous fileRevision = *uintPtr; 7952cc112dSEd Tanous } 8052cc112dSEd Tanous } 8152cc112dSEd Tanous else if (item.key() == "system_uuid") 8252cc112dSEd Tanous { 8352cc112dSEd Tanous const std::string* jSystemUuid = 8452cc112dSEd Tanous item.value().get_ptr<const std::string*>(); 8552cc112dSEd Tanous if (jSystemUuid != nullptr) 8652cc112dSEd Tanous { 8752cc112dSEd Tanous systemUuid = *jSystemUuid; 8852cc112dSEd Tanous } 8952cc112dSEd Tanous } 9052cc112dSEd Tanous else if (item.key() == "auth_config") 9152cc112dSEd Tanous { 9252cc112dSEd Tanous SessionStore::getInstance() 9352cc112dSEd Tanous .getAuthMethodsConfig() 9452cc112dSEd Tanous .fromJson(item.value()); 9552cc112dSEd Tanous } 9652cc112dSEd Tanous else if (item.key() == "sessions") 9752cc112dSEd Tanous { 9852cc112dSEd Tanous for (const auto& elem : item.value()) 9952cc112dSEd Tanous { 10052cc112dSEd Tanous std::shared_ptr<UserSession> newSession = 10152cc112dSEd Tanous UserSession::fromJson(elem); 10252cc112dSEd Tanous 10352cc112dSEd Tanous if (newSession == nullptr) 10452cc112dSEd Tanous { 105*62598e31SEd Tanous BMCWEB_LOG_ERROR("Problem reading session " 106*62598e31SEd Tanous "from persistent store"); 10752cc112dSEd Tanous continue; 10852cc112dSEd Tanous } 10952cc112dSEd Tanous 110*62598e31SEd Tanous BMCWEB_LOG_DEBUG("Restored session: {} {} {}", 111*62598e31SEd Tanous newSession->csrfToken, 112*62598e31SEd Tanous newSession->uniqueId, 113*62598e31SEd Tanous newSession->sessionToken); 11452cc112dSEd Tanous SessionStore::getInstance().authTokens.emplace( 11552cc112dSEd Tanous newSession->sessionToken, newSession); 11652cc112dSEd Tanous } 11752cc112dSEd Tanous } 118f2a4a606SManojkiran Eda else if (item.key() == "timeout") 119f2a4a606SManojkiran Eda { 120f2a4a606SManojkiran Eda const int64_t* jTimeout = 121f2a4a606SManojkiran Eda item.value().get_ptr<int64_t*>(); 122f2a4a606SManojkiran Eda if (jTimeout == nullptr) 123f2a4a606SManojkiran Eda { 124*62598e31SEd Tanous BMCWEB_LOG_DEBUG( 125*62598e31SEd Tanous "Problem reading session timeout value"); 126f2a4a606SManojkiran Eda continue; 127f2a4a606SManojkiran Eda } 128f2a4a606SManojkiran Eda std::chrono::seconds sessionTimeoutInseconds(*jTimeout); 129*62598e31SEd Tanous BMCWEB_LOG_DEBUG("Restored Session Timeout: {}", 130*62598e31SEd Tanous sessionTimeoutInseconds.count()); 131f2a4a606SManojkiran Eda SessionStore::getInstance().updateSessionTimeout( 132f2a4a606SManojkiran Eda sessionTimeoutInseconds); 133f2a4a606SManojkiran Eda } 13428afb49cSJunLin Chen else if (item.key() == "eventservice_config") 13528afb49cSJunLin Chen { 13628afb49cSJunLin Chen EventServiceStore::getInstance() 13728afb49cSJunLin Chen .getEventServiceConfig() 13828afb49cSJunLin Chen .fromJson(item.value()); 13928afb49cSJunLin Chen } 14028afb49cSJunLin Chen else if (item.key() == "subscriptions") 14128afb49cSJunLin Chen { 14228afb49cSJunLin Chen for (const auto& elem : item.value()) 14328afb49cSJunLin Chen { 14428afb49cSJunLin Chen std::shared_ptr<UserSubscription> newSubscription = 14528afb49cSJunLin Chen UserSubscription::fromJson(elem); 14628afb49cSJunLin Chen 14728afb49cSJunLin Chen if (newSubscription == nullptr) 14828afb49cSJunLin Chen { 149*62598e31SEd Tanous BMCWEB_LOG_ERROR("Problem reading subscription " 150*62598e31SEd Tanous "from persistent store"); 15128afb49cSJunLin Chen continue; 15228afb49cSJunLin Chen } 15328afb49cSJunLin Chen 154*62598e31SEd Tanous BMCWEB_LOG_DEBUG("Restored subscription: {} {}", 155*62598e31SEd Tanous newSubscription->id, 156*62598e31SEd Tanous newSubscription->customText); 15728afb49cSJunLin Chen EventServiceStore::getInstance() 15828afb49cSJunLin Chen .subscriptionsConfigMap.emplace( 15928afb49cSJunLin Chen newSubscription->id, newSubscription); 16028afb49cSJunLin Chen } 16128afb49cSJunLin Chen } 16252cc112dSEd Tanous else 16352cc112dSEd Tanous { 16452cc112dSEd Tanous // Do nothing in the case of extra fields. We may have 16552cc112dSEd Tanous // cases where fields are added in the future, and we 16652cc112dSEd Tanous // want to at least attempt to gracefully support 16752cc112dSEd Tanous // downgrades in that case, even if we don't officially 16852cc112dSEd Tanous // support it 16952cc112dSEd Tanous } 17052cc112dSEd Tanous } 17152cc112dSEd Tanous } 17252cc112dSEd Tanous } 17352cc112dSEd Tanous bool needWrite = false; 17452cc112dSEd Tanous 17552cc112dSEd Tanous if (systemUuid.empty()) 17652cc112dSEd Tanous { 1772c6ffdb0SEd Tanous systemUuid = bmcweb::getRandomUUID(); 17852cc112dSEd Tanous needWrite = true; 17952cc112dSEd Tanous } 18052cc112dSEd Tanous if (fileRevision < jsonRevision) 18152cc112dSEd Tanous { 18252cc112dSEd Tanous needWrite = true; 18352cc112dSEd Tanous } 18452cc112dSEd Tanous // write revision changes or system uuid changes immediately 18552cc112dSEd Tanous if (needWrite) 18652cc112dSEd Tanous { 18752cc112dSEd Tanous writeData(); 18852cc112dSEd Tanous } 18952cc112dSEd Tanous } 19052cc112dSEd Tanous 19152cc112dSEd Tanous void writeData() 19252cc112dSEd Tanous { 19352cc112dSEd Tanous std::ofstream persistentFile(filename); 19452cc112dSEd Tanous 19552cc112dSEd Tanous // set the permission of the file to 640 19652cc112dSEd Tanous std::filesystem::perms permission = 19752cc112dSEd Tanous std::filesystem::perms::owner_read | 19852cc112dSEd Tanous std::filesystem::perms::owner_write | 19952cc112dSEd Tanous std::filesystem::perms::group_read; 20052cc112dSEd Tanous std::filesystem::permissions(filename, permission); 2015fb91ba4SEd Tanous const auto& c = SessionStore::getInstance().getAuthMethodsConfig(); 20228afb49cSJunLin Chen const auto& eventServiceConfig = 20328afb49cSJunLin Chen EventServiceStore::getInstance().getEventServiceConfig(); 2041476687dSEd Tanous nlohmann::json::object_t data; 2051476687dSEd Tanous nlohmann::json& authConfig = data["auth_config"]; 20652cc112dSEd Tanous 2071476687dSEd Tanous authConfig["XToken"] = c.xtoken; 2081476687dSEd Tanous authConfig["Cookie"] = c.cookie; 2091476687dSEd Tanous authConfig["SessionToken"] = c.sessionToken; 2101476687dSEd Tanous authConfig["BasicAuth"] = c.basic; 2111476687dSEd Tanous authConfig["TLS"] = c.tls; 21228afb49cSJunLin Chen 2131476687dSEd Tanous nlohmann::json& eventserviceConfig = data["eventservice_config"]; 2141476687dSEd Tanous eventserviceConfig["ServiceEnabled"] = eventServiceConfig.enabled; 2151476687dSEd Tanous eventserviceConfig["DeliveryRetryAttempts"] = 2161476687dSEd Tanous eventServiceConfig.retryAttempts; 2171476687dSEd Tanous eventserviceConfig["DeliveryRetryIntervalSeconds"] = 2181476687dSEd Tanous eventServiceConfig.retryTimeoutInterval; 2191476687dSEd Tanous 2201476687dSEd Tanous data["system_uuid"] = systemUuid; 2211476687dSEd Tanous data["revision"] = jsonRevision; 2221476687dSEd Tanous data["timeout"] = SessionStore::getInstance().getTimeoutInSeconds(); 2235fb91ba4SEd Tanous 2245fb91ba4SEd Tanous nlohmann::json& sessions = data["sessions"]; 2255fb91ba4SEd Tanous sessions = nlohmann::json::array(); 2265fb91ba4SEd Tanous for (const auto& p : SessionStore::getInstance().authTokens) 2275fb91ba4SEd Tanous { 2285fb91ba4SEd Tanous if (p.second->persistence != 2295fb91ba4SEd Tanous persistent_data::PersistenceType::SINGLE_REQUEST) 2305fb91ba4SEd Tanous { 2311476687dSEd Tanous nlohmann::json::object_t session; 2321476687dSEd Tanous session["unique_id"] = p.second->uniqueId; 2331476687dSEd Tanous session["session_token"] = p.second->sessionToken; 2341476687dSEd Tanous session["username"] = p.second->username; 2351476687dSEd Tanous session["csrf_token"] = p.second->csrfToken; 2361476687dSEd Tanous session["client_ip"] = p.second->clientIp; 237bb759e3aSEd Tanous if (p.second->clientId) 238bb759e3aSEd Tanous { 239bb759e3aSEd Tanous session["client_id"] = *p.second->clientId; 240bb759e3aSEd Tanous } 241b2ba3072SPatrick Williams sessions.emplace_back(std::move(session)); 2425fb91ba4SEd Tanous } 2435fb91ba4SEd Tanous } 24428afb49cSJunLin Chen nlohmann::json& subscriptions = data["subscriptions"]; 24528afb49cSJunLin Chen subscriptions = nlohmann::json::array(); 24628afb49cSJunLin Chen for (const auto& it : 24728afb49cSJunLin Chen EventServiceStore::getInstance().subscriptionsConfigMap) 24828afb49cSJunLin Chen { 24928afb49cSJunLin Chen std::shared_ptr<UserSubscription> subValue = it.second; 25028afb49cSJunLin Chen if (subValue->subscriptionType == "SSE") 25128afb49cSJunLin Chen { 252*62598e31SEd Tanous BMCWEB_LOG_DEBUG("The subscription type is SSE, so skipping."); 25328afb49cSJunLin Chen continue; 25428afb49cSJunLin Chen } 255601c71aeSEd Tanous nlohmann::json::object_t headers; 256601c71aeSEd Tanous for (const boost::beast::http::fields::value_type& header : 257601c71aeSEd Tanous subValue->httpHeaders) 258601c71aeSEd Tanous { 259601c71aeSEd Tanous // Note, these are technically copies because nlohmann doesn't 260601c71aeSEd Tanous // support key lookup by std::string_view. At least the 261601c71aeSEd Tanous // following code can use move 262601c71aeSEd Tanous // https://github.com/nlohmann/json/issues/1529 263601c71aeSEd Tanous std::string name(header.name_string()); 264601c71aeSEd Tanous headers[std::move(name)] = header.value(); 265601c71aeSEd Tanous } 266601c71aeSEd Tanous 2671476687dSEd Tanous nlohmann::json::object_t subscription; 2681476687dSEd Tanous 2691476687dSEd Tanous subscription["Id"] = subValue->id; 2701476687dSEd Tanous subscription["Context"] = subValue->customText; 2711476687dSEd Tanous subscription["DeliveryRetryPolicy"] = subValue->retryPolicy; 2721476687dSEd Tanous subscription["Destination"] = subValue->destinationUrl; 2731476687dSEd Tanous subscription["EventFormatType"] = subValue->eventFormatType; 2741476687dSEd Tanous subscription["HttpHeaders"] = std::move(headers); 2751476687dSEd Tanous subscription["MessageIds"] = subValue->registryMsgIds; 2761476687dSEd Tanous subscription["Protocol"] = subValue->protocol; 2771476687dSEd Tanous subscription["RegistryPrefixes"] = subValue->registryPrefixes; 2781476687dSEd Tanous subscription["ResourceTypes"] = subValue->resourceTypes; 2791476687dSEd Tanous subscription["SubscriptionType"] = subValue->subscriptionType; 2801476687dSEd Tanous subscription["MetricReportDefinitions"] = 2811476687dSEd Tanous subValue->metricReportDefinitions; 2821476687dSEd Tanous 283b2ba3072SPatrick Williams subscriptions.emplace_back(std::move(subscription)); 28428afb49cSJunLin Chen } 28552cc112dSEd Tanous persistentFile << data; 28652cc112dSEd Tanous } 28752cc112dSEd Tanous 288e05aec50SEd Tanous std::string systemUuid; 28952cc112dSEd Tanous }; 29052cc112dSEd Tanous 29152cc112dSEd Tanous inline ConfigFile& getConfig() 29252cc112dSEd Tanous { 29352cc112dSEd Tanous static ConfigFile f; 29452cc112dSEd Tanous return f; 29552cc112dSEd Tanous } 29652cc112dSEd Tanous 29752cc112dSEd Tanous } // namespace persistent_data 298