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 
ConfigFile()2752cc112dSEd Tanous     ConfigFile()
2852cc112dSEd Tanous     {
2952cc112dSEd Tanous         readData();
3052cc112dSEd Tanous     }
3152cc112dSEd Tanous 
~ConfigFile()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
readData()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             {
6062598e31SEd Tanous                 BMCWEB_LOG_ERROR("Error parsing persistent data in json file.");
6152cc112dSEd Tanous             }
6252cc112dSEd Tanous             else
6352cc112dSEd Tanous             {
64*0bdda665SEd Tanous                 const nlohmann::json::object_t* obj =
65*0bdda665SEd Tanous                     data.get_ptr<nlohmann::json::object_t*>();
66*0bdda665SEd Tanous                 if (obj == nullptr)
6752cc112dSEd Tanous                 {
68*0bdda665SEd Tanous                     return;
69*0bdda665SEd Tanous                 }
70*0bdda665SEd Tanous                 for (const auto& item : *obj)
71*0bdda665SEd Tanous                 {
72*0bdda665SEd Tanous                     if (item.first == "revision")
7352cc112dSEd Tanous                     {
7452cc112dSEd Tanous                         fileRevision = 0;
7552cc112dSEd Tanous 
7652cc112dSEd Tanous                         const uint64_t* uintPtr =
77*0bdda665SEd Tanous                             item.second.get_ptr<const uint64_t*>();
7852cc112dSEd Tanous                         if (uintPtr == nullptr)
7952cc112dSEd Tanous                         {
8062598e31SEd Tanous                             BMCWEB_LOG_ERROR("Failed to read revision flag");
8152cc112dSEd Tanous                         }
8252cc112dSEd Tanous                         else
8352cc112dSEd Tanous                         {
8452cc112dSEd Tanous                             fileRevision = *uintPtr;
8552cc112dSEd Tanous                         }
8652cc112dSEd Tanous                     }
87*0bdda665SEd Tanous                     else if (item.first == "system_uuid")
8852cc112dSEd Tanous                     {
8952cc112dSEd Tanous                         const std::string* jSystemUuid =
90*0bdda665SEd Tanous                             item.second.get_ptr<const std::string*>();
9152cc112dSEd Tanous                         if (jSystemUuid != nullptr)
9252cc112dSEd Tanous                         {
9352cc112dSEd Tanous                             systemUuid = *jSystemUuid;
9452cc112dSEd Tanous                         }
9552cc112dSEd Tanous                     }
96*0bdda665SEd Tanous                     else if (item.first == "auth_config")
9752cc112dSEd Tanous                     {
9852cc112dSEd Tanous                         SessionStore::getInstance()
9952cc112dSEd Tanous                             .getAuthMethodsConfig()
100*0bdda665SEd Tanous                             .fromJson(item.second);
10152cc112dSEd Tanous                     }
102*0bdda665SEd Tanous                     else if (item.first == "sessions")
10352cc112dSEd Tanous                     {
104*0bdda665SEd Tanous                         for (const auto& elem : item.second)
10552cc112dSEd Tanous                         {
10652cc112dSEd Tanous                             std::shared_ptr<UserSession> newSession =
10752cc112dSEd Tanous                                 UserSession::fromJson(elem);
10852cc112dSEd Tanous 
10952cc112dSEd Tanous                             if (newSession == nullptr)
11052cc112dSEd Tanous                             {
11162598e31SEd Tanous                                 BMCWEB_LOG_ERROR("Problem reading session "
11262598e31SEd Tanous                                                  "from persistent store");
11352cc112dSEd Tanous                                 continue;
11452cc112dSEd Tanous                             }
11552cc112dSEd Tanous 
11662598e31SEd Tanous                             BMCWEB_LOG_DEBUG("Restored session: {} {} {}",
11762598e31SEd Tanous                                              newSession->csrfToken,
11862598e31SEd Tanous                                              newSession->uniqueId,
11962598e31SEd Tanous                                              newSession->sessionToken);
12052cc112dSEd Tanous                             SessionStore::getInstance().authTokens.emplace(
12152cc112dSEd Tanous                                 newSession->sessionToken, newSession);
12252cc112dSEd Tanous                         }
12352cc112dSEd Tanous                     }
124*0bdda665SEd Tanous                     else if (item.first == "timeout")
125f2a4a606SManojkiran Eda                     {
126f2a4a606SManojkiran Eda                         const int64_t* jTimeout =
127*0bdda665SEd Tanous                             item.second.get_ptr<const int64_t*>();
128f2a4a606SManojkiran Eda                         if (jTimeout == nullptr)
129f2a4a606SManojkiran Eda                         {
13062598e31SEd Tanous                             BMCWEB_LOG_DEBUG(
13162598e31SEd Tanous                                 "Problem reading session timeout value");
132f2a4a606SManojkiran Eda                             continue;
133f2a4a606SManojkiran Eda                         }
134f2a4a606SManojkiran Eda                         std::chrono::seconds sessionTimeoutInseconds(*jTimeout);
13562598e31SEd Tanous                         BMCWEB_LOG_DEBUG("Restored Session Timeout: {}",
13662598e31SEd Tanous                                          sessionTimeoutInseconds.count());
137f2a4a606SManojkiran Eda                         SessionStore::getInstance().updateSessionTimeout(
138f2a4a606SManojkiran Eda                             sessionTimeoutInseconds);
139f2a4a606SManojkiran Eda                     }
140*0bdda665SEd Tanous                     else if (item.first == "eventservice_config")
14128afb49cSJunLin Chen                     {
142*0bdda665SEd Tanous                         const nlohmann::json::object_t* esobj =
143*0bdda665SEd Tanous                             item.second
144*0bdda665SEd Tanous                                 .get_ptr<const nlohmann::json::object_t*>();
145*0bdda665SEd Tanous                         if (esobj == nullptr)
146*0bdda665SEd Tanous                         {
147*0bdda665SEd Tanous                             BMCWEB_LOG_DEBUG(
148*0bdda665SEd Tanous                                 "Problem reading EventService value");
149*0bdda665SEd Tanous                             continue;
150*0bdda665SEd Tanous                         }
151*0bdda665SEd Tanous 
15228afb49cSJunLin Chen                         EventServiceStore::getInstance()
15328afb49cSJunLin Chen                             .getEventServiceConfig()
154*0bdda665SEd Tanous                             .fromJson(*esobj);
15528afb49cSJunLin Chen                     }
156*0bdda665SEd Tanous                     else if (item.first == "subscriptions")
15728afb49cSJunLin Chen                     {
158*0bdda665SEd Tanous                         for (const auto& elem : item.second)
15928afb49cSJunLin Chen                         {
16028afb49cSJunLin Chen                             std::shared_ptr<UserSubscription> newSubscription =
16128afb49cSJunLin Chen                                 UserSubscription::fromJson(elem);
16228afb49cSJunLin Chen 
16328afb49cSJunLin Chen                             if (newSubscription == nullptr)
16428afb49cSJunLin Chen                             {
16562598e31SEd Tanous                                 BMCWEB_LOG_ERROR("Problem reading subscription "
16662598e31SEd Tanous                                                  "from persistent store");
16728afb49cSJunLin Chen                                 continue;
16828afb49cSJunLin Chen                             }
16928afb49cSJunLin Chen 
17062598e31SEd Tanous                             BMCWEB_LOG_DEBUG("Restored subscription: {} {}",
17162598e31SEd Tanous                                              newSubscription->id,
17262598e31SEd Tanous                                              newSubscription->customText);
17328afb49cSJunLin Chen                             EventServiceStore::getInstance()
17428afb49cSJunLin Chen                                 .subscriptionsConfigMap.emplace(
17528afb49cSJunLin Chen                                     newSubscription->id, newSubscription);
17628afb49cSJunLin Chen                         }
17728afb49cSJunLin Chen                     }
17852cc112dSEd Tanous                     else
17952cc112dSEd Tanous                     {
18052cc112dSEd Tanous                         // Do nothing in the case of extra fields.  We may have
18152cc112dSEd Tanous                         // cases where fields are added in the future, and we
18252cc112dSEd Tanous                         // want to at least attempt to gracefully support
18352cc112dSEd Tanous                         // downgrades in that case, even if we don't officially
18452cc112dSEd Tanous                         // support it
18552cc112dSEd Tanous                     }
18652cc112dSEd Tanous                 }
18752cc112dSEd Tanous             }
18852cc112dSEd Tanous         }
18952cc112dSEd Tanous         bool needWrite = false;
19052cc112dSEd Tanous 
19152cc112dSEd Tanous         if (systemUuid.empty())
19252cc112dSEd Tanous         {
1932c6ffdb0SEd Tanous             systemUuid = bmcweb::getRandomUUID();
19452cc112dSEd Tanous             needWrite = true;
19552cc112dSEd Tanous         }
19652cc112dSEd Tanous         if (fileRevision < jsonRevision)
19752cc112dSEd Tanous         {
19852cc112dSEd Tanous             needWrite = true;
19952cc112dSEd Tanous         }
20052cc112dSEd Tanous         // write revision changes or system uuid changes immediately
20152cc112dSEd Tanous         if (needWrite)
20252cc112dSEd Tanous         {
20352cc112dSEd Tanous             writeData();
20452cc112dSEd Tanous         }
20552cc112dSEd Tanous     }
20652cc112dSEd Tanous 
writeData()20752cc112dSEd Tanous     void writeData()
20852cc112dSEd Tanous     {
20952cc112dSEd Tanous         std::ofstream persistentFile(filename);
21052cc112dSEd Tanous 
21152cc112dSEd Tanous         // set the permission of the file to 640
21252cc112dSEd Tanous         std::filesystem::perms permission =
21352cc112dSEd Tanous             std::filesystem::perms::owner_read |
21452cc112dSEd Tanous             std::filesystem::perms::owner_write |
21552cc112dSEd Tanous             std::filesystem::perms::group_read;
21652cc112dSEd Tanous         std::filesystem::permissions(filename, permission);
2175fb91ba4SEd Tanous         const auto& c = SessionStore::getInstance().getAuthMethodsConfig();
21828afb49cSJunLin Chen         const auto& eventServiceConfig =
21928afb49cSJunLin Chen             EventServiceStore::getInstance().getEventServiceConfig();
2201476687dSEd Tanous         nlohmann::json::object_t data;
2211476687dSEd Tanous         nlohmann::json& authConfig = data["auth_config"];
22252cc112dSEd Tanous 
2231476687dSEd Tanous         authConfig["XToken"] = c.xtoken;
2241476687dSEd Tanous         authConfig["Cookie"] = c.cookie;
2251476687dSEd Tanous         authConfig["SessionToken"] = c.sessionToken;
2261476687dSEd Tanous         authConfig["BasicAuth"] = c.basic;
2271476687dSEd Tanous         authConfig["TLS"] = c.tls;
22828afb49cSJunLin Chen 
2291476687dSEd Tanous         nlohmann::json& eventserviceConfig = data["eventservice_config"];
2301476687dSEd Tanous         eventserviceConfig["ServiceEnabled"] = eventServiceConfig.enabled;
2311476687dSEd Tanous         eventserviceConfig["DeliveryRetryAttempts"] =
2321476687dSEd Tanous             eventServiceConfig.retryAttempts;
2331476687dSEd Tanous         eventserviceConfig["DeliveryRetryIntervalSeconds"] =
2341476687dSEd Tanous             eventServiceConfig.retryTimeoutInterval;
2351476687dSEd Tanous 
2361476687dSEd Tanous         data["system_uuid"] = systemUuid;
2371476687dSEd Tanous         data["revision"] = jsonRevision;
2381476687dSEd Tanous         data["timeout"] = SessionStore::getInstance().getTimeoutInSeconds();
2395fb91ba4SEd Tanous 
2405fb91ba4SEd Tanous         nlohmann::json& sessions = data["sessions"];
2415fb91ba4SEd Tanous         sessions = nlohmann::json::array();
2425fb91ba4SEd Tanous         for (const auto& p : SessionStore::getInstance().authTokens)
2435fb91ba4SEd Tanous         {
2445fb91ba4SEd Tanous             if (p.second->persistence !=
2455fb91ba4SEd Tanous                 persistent_data::PersistenceType::SINGLE_REQUEST)
2465fb91ba4SEd Tanous             {
2471476687dSEd Tanous                 nlohmann::json::object_t session;
2481476687dSEd Tanous                 session["unique_id"] = p.second->uniqueId;
2491476687dSEd Tanous                 session["session_token"] = p.second->sessionToken;
2501476687dSEd Tanous                 session["username"] = p.second->username;
2511476687dSEd Tanous                 session["csrf_token"] = p.second->csrfToken;
2521476687dSEd Tanous                 session["client_ip"] = p.second->clientIp;
253e01d0c36SEd Tanous                 const std::optional<std::string>& clientId = p.second->clientId;
254e01d0c36SEd Tanous                 if (clientId)
255bb759e3aSEd Tanous                 {
256e01d0c36SEd Tanous                     session["client_id"] = *clientId;
257bb759e3aSEd Tanous                 }
258b2ba3072SPatrick Williams                 sessions.emplace_back(std::move(session));
2595fb91ba4SEd Tanous             }
2605fb91ba4SEd Tanous         }
26128afb49cSJunLin Chen         nlohmann::json& subscriptions = data["subscriptions"];
26228afb49cSJunLin Chen         subscriptions = nlohmann::json::array();
26328afb49cSJunLin Chen         for (const auto& it :
26428afb49cSJunLin Chen              EventServiceStore::getInstance().subscriptionsConfigMap)
26528afb49cSJunLin Chen         {
26628afb49cSJunLin Chen             std::shared_ptr<UserSubscription> subValue = it.second;
26728afb49cSJunLin Chen             if (subValue->subscriptionType == "SSE")
26828afb49cSJunLin Chen             {
26962598e31SEd Tanous                 BMCWEB_LOG_DEBUG("The subscription type is SSE, so skipping.");
27028afb49cSJunLin Chen                 continue;
27128afb49cSJunLin Chen             }
272601c71aeSEd Tanous             nlohmann::json::object_t headers;
273601c71aeSEd Tanous             for (const boost::beast::http::fields::value_type& header :
274601c71aeSEd Tanous                  subValue->httpHeaders)
275601c71aeSEd Tanous             {
276601c71aeSEd Tanous                 // Note, these are technically copies because nlohmann doesn't
277601c71aeSEd Tanous                 // support key lookup by std::string_view.  At least the
278601c71aeSEd Tanous                 // following code can use move
279601c71aeSEd Tanous                 // https://github.com/nlohmann/json/issues/1529
280601c71aeSEd Tanous                 std::string name(header.name_string());
281601c71aeSEd Tanous                 headers[std::move(name)] = header.value();
282601c71aeSEd Tanous             }
283601c71aeSEd Tanous 
2841476687dSEd Tanous             nlohmann::json::object_t subscription;
2851476687dSEd Tanous 
2861476687dSEd Tanous             subscription["Id"] = subValue->id;
2871476687dSEd Tanous             subscription["Context"] = subValue->customText;
2881476687dSEd Tanous             subscription["DeliveryRetryPolicy"] = subValue->retryPolicy;
2891476687dSEd Tanous             subscription["Destination"] = subValue->destinationUrl;
2901476687dSEd Tanous             subscription["EventFormatType"] = subValue->eventFormatType;
2911476687dSEd Tanous             subscription["HttpHeaders"] = std::move(headers);
2921476687dSEd Tanous             subscription["MessageIds"] = subValue->registryMsgIds;
2931476687dSEd Tanous             subscription["Protocol"] = subValue->protocol;
2941476687dSEd Tanous             subscription["RegistryPrefixes"] = subValue->registryPrefixes;
2951476687dSEd Tanous             subscription["ResourceTypes"] = subValue->resourceTypes;
2961476687dSEd Tanous             subscription["SubscriptionType"] = subValue->subscriptionType;
2971476687dSEd Tanous             subscription["MetricReportDefinitions"] =
2981476687dSEd Tanous                 subValue->metricReportDefinitions;
2991476687dSEd Tanous 
300b2ba3072SPatrick Williams             subscriptions.emplace_back(std::move(subscription));
30128afb49cSJunLin Chen         }
30252cc112dSEd Tanous         persistentFile << data;
30352cc112dSEd Tanous     }
30452cc112dSEd Tanous 
305e05aec50SEd Tanous     std::string systemUuid;
30652cc112dSEd Tanous };
30752cc112dSEd Tanous 
getConfig()30852cc112dSEd Tanous inline ConfigFile& getConfig()
30952cc112dSEd Tanous {
31052cc112dSEd Tanous     static ConfigFile f;
31152cc112dSEd Tanous     return f;
31252cc112dSEd Tanous }
31352cc112dSEd Tanous 
31452cc112dSEd Tanous } // namespace persistent_data
315