blob: 1ca925eb23800d274ea61a31a33eb9153ad76732 [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>
12#include <pam_authenticate.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070013#include <sessions.hpp>
Ed Tanousba9f9a62017-10-11 16:40:35 -070014
Gunnar Mills1214b7e2020-06-04 10:11:30 -050015#include <filesystem>
James Feist3909dc82020-04-03 10:58:55 -070016#include <fstream>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050017#include <random>
18
Ed Tanous1abe55e2018-09-05 08:30:59 -070019namespace persistent_data
20{
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +010021
Ed Tanous52cc1122020-07-18 13:51:21 -070022class ConfigFile
Ed Tanous1abe55e2018-09-05 08:30:59 -070023{
Ed Tanous271584a2019-07-09 16:24:22 -070024 uint64_t jsonRevision = 1;
Ed Tanousc963aa42017-10-27 16:00:19 -070025
Ed Tanous1abe55e2018-09-05 08:30:59 -070026 public:
Ratan Gupta845cb7d2019-07-12 00:32:25 +053027 // todo(ed) should read this from a fixed location somewhere, not CWD
28 static constexpr const char* filename = "bmcweb_persistent_data.json";
29
Ed Tanous52cc1122020-07-18 13:51:21 -070030 ConfigFile()
Ed Tanous1abe55e2018-09-05 08:30:59 -070031 {
32 readData();
Ed Tanousc963aa42017-10-27 16:00:19 -070033 }
Ed Tanousc963aa42017-10-27 16:00:19 -070034
Ed Tanous52cc1122020-07-18 13:51:21 -070035 ~ConfigFile()
Ed Tanous1abe55e2018-09-05 08:30:59 -070036 {
Gunnar Mills83cf8182020-11-11 15:37:34 -060037 // Make sure we aren't writing stale sessions
38 persistent_data::SessionStore::getInstance().applySessionTimeouts();
Ed Tanous1abe55e2018-09-05 08:30:59 -070039 if (persistent_data::SessionStore::getInstance().needsWrite())
40 {
41 writeData();
Kowalski, Kamil5cef0f72018-02-15 15:26:51 +010042 }
Ed Tanousc963aa42017-10-27 16:00:19 -070043 }
Ed Tanousc963aa42017-10-27 16:00:19 -070044
Ed Tanousecd6a3a2022-01-07 09:18:40 -080045 ConfigFile(const ConfigFile&) = delete;
46 ConfigFile(ConfigFile&&) = delete;
47 ConfigFile& operator=(const ConfigFile&) = delete;
48 ConfigFile& operator=(ConfigFile&&) = delete;
49
Ed Tanous1abe55e2018-09-05 08:30:59 -070050 // TODO(ed) this should really use protobuf, or some other serialization
51 // library, but adding another dependency is somewhat outside the scope of
52 // this application for the moment
53 void readData()
54 {
55 std::ifstream persistentFile(filename);
Ed Tanous271584a2019-07-09 16:24:22 -070056 uint64_t fileRevision = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -070057 if (persistentFile.is_open())
58 {
59 // call with exceptions disabled
60 auto data = nlohmann::json::parse(persistentFile, nullptr, false);
61 if (data.is_discarded())
62 {
63 BMCWEB_LOG_ERROR
64 << "Error parsing persistent data in json file.";
65 }
66 else
67 {
68 for (const auto& item : data.items())
69 {
70 if (item.key() == "revision")
71 {
72 fileRevision = 0;
73
74 const uint64_t* uintPtr =
75 item.value().get_ptr<const uint64_t*>();
76 if (uintPtr == nullptr)
77 {
78 BMCWEB_LOG_ERROR << "Failed to read revision flag";
79 }
80 else
81 {
82 fileRevision = *uintPtr;
83 }
84 }
85 else if (item.key() == "system_uuid")
86 {
87 const std::string* jSystemUuid =
88 item.value().get_ptr<const std::string*>();
89 if (jSystemUuid != nullptr)
90 {
91 systemUuid = *jSystemUuid;
92 }
93 }
Zbigniew Kurzynski78158632019-11-05 12:57:37 +010094 else if (item.key() == "auth_config")
95 {
96 SessionStore::getInstance()
97 .getAuthMethodsConfig()
98 .fromJson(item.value());
99 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700100 else if (item.key() == "sessions")
101 {
102 for (const auto& elem : item.value())
103 {
104 std::shared_ptr<UserSession> newSession =
105 UserSession::fromJson(elem);
106
107 if (newSession == nullptr)
108 {
109 BMCWEB_LOG_ERROR << "Problem reading session "
110 "from persistent store";
111 continue;
112 }
113
114 BMCWEB_LOG_DEBUG
115 << "Restored session: " << newSession->csrfToken
116 << " " << newSession->uniqueId << " "
117 << newSession->sessionToken;
118 SessionStore::getInstance().authTokens.emplace(
119 newSession->sessionToken, newSession);
120 }
121 }
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530122 else if (item.key() == "timeout")
123 {
124 const int64_t* jTimeout =
125 item.value().get_ptr<int64_t*>();
126 if (jTimeout == nullptr)
127 {
128 BMCWEB_LOG_DEBUG
129 << "Problem reading session timeout value";
130 continue;
131 }
132 std::chrono::seconds sessionTimeoutInseconds(*jTimeout);
133 BMCWEB_LOG_DEBUG << "Restored Session Timeout: "
134 << sessionTimeoutInseconds.count();
135 SessionStore::getInstance().updateSessionTimeout(
136 sessionTimeoutInseconds);
137 }
JunLin Chen28afb492021-02-24 17:13:29 +0800138 else if (item.key() == "eventservice_config")
139 {
140 EventServiceStore::getInstance()
141 .getEventServiceConfig()
142 .fromJson(item.value());
143 }
144 else if (item.key() == "subscriptions")
145 {
146 for (const auto& elem : item.value())
147 {
148 std::shared_ptr<UserSubscription> newSubscription =
149 UserSubscription::fromJson(elem);
150
151 if (newSubscription == nullptr)
152 {
153 BMCWEB_LOG_ERROR
154 << "Problem reading subscription "
155 "from persistent store";
156 continue;
157 }
158
159 BMCWEB_LOG_DEBUG << "Restored subscription: "
160 << newSubscription->id << " "
161 << newSubscription->customText;
162 EventServiceStore::getInstance()
163 .subscriptionsConfigMap.emplace(
164 newSubscription->id, newSubscription);
165 }
166 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700167 else
168 {
169 // Do nothing in the case of extra fields. We may have
170 // cases where fields are added in the future, and we
171 // want to at least attempt to gracefully support
172 // downgrades in that case, even if we don't officially
173 // support it
174 }
175 }
176 }
177 }
178 bool needWrite = false;
179
180 if (systemUuid.empty())
181 {
182 systemUuid =
183 boost::uuids::to_string(boost::uuids::random_generator()());
184 needWrite = true;
185 }
186 if (fileRevision < jsonRevision)
187 {
188 needWrite = true;
189 }
190 // write revision changes or system uuid changes immediately
191 if (needWrite)
192 {
193 writeData();
194 }
195 }
196
197 void writeData()
198 {
199 std::ofstream persistentFile(filename);
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530200
201 // set the permission of the file to 640
Ed Tanous52cc1122020-07-18 13:51:21 -0700202 std::filesystem::perms permission =
203 std::filesystem::perms::owner_read |
204 std::filesystem::perms::owner_write |
205 std::filesystem::perms::group_read;
206 std::filesystem::permissions(filename, permission);
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700207 const auto& c = SessionStore::getInstance().getAuthMethodsConfig();
JunLin Chen28afb492021-02-24 17:13:29 +0800208 const auto& eventServiceConfig =
209 EventServiceStore::getInstance().getEventServiceConfig();
Ed Tanous14766872022-03-15 10:44:42 -0700210 nlohmann::json::object_t data;
211 nlohmann::json& authConfig = data["auth_config"];
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530212
Ed Tanous14766872022-03-15 10:44:42 -0700213 authConfig["XToken"] = c.xtoken;
214 authConfig["Cookie"] = c.cookie;
215 authConfig["SessionToken"] = c.sessionToken;
216 authConfig["BasicAuth"] = c.basic;
217 authConfig["TLS"] = c.tls;
JunLin Chen28afb492021-02-24 17:13:29 +0800218
Ed Tanous14766872022-03-15 10:44:42 -0700219 nlohmann::json& eventserviceConfig = data["eventservice_config"];
220 eventserviceConfig["ServiceEnabled"] = eventServiceConfig.enabled;
221 eventserviceConfig["DeliveryRetryAttempts"] =
222 eventServiceConfig.retryAttempts;
223 eventserviceConfig["DeliveryRetryIntervalSeconds"] =
224 eventServiceConfig.retryTimeoutInterval;
225
226 data["system_uuid"] = systemUuid;
227 data["revision"] = jsonRevision;
228 data["timeout"] = SessionStore::getInstance().getTimeoutInSeconds();
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700229
230 nlohmann::json& sessions = data["sessions"];
231 sessions = nlohmann::json::array();
232 for (const auto& p : SessionStore::getInstance().authTokens)
233 {
234 if (p.second->persistence !=
235 persistent_data::PersistenceType::SINGLE_REQUEST)
236 {
Ed Tanous14766872022-03-15 10:44:42 -0700237 nlohmann::json::object_t session;
238 session["unique_id"] = p.second->uniqueId;
239 session["session_token"] = p.second->sessionToken;
240 session["username"] = p.second->username;
241 session["csrf_token"] = p.second->csrfToken;
242 session["client_ip"] = p.second->clientIp;
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700243#ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE
Ed Tanous14766872022-03-15 10:44:42 -0700244 session["client_id"] = p.second->clientId;
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700245#endif
Ed Tanous14766872022-03-15 10:44:42 -0700246 sessions.push_back(std::move(session));
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700247 }
248 }
JunLin Chen28afb492021-02-24 17:13:29 +0800249 nlohmann::json& subscriptions = data["subscriptions"];
250 subscriptions = nlohmann::json::array();
251 for (const auto& it :
252 EventServiceStore::getInstance().subscriptionsConfigMap)
253 {
254 std::shared_ptr<UserSubscription> subValue = it.second;
255 if (subValue->subscriptionType == "SSE")
256 {
257 BMCWEB_LOG_DEBUG
258 << "The subscription type is SSE, so skipping.";
259 continue;
260 }
Ed Tanous601c71a2021-09-08 16:40:12 -0700261 nlohmann::json::object_t headers;
262 for (const boost::beast::http::fields::value_type& header :
263 subValue->httpHeaders)
264 {
265 // Note, these are technically copies because nlohmann doesn't
266 // support key lookup by std::string_view. At least the
267 // following code can use move
268 // https://github.com/nlohmann/json/issues/1529
269 std::string name(header.name_string());
270 headers[std::move(name)] = header.value();
271 }
272
Ed Tanous14766872022-03-15 10:44:42 -0700273 nlohmann::json::object_t subscription;
274
275 subscription["Id"] = subValue->id;
276 subscription["Context"] = subValue->customText;
277 subscription["DeliveryRetryPolicy"] = subValue->retryPolicy;
278 subscription["Destination"] = subValue->destinationUrl;
279 subscription["EventFormatType"] = subValue->eventFormatType;
280 subscription["HttpHeaders"] = std::move(headers);
281 subscription["MessageIds"] = subValue->registryMsgIds;
282 subscription["Protocol"] = subValue->protocol;
283 subscription["RegistryPrefixes"] = subValue->registryPrefixes;
284 subscription["ResourceTypes"] = subValue->resourceTypes;
285 subscription["SubscriptionType"] = subValue->subscriptionType;
286 subscription["MetricReportDefinitions"] =
287 subValue->metricReportDefinitions;
288
289 subscriptions.push_back(std::move(subscription));
JunLin Chen28afb492021-02-24 17:13:29 +0800290 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700291 persistentFile << data;
292 }
293
Ed Tanouse05aec52022-01-25 10:28:56 -0800294 std::string systemUuid;
Ed Tanousba9f9a62017-10-11 16:40:35 -0700295};
296
Ed Tanous52cc1122020-07-18 13:51:21 -0700297inline ConfigFile& getConfig()
298{
299 static ConfigFile f;
300 return f;
301}
302
Ed Tanous1abe55e2018-09-05 08:30:59 -0700303} // namespace persistent_data