diff options
-rw-r--r-- | include/persistent_data_middleware.hpp | 7 | ||||
-rw-r--r-- | include/sessions.hpp | 61 | ||||
-rw-r--r-- | include/token_authorization_middleware.hpp | 17 | ||||
-rw-r--r-- | redfish-core/lib/account_service.hpp | 109 | ||||
-rw-r--r-- | static/redfish/v1/$metadata/index.xml | 4 | ||||
-rw-r--r-- | static/redfish/v1/JsonSchemas/OemAccountService/index.json | 102 | ||||
-rw-r--r-- | static/redfish/v1/schema/OemAccountService_v1.xml | 71 |
7 files changed, 359 insertions, 12 deletions
diff --git a/include/persistent_data_middleware.hpp b/include/persistent_data_middleware.hpp index c368ab21dc..348079ba61 100644 --- a/include/persistent_data_middleware.hpp +++ b/include/persistent_data_middleware.hpp @@ -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()) @@ -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; diff --git a/include/sessions.hpp b/include/sessions.hpp index df65d6155d..749349476c 100644 --- a/include/sessions.hpp +++ b/include/sessions.hpp @@ -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 @@ -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; @@ -501,6 +549,7 @@ class SessionStore std::random_device rd; bool needWrite{false}; std::chrono::minutes timeoutInMinutes; + AuthConfigMethods authMethodsConfig; }; } // namespace persistent_data @@ -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 diff --git a/include/token_authorization_middleware.hpp b/include/token_authorization_middleware.hpp index 0a440501e1..7e4e3bb22f 100644 --- a/include/token_authorization_middleware.hpp +++ b/include/token_authorization_middleware.hpp @@ -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); } @@ -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); } diff --git a/redfish-core/lib/account_service.hpp b/redfish-core/lib/account_service.hpp index 59e2d1cd67..bc8ce771d6 100644 --- a/redfish-core/lib/account_service.hpp +++ b/redfish-core/lib/account_service.hpp @@ -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, @@ -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. @@ -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/" @@ -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", @@ -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; } @@ -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, @@ -1221,6 +1312,8 @@ class AccountService : public Node std::variant<uint16_t>(*lockoutThreshold)); } } + + CrowApp& app; }; class AccountsCollection : public Node diff --git a/static/redfish/v1/$metadata/index.xml b/static/redfish/v1/$metadata/index.xml index 73833652d7..4804e48024 100644 --- a/static/redfish/v1/$metadata/index.xml +++ b/static/redfish/v1/$metadata/index.xml @@ -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> diff --git a/static/redfish/v1/JsonSchemas/OemAccountService/index.json b/static/redfish/v1/JsonSchemas/OemAccountService/index.json new file mode 100644 index 0000000000..528317086c --- /dev/null +++ b/static/redfish/v1/JsonSchemas/OemAccountService/index.json @@ -0,0 +1,102 @@ +{ + "$id": "http://redfish.dmtf.org/schemas/v1/OemAccountService.v1_0_0.json", + "$schema": "http://redfish.dmtf.org/schemas/v1/redfish-schema-v1.json", + "copyright": "Copyright 2014-2019 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright", + "definitions": { + "AccountService": { + "additionalProperties": false, + "description": "OEM Extension for AccountService", + "longDescription": "OEM Extension for AccountService providing info about TLS Auth.", + "patternProperties": { + "^([a-zA-Z_][a-zA-Z0-9_]*)?@(odata|Redfish|Message)\\.[a-zA-Z_][a-zA-Z0-9_]*$": { + "description": "This property shall specify a valid odata or Redfish property.", + "type": [ + "array", + "boolean", + "integer", + "number", + "null", + "object", + "string" + ] + } + }, + "properties": { + "AuthMethods": { + "anyOf": [ + { + "$ref": "#/definitions/AuthMethodsConfig" + }, + { + "type": "null" + } + ], + "description": "Authorization Methods configuration.", + "longDescription": "Configuration describing which auth methods are enabled." + } + }, + "type": "object" + }, + "AuthMethodsConfig": { + "additionalProperties": false, + "description": "Authorization Methods configuration.", + "longDescription": "Configuration describing which auth methods are enabled.", + "patternProperties": { + "^([a-zA-Z_][a-zA-Z0-9_]*)?@(odata|Redfish|Message)\\.[a-zA-Z_][a-zA-Z0-9_]*$": { + "description": "This property shall specify a valid odata or Redfish property.", + "type": [ + "array", + "boolean", + "integer", + "number", + "null", + "object", + "string" + ] + } + }, + "properties": { + "BasicAuth": { + "description": "Indicates whether BasicAuth authorization is enabled.", + "longDescription": "The value of this property shall be a boolean indicating whether BasicAuth authorization is enabled.", + "readonly": false, + "type": [ + "boolean", + "null" + ] + }, + "Cookie": { + "description": "Indicates whether Cookie authorization is enabled.", + "longDescription": "The value of this property shall be a boolean indicating whether Cookie authorization is enabled.", + "readonly": false, + "type": [ + "boolean", + "null" + ] + }, + "SessionToken": { + "description": "Indicates whether SessionToken authorization is enabled.", + "longDescription": "The value of this property shall be a boolean indicating whether SessionToken authorization is enabled.", + "readonly": false, + "type": [ + "boolean", + "null" + ] + }, + "XToken": { + "description": "Indicates whether XToken authorization is enabled.", + "longDescription": "The value of this property shall be a boolean indicating whether XToken authorization is enabled.", + "readonly": false, + "type": [ + "boolean", + "null" + ] + } + }, + "type": "object" + } + }, + "owningEntity": "OpenBMC", + "release": "1.0", + "title": "#OemAccountService.v1_0_0" +}
\ No newline at end of file diff --git a/static/redfish/v1/schema/OemAccountService_v1.xml b/static/redfish/v1/schema/OemAccountService_v1.xml new file mode 100644 index 0000000000..626097b3c8 --- /dev/null +++ b/static/redfish/v1/schema/OemAccountService_v1.xml @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="UTF-8"?> +<edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0"> + + <edmx:Reference Uri="http://docs.oasis-open.org/odata/odata/v4.0/errata03/csd01/complete/vocabularies/Org.OData.Core.V1.xml"> + <edmx:Include Namespace="Org.OData.Core.V1" Alias="OData"/> + </edmx:Reference> + <edmx:Reference Uri="http://docs.oasis-open.org/odata/odata/v4.0/errata03/csd01/complete/vocabularies/Org.OData.Measures.V1.xml"> + <edmx:Include Namespace="Org.OData.Measures.V1" Alias="Measures"/> + </edmx:Reference> + <edmx:Reference Uri="http://redfish.dmtf.org/schemas/v1/RedfishExtensions_v1.xml"> + <edmx:Include Namespace="RedfishExtensions.v1_0_0" Alias="Redfish"/> + </edmx:Reference> + <edmx:Reference Uri="http://redfish.dmtf.org/schemas/v1/Resource_v1.xml"> + <edmx:Include Namespace="Resource"/> + <edmx:Include Namespace="Resource.v1_0_0"/> + </edmx:Reference> + + <edmx:DataServices> + + <Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="OemAccountService"> + <Annotation Term="Redfish.OwningEntity" String="OpenBMC"/> + </Schema> + + <Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="OemAccountService.v1_0_0"> + <Annotation Term="Redfish.OwningEntity" String="OpenBMC"/> + <Annotation Term="Redfish.Release" String="1.0"/> + + <ComplexType Name="AuthMethodsConfig"> + <Annotation Term="OData.AdditionalProperties" Bool="false"/> + <Annotation Term="OData.Description" String="Authorization Methods configuration."/> + <Annotation Term="OData.LongDescription" String="Configuration describing which auth methods are enabled."/> + + <Property Name="BasicAuth" Type="Edm.Boolean"> + <Annotation Term="OData.Permissions" EnumMember="OData.Permission/ReadWrite"/> + <Annotation Term="OData.Description" String="Indicates whether BasicAuth authorization is enabled."/> + <Annotation Term="OData.LongDescription" String="The value of this property shall be a boolean indicating whether BasicAuth authorization is enabled."/> + </Property> + + <Property Name="Cookie" Type="Edm.Boolean"> + <Annotation Term="OData.Permissions" EnumMember="OData.Permission/ReadWrite"/> + <Annotation Term="OData.Description" String="Indicates whether Cookie authorization is enabled."/> + <Annotation Term="OData.LongDescription" String="The value of this property shall be a boolean indicating whether Cookie authorization is enabled."/> + </Property> + + <Property Name="SessionToken" Type="Edm.Boolean"> + <Annotation Term="OData.Permissions" EnumMember="OData.Permission/ReadWrite"/> + <Annotation Term="OData.Description" String="Indicates whether SessionToken authorization is enabled."/> + <Annotation Term="OData.LongDescription" String="The value of this property shall be a boolean indicating whether SessionToken authorization is enabled."/> + </Property> + + <Property Name="XToken" Type="Edm.Boolean"> + <Annotation Term="OData.Permissions" EnumMember="OData.Permission/ReadWrite"/> + <Annotation Term="OData.Description" String="Indicates whether XToken authorization is enabled."/> + <Annotation Term="OData.LongDescription" String="The value of this property shall be a boolean indicating whether XToken authorization is enabled."/> + </Property> + </ComplexType> + + <!--Base entity type for array members--> + <EntityType Name="AccountService" BaseType="Resource.OemObject" Abstract="true"> + <Annotation Term="OData.Description" String="OEM Extension for AccountService"/> + <Annotation Term="OData.LongDescription" String="OEM Extension for AccountService providing info about TLS Auth."/> + + <Property Name="AuthMethods" Type="OemAccountService.v1_0_0.AuthMethodsConfig"> + <Annotation Term="OData.Description" String="Authorization Methods configuration."/> + <Annotation Term="OData.LongDescription" String="Configuration describing which auth methods are enabled."/> + </Property> + </EntityType> + </Schema> + + </edmx:DataServices> +</edmx:Edmx> |