The Battle for Wesnoth  1.15.9+dev
forum_user_handler.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2018 by Thomas Baumhauer <thomas.baumhauer@NOSPAMgmail.com>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #ifdef HAVE_MYSQLPP
16 
18 #include "hash.hpp"
19 #include "log.hpp"
20 #include "config.hpp"
21 
22 #include <boost/asio/post.hpp>
23 
24 #include <cstdlib>
25 #include <sstream>
26 
27 static lg::log_domain log_mp_user_handler("mp_user_handler");
28 #define ERR_UH LOG_STREAM(err, log_mp_user_handler)
29 #define WRN_UH LOG_STREAM(warn, log_mp_user_handler)
30 #define LOG_UH LOG_STREAM(info, log_mp_user_handler)
31 #define DBG_UH LOG_STREAM(debug, log_mp_user_handler)
32 
33 namespace {
34  const int USER_INACTIVE = 1;
35  const int USER_IGNORE = 2;
36 }
37 
38 fuh::fuh(const config& c)
39  : conn_(c)
40  , db_users_table_(c["db_users_table"].str())
41  , db_extra_table_(c["db_extra_table"].str())
42  , mp_mod_group_(0)
43 {
44  try {
45  mp_mod_group_ = std::stoi(c["mp_mod_group"].str());
46  } catch(...) {
47  ERR_UH << "Failed to convert the mp_mod_group value of '" << c["mp_mod_group"].str() << "' into an int! Defaulting to " << mp_mod_group_ << "." << std::endl;
48  }
49 }
50 
51 bool fuh::login(const std::string& name, const std::string& password, const std::string& seed) {
52  // Retrieve users' password as hash
53  std::string hash;
54 
55  try {
56  hash = get_hash(name);
57  } catch (const error& e) {
58  ERR_UH << "Could not retrieve hash for user '" << name << "' :" << e.message << std::endl;
59  return false;
60  }
61 
62  std::string valid_hash;
63 
64  if(utils::md5::is_valid_hash(hash)) { // md5 hash
65  valid_hash = utils::md5(hash.substr(12,34), seed).base64_digest();
66  } else if(utils::bcrypt::is_valid_prefix(hash)) { // bcrypt hash
67  valid_hash = utils::md5(hash, seed).base64_digest();
68  } else {
69  ERR_UH << "Invalid hash for user '" << name << "'" << std::endl;
70  return false;
71  }
72 
73  if(password == valid_hash) return true;
74 
75  return false;
76 }
77 
78 std::string fuh::extract_salt(const std::string& name) {
79 
80  // Some double security, this should never be needed
81  if(!(user_exists(name))) {
82  return "";
83  }
84 
85  std::string hash;
86 
87  try {
88  hash = get_hash(name);
89  } catch (const error& e) {
90  ERR_UH << "Could not retrieve hash for user '" << name << "' :" << e.message << std::endl;
91  return "";
92  }
93 
95  return hash.substr(0,12);
96 
98  try {
100  } catch(const utils::hash_error& err) {
101  ERR_UH << "Error getting salt from hash of user '" << name << "': " << err.what() << std::endl;
102  return "";
103  }
104  }
105 
106  return "";
107 }
108 
109 void fuh::user_logged_in(const std::string& name) {
110  conn_.write_user_int("user_lastvisit", name, static_cast<int>(std::time(nullptr)));
111 }
112 
113 bool fuh::user_exists(const std::string& name) {
114  return conn_.user_exists(name);
115 }
116 
117 long fuh::get_forum_id(const std::string& name) {
118  return conn_.get_forum_id(name);
119 }
120 
121 bool fuh::user_is_active(const std::string& name) {
122  int user_type = conn_.get_user_int(db_users_table_, "user_type", name);
123  return user_type != USER_INACTIVE && user_type != USER_IGNORE;
124 }
125 
126 bool fuh::user_is_moderator(const std::string& name) {
127  if(!user_exists(name)){
128  return false;
129  }
130  return conn_.get_user_int(db_extra_table_, "user_is_moderator", name) == 1 || (mp_mod_group_ != 0 && conn_.is_user_in_group(name, mp_mod_group_));
131 }
132 
133 void fuh::set_is_moderator(const std::string& name, const bool& is_moderator) {
134  if(!user_exists(name)){
135  return;
136  }
137  conn_.write_user_int("user_is_moderator", name, is_moderator);
138 }
139 
140 fuh::ban_info fuh::user_is_banned(const std::string& name, const std::string& addr)
141 {
142  ban_check b = conn_.get_ban_info(name, addr);
143  switch(b.get_ban_type())
144  {
145  case BAN_NONE:
146  return {};
147  case BAN_IP:
148  LOG_UH << "User '" << name << "' ip " << addr << " banned by IP address\n";
149  return { BAN_IP, b.get_ban_duration() };
150  case BAN_USER:
151  LOG_UH << "User '" << name << "' uid " << b.get_user_id() << " banned by uid\n";
152  return { BAN_USER, b.get_ban_duration() };
153  case BAN_EMAIL:
154  LOG_UH << "User '" << name << "' email " << b.get_email() << " banned by email address\n";
155  return { BAN_EMAIL, b.get_ban_duration() };
156  default:
157  ERR_UH << "Invalid ban type '" << b.get_ban_type() << "' returned for user '" << name << "'\n";
158  return {};
159  }
160 }
161 
162 std::string fuh::user_info(const std::string& name) {
163  if(!user_exists(name)) {
164  throw error("No user with the name '" + name + "' exists.");
165  }
166 
167  std::time_t reg_date = get_registrationdate(name);
168  std::time_t ll_date = get_lastlogin(name);
169 
170  std::string reg_string = ctime(&reg_date);
171  std::string ll_string;
172 
173  if(ll_date) {
174  ll_string = ctime(&ll_date);
175  } else {
176  ll_string = "Never\n";
177  }
178 
179  std::stringstream info;
180  info << "Name: " << name << "\n"
181  << "Registered: " << reg_string
182  << "Last login: " << ll_string;
183  if(!user_is_active(name)) {
184  info << "This account is currently inactive.\n";
185  }
186 
187  return info.str();
188 }
189 
190 std::string fuh::get_hash(const std::string& user) {
191  return conn_.get_user_string(db_users_table_, "user_password", user);
192 }
193 
194 std::time_t fuh::get_lastlogin(const std::string& user) {
195  return std::time_t(conn_.get_user_int(db_extra_table_, "user_lastvisit", user));
196 }
197 
198 std::time_t fuh::get_registrationdate(const std::string& user) {
199  return std::time_t(conn_.get_user_int(db_users_table_, "user_regdate", user));
200 }
201 
202 std::string fuh::get_uuid(){
203  return conn_.get_uuid();
204 }
205 
206 std::string fuh::get_tournaments(){
207  return conn_.get_tournaments();
208 }
209 
210 void fuh::async_get_and_send_game_history(boost::asio::io_service& io_service, server_base& s_base, socket_ptr player_socket, int player_id, int offset) {
211  boost::asio::post([this, &s_base, player_socket, player_id, offset, &io_service] {
212  boost::asio::post(io_service, [player_socket, &s_base, doc = conn_.get_game_history(player_id, offset)]{
213  s_base.async_send_doc_queued(player_socket, *doc);
214  });
215  });
216 }
217 
218 void fuh::db_insert_game_info(const std::string& uuid, int game_id, const std::string& version, const std::string& name, int reload, int observers, int is_public, int has_password){
219  conn_.insert_game_info(uuid, game_id, version, name, reload, observers, is_public, has_password);
220 }
221 
222 void fuh::db_update_game_end(const std::string& uuid, int game_id, const std::string& replay_location){
223  conn_.update_game_end(uuid, game_id, replay_location);
224 }
225 
226 void fuh::db_insert_game_player_info(const std::string& uuid, int game_id, const std::string& username, int side_number, int is_host, const std::string& faction, const std::string& version, const std::string& source, const std::string& current_user){
227  conn_.insert_game_player_info(uuid, game_id, username, side_number, is_host, faction, version, source, current_user);
228 }
229 
230 void fuh::db_insert_game_content_info(const std::string& uuid, int game_id, const std::string& type, const std::string& name, const std::string& id, const std::string& source, const std::string& version){
231  conn_.insert_game_content_info(uuid, game_id, type, name, id, source, version);
232 }
233 
234 void fuh::db_set_oos_flag(const std::string& uuid, int game_id){
235  conn_.set_oos_flag(uuid, game_id);
236 }
237 
238 void fuh::async_test_query(boost::asio::io_service& io_service, int limit) {
239  boost::asio::post([this, limit, &io_service] {
240  ERR_UH << "async test query starts!" << std::endl;
241  int i = conn_.async_test_query(limit);
242  boost::asio::post(io_service, [i]{ ERR_UH << "async test query output: " << i << std::endl; });
243  });
244 }
245 
246 #endif //HAVE_MYSQLPP
std::string get_hash(const std::string &user)
int get_ban_duration()
virtual std::string base64_digest() const override
Definition: hash.cpp:121
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:39
logger & info()
Definition: log.cpp:91
std::string get_uuid()
static bool is_valid_prefix(const std::string &hash)
Definition: hash.cpp:187
void user_logged_in(const std::string &name)
Sets the last login time to the current time.
std::time_t get_lastlogin(const std::string &user)
std::string user_info(const std::string &name)
void async_get_and_send_game_history(boost::asio::io_service &io_service, server_base &s_base, socket_ptr player_socket, int player_id, int offset)
Runs an asynchronous query to fetch the user&#39;s game history data.
bool user_exists(const std::string &name)
std::string get_email()
void set_is_moderator(const std::string &name, const bool &is_moderator)
Sets or unsets whether the player should be considered a moderator in the extra table.
fuh(const config &c)
Reads wesnothd&#39;s config for the data needed to initialize this class and dbconn.
#define b
long get_forum_id(const std::string &name)
static bool is_valid_hash(const std::string &hash)
Definition: hash.cpp:96
ban_info user_is_banned(const std::string &name, const std::string &addr)
const char * what() const noexcept
Definition: exceptions.hpp:37
std::string get_tournaments()
bool login(const std::string &name, const std::string &password, const std::string &seed)
Retrieves the player&#39;s hashed password from the phpbb forum database and checks if it matches the has...
void db_set_oos_flag(const std::string &uuid, int game_id)
Sets the OOS flag in the database if wesnothd is told by a client it has detected an OOS error...
std::size_t i
Definition: function.cpp:933
logger & err()
Definition: log.cpp:79
long get_ban_type()
bool user_exists(const std::string &name)
bool user_is_active(const std::string &name)
Ban status description.
std::string password(const std::string &server, const std::string &login)
static bcrypt from_hash_string(const std::string &input)
Definition: hash.cpp:168
std::string name
Definition: sdl_ttf.cpp:70
std::time_t get_registrationdate(const std::string &user)
void async_send_doc_queued(socket_ptr socket, simple_wml::document &doc)
High level wrapper for sending a WML document.
bool user_is_moderator(const std::string &name)
void db_insert_game_info(const std::string &uuid, int game_id, const std::string &version, const std::string &name, int reload, int observers, int is_public, int has_password)
Inserts game related information.
std::string db_users_table_
The name of the table that contains forum user information.
Definition: dbconn.hpp:157
int get_user_id()
void db_insert_game_content_info(const std::string &uuid, int game_id, const std::string &type, const std::string &name, const std::string &id, const std::string &source, const std::string &version)
Inserts information about the content being played.
std::shared_ptr< boost::asio::ip::tcp::socket > socket_ptr
Definition: server_base.hpp:43
#define e
std::string get_salt() const
Definition: hash.cpp:194
int side_number
Definition: game_info.hpp:39
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:60
std::string extract_salt(const std::string &name)
Needed because the hashing algorithm used by phpbb requires some info from the original hash to recre...
mock_char c
void db_update_game_end(const std::string &uuid, int game_id, const std::string &replay_location)
Update the game related information when the game ends.
void async_test_query(boost::asio::io_service &io_service, int limit)
A simple test query for running a query asynchronously.
void db_insert_game_player_info(const std::string &uuid, int game_id, const std::string &username, int side_number, int is_host, const std::string &faction, const std::string &version, const std::string &source, const std::string &current_user)
Inserts player information per side.
std::string db_extra_table_
The name of the table that contains additional user information.
Definition: dbconn.hpp:161