Serialize the config objects

This commit serializes the config object into cereal
path and restores the config object when the phosphor-ldap-conf
restarts.

TestedBy: Unit tested
          Serialize the object
          Restart the phosphor-ldap-conf restores the object.
          Ldap/Local authentication works fine.

Signed-off-by: Ratan Gupta <ratagupt@linux.vnet.ibm.com>
Change-Id: Ie6e940ddd6851085dc4213677dfb20e3afa0964f
diff --git a/phosphor-ldap-config/Makefile.am b/phosphor-ldap-config/Makefile.am
index 7080ac5..3052b6d 100644
--- a/phosphor-ldap-config/Makefile.am
+++ b/phosphor-ldap-config/Makefile.am
@@ -9,8 +9,7 @@
                 main.cpp \
                 utils.cpp \
                 ldap_config.cpp \
-                ldap_config_mgr.cpp \
-                ldap_config_serialize.cpp
+                ldap_config_mgr.cpp
 
 phosphor_ldap_conf_LDFLAGS = $(SDBUSPLUS_LIBS) \
                              $(PHOSPHOR_DBUS_INTERFACES_LIBS) \
diff --git a/phosphor-ldap-config/ldap_config.cpp b/phosphor-ldap-config/ldap_config.cpp
index 746f324..126cc46 100644
--- a/phosphor-ldap-config/ldap_config.cpp
+++ b/phosphor-ldap-config/ldap_config.cpp
@@ -1,11 +1,19 @@
 #include "ldap_config_mgr.hpp"
 #include "ldap_config.hpp"
-#include "ldap_config_serialize.hpp"
 #include "utils.hpp"
+
+#include <cereal/types/string.hpp>
+#include <cereal/types/vector.hpp>
+#include <cereal/archives/binary.hpp>
 #include <filesystem>
 #include <fstream>
 #include <sstream>
 
+// Register class version
+// From cereal documentation;
+// "This macro should be placed at global scope"
+CEREAL_CLASS_VERSION(phosphor::ldap::Config, CLASS_VERSION);
+
 namespace phosphor
 {
 namespace ldap
@@ -38,8 +46,8 @@
                ConfigMgr& parent) :
     Ifaces(bus, path, true),
     secureLDAP(secureLDAP), lDAPBindPassword(std::move(lDAPBindDNPassword)),
-    configFilePath(filePath), tlsCacertFile(caCertFile), bus(bus),
-    parent(parent)
+    tlsCacertFile(caCertFile), configFilePath(filePath), objectPath(path),
+    bus(bus), parent(parent)
 {
     ConfigIface::lDAPServerURI(lDAPServerURI);
     ConfigIface::lDAPBindDN(lDAPBindDN);
@@ -49,16 +57,57 @@
     EnableIface::enabled(lDAPServiceEnabled);
     ConfigIface::userNameAttribute(userNameAttr);
     ConfigIface::groupNameAttribute(groupNameAttr);
-    // Don't update the bindDN password under ConfigIface::
+    // NOTE: Don't update the bindDN password under ConfigIface
     if (enabled())
     {
         writeConfig();
     }
+    // save the config.
+    configPersistPath = parent.dbusPersistentPath;
+    configPersistPath += objectPath;
+
+    // create the persistent directory
+    fs::create_directories(configPersistPath);
+
+    configPersistPath += "/config";
+
+    std::ofstream os(configPersistPath, std::ios::binary | std::ios::out);
+    // remove the read permission from others
+    auto permission =
+        fs::perms::owner_read | fs::perms::owner_write | fs::perms::group_read;
+    fs::permissions(configPersistPath, permission);
+
+    serialize();
+
     // Emit deferred signal.
     this->emit_object_added();
     parent.startOrStopService(nslcdService, enabled());
 }
 
+Config::Config(sdbusplus::bus::bus& bus, const char* path, const char* filePath,
+               const char* caCertFile, ConfigIface::Type lDAPType,
+               ConfigMgr& parent) :
+    Ifaces(bus, path, true),
+    tlsCacertFile(caCertFile), configFilePath(filePath), objectPath(path),
+    bus(bus), parent(parent)
+{
+    ConfigIface::lDAPType(lDAPType);
+
+    configPersistPath = parent.dbusPersistentPath;
+    configPersistPath += objectPath;
+
+    // create the persistent directory
+    fs::create_directories(configPersistPath);
+
+    configPersistPath += "/config";
+
+    std::ofstream os(configPersistPath, std::ios::binary | std::ios::out);
+    // remove the read permission from others
+    auto permission =
+        fs::perms::owner_read | fs::perms::owner_write | fs::perms::group_read;
+    fs::permissions(configPersistPath, permission);
+}
+
 void Config::writeConfig()
 {
     std::stringstream confData;
@@ -195,6 +244,7 @@
             writeConfig();
             parent.startOrStopService(nslcdService, enabled());
         }
+        serialize();
     }
     catch (const InternalFailure& e)
     {
@@ -245,6 +295,8 @@
             writeConfig();
             parent.startOrStopService(nslcdService, enabled());
         }
+        // save the object.
+        serialize();
     }
     catch (const InternalFailure& e)
     {
@@ -290,6 +342,8 @@
             writeConfig();
             parent.startOrStopService(nslcdService, enabled());
         }
+        // save the object.
+        serialize();
     }
     catch (const InternalFailure& e)
     {
@@ -331,6 +385,8 @@
             writeConfig();
             parent.startOrStopService(nslcdService, enabled());
         }
+        // save the object.
+        serialize();
     }
     catch (const InternalFailure& e)
     {
@@ -365,6 +421,8 @@
 
             parent.startOrStopService(nslcdService, enabled());
         }
+        // save the object.
+        serialize();
     }
     catch (const InternalFailure& e)
     {
@@ -401,8 +459,8 @@
         // TODO in later commit, one of the config would be active
         // at any moment of time.
         parent.startOrStopService(nslcdService, value);
-        // save the enabled property.
-        serialize(*this, parent.dbusPersistentPath);
+        // save the object.
+        serialize();
     }
     catch (const InternalFailure& e)
     {
@@ -433,6 +491,8 @@
 
             parent.startOrStopService(nslcdService, enabled());
         }
+        // save the object.
+        serialize();
     }
     catch (const InternalFailure& e)
     {
@@ -463,6 +523,8 @@
 
             parent.startOrStopService(nslcdService, enabled());
         }
+        // save the object.
+        serialize();
     }
     catch (const InternalFailure& e)
     {
@@ -476,5 +538,86 @@
     return val;
 }
 
+template <class Archive>
+void Config::save(Archive& archive, const std::uint32_t version) const
+{
+    archive(this->enabled());
+    archive(lDAPServerURI());
+    archive(lDAPBindDN());
+    archive(lDAPBaseDN());
+    archive(lDAPSearchScope());
+    archive(lDAPBindPassword);
+    archive(userNameAttribute());
+    archive(groupNameAttribute());
+}
+
+template <class Archive>
+void Config::load(Archive& archive, const std::uint32_t version)
+{
+
+    bool bVal;
+    archive(bVal);
+    EnableIface::enabled(bVal);
+
+    std::string str;
+    archive(str);
+    ConfigIface::lDAPServerURI(str);
+
+    archive(str);
+    ConfigIface::lDAPBindDN(str);
+
+    archive(str);
+    ConfigIface::lDAPBaseDN(str);
+
+    ConfigIface::SearchScope scope;
+    archive(scope);
+    ConfigIface::lDAPSearchScope(scope);
+
+    archive(str);
+    lDAPBindPassword = str;
+
+    archive(str);
+    ConfigIface::userNameAttribute(str);
+
+    archive(str);
+    ConfigIface::groupNameAttribute(str);
+}
+
+void Config::serialize()
+{
+    std::ofstream os(configPersistPath.string(),
+                     std::ios::binary | std::ios::out);
+    cereal::BinaryOutputArchive oarchive(os);
+    oarchive(*this);
+    return;
+}
+
+bool Config::deserialize()
+{
+    try
+    {
+        if (fs::exists(configPersistPath))
+        {
+            std::ifstream is(configPersistPath.c_str(),
+                             std::ios::in | std::ios::binary);
+            cereal::BinaryInputArchive iarchive(is);
+            iarchive(*this);
+            return true;
+        }
+        return false;
+    }
+    catch (cereal::Exception& e)
+    {
+        log<level::ERR>(e.what());
+        std::error_code ec;
+        fs::remove(configPersistPath, ec);
+        return false;
+    }
+    catch (const fs::filesystem_error& e)
+    {
+        return false;
+    }
+}
+
 } // namespace ldap
 } // namespace phosphor
diff --git a/phosphor-ldap-config/ldap_config.hpp b/phosphor-ldap-config/ldap_config.hpp
index 858d1a3..6eceb50 100644
--- a/phosphor-ldap-config/ldap_config.hpp
+++ b/phosphor-ldap-config/ldap_config.hpp
@@ -11,6 +11,7 @@
 #include <sdbusplus/bus.hpp>
 #include <sdbusplus/server/object.hpp>
 #include <string>
+#include <filesystem>
 
 namespace phosphor
 {
@@ -24,7 +25,7 @@
 using Ifaces = sdbusplus::server::object::object<ConfigIface, EnableIface>;
 using CreateIface = sdbusplus::server::object::object<
     sdbusplus::xyz::openbmc_project::User::Ldap::server::Create>;
-
+namespace fs = std::filesystem;
 class ConfigMgr;
 class MockConfigMgr;
 
@@ -74,6 +75,18 @@
            bool lDAPServiceEnabled, std::string groupNameAttribute,
            std::string userNameAttribute, ConfigMgr& parent);
 
+    /** @brief Constructor to put object onto bus at a D-Bus path.
+     *  @param[in] bus - Bus to attach to.
+     *  @param[in] path - The D-Bus object path to attach at.
+     *  @param[in] filePath - LDAP configuration file.
+     *  @param[in] lDAPType - Specifies the LDAP server type which can be AD
+     *              or openLDAP.
+     *  @param[in] parent - parent of config object.
+     */
+    Config(sdbusplus::bus::bus& bus, const char* path, const char* filePath,
+           const char* caCertFile, ConfigIface::Type lDAPType,
+           ConfigMgr& parent);
+
     using ConfigIface::groupNameAttribute;
     using ConfigIface::lDAPBaseDN;
     using ConfigIface::lDAPBindDN;
@@ -140,12 +153,43 @@
      */
     std::string lDAPBindDNPassword(std::string value) override;
 
-    bool secureLDAP;
+    /** @brief Function required by Cereal to perform deserialization.
+     *  @tparam Archive - Cereal archive type (binary in our case).
+     *  @param[in] archive - reference to Cereal archive.
+     *  @param[in] version - Class version that enables handling
+     *                       a serialized data across code levels
+     */
+    template <class Archive>
+    void load(Archive& archive, const std::uint32_t version);
+
+    /** @brief Function required by Cereal to perform serialization.
+     *  @tparam Archive - Cereal archive type (binary in our case).
+     *  @param[in] archive - reference to Cereal archive.
+     *  @param[in] version - Class version that enables handling
+     *                       a serialized data across code levels
+     */
+    template <class Archive>
+    void save(Archive& archive, const std::uint32_t version) const;
+
+    /** @brief Serialize and persist this object at the persist
+     *         location.
+     */
+    void serialize();
+
+    /** @brief Deserialize LDAP config data from the persistent location
+     *         into this object
+     *  @return bool - true if the deserialization was successful, false
+     *                 otherwise.
+     */
+    bool deserialize();
 
   private:
+    bool secureLDAP;
     std::string lDAPBindPassword{};
-    std::string configFilePath{};
     std::string tlsCacertFile{};
+    std::string configFilePath{};
+    std::string objectPath{};
+    std::filesystem::path configPersistPath{};
 
     /** @brief Persistent sdbusplus D-Bus bus connection. */
     sdbusplus::bus::bus& bus;
diff --git a/phosphor-ldap-config/ldap_config_mgr.cpp b/phosphor-ldap-config/ldap_config_mgr.cpp
index a60e8f8..65d4bda 100644
--- a/phosphor-ldap-config/ldap_config_mgr.cpp
+++ b/phosphor-ldap-config/ldap_config_mgr.cpp
@@ -1,6 +1,5 @@
 #include "ldap_config_mgr.hpp"
 #include "ldap_config.hpp"
-#include "ldap_config_serialize.hpp"
 
 #include "utils.hpp"
 #include <filesystem>
@@ -73,12 +72,6 @@
     }
 }
 
-void ConfigMgr::deleteObject()
-{
-    // TODO Not needed the delete functionality.
-    // will do in later commit
-}
-
 std::string ConfigMgr::createConfig(
     std::string lDAPServerURI, std::string lDAPBindDN, std::string lDAPBaseDN,
     std::string lDAPBindDNPassword, CreateIface::SearchScope lDAPSearchScope,
@@ -167,17 +160,31 @@
     {
         openLDAPConfigPtr = std::make_unique<Config>(
             bus, openLDAPDbusObjectPath.c_str(), configFilePath.c_str(),
-            tlsCacertFile.c_str(), false, "", "", "", "",
-            ConfigIface::SearchScope::sub, ConfigIface::Type::OpenLdap, false,
-            "", "", *this);
+            tlsCacertFile.c_str(), ConfigIface::Type::OpenLdap, *this);
+        openLDAPConfigPtr->emit_object_added();
     }
     if (!ADConfigPtr)
     {
         ADConfigPtr = std::make_unique<Config>(
             bus, ADDbusObjectPath.c_str(), configFilePath.c_str(),
-            tlsCacertFile.c_str(), false, "", "", "", "",
-            ConfigIface::SearchScope::sub, ConfigIface::Type::ActiveDirectory,
-            false, "", "", *this);
+            tlsCacertFile.c_str(), ConfigIface::Type::ActiveDirectory, *this);
+        ADConfigPtr->emit_object_added();
+    }
+}
+
+void ConfigMgr::restore()
+{
+    createDefaultObjects();
+    // Restore the ldap config and their mappings
+    if (ADConfigPtr->deserialize())
+    {
+        // Restore the role mappings in later commit
+        ADConfigPtr->emit_object_added();
+    }
+    if (openLDAPConfigPtr->deserialize())
+    {
+        // Restore the role mappings in later commit
+        openLDAPConfigPtr->emit_object_added();
     }
 }
 
diff --git a/phosphor-ldap-config/ldap_config_mgr.hpp b/phosphor-ldap-config/ldap_config_mgr.hpp
index a3895ae..812528b 100644
--- a/phosphor-ldap-config/ldap_config_mgr.hpp
+++ b/phosphor-ldap-config/ldap_config_mgr.hpp
@@ -99,13 +99,9 @@
      */
     virtual void startOrStopService(const std::string& service, bool value);
 
-    /** @brief delete the config D-Bus object.
+    /** @brief Populate existing config into D-Bus properties
      */
-    void deleteObject();
-
-    /* Create the default active directory and the openldap config
-     * objects. */
-    virtual void createDefaultObjects();
+    virtual void restore();
 
     /* ldap service enabled property would be saved under
      * this path.
@@ -130,6 +126,10 @@
     std::unique_ptr<Config> openLDAPConfigPtr = nullptr;
     /** @brief Pointer to a AD Config D-Bus object */
     std::unique_ptr<Config> ADConfigPtr = nullptr;
+
+    /* Create the default active directory and the openldap config
+     * objects. */
+    virtual void createDefaultObjects();
 };
 } // namespace ldap
 } // namespace phosphor
diff --git a/phosphor-ldap-config/ldap_config_serialize.cpp b/phosphor-ldap-config/ldap_config_serialize.cpp
deleted file mode 100644
index 6c80a74..0000000
--- a/phosphor-ldap-config/ldap_config_serialize.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-#include <cereal/types/string.hpp>
-#include <cereal/types/vector.hpp>
-#include <cereal/archives/binary.hpp>
-#include <fstream>
-
-#include "ldap_config_serialize.hpp"
-#include "ldap_config.hpp"
-#include <phosphor-logging/log.hpp>
-#include "config.h"
-
-// Register class version
-// From cereal documentation;
-// "This macro should be placed at global scope"
-CEREAL_CLASS_VERSION(phosphor::ldap::Config, CLASS_VERSION);
-
-namespace phosphor
-{
-namespace ldap
-{
-
-using namespace phosphor::logging;
-
-/** @brief Function required by Cereal to perform serialization.
- *  @tparam Archive - Cereal archive type (binary in our case).
- *  @param[in] archive - reference to Cereal archive.
- *  @param[in] config - const reference to ldap config.
- *  @param[in] version - Class version that enables handling
- *                       a serialized data across code levels
- */
-template <class Archive>
-void save(Archive& archive, const Config& config, const std::uint32_t version)
-{
-    archive(config.enabled());
-}
-
-/** @brief Function required by Cereal to perform deserialization.
- *  @tparam Archive - Cereal archive type (binary in our case).
- *  @param[in] archive - reference to Cereal archive.
- *  @param[in] config -  reference of ldap config object.
- *  @param[in] version - Class version that enables handling
- *                       a serialized data across code levels
- */
-template <class Archive>
-void load(Archive& archive, Config& config, const std::uint32_t version)
-{
-    bool enabled = false;
-    archive(enabled);
-    config.enabled(enabled);
-}
-
-fs::path serialize(const Config& config, const fs::path& path)
-{
-    fs::create_directories(path.parent_path());
-
-    std::ofstream os(path.string(), std::ios::binary);
-    cereal::BinaryOutputArchive oarchive(os);
-    oarchive(config);
-    return path;
-}
-
-bool deserialize(const fs::path& path, Config& config)
-{
-    try
-    {
-        if (fs::exists(path))
-        {
-            std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
-            cereal::BinaryInputArchive iarchive(is);
-            iarchive(config);
-            return true;
-        }
-        return false;
-    }
-    catch (cereal::Exception& e)
-    {
-        log<level::ERR>(e.what());
-        std::error_code ec;
-        fs::remove(path, ec);
-        return false;
-    }
-    catch (const fs::filesystem_error& e)
-    {
-        return false;
-    }
-}
-
-} // namespace ldap
-} // namespace phosphor
diff --git a/phosphor-ldap-config/ldap_config_serialize.hpp b/phosphor-ldap-config/ldap_config_serialize.hpp
deleted file mode 100644
index b20ccd4..0000000
--- a/phosphor-ldap-config/ldap_config_serialize.hpp
+++ /dev/null
@@ -1,29 +0,0 @@
-#pragma once
-
-#include <filesystem>
-#include "ldap_config.hpp"
-
-namespace phosphor
-{
-namespace ldap
-{
-
-namespace fs = std::filesystem;
-
-/** @brief Serialize and persist LDAP service status property.
- *  @param[in] config - const reference to LDAP config object.
- *  @param[in] path -  path of persistent location where D-Bus property would be
- *                     saved.
- *  @return fs::path - pathname of persisted LDAP Config file.
- */
-fs::path serialize(const Config& config, const fs::path& path);
-
-/** @brief Deserialize LDAP service status into a D-Bus object
- *  @param[in] path - pathname of persisted LDAP Config file.
- *  @param[in] config - reference of the object which needs to be deserialized.
- *  @return bool - true if the deserialization was successful, false otherwise.
- */
-bool deserialize(const fs::path& path, Config& config);
-
-} // namespace ldap
-} // namespace phosphor
diff --git a/phosphor-ldap-config/main.cpp b/phosphor-ldap-config/main.cpp
index e0ac638..3a2d5f5 100644
--- a/phosphor-ldap-config/main.cpp
+++ b/phosphor-ldap-config/main.cpp
@@ -27,7 +27,7 @@
 
     phosphor::ldap::ConfigMgr mgr(bus, LDAP_CONFIG_ROOT, LDAP_CONFIG_FILE,
                                   LDAP_CONF_PERSIST_PATH, TLS_CACERT_FILE);
-    mgr.createDefaultObjects();
+    mgr.restore();
 
     bus.request_name(LDAP_CONFIG_BUSNAME);