Redfish Session : Support ClientOriginIPAddress

This commit implements the ClientOriginIPAddress property on
the session resource. The IP address is persisted across the reboot

Tested by:
  1. Create session
     POST https://${bmc}/redfish/v1/SessionService/Sessions -d '{"UserName":<>, "Password":<>}'
  2. Check the session gets updated with the ClientOriginIPAddress
     GET https://${bmc}/redfish/v1/SessionService/Sessions/<id>
  3. Redfish validator passed
  4. Create session and reboot the BMC to ensure the IP address is persisted
  5. Tested the basic auth populates the clientIp at req

Signed-off-by: Sunitha Harish <sunharis@in.ibm.com>
Change-Id: Iaa60d0657c991bde4bcf6c86819055c71c92e421
diff --git a/http/http_connection.hpp b/http/http_connection.hpp
index ec8e9d5..6610bee 100644
--- a/http/http_connection.hpp
+++ b/http/http_connection.hpp
@@ -241,7 +241,8 @@
             session =
                 persistent_data::SessionStore::getInstance()
                     .generateUserSession(
-                        sslUser, persistent_data::PersistenceType::TIMEOUT);
+                        sslUser, persistent_data::PersistenceType::TIMEOUT,
+                        false, req->ipAddress.to_string());
             if (auto sp = session.lock())
             {
                 BMCWEB_LOG_DEBUG << this
@@ -278,6 +279,10 @@
     {
 
         startDeadline(0);
+
+        // Fetch the client IP address
+        readClientIp();
+
         // TODO(ed) Abstract this to a more clever class with the idea of an
         // asynchronous "start"
         if constexpr (std::is_same_v<Adaptor,
@@ -319,7 +324,7 @@
         BMCWEB_LOG_INFO << "Request: "
                         << " " << this << " HTTP/" << req->version() / 10 << "."
                         << req->version() % 10 << ' ' << req->methodString()
-                        << " " << req->target();
+                        << " " << req->target() << " " << req->ipAddress;
 
         needToCallAfterHandlers = false;
 
@@ -461,6 +466,26 @@
         res.completeRequestHandler = nullptr;
     }
 
+    void readClientIp()
+    {
+        boost::system::error_code ec;
+        BMCWEB_LOG_DEBUG << "Fetch the client IP address";
+        boost::asio::ip::tcp::endpoint endpoint =
+            boost::beast::get_lowest_layer(adaptor).remote_endpoint(ec);
+
+        if (ec)
+        {
+            // If remote endpoint fails keep going. "ClientOriginIPAddress"
+            // will be empty.
+            BMCWEB_LOG_ERROR << "Failed to get the client's IP Address. ec : "
+                             << ec;
+        }
+        else
+        {
+            req->ipAddress = endpoint.address();
+        }
+    }
+
   private:
     void doReadHeaders()
     {
diff --git a/http/http_request.hpp b/http/http_request.hpp
index e5c0c9e..8bcce06 100644
--- a/http/http_request.hpp
+++ b/http/http_request.hpp
@@ -4,6 +4,7 @@
 #include "sessions.hpp"
 
 #include <boost/asio/io_context.hpp>
+#include <boost/asio/ip/address.hpp>
 #include <boost/beast/http/message.hpp>
 #include <boost/beast/http/string_body.hpp>
 #include <boost/beast/websocket.hpp>
@@ -24,6 +25,7 @@
     const std::string& body;
 
     boost::asio::io_context* ioService{};
+    boost::asio::ip::address ipAddress{};
 
     std::shared_ptr<persistent_data::UserSession> session;