Implement Redfish PasswordChangeRequired

This implements the Redfish PasswordChangeRequired handling.  See
section 13.3.7.1 "Password change required handling" in the 1.9.1 spec:
https://www.dmtf.org/sites/default/files/standards/documents/DSP0266_1.9.1.pdf

These portions of the spec are implemented:
 - Authenticatation with a correct but expired password creates a
   session:
    - The session is restricted to the ConfigureSelf privilege which
      allows a user to change their own password (via GET and PATCH
      Password for their own account).  Support for the ConfigureSelf
      privilege is already in BMCWeb.
    - The session object has the PasswordChangeRequired message.
 - All other operations respond with http status code 403 Forbidden
   and include the PasswordChangeRequired message.
 - The ManagerAccount (URI /redfish/v1/AccountService/Accounts/USER)
   PasswordChangeRequired property is implemented for local accounts
   but not present for remote accounts.

This has the following additional behavior:

The PasswordChangeRequired property is updated at the start of each new
REST operation, even within an existing session.  This behavior
implements a "dynamic" PasswordChangeRequired handling that responds to
changes to the underlying "password expired" status.  Specifically:
 - Sessions restricted by the PasswordChangeRequired handling lose that
   restriction when the underlying account password is changed.
 - Sessions become subject to the PasswordChangeRequired handling
   restrictions whenever the underlying account password expires.
 - The mechanism is to check if the password is expired at the start of
   every new REST API operation, effectively updating the ManagerAccount
   PasswordChangeRequired property each time.  This makes BMCWeb
   responsive to changes in the underlying account due to other activity
   on the BMC.

Notes:
1. Note that when an account password status is changed (for example,
   the password becomes expired or is changed) and that account has
   active sessions, those sessions remain.  They are not deleted.  Any
   current operations are allowed to complete.  Subsequent operations
   with that session pick up the new password status.

2. This does not implement OWASP recommendations which call for sessions
   to be dropped when there is a significant change to the underlying
   account.  For example, when the password is changed, the password
   becomes expired, or when the account's Role changes.  OWASP's
   recommendation is due to the session fixation vulnerability.  See the
   OWASP Session Management Cheat Sheet section "Renew the Session ID
   After Any Privilege Level Change":
   https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html#renew-the-session-id-after-any-privilege-level-change

   BMCWeb protects against session fixation vulnerabilities because it
   always regenerates new session IDs when successful authentication
   creates a new session.

3. Users authenticating via mTLS are not subject to the
   PasswordChangeRequired behavior because mTLS takes precedence over
   password-based authentication.

Tested:
0. Setup:
    - The `passwd --expire USERNAME` command was used to expire
      passwords.  The `chage USER` command was also used.
    - The following were used to change the password: Redfish API,
      passwd command, and the SSH password change dialog.
    - Tested the following via Basic Auth, /login, and Redfish login
      (except where Basic Auth does not create a persistent session).
    - Only local user account were tested.
    - Did not test authentication via mTLS or with LDAP users.
1. When the password is not expired, authentication behaves as usual
   for both correct and incorrect passwords.
2. When the password is incorrect and expired, authentication fails as
   usual.
3. When the password is correct but expired:
    A. A session is created and has the PasswordChangeRequired message.
    B. That session cannot access resources that require Login privilege
       and the 403 message contains the PasswordChangeRequired message.
    C. That session can be used to GET the user's account, PATCH the
       Password, and DELETE the session object.
    D. The account PasswordChangeRequired reports true.
4. While a session is established, try expiring and changing
   (unexpiring) the password using various mechanisms.  Ensure both the
   session object and the ManagerAccount PasswordChangeRequired property
   report the correct condition, and ensure PasswordChangeRequired
   handling (restricting operations to ConfigureSelf when
   PasswordChangeRequired is true) is applied correctly.

Signed-off-by: Joseph Reynolds <joseph-reynolds@charter.net>
Change-Id: Iedc61dea8f949e4b182e14dc189de02d1f74d3e8
diff --git a/redfish-core/include/node.hpp b/redfish-core/include/node.hpp
index 9086f1e..a6e1e27 100644
--- a/redfish-core/include/node.hpp
+++ b/redfish-core/include/node.hpp
@@ -169,8 +169,8 @@
         res.end();
     }
 
-    /* @brief Would the operation be allowed if the user did not have
-     * the ConfigureSelf Privilege?
+    /* @brief Would the operation be allowed if the user did not have the
+     * ConfigureSelf Privilege?  Also honors session.isConfigureSelfOnly.
      *
      * @param req      the request
      *
@@ -181,9 +181,18 @@
         const std::string& userRole = req.userRole;
         BMCWEB_LOG_DEBUG << "isAllowedWithoutConfigureSelf for the role "
                          << req.userRole;
-        Privileges effectiveUserPrivileges =
-            redfish::getUserPrivileges(userRole);
-        effectiveUserPrivileges.resetSinglePrivilege("ConfigureSelf");
+        Privileges effectiveUserPrivileges;
+        if (req.session && req.session->isConfigureSelfOnly)
+        {
+            // The session has no privileges because it is limited to
+            // configureSelfOnly and we are disregarding that privilege.
+            // Note that some operations do not require any privilege.
+        }
+        else
+        {
+            effectiveUserPrivileges = redfish::getUserPrivileges(userRole);
+            effectiveUserPrivileges.resetSinglePrivilege("ConfigureSelf");
+        }
         const auto& requiredPrivilegesIt = entityPrivileges.find(req.method());
         return (requiredPrivilegesIt != entityPrivileges.end()) &&
                isOperationAllowedWithPrivileges(requiredPrivilegesIt->second,