00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034 #include "srv-users.h"
00035
00036 #include <dirent.h>
00037
00038 #include "common/wave_ex.h"
00039 #include "crypto/crypto.h"
00040 #include "datahash/datahash_text.h"
00041 #include "datahash/datahash_util.h"
00042 #include "perf/perf.h"
00043 #include "threadsafe/threadsafe_map.h"
00044 #include "util/file.h"
00045
00046
00047 namespace aesop {
00048
00049
00050 UserManager::~UserManager(void) throw() { }
00051
00052
00053
00054
00055
00056 static const int s_minUsernameLength = 3;
00057 static const int s_maxUsernameLength = 15;
00058
00059
00060 static const char * s_configFilename = "user-db.config";
00061
00062
00063
00065
00066
00067
00069
00070 class UserMgr : public UserManager {
00071 public:
00072 UserMgr(void) throw();
00073 ~UserMgr(void) throw() { }
00074
00075
00076 void initialize(IN const Datahash * params);
00077
00078
00079 bool canCreateNewUsersTS(void) const throw() { return m_canCreateNewUsers; }
00080 bool passwordRequiredTS(void) const throw() { return m_passwordRequired; }
00081 void getUsernamesTS(OUT SetString& names);
00082 bool logInAsUserTS(IN const char * username,
00083 IN const char * password,
00084 OUT std::string& playerGuid,
00085 OUT std::string& diagnostic);
00086 bool createUserTS(IN const char * username,
00087 OUT std::string& diagnostic);
00088 bool isAdminTS(IN const char * username);
00089
00090 private:
00091
00092 typedef threadsafe_map<std::string, long> user_map_t;
00093
00094
00095 void getUserPath(IN const char * username,
00096 OUT std::string& path);
00097
00098
00099 std::string m_userDir;
00100 user_map_t m_userMap;
00101 smart_mutex m_mutex;
00102 bool m_canCreateNewUsers;
00103 bool m_passwordRequired;
00104 };
00105
00106
00107
00108 UserMgr::UserMgr(void)
00109 throw()
00110 {
00111 }
00112
00113
00114
00115 void
00116 UserMgr::initialize
00117 (
00118 IN const Datahash * params
00119 )
00120 {
00121 ASSERT(params, "null");
00122
00123 m_userDir = getString(params, "userDir");
00124 DPRINTF("Using user directory = %s", m_userDir.c_str());
00125
00126
00127 std::string configFile = m_userDir;
00128 configFile += "/";
00129 configFile += s_configFilename;
00130
00131 DPRINTF(" Loading user db policies from file: %s", configFile.c_str());
00132
00133 smart_ptr<Datahash> config = readHashFromTextFile(configFile.c_str());
00134 ASSERT(config, "null");
00135
00136
00137 m_canCreateNewUsers = getBoolean(config, "canCreateNewUsers");
00138 m_passwordRequired = getBoolean(config, "passwordRequired");
00139 }
00140
00141
00142
00144
00145
00146
00148
00149 void
00150 UserMgr::getUsernamesTS
00151 (
00152 OUT SetString& names
00153 )
00154 {
00155 perf::Timer timer("UserManager::getUsernames");
00156
00157
00158 names.clear();
00159
00160
00161 DIR * d = opendir(m_userDir.c_str());
00162 if (!d) {
00163 WAVE_EX(wex);
00164 wex << "Failed to open user directory for reading: ";
00165 wex << m_userDir;
00166 }
00167
00168
00169 while (dirent * entry = readdir(d)) {
00170 if ('.' == entry->d_name[0])
00171 continue;
00172
00173 if (strcmp("user", GetExtension(entry->d_name)))
00174 continue;
00175
00176 std::string login;
00177 GetFileRoot(entry->d_name, login);
00178 if (!isValidUsername(login.c_str())) {
00179 DPRINTF("Invalid username: %s", login.c_str());
00180 continue;
00181 }
00182
00183
00184 names.insert(login);
00185 }
00186 }
00187
00188
00189
00190 bool
00191 UserMgr::logInAsUserTS
00192 (
00193 IN const char * username,
00194 IN const char * password,
00195 OUT std::string& playerGuid,
00196 OUT std::string& diagnostic
00197 )
00198 {
00199 perf::Timer timer("UserManager::logInAsUser");
00200 ASSERT(username, "null");
00201 ASSERT(password, "null");
00202 playerGuid = "";
00203 diagnostic = "";
00204
00205 if (!isValidUsername(username)) {
00206 diagnostic = "Username has invalid format";
00207 return false;
00208 }
00209
00210
00211 std::string filename;
00212 this->getUserPath(username, filename);
00213
00214
00215 smart_ptr<Datahash> hash = readHashFromTextFile(filename.c_str());
00216 ASSERT(hash, "null");
00217
00218
00219 if (this->passwordRequiredTS()) {
00220
00221 if (!password || !*password) {
00222 diagnostic = "Password required but none provided?";
00223 return false;
00224 }
00225
00226
00227
00228 std::string to_hash = username;
00229 to_hash += ":";
00230 to_hash += password;
00231
00232 std::string in_sha1 = crypto::getSHA1(to_hash.c_str());
00233
00234
00235
00236 if (hash->count("password")) {
00237 std::string file_pw = getString(hash, "password");
00238 if (file_pw != in_sha1) {
00239 diagnostic = "Invalid password";
00240 return false;
00241 }
00242
00243 } else {
00244
00245 DPRINTF("Setting password for user: %s", username);
00246 hash->insert("password", in_sha1.c_str());
00247 writeHashToTextFile(hash, filename.c_str());
00248 }
00249 }
00250
00251
00252 mlock lock(m_mutex);
00253
00254 long count;
00255 if (!m_userMap.lookup(username, count)) {
00256 count = 0;
00257 } else {
00258 ++count;
00259 }
00260 DPRINTF("Count for '%s': %ld", username, count);
00261 playerGuid = username;
00262 if (count) {
00263 char buffer[64];
00264 sprintf(buffer, "%ld", count);
00265 playerGuid += buffer;
00266 }
00267 m_userMap.insert(username, count);
00268
00269 DPRINTF("Tag: %s", playerGuid.c_str());
00270
00271 return true;
00272 }
00273
00274
00275
00276 bool
00277 UserMgr::createUserTS
00278 (
00279 IN const char * username,
00280 OUT std::string& diagnostic
00281 )
00282 {
00283 perf::Timer timer("UserManager::createUser");
00284 ASSERT(username, "null");
00285 diagnostic = "";
00286
00287
00288 if (!isValidUsername(username)) {
00289 diagnostic = getUsernameRestrictions();
00290 return false;
00291 }
00292
00293
00294 std::string path;
00295 this->getUserPath(username, path);
00296
00297
00298 if (doesPathExist(path.c_str())) {
00299 diagnostic = "Username '";
00300 diagnostic += username;
00301 diagnostic += "' already exists.";
00302 return false;
00303 }
00304
00305
00306 createEmptyFileIfDoesNotExist(path.c_str());
00307 return true;
00308 }
00309
00310
00311
00312 bool
00313 UserMgr::isAdminTS
00314 (
00315 IN const char * username
00316 )
00317 {
00318 ASSERT(username, "null");
00319
00320 std::string path;
00321 this->getUserPath(username, path);
00322
00323 smart_ptr<Datahash> hash = readHashFromTextFile(path.c_str());
00324 ASSERT(hash, "null");
00325
00326 std::string isAdmin = getOptionalString(hash, "isAdmin", "false");
00327 return getBooleanValueFromString(isAdmin.c_str());
00328 }
00329
00330
00331
00333
00334
00335
00337
00338 void
00339 UserMgr::getUserPath
00340 (
00341 IN const char * username,
00342 OUT std::string& path
00343 )
00344 {
00345 ASSERT(username, "null");
00346
00347 path = m_userDir;
00348 path += "/";
00349 path += username;
00350 path += ".user";
00351 }
00352
00353
00354
00356
00357
00358
00360
00361 smart_ptr<UserManager>
00362 UserManager::create
00363 (
00364 IN const Datahash * params
00365 )
00366 {
00367 ASSERT(params, "null");
00368
00369 smart_ptr<UserMgr> local = new UserMgr;
00370 ASSERT(local, "out of memory");
00371
00372 local->initialize(params);
00373
00374 return local;
00375 }
00376
00377
00378
00379 bool
00380 isValidUsername
00381 (
00382 IN const char * username
00383 )
00384 {
00385 ASSERT(username, "null");
00386
00387 int len = strlen(username);
00388 if (len < s_minUsernameLength || len > s_maxUsernameLength) {
00389 return false;
00390 }
00391
00392 for (; *username; ++username) {
00393 if (*username < 'a' || *username > 'z')
00394 return false;
00395 }
00396
00397 return true;
00398 }
00399
00400
00401
00402 std::string
00403 getUsernameRestrictions
00404 (
00405 void
00406 )
00407 {
00408 std::ostringstream oss;
00409 oss << "Usernames can contain only lower-case characters, and must be ";
00410 oss << s_minUsernameLength << " to " << s_maxUsernameLength;
00411 oss << " characters long.";
00412
00413 return oss.str();
00414 }
00415
00416
00417
00418 };
00419