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