Move SessionService to free methods
In line with other patchsets doing the same thing, move SessionService
to free methods.
Tested:
curl --insecure -X POST -d "{\"UserName\": \"root\", \"Password\": \"0penBmc\"}" https://192.168.7.2/redfish/v1/SessionService/Sessions
succeeds and returns a result.
redfishtool -S Always -A Session -u root -p 0penBmc -vvvvv -r 192.168.7.2 raw GET "/redfish/v1/SessionService/Sessions"
Succeeds and returns the sesion created previously, and deletes its own
session successfully.
Signed-off-by: Ed Tanous <edtanous@google.com>
Change-Id: I38aeeef2143898510e6c645719deaa7d56a418dc
diff --git a/redfish-core/lib/redfish_sessions.hpp b/redfish-core/lib/redfish_sessions.hpp
index a66f460..5e7d3df 100644
--- a/redfish-core/lib/redfish_sessions.hpp
+++ b/redfish-core/lib/redfish_sessions.hpp
@@ -25,8 +25,6 @@
namespace redfish
{
-class SessionCollection;
-
inline void fillSessionObject(crow::Response& res,
const persistent_data::UserSession& session)
{
@@ -45,240 +43,252 @@
#endif
}
+inline void
+ handleSessionGet(const crow::Request& /*req*/,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& sessionId)
+{
+ // Note that control also reaches here via doPost and doDelete.
+ auto session =
+ persistent_data::SessionStore::getInstance().getSessionByUid(sessionId);
+
+ if (session == nullptr)
+ {
+ messages::resourceNotFound(asyncResp->res, "Session", sessionId);
+ return;
+ }
+
+ fillSessionObject(asyncResp->res, *session);
+}
+
+inline void
+ handleSessionDelete(const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& sessionId)
+{
+ auto session =
+ persistent_data::SessionStore::getInstance().getSessionByUid(sessionId);
+
+ if (session == nullptr)
+ {
+ messages::resourceNotFound(asyncResp->res, "Session", sessionId);
+ return;
+ }
+
+ // Perform a proper ConfigureSelf authority check. If a
+ // session is being used to DELETE some other user's session,
+ // then the ConfigureSelf privilege does not apply. In that
+ // case, perform the authority check again without the user's
+ // ConfigureSelf privilege.
+ if (session->username != req.session->username)
+ {
+ Privileges effectiveUserPrivileges =
+ redfish::getUserPrivileges(req.userRole);
+
+ if (!effectiveUserPrivileges.isSupersetOf({"ConfigureUsers"}))
+ {
+ messages::insufficientPrivilege(asyncResp->res);
+ return;
+ }
+ }
+
+ persistent_data::SessionStore::getInstance().removeSession(session);
+ messages::success(asyncResp->res);
+}
+
+inline nlohmann::json getSessionCollectionMembers()
+{
+ std::vector<const std::string*> sessionIds =
+ persistent_data::SessionStore::getInstance().getUniqueIds(
+ false, persistent_data::PersistenceType::TIMEOUT);
+ nlohmann::json ret = nlohmann::json::array();
+ for (const std::string* uid : sessionIds)
+ {
+ ret.push_back(
+ {{"@odata.id", "/redfish/v1/SessionService/Sessions/" + *uid}});
+ }
+ return ret;
+}
+
+inline void handleSessionCollectionGet(
+ const crow::Request& /*req*/,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+{
+ asyncResp->res.jsonValue["Members"] = getSessionCollectionMembers();
+ asyncResp->res.jsonValue["Members@odata.count"] =
+ asyncResp->res.jsonValue["Members"].size();
+ asyncResp->res.jsonValue["@odata.type"] =
+ "#SessionCollection.SessionCollection";
+ asyncResp->res.jsonValue["@odata.id"] =
+ "/redfish/v1/SessionService/Sessions/";
+ asyncResp->res.jsonValue["Name"] = "Session Collection";
+ asyncResp->res.jsonValue["Description"] = "Session Collection";
+}
+
+inline void handleSessionCollectionMembersGet(
+ const crow::Request& /*req*/,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+{
+ asyncResp->res.jsonValue = getSessionCollectionMembers();
+}
+
+void handleSessionCollectionPost(
+ const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+{
+ std::string username;
+ std::string password;
+ std::optional<nlohmann::json> oemObject;
+ std::string clientId;
+ if (!json_util::readJsonPatch(req, asyncResp->res, "UserName", username,
+ "Password", password, "Oem", oemObject))
+ {
+ return;
+ }
+
+ if (password.empty() || username.empty() ||
+ asyncResp->res.result() != boost::beast::http::status::ok)
+ {
+ if (username.empty())
+ {
+ messages::propertyMissing(asyncResp->res, "UserName");
+ }
+
+ if (password.empty())
+ {
+ messages::propertyMissing(asyncResp->res, "Password");
+ }
+
+ return;
+ }
+
+ int pamrc = pamAuthenticateUser(username, password);
+ bool isConfigureSelfOnly = pamrc == PAM_NEW_AUTHTOK_REQD;
+ if ((pamrc != PAM_SUCCESS) && !isConfigureSelfOnly)
+ {
+ messages::resourceAtUriUnauthorized(asyncResp->res, req.urlView,
+ "Invalid username or password");
+ return;
+ }
+#ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE
+ if (oemObject)
+ {
+ std::optional<nlohmann::json> bmcOem;
+ if (!json_util::readJson(*oemObject, asyncResp->res, "OpenBMC", bmcOem))
+ {
+ return;
+ }
+ if (!json_util::readJson(*bmcOem, asyncResp->res, "ClientID", clientId))
+ {
+ BMCWEB_LOG_ERROR << "Could not read ClientId";
+ return;
+ }
+ }
+#endif
+
+ // User is authenticated - create session
+ std::shared_ptr<persistent_data::UserSession> session =
+ persistent_data::SessionStore::getInstance().generateUserSession(
+ username, req.ipAddress, clientId,
+ persistent_data::PersistenceType::TIMEOUT, isConfigureSelfOnly);
+ asyncResp->res.addHeader("X-Auth-Token", session->sessionToken);
+ asyncResp->res.addHeader(
+ "Location", "/redfish/v1/SessionService/Sessions/" + session->uniqueId);
+ asyncResp->res.result(boost::beast::http::status::created);
+ if (session->isConfigureSelfOnly)
+ {
+ messages::passwordChangeRequired(
+ asyncResp->res,
+ crow::utility::urlFromPieces("redfish", "v1", "AccountService",
+ "Accounts", req.session->username));
+ }
+
+ fillSessionObject(asyncResp->res, *session);
+}
+inline void
+ handleSessionServiceGet(const crow::Request& /* req */,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+
+{
+ asyncResp->res.jsonValue["@odata.type"] =
+ "#SessionService.v1_0_2.SessionService";
+ asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/SessionService/";
+ asyncResp->res.jsonValue["Name"] = "Session Service";
+ asyncResp->res.jsonValue["Id"] = "SessionService";
+ asyncResp->res.jsonValue["Description"] = "Session Service";
+ asyncResp->res.jsonValue["SessionTimeout"] =
+ persistent_data::SessionStore::getInstance().getTimeoutInSeconds();
+ asyncResp->res.jsonValue["ServiceEnabled"] = true;
+
+ asyncResp->res.jsonValue["Sessions"] = {
+ {"@odata.id", "/redfish/v1/SessionService/Sessions"}};
+}
+
+inline void handleSessionServicePatch(
+ const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+{
+ std::optional<int64_t> sessionTimeout;
+ if (!json_util::readJsonPatch(req, asyncResp->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(asyncResp->res,
+ std::to_string(*sessionTimeout),
+ "SessionTimeOut");
+ }
+ }
+}
+
inline void requestRoutesSession(App& app)
{
BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/<str>/")
.privileges(redfish::privileges::getSession)
- .methods(boost::beast::http::verb::get)(
- [](const crow::Request& /*req*/,
- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
- const std::string& sessionId) -> void {
- // Note that control also reaches here via doPost and doDelete.
- auto session = persistent_data::SessionStore::getInstance()
- .getSessionByUid(sessionId);
-
- if (session == nullptr)
- {
- messages::resourceNotFound(asyncResp->res, "Session",
- sessionId);
- return;
- }
-
- fillSessionObject(asyncResp->res, *session);
- });
+ .methods(boost::beast::http::verb::get)(handleSessionGet);
BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/<str>/")
.privileges(redfish::privileges::deleteSession)
- .methods(boost::beast::http::verb::delete_)(
- [](const crow::Request& req,
- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
- const std::string& sessionId) -> void {
- auto session = persistent_data::SessionStore::getInstance()
- .getSessionByUid(sessionId);
-
- if (session == nullptr)
- {
- messages::resourceNotFound(asyncResp->res, "Session",
- sessionId);
- return;
- }
-
- // Perform a proper ConfigureSelf authority check. If a
- // session is being used to DELETE some other user's session,
- // then the ConfigureSelf privilege does not apply. In that
- // case, perform the authority check again without the user's
- // ConfigureSelf privilege.
- if (session->username != req.session->username)
- {
- Privileges effectiveUserPrivileges =
- redfish::getUserPrivileges(req.userRole);
-
- if (!effectiveUserPrivileges.isSupersetOf(
- {"ConfigureUsers"}))
- {
- messages::insufficientPrivilege(asyncResp->res);
- return;
- }
- }
-
- persistent_data::SessionStore::getInstance().removeSession(
- session);
- messages::success(asyncResp->res);
- });
+ .methods(boost::beast::http::verb::delete_)(handleSessionDelete);
BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/")
.privileges(redfish::privileges::getSessionCollection)
- .methods(boost::beast::http::verb::get)(
- [](const crow::Request& /*req*/,
- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
- std::vector<const std::string*> sessionIds =
- persistent_data::SessionStore::getInstance().getUniqueIds(
- false, persistent_data::PersistenceType::TIMEOUT);
+ .methods(boost::beast::http::verb::get)(handleSessionCollectionGet);
- asyncResp->res.jsonValue["Members@odata.count"] =
- sessionIds.size();
- asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
- for (const std::string* uid : sessionIds)
- {
- asyncResp->res.jsonValue["Members"].push_back(
- {{"@odata.id",
- "/redfish/v1/SessionService/Sessions/" + *uid}});
- }
- asyncResp->res.jsonValue["Members@odata.count"] =
- sessionIds.size();
- asyncResp->res.jsonValue["@odata.type"] =
- "#SessionCollection.SessionCollection";
- asyncResp->res.jsonValue["@odata.id"] =
- "/redfish/v1/SessionService/Sessions/";
- asyncResp->res.jsonValue["Name"] = "Session Collection";
- asyncResp->res.jsonValue["Description"] = "Session Collection";
- });
-
+ // Note, the next route technically doesn't match the privilege
+ // registry given the way login mechanisms work. The base privilege
+ // registry lists this endpoint as requiring login privilege, but because
+ // this is the endpoint responsible for giving the login privilege, and it
+ // is itself its own route, it needs to not require Login
BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/")
- // Note, this technically doesn't match the privilege registry given the
- // way login mechanisms work. The base privilege registry lists this
- // endpoint as requiring login privilege, but because this is the
- // endpoint responsible for giving the login privilege, and it is itself
- // its own route, it needs to not require Login
.privileges({})
- .methods(boost::beast::http::verb::post)(
- [](const crow::Request& req,
- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
- std::string username;
- std::string password;
- std::optional<nlohmann::json> oemObject;
- std::string clientId;
- if (!json_util::readJsonPatch(req, asyncResp->res, "UserName",
- username, "Password", password,
- "Oem", oemObject))
- {
- return;
- }
-
- if (password.empty() || username.empty() ||
- asyncResp->res.result() != boost::beast::http::status::ok)
- {
- if (username.empty())
- {
- messages::propertyMissing(asyncResp->res, "UserName");
- }
-
- if (password.empty())
- {
- messages::propertyMissing(asyncResp->res, "Password");
- }
-
- return;
- }
-
- int pamrc = pamAuthenticateUser(username, password);
- bool isConfigureSelfOnly = pamrc == PAM_NEW_AUTHTOK_REQD;
- if ((pamrc != PAM_SUCCESS) && !isConfigureSelfOnly)
- {
- messages::resourceAtUriUnauthorized(
- asyncResp->res, req.urlView,
- "Invalid username or password");
- return;
- }
-#ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE
- if (oemObject)
- {
- std::optional<nlohmann::json> bmcOem;
- if (!json_util::readJson(*oemObject, asyncResp->res,
- "OpenBMC", bmcOem))
- {
- return;
- }
- if (!json_util::readJson(*bmcOem, asyncResp->res,
- "ClientID", clientId))
- {
- BMCWEB_LOG_ERROR << "Could not read ClientId";
- return;
- }
- }
-#endif
-
- // User is authenticated - create session
- std::shared_ptr<persistent_data::UserSession> session =
- persistent_data::SessionStore::getInstance()
- .generateUserSession(
- username, req.ipAddress, clientId,
- persistent_data::PersistenceType::TIMEOUT,
- isConfigureSelfOnly);
- asyncResp->res.addHeader("X-Auth-Token", session->sessionToken);
- asyncResp->res.addHeader(
- "Location",
- "/redfish/v1/SessionService/Sessions/" + session->uniqueId);
- asyncResp->res.result(boost::beast::http::status::created);
- if (session->isConfigureSelfOnly)
- {
- messages::passwordChangeRequired(
- asyncResp->res, crow::utility::urlFromPieces(
- "redfish", "v1", "AccountService",
- "Accounts", req.session->username));
- }
-
- fillSessionObject(asyncResp->res, *session);
- });
+ .methods(boost::beast::http::verb::post)(handleSessionCollectionPost);
BMCWEB_ROUTE(app, "/redfish/v1/SessionService/")
.privileges(redfish::privileges::getSessionService)
- .methods(boost::beast::http::verb::get)(
- [](const crow::Request& /* req */,
- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
- asyncResp->res.jsonValue["@odata.type"] =
- "#SessionService.v1_0_2.SessionService";
- asyncResp->res.jsonValue["@odata.id"] =
- "/redfish/v1/SessionService/";
- asyncResp->res.jsonValue["Name"] = "Session Service";
- asyncResp->res.jsonValue["Id"] = "SessionService";
- asyncResp->res.jsonValue["Description"] = "Session Service";
- asyncResp->res.jsonValue["SessionTimeout"] =
- persistent_data::SessionStore::getInstance()
- .getTimeoutInSeconds();
- asyncResp->res.jsonValue["ServiceEnabled"] = true;
-
- asyncResp->res.jsonValue["Sessions"] = {
- {"@odata.id", "/redfish/v1/SessionService/Sessions"}};
- });
+ .methods(boost::beast::http::verb::get)(handleSessionServiceGet);
BMCWEB_ROUTE(app, "/redfish/v1/SessionService/")
.privileges(redfish::privileges::patchSessionService)
- .methods(boost::beast::http::verb::patch)(
- [](const crow::Request& req,
- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
- std::optional<int64_t> sessionTimeout;
- if (!json_util::readJsonPatch(req, asyncResp->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(
- asyncResp->res, std::to_string(*sessionTimeout),
- "SessionTimeOut");
- }
- }
- });
+ .methods(boost::beast::http::verb::patch)(handleSessionServicePatch);
}
} // namespace redfish