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

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

tidied up windows installation

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