blob: 5aa597655c560d6ec4582bd71f4507c401076909 [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"
6#include "http_request.hpp"
7#include "http_response.hpp"
Ed Tanous2c6ffdb2023-06-28 11:28:38 -07008#include "ossl_random.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08009#include "sessions.hpp"
10
Ed Tanousc282e8b2024-07-01 08:56:34 -070011#include <boost/beast/core/file_posix.hpp>
Ed Tanous601c71a2021-09-08 16:40:12 -070012#include <boost/beast/http/fields.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070013#include <nlohmann/json.hpp>
Ed Tanousba9f9a62017-10-11 16:40:35 -070014
Gunnar Mills1214b7e2020-06-04 10:11:30 -050015#include <filesystem>
James Feist3909dc82020-04-03 10:58:55 -070016#include <fstream>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050017#include <random>
Ed Tanousc282e8b2024-07-01 08:56:34 -070018#include <system_error>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050019
Ed Tanous1abe55e2018-09-05 08:30:59 -070020namespace persistent_data
21{
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +010022
Ed Tanous52cc1122020-07-18 13:51:21 -070023class ConfigFile
Ed Tanous1abe55e2018-09-05 08:30:59 -070024{
Ed Tanous271584a2019-07-09 16:24:22 -070025 uint64_t jsonRevision = 1;
Ed Tanousc963aa42017-10-27 16:00:19 -070026
Ed Tanous1abe55e2018-09-05 08:30:59 -070027 public:
Ratan Gupta845cb7d2019-07-12 00:32:25 +053028 // todo(ed) should read this from a fixed location somewhere, not CWD
29 static constexpr const char* filename = "bmcweb_persistent_data.json";
30
Ed Tanous52cc1122020-07-18 13:51:21 -070031 ConfigFile()
Ed Tanous1abe55e2018-09-05 08:30:59 -070032 {
33 readData();
Ed Tanousc963aa42017-10-27 16:00:19 -070034 }
Ed Tanousc963aa42017-10-27 16:00:19 -070035
Ed Tanous52cc1122020-07-18 13:51:21 -070036 ~ConfigFile()
Ed Tanous1abe55e2018-09-05 08:30:59 -070037 {
Gunnar Mills83cf8182020-11-11 15:37:34 -060038 // Make sure we aren't writing stale sessions
39 persistent_data::SessionStore::getInstance().applySessionTimeouts();
Ed Tanous1abe55e2018-09-05 08:30:59 -070040 if (persistent_data::SessionStore::getInstance().needsWrite())
41 {
42 writeData();
Kowalski, Kamil5cef0f72018-02-15 15:26:51 +010043 }
Ed Tanousc963aa42017-10-27 16:00:19 -070044 }
Ed Tanousc963aa42017-10-27 16:00:19 -070045
Ed Tanousecd6a3a2022-01-07 09:18:40 -080046 ConfigFile(const ConfigFile&) = delete;
47 ConfigFile(ConfigFile&&) = delete;
48 ConfigFile& operator=(const ConfigFile&) = delete;
49 ConfigFile& operator=(ConfigFile&&) = delete;
50
Ed Tanous1abe55e2018-09-05 08:30:59 -070051 // TODO(ed) this should really use protobuf, or some other serialization
52 // library, but adding another dependency is somewhat outside the scope of
53 // this application for the moment
54 void readData()
55 {
56 std::ifstream persistentFile(filename);
Ed Tanous271584a2019-07-09 16:24:22 -070057 uint64_t fileRevision = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -070058 if (persistentFile.is_open())
59 {
60 // call with exceptions disabled
61 auto data = nlohmann::json::parse(persistentFile, nullptr, false);
62 if (data.is_discarded())
63 {
Ed Tanous62598e32023-07-17 17:06:25 -070064 BMCWEB_LOG_ERROR("Error parsing persistent data in json file.");
Ed Tanous1abe55e2018-09-05 08:30:59 -070065 }
66 else
67 {
Ed Tanous0bdda662023-08-03 17:27:34 -070068 const nlohmann::json::object_t* obj =
69 data.get_ptr<nlohmann::json::object_t*>();
70 if (obj == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -070071 {
Ed Tanous0bdda662023-08-03 17:27:34 -070072 return;
73 }
74 for (const auto& item : *obj)
75 {
76 if (item.first == "revision")
Ed Tanous1abe55e2018-09-05 08:30:59 -070077 {
78 fileRevision = 0;
79
80 const uint64_t* uintPtr =
Ed Tanous0bdda662023-08-03 17:27:34 -070081 item.second.get_ptr<const uint64_t*>();
Ed Tanous1abe55e2018-09-05 08:30:59 -070082 if (uintPtr == nullptr)
83 {
Ed Tanous62598e32023-07-17 17:06:25 -070084 BMCWEB_LOG_ERROR("Failed to read revision flag");
Ed Tanous1abe55e2018-09-05 08:30:59 -070085 }
86 else
87 {
88 fileRevision = *uintPtr;
89 }
90 }
Ed Tanous0bdda662023-08-03 17:27:34 -070091 else if (item.first == "system_uuid")
Ed Tanous1abe55e2018-09-05 08:30:59 -070092 {
93 const std::string* jSystemUuid =
Ed Tanous0bdda662023-08-03 17:27:34 -070094 item.second.get_ptr<const std::string*>();
Ed Tanous1abe55e2018-09-05 08:30:59 -070095 if (jSystemUuid != nullptr)
96 {
97 systemUuid = *jSystemUuid;
98 }
99 }
Ed Tanous0bdda662023-08-03 17:27:34 -0700100 else if (item.first == "auth_config")
Zbigniew Kurzynski78158632019-11-05 12:57:37 +0100101 {
102 SessionStore::getInstance()
103 .getAuthMethodsConfig()
Ed Tanous0bdda662023-08-03 17:27:34 -0700104 .fromJson(item.second);
Zbigniew Kurzynski78158632019-11-05 12:57:37 +0100105 }
Ed Tanous0bdda662023-08-03 17:27:34 -0700106 else if (item.first == "sessions")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700107 {
Ed Tanous0bdda662023-08-03 17:27:34 -0700108 for (const auto& elem : item.second)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700109 {
110 std::shared_ptr<UserSession> newSession =
111 UserSession::fromJson(elem);
112
113 if (newSession == nullptr)
114 {
Ed Tanous62598e32023-07-17 17:06:25 -0700115 BMCWEB_LOG_ERROR("Problem reading session "
116 "from persistent store");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700117 continue;
118 }
119
Ed Tanous62598e32023-07-17 17:06:25 -0700120 BMCWEB_LOG_DEBUG("Restored session: {} {} {}",
121 newSession->csrfToken,
122 newSession->uniqueId,
123 newSession->sessionToken);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700124 SessionStore::getInstance().authTokens.emplace(
125 newSession->sessionToken, newSession);
126 }
127 }
Ed Tanous0bdda662023-08-03 17:27:34 -0700128 else if (item.first == "timeout")
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530129 {
130 const int64_t* jTimeout =
Ed Tanous0bdda662023-08-03 17:27:34 -0700131 item.second.get_ptr<const int64_t*>();
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530132 if (jTimeout == nullptr)
133 {
Ed Tanous62598e32023-07-17 17:06:25 -0700134 BMCWEB_LOG_DEBUG(
135 "Problem reading session timeout value");
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530136 continue;
137 }
138 std::chrono::seconds sessionTimeoutInseconds(*jTimeout);
Ed Tanous62598e32023-07-17 17:06:25 -0700139 BMCWEB_LOG_DEBUG("Restored Session Timeout: {}",
140 sessionTimeoutInseconds.count());
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530141 SessionStore::getInstance().updateSessionTimeout(
142 sessionTimeoutInseconds);
143 }
Ed Tanous0bdda662023-08-03 17:27:34 -0700144 else if (item.first == "eventservice_config")
JunLin Chen28afb492021-02-24 17:13:29 +0800145 {
Ed Tanous0bdda662023-08-03 17:27:34 -0700146 const nlohmann::json::object_t* esobj =
147 item.second
148 .get_ptr<const nlohmann::json::object_t*>();
149 if (esobj == nullptr)
150 {
151 BMCWEB_LOG_DEBUG(
152 "Problem reading EventService value");
153 continue;
154 }
155
JunLin Chen28afb492021-02-24 17:13:29 +0800156 EventServiceStore::getInstance()
157 .getEventServiceConfig()
Ed Tanous0bdda662023-08-03 17:27:34 -0700158 .fromJson(*esobj);
JunLin Chen28afb492021-02-24 17:13:29 +0800159 }
Ed Tanous0bdda662023-08-03 17:27:34 -0700160 else if (item.first == "subscriptions")
JunLin Chen28afb492021-02-24 17:13:29 +0800161 {
Ed Tanous0bdda662023-08-03 17:27:34 -0700162 for (const auto& elem : item.second)
JunLin Chen28afb492021-02-24 17:13:29 +0800163 {
Ed Tanous4b712a22023-08-02 12:56:52 -0700164 std::optional<UserSubscription> newSub =
JunLin Chen28afb492021-02-24 17:13:29 +0800165 UserSubscription::fromJson(elem);
166
Ed Tanous4b712a22023-08-02 12:56:52 -0700167 if (!newSub)
JunLin Chen28afb492021-02-24 17:13:29 +0800168 {
Ed Tanous62598e32023-07-17 17:06:25 -0700169 BMCWEB_LOG_ERROR("Problem reading subscription "
170 "from persistent store");
JunLin Chen28afb492021-02-24 17:13:29 +0800171 continue;
172 }
173
Ed Tanous62598e32023-07-17 17:06:25 -0700174 BMCWEB_LOG_DEBUG("Restored subscription: {} {}",
Ed Tanous4b712a22023-08-02 12:56:52 -0700175 newSub->id, newSub->customText);
176
Myung Bae5fe4ef32024-10-19 09:56:02 -0400177 EventServiceStore::getInstance()
178 .subscriptionsConfigMap.emplace(
179 newSub->id,
180 std::make_shared<UserSubscription>(
181 std::move(*newSub)));
JunLin Chen28afb492021-02-24 17:13:29 +0800182 }
183 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700184 else
185 {
186 // Do nothing in the case of extra fields. We may have
187 // cases where fields are added in the future, and we
188 // want to at least attempt to gracefully support
189 // downgrades in that case, even if we don't officially
190 // support it
191 }
192 }
193 }
194 }
195 bool needWrite = false;
196
197 if (systemUuid.empty())
198 {
Ed Tanous2c6ffdb2023-06-28 11:28:38 -0700199 systemUuid = bmcweb::getRandomUUID();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700200 needWrite = true;
201 }
202 if (fileRevision < jsonRevision)
203 {
204 needWrite = true;
205 }
206 // write revision changes or system uuid changes immediately
207 if (needWrite)
208 {
209 writeData();
210 }
211 }
212
213 void writeData()
214 {
Ed Tanousc282e8b2024-07-01 08:56:34 -0700215 std::filesystem::path path(filename);
216 path = path.parent_path();
Myung Baed8f8a7d2024-10-23 12:55:08 -0400217 if (!path.empty())
Ed Tanousc282e8b2024-07-01 08:56:34 -0700218 {
Myung Baed8f8a7d2024-10-23 12:55:08 -0400219 std::error_code ecDir;
220 std::filesystem::create_directories(path, ecDir);
221 if (ecDir)
222 {
223 BMCWEB_LOG_CRITICAL("Can't create persistent folders {}",
224 ecDir.message());
225 return;
226 }
Ed Tanousc282e8b2024-07-01 08:56:34 -0700227 }
228 boost::beast::file_posix persistentFile;
229 boost::system::error_code ec;
230 persistentFile.open(filename, boost::beast::file_mode::write, ec);
231 if (ec)
232 {
233 BMCWEB_LOG_CRITICAL("Unable to store persistent data to file {}",
234 ec.message());
235 return;
236 }
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530237
238 // set the permission of the file to 640
Ed Tanous52cc1122020-07-18 13:51:21 -0700239 std::filesystem::perms permission =
240 std::filesystem::perms::owner_read |
241 std::filesystem::perms::owner_write |
242 std::filesystem::perms::group_read;
Ed Tanousc282e8b2024-07-01 08:56:34 -0700243 std::filesystem::permissions(filename, permission, ec);
244 if (ec)
245 {
246 BMCWEB_LOG_CRITICAL("Failed to set filesystem permissions {}",
247 ec.message());
248 return;
249 }
Ed Tanous3ce36882024-06-09 10:58:16 -0700250 const AuthConfigMethods& c =
251 SessionStore::getInstance().getAuthMethodsConfig();
JunLin Chen28afb492021-02-24 17:13:29 +0800252 const auto& eventServiceConfig =
253 EventServiceStore::getInstance().getEventServiceConfig();
Ed Tanous14766872022-03-15 10:44:42 -0700254 nlohmann::json::object_t data;
255 nlohmann::json& authConfig = data["auth_config"];
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530256
Ed Tanous14766872022-03-15 10:44:42 -0700257 authConfig["XToken"] = c.xtoken;
258 authConfig["Cookie"] = c.cookie;
259 authConfig["SessionToken"] = c.sessionToken;
260 authConfig["BasicAuth"] = c.basic;
261 authConfig["TLS"] = c.tls;
Ed Tanous3281bcf2024-06-25 16:02:05 -0700262 authConfig["TLSStrict"] = c.tlsStrict;
Ed Tanous3ce36882024-06-09 10:58:16 -0700263 authConfig["TLSCommonNameParseMode"] =
264 static_cast<int>(c.mTLSCommonNameParsingMode);
JunLin Chen28afb492021-02-24 17:13:29 +0800265
Ed Tanous14766872022-03-15 10:44:42 -0700266 nlohmann::json& eventserviceConfig = data["eventservice_config"];
267 eventserviceConfig["ServiceEnabled"] = eventServiceConfig.enabled;
268 eventserviceConfig["DeliveryRetryAttempts"] =
269 eventServiceConfig.retryAttempts;
270 eventserviceConfig["DeliveryRetryIntervalSeconds"] =
271 eventServiceConfig.retryTimeoutInterval;
272
273 data["system_uuid"] = systemUuid;
274 data["revision"] = jsonRevision;
275 data["timeout"] = SessionStore::getInstance().getTimeoutInSeconds();
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700276
277 nlohmann::json& sessions = data["sessions"];
278 sessions = nlohmann::json::array();
279 for (const auto& p : SessionStore::getInstance().authTokens)
280 {
Ed Tanous89cda632024-04-16 08:45:54 -0700281 if (p.second->sessionType != persistent_data::SessionType::Basic &&
282 p.second->sessionType !=
283 persistent_data::SessionType::MutualTLS)
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700284 {
Ed Tanous14766872022-03-15 10:44:42 -0700285 nlohmann::json::object_t session;
286 session["unique_id"] = p.second->uniqueId;
287 session["session_token"] = p.second->sessionToken;
288 session["username"] = p.second->username;
289 session["csrf_token"] = p.second->csrfToken;
290 session["client_ip"] = p.second->clientIp;
Ed Tanouse01d0c32023-06-30 13:21:32 -0700291 const std::optional<std::string>& clientId = p.second->clientId;
292 if (clientId)
Ed Tanousbb759e32022-08-02 17:07:54 -0700293 {
Ed Tanouse01d0c32023-06-30 13:21:32 -0700294 session["client_id"] = *clientId;
Ed Tanousbb759e32022-08-02 17:07:54 -0700295 }
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500296 sessions.emplace_back(std::move(session));
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700297 }
298 }
JunLin Chen28afb492021-02-24 17:13:29 +0800299 nlohmann::json& subscriptions = data["subscriptions"];
300 subscriptions = nlohmann::json::array();
301 for (const auto& it :
302 EventServiceStore::getInstance().subscriptionsConfigMap)
303 {
Myung Bae5fe4ef32024-10-19 09:56:02 -0400304 if (it.second == nullptr)
305 {
306 continue;
307 }
308 const UserSubscription& subValue = *it.second;
wenlitaofbfb7882024-07-12 11:25:00 +0800309 if (subValue.subscriptionType == "SSE")
JunLin Chen28afb492021-02-24 17:13:29 +0800310 {
Ed Tanous62598e32023-07-17 17:06:25 -0700311 BMCWEB_LOG_DEBUG("The subscription type is SSE, so skipping.");
JunLin Chen28afb492021-02-24 17:13:29 +0800312 continue;
313 }
Ed Tanous601c71a2021-09-08 16:40:12 -0700314 nlohmann::json::object_t headers;
315 for (const boost::beast::http::fields::value_type& header :
wenlitaofbfb7882024-07-12 11:25:00 +0800316 subValue.httpHeaders)
Ed Tanous601c71a2021-09-08 16:40:12 -0700317 {
318 // Note, these are technically copies because nlohmann doesn't
319 // support key lookup by std::string_view. At least the
320 // following code can use move
321 // https://github.com/nlohmann/json/issues/1529
322 std::string name(header.name_string());
323 headers[std::move(name)] = header.value();
324 }
325
Ed Tanous14766872022-03-15 10:44:42 -0700326 nlohmann::json::object_t subscription;
327
wenlitaofbfb7882024-07-12 11:25:00 +0800328 subscription["Id"] = subValue.id;
329 subscription["Context"] = subValue.customText;
330 subscription["DeliveryRetryPolicy"] = subValue.retryPolicy;
Myung Bae5064a252024-10-04 09:34:25 -0700331 subscription["SendHeartbeat"] = subValue.sendHeartbeat;
332 subscription["HeartbeatIntervalMinutes"] =
333 subValue.hbIntervalMinutes;
wenlitaofbfb7882024-07-12 11:25:00 +0800334 subscription["Destination"] = subValue.destinationUrl;
335 subscription["EventFormatType"] = subValue.eventFormatType;
Ed Tanous14766872022-03-15 10:44:42 -0700336 subscription["HttpHeaders"] = std::move(headers);
wenlitaofbfb7882024-07-12 11:25:00 +0800337 subscription["MessageIds"] = subValue.registryMsgIds;
338 subscription["Protocol"] = subValue.protocol;
339 subscription["RegistryPrefixes"] = subValue.registryPrefixes;
Ed Tanousa14c9112024-09-04 10:46:47 -0700340 subscription["OriginResources"] = subValue.originResources;
wenlitaofbfb7882024-07-12 11:25:00 +0800341 subscription["ResourceTypes"] = subValue.resourceTypes;
342 subscription["SubscriptionType"] = subValue.subscriptionType;
Ed Tanous14766872022-03-15 10:44:42 -0700343 subscription["MetricReportDefinitions"] =
wenlitaofbfb7882024-07-12 11:25:00 +0800344 subValue.metricReportDefinitions;
Ed Tanous19bb3622024-07-05 10:07:40 -0500345 subscription["VerifyCertificate"] = subValue.verifyCertificate;
Ed Tanous14766872022-03-15 10:44:42 -0700346
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500347 subscriptions.emplace_back(std::move(subscription));
JunLin Chen28afb492021-02-24 17:13:29 +0800348 }
Ed Tanousc282e8b2024-07-01 08:56:34 -0700349 std::string out = nlohmann::json(data).dump(
350 -1, ' ', true, nlohmann::json::error_handler_t::replace);
351 persistentFile.write(out.data(), out.size(), ec);
352 if (ec)
353 {
354 BMCWEB_LOG_ERROR("Failed to write file {}", ec.message());
355 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700356 }
357
Ed Tanouse05aec52022-01-25 10:28:56 -0800358 std::string systemUuid;
Ed Tanousba9f9a62017-10-11 16:40:35 -0700359};
360
Ed Tanous52cc1122020-07-18 13:51:21 -0700361inline ConfigFile& getConfig()
362{
363 static ConfigFile f;
364 return f;
365}
366
Ed Tanous1abe55e2018-09-05 08:30:59 -0700367} // namespace persistent_data