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 
46ecd6a3a2SEd Tanous     ConfigFile(const ConfigFile&) = delete;
47ecd6a3a2SEd Tanous     ConfigFile(ConfigFile&&) = delete;
48ecd6a3a2SEd Tanous     ConfigFile& operator=(const ConfigFile&) = delete;
49ecd6a3a2SEd Tanous     ConfigFile& operator=(ConfigFile&&) = delete;
50ecd6a3a2SEd 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 
292*e05aec50SEd 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