blob: 4178d7d69f83fab0996f9b74a9985f72996a76e2 [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 Tanousba9f9a62017-10-11 16:40:35 -07004#include <boost/container/flat_map.hpp>
5#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 Tanous1abe55e2018-09-05 08:30:59 -070045 // TODO(ed) this should really use protobuf, or some other serialization
46 // library, but adding another dependency is somewhat outside the scope of
47 // this application for the moment
48 void readData()
49 {
50 std::ifstream persistentFile(filename);
Ed Tanous271584a2019-07-09 16:24:22 -070051 uint64_t fileRevision = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -070052 if (persistentFile.is_open())
53 {
54 // call with exceptions disabled
55 auto data = nlohmann::json::parse(persistentFile, nullptr, false);
56 if (data.is_discarded())
57 {
58 BMCWEB_LOG_ERROR
59 << "Error parsing persistent data in json file.";
60 }
61 else
62 {
63 for (const auto& item : data.items())
64 {
65 if (item.key() == "revision")
66 {
67 fileRevision = 0;
68
69 const uint64_t* uintPtr =
70 item.value().get_ptr<const uint64_t*>();
71 if (uintPtr == nullptr)
72 {
73 BMCWEB_LOG_ERROR << "Failed to read revision flag";
74 }
75 else
76 {
77 fileRevision = *uintPtr;
78 }
79 }
80 else if (item.key() == "system_uuid")
81 {
82 const std::string* jSystemUuid =
83 item.value().get_ptr<const std::string*>();
84 if (jSystemUuid != nullptr)
85 {
86 systemUuid = *jSystemUuid;
87 }
88 }
Zbigniew Kurzynski78158632019-11-05 12:57:37 +010089 else if (item.key() == "auth_config")
90 {
91 SessionStore::getInstance()
92 .getAuthMethodsConfig()
93 .fromJson(item.value());
94 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070095 else if (item.key() == "sessions")
96 {
97 for (const auto& elem : item.value())
98 {
99 std::shared_ptr<UserSession> newSession =
100 UserSession::fromJson(elem);
101
102 if (newSession == nullptr)
103 {
104 BMCWEB_LOG_ERROR << "Problem reading session "
105 "from persistent store";
106 continue;
107 }
108
109 BMCWEB_LOG_DEBUG
110 << "Restored session: " << newSession->csrfToken
111 << " " << newSession->uniqueId << " "
112 << newSession->sessionToken;
113 SessionStore::getInstance().authTokens.emplace(
114 newSession->sessionToken, newSession);
115 }
116 }
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530117 else if (item.key() == "timeout")
118 {
119 const int64_t* jTimeout =
120 item.value().get_ptr<int64_t*>();
121 if (jTimeout == nullptr)
122 {
123 BMCWEB_LOG_DEBUG
124 << "Problem reading session timeout value";
125 continue;
126 }
127 std::chrono::seconds sessionTimeoutInseconds(*jTimeout);
128 BMCWEB_LOG_DEBUG << "Restored Session Timeout: "
129 << sessionTimeoutInseconds.count();
130 SessionStore::getInstance().updateSessionTimeout(
131 sessionTimeoutInseconds);
132 }
JunLin Chen28afb492021-02-24 17:13:29 +0800133 else if (item.key() == "eventservice_config")
134 {
135 EventServiceStore::getInstance()
136 .getEventServiceConfig()
137 .fromJson(item.value());
138 }
139 else if (item.key() == "subscriptions")
140 {
141 for (const auto& elem : item.value())
142 {
143 std::shared_ptr<UserSubscription> newSubscription =
144 UserSubscription::fromJson(elem);
145
146 if (newSubscription == nullptr)
147 {
148 BMCWEB_LOG_ERROR
149 << "Problem reading subscription "
150 "from persistent store";
151 continue;
152 }
153
154 BMCWEB_LOG_DEBUG << "Restored subscription: "
155 << newSubscription->id << " "
156 << newSubscription->customText;
157 EventServiceStore::getInstance()
158 .subscriptionsConfigMap.emplace(
159 newSubscription->id, newSubscription);
160 }
161 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700162 else
163 {
164 // Do nothing in the case of extra fields. We may have
165 // cases where fields are added in the future, and we
166 // want to at least attempt to gracefully support
167 // downgrades in that case, even if we don't officially
168 // support it
169 }
170 }
171 }
172 }
173 bool needWrite = false;
174
175 if (systemUuid.empty())
176 {
177 systemUuid =
178 boost::uuids::to_string(boost::uuids::random_generator()());
179 needWrite = true;
180 }
181 if (fileRevision < jsonRevision)
182 {
183 needWrite = true;
184 }
185 // write revision changes or system uuid changes immediately
186 if (needWrite)
187 {
188 writeData();
189 }
190 }
191
192 void writeData()
193 {
194 std::ofstream persistentFile(filename);
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530195
196 // set the permission of the file to 640
Ed Tanous52cc1122020-07-18 13:51:21 -0700197 std::filesystem::perms permission =
198 std::filesystem::perms::owner_read |
199 std::filesystem::perms::owner_write |
200 std::filesystem::perms::group_read;
201 std::filesystem::permissions(filename, permission);
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700202 const auto& c = SessionStore::getInstance().getAuthMethodsConfig();
JunLin Chen28afb492021-02-24 17:13:29 +0800203 const auto& eventServiceConfig =
204 EventServiceStore::getInstance().getEventServiceConfig();
Ed Tanousdc511aa2020-10-21 12:33:42 -0700205 nlohmann::json data{
206 {"auth_config",
207 {{"XToken", c.xtoken},
208 {"Cookie", c.cookie},
209 {"SessionToken", c.sessionToken},
210 {"BasicAuth", c.basic},
211 {"TLS", c.tls}}
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530212
Ed Tanousdc511aa2020-10-21 12:33:42 -0700213 },
JunLin Chen28afb492021-02-24 17:13:29 +0800214 {"eventservice_config",
215 {{"ServiceEnabled", eventServiceConfig.enabled},
216 {"DeliveryRetryAttempts", eventServiceConfig.retryAttempts},
217 {"DeliveryRetryIntervalSeconds",
218 eventServiceConfig.retryTimeoutInterval}}
219
220 },
Ed Tanousdc511aa2020-10-21 12:33:42 -0700221 {"system_uuid", systemUuid},
222 {"revision", jsonRevision},
223 {"timeout", SessionStore::getInstance().getTimeoutInSeconds()}};
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700224
225 nlohmann::json& sessions = data["sessions"];
226 sessions = nlohmann::json::array();
227 for (const auto& p : SessionStore::getInstance().authTokens)
228 {
229 if (p.second->persistence !=
230 persistent_data::PersistenceType::SINGLE_REQUEST)
231 {
232 sessions.push_back({
233 {"unique_id", p.second->uniqueId},
234 {"session_token", p.second->sessionToken},
235 {"username", p.second->username},
236 {"csrf_token", p.second->csrfToken},
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -0500237 {"client_ip", p.second->clientIp},
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700238#ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE
239 {"client_id", p.second->clientId},
240#endif
241 });
242 }
243 }
JunLin Chen28afb492021-02-24 17:13:29 +0800244 nlohmann::json& subscriptions = data["subscriptions"];
245 subscriptions = nlohmann::json::array();
246 for (const auto& it :
247 EventServiceStore::getInstance().subscriptionsConfigMap)
248 {
249 std::shared_ptr<UserSubscription> subValue = it.second;
250 if (subValue->subscriptionType == "SSE")
251 {
252 BMCWEB_LOG_DEBUG
253 << "The subscription type is SSE, so skipping.";
254 continue;
255 }
256 subscriptions.push_back({
257 {"Id", subValue->id},
258 {"Context", subValue->customText},
259 {"DeliveryRetryPolicy", subValue->retryPolicy},
260 {"Destination", subValue->destinationUrl},
261 {"EventFormatType", subValue->eventFormatType},
262 {"HttpHeaders", subValue->httpHeaders},
263 {"MessageIds", subValue->registryMsgIds},
264 {"Protocol", subValue->protocol},
265 {"RegistryPrefixes", subValue->registryPrefixes},
266 {"ResourceTypes", subValue->resourceTypes},
267 {"SubscriptionType", subValue->subscriptionType},
268 {"MetricReportDefinitions", subValue->metricReportDefinitions},
269
270 });
271 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700272 persistentFile << data;
273 }
274
275 std::string systemUuid{""};
Ed Tanousba9f9a62017-10-11 16:40:35 -0700276};
277
Ed Tanous52cc1122020-07-18 13:51:21 -0700278inline ConfigFile& getConfig()
279{
280 static ConfigFile f;
281 return f;
282}
283
Ed Tanous1abe55e2018-09-05 08:30:59 -0700284} // namespace persistent_data