ldap: Add application to configure privilege for LDAP groups

The application implements the xyz.openbmc_project.User.PrivilegeMapper
D-Bus interface to configure privilege levels for LDAP groups. The Create
method is used to create privilege mapping for the LDAP group. D-Bus
object is created for each LDAP group and implements the D-Bus interface
xyz.openbmc_project.User.PrivilegeMapperEntry.
:
Change-Id: I20935229a8a79ce1e52a857672a6a0085cb5ace4
Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
diff --git a/phosphor-ldap-mapper/Makefile.am b/phosphor-ldap-mapper/Makefile.am
new file mode 100644
index 0000000..4aeca7b
--- /dev/null
+++ b/phosphor-ldap-mapper/Makefile.am
@@ -0,0 +1,19 @@
+sbin_PROGRAMS = phosphor-ldap-mapper
+
+noinst_HEADERS = ldap_mapper_mgr.hpp \
+                 ldap_mapper_entry.hpp
+
+phosphor_ldap_mapper_SOURCES = \
+                main.cpp \
+                ldap_mapper_mgr.cpp \
+                ldap_mapper_entry.cpp
+
+phosphor_ldap_mapper_LDFLAGS = $(SDBUSPLUS_LIBS) \
+                               $(PHOSPHOR_DBUS_INTERFACES_LIBS) \
+                               $(PHOSPHOR_LOGGING_LIBS) \
+                               -lstdc++fs
+
+phosphor_ldap_mapper_CXXFLAGS = $(SYSTEMD_CFLAGS) \
+                                $(PHOSPHOR_DBUS_INTERFACES_CFLAGS) \
+                                $(PHOSPHOR_LOGGING_CFLAGS) \
+                                -flto
diff --git a/phosphor-ldap-mapper/ldap_mapper_entry.cpp b/phosphor-ldap-mapper/ldap_mapper_entry.cpp
new file mode 100644
index 0000000..64d3578
--- /dev/null
+++ b/phosphor-ldap-mapper/ldap_mapper_entry.cpp
@@ -0,0 +1,62 @@
+#include <experimental/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_mapper_entry.hpp"
+#include "ldap_mapper_mgr.hpp"
+
+namespace phosphor
+{
+namespace user
+{
+
+using namespace phosphor::logging;
+using InvalidArgument =
+    sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
+using Argument = xyz::openbmc_project::Common::InvalidArgument;
+
+LDAPMapperEntry::LDAPMapperEntry(sdbusplus::bus::bus &bus, const char *path,
+                                 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)
+{
+    Ifaces::privilege(privilege, true);
+    Ifaces::groupName(groupName, true);
+    Ifaces::emit_object_added();
+}
+
+void LDAPMapperEntry::delete_(void)
+{
+    manager.deletePrivilegeMapper(id);
+}
+
+std::string LDAPMapperEntry::groupName(std::string value)
+{
+    if (value == Ifaces::groupName())
+    {
+        return value;
+    }
+
+    manager.checkPrivilegeMapper(value);
+    return Ifaces::groupName(value);
+}
+
+std::string LDAPMapperEntry::privilege(std::string value)
+{
+    if (value == Ifaces::privilege())
+    {
+        return value;
+    }
+
+    manager.checkPrivilegeLevel(value);
+    return Ifaces::privilege(value);
+}
+
+} // namespace user
+} // namespace phosphor
diff --git a/phosphor-ldap-mapper/ldap_mapper_entry.hpp b/phosphor-ldap-mapper/ldap_mapper_entry.hpp
new file mode 100644
index 0000000..689639d
--- /dev/null
+++ b/phosphor-ldap-mapper/ldap_mapper_entry.hpp
@@ -0,0 +1,83 @@
+#pragma once
+
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server/object.hpp>
+#include <xyz/openbmc_project/User/PrivilegeMapperEntry/server.hpp>
+#include <xyz/openbmc_project/Object/Delete/server.hpp>
+
+namespace phosphor
+{
+namespace user
+{
+
+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>;
+
+// Forward declaration for LDAPMapperMgr
+class LDAPMapperMgr;
+
+using Id = size_t;
+
+/** @class LDAPMapperEntry
+ *
+ *  @brief This D-Bus object represents the privilege level for the LDAP group.
+ */
+class LDAPMapperEntry : public Ifaces
+{
+  public:
+    LDAPMapperEntry() = delete;
+    ~LDAPMapperEntry() = default;
+    LDAPMapperEntry(const LDAPMapperEntry &) = delete;
+    LDAPMapperEntry &operator=(const LDAPMapperEntry &) = delete;
+    LDAPMapperEntry(LDAPMapperEntry &&) = default;
+    LDAPMapperEntry &operator=(LDAPMapperEntry &&) = default;
+
+    /** @brief Constructs LDAP privilege mapper entry object.
+     *
+     *  @param[in] bus  - sdbusplus handler
+     *  @param[in] path - D-Bus path
+     *  @param[in] privilege - the privilege for the group
+     *  @param[in] parent - LDAP privilege mapper manager
+     */
+    LDAPMapperEntry(sdbusplus::bus::bus &bus, const char *path,
+                    const std::string &groupName, const std::string &privilege,
+                    LDAPMapperMgr &parent);
+
+    /** @brief Delete privilege mapper entry object
+     *
+     *  This method deletes the privilege mapper entry.
+     */
+    void delete_(void) override;
+
+    /** @brief Update the group name of the mapper object
+     *
+     *  @param[in] value - group name
+     *
+     *  @return On success the updated group name
+     */
+    std::string groupName(std::string value) override;
+
+    /** @brief Update privilege associated with LDAP group
+     *
+     *  @param[in] value - privilege level
+     *
+     *  @return On success the updated privilege level
+     */
+    std::string privilege(std::string value) override;
+
+    using sdbusplus::xyz::openbmc_project::User::server::PrivilegeMapperEntry::
+        privilege;
+
+    using sdbusplus::xyz::openbmc_project::User::server::PrivilegeMapperEntry::
+        groupName;
+
+  private:
+    Id id;
+    LDAPMapperMgr &manager;
+};
+
+} // namespace user
+} // namespace phosphor
diff --git a/phosphor-ldap-mapper/ldap_mapper_mgr.cpp b/phosphor-ldap-mapper/ldap_mapper_mgr.cpp
new file mode 100644
index 0000000..9fe40ad
--- /dev/null
+++ b/phosphor-ldap-mapper/ldap_mapper_mgr.cpp
@@ -0,0 +1,88 @@
+#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_mapper_mgr.hpp"
+
+namespace phosphor
+{
+namespace user
+{
+
+using namespace phosphor::logging;
+using InvalidArgument =
+    sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
+using Argument = xyz::openbmc_project::Common::InvalidArgument;
+using PrivilegeMappingExists = sdbusplus::xyz::openbmc_project::User::Common::
+    Error::PrivilegeMappingExists;
+
+LDAPMapperMgr::LDAPMapperMgr(sdbusplus::bus::bus &bus, const char *path) :
+    MapperMgrIface(bus, path), bus(bus), path(path)
+{
+}
+
+ObjectPath LDAPMapperMgr::create(std::string groupName, std::string privilege)
+{
+    checkPrivilegeMapper(groupName);
+    checkPrivilegeLevel(privilege);
+
+    entryId++;
+
+    // Object path for the LDAP group privilege mapper entry
+    auto mapperObject =
+        std::string(mapperMgrRoot) + "/" + std::to_string(entryId);
+
+    // Create mapping for LDAP privilege mapper entry
+    auto entry = std::make_unique<phosphor::user::LDAPMapperEntry>(
+        bus, mapperObject.c_str(), groupName, privilege, *this);
+
+    PrivilegeMapperList.emplace(entryId, std::move(entry));
+
+    return mapperObject;
+}
+
+void LDAPMapperMgr::deletePrivilegeMapper(Id id)
+{
+    PrivilegeMapperList.erase(id);
+}
+
+void LDAPMapperMgr::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 LDAPMapperMgr::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()));
+    }
+}
+
+} // namespace user
+} // namespace phosphor
diff --git a/phosphor-ldap-mapper/ldap_mapper_mgr.hpp b/phosphor-ldap-mapper/ldap_mapper_mgr.hpp
new file mode 100644
index 0000000..450626d
--- /dev/null
+++ b/phosphor-ldap-mapper/ldap_mapper_mgr.hpp
@@ -0,0 +1,103 @@
+#pragma once
+
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server/object.hpp>
+#include "ldap_mapper_entry.hpp"
+#include <xyz/openbmc_project/User/PrivilegeMapper/server.hpp>
+#include <map>
+#include <set>
+
+namespace phosphor
+{
+
+namespace user
+{
+
+using MapperMgrIface =
+    sdbusplus::xyz::openbmc_project::User::server::PrivilegeMapper;
+using ObjectPath = sdbusplus::message::object_path;
+
+// D-Bus root for LDAP privilege mapper
+constexpr auto mapperMgrRoot = "/xyz/openbmc_project/user/ldap";
+
+/** @class LDAPMapperMgr
+ *
+ *  @brief Responsible for managing LDAP groups to privilege mapping.
+ */
+class LDAPMapperMgr : public MapperMgrIface
+{
+  public:
+    LDAPMapperMgr() = delete;
+    ~LDAPMapperMgr() = default;
+    LDAPMapperMgr(const LDAPMapperMgr &) = delete;
+    LDAPMapperMgr &operator=(const LDAPMapperMgr &) = delete;
+    LDAPMapperMgr(LDAPMapperMgr &&) = delete;
+    LDAPMapperMgr &operator=(LDAPMapperMgr &&) = delete;
+
+    /** @brief Constructs LDAPMapperMgr object.
+     *
+     *  @param[in] bus  - sdbusplus handler
+     *  @param[in] path - D-Bus path
+     */
+    LDAPMapperMgr(sdbusplus::bus::bus &bus, const char *path);
+
+    /** @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] groupName - name of the LDAP group for which privilege
+     *                         mapping is 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);
+
+  private:
+    /** @brief sdbusplus handler */
+    sdbusplus::bus::bus &bus;
+
+    /** @brief object path for the manager object*/
+    const std::string path;
+
+    /** @brief available privileges container */
+    std::set<std::string> privMgr = {"priv-admin", "priv-operator", "priv-user",
+                                     "priv-callback"};
+
+    /** @brief Id of the last privilege mapper entry */
+    Id entryId = 0;
+
+    /** @brief container to hold privilege mapper objects */
+    std::map<Id, std::unique_ptr<phosphor::user::LDAPMapperEntry>>
+        PrivilegeMapperList;
+};
+
+} // namespace user
+} // namespace phosphor
diff --git a/phosphor-ldap-mapper/main.cpp b/phosphor-ldap-mapper/main.cpp
new file mode 100644
index 0000000..b4a0650
--- /dev/null
+++ b/phosphor-ldap-mapper/main.cpp
@@ -0,0 +1,25 @@
+#include <string>
+#include <experimental/filesystem>
+#include "config.h"
+#include "ldap_mapper_mgr.hpp"
+
+int main(int argc, char** argv)
+{
+    auto bus = sdbusplus::bus::new_default();
+    sdbusplus::server::manager::manager objManager(
+        bus, phosphor::user::mapperMgrRoot);
+
+    phosphor::user::LDAPMapperMgr mapperMgr(bus, phosphor::user::mapperMgrRoot);
+
+    // Claim the bus name for the application
+    bus.request_name(LDAP_MAPPER_MANAGER_BUSNAME);
+
+    // Wait for client request
+    while (true)
+    {
+        // Process D-Bus calls
+        bus.process_discard();
+        bus.wait();
+    }
+    return 0;
+}