Clean up SessionService POST
Previously, SessionService would return a json decode error for all
errors, even if certain keys were incorrect. This updates the POST
handler to use a better method, and should reduce the binary size a
little, given that we're removing the possibly throwing get<> code.
This needs a very good review. This endpoint is available pre-auth, and
needs to be secure regardless of input.
Tested By:
curl --noproxy "*" -k -X POST -d '{"UserName": "root", "Password":
"0penBmc"}' https://10.243.48.30/redfish/v1/SessionService/Sessions/
Returns correct response.
Tested several variations, including extra parameters, missing username,
missing password, ect. Got correct error code in all cases.
Change-Id: Ic8e6edd9f7badc0d22aa8dafa9c0c260386712ac
Signed-off-by: Ed Tanous <ed.tanous@intel.com>
diff --git a/redfish-core/lib/redfish_sessions.hpp b/redfish-core/lib/redfish_sessions.hpp
index 2406250..145bbb1 100644
--- a/redfish-core/lib/redfish_sessions.hpp
+++ b/redfish-core/lib/redfish_sessions.hpp
@@ -168,157 +168,96 @@
void doPost(crow::Response& res, const crow::Request& req,
const std::vector<std::string>& params) override
{
- boost::beast::http::status status;
- std::string username;
- bool userAuthSuccessful =
- authenticateUser(req, status, username, res.jsonValue);
- res.result(status);
-
- if (!userAuthSuccessful)
+ nlohmann::json postRequest;
+ if (!json_util::processJsonFromRequest(res, req, postRequest))
{
res.end();
return;
}
- // User is authenticated - create session for him
- auto session = crow::persistent_data::SessionStore::getInstance()
- .generateUserSession(username);
- res.addHeader("X-Auth-Token", session->sessionToken);
-
- res.addHeader("Location", "/redfish/v1/SessionService/Sessions/" +
- session->uniqueId);
-
- // Return data for created session
- memberSession.doGet(res, req, {session->uniqueId});
-
- // No need for res.end(), as it is called by doGet()
- }
-
- /**
- * @brief Verifies data provided in request and tries to authenticate user
- *
- * @param[in] req Crow request containing authentication data
- * @param[out] httpRespCode HTTP Code that should be returned in response
- * @param[out] user Retrieved username - not filled on failure
- * @param[out] errJson JSON to which error messages will be written
- *
- * @return true if authentication was successful, false otherwise
- */
- bool authenticateUser(const crow::Request& req,
- boost::beast::http::status& httpRespCode,
- std::string& user, nlohmann::json& errJson)
- {
- // We need only UserName and Password - nothing more, nothing less
- static constexpr const unsigned int numberOfRequiredFieldsInReq = 2;
-
- // call with exceptions disabled
- auto loginCredentials = nlohmann::json::parse(req.body, nullptr, false);
- if (loginCredentials.is_discarded())
+ std::string username;
+ std::string password;
+ for (const auto& item : postRequest.items())
{
- httpRespCode = boost::beast::http::status::bad_request;
+ const std::string* strVal =
+ item.value().get_ptr<const std::string*>();
+ if (item.key() == "UserName")
+ {
+ if (strVal == nullptr)
+ {
+ res.result(boost::beast::http::status::bad_request);
+ messages::addMessageToErrorJson(
+ res.jsonValue, messages::propertyValueTypeError(
+ item.value().dump(), item.key()));
+ continue;
+ }
+ username = *strVal;
+ }
+ else if (item.key() == "Password")
+ {
+ if (strVal == nullptr)
+ {
+ res.result(boost::beast::http::status::bad_request);
+ messages::addMessageToErrorJson(
+ res.jsonValue, messages::propertyValueTypeError(
+ item.value().dump(), item.key()));
+ continue;
+ }
- messages::addMessageToErrorJson(errJson, messages::malformedJSON());
-
- return false;
+ password = *strVal;
+ }
+ else
+ {
+ res.result(boost::beast::http::status::bad_request);
+ messages::addMessageToErrorJson(
+ res.jsonValue, messages::propertyUnknown(item.key()));
+ continue;
+ }
}
- // Check that there are only as many fields as there should be
- if (loginCredentials.size() != numberOfRequiredFieldsInReq)
+ if (password.empty() || username.empty() ||
+ res.result() != boost::beast::http::status::ok)
{
- httpRespCode = boost::beast::http::status::bad_request;
-
- messages::addMessageToErrorJson(errJson, messages::malformedJSON());
-
- return false;
- }
-
- // Find fields that we need - UserName and Password
- auto userIt = loginCredentials.find("UserName");
- auto passIt = loginCredentials.find("Password");
- if (userIt == loginCredentials.end() ||
- passIt == loginCredentials.end())
- {
- httpRespCode = boost::beast::http::status::bad_request;
-
- if (userIt == loginCredentials.end())
- {
- messages::addMessageToErrorJson(
- errJson, messages::propertyMissing("UserName"));
- }
-
- if (passIt == loginCredentials.end())
- {
- messages::addMessageToErrorJson(
- errJson, messages::propertyMissing("Password"));
- }
-
- return false;
- }
-
- // Check that given data is of valid type (string)
- if (!userIt->is_string() || !passIt->is_string())
- {
- httpRespCode = boost::beast::http::status::bad_request;
-
- if (!userIt->is_string())
- {
- messages::addMessageToErrorJson(
- errJson, messages::propertyValueTypeError(userIt->dump(),
- "UserName"));
- }
-
- if (!passIt->is_string())
- {
- messages::addMessageToErrorJson(
- errJson, messages::propertyValueTypeError(userIt->dump(),
- "Password"));
- }
-
- return false;
- }
-
- // Extract username and password
- std::string username = userIt->get<const std::string>();
- std::string password = passIt->get<const std::string>();
-
- // Verify that required fields are not empty
- if (username.empty() || password.empty())
- {
- httpRespCode = boost::beast::http::status::bad_request;
-
if (username.empty())
{
+ res.result(boost::beast::http::status::bad_request);
messages::addMessageToErrorJson(
- errJson, messages::propertyMissing("UserName"));
+ res.jsonValue, messages::propertyMissing("UserName"));
}
if (password.empty())
{
+ res.result(boost::beast::http::status::bad_request);
messages::addMessageToErrorJson(
- errJson, messages::propertyMissing("Password"));
+ res.jsonValue, messages::propertyMissing("Password"));
}
+ res.end();
- return false;
+ return;
}
- // Finally - try to authenticate user
if (!pamAuthenticateUser(username, password))
{
- httpRespCode = boost::beast::http::status::unauthorized;
+ res.result(boost::beast::http::status::unauthorized);
messages::addMessageToErrorJson(
- errJson,
+ res.jsonValue,
messages::resourceAtUriUnauthorized(
std::string(req.url), "Invalid username or password"));
+ res.end();
- return false;
+ return;
}
- // User authenticated successfully
- httpRespCode = boost::beast::http::status::ok;
- user = username;
-
- return true;
+ // User is authenticated - create session
+ std::shared_ptr<crow::persistent_data::UserSession> session =
+ crow::persistent_data::SessionStore::getInstance()
+ .generateUserSession(username);
+ res.addHeader("X-Auth-Token", session->sessionToken);
+ res.addHeader("Location", "/redfish/v1/SessionService/Sessions/" +
+ session->uniqueId);
+ res.result(boost::beast::http::status::created);
+ memberSession.doGet(res, req, {session->uniqueId});
}
/**