blob: d210800adbd5224c95a16dcb6ed36781c7db4b7b [file] [log] [blame]
Ed Tanous40e9b922024-09-10 13:50:16 -07001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright OpenBMC Authors
Ed Tanousba9f9a62017-10-11 16:40:35 -07003#pragma once
4
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08005#include "event_service_store.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -08006#include "logging.hpp"
Ed Tanous2c6ffdb2023-06-28 11:28:38 -07007#include "ossl_random.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08008#include "sessions.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -08009// NOLINTNEXTLINE(misc-include-cleaner)
10#include "utility.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080011
Ed Tanousd7857202025-01-28 15:32:26 -080012#include <boost/beast/core/file_base.hpp>
Ed Tanousc282e8b2024-07-01 08:56:34 -070013#include <boost/beast/core/file_posix.hpp>
Ed Tanous601c71a2021-09-08 16:40:12 -070014#include <boost/beast/http/fields.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070015#include <nlohmann/json.hpp>
Ed Tanousba9f9a62017-10-11 16:40:35 -070016
Ed Tanousd7857202025-01-28 15:32:26 -080017#include <chrono>
18#include <cstdint>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050019#include <filesystem>
James Feist3909dc82020-04-03 10:58:55 -070020#include <fstream>
Ed Tanousd7857202025-01-28 15:32:26 -080021#include <memory>
22#include <optional>
23#include <string>
Ed Tanousc282e8b2024-07-01 08:56:34 -070024#include <system_error>
Ed Tanousd7857202025-01-28 15:32:26 -080025#include <utility>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050026
Ed Tanous1abe55e2018-09-05 08:30:59 -070027namespace persistent_data
28{
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +010029
Ed Tanous52cc1122020-07-18 13:51:21 -070030class ConfigFile
Ed Tanous1abe55e2018-09-05 08:30:59 -070031{
Ed Tanous271584a2019-07-09 16:24:22 -070032 uint64_t jsonRevision = 1;
Ed Tanousc963aa42017-10-27 16:00:19 -070033
Ed Tanous1abe55e2018-09-05 08:30:59 -070034 public:
Ratan Gupta845cb7d2019-07-12 00:32:25 +053035 // todo(ed) should read this from a fixed location somewhere, not CWD
36 static constexpr const char* filename = "bmcweb_persistent_data.json";
37
Ed Tanous52cc1122020-07-18 13:51:21 -070038 ConfigFile()
Ed Tanous1abe55e2018-09-05 08:30:59 -070039 {
40 readData();
Ed Tanousc963aa42017-10-27 16:00:19 -070041 }
Ed Tanousc963aa42017-10-27 16:00:19 -070042
Ed Tanous52cc1122020-07-18 13:51:21 -070043 ~ConfigFile()
Ed Tanous1abe55e2018-09-05 08:30:59 -070044 {
Gunnar Mills83cf8182020-11-11 15:37:34 -060045 // Make sure we aren't writing stale sessions
46 persistent_data::SessionStore::getInstance().applySessionTimeouts();
Ed Tanous1abe55e2018-09-05 08:30:59 -070047 if (persistent_data::SessionStore::getInstance().needsWrite())
48 {
49 writeData();
Kowalski, Kamil5cef0f72018-02-15 15:26:51 +010050 }
Ed Tanousc963aa42017-10-27 16:00:19 -070051 }
Ed Tanousc963aa42017-10-27 16:00:19 -070052
Ed Tanousecd6a3a2022-01-07 09:18:40 -080053 ConfigFile(const ConfigFile&) = delete;
54 ConfigFile(ConfigFile&&) = delete;
55 ConfigFile& operator=(const ConfigFile&) = delete;
56 ConfigFile& operator=(ConfigFile&&) = delete;
57
Ed Tanous1abe55e2018-09-05 08:30:59 -070058 // TODO(ed) this should really use protobuf, or some other serialization
59 // library, but adding another dependency is somewhat outside the scope of
60 // this application for the moment
61 void readData()
62 {
63 std::ifstream persistentFile(filename);
Ed Tanous271584a2019-07-09 16:24:22 -070064 uint64_t fileRevision = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -070065 if (persistentFile.is_open())
66 {
67 // call with exceptions disabled
68 auto data = nlohmann::json::parse(persistentFile, nullptr, false);
69 if (data.is_discarded())
70 {
Ed Tanous62598e32023-07-17 17:06:25 -070071 BMCWEB_LOG_ERROR("Error parsing persistent data in json file.");
Ed Tanous1abe55e2018-09-05 08:30:59 -070072 }
73 else
74 {
Ed Tanous0bdda662023-08-03 17:27:34 -070075 const nlohmann::json::object_t* obj =
76 data.get_ptr<nlohmann::json::object_t*>();
77 if (obj == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -070078 {
Ed Tanous0bdda662023-08-03 17:27:34 -070079 return;
80 }
81 for (const auto& item : *obj)
82 {
83 if (item.first == "revision")
Ed Tanous1abe55e2018-09-05 08:30:59 -070084 {
85 fileRevision = 0;
86
87 const uint64_t* uintPtr =
Ed Tanous0bdda662023-08-03 17:27:34 -070088 item.second.get_ptr<const uint64_t*>();
Ed Tanous1abe55e2018-09-05 08:30:59 -070089 if (uintPtr == nullptr)
90 {
Ed Tanous62598e32023-07-17 17:06:25 -070091 BMCWEB_LOG_ERROR("Failed to read revision flag");
Ed Tanous1abe55e2018-09-05 08:30:59 -070092 }
93 else
94 {
95 fileRevision = *uintPtr;
96 }
97 }
Ed Tanous0bdda662023-08-03 17:27:34 -070098 else if (item.first == "system_uuid")
Ed Tanous1abe55e2018-09-05 08:30:59 -070099 {
100 const std::string* jSystemUuid =
Ed Tanous0bdda662023-08-03 17:27:34 -0700101 item.second.get_ptr<const std::string*>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700102 if (jSystemUuid != nullptr)
103 {
104 systemUuid = *jSystemUuid;
105 }
106 }
Corey Ethingtone30d3342025-06-24 11:25:11 -0400107 else if (item.first == "service_identification")
108 {
109 const std::string* jServiceIdentification =
110 item.second.get_ptr<const std::string*>();
111 if (jServiceIdentification != nullptr)
112 {
113 serviceIdentification = *jServiceIdentification;
114 }
115 }
Ed Tanous0bdda662023-08-03 17:27:34 -0700116 else if (item.first == "auth_config")
Zbigniew Kurzynski78158632019-11-05 12:57:37 +0100117 {
Ed Tanous82b286f2025-05-06 13:29:48 -0700118 const nlohmann::json::object_t* jObj =
119 item.second
120 .get_ptr<const nlohmann::json::object_t*>();
121 if (jObj == nullptr)
122 {
123 continue;
124 }
Zbigniew Kurzynski78158632019-11-05 12:57:37 +0100125 SessionStore::getInstance()
126 .getAuthMethodsConfig()
Ed Tanous82b286f2025-05-06 13:29:48 -0700127 .fromJson(*jObj);
Zbigniew Kurzynski78158632019-11-05 12:57:37 +0100128 }
Ed Tanous0bdda662023-08-03 17:27:34 -0700129 else if (item.first == "sessions")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700130 {
Ed Tanous0bdda662023-08-03 17:27:34 -0700131 for (const auto& elem : item.second)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700132 {
Ed Tanous82b286f2025-05-06 13:29:48 -0700133 const nlohmann::json::object_t* jObj =
134 elem.get_ptr<const nlohmann::json::object_t*>();
135 if (jObj == nullptr)
136 {
137 continue;
138 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700139 std::shared_ptr<UserSession> newSession =
Ed Tanous82b286f2025-05-06 13:29:48 -0700140 UserSession::fromJson(*jObj);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700141
142 if (newSession == nullptr)
143 {
Ed Tanous62598e32023-07-17 17:06:25 -0700144 BMCWEB_LOG_ERROR("Problem reading session "
145 "from persistent store");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700146 continue;
147 }
148
Ed Tanous62598e32023-07-17 17:06:25 -0700149 BMCWEB_LOG_DEBUG("Restored session: {} {} {}",
150 newSession->csrfToken,
151 newSession->uniqueId,
152 newSession->sessionToken);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700153 SessionStore::getInstance().authTokens.emplace(
154 newSession->sessionToken, newSession);
155 }
156 }
Ed Tanous0bdda662023-08-03 17:27:34 -0700157 else if (item.first == "timeout")
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530158 {
159 const int64_t* jTimeout =
Ed Tanous0bdda662023-08-03 17:27:34 -0700160 item.second.get_ptr<const int64_t*>();
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530161 if (jTimeout == nullptr)
162 {
Ed Tanous62598e32023-07-17 17:06:25 -0700163 BMCWEB_LOG_DEBUG(
164 "Problem reading session timeout value");
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530165 continue;
166 }
167 std::chrono::seconds sessionTimeoutInseconds(*jTimeout);
Ed Tanous62598e32023-07-17 17:06:25 -0700168 BMCWEB_LOG_DEBUG("Restored Session Timeout: {}",
169 sessionTimeoutInseconds.count());
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530170 SessionStore::getInstance().updateSessionTimeout(
171 sessionTimeoutInseconds);
172 }
Ed Tanous0bdda662023-08-03 17:27:34 -0700173 else if (item.first == "eventservice_config")
JunLin Chen28afb492021-02-24 17:13:29 +0800174 {
Ed Tanous0bdda662023-08-03 17:27:34 -0700175 const nlohmann::json::object_t* esobj =
176 item.second
177 .get_ptr<const nlohmann::json::object_t*>();
178 if (esobj == nullptr)
179 {
180 BMCWEB_LOG_DEBUG(
181 "Problem reading EventService value");
182 continue;
183 }
184
JunLin Chen28afb492021-02-24 17:13:29 +0800185 EventServiceStore::getInstance()
186 .getEventServiceConfig()
Ed Tanous0bdda662023-08-03 17:27:34 -0700187 .fromJson(*esobj);
JunLin Chen28afb492021-02-24 17:13:29 +0800188 }
Ed Tanous0bdda662023-08-03 17:27:34 -0700189 else if (item.first == "subscriptions")
JunLin Chen28afb492021-02-24 17:13:29 +0800190 {
Ed Tanous0bdda662023-08-03 17:27:34 -0700191 for (const auto& elem : item.second)
JunLin Chen28afb492021-02-24 17:13:29 +0800192 {
Ed Tanous82b286f2025-05-06 13:29:48 -0700193 const nlohmann::json::object_t* subobj =
194 elem.get_ptr<const nlohmann::json::object_t*>();
195 if (subobj == nullptr)
196 {
197 continue;
198 }
199
Ed Tanous4b712a22023-08-02 12:56:52 -0700200 std::optional<UserSubscription> newSub =
Ed Tanous82b286f2025-05-06 13:29:48 -0700201 UserSubscription::fromJson(*subobj);
JunLin Chen28afb492021-02-24 17:13:29 +0800202
Ed Tanous4b712a22023-08-02 12:56:52 -0700203 if (!newSub)
JunLin Chen28afb492021-02-24 17:13:29 +0800204 {
Myung Bae6136e852025-05-14 07:53:45 -0400205 BMCWEB_LOG_ERROR(
206 "Problem reading subscription from persistent store");
JunLin Chen28afb492021-02-24 17:13:29 +0800207 continue;
208 }
209
Myung Bae6136e852025-05-14 07:53:45 -0400210 std::string id = newSub->id;
211 BMCWEB_LOG_DEBUG("Restored subscription: {} {}", id,
212 newSub->customText);
Ed Tanous4b712a22023-08-02 12:56:52 -0700213
Myung Bae5fe4ef32024-10-19 09:56:02 -0400214 EventServiceStore::getInstance()
215 .subscriptionsConfigMap.emplace(
Myung Bae6136e852025-05-14 07:53:45 -0400216 id, std::make_shared<UserSubscription>(
217 std::move(*newSub)));
JunLin Chen28afb492021-02-24 17:13:29 +0800218 }
219 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700220 else
221 {
222 // Do nothing in the case of extra fields. We may have
223 // cases where fields are added in the future, and we
224 // want to at least attempt to gracefully support
225 // downgrades in that case, even if we don't officially
226 // support it
227 }
228 }
229 }
230 }
231 bool needWrite = false;
232
233 if (systemUuid.empty())
234 {
Ed Tanous2c6ffdb2023-06-28 11:28:38 -0700235 systemUuid = bmcweb::getRandomUUID();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700236 needWrite = true;
237 }
238 if (fileRevision < jsonRevision)
239 {
240 needWrite = true;
241 }
242 // write revision changes or system uuid changes immediately
243 if (needWrite)
244 {
245 writeData();
246 }
247 }
248
249 void writeData()
250 {
Ed Tanousc282e8b2024-07-01 08:56:34 -0700251 std::filesystem::path path(filename);
252 path = path.parent_path();
Myung Baed8f8a7d2024-10-23 12:55:08 -0400253 if (!path.empty())
Ed Tanousc282e8b2024-07-01 08:56:34 -0700254 {
Myung Baed8f8a7d2024-10-23 12:55:08 -0400255 std::error_code ecDir;
256 std::filesystem::create_directories(path, ecDir);
257 if (ecDir)
258 {
259 BMCWEB_LOG_CRITICAL("Can't create persistent folders {}",
260 ecDir.message());
261 return;
262 }
Ed Tanousc282e8b2024-07-01 08:56:34 -0700263 }
264 boost::beast::file_posix persistentFile;
265 boost::system::error_code ec;
266 persistentFile.open(filename, boost::beast::file_mode::write, ec);
267 if (ec)
268 {
269 BMCWEB_LOG_CRITICAL("Unable to store persistent data to file {}",
270 ec.message());
271 return;
272 }
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530273
274 // set the permission of the file to 640
Ed Tanous52cc1122020-07-18 13:51:21 -0700275 std::filesystem::perms permission =
276 std::filesystem::perms::owner_read |
277 std::filesystem::perms::owner_write |
278 std::filesystem::perms::group_read;
Ed Tanousc282e8b2024-07-01 08:56:34 -0700279 std::filesystem::permissions(filename, permission, ec);
280 if (ec)
281 {
282 BMCWEB_LOG_CRITICAL("Failed to set filesystem permissions {}",
283 ec.message());
284 return;
285 }
Ed Tanous3ce36882024-06-09 10:58:16 -0700286 const AuthConfigMethods& c =
287 SessionStore::getInstance().getAuthMethodsConfig();
JunLin Chen28afb492021-02-24 17:13:29 +0800288 const auto& eventServiceConfig =
289 EventServiceStore::getInstance().getEventServiceConfig();
Ed Tanous14766872022-03-15 10:44:42 -0700290 nlohmann::json::object_t data;
291 nlohmann::json& authConfig = data["auth_config"];
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530292
Ed Tanous14766872022-03-15 10:44:42 -0700293 authConfig["XToken"] = c.xtoken;
294 authConfig["Cookie"] = c.cookie;
295 authConfig["SessionToken"] = c.sessionToken;
296 authConfig["BasicAuth"] = c.basic;
297 authConfig["TLS"] = c.tls;
Ed Tanous3281bcf2024-06-25 16:02:05 -0700298 authConfig["TLSStrict"] = c.tlsStrict;
Malik Akbar Hashemi Rafsanjani86e41a82025-05-06 11:23:32 -0700299 authConfig["MTLSCommonNameParseMode"] =
Ed Tanous3ce36882024-06-09 10:58:16 -0700300 static_cast<int>(c.mTLSCommonNameParsingMode);
JunLin Chen28afb492021-02-24 17:13:29 +0800301
Ed Tanous14766872022-03-15 10:44:42 -0700302 nlohmann::json& eventserviceConfig = data["eventservice_config"];
303 eventserviceConfig["ServiceEnabled"] = eventServiceConfig.enabled;
304 eventserviceConfig["DeliveryRetryAttempts"] =
305 eventServiceConfig.retryAttempts;
306 eventserviceConfig["DeliveryRetryIntervalSeconds"] =
307 eventServiceConfig.retryTimeoutInterval;
308
309 data["system_uuid"] = systemUuid;
Corey Ethingtone30d3342025-06-24 11:25:11 -0400310 data["service_identification"] = serviceIdentification;
Ed Tanous14766872022-03-15 10:44:42 -0700311 data["revision"] = jsonRevision;
312 data["timeout"] = SessionStore::getInstance().getTimeoutInSeconds();
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700313
314 nlohmann::json& sessions = data["sessions"];
315 sessions = nlohmann::json::array();
316 for (const auto& p : SessionStore::getInstance().authTokens)
317 {
Ed Tanous89cda632024-04-16 08:45:54 -0700318 if (p.second->sessionType != persistent_data::SessionType::Basic &&
319 p.second->sessionType !=
320 persistent_data::SessionType::MutualTLS)
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700321 {
Ed Tanous14766872022-03-15 10:44:42 -0700322 nlohmann::json::object_t session;
323 session["unique_id"] = p.second->uniqueId;
324 session["session_token"] = p.second->sessionToken;
325 session["username"] = p.second->username;
326 session["csrf_token"] = p.second->csrfToken;
327 session["client_ip"] = p.second->clientIp;
Ed Tanouse01d0c32023-06-30 13:21:32 -0700328 const std::optional<std::string>& clientId = p.second->clientId;
329 if (clientId)
Ed Tanousbb759e32022-08-02 17:07:54 -0700330 {
Ed Tanouse01d0c32023-06-30 13:21:32 -0700331 session["client_id"] = *clientId;
Ed Tanousbb759e32022-08-02 17:07:54 -0700332 }
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500333 sessions.emplace_back(std::move(session));
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700334 }
335 }
JunLin Chen28afb492021-02-24 17:13:29 +0800336 nlohmann::json& subscriptions = data["subscriptions"];
337 subscriptions = nlohmann::json::array();
338 for (const auto& it :
339 EventServiceStore::getInstance().subscriptionsConfigMap)
340 {
Myung Bae5fe4ef32024-10-19 09:56:02 -0400341 if (it.second == nullptr)
342 {
343 continue;
344 }
345 const UserSubscription& subValue = *it.second;
wenlitaofbfb7882024-07-12 11:25:00 +0800346 if (subValue.subscriptionType == "SSE")
JunLin Chen28afb492021-02-24 17:13:29 +0800347 {
Ed Tanous62598e32023-07-17 17:06:25 -0700348 BMCWEB_LOG_DEBUG("The subscription type is SSE, so skipping.");
JunLin Chen28afb492021-02-24 17:13:29 +0800349 continue;
350 }
Ed Tanous601c71a2021-09-08 16:40:12 -0700351 nlohmann::json::object_t headers;
352 for (const boost::beast::http::fields::value_type& header :
wenlitaofbfb7882024-07-12 11:25:00 +0800353 subValue.httpHeaders)
Ed Tanous601c71a2021-09-08 16:40:12 -0700354 {
355 // Note, these are technically copies because nlohmann doesn't
356 // support key lookup by std::string_view. At least the
357 // following code can use move
358 // https://github.com/nlohmann/json/issues/1529
359 std::string name(header.name_string());
360 headers[std::move(name)] = header.value();
361 }
362
Ed Tanous14766872022-03-15 10:44:42 -0700363 nlohmann::json::object_t subscription;
364
wenlitaofbfb7882024-07-12 11:25:00 +0800365 subscription["Id"] = subValue.id;
366 subscription["Context"] = subValue.customText;
367 subscription["DeliveryRetryPolicy"] = subValue.retryPolicy;
Myung Bae5064a252024-10-04 09:34:25 -0700368 subscription["SendHeartbeat"] = subValue.sendHeartbeat;
369 subscription["HeartbeatIntervalMinutes"] =
370 subValue.hbIntervalMinutes;
wenlitaofbfb7882024-07-12 11:25:00 +0800371 subscription["Destination"] = subValue.destinationUrl;
372 subscription["EventFormatType"] = subValue.eventFormatType;
Ed Tanous14766872022-03-15 10:44:42 -0700373 subscription["HttpHeaders"] = std::move(headers);
wenlitaofbfb7882024-07-12 11:25:00 +0800374 subscription["MessageIds"] = subValue.registryMsgIds;
375 subscription["Protocol"] = subValue.protocol;
376 subscription["RegistryPrefixes"] = subValue.registryPrefixes;
Ed Tanousa14c9112024-09-04 10:46:47 -0700377 subscription["OriginResources"] = subValue.originResources;
wenlitaofbfb7882024-07-12 11:25:00 +0800378 subscription["ResourceTypes"] = subValue.resourceTypes;
379 subscription["SubscriptionType"] = subValue.subscriptionType;
Ed Tanous14766872022-03-15 10:44:42 -0700380 subscription["MetricReportDefinitions"] =
wenlitaofbfb7882024-07-12 11:25:00 +0800381 subValue.metricReportDefinitions;
Ed Tanous19bb3622024-07-05 10:07:40 -0500382 subscription["VerifyCertificate"] = subValue.verifyCertificate;
Ed Tanous14766872022-03-15 10:44:42 -0700383
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500384 subscriptions.emplace_back(std::move(subscription));
JunLin Chen28afb492021-02-24 17:13:29 +0800385 }
Ed Tanousc282e8b2024-07-01 08:56:34 -0700386 std::string out = nlohmann::json(data).dump(
387 -1, ' ', true, nlohmann::json::error_handler_t::replace);
388 persistentFile.write(out.data(), out.size(), ec);
389 if (ec)
390 {
391 BMCWEB_LOG_ERROR("Failed to write file {}", ec.message());
392 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700393 }
394
Ed Tanouse05aec52022-01-25 10:28:56 -0800395 std::string systemUuid;
Corey Ethingtone30d3342025-06-24 11:25:11 -0400396 std::string serviceIdentification;
Ed Tanousba9f9a62017-10-11 16:40:35 -0700397};
398
Ed Tanous52cc1122020-07-18 13:51:21 -0700399inline ConfigFile& getConfig()
400{
401 static ConfigFile f;
402 return f;
403}
404
Ed Tanous1abe55e2018-09-05 08:30:59 -0700405} // namespace persistent_data