blob: 02f2ccec11b8e218274831ef5748f9b65d7b834a [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/container/flat_map.hpp>
6#include <boost/uuid/uuid.hpp>
7#include <boost/uuid/uuid_generators.hpp>
8#include <boost/uuid/uuid_io.hpp>
JunLin Chen28afb492021-02-24 17:13:29 +08009#include <event_service_store.hpp>
Ed Tanous04e438c2020-10-03 08:06:26 -070010#include <http_request.hpp>
11#include <http_response.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070012#include <nlohmann/json.hpp>
13#include <pam_authenticate.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070014#include <sessions.hpp>
Ed Tanousba9f9a62017-10-11 16:40:35 -070015
Gunnar Mills1214b7e2020-06-04 10:11:30 -050016#include <filesystem>
James Feist3909dc82020-04-03 10:58:55 -070017#include <fstream>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050018#include <random>
19
Ed Tanous1abe55e2018-09-05 08:30:59 -070020namespace persistent_data
21{
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +010022
Ed Tanous52cc1122020-07-18 13:51:21 -070023class ConfigFile
Ed Tanous1abe55e2018-09-05 08:30:59 -070024{
Ed Tanous271584a2019-07-09 16:24:22 -070025 uint64_t jsonRevision = 1;
Ed Tanousc963aa42017-10-27 16:00:19 -070026
Ed Tanous1abe55e2018-09-05 08:30:59 -070027 public:
Ratan Gupta845cb7d2019-07-12 00:32:25 +053028 // todo(ed) should read this from a fixed location somewhere, not CWD
29 static constexpr const char* filename = "bmcweb_persistent_data.json";
30
Ed Tanous52cc1122020-07-18 13:51:21 -070031 ConfigFile()
Ed Tanous1abe55e2018-09-05 08:30:59 -070032 {
33 readData();
Ed Tanousc963aa42017-10-27 16:00:19 -070034 }
Ed Tanousc963aa42017-10-27 16:00:19 -070035
Ed Tanous52cc1122020-07-18 13:51:21 -070036 ~ConfigFile()
Ed Tanous1abe55e2018-09-05 08:30:59 -070037 {
Gunnar Mills83cf8182020-11-11 15:37:34 -060038 // Make sure we aren't writing stale sessions
39 persistent_data::SessionStore::getInstance().applySessionTimeouts();
Ed Tanous1abe55e2018-09-05 08:30:59 -070040 if (persistent_data::SessionStore::getInstance().needsWrite())
41 {
42 writeData();
Kowalski, Kamil5cef0f72018-02-15 15:26:51 +010043 }
Ed Tanousc963aa42017-10-27 16:00:19 -070044 }
Ed Tanousc963aa42017-10-27 16:00:19 -070045
Ed Tanousecd6a3a2022-01-07 09:18:40 -080046 ConfigFile(const ConfigFile&) = delete;
47 ConfigFile(ConfigFile&&) = delete;
48 ConfigFile& operator=(const ConfigFile&) = delete;
49 ConfigFile& operator=(ConfigFile&&) = delete;
50
Ed Tanous1abe55e2018-09-05 08:30:59 -070051 // TODO(ed) this should really use protobuf, or some other serialization
52 // library, but adding another dependency is somewhat outside the scope of
53 // this application for the moment
54 void readData()
55 {
56 std::ifstream persistentFile(filename);
Ed Tanous271584a2019-07-09 16:24:22 -070057 uint64_t fileRevision = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -070058 if (persistentFile.is_open())
59 {
60 // call with exceptions disabled
61 auto data = nlohmann::json::parse(persistentFile, nullptr, false);
62 if (data.is_discarded())
63 {
64 BMCWEB_LOG_ERROR
65 << "Error parsing persistent data in json file.";
66 }
67 else
68 {
69 for (const auto& item : data.items())
70 {
71 if (item.key() == "revision")
72 {
73 fileRevision = 0;
74
75 const uint64_t* uintPtr =
76 item.value().get_ptr<const uint64_t*>();
77 if (uintPtr == nullptr)
78 {
79 BMCWEB_LOG_ERROR << "Failed to read revision flag";
80 }
81 else
82 {
83 fileRevision = *uintPtr;
84 }
85 }
86 else if (item.key() == "system_uuid")
87 {
88 const std::string* jSystemUuid =
89 item.value().get_ptr<const std::string*>();
90 if (jSystemUuid != nullptr)
91 {
92 systemUuid = *jSystemUuid;
93 }
94 }
Zbigniew Kurzynski78158632019-11-05 12:57:37 +010095 else if (item.key() == "auth_config")
96 {
97 SessionStore::getInstance()
98 .getAuthMethodsConfig()
99 .fromJson(item.value());
100 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700101 else if (item.key() == "sessions")
102 {
103 for (const auto& elem : item.value())
104 {
105 std::shared_ptr<UserSession> newSession =
106 UserSession::fromJson(elem);
107
108 if (newSession == nullptr)
109 {
110 BMCWEB_LOG_ERROR << "Problem reading session "
111 "from persistent store";
112 continue;
113 }
114
115 BMCWEB_LOG_DEBUG
116 << "Restored session: " << newSession->csrfToken
117 << " " << newSession->uniqueId << " "
118 << newSession->sessionToken;
119 SessionStore::getInstance().authTokens.emplace(
120 newSession->sessionToken, newSession);
121 }
122 }
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530123 else if (item.key() == "timeout")
124 {
125 const int64_t* jTimeout =
126 item.value().get_ptr<int64_t*>();
127 if (jTimeout == nullptr)
128 {
129 BMCWEB_LOG_DEBUG
130 << "Problem reading session timeout value";
131 continue;
132 }
133 std::chrono::seconds sessionTimeoutInseconds(*jTimeout);
134 BMCWEB_LOG_DEBUG << "Restored Session Timeout: "
135 << sessionTimeoutInseconds.count();
136 SessionStore::getInstance().updateSessionTimeout(
137 sessionTimeoutInseconds);
138 }
JunLin Chen28afb492021-02-24 17:13:29 +0800139 else if (item.key() == "eventservice_config")
140 {
141 EventServiceStore::getInstance()
142 .getEventServiceConfig()
143 .fromJson(item.value());
144 }
145 else if (item.key() == "subscriptions")
146 {
147 for (const auto& elem : item.value())
148 {
149 std::shared_ptr<UserSubscription> newSubscription =
150 UserSubscription::fromJson(elem);
151
152 if (newSubscription == nullptr)
153 {
154 BMCWEB_LOG_ERROR
155 << "Problem reading subscription "
156 "from persistent store";
157 continue;
158 }
159
160 BMCWEB_LOG_DEBUG << "Restored subscription: "
161 << newSubscription->id << " "
162 << newSubscription->customText;
163 EventServiceStore::getInstance()
164 .subscriptionsConfigMap.emplace(
165 newSubscription->id, newSubscription);
166 }
167 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700168 else
169 {
170 // Do nothing in the case of extra fields. We may have
171 // cases where fields are added in the future, and we
172 // want to at least attempt to gracefully support
173 // downgrades in that case, even if we don't officially
174 // support it
175 }
176 }
177 }
178 }
179 bool needWrite = false;
180
181 if (systemUuid.empty())
182 {
183 systemUuid =
184 boost::uuids::to_string(boost::uuids::random_generator()());
185 needWrite = true;
186 }
187 if (fileRevision < jsonRevision)
188 {
189 needWrite = true;
190 }
191 // write revision changes or system uuid changes immediately
192 if (needWrite)
193 {
194 writeData();
195 }
196 }
197
198 void writeData()
199 {
200 std::ofstream persistentFile(filename);
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530201
202 // set the permission of the file to 640
Ed Tanous52cc1122020-07-18 13:51:21 -0700203 std::filesystem::perms permission =
204 std::filesystem::perms::owner_read |
205 std::filesystem::perms::owner_write |
206 std::filesystem::perms::group_read;
207 std::filesystem::permissions(filename, permission);
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700208 const auto& c = SessionStore::getInstance().getAuthMethodsConfig();
JunLin Chen28afb492021-02-24 17:13:29 +0800209 const auto& eventServiceConfig =
210 EventServiceStore::getInstance().getEventServiceConfig();
Ed Tanousdc511aa2020-10-21 12:33:42 -0700211 nlohmann::json data{
212 {"auth_config",
213 {{"XToken", c.xtoken},
214 {"Cookie", c.cookie},
215 {"SessionToken", c.sessionToken},
216 {"BasicAuth", c.basic},
217 {"TLS", c.tls}}
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530218
Ed Tanousdc511aa2020-10-21 12:33:42 -0700219 },
JunLin Chen28afb492021-02-24 17:13:29 +0800220 {"eventservice_config",
221 {{"ServiceEnabled", eventServiceConfig.enabled},
222 {"DeliveryRetryAttempts", eventServiceConfig.retryAttempts},
223 {"DeliveryRetryIntervalSeconds",
224 eventServiceConfig.retryTimeoutInterval}}
225
226 },
Ed Tanousdc511aa2020-10-21 12:33:42 -0700227 {"system_uuid", systemUuid},
228 {"revision", jsonRevision},
229 {"timeout", SessionStore::getInstance().getTimeoutInSeconds()}};
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700230
231 nlohmann::json& sessions = data["sessions"];
232 sessions = nlohmann::json::array();
233 for (const auto& p : SessionStore::getInstance().authTokens)
234 {
235 if (p.second->persistence !=
236 persistent_data::PersistenceType::SINGLE_REQUEST)
237 {
238 sessions.push_back({
239 {"unique_id", p.second->uniqueId},
240 {"session_token", p.second->sessionToken},
241 {"username", p.second->username},
242 {"csrf_token", p.second->csrfToken},
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -0500243 {"client_ip", p.second->clientIp},
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700244#ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE
245 {"client_id", p.second->clientId},
246#endif
247 });
248 }
249 }
JunLin Chen28afb492021-02-24 17:13:29 +0800250 nlohmann::json& subscriptions = data["subscriptions"];
251 subscriptions = nlohmann::json::array();
252 for (const auto& it :
253 EventServiceStore::getInstance().subscriptionsConfigMap)
254 {
255 std::shared_ptr<UserSubscription> subValue = it.second;
256 if (subValue->subscriptionType == "SSE")
257 {
258 BMCWEB_LOG_DEBUG
259 << "The subscription type is SSE, so skipping.";
260 continue;
261 }
Ed Tanous601c71a2021-09-08 16:40:12 -0700262 nlohmann::json::object_t headers;
263 for (const boost::beast::http::fields::value_type& header :
264 subValue->httpHeaders)
265 {
266 // Note, these are technically copies because nlohmann doesn't
267 // support key lookup by std::string_view. At least the
268 // following code can use move
269 // https://github.com/nlohmann/json/issues/1529
270 std::string name(header.name_string());
271 headers[std::move(name)] = header.value();
272 }
273
JunLin Chen28afb492021-02-24 17:13:29 +0800274 subscriptions.push_back({
275 {"Id", subValue->id},
276 {"Context", subValue->customText},
277 {"DeliveryRetryPolicy", subValue->retryPolicy},
278 {"Destination", subValue->destinationUrl},
279 {"EventFormatType", subValue->eventFormatType},
Ed Tanous601c71a2021-09-08 16:40:12 -0700280 {"HttpHeaders", std::move(headers)},
JunLin Chen28afb492021-02-24 17:13:29 +0800281 {"MessageIds", subValue->registryMsgIds},
282 {"Protocol", subValue->protocol},
283 {"RegistryPrefixes", subValue->registryPrefixes},
284 {"ResourceTypes", subValue->resourceTypes},
285 {"SubscriptionType", subValue->subscriptionType},
286 {"MetricReportDefinitions", subValue->metricReportDefinitions},
JunLin Chen28afb492021-02-24 17:13:29 +0800287 });
288 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700289 persistentFile << data;
290 }
291
Ed Tanouse05aec52022-01-25 10:28:56 -0800292 std::string systemUuid;
Ed Tanousba9f9a62017-10-11 16:40:35 -0700293};
294
Ed Tanous52cc1122020-07-18 13:51:21 -0700295inline ConfigFile& getConfig()
296{
297 static ConfigFile f;
298 return f;
299}
300
Ed Tanous1abe55e2018-09-05 08:30:59 -0700301} // namespace persistent_data