json_serializer: handled corrupted files

When the `/var/lib/usr_mgr.conf` file is either empty or corrupted
JSON, the daemon will crash and not recover.  Handle this by catching
JSON load exceptions and deleting the corrupted file.

Fixes openbmc/phosphor-user-manager#19.

Tested: Added additional test cases to cover the corruption case and
update the test case to cover the non-throwing behavior.

Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
Change-Id: I2be787771ea3d72af924615a6eee17cf2f393e9a
diff --git a/json_serializer.hpp b/json_serializer.hpp
index 81a6a4d..8882cc5 100644
--- a/json_serializer.hpp
+++ b/json_serializer.hpp
@@ -105,19 +105,35 @@
             return false;
         }
     }
-    void load()
+    bool load()
     {
         std::ifstream file(mfaConfPath.data());
 
         if (file.is_open())
         {
-            file >> jsonData;
-            file.close();
+            try
+            {
+                file >> jsonData;
+                file.close();
+                return true;
+            }
+            catch (const nlohmann::json::parse_error& e)
+            {
+                lg2::error("JSON parsing error: {MSG} in file {FILENAME}",
+                           "MSG", e.what(), "FILENAME", mfaConfPath);
+                if (std::filesystem::exists(mfaConfPath))
+                {
+                    std::filesystem::remove(mfaConfPath);
+                }
+                file.close();
+                return false;
+            }
         }
         else
         {
             lg2::error("Unable to open file for reading {FILENAME}", "FILENAME",
                        mfaConfPath);
+            return false;
         }
     }
 
diff --git a/test/json_serializer_test.cpp b/test/json_serializer_test.cpp
index 50ac53f..4465918 100644
--- a/test/json_serializer_test.cpp
+++ b/test/json_serializer_test.cpp
@@ -108,8 +108,8 @@
     ofs.close();
 
     JsonSerializer s(test_file);
-    // nlohmann::json will throw a parse error for empty files or invalid JSON
-    EXPECT_THROW(s.load(), nlohmann::json::parse_error);
+    EXPECT_FALSE(s.load());
+    EXPECT_FALSE(std::filesystem::exists(test_file));
 }
 
 TEST_F(JsonSerializerTest, LoadGarbledJsonFile)
@@ -120,6 +120,6 @@
     ofs.close();
 
     JsonSerializer s(test_file);
-    // nlohmann::json will throw a parse error for incomplete JSON
-    EXPECT_THROW(s.load(), nlohmann::json::parse_error);
+    EXPECT_FALSE(s.load());
+    EXPECT_FALSE(std::filesystem::exists(test_file));
 }
diff --git a/user_mgr.cpp b/user_mgr.cpp
index 2aa15e8..fa363b4 100644
--- a/user_mgr.cpp
+++ b/user_mgr.cpp
@@ -1509,9 +1509,8 @@
 void UserMgr::load()
 {
     std::optional<std::string> authTypeStr;
-    if (std::filesystem::exists(mfaConfPath))
+    if (std::filesystem::exists(mfaConfPath) && serializer.load())
     {
-        serializer.load();
         serializer.deserialize("authtype", authTypeStr);
     }
     auto authType =