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

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

changed default timeout on authentication from 10 minutes to 30 minutes
and stopped treating the admin users any differently (i.e. admin
authentication also lasts 30 minutes (was 5 mins.))

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