Basic support for User manager service

Basic support for User Manager service methods
are implemented.

Change-Id: Id42432ec6dd421b99971268add931dcd70876f7c
Signed-off-by: Richard Marian Thomaiyar <richard.marian.thomaiyar@linux.intel.com>
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..37469de
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,84 @@
+---
+Language:        Cpp
+# BasedOnStyle:  LLVM
+AccessModifierOffset: -2
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlinesLeft: false
+AlignOperands:   true
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: None
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: false
+BinPackArguments: true
+BinPackParameters: true
+BraceWrapping:
+  AfterClass:      true
+  AfterControlStatement: true
+  AfterEnum:       true
+  AfterFunction:   true
+  AfterNamespace:  true
+  AfterObjCDeclaration: true
+  AfterStruct:     true
+  AfterUnion:      true
+  BeforeCatch:     true
+  BeforeElse:      true
+  IndentBraces:    false
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Custom
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializers: AfterColon
+ColumnLimit:     80
+CommentPragmas:  '^ IWYU pragma:'
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DerivePointerAlignment: true
+PointerAlignment: Left
+DisableFormat:   false
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: true
+ForEachMacros:   [ foreach, Q_FOREACH, BOOST_FOREACH ]
+IndentCaseLabels: true
+IndentWidth:     4
+IndentWrappedFunctionNames: true
+KeepEmptyLinesAtTheStartOfBlocks: true
+MacroBlockBegin: ''
+MacroBlockEnd:   ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBlockIndentWidth: 2
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: true
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 60
+PointerAlignment: Right
+ReflowComments:  true
+SortIncludes:    false
+SpaceAfterCStyleCast: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeParens: ControlStatements
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles:  false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard:        Cpp11
+TabWidth:        4
+UseTab:          Never
+...
diff --git a/Makefile.am b/Makefile.am
index 5d5e14f..b8b753d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,18 +1,26 @@
 sbin_PROGRAMS = phosphor-user-manager
 
-noinst_HEADERS = user.hpp
+noinst_HEADERS = user.hpp user_mgr.hpp users.hpp
 
 phosphor_user_manager_SOURCES = \
                 user.cpp \
-                mainapp.cpp
+                mainapp.cpp \
+                user_mgr.cpp \
+                users.cpp
 
 phosphor_user_manager_LDFLAGS = $(SDBUSPLUS_LIBS) \
                                 $(PHOSPHOR_DBUS_INTERFACES_LIBS) \
                                 $(PHOSPHOR_LOGGING_LIBS) \
+                                $(BOOST_CPPFLAGS) \
                                 -lcrypt \
                                 -lstdc++fs
 
 phosphor_user_manager_CXXFLAGS = $(SYSTEMD_CFLAGS) \
                                  $(PHOSPHOR_DBUS_INTERFACES_CFLAGS) \
-                                 $(PHOSPHOR_LOGGING_CFLAGS)
+                                 $(PHOSPHOR_LOGGING_CFLAGS) \
+                                 $(BOOST_CPPFLAGS) \
+                                 -DBOOST_ALL_NO_LIB \
+                                 -DBOOST_SYSTEM_NO_DEPRECATED \
+                                 -DBOOST_ERROR_CODE_HEADER_ONLY
+
 SUBDIRS = . test
diff --git a/file.hpp b/file.hpp
index 7b22b3e..34b1422 100644
--- a/file.hpp
+++ b/file.hpp
@@ -15,75 +15,72 @@
  */
 class File
 {
-    private:
-        /** @brief handler for operating on file */
-        FILE *fp = NULL;
+  private:
+    /** @brief handler for operating on file */
+    FILE* fp = NULL;
 
-        /** @brief File name. Needed in the case where the temp
-         *         needs to be removed
-         */
-        const std::string& name;
+    /** @brief File name. Needed in the case where the temp
+     *         needs to be removed
+     */
+    const std::string& name;
 
-        /** @brief Should the file be removed at exit */
-        bool removeOnExit = false;
+    /** @brief Should the file be removed at exit */
+    bool removeOnExit = false;
 
-    public:
-        File() = delete;
-        File(const File&) = delete;
-        File& operator=(const File&) = delete;
-        File(File&&) = delete;
-        File& operator=(File&&) = delete;
+  public:
+    File() = delete;
+    File(const File&) = delete;
+    File& operator=(const File&) = delete;
+    File(File&&) = delete;
+    File& operator=(File&&) = delete;
 
-        /** @brief Opens file and uses it to do file operation
-         *
-         *  @param[in] name         - File name
-         *  @param[in] mode         - File open mode
-         *  @param[in] removeOnExit - File to be removed at exit or no
-         */
-        File(const std::string& name,
-             const std::string& mode,
-             bool removeOnExit = false) :
-            name(name),
-            removeOnExit(removeOnExit)
+    /** @brief Opens file and uses it to do file operation
+     *
+     *  @param[in] name         - File name
+     *  @param[in] mode         - File open mode
+     *  @param[in] removeOnExit - File to be removed at exit or no
+     */
+    File(const std::string& name, const std::string& mode,
+         bool removeOnExit = false) :
+        name(name),
+        removeOnExit(removeOnExit)
+    {
+        fp = fopen(name.c_str(), mode.c_str());
+    }
+
+    /** @brief Opens file using provided file descriptor
+     *
+     *  @param[in] fd           - File descriptor
+     *  @param[in] name         - File name
+     *  @param[in] mode         - File open mode
+     *  @param[in] removeOnExit - File to be removed at exit or no
+     */
+    File(int fd, const std::string& name, const std::string& mode,
+         bool removeOnExit = false) :
+        name(name),
+        removeOnExit(removeOnExit)
+    {
+        fp = fdopen(fd, mode.c_str());
+    }
+
+    ~File()
+    {
+        if (fp)
         {
-            fp = fopen(name.c_str(), mode.c_str());
+            fclose(fp);
         }
 
-        /** @brief Opens file using provided file descriptor
-         *
-         *  @param[in] fd           - File descriptor
-         *  @param[in] name         - File name
-         *  @param[in] mode         - File open mode
-         *  @param[in] removeOnExit - File to be removed at exit or no
-         */
-        File(int fd,
-             const std::string& name,
-             const std::string& mode,
-             bool removeOnExit = false) :
-            name(name),
-            removeOnExit(removeOnExit)
+        // Needed for exception safety
+        if (removeOnExit && fs::exists(name))
         {
-            fp = fdopen(fd, mode.c_str());
+            fs::remove(name);
         }
+    }
 
-        ~File()
-        {
-            if (fp)
-            {
-                fclose(fp);
-            }
-
-            // Needed for exception safety
-            if (removeOnExit && fs::exists(name))
-            {
-                fs::remove(name);
-            }
-        }
-
-        auto operator()()
-        {
-            return fp;
-        }
+    auto operator()()
+    {
+        return fp;
+    }
 };
 
 } // namespace user
diff --git a/mainapp.cpp b/mainapp.cpp
index 04c7825..c9da030 100644
--- a/mainapp.cpp
+++ b/mainapp.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 #include <string>
-#include "user.hpp"
+#include "user_mgr.hpp"
 #include "config.h"
 
 // D-Bus root for user manager
@@ -23,22 +23,15 @@
 int main(int argc, char** argv)
 {
     auto bus = sdbusplus::bus::new_default();
-
-    // This is hard coded "root" user.
-    // TODO: This would need to be changed when the complete
-    //       user management code is written. May be, have manager
-    //       create these user objects.
-    //       Issue: openbmc/openbmc#2299
-    auto objPath = std::string{USER_MANAGER_ROOT} + '/' + "root";
-
     sdbusplus::server::manager::manager objManager(bus, USER_MANAGER_ROOT);
-    phosphor::user::User user(bus, objPath.c_str());
+
+    phosphor::user::UserMgr userMgr(bus, USER_MANAGER_ROOT);
 
     // Claim the bus now
     bus.request_name(USER_MANAGER_BUSNAME);
 
     // Wait for client request
-    while(true)
+    while (true)
     {
         // process dbus calls / signals discarding unhandled
         bus.process_discard();
diff --git a/shadowlock.hpp b/shadowlock.hpp
index dc17b5a..81903df 100644
--- a/shadowlock.hpp
+++ b/shadowlock.hpp
@@ -2,6 +2,7 @@
 
 #include <stdio.h>
 #include <cassert>
+#include <shadow.h>
 #include <phosphor-logging/log.hpp>
 #include <phosphor-logging/elog.hpp>
 #include <xyz/openbmc_project/Common/error.hpp>
@@ -13,8 +14,8 @@
 namespace shadow
 {
 
-using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
-                            Error::InternalFailure;
+using InternalFailure =
+    sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
 using namespace phosphor::logging;
 
 /** @class Lock
@@ -22,29 +23,29 @@
  */
 class Lock
 {
-    public:
-        Lock(const Lock&) = delete;
-        Lock& operator=(const Lock&) = delete;
-        Lock(Lock&&) = delete;
-        Lock& operator=(Lock&&) = delete;
+  public:
+    Lock(const Lock&) = delete;
+    Lock& operator=(const Lock&) = delete;
+    Lock(Lock&&) = delete;
+    Lock& operator=(Lock&&) = delete;
 
-        /** @brief Default constructor that just locks the shadow file */
-        Lock()
+    /** @brief Default constructor that just locks the shadow file */
+    Lock()
+    {
+        if (!lckpwdf())
         {
-            if (!lckpwdf())
-            {
-                log<level::ERR>("Locking Shadow failed");
-                elog<InternalFailure>();
-            }
+            log<level::ERR>("Locking Shadow failed");
+            elog<InternalFailure>();
         }
-        ~Lock()
+    }
+    ~Lock()
+    {
+        if (!ulckpwdf())
         {
-            if(!ulckpwdf())
-            {
-                log<level::ERR>("Un-Locking Shadow failed");
-                elog<InternalFailure>();
-            }
+            log<level::ERR>("Un-Locking Shadow failed");
+            elog<InternalFailure>();
         }
+    }
 };
 
 } // namespace shadow
diff --git a/test/utest.cpp b/test/utest.cpp
index 33e0576..5e17283 100644
--- a/test/utest.cpp
+++ b/test/utest.cpp
@@ -28,77 +28,70 @@
 
 class UserTest : public ::testing::Test
 {
-    public:
-        const std::string md5Salt = '$' + std::string(MD5) + '$'
-                                    + std::string(salt) + '$';
-        const std::string shaSalt = '$' + std::string(SHA512) + '$'
-                                    + std::string(salt) + '$';
+  public:
+    const std::string md5Salt =
+        '$' + std::string(MD5) + '$' + std::string(salt) + '$';
+    const std::string shaSalt =
+        '$' + std::string(SHA512) + '$' + std::string(salt) + '$';
 
-        const std::string entry = fs::path(path).filename().string() +
-                                  ':' + std::string(spPwdp);
-        sdbusplus::bus::bus bus;
-        phosphor::user::User user;
+    const std::string entry =
+        fs::path(path).filename().string() + ':' + std::string(spPwdp);
+    sdbusplus::bus::bus bus;
+    phosphor::user::User user;
 
-        // Gets called as part of each TEST_F construction
-        UserTest()
-            : bus(sdbusplus::bus::new_default()),
-              user(bus, path)
+    // Gets called as part of each TEST_F construction
+    UserTest() : bus(sdbusplus::bus::new_default()), user(bus, path)
+    {
+        // Create a shadow file entry
+        std::ofstream file(testShadow);
+        file << entry;
+        file.close();
+
+        // File to compare against
+        std::ofstream compare(shadowCompare);
+        compare << entry;
+        compare.close();
+    }
+
+    // Gets called as part of each TEST_F destruction
+    ~UserTest()
+    {
+        if (fs::exists(testShadow))
         {
-            // Create a shadow file entry
-            std::ofstream file(testShadow);
-            file << entry;
-            file.close();
-
-            // File to compare against
-            std::ofstream compare(shadowCompare);
-            compare << entry;
-            compare.close();
+            fs::remove(testShadow);
         }
 
-        // Gets called as part of each TEST_F destruction
-        ~UserTest()
+        if (fs::exists(shadowCompare))
         {
-            if (fs::exists(testShadow))
-            {
-                fs::remove(testShadow);
-            }
-
-            if (fs::exists(shadowCompare))
-            {
-                fs::remove(shadowCompare);
-            }
+            fs::remove(shadowCompare);
         }
+    }
 
-        /** @brief wrapper for get crypt field */
-        auto getCryptField(char* data)
-        {
-            return User::getCryptField(
-                            std::forward<decltype(data)>(data));
-        }
+    /** @brief wrapper for get crypt field */
+    auto getCryptField(char* data)
+    {
+        return User::getCryptField(std::forward<decltype(data)>(data));
+    }
 
-        /** @brief wrapper for getSaltString */
-        auto getSaltString(const std::string& crypt,
-                           const std::string& salt)
-        {
-            return User::getSaltString(
-                            std::forward<decltype(crypt)>(crypt),
-                                std::forward<decltype(salt)>(salt));
-        }
+    /** @brief wrapper for getSaltString */
+    auto getSaltString(const std::string& crypt, const std::string& salt)
+    {
+        return User::getSaltString(std::forward<decltype(crypt)>(crypt),
+                                   std::forward<decltype(salt)>(salt));
+    }
 
-        /** @brief wrapper for generateHash */
-        auto generateHash(const std::string& password,
-                          const std::string& salt)
-        {
-            return User::generateHash(
-                            std::forward<decltype(password)>(password),
-                                std::forward<decltype(salt)>(salt));
-        }
+    /** @brief wrapper for generateHash */
+    auto generateHash(const std::string& password, const std::string& salt)
+    {
+        return User::generateHash(std::forward<decltype(password)>(password),
+                                  std::forward<decltype(salt)>(salt));
+    }
 
-        /** @brief Applies the new password */
-        auto applyPassword()
-        {
-            return user.applyPassword(testShadow, password, salt);
-        }
+    /** @brief Applies the new password */
+    auto applyPassword()
+    {
+        return user.applyPassword(testShadow, password, salt);
+    }
 };
 
 /** @brief Makes sure that SHA512 crypt field is extracted
@@ -175,8 +168,12 @@
     // Compare the permission of 2 files.
     // file rename would make sure that the permissions
     // of old are moved to new
-    struct stat shadow{};
-    struct stat compare{};
+    struct stat shadow
+    {
+    };
+    struct stat compare
+    {
+    };
 
     stat(testShadow, &shadow);
     stat(shadowCompare, &compare);
diff --git a/user.cpp b/user.cpp
index 447bfcd..6999a98 100644
--- a/user.cpp
+++ b/user.cpp
@@ -40,10 +40,10 @@
 constexpr int SALT_LENGTH = 16;
 
 using namespace phosphor::logging;
-using InsufficientPermission = sdbusplus::xyz::openbmc_project::Common::
-                                    Error::InsufficientPermission;
-using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
-                                    Error::InternalFailure;
+using InsufficientPermission =
+    sdbusplus::xyz::openbmc_project::Common::Error::InsufficientPermission;
+using InternalFailure =
+    sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
 // Sets or updates the password
 void User::setPassword(std::string newPassword)
 {
@@ -68,15 +68,14 @@
 }
 
 void User::applyPassword(const std::string& shadowFile,
-                         const std::string& password,
-                         const std::string& salt)
+                         const std::string& password, const std::string& salt)
 {
     // Needed by getspnam_r
     struct spwd shdp;
     struct spwd* pshdp;
 
     // This should be fine even if SHA512 is used.
-    std::array<char,1024> buffer{};
+    std::array<char, 1024> buffer{};
 
     // Open the shadow file for reading
     phosphor::user::File shadow(shadowFile, "r");
@@ -87,10 +86,10 @@
 
     // open temp shadow file, by suffixing random name in shadow file name.
     std::vector<char> tempFileName(shadowFile.begin(), shadowFile.end());
-    std::vector<char> fileTemplate = {
-                             '_', '_', 'X', 'X', 'X', 'X', 'X', 'X', '\0' };
-    tempFileName.insert(
-              tempFileName.end(), fileTemplate.begin(), fileTemplate.end());
+    std::vector<char> fileTemplate = {'_', '_', 'X', 'X', 'X',
+                                      'X', 'X', 'X', '\0'};
+    tempFileName.insert(tempFileName.end(), fileTemplate.begin(),
+                        fileTemplate.end());
 
     int fd = mkstemp(tempFileName.data());
     if (fd == -1)
@@ -112,7 +111,9 @@
 
     // Change the permission of this new temp file
     // to be same as shadow so that it's secure
-    struct stat st{};
+    struct stat st
+    {
+    };
     auto r = fstat(fileno((shadow)()), &st);
     if (r < 0)
     {
@@ -184,15 +185,13 @@
     }
     else
     {
-        log<level::ERR>(errMsg.c_str(),
-                entry("USER=%s",user.c_str()),
-                    entry("ERRNO=%d", errNo));
+        log<level::ERR>(errMsg.c_str(), entry("USER=%s", user.c_str()),
+                        entry("ERRNO=%d", errNo));
         elog<InternalFailure>();
     }
 }
 
-std::string User::hashPassword(char* spPwdp,
-                               const std::string& password,
+std::string User::hashPassword(char* spPwdp, const std::string& password,
                                const std::string& salt)
 {
     // Parse and get crypt algo
@@ -200,7 +199,7 @@
     if (cryptAlgo.empty())
     {
         log<level::ERR>("Error finding crypt algo",
-                entry("USER=%s",user.c_str()));
+                        entry("USER=%s", user.c_str()));
         elog<InternalFailure>();
     }
 
@@ -226,7 +225,7 @@
     // Standard mersenne_twister_engine seeded with rd()
     std::mt19937 gen(rd());
 
-    std::uniform_int_distribution<> dis(0, set.size()-1);
+    std::uniform_int_distribution<> dis(0, set.size() - 1);
     for (int count = 0; count < length; count++)
     {
         // Use dis to transform the random unsigned int generated by
diff --git a/user.hpp b/user.hpp
index ca8673f..2e57702 100644
--- a/user.hpp
+++ b/user.hpp
@@ -23,113 +23,107 @@
  */
 class User : public Interface
 {
-    public:
-        User() = delete;
-        ~User() = default;
-        User(const User&) = delete;
-        User& operator=(const User&) = delete;
-        User(User&&) = delete;
-        User& operator=(User&&) = delete;
+  public:
+    User() = delete;
+    ~User() = default;
+    User(const User&) = delete;
+    User& operator=(const User&) = delete;
+    User(User&&) = delete;
+    User& operator=(User&&) = delete;
 
-        /** @brief Constructs User object.
-         *
-         *  @param[in] bus  - sdbusplus handler
-         *  @param[in] path - D-Bus path
-         */
-        User(sdbusplus::bus::bus& bus, const char* path)
-            : Interface(bus, path),
-              bus(bus),
-              path(path),
-              user(fs::path(path).filename())
-        {
-            // Do nothing
-        }
+    /** @brief Constructs User object.
+     *
+     *  @param[in] bus  - sdbusplus handler
+     *  @param[in] path - D-Bus path
+     */
+    User(sdbusplus::bus::bus& bus, const char* path) :
+        Interface(bus, path), bus(bus), path(path),
+        user(fs::path(path).filename())
+    {
+        // Do nothing
+    }
 
-        /** @brief user password set method. If this is called for
-         *         a user ID that already has the password, the password
-         *         would be updated, else password would be created.
-         *         Since this needs an already authenticated session,
-         *         old password is not needed.
-         *
-         *  @param[in] newPassword - New password
-         */
-        void setPassword(std::string newPassword) override;
+    /** @brief user password set method. If this is called for
+     *         a user ID that already has the password, the password
+     *         would be updated, else password would be created.
+     *         Since this needs an already authenticated session,
+     *         old password is not needed.
+     *
+     *  @param[in] newPassword - New password
+     */
+    void setPassword(std::string newPassword) override;
 
+  private:
+    /** @brief sdbusplus handler */
+    sdbusplus::bus::bus& bus;
 
-    private:
-        /** @brief sdbusplus handler */
-        sdbusplus::bus::bus& bus;
+    /** @brief object path */
+    const std::string& path;
 
-        /** @brief object path */
-        const std::string& path;
+    /** @brief User id extracted from object path */
+    const std::string user;
 
-        /** @brief User id extracted from object path */
-        const std::string user;
+    /** @brief Returns a random string from set [A-Za-z0-9./]
+     *         of length size
+     *
+     *  @param[in] numChars - length of string
+     */
+    static const std::string randomString(int length);
 
-        /** @brief Returns a random string from set [A-Za-z0-9./]
-         *         of length size
-         *
-         *  @param[in] numChars - length of string
-         */
-        static const std::string randomString(int length);
+    /** @brief Returns password hash created with crypt algo,
+     *         salt and password
+     *
+     *  @param[in] spPwdp   - sp_pwdp of struct spwd
+     *  @param[in] password - clear text password
+     *  @param[in] salt     - Random salt
+     */
+    std::string hashPassword(char* spPwdp, const std::string& password,
+                             const std::string& salt);
 
-        /** @brief Returns password hash created with crypt algo,
-         *         salt and password
-         *
-         *  @param[in] spPwdp   - sp_pwdp of struct spwd
-         *  @param[in] password - clear text password
-         *  @param[in] salt     - Random salt
-         */
-        std::string hashPassword(char* spPwdp,
-                                 const std::string& password,
-                                 const std::string& salt);
+    /** @brief Extracts crypto number from the shadow entry for user
+     *
+     *  @param[in] spPwdp - sp_pwdp of struct spwd
+     */
+    static CryptAlgo getCryptField(char* spPwdp);
 
-        /** @brief Extracts crypto number from the shadow entry for user
-         *
-         *  @param[in] spPwdp - sp_pwdp of struct spwd
-         */
-        static CryptAlgo getCryptField(char* spPwdp);
+    /** @brief Generates one-way hash based on salt and password
+     *
+     *  @param[in] password - clear text password
+     *  @param[in] salt     - Combination of crypto method and salt
+     *                        Eg: $1$HELLO$, where in 1 is crypto method
+     *                        and HELLO is salt
+     */
+    static std::string generateHash(const std::string& password,
+                                    const std::string& salt);
 
-        /** @brief Generates one-way hash based on salt and password
-         *
-         *  @param[in] password - clear text password
-         *  @param[in] salt     - Combination of crypto method and salt
-         *                        Eg: $1$HELLO$, where in 1 is crypto method
-         *                        and HELLO is salt
-         */
-        static std::string generateHash(const std::string& password,
-                                        const std::string& salt);
+    /** @brief Returns salt string with $ delimiter.
+     *         Eg: If crypt is 1 and salt is HELLO, returns $1$HELLO$
+     *
+     *  @param[in] crypt - Crypt number in string
+     *  @param[in] salt  - salt
+     */
+    static std::string getSaltString(const std::string& crypt,
+                                     const std::string& salt);
 
-        /** @brief Returns salt string with $ delimiter.
-         *         Eg: If crypt is 1 and salt is HELLO, returns $1$HELLO$
-         *
-         *  @param[in] crypt - Crypt number in string
-         *  @param[in] salt  - salt
-         */
-        static std::string getSaltString(const std::string& crypt,
-                                         const std::string& salt);
+    /** @brief Applies the password for a given user.
+     *         Writes shadow entries into a temp file
+     *
+     *  @param[in] shadowFile - shadow password file
+     *  @param[in] password   - clear text password
+     *  @param[in] salt       - salt
+     */
+    void applyPassword(const std::string& shadowFile,
+                       const std::string& password, const std::string& salt);
 
-        /** @brief Applies the password for a given user.
-         *         Writes shadow entries into a temp file
-         *
-         *  @param[in] shadowFile - shadow password file
-         *  @param[in] password   - clear text password
-         *  @param[in] salt       - salt
-         */
-        void applyPassword(const std::string& shadowFile,
-                           const std::string& password,
-                           const std::string& salt);
+    /** @brief Wrapper for raising exception
+     *
+     *  @param[in] errNo  - errno
+     *  @param[in] errMsg - Error message
+     */
+    void raiseException(int errNo, const std::string& errMsg);
 
-        /** @brief Wrapper for raising exception
-         *
-         *  @param[in] errNo  - errno
-         *  @param[in] errMsg - Error message
-         */
-        void raiseException(int errNo,
-                            const std::string& errMsg);
-
-        /** @brief For enabling test cases */
-        friend class UserTest;
+    /** @brief For enabling test cases */
+    friend class UserTest;
 };
 
 } // namespace user
diff --git a/user_mgr.cpp b/user_mgr.cpp
new file mode 100644
index 0000000..3987088
--- /dev/null
+++ b/user_mgr.cpp
@@ -0,0 +1,598 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#include <shadow.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <fstream>
+#include <grp.h>
+#include <pwd.h>
+#include <regex>
+#include <algorithm>
+#include <numeric>
+#include <boost/process/child.hpp>
+#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 "shadowlock.hpp"
+#include "file.hpp"
+#include "user_mgr.hpp"
+#include "users.hpp"
+#include "config.h"
+
+namespace phosphor
+{
+namespace user
+{
+
+static constexpr const char *passwdFileName = "/etc/passwd";
+static constexpr size_t ipmiMaxUsers = 15;
+static constexpr size_t ipmiMaxUserNameLen = 16;
+static constexpr size_t systemMaxUserNameLen = 30;
+static constexpr size_t maxSystemUsers = 30;
+static constexpr const char *grpSsh = "ssh";
+
+using namespace phosphor::logging;
+using InsufficientPermission =
+    sdbusplus::xyz::openbmc_project::Common::Error::InsufficientPermission;
+using InternalFailure =
+    sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+using InvalidArgument =
+    sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
+using UserNameExists =
+    sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameExists;
+using UserNameDoesNotExist =
+    sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameDoesNotExist;
+using UserNameGroupFail =
+    sdbusplus::xyz::openbmc_project::User::Common::Error::UserNameGroupFail;
+
+using NoResource =
+    sdbusplus::xyz::openbmc_project::User::Common::Error::NoResource;
+
+using Argument = xyz::openbmc_project::Common::InvalidArgument;
+
+template <typename... ArgTypes>
+static void executeCmd(const char *path, ArgTypes &&... tArgs)
+{
+    boost::process::child execProg(path, const_cast<char *>(tArgs)...);
+    execProg.wait();
+    int retCode = execProg.exit_code();
+    if (retCode)
+    {
+        log<level::ERR>("Command execution failed", entry("PATH=%s", path),
+                        entry("RETURN_CODE:%d", retCode));
+        elog<InternalFailure>();
+    }
+    return;
+}
+
+static std::string getCSVFromVector(std::vector<std::string> vec)
+{
+    switch (vec.size())
+    {
+        case 0:
+        {
+            return "";
+        }
+        break;
+
+        case 1:
+        {
+            return std::string{vec[0]};
+        }
+        break;
+
+        default:
+        {
+            return std::accumulate(
+                std::next(vec.begin()), vec.end(), vec[0],
+                [](std::string a, std::string b) { return a + ',' + b; });
+        }
+    }
+}
+
+static bool removeStringFromCSV(std::string &csvStr, const std::string &delStr)
+{
+    std::string::size_type delStrPos = csvStr.find(delStr);
+    if (delStrPos != std::string::npos)
+    {
+        // need to also delete the comma char
+        if (delStrPos == 0)
+        {
+            csvStr.erase(delStrPos, delStr.size() + 1);
+        }
+        else
+        {
+            csvStr.erase(delStrPos - 1, delStr.size() + 1);
+        }
+        return true;
+    }
+    return false;
+}
+
+bool UserMgr::isUserExist(const std::string &userName)
+{
+    if (userName.empty())
+    {
+        log<level::ERR>("User name is empty");
+        elog<InvalidArgument>(Argument::ARGUMENT_NAME("User name"),
+                              Argument::ARGUMENT_VALUE("Null"));
+    }
+    if (usersList.find(userName) == usersList.end())
+    {
+        return false;
+    }
+    return true;
+}
+
+void UserMgr::throwForUserDoesNotExist(const std::string &userName)
+{
+    if (isUserExist(userName) == false)
+    {
+        log<level::ERR>("User does not exist",
+                        entry("USER_NAME=%s", userName.c_str()));
+        elog<UserNameDoesNotExist>();
+    }
+}
+
+void UserMgr::throwForUserExists(const std::string &userName)
+{
+    if (isUserExist(userName) == true)
+    {
+        log<level::ERR>("User already exists",
+                        entry("USER_NAME=%s", userName.c_str()));
+        elog<UserNameExists>();
+    }
+}
+
+void UserMgr::throwForUserNameConstraints(
+    const std::string &userName, const std::vector<std::string> &groupNames)
+{
+    if (std::find(groupNames.begin(), groupNames.end(), "ipmi") !=
+        groupNames.end())
+    {
+        if (userName.length() > ipmiMaxUserNameLen)
+        {
+            log<level::ERR>("IPMI user name length limitation",
+                            entry("SIZE=%d", userName.length()));
+            elog<UserNameGroupFail>(
+                xyz::openbmc_project::User::Common::UserNameGroupFail::REASON(
+                    "IPMI length"));
+        }
+    }
+    if (userName.length() > systemMaxUserNameLen)
+    {
+        log<level::ERR>("User name length limitation",
+                        entry("SIZE=%d", userName.length()));
+        elog<InvalidArgument>(Argument::ARGUMENT_NAME("User name"),
+                              Argument::ARGUMENT_VALUE("Invalid length"));
+    }
+    if (!std::regex_match(userName.c_str(),
+                          std::regex("[a-zA-z_][a-zA-Z_0-9]*")))
+    {
+        log<level::ERR>("Invalid user name",
+                        entry("USER_NAME=%s", userName.c_str()));
+        elog<InvalidArgument>(Argument::ARGUMENT_NAME("User name"),
+                              Argument::ARGUMENT_VALUE("Invalid data"));
+    }
+}
+
+void UserMgr::throwForMaxGrpUserCount(
+    const std::vector<std::string> &groupNames)
+{
+    if (std::find(groupNames.begin(), groupNames.end(), "ipmi") !=
+        groupNames.end())
+    {
+        if (getIpmiUsersCount() >= ipmiMaxUsers)
+        {
+            log<level::ERR>("IPMI user limit reached");
+            elog<NoResource>(
+                xyz::openbmc_project::User::Common::NoResource::REASON(
+                    "ipmi user count reached"));
+        }
+    }
+    else
+    {
+        if (usersList.size() > 0 && (usersList.size() - getIpmiUsersCount()) >=
+                                        (maxSystemUsers - ipmiMaxUsers))
+        {
+            log<level::ERR>("Non-ipmi User limit reached");
+            elog<NoResource>(
+                xyz::openbmc_project::User::Common::NoResource::REASON(
+                    "Non-ipmi user count reached"));
+        }
+    }
+    return;
+}
+
+void UserMgr::throwForInvalidPrivilege(const std::string &priv)
+{
+    if (!priv.empty() &&
+        (std::find(privMgr.begin(), privMgr.end(), priv) == privMgr.end()))
+    {
+        log<level::ERR>("Invalid privilege");
+        elog<InvalidArgument>(Argument::ARGUMENT_NAME("Privilege"),
+                              Argument::ARGUMENT_VALUE(priv.c_str()));
+    }
+}
+
+void UserMgr::throwForInvalidGroups(const std::vector<std::string> &groupNames)
+{
+    for (auto &group : groupNames)
+    {
+        if (std::find(groupsMgr.begin(), groupsMgr.end(), group) ==
+            groupsMgr.end())
+        {
+            log<level::ERR>("Invalid Group Name listed");
+            elog<InvalidArgument>(Argument::ARGUMENT_NAME("GroupName"),
+                                  Argument::ARGUMENT_VALUE(group.c_str()));
+        }
+    }
+}
+
+void UserMgr::createUser(std::string userName,
+                         std::vector<std::string> groupNames, std::string priv,
+                         bool enabled)
+{
+    throwForInvalidPrivilege(priv);
+    throwForInvalidGroups(groupNames);
+    // All user management lock has to be based on /etc/shadow
+    phosphor::user::shadow::Lock lock();
+    throwForUserExists(userName);
+    throwForUserNameConstraints(userName, groupNames);
+    throwForMaxGrpUserCount(groupNames);
+
+    std::string groups = getCSVFromVector(groupNames);
+    bool sshRequested = removeStringFromCSV(groups, grpSsh);
+
+    // treat privilege as a group - This is to avoid using different file to
+    // store the same.
+    if (groups.size() != 0)
+    {
+        groups += ",";
+    }
+    groups += priv;
+
+    try
+    {
+        executeCmd("/usr/sbin/useradd", userName.c_str(), "-G", groups.c_str(),
+                   "-M", "-N", "-s",
+                   (sshRequested ? "/bin/sh" : "/bin/nologin"), "-e",
+                   (enabled ? "" : "1970-01-02"));
+    }
+    catch (const InternalFailure &e)
+    {
+        log<level::ERR>("Unable to create new user");
+        elog<InternalFailure>();
+    }
+
+    // Add the users object before sending out the signal
+    std::string userObj = std::string(usersObjPath) + "/" + userName;
+    std::sort(groupNames.begin(), groupNames.end());
+    usersList.emplace(
+        userName, std::move(std::make_unique<phosphor::user::Users>(
+                      bus, userObj.c_str(), groupNames, priv, enabled, *this)));
+
+    log<level::INFO>("User created successfully",
+                     entry("USER_NAME=%s", userName.c_str()));
+    return;
+}
+
+void UserMgr::deleteUser(std::string userName)
+{
+    // All user management lock has to be based on /etc/shadow
+    phosphor::user::shadow::Lock lock();
+    throwForUserDoesNotExist(userName);
+    try
+    {
+        executeCmd("/usr/sbin/userdel", userName.c_str());
+    }
+    catch (const InternalFailure &e)
+    {
+        log<level::ERR>("User delete failed",
+                        entry("USER_NAME=%s", userName.c_str()));
+        elog<InternalFailure>();
+    }
+
+    usersList.erase(userName);
+
+    log<level::INFO>("User deleted successfully",
+                     entry("USER_NAME=%s", userName.c_str()));
+    return;
+}
+
+void UserMgr::renameUser(std::string userName, std::string newUserName)
+{
+    // All user management lock has to be based on /etc/shadow
+    phosphor::user::shadow::Lock lock();
+    throwForUserDoesNotExist(userName);
+    throwForUserExists(newUserName);
+    throwForUserNameConstraints(newUserName,
+                                usersList[userName].get()->userGroups());
+    try
+    {
+        executeCmd("/usr/sbin/usermod", "-l", newUserName.c_str(),
+                   userName.c_str());
+    }
+    catch (const InternalFailure &e)
+    {
+        log<level::ERR>("User rename failed",
+                        entry("USER_NAME=%s", userName.c_str()));
+        elog<InternalFailure>();
+    }
+    const auto &user = usersList[userName];
+    std::string priv = user.get()->userPrivilege();
+    std::vector<std::string> groupNames = user.get()->userGroups();
+    bool enabled = user.get()->userEnabled();
+    std::string newUserObj = std::string(usersObjPath) + "/" + newUserName;
+    // Special group 'ipmi' needs a way to identify user renamed, in order to
+    // update encrypted password. It can't rely only on InterfacesRemoved &
+    // InterfacesAdded. So first send out userRenamed signal.
+    this->userRenamed(userName, newUserName);
+    usersList.erase(userName);
+    usersList.emplace(
+        newUserName,
+        std::move(std::make_unique<phosphor::user::Users>(
+            bus, newUserObj.c_str(), groupNames, priv, enabled, *this)));
+    return;
+}
+
+void UserMgr::updateGroupsAndPriv(const std::string &userName,
+                                  const std::vector<std::string> &groupNames,
+                                  const std::string &priv)
+{
+    throwForInvalidPrivilege(priv);
+    throwForInvalidGroups(groupNames);
+    // All user management lock has to be based on /etc/shadow
+    phosphor::user::shadow::Lock lock();
+    throwForUserDoesNotExist(userName);
+    const std::vector<std::string> &oldGroupNames =
+        usersList[userName].get()->userGroups();
+    std::vector<std::string> groupDiff;
+    // Note: already dealing with sorted group lists.
+    std::set_symmetric_difference(oldGroupNames.begin(), oldGroupNames.end(),
+                                  groupNames.begin(), groupNames.end(),
+                                  std::back_inserter(groupDiff));
+    if (std::find(groupDiff.begin(), groupDiff.end(), "ipmi") !=
+        groupDiff.end())
+    {
+        throwForUserNameConstraints(userName, groupNames);
+        throwForMaxGrpUserCount(groupNames);
+    }
+
+    std::string groups = getCSVFromVector(groupNames);
+    bool sshRequested = removeStringFromCSV(groups, grpSsh);
+
+    // treat privilege as a group - This is to avoid using different file to
+    // store the same.
+    if (groups.size() != 0)
+    {
+        groups += ",";
+    }
+    groups += priv;
+    try
+    {
+        executeCmd("/usr/sbin/usermod", userName.c_str(), "-G", groups.c_str(),
+                   "-s", (sshRequested ? "/bin/sh" : "/bin/nologin"));
+    }
+    catch (const InternalFailure &e)
+    {
+        log<level::ERR>("Unable to modify user privilege / groups");
+        elog<InternalFailure>();
+    }
+
+    log<level::INFO>("User groups / privilege updated successfully",
+                     entry("USER_NAME=%s", userName.c_str()));
+    return;
+}
+
+void UserMgr::userEnable(const std::string &userName, bool enabled)
+{
+    // All user management lock has to be based on /etc/shadow
+    phosphor::user::shadow::Lock lock();
+    throwForUserDoesNotExist(userName);
+    try
+    {
+        executeCmd("/usr/sbin/usermod", userName.c_str(), "-e",
+                   (enabled ? "" : "1970-01-02"));
+    }
+    catch (const InternalFailure &e)
+    {
+        log<level::ERR>("Unable to modify user enabled state");
+        elog<InternalFailure>();
+    }
+
+    log<level::INFO>("User enabled/disabled state updated successfully",
+                     entry("USER_NAME=%s", userName.c_str()),
+                     entry("ENABLED=%d", enabled));
+    return;
+}
+
+UserSSHLists UserMgr::getUserAndSshGrpList()
+{
+    // All user management lock has to be based on /etc/shadow
+    phosphor::user::shadow::Lock lock();
+
+    std::vector<std::string> userList;
+    std::vector<std::string> sshUsersList;
+    struct passwd pw, *pwp = nullptr;
+    std::array<char, 1024> buffer{};
+
+    phosphor::user::File passwd(passwdFileName, "r");
+    if ((passwd)() == NULL)
+    {
+        log<level::ERR>("Error opening the passwd file");
+        elog<InternalFailure>();
+    }
+
+    while (true)
+    {
+        auto r = fgetpwent_r((passwd)(), &pw, buffer.data(), buffer.max_size(),
+                             &pwp);
+        if ((r != 0) || (pwp == NULL))
+        {
+            // Any error, break the loop.
+            break;
+        }
+        // All users whose UID >= 1000 and < 65534
+        if ((pwp->pw_uid >= 1000) && (pwp->pw_uid < 65534))
+        {
+            std::string userName(pwp->pw_name);
+            userList.emplace_back(userName);
+
+            // ssh doesn't have separate group. Check login shell entry to
+            // get all users list which are member of ssh group.
+            std::string loginShell(pwp->pw_shell);
+            if (loginShell == "/bin/sh")
+            {
+                sshUsersList.emplace_back(userName);
+            }
+        }
+    }
+    endpwent();
+    return std::make_pair(std::move(userList), std::move(sshUsersList));
+}
+
+size_t UserMgr::getIpmiUsersCount()
+{
+    std::vector<std::string> userList = getUsersInGroup("ipmi");
+    return userList.size();
+}
+
+bool UserMgr::isUserEnabled(const std::string &userName)
+{
+    // All user management lock has to be based on /etc/shadow
+    phosphor::user::shadow::Lock lock();
+    std::array<char, 4096> buffer{};
+    struct spwd spwd;
+    struct spwd *resultPtr = nullptr;
+    int status = getspnam_r(userName.c_str(), &spwd, buffer.data(),
+                            buffer.max_size(), &resultPtr);
+    if (!status && (&spwd == resultPtr))
+    {
+        if (resultPtr->sp_expire >= 0)
+        {
+            return false; // user locked out
+        }
+        return true;
+    }
+    return false; // assume user is disabled for any error.
+}
+
+std::vector<std::string> UserMgr::getUsersInGroup(const std::string &groupName)
+{
+    std::vector<std::string> usersInGroup;
+    // Should be more than enough to get the pwd structure.
+    std::array<char, 4096> buffer{};
+    struct group grp;
+    struct group *resultPtr = nullptr;
+
+    int status = getgrnam_r(groupName.c_str(), &grp, buffer.data(),
+                            buffer.max_size(), &resultPtr);
+
+    if (!status && (&grp == resultPtr))
+    {
+        for (; *(grp.gr_mem) != NULL; ++(grp.gr_mem))
+        {
+            usersInGroup.emplace_back(*(grp.gr_mem));
+        }
+    }
+    else
+    {
+        log<level::ERR>("Group not found",
+                        entry("GROUP=%s", groupName.c_str()));
+        // Don't throw error, just return empty userList - fallback
+    }
+    return usersInGroup;
+}
+
+void UserMgr::initUserObjects(void)
+{
+    // All user management lock has to be based on /etc/shadow
+    phosphor::user::shadow::Lock lock();
+    std::vector<std::string> userNameList;
+    std::vector<std::string> sshGrpUsersList;
+    UserSSHLists userSSHLists = getUserAndSshGrpList();
+    userNameList = std::move(userSSHLists.first);
+    sshGrpUsersList = std::move(userSSHLists.second);
+
+    if (!userNameList.empty())
+    {
+        std::map<std::string, std::vector<std::string>> groupLists;
+        for (auto &grp : groupsMgr)
+        {
+            if (grp == grpSsh)
+            {
+                groupLists.emplace(grp, sshGrpUsersList);
+            }
+            else
+            {
+                std::vector<std::string> grpUsersList = getUsersInGroup(grp);
+                groupLists.emplace(grp, grpUsersList);
+            }
+        }
+        for (auto &grp : privMgr)
+        {
+            std::vector<std::string> grpUsersList = getUsersInGroup(grp);
+            groupLists.emplace(grp, grpUsersList);
+        }
+
+        for (auto &user : userNameList)
+        {
+            std::vector<std::string> userGroups;
+            std::string userPriv;
+            for (const auto &grp : groupLists)
+            {
+                std::vector<std::string> tempGrp = grp.second;
+                if (std::find(tempGrp.begin(), tempGrp.end(), user) !=
+                    tempGrp.end())
+                {
+                    if (std::find(privMgr.begin(), privMgr.end(), grp.first) !=
+                        privMgr.end())
+                    {
+                        userPriv = grp.first;
+                    }
+                    else
+                    {
+                        userGroups.emplace_back(grp.first);
+                    }
+                }
+            }
+            // Add user objects to the Users path.
+            auto objPath = std::string(usersObjPath) + "/" + user;
+            std::sort(userGroups.begin(), userGroups.end());
+            usersList.emplace(user,
+                              std::move(std::make_unique<phosphor::user::Users>(
+                                  bus, objPath.c_str(), userGroups, userPriv,
+                                  isUserEnabled(user), *this)));
+        }
+    }
+}
+
+UserMgr::UserMgr(sdbusplus::bus::bus &bus, const char *path) :
+    UserMgrIface(bus, path), bus(bus), path(path)
+{
+    UserMgrIface::allPrivileges(privMgr);
+    std::sort(groupsMgr.begin(), groupsMgr.end());
+    UserMgrIface::allGroups(groupsMgr);
+    initUserObjects();
+}
+
+} // namespace user
+} // namespace phosphor
diff --git a/user_mgr.hpp b/user_mgr.hpp
new file mode 100644
index 0000000..44e14f7
--- /dev/null
+++ b/user_mgr.hpp
@@ -0,0 +1,207 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+#pragma once
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server/object.hpp>
+#include <xyz/openbmc_project/User/Manager/server.hpp>
+#include <unordered_map>
+#include "users.hpp"
+
+namespace phosphor
+{
+namespace user
+{
+
+using UserMgrIface = sdbusplus::xyz::openbmc_project::User::server::Manager;
+using UserSSHLists =
+    std::pair<std::vector<std::string>, std::vector<std::string>>;
+/** @class UserMgr
+ *  @brief Responsible for managing user accounts over the D-Bus interface.
+ */
+class UserMgr : public UserMgrIface
+{
+  public:
+    UserMgr() = delete;
+    ~UserMgr() = default;
+    UserMgr(const UserMgr &) = delete;
+    UserMgr &operator=(const UserMgr &) = delete;
+    UserMgr(UserMgr &&) = delete;
+    UserMgr &operator=(UserMgr &&) = delete;
+
+    /** @brief Constructs UserMgr object.
+     *
+     *  @param[in] bus  - sdbusplus handler
+     *  @param[in] path - D-Bus path
+     */
+    UserMgr(sdbusplus::bus::bus &bus, const char *path);
+
+    /** @brief create user method.
+     *  This method creates a new user as requested
+     *
+     *  @param[in] userName - Name of the user which has to be created
+     *  @param[in] groupNames - Group names list, to which user has to be added.
+     *  @param[in] priv - Privilege of the user.
+     *  @param[in] enabled - State of the user enabled / disabled.
+     */
+    void createUser(std::string userName, std::vector<std::string> groupNames,
+                    std::string priv, bool enabled) override;
+
+    /** @brief rename user method.
+     *  This method renames the user as requested
+     *
+     *  @param[in] userName - current name of the user
+     *  @param[in] newUserName - new user name to which it has to be renamed.
+     */
+    void renameUser(std::string userName, std::string newUserName) override;
+
+    /** @brief delete user method.
+     *  This method deletes the user as requested
+     *
+     *  @param[in] userName - Name of the user which has to be deleted
+     */
+    void deleteUser(std::string userName);
+
+    /** @brief Update user groups & privilege.
+     *  This method updates user groups & privilege
+     *
+     *  @param[in] userName - user name, for which update is requested
+     *  @param[in] groupName - Group to be updated..
+     *  @param[in] priv - Privilege to be updated.
+     */
+    void updateGroupsAndPriv(const std::string &userName,
+                             const std::vector<std::string> &groups,
+                             const std::string &priv);
+
+    /** @brief Update user enabled state.
+     *  This method enables / disables user
+     *
+     *  @param[in] userName - user name, for which update is requested
+     *  @param[in] enabled - enable / disable the user
+     */
+    void userEnable(const std::string &userName, bool enabled);
+
+  private:
+    /** @brief sdbusplus handler */
+    sdbusplus::bus::bus &bus;
+
+    /** @brief object path */
+    const std::string path;
+
+    /** @brief privilege manager container */
+    std::vector<std::string> privMgr = {"priv-admin", "priv-operator",
+                                        "priv-user", "priv-callback"};
+
+    /** @brief groups manager container */
+    std::vector<std::string> groupsMgr = {"web", "redfish", "ipmi", "ssh"};
+
+    /** @brief map container to hold users object */
+    using UserName = std::string;
+    std::unordered_map<UserName, std::unique_ptr<phosphor::user::Users>>
+        usersList;
+
+    /** @brief get users in group
+     *  method to get group user list
+     *
+     *  @param[in] groupName - group name
+     *
+     *  @return userList  - list of users in the group.
+     */
+    std::vector<std::string> getUsersInGroup(const std::string &groupName);
+
+    /** @brief get user & SSH users list
+     *  method to get the users and ssh users list.
+     *
+     *@return - vector of User & SSH user lists
+     */
+    UserSSHLists getUserAndSshGrpList(void);
+
+    /** @brief check for user presence
+     *  method to check for user existence
+     *
+     *  @param[in] userName - name of the user
+     *  @return -true if user exists and false if not.
+     */
+    bool isUserExist(const std::string &userName);
+
+    /** @brief check user exists
+     *  method to check whether user exist, and throw if not.
+     *
+     *  @param[in] userName - name of the user
+     */
+    void throwForUserDoesNotExist(const std::string &userName);
+
+    /** @brief check user does not exist
+     *  method to check whether does not exist, and throw if exists.
+     *
+     *  @param[in] userName - name of the user
+     */
+    void throwForUserExists(const std::string &userName);
+
+    /** @brief check user name constraints
+     *  method to check user name constraints and throw if failed.
+     *
+     *  @param[in] userName - name of the user
+     *  @param[in] groupNames - user groups
+     */
+    void
+        throwForUserNameConstraints(const std::string &userName,
+                                    const std::vector<std::string> &groupNames);
+
+    /** @brief check group user count
+     *  method to check max group user count, and throw if limit reached
+     *
+     *  @param[in] groupNames - group name
+     */
+    void throwForMaxGrpUserCount(const std::vector<std::string> &groupNames);
+
+    /** @brief check for valid privielge
+     *  method to check valid privilege, and throw if invalid
+     *
+     *  @param[in] priv - privilege of the user
+     */
+    void throwForInvalidPrivilege(const std::string &priv);
+
+    /** @brief check for valid groups
+     *  method to check valid groups, and throw if invalid
+     *
+     *  @param[in] groupNames - user groups
+     */
+    void throwForInvalidGroups(const std::vector<std::string> &groupName);
+
+    /** @brief get user enabled state
+     *  method to get user enabled state.
+     *
+     *  @param[in] userName - name of the user
+     *  @return - user enabled status (true/false)
+     */
+    bool isUserEnabled(const std::string &userName);
+
+    /** @brief initialize the user manager objects
+     *  method to initialize the user manager objects accordingly
+     *
+     */
+    void initUserObjects(void);
+
+    /** @brief get IPMI user count
+     *  method to get IPMI user count
+     *
+     * @return - returns user count
+     */
+    size_t getIpmiUsersCount(void);
+};
+
+} // namespace user
+} // namespace phosphor
diff --git a/users.cpp b/users.cpp
new file mode 100644
index 0000000..c904916
--- /dev/null
+++ b/users.cpp
@@ -0,0 +1,146 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#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 "user_mgr.hpp"
+#include "users.hpp"
+#include "config.h"
+
+namespace phosphor
+{
+namespace user
+{
+
+using namespace phosphor::logging;
+using InsufficientPermission =
+    sdbusplus::xyz::openbmc_project::Common::Error::InsufficientPermission;
+using InternalFailure =
+    sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+using InvalidArgument =
+    sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
+using NoResource =
+    sdbusplus::xyz::openbmc_project::User::Common::Error::NoResource;
+
+using Argument = xyz::openbmc_project::Common::InvalidArgument;
+
+/** @brief Constructs UserMgr object.
+ *
+ *  @param[in] bus  - sdbusplus handler
+ *  @param[in] path - D-Bus path
+ *  @param[in] groups - users group list
+ *  @param[in] priv - user privilege
+ *  @param[in] enabled - user enabled state
+ *  @param[in] parent - user manager - parent object
+ */
+Users::Users(sdbusplus::bus::bus &bus, const char *path,
+             std::vector<std::string> groups, std::string priv, bool enabled,
+             UserMgr &parent) :
+    UsersIface(bus, path, true),
+    DeleteIface(bus, path),
+    userName(std::experimental::filesystem::path(path).filename()),
+    manager(parent)
+{
+    UsersIface::userPrivilege(priv, true);
+    UsersIface::userGroups(groups, true);
+    UsersIface::userEnabled(enabled, true);
+    UsersIface::emit_object_added();
+}
+
+/** @brief delete user method.
+ *  This method deletes the user as requested
+ *
+ */
+void Users::delete_(void)
+{
+    manager.deleteUser(userName);
+}
+
+/** @brief update user privilege
+ *
+ *  @param[in] value - User privilege
+ */
+std::string Users::userPrivilege(std::string value)
+{
+    if (value == UsersIface::userPrivilege())
+    {
+        return value;
+    }
+    manager.updateGroupsAndPriv(userName, UsersIface::userGroups(), value);
+    return UsersIface::userPrivilege(value);
+}
+
+/** @brief list user privilege
+ *
+ */
+std::string Users::userPrivilege(void) const
+{
+    return UsersIface::userPrivilege();
+}
+
+/** @brief update user groups
+ *
+ *  @param[in] value - User groups
+ */
+std::vector<std::string> Users::userGroups(std::vector<std::string> value)
+{
+    if (value == UsersIface::userGroups())
+    {
+        return value;
+    }
+    std::sort(value.begin(), value.end());
+    manager.updateGroupsAndPriv(userName, value, UsersIface::userPrivilege());
+    return UsersIface::userGroups(value);
+}
+
+/** @brief list user groups
+ *
+ */
+std::vector<std::string> Users::userGroups(void) const
+{
+    return UsersIface::userGroups();
+}
+
+/** @brief lists user enabled state
+ *
+ */
+bool Users::userEnabled(void) const
+{
+    return UsersIface::userEnabled();
+}
+
+/** @brief update user enabled state
+ *
+ *  @param[in] value - bool value
+ */
+bool Users::userEnabled(bool value)
+{
+    if (value == UsersIface::userEnabled())
+    {
+        return value;
+    }
+    manager.userEnable(userName, value);
+    return UsersIface::userEnabled(value);
+}
+
+} // namespace user
+} // namespace phosphor
diff --git a/users.hpp b/users.hpp
new file mode 100644
index 0000000..84a0f86
--- /dev/null
+++ b/users.hpp
@@ -0,0 +1,111 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+#pragma once
+#include <experimental/filesystem>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server/object.hpp>
+#include <xyz/openbmc_project/User/Attributes/server.hpp>
+#include <xyz/openbmc_project/Object/Delete/server.hpp>
+
+namespace phosphor
+{
+namespace user
+{
+
+namespace Base = sdbusplus::xyz::openbmc_project;
+using UsersIface =
+    sdbusplus::server::object::object<Base::User::server::Attributes>;
+using DeleteIface =
+    sdbusplus::server::object::object<Base::Object::server::Delete>;
+
+// Place where all user objects has to be created
+constexpr auto usersObjPath = "/xyz/openbmc_project/user";
+
+class UserMgr; // Forward declaration for UserMgr.
+
+/** @class Users
+ *  @brief Lists User objects and it's properties
+ */
+class Users : public UsersIface, DeleteIface
+{
+  public:
+    Users() = delete;
+    ~Users() = default;
+    Users(const Users &) = delete;
+    Users &operator=(const Users &) = delete;
+    Users(Users &&) = delete;
+    Users &operator=(Users &&) = delete;
+
+    /** @brief Constructs UserMgr object.
+     *
+     *  @param[in] bus  - sdbusplus handler
+     *  @param[in] path - D-Bus path
+     *  @param[in] groups - users group list
+     *  @param[in] priv - users privilege
+     *  @param[in] enabled - user enabled state
+     *  @param[in] parent - user manager - parent object
+     */
+    Users(sdbusplus::bus::bus &bus, const char *path,
+          std::vector<std::string> groups, std::string priv, bool enabled,
+          UserMgr &parent);
+
+    /** @brief delete user method.
+     *  This method deletes the user as requested
+     *
+     */
+    void delete_(void) override;
+
+    /** @brief update user privilege
+     *
+     *  @param[in] value - User privilege
+     */
+    std::string userPrivilege(std::string value) override;
+
+    /** @brief lists user privilege
+     *
+     */
+    std::string userPrivilege(void) const override;
+
+    /** @brief update user groups
+     *
+     *  @param[in] value - User groups
+     */
+    std::vector<std::string>
+        userGroups(std::vector<std::string> value) override;
+
+    /** @brief list user groups
+     *
+     */
+    std::vector<std::string> userGroups(void) const override;
+
+    /** @brief lists user enabled state
+     *
+     */
+    bool userEnabled(void) const override;
+
+    /** @brief update user enabled state
+     *
+     *  @param[in] value - bool value
+     */
+    bool userEnabled(bool value) override;
+
+  private:
+    std::string userName;
+    UserMgr &manager;
+};
+
+} // namespace user
+} // namespace phosphor