Added new pre-defined usergroup hostconsole

The new pre-defined usergroup named "hostconsole" is added to
differentiate access between host console and manager console.
The only users allowed to interact with host console are part of the
"hostconsole" group.

Note: The changes are spread across multiple repositories listed under
"Related commits:"

The phosphor-user-manager changes are as follows:
- Added new pre-defined user group called "hostconsole"
- Added CI tests to validate the new group.

Tested:
  Loaded on system and qemu eumulator. Made sure that user is only
  allowed to access host console if it is member of hostconsole group.

Related commits:
  docs: https://gerrit.openbmc.org/c/openbmc/docs/+/60968
  phosphor-user-manager: https://gerrit.openbmc.org/c/openbmc/phosphor-user-manager/+/61583
  openbmc: https://gerrit.openbmc.org/c/openbmc/openbmc/+/61582
  obmc-console: https://gerrit.openbmc.org/c/openbmc/obmc-console/+/61581
  bmcweb: https://gerrit.openbmc.org/c/openbmc/bmcweb/+/61580

Change-Id: I700a295c99c429b42e6db667c9726792a351e71d
Signed-off-by: Ninad Palsule <ninadpalsule@us.ibm.com>
diff --git a/test/user_mgr_test.cpp b/test/user_mgr_test.cpp
index 40751e9..0bd5e1c 100644
--- a/test/user_mgr_test.cpp
+++ b/test/user_mgr_test.cpp
@@ -579,6 +579,7 @@
     EXPECT_NO_THROW(throwForInvalidGroups({"ssh"}));
     EXPECT_NO_THROW(throwForInvalidGroups({"redfish"}));
     EXPECT_NO_THROW(throwForInvalidGroups({"web"}));
+    EXPECT_NO_THROW(throwForInvalidGroups({"hostconsole"}));
 }
 
 TEST_F(UserMgrInTest, RenameUserOnSuccess)
@@ -805,6 +806,20 @@
     EXPECT_NO_THROW(UserMgr::deleteUser(username));
 }
 
+TEST_F(UserMgrInTest, CreateDeleteUserSuccessForHostConsole)
+{
+    std::string username = "user001";
+    EXPECT_NO_THROW(
+        UserMgr::createUser(username, {"hostconsole"}, "priv-user", true));
+    EXPECT_NO_THROW(UserMgr::deleteUser(username));
+    EXPECT_NO_THROW(
+        UserMgr::createUser(username, {"hostconsole"}, "priv-admin", true));
+    EXPECT_NO_THROW(UserMgr::deleteUser(username));
+    EXPECT_NO_THROW(
+        UserMgr::createUser(username, {"hostconsole"}, "priv-operator", true));
+    EXPECT_NO_THROW(UserMgr::deleteUser(username));
+}
+
 TEST_F(UserMgrInTest, UserEnableThrowsInternalFailureIfExecuteUserModifyFail)
 {
     std::string username = "user001";
@@ -959,8 +974,10 @@
 
 TEST_F(UserMgrInTest, CheckAndThrowForMaxGroupCountOnSuccess)
 {
-    EXPECT_THAT(allGroups().size(), 4);
-    for (size_t i = 0; i < maxSystemGroupCount - 4; ++i)
+    constexpr size_t predefGroupCount = 5;
+
+    EXPECT_THAT(allGroups().size(), predefGroupCount);
+    for (size_t i = 0; i < maxSystemGroupCount - predefGroupCount; ++i)
     {
         std::string groupName = "openbmc_rfr_role";
         groupName += std::to_string(i);
@@ -969,7 +986,7 @@
     EXPECT_THROW(
         createGroup("openbmc_rfr_AnotherRole"),
         sdbusplus::xyz::openbmc_project::User::Common::Error::NoResource);
-    for (size_t i = 0; i < maxSystemGroupCount - 4; ++i)
+    for (size_t i = 0; i < maxSystemGroupCount - predefGroupCount; ++i)
     {
         std::string groupName = "openbmc_rfr_role";
         groupName += std::to_string(i);
@@ -990,7 +1007,27 @@
 TEST_F(UserMgrInTest, ByDefaultAllGroupsArePredefinedGroups)
 {
     EXPECT_THAT(allGroups(),
-                testing::UnorderedElementsAre("web", "redfish", "ipmi", "ssh"));
+                testing::UnorderedElementsAre("web", "redfish", "ipmi", "ssh",
+                                              "hostconsole"));
+}
+
+TEST_F(UserMgrInTest, AddGroupThrowsIfPreDefinedGroupAdd)
+{
+    EXPECT_THROW(
+        createGroup("ipmi"),
+        sdbusplus::xyz::openbmc_project::User::Common::Error::GroupNameExists);
+    EXPECT_THROW(
+        createGroup("web"),
+        sdbusplus::xyz::openbmc_project::User::Common::Error::GroupNameExists);
+    EXPECT_THROW(
+        createGroup("redfish"),
+        sdbusplus::xyz::openbmc_project::User::Common::Error::GroupNameExists);
+    EXPECT_THROW(
+        createGroup("ssh"),
+        sdbusplus::xyz::openbmc_project::User::Common::Error::GroupNameExists);
+    EXPECT_THROW(
+        createGroup("hostconsole"),
+        sdbusplus::xyz::openbmc_project::User::Common::Error::GroupNameExists);
 }
 
 TEST_F(UserMgrInTest, DeleteGroupThrowsIfGroupIsNotAllowedToChange)
@@ -1007,6 +1044,9 @@
     EXPECT_THROW(
         deleteGroup("ssh"),
         sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
+    EXPECT_THROW(
+        deleteGroup("hostconsole"),
+        sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
 }
 
 TEST_F(UserMgrInTest,
@@ -1046,7 +1086,8 @@
 TEST(ReadAllGroupsOnSystemTest, OnlyReturnsPredefinedGroups)
 {
     EXPECT_THAT(UserMgr::readAllGroupsOnSystem(),
-                testing::UnorderedElementsAre("web", "redfish", "ipmi", "ssh"));
+                testing::UnorderedElementsAre("web", "redfish", "ipmi", "ssh",
+                                              "hostconsole"));
 }
 
 } // namespace user
diff --git a/user_mgr.cpp b/user_mgr.cpp
index 61d2224..edb25f3 100644
--- a/user_mgr.cpp
+++ b/user_mgr.cpp
@@ -110,8 +110,8 @@
 {
 
 // The hardcoded groups in OpenBMC projects
-constexpr std::array<const char*, 4> predefinedGroups = {"web", "redfish",
-                                                         "ipmi", "ssh"};
+constexpr std::array<const char*, 5> predefinedGroups = {
+    "web", "redfish", "ipmi", "ssh", "hostconsole"};
 
 // These prefixes are for Dynamic Redfish authorization. See
 // https://github.com/openbmc/docs/blob/master/designs/redfish-authorization.md