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

Last change on this file since 9620 was 9620, checked in by kjdon, 19 years ago

added some x++ -> ++x changes submitted by Emanuel Dejanu

  • 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 *********************************************************************/
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 g_EmptyText;
51
52 // encrypt the password
53 char *text_cstr = text.getcstr();
54 if (text_cstr == NULL) return g_EmptyText;
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// removes spaces from user groups
236text_t format_user_groups(const text_t user_groups){
237
238 text_t new_groups = g_EmptyText;
239 text_t::const_iterator here = user_groups.begin();
240 text_t::const_iterator end = user_groups.end();
241 while (here != end) {
242 if (*here != ' '&& *here != '\t' && *here != '\n') {
243 new_groups.push_back(*here);
244 }
245 ++here;
246 }
247 return new_groups;
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 g_EmptyText;
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.