blob: d7230cbf1c85c5f24fec95e48bb3b5b9bca905f2 [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 Tanous1abe55e2018-09-05 08:30:59 -070046 // TODO(ed) this should really use protobuf, or some other serialization
47 // library, but adding another dependency is somewhat outside the scope of
48 // this application for the moment
49 void readData()
50 {
51 std::ifstream persistentFile(filename);
Ed Tanous271584a2019-07-09 16:24:22 -070052 uint64_t fileRevision = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -070053 if (persistentFile.is_open())
54 {
55 // call with exceptions disabled
56 auto data = nlohmann::json::parse(persistentFile, nullptr, false);
57 if (data.is_discarded())
58 {
59 BMCWEB_LOG_ERROR
60 << "Error parsing persistent data in json file.";
61 }
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 {
74 BMCWEB_LOG_ERROR << "Failed to read revision flag";
75 }
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 {
105 BMCWEB_LOG_ERROR << "Problem reading session "
106 "from persistent store";
107 continue;
108 }
109
110 BMCWEB_LOG_DEBUG
111 << "Restored session: " << newSession->csrfToken
112 << " " << newSession->uniqueId << " "
113 << newSession->sessionToken;
114 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 {
124 BMCWEB_LOG_DEBUG
125 << "Problem reading session timeout value";
126 continue;
127 }
128 std::chrono::seconds sessionTimeoutInseconds(*jTimeout);
129 BMCWEB_LOG_DEBUG << "Restored Session Timeout: "
130 << sessionTimeoutInseconds.count();
131 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 {
149 BMCWEB_LOG_ERROR
150 << "Problem reading subscription "
151 "from persistent store";
152 continue;
153 }
154
155 BMCWEB_LOG_DEBUG << "Restored subscription: "
156 << newSubscription->id << " "
157 << newSubscription->customText;
158 EventServiceStore::getInstance()
159 .subscriptionsConfigMap.emplace(
160 newSubscription->id, newSubscription);
161 }
162 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700163 else
164 {
165 // Do nothing in the case of extra fields. We may have
166 // cases where fields are added in the future, and we
167 // want to at least attempt to gracefully support
168 // downgrades in that case, even if we don't officially
169 // support it
170 }
171 }
172 }
173 }
174 bool needWrite = false;
175
176 if (systemUuid.empty())
177 {
178 systemUuid =
179 boost::uuids::to_string(boost::uuids::random_generator()());
180 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 Tanousdc511aa2020-10-21 12:33:42 -0700206 nlohmann::json data{
207 {"auth_config",
208 {{"XToken", c.xtoken},
209 {"Cookie", c.cookie},
210 {"SessionToken", c.sessionToken},
211 {"BasicAuth", c.basic},
212 {"TLS", c.tls}}
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530213
Ed Tanousdc511aa2020-10-21 12:33:42 -0700214 },
JunLin Chen28afb492021-02-24 17:13:29 +0800215 {"eventservice_config",
216 {{"ServiceEnabled", eventServiceConfig.enabled},
217 {"DeliveryRetryAttempts", eventServiceConfig.retryAttempts},
218 {"DeliveryRetryIntervalSeconds",
219 eventServiceConfig.retryTimeoutInterval}}
220
221 },
Ed Tanousdc511aa2020-10-21 12:33:42 -0700222 {"system_uuid", systemUuid},
223 {"revision", jsonRevision},
224 {"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 {
233 sessions.push_back({
234 {"unique_id", p.second->uniqueId},
235 {"session_token", p.second->sessionToken},
236 {"username", p.second->username},
237 {"csrf_token", p.second->csrfToken},
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -0500238 {"client_ip", p.second->clientIp},
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700239#ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE
240 {"client_id", p.second->clientId},
241#endif
242 });
243 }
244 }
JunLin Chen28afb492021-02-24 17:13:29 +0800245 nlohmann::json& subscriptions = data["subscriptions"];
246 subscriptions = nlohmann::json::array();
247 for (const auto& it :
248 EventServiceStore::getInstance().subscriptionsConfigMap)
249 {
250 std::shared_ptr<UserSubscription> subValue = it.second;
251 if (subValue->subscriptionType == "SSE")
252 {
253 BMCWEB_LOG_DEBUG
254 << "The subscription type is SSE, so skipping.";
255 continue;
256 }
Ed Tanous601c71a2021-09-08 16:40:12 -0700257 nlohmann::json::object_t headers;
258 for (const boost::beast::http::fields::value_type& header :
259 subValue->httpHeaders)
260 {
261 // Note, these are technically copies because nlohmann doesn't
262 // support key lookup by std::string_view. At least the
263 // following code can use move
264 // https://github.com/nlohmann/json/issues/1529
265 std::string name(header.name_string());
266 headers[std::move(name)] = header.value();
267 }
268
JunLin Chen28afb492021-02-24 17:13:29 +0800269 subscriptions.push_back({
270 {"Id", subValue->id},
271 {"Context", subValue->customText},
272 {"DeliveryRetryPolicy", subValue->retryPolicy},
273 {"Destination", subValue->destinationUrl},
274 {"EventFormatType", subValue->eventFormatType},
Ed Tanous601c71a2021-09-08 16:40:12 -0700275 {"HttpHeaders", std::move(headers)},
JunLin Chen28afb492021-02-24 17:13:29 +0800276 {"MessageIds", subValue->registryMsgIds},
277 {"Protocol", subValue->protocol},
278 {"RegistryPrefixes", subValue->registryPrefixes},
279 {"ResourceTypes", subValue->resourceTypes},
280 {"SubscriptionType", subValue->subscriptionType},
281 {"MetricReportDefinitions", subValue->metricReportDefinitions},
JunLin Chen28afb492021-02-24 17:13:29 +0800282 });
283 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700284 persistentFile << data;
285 }
286
287 std::string systemUuid{""};
Ed Tanousba9f9a62017-10-11 16:40:35 -0700288};
289
Ed Tanous52cc1122020-07-18 13:51:21 -0700290inline ConfigFile& getConfig()
291{
292 static ConfigFile f;
293 return f;
294}
295
Ed Tanous1abe55e2018-09-05 08:30:59 -0700296} // namespace persistent_data