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