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["Members@odata.count"] = sessionIds.size(); 143 res.jsonValue["@odata.type"] = "#SessionCollection.SessionCollection"; 144 res.jsonValue["@odata.id"] = "/redfish/v1/SessionService/Sessions/"; 145 res.jsonValue["@odata.context"] = 146 "/redfish/v1/$metadata#SessionCollection.SessionCollection"; 147 res.jsonValue["Name"] = "Session Collection"; 148 res.jsonValue["Description"] = "Session Collection"; 149 res.end(); 150 } 151 152 void doPost(crow::Response& res, const crow::Request& req, 153 const std::vector<std::string>& params) override 154 { 155 std::string username; 156 std::string password; 157 if (!json_util::readJson(req, res, "UserName", username, "Password", 158 password)) 159 { 160 res.end(); 161 return; 162 } 163 164 if (password.empty() || username.empty() || 165 res.result() != boost::beast::http::status::ok) 166 { 167 if (username.empty()) 168 { 169 messages::propertyMissing(res, "UserName"); 170 } 171 172 if (password.empty()) 173 { 174 messages::propertyMissing(res, "Password"); 175 } 176 res.end(); 177 178 return; 179 } 180 181 if (!pamAuthenticateUser(username, password)) 182 { 183 messages::resourceAtUriUnauthorized(res, std::string(req.url), 184 "Invalid username or password"); 185 res.end(); 186 187 return; 188 } 189 190 // User is authenticated - create session 191 std::shared_ptr<crow::persistent_data::UserSession> session = 192 crow::persistent_data::SessionStore::getInstance() 193 .generateUserSession(username); 194 res.addHeader("X-Auth-Token", session->sessionToken); 195 res.addHeader("Location", "/redfish/v1/SessionService/Sessions/" + 196 session->uniqueId); 197 res.result(boost::beast::http::status::created); 198 memberSession.doGet(res, req, {session->uniqueId}); 199 } 200 201 /** 202 * Member session to ensure consistency between collection's doPost and 203 * member's doGet, as they should return 100% matching data 204 */ 205 Sessions memberSession; 206 }; 207 208 class SessionService : public Node 209 { 210 public: 211 SessionService(CrowApp& app) : Node(app, "/redfish/v1/SessionService/") 212 { 213 214 entityPrivileges = { 215 {boost::beast::http::verb::get, {{"Login"}}}, 216 {boost::beast::http::verb::head, {{"Login"}}}, 217 {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 218 {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 219 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 220 {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 221 } 222 223 private: 224 void doGet(crow::Response& res, const crow::Request& req, 225 const std::vector<std::string>& params) override 226 { 227 res.jsonValue["@odata.type"] = "#SessionService.v1_0_2.SessionService"; 228 res.jsonValue["@odata.id"] = "/redfish/v1/SessionService/"; 229 res.jsonValue["@odata.context"] = 230 "/redfish/v1/$metadata#SessionService.SessionService"; 231 res.jsonValue["Name"] = "Session Service"; 232 res.jsonValue["Id"] = "SessionService"; 233 res.jsonValue["Description"] = "Session Service"; 234 res.jsonValue["SessionTimeout"] = 235 crow::persistent_data::SessionStore::getInstance() 236 .getTimeoutInSeconds(); 237 res.jsonValue["ServiceEnabled"] = true; 238 239 res.jsonValue["Sessions"] = { 240 {"@odata.id", "/redfish/v1/SessionService/Sessions"}}; 241 242 res.end(); 243 } 244 }; 245 246 } // namespace redfish 247