Redfish(Authorization): Add the privilege in the user session object.
This commit fetches the user privilege during creation of the
session by making D-bus call and add the privilege in the
user session object.
Change-Id: I0e9da8a52df00fc753b13101066ce6d0be9e2ce3
Signed-off-by: Ratan Gupta <ratagupt@linux.vnet.ibm.com>
diff --git a/include/sessions.hpp b/include/sessions.hpp
index 6bc1c99..f8f3e8e 100644
--- a/include/sessions.hpp
+++ b/include/sessions.hpp
@@ -1,17 +1,15 @@
#pragma once
-#include <crow/app.h>
-#include <crow/http_request.h>
-#include <crow/http_response.h>
-
#include <boost/container/flat_map.hpp>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
+#include <dbus_singleton.hpp>
#include <nlohmann/json.hpp>
#include <pam_authenticate.hpp>
#include <random>
-#include <webassets.hpp>
+
+#include "crow/logging.h"
namespace crow
{
@@ -25,11 +23,258 @@
SINGLE_REQUEST // User times out once this request is completed.
};
+constexpr char const* userService = "xyz.openbmc_project.User.Manager";
+constexpr char const* userObjPath = "/xyz/openbmc_project/user";
+constexpr char const* userAttrIface = "xyz.openbmc_project.User.Attributes";
+constexpr char const* dbusPropertiesIface = "org.freedesktop.DBus.Properties";
+
+class SessionStore;
+
+struct UserRoleMap
+{
+ using GetManagedPropertyType =
+ boost::container::flat_map<std::string,
+ std::variant<std::string, bool>>;
+
+ using InterfacesPropertiesType =
+ boost::container::flat_map<std::string, GetManagedPropertyType>;
+
+ using GetManagedObjectsType = std::vector<
+ std::pair<sdbusplus::message::object_path, InterfacesPropertiesType>>;
+
+ static UserRoleMap& getInstance()
+ {
+ static UserRoleMap userRoleMap;
+ return userRoleMap;
+ }
+
+ UserRoleMap(const UserRoleMap&) = delete;
+ UserRoleMap& operator=(const UserRoleMap&) = delete;
+
+ std::string getUserRole(std::string_view name)
+ {
+ auto it = roleMap.find(std::string(name));
+ if (it == roleMap.end())
+ {
+ BMCWEB_LOG_ERROR << "User name " << name
+ << " is not found in the UserRoleMap.";
+ return "";
+ }
+ return it->second;
+ }
+
+ std::string
+ extractUserRole(const InterfacesPropertiesType& interfacesProperties)
+ {
+ auto iface = interfacesProperties.find(userAttrIface);
+ if (iface == interfacesProperties.end())
+ {
+ return {};
+ }
+
+ auto& properties = iface->second;
+ auto property = properties.find("UserPrivilege");
+ if (property == properties.end())
+ {
+ return {};
+ }
+
+ const std::string* role = std::get_if<std::string>(&property->second);
+ if (role == nullptr)
+ {
+ BMCWEB_LOG_ERROR << "UserPrivilege property value is null";
+ return {};
+ }
+
+ return *role;
+ }
+
+ private:
+ void userAdded(sdbusplus::message::message& m)
+ {
+ sdbusplus::message::object_path objPath;
+ InterfacesPropertiesType interfacesProperties;
+
+ try
+ {
+ m.read(objPath, interfacesProperties);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ BMCWEB_LOG_ERROR << "Failed to parse user add signal."
+ << "ERROR=" << e.what()
+ << "REPLY_SIG=" << m.get_signature();
+ return;
+ }
+ BMCWEB_LOG_DEBUG << "obj path = " << objPath.str;
+
+ std::size_t lastPos = objPath.str.rfind("/");
+ if (lastPos == std::string::npos)
+ {
+ return;
+ };
+
+ std::string name = objPath.str.substr(lastPos + 1);
+ std::string role = this->extractUserRole(interfacesProperties);
+
+ // Insert the newly added user name and the role
+ auto res = roleMap.emplace(name, role);
+ if (res.second == false)
+ {
+ BMCWEB_LOG_ERROR << "Insertion of the user=\"" << name
+ << "\" in the roleMap failed.";
+ return;
+ }
+ }
+
+ void userRemoved(sdbusplus::message::message& m)
+ {
+ sdbusplus::message::object_path objPath;
+
+ try
+ {
+ m.read(objPath);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ BMCWEB_LOG_ERROR << "Failed to parse user delete signal.";
+ BMCWEB_LOG_ERROR << "ERROR=" << e.what()
+ << "REPLY_SIG=" << m.get_signature();
+ return;
+ }
+
+ BMCWEB_LOG_DEBUG << "obj path = " << objPath.str;
+
+ std::size_t lastPos = objPath.str.rfind("/");
+ if (lastPos == std::string::npos)
+ {
+ return;
+ };
+
+ // User name must be atleast 1 char in length.
+ if ((lastPos + 1) >= objPath.str.length())
+ {
+ return;
+ }
+
+ std::string name = objPath.str.substr(lastPos + 1);
+
+ roleMap.erase(name);
+ }
+
+ void userPropertiesChanged(sdbusplus::message::message& m)
+ {
+ std::string interface;
+ GetManagedPropertyType changedProperties;
+ m.read(interface, changedProperties);
+ const std::string path = m.get_path();
+
+ BMCWEB_LOG_DEBUG << "Object Path = \"" << path << "\"";
+
+ std::size_t lastPos = path.rfind("/");
+ if (lastPos == std::string::npos)
+ {
+ return;
+ };
+
+ // User name must be at least 1 char in length.
+ if ((lastPos + 1) == path.length())
+ {
+ return;
+ }
+
+ std::string user = path.substr(lastPos + 1);
+
+ BMCWEB_LOG_DEBUG << "User Name = \"" << user << "\"";
+
+ auto index = changedProperties.find("UserPrivilege");
+ if (index == changedProperties.end())
+ {
+ return;
+ }
+
+ const std::string* role = std::get_if<std::string>(&index->second);
+ if (role == nullptr)
+ {
+ return;
+ }
+ BMCWEB_LOG_DEBUG << "Role = \"" << *role << "\"";
+
+ auto it = roleMap.find(user);
+ if (it == roleMap.end())
+ {
+ BMCWEB_LOG_ERROR << "User Name = \"" << user
+ << "\" is not found. But, received "
+ "propertiesChanged signal";
+ return;
+ }
+ it->second = *role;
+ }
+
+ UserRoleMap() :
+ userAddedSignal(
+ *crow::connections::systemBus,
+ sdbusplus::bus::match::rules::interfacesAdded(userObjPath),
+ [this](sdbusplus::message::message& m) {
+ BMCWEB_LOG_DEBUG << "User Added";
+ this->userAdded(m);
+ }),
+ userRemovedSignal(
+ *crow::connections::systemBus,
+ sdbusplus::bus::match::rules::interfacesRemoved(userObjPath),
+ [this](sdbusplus::message::message& m) {
+ BMCWEB_LOG_DEBUG << "User Removed";
+ this->userRemoved(m);
+ }),
+ userPropertiesChangedSignal(
+ *crow::connections::systemBus,
+ sdbusplus::bus::match::rules::path_namespace(userObjPath) +
+ sdbusplus::bus::match::rules::type::signal() +
+ sdbusplus::bus::match::rules::member("PropertiesChanged") +
+ sdbusplus::bus::match::rules::interface(dbusPropertiesIface) +
+ sdbusplus::bus::match::rules::argN(0, userAttrIface),
+ [this](sdbusplus::message::message& m) {
+ BMCWEB_LOG_DEBUG << "Properties Changed";
+ this->userPropertiesChanged(m);
+ })
+ {
+ crow::connections::systemBus->async_method_call(
+ [this](boost::system::error_code ec,
+ GetManagedObjectsType& managedObjects) {
+ if (ec)
+ {
+ BMCWEB_LOG_DEBUG << "User manager call failed, ignoring";
+ return;
+ }
+
+ for (auto& managedObj : managedObjects)
+ {
+ std::size_t lastPos = managedObj.first.str.rfind("/");
+ if (lastPos == std::string::npos)
+ {
+ continue;
+ };
+ std::string name = managedObj.first.str.substr(lastPos + 1);
+ std::string role = extractUserRole(managedObj.second);
+ roleMap.emplace(name, role);
+ }
+ },
+ userService, userObjPath, "org.freedesktop.DBus.ObjectManager",
+ "GetManagedObjects");
+ }
+
+ boost::container::flat_map<std::string, std::string> roleMap;
+ sdbusplus::bus::match_t userAddedSignal;
+ sdbusplus::bus::match_t userRemovedSignal;
+ sdbusplus::bus::match_t userPropertiesChangedSignal;
+};
+
struct UserSession
{
std::string uniqueId;
std::string sessionToken;
std::string username;
+ std::string userRole;
std::string csrfToken;
std::chrono::time_point<std::chrono::steady_clock> lastUpdated;
PersistenceType persistence;
@@ -138,8 +383,14 @@
{
uniqueId[i] = alphanum[dist(rd)];
}
+
+ // Get the User Privilege
+ const std::string& role =
+ UserRoleMap::getInstance().getUserRole(username);
+
+ BMCWEB_LOG_DEBUG << "user name=\"" << username << "\" role = " << role;
auto session = std::make_shared<UserSession>(UserSession{
- uniqueId, sessionToken, std::string(username), csrfToken,
+ uniqueId, sessionToken, std::string(username), role, csrfToken,
std::chrono::steady_clock::now(), persistence});
auto it = authTokens.emplace(std::make_pair(sessionToken, session));
// Only need to write to disk if session isn't about to be destroyed.
@@ -250,6 +501,7 @@
}
}
}
+
std::chrono::time_point<std::chrono::steady_clock> lastTimeoutUpdate;
boost::container::flat_map<std::string, std::shared_ptr<UserSession>>
authTokens;
diff --git a/src/webserver_main.cpp b/src/webserver_main.cpp
index 1c9c1f1..602c216 100644
--- a/src/webserver_main.cpp
+++ b/src/webserver_main.cpp
@@ -99,6 +99,10 @@
std::make_shared<sdbusplus::asio::connection>(*io);
redfish::RedfishService redfish(app);
+ // Keep the user role map hot in memory and
+ // track the changes using match object
+ crow::persistent_data::UserRoleMap::getInstance();
+
app.run();
io->run();