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

Last change on this file since 411 was 386, checked in by rjmcnab, 25 years ago

fixed an error to do with the way time is dealt with on different machines.
It seems that there is no direct corresponding function to mktime and
localtime or gmtime might not get you back to where you started.

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