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_, {{"ConfigureManager"}}}, 39 {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 40 } 41 42 private: 43 void doGet(crow::Response& res, const crow::Request& req, 44 const std::vector<std::string>& params) override 45 { 46 auto session = 47 crow::persistent_data::SessionStore::getInstance().getSessionByUid( 48 params[0]); 49 50 if (session == nullptr) 51 { 52 messages::resourceNotFound(res, "Session", params[0]); 53 res.end(); 54 return; 55 } 56 57 res.jsonValue["Id"] = session->uniqueId; 58 res.jsonValue["UserName"] = session->username; 59 res.jsonValue["@odata.id"] = 60 "/redfish/v1/SessionService/Sessions/" + session->uniqueId; 61 res.jsonValue["@odata.type"] = "#Session.v1_0_2.Session"; 62 res.jsonValue["@odata.context"] = 63 "/redfish/v1/$metadata#Session.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 // DELETE should return representation of object that will be removed 97 doGet(res, req, params); 98 99 crow::persistent_data::SessionStore::getInstance().removeSession( 100 session); 101 } 102 103 /** 104 * This allows SessionCollection to reuse this class' doGet method, to 105 * maintain consistency of returned data, as Collection's doPost should 106 * return data for created member which should match member's doGet result 107 * in 100% 108 */ 109 friend SessionCollection; 110 }; 111 112 class SessionCollection : public Node 113 { 114 public: 115 SessionCollection(CrowApp& app) : 116 Node(app, "/redfish/v1/SessionService/Sessions/"), memberSession(app) 117 { 118 entityPrivileges = { 119 {boost::beast::http::verb::get, {{"Login"}}}, 120 {boost::beast::http::verb::head, {{"Login"}}}, 121 {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 122 {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 123 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 124 {boost::beast::http::verb::post, {}}}; 125 } 126 127 private: 128 void doGet(crow::Response& res, const crow::Request& req, 129 const std::vector<std::string>& params) override 130 { 131 std::vector<const std::string*> sessionIds = 132 crow::persistent_data::SessionStore::getInstance().getUniqueIds( 133 false, crow::persistent_data::PersistenceType::TIMEOUT); 134 135 res.jsonValue["Members@odata.count"] = sessionIds.size(); 136 res.jsonValue["Members"] = nlohmann::json::array(); 137 for (const std::string* uid : sessionIds) 138 { 139 res.jsonValue["Members"].push_back( 140 {{"@odata.id", "/redfish/v1/SessionService/Sessions/" + *uid}}); 141 } 142 res.jsonValue["@odata.type"] = "#SessionCollection.SessionCollection"; 143 res.jsonValue["@odata.id"] = "/redfish/v1/SessionService/Sessions/"; 144 res.jsonValue["@odata.context"] = 145 "/redfish/v1/$metadata#SessionCollection.SessionCollection"; 146 res.jsonValue["Name"] = "Session Collection"; 147 res.jsonValue["Description"] = "Session Collection"; 148 res.end(); 149 } 150 151 void doPost(crow::Response& res, const crow::Request& req, 152 const std::vector<std::string>& params) override 153 { 154 std::string username; 155 std::string password; 156 if (!json_util::readJson(req, res, "UserName", username, "Password", 157 password)) 158 { 159 res.end(); 160 return; 161 } 162 163 if (password.empty() || username.empty() || 164 res.result() != boost::beast::http::status::ok) 165 { 166 if (username.empty()) 167 { 168 messages::propertyMissing(res, "UserName"); 169 } 170 171 if (password.empty()) 172 { 173 messages::propertyMissing(res, "Password"); 174 } 175 res.end(); 176 177 return; 178 } 179 180 if (!pamAuthenticateUser(username, password)) 181 { 182 messages::resourceAtUriUnauthorized(res, std::string(req.url), 183 "Invalid username or password"); 184 res.end(); 185 186 return; 187 } 188 189 // User is authenticated - create session 190 std::shared_ptr<crow::persistent_data::UserSession> session = 191 crow::persistent_data::SessionStore::getInstance() 192 .generateUserSession(username); 193 res.addHeader("X-Auth-Token", session->sessionToken); 194 res.addHeader("Location", "/redfish/v1/SessionService/Sessions/" + 195 session->uniqueId); 196 res.result(boost::beast::http::status::created); 197 memberSession.doGet(res, req, {session->uniqueId}); 198 } 199 200 /** 201 * Member session to ensure consistency between collection's doPost and 202 * member's doGet, as they should return 100% matching data 203 */ 204 Sessions memberSession; 205 }; 206 207 class SessionService : public Node 208 { 209 public: 210 SessionService(CrowApp& app) : Node(app, "/redfish/v1/SessionService/") 211 { 212 213 entityPrivileges = { 214 {boost::beast::http::verb::get, {{"Login"}}}, 215 {boost::beast::http::verb::head, {{"Login"}}}, 216 {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 217 {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 218 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 219 {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 220 } 221 222 private: 223 void doGet(crow::Response& res, const crow::Request& req, 224 const std::vector<std::string>& params) override 225 { 226 res.jsonValue["@odata.type"] = "#SessionService.v1_0_2.SessionService"; 227 res.jsonValue["@odata.id"] = "/redfish/v1/SessionService/"; 228 res.jsonValue["@odata.context"] = 229 "/redfish/v1/$metadata#SessionService.SessionService"; 230 res.jsonValue["Name"] = "Session Service"; 231 res.jsonValue["Id"] = "SessionService"; 232 res.jsonValue["Description"] = "Session Service"; 233 res.jsonValue["SessionTimeout"] = 234 crow::persistent_data::SessionStore::getInstance() 235 .getTimeoutInSeconds(); 236 res.jsonValue["ServiceEnabled"] = true; 237 238 res.end(); 239 } 240 }; 241 242 } // namespace redfish 243