ORE Studio 0.0.4
Loading...
Searching...
No Matches
client_session.hpp
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * Copyright (C) 2025 Marco Craveiro <marco.craveiro@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it under
6 * the terms of the GNU General Public License as published by the Free Software
7 * Foundation; either version 3 of the License, or (at your option) any later
8 * version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13 * details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 51
17 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 *
19 */
20#ifndef ORES_COMMS_NET_CLIENT_SESSION_HPP
21#define ORES_COMMS_NET_CLIENT_SESSION_HPP
22
23#include <memory>
24#include <optional>
25#include <expected>
26#include <string>
27#include <boost/uuid/uuid.hpp>
28#include "ores.utility/log/make_logger.hpp"
29#include "ores.comms/messaging/message_types.hpp"
30#include "ores.comms/messaging/frame.hpp"
31#include "ores.comms/messaging/error_protocol.hpp"
32#include "ores.comms/net/client.hpp"
33#include "ores.comms/net/client_options.hpp"
34
35namespace ores::comms::net {
36
41 boost::uuids::uuid account_id;
42 std::string username;
43 bool is_admin;
44};
45
50 not_connected,
51 not_logged_in,
52 login_required,
53 admin_required,
54 request_failed,
55 deserialization_failed,
56 server_error
57};
58
59template<typename Request>
60concept Serializable = requires(Request req) {
61 { req.serialize() } -> std::convertible_to<std::vector<std::byte>>;
62};
63
64template<typename Response>
65concept Deserializable = requires(std::span<const std::byte> data) {
66 {
67 Response::deserialize(data)
68 } -> std::same_as<std::expected<Response, messaging::error_code>>;
69};
70
81class client_session final {
82private:
83 inline static std::string_view logger_name =
84 "ores.comms.net.client_session";
85
86 static auto& lg() {
87 using namespace ores::utility::log;
88 static auto instance = make_logger(logger_name);
89 return instance;
90 }
91
92public:
93 client_session() = default;
95
96 // Non-copyable
97 client_session(const client_session&) = delete;
98 client_session& operator=(const client_session&) = delete;
99
100 // Movable
101 client_session(client_session&&) = default;
102 client_session& operator=(client_session&&) = default;
103
110 std::expected<void, client_session_error> connect(client_options options);
111
117 void disconnect();
118
122 [[nodiscard]] bool is_connected() const noexcept;
123
127 [[nodiscard]] bool is_logged_in() const noexcept {
128 return session_info_.has_value();
129 }
130
139 session_info_ = std::move(info);
140 }
141
147 void clear_session_info() noexcept {
148 session_info_.reset();
149 }
150
154 [[nodiscard]] bool is_admin() const noexcept {
155 return session_info_.has_value() && session_info_->is_admin;
156 }
157
161 [[nodiscard]] const std::optional<client_session_info>& session_info() const noexcept {
162 return session_info_;
163 }
164
176 template <Serializable RequestType,
177 Deserializable ResponseType,
178 messaging::message_type RequestMsgType>
179 std::expected<ResponseType, client_session_error>
180 process_request(RequestType request) {
181 using namespace ores::utility::log;
182
183 if (!client_ || !client_->is_connected()) {
184 BOOST_LOG_SEV(lg(), error) << "Not connected to server";
185 return std::unexpected(client_session_error::not_connected);
186 }
187
188 BOOST_LOG_SEV(lg(), debug) << "Processing request type: "
189 << RequestMsgType;
190
191 auto payload = request.serialize();
192 messaging::frame request_frame(RequestMsgType, 0, std::move(payload));
193
194 auto response_result = client_->send_request_sync(std::move(request_frame));
195
196 if (!response_result) {
197 BOOST_LOG_SEV(lg(), error) << "Request failed with error code: "
198 << static_cast<int>(response_result.error());
199 return std::unexpected(client_session_error::request_failed);
200 }
201
202 auto decompressed = response_result->decompressed_payload();
203 if (!decompressed) {
204 BOOST_LOG_SEV(lg(), error) << "Failed to decompress response payload";
205 return std::unexpected(client_session_error::deserialization_failed);
206 }
207
208 // Check for error response
209 if (response_result->header().type == messaging::message_type::error_response) {
210 auto err_resp = messaging::error_response::deserialize(*decompressed);
211 if (err_resp) {
212 BOOST_LOG_SEV(lg(), error) << "Server returned error: "
213 << err_resp->message;
214 }
215 return std::unexpected(client_session_error::server_error);
216 }
217
218 auto response = ResponseType::deserialize(*decompressed);
219 if (!response) {
220 BOOST_LOG_SEV(lg(), error) << "Failed to deserialize response";
221 return std::unexpected(client_session_error::deserialization_failed);
222 }
223
224 BOOST_LOG_SEV(lg(), debug) << "Successfully processed request";
225 return std::move(*response);
226 }
227
239 template <Serializable RequestType,
240 Deserializable ResponseType,
241 messaging::message_type RequestMsgType>
242 std::expected<ResponseType, client_session_error>
243 process_authenticated_request(RequestType request) {
244 using namespace ores::utility::log;
245 if (!is_logged_in()) {
246 BOOST_LOG_SEV(lg(), warn) << "Attempted authenticated request while "
247 << "not logged in";
248 return std::unexpected(client_session_error::not_logged_in);
249 }
250 return process_request<RequestType, ResponseType, RequestMsgType>(
251 std::move(request));
252 }
253
266 template <Serializable RequestType,
267 Deserializable ResponseType,
268 messaging::message_type RequestMsgType>
269 std::expected<ResponseType, client_session_error>
270 process_admin_request(RequestType request) {
271 using namespace ores::utility::log;
272 if (!is_logged_in()) {
273 BOOST_LOG_SEV(lg(), warn) << "Attempted admin request while "
274 << "not logged in";
275 return std::unexpected(client_session_error::not_logged_in);
276 }
277 if (!is_admin()) {
278 BOOST_LOG_SEV(lg(), warn) << "Attempted admin request without "
279 << "admin privileges";
280 return std::unexpected(client_session_error::admin_required);
281 }
282 return process_request<RequestType, ResponseType, RequestMsgType>(
283 std::move(request));
284 }
285
286private:
287 std::shared_ptr<client> client_;
288 std::optional<client_session_info> session_info_;
289};
290
294std::string to_string(client_session_error error);
295
296}
297
298#endif
Contains the networking elements of the comms library.
Definition client.hpp:42
std::string to_string(client_session_error error)
Convert client_session_error to string for display.
Definition client_session.cpp:79
client_session_error
Error codes specific to client session operations.
Definition client_session.hpp:49
Implements logging for ORE Studio.
Definition lifecycle_manager.hpp:30
static std::expected< error_response, error_code > deserialize(std::span< const std::byte > data)
Deserialize from frame payload.
Definition handshake.cpp:112
Complete frame with header and payload.
Definition frame.hpp:77
Configuration for the client.
Definition client_options.hpp:78
Information about the client's authenticated session.
Definition client_session.hpp:40
Client-side session manager providing auth-aware request handling.
Definition client_session.hpp:81
std::expected< void, client_session_error > connect(client_options options)
Connect to the server.
Definition client_session.cpp:33
bool is_connected() const noexcept
Check if connected to server.
Definition client_session.cpp:75
const std::optional< client_session_info > & session_info() const noexcept
Get current session info if logged in.
Definition client_session.hpp:161
std::expected< ResponseType, client_session_error > process_request(RequestType request)
Process a request that does not require authentication.
Definition client_session.hpp:180
std::expected< ResponseType, client_session_error > process_authenticated_request(RequestType request)
Process a request that requires authentication.
Definition client_session.hpp:243
bool is_admin() const noexcept
Check if logged in as admin.
Definition client_session.hpp:154
bool is_logged_in() const noexcept
Check if logged in.
Definition client_session.hpp:127
void clear_session_info() noexcept
Clear session info on logout.
Definition client_session.hpp:147
void disconnect()
Disconnect from the server.
Definition client_session.cpp:56
void set_session_info(client_session_info info)
Set session info after successful login.
Definition client_session.hpp:138
std::expected< ResponseType, client_session_error > process_admin_request(RequestType request)
Process a request that requires admin privileges.
Definition client_session.hpp:270