blob: 819d69d89f2eaf0d4072957da61fe37588366549 [file] [log] [blame]
Ed Tanousba9f9a62017-10-11 16:40:35 -07001#pragma once
2
Ed Tanousc94ad492019-10-10 15:39:33 -07003#include <app.h>
4#include <http_request.h>
5#include <http_response.h>
Ed Tanous1abe55e2018-09-05 08:30:59 -07006
Ed Tanousba9f9a62017-10-11 16:40:35 -07007#include <boost/container/flat_map.hpp>
8#include <boost/uuid/uuid.hpp>
9#include <boost/uuid/uuid_generators.hpp>
10#include <boost/uuid/uuid_io.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 crow
20{
Ed Tanousba9f9a62017-10-11 16:40:35 -070021
Ed Tanous1abe55e2018-09-05 08:30:59 -070022namespace persistent_data
23{
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +010024
Ratan Gupta845cb7d2019-07-12 00:32:25 +053025namespace fs = std::filesystem;
26
Ed Tanous1abe55e2018-09-05 08:30:59 -070027class Middleware
28{
Ed Tanous271584a2019-07-09 16:24:22 -070029 uint64_t jsonRevision = 1;
Ed Tanousc963aa42017-10-27 16:00:19 -070030
Ed Tanous1abe55e2018-09-05 08:30:59 -070031 public:
Ratan Gupta845cb7d2019-07-12 00:32:25 +053032 // todo(ed) should read this from a fixed location somewhere, not CWD
33 static constexpr const char* filename = "bmcweb_persistent_data.json";
34
Ed Tanous1abe55e2018-09-05 08:30:59 -070035 struct Context
Gunnar Mills1214b7e2020-06-04 10:11:30 -050036 {};
Ed Tanousc963aa42017-10-27 16:00:19 -070037
Ed Tanous1abe55e2018-09-05 08:30:59 -070038 Middleware()
39 {
40 readData();
Ed Tanousc963aa42017-10-27 16:00:19 -070041 }
Ed Tanousc963aa42017-10-27 16:00:19 -070042
Ed Tanous1abe55e2018-09-05 08:30:59 -070043 ~Middleware()
44 {
45 if (persistent_data::SessionStore::getInstance().needsWrite())
46 {
47 writeData();
Kowalski, Kamil5cef0f72018-02-15 15:26:51 +010048 }
Ed Tanousc963aa42017-10-27 16:00:19 -070049 }
Ed Tanousc963aa42017-10-27 16:00:19 -070050
Ed Tanous1abe55e2018-09-05 08:30:59 -070051 void beforeHandle(crow::Request& req, Response& res, Context& ctx)
Gunnar Mills1214b7e2020-06-04 10:11:30 -050052 {}
Ed Tanousc963aa42017-10-27 16:00:19 -070053
Ed Tanous1abe55e2018-09-05 08:30:59 -070054 void afterHandle(Request& req, Response& res, Context& ctx)
Gunnar Mills1214b7e2020-06-04 10:11:30 -050055 {}
Ed Tanousc963aa42017-10-27 16:00:19 -070056
Ed Tanous1abe55e2018-09-05 08:30:59 -070057 // TODO(ed) this should really use protobuf, or some other serialization
58 // library, but adding another dependency is somewhat outside the scope of
59 // this application for the moment
60 void readData()
61 {
62 std::ifstream persistentFile(filename);
Ed Tanous271584a2019-07-09 16:24:22 -070063 uint64_t fileRevision = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -070064 if (persistentFile.is_open())
65 {
66 // call with exceptions disabled
67 auto data = nlohmann::json::parse(persistentFile, nullptr, false);
68 if (data.is_discarded())
69 {
70 BMCWEB_LOG_ERROR
71 << "Error parsing persistent data in json file.";
72 }
73 else
74 {
75 for (const auto& item : data.items())
76 {
77 if (item.key() == "revision")
78 {
79 fileRevision = 0;
80
81 const uint64_t* uintPtr =
82 item.value().get_ptr<const uint64_t*>();
83 if (uintPtr == nullptr)
84 {
85 BMCWEB_LOG_ERROR << "Failed to read revision flag";
86 }
87 else
88 {
89 fileRevision = *uintPtr;
90 }
91 }
92 else if (item.key() == "system_uuid")
93 {
94 const std::string* jSystemUuid =
95 item.value().get_ptr<const std::string*>();
96 if (jSystemUuid != nullptr)
97 {
98 systemUuid = *jSystemUuid;
99 }
100 }
Zbigniew Kurzynski78158632019-11-05 12:57:37 +0100101 else if (item.key() == "auth_config")
102 {
103 SessionStore::getInstance()
104 .getAuthMethodsConfig()
105 .fromJson(item.value());
106 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700107 else if (item.key() == "sessions")
108 {
109 for (const auto& elem : item.value())
110 {
111 std::shared_ptr<UserSession> newSession =
112 UserSession::fromJson(elem);
113
114 if (newSession == nullptr)
115 {
116 BMCWEB_LOG_ERROR << "Problem reading session "
117 "from persistent store";
118 continue;
119 }
120
121 BMCWEB_LOG_DEBUG
122 << "Restored session: " << newSession->csrfToken
123 << " " << newSession->uniqueId << " "
124 << newSession->sessionToken;
125 SessionStore::getInstance().authTokens.emplace(
126 newSession->sessionToken, newSession);
127 }
128 }
129 else
130 {
131 // Do nothing in the case of extra fields. We may have
132 // cases where fields are added in the future, and we
133 // want to at least attempt to gracefully support
134 // downgrades in that case, even if we don't officially
135 // support it
136 }
137 }
138 }
139 }
140 bool needWrite = false;
141
142 if (systemUuid.empty())
143 {
144 systemUuid =
145 boost::uuids::to_string(boost::uuids::random_generator()());
146 needWrite = true;
147 }
148 if (fileRevision < jsonRevision)
149 {
150 needWrite = true;
151 }
152 // write revision changes or system uuid changes immediately
153 if (needWrite)
154 {
155 writeData();
156 }
157 }
158
159 void writeData()
160 {
161 std::ofstream persistentFile(filename);
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530162
163 // set the permission of the file to 640
164 fs::perms permission = fs::perms::owner_read | fs::perms::owner_write |
165 fs::perms::group_read;
166 fs::permissions(filename, permission);
167
Ed Tanous1abe55e2018-09-05 08:30:59 -0700168 nlohmann::json data{
169 {"sessions", SessionStore::getInstance().authTokens},
Zbigniew Kurzynski78158632019-11-05 12:57:37 +0100170 {"auth_config", SessionStore::getInstance().getAuthMethodsConfig()},
Ed Tanous1abe55e2018-09-05 08:30:59 -0700171 {"system_uuid", systemUuid},
172 {"revision", jsonRevision}};
173 persistentFile << data;
174 }
175
176 std::string systemUuid{""};
Ed Tanousba9f9a62017-10-11 16:40:35 -0700177};
178
Ed Tanous1abe55e2018-09-05 08:30:59 -0700179} // namespace persistent_data
180} // namespace crow