network, web_service: Add Verification backend and use new lobby API
Added verify_backend to load user_data for members. and removed method to generate UID as this is now done server-side. Added GetUsername function and a "token" param to room_member. Also added a username to ChatEntry, so that the username can be shown (along with nicknames) in the chat dialog.
This commit is contained in:
parent
5f0e189238
commit
1a8841f96e
|
@ -42,4 +42,7 @@
|
||||||
url = https://github.com/zeromq/libzmq
|
url = https://github.com/zeromq/libzmq
|
||||||
[submodule "externals/cppzmq"]
|
[submodule "externals/cppzmq"]
|
||||||
path = externals/cppzmq
|
path = externals/cppzmq
|
||||||
url = https://github.com/zeromq/cppzmq
|
url = https://github.com/zeromq/cppzmq
|
||||||
|
[submodule "cpp-jwt"]
|
||||||
|
path = externals/cpp-jwt
|
||||||
|
url = https://github.com/arun11299/cpp-jwt.git
|
||||||
|
|
|
@ -82,6 +82,10 @@ if (ENABLE_WEB_SERVICE)
|
||||||
target_include_directories(ssl INTERFACE ./libressl/include)
|
target_include_directories(ssl INTERFACE ./libressl/include)
|
||||||
target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP)
|
target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP)
|
||||||
|
|
||||||
|
# JSON
|
||||||
|
add_library(json-headers INTERFACE)
|
||||||
|
target_include_directories(json-headers INTERFACE ./json)
|
||||||
|
|
||||||
# lurlparser
|
# lurlparser
|
||||||
add_subdirectory(lurlparser EXCLUDE_FROM_ALL)
|
add_subdirectory(lurlparser EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
|
@ -89,9 +93,9 @@ if (ENABLE_WEB_SERVICE)
|
||||||
add_library(httplib INTERFACE)
|
add_library(httplib INTERFACE)
|
||||||
target_include_directories(httplib INTERFACE ./httplib)
|
target_include_directories(httplib INTERFACE ./httplib)
|
||||||
|
|
||||||
# JSON
|
# cpp-jwt
|
||||||
add_library(json-headers INTERFACE)
|
add_library(cpp-jwt INTERFACE)
|
||||||
target_include_directories(json-headers INTERFACE ./json)
|
target_include_directories(cpp-jwt INTERFACE ./cpp-jwt/include)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (ENABLE_SCRIPTING)
|
if (ENABLE_SCRIPTING)
|
||||||
|
|
|
@ -7,8 +7,10 @@ add_library(network STATIC
|
||||||
room.h
|
room.h
|
||||||
room_member.cpp
|
room_member.cpp
|
||||||
room_member.h
|
room_member.h
|
||||||
|
verify_user.cpp
|
||||||
|
verify_user.h
|
||||||
)
|
)
|
||||||
|
|
||||||
create_target_directory_groups(network)
|
create_target_directory_groups(network)
|
||||||
|
|
||||||
target_link_libraries(network PRIVATE common enet)
|
target_link_libraries(network PRIVATE common cpp-jwt enet)
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "enet/enet.h"
|
#include "enet/enet.h"
|
||||||
#include "network/packet.h"
|
#include "network/packet.h"
|
||||||
#include "network/room.h"
|
#include "network/room.h"
|
||||||
|
#include "network/verify_user.h"
|
||||||
|
|
||||||
namespace Network {
|
namespace Network {
|
||||||
|
|
||||||
|
@ -28,6 +29,9 @@ public:
|
||||||
std::atomic<State> state{State::Closed}; ///< Current state of the room.
|
std::atomic<State> state{State::Closed}; ///< Current state of the room.
|
||||||
RoomInformation room_information; ///< Information about this room.
|
RoomInformation room_information; ///< Information about this room.
|
||||||
|
|
||||||
|
std::string verify_UID; ///< A GUID which may be used for verfication.
|
||||||
|
mutable std::mutex verify_UID_mutex; ///< Mutex for verify_UID
|
||||||
|
|
||||||
std::string password; ///< The password required to connect to this room.
|
std::string password; ///< The password required to connect to this room.
|
||||||
|
|
||||||
struct Member {
|
struct Member {
|
||||||
|
@ -35,7 +39,9 @@ public:
|
||||||
std::string console_id_hash; ///< A hash of the console ID of the member.
|
std::string console_id_hash; ///< A hash of the console ID of the member.
|
||||||
GameInfo game_info; ///< The current game of the member
|
GameInfo game_info; ///< The current game of the member
|
||||||
MacAddress mac_address; ///< The assigned mac address of the member.
|
MacAddress mac_address; ///< The assigned mac address of the member.
|
||||||
ENetPeer* peer; ///< The remote peer.
|
/// Data of the user, often including authenticated forum username.
|
||||||
|
VerifyUser::UserData user_data;
|
||||||
|
ENetPeer* peer; ///< The remote peer.
|
||||||
};
|
};
|
||||||
using MemberList = std::vector<Member>;
|
using MemberList = std::vector<Member>;
|
||||||
MemberList members; ///< Information about the members of this room
|
MemberList members; ///< Information about the members of this room
|
||||||
|
@ -48,6 +54,9 @@ public:
|
||||||
/// Thread that receives and dispatches network packets
|
/// Thread that receives and dispatches network packets
|
||||||
std::unique_ptr<std::thread> room_thread;
|
std::unique_ptr<std::thread> room_thread;
|
||||||
|
|
||||||
|
/// Verification backend of the room
|
||||||
|
std::unique_ptr<VerifyUser::Backend> verify_backend;
|
||||||
|
|
||||||
/// Thread function that will receive and dispatch messages until the room is destroyed.
|
/// Thread function that will receive and dispatch messages until the room is destroyed.
|
||||||
void ServerLoop();
|
void ServerLoop();
|
||||||
void StartLoop();
|
void StartLoop();
|
||||||
|
@ -165,11 +174,6 @@ public:
|
||||||
* to all other clients.
|
* to all other clients.
|
||||||
*/
|
*/
|
||||||
void HandleClientDisconnection(ENetPeer* client);
|
void HandleClientDisconnection(ENetPeer* client);
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a random ID in the form 12345678-1234-1234-1234-123456789012
|
|
||||||
*/
|
|
||||||
void CreateUniqueID();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// RoomImpl
|
// RoomImpl
|
||||||
|
@ -238,6 +242,9 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {
|
||||||
std::string pass;
|
std::string pass;
|
||||||
packet >> pass;
|
packet >> pass;
|
||||||
|
|
||||||
|
std::string token;
|
||||||
|
packet >> token;
|
||||||
|
|
||||||
if (pass != password) {
|
if (pass != password) {
|
||||||
SendWrongPassword(event->peer);
|
SendWrongPassword(event->peer);
|
||||||
return;
|
return;
|
||||||
|
@ -276,6 +283,13 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {
|
||||||
member.nickname = nickname;
|
member.nickname = nickname;
|
||||||
member.peer = event->peer;
|
member.peer = event->peer;
|
||||||
|
|
||||||
|
std::string uid;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(verify_UID_mutex);
|
||||||
|
uid = verify_UID;
|
||||||
|
}
|
||||||
|
member.user_data = verify_backend->LoadUserData(uid, token);
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(member_mutex);
|
std::lock_guard<std::mutex> lock(member_mutex);
|
||||||
members.push_back(std::move(member));
|
members.push_back(std::move(member));
|
||||||
|
@ -407,7 +421,6 @@ void Room::RoomImpl::BroadcastRoomInformation() {
|
||||||
packet << room_information.name;
|
packet << room_information.name;
|
||||||
packet << room_information.description;
|
packet << room_information.description;
|
||||||
packet << room_information.member_slots;
|
packet << room_information.member_slots;
|
||||||
packet << room_information.uid;
|
|
||||||
packet << room_information.port;
|
packet << room_information.port;
|
||||||
packet << room_information.preferred_game;
|
packet << room_information.preferred_game;
|
||||||
|
|
||||||
|
@ -419,6 +432,9 @@ void Room::RoomImpl::BroadcastRoomInformation() {
|
||||||
packet << member.mac_address;
|
packet << member.mac_address;
|
||||||
packet << member.game_info.name;
|
packet << member.game_info.name;
|
||||||
packet << member.game_info.id;
|
packet << member.game_info.id;
|
||||||
|
packet << member.user_data.username;
|
||||||
|
packet << member.user_data.display_name;
|
||||||
|
packet << member.user_data.avatar_url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -511,6 +527,7 @@ void Room::RoomImpl::HandleChatPacket(const ENetEvent* event) {
|
||||||
Packet out_packet;
|
Packet out_packet;
|
||||||
out_packet << static_cast<u8>(IdChatMessage);
|
out_packet << static_cast<u8>(IdChatMessage);
|
||||||
out_packet << sending_member->nickname;
|
out_packet << sending_member->nickname;
|
||||||
|
out_packet << sending_member->user_data.username;
|
||||||
out_packet << message;
|
out_packet << message;
|
||||||
|
|
||||||
ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(),
|
ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(),
|
||||||
|
@ -567,20 +584,6 @@ void Room::RoomImpl::HandleClientDisconnection(ENetPeer* client) {
|
||||||
BroadcastRoomInformation();
|
BroadcastRoomInformation();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Room::RoomImpl::CreateUniqueID() {
|
|
||||||
std::uniform_int_distribution<> dis(0, 9999);
|
|
||||||
std::ostringstream stream;
|
|
||||||
stream << std::setfill('0') << std::setw(4) << dis(random_gen);
|
|
||||||
stream << std::setfill('0') << std::setw(4) << dis(random_gen) << "-";
|
|
||||||
stream << std::setfill('0') << std::setw(4) << dis(random_gen) << "-";
|
|
||||||
stream << std::setfill('0') << std::setw(4) << dis(random_gen) << "-";
|
|
||||||
stream << std::setfill('0') << std::setw(4) << dis(random_gen) << "-";
|
|
||||||
stream << std::setfill('0') << std::setw(4) << dis(random_gen);
|
|
||||||
stream << std::setfill('0') << std::setw(4) << dis(random_gen);
|
|
||||||
stream << std::setfill('0') << std::setw(4) << dis(random_gen);
|
|
||||||
room_information.uid = stream.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Room
|
// Room
|
||||||
Room::Room() : room_impl{std::make_unique<RoomImpl>()} {}
|
Room::Room() : room_impl{std::make_unique<RoomImpl>()} {}
|
||||||
|
|
||||||
|
@ -589,7 +592,7 @@ Room::~Room() = default;
|
||||||
bool Room::Create(const std::string& name, const std::string& description,
|
bool Room::Create(const std::string& name, const std::string& description,
|
||||||
const std::string& server_address, u16 server_port, const std::string& password,
|
const std::string& server_address, u16 server_port, const std::string& password,
|
||||||
const u32 max_connections, const std::string& preferred_game,
|
const u32 max_connections, const std::string& preferred_game,
|
||||||
u64 preferred_game_id) {
|
u64 preferred_game_id, std::unique_ptr<VerifyUser::Backend> verify_backend) {
|
||||||
ENetAddress address;
|
ENetAddress address;
|
||||||
address.host = ENET_HOST_ANY;
|
address.host = ENET_HOST_ANY;
|
||||||
if (!server_address.empty()) {
|
if (!server_address.empty()) {
|
||||||
|
@ -597,8 +600,8 @@ bool Room::Create(const std::string& name, const std::string& description,
|
||||||
}
|
}
|
||||||
address.port = server_port;
|
address.port = server_port;
|
||||||
|
|
||||||
// In order to send the room is full message to the connecting client, we need to leave one slot
|
// In order to send the room is full message to the connecting client, we need to leave one
|
||||||
// open so enet won't reject the incoming connection without telling us
|
// slot open so enet won't reject the incoming connection without telling us
|
||||||
room_impl->server = enet_host_create(&address, max_connections + 1, NumChannels, 0, 0);
|
room_impl->server = enet_host_create(&address, max_connections + 1, NumChannels, 0, 0);
|
||||||
if (!room_impl->server) {
|
if (!room_impl->server) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -612,7 +615,7 @@ bool Room::Create(const std::string& name, const std::string& description,
|
||||||
room_impl->room_information.preferred_game = preferred_game;
|
room_impl->room_information.preferred_game = preferred_game;
|
||||||
room_impl->room_information.preferred_game_id = preferred_game_id;
|
room_impl->room_information.preferred_game_id = preferred_game_id;
|
||||||
room_impl->password = password;
|
room_impl->password = password;
|
||||||
room_impl->CreateUniqueID();
|
room_impl->verify_backend = std::move(verify_backend);
|
||||||
|
|
||||||
room_impl->StartLoop();
|
room_impl->StartLoop();
|
||||||
return true;
|
return true;
|
||||||
|
@ -626,12 +629,20 @@ const RoomInformation& Room::GetRoomInformation() const {
|
||||||
return room_impl->room_information;
|
return room_impl->room_information;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Room::GetVerifyUID() const {
|
||||||
|
std::lock_guard<std::mutex> lock(room_impl->verify_UID_mutex);
|
||||||
|
return room_impl->verify_UID;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<Room::Member> Room::GetRoomMemberList() const {
|
std::vector<Room::Member> Room::GetRoomMemberList() const {
|
||||||
std::vector<Room::Member> member_list;
|
std::vector<Room::Member> member_list;
|
||||||
std::lock_guard<std::mutex> lock(room_impl->member_mutex);
|
std::lock_guard<std::mutex> lock(room_impl->member_mutex);
|
||||||
for (const auto& member_impl : room_impl->members) {
|
for (const auto& member_impl : room_impl->members) {
|
||||||
Member member;
|
Member member;
|
||||||
member.nickname = member_impl.nickname;
|
member.nickname = member_impl.nickname;
|
||||||
|
member.username = member_impl.user_data.username;
|
||||||
|
member.display_name = member_impl.user_data.display_name;
|
||||||
|
member.avatar_url = member_impl.user_data.avatar_url;
|
||||||
member.mac_address = member_impl.mac_address;
|
member.mac_address = member_impl.mac_address;
|
||||||
member.game_info = member_impl.game_info;
|
member.game_info = member_impl.game_info;
|
||||||
member_list.push_back(member);
|
member_list.push_back(member);
|
||||||
|
@ -643,6 +654,11 @@ bool Room::HasPassword() const {
|
||||||
return !room_impl->password.empty();
|
return !room_impl->password.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Room::SetVerifyUID(const std::string& uid) {
|
||||||
|
std::lock_guard<std::mutex> lock(room_impl->verify_UID_mutex);
|
||||||
|
room_impl->verify_UID = uid;
|
||||||
|
}
|
||||||
|
|
||||||
void Room::Destroy() {
|
void Room::Destroy() {
|
||||||
room_impl->state = State::Closed;
|
room_impl->state = State::Closed;
|
||||||
room_impl->room_thread->join();
|
room_impl->room_thread->join();
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "network/verify_user.h"
|
||||||
|
|
||||||
namespace Network {
|
namespace Network {
|
||||||
|
|
||||||
|
@ -27,7 +28,6 @@ struct RoomInformation {
|
||||||
std::string name; ///< Name of the server
|
std::string name; ///< Name of the server
|
||||||
std::string description; ///< Server description
|
std::string description; ///< Server description
|
||||||
u32 member_slots; ///< Maximum number of members in this room
|
u32 member_slots; ///< Maximum number of members in this room
|
||||||
std::string uid; ///< The unique ID of the room
|
|
||||||
u16 port; ///< The port of this room
|
u16 port; ///< The port of this room
|
||||||
std::string preferred_game; ///< Game to advertise that you want to play
|
std::string preferred_game; ///< Game to advertise that you want to play
|
||||||
u64 preferred_game_id; ///< Title ID for the advertised game
|
u64 preferred_game_id; ///< Title ID for the advertised game
|
||||||
|
@ -72,9 +72,12 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Member {
|
struct Member {
|
||||||
std::string nickname; ///< The nickname of the member.
|
std::string nickname; ///< The nickname of the member.
|
||||||
GameInfo game_info; ///< The current game of the member
|
std::string username; ///< The web services username of the member. Can be empty.
|
||||||
MacAddress mac_address; ///< The assigned mac address of the member.
|
std::string display_name; ///< The web services display name of the member. Can be empty.
|
||||||
|
std::string avatar_url; ///< Url to the member's avatar. Can be empty.
|
||||||
|
GameInfo game_info; ///< The current game of the member
|
||||||
|
MacAddress mac_address; ///< The assigned mac address of the member.
|
||||||
};
|
};
|
||||||
|
|
||||||
Room();
|
Room();
|
||||||
|
@ -90,6 +93,11 @@ public:
|
||||||
*/
|
*/
|
||||||
const RoomInformation& GetRoomInformation() const;
|
const RoomInformation& GetRoomInformation() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the verify UID of this room.
|
||||||
|
*/
|
||||||
|
std::string GetVerifyUID() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a list of the mbmers connected to the room.
|
* Gets a list of the mbmers connected to the room.
|
||||||
*/
|
*/
|
||||||
|
@ -108,7 +116,13 @@ public:
|
||||||
const std::string& server = "", u16 server_port = DefaultRoomPort,
|
const std::string& server = "", u16 server_port = DefaultRoomPort,
|
||||||
const std::string& password = "",
|
const std::string& password = "",
|
||||||
const u32 max_connections = MaxConcurrentConnections,
|
const u32 max_connections = MaxConcurrentConnections,
|
||||||
const std::string& preferred_game = "", u64 preferred_game_id = 0);
|
const std::string& preferred_game = "", u64 preferred_game_id = 0,
|
||||||
|
std::unique_ptr<VerifyUser::Backend> verify_backend = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the verification GUID of the room.
|
||||||
|
*/
|
||||||
|
void SetVerifyUID(const std::string& uid);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroys the socket
|
* Destroys the socket
|
||||||
|
|
|
@ -33,7 +33,11 @@ public:
|
||||||
void SetState(const State new_state);
|
void SetState(const State new_state);
|
||||||
bool IsConnected() const;
|
bool IsConnected() const;
|
||||||
|
|
||||||
std::string nickname; ///< The nickname of this member.
|
std::string nickname; ///< The nickname of this member.
|
||||||
|
|
||||||
|
std::string username; ///< The username of this member.
|
||||||
|
mutable std::mutex username_mutex; ///< Mutex for locking username.
|
||||||
|
|
||||||
MacAddress mac_address; ///< The mac_address of this member.
|
MacAddress mac_address; ///< The mac_address of this member.
|
||||||
|
|
||||||
std::mutex network_mutex; ///< Mutex that controls access to the `client` variable.
|
std::mutex network_mutex; ///< Mutex that controls access to the `client` variable.
|
||||||
|
@ -80,7 +84,7 @@ public:
|
||||||
*/
|
*/
|
||||||
void SendJoinRequest(const std::string& nickname, const std::string& console_id_hash,
|
void SendJoinRequest(const std::string& nickname, const std::string& console_id_hash,
|
||||||
const MacAddress& preferred_mac = NoPreferredMac,
|
const MacAddress& preferred_mac = NoPreferredMac,
|
||||||
const std::string& password = "");
|
const std::string& password = "", const std::string& token = "");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts a MAC Address from a received ENet packet.
|
* Extracts a MAC Address from a received ENet packet.
|
||||||
|
@ -210,7 +214,8 @@ void RoomMember::RoomMemberImpl::Send(Packet&& packet) {
|
||||||
void RoomMember::RoomMemberImpl::SendJoinRequest(const std::string& nickname,
|
void RoomMember::RoomMemberImpl::SendJoinRequest(const std::string& nickname,
|
||||||
const std::string& console_id_hash,
|
const std::string& console_id_hash,
|
||||||
const MacAddress& preferred_mac,
|
const MacAddress& preferred_mac,
|
||||||
const std::string& password) {
|
const std::string& password,
|
||||||
|
const std::string& token) {
|
||||||
Packet packet;
|
Packet packet;
|
||||||
packet << static_cast<u8>(IdJoinRequest);
|
packet << static_cast<u8>(IdJoinRequest);
|
||||||
packet << nickname;
|
packet << nickname;
|
||||||
|
@ -218,6 +223,7 @@ void RoomMember::RoomMemberImpl::SendJoinRequest(const std::string& nickname,
|
||||||
packet << preferred_mac;
|
packet << preferred_mac;
|
||||||
packet << network_version;
|
packet << network_version;
|
||||||
packet << password;
|
packet << password;
|
||||||
|
packet << token;
|
||||||
Send(std::move(packet));
|
Send(std::move(packet));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,7 +238,6 @@ void RoomMember::RoomMemberImpl::HandleRoomInformationPacket(const ENetEvent* ev
|
||||||
packet >> info.name;
|
packet >> info.name;
|
||||||
packet >> info.description;
|
packet >> info.description;
|
||||||
packet >> info.member_slots;
|
packet >> info.member_slots;
|
||||||
packet >> info.uid;
|
|
||||||
packet >> info.port;
|
packet >> info.port;
|
||||||
packet >> info.preferred_game;
|
packet >> info.preferred_game;
|
||||||
room_information.name = info.name;
|
room_information.name = info.name;
|
||||||
|
@ -250,6 +255,16 @@ void RoomMember::RoomMemberImpl::HandleRoomInformationPacket(const ENetEvent* ev
|
||||||
packet >> member.mac_address;
|
packet >> member.mac_address;
|
||||||
packet >> member.game_info.name;
|
packet >> member.game_info.name;
|
||||||
packet >> member.game_info.id;
|
packet >> member.game_info.id;
|
||||||
|
packet >> member.username;
|
||||||
|
packet >> member.display_name;
|
||||||
|
packet >> member.avatar_url;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(username_mutex);
|
||||||
|
if (member.nickname == nickname) {
|
||||||
|
username = member.username;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Invoke(room_information);
|
Invoke(room_information);
|
||||||
}
|
}
|
||||||
|
@ -297,6 +312,7 @@ void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) {
|
||||||
|
|
||||||
ChatEntry chat_entry{};
|
ChatEntry chat_entry{};
|
||||||
packet >> chat_entry.nickname;
|
packet >> chat_entry.nickname;
|
||||||
|
packet >> chat_entry.username;
|
||||||
packet >> chat_entry.message;
|
packet >> chat_entry.message;
|
||||||
Invoke<ChatEntry>(chat_entry);
|
Invoke<ChatEntry>(chat_entry);
|
||||||
}
|
}
|
||||||
|
@ -391,6 +407,11 @@ const std::string& RoomMember::GetNickname() const {
|
||||||
return room_member_impl->nickname;
|
return room_member_impl->nickname;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string& RoomMember::GetUsername() const {
|
||||||
|
std::lock_guard<std::mutex> lock(room_member_impl->username_mutex);
|
||||||
|
return room_member_impl->username;
|
||||||
|
}
|
||||||
|
|
||||||
const MacAddress& RoomMember::GetMacAddress() const {
|
const MacAddress& RoomMember::GetMacAddress() const {
|
||||||
ASSERT_MSG(IsConnected(), "Tried to get MAC address while not connected");
|
ASSERT_MSG(IsConnected(), "Tried to get MAC address while not connected");
|
||||||
return room_member_impl->mac_address;
|
return room_member_impl->mac_address;
|
||||||
|
@ -402,7 +423,8 @@ RoomInformation RoomMember::GetRoomInformation() const {
|
||||||
|
|
||||||
void RoomMember::Join(const std::string& nick, const std::string& console_id_hash,
|
void RoomMember::Join(const std::string& nick, const std::string& console_id_hash,
|
||||||
const char* server_addr, u16 server_port, u16 client_port,
|
const char* server_addr, u16 server_port, u16 client_port,
|
||||||
const MacAddress& preferred_mac, const std::string& password) {
|
const MacAddress& preferred_mac, const std::string& password,
|
||||||
|
const std::string& token) {
|
||||||
// If the member is connected, kill the connection first
|
// If the member is connected, kill the connection first
|
||||||
if (room_member_impl->loop_thread && room_member_impl->loop_thread->joinable()) {
|
if (room_member_impl->loop_thread && room_member_impl->loop_thread->joinable()) {
|
||||||
Leave();
|
Leave();
|
||||||
|
@ -435,7 +457,7 @@ void RoomMember::Join(const std::string& nick, const std::string& console_id_has
|
||||||
if (net > 0 && event.type == ENET_EVENT_TYPE_CONNECT) {
|
if (net > 0 && event.type == ENET_EVENT_TYPE_CONNECT) {
|
||||||
room_member_impl->nickname = nick;
|
room_member_impl->nickname = nick;
|
||||||
room_member_impl->StartLoop();
|
room_member_impl->StartLoop();
|
||||||
room_member_impl->SendJoinRequest(nick, console_id_hash, preferred_mac, password);
|
room_member_impl->SendJoinRequest(nick, console_id_hash, preferred_mac, password, token);
|
||||||
SendGameInfo(room_member_impl->current_game_info);
|
SendGameInfo(room_member_impl->current_game_info);
|
||||||
} else {
|
} else {
|
||||||
enet_peer_disconnect(room_member_impl->server, 0);
|
enet_peer_disconnect(room_member_impl->server, 0);
|
||||||
|
|
|
@ -35,7 +35,9 @@ struct WifiPacket {
|
||||||
/// Represents a chat message.
|
/// Represents a chat message.
|
||||||
struct ChatEntry {
|
struct ChatEntry {
|
||||||
std::string nickname; ///< Nickname of the client who sent this message.
|
std::string nickname; ///< Nickname of the client who sent this message.
|
||||||
std::string message; ///< Body of the message.
|
/// Web services username of the client who sent this message, can be empty.
|
||||||
|
std::string username;
|
||||||
|
std::string message; ///< Body of the message.
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -64,7 +66,10 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MemberInformation {
|
struct MemberInformation {
|
||||||
std::string nickname; ///< Nickname of the member.
|
std::string nickname; ///< Nickname of the member.
|
||||||
|
std::string username; ///< The web services username of the member. Can be empty.
|
||||||
|
std::string display_name; ///< The web services display name of the member. Can be empty.
|
||||||
|
std::string avatar_url; ///< Url to the member's avatar. Can be empty.
|
||||||
GameInfo game_info; ///< Name of the game they're currently playing, or empty if they're
|
GameInfo game_info; ///< Name of the game they're currently playing, or empty if they're
|
||||||
/// not playing anything.
|
/// not playing anything.
|
||||||
MacAddress mac_address; ///< MAC address associated with this member.
|
MacAddress mac_address; ///< MAC address associated with this member.
|
||||||
|
@ -100,6 +105,11 @@ public:
|
||||||
*/
|
*/
|
||||||
const std::string& GetNickname() const;
|
const std::string& GetNickname() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the username of the RoomMember.
|
||||||
|
*/
|
||||||
|
const std::string& GetUsername() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the MAC address of the RoomMember.
|
* Returns the MAC address of the RoomMember.
|
||||||
*/
|
*/
|
||||||
|
@ -123,7 +133,7 @@ public:
|
||||||
void Join(const std::string& nickname, const std::string& console_id_hash,
|
void Join(const std::string& nickname, const std::string& console_id_hash,
|
||||||
const char* server_addr = "127.0.0.1", const u16 server_port = DefaultRoomPort,
|
const char* server_addr = "127.0.0.1", const u16 server_port = DefaultRoomPort,
|
||||||
const u16 client_port = 0, const MacAddress& preferred_mac = NoPreferredMac,
|
const u16 client_port = 0, const MacAddress& preferred_mac = NoPreferredMac,
|
||||||
const std::string& password = "");
|
const std::string& password = "", const std::string& token = "");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a WiFi packet to the room.
|
* Sends a WiFi packet to the room.
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
// Copyright 2018 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "network/verify_user.h"
|
||||||
|
|
||||||
|
namespace Network::VerifyUser {
|
||||||
|
|
||||||
|
Backend::~Backend() = default;
|
||||||
|
|
||||||
|
NullBackend::~NullBackend() = default;
|
||||||
|
|
||||||
|
UserData NullBackend::LoadUserData([[maybe_unused]] const std::string& verify_UID,
|
||||||
|
[[maybe_unused]] const std::string& token) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Network::VerifyUser
|
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright 2018 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
|
||||||
|
namespace Network::VerifyUser {
|
||||||
|
|
||||||
|
struct UserData {
|
||||||
|
std::string username;
|
||||||
|
std::string display_name;
|
||||||
|
std::string avatar_url;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A backend used for verifying users and loading user data.
|
||||||
|
*/
|
||||||
|
class Backend {
|
||||||
|
public:
|
||||||
|
virtual ~Backend();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies the given token and loads the information into a UserData struct.
|
||||||
|
* @param verify_UID A GUID that may be used for verification.
|
||||||
|
* @param token A token that contains user data and verification data. The format and content is
|
||||||
|
* decided by backends.
|
||||||
|
*/
|
||||||
|
virtual UserData LoadUserData(const std::string& verify_UID, const std::string& token) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A null backend where the token is ignored.
|
||||||
|
* No verification is performed here and the function returns an empty UserData.
|
||||||
|
*/
|
||||||
|
class NullBackend final : public Backend {
|
||||||
|
public:
|
||||||
|
~NullBackend();
|
||||||
|
|
||||||
|
UserData LoadUserData(const std::string& verify_UID, const std::string& token) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Network::VerifyUser
|
|
@ -5,6 +5,8 @@ add_library(web_service STATIC
|
||||||
telemetry_json.h
|
telemetry_json.h
|
||||||
verify_login.cpp
|
verify_login.cpp
|
||||||
verify_login.h
|
verify_login.h
|
||||||
|
verify_user_jwt.cpp
|
||||||
|
verify_user_jwt.h
|
||||||
web_backend.cpp
|
web_backend.cpp
|
||||||
web_backend.h
|
web_backend.h
|
||||||
)
|
)
|
||||||
|
@ -15,4 +17,4 @@ get_directory_property(OPENSSL_LIBS
|
||||||
DIRECTORY ${PROJECT_SOURCE_DIR}/externals/libressl
|
DIRECTORY ${PROJECT_SOURCE_DIR}/externals/libressl
|
||||||
DEFINITION OPENSSL_LIBS)
|
DEFINITION OPENSSL_LIBS)
|
||||||
target_compile_definitions(web_service PRIVATE -DCPPHTTPLIB_OPENSSL_SUPPORT)
|
target_compile_definitions(web_service PRIVATE -DCPPHTTPLIB_OPENSSL_SUPPORT)
|
||||||
target_link_libraries(web_service PRIVATE common json-headers ${OPENSSL_LIBS} httplib lurlparser)
|
target_link_libraries(web_service PRIVATE common network json-headers ${OPENSSL_LIBS} httplib lurlparser cpp-jwt)
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
// Copyright 2018 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <system_error>
|
||||||
|
#include <jwt/jwt.hpp>
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "common/web_result.h"
|
||||||
|
#include "web_service/verify_user_jwt.h"
|
||||||
|
#include "web_service/web_backend.h"
|
||||||
|
|
||||||
|
namespace WebService {
|
||||||
|
|
||||||
|
static std::string public_key;
|
||||||
|
std::string GetPublicKey(const std::string& host) {
|
||||||
|
if (public_key.empty()) {
|
||||||
|
Client client(host, "", ""); // no need for credentials here
|
||||||
|
public_key = client.GetJson("/jwt/external/key.pem", true).returned_data;
|
||||||
|
if (public_key.empty()) {
|
||||||
|
LOG_ERROR(WebService, "Could not fetch external JWT public key, verification may fail");
|
||||||
|
} else {
|
||||||
|
LOG_INFO(WebService, "Fetched external JWT public key (size={})", public_key.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return public_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
VerifyUserJWT::VerifyUserJWT(const std::string& host) : pub_key(GetPublicKey(host)) {}
|
||||||
|
|
||||||
|
Network::VerifyUser::UserData VerifyUserJWT::LoadUserData(const std::string& verify_UID,
|
||||||
|
const std::string& token) {
|
||||||
|
const std::string audience = fmt::format("external-{}", verify_UID);
|
||||||
|
using namespace jwt::params;
|
||||||
|
std::error_code error;
|
||||||
|
auto decoded =
|
||||||
|
jwt::decode(token, algorithms({"rs256"}), error, secret(pub_key), issuer("citra-core"),
|
||||||
|
aud(audience), validate_iat(true), validate_jti(true));
|
||||||
|
if (error) {
|
||||||
|
LOG_INFO(WebService, "Verification failed: category={}, code={}, message={}",
|
||||||
|
error.category().name(), error.value(), error.message());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
Network::VerifyUser::UserData user_data{};
|
||||||
|
if (decoded.payload().has_claim("username")) {
|
||||||
|
user_data.username = decoded.payload().get_claim_value<std::string>("username");
|
||||||
|
}
|
||||||
|
if (decoded.payload().has_claim("displayName")) {
|
||||||
|
user_data.display_name = decoded.payload().get_claim_value<std::string>("displayName");
|
||||||
|
}
|
||||||
|
if (decoded.payload().has_claim("avatarUrl")) {
|
||||||
|
user_data.avatar_url = decoded.payload().get_claim_value<std::string>("avatarUrl");
|
||||||
|
}
|
||||||
|
return user_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace WebService
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2018 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include "network/verify_user.h"
|
||||||
|
#include "web_service/web_backend.h"
|
||||||
|
|
||||||
|
namespace WebService {
|
||||||
|
|
||||||
|
class VerifyUserJWT final : public Network::VerifyUser::Backend {
|
||||||
|
public:
|
||||||
|
VerifyUserJWT(const std::string& host);
|
||||||
|
~VerifyUserJWT() = default;
|
||||||
|
|
||||||
|
Network::VerifyUser::UserData LoadUserData(const std::string& verify_UID,
|
||||||
|
const std::string& token) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string pub_key;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace WebService
|
Reference in New Issue