diff --git a/src/lang/english.txt b/src/lang/english.txt index bb55d2194c..aff5834e51 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2487,6 +2487,7 @@ STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Your player nam STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Administrative actions to perform for this client STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Administrative actions to perform for this company STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Join this company +STR_NETWORK_CLIENT_LIST_COMPANY_AUTHORIZE_TOOLTIP :{BLACK}Authorize this client to join your company STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Send a message to this player STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Send a message to all players of this company STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Send a message to all spectators diff --git a/src/network/network.cpp b/src/network/network.cpp index 5e16ae49fb..710c2091c4 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -126,6 +126,28 @@ NetworkClientInfo::~NetworkClientInfo() return nullptr; } +/** + * Returns whether the given company can be joined by this client. + * @param company_id The id of the company. + * @return \c true when this company is allowed to join, otherwise \c false. + */ +bool NetworkClientInfo::CanJoinCompany(CompanyID company_id) const +{ + Company *c = Company::GetIfValid(company_id); + return c != nullptr && c->allow_list.Contains(this->public_key); +} + +/** + * Returns whether the given company can be joined by this client. + * @param company_id The id of the company. + * @return \c true when this company is allowed to join, otherwise \c false. + */ +bool NetworkCanJoinCompany(CompanyID company_id) +{ + NetworkClientInfo *info = NetworkClientInfo::GetByClientID(_network_own_client_id); + return info != nullptr && info->CanJoinCompany(company_id); +} + /** * Return the client state given it's client-identifier * @param client_id the ClientID to search for @@ -889,6 +911,9 @@ static void NetworkInitGameInfo() ci->client_playas = COMPANY_SPECTATOR; ci->client_name = _settings_client.network.client_name; + + NetworkAuthenticationClientHandler::EnsureValidSecretKeyAndUpdatePublicKey(_settings_client.network.client_secret_key, _settings_client.network.client_public_key); + ci->public_key = _settings_client.network.client_public_key; } /** diff --git a/src/network/network_base.h b/src/network/network_base.h index 0b3cbde7d2..497db78398 100644 --- a/src/network/network_base.h +++ b/src/network/network_base.h @@ -36,6 +36,8 @@ struct NetworkClientInfo : NetworkClientInfoPool::PoolItem<&_networkclientinfo_p ~NetworkClientInfo(); static NetworkClientInfo *GetByClientID(ClientID client_id); + + bool CanJoinCompany(CompanyID company_id) const; }; #endif /* NETWORK_BASE_H */ diff --git a/src/network/network_crypto.cpp b/src/network/network_crypto.cpp index 3cea679869..38a1f9dd09 100644 --- a/src/network/network_crypto.cpp +++ b/src/network/network_crypto.cpp @@ -443,6 +443,15 @@ void CombinedAuthenticationServerHandler::Add(CombinedAuthenticationServerHandle this->SendResponse(); } +/** + * Ensures that the given secret key is valid, and when not overwrite it with a valid secret key. Then update the public key to be associated with the secret key. + * @param secret_key The location where the secret key is stored; can be overwritten when invalid. + * @param public_key The location where the public key is stored; can be overwritten when invalid. + */ +/* static */ void NetworkAuthenticationClientHandler::EnsureValidSecretKeyAndUpdatePublicKey(std::string &secret_key, std::string &public_key) +{ + X25519AuthorizedKeyClientHandler::GetValidSecretKeyAndUpdatePublicKey(secret_key, public_key); +} /** * Create a NetworkAuthenticationClientHandler. diff --git a/src/network/network_crypto.h b/src/network/network_crypto.h index b63c90d8a0..74424860a9 100644 --- a/src/network/network_crypto.h +++ b/src/network/network_crypto.h @@ -248,6 +248,7 @@ public: */ virtual bool ReceiveEnableEncryption(struct Packet &p) = 0; + static void EnsureValidSecretKeyAndUpdatePublicKey(std::string &secret_key, std::string &public_key); static std::unique_ptr Create(std::shared_ptr password_handler, std::string &secret_key, std::string &public_key); }; diff --git a/src/network/network_func.h b/src/network/network_func.h index 91af613c04..35539fa9f6 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -75,6 +75,7 @@ void NetworkServerNewCompany(const Company *company, NetworkClientInfo *ci); bool NetworkServerChangeClientName(ClientID client_id, const std::string &new_name); +bool NetworkCanJoinCompany(CompanyID company_id); void NetworkServerDoMove(ClientID client_id, CompanyID company_id); void NetworkServerSendRcon(ClientID client_id, TextColour colour_code, const std::string &string); void NetworkServerSendChat(NetworkAction action, DestType type, int dest, const std::string &msg, ClientID from_id, int64_t data = 0, bool from_admin = false); diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index fbd90c5a12..12333ef4d7 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -1536,6 +1536,12 @@ private: ShowNetworkChatQueryWindow(DESTTYPE_CLIENT, client_id); } + static void OnClickClientAuthorize([[maybe_unused]] NetworkClientListWindow *w, [[maybe_unused]] Point pt, ClientID client_id) + { + AutoRestoreBackup cur_company(_current_company, NetworkClientInfo::GetByClientID(_network_own_client_id)->client_playas); + Command::Post(NetworkClientInfo::GetByClientID(client_id)->public_key); + } + /** * Part of RebuildList() to create the information for a single company. * @param company_id The company to build the list for. @@ -1558,6 +1564,7 @@ private: if (_network_server) this->buttons[line_count].push_back(std::make_unique(SPR_ADMIN, STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP, COLOUR_RED, ci->client_id, &NetworkClientListWindow::OnClickClientAdmin, _network_own_client_id == ci->client_id)); if (_network_own_client_id != ci->client_id) this->buttons[line_count].push_back(std::make_unique(SPR_CHAT, STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP, COLOUR_ORANGE, ci->client_id, &NetworkClientListWindow::OnClickClientChat)); + if (_network_own_client_id != ci->client_id && client_playas != COMPANY_SPECTATOR && !ci->CanJoinCompany(client_playas)) this->buttons[line_count].push_back(std::make_unique(SPR_JOIN, STR_NETWORK_CLIENT_LIST_COMPANY_AUTHORIZE_TOOLTIP, COLOUR_GREEN, ci->client_id, &NetworkClientListWindow::OnClickClientAuthorize)); if (ci->client_id == _network_own_client_id) { this->player_self_index = this->line_count; diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp index 58589da48c..1f71b10671 100644 --- a/src/toolbar_gui.cpp +++ b/src/toolbar_gui.cpp @@ -97,7 +97,7 @@ static CallBackFunction _last_started_action = CBF_NONE; ///< Last started user */ class DropDownListCompanyItem : public DropDownIcon, true>> { public: - DropDownListCompanyItem(CompanyID company, bool shaded) : DropDownIcon, true>>(SPR_COMPANY_ICON, COMPANY_SPRITE_COLOUR(company), NetworkCompanyIsPassworded(company) ? SPR_LOCK : SPR_EMPTY, PAL_NONE, STR_NULL, company, false, shaded) + DropDownListCompanyItem(CompanyID company, bool shaded) : DropDownIcon, true>>(SPR_COMPANY_ICON, COMPANY_SPRITE_COLOUR(company), NetworkCanJoinCompany(company) ? SPR_EMPTY : SPR_LOCK, PAL_NONE, STR_NULL, company, false, shaded) { SetDParam(0, company); SetDParam(1, company);