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