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

Last change on this file since 533 was 533, checked in by sjboddie, 25 years ago

added GPL notice

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 10.5 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 533 1999-09-07 04:57:01Z sjboddie $
25 *
26 *********************************************************************/
27
28/*
29 $Log$
30 Revision 1.4 1999/09/07 04:57:00 sjboddie
31 added GPL notice
32
33 Revision 1.3 1999/09/02 00:30:04 rjmcnab
34 added option for specifying whether the gdbm database should be locked
35
36 Revision 1.2 1999/07/14 08:30:01 rjmcnab
37 fixed an error to do with the way time is dealt with on different machines.
38 It seems that there is no direct corresponding function to mktime and
39 localtime or gmtime might not get you back to where you started.
40
41 Revision 1.1 1999/07/13 23:22:04 rjmcnab
42 Initial revision.
43
44 */
45
46
47#include "userdb.h"
48#include "gsdltimes.h"
49#include "fileutil.h"
50#include <stdlib.h>
51
52// include crypt
53//#include <crypt.h>
54#include <unistd.h>
55
56
57
58// few useful functions
59
60text_t crypt_text (const text_t &text) {
61 static const char *salt = "Tp";
62 text_t crypt_password;
63
64 if (text.empty()) return "";
65
66 // encrypt the password
67 char *text_cstr = text.getcstr();
68 if (text_cstr == NULL) return "";
69 crypt_password = crypt(text_cstr, salt);
70 delete text_cstr;
71
72 return crypt_password;
73}
74
75
76// username_ok tests to make sure a username is ok. a username
77// must be at least 2 characters long, but no longer than 30
78// characters long. it can contain the characters a-z A-Z 0-9
79// . and _
80bool username_ok (const text_t &username) {
81 if (username.size() < 2 || username.size() > 30) return false;
82
83 text_t::const_iterator here = username.begin();
84 text_t::const_iterator end = username.end();
85 while (here != end) {
86 if ((*here >= 'a' && *here <= 'z') ||
87 (*here >= 'A' && *here <= 'Z') ||
88 (*here >= '0' && *here <= '9') ||
89 *here == '.' ||
90 *here == '_') {
91 // ok
92 } else return false;
93 here++;
94 }
95
96 return true;
97}
98
99// password_ok tests to make sure a password is ok. a password
100// must be at least 3 characters long but no longer than 8 characters
101// long. it can contain any character in the range 0x20-0x7e
102bool password_ok (const text_t &password) {
103 if (password.size() < 3 || password.size() > 8) return false;
104
105 text_t::const_iterator here = password.begin();
106 text_t::const_iterator end = password.end();
107 while (here != end) {
108 if (*here >= 0x20 && *here <= 0x7e) {
109 // ok
110 } else return false;
111 here++;
112 }
113
114 return true;
115}
116
117
118
119
120/////////////
121// userinfo_t
122/////////////
123
124void userinfo_t::clear () {
125 username.clear();
126 password.clear();
127 enabled = false;
128 groups.clear();
129 comment.clear();
130}
131
132userinfo_t &userinfo_t::operator=(const userinfo_t &x) {
133 username = x.username;
134 password = x.password;
135 enabled = x.enabled;
136 groups = x.groups;
137 comment = x.comment;
138
139 return *this;
140}
141
142
143
144// functions dealing with user databases
145
146// returns true on success (in which case userinfo will contain
147// the information for this user)
148bool get_user_info (gdbmclass &userdb, const text_t &username,
149 userinfo_t &userinfo) {
150 userinfo.clear();
151
152 infodbclass info;
153 if (userdb.getinfo (username, info)) {
154 userinfo.username = info["username"];
155 userinfo.password = info["password"];
156 userinfo.enabled = (info["enabled"] == "true");
157 userinfo.groups = info["groups"];
158 userinfo.comment = info["comment"];
159 return true;
160 }
161
162 return false;
163}
164
165bool get_user_info (const text_t &userdbfile, const text_t &username,
166 userinfo_t &userinfo) {
167 gdbmclass userdb;
168 if (!userdb.opendatabase(userdbfile, GDBM_READER, 1000, true)) {
169/* if (!userdbfile.empty() && !file_exists (userdbfile) &&
170 username == "admin") {*/
171 if (!userdbfile.empty() && username == "admin") {
172 // no database -- create a database with an initial account
173 userinfo.clear();
174 userinfo.username = "admin";
175 userinfo.password = crypt_text("admin");
176 userinfo.enabled = true;
177 userinfo.groups = "administrator";
178 userinfo.comment = "change the password for this account as soon as possible";
179 return set_user_info (userdbfile, username, userinfo);
180 }
181 return false;
182 }
183
184 bool success = get_user_info (userdb, username, userinfo);
185 userdb.closedatabase();
186
187 return success;
188}
189
190// returns true on success
191bool set_user_info (gdbmclass &userdb, const text_t &username,
192 const userinfo_t &userinfo) {
193 infodbclass info;
194 info["username"] = userinfo.username;
195 info["password"] = userinfo.password;
196 info["enabled"] = userinfo.enabled ? "true" : "false";
197 info["groups"] = userinfo.groups;
198 info["comment"] = userinfo.comment;
199
200 return userdb.setinfo (username, info);
201}
202
203bool set_user_info (const text_t &userdbfile, const text_t &username,
204 const userinfo_t &userinfo) {
205 gdbmclass userdb;
206 if (!userdb.opendatabase(userdbfile, GDBM_WRCREAT, 1000, true)) return false;
207
208 bool success = set_user_info (userdb, username, userinfo);
209 userdb.closedatabase();
210
211 return success;
212}
213
214// removes a user from the user database -- forever
215void delete_user (gdbmclass &userdb, const text_t &username) {
216 userdb.deletekey (username);
217}
218
219void delete_user (const text_t &userdbfile, const text_t &username) {
220 gdbmclass userdb;
221 if (!userdb.opendatabase(userdbfile, GDBM_WRCREAT, 1000, true)) return;
222
223 delete_user (userdb, username);
224 userdb.closedatabase();
225}
226
227// gets a list of all the users in the database. returns true
228// on success
229void get_user_list (gdbmclass &userdb, text_tarray &userlist) {
230 userlist.erase (userlist.begin(), userlist.end());
231
232 text_t user = userdb.getfirstkey ();
233 while (!user.empty()) {
234 userlist.push_back(user);
235 user = userdb.getnextkey (user);
236 }
237}
238
239// returns true if the user's password is correct.
240bool check_passwd (const userinfo_t &thisuser, const text_t &password) {
241 // couple of basic checks
242 if (thisuser.username.empty() || thisuser.password.empty() ||
243 password.empty()) return false;
244
245 text_t crypt_password = crypt_text(password);
246 return (thisuser.password == crypt_password);
247}
248
249
250// functions dealing with databases of temporary keys
251
252// generates a random key for the user, stores it in the database and
253// returns it so that it can be used in page generation
254// returns "" on failure
255text_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
287text_t generate_key (const text_t &keydbfile, const text_t &username) {
288 gdbmclass keydb;
289 if (!keydb.opendatabase(keydbfile, GDBM_WRCREAT, 1000, true)) return "";
290
291 text_t key = generate_key (keydb, username);
292 keydb.closedatabase();
293
294 return key;
295}
296
297
298// checks to see if there is a key for this particular user in the
299// database that hasn't decayed. a short decay is used when group
300// is set to administrator
301bool 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
329bool 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
340
341// remove_old_keys will remove all keys created more than keydecay ago.
342// use sparingly, it can be quite an expensive function
343void 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}
Note: See TracBrowser for help on using the repository browser.