blob: 5d4661c503d579c21d257bd3a6cb538f1d452bd1 [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 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 {
37 if (persistent_data::SessionStore::getInstance().needsWrite())
38 {
39 writeData();
Kowalski, Kamil5cef0f72018-02-15 15:26:51 +010040 }
Ed Tanousc963aa42017-10-27 16:00:19 -070041 }
Ed Tanousc963aa42017-10-27 16:00:19 -070042
Ed Tanous1abe55e2018-09-05 08:30:59 -070043 // TODO(ed) this should really use protobuf, or some other serialization
44 // library, but adding another dependency is somewhat outside the scope of
45 // this application for the moment
46 void readData()
47 {
48 std::ifstream persistentFile(filename);
Ed Tanous271584a2019-07-09 16:24:22 -070049 uint64_t fileRevision = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -070050 if (persistentFile.is_open())
51 {
52 // call with exceptions disabled
53 auto data = nlohmann::json::parse(persistentFile, nullptr, false);
54 if (data.is_discarded())
55 {
56 BMCWEB_LOG_ERROR
57 << "Error parsing persistent data in json file.";
58 }
59 else
60 {
61 for (const auto& item : data.items())
62 {
63 if (item.key() == "revision")
64 {
65 fileRevision = 0;
66
67 const uint64_t* uintPtr =
68 item.value().get_ptr<const uint64_t*>();
69 if (uintPtr == nullptr)
70 {
71 BMCWEB_LOG_ERROR << "Failed to read revision flag";
72 }
73 else
74 {
75 fileRevision = *uintPtr;
76 }
77 }
78 else if (item.key() == "system_uuid")
79 {
80 const std::string* jSystemUuid =
81 item.value().get_ptr<const std::string*>();
82 if (jSystemUuid != nullptr)
83 {
84 systemUuid = *jSystemUuid;
85 }
86 }
Zbigniew Kurzynski78158632019-11-05 12:57:37 +010087 else if (item.key() == "auth_config")
88 {
89 SessionStore::getInstance()
90 .getAuthMethodsConfig()
91 .fromJson(item.value());
92 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070093 else if (item.key() == "sessions")
94 {
95 for (const auto& elem : item.value())
96 {
97 std::shared_ptr<UserSession> newSession =
98 UserSession::fromJson(elem);
99
100 if (newSession == nullptr)
101 {
102 BMCWEB_LOG_ERROR << "Problem reading session "
103 "from persistent store";
104 continue;
105 }
106
107 BMCWEB_LOG_DEBUG
108 << "Restored session: " << newSession->csrfToken
109 << " " << newSession->uniqueId << " "
110 << newSession->sessionToken;
111 SessionStore::getInstance().authTokens.emplace(
112 newSession->sessionToken, newSession);
113 }
114 }
115 else
116 {
117 // Do nothing in the case of extra fields. We may have
118 // cases where fields are added in the future, and we
119 // want to at least attempt to gracefully support
120 // downgrades in that case, even if we don't officially
121 // support it
122 }
123 }
124 }
125 }
126 bool needWrite = false;
127
128 if (systemUuid.empty())
129 {
130 systemUuid =
131 boost::uuids::to_string(boost::uuids::random_generator()());
132 needWrite = true;
133 }
134 if (fileRevision < jsonRevision)
135 {
136 needWrite = true;
137 }
138 // write revision changes or system uuid changes immediately
139 if (needWrite)
140 {
141 writeData();
142 }
143 }
144
145 void writeData()
146 {
147 std::ofstream persistentFile(filename);
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530148
149 // set the permission of the file to 640
Ed Tanous52cc1122020-07-18 13:51:21 -0700150 std::filesystem::perms permission =
151 std::filesystem::perms::owner_read |
152 std::filesystem::perms::owner_write |
153 std::filesystem::perms::group_read;
154 std::filesystem::permissions(filename, permission);
Ratan Gupta845cb7d2019-07-12 00:32:25 +0530155
Ed Tanous1abe55e2018-09-05 08:30:59 -0700156 nlohmann::json data{
157 {"sessions", SessionStore::getInstance().authTokens},
Zbigniew Kurzynski78158632019-11-05 12:57:37 +0100158 {"auth_config", SessionStore::getInstance().getAuthMethodsConfig()},
Ed Tanous1abe55e2018-09-05 08:30:59 -0700159 {"system_uuid", systemUuid},
160 {"revision", jsonRevision}};
161 persistentFile << data;
162 }
163
164 std::string systemUuid{""};
Ed Tanousba9f9a62017-10-11 16:40:35 -0700165};
166
Ed Tanous52cc1122020-07-18 13:51:21 -0700167inline ConfigFile& getConfig()
168{
169 static ConfigFile f;
170 return f;
171}
172
Ed Tanous1abe55e2018-09-05 08:30:59 -0700173} // namespace persistent_data