root/gsdl/trunk/src/recpt/userdb.cpp @ 14269

Revision 14269, 15.7 KB (checked in by qq6, 13 years ago)

Jeffrey has fixed operating records of users database on Windows

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1/**********************************************************************
2 *
3 * userdb.cpp -- functions to handle a user database
4 * Copyright (C) 1999  DigiLib Systems Limited, New Zealand
5 *
6 * A component of the Greenstone digital library software
7 * from the New Zealand Digital Library Project at the
8 * University of Waikato, New Zealand.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 *********************************************************************/
25
26#include "gsdlconf.h"
27#include "userdb.h"
28#include "gsdltimes.h"
29#include "fileutil.h"
30#include <stdlib.h>
31
32// include crypt
33#if defined(__WIN32__)
34#include "crypt.h"
35#else
36#if defined(HAVE_CRYPT_H)
37#include <crypt.h>
38#else
39#include <unistd.h>
40#endif
41#endif
42
43//==========================================//
44//      userinfo_t functions (Start)        //
45//==========================================//
46userinfo_t::userinfo_t()
47{
48  clear();
49}
50
51userinfo_t::~userinfo_t(){};
52
53void userinfo_t::clear () {
54  username.clear();
55  password.clear();
56  enabled = false;
57  groups.clear();
58  comment.clear();
59}
60
61userinfo_t &userinfo_t::operator=(const userinfo_t &x) {
62  username = x.username;
63  password = x.password;
64  enabled = x.enabled;
65  groups = x.groups;
66  comment = x.comment;
67 
68  return *this;
69}
70//==========================================//
71//       userinfo_t functions (END)         //
72//==========================================//
73
74//==========================================//
75//      userdbclass functions (Start)       //
76//==========================================//
77userdbclass::userdbclass(const text_t &userdbfilename)
78{
79  storeduserdbfilename = userdbfilename;
80  activated = (!userdb.opendatabase(storeduserdbfilename, GDBM_READER, 1000, true)) ? false : true;
81  if (activated == false)
82    {
83      activated = (!userdb.opendatabase(storeduserdbfilename, GDBM_WRCREAT, 1000, true)) ? false : true;
84      if (activated == true)
85        {
86          userdb.closedatabase();
87          activated = (!userdb.opendatabase(storeduserdbfilename, GDBM_READER, 1000, true)) ? false : true;
88        }
89    }
90   
91  external_db = false;
92}
93
94userdbclass::userdbclass(const gdbmclass &external_userdb)
95{
96  userdb = external_userdb;
97  activated = true;
98  external_db = true;
99}
100
101userdbclass::~userdbclass()
102{
103  if (external_db == false) { userdb.closedatabase();}
104}
105
106// few useful functions
107text_t userdbclass::crypt_text (const text_t &text)
108{
109  static const char *salt = "Tp";
110  text_t crypt_password;
111 
112  if (text.empty()) return g_EmptyText;
113 
114  // encrypt the password
115  char *text_cstr = text.getcstr();
116  if (text_cstr == NULL) return g_EmptyText;
117  crypt_password = crypt(text_cstr, salt);
118  delete []text_cstr;
119
120  return crypt_password;
121}
122
123// username_ok tests to make sure a username is ok. a username
124// must be at least 2 characters long, but no longer than 30
125// characters long. it can contain the characters a-z A-Z 0-9
126// . and _
127bool userdbclass::username_ok (const text_t &username)
128{
129  if (username.size() < 2 || username.size() > 30) return false;
130
131  text_t::const_iterator here = username.begin();
132  text_t::const_iterator end = username.end();
133  while (here != end)
134    {
135      if ((*here >= 'a' && *here <= 'z') ||
136          (*here >= 'A' && *here <= 'Z') ||
137          (*here >= '0' && *here <= '9') ||
138          *here == '.' ||
139          *here == '_')
140        {
141          // ok
142        } else return false;
143      ++here;
144    }
145 
146  return true;
147}
148
149// password_ok tests to make sure a password is ok. a password
150// must be at least 3 characters long but no longer than 8 characters
151// long. it can contain any character in the range 0x20-0x7e
152bool userdbclass::password_ok (const text_t &password)
153{
154  if (password.size() < 3 || password.size() > 8) return false;
155
156  text_t::const_iterator here = password.begin();
157  text_t::const_iterator end = password.end();
158  while (here != end) {
159    if (*here >= 0x20 && *here <= 0x7e)
160      {
161        // ok
162      } else return false;
163    ++here;
164  }
165
166  return true;
167}
168
169// removes spaces from user groups
170text_t userdbclass::format_user_groups(const text_t user_groups)
171{
172  text_t new_groups = g_EmptyText;
173  text_t::const_iterator here = user_groups.begin();
174  text_t::const_iterator end = user_groups.end();
175  while (here != end) {
176    if (*here != ' '&& *here != '\t' && *here != '\n')
177      {
178        new_groups.push_back(*here);
179      }
180    ++here;
181  }
182  return new_groups;
183}
184
185// functions dealing with user databases
186// returns true on success (in which case userinfo will contain
187// the information for this user)
188// @return 0 success
189// @return -1 database is not currently connected
190// @return -2 not defined username
191int userdbclass::get_user_info (const text_t &username, userinfo_t &userinfo)
192{
193  // Let's make sure the connection has been established.
194  if (activated == true)
195    {
196      userinfo.clear();
197      infodbclass info;
198      // See if we can get the user's infomration
199      if (userdb.getinfo (username, info))
200        {
201          userinfo.username = info["username"];
202          userinfo.password = info["password"];
203          userinfo.enabled = (info["enabled"] == "true");
204          userinfo.groups = info["groups"];
205          userinfo.comment = info["comment"];
206          return ERRNO_SUCCEED;
207        }
208      // If we failed to retrieve the users information, we should check if the username is admin or not.
209      // If it is the admin user, let's create a new account for the admin user.
210      else if (username == "admin")
211        {
212          userinfo.clear();
213          userinfo.username = "admin";
214          userinfo.password = crypt_text("admin");
215          userinfo.enabled = true;
216          userinfo.groups = "administrator,colbuilder";
217          userinfo.comment = "change the password for this account as soon as possible";
218          // Return the set result.
219          return set_user_info (username, userinfo);
220        }
221      // The username is not found, return false
222      return ERRNO_USERNOTFOUND;
223    }
224  // Failed to connect to the database, return false.
225  return ERRNO_CONNECTIONFAILED;
226}
227
228// returns true on success
229int userdbclass::set_user_info (const text_t &username, const userinfo_t &userinfo)
230{
231  // Let's make sure the connection has been established.
232  if (activated == true)
233    {
234      infodbclass info;
235      info["username"] = userinfo.username;
236      info["password"] = userinfo.password;
237      info["enabled"] = userinfo.enabled ? "true" : "false";
238      info["groups"] = userinfo.groups;
239      info["comment"] = userinfo.comment;
240      userdb.closedatabase();
241      userdb.opendatabase(storeduserdbfilename, GDBM_WRCREAT, 1000, true);
242      int result = (userdb.setinfo (username, info)) ? ERRNO_SUCCEED : ERRNO_GDBMACTIONFILED;
243      userdb.closedatabase();
244      userdb.opendatabase(storeduserdbfilename, GDBM_READER, 1000, true);         
245      return  result;
246    }
247  return ERRNO_CONNECTIONFAILED;
248}
249
250// returns true if the user's password is correct.
251int userdbclass::check_passwd (const text_t &username, const text_t &password)
252{
253  userinfo_t thisuser;
254  int returned = get_user_info(username, thisuser);
255  if(returned != ERRNO_SUCCEED) return returned;
256  // couple of basic checks
257  if (thisuser.username.empty() || thisuser.password.empty() ||
258      password.empty()) return ERRNO_MISSINGPASSWORD;
259
260  text_t crypt_password = crypt_text(password);
261  return (thisuser.password == crypt_password) ? ERRNO_SUCCEED : ERRNO_PASSWORDMISMATCH;
262}
263
264int userdbclass::add_user (const userinfo_t &userinfo)
265{
266  // Let's make sure the connection has been established.
267  if (activated == true)
268    {
269      infodbclass info;
270      if (userdb.getinfo (userinfo.username, info))
271        {
272          // There is an existing username already
273          return ERRNO_EXISTINGUSERNAME;
274        }
275      else
276        {
277          return set_user_info(userinfo.username, userinfo);
278        }     
279    }
280  return ERRNO_CONNECTIONFAILED;
281}
282
283int userdbclass::edit_user (const userinfo_t &userinfo)
284{
285  // Let's make sure the connection has been established.
286  if (activated == true)
287    {
288      infodbclass info;
289      if (userdb.getinfo (userinfo.username, info))
290        {
291          return set_user_info(userinfo.username, userinfo);
292        }
293      else
294        {
295          // The user does not exist in the database.
296          return ERRNO_USERNOTFOUND;
297        }     
298    }
299  return ERRNO_CONNECTIONFAILED;
300}
301
302int userdbclass::delete_user (const text_t &username)
303
304  // Let's make sure the connection has been established.
305  if (activated == true)
306    {
307      userdb.closedatabase();
308      userdb.opendatabase(storeduserdbfilename, GDBM_WRCREAT, 1000, true);
309      userdb.deletekey (username);
310      userdb.closedatabase();
311      userdb.opendatabase(storeduserdbfilename, GDBM_READER, 1000, true);
312      return ERRNO_SUCCEED;
313    }
314  return ERRNO_CONNECTIONFAILED;
315}
316
317// gets all the users' information in the database. returns true
318// on success
319int userdbclass::get_all_users(userinfo_tarray &userinfo_array)
320{
321  // Let's make sure the connection has been established.
322  if (activated == true)
323    {
324      userinfo_array.erase(userinfo_array.begin(), userinfo_array.end());
325      text_t user = userdb.getfirstkey();
326      while (!user.empty()) {
327        userinfo_t one_userinfo;
328        int returned = get_user_info(user, one_userinfo);
329        if (returned != ERRNO_SUCCEED) return returned;
330        userinfo_array.push_back(one_userinfo);
331        user = userdb.getnextkey(user);
332      }
333      return ERRNO_SUCCEED;
334    }
335  return ERRNO_CONNECTIONFAILED;
336}
337
338// gets a list of all the users in the database. returns true
339// on success
340int userdbclass::get_user_list (text_tarray &userlist)
341{
342  // Let's make sure the connection has been established.
343  if (activated == true)
344    {
345      userlist.erase (userlist.begin(), userlist.end());
346     
347      text_t user = userdb.getfirstkey ();
348      while (!user.empty()) {
349        userlist.push_back(user);
350        user = userdb.getnextkey (user);
351      }
352      return ERRNO_SUCCEED;
353    }
354  return ERRNO_CONNECTIONFAILED;
355}
356//==========================================//
357//       userdbclass functions (End)        //
358//==========================================//
359
360//==========================================//
361//      keydbclass functions (Start)        //
362//==========================================//
363keydbclass::keydbclass(const text_t &keydbfilename)
364{
365  storedkeydbfilename = keydbfilename;
366  activated = (!keydb.opendatabase(storedkeydbfilename, GDBM_READER, 1000, true)) ? false : true;
367  if (activated == false)
368    {
369      activated = (!keydb.opendatabase(storedkeydbfilename, GDBM_WRCREAT, 1000, true)) ? false : true;
370      if (activated == true)
371        {
372          keydb.closedatabase();
373          activated = (!keydb.opendatabase(storedkeydbfilename, GDBM_READER, 1000, true)) ? false : true;
374        }
375    }
376  external_db = false;
377}
378
379keydbclass::keydbclass(const gdbmclass &external_keydb)
380{
381  keydb = external_keydb;
382  activated = true;
383  external_db = true;
384}
385
386keydbclass::~keydbclass()
387{
388  if (external_db == false) { keydb.closedatabase();}
389}
390// generates a random key for the user, stores it in the database and
391// returns it so that it can be used in page generation
392// returns "" on failure
393text_t keydbclass::generate_key (const text_t &username)
394{
395  // Let's make sure the connection has been established.
396  if (activated == true)
397    {
398      static const char *numconvert = "0123456789abcdefghijklmnopqrstuvwxyz"
399        "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
400     
401      // loop looking for a suitable new key
402      text_t userkey;
403      text_t crypt_userkey;
404      do {
405        // convert to base 62 :-)
406        int userkey_int = rand ();
407        while (userkey_int > 0)
408          {
409            userkey.push_back (numconvert[userkey_int%62]);
410            userkey_int /= 62;
411          }
412       
413        // make sure this key is not in the database
414        crypt_userkey = userdbclass::crypt_text(userkey);
415        if (keydb.exists (crypt_userkey)) userkey.clear();
416      } while (userkey.empty());
417     
418      // enter the key into the database
419      infodbclass keydata;
420      keydata["user"] = username;
421      keydata["time"] = time2text(time(NULL));
422     
423      keydb.closedatabase();
424      keydb.opendatabase(storedkeydbfilename, GDBM_WRCREAT, 1000, true);
425      if (!keydb.setinfo (crypt_userkey, keydata))
426        {
427          userkey.clear(); // failed
428        }
429      keydb.closedatabase();
430      keydb.opendatabase(storedkeydbfilename, GDBM_READER, 1000, true);
431     
432      return userkey;
433    }
434  return "";
435}
436
437// checks to see if there is a key for this particular user in the
438// database that hasn't decayed. a short decay is used when group
439// is set to administrator
440bool keydbclass::check_key (const userinfo_t &thisuser, const text_t &key, const text_t &group, int keydecay)
441{
442  // Let's make sure the connection has been established.
443  if (activated == true)
444    {
445      if (thisuser.username.empty() || key.empty()) return false;
446     
447      // the keydecay is set to 5 minute for things requiring the
448      // administrator
449      //  if (group == "administrator") keydecay = 300;
450     
451      // success if there is a key in the key database that is owned by this
452      // user whose creation time is less that keydecay
453      text_t crypt_key = userdbclass::crypt_text(key);
454      infodbclass info;
455      if (keydb.getinfo (crypt_key, info))  {
456        if (info["user"] == thisuser.username) {
457          time_t keycreation = text2time (info["time"]);
458          if (keycreation != (time_t)-1 && difftime (text2time(time2text(time(NULL))),
459                                                     keycreation) <= keydecay) {
460            // succeeded, update the key's time
461            info["time"] = time2text(time(NULL));
462            keydb.closedatabase();
463            keydb.opendatabase(storedkeydbfilename, GDBM_WRCREAT, 1000, true);
464            keydb.setinfo (crypt_key, info);
465            keydb.closedatabase();
466            keydb.opendatabase(storedkeydbfilename, GDBM_READER, 1000, true);
467            return true;
468          }
469        }
470      }
471    }
472  return false;
473}
474
475// remove_old_keys will remove all keys created more than keydecay ago.
476// use sparingly, it can be quite an expensive function
477void keydbclass::remove_old_keys (int keydecay)
478{
479  // Let's make sure the connection has been established.
480  if (activated == true)
481    { 
482      // get a list of keys created more than keydecay seconds agon
483      text_tarray oldkeys;
484      text_t key = keydb.getfirstkey ();
485      infodbclass info;
486      time_t timenow = text2time(time2text(time(NULL)));
487      time_t keycreation = (time_t)-1;
488      while (!key.empty()) {
489        if (keydb.getinfo (key, info))  {
490          keycreation = text2time (info["time"]);
491          if (keycreation != (time_t)-1 && difftime (timenow, keycreation) > keydecay) {
492            // found an old key
493            oldkeys.push_back(key);
494          }
495        }
496       
497        key = keydb.getnextkey (key);
498      }
499     
500      // delete the keys
501      text_tarray::iterator keys_here = oldkeys.begin();
502      text_tarray::iterator keys_end = oldkeys.end();
503      while (keys_here != keys_end) {
504        keydb.deletekey(*keys_here);
505        ++keys_here;
506      }
507    }
508}
509//==========================================//
510//       keydbclass functions (End)         //
511//==========================================//
Note: See TracBrowser for help on using the browser.