Add PATCH support for SessionTimeout Property

- This commit would add the patch support for the session
  timeout propery under the sessionservice.
- This commit also brings in support for persistent session
  timeout property.

Tested By:
1. Redfish validator passed.
2. PATCH the session time out property using the below command

PATCH -d '{"SessionTimeout": 100}' https://<bmcip>/redfish/v1/SessionService

3. GET on sessionservice should return the value of time out which is
   patched by using the above command & also GET on the session service fails
   with Unauthorized error post the patched timeout value.
4. And also, the existing sessions that are open for the new timeout value are
   also closed.
5. As per the schema , the range of values that are allowed for
   session timeout are between 30 sec to 86400 sec, so any value which
   is patched out of the range is failed with an appropriate error message.
6. PATCH the session timeout to new value using 2, and them restart the bmcweb
   and the GET using 3 should return the new value.

Signed-off-by: Manojkiran Eda <manojkiran.eda@gmail.com>
Change-Id: Id50eacc5018b7a82371fd37a2ae1e7fb7596ed2b
diff --git a/include/persistent_data.hpp b/include/persistent_data.hpp
index 5d4661c..03457c7 100644
--- a/include/persistent_data.hpp
+++ b/include/persistent_data.hpp
@@ -112,6 +112,22 @@
                                 newSession->sessionToken, newSession);
                         }
                     }
+                    else if (item.key() == "timeout")
+                    {
+                        const int64_t* jTimeout =
+                            item.value().get_ptr<int64_t*>();
+                        if (jTimeout == nullptr)
+                        {
+                            BMCWEB_LOG_DEBUG
+                                << "Problem reading session timeout value";
+                            continue;
+                        }
+                        std::chrono::seconds sessionTimeoutInseconds(*jTimeout);
+                        BMCWEB_LOG_DEBUG << "Restored Session Timeout: "
+                                         << sessionTimeoutInseconds.count();
+                        SessionStore::getInstance().updateSessionTimeout(
+                            sessionTimeoutInseconds);
+                    }
                     else
                     {
                         // Do nothing in the case of extra fields.  We may have
@@ -157,7 +173,8 @@
             {"sessions", SessionStore::getInstance().authTokens},
             {"auth_config", SessionStore::getInstance().getAuthMethodsConfig()},
             {"system_uuid", systemUuid},
-            {"revision", jsonRevision}};
+            {"revision", jsonRevision},
+            {"timeout", SessionStore::getInstance().getTimeoutInSeconds()}};
         persistentFile << data;
     }
 
diff --git a/include/sessions.hpp b/include/sessions.hpp
index e745845..cd549dc 100644
--- a/include/sessions.hpp
+++ b/include/sessions.hpp
@@ -358,7 +358,13 @@
     }
     int64_t getTimeoutInSeconds() const
     {
-        return std::chrono::seconds(timeoutInMinutes).count();
+        return std::chrono::seconds(timeoutInSeconds).count();
+    }
+
+    void updateSessionTimeout(std::chrono::seconds newTimeoutInSeconds)
+    {
+        timeoutInSeconds = newTimeoutInSeconds;
+        needWrite = true;
     }
 
     static SessionStore& getInstance()
@@ -377,24 +383,24 @@
 
     std::chrono::time_point<std::chrono::steady_clock> lastTimeoutUpdate;
     bool needWrite{false};
-    std::chrono::minutes timeoutInMinutes;
+    std::chrono::seconds timeoutInSeconds;
     AuthConfigMethods authMethodsConfig;
 
   private:
-    SessionStore() : timeoutInMinutes(60)
+    SessionStore() : timeoutInSeconds(3600)
     {}
 
     void applySessionTimeouts()
     {
         auto timeNow = std::chrono::steady_clock::now();
-        if (timeNow - lastTimeoutUpdate > std::chrono::minutes(1))
+        if (timeNow - lastTimeoutUpdate > std::chrono::seconds(1))
         {
             lastTimeoutUpdate = timeNow;
             auto authTokensIt = authTokens.begin();
             while (authTokensIt != authTokens.end())
             {
                 if (timeNow - authTokensIt->second->lastUpdated >=
-                    timeoutInMinutes)
+                    timeoutInSeconds)
                 {
 #ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE
                     crow::ibm_mc_lock::Lock::getInstance().releaseLock(
diff --git a/redfish-core/lib/redfish_sessions.hpp b/redfish-core/lib/redfish_sessions.hpp
index fbbffcb..e325989 100644
--- a/redfish-core/lib/redfish_sessions.hpp
+++ b/redfish-core/lib/redfish_sessions.hpp
@@ -285,6 +285,40 @@
 
         res.end();
     }
+
+    void doPatch(crow::Response& res, const crow::Request& req,
+                 const std::vector<std::string>&) override
+    {
+        std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
+        std::optional<int64_t> sessionTimeout;
+        if (!json_util::readJson(req, res, "SessionTimeout", sessionTimeout))
+        {
+            return;
+        }
+
+        if (sessionTimeout)
+        {
+            // The mininum & maximum allowed values for session timeout are 30
+            // seconds and 86400 seconds respectively as per the session service
+            // schema mentioned at
+            // https://redfish.dmtf.org/schemas/v1/SessionService.v1_1_7.json
+
+            if (*sessionTimeout <= 86400 && *sessionTimeout >= 30)
+            {
+                std::chrono::seconds sessionTimeoutInseconds(*sessionTimeout);
+                persistent_data::SessionStore::getInstance()
+                    .updateSessionTimeout(sessionTimeoutInseconds);
+                messages::propertyValueModified(
+                    asyncResp->res, "SessionTimeOut",
+                    std::to_string(*sessionTimeout));
+            }
+            else
+            {
+                messages::propertyValueNotInList(
+                    res, std::to_string(*sessionTimeout), "SessionTimeOut");
+            }
+        }
+    }
 };
 
 } // namespace redfish