blob: 5079a8c151a5ce6031184cc56af5551148be1e7b [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 {
64 for (const auto& item : data.items())
65 {
66 if (item.key() == "revision")
67 {
68 fileRevision = 0;
69
70 const uint64_t* uintPtr =
71 item.value().get_ptr<const uint64_t*>();
72 if (uintPtr == nullptr)
73 {
Ed Tanous62598e32023-07-17 17:06:25 -070074 BMCWEB_LOG_ERROR("Failed to read revision flag");
Ed Tanous1abe55e2018-09-05 08:30:59 -070075 }
76 else
77 {
78 fileRevision = *uintPtr;
79 }
80 }
81 else if (item.key() == "system_uuid")
82 {
83 const std::string* jSystemUuid =
84 item.value().get_ptr<const std::string*>();
85 if (jSystemUuid != nullptr)
86 {
87 systemUuid = *jSystemUuid;
88 }
89 }
Zbigniew Kurzynski78158632019-11-05 12:57:37 +010090 else if (item.key() == "auth_config")
91 {
92 SessionStore::getInstance()
93 .getAuthMethodsConfig()
94 .fromJson(item.value());
95 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070096 else if (item.key() == "sessions")
97 {
98 for (const auto& elem : item.value())
99 {
100 std::shared_ptr<UserSession> newSession =
101 UserSession::fromJson(elem);
102
103 if (newSession == nullptr)
104 {
Ed Tanous62598e32023-07-17 17:06:25 -0700105 BMCWEB_LOG_ERROR("Problem reading session "
106 "from persistent store");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700107 continue;
108 }
109
Ed Tanous62598e32023-07-17 17:06:25 -0700110 BMCWEB_LOG_DEBUG("Restored session: {} {} {}",
111 newSession->csrfToken,
112 newSession->uniqueId,
113 newSession->sessionToken);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700114 SessionStore::getInstance().authTokens.emplace(
115 newSession->sessionToken, newSession);
116 }
117 }
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530118 else if (item.key() == "timeout")
119 {
120 const int64_t* jTimeout =
121 item.value().get_ptr<int64_t*>();
122 if (jTimeout == nullptr)
123 {
Ed Tanous62598e32023-07-17 17:06:25 -0700124 BMCWEB_LOG_DEBUG(
125 "Problem reading session timeout value");
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530126 continue;
127 }
128 std::chrono::seconds sessionTimeoutInseconds(*jTimeout);
Ed Tanous62598e32023-07-17 17:06:25 -0700129 BMCWEB_LOG_DEBUG("Restored Session Timeout: {}",
130 sessionTimeoutInseconds.count());
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530131 SessionStore::getInstance().updateSessionTimeout(
132 sessionTimeoutInseconds);
133 }
JunLin Chen28afb492021-02-24 17:13:29 +0800134 else if (item.key() == "eventservice_config")
135 {
136 EventServiceStore::getInstance()
137 .getEventServiceConfig()
138 .fromJson(item.value());
139 }
140 else if (item.key() == "subscriptions")
141 {
142 for (const auto& elem : item.value())
143 {
144 std::shared_ptr<UserSubscription> newSubscription =
145 UserSubscription::fromJson(elem);
146
147 if (newSubscription == nullptr)
148 {
Ed Tanous62598e32023-07-17 17:06:25 -0700149 BMCWEB_LOG_ERROR("Problem reading subscription "
150 "from persistent store");
JunLin Chen28afb492021-02-24 17:13:29 +0800151 continue;
152 }
153
Ed Tanous62598e32023-07-17 17:06:25 -0700154 BMCWEB_LOG_DEBUG("Restored subscription: {} {}",
155 newSubscription->id,
156 newSubscription->customText);
JunLin Chen28afb492021-02-24 17:13:29 +0800157 EventServiceStore::getInstance()
158 .subscriptionsConfigMap.emplace(
159 newSubscription->id, newSubscription);
160 }
161 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700162 else
163 {
164 // Do nothing in the case of extra fields. We may have
165 // cases where fields are added in the future, and we
166 // want to at least attempt to gracefully support
167 // downgrades in that case, even if we don't officially
168 // support it
169 }
170 }
171 }
172 }
173 bool needWrite = false;
174
175 if (systemUuid.empty())
176 {
Ed Tanous2c6ffdb2023-06-28 11:28:38 -0700177 systemUuid = bmcweb::getRandomUUID();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700178 needWrite = true;
179 }
180 if (fileRevision < jsonRevision)
181 {
182 needWrite = true;
183 }
184 // write revision changes or system uuid changes immediately
185 if (needWrite)
186 {
187 writeData();
188 }
189 }
190
191 void writeData()
192 {
193 std::ofstream persistentFile(filename);
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530194
195 // set the permission of the file to 640
Ed Tanous52cc1122020-07-18 13:51:21 -0700196 std::filesystem::perms permission =
197 std::filesystem::perms::owner_read |
198 std::filesystem::perms::owner_write |
199 std::filesystem::perms::group_read;
200 std::filesystem::permissions(filename, permission);
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700201 const auto& c = SessionStore::getInstance().getAuthMethodsConfig();
JunLin Chen28afb492021-02-24 17:13:29 +0800202 const auto& eventServiceConfig =
203 EventServiceStore::getInstance().getEventServiceConfig();
Ed Tanous14766872022-03-15 10:44:42 -0700204 nlohmann::json::object_t data;
205 nlohmann::json& authConfig = data["auth_config"];
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530206
Ed Tanous14766872022-03-15 10:44:42 -0700207 authConfig["XToken"] = c.xtoken;
208 authConfig["Cookie"] = c.cookie;
209 authConfig["SessionToken"] = c.sessionToken;
210 authConfig["BasicAuth"] = c.basic;
211 authConfig["TLS"] = c.tls;
JunLin Chen28afb492021-02-24 17:13:29 +0800212
Ed Tanous14766872022-03-15 10:44:42 -0700213 nlohmann::json& eventserviceConfig = data["eventservice_config"];
214 eventserviceConfig["ServiceEnabled"] = eventServiceConfig.enabled;
215 eventserviceConfig["DeliveryRetryAttempts"] =
216 eventServiceConfig.retryAttempts;
217 eventserviceConfig["DeliveryRetryIntervalSeconds"] =
218 eventServiceConfig.retryTimeoutInterval;
219
220 data["system_uuid"] = systemUuid;
221 data["revision"] = jsonRevision;
222 data["timeout"] = SessionStore::getInstance().getTimeoutInSeconds();
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700223
224 nlohmann::json& sessions = data["sessions"];
225 sessions = nlohmann::json::array();
226 for (const auto& p : SessionStore::getInstance().authTokens)
227 {
228 if (p.second->persistence !=
229 persistent_data::PersistenceType::SINGLE_REQUEST)
230 {
Ed Tanous14766872022-03-15 10:44:42 -0700231 nlohmann::json::object_t session;
232 session["unique_id"] = p.second->uniqueId;
233 session["session_token"] = p.second->sessionToken;
234 session["username"] = p.second->username;
235 session["csrf_token"] = p.second->csrfToken;
236 session["client_ip"] = p.second->clientIp;
Ed Tanousbb759e32022-08-02 17:07:54 -0700237 if (p.second->clientId)
238 {
239 session["client_id"] = *p.second->clientId;
240 }
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500241 sessions.emplace_back(std::move(session));
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700242 }
243 }
JunLin Chen28afb492021-02-24 17:13:29 +0800244 nlohmann::json& subscriptions = data["subscriptions"];
245 subscriptions = nlohmann::json::array();
246 for (const auto& it :
247 EventServiceStore::getInstance().subscriptionsConfigMap)
248 {
249 std::shared_ptr<UserSubscription> subValue = it.second;
250 if (subValue->subscriptionType == "SSE")
251 {
Ed Tanous62598e32023-07-17 17:06:25 -0700252 BMCWEB_LOG_DEBUG("The subscription type is SSE, so skipping.");
JunLin Chen28afb492021-02-24 17:13:29 +0800253 continue;
254 }
Ed Tanous601c71a2021-09-08 16:40:12 -0700255 nlohmann::json::object_t headers;
256 for (const boost::beast::http::fields::value_type& header :
257 subValue->httpHeaders)
258 {
259 // Note, these are technically copies because nlohmann doesn't
260 // support key lookup by std::string_view. At least the
261 // following code can use move
262 // https://github.com/nlohmann/json/issues/1529
263 std::string name(header.name_string());
264 headers[std::move(name)] = header.value();
265 }
266
Ed Tanous14766872022-03-15 10:44:42 -0700267 nlohmann::json::object_t subscription;
268
269 subscription["Id"] = subValue->id;
270 subscription["Context"] = subValue->customText;
271 subscription["DeliveryRetryPolicy"] = subValue->retryPolicy;
272 subscription["Destination"] = subValue->destinationUrl;
273 subscription["EventFormatType"] = subValue->eventFormatType;
274 subscription["HttpHeaders"] = std::move(headers);
275 subscription["MessageIds"] = subValue->registryMsgIds;
276 subscription["Protocol"] = subValue->protocol;
277 subscription["RegistryPrefixes"] = subValue->registryPrefixes;
278 subscription["ResourceTypes"] = subValue->resourceTypes;
279 subscription["SubscriptionType"] = subValue->subscriptionType;
280 subscription["MetricReportDefinitions"] =
281 subValue->metricReportDefinitions;
282
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500283 subscriptions.emplace_back(std::move(subscription));
JunLin Chen28afb492021-02-24 17:13:29 +0800284 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700285 persistentFile << data;
286 }
287
Ed Tanouse05aec52022-01-25 10:28:56 -0800288 std::string systemUuid;
Ed Tanousba9f9a62017-10-11 16:40:35 -0700289};
290
Ed Tanous52cc1122020-07-18 13:51:21 -0700291inline ConfigFile& getConfig()
292{
293 static ConfigFile f;
294 return f;
295}
296
Ed Tanous1abe55e2018-09-05 08:30:59 -0700297} // namespace persistent_data