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 67 res.end(); 68 } 69 70 void doDelete(crow::Response& res, const crow::Request& req, 71 const std::vector<std::string>& params) override 72 { 73 // Need only 1 param which should be id of session to be deleted 74 if (params.size() != 1) 75 { 76 // This should be handled by crow and never happen 77 BMCWEB_LOG_ERROR << "Session DELETE has been called with invalid " 78 "number of params"; 79 80 messages::generalError(res); 81 res.end(); 82 return; 83 } 84 85 auto session = 86 crow::persistent_data::SessionStore::getInstance().getSessionByUid( 87 params[0]); 88 89 if (session == nullptr) 90 { 91 messages::resourceNotFound(res, "Session", params[0]); 92 res.end(); 93 return; 94 } 95 96 // Perform a proper ConfigureSelf authority check. If a 97 // session is being used to DELETE some other user's session, 98 // then the ConfigureSelf privilege does not apply. In that 99 // case, perform the authority check again without the user's 100 // ConfigureSelf privilege. 101 if (session->username != req.session->username) 102 { 103 if (!isAllowedWithoutConfigureSelf(req)) 104 { 105 BMCWEB_LOG_WARNING << "DELETE Session denied access"; 106 messages::insufficientPrivilege(res); 107 res.end(); 108 return; 109 } 110 } 111 112 // DELETE should return representation of object that will be removed 113 doGet(res, req, params); 114 115 crow::persistent_data::SessionStore::getInstance().removeSession( 116 session); 117 } 118 119 /** 120 * This allows SessionCollection to reuse this class' doGet method, to 121 * maintain consistency of returned data, as Collection's doPost should 122 * return data for created member which should match member's doGet result 123 * in 100% 124 */ 125 friend SessionCollection; 126 }; 127 128 class SessionCollection : public Node 129 { 130 public: 131 SessionCollection(CrowApp& app) : 132 Node(app, "/redfish/v1/SessionService/Sessions/"), memberSession(app) 133 { 134 entityPrivileges = { 135 {boost::beast::http::verb::get, {{"Login"}}}, 136 {boost::beast::http::verb::head, {{"Login"}}}, 137 {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 138 {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 139 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 140 {boost::beast::http::verb::post, {}}}; 141 } 142 143 private: 144 void doGet(crow::Response& res, const crow::Request& req, 145 const std::vector<std::string>& params) override 146 { 147 std::vector<const std::string*> sessionIds = 148 crow::persistent_data::SessionStore::getInstance().getUniqueIds( 149 false, crow::persistent_data::PersistenceType::TIMEOUT); 150 151 res.jsonValue["Members@odata.count"] = sessionIds.size(); 152 res.jsonValue["Members"] = nlohmann::json::array(); 153 for (const std::string* uid : sessionIds) 154 { 155 res.jsonValue["Members"].push_back( 156 {{"@odata.id", "/redfish/v1/SessionService/Sessions/" + *uid}}); 157 } 158 res.jsonValue["Members@odata.count"] = sessionIds.size(); 159 res.jsonValue["@odata.type"] = "#SessionCollection.SessionCollection"; 160 res.jsonValue["@odata.id"] = "/redfish/v1/SessionService/Sessions/"; 161 res.jsonValue["Name"] = "Session Collection"; 162 res.jsonValue["Description"] = "Session Collection"; 163 res.end(); 164 } 165 166 void doPost(crow::Response& res, const crow::Request& req, 167 const std::vector<std::string>& params) override 168 { 169 std::string username; 170 std::string password; 171 if (!json_util::readJson(req, res, "UserName", username, "Password", 172 password)) 173 { 174 res.end(); 175 return; 176 } 177 178 if (password.empty() || username.empty() || 179 res.result() != boost::beast::http::status::ok) 180 { 181 if (username.empty()) 182 { 183 messages::propertyMissing(res, "UserName"); 184 } 185 186 if (password.empty()) 187 { 188 messages::propertyMissing(res, "Password"); 189 } 190 res.end(); 191 192 return; 193 } 194 195 int pamrc = pamAuthenticateUser(username, password); 196 bool isConfigureSelfOnly = pamrc == PAM_NEW_AUTHTOK_REQD; 197 if ((pamrc != PAM_SUCCESS) && !isConfigureSelfOnly) 198 { 199 messages::resourceAtUriUnauthorized(res, std::string(req.url), 200 "Invalid username or password"); 201 res.end(); 202 203 return; 204 } 205 206 // User is authenticated - create session 207 std::shared_ptr<crow::persistent_data::UserSession> session = 208 crow::persistent_data::SessionStore::getInstance() 209 .generateUserSession( 210 username, crow::persistent_data::PersistenceType::TIMEOUT, 211 isConfigureSelfOnly); 212 res.addHeader("X-Auth-Token", session->sessionToken); 213 res.addHeader("Location", "/redfish/v1/SessionService/Sessions/" + 214 session->uniqueId); 215 res.result(boost::beast::http::status::created); 216 if (session->isConfigureSelfOnly) 217 { 218 messages::passwordChangeRequired( 219 res, 220 "/redfish/v1/AccountService/Accounts/" + session->username); 221 } 222 memberSession.doGet(res, req, {session->uniqueId}); 223 } 224 225 /** 226 * Member session to ensure consistency between collection's doPost and 227 * member's doGet, as they should return 100% matching data 228 */ 229 Sessions memberSession; 230 }; 231 232 class SessionService : public Node 233 { 234 public: 235 SessionService(CrowApp& app) : Node(app, "/redfish/v1/SessionService/") 236 { 237 238 entityPrivileges = { 239 {boost::beast::http::verb::get, {{"Login"}}}, 240 {boost::beast::http::verb::head, {{"Login"}}}, 241 {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 242 {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 243 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 244 {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 245 } 246 247 private: 248 void doGet(crow::Response& res, const crow::Request& req, 249 const std::vector<std::string>& params) override 250 { 251 res.jsonValue["@odata.type"] = "#SessionService.v1_0_2.SessionService"; 252 res.jsonValue["@odata.id"] = "/redfish/v1/SessionService/"; 253 res.jsonValue["Name"] = "Session Service"; 254 res.jsonValue["Id"] = "SessionService"; 255 res.jsonValue["Description"] = "Session Service"; 256 res.jsonValue["SessionTimeout"] = 257 crow::persistent_data::SessionStore::getInstance() 258 .getTimeoutInSeconds(); 259 res.jsonValue["ServiceEnabled"] = true; 260 261 res.jsonValue["Sessions"] = { 262 {"@odata.id", "/redfish/v1/SessionService/Sessions"}}; 263 264 res.end(); 265 } 266 }; 267 268 } // namespace redfish 269