1 /* 2 // Copyright (c) 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 #pragma once 17 18 #include "error_messages.hpp" 19 #include "node.hpp" 20 #include "persistent_data_middleware.hpp" 21 22 namespace redfish 23 { 24 25 class SessionCollection; 26 27 class Sessions : public Node 28 { 29 public: 30 Sessions(CrowApp& app) : 31 Node(app, "/redfish/v1/SessionService/Sessions/<str>/", std::string()) 32 { 33 entityPrivileges = { 34 {boost::beast::http::verb::get, {{"Login"}}}, 35 {boost::beast::http::verb::head, {{"Login"}}}, 36 {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 37 {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 38 {boost::beast::http::verb::delete_, 39 {{"ConfigureManager"}, {"ConfigureSelf"}}}, 40 {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 41 } 42 43 private: 44 void doGet(crow::Response& res, const crow::Request& req, 45 const std::vector<std::string>& params) override 46 { 47 // Note that control also reaches here via doPost and doDelete. 48 auto session = 49 crow::persistent_data::SessionStore::getInstance().getSessionByUid( 50 params[0]); 51 52 if (session == nullptr) 53 { 54 messages::resourceNotFound(res, "Session", params[0]); 55 res.end(); 56 return; 57 } 58 59 res.jsonValue["Id"] = session->uniqueId; 60 res.jsonValue["UserName"] = session->username; 61 res.jsonValue["@odata.id"] = 62 "/redfish/v1/SessionService/Sessions/" + session->uniqueId; 63 res.jsonValue["@odata.type"] = "#Session.v1_0_2.Session"; 64 res.jsonValue["Name"] = "User Session"; 65 res.jsonValue["Description"] = "Manager User Session"; 66 #ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE 67 res.jsonValue["Oem"]["OpenBMC"]["@odata.type"] = 68 "#OemSession.v1_0_0.Session"; 69 res.jsonValue["Oem"]["OpenBMC"]["ClientID"] = session->clientId; 70 #endif 71 res.end(); 72 } 73 74 void doDelete(crow::Response& res, const crow::Request& req, 75 const std::vector<std::string>& params) override 76 { 77 // Need only 1 param which should be id of session to be deleted 78 if (params.size() != 1) 79 { 80 // This should be handled by crow and never happen 81 BMCWEB_LOG_ERROR << "Session DELETE has been called with invalid " 82 "number of params"; 83 84 messages::generalError(res); 85 res.end(); 86 return; 87 } 88 89 auto session = 90 crow::persistent_data::SessionStore::getInstance().getSessionByUid( 91 params[0]); 92 93 if (session == nullptr) 94 { 95 messages::resourceNotFound(res, "Session", params[0]); 96 res.end(); 97 return; 98 } 99 100 // Perform a proper ConfigureSelf authority check. If a 101 // session is being used to DELETE some other user's session, 102 // then the ConfigureSelf privilege does not apply. In that 103 // case, perform the authority check again without the user's 104 // ConfigureSelf privilege. 105 if (session->username != req.session->username) 106 { 107 if (!isAllowedWithoutConfigureSelf(req)) 108 { 109 BMCWEB_LOG_WARNING << "DELETE Session denied access"; 110 messages::insufficientPrivilege(res); 111 res.end(); 112 return; 113 } 114 } 115 116 // DELETE should return representation of object that will be removed 117 doGet(res, req, params); 118 119 crow::persistent_data::SessionStore::getInstance().removeSession( 120 session); 121 } 122 123 /** 124 * This allows SessionCollection to reuse this class' doGet method, to 125 * maintain consistency of returned data, as Collection's doPost should 126 * return data for created member which should match member's doGet result 127 * in 100% 128 */ 129 friend SessionCollection; 130 }; 131 132 class SessionCollection : public Node 133 { 134 public: 135 SessionCollection(CrowApp& app) : 136 Node(app, "/redfish/v1/SessionService/Sessions/"), memberSession(app) 137 { 138 entityPrivileges = { 139 {boost::beast::http::verb::get, {{"Login"}}}, 140 {boost::beast::http::verb::head, {{"Login"}}}, 141 {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 142 {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 143 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 144 {boost::beast::http::verb::post, {}}}; 145 } 146 147 private: 148 void doGet(crow::Response& res, const crow::Request& req, 149 const std::vector<std::string>& params) override 150 { 151 std::vector<const std::string*> sessionIds = 152 crow::persistent_data::SessionStore::getInstance().getUniqueIds( 153 false, crow::persistent_data::PersistenceType::TIMEOUT); 154 155 res.jsonValue["Members@odata.count"] = sessionIds.size(); 156 res.jsonValue["Members"] = nlohmann::json::array(); 157 for (const std::string* uid : sessionIds) 158 { 159 res.jsonValue["Members"].push_back( 160 {{"@odata.id", "/redfish/v1/SessionService/Sessions/" + *uid}}); 161 } 162 res.jsonValue["Members@odata.count"] = sessionIds.size(); 163 res.jsonValue["@odata.type"] = "#SessionCollection.SessionCollection"; 164 res.jsonValue["@odata.id"] = "/redfish/v1/SessionService/Sessions/"; 165 res.jsonValue["Name"] = "Session Collection"; 166 res.jsonValue["Description"] = "Session Collection"; 167 res.end(); 168 } 169 170 void doPost(crow::Response& res, const crow::Request& req, 171 const std::vector<std::string>& params) override 172 { 173 std::string username; 174 std::string password; 175 std::optional<nlohmann::json> oemObject; 176 std::string clientId; 177 if (!json_util::readJson(req, res, "UserName", username, "Password", 178 password, "Oem", oemObject)) 179 { 180 res.end(); 181 return; 182 } 183 184 if (password.empty() || username.empty() || 185 res.result() != boost::beast::http::status::ok) 186 { 187 if (username.empty()) 188 { 189 messages::propertyMissing(res, "UserName"); 190 } 191 192 if (password.empty()) 193 { 194 messages::propertyMissing(res, "Password"); 195 } 196 res.end(); 197 198 return; 199 } 200 201 int pamrc = pamAuthenticateUser(username, password); 202 bool isConfigureSelfOnly = pamrc == PAM_NEW_AUTHTOK_REQD; 203 if ((pamrc != PAM_SUCCESS) && !isConfigureSelfOnly) 204 { 205 messages::resourceAtUriUnauthorized(res, std::string(req.url), 206 "Invalid username or password"); 207 res.end(); 208 209 return; 210 } 211 #ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE 212 if (oemObject) 213 { 214 std::optional<nlohmann::json> bmcOem; 215 if (!json_util::readJson(*oemObject, res, "OpenBMC", bmcOem)) 216 { 217 res.end(); 218 return; 219 } 220 if (!json_util::readJson(*bmcOem, res, "ClientID", clientId)) 221 { 222 BMCWEB_LOG_ERROR << "Could not read ClientId"; 223 res.end(); 224 return; 225 } 226 } 227 #endif 228 // User is authenticated - create session 229 std::shared_ptr<crow::persistent_data::UserSession> session = 230 crow::persistent_data::SessionStore::getInstance() 231 .generateUserSession( 232 username, crow::persistent_data::PersistenceType::TIMEOUT, 233 isConfigureSelfOnly, clientId); 234 res.addHeader("X-Auth-Token", session->sessionToken); 235 res.addHeader("Location", "/redfish/v1/SessionService/Sessions/" + 236 session->uniqueId); 237 res.result(boost::beast::http::status::created); 238 if (session->isConfigureSelfOnly) 239 { 240 messages::passwordChangeRequired( 241 res, 242 "/redfish/v1/AccountService/Accounts/" + session->username); 243 } 244 memberSession.doGet(res, req, {session->uniqueId}); 245 } 246 247 /** 248 * Member session to ensure consistency between collection's doPost and 249 * member's doGet, as they should return 100% matching data 250 */ 251 Sessions memberSession; 252 }; 253 254 class SessionService : public Node 255 { 256 public: 257 SessionService(CrowApp& app) : Node(app, "/redfish/v1/SessionService/") 258 { 259 260 entityPrivileges = { 261 {boost::beast::http::verb::get, {{"Login"}}}, 262 {boost::beast::http::verb::head, {{"Login"}}}, 263 {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 264 {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 265 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 266 {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 267 } 268 269 private: 270 void doGet(crow::Response& res, const crow::Request& req, 271 const std::vector<std::string>& params) override 272 { 273 res.jsonValue["@odata.type"] = "#SessionService.v1_0_2.SessionService"; 274 res.jsonValue["@odata.id"] = "/redfish/v1/SessionService/"; 275 res.jsonValue["Name"] = "Session Service"; 276 res.jsonValue["Id"] = "SessionService"; 277 res.jsonValue["Description"] = "Session Service"; 278 res.jsonValue["SessionTimeout"] = 279 crow::persistent_data::SessionStore::getInstance() 280 .getTimeoutInSeconds(); 281 res.jsonValue["ServiceEnabled"] = true; 282 283 res.jsonValue["Sessions"] = { 284 {"@odata.id", "/redfish/v1/SessionService/Sessions"}}; 285 286 res.end(); 287 } 288 }; 289 290 } // namespace redfish 291