blob: 13a43c4ac26aceeadbb9b63052e0402375a02341 [file] [log] [blame]
Ed Tanousba9f9a62017-10-11 16:40:35 -07001#pragma once
2
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08003#include "event_service_store.hpp"
4#include "http_request.hpp"
5#include "http_response.hpp"
Ed Tanous2c6ffdb2023-06-28 11:28:38 -07006#include "ossl_random.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08007#include "sessions.hpp"
8
Ed Tanous601c71a2021-09-08 16:40:12 -07009#include <boost/beast/http/fields.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070010#include <nlohmann/json.hpp>
Ed Tanousba9f9a62017-10-11 16:40:35 -070011
Gunnar Mills1214b7e2020-06-04 10:11:30 -050012#include <filesystem>
James Feist3909dc82020-04-03 10:58:55 -070013#include <fstream>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050014#include <random>
15
Ed Tanous1abe55e2018-09-05 08:30:59 -070016namespace persistent_data
17{
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +010018
Ed Tanous52cc1122020-07-18 13:51:21 -070019class ConfigFile
Ed Tanous1abe55e2018-09-05 08:30:59 -070020{
Ed Tanous271584a2019-07-09 16:24:22 -070021 uint64_t jsonRevision = 1;
Ed Tanousc963aa42017-10-27 16:00:19 -070022
Ed Tanous1abe55e2018-09-05 08:30:59 -070023 public:
Ratan Gupta845cb7d2019-07-12 00:32:25 +053024 // todo(ed) should read this from a fixed location somewhere, not CWD
25 static constexpr const char* filename = "bmcweb_persistent_data.json";
26
Ed Tanous52cc1122020-07-18 13:51:21 -070027 ConfigFile()
Ed Tanous1abe55e2018-09-05 08:30:59 -070028 {
29 readData();
Ed Tanousc963aa42017-10-27 16:00:19 -070030 }
Ed Tanousc963aa42017-10-27 16:00:19 -070031
Ed Tanous52cc1122020-07-18 13:51:21 -070032 ~ConfigFile()
Ed Tanous1abe55e2018-09-05 08:30:59 -070033 {
Gunnar Mills83cf8182020-11-11 15:37:34 -060034 // Make sure we aren't writing stale sessions
35 persistent_data::SessionStore::getInstance().applySessionTimeouts();
Ed Tanous1abe55e2018-09-05 08:30:59 -070036 if (persistent_data::SessionStore::getInstance().needsWrite())
37 {
38 writeData();
Kowalski, Kamil5cef0f72018-02-15 15:26:51 +010039 }
Ed Tanousc963aa42017-10-27 16:00:19 -070040 }
Ed Tanousc963aa42017-10-27 16:00:19 -070041
Ed Tanousecd6a3a2022-01-07 09:18:40 -080042 ConfigFile(const ConfigFile&) = delete;
43 ConfigFile(ConfigFile&&) = delete;
44 ConfigFile& operator=(const ConfigFile&) = delete;
45 ConfigFile& operator=(ConfigFile&&) = delete;
46
Ed Tanous1abe55e2018-09-05 08:30:59 -070047 // TODO(ed) this should really use protobuf, or some other serialization
48 // library, but adding another dependency is somewhat outside the scope of
49 // this application for the moment
50 void readData()
51 {
52 std::ifstream persistentFile(filename);
Ed Tanous271584a2019-07-09 16:24:22 -070053 uint64_t fileRevision = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -070054 if (persistentFile.is_open())
55 {
56 // call with exceptions disabled
57 auto data = nlohmann::json::parse(persistentFile, nullptr, false);
58 if (data.is_discarded())
59 {
Ed Tanous62598e32023-07-17 17:06:25 -070060 BMCWEB_LOG_ERROR("Error parsing persistent data in json file.");
Ed Tanous1abe55e2018-09-05 08:30:59 -070061 }
62 else
63 {
Ed Tanous0bdda662023-08-03 17:27:34 -070064 const nlohmann::json::object_t* obj =
65 data.get_ptr<nlohmann::json::object_t*>();
66 if (obj == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -070067 {
Ed Tanous0bdda662023-08-03 17:27:34 -070068 return;
69 }
70 for (const auto& item : *obj)
71 {
72 if (item.first == "revision")
Ed Tanous1abe55e2018-09-05 08:30:59 -070073 {
74 fileRevision = 0;
75
76 const uint64_t* uintPtr =
Ed Tanous0bdda662023-08-03 17:27:34 -070077 item.second.get_ptr<const uint64_t*>();
Ed Tanous1abe55e2018-09-05 08:30:59 -070078 if (uintPtr == nullptr)
79 {
Ed Tanous62598e32023-07-17 17:06:25 -070080 BMCWEB_LOG_ERROR("Failed to read revision flag");
Ed Tanous1abe55e2018-09-05 08:30:59 -070081 }
82 else
83 {
84 fileRevision = *uintPtr;
85 }
86 }
Ed Tanous0bdda662023-08-03 17:27:34 -070087 else if (item.first == "system_uuid")
Ed Tanous1abe55e2018-09-05 08:30:59 -070088 {
89 const std::string* jSystemUuid =
Ed Tanous0bdda662023-08-03 17:27:34 -070090 item.second.get_ptr<const std::string*>();
Ed Tanous1abe55e2018-09-05 08:30:59 -070091 if (jSystemUuid != nullptr)
92 {
93 systemUuid = *jSystemUuid;
94 }
95 }
Ed Tanous0bdda662023-08-03 17:27:34 -070096 else if (item.first == "auth_config")
Zbigniew Kurzynski78158632019-11-05 12:57:37 +010097 {
98 SessionStore::getInstance()
99 .getAuthMethodsConfig()
Ed Tanous0bdda662023-08-03 17:27:34 -0700100 .fromJson(item.second);
Zbigniew Kurzynski78158632019-11-05 12:57:37 +0100101 }
Ed Tanous0bdda662023-08-03 17:27:34 -0700102 else if (item.first == "sessions")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700103 {
Ed Tanous0bdda662023-08-03 17:27:34 -0700104 for (const auto& elem : item.second)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700105 {
106 std::shared_ptr<UserSession> newSession =
107 UserSession::fromJson(elem);
108
109 if (newSession == nullptr)
110 {
Ed Tanous62598e32023-07-17 17:06:25 -0700111 BMCWEB_LOG_ERROR("Problem reading session "
112 "from persistent store");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700113 continue;
114 }
115
Ed Tanous62598e32023-07-17 17:06:25 -0700116 BMCWEB_LOG_DEBUG("Restored session: {} {} {}",
117 newSession->csrfToken,
118 newSession->uniqueId,
119 newSession->sessionToken);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700120 SessionStore::getInstance().authTokens.emplace(
121 newSession->sessionToken, newSession);
122 }
123 }
Ed Tanous0bdda662023-08-03 17:27:34 -0700124 else if (item.first == "timeout")
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530125 {
126 const int64_t* jTimeout =
Ed Tanous0bdda662023-08-03 17:27:34 -0700127 item.second.get_ptr<const int64_t*>();
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530128 if (jTimeout == nullptr)
129 {
Ed Tanous62598e32023-07-17 17:06:25 -0700130 BMCWEB_LOG_DEBUG(
131 "Problem reading session timeout value");
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530132 continue;
133 }
134 std::chrono::seconds sessionTimeoutInseconds(*jTimeout);
Ed Tanous62598e32023-07-17 17:06:25 -0700135 BMCWEB_LOG_DEBUG("Restored Session Timeout: {}",
136 sessionTimeoutInseconds.count());
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530137 SessionStore::getInstance().updateSessionTimeout(
138 sessionTimeoutInseconds);
139 }
Ed Tanous0bdda662023-08-03 17:27:34 -0700140 else if (item.first == "eventservice_config")
JunLin Chen28afb492021-02-24 17:13:29 +0800141 {
Ed Tanous0bdda662023-08-03 17:27:34 -0700142 const nlohmann::json::object_t* esobj =
143 item.second
144 .get_ptr<const nlohmann::json::object_t*>();
145 if (esobj == nullptr)
146 {
147 BMCWEB_LOG_DEBUG(
148 "Problem reading EventService value");
149 continue;
150 }
151
JunLin Chen28afb492021-02-24 17:13:29 +0800152 EventServiceStore::getInstance()
153 .getEventServiceConfig()
Ed Tanous0bdda662023-08-03 17:27:34 -0700154 .fromJson(*esobj);
JunLin Chen28afb492021-02-24 17:13:29 +0800155 }
Ed Tanous0bdda662023-08-03 17:27:34 -0700156 else if (item.first == "subscriptions")
JunLin Chen28afb492021-02-24 17:13:29 +0800157 {
Ed Tanous0bdda662023-08-03 17:27:34 -0700158 for (const auto& elem : item.second)
JunLin Chen28afb492021-02-24 17:13:29 +0800159 {
Ed Tanous4b712a22023-08-02 12:56:52 -0700160 std::optional<UserSubscription> newSub =
JunLin Chen28afb492021-02-24 17:13:29 +0800161 UserSubscription::fromJson(elem);
162
Ed Tanous4b712a22023-08-02 12:56:52 -0700163 if (!newSub)
JunLin Chen28afb492021-02-24 17:13:29 +0800164 {
Ed Tanous62598e32023-07-17 17:06:25 -0700165 BMCWEB_LOG_ERROR("Problem reading subscription "
166 "from persistent store");
JunLin Chen28afb492021-02-24 17:13:29 +0800167 continue;
168 }
169
Ed Tanous62598e32023-07-17 17:06:25 -0700170 BMCWEB_LOG_DEBUG("Restored subscription: {} {}",
Ed Tanous4b712a22023-08-02 12:56:52 -0700171 newSub->id, newSub->customText);
172
173 boost::container::flat_map<
174 std::string, UserSubscription>& configMap =
175 EventServiceStore::getInstance()
176 .subscriptionsConfigMap;
177 configMap.emplace(newSub->id, *newSub);
JunLin Chen28afb492021-02-24 17:13:29 +0800178 }
179 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700180 else
181 {
182 // Do nothing in the case of extra fields. We may have
183 // cases where fields are added in the future, and we
184 // want to at least attempt to gracefully support
185 // downgrades in that case, even if we don't officially
186 // support it
187 }
188 }
189 }
190 }
191 bool needWrite = false;
192
193 if (systemUuid.empty())
194 {
Ed Tanous2c6ffdb2023-06-28 11:28:38 -0700195 systemUuid = bmcweb::getRandomUUID();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700196 needWrite = true;
197 }
198 if (fileRevision < jsonRevision)
199 {
200 needWrite = true;
201 }
202 // write revision changes or system uuid changes immediately
203 if (needWrite)
204 {
205 writeData();
206 }
207 }
208
209 void writeData()
210 {
211 std::ofstream persistentFile(filename);
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530212
213 // set the permission of the file to 640
Ed Tanous52cc1122020-07-18 13:51:21 -0700214 std::filesystem::perms permission =
215 std::filesystem::perms::owner_read |
216 std::filesystem::perms::owner_write |
217 std::filesystem::perms::group_read;
218 std::filesystem::permissions(filename, permission);
Ed Tanous3ce36882024-06-09 10:58:16 -0700219 const AuthConfigMethods& c =
220 SessionStore::getInstance().getAuthMethodsConfig();
JunLin Chen28afb492021-02-24 17:13:29 +0800221 const auto& eventServiceConfig =
222 EventServiceStore::getInstance().getEventServiceConfig();
Ed Tanous14766872022-03-15 10:44:42 -0700223 nlohmann::json::object_t data;
224 nlohmann::json& authConfig = data["auth_config"];
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530225
Ed Tanous14766872022-03-15 10:44:42 -0700226 authConfig["XToken"] = c.xtoken;
227 authConfig["Cookie"] = c.cookie;
228 authConfig["SessionToken"] = c.sessionToken;
229 authConfig["BasicAuth"] = c.basic;
230 authConfig["TLS"] = c.tls;
Ed Tanous3281bcf2024-06-25 16:02:05 -0700231 authConfig["TLSStrict"] = c.tlsStrict;
Ed Tanous3ce36882024-06-09 10:58:16 -0700232 authConfig["TLSCommonNameParseMode"] =
233 static_cast<int>(c.mTLSCommonNameParsingMode);
JunLin Chen28afb492021-02-24 17:13:29 +0800234
Ed Tanous14766872022-03-15 10:44:42 -0700235 nlohmann::json& eventserviceConfig = data["eventservice_config"];
236 eventserviceConfig["ServiceEnabled"] = eventServiceConfig.enabled;
237 eventserviceConfig["DeliveryRetryAttempts"] =
238 eventServiceConfig.retryAttempts;
239 eventserviceConfig["DeliveryRetryIntervalSeconds"] =
240 eventServiceConfig.retryTimeoutInterval;
241
242 data["system_uuid"] = systemUuid;
243 data["revision"] = jsonRevision;
244 data["timeout"] = SessionStore::getInstance().getTimeoutInSeconds();
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700245
246 nlohmann::json& sessions = data["sessions"];
247 sessions = nlohmann::json::array();
248 for (const auto& p : SessionStore::getInstance().authTokens)
249 {
Ed Tanous89cda632024-04-16 08:45:54 -0700250 if (p.second->sessionType != persistent_data::SessionType::Basic &&
251 p.second->sessionType !=
252 persistent_data::SessionType::MutualTLS)
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700253 {
Ed Tanous14766872022-03-15 10:44:42 -0700254 nlohmann::json::object_t session;
255 session["unique_id"] = p.second->uniqueId;
256 session["session_token"] = p.second->sessionToken;
257 session["username"] = p.second->username;
258 session["csrf_token"] = p.second->csrfToken;
259 session["client_ip"] = p.second->clientIp;
Ed Tanouse01d0c32023-06-30 13:21:32 -0700260 const std::optional<std::string>& clientId = p.second->clientId;
261 if (clientId)
Ed Tanousbb759e32022-08-02 17:07:54 -0700262 {
Ed Tanouse01d0c32023-06-30 13:21:32 -0700263 session["client_id"] = *clientId;
Ed Tanousbb759e32022-08-02 17:07:54 -0700264 }
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500265 sessions.emplace_back(std::move(session));
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700266 }
267 }
JunLin Chen28afb492021-02-24 17:13:29 +0800268 nlohmann::json& subscriptions = data["subscriptions"];
269 subscriptions = nlohmann::json::array();
270 for (const auto& it :
271 EventServiceStore::getInstance().subscriptionsConfigMap)
272 {
Ed Tanous4b712a22023-08-02 12:56:52 -0700273 const UserSubscription& subValue = it.second;
wenlitaofbfb7882024-07-12 11:25:00 +0800274 if (subValue.subscriptionType == "SSE")
JunLin Chen28afb492021-02-24 17:13:29 +0800275 {
Ed Tanous62598e32023-07-17 17:06:25 -0700276 BMCWEB_LOG_DEBUG("The subscription type is SSE, so skipping.");
JunLin Chen28afb492021-02-24 17:13:29 +0800277 continue;
278 }
Ed Tanous601c71a2021-09-08 16:40:12 -0700279 nlohmann::json::object_t headers;
280 for (const boost::beast::http::fields::value_type& header :
wenlitaofbfb7882024-07-12 11:25:00 +0800281 subValue.httpHeaders)
Ed Tanous601c71a2021-09-08 16:40:12 -0700282 {
283 // Note, these are technically copies because nlohmann doesn't
284 // support key lookup by std::string_view. At least the
285 // following code can use move
286 // https://github.com/nlohmann/json/issues/1529
287 std::string name(header.name_string());
288 headers[std::move(name)] = header.value();
289 }
290
Ed Tanous14766872022-03-15 10:44:42 -0700291 nlohmann::json::object_t subscription;
292
wenlitaofbfb7882024-07-12 11:25:00 +0800293 subscription["Id"] = subValue.id;
294 subscription["Context"] = subValue.customText;
295 subscription["DeliveryRetryPolicy"] = subValue.retryPolicy;
296 subscription["Destination"] = subValue.destinationUrl;
297 subscription["EventFormatType"] = subValue.eventFormatType;
Ed Tanous14766872022-03-15 10:44:42 -0700298 subscription["HttpHeaders"] = std::move(headers);
wenlitaofbfb7882024-07-12 11:25:00 +0800299 subscription["MessageIds"] = subValue.registryMsgIds;
300 subscription["Protocol"] = subValue.protocol;
301 subscription["RegistryPrefixes"] = subValue.registryPrefixes;
Ed Tanousa14c9112024-09-04 10:46:47 -0700302 subscription["OriginResources"] = subValue.originResources;
wenlitaofbfb7882024-07-12 11:25:00 +0800303 subscription["ResourceTypes"] = subValue.resourceTypes;
304 subscription["SubscriptionType"] = subValue.subscriptionType;
Ed Tanous14766872022-03-15 10:44:42 -0700305 subscription["MetricReportDefinitions"] =
wenlitaofbfb7882024-07-12 11:25:00 +0800306 subValue.metricReportDefinitions;
Ed Tanous19bb3622024-07-05 10:07:40 -0500307 subscription["VerifyCertificate"] = subValue.verifyCertificate;
Ed Tanous14766872022-03-15 10:44:42 -0700308
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500309 subscriptions.emplace_back(std::move(subscription));
JunLin Chen28afb492021-02-24 17:13:29 +0800310 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700311 persistentFile << data;
312 }
313
Ed Tanouse05aec52022-01-25 10:28:56 -0800314 std::string systemUuid;
Ed Tanousba9f9a62017-10-11 16:40:35 -0700315};
316
Ed Tanous52cc1122020-07-18 13:51:21 -0700317inline ConfigFile& getConfig()
318{
319 static ConfigFile f;
320 return f;
321}
322
Ed Tanous1abe55e2018-09-05 08:30:59 -0700323} // namespace persistent_data