Handle Redfish PasswordChangeRequired

This enhances BMCWeb authentication to recognize when the user's password is
correct but expired.  The Redfish SessionService is enhanced to comply with
the Redfish PasswordChangeRequired spec which allows the session to be
created, but limits that sesion to changing the password only, and includes
the PasswordChangeRequired message in the response body.

Specifically, when the account's password is expired, a successful
authentication via the following interfaces will have these results:
- POST /redfish/v1/SessionService/Sessions -- follows Redfish spec
- POST /login -- creates a session limited to changing the password,
                 similar to Redfish
- Basic authentication -- continues to treat the password
  change required condition as an authentication failure and gives no
  indication the password is expired.
- Cookie auth -- works as before
- Token auth -- works as before

This patchset is intended to allow web applications to use the presence
of the Redfish PasswordChangeRequired message or the extendedMessage
field to trigger the password change dialog.

This does not implement the PasswordChangeRequired property in the
ManagerAccount resource.

This implements the Redfish privilege overrides associated with the
ConfigureSelf privilege.  Specifically, this correctly implements the
Password property override, and the ManagerAccount Resource URI override.

When an API results in 403 Forbidden and the issuing session has the
PasswordChangeRequired condition, appropriate JSON is given.

Tested:
  Yes, see https://github.com/openbmc/bmcweb/issues/103
  No, did not run Redfish validator

Signed-off-by: Joseph Reynolds <joseph-reynolds@charter.net>
Change-Id: Ibbf5f6414ac55c0e7bea14c721f6db227b52fe40
diff --git a/redfish-core/lib/redfish_sessions.hpp b/redfish-core/lib/redfish_sessions.hpp
index d4085af..c4b0a4f 100644
--- a/redfish-core/lib/redfish_sessions.hpp
+++ b/redfish-core/lib/redfish_sessions.hpp
@@ -35,7 +35,8 @@
             {boost::beast::http::verb::head, {{"Login"}}},
             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
-            {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
+            {boost::beast::http::verb::delete_,
+             {{"ConfigureManager"}, {"ConfigureSelf"}}},
             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
     }
 
@@ -43,6 +44,7 @@
     void doGet(crow::Response& res, const crow::Request& req,
                const std::vector<std::string>& params) override
     {
+        // Note that control also reaches here via doPost and doDelete.
         auto session =
             crow::persistent_data::SessionStore::getInstance().getSessionByUid(
                 params[0]);
@@ -63,6 +65,12 @@
             "/redfish/v1/$metadata#Session.Session";
         res.jsonValue["Name"] = "User Session";
         res.jsonValue["Description"] = "Manager User Session";
+        if (session->isConfigureSelfOnly)
+        {
+            messages::passwordChangeRequired(
+                res,
+                "/redfish/v1/AccountService/Accounts/" + session->username);
+        }
 
         res.end();
     }
@@ -93,6 +101,24 @@
             return;
         }
 
+        // Perform a tighter authority check for how the ConfigureSelf
+        // privilege interacts with the Session resource URI override.
+        // (Meaning: the ConfigureSelf privilege only applies to that
+        // session's Session resource.)  If a session is DELETEing
+        // some other session, then the ConfigureSelf privilege does
+        // not apply, so remove the user's ConfigureSelf privilege and
+        // perform the authority check again.
+        if (session->uniqueId != req.session->uniqueId)
+        {
+            if (!isAllowedWithoutConfigureSelf(req))
+            {
+                BMCWEB_LOG_WARNING << "DELETE Session denied access";
+                messages::accessDenied(res, std::string(req.url));
+                res.end();
+                return;
+            }
+        }
+
         // DELETE should return representation of object that will be removed
         doGet(res, req, params);
 
@@ -178,7 +204,8 @@
             return;
         }
 
-        if (!pamAuthenticateUser(username, password))
+        bool passwordChangeRequired = false;
+        if (!pamAuthenticateUser(username, password, passwordChangeRequired))
         {
             messages::resourceAtUriUnauthorized(res, std::string(req.url),
                                                 "Invalid username or password");
@@ -190,7 +217,7 @@
         // User is authenticated - create session
         std::shared_ptr<crow::persistent_data::UserSession> session =
             crow::persistent_data::SessionStore::getInstance()
-                .generateUserSession(username);
+                .generateUserSession(username, passwordChangeRequired);
         res.addHeader("X-Auth-Token", session->sessionToken);
         res.addHeader("Location", "/redfish/v1/SessionService/Sessions/" +
                                       session->uniqueId);