blob: fff08d39f73cb9647de8e14ea0d6c307d477dc9f [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 {
160 std::shared_ptr<UserSubscription> newSubscription =
161 UserSubscription::fromJson(elem);
162
163 if (newSubscription == nullptr)
164 {
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: {} {}",
171 newSubscription->id,
172 newSubscription->customText);
JunLin Chen28afb492021-02-24 17:13:29 +0800173 EventServiceStore::getInstance()
174 .subscriptionsConfigMap.emplace(
175 newSubscription->id, newSubscription);
176 }
177 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700178 else
179 {
180 // Do nothing in the case of extra fields. We may have
181 // cases where fields are added in the future, and we
182 // want to at least attempt to gracefully support
183 // downgrades in that case, even if we don't officially
184 // support it
185 }
186 }
187 }
188 }
189 bool needWrite = false;
190
191 if (systemUuid.empty())
192 {
Ed Tanous2c6ffdb2023-06-28 11:28:38 -0700193 systemUuid = bmcweb::getRandomUUID();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700194 needWrite = true;
195 }
196 if (fileRevision < jsonRevision)
197 {
198 needWrite = true;
199 }
200 // write revision changes or system uuid changes immediately
201 if (needWrite)
202 {
203 writeData();
204 }
205 }
206
207 void writeData()
208 {
209 std::ofstream persistentFile(filename);
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530210
211 // set the permission of the file to 640
Ed Tanous52cc1122020-07-18 13:51:21 -0700212 std::filesystem::perms permission =
213 std::filesystem::perms::owner_read |
214 std::filesystem::perms::owner_write |
215 std::filesystem::perms::group_read;
216 std::filesystem::permissions(filename, permission);
Ed Tanous3ce36882024-06-09 10:58:16 -0700217 const AuthConfigMethods& c =
218 SessionStore::getInstance().getAuthMethodsConfig();
JunLin Chen28afb492021-02-24 17:13:29 +0800219 const auto& eventServiceConfig =
220 EventServiceStore::getInstance().getEventServiceConfig();
Ed Tanous14766872022-03-15 10:44:42 -0700221 nlohmann::json::object_t data;
222 nlohmann::json& authConfig = data["auth_config"];
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530223
Ed Tanous14766872022-03-15 10:44:42 -0700224 authConfig["XToken"] = c.xtoken;
225 authConfig["Cookie"] = c.cookie;
226 authConfig["SessionToken"] = c.sessionToken;
227 authConfig["BasicAuth"] = c.basic;
228 authConfig["TLS"] = c.tls;
Ed Tanous3ce36882024-06-09 10:58:16 -0700229 authConfig["TLSCommonNameParseMode"] =
230 static_cast<int>(c.mTLSCommonNameParsingMode);
JunLin Chen28afb492021-02-24 17:13:29 +0800231
Ed Tanous14766872022-03-15 10:44:42 -0700232 nlohmann::json& eventserviceConfig = data["eventservice_config"];
233 eventserviceConfig["ServiceEnabled"] = eventServiceConfig.enabled;
234 eventserviceConfig["DeliveryRetryAttempts"] =
235 eventServiceConfig.retryAttempts;
236 eventserviceConfig["DeliveryRetryIntervalSeconds"] =
237 eventServiceConfig.retryTimeoutInterval;
238
239 data["system_uuid"] = systemUuid;
240 data["revision"] = jsonRevision;
241 data["timeout"] = SessionStore::getInstance().getTimeoutInSeconds();
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700242
243 nlohmann::json& sessions = data["sessions"];
244 sessions = nlohmann::json::array();
245 for (const auto& p : SessionStore::getInstance().authTokens)
246 {
247 if (p.second->persistence !=
248 persistent_data::PersistenceType::SINGLE_REQUEST)
249 {
Ed Tanous14766872022-03-15 10:44:42 -0700250 nlohmann::json::object_t session;
251 session["unique_id"] = p.second->uniqueId;
252 session["session_token"] = p.second->sessionToken;
253 session["username"] = p.second->username;
254 session["csrf_token"] = p.second->csrfToken;
255 session["client_ip"] = p.second->clientIp;
Ed Tanouse01d0c32023-06-30 13:21:32 -0700256 const std::optional<std::string>& clientId = p.second->clientId;
257 if (clientId)
Ed Tanousbb759e32022-08-02 17:07:54 -0700258 {
Ed Tanouse01d0c32023-06-30 13:21:32 -0700259 session["client_id"] = *clientId;
Ed Tanousbb759e32022-08-02 17:07:54 -0700260 }
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500261 sessions.emplace_back(std::move(session));
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700262 }
263 }
JunLin Chen28afb492021-02-24 17:13:29 +0800264 nlohmann::json& subscriptions = data["subscriptions"];
265 subscriptions = nlohmann::json::array();
266 for (const auto& it :
267 EventServiceStore::getInstance().subscriptionsConfigMap)
268 {
269 std::shared_ptr<UserSubscription> subValue = it.second;
270 if (subValue->subscriptionType == "SSE")
271 {
Ed Tanous62598e32023-07-17 17:06:25 -0700272 BMCWEB_LOG_DEBUG("The subscription type is SSE, so skipping.");
JunLin Chen28afb492021-02-24 17:13:29 +0800273 continue;
274 }
Ed Tanous601c71a2021-09-08 16:40:12 -0700275 nlohmann::json::object_t headers;
276 for (const boost::beast::http::fields::value_type& header :
277 subValue->httpHeaders)
278 {
279 // Note, these are technically copies because nlohmann doesn't
280 // support key lookup by std::string_view. At least the
281 // following code can use move
282 // https://github.com/nlohmann/json/issues/1529
283 std::string name(header.name_string());
284 headers[std::move(name)] = header.value();
285 }
286
Ed Tanous14766872022-03-15 10:44:42 -0700287 nlohmann::json::object_t subscription;
288
289 subscription["Id"] = subValue->id;
290 subscription["Context"] = subValue->customText;
291 subscription["DeliveryRetryPolicy"] = subValue->retryPolicy;
292 subscription["Destination"] = subValue->destinationUrl;
293 subscription["EventFormatType"] = subValue->eventFormatType;
294 subscription["HttpHeaders"] = std::move(headers);
295 subscription["MessageIds"] = subValue->registryMsgIds;
296 subscription["Protocol"] = subValue->protocol;
297 subscription["RegistryPrefixes"] = subValue->registryPrefixes;
298 subscription["ResourceTypes"] = subValue->resourceTypes;
299 subscription["SubscriptionType"] = subValue->subscriptionType;
300 subscription["MetricReportDefinitions"] =
301 subValue->metricReportDefinitions;
302
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500303 subscriptions.emplace_back(std::move(subscription));
JunLin Chen28afb492021-02-24 17:13:29 +0800304 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700305 persistentFile << data;
306 }
307
Ed Tanouse05aec52022-01-25 10:28:56 -0800308 std::string systemUuid;
Ed Tanousba9f9a62017-10-11 16:40:35 -0700309};
310
Ed Tanous52cc1122020-07-18 13:51:21 -0700311inline ConfigFile& getConfig()
312{
313 static ConfigFile f;
314 return f;
315}
316
Ed Tanous1abe55e2018-09-05 08:30:59 -0700317} // namespace persistent_data