summaryrefslogtreecommitdiff
path: root/include/persistent_data_middleware.hpp
blob: 706f6f423ab63cbaf001ddbb65c80a8e16ad2076 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#pragma once

#include <nlohmann/json.hpp>
#include <pam_authenticate.hpp>
#include <sessions.hpp>
#include <webassets.hpp>
#include <random>
#include <crow/app.h>
#include <crow/http_request.h>
#include <crow/http_response.h>
#include <boost/container/flat_map.hpp>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>

namespace crow {

namespace persistent_data {

class Middleware {
  // todo(ed) should read this from a fixed location somewhere, not CWD
  static constexpr const char* filename = "bmcweb_persistent_data.json";
  int jsonRevision = 1;

 public:
  struct Context {};

  Middleware() { readData(); }

  ~Middleware() {
    if (persistent_data::SessionStore::getInstance().needsWrite()) {
      writeData();
    }
  }

  void beforeHandle(crow::Request& req, Response& res, Context& ctx) {}

  void afterHandle(Request& req, Response& res, Context& ctx) {}

  // TODO(ed) this should really use protobuf, or some other serialization
  // library, but adding another dependency is somewhat outside the scope of
  // this application for the moment
  void readData() {
    std::ifstream persistentFile(filename);
    int fileRevision = 0;
    if (persistentFile.is_open()) {
      // call with exceptions disabled
      auto data = nlohmann::json::parse(persistentFile, nullptr, false);
      if (data.is_discarded()) {
        BMCWEB_LOG_ERROR << "Error parsing persistent data in json file.";
      } else {
        for (const auto& item : data.items()) {
          if (item.key() == "revision") {
            fileRevision = 0;

            const uint64_t* uintPtr = item.value().get_ptr<const uint64_t*>();
            if (uintPtr == nullptr) {
              BMCWEB_LOG_ERROR << "Failed to read revision flag";
            } else {
              fileRevision = *uintPtr;
            }
          } else if (item.key() == "system_uuid") {
            const std::string* jSystemUuid =
                item.value().get_ptr<const std::string*>();
            if (jSystemUuid != nullptr) {
              systemUuid = *jSystemUuid;
            }
          } else if (item.key() == "sessions") {
            for (const auto& elem : item.value()) {
              std::shared_ptr<UserSession> newSession =
                  UserSession::fromJson(elem);

              if (newSession == nullptr) {
                BMCWEB_LOG_ERROR
                    << "Problem reading session from persistent store";
                continue;
              }

              BMCWEB_LOG_DEBUG << "Restored session: " << newSession->csrfToken
                               << " " << newSession->uniqueId << " "
                               << newSession->sessionToken;
              SessionStore::getInstance().authTokens.emplace(
                  newSession->sessionToken, newSession);
            }
          } else {
            // Do nothing in the case of extra fields.  We may have cases where
            // fields are added in the future, and we want to at least attempt
            // to gracefully support downgrades in that case, even if we don't
            // officially support it
          }
        }
      }
    }
    bool needWrite = false;

    if (systemUuid.empty()) {
      systemUuid = boost::uuids::to_string(boost::uuids::random_generator()());
      needWrite = true;
    }
    if (fileRevision < jsonRevision) {
      needWrite = true;
    }
    // write revision changes or system uuid changes immediately
    if (needWrite) {
      writeData();
    }
  }

  void writeData() {
    std::ofstream persistentFile(filename);
    nlohmann::json data{{"sessions", SessionStore::getInstance().authTokens},
                        {"system_uuid", systemUuid},
                        {"revision", jsonRevision}};
    persistentFile << data;
  }

  std::string systemUuid{""};
};

}  // namespace persistent_data
}  // namespace crow