blob: 0cdedcd356d8d20867f76d2bb2170bc6dddaa3af [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 Tanousc282e8b2024-07-01 08:56:34 -07009#include <boost/beast/core/file_posix.hpp>
Ed Tanous601c71a2021-09-08 16:40:12 -070010#include <boost/beast/http/fields.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070011#include <nlohmann/json.hpp>
Ed Tanousba9f9a62017-10-11 16:40:35 -070012
Gunnar Mills1214b7e2020-06-04 10:11:30 -050013#include <filesystem>
James Feist3909dc82020-04-03 10:58:55 -070014#include <fstream>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050015#include <random>
Ed Tanousc282e8b2024-07-01 08:56:34 -070016#include <system_error>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050017
Ed Tanous1abe55e2018-09-05 08:30:59 -070018namespace persistent_data
19{
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +010020
Ed Tanous52cc1122020-07-18 13:51:21 -070021class ConfigFile
Ed Tanous1abe55e2018-09-05 08:30:59 -070022{
Ed Tanous271584a2019-07-09 16:24:22 -070023 uint64_t jsonRevision = 1;
Ed Tanousc963aa42017-10-27 16:00:19 -070024
Ed Tanous1abe55e2018-09-05 08:30:59 -070025 public:
Ratan Gupta845cb7d2019-07-12 00:32:25 +053026 // todo(ed) should read this from a fixed location somewhere, not CWD
27 static constexpr const char* filename = "bmcweb_persistent_data.json";
28
Ed Tanous52cc1122020-07-18 13:51:21 -070029 ConfigFile()
Ed Tanous1abe55e2018-09-05 08:30:59 -070030 {
31 readData();
Ed Tanousc963aa42017-10-27 16:00:19 -070032 }
Ed Tanousc963aa42017-10-27 16:00:19 -070033
Ed Tanous52cc1122020-07-18 13:51:21 -070034 ~ConfigFile()
Ed Tanous1abe55e2018-09-05 08:30:59 -070035 {
Gunnar Mills83cf8182020-11-11 15:37:34 -060036 // Make sure we aren't writing stale sessions
37 persistent_data::SessionStore::getInstance().applySessionTimeouts();
Ed Tanous1abe55e2018-09-05 08:30:59 -070038 if (persistent_data::SessionStore::getInstance().needsWrite())
39 {
40 writeData();
Kowalski, Kamil5cef0f72018-02-15 15:26:51 +010041 }
Ed Tanousc963aa42017-10-27 16:00:19 -070042 }
Ed Tanousc963aa42017-10-27 16:00:19 -070043
Ed Tanousecd6a3a2022-01-07 09:18:40 -080044 ConfigFile(const ConfigFile&) = delete;
45 ConfigFile(ConfigFile&&) = delete;
46 ConfigFile& operator=(const ConfigFile&) = delete;
47 ConfigFile& operator=(ConfigFile&&) = delete;
48
Ed Tanous1abe55e2018-09-05 08:30:59 -070049 // TODO(ed) this should really use protobuf, or some other serialization
50 // library, but adding another dependency is somewhat outside the scope of
51 // this application for the moment
52 void readData()
53 {
54 std::ifstream persistentFile(filename);
Ed Tanous271584a2019-07-09 16:24:22 -070055 uint64_t fileRevision = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -070056 if (persistentFile.is_open())
57 {
58 // call with exceptions disabled
59 auto data = nlohmann::json::parse(persistentFile, nullptr, false);
60 if (data.is_discarded())
61 {
Ed Tanous62598e32023-07-17 17:06:25 -070062 BMCWEB_LOG_ERROR("Error parsing persistent data in json file.");
Ed Tanous1abe55e2018-09-05 08:30:59 -070063 }
64 else
65 {
Ed Tanous0bdda662023-08-03 17:27:34 -070066 const nlohmann::json::object_t* obj =
67 data.get_ptr<nlohmann::json::object_t*>();
68 if (obj == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -070069 {
Ed Tanous0bdda662023-08-03 17:27:34 -070070 return;
71 }
72 for (const auto& item : *obj)
73 {
74 if (item.first == "revision")
Ed Tanous1abe55e2018-09-05 08:30:59 -070075 {
76 fileRevision = 0;
77
78 const uint64_t* uintPtr =
Ed Tanous0bdda662023-08-03 17:27:34 -070079 item.second.get_ptr<const uint64_t*>();
Ed Tanous1abe55e2018-09-05 08:30:59 -070080 if (uintPtr == nullptr)
81 {
Ed Tanous62598e32023-07-17 17:06:25 -070082 BMCWEB_LOG_ERROR("Failed to read revision flag");
Ed Tanous1abe55e2018-09-05 08:30:59 -070083 }
84 else
85 {
86 fileRevision = *uintPtr;
87 }
88 }
Ed Tanous0bdda662023-08-03 17:27:34 -070089 else if (item.first == "system_uuid")
Ed Tanous1abe55e2018-09-05 08:30:59 -070090 {
91 const std::string* jSystemUuid =
Ed Tanous0bdda662023-08-03 17:27:34 -070092 item.second.get_ptr<const std::string*>();
Ed Tanous1abe55e2018-09-05 08:30:59 -070093 if (jSystemUuid != nullptr)
94 {
95 systemUuid = *jSystemUuid;
96 }
97 }
Ed Tanous0bdda662023-08-03 17:27:34 -070098 else if (item.first == "auth_config")
Zbigniew Kurzynski78158632019-11-05 12:57:37 +010099 {
100 SessionStore::getInstance()
101 .getAuthMethodsConfig()
Ed Tanous0bdda662023-08-03 17:27:34 -0700102 .fromJson(item.second);
Zbigniew Kurzynski78158632019-11-05 12:57:37 +0100103 }
Ed Tanous0bdda662023-08-03 17:27:34 -0700104 else if (item.first == "sessions")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700105 {
Ed Tanous0bdda662023-08-03 17:27:34 -0700106 for (const auto& elem : item.second)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700107 {
108 std::shared_ptr<UserSession> newSession =
109 UserSession::fromJson(elem);
110
111 if (newSession == nullptr)
112 {
Ed Tanous62598e32023-07-17 17:06:25 -0700113 BMCWEB_LOG_ERROR("Problem reading session "
114 "from persistent store");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700115 continue;
116 }
117
Ed Tanous62598e32023-07-17 17:06:25 -0700118 BMCWEB_LOG_DEBUG("Restored session: {} {} {}",
119 newSession->csrfToken,
120 newSession->uniqueId,
121 newSession->sessionToken);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700122 SessionStore::getInstance().authTokens.emplace(
123 newSession->sessionToken, newSession);
124 }
125 }
Ed Tanous0bdda662023-08-03 17:27:34 -0700126 else if (item.first == "timeout")
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530127 {
128 const int64_t* jTimeout =
Ed Tanous0bdda662023-08-03 17:27:34 -0700129 item.second.get_ptr<const int64_t*>();
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530130 if (jTimeout == nullptr)
131 {
Ed Tanous62598e32023-07-17 17:06:25 -0700132 BMCWEB_LOG_DEBUG(
133 "Problem reading session timeout value");
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530134 continue;
135 }
136 std::chrono::seconds sessionTimeoutInseconds(*jTimeout);
Ed Tanous62598e32023-07-17 17:06:25 -0700137 BMCWEB_LOG_DEBUG("Restored Session Timeout: {}",
138 sessionTimeoutInseconds.count());
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530139 SessionStore::getInstance().updateSessionTimeout(
140 sessionTimeoutInseconds);
141 }
Ed Tanous0bdda662023-08-03 17:27:34 -0700142 else if (item.first == "eventservice_config")
JunLin Chen28afb492021-02-24 17:13:29 +0800143 {
Ed Tanous0bdda662023-08-03 17:27:34 -0700144 const nlohmann::json::object_t* esobj =
145 item.second
146 .get_ptr<const nlohmann::json::object_t*>();
147 if (esobj == nullptr)
148 {
149 BMCWEB_LOG_DEBUG(
150 "Problem reading EventService value");
151 continue;
152 }
153
JunLin Chen28afb492021-02-24 17:13:29 +0800154 EventServiceStore::getInstance()
155 .getEventServiceConfig()
Ed Tanous0bdda662023-08-03 17:27:34 -0700156 .fromJson(*esobj);
JunLin Chen28afb492021-02-24 17:13:29 +0800157 }
Ed Tanous0bdda662023-08-03 17:27:34 -0700158 else if (item.first == "subscriptions")
JunLin Chen28afb492021-02-24 17:13:29 +0800159 {
Ed Tanous0bdda662023-08-03 17:27:34 -0700160 for (const auto& elem : item.second)
JunLin Chen28afb492021-02-24 17:13:29 +0800161 {
Ed Tanous4b712a22023-08-02 12:56:52 -0700162 std::optional<UserSubscription> newSub =
JunLin Chen28afb492021-02-24 17:13:29 +0800163 UserSubscription::fromJson(elem);
164
Ed Tanous4b712a22023-08-02 12:56:52 -0700165 if (!newSub)
JunLin Chen28afb492021-02-24 17:13:29 +0800166 {
Ed Tanous62598e32023-07-17 17:06:25 -0700167 BMCWEB_LOG_ERROR("Problem reading subscription "
168 "from persistent store");
JunLin Chen28afb492021-02-24 17:13:29 +0800169 continue;
170 }
171
Ed Tanous62598e32023-07-17 17:06:25 -0700172 BMCWEB_LOG_DEBUG("Restored subscription: {} {}",
Ed Tanous4b712a22023-08-02 12:56:52 -0700173 newSub->id, newSub->customText);
174
Myung Bae5fe4ef32024-10-19 09:56:02 -0400175 EventServiceStore::getInstance()
176 .subscriptionsConfigMap.emplace(
177 newSub->id,
178 std::make_shared<UserSubscription>(
179 std::move(*newSub)));
JunLin Chen28afb492021-02-24 17:13:29 +0800180 }
181 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700182 else
183 {
184 // Do nothing in the case of extra fields. We may have
185 // cases where fields are added in the future, and we
186 // want to at least attempt to gracefully support
187 // downgrades in that case, even if we don't officially
188 // support it
189 }
190 }
191 }
192 }
193 bool needWrite = false;
194
195 if (systemUuid.empty())
196 {
Ed Tanous2c6ffdb2023-06-28 11:28:38 -0700197 systemUuid = bmcweb::getRandomUUID();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700198 needWrite = true;
199 }
200 if (fileRevision < jsonRevision)
201 {
202 needWrite = true;
203 }
204 // write revision changes or system uuid changes immediately
205 if (needWrite)
206 {
207 writeData();
208 }
209 }
210
211 void writeData()
212 {
Ed Tanousc282e8b2024-07-01 08:56:34 -0700213 std::filesystem::path path(filename);
214 path = path.parent_path();
Myung Baed8f8a7d2024-10-23 12:55:08 -0400215 if (!path.empty())
Ed Tanousc282e8b2024-07-01 08:56:34 -0700216 {
Myung Baed8f8a7d2024-10-23 12:55:08 -0400217 std::error_code ecDir;
218 std::filesystem::create_directories(path, ecDir);
219 if (ecDir)
220 {
221 BMCWEB_LOG_CRITICAL("Can't create persistent folders {}",
222 ecDir.message());
223 return;
224 }
Ed Tanousc282e8b2024-07-01 08:56:34 -0700225 }
226 boost::beast::file_posix persistentFile;
227 boost::system::error_code ec;
228 persistentFile.open(filename, boost::beast::file_mode::write, ec);
229 if (ec)
230 {
231 BMCWEB_LOG_CRITICAL("Unable to store persistent data to file {}",
232 ec.message());
233 return;
234 }
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530235
236 // set the permission of the file to 640
Ed Tanous52cc1122020-07-18 13:51:21 -0700237 std::filesystem::perms permission =
238 std::filesystem::perms::owner_read |
239 std::filesystem::perms::owner_write |
240 std::filesystem::perms::group_read;
Ed Tanousc282e8b2024-07-01 08:56:34 -0700241 std::filesystem::permissions(filename, permission, ec);
242 if (ec)
243 {
244 BMCWEB_LOG_CRITICAL("Failed to set filesystem permissions {}",
245 ec.message());
246 return;
247 }
Ed Tanous3ce36882024-06-09 10:58:16 -0700248 const AuthConfigMethods& c =
249 SessionStore::getInstance().getAuthMethodsConfig();
JunLin Chen28afb492021-02-24 17:13:29 +0800250 const auto& eventServiceConfig =
251 EventServiceStore::getInstance().getEventServiceConfig();
Ed Tanous14766872022-03-15 10:44:42 -0700252 nlohmann::json::object_t data;
253 nlohmann::json& authConfig = data["auth_config"];
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530254
Ed Tanous14766872022-03-15 10:44:42 -0700255 authConfig["XToken"] = c.xtoken;
256 authConfig["Cookie"] = c.cookie;
257 authConfig["SessionToken"] = c.sessionToken;
258 authConfig["BasicAuth"] = c.basic;
259 authConfig["TLS"] = c.tls;
Ed Tanous3281bcf2024-06-25 16:02:05 -0700260 authConfig["TLSStrict"] = c.tlsStrict;
Ed Tanous3ce36882024-06-09 10:58:16 -0700261 authConfig["TLSCommonNameParseMode"] =
262 static_cast<int>(c.mTLSCommonNameParsingMode);
JunLin Chen28afb492021-02-24 17:13:29 +0800263
Ed Tanous14766872022-03-15 10:44:42 -0700264 nlohmann::json& eventserviceConfig = data["eventservice_config"];
265 eventserviceConfig["ServiceEnabled"] = eventServiceConfig.enabled;
266 eventserviceConfig["DeliveryRetryAttempts"] =
267 eventServiceConfig.retryAttempts;
268 eventserviceConfig["DeliveryRetryIntervalSeconds"] =
269 eventServiceConfig.retryTimeoutInterval;
270
271 data["system_uuid"] = systemUuid;
272 data["revision"] = jsonRevision;
273 data["timeout"] = SessionStore::getInstance().getTimeoutInSeconds();
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700274
275 nlohmann::json& sessions = data["sessions"];
276 sessions = nlohmann::json::array();
277 for (const auto& p : SessionStore::getInstance().authTokens)
278 {
Ed Tanous89cda632024-04-16 08:45:54 -0700279 if (p.second->sessionType != persistent_data::SessionType::Basic &&
280 p.second->sessionType !=
281 persistent_data::SessionType::MutualTLS)
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700282 {
Ed Tanous14766872022-03-15 10:44:42 -0700283 nlohmann::json::object_t session;
284 session["unique_id"] = p.second->uniqueId;
285 session["session_token"] = p.second->sessionToken;
286 session["username"] = p.second->username;
287 session["csrf_token"] = p.second->csrfToken;
288 session["client_ip"] = p.second->clientIp;
Ed Tanouse01d0c32023-06-30 13:21:32 -0700289 const std::optional<std::string>& clientId = p.second->clientId;
290 if (clientId)
Ed Tanousbb759e32022-08-02 17:07:54 -0700291 {
Ed Tanouse01d0c32023-06-30 13:21:32 -0700292 session["client_id"] = *clientId;
Ed Tanousbb759e32022-08-02 17:07:54 -0700293 }
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500294 sessions.emplace_back(std::move(session));
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700295 }
296 }
JunLin Chen28afb492021-02-24 17:13:29 +0800297 nlohmann::json& subscriptions = data["subscriptions"];
298 subscriptions = nlohmann::json::array();
299 for (const auto& it :
300 EventServiceStore::getInstance().subscriptionsConfigMap)
301 {
Myung Bae5fe4ef32024-10-19 09:56:02 -0400302 if (it.second == nullptr)
303 {
304 continue;
305 }
306 const UserSubscription& subValue = *it.second;
wenlitaofbfb7882024-07-12 11:25:00 +0800307 if (subValue.subscriptionType == "SSE")
JunLin Chen28afb492021-02-24 17:13:29 +0800308 {
Ed Tanous62598e32023-07-17 17:06:25 -0700309 BMCWEB_LOG_DEBUG("The subscription type is SSE, so skipping.");
JunLin Chen28afb492021-02-24 17:13:29 +0800310 continue;
311 }
Ed Tanous601c71a2021-09-08 16:40:12 -0700312 nlohmann::json::object_t headers;
313 for (const boost::beast::http::fields::value_type& header :
wenlitaofbfb7882024-07-12 11:25:00 +0800314 subValue.httpHeaders)
Ed Tanous601c71a2021-09-08 16:40:12 -0700315 {
316 // Note, these are technically copies because nlohmann doesn't
317 // support key lookup by std::string_view. At least the
318 // following code can use move
319 // https://github.com/nlohmann/json/issues/1529
320 std::string name(header.name_string());
321 headers[std::move(name)] = header.value();
322 }
323
Ed Tanous14766872022-03-15 10:44:42 -0700324 nlohmann::json::object_t subscription;
325
wenlitaofbfb7882024-07-12 11:25:00 +0800326 subscription["Id"] = subValue.id;
327 subscription["Context"] = subValue.customText;
328 subscription["DeliveryRetryPolicy"] = subValue.retryPolicy;
Myung Bae5064a252024-10-04 09:34:25 -0700329 subscription["SendHeartbeat"] = subValue.sendHeartbeat;
330 subscription["HeartbeatIntervalMinutes"] =
331 subValue.hbIntervalMinutes;
wenlitaofbfb7882024-07-12 11:25:00 +0800332 subscription["Destination"] = subValue.destinationUrl;
333 subscription["EventFormatType"] = subValue.eventFormatType;
Ed Tanous14766872022-03-15 10:44:42 -0700334 subscription["HttpHeaders"] = std::move(headers);
wenlitaofbfb7882024-07-12 11:25:00 +0800335 subscription["MessageIds"] = subValue.registryMsgIds;
336 subscription["Protocol"] = subValue.protocol;
337 subscription["RegistryPrefixes"] = subValue.registryPrefixes;
Ed Tanousa14c9112024-09-04 10:46:47 -0700338 subscription["OriginResources"] = subValue.originResources;
wenlitaofbfb7882024-07-12 11:25:00 +0800339 subscription["ResourceTypes"] = subValue.resourceTypes;
340 subscription["SubscriptionType"] = subValue.subscriptionType;
Ed Tanous14766872022-03-15 10:44:42 -0700341 subscription["MetricReportDefinitions"] =
wenlitaofbfb7882024-07-12 11:25:00 +0800342 subValue.metricReportDefinitions;
Ed Tanous19bb3622024-07-05 10:07:40 -0500343 subscription["VerifyCertificate"] = subValue.verifyCertificate;
Ed Tanous14766872022-03-15 10:44:42 -0700344
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500345 subscriptions.emplace_back(std::move(subscription));
JunLin Chen28afb492021-02-24 17:13:29 +0800346 }
Ed Tanousc282e8b2024-07-01 08:56:34 -0700347 std::string out = nlohmann::json(data).dump(
348 -1, ' ', true, nlohmann::json::error_handler_t::replace);
349 persistentFile.write(out.data(), out.size(), ec);
350 if (ec)
351 {
352 BMCWEB_LOG_ERROR("Failed to write file {}", ec.message());
353 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700354 }
355
Ed Tanouse05aec52022-01-25 10:28:56 -0800356 std::string systemUuid;
Ed Tanousba9f9a62017-10-11 16:40:35 -0700357};
358
Ed Tanous52cc1122020-07-18 13:51:21 -0700359inline ConfigFile& getConfig()
360{
361 static ConfigFile f;
362 return f;
363}
364
Ed Tanous1abe55e2018-09-05 08:30:59 -0700365} // namespace persistent_data