blob: d43b4f2f59cf00402a673c91240423b1babb4871 [file] [log] [blame]
Ed Tanousba9f9a62017-10-11 16:40:35 -07001#pragma once
2
Ed Tanousba9f9a62017-10-11 16:40:35 -07003#include <nlohmann/json.hpp>
4#include <pam_authenticate.hpp>
Borawski.Lukasz4b1b8682018-04-04 12:50:16 +02005#include <sessions.hpp>
Ed Tanousba9f9a62017-10-11 16:40:35 -07006#include <webassets.hpp>
Borawski.Lukasz16238972018-01-17 15:36:53 +01007#include <random>
Ed Tanousba9f9a62017-10-11 16:40:35 -07008#include <crow/app.h>
9#include <crow/http_request.h>
10#include <crow/http_response.h>
11#include <boost/container/flat_map.hpp>
12#include <boost/uuid/uuid.hpp>
13#include <boost/uuid/uuid_generators.hpp>
14#include <boost/uuid/uuid_io.hpp>
15
16namespace crow {
17
Ed Tanous55c7b7a2018-05-22 15:27:24 -070018namespace persistent_data {
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +010019
Ed Tanousc963aa42017-10-27 16:00:19 -070020class Middleware {
21 // todo(ed) should read this from a fixed location somewhere, not CWD
22 static constexpr const char* filename = "bmcweb_persistent_data.json";
Ed Tanous55c7b7a2018-05-22 15:27:24 -070023 int jsonRevision = 1;
Ed Tanousc963aa42017-10-27 16:00:19 -070024
25 public:
Ed Tanous55c7b7a2018-05-22 15:27:24 -070026 struct Context {};
Ed Tanousc963aa42017-10-27 16:00:19 -070027
Ed Tanous55c7b7a2018-05-22 15:27:24 -070028 Middleware() { readData(); }
Ed Tanousc963aa42017-10-27 16:00:19 -070029
30 ~Middleware() {
Ed Tanous55c7b7a2018-05-22 15:27:24 -070031 if (persistent_data::SessionStore::getInstance().needsWrite()) {
32 writeData();
Ed Tanousc963aa42017-10-27 16:00:19 -070033 }
34 }
35
Ed Tanous55c7b7a2018-05-22 15:27:24 -070036 void beforeHandle(crow::Request& req, Response& res, Context& ctx) {}
Ed Tanousc963aa42017-10-27 16:00:19 -070037
Ed Tanous55c7b7a2018-05-22 15:27:24 -070038 void afterHandle(Request& req, Response& res, Context& ctx) {}
Ed Tanousc963aa42017-10-27 16:00:19 -070039
40 // TODO(ed) this should really use protobuf, or some other serialization
41 // library, but adding another dependency is somewhat outside the scope of
42 // this application for the moment
Ed Tanous55c7b7a2018-05-22 15:27:24 -070043 void readData() {
44 std::ifstream persistentFile(filename);
45 int fileRevision = 0;
46 if (persistentFile.is_open()) {
Ed Tanousc963aa42017-10-27 16:00:19 -070047 // call with exceptions disabled
Ed Tanous55c7b7a2018-05-22 15:27:24 -070048 auto data = nlohmann::json::parse(persistentFile, nullptr, false);
Ed Tanouse1ae5332018-07-09 15:21:27 -070049 if (data.is_discarded()) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -070050 BMCWEB_LOG_ERROR << "Error parsing persistent data in json file.";
Ed Tanouse1ae5332018-07-09 15:21:27 -070051 } else {
52 for (const auto& item : data.items()) {
53 if (item.key() == "revision") {
Ed Tanous55c7b7a2018-05-22 15:27:24 -070054 fileRevision = 0;
Kowalski, Kamil5cef0f72018-02-15 15:26:51 +010055
Ed Tanouse1ae5332018-07-09 15:21:27 -070056 const uint64_t* uintPtr = item.value().get_ptr<const uint64_t*>();
57 if (uintPtr == nullptr) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -070058 BMCWEB_LOG_ERROR << "Failed to read revision flag";
Ed Tanouse1ae5332018-07-09 15:21:27 -070059 } else {
Ed Tanous55c7b7a2018-05-22 15:27:24 -070060 fileRevision = *uintPtr;
Kowalski, Kamil5cef0f72018-02-15 15:26:51 +010061 }
Ed Tanouse1ae5332018-07-09 15:21:27 -070062 } else if (item.key() == "system_uuid") {
63 const std::string* jSystemUuid =
64 item.value().get_ptr<const std::string*>();
65 if (jSystemUuid != nullptr) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -070066 systemUuid = *jSystemUuid;
Ed Tanouse1ae5332018-07-09 15:21:27 -070067 }
68 } else if (item.key() == "sessions") {
69 for (const auto& elem : item.value()) {
70 std::shared_ptr<UserSession> newSession =
71 UserSession::fromJson(elem);
72
73 if (newSession == nullptr) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -070074 BMCWEB_LOG_ERROR
Ed Tanouse1ae5332018-07-09 15:21:27 -070075 << "Problem reading session from persistent store";
76 continue;
77 }
78
Ed Tanous55c7b7a2018-05-22 15:27:24 -070079 BMCWEB_LOG_DEBUG << "Restored session: " << newSession->csrfToken
80 << " " << newSession->uniqueId << " "
81 << newSession->sessionToken;
82 SessionStore::getInstance().authTokens.emplace(
83 newSession->sessionToken, newSession);
Ed Tanouse1ae5332018-07-09 15:21:27 -070084 }
85 } else {
86 // Do nothing in the case of extra fields. We may have cases where
87 // fields are added in the future, and we want to at least attempt
88 // to gracefully support downgrades in that case, even if we don't
89 // officially support it
Kowalski, Kamil5cef0f72018-02-15 15:26:51 +010090 }
91 }
Ed Tanousc963aa42017-10-27 16:00:19 -070092 }
93 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -070094 bool needWrite = false;
Ed Tanousc963aa42017-10-27 16:00:19 -070095
Ed Tanous55c7b7a2018-05-22 15:27:24 -070096 if (systemUuid.empty()) {
97 systemUuid = boost::uuids::to_string(boost::uuids::random_generator()());
98 needWrite = true;
Ed Tanousc963aa42017-10-27 16:00:19 -070099 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700100 if (fileRevision < jsonRevision) {
101 needWrite = true;
Ed Tanousc963aa42017-10-27 16:00:19 -0700102 }
103 // write revision changes or system uuid changes immediately
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700104 if (needWrite) {
105 writeData();
Ed Tanousc963aa42017-10-27 16:00:19 -0700106 }
107 }
108
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700109 void writeData() {
110 std::ofstream persistentFile(filename);
Ed Tanouse1ae5332018-07-09 15:21:27 -0700111 nlohmann::json data{
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700112 {"sessions", SessionStore::getInstance().authTokens},
113 {"system_uuid", systemUuid},
114 {"revision", jsonRevision}};
115 persistentFile << data;
Ed Tanousc963aa42017-10-27 16:00:19 -0700116 }
117
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700118 std::string systemUuid{""};
Ed Tanousba9f9a62017-10-11 16:40:35 -0700119};
120
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700121} // namespace persistent_data
Ed Tanousba9f9a62017-10-11 16:40:35 -0700122} // namespace crow