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