1 #pragma once 2 3 #include <app.h> 4 #include <http_request.h> 5 #include <http_response.h> 6 7 #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> 11 #include <nlohmann/json.hpp> 12 #include <pam_authenticate.hpp> 13 #include <sessions.hpp> 14 15 #include <filesystem> 16 #include <fstream> 17 #include <random> 18 19 namespace persistent_data 20 { 21 22 class ConfigFile 23 { 24 uint64_t jsonRevision = 1; 25 26 public: 27 // todo(ed) should read this from a fixed location somewhere, not CWD 28 static constexpr const char* filename = "bmcweb_persistent_data.json"; 29 30 ConfigFile() 31 { 32 readData(); 33 } 34 35 ~ConfigFile() 36 { 37 if (persistent_data::SessionStore::getInstance().needsWrite()) 38 { 39 writeData(); 40 } 41 } 42 43 // 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); 49 uint64_t fileRevision = 0; 50 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 } 87 else if (item.key() == "auth_config") 88 { 89 SessionStore::getInstance() 90 .getAuthMethodsConfig() 91 .fromJson(item.value()); 92 } 93 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); 148 149 // set the permission of the file to 640 150 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); 155 156 nlohmann::json data{ 157 {"sessions", SessionStore::getInstance().authTokens}, 158 {"auth_config", SessionStore::getInstance().getAuthMethodsConfig()}, 159 {"system_uuid", systemUuid}, 160 {"revision", jsonRevision}}; 161 persistentFile << data; 162 } 163 164 std::string systemUuid{""}; 165 }; 166 167 inline ConfigFile& getConfig() 168 { 169 static ConfigFile f; 170 return f; 171 } 172 173 } // namespace persistent_data 174