Changeset 13844 for trunk/gsdl/src/recpt/userdb.cpp
- Timestamp:
- 2007-02-01T16:30:11+13:00 (17 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/gsdl/src/recpt/userdb.cpp
r9620 r13844 41 41 #endif 42 42 43 44 // few useful functions 45 46 text_t crypt_text (const text_t &text) { 47 static const char *salt = "Tp"; 48 text_t crypt_password; 49 50 if (text.empty()) return g_EmptyText; 51 52 // encrypt the password 53 char *text_cstr = text.getcstr(); 54 if (text_cstr == NULL) return g_EmptyText; 55 crypt_password = crypt(text_cstr, salt); 56 delete []text_cstr; 57 58 return crypt_password; 59 } 60 61 62 // username_ok tests to make sure a username is ok. a username 63 // must be at least 2 characters long, but no longer than 30 64 // characters long. it can contain the characters a-z A-Z 0-9 65 // . and _ 66 bool username_ok (const text_t &username) { 67 if (username.size() < 2 || username.size() > 30) return false; 68 69 text_t::const_iterator here = username.begin(); 70 text_t::const_iterator end = username.end(); 71 while (here != end) { 72 if ((*here >= 'a' && *here <= 'z') || 73 (*here >= 'A' && *here <= 'Z') || 74 (*here >= '0' && *here <= '9') || 75 *here == '.' || 76 *here == '_') { 77 // ok 78 } else return false; 79 ++here; 80 } 81 82 return true; 83 } 84 85 // password_ok tests to make sure a password is ok. a password 86 // must be at least 3 characters long but no longer than 8 characters 87 // long. it can contain any character in the range 0x20-0x7e 88 bool password_ok (const text_t &password) { 89 if (password.size() < 3 || password.size() > 8) return false; 90 91 text_t::const_iterator here = password.begin(); 92 text_t::const_iterator end = password.end(); 93 while (here != end) { 94 if (*here >= 0x20 && *here <= 0x7e) { 95 // ok 96 } else return false; 97 ++here; 98 } 99 100 return true; 101 } 102 103 104 105 106 ///////////// 107 // userinfo_t 108 ///////////// 43 //==========================================// 44 // userinfo_t functions (Start) // 45 //==========================================// 46 userinfo_t::userinfo_t() 47 { 48 clear(); 49 } 50 51 userinfo_t::~userinfo_t(){}; 109 52 110 53 void userinfo_t::clear () { … … 125 68 return *this; 126 69 } 127 128 129 130 // functions dealing with user databases 131 132 // returns true on success (in which case userinfo will contain 133 // the information for this user) 134 bool get_user_info (gdbmclass &userdb, const text_t &username, 135 userinfo_t &userinfo) { 136 userinfo.clear(); 70 //==========================================// 71 // userinfo_t functions (END) // 72 //==========================================// 73 74 //==========================================// 75 // userdbclass functions (Start) // 76 //==========================================// 77 userdbclass::userdbclass(const text_t &userdbfilename) 78 { 79 activated = (!userdb.opendatabase(userdbfilename, GDBM_WRCREAT, 1000, true)) ? false : true; 80 external_db = false; 81 } 82 83 userdbclass::userdbclass(const gdbmclass &external_userdb) 84 { 85 userdb = external_userdb; 86 activated = true; 87 external_db = true; 88 } 89 90 userdbclass::~userdbclass() 91 { 92 if (external_db == false) { userdb.closedatabase();} 93 } 94 95 // few useful functions 96 text_t userdbclass::crypt_text (const text_t &text) 97 { 98 static const char *salt = "Tp"; 99 text_t crypt_password; 137 100 138 infodbclass info; 139 if (userdb.getinfo (username, info)) { 140 userinfo.username = info["username"]; 141 userinfo.password = info["password"]; 142 userinfo.enabled = (info["enabled"] == "true"); 143 userinfo.groups = info["groups"]; 144 userinfo.comment = info["comment"]; 145 return true; 101 if (text.empty()) return g_EmptyText; 102 103 // encrypt the password 104 char *text_cstr = text.getcstr(); 105 if (text_cstr == NULL) return g_EmptyText; 106 crypt_password = crypt(text_cstr, salt); 107 delete []text_cstr; 108 109 return crypt_password; 110 } 111 112 // username_ok tests to make sure a username is ok. a username 113 // must be at least 2 characters long, but no longer than 30 114 // characters long. it can contain the characters a-z A-Z 0-9 115 // . and _ 116 bool userdbclass::username_ok (const text_t &username) 117 { 118 if (username.size() < 2 || username.size() > 30) return false; 119 120 text_t::const_iterator here = username.begin(); 121 text_t::const_iterator end = username.end(); 122 while (here != end) 123 { 124 if ((*here >= 'a' && *here <= 'z') || 125 (*here >= 'A' && *here <= 'Z') || 126 (*here >= '0' && *here <= '9') || 127 *here == '.' || 128 *here == '_') 129 { 130 // ok 131 } else return false; 132 ++here; 133 } 134 135 return true; 136 } 137 138 // password_ok tests to make sure a password is ok. a password 139 // must be at least 3 characters long but no longer than 8 characters 140 // long. it can contain any character in the range 0x20-0x7e 141 bool userdbclass::password_ok (const text_t &password) 142 { 143 if (password.size() < 3 || password.size() > 8) return false; 144 145 text_t::const_iterator here = password.begin(); 146 text_t::const_iterator end = password.end(); 147 while (here != end) { 148 if (*here >= 0x20 && *here <= 0x7e) 149 { 150 // ok 151 } else return false; 152 ++here; 146 153 } 147 154 148 return false; 149 } 150 151 bool get_user_info (const text_t &userdbfile, const text_t &username, 152 userinfo_t &userinfo) { 153 gdbmclass userdb; 154 if (!userdb.opendatabase(userdbfile, GDBM_READER, 1000, true)) { 155 /* if (!userdbfile.empty() && !file_exists (userdbfile) && 156 username == "admin") {*/ 157 if (!userdbfile.empty() && username == "admin") { 158 // no database -- create a database with an initial account 159 userinfo.clear(); 160 userinfo.username = "admin"; 161 userinfo.password = crypt_text("admin"); 162 userinfo.enabled = true; 163 userinfo.groups = "administrator,colbuilder"; 164 userinfo.comment = "change the password for this account as soon as possible"; 165 return set_user_info (userdbfile, username, userinfo); 166 } 167 return false; 168 } 169 170 bool success = get_user_info (userdb, username, userinfo); 171 userdb.closedatabase(); 172 173 return success; 174 } 175 176 // returns true on success 177 bool set_user_info (gdbmclass &userdb, const text_t &username, 178 const userinfo_t &userinfo) { 179 infodbclass info; 180 info["username"] = userinfo.username; 181 info["password"] = userinfo.password; 182 info["enabled"] = userinfo.enabled ? "true" : "false"; 183 info["groups"] = userinfo.groups; 184 info["comment"] = userinfo.comment; 185 186 return userdb.setinfo (username, info); 187 } 188 189 bool set_user_info (const text_t &userdbfile, const text_t &username, 190 const userinfo_t &userinfo) { 191 gdbmclass userdb; 192 if (!userdb.opendatabase(userdbfile, GDBM_WRCREAT, 1000, true)) return false; 193 194 bool success = set_user_info (userdb, username, userinfo); 195 userdb.closedatabase(); 196 197 return success; 198 } 199 200 // removes a user from the user database -- forever 201 void delete_user (gdbmclass &userdb, const text_t &username) { 202 userdb.deletekey (username); 203 } 204 205 void delete_user (const text_t &userdbfile, const text_t &username) { 206 gdbmclass userdb; 207 if (!userdb.opendatabase(userdbfile, GDBM_WRCREAT, 1000, true)) return; 208 209 delete_user (userdb, username); 210 userdb.closedatabase(); 211 } 212 213 // gets a list of all the users in the database. returns true 214 // on success 215 void get_user_list (gdbmclass &userdb, text_tarray &userlist) { 216 userlist.erase (userlist.begin(), userlist.end()); 217 218 text_t user = userdb.getfirstkey (); 219 while (!user.empty()) { 220 userlist.push_back(user); 221 user = userdb.getnextkey (user); 222 } 223 } 224 225 // returns true if the user's password is correct. 226 bool check_passwd (const userinfo_t &thisuser, const text_t &password) { 227 // couple of basic checks 228 if (thisuser.username.empty() || thisuser.password.empty() || 229 password.empty()) return false; 230 231 text_t crypt_password = crypt_text(password); 232 return (thisuser.password == crypt_password); 155 return true; 233 156 } 234 157 235 158 // removes spaces from user groups 236 text_t format_user_groups(const text_t user_groups){237 159 text_t userdbclass::format_user_groups(const text_t user_groups) 160 { 238 161 text_t new_groups = g_EmptyText; 239 162 text_t::const_iterator here = user_groups.begin(); 240 163 text_t::const_iterator end = user_groups.end(); 241 164 while (here != end) { 242 if (*here != ' '&& *here != '\t' && *here != '\n') { 243 new_groups.push_back(*here); 244 } 165 if (*here != ' '&& *here != '\t' && *here != '\n') 166 { 167 new_groups.push_back(*here); 168 } 245 169 ++here; 246 170 } … … 248 172 } 249 173 250 // functions dealing with databases of temporary keys 251 174 // functions dealing with user databases 175 // returns true on success (in which case userinfo will contain 176 // the information for this user) 177 // @return 0 success 178 // @return -1 database is not currently connected 179 // @return -2 not defined username 180 int userdbclass::get_user_info (const text_t &username, userinfo_t &userinfo) 181 { 182 // Let's make sure the connection has been established. 183 if (activated == true) 184 { 185 userinfo.clear(); 186 infodbclass info; 187 // See if we can get the user's infomration 188 if (userdb.getinfo (username, info)) 189 { 190 userinfo.username = info["username"]; 191 userinfo.password = info["password"]; 192 userinfo.enabled = (info["enabled"] == "true"); 193 userinfo.groups = info["groups"]; 194 userinfo.comment = info["comment"]; 195 return ERRNO_SUCCEED; 196 } 197 // If we failed to retrieve the users information, we should check if the username is admin or not. 198 // If it is the admin user, let's create a new account for the admin user. 199 else if (username == "admin") 200 { 201 userinfo.clear(); 202 userinfo.username = "admin"; 203 userinfo.password = crypt_text("admin"); 204 userinfo.enabled = true; 205 userinfo.groups = "administrator,colbuilder"; 206 userinfo.comment = "change the password for this account as soon as possible"; 207 // Return the set result. 208 return set_user_info (username, userinfo); 209 } 210 // The username is not found, return false 211 return ERRNO_USERNOTFOUND; 212 } 213 // Failed to connect to the database, return false. 214 return ERRNO_CONNECTIONFAILED; 215 } 216 217 // returns true on success 218 int userdbclass::set_user_info (const text_t &username, const userinfo_t &userinfo) 219 { 220 // Let's make sure the connection has been established. 221 if (activated == true) 222 { 223 infodbclass info; 224 info["username"] = userinfo.username; 225 info["password"] = userinfo.password; 226 info["enabled"] = userinfo.enabled ? "true" : "false"; 227 info["groups"] = userinfo.groups; 228 info["comment"] = userinfo.comment; 229 230 return (userdb.setinfo (username, info)) ? ERRNO_SUCCEED : ERRNO_GDBMACTIONFILED ; 231 } 232 return ERRNO_CONNECTIONFAILED; 233 } 234 235 // returns true if the user's password is correct. 236 int userdbclass::check_passwd (const text_t &username, const text_t &password) 237 { 238 userinfo_t thisuser; 239 int returned = get_user_info(username, thisuser); 240 if(returned != ERRNO_SUCCEED) return returned; 241 // couple of basic checks 242 if (thisuser.username.empty() || thisuser.password.empty() || 243 password.empty()) return ERRNO_MISSINGPASSWORD; 244 245 text_t crypt_password = crypt_text(password); 246 return (thisuser.password == crypt_password) ? ERRNO_SUCCEED : ERRNO_PASSWORDMISMATCH; 247 } 248 249 int userdbclass::add_user (const userinfo_t &userinfo) 250 { 251 // Let's make sure the connection has been established. 252 if (activated == true) 253 { 254 infodbclass info; 255 if (userdb.getinfo (userinfo.username, info)) 256 { 257 // There is an existing username already 258 return ERRNO_EXISTINGUSERNAME; 259 } 260 else 261 { 262 return set_user_info(userinfo.username, userinfo); 263 } 264 } 265 return ERRNO_CONNECTIONFAILED; 266 } 267 268 int userdbclass::edit_user (const userinfo_t &userinfo) 269 { 270 // Let's make sure the connection has been established. 271 if (activated == true) 272 { 273 infodbclass info; 274 if (userdb.getinfo (userinfo.username, info)) 275 { 276 return set_user_info(userinfo.username, userinfo); 277 } 278 else 279 { 280 // The user does not exist in the database. 281 return ERRNO_USERNOTFOUND; 282 } 283 } 284 return ERRNO_CONNECTIONFAILED; 285 } 286 287 int userdbclass::delete_user (const text_t &username) 288 { 289 // Let's make sure the connection has been established. 290 if (activated == true) 291 { 292 userdb.deletekey (username); 293 return ERRNO_SUCCEED; 294 } 295 return ERRNO_CONNECTIONFAILED; 296 } 297 298 // gets all the users' information in the database. returns true 299 // on success 300 int userdbclass::get_all_users(userinfo_tarray &userinfo_array) 301 { 302 // Let's make sure the connection has been established. 303 if (activated == true) 304 { 305 userinfo_array.erase(userinfo_array.begin(), userinfo_array.end()); 306 text_t user = userdb.getfirstkey(); 307 while (!user.empty()) { 308 userinfo_t one_userinfo; 309 int returned = get_user_info(user, one_userinfo); 310 if (returned != ERRNO_SUCCEED) return returned; 311 userinfo_array.push_back(one_userinfo); 312 user = userdb.getnextkey(user); 313 } 314 return ERRNO_SUCCEED; 315 } 316 return ERRNO_CONNECTIONFAILED; 317 } 318 319 // gets a list of all the users in the database. returns true 320 // on success 321 int userdbclass::get_user_list (text_tarray &userlist) 322 { 323 // Let's make sure the connection has been established. 324 if (activated == true) 325 { 326 userlist.erase (userlist.begin(), userlist.end()); 327 328 text_t user = userdb.getfirstkey (); 329 while (!user.empty()) { 330 userlist.push_back(user); 331 user = userdb.getnextkey (user); 332 } 333 return ERRNO_SUCCEED; 334 } 335 return ERRNO_CONNECTIONFAILED; 336 } 337 //==========================================// 338 // userdbclass functions (End) // 339 //==========================================// 340 341 //==========================================// 342 // keydbclass functions (Start) // 343 //==========================================// 344 keydbclass::keydbclass(const text_t &keydbfilename) 345 { 346 activated = (!keydb.opendatabase(keydbfilename, GDBM_WRCREAT, 1000, true)) ? false : true; 347 external_db = false; 348 } 349 350 keydbclass::keydbclass(const gdbmclass &external_keydb) 351 { 352 keydb = external_keydb; 353 activated = true; 354 external_db = true; 355 } 356 357 keydbclass::~keydbclass() 358 { 359 if (external_db == false) { keydb.closedatabase();} 360 } 252 361 // generates a random key for the user, stores it in the database and 253 362 // returns it so that it can be used in page generation 254 363 // returns "" on failure 255 text_t generate_key (gdbmclass &keydb, const text_t &username) { 256 static const char *numconvert = "0123456789abcdefghijklmnopqrstuvwxyz" 257 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 258 259 // loop looking for a suitable new key 260 text_t userkey; 261 text_t crypt_userkey; 262 do { 263 // convert to base 62 :-) 264 int userkey_int = rand (); 265 while (userkey_int > 0) { 266 userkey.push_back (numconvert[userkey_int%62]); 267 userkey_int /= 62; 268 } 269 270 // make sure this key is not in the database 271 crypt_userkey = crypt_text(userkey); 272 if (keydb.exists (crypt_userkey)) userkey.clear(); 273 } while (userkey.empty()); 274 275 // enter the key into the database 276 infodbclass keydata; 277 keydata["user"] = username; 278 keydata["time"] = time2text(time(NULL)); 279 280 if (!keydb.setinfo (crypt_userkey, keydata)) { 281 userkey.clear(); // failed 282 } 283 284 return userkey; 285 } 286 287 text_t generate_key (const text_t &keydbfile, const text_t &username) { 288 gdbmclass keydb; 289 if (!keydb.opendatabase(keydbfile, GDBM_WRCREAT, 1000, true)) return g_EmptyText; 290 291 text_t key = generate_key (keydb, username); 292 keydb.closedatabase(); 293 294 return key; 295 } 296 364 text_t keydbclass::generate_key (const text_t &username) 365 { 366 // Let's make sure the connection has been established. 367 if (activated == true) 368 { 369 static const char *numconvert = "0123456789abcdefghijklmnopqrstuvwxyz" 370 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 371 372 // loop looking for a suitable new key 373 text_t userkey; 374 text_t crypt_userkey; 375 do { 376 // convert to base 62 :-) 377 int userkey_int = rand (); 378 while (userkey_int > 0) 379 { 380 userkey.push_back (numconvert[userkey_int%62]); 381 userkey_int /= 62; 382 } 383 384 // make sure this key is not in the database 385 crypt_userkey = userdbclass::crypt_text(userkey); 386 if (keydb.exists (crypt_userkey)) userkey.clear(); 387 } while (userkey.empty()); 388 389 // enter the key into the database 390 infodbclass keydata; 391 keydata["user"] = username; 392 keydata["time"] = time2text(time(NULL)); 393 394 if (!keydb.setinfo (crypt_userkey, keydata)) 395 { 396 userkey.clear(); // failed 397 } 398 399 return userkey; 400 } 401 return ""; 402 } 297 403 298 404 // checks to see if there is a key for this particular user in the 299 405 // database that hasn't decayed. a short decay is used when group 300 406 // is set to administrator 301 bool check_key (gdbmclass &keydb, const userinfo_t &thisuser, 302 const text_t &key, const text_t &group, int keydecay) { 303 if (thisuser.username.empty() || key.empty()) return false; 304 305 // the keydecay is set to 5 minute for things requiring the 306 // administrator 307 // if (group == "administrator") keydecay = 300; 308 309 // success if there is a key in the key database that is owned by this 310 // user whose creation time is less that keydecay 311 text_t crypt_key = crypt_text(key); 312 infodbclass info; 313 if (keydb.getinfo (crypt_key, info)) { 314 if (info["user"] == thisuser.username) { 315 time_t keycreation = text2time (info["time"]); 316 if (keycreation != (time_t)-1 && difftime (text2time(time2text(time(NULL))), 317 keycreation) <= keydecay) { 318 // succeeded, update the key's time 319 info["time"] = time2text(time(NULL)); 320 keydb.setinfo (crypt_key, info); 321 return true; 322 } 323 } 324 } 325 326 return false;; 327 } 328 329 bool check_key (const text_t &keydbfile, const userinfo_t &thisuser, 330 const text_t &key, const text_t &group, int keydecay) { 331 gdbmclass keydb; 332 if (!keydb.opendatabase(keydbfile, GDBM_WRCREAT, 1000, true)) return false; 333 334 bool success = check_key (keydb, thisuser, key, group, keydecay); 335 keydb.closedatabase(); 336 337 return success; 338 } 339 407 bool keydbclass::check_key (const userinfo_t &thisuser, const text_t &key, const text_t &group, int keydecay) 408 { 409 // Let's make sure the connection has been established. 410 if (activated == true) 411 { 412 if (thisuser.username.empty() || key.empty()) return false; 413 414 // the keydecay is set to 5 minute for things requiring the 415 // administrator 416 // if (group == "administrator") keydecay = 300; 417 418 // success if there is a key in the key database that is owned by this 419 // user whose creation time is less that keydecay 420 text_t crypt_key = userdbclass::crypt_text(key); 421 infodbclass info; 422 if (keydb.getinfo (crypt_key, info)) { 423 if (info["user"] == thisuser.username) { 424 time_t keycreation = text2time (info["time"]); 425 if (keycreation != (time_t)-1 && difftime (text2time(time2text(time(NULL))), 426 keycreation) <= keydecay) { 427 // succeeded, update the key's time 428 info["time"] = time2text(time(NULL)); 429 keydb.setinfo (crypt_key, info); 430 return true; 431 } 432 } 433 } 434 } 435 return false; 436 } 340 437 341 438 // remove_old_keys will remove all keys created more than keydecay ago. 342 439 // use sparingly, it can be quite an expensive function 343 void remove_old_keys (const text_t &keydbfile, int keydecay) { 344 // open the key database 345 gdbmclass keydb; 346 if (!keydb.opendatabase(keydbfile, GDBM_WRCREAT, 1000, true)) return; 347 348 // get a list of keys created more than keydecay seconds agon 349 text_tarray oldkeys; 350 text_t key = keydb.getfirstkey (); 351 infodbclass info; 352 time_t timenow = text2time(time2text(time(NULL))); 353 time_t keycreation = (time_t)-1; 354 while (!key.empty()) { 355 if (keydb.getinfo (key, info)) { 356 keycreation = text2time (info["time"]); 357 if (keycreation != (time_t)-1 && difftime (timenow, keycreation) > keydecay) { 358 // found an old key 359 oldkeys.push_back(key); 360 } 361 } 362 363 key = keydb.getnextkey (key); 364 } 365 366 // delete the keys 367 text_tarray::iterator keys_here = oldkeys.begin(); 368 text_tarray::iterator keys_end = oldkeys.end(); 369 while (keys_here != keys_end) { 370 keydb.deletekey(*keys_here); 371 ++keys_here; 372 } 373 374 // close the key database 375 keydb.closedatabase(); 376 } 440 void keydbclass::remove_old_keys (int keydecay) 441 { 442 // Let's make sure the connection has been established. 443 if (activated == true) 444 { 445 // get a list of keys created more than keydecay seconds agon 446 text_tarray oldkeys; 447 text_t key = keydb.getfirstkey (); 448 infodbclass info; 449 time_t timenow = text2time(time2text(time(NULL))); 450 time_t keycreation = (time_t)-1; 451 while (!key.empty()) { 452 if (keydb.getinfo (key, info)) { 453 keycreation = text2time (info["time"]); 454 if (keycreation != (time_t)-1 && difftime (timenow, keycreation) > keydecay) { 455 // found an old key 456 oldkeys.push_back(key); 457 } 458 } 459 460 key = keydb.getnextkey (key); 461 } 462 463 // delete the keys 464 text_tarray::iterator keys_here = oldkeys.begin(); 465 text_tarray::iterator keys_end = oldkeys.end(); 466 while (keys_here != keys_end) { 467 keydb.deletekey(*keys_here); 468 ++keys_here; 469 } 470 } 471 } 472 //==========================================// 473 // keydbclass functions (End) // 474 //==========================================//
Note:
See TracChangeset
for help on using the changeset viewer.