source: trunk/gsdl/src/recpt/authenaction.cpp@ 373

Last change on this file since 373 was 369, checked in by rjmcnab, 25 years ago

Got something basic working.

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 14.6 KB
Line 
1/**********************************************************************
2 *
3 * authenaction.cpp -- authenticating users
4 * Copyright (C) 1999 DigiLib Systems Limited, New Zealand
5 *
6 * PUT COPYRIGHT NOTICE HERE
7 *
8 * $Id: authenaction.cpp 369 1999-07-11 10:47:32Z rjmcnab $
9 *
10 *********************************************************************/
11
12/*
13 $Log$
14 Revision 1.2 1999/07/11 10:47:32 rjmcnab
15 Got something basic working.
16
17 Revision 1.1 1999/07/10 22:19:29 rjmcnab
18 Initial revision.
19
20
21 */
22
23
24#include "authenaction.h"
25#include "fileutil.h"
26#include "cfgread.h"
27#include "cgiutils.h"
28#include "infodbclass.h"
29#include "gsdltimes.h"
30
31
32/////////////////////////
33// a few useful functions
34/////////////////////////
35
36
37// reads in the password file. It returns true if successful
38// format is: username password status [groups]
39static bool read_passwd (const text_t &passwd_filename, userinfo_tmap &userinfo) {
40 text_tarray passwdline;
41 userinfo_t thisuserinfo;
42
43 userinfo.erase(userinfo.begin(), userinfo.end());
44
45 char *cstr = passwd_filename.getcstr();
46 ifstream passwdin (cstr);
47 delete cstr;
48
49 if (passwdin) {
50 while (read_cfg_line(passwdin, passwdline) >= 0) {
51 if (passwdline.size () >= 3) {
52 // get the information about the user
53 thisuserinfo.clear();
54 thisuserinfo.username = passwdline[0];
55 thisuserinfo.password = passwdline[1];
56 if (passwdline[2] == "enabled") thisuserinfo.status = userinfo_t::enabled;
57 else thisuserinfo.status = userinfo_t::disabled;
58 if (passwdline.size() >= 4) thisuserinfo.groups = passwdline[3];
59
60 // insert the user information into the map
61 if (!thisuserinfo.username.empty()) {
62 userinfo[thisuserinfo.username] = thisuserinfo;
63 }
64 }
65 }
66
67 passwdin.close ();
68 return true;
69 }
70
71 return false;
72}
73
74// returns true if the user was found, false otherwise
75static bool get_user_info (const userinfo_tmap &userinfo, const text_t &username,
76 userinfo_t &thisuser) {
77 thisuser.clear();
78
79 userinfo_tmap::const_iterator thisuser_here = userinfo.find (username);
80 if (thisuser_here != userinfo.end()) {
81 thisuser = (*thisuser_here).second;
82 return true;
83 }
84
85 return false;
86}
87
88// return true if the password matches, false if it doesn't
89static bool check_passwd (const userinfo_t &thisuser, const text_t &password) {
90 if (!thisuser.username.empty() && !thisuser.password.empty() &&
91 !password.empty() && thisuser.password == password) return true;
92
93 return false;
94}
95
96// returns true if the key is still valid for this user,
97// and false otherwise
98static bool check_key (const text_t &keyfile, const userinfo_t &thisuser,
99 const text_t &key, const text_t &group, int keydecay,
100 ostream &logout) {
101 outconvertclass text_t2ascii;
102
103 if (keyfile.empty() || thisuser.username.empty() ||
104 key.empty()) return false;
105
106 // the keydecay is set to 1/2 minute for things requiring the
107 // administrator
108 if (group == "administrator") keydecay = 30;
109
110 // open the key database
111 gdbmclass keydb;
112 if (!keydb.opendatabase (keyfile, GDBM_WRCREAT, 1000)) {
113 logout << text_t2ascii << "Error: write open failed for key database \""
114 << keyfile << "\"\n";
115 return false; // failed
116 }
117
118 // success if there is a key in the key database that is owned by this
119 // user whose creation time is less that keydecay
120 infodbclass info;
121 bool success = false;
122 if (keydb.getinfo (key, info)) {
123 if (info["user"] == thisuser.username) {
124 time_t keycreation = text2time (info["time"]);
125 if (keycreation == (time_t)-1) {
126 logout << text_t2ascii << "Error: failed to convert an authentication "
127 "key into its equivalent time_t. Time text was \"" << info["time"]
128 << "\" for key \"" << key << "\"\n";
129
130 } else if (difftime (time(NULL), keycreation) <= keydecay) {
131 // succeeded, update the key's time
132 success = true;
133 info["time"] = time2text(time(NULL));
134 if (!keydb.setinfo (key, info)) {
135 logout << text_t2ascii << "Error: setinfo failed for key database \""
136 << keyfile << "\"\n";
137 }
138 }
139 }
140 }
141
142 // close the database
143 keydb.closedatabase();
144
145 return success;
146}
147
148
149static text_t generate_key (const text_t &keyfile, const text_t &username,
150 ostream &logout) {
151 static const char *numconvert = "0123456789abcdefghijklmnopqrstuvwxyz"
152 "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
153 outconvertclass text_t2ascii;
154
155 // open the key database
156 gdbmclass keydb;
157 if (!keydb.opendatabase (keyfile, GDBM_WRCREAT, 1000)) {
158 logout << text_t2ascii << "Error: write open failed for key database \""
159 << keyfile << "\"\n";
160 return ""; // failed
161 }
162
163 // loop looking for a suitable new key
164 text_t userkey;
165 do {
166 int userkey_int = rand ();
167 while (userkey_int > 0) {
168 userkey.push_back (numconvert[userkey_int%62]);
169 userkey_int /= 62;
170 }
171
172 // make sure this key is not in the database
173 if (keydb.exists (userkey)) userkey.clear();
174 } while (userkey.empty());
175
176 // enter the key into the database
177 infodbclass keydata;
178 keydata["user"] = username;
179 keydata["time"] = time2text(time(NULL));
180
181 if (!keydb.setinfo (userkey, keydata)) {
182 logout << text_t2ascii << "Error: setinfo failed for key database \""
183 << keyfile << "\"\n";
184 userkey.clear(); // failed
185 }
186
187 // close the database
188 keydb.closedatabase();
189
190 return userkey;
191}
192
193
194/////////////
195// userinfo_t
196/////////////
197
198
199void userinfo_t::clear () {
200 username.clear();
201 password.clear();
202 status = invalid;
203 groups.clear();
204}
205
206
207
208///////////////
209// authenaction
210///////////////
211
212authenaction::authenaction () {
213 keydecay = 600; // 10 minutes
214 recpt = NULL;
215
216 // this action uses cgi variable "a"
217 cgiarginfo arg_ainfo;
218 arg_ainfo.shortname = "a";
219 arg_ainfo.longname = "action";
220 arg_ainfo.multiplechar = true;
221 arg_ainfo.defaultstatus = cgiarginfo::weak;
222 arg_ainfo.argdefault = "a";
223 arg_ainfo.savedarginfo = cgiarginfo::must;
224 argsinfo.addarginfo (NULL, arg_ainfo);
225
226 // "us"
227 arg_ainfo.shortname = "us";
228 arg_ainfo.longname = "user account status";
229 arg_ainfo.multiplechar = true;
230 arg_ainfo.defaultstatus = cgiarginfo::weak;
231 arg_ainfo.argdefault = "invalid";
232 arg_ainfo.savedarginfo = cgiarginfo::mustnot;
233 argsinfo.addarginfo (NULL, arg_ainfo);
234
235 // "ug"
236 arg_ainfo.shortname = "ug";
237 arg_ainfo.longname = "user groups"; // comma seperated list
238 arg_ainfo.multiplechar = true;
239 arg_ainfo.defaultstatus = cgiarginfo::weak;
240 arg_ainfo.argdefault = "";
241 arg_ainfo.savedarginfo = cgiarginfo::mustnot;
242 argsinfo.addarginfo (NULL, arg_ainfo);
243
244 // "un"
245 arg_ainfo.shortname = "un";
246 arg_ainfo.longname = "user name";
247 arg_ainfo.multiplechar = true;
248 arg_ainfo.defaultstatus = cgiarginfo::weak;
249 arg_ainfo.argdefault = "";
250 arg_ainfo.savedarginfo = cgiarginfo::must;
251 argsinfo.addarginfo (NULL, arg_ainfo);
252
253 // "pw"
254 arg_ainfo.shortname = "pw";
255 arg_ainfo.longname = "password";
256 arg_ainfo.multiplechar = true;
257 arg_ainfo.defaultstatus = cgiarginfo::weak;
258 arg_ainfo.argdefault = "";
259 arg_ainfo.savedarginfo = cgiarginfo::mustnot;
260 argsinfo.addarginfo (NULL, arg_ainfo);
261
262 // "ky" - gives a specific user authentication for a
263 // limited amount of time
264 arg_ainfo.shortname = "ky";
265 arg_ainfo.longname = "user time key";
266 arg_ainfo.multiplechar = true;
267 arg_ainfo.defaultstatus = cgiarginfo::weak;
268 arg_ainfo.argdefault = "";
269 arg_ainfo.savedarginfo = cgiarginfo::must;
270 argsinfo.addarginfo (NULL, arg_ainfo);
271
272 // "ua" - ""=no, "1"=yes
273 arg_ainfo.shortname = "ua";
274 arg_ainfo.longname = "whether a user has been authenticated";
275 arg_ainfo.multiplechar = true;
276 arg_ainfo.defaultstatus = cgiarginfo::weak;
277 arg_ainfo.argdefault = "";
278 arg_ainfo.savedarginfo = cgiarginfo::mustnot;
279 argsinfo.addarginfo (NULL, arg_ainfo);
280
281 // "er" - compressed arguments for the referer page
282 arg_ainfo.shortname = "er";
283 arg_ainfo.longname = "the compressed args of the refer page";
284 arg_ainfo.multiplechar = true;
285 arg_ainfo.defaultstatus = cgiarginfo::weak;
286 arg_ainfo.argdefault = "";
287 arg_ainfo.savedarginfo = cgiarginfo::mustnot;
288 argsinfo.addarginfo (NULL, arg_ainfo);
289
290 // "uan" - whether user authentication is needed
291 arg_ainfo.shortname = "uan";
292 arg_ainfo.longname = "whether user authentication is needed";
293 arg_ainfo.multiplechar = true;
294 arg_ainfo.defaultstatus = cgiarginfo::weak;
295 arg_ainfo.argdefault = "";
296 arg_ainfo.savedarginfo = cgiarginfo::mustnot;
297 argsinfo.addarginfo (NULL, arg_ainfo);
298}
299
300void authenaction::configure (const text_t &key, const text_tarray &cfgline) {
301 // get the password filename
302 if (cfgline.size() == 1) {
303 if (key == "passwdfile") passwdfile = cfgline[0];
304 else if (key == "keyfile") keyfile = cfgline[0];
305 else if (key == "keydecay") keydecay = cfgline[0].getint();
306 else if (key == "gsdlhome") {
307 if (passwdfile.empty())
308 passwdfile = filename_cat (cfgline[0], "etc", "passwd");
309 if (keyfile.empty())
310 keyfile = filename_cat (cfgline[0], "etc", "key.db");
311 }
312 }
313
314 action::configure (key, cfgline);
315}
316
317bool authenaction::init (ostream &logout) {
318 outconvertclass text_t2ascii;
319
320 // read in the password file
321 if (!read_passwd (passwdfile, userinfo)) {
322 logout << text_t2ascii << "Error: could not read in the password file "
323 << passwdfile << "\n";
324 return false;
325 }
326
327 return action::init (logout);
328}
329
330bool authenaction::check_cgiargs (cgiargsinfoclass &/*argsinfo*/, cgiargsclass &/*args*/,
331 ostream &/*logout*/) {
332 return true;
333}
334
335// returns false if there is a major problem with the cgi arguments -- not
336// if authentication fails. If the authentication fails "un" will be empty
337bool authenaction::check_external_cgiargs (cgiargsinfoclass &argsinfo,
338 cgiargsclass &args,
339 outconvertclass &outconvert,
340 const text_t &saveconf,
341 ostream &logout) {
342 // success will be dealt with using a redirect in get_cgihead_info if neede
343 // failure means we have to redirect to this action to get authentication
344 // (if we are not already doing this)
345
346 userinfo_t thisuser;
347
348 text_t &args_uan = args["uan"];
349 text_t &args_un = args["un"];
350 text_t &args_pw = args["pw"];
351 text_t &args_us = args["us"];
352 text_t &args_ug = args["ug"];
353 text_t &args_ky = args["ky"];
354 text_t &args_ua = args["ua"];
355
356 args_ua.clear(); // default = false;
357 if (args_un.empty() || args_pw.empty()) args_us = "invalid";
358 else args_us = "failed";
359
360 // make sure we have a username
361 if (!args_un.empty() && get_user_info (userinfo, args_un, thisuser)) {
362 if (!args_pw.empty()) {
363 // we are authenticating using a password
364 if (check_passwd (thisuser, args_pw)) {
365 args_ua = "1"; // succeeded
366 }
367
368 } else if (!args_ky.empty()) {
369 // we are authenticating using a key
370 if (check_key (keyfile, thisuser, args_ky, args_ug, keydecay, logout))
371 args_ua = "1";
372 else args_us = "stalekey";
373 }
374 }
375
376 args_pw.clear(); // password goes no further
377
378 if (!args_ua.empty()) {
379 if (thisuser.status==userinfo_t::enabled) {
380 // check to make sure the user is in the required group
381 if (!args_ug.empty()) {
382 thisuser.status = userinfo_t::permissiondenied;
383 text_t::const_iterator group_here = thisuser.groups.begin();
384 text_t::const_iterator group_end = thisuser.groups.end();
385 text_t thisgroup;
386 while (group_here != group_end) {
387 group_here = getdelimitstr (group_here, group_end, ',', thisgroup);
388 if (thisgroup == args_ug) {
389 thisuser.status = userinfo_t::enabled;
390 break;
391 }
392 }
393 }
394
395 if (thisuser.status==userinfo_t::enabled) {
396 // succeeded, get info about this user
397 // note: we don't need to set "ug" as it is already set to
398 // what it needs to be
399 args_us = "enabled";
400 args_ky = generate_key (keyfile, args_un, logout); // new key
401
402 } else {
403 // succeeded, however, the user is not in the correct group
404 args_ua.clear();
405 args_us = "permissiondenied";
406 args_ug.clear();
407 args_ky.clear();
408
409 }
410 } else {
411 // succeeded, however, the account is disabled
412 args_ua.clear();
413 args_us = "disabled";
414 args_ug.clear();
415 args_ky.clear();
416 }
417
418 } else {
419 // failure, reset info about the user
420 args_ug.clear();
421 args_ky.clear();
422 }
423
424 // we will have to redirect the user if authentication is needed,
425 // it failed, and we weren't on our way to be authenticated anyway
426 if (!args_uan.empty() && args_ua.empty() && args["a"] != "a") {
427 // need to save the current arguments in "er"
428 text_t &arg_er = args["er"];
429 if (!compress_save_args(argsinfo, saveconf, args, arg_er, outconvert, logout))
430 arg_er.clear();
431
432 // redirect to this action
433 args["a"] = "a";
434 }
435
436 return true;
437}
438
439void authenaction::get_cgihead_info (cgiargsclass &/*args*/, response_t &response,
440 text_t &response_data, ostream &/*logout*/) {
441 response = content;
442 response_data = "text/html";
443}
444
445void authenaction::define_internal_macros (displayclass &disp, cgiargsclass &args,
446 recptproto */*collectproto*/, ostream &/*logout*/) {
447 // sets _authen:messageextra_ based on the value of args["us"]
448 // _authen:hiddenargs_ to contain all the arguments that were
449 // explicitly set
450 disp.setmacro ("messagestatus", "authen", ("_authen:message" + args["us"]
451 + "_"));
452
453 // get a list of saved configuration arguments (if possible)
454 text_t saveconf;
455 text_tset saveconfset;
456 if (recpt != NULL) {
457 saveconf = recpt->get_configinfo().saveconf;
458 splitchar (saveconf.begin(), saveconf.end(), '-', saveconfset);
459 }
460
461 text_t hiddenargs;
462 cgiargsclass::const_iterator args_here = args.begin();
463 cgiargsclass::const_iterator args_end = args.end();
464 while (args_here != args_end) {
465 // set this as a hidden argument if it came from the cgi arguments,
466 // its not the compressed arguments, the query string, a user name or
467 // password, and if it is not in the compressed arguments
468 if ((*args_here).second.source == cgiarg_t::cgi_arg &&
469 (*args_here).first != "e" && (*args_here).first != "q" &&
470 (*args_here).first != "un" && (*args_here).first != "pw" &&
471 saveconfset.find((*args_here).first) == saveconfset.end()) {
472 hiddenargs += "<input type=hidden name=\"" + (*args_here).first +
473 "\" value=\"_cgiarg" + (*args_here).first + "_\">\n";
474 }
475
476 args_here++;
477 }
478
479 disp.setmacro ("hiddenargs", "authen", hiddenargs);
480}
481
482void authenaction::define_external_macros (displayclass &/*disp*/, cgiargsclass &/*args*/,
483 recptproto */*collectproto*/, ostream &/*logout*/) {
484}
485
486bool authenaction::do_action (cgiargsclass &args, recptproto */*collectproto*/,
487 displayclass &disp, outconvertclass &outconvert,
488 ostream &textout, ostream &/*logout*/) {
489 if (args["us"] == "enabled") {
490 // have been authenticated
491 textout << outconvert << disp
492 << "_authenok:header_\n_authenok:content_\n_authenok:footer_\n";
493 return true;
494 }
495
496 // need to be authenticated
497 textout << outconvert << disp
498 << "_authen:header_\n_authen:content_\n_authen:footer_\n";
499
500 return true;
501}
Note: See TracBrowser for help on using the repository browser.