blob: 3bd256d931c237bc24a8c1af7635c806c8bca080 [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 Tanous5fb91ba2020-09-28 15:41:28 -0700217 const auto& c = SessionStore::getInstance().getAuthMethodsConfig();
JunLin Chen28afb492021-02-24 17:13:29 +0800218 const auto& eventServiceConfig =
219 EventServiceStore::getInstance().getEventServiceConfig();
Ed Tanous14766872022-03-15 10:44:42 -0700220 nlohmann::json::object_t data;
221 nlohmann::json& authConfig = data["auth_config"];
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530222
Ed Tanous14766872022-03-15 10:44:42 -0700223 authConfig["XToken"] = c.xtoken;
224 authConfig["Cookie"] = c.cookie;
225 authConfig["SessionToken"] = c.sessionToken;
226 authConfig["BasicAuth"] = c.basic;
227 authConfig["TLS"] = c.tls;
JunLin Chen28afb492021-02-24 17:13:29 +0800228
Ed Tanous14766872022-03-15 10:44:42 -0700229 nlohmann::json& eventserviceConfig = data["eventservice_config"];
230 eventserviceConfig["ServiceEnabled"] = eventServiceConfig.enabled;
231 eventserviceConfig["DeliveryRetryAttempts"] =
232 eventServiceConfig.retryAttempts;
233 eventserviceConfig["DeliveryRetryIntervalSeconds"] =
234 eventServiceConfig.retryTimeoutInterval;
235
236 data["system_uuid"] = systemUuid;
237 data["revision"] = jsonRevision;
238 data["timeout"] = SessionStore::getInstance().getTimeoutInSeconds();
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700239
240 nlohmann::json& sessions = data["sessions"];
241 sessions = nlohmann::json::array();
242 for (const auto& p : SessionStore::getInstance().authTokens)
243 {
244 if (p.second->persistence !=
245 persistent_data::PersistenceType::SINGLE_REQUEST)
246 {
Ed Tanous14766872022-03-15 10:44:42 -0700247 nlohmann::json::object_t session;
248 session["unique_id"] = p.second->uniqueId;
249 session["session_token"] = p.second->sessionToken;
250 session["username"] = p.second->username;
251 session["csrf_token"] = p.second->csrfToken;
252 session["client_ip"] = p.second->clientIp;
Ed Tanouse01d0c32023-06-30 13:21:32 -0700253 const std::optional<std::string>& clientId = p.second->clientId;
254 if (clientId)
Ed Tanousbb759e32022-08-02 17:07:54 -0700255 {
Ed Tanouse01d0c32023-06-30 13:21:32 -0700256 session["client_id"] = *clientId;
Ed Tanousbb759e32022-08-02 17:07:54 -0700257 }
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500258 sessions.emplace_back(std::move(session));
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700259 }
260 }
JunLin Chen28afb492021-02-24 17:13:29 +0800261 nlohmann::json& subscriptions = data["subscriptions"];
262 subscriptions = nlohmann::json::array();
263 for (const auto& it :
264 EventServiceStore::getInstance().subscriptionsConfigMap)
265 {
266 std::shared_ptr<UserSubscription> subValue = it.second;
267 if (subValue->subscriptionType == "SSE")
268 {
Ed Tanous62598e32023-07-17 17:06:25 -0700269 BMCWEB_LOG_DEBUG("The subscription type is SSE, so skipping.");
JunLin Chen28afb492021-02-24 17:13:29 +0800270 continue;
271 }
Ed Tanous601c71a2021-09-08 16:40:12 -0700272 nlohmann::json::object_t headers;
273 for (const boost::beast::http::fields::value_type& header :
274 subValue->httpHeaders)
275 {
276 // Note, these are technically copies because nlohmann doesn't
277 // support key lookup by std::string_view. At least the
278 // following code can use move
279 // https://github.com/nlohmann/json/issues/1529
280 std::string name(header.name_string());
281 headers[std::move(name)] = header.value();
282 }
283
Ed Tanous14766872022-03-15 10:44:42 -0700284 nlohmann::json::object_t subscription;
285
286 subscription["Id"] = subValue->id;
287 subscription["Context"] = subValue->customText;
288 subscription["DeliveryRetryPolicy"] = subValue->retryPolicy;
289 subscription["Destination"] = subValue->destinationUrl;
290 subscription["EventFormatType"] = subValue->eventFormatType;
291 subscription["HttpHeaders"] = std::move(headers);
292 subscription["MessageIds"] = subValue->registryMsgIds;
293 subscription["Protocol"] = subValue->protocol;
294 subscription["RegistryPrefixes"] = subValue->registryPrefixes;
295 subscription["ResourceTypes"] = subValue->resourceTypes;
296 subscription["SubscriptionType"] = subValue->subscriptionType;
297 subscription["MetricReportDefinitions"] =
298 subValue->metricReportDefinitions;
299
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500300 subscriptions.emplace_back(std::move(subscription));
JunLin Chen28afb492021-02-24 17:13:29 +0800301 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700302 persistentFile << data;
303 }
304
Ed Tanouse05aec52022-01-25 10:28:56 -0800305 std::string systemUuid;
Ed Tanousba9f9a62017-10-11 16:40:35 -0700306};
307
Ed Tanous52cc1122020-07-18 13:51:21 -0700308inline ConfigFile& getConfig()
309{
310 static ConfigFile f;
311 return f;
312}
313
Ed Tanous1abe55e2018-09-05 08:30:59 -0700314} // namespace persistent_data