blob: a08ca7e08657c7732633f6a55e5d3c6d29c78dee [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 {
60 BMCWEB_LOG_ERROR
61 << "Error parsing persistent data in json file.";
62 }
63 else
64 {
65 for (const auto& item : data.items())
66 {
67 if (item.key() == "revision")
68 {
69 fileRevision = 0;
70
71 const uint64_t* uintPtr =
72 item.value().get_ptr<const uint64_t*>();
73 if (uintPtr == nullptr)
74 {
75 BMCWEB_LOG_ERROR << "Failed to read revision flag";
76 }
77 else
78 {
79 fileRevision = *uintPtr;
80 }
81 }
82 else if (item.key() == "system_uuid")
83 {
84 const std::string* jSystemUuid =
85 item.value().get_ptr<const std::string*>();
86 if (jSystemUuid != nullptr)
87 {
88 systemUuid = *jSystemUuid;
89 }
90 }
Zbigniew Kurzynski78158632019-11-05 12:57:37 +010091 else if (item.key() == "auth_config")
92 {
93 SessionStore::getInstance()
94 .getAuthMethodsConfig()
95 .fromJson(item.value());
96 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070097 else if (item.key() == "sessions")
98 {
99 for (const auto& elem : item.value())
100 {
101 std::shared_ptr<UserSession> newSession =
102 UserSession::fromJson(elem);
103
104 if (newSession == nullptr)
105 {
106 BMCWEB_LOG_ERROR << "Problem reading session "
107 "from persistent store";
108 continue;
109 }
110
111 BMCWEB_LOG_DEBUG
112 << "Restored session: " << newSession->csrfToken
113 << " " << newSession->uniqueId << " "
114 << newSession->sessionToken;
115 SessionStore::getInstance().authTokens.emplace(
116 newSession->sessionToken, newSession);
117 }
118 }
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530119 else if (item.key() == "timeout")
120 {
121 const int64_t* jTimeout =
122 item.value().get_ptr<int64_t*>();
123 if (jTimeout == nullptr)
124 {
125 BMCWEB_LOG_DEBUG
126 << "Problem reading session timeout value";
127 continue;
128 }
129 std::chrono::seconds sessionTimeoutInseconds(*jTimeout);
130 BMCWEB_LOG_DEBUG << "Restored Session Timeout: "
131 << sessionTimeoutInseconds.count();
132 SessionStore::getInstance().updateSessionTimeout(
133 sessionTimeoutInseconds);
134 }
JunLin Chen28afb492021-02-24 17:13:29 +0800135 else if (item.key() == "eventservice_config")
136 {
137 EventServiceStore::getInstance()
138 .getEventServiceConfig()
139 .fromJson(item.value());
140 }
141 else if (item.key() == "subscriptions")
142 {
143 for (const auto& elem : item.value())
144 {
145 std::shared_ptr<UserSubscription> newSubscription =
146 UserSubscription::fromJson(elem);
147
148 if (newSubscription == nullptr)
149 {
150 BMCWEB_LOG_ERROR
151 << "Problem reading subscription "
152 "from persistent store";
153 continue;
154 }
155
156 BMCWEB_LOG_DEBUG << "Restored subscription: "
157 << newSubscription->id << " "
158 << newSubscription->customText;
159 EventServiceStore::getInstance()
160 .subscriptionsConfigMap.emplace(
161 newSubscription->id, newSubscription);
162 }
163 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700164 else
165 {
166 // Do nothing in the case of extra fields. We may have
167 // cases where fields are added in the future, and we
168 // want to at least attempt to gracefully support
169 // downgrades in that case, even if we don't officially
170 // support it
171 }
172 }
173 }
174 }
175 bool needWrite = false;
176
177 if (systemUuid.empty())
178 {
Ed Tanous2c6ffdb2023-06-28 11:28:38 -0700179 systemUuid = bmcweb::getRandomUUID();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700180 needWrite = true;
181 }
182 if (fileRevision < jsonRevision)
183 {
184 needWrite = true;
185 }
186 // write revision changes or system uuid changes immediately
187 if (needWrite)
188 {
189 writeData();
190 }
191 }
192
193 void writeData()
194 {
195 std::ofstream persistentFile(filename);
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530196
197 // set the permission of the file to 640
Ed Tanous52cc1122020-07-18 13:51:21 -0700198 std::filesystem::perms permission =
199 std::filesystem::perms::owner_read |
200 std::filesystem::perms::owner_write |
201 std::filesystem::perms::group_read;
202 std::filesystem::permissions(filename, permission);
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700203 const auto& c = SessionStore::getInstance().getAuthMethodsConfig();
JunLin Chen28afb492021-02-24 17:13:29 +0800204 const auto& eventServiceConfig =
205 EventServiceStore::getInstance().getEventServiceConfig();
Ed Tanous14766872022-03-15 10:44:42 -0700206 nlohmann::json::object_t data;
207 nlohmann::json& authConfig = data["auth_config"];
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530208
Ed Tanous14766872022-03-15 10:44:42 -0700209 authConfig["XToken"] = c.xtoken;
210 authConfig["Cookie"] = c.cookie;
211 authConfig["SessionToken"] = c.sessionToken;
212 authConfig["BasicAuth"] = c.basic;
213 authConfig["TLS"] = c.tls;
JunLin Chen28afb492021-02-24 17:13:29 +0800214
Ed Tanous14766872022-03-15 10:44:42 -0700215 nlohmann::json& eventserviceConfig = data["eventservice_config"];
216 eventserviceConfig["ServiceEnabled"] = eventServiceConfig.enabled;
217 eventserviceConfig["DeliveryRetryAttempts"] =
218 eventServiceConfig.retryAttempts;
219 eventserviceConfig["DeliveryRetryIntervalSeconds"] =
220 eventServiceConfig.retryTimeoutInterval;
221
222 data["system_uuid"] = systemUuid;
223 data["revision"] = jsonRevision;
224 data["timeout"] = SessionStore::getInstance().getTimeoutInSeconds();
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700225
226 nlohmann::json& sessions = data["sessions"];
227 sessions = nlohmann::json::array();
228 for (const auto& p : SessionStore::getInstance().authTokens)
229 {
230 if (p.second->persistence !=
231 persistent_data::PersistenceType::SINGLE_REQUEST)
232 {
Ed Tanous14766872022-03-15 10:44:42 -0700233 nlohmann::json::object_t session;
234 session["unique_id"] = p.second->uniqueId;
235 session["session_token"] = p.second->sessionToken;
236 session["username"] = p.second->username;
237 session["csrf_token"] = p.second->csrfToken;
238 session["client_ip"] = p.second->clientIp;
Ed Tanousbb759e32022-08-02 17:07:54 -0700239 if (p.second->clientId)
240 {
241 session["client_id"] = *p.second->clientId;
242 }
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500243 sessions.emplace_back(std::move(session));
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700244 }
245 }
JunLin Chen28afb492021-02-24 17:13:29 +0800246 nlohmann::json& subscriptions = data["subscriptions"];
247 subscriptions = nlohmann::json::array();
248 for (const auto& it :
249 EventServiceStore::getInstance().subscriptionsConfigMap)
250 {
251 std::shared_ptr<UserSubscription> subValue = it.second;
252 if (subValue->subscriptionType == "SSE")
253 {
254 BMCWEB_LOG_DEBUG
255 << "The subscription type is SSE, so skipping.";
256 continue;
257 }
Ed Tanous601c71a2021-09-08 16:40:12 -0700258 nlohmann::json::object_t headers;
259 for (const boost::beast::http::fields::value_type& header :
260 subValue->httpHeaders)
261 {
262 // Note, these are technically copies because nlohmann doesn't
263 // support key lookup by std::string_view. At least the
264 // following code can use move
265 // https://github.com/nlohmann/json/issues/1529
266 std::string name(header.name_string());
267 headers[std::move(name)] = header.value();
268 }
269
Ed Tanous14766872022-03-15 10:44:42 -0700270 nlohmann::json::object_t subscription;
271
272 subscription["Id"] = subValue->id;
273 subscription["Context"] = subValue->customText;
274 subscription["DeliveryRetryPolicy"] = subValue->retryPolicy;
275 subscription["Destination"] = subValue->destinationUrl;
276 subscription["EventFormatType"] = subValue->eventFormatType;
277 subscription["HttpHeaders"] = std::move(headers);
278 subscription["MessageIds"] = subValue->registryMsgIds;
279 subscription["Protocol"] = subValue->protocol;
280 subscription["RegistryPrefixes"] = subValue->registryPrefixes;
281 subscription["ResourceTypes"] = subValue->resourceTypes;
282 subscription["SubscriptionType"] = subValue->subscriptionType;
283 subscription["MetricReportDefinitions"] =
284 subValue->metricReportDefinitions;
285
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500286 subscriptions.emplace_back(std::move(subscription));
JunLin Chen28afb492021-02-24 17:13:29 +0800287 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700288 persistentFile << data;
289 }
290
Ed Tanouse05aec52022-01-25 10:28:56 -0800291 std::string systemUuid;
Ed Tanousba9f9a62017-10-11 16:40:35 -0700292};
293
Ed Tanous52cc1122020-07-18 13:51:21 -0700294inline ConfigFile& getConfig()
295{
296 static ConfigFile f;
297 return f;
298}
299
Ed Tanous1abe55e2018-09-05 08:30:59 -0700300} // namespace persistent_data