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