1 #pragma once 2 3 #include <app.hpp> 4 #include <boost/container/flat_map.hpp> 5 #include <boost/uuid/uuid.hpp> 6 #include <boost/uuid/uuid_generators.hpp> 7 #include <boost/uuid/uuid_io.hpp> 8 #include <http_request.hpp> 9 #include <http_response.hpp> 10 #include <nlohmann/json.hpp> 11 #include <pam_authenticate.hpp> 12 #include <sessions.hpp> 13 14 #include <filesystem> 15 #include <fstream> 16 #include <random> 17 18 namespace persistent_data 19 { 20 21 class ConfigFile 22 { 23 uint64_t jsonRevision = 1; 24 25 public: 26 // todo(ed) should read this from a fixed location somewhere, not CWD 27 static constexpr const char* filename = "bmcweb_persistent_data.json"; 28 29 ConfigFile() 30 { 31 readData(); 32 } 33 34 ~ConfigFile() 35 { 36 // Make sure we aren't writing stale sessions 37 persistent_data::SessionStore::getInstance().applySessionTimeouts(); 38 if (persistent_data::SessionStore::getInstance().needsWrite()) 39 { 40 writeData(); 41 } 42 } 43 44 // TODO(ed) this should really use protobuf, or some other serialization 45 // library, but adding another dependency is somewhat outside the scope of 46 // this application for the moment 47 void readData() 48 { 49 std::ifstream persistentFile(filename); 50 uint64_t fileRevision = 0; 51 if (persistentFile.is_open()) 52 { 53 // call with exceptions disabled 54 auto data = nlohmann::json::parse(persistentFile, nullptr, false); 55 if (data.is_discarded()) 56 { 57 BMCWEB_LOG_ERROR 58 << "Error parsing persistent data in json file."; 59 } 60 else 61 { 62 for (const auto& item : data.items()) 63 { 64 if (item.key() == "revision") 65 { 66 fileRevision = 0; 67 68 const uint64_t* uintPtr = 69 item.value().get_ptr<const uint64_t*>(); 70 if (uintPtr == nullptr) 71 { 72 BMCWEB_LOG_ERROR << "Failed to read revision flag"; 73 } 74 else 75 { 76 fileRevision = *uintPtr; 77 } 78 } 79 else if (item.key() == "system_uuid") 80 { 81 const std::string* jSystemUuid = 82 item.value().get_ptr<const std::string*>(); 83 if (jSystemUuid != nullptr) 84 { 85 systemUuid = *jSystemUuid; 86 } 87 } 88 else if (item.key() == "auth_config") 89 { 90 SessionStore::getInstance() 91 .getAuthMethodsConfig() 92 .fromJson(item.value()); 93 } 94 else if (item.key() == "sessions") 95 { 96 for (const auto& elem : item.value()) 97 { 98 std::shared_ptr<UserSession> newSession = 99 UserSession::fromJson(elem); 100 101 if (newSession == nullptr) 102 { 103 BMCWEB_LOG_ERROR << "Problem reading session " 104 "from persistent store"; 105 continue; 106 } 107 108 BMCWEB_LOG_DEBUG 109 << "Restored session: " << newSession->csrfToken 110 << " " << newSession->uniqueId << " " 111 << newSession->sessionToken; 112 SessionStore::getInstance().authTokens.emplace( 113 newSession->sessionToken, newSession); 114 } 115 } 116 else if (item.key() == "timeout") 117 { 118 const int64_t* jTimeout = 119 item.value().get_ptr<int64_t*>(); 120 if (jTimeout == nullptr) 121 { 122 BMCWEB_LOG_DEBUG 123 << "Problem reading session timeout value"; 124 continue; 125 } 126 std::chrono::seconds sessionTimeoutInseconds(*jTimeout); 127 BMCWEB_LOG_DEBUG << "Restored Session Timeout: " 128 << sessionTimeoutInseconds.count(); 129 SessionStore::getInstance().updateSessionTimeout( 130 sessionTimeoutInseconds); 131 } 132 else 133 { 134 // Do nothing in the case of extra fields. We may have 135 // cases where fields are added in the future, and we 136 // want to at least attempt to gracefully support 137 // downgrades in that case, even if we don't officially 138 // support it 139 } 140 } 141 } 142 } 143 bool needWrite = false; 144 145 if (systemUuid.empty()) 146 { 147 systemUuid = 148 boost::uuids::to_string(boost::uuids::random_generator()()); 149 needWrite = true; 150 } 151 if (fileRevision < jsonRevision) 152 { 153 needWrite = true; 154 } 155 // write revision changes or system uuid changes immediately 156 if (needWrite) 157 { 158 writeData(); 159 } 160 } 161 162 void writeData() 163 { 164 std::ofstream persistentFile(filename); 165 166 // set the permission of the file to 640 167 std::filesystem::perms permission = 168 std::filesystem::perms::owner_read | 169 std::filesystem::perms::owner_write | 170 std::filesystem::perms::group_read; 171 std::filesystem::permissions(filename, permission); 172 const auto& c = SessionStore::getInstance().getAuthMethodsConfig(); 173 nlohmann::json data{ 174 {"auth_config", 175 {{"XToken", c.xtoken}, 176 {"Cookie", c.cookie}, 177 {"SessionToken", c.sessionToken}, 178 {"BasicAuth", c.basic}, 179 {"TLS", c.tls}} 180 181 }, 182 {"system_uuid", systemUuid}, 183 {"revision", jsonRevision}, 184 {"timeout", SessionStore::getInstance().getTimeoutInSeconds()}}; 185 186 nlohmann::json& sessions = data["sessions"]; 187 sessions = nlohmann::json::array(); 188 for (const auto& p : SessionStore::getInstance().authTokens) 189 { 190 if (p.second->persistence != 191 persistent_data::PersistenceType::SINGLE_REQUEST) 192 { 193 sessions.push_back({ 194 {"unique_id", p.second->uniqueId}, 195 {"session_token", p.second->sessionToken}, 196 {"username", p.second->username}, 197 {"csrf_token", p.second->csrfToken}, 198 {"client_ip", p.second->clientIp}, 199 #ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE 200 {"client_id", p.second->clientId}, 201 #endif 202 }); 203 } 204 } 205 persistentFile << data; 206 } 207 208 std::string systemUuid{""}; 209 }; 210 211 inline ConfigFile& getConfig() 212 { 213 static ConfigFile f; 214 return f; 215 } 216 217 } // namespace persistent_data 218