source: trunk/gsdl/src/recpt/userdb.cpp@ 1233

Last change on this file since 1233 was 1183, checked in by sjboddie, 24 years ago

the initial admin user now belongs to the colbuilder group by default
(as well as the administrator group)

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 11.0 KB
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 * $Id: userdb.cpp 1183 2000-05-22 12:30:36Z sjboddie $
25 *
26 *********************************************************************/
27
28/*
29 $Log$
30 Revision 1.7 2000/05/22 12:30:36 sjboddie
31 the initial admin user now belongs to the colbuilder group by default
32 (as well as the administrator group)
33
34 Revision 1.6 2000/03/01 22:23:09 sjboddie
35 tidied up windows installation
36
37 Revision 1.5 2000/02/29 21:53:09 sjboddie
38 tidied up crypt includes
39
40 Revision 1.4 1999/09/07 04:57:00 sjboddie
41 added GPL notice
42
43 Revision 1.3 1999/09/02 00:30:04 rjmcnab
44 added option for specifying whether the gdbm database should be locked
45
46 Revision 1.2 1999/07/14 08:30:01 rjmcnab
47 fixed an error to do with the way time is dealt with on different machines.
48 It seems that there is no direct corresponding function to mktime and
49 localtime or gmtime might not get you back to where you started.
50
51 Revision 1.1 1999/07/13 23:22:04 rjmcnab
52 Initial revision.
53
54 */
55
56#include "gsdlconf.h"
57#include "userdb.h"
58#include "gsdltimes.h"
59#include "fileutil.h"
60#include <stdlib.h>
61
62// include crypt
63#if defined(__WIN32__)
64#include "crypt.h"
65#else
66#if defined(HAVE_CRYPT_H)
67#include <crypt.h>
68#else
69#include <unistd.h>
70#endif
71#endif
72
73
74// few useful functions
75
76text_t crypt_text (const text_t &text) {
77 static const char *salt = "Tp";
78 text_t crypt_password;
79
80 if (text.empty()) return "";
81
82 // encrypt the password
83 char *text_cstr = text.getcstr();
84 if (text_cstr == NULL) return "";
85 crypt_password = crypt(text_cstr, salt);
86 delete text_cstr;
87
88 return crypt_password;
89}
90
91
92// username_ok tests to make sure a username is ok. a username
93// must be at least 2 characters long, but no longer than 30
94// characters long. it can contain the characters a-z A-Z 0-9
95// . and _
96bool username_ok (const text_t &username) {
97 if (username.size() < 2 || username.size() > 30) return false;
98
99 text_t::const_iterator here = username.begin();
100 text_t::const_iterator end = username.end();
101 while (here != end) {
102 if ((*here >= 'a' && *here <= 'z') ||
103 (*here >= 'A' && *here <= 'Z') ||
104 (*here >= '0' && *here <= '9') ||
105 *here == '.' ||
106 *here == '_') {
107 // ok
108 } else return false;
109 here++;
110 }
111
112 return true;
113}
114
115// password_ok tests to make sure a password is ok. a password
116// must be at least 3 characters long but no longer than 8 characters
117// long. it can contain any character in the range 0x20-0x7e
118bool password_ok (const text_t &password) {
119 if (password.size() < 3 || password.size() > 8) return false;
120
121 text_t::const_iterator here = password.begin();
122 text_t::const_iterator end = password.end();
123 while (here != end) {
124 if (*here >= 0x20 && *here <= 0x7e) {
125 // ok
126 } else return false;
127 here++;
128 }
129
130 return true;
131}
132
133
134
135
136/////////////
137// userinfo_t
138/////////////
139
140void userinfo_t::clear () {
141 username.clear();
142 password.clear();
143 enabled = false;
144 groups.clear();
145 comment.clear();
146}
147
148userinfo_t &userinfo_t::operator=(const userinfo_t &x) {
149 username = x.username;
150 password = x.password;
151 enabled = x.enabled;
152 groups = x.groups;
153 comment = x.comment;
154
155 return *this;
156}
157
158
159
160// functions dealing with user databases
161
162// returns true on success (in which case userinfo will contain
163// the information for this user)
164bool get_user_info (gdbmclass &userdb, const text_t &username,
165 userinfo_t &userinfo) {
166 userinfo.clear();
167
168 infodbclass info;
169 if (userdb.getinfo (username, info)) {
170 userinfo.username = info["username"];
171 userinfo.password = info["password"];
172 userinfo.enabled = (info["enabled"] == "true");
173 userinfo.groups = info["groups"];
174 userinfo.comment = info["comment"];
175 return true;
176 }
177
178 return false;
179}
180
181bool get_user_info (const text_t &userdbfile, const text_t &username,
182 userinfo_t &userinfo) {
183 gdbmclass userdb;
184 if (!userdb.opendatabase(userdbfile, GDBM_READER, 1000, true)) {
185/* if (!userdbfile.empty() && !file_exists (userdbfile) &&
186 username == "admin") {*/
187 if (!userdbfile.empty() && username == "admin") {
188 // no database -- create a database with an initial account
189 userinfo.clear();
190 userinfo.username = "admin";
191 userinfo.password = crypt_text("admin");
192 userinfo.enabled = true;
193 userinfo.groups = "administrator,colbuilder";
194 userinfo.comment = "change the password for this account as soon as possible";
195 return set_user_info (userdbfile, username, userinfo);
196 }
197 return false;
198 }
199
200 bool success = get_user_info (userdb, username, userinfo);
201 userdb.closedatabase();
202
203 return success;
204}
205
206// returns true on success
207bool set_user_info (gdbmclass &userdb, const text_t &username,
208 const userinfo_t &userinfo) {
209 infodbclass info;
210 info["username"] = userinfo.username;
211 info["password"] = userinfo.password;
212 info["enabled"] = userinfo.enabled ? "true" : "false";
213 info["groups"] = userinfo.groups;
214 info["comment"] = userinfo.comment;
215
216 return userdb.setinfo (username, info);
217}
218
219bool set_user_info (const text_t &userdbfile, const text_t &username,
220 const userinfo_t &userinfo) {
221 gdbmclass userdb;
222 if (!userdb.opendatabase(userdbfile, GDBM_WRCREAT, 1000, true)) return false;
223
224 bool success = set_user_info (userdb, username, userinfo);
225 userdb.closedatabase();
226
227 return success;
228}
229
230// removes a user from the user database -- forever
231void delete_user (gdbmclass &userdb, const text_t &username) {
232 userdb.deletekey (username);
233}
234
235void delete_user (const text_t &userdbfile, const text_t &username) {
236 gdbmclass userdb;
237 if (!userdb.opendatabase(userdbfile, GDBM_WRCREAT, 1000, true)) return;
238
239 delete_user (userdb, username);
240 userdb.closedatabase();
241}
242
243// gets a list of all the users in the database. returns true
244// on success
245void get_user_list (gdbmclass &userdb, text_tarray &userlist) {
246 userlist.erase (userlist.begin(), userlist.end());
247
248 text_t user = userdb.getfirstkey ();
249 while (!user.empty()) {
250 userlist.push_back(user);
251 user = userdb.getnextkey (user);
252 }
253}
254
255// returns true if the user's password is correct.
256bool check_passwd (const userinfo_t &thisuser, const text_t &password) {
257 // couple of basic checks
258 if (thisuser.username.empty() || thisuser.password.empty() ||
259 password.empty()) return false;
260
261 text_t crypt_password = crypt_text(password);
262 return (thisuser.password == crypt_password);
263}
264
265
266// functions dealing with databases of temporary keys
267
268// generates a random key for the user, stores it in the database and
269// returns it so that it can be used in page generation
270// returns "" on failure
271text_t generate_key (gdbmclass &keydb, const text_t &username) {
272 static const char *numconvert = "0123456789abcdefghijklmnopqrstuvwxyz"
273 "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
274
275 // loop looking for a suitable new key
276 text_t userkey;
277 text_t crypt_userkey;
278 do {
279 // convert to base 62 :-)
280 int userkey_int = rand ();
281 while (userkey_int > 0) {
282 userkey.push_back (numconvert[userkey_int%62]);
283 userkey_int /= 62;
284 }
285
286 // make sure this key is not in the database
287 crypt_userkey = crypt_text(userkey);
288 if (keydb.exists (crypt_userkey)) userkey.clear();
289 } while (userkey.empty());
290
291 // enter the key into the database
292 infodbclass keydata;
293 keydata["user"] = username;
294 keydata["time"] = time2text(time(NULL));
295
296 if (!keydb.setinfo (crypt_userkey, keydata)) {
297 userkey.clear(); // failed
298 }
299
300 return userkey;
301}
302
303text_t generate_key (const text_t &keydbfile, const text_t &username) {
304 gdbmclass keydb;
305 if (!keydb.opendatabase(keydbfile, GDBM_WRCREAT, 1000, true)) return "";
306
307 text_t key = generate_key (keydb, username);
308 keydb.closedatabase();
309
310 return key;
311}
312
313
314// checks to see if there is a key for this particular user in the
315// database that hasn't decayed. a short decay is used when group
316// is set to administrator
317bool check_key (gdbmclass &keydb, const userinfo_t &thisuser,
318 const text_t &key, const text_t &group, int keydecay) {
319 if (thisuser.username.empty() || key.empty()) return false;
320
321 // the keydecay is set to 5 minute for things requiring the
322 // administrator
323 if (group == "administrator") keydecay = 300;
324
325 // success if there is a key in the key database that is owned by this
326 // user whose creation time is less that keydecay
327 text_t crypt_key = crypt_text(key);
328 infodbclass info;
329 if (keydb.getinfo (crypt_key, info)) {
330 if (info["user"] == thisuser.username) {
331 time_t keycreation = text2time (info["time"]);
332 if (keycreation != (time_t)-1 && difftime (text2time(time2text(time(NULL))),
333 keycreation) <= keydecay) {
334 // succeeded, update the key's time
335 info["time"] = time2text(time(NULL));
336 keydb.setinfo (crypt_key, info);
337 return true;
338 }
339 }
340 }
341
342 return false;;
343}
344
345bool check_key (const text_t &keydbfile, const userinfo_t &thisuser,
346 const text_t &key, const text_t &group, int keydecay) {
347 gdbmclass keydb;
348 if (!keydb.opendatabase(keydbfile, GDBM_WRCREAT, 1000, true)) return false;
349
350 bool success = check_key (keydb, thisuser, key, group, keydecay);
351 keydb.closedatabase();
352
353 return success;
354}
355
356
357// remove_old_keys will remove all keys created more than keydecay ago.
358// use sparingly, it can be quite an expensive function
359void remove_old_keys (const text_t &keydbfile, int keydecay) {
360 // open the key database
361 gdbmclass keydb;
362 if (!keydb.opendatabase(keydbfile, GDBM_WRCREAT, 1000, true)) return;
363
364 // get a list of keys created more than keydecay seconds agon
365 text_tarray oldkeys;
366 text_t key = keydb.getfirstkey ();
367 infodbclass info;
368 time_t timenow = text2time(time2text(time(NULL)));
369 time_t keycreation = (time_t)-1;
370 while (!key.empty()) {
371 if (keydb.getinfo (key, info)) {
372 keycreation = text2time (info["time"]);
373 if (keycreation != (time_t)-1 && difftime (timenow, keycreation) > keydecay) {
374 // found an old key
375 oldkeys.push_back(key);
376 }
377 }
378
379 key = keydb.getnextkey (key);
380 }
381
382 // delete the keys
383 text_tarray::iterator keys_here = oldkeys.begin();
384 text_tarray::iterator keys_end = oldkeys.end();
385 while (keys_here != keys_end) {
386 keydb.deletekey(*keys_here);
387 keys_here++;
388 }
389
390 // close the key database
391 keydb.closedatabase();
392}
Note: See TracBrowser for help on using the repository browser.