blob: de5d6780f81dafbc25dc9d2e05e5fc9bd6d127ce [file] [log] [blame]
Ed Tanousba9f9a62017-10-11 16:40:35 -07001#pragma once
2
Ed Tanous04e438c2020-10-03 08:06:26 -07003#include <app.hpp>
Ed Tanous601c71a2021-09-08 16:40:12 -07004#include <boost/beast/http/fields.hpp>
Ed Tanousba9f9a62017-10-11 16:40:35 -07005#include <boost/uuid/uuid.hpp>
6#include <boost/uuid/uuid_generators.hpp>
7#include <boost/uuid/uuid_io.hpp>
JunLin Chen28afb492021-02-24 17:13:29 +08008#include <event_service_store.hpp>
Ed Tanous04e438c2020-10-03 08:06:26 -07009#include <http_request.hpp>
10#include <http_response.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070011#include <nlohmann/json.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070012#include <sessions.hpp>
Ed Tanousba9f9a62017-10-11 16:40:35 -070013
Gunnar Mills1214b7e2020-06-04 10:11:30 -050014#include <filesystem>
James Feist3909dc82020-04-03 10:58:55 -070015#include <fstream>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050016#include <random>
17
Ed Tanous1abe55e2018-09-05 08:30:59 -070018namespace persistent_data
19{
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +010020
Ed Tanous52cc1122020-07-18 13:51:21 -070021class ConfigFile
Ed Tanous1abe55e2018-09-05 08:30:59 -070022{
Ed Tanous271584a2019-07-09 16:24:22 -070023 uint64_t jsonRevision = 1;
Ed Tanousc963aa42017-10-27 16:00:19 -070024
Ed Tanous1abe55e2018-09-05 08:30:59 -070025 public:
Ratan Gupta845cb7d2019-07-12 00:32:25 +053026 // todo(ed) should read this from a fixed location somewhere, not CWD
27 static constexpr const char* filename = "bmcweb_persistent_data.json";
28
Ed Tanous52cc1122020-07-18 13:51:21 -070029 ConfigFile()
Ed Tanous1abe55e2018-09-05 08:30:59 -070030 {
31 readData();
Ed Tanousc963aa42017-10-27 16:00:19 -070032 }
Ed Tanousc963aa42017-10-27 16:00:19 -070033
Ed Tanous52cc1122020-07-18 13:51:21 -070034 ~ConfigFile()
Ed Tanous1abe55e2018-09-05 08:30:59 -070035 {
Gunnar Mills83cf8182020-11-11 15:37:34 -060036 // Make sure we aren't writing stale sessions
37 persistent_data::SessionStore::getInstance().applySessionTimeouts();
Ed Tanous1abe55e2018-09-05 08:30:59 -070038 if (persistent_data::SessionStore::getInstance().needsWrite())
39 {
40 writeData();
Kowalski, Kamil5cef0f72018-02-15 15:26:51 +010041 }
Ed Tanousc963aa42017-10-27 16:00:19 -070042 }
Ed Tanousc963aa42017-10-27 16:00:19 -070043
Ed Tanousecd6a3a2022-01-07 09:18:40 -080044 ConfigFile(const ConfigFile&) = delete;
45 ConfigFile(ConfigFile&&) = delete;
46 ConfigFile& operator=(const ConfigFile&) = delete;
47 ConfigFile& operator=(ConfigFile&&) = delete;
48
Ed Tanous1abe55e2018-09-05 08:30:59 -070049 // TODO(ed) this should really use protobuf, or some other serialization
50 // library, but adding another dependency is somewhat outside the scope of
51 // this application for the moment
52 void readData()
53 {
54 std::ifstream persistentFile(filename);
Ed Tanous271584a2019-07-09 16:24:22 -070055 uint64_t fileRevision = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -070056 if (persistentFile.is_open())
57 {
58 // call with exceptions disabled
59 auto data = nlohmann::json::parse(persistentFile, nullptr, false);
60 if (data.is_discarded())
61 {
62 BMCWEB_LOG_ERROR
63 << "Error parsing persistent data in json file.";
64 }
65 else
66 {
67 for (const auto& item : data.items())
68 {
69 if (item.key() == "revision")
70 {
71 fileRevision = 0;
72
73 const uint64_t* uintPtr =
74 item.value().get_ptr<const uint64_t*>();
75 if (uintPtr == nullptr)
76 {
77 BMCWEB_LOG_ERROR << "Failed to read revision flag";
78 }
79 else
80 {
81 fileRevision = *uintPtr;
82 }
83 }
84 else if (item.key() == "system_uuid")
85 {
86 const std::string* jSystemUuid =
87 item.value().get_ptr<const std::string*>();
88 if (jSystemUuid != nullptr)
89 {
90 systemUuid = *jSystemUuid;
91 }
92 }
Zbigniew Kurzynski78158632019-11-05 12:57:37 +010093 else if (item.key() == "auth_config")
94 {
95 SessionStore::getInstance()
96 .getAuthMethodsConfig()
97 .fromJson(item.value());
98 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070099 else if (item.key() == "sessions")
100 {
101 for (const auto& elem : item.value())
102 {
103 std::shared_ptr<UserSession> newSession =
104 UserSession::fromJson(elem);
105
106 if (newSession == nullptr)
107 {
108 BMCWEB_LOG_ERROR << "Problem reading session "
109 "from persistent store";
110 continue;
111 }
112
113 BMCWEB_LOG_DEBUG
114 << "Restored session: " << newSession->csrfToken
115 << " " << newSession->uniqueId << " "
116 << newSession->sessionToken;
117 SessionStore::getInstance().authTokens.emplace(
118 newSession->sessionToken, newSession);
119 }
120 }
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530121 else if (item.key() == "timeout")
122 {
123 const int64_t* jTimeout =
124 item.value().get_ptr<int64_t*>();
125 if (jTimeout == nullptr)
126 {
127 BMCWEB_LOG_DEBUG
128 << "Problem reading session timeout value";
129 continue;
130 }
131 std::chrono::seconds sessionTimeoutInseconds(*jTimeout);
132 BMCWEB_LOG_DEBUG << "Restored Session Timeout: "
133 << sessionTimeoutInseconds.count();
134 SessionStore::getInstance().updateSessionTimeout(
135 sessionTimeoutInseconds);
136 }
JunLin Chen28afb492021-02-24 17:13:29 +0800137 else if (item.key() == "eventservice_config")
138 {
139 EventServiceStore::getInstance()
140 .getEventServiceConfig()
141 .fromJson(item.value());
142 }
143 else if (item.key() == "subscriptions")
144 {
145 for (const auto& elem : item.value())
146 {
147 std::shared_ptr<UserSubscription> newSubscription =
148 UserSubscription::fromJson(elem);
149
150 if (newSubscription == nullptr)
151 {
152 BMCWEB_LOG_ERROR
153 << "Problem reading subscription "
154 "from persistent store";
155 continue;
156 }
157
158 BMCWEB_LOG_DEBUG << "Restored subscription: "
159 << newSubscription->id << " "
160 << newSubscription->customText;
161 EventServiceStore::getInstance()
162 .subscriptionsConfigMap.emplace(
163 newSubscription->id, newSubscription);
164 }
165 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700166 else
167 {
168 // Do nothing in the case of extra fields. We may have
169 // cases where fields are added in the future, and we
170 // want to at least attempt to gracefully support
171 // downgrades in that case, even if we don't officially
172 // support it
173 }
174 }
175 }
176 }
177 bool needWrite = false;
178
179 if (systemUuid.empty())
180 {
181 systemUuid =
182 boost::uuids::to_string(boost::uuids::random_generator()());
183 needWrite = true;
184 }
185 if (fileRevision < jsonRevision)
186 {
187 needWrite = true;
188 }
189 // write revision changes or system uuid changes immediately
190 if (needWrite)
191 {
192 writeData();
193 }
194 }
195
196 void writeData()
197 {
198 std::ofstream persistentFile(filename);
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530199
200 // set the permission of the file to 640
Ed Tanous52cc1122020-07-18 13:51:21 -0700201 std::filesystem::perms permission =
202 std::filesystem::perms::owner_read |
203 std::filesystem::perms::owner_write |
204 std::filesystem::perms::group_read;
205 std::filesystem::permissions(filename, permission);
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700206 const auto& c = SessionStore::getInstance().getAuthMethodsConfig();
JunLin Chen28afb492021-02-24 17:13:29 +0800207 const auto& eventServiceConfig =
208 EventServiceStore::getInstance().getEventServiceConfig();
Ed Tanous14766872022-03-15 10:44:42 -0700209 nlohmann::json::object_t data;
210 nlohmann::json& authConfig = data["auth_config"];
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530211
Ed Tanous14766872022-03-15 10:44:42 -0700212 authConfig["XToken"] = c.xtoken;
213 authConfig["Cookie"] = c.cookie;
214 authConfig["SessionToken"] = c.sessionToken;
215 authConfig["BasicAuth"] = c.basic;
216 authConfig["TLS"] = c.tls;
JunLin Chen28afb492021-02-24 17:13:29 +0800217
Ed Tanous14766872022-03-15 10:44:42 -0700218 nlohmann::json& eventserviceConfig = data["eventservice_config"];
219 eventserviceConfig["ServiceEnabled"] = eventServiceConfig.enabled;
220 eventserviceConfig["DeliveryRetryAttempts"] =
221 eventServiceConfig.retryAttempts;
222 eventserviceConfig["DeliveryRetryIntervalSeconds"] =
223 eventServiceConfig.retryTimeoutInterval;
224
225 data["system_uuid"] = systemUuid;
226 data["revision"] = jsonRevision;
227 data["timeout"] = SessionStore::getInstance().getTimeoutInSeconds();
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700228
229 nlohmann::json& sessions = data["sessions"];
230 sessions = nlohmann::json::array();
231 for (const auto& p : SessionStore::getInstance().authTokens)
232 {
233 if (p.second->persistence !=
234 persistent_data::PersistenceType::SINGLE_REQUEST)
235 {
Ed Tanous14766872022-03-15 10:44:42 -0700236 nlohmann::json::object_t session;
237 session["unique_id"] = p.second->uniqueId;
238 session["session_token"] = p.second->sessionToken;
239 session["username"] = p.second->username;
240 session["csrf_token"] = p.second->csrfToken;
241 session["client_ip"] = p.second->clientIp;
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700242#ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE
Ed Tanous14766872022-03-15 10:44:42 -0700243 session["client_id"] = p.second->clientId;
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700244#endif
Ed Tanous14766872022-03-15 10:44:42 -0700245 sessions.push_back(std::move(session));
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700246 }
247 }
JunLin Chen28afb492021-02-24 17:13:29 +0800248 nlohmann::json& subscriptions = data["subscriptions"];
249 subscriptions = nlohmann::json::array();
250 for (const auto& it :
251 EventServiceStore::getInstance().subscriptionsConfigMap)
252 {
253 std::shared_ptr<UserSubscription> subValue = it.second;
254 if (subValue->subscriptionType == "SSE")
255 {
256 BMCWEB_LOG_DEBUG
257 << "The subscription type is SSE, so skipping.";
258 continue;
259 }
Ed Tanous601c71a2021-09-08 16:40:12 -0700260 nlohmann::json::object_t headers;
261 for (const boost::beast::http::fields::value_type& header :
262 subValue->httpHeaders)
263 {
264 // Note, these are technically copies because nlohmann doesn't
265 // support key lookup by std::string_view. At least the
266 // following code can use move
267 // https://github.com/nlohmann/json/issues/1529
268 std::string name(header.name_string());
269 headers[std::move(name)] = header.value();
270 }
271
Ed Tanous14766872022-03-15 10:44:42 -0700272 nlohmann::json::object_t subscription;
273
274 subscription["Id"] = subValue->id;
275 subscription["Context"] = subValue->customText;
276 subscription["DeliveryRetryPolicy"] = subValue->retryPolicy;
277 subscription["Destination"] = subValue->destinationUrl;
278 subscription["EventFormatType"] = subValue->eventFormatType;
279 subscription["HttpHeaders"] = std::move(headers);
280 subscription["MessageIds"] = subValue->registryMsgIds;
281 subscription["Protocol"] = subValue->protocol;
282 subscription["RegistryPrefixes"] = subValue->registryPrefixes;
283 subscription["ResourceTypes"] = subValue->resourceTypes;
284 subscription["SubscriptionType"] = subValue->subscriptionType;
285 subscription["MetricReportDefinitions"] =
286 subValue->metricReportDefinitions;
287
288 subscriptions.push_back(std::move(subscription));
JunLin Chen28afb492021-02-24 17:13:29 +0800289 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700290 persistentFile << data;
291 }
292
Ed Tanouse05aec52022-01-25 10:28:56 -0800293 std::string systemUuid;
Ed Tanousba9f9a62017-10-11 16:40:35 -0700294};
295
Ed Tanous52cc1122020-07-18 13:51:21 -0700296inline ConfigFile& getConfig()
297{
298 static ConfigFile f;
299 return f;
300}
301
Ed Tanous1abe55e2018-09-05 08:30:59 -0700302} // namespace persistent_data