blob: c368ab21dcf42c789201f9766ec17404b2b2cfd0 [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>
Ratan Gupta845cb7d2019-07-12 00:32:25 +053011#include <filesystem>
Ed Tanous1abe55e2018-09-05 08:30:59 -070012#include <nlohmann/json.hpp>
13#include <pam_authenticate.hpp>
14#include <random>
15#include <sessions.hpp>
16#include <webassets.hpp>
Ed Tanousba9f9a62017-10-11 16:40:35 -070017
Ed Tanous1abe55e2018-09-05 08:30:59 -070018namespace crow
19{
Ed Tanousba9f9a62017-10-11 16:40:35 -070020
Ed Tanous1abe55e2018-09-05 08:30:59 -070021namespace persistent_data
22{
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +010023
Ratan Gupta845cb7d2019-07-12 00:32:25 +053024namespace fs = std::filesystem;
25
Ed Tanous1abe55e2018-09-05 08:30:59 -070026class Middleware
27{
Ed Tanous271584a2019-07-09 16:24:22 -070028 uint64_t jsonRevision = 1;
Ed Tanousc963aa42017-10-27 16:00:19 -070029
Ed Tanous1abe55e2018-09-05 08:30:59 -070030 public:
Ratan Gupta845cb7d2019-07-12 00:32:25 +053031 // todo(ed) should read this from a fixed location somewhere, not CWD
32 static constexpr const char* filename = "bmcweb_persistent_data.json";
33
Ed Tanous1abe55e2018-09-05 08:30:59 -070034 struct Context
35 {
36 };
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)
52 {
Ed Tanousc963aa42017-10-27 16:00:19 -070053 }
Ed Tanousc963aa42017-10-27 16:00:19 -070054
Ed Tanous1abe55e2018-09-05 08:30:59 -070055 void afterHandle(Request& req, Response& res, Context& ctx)
56 {
57 }
Ed Tanousc963aa42017-10-27 16:00:19 -070058
Ed Tanous1abe55e2018-09-05 08:30:59 -070059 // TODO(ed) this should really use protobuf, or some other serialization
60 // library, but adding another dependency is somewhat outside the scope of
61 // this application for the moment
62 void readData()
63 {
64 std::ifstream persistentFile(filename);
Ed Tanous271584a2019-07-09 16:24:22 -070065 uint64_t fileRevision = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -070066 if (persistentFile.is_open())
67 {
68 // call with exceptions disabled
69 auto data = nlohmann::json::parse(persistentFile, nullptr, false);
70 if (data.is_discarded())
71 {
72 BMCWEB_LOG_ERROR
73 << "Error parsing persistent data in json file.";
74 }
75 else
76 {
77 for (const auto& item : data.items())
78 {
79 if (item.key() == "revision")
80 {
81 fileRevision = 0;
82
83 const uint64_t* uintPtr =
84 item.value().get_ptr<const uint64_t*>();
85 if (uintPtr == nullptr)
86 {
87 BMCWEB_LOG_ERROR << "Failed to read revision flag";
88 }
89 else
90 {
91 fileRevision = *uintPtr;
92 }
93 }
94 else if (item.key() == "system_uuid")
95 {
96 const std::string* jSystemUuid =
97 item.value().get_ptr<const std::string*>();
98 if (jSystemUuid != nullptr)
99 {
100 systemUuid = *jSystemUuid;
101 }
102 }
103 else if (item.key() == "sessions")
104 {
105 for (const auto& elem : item.value())
106 {
107 std::shared_ptr<UserSession> newSession =
108 UserSession::fromJson(elem);
109
110 if (newSession == nullptr)
111 {
112 BMCWEB_LOG_ERROR << "Problem reading session "
113 "from persistent store";
114 continue;
115 }
116
117 BMCWEB_LOG_DEBUG
118 << "Restored session: " << newSession->csrfToken
119 << " " << newSession->uniqueId << " "
120 << newSession->sessionToken;
121 SessionStore::getInstance().authTokens.emplace(
122 newSession->sessionToken, newSession);
123 }
124 }
125 else
126 {
127 // Do nothing in the case of extra fields. We may have
128 // cases where fields are added in the future, and we
129 // want to at least attempt to gracefully support
130 // downgrades in that case, even if we don't officially
131 // support it
132 }
133 }
134 }
135 }
136 bool needWrite = false;
137
138 if (systemUuid.empty())
139 {
140 systemUuid =
141 boost::uuids::to_string(boost::uuids::random_generator()());
142 needWrite = true;
143 }
144 if (fileRevision < jsonRevision)
145 {
146 needWrite = true;
147 }
148 // write revision changes or system uuid changes immediately
149 if (needWrite)
150 {
151 writeData();
152 }
153 }
154
155 void writeData()
156 {
157 std::ofstream persistentFile(filename);
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530158
159 // set the permission of the file to 640
160 fs::perms permission = fs::perms::owner_read | fs::perms::owner_write |
161 fs::perms::group_read;
162 fs::permissions(filename, permission);
163
Ed Tanous1abe55e2018-09-05 08:30:59 -0700164 nlohmann::json data{
165 {"sessions", SessionStore::getInstance().authTokens},
166 {"system_uuid", systemUuid},
167 {"revision", jsonRevision}};
168 persistentFile << data;
169 }
170
171 std::string systemUuid{""};
Ed Tanousba9f9a62017-10-11 16:40:35 -0700172};
173
Ed Tanous1abe55e2018-09-05 08:30:59 -0700174} // namespace persistent_data
175} // namespace crow