source: branches/New_Config_Format-branch/gsdl/src/recpt/userdb.cpp@ 1279

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

merged changes to trunk into New_Config_Format branch

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