Create role mapping under ldap config object

Each ldap config object should be have its own
mapping object.

This is to align with the redfish.
https://redfish.dmtf.org/schemas/AccountService.v1_4_0.json

As per redfish, Each config will have it's own
"RemoteRoleMapping".

Mapping object should be persisted and restores
when the phosphor-ldap-conf restarts.

TestedBy:
          Unit Tested.
          Creation of privilege mapping.
          Persist the priv-mapping.
          Restores the priv-mapping.

Signed-off-by: Ratan Gupta <ratagupt@linux.vnet.ibm.com>
Change-Id: I5ab4aeffae61f9cc57c1338f94784d0fe5607cd3
diff --git a/phosphor-ldap-config/Makefile.am b/phosphor-ldap-config/Makefile.am
index 3052b6d..d6d8454 100644
--- a/phosphor-ldap-config/Makefile.am
+++ b/phosphor-ldap-config/Makefile.am
@@ -1,15 +1,18 @@
 bin_PROGRAMS = phosphor-ldap-conf
 
-noinst_HEADERS = \
-            ldap_config.hpp \
+noinst_HEADERS = ldap_config.hpp \
             ldap_config_mgr.hpp \
+            ldap_mapper_entry.hpp \
+            ldap_mapper_serialize.hpp \
             utils.hpp
 
 phosphor_ldap_conf_SOURCES = \
                 main.cpp \
                 utils.cpp \
                 ldap_config.cpp \
-                ldap_config_mgr.cpp
+                ldap_config_mgr.cpp \
+                ldap_mapper_entry.cpp \
+                ldap_mapper_serialize.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 03c6ffc..b22d684 100644
--- a/phosphor-ldap-config/ldap_config.cpp
+++ b/phosphor-ldap-config/ldap_config.cpp
@@ -5,6 +5,10 @@
 #include <cereal/types/string.hpp>
 #include <cereal/types/vector.hpp>
 #include <cereal/archives/binary.hpp>
+#include "ldap_mapper_serialize.hpp"
+
+#include <xyz/openbmc_project/Common/error.hpp>
+#include <xyz/openbmc_project/User/Common/error.hpp>
 #include <filesystem>
 #include <fstream>
 #include <sstream>
@@ -27,9 +31,12 @@
 using namespace phosphor::logging;
 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
 namespace fs = std::filesystem;
+
 using Argument = xyz::openbmc_project::Common::InvalidArgument;
 using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
 using NotAllowedArgument = xyz::openbmc_project::Common::NotAllowed;
+using PrivilegeMappingExists = sdbusplus::xyz::openbmc_project::User::Common::
+    Error::PrivilegeMappingExists;
 
 using Line = std::string;
 using Key = std::string;
@@ -625,5 +632,114 @@
     }
 }
 
+ObjectPath Config::create(std::string groupName, std::string privilege)
+{
+    checkPrivilegeMapper(groupName);
+    checkPrivilegeLevel(privilege);
+
+    entryId++;
+
+    // Object path for the LDAP group privilege mapper entry
+    fs::path mapperObjectPath = objectPath;
+    mapperObjectPath /= "role_map";
+    mapperObjectPath /= std::to_string(entryId);
+
+    fs::path persistPath = parent.dbusPersistentPath;
+    persistPath += mapperObjectPath;
+
+    // Create mapping for LDAP privilege mapper entry
+    auto entry = std::make_unique<LDAPMapperEntry>(
+        bus, mapperObjectPath.string().c_str(), persistPath.string().c_str(),
+        groupName, privilege, *this);
+
+    phosphor::ldap::serialize(*entry, std::move(persistPath));
+
+    PrivilegeMapperList.emplace(entryId, std::move(entry));
+    return mapperObjectPath.string();
+}
+
+void Config::deletePrivilegeMapper(Id id)
+{
+    fs::path mapperObjectPath = objectPath;
+    mapperObjectPath /= "role_map";
+    mapperObjectPath /= std::to_string(id);
+
+    fs::path persistPath = parent.dbusPersistentPath;
+    persistPath += std::move(mapperObjectPath);
+
+    // Delete the persistent representation of the privilege mapper.
+    fs::remove(std::move(persistPath));
+
+    PrivilegeMapperList.erase(id);
+}
+void Config::checkPrivilegeMapper(const std::string& groupName)
+{
+    if (groupName.empty())
+    {
+        log<level::ERR>("Group name is empty");
+        elog<InvalidArgument>(Argument::ARGUMENT_NAME("Group name"),
+                              Argument::ARGUMENT_VALUE("Null"));
+    }
+
+    for (const auto& val : PrivilegeMapperList)
+    {
+        if (val.second.get()->groupName() == groupName)
+        {
+            log<level::ERR>("Group name already exists");
+            elog<PrivilegeMappingExists>();
+        }
+    }
+}
+
+void Config::checkPrivilegeLevel(const std::string& privilege)
+{
+    if (privilege.empty())
+    {
+        log<level::ERR>("Privilege level is empty");
+        elog<InvalidArgument>(Argument::ARGUMENT_NAME("Privilege level"),
+                              Argument::ARGUMENT_VALUE("Null"));
+    }
+
+    if (std::find(privMgr.begin(), privMgr.end(), privilege) == privMgr.end())
+    {
+        log<level::ERR>("Invalid privilege");
+        elog<InvalidArgument>(Argument::ARGUMENT_NAME("Privilege level"),
+                              Argument::ARGUMENT_VALUE(privilege.c_str()));
+    }
+}
+
+void Config::restoreRoleMapping()
+{
+    namespace fs = std::filesystem;
+    fs::path dir = parent.dbusPersistentPath;
+    dir += objectPath;
+    dir /= "role_map";
+
+    if (!fs::exists(dir) || fs::is_empty(dir))
+    {
+        return;
+    }
+
+    for (auto& file : fs::directory_iterator(dir))
+    {
+        std::string id = file.path().filename().c_str();
+        size_t idNum = std::stol(id);
+
+        auto entryPath = objectPath + '/' + "role_map" + '/' + id;
+        auto persistPath = parent.dbusPersistentPath + entryPath;
+        auto entry = std::make_unique<LDAPMapperEntry>(
+            bus, entryPath.c_str(), persistPath.c_str(), *this);
+        if (phosphor::ldap::deserialize(file.path(), *entry))
+        {
+            entry->Interfaces::emit_object_added();
+            PrivilegeMapperList.emplace(idNum, std::move(entry));
+            if (idNum > entryId)
+            {
+                entryId = idNum;
+            }
+        }
+    }
+}
+
 } // namespace ldap
 } // namespace phosphor
diff --git a/phosphor-ldap-config/ldap_config.hpp b/phosphor-ldap-config/ldap_config.hpp
index 0d2adf1..cbd2e04 100644
--- a/phosphor-ldap-config/ldap_config.hpp
+++ b/phosphor-ldap-config/ldap_config.hpp
@@ -4,14 +4,18 @@
 #include <xyz/openbmc_project/Object/Enable/server.hpp>
 #include <xyz/openbmc_project/User/Ldap/Create/server.hpp>
 #include <xyz/openbmc_project/User/Ldap/Config/server.hpp>
+#include <xyz/openbmc_project/User/PrivilegeMapper/server.hpp>
 #include <xyz/openbmc_project/Common/error.hpp>
+#include "ldap_mapper_entry.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>
+
 #include <filesystem>
+#include <set>
+#include <string>
 
 namespace phosphor
 {
@@ -22,10 +26,16 @@
 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
 using ConfigIface = sdbusplus::xyz::openbmc_project::User::Ldap::server::Config;
 using EnableIface = sdbusplus::xyz::openbmc_project::Object::server::Enable;
-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;
+using MapperIface =
+    sdbusplus::xyz::openbmc_project::User::server::PrivilegeMapper;
+
+using Ifaces =
+    sdbusplus::server::object::object<ConfigIface, EnableIface, MapperIface>;
+using ObjectPath = sdbusplus::message::object_path;
+
 class ConfigMgr;
 class MockConfigMgr;
 
@@ -189,6 +199,49 @@
      */
     bool enableService(bool value);
 
+    /** @brief Creates a mapping for the group to the privilege
+     *
+     *  @param[in] groupName - Group Name to which the privilege needs to be
+     *                         assigned.
+     *  @param[in] privilege - The privilege role associated with the group.
+     *
+     *  @return On success return the D-Bus object path of the created privilege
+     *          mapper entry.
+     */
+    ObjectPath create(std::string groupName, std::string privilege) override;
+
+    /** @brief Delete privilege mapping for LDAP group
+     *
+     *  This method deletes the privilege mapping
+     *
+     *  @param[in] id - id of the object which needs to be deleted.
+     */
+    void deletePrivilegeMapper(Id id);
+
+    /** @brief Check if LDAP group privilege mapping requested is valid
+     *
+     *  Check if the privilege mapping already exists for the LDAP group name
+     *  and group name is empty.
+     *
+     *  @param[in] groupName - LDAP group name
+     *
+     *  @return throw exception if the conditions are not met.
+     */
+    void checkPrivilegeMapper(const std::string& groupName);
+
+    /** @brief Check if the privilege level is a valid one
+     *
+     *  @param[in] privilege - Privilege level
+     *
+     *  @return throw exception if the conditions are not met.
+     */
+    void checkPrivilegeLevel(const std::string& privilege);
+
+    /** @brief Construct LDAP mapper entry D-Bus objects from their persisted
+     *         representations.
+     */
+    void restoreRoleMapping();
+
   private:
     bool secureLDAP;
     std::string lDAPBindPassword{};
@@ -207,6 +260,16 @@
     /** @brief reference to config manager object */
     ConfigMgr& parent;
 
+    /** @brief Id of the last privilege mapper entry */
+    Id entryId = 0;
+
+    /** @brief container to hold privilege mapper objects */
+    std::map<Id, std::unique_ptr<LDAPMapperEntry>> PrivilegeMapperList;
+
+    /** @brief available privileges container */
+    std::set<std::string> privMgr = {"priv-admin", "priv-operator", "priv-user",
+                                     "priv-callback"};
+
     friend class MockConfigMgr;
 };
 
diff --git a/phosphor-ldap-config/ldap_config_mgr.cpp b/phosphor-ldap-config/ldap_config_mgr.cpp
index 9142538..401aacc 100644
--- a/phosphor-ldap-config/ldap_config_mgr.cpp
+++ b/phosphor-ldap-config/ldap_config_mgr.cpp
@@ -198,12 +198,14 @@
     // Restore the ldap config and their mappings
     if (ADConfigPtr->deserialize())
     {
-        // Restore the role mappings in later commit
+        // Restore the role mappings
+        ADConfigPtr->restoreRoleMapping();
         ADConfigPtr->emit_object_added();
     }
     if (openLDAPConfigPtr->deserialize())
     {
-        // Restore the role mappings in later commit
+        // Restore the role mappings
+        openLDAPConfigPtr->restoreRoleMapping();
         openLDAPConfigPtr->emit_object_added();
     }
 }
diff --git a/phosphor-ldap-config/ldap_mapper_entry.cpp b/phosphor-ldap-config/ldap_mapper_entry.cpp
index 8410942..09b0e0f 100644
--- a/phosphor-ldap-config/ldap_mapper_entry.cpp
+++ b/phosphor-ldap-config/ldap_mapper_entry.cpp
@@ -1,17 +1,17 @@
-#include <experimental/filesystem>
+#include <filesystem>
 #include <xyz/openbmc_project/Common/error.hpp>
 #include <xyz/openbmc_project/User/Common/error.hpp>
 #include <phosphor-logging/log.hpp>
 #include <phosphor-logging/elog.hpp>
 #include <phosphor-logging/elog-errors.hpp>
 #include "config.h"
+#include "ldap_config.hpp"
 #include "ldap_mapper_entry.hpp"
-#include "ldap_mapper_mgr.hpp"
 #include "ldap_mapper_serialize.hpp"
 
 namespace phosphor
 {
-namespace user
+namespace ldap
 {
 
 using namespace phosphor::logging;
@@ -22,22 +22,21 @@
 LDAPMapperEntry::LDAPMapperEntry(sdbusplus::bus::bus &bus, const char *path,
                                  const char *filePath,
                                  const std::string &groupName,
-                                 const std::string &privilege,
-                                 LDAPMapperMgr &parent) :
-    Ifaces(bus, path, true),
-    id(std::stol(std::experimental::filesystem::path(path).filename())),
-    manager(parent), persistPath(filePath)
+                                 const std::string &privilege, Config &parent) :
+    Interfaces(bus, path, true),
+    id(std::stol(std::filesystem::path(path).filename())), manager(parent),
+    persistPath(filePath)
 {
-    Ifaces::privilege(privilege, true);
-    Ifaces::groupName(groupName, true);
-    Ifaces::emit_object_added();
+    Interfaces::privilege(privilege, true);
+    Interfaces::groupName(groupName, true);
+    Interfaces::emit_object_added();
 }
 
 LDAPMapperEntry::LDAPMapperEntry(sdbusplus::bus::bus &bus, const char *path,
-                                 const char *filePath, LDAPMapperMgr &parent) :
-    Ifaces(bus, path, true),
-    id(std::stol(std::experimental::filesystem::path(path).filename())),
-    manager(parent), persistPath(filePath)
+                                 const char *filePath, Config &parent) :
+    Interfaces(bus, path, true),
+    id(std::stol(std::filesystem::path(path).filename())), manager(parent),
+    persistPath(filePath)
 {
 }
 
@@ -48,29 +47,29 @@
 
 std::string LDAPMapperEntry::groupName(std::string value)
 {
-    if (value == Ifaces::groupName())
+    if (value == Interfaces::groupName())
     {
         return value;
     }
 
     manager.checkPrivilegeMapper(value);
-    auto val = Ifaces::groupName(value);
-    serialize(*this, id, persistPath);
+    auto val = Interfaces::groupName(value);
+    serialize(*this, persistPath);
     return val;
 }
 
 std::string LDAPMapperEntry::privilege(std::string value)
 {
-    if (value == Ifaces::privilege())
+    if (value == Interfaces::privilege())
     {
         return value;
     }
 
     manager.checkPrivilegeLevel(value);
-    auto val = Ifaces::privilege(value);
-    serialize(*this, id, persistPath);
+    auto val = Interfaces::privilege(value);
+    serialize(*this, persistPath);
     return val;
 }
 
-} // namespace user
+} // namespace ldap
 } // namespace phosphor
diff --git a/phosphor-ldap-config/ldap_mapper_entry.hpp b/phosphor-ldap-config/ldap_mapper_entry.hpp
index dea85c2..310f006 100644
--- a/phosphor-ldap-config/ldap_mapper_entry.hpp
+++ b/phosphor-ldap-config/ldap_mapper_entry.hpp
@@ -7,17 +7,17 @@
 
 namespace phosphor
 {
-namespace user
+namespace ldap
 {
 
 namespace Base = sdbusplus::xyz::openbmc_project;
 using Entry =
     sdbusplus::xyz::openbmc_project::User::server::PrivilegeMapperEntry;
 using Delete = sdbusplus::xyz::openbmc_project::Object::server::Delete;
-using Ifaces = sdbusplus::server::object::object<Entry, Delete>;
+using Interfaces = sdbusplus::server::object::object<Entry, Delete>;
 
-// Forward declaration for LDAPMapperMgr
-class LDAPMapperMgr;
+// Forward declaration for Config
+class Config;
 
 using Id = size_t;
 
@@ -25,7 +25,7 @@
  *
  *  @brief This D-Bus object represents the privilege level for the LDAP group.
  */
-class LDAPMapperEntry : public Ifaces
+class LDAPMapperEntry : public Interfaces
 {
   public:
     LDAPMapperEntry() = delete;
@@ -46,7 +46,7 @@
      */
     LDAPMapperEntry(sdbusplus::bus::bus &bus, const char *path,
                     const char *filePath, const std::string &groupName,
-                    const std::string &privilege, LDAPMapperMgr &parent);
+                    const std::string &privilege, Config &parent);
 
     /** @brief Constructs LDAP privilege mapper entry object
      *
@@ -56,7 +56,7 @@
      *  @param[in] parent - LDAP privilege mapper manager
      */
     LDAPMapperEntry(sdbusplus::bus::bus &bus, const char *path,
-                    const char *filePath, LDAPMapperMgr &parent);
+                    const char *filePath, Config &parent);
 
     /** @brief Delete privilege mapper entry object
      *
@@ -88,11 +88,11 @@
 
   private:
     Id id;
-    LDAPMapperMgr &manager;
+    Config &manager;
 
     /** @brief serialization directory path */
     std::string persistPath;
 };
 
-} // namespace user
+} // namespace ldap
 } // namespace phosphor
diff --git a/phosphor-ldap-config/ldap_mapper_serialize.cpp b/phosphor-ldap-config/ldap_mapper_serialize.cpp
index 3ef809e..7fe2398 100644
--- a/phosphor-ldap-config/ldap_mapper_serialize.cpp
+++ b/phosphor-ldap-config/ldap_mapper_serialize.cpp
@@ -8,11 +8,11 @@
 // Register class version
 // From cereal documentation;
 // "This macro should be placed at global scope"
-CEREAL_CLASS_VERSION(phosphor::user::LDAPMapperEntry, CLASS_VERSION);
+CEREAL_CLASS_VERSION(phosphor::ldap::LDAPMapperEntry, CLASS_VERSION);
 
 namespace phosphor
 {
-namespace user
+namespace ldap
 {
 
 using namespace phosphor::logging;
@@ -54,10 +54,10 @@
         privilege(privilege, true);
 }
 
-fs::path serialize(const LDAPMapperEntry& entry, Id id, const fs::path& dir)
+fs::path serialize(const LDAPMapperEntry& entry, const fs::path& path)
 {
-    auto path = dir / std::to_string(id);
-    std::ofstream os(path.c_str(), std::ios::binary);
+    fs::create_directories(path.parent_path());
+    std::ofstream os(path.c_str(), std::ios::binary | std::ios::out);
     cereal::BinaryOutputArchive oarchive(os);
     oarchive(entry);
     return path;
@@ -90,5 +90,5 @@
     }
 }
 
-} // namespace user
+} // namespace ldap
 } // namespace phosphor
diff --git a/phosphor-ldap-config/ldap_mapper_serialize.hpp b/phosphor-ldap-config/ldap_mapper_serialize.hpp
index 5ab71c0..371a556 100644
--- a/phosphor-ldap-config/ldap_mapper_serialize.hpp
+++ b/phosphor-ldap-config/ldap_mapper_serialize.hpp
@@ -1,26 +1,24 @@
 #pragma once
 
-#include <experimental/filesystem>
+#include <filesystem>
 #include "config.h"
 #include "ldap_mapper_entry.hpp"
 
 namespace phosphor
 {
-namespace user
+namespace ldap
 {
 
-namespace fs = std::experimental::filesystem;
+namespace fs = std::filesystem;
 
 /** @brief Serialize and persist LDAP privilege mapper D-Bus object
  *
  *  @param[in] entry - LDAP privilege mapper entry
- *  @param[in] id - filename of the persisted LDAP mapper entry
- *  @param[in] dir - pathname of directory where the serialized privilege
- *                   mappings are stored.
+ *  @param[in] path - pathname of persisted LDAP mapper entry
  *
  *  @return fs::path - pathname of persisted error file
  */
-fs::path serialize(const LDAPMapperEntry& entry, Id id, const fs::path& dir);
+fs::path serialize(const LDAPMapperEntry& entry, const fs::path& dir);
 
 /** @brief Deserialize a persisted LDAP privilege mapper into a D-Bus object
  *
@@ -32,5 +30,5 @@
  */
 bool deserialize(const fs::path& path, LDAPMapperEntry& entry);
 
-} // namespace user
+} // namespace ldap
 } // namespace phosphor
diff --git a/test/Makefile.am b/test/Makefile.am
index 9d11428..4f4f9a1 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -30,7 +30,9 @@
 ldap_config_test_SOURCES  = ldap_config_test.cpp utils_test.cpp
 ldap_config_test_LDADD  = $(top_builddir)/phosphor-ldap-config/ldap_config.o \
                           $(top_builddir)/phosphor-ldap-config/utils.o \
-                          $(top_builddir)/phosphor-ldap-config/ldap_config_mgr.o
+                          $(top_builddir)/phosphor-ldap-config/ldap_config_mgr.o \
+                          $(top_builddir)/phosphor-ldap-config/ldap_mapper_entry.o \
+                          $(top_builddir)/phosphor-ldap-config/ldap_mapper_serialize.o
 
 check_PROGRAMS += ldap_mapper_test
 ldap_mapper_test_CPPFLAGS = $(cppflags)