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

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

Removed CVS logging information from source files

  • 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.