Ignore:
Timestamp:
2007-02-01T16:30:11+13:00 (17 years ago)
Author:
mdewsnip
Message:

Jeffrey's (DL Consulting) changes to userdb to make it a proper class, so it can be easily extended for collection-specific receptionists.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/gsdl/src/recpt/userdb.cpp

    r9620 r13844  
    4141#endif
    4242
    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//==========================================//
     46userinfo_t::userinfo_t()
     47{
     48  clear();
     49}
     50
     51userinfo_t::~userinfo_t(){};
    10952
    11053void userinfo_t::clear () {
     
    12568  return *this;
    12669}
    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//==========================================//
     77userdbclass::userdbclass(const text_t &userdbfilename)
     78{
     79  activated = (!userdb.opendatabase(userdbfilename, GDBM_WRCREAT, 1000, true)) ? false : true;
     80  external_db = false;
     81}
     82
     83userdbclass::userdbclass(const gdbmclass &external_userdb)
     84{
     85  userdb = external_userdb;
     86  activated = true;
     87  external_db = true;
     88}
     89
     90userdbclass::~userdbclass()
     91{
     92  if (external_db == false) { userdb.closedatabase();}
     93}
     94
     95// few useful functions
     96text_t userdbclass::crypt_text (const text_t &text)
     97{
     98  static const char *salt = "Tp";
     99  text_t crypt_password;
    137100 
    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 _
     116bool 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
     141bool 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;
    146153  }
    147154
    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;
    233156}
    234157
    235158// removes spaces from user groups
    236 text_t format_user_groups(const text_t user_groups){
    237 
     159text_t userdbclass::format_user_groups(const text_t user_groups)
     160{
    238161  text_t new_groups = g_EmptyText;
    239162  text_t::const_iterator here = user_groups.begin();
    240163  text_t::const_iterator end = user_groups.end();
    241164  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      }
    245169    ++here;
    246170  }
     
    248172}
    249173
    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
     180int 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
     218int 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.
     236int 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
     249int 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
     268int 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
     287int 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
     300int 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
     321int 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//==========================================//
     344keydbclass::keydbclass(const text_t &keydbfilename)
     345{
     346  activated = (!keydb.opendatabase(keydbfilename, GDBM_WRCREAT, 1000, true)) ? false : true;
     347  external_db = false;
     348}
     349
     350keydbclass::keydbclass(const gdbmclass &external_keydb)
     351{
     352  keydb = external_keydb;
     353  activated = true;
     354  external_db = true;
     355}
     356
     357keydbclass::~keydbclass()
     358{
     359  if (external_db == false) { keydb.closedatabase();}
     360}
    252361// generates a random key for the user, stores it in the database and
    253362// returns it so that it can be used in page generation
    254363// 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 
     364text_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}
    297403
    298404// checks to see if there is a key for this particular user in the
    299405// database that hasn't decayed. a short decay is used when group
    300406// 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 
     407bool 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}
    340437
    341438// remove_old_keys will remove all keys created more than keydecay ago.
    342439// 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 }
     440void 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.