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

Last change on this file since 2750 was 2750, checked in by kjm18, 23 years ago

can now have spaces after the commas in the groups field of add user form -
these are removed before the entry is written to the db

  • 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 "";
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// removes spaces from user groups
236text_t format_user_groups(const text_t user_groups){
237
238 text_t new_groups = "";
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 "";
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.