phosphor-ldap-conf: implement restore and add error handling

Upon startup, restore D-Bus properties from LDAP config file if
it exists.

Change-Id: I63b5a41eec8937ddbd5e8b4471936376602b6b0e
Signed-off-by: Nagaraju Goruganti <ngorugan@in.ibm.com>
diff --git a/phosphor-ldap-config/ldap_configuration.cpp b/phosphor-ldap-config/ldap_configuration.cpp
index 06a4d5d..6680541 100644
--- a/phosphor-ldap-config/ldap_configuration.cpp
+++ b/phosphor-ldap-config/ldap_configuration.cpp
@@ -1,7 +1,5 @@
-#include <phosphor-logging/elog.hpp>
-#include <phosphor-logging/elog-errors.hpp>
 #include "ldap_configuration.hpp"
-#include "config.h"
+#include <experimental/filesystem>
 #include <fstream>
 #include <sstream>
 
@@ -11,6 +9,15 @@
 {
 constexpr auto nslcdService = "nslcd.service";
 
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+namespace fs = std::experimental::filesystem;
+
+using Line = std::string;
+using Key = std::string;
+using Val = std::string;
+using ConfigInfo = std::map<Key, Val>;
+
 Config::Config(sdbusplus::bus::bus& bus, const char* path, const char* filePath,
                bool secureLDAP, std::string lDAPServerURI,
                std::string lDAPBindDN, std::string lDAPBaseDN,
@@ -35,7 +42,6 @@
 
 void Config::writeConfig()
 {
-    std::fstream stream(configFilePath.c_str(), std::fstream::out);
     std::stringstream confData;
     confData << "uid root\n";
     confData << "gid root\n\n";
@@ -97,117 +103,221 @@
         confData << "map passwd uid cn\n";
         confData << "map passwd gecos displayName\n";
     }
-    stream << confData.str();
-    stream.flush();
-    stream.close();
+    try
+    {
+        std::fstream stream(configFilePath.c_str(), std::fstream::out);
+        stream << confData.str();
+        stream.flush();
+        stream.close();
+    }
+    catch (const std::exception& e)
+    {
+        log<level::ERR>(e.what());
+        elog<InternalFailure>();
+    }
     return;
 }
 
 bool Config::secureLDAP(bool value)
 {
-    if (value == secureLDAP())
+    bool val = false;
+    try
     {
-        return value;
-    }
+        if (value == secureLDAP())
+        {
+            return value;
+        }
 
-    auto val = ConfigIface::secureLDAP(value);
-    writeConfig();
-    parent.restartService(nslcdService);
+        val = ConfigIface::secureLDAP(value);
+        writeConfig();
+        parent.restartService(nslcdService);
+    }
+    catch (const InternalFailure& e)
+    {
+        throw;
+    }
+    catch (const std::exception& e)
+    {
+        log<level::ERR>(e.what());
+        elog<InternalFailure>();
+    }
 
     return val;
 }
 
 std::string Config::lDAPServerURI(std::string value)
 {
-    if (value == lDAPServerURI())
+    std::string val;
+    try
     {
-        return value;
-    }
+        if (value == lDAPServerURI())
+        {
+            return value;
+        }
 
-    auto val = ConfigIface::lDAPServerURI(value);
-    writeConfig();
-    parent.restartService(nslcdService);
+        val = ConfigIface::lDAPServerURI(value);
+        writeConfig();
+        parent.restartService(nslcdService);
+    }
+    catch (const InternalFailure& e)
+    {
+        throw;
+    }
+    catch (const std::exception& e)
+    {
+        log<level::ERR>(e.what());
+        elog<InternalFailure>();
+    }
 
     return val;
 }
 
 std::string Config::lDAPBindDN(std::string value)
 {
-    if (value == lDAPBindDN())
+    std::string val;
+    try
     {
-        return value;
+        if (value == lDAPBindDN())
+        {
+            return value;
+        }
+
+        val = ConfigIface::lDAPBindDN(value);
+        writeConfig();
+        parent.restartService(nslcdService);
     }
-
-    auto val = ConfigIface::lDAPBindDN(value);
-    writeConfig();
-    parent.restartService(nslcdService);
-
+    catch (const InternalFailure& e)
+    {
+        throw;
+    }
+    catch (const std::exception& e)
+    {
+        log<level::ERR>(e.what());
+        elog<InternalFailure>();
+    }
     return val;
 }
 
 std::string Config::lDAPBaseDN(std::string value)
 {
-    if (value == lDAPBaseDN())
+    std::string val;
+    try
     {
-        return value;
+        if (value == lDAPBaseDN())
+        {
+            return value;
+        }
+
+        val = ConfigIface::lDAPBaseDN(value);
+        writeConfig();
+        parent.restartService(nslcdService);
     }
-
-    auto val = ConfigIface::lDAPBaseDN(value);
-    writeConfig();
-    parent.restartService(nslcdService);
-
+    catch (const InternalFailure& e)
+    {
+        throw;
+    }
+    catch (const std::exception& e)
+    {
+        log<level::ERR>(e.what());
+        elog<InternalFailure>();
+    }
     return val;
 }
 
 std::string Config::lDAPBINDDNpassword(std::string value)
 {
-    if (value == lDAPBINDDNpassword())
+    std::string val;
+    try
     {
-        return value;
+        if (value == lDAPBINDDNpassword())
+        {
+            return value;
+        }
+
+        val = ConfigIface::lDAPBINDDNpassword(value);
+        writeConfig();
+        parent.restartService(nslcdService);
     }
-
-    auto val = ConfigIface::lDAPBINDDNpassword(value);
-    writeConfig();
-    parent.restartService(nslcdService);
-
+    catch (const InternalFailure& e)
+    {
+        throw;
+    }
+    catch (const std::exception& e)
+    {
+        log<level::ERR>(e.what());
+        elog<InternalFailure>();
+    }
     return val;
 }
 
 ldap_base::Config::SearchScope
     Config::lDAPSearchScope(ldap_base::Config::SearchScope value)
 {
-    if (value == lDAPSearchScope())
+    ldap_base::Config::SearchScope val;
+    try
     {
-        return value;
+        if (value == lDAPSearchScope())
+        {
+            return value;
+        }
+
+        val = ConfigIface::lDAPSearchScope(value);
+        writeConfig();
+        parent.restartService(nslcdService);
     }
-
-    auto val = ConfigIface::lDAPSearchScope(value);
-    writeConfig();
-    parent.restartService(nslcdService);
-
+    catch (const InternalFailure& e)
+    {
+        throw;
+    }
+    catch (const std::exception& e)
+    {
+        log<level::ERR>(e.what());
+        elog<InternalFailure>();
+    }
     return val;
 }
 
 ldap_base::Config::Type Config::lDAPType(ldap_base::Config::Type value)
 {
-    if (value == lDAPType())
+    ldap_base::Config::Type val;
+    try
     {
-        return value;
+        if (value == lDAPType())
+        {
+            return value;
+        }
+
+        val = ConfigIface::lDAPType(value);
+        writeConfig();
+        parent.restartService(nslcdService);
     }
-
-    auto val = ConfigIface::lDAPType(value);
-    writeConfig();
-    parent.restartService(nslcdService);
-
+    catch (const InternalFailure& e)
+    {
+        throw;
+    }
+    catch (const std::exception& e)
+    {
+        log<level::ERR>(e.what());
+        elog<InternalFailure>();
+    }
     return val;
 }
 
 void ConfigMgr::restartService(const std::string& service)
 {
-    auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
-                                      SYSTEMD_INTERFACE, "RestartUnit");
-    method.append(service.c_str(), "replace");
-    bus.call_noreply(method);
+    try
+    {
+        auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
+                                          SYSTEMD_INTERFACE, "RestartUnit");
+        method.append(service.c_str(), "replace");
+        bus.call_noreply(method);
+    }
+    catch (const sdbusplus::exception::SdBusError& ex)
+    {
+        log<level::ERR>("Failed to restart nslcd service",
+                        entry("ERR=%s", ex.what()));
+        elog<InternalFailure>();
+    }
 }
 
 std::string
@@ -217,6 +327,7 @@
                             ldap_base::Create::SearchScope lDAPSearchScope,
                             ldap_base::Create::Type lDAPType)
 {
+    // TODO Validate parameters passed-in.
     // With current implementation we support only one LDAP server.
     configPtr.reset(nullptr);
 
@@ -230,5 +341,137 @@
     return objPath;
 }
 
+void ConfigMgr::restore(const char* filePath)
+{
+    if (!fs::exists(filePath))
+    {
+        log<level::ERR>("Config file doesn't exists",
+                        entry("LDAP_CONFIG_FILE=%s", LDAP_CONFIG_FILE));
+        return;
+    }
+
+    ConfigInfo configValues;
+
+    try
+    {
+        std::fstream stream(filePath, std::fstream::in);
+        Line line;
+        // read characters from stream and places them into line
+        while (std::getline(stream, line))
+        {
+            // remove leading and trailing extra spaces
+            auto firstScan = line.find_first_not_of(' ');
+            auto first =
+                (firstScan == std::string::npos ? line.length() : firstScan);
+            auto last = line.find_last_not_of(' ');
+            line = line.substr(first, last - first + 1);
+            // reduce multiple spaces between two words to a single space
+            auto pred = [](char a, char b) {
+                return (a == b && a == ' ') ? true : false;
+            };
+
+            auto lastPos = std::unique(line.begin(), line.end(), pred);
+
+            line.erase(lastPos, line.end());
+
+            // Ignore if line is empty or starts with '#'
+            if (line.empty() || line.at(0) == '#')
+            {
+                continue;
+            }
+
+            Key key;
+            std::istringstream isLine(line);
+            // extract characters from isLine and stores them into
+            // key until the delimitation character ' ' is found.
+            // If the delimiter is found, it is extracted and discarded
+            // the next input operation will begin after it.
+            if (std::getline(isLine, key, ' '))
+            {
+                Val value;
+                // extract characters after delimitation character ' '
+                if (std::getline(isLine, value, ' '))
+                {
+                    // skip line if it starts with "base shadow" or
+                    // "base passwd" because we would have 3 entries
+                    // ("base lDAPBaseDN" , "base passwd lDAPBaseDN" and
+                    // "base shadow lDAPBaseDN") for the property "lDAPBaseDN",
+                    // one is enough to restore it.
+
+                    if ((key == "base") &&
+                        (value == "passwd" || value == "shadow"))
+                    {
+                        continue;
+                    }
+                    // skip the line if it starts with "map passwd".
+                    // if config type is AD "map group" entry would be add to
+                    // the map configValues. For OpenLdap config file no map
+                    // entry would be there.
+                    if ((key == "map") && (value == "passwd"))
+                    {
+                        continue;
+                    }
+                    configValues[key] = value;
+                }
+            }
+        }
+
+        // extract properties from configValues map
+        bool secureLDAP;
+        if (configValues["ssl"] == "on")
+        {
+            secureLDAP = true;
+        }
+        else
+        {
+            secureLDAP = false;
+        }
+
+        ldap_base::Create::SearchScope lDAPSearchScope;
+        if (configValues["scope"] == "sub")
+        {
+            lDAPSearchScope = ldap_base::Create::SearchScope::sub;
+        }
+        else if (configValues["scope"] == "one")
+        {
+            lDAPSearchScope = ldap_base::Create::SearchScope::one;
+        }
+        else
+        {
+            lDAPSearchScope = ldap_base::Create::SearchScope::base;
+        }
+
+        ldap_base::Create::Type lDAPType;
+        // If the file is having a line which starts with "map group"
+        if (configValues["map"] == "group")
+        {
+            lDAPType = ldap_base::Create::Type::ActiveDirectory;
+        }
+        else
+        {
+            lDAPType = ldap_base::Create::Type::OpenLdap;
+        }
+
+        createConfig(
+            secureLDAP, std::move(configValues["uri"]),
+            std::move(configValues["binddn"]), std::move(configValues["base"]),
+            std::move(configValues["bindpw"]), lDAPSearchScope, lDAPType);
+    }
+    catch (const InvalidArgument& e)
+    {
+        // Don't throw - we don't want to create a D-Bus
+        // object upon finding empty values in config, as
+        // this can be a default config.
+    }
+    catch (const InternalFailure& e)
+    {
+        throw;
+    }
+    catch (const std::exception& e)
+    {
+        log<level::ERR>(e.what());
+        elog<InternalFailure>();
+    }
+}
 } // namespace ldap
 } // namespace phosphor
diff --git a/phosphor-ldap-config/ldap_configuration.hpp b/phosphor-ldap-config/ldap_configuration.hpp
index f2bf02a..a0324c1 100644
--- a/phosphor-ldap-config/ldap_configuration.hpp
+++ b/phosphor-ldap-config/ldap_configuration.hpp
@@ -1,9 +1,14 @@
 #pragma once
 
-#include <sdbusplus/bus.hpp>
-#include <sdbusplus/server/object.hpp>
+#include "config.h"
 #include <xyz/openbmc_project/User/Ldap/Config/server.hpp>
 #include <xyz/openbmc_project/User/Ldap/Create/server.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+#include <phosphor-logging/log.hpp>
+#include <phosphor-logging/elog.hpp>
+#include <phosphor-logging/elog-errors.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server/object.hpp>
 #include <string>
 
 namespace phosphor
@@ -15,6 +20,8 @@
 static constexpr auto LDAPNsSwitchFile = "/etc/nsswitch_ldap.conf";
 static constexpr auto linuxNsSwitchFile = "/etc/nsswitch_linux.conf";
 
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
 namespace ldap_base = sdbusplus::xyz::openbmc_project::User::Ldap::server;
 using ConfigIface = sdbusplus::server::object::object<ldap_base::Config>;
 using CreateIface = sdbusplus::server::object::object<ldap_base::Create>;
@@ -144,9 +151,19 @@
      *  @param[in] filePath - LDAP configuration file.
      */
     ConfigMgr(sdbusplus::bus::bus& bus, const char* path) :
-        CreateIface(bus, path), bus(bus)
+        CreateIface(bus, path, true), bus(bus)
     {
-        // TODO  restore config object if config file exists.
+        try
+        {
+            restore(LDAP_CONFIG_FILE);
+            emit_object_added();
+        }
+        catch (const std::exception& e)
+        {
+            configPtr.reset(nullptr);
+            log<level::ERR>(e.what());
+            elog<InternalFailure>();
+        }
     }
 
     /** @brief concrete implementation of the pure virtual funtion
@@ -179,6 +196,11 @@
 
     /** @brief Pointer to a Config D-Bus object */
     std::unique_ptr<Config> configPtr = nullptr;
+
+    /** @brief Populate existing config into D-Bus properties
+     *  @param[in] filePath - LDAP config file path
+     */
+    virtual void restore(const char* filePath);
 };
 } // namespace ldap
 } // namespace phosphor