Skip to content

Commit

Permalink
Auth methods configuration
Browse files Browse the repository at this point in the history
Added Oem extension for AccountService allowing user to configure
which authentication methods should be enabled. User is now able
to turn on and off authentication methods like BasicAuth, XToken, etc.
User is not allowed to turn off all of the methods at once - at least
one method has to be active to prevent lock-out. This configuration
is persistent, will be saved on file-system and will be loaded on
bmcweb's restart.

Tested:
No regression found in manual testing. By default everything works as before,
and disabling auth method prevents user to authenticate by it. Tested that
user is not allowed to disable all the methods - either in one PATCH or by
disabling them one at a time.
ServiceValidator run with success.

This change is a fix for this request:
https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/23590/18

which was revert here:
https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/26869

Signed-off-by: Zbigniew Kurzynski <zbigniew.kurzynski@intel.com>
Change-Id: I66b5ad423746f1992070a14f2983a07b1320190e
  • Loading branch information
zkurzyns committed Nov 7, 2019
1 parent 1f8c7b5 commit 7815863
Show file tree
Hide file tree
Showing 7 changed files with 359 additions and 12 deletions.
7 changes: 7 additions & 0 deletions include/persistent_data_middleware.hpp
Expand Up @@ -100,6 +100,12 @@ class Middleware
systemUuid = *jSystemUuid;
}
}
else if (item.key() == "auth_config")
{
SessionStore::getInstance()
.getAuthMethodsConfig()
.fromJson(item.value());
}
else if (item.key() == "sessions")
{
for (const auto& elem : item.value())
Expand Down Expand Up @@ -163,6 +169,7 @@ class Middleware

nlohmann::json data{
{"sessions", SessionStore::getInstance().authTokens},
{"auth_config", SessionStore::getInstance().getAuthMethodsConfig()},
{"system_uuid", systemUuid},
{"revision", jsonRevision}};
persistentFile << data;
Expand Down
61 changes: 61 additions & 0 deletions include/sessions.hpp
Expand Up @@ -339,6 +339,43 @@ struct UserSession
}
};

struct AuthConfigMethods
{
bool xtoken = true;
bool cookie = true;
bool sessionToken = true;
bool basic = true;

void fromJson(const nlohmann::json& j)
{
for (const auto& element : j.items())
{
const bool* value = element.value().get_ptr<const bool*>();
if (value == nullptr)
{
continue;
}

if (element.key() == "XToken")
{
xtoken = *value;
}
else if (element.key() == "Cookie")
{
cookie = *value;
}
else if (element.key() == "SessionToken")
{
sessionToken = *value;
}
else if (element.key() == "BasicAuth")
{
basic = *value;
}
}
}
};

class Middleware;

class SessionStore
Expand Down Expand Up @@ -445,6 +482,17 @@ class SessionStore
return ret;
}

void updateAuthMethodsConfig(const AuthConfigMethods& config)
{
authMethodsConfig = config;
needWrite = true;
}

AuthConfigMethods& getAuthMethodsConfig()
{
return authMethodsConfig;
}

bool needsWrite()
{
return needWrite;
Expand Down Expand Up @@ -501,6 +549,7 @@ class SessionStore
std::random_device rd;
bool needWrite{false};
std::chrono::minutes timeoutInMinutes;
AuthConfigMethods authMethodsConfig;
};

} // namespace persistent_data
Expand All @@ -526,4 +575,16 @@ struct adl_serializer<std::shared_ptr<crow::persistent_data::UserSession>>
}
}
};

template <> struct adl_serializer<crow::persistent_data::AuthConfigMethods>
{
static void to_json(nlohmann::json& j,
const crow::persistent_data::AuthConfigMethods& c)
{
j = nlohmann::json{{"XToken", c.xtoken},
{"Cookie", c.cookie},
{"SessionToken", c.sessionToken},
{"BasicAuth", c.basic}};
}
};
} // namespace nlohmann
17 changes: 13 additions & 4 deletions include/token_authorization_middleware.hpp
Expand Up @@ -31,8 +31,15 @@ class Middleware
return;
}

req.session = performXtokenAuth(req);
if (req.session == nullptr)
const crow::persistent_data::AuthConfigMethods& authMethodsConfig =
crow::persistent_data::SessionStore::getInstance()
.getAuthMethodsConfig();

if (req.session == nullptr && authMethodsConfig.xtoken)
{
req.session = performXtokenAuth(req);
}
if (req.session == nullptr && authMethodsConfig.cookie)
{
req.session = performCookieAuth(req);
}
Expand All @@ -42,11 +49,13 @@ class Middleware
if (!authHeader.empty())
{
// Reject any kind of auth other than basic or token
if (boost::starts_with(authHeader, "Token "))
if (boost::starts_with(authHeader, "Token ") &&
authMethodsConfig.sessionToken)
{
req.session = performTokenAuth(authHeader);
}
else if (boost::starts_with(authHeader, "Basic "))
else if (boost::starts_with(authHeader, "Basic ") &&
authMethodsConfig.basic)
{
req.session = performBasicAuth(authHeader);
}
Expand Down
109 changes: 101 additions & 8 deletions redfish-core/lib/account_service.hpp
Expand Up @@ -553,7 +553,8 @@ inline void getLDAPConfigData(const std::string& ldapType,
class AccountService : public Node
{
public:
AccountService(CrowApp& app) : Node(app, "/redfish/v1/AccountService/")
AccountService(CrowApp& app) :
Node(app, "/redfish/v1/AccountService/"), app(app)
{
entityPrivileges = {
{boost::beast::http::verb::get,
Expand Down Expand Up @@ -887,6 +888,65 @@ class AccountService : public Node
ldapEnableInterface, "Enabled", std::variant<bool>(serviceEnabled));
}

void handleAuthMethodsPatch(nlohmann::json& input,
const std::shared_ptr<AsyncResp>& asyncResp)
{
std::optional<bool> basicAuth;
std::optional<bool> cookie;
std::optional<bool> sessionToken;
std::optional<bool> xToken;

if (!json_util::readJson(input, asyncResp->res, "BasicAuth", basicAuth,
"Cookie", cookie, "SessionToken", sessionToken,
"XToken", xToken))
{
BMCWEB_LOG_ERROR << "Cannot read values from AuthMethod tag";
return;
}

// Make a copy of methods configuration
crow::persistent_data::AuthConfigMethods authMethodsConfig =
crow::persistent_data::SessionStore::getInstance()
.getAuthMethodsConfig();

if (basicAuth)
{
authMethodsConfig.basic = *basicAuth;
}

if (cookie)
{
authMethodsConfig.cookie = *cookie;
}

if (sessionToken)
{
authMethodsConfig.sessionToken = *sessionToken;
}

if (xToken)
{
authMethodsConfig.xtoken = *xToken;
}

if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
!authMethodsConfig.sessionToken && !authMethodsConfig.xtoken)
{
// Do not allow user to disable everything
messages::actionNotSupported(asyncResp->res,
"of disabling all available methods");
return;
}

crow::persistent_data::SessionStore::getInstance()
.updateAuthMethodsConfig(authMethodsConfig);
// Save configuration immediately
app.template getMiddleware<crow::persistent_data::Middleware>()
.writeData();

messages::success(asyncResp->res);
}

/**
* @brief Get the required values from the given JSON, validates the
* value and create the LDAP config object.
Expand Down Expand Up @@ -1063,6 +1123,10 @@ class AccountService : public Node
void doGet(crow::Response& res, const crow::Request& req,
const std::vector<std::string>& params) override
{
const crow::persistent_data::AuthConfigMethods& authMethodsConfig =
crow::persistent_data::SessionStore::getInstance()
.getAuthMethodsConfig();

auto asyncResp = std::make_shared<AsyncResp>(res);
res.jsonValue = {
{"@odata.context", "/redfish/v1/"
Expand All @@ -1078,6 +1142,16 @@ class AccountService : public Node
{"Accounts",
{{"@odata.id", "/redfish/v1/AccountService/Accounts"}}},
{"Roles", {{"@odata.id", "/redfish/v1/AccountService/Roles"}}},
{"Oem",
{{"OpenBMC",
{{"@odata.type", "#OemAccountService.v1_0_0.AccountService"},
{"AuthMethods",
{
{"BasicAuth", authMethodsConfig.basic},
{"SessionToken", authMethodsConfig.sessionToken},
{"XToken", authMethodsConfig.xtoken},
{"Cookie", authMethodsConfig.cookie},
}}}}}},
{"LDAP",
{{"Certificates",
{{"@odata.id",
Expand Down Expand Up @@ -1155,13 +1229,14 @@ class AccountService : public Node
std::optional<uint16_t> maxPasswordLength;
std::optional<nlohmann::json> ldapObject;
std::optional<nlohmann::json> activeDirectoryObject;

if (!json_util::readJson(req, res, "AccountLockoutDuration",
unlockTimeout, "AccountLockoutThreshold",
lockoutThreshold, "MaxPasswordLength",
maxPasswordLength, "MinPasswordLength",
minPasswordLength, "LDAP", ldapObject,
"ActiveDirectory", activeDirectoryObject))
std::optional<nlohmann::json> oemObject;

if (!json_util::readJson(
req, res, "AccountLockoutDuration", unlockTimeout,
"AccountLockoutThreshold", lockoutThreshold,
"MaxPasswordLength", maxPasswordLength, "MinPasswordLength",
minPasswordLength, "LDAP", ldapObject, "ActiveDirectory",
activeDirectoryObject, "Oem", oemObject))
{
return;
}
Expand All @@ -1181,6 +1256,22 @@ class AccountService : public Node
handleLDAPPatch(*ldapObject, asyncResp, req, params, "LDAP");
}

if (std::optional<nlohmann::json> oemOpenBMCObject;
oemObject &&
json_util::readJson(*oemObject, res, "OpenBMC", oemOpenBMCObject))
{
if (std::optional<nlohmann::json> authMethodsObject;
oemOpenBMCObject &&
json_util::readJson(*oemOpenBMCObject, res, "AuthMethods",
authMethodsObject))
{
if (authMethodsObject)
{
handleAuthMethodsPatch(*authMethodsObject, asyncResp);
}
}
}

if (activeDirectoryObject)
{
handleLDAPPatch(*activeDirectoryObject, asyncResp, req, params,
Expand Down Expand Up @@ -1221,6 +1312,8 @@ class AccountService : public Node
std::variant<uint16_t>(*lockoutThreshold));
}
}

CrowApp& app;
};

class AccountsCollection : public Node
Expand Down
4 changes: 4 additions & 0 deletions static/redfish/v1/$metadata/index.xml
Expand Up @@ -1045,6 +1045,10 @@
<edmx:Include Namespace="NetworkPort.v1_2_2"/>
<edmx:Include Namespace="NetworkPort.v1_2_3"/>
</edmx:Reference>
<edmx:Reference Uri="/redfish/v1/schema/OemAccountService_v1.xml">
<edmx:Include Namespace="OemAccountService"/>
<edmx:Include Namespace="OemAccountService.v1_0_0"/>
</edmx:Reference>
<edmx:Reference Uri="/redfish/v1/schema/NetworkPortCollection_v1.xml">
<edmx:Include Namespace="NetworkPortCollection"/>
</edmx:Reference>
Expand Down

0 comments on commit 7815863

Please sign in to comment.