The Battle for Wesnoth  1.15.5+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 <cstdlib>
23 #include <sstream>
24 
25 static lg::log_domain log_mp_user_handler("mp_user_handler");
26 #define ERR_UH LOG_STREAM(err, log_mp_user_handler)
27 #define WRN_UH LOG_STREAM(warn, log_mp_user_handler)
28 #define LOG_UH LOG_STREAM(info, log_mp_user_handler)
29 #define DBG_UH LOG_STREAM(debug, log_mp_user_handler)
30 
31 namespace {
32  const int USER_INACTIVE = 1;
33  const int USER_IGNORE = 2;
34 }
35 
36 fuh::fuh(const config& c)
37  : conn(c)
38  , db_users_table_(c["db_users_table"].str())
39  , db_extra_table_(c["db_extra_table"].str())
40  , mp_mod_group_(0)
41 {
42  try {
43  mp_mod_group_ = std::stoi(c["mp_mod_group"].str());
44  } catch(...) {
45  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;
46  }
47 }
48 
49 bool fuh::login(const std::string& name, const std::string& password, const std::string& seed) {
50  // Retrieve users' password as hash
51  std::string hash;
52 
53  try {
54  hash = get_hash(name);
55  } catch (const error& e) {
56  ERR_UH << "Could not retrieve hash for user '" << name << "' :" << e.message << std::endl;
57  return false;
58  }
59 
60  std::string valid_hash;
61 
62  if(utils::md5::is_valid_hash(hash)) { // md5 hash
63  valid_hash = utils::md5(hash.substr(12,34), seed).base64_digest();
64  } else if(utils::bcrypt::is_valid_prefix(hash)) { // bcrypt hash
65  valid_hash = utils::md5(hash, seed).base64_digest();
66  } else {
67  ERR_UH << "Invalid hash for user '" << name << "'" << std::endl;
68  return false;
69  }
70 
71  if(password == valid_hash) return true;
72 
73  return false;
74 }
75 
77 
78  // Some double security, this should never be needed
79  if(!(user_exists(name))) {
80  return "";
81  }
82 
83  std::string hash;
84 
85  try {
86  hash = get_hash(name);
87  } catch (const error& e) {
88  ERR_UH << "Could not retrieve hash for user '" << name << "' :" << e.message << std::endl;
89  return "";
90  }
91 
93  return hash.substr(0,12);
94 
96  try {
98  } catch(const utils::hash_error& err) {
99  ERR_UH << "Error getting salt from hash of user '" << name << "': " << err.what() << std::endl;
100  return "";
101  }
102  }
103 
104  return "";
105 }
106 
107 void fuh::user_logged_in(const std::string& name) {
108  set_lastlogin(name, std::time(nullptr));
109 }
110 
111 bool fuh::user_exists(const std::string& name) {
112  return conn.user_exists(name);
113 }
114 
115 bool fuh::user_is_active(const std::string& name) {
116  int user_type = conn.get_user_int(db_users_table_, "user_type", name);
117  return user_type != USER_INACTIVE && user_type != USER_IGNORE;
118 }
119 
120 bool fuh::user_is_moderator(const std::string& name) {
121  if(!user_exists(name)){
122  return false;
123  }
124  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_));
125 }
126 
127 void fuh::set_is_moderator(const std::string& name, const bool& is_moderator) {
128  if(!user_exists(name)){
129  return;
130  }
131  conn.write_user_int("user_is_moderator", name, is_moderator);
132 }
133 
135 {
136  //
137  // NOTE: glob IP and email address bans are NOT supported yet since they
138  // require a different kind of query that isn't supported by our
139  // prepared SQL statement API right now. However, they are basically
140  // never used on forums.wesnoth.org, so this shouldn't be a problem
141  // for the time being.
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 
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 
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 void fuh::set_lastlogin(const std::string& user, const std::time_t& lastlogin) {
203  conn.write_user_int("user_lastvisit", user, static_cast<int>(lastlogin));
204 }
205 
207  return conn.get_uuid();
208 }
209 
211  return conn.get_tournaments();
212 }
213 
214 void fuh::db_insert_game_info(const std::string& uuid, int game_id, const std::string& version, const std::string& name, const std::string& map_name, const std::string& era_name, int reload, int observers, int is_public, int has_password, const std::string& map_source, const std::string& map_version, const std::string& era_source, const std::string& era_version){
215  conn.insert_game_info(uuid, game_id, version, name, map_name, era_name, reload, observers, is_public, has_password, map_source, map_version, era_source, era_version);
216 }
217 
218 void fuh::db_update_game_end(const std::string& uuid, int game_id, const std::string& replay_location){
219  conn.update_game_end(uuid, game_id, replay_location);
220 }
221 
222 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){
223  conn.insert_game_player_info(uuid, game_id, username, side_number, is_host, faction, version, source, current_user);
224 }
225 
226 void fuh::db_insert_modification_info(const std::string& uuid, int game_id, const std::string& modification_name, const std::string& modification_source, const std::string& modification_version){
227  conn.insert_modification_info(uuid, game_id, modification_name, modification_source, modification_version);
228 }
229 
230 void fuh::db_set_oos_flag(const std::string& uuid, int game_id){
231  conn.set_oos_flag(uuid, game_id);
232 }
233 
234 #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:90
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)
Executed when the user with the given name logged in.
std::time_t get_lastlogin(const std::string &user)
std::string str
Definition: statement.cpp:110
void db_insert_game_info(const std::string &uuid, int game_id, const std::string &version, const std::string &name, const std::string &map_name, const std::string &era_name, int reload, int observers, int is_public, int has_password, const std::string &map_source, const std::string &map_version, const std::string &era_source, const std::string &era_version)
std::string user_info(const std::string &name)
Returns a string containing info like the last login of this user.
bool user_exists(const std::string &name)
Returns true if a user with the given name exists.
std::string get_email()
void set_is_moderator(const std::string &name, const bool &is_moderator)
Mark this user as a moderator.
fuh(const config &c)
#define b
static bool is_valid_hash(const std::string &hash)
Definition: hash.cpp:96
void db_insert_modification_info(const std::string &uuid, int game_id, const std::string &modification_name, const std::string &modification_source, const std::string &modification_version)
ban_info user_is_banned(const std::string &name, const std::string &addr)
Returns true if this user account or IP address is banned.
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)
Return true if the given password matches the password for the given user.
void db_set_oos_flag(const std::string &uuid, int game_id)
logger & err()
Definition: log.cpp:78
long get_ban_type()
bool user_exists(const std::string &name)
bool user_is_active(const std::string &name)
Returns true if the specified user account is usable for logins.
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::time_t get_registrationdate(const std::string &user)
bool user_is_moderator(const std::string &name)
Returns true if this user is a moderator on this server.
std::string db_users_table_
Definition: dbconn.hpp:53
int get_user_id()
void set_lastlogin(const std::string &user, const std::time_t &lastlogin)
#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:68
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)
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)
std::string db_extra_table_
Definition: dbconn.hpp:55