Reimplement presistent data loading in no-throw way

Reimplemented persistent data file loading in no-throw approach
to avoid errors during startup when bmcweb_persistent_data.json
has been corrupted. Additionally this will allow to turn off all
exceptions in the project (removed try-catch).

Change-Id: I9bf863ebfd7ce9125d1e7e948f7ac739db94e009
Signed-off-by: Kowalski, Kamil <kamil.kowalski@intel.com>
Signed-off-by: Ed Tanous <ed.tanous@intel.com>
diff --git a/include/persistent_data_middleware.hpp b/include/persistent_data_middleware.hpp
index 9d6195c..fcab52f 100644
--- a/include/persistent_data_middleware.hpp
+++ b/include/persistent_data_middleware.hpp
@@ -47,10 +47,36 @@
       // call with exceptions disabled
       auto data = nlohmann::json::parse(persistent_file, nullptr, false);
       if (!data.is_discarded()) {
-        file_revision = data.value("revision", 0);
-        PersistentData::session_store->auth_tokens =
-            data.value("sessions", decltype(session_store->auth_tokens)());
-        system_uuid = data.value("system_uuid", "");
+        auto jRevision = data.find("revision");
+        auto jUuid = data.find("system_uuid");
+        auto jSessions = data.find("sessions");
+
+        file_revision = 0;
+        if (jRevision != data.end()) {
+          if (jRevision->is_number_integer()) {
+            file_revision = jRevision->get<int>();
+          }
+        }
+
+        system_uuid = "";
+        if (jUuid != data.end()) {
+          if (jUuid->is_string()) {
+            system_uuid = jUuid->get<std::string>();
+          }
+        }
+
+        if (jSessions != data.end()) {
+          if (jSessions->is_object()) {
+            for (const auto& elem : *jSessions) {
+              UserSession newSession;
+
+              if (newSession.fromJson(elem)) {
+                session_store->auth_tokens.emplace(newSession.unique_id,
+                                                   std::move(newSession));
+              }
+            }
+          }
+        }
       }
     }
     bool need_write = false;
diff --git a/include/sessions.hpp b/include/sessions.hpp
index 6d4ab4d..90df93e 100644
--- a/include/sessions.hpp
+++ b/include/sessions.hpp
@@ -28,6 +28,49 @@
   std::string csrf_token;
   std::chrono::time_point<std::chrono::steady_clock> last_updated;
   PersistenceType persistence;
+
+  /**
+   * @brief Fills object with data from UserSession's JSON representation
+   *
+   * This replaces nlohmann's from_json to ensure no-throw approach
+   *
+   * @param[in] j   JSON object from which data should be loaded
+   *
+   * @return true if data has been loaded properly, false otherwise
+   */
+  bool fromJson(const nlohmann::json& j) {
+    auto jUid = j.find("unique_id");
+    auto jToken = j.find("session_token");
+    auto jUsername = j.find("username");
+    auto jCsrf = j.find("csrf_token");
+
+    // Verify existence
+    if (jUid == j.end() || jToken == j.end() || jUsername == j.end() ||
+        jCsrf == j.end()) {
+      return false;
+    }
+
+    // Verify types
+    if (!jUid->is_string() || !jToken->is_string() || !jUsername->is_string() ||
+        !jCsrf->is_string()) {
+      return false;
+    }
+
+    unique_id = jUid->get<std::string>();
+    session_token = jToken->get<std::string>();
+    username = jUsername->get<std::string>();
+    csrf_token = jCsrf->get<std::string>();
+
+    // For now, sessions that were persisted through a reboot get their timer
+    // reset.  This could probably be overcome with a better understanding of
+    // wall clock time and steady timer time, possibly persisting values with
+    // wall clock time instead of steady timer, but the tradeoffs of all the
+    // corner cases involved are non-trivial, so this is done temporarily
+    last_updated = std::chrono::steady_clock::now();
+    persistence = PersistenceType::TIMEOUT;
+
+    return true;
+  }
 };
 
 void to_json(nlohmann::json& j, const UserSession& p) {
@@ -39,23 +82,6 @@
   }
 }
 
-void from_json(const nlohmann::json& j, UserSession& p) {
-  try {
-    p.unique_id = j.at("unique_id").get<std::string>();
-    p.session_token = j.at("session_token").get<std::string>();
-    p.username = j.at("username").get<std::string>();
-    p.csrf_token = j.at("csrf_token").get<std::string>();
-    // For now, sessions that were persisted through a reboot get their timer
-    // reset.  This could probably be overcome with a better understanding of
-    // wall clock time and steady timer time, possibly persisting values with
-    // wall clock time instead of steady timer, but the tradeoffs of all the
-    // corner cases involved are non-trivial, so this is done temporarily
-    p.last_updated = std::chrono::steady_clock::now();
-  } catch (std::out_of_range) {
-    // do nothing.  Session API incompatibility, leave sessions empty
-  }
-}
-
 class Middleware;
 
 class SessionStore {