152cc112dSEd Tanous #pragma once
252cc112dSEd Tanous 
304e438cbSEd Tanous #include <app.hpp>
452cc112dSEd Tanous #include <boost/container/flat_map.hpp>
552cc112dSEd Tanous #include <boost/uuid/uuid.hpp>
652cc112dSEd Tanous #include <boost/uuid/uuid_generators.hpp>
752cc112dSEd Tanous #include <boost/uuid/uuid_io.hpp>
804e438cbSEd Tanous #include <http_request.hpp>
904e438cbSEd Tanous #include <http_response.hpp>
1052cc112dSEd Tanous #include <nlohmann/json.hpp>
1152cc112dSEd Tanous #include <pam_authenticate.hpp>
1252cc112dSEd Tanous #include <sessions.hpp>
1352cc112dSEd Tanous 
1452cc112dSEd Tanous #include <filesystem>
1552cc112dSEd Tanous #include <fstream>
1652cc112dSEd Tanous #include <random>
1752cc112dSEd Tanous 
1852cc112dSEd Tanous namespace persistent_data
1952cc112dSEd Tanous {
2052cc112dSEd Tanous 
2152cc112dSEd Tanous class ConfigFile
2252cc112dSEd Tanous {
2352cc112dSEd Tanous     uint64_t jsonRevision = 1;
2452cc112dSEd Tanous 
2552cc112dSEd Tanous   public:
2652cc112dSEd Tanous     // todo(ed) should read this from a fixed location somewhere, not CWD
2752cc112dSEd Tanous     static constexpr const char* filename = "bmcweb_persistent_data.json";
2852cc112dSEd Tanous 
2952cc112dSEd Tanous     ConfigFile()
3052cc112dSEd Tanous     {
3152cc112dSEd Tanous         readData();
3252cc112dSEd Tanous     }
3352cc112dSEd Tanous 
3452cc112dSEd Tanous     ~ConfigFile()
3552cc112dSEd Tanous     {
3652cc112dSEd Tanous         if (persistent_data::SessionStore::getInstance().needsWrite())
3752cc112dSEd Tanous         {
3852cc112dSEd Tanous             writeData();
3952cc112dSEd Tanous         }
4052cc112dSEd Tanous     }
4152cc112dSEd Tanous 
4252cc112dSEd Tanous     // TODO(ed) this should really use protobuf, or some other serialization
4352cc112dSEd Tanous     // library, but adding another dependency is somewhat outside the scope of
4452cc112dSEd Tanous     // this application for the moment
4552cc112dSEd Tanous     void readData()
4652cc112dSEd Tanous     {
4752cc112dSEd Tanous         std::ifstream persistentFile(filename);
4852cc112dSEd Tanous         uint64_t fileRevision = 0;
4952cc112dSEd Tanous         if (persistentFile.is_open())
5052cc112dSEd Tanous         {
5152cc112dSEd Tanous             // call with exceptions disabled
5252cc112dSEd Tanous             auto data = nlohmann::json::parse(persistentFile, nullptr, false);
5352cc112dSEd Tanous             if (data.is_discarded())
5452cc112dSEd Tanous             {
5552cc112dSEd Tanous                 BMCWEB_LOG_ERROR
5652cc112dSEd Tanous                     << "Error parsing persistent data in json file.";
5752cc112dSEd Tanous             }
5852cc112dSEd Tanous             else
5952cc112dSEd Tanous             {
6052cc112dSEd Tanous                 for (const auto& item : data.items())
6152cc112dSEd Tanous                 {
6252cc112dSEd Tanous                     if (item.key() == "revision")
6352cc112dSEd Tanous                     {
6452cc112dSEd Tanous                         fileRevision = 0;
6552cc112dSEd Tanous 
6652cc112dSEd Tanous                         const uint64_t* uintPtr =
6752cc112dSEd Tanous                             item.value().get_ptr<const uint64_t*>();
6852cc112dSEd Tanous                         if (uintPtr == nullptr)
6952cc112dSEd Tanous                         {
7052cc112dSEd Tanous                             BMCWEB_LOG_ERROR << "Failed to read revision flag";
7152cc112dSEd Tanous                         }
7252cc112dSEd Tanous                         else
7352cc112dSEd Tanous                         {
7452cc112dSEd Tanous                             fileRevision = *uintPtr;
7552cc112dSEd Tanous                         }
7652cc112dSEd Tanous                     }
7752cc112dSEd Tanous                     else if (item.key() == "system_uuid")
7852cc112dSEd Tanous                     {
7952cc112dSEd Tanous                         const std::string* jSystemUuid =
8052cc112dSEd Tanous                             item.value().get_ptr<const std::string*>();
8152cc112dSEd Tanous                         if (jSystemUuid != nullptr)
8252cc112dSEd Tanous                         {
8352cc112dSEd Tanous                             systemUuid = *jSystemUuid;
8452cc112dSEd Tanous                         }
8552cc112dSEd Tanous                     }
8652cc112dSEd Tanous                     else if (item.key() == "auth_config")
8752cc112dSEd Tanous                     {
8852cc112dSEd Tanous                         SessionStore::getInstance()
8952cc112dSEd Tanous                             .getAuthMethodsConfig()
9052cc112dSEd Tanous                             .fromJson(item.value());
9152cc112dSEd Tanous                     }
9252cc112dSEd Tanous                     else if (item.key() == "sessions")
9352cc112dSEd Tanous                     {
9452cc112dSEd Tanous                         for (const auto& elem : item.value())
9552cc112dSEd Tanous                         {
9652cc112dSEd Tanous                             std::shared_ptr<UserSession> newSession =
9752cc112dSEd Tanous                                 UserSession::fromJson(elem);
9852cc112dSEd Tanous 
9952cc112dSEd Tanous                             if (newSession == nullptr)
10052cc112dSEd Tanous                             {
10152cc112dSEd Tanous                                 BMCWEB_LOG_ERROR << "Problem reading session "
10252cc112dSEd Tanous                                                     "from persistent store";
10352cc112dSEd Tanous                                 continue;
10452cc112dSEd Tanous                             }
10552cc112dSEd Tanous 
10652cc112dSEd Tanous                             BMCWEB_LOG_DEBUG
10752cc112dSEd Tanous                                 << "Restored session: " << newSession->csrfToken
10852cc112dSEd Tanous                                 << " " << newSession->uniqueId << " "
10952cc112dSEd Tanous                                 << newSession->sessionToken;
11052cc112dSEd Tanous                             SessionStore::getInstance().authTokens.emplace(
11152cc112dSEd Tanous                                 newSession->sessionToken, newSession);
11252cc112dSEd Tanous                         }
11352cc112dSEd Tanous                     }
114f2a4a606SManojkiran Eda                     else if (item.key() == "timeout")
115f2a4a606SManojkiran Eda                     {
116f2a4a606SManojkiran Eda                         const int64_t* jTimeout =
117f2a4a606SManojkiran Eda                             item.value().get_ptr<int64_t*>();
118f2a4a606SManojkiran Eda                         if (jTimeout == nullptr)
119f2a4a606SManojkiran Eda                         {
120f2a4a606SManojkiran Eda                             BMCWEB_LOG_DEBUG
121f2a4a606SManojkiran Eda                                 << "Problem reading session timeout value";
122f2a4a606SManojkiran Eda                             continue;
123f2a4a606SManojkiran Eda                         }
124f2a4a606SManojkiran Eda                         std::chrono::seconds sessionTimeoutInseconds(*jTimeout);
125f2a4a606SManojkiran Eda                         BMCWEB_LOG_DEBUG << "Restored Session Timeout: "
126f2a4a606SManojkiran Eda                                          << sessionTimeoutInseconds.count();
127f2a4a606SManojkiran Eda                         SessionStore::getInstance().updateSessionTimeout(
128f2a4a606SManojkiran Eda                             sessionTimeoutInseconds);
129f2a4a606SManojkiran Eda                     }
13052cc112dSEd Tanous                     else
13152cc112dSEd Tanous                     {
13252cc112dSEd Tanous                         // Do nothing in the case of extra fields.  We may have
13352cc112dSEd Tanous                         // cases where fields are added in the future, and we
13452cc112dSEd Tanous                         // want to at least attempt to gracefully support
13552cc112dSEd Tanous                         // downgrades in that case, even if we don't officially
13652cc112dSEd Tanous                         // support it
13752cc112dSEd Tanous                     }
13852cc112dSEd Tanous                 }
13952cc112dSEd Tanous             }
14052cc112dSEd Tanous         }
14152cc112dSEd Tanous         bool needWrite = false;
14252cc112dSEd Tanous 
14352cc112dSEd Tanous         if (systemUuid.empty())
14452cc112dSEd Tanous         {
14552cc112dSEd Tanous             systemUuid =
14652cc112dSEd Tanous                 boost::uuids::to_string(boost::uuids::random_generator()());
14752cc112dSEd Tanous             needWrite = true;
14852cc112dSEd Tanous         }
14952cc112dSEd Tanous         if (fileRevision < jsonRevision)
15052cc112dSEd Tanous         {
15152cc112dSEd Tanous             needWrite = true;
15252cc112dSEd Tanous         }
15352cc112dSEd Tanous         // write revision changes or system uuid changes immediately
15452cc112dSEd Tanous         if (needWrite)
15552cc112dSEd Tanous         {
15652cc112dSEd Tanous             writeData();
15752cc112dSEd Tanous         }
15852cc112dSEd Tanous     }
15952cc112dSEd Tanous 
16052cc112dSEd Tanous     void writeData()
16152cc112dSEd Tanous     {
16252cc112dSEd Tanous         std::ofstream persistentFile(filename);
16352cc112dSEd Tanous 
16452cc112dSEd Tanous         // set the permission of the file to 640
16552cc112dSEd Tanous         std::filesystem::perms permission =
16652cc112dSEd Tanous             std::filesystem::perms::owner_read |
16752cc112dSEd Tanous             std::filesystem::perms::owner_write |
16852cc112dSEd Tanous             std::filesystem::perms::group_read;
16952cc112dSEd Tanous         std::filesystem::permissions(filename, permission);
1705fb91ba4SEd Tanous         const auto& c = SessionStore::getInstance().getAuthMethodsConfig();
171dc511aa7SEd Tanous         nlohmann::json data{
172dc511aa7SEd Tanous             {"auth_config",
1735fb91ba4SEd Tanous              {{"XToken", c.xtoken},
1745fb91ba4SEd Tanous               {"Cookie", c.cookie},
1755fb91ba4SEd Tanous               {"SessionToken", c.sessionToken},
1765fb91ba4SEd Tanous               {"BasicAuth", c.basic},
1775fb91ba4SEd Tanous               {"TLS", c.tls}}
17852cc112dSEd Tanous 
1795fb91ba4SEd Tanous             },
18052cc112dSEd Tanous             {"system_uuid", systemUuid},
181dc511aa7SEd Tanous             {"revision", jsonRevision},
182dc511aa7SEd Tanous             {"timeout", SessionStore::getInstance().getTimeoutInSeconds()}};
1835fb91ba4SEd Tanous 
1845fb91ba4SEd Tanous         nlohmann::json& sessions = data["sessions"];
1855fb91ba4SEd Tanous         sessions = nlohmann::json::array();
1865fb91ba4SEd Tanous         for (const auto& p : SessionStore::getInstance().authTokens)
1875fb91ba4SEd Tanous         {
1885fb91ba4SEd Tanous             if (p.second->persistence !=
1895fb91ba4SEd Tanous                 persistent_data::PersistenceType::SINGLE_REQUEST)
1905fb91ba4SEd Tanous             {
1915fb91ba4SEd Tanous                 sessions.push_back({
1925fb91ba4SEd Tanous                     {"unique_id", p.second->uniqueId},
1935fb91ba4SEd Tanous                     {"session_token", p.second->sessionToken},
1945fb91ba4SEd Tanous                     {"username", p.second->username},
1955fb91ba4SEd Tanous                     {"csrf_token", p.second->csrfToken},
196*c0ea7ae1SSunitha Harish                     {"client_ip", p.second->clientIp},
1975fb91ba4SEd Tanous #ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE
1985fb91ba4SEd Tanous                     {"client_id", p.second->clientId},
1995fb91ba4SEd Tanous #endif
2005fb91ba4SEd Tanous                 });
2015fb91ba4SEd Tanous             }
2025fb91ba4SEd Tanous         }
20352cc112dSEd Tanous         persistentFile << data;
20452cc112dSEd Tanous     }
20552cc112dSEd Tanous 
20652cc112dSEd Tanous     std::string systemUuid{""};
20752cc112dSEd Tanous };
20852cc112dSEd Tanous 
20952cc112dSEd Tanous inline ConfigFile& getConfig()
21052cc112dSEd Tanous {
21152cc112dSEd Tanous     static ConfigFile f;
21252cc112dSEd Tanous     return f;
21352cc112dSEd Tanous }
21452cc112dSEd Tanous 
21552cc112dSEd Tanous } // namespace persistent_data
216