root/main/trunk/greenstone2/runtime-src/src/recpt/authenaction.cpp @ 28899

Revision 28899, 12.0 KB (checked in by ak19, 5 years ago)

Third commit for security, for ensuring cgiargs macros are websafe. This time all the changes to the runtime action classes.

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1/**********************************************************************
2 *
3 * authenaction.cpp -- authenticating users
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 "gsdl_modules_cfg.h"
27#ifdef GSDL_USE_AUTHEN_ACTION
28
29#include "authenaction.h"
30#include "fileutil.h"
31#include "cfgread.h"
32#include "cgiutils.h"
33#include "gsdltimes.h"
34
35
36///////////////
37// authenaction
38///////////////
39
40authenaction::authenaction () {
41  keydecay = 1800; // 30 minutes
42  recpt = NULL;
43
44  // this action uses cgi variable "a"
45  cgiarginfo arg_ainfo;
46  arg_ainfo.shortname = "a";
47  arg_ainfo.longname = "action";
48  arg_ainfo.multiplechar = true;
49  arg_ainfo.multiplevalue = false;
50  arg_ainfo.defaultstatus = cgiarginfo::weak;
51  arg_ainfo.argdefault = "a";
52  arg_ainfo.savedarginfo = cgiarginfo::must;
53  argsinfo.addarginfo (NULL, arg_ainfo);
54
55  // "us"
56  arg_ainfo.shortname = "us";
57  arg_ainfo.longname = "user account status";
58  arg_ainfo.multiplechar = true;
59  arg_ainfo.multiplevalue = false;
60  arg_ainfo.defaultstatus = cgiarginfo::weak;
61  arg_ainfo.argdefault = "invalid";
62  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
63  argsinfo.addarginfo (NULL, arg_ainfo);
64
65  // "ug"
66  arg_ainfo.shortname = "ug";
67  arg_ainfo.longname = "user groups"; // comma seperated list
68  arg_ainfo.multiplechar = true;
69  arg_ainfo.multiplevalue = false;
70  arg_ainfo.defaultstatus = cgiarginfo::weak;
71  arg_ainfo.argdefault = g_EmptyText;
72  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
73  argsinfo.addarginfo (NULL, arg_ainfo);
74
75  // "un"
76  arg_ainfo.shortname = "un";
77  arg_ainfo.longname = "user name";
78  arg_ainfo.multiplechar = true;
79  arg_ainfo.multiplevalue = false;
80  arg_ainfo.defaultstatus = cgiarginfo::weak;
81  arg_ainfo.argdefault = g_EmptyText;
82  arg_ainfo.savedarginfo = cgiarginfo::must;
83  argsinfo.addarginfo (NULL, arg_ainfo);
84
85  // "pw"
86  arg_ainfo.shortname = "pw";
87  arg_ainfo.longname = "password";
88  arg_ainfo.multiplechar = true;
89  arg_ainfo.multiplevalue = false;
90  arg_ainfo.defaultstatus = cgiarginfo::weak;
91  arg_ainfo.argdefault = g_EmptyText;
92  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
93  argsinfo.addarginfo (NULL, arg_ainfo);
94
95  // "ky" - gives a specific user authentication for a
96  // limited amount of time
97  arg_ainfo.shortname = "ky";
98  arg_ainfo.longname = "user time key";
99  arg_ainfo.multiplechar = true;
100  arg_ainfo.multiplevalue = false;
101  arg_ainfo.defaultstatus = cgiarginfo::weak;
102  arg_ainfo.argdefault = g_EmptyText;
103  arg_ainfo.savedarginfo = cgiarginfo::must;
104  argsinfo.addarginfo (NULL, arg_ainfo);
105
106  // "ua" - ""=no, "1"=yes
107  arg_ainfo.shortname = "ua";
108  arg_ainfo.longname = "whether a user has been authenticated";
109  arg_ainfo.multiplechar = true;
110  arg_ainfo.multiplevalue = false;
111  arg_ainfo.defaultstatus = cgiarginfo::weak;
112  arg_ainfo.argdefault = g_EmptyText;
113  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
114  argsinfo.addarginfo (NULL, arg_ainfo);
115
116  // "er" - compressed arguments for the referer page
117  arg_ainfo.shortname = "er";
118  arg_ainfo.longname = "the compressed args of the refer page";
119  arg_ainfo.multiplechar = true;
120  arg_ainfo.multiplevalue = false;
121  arg_ainfo.defaultstatus = cgiarginfo::weak;
122  arg_ainfo.argdefault = g_EmptyText;
123  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
124  argsinfo.addarginfo (NULL, arg_ainfo);
125
126  // "uan" - whether user authentication is needed
127  arg_ainfo.shortname = "uan";
128  arg_ainfo.longname = "whether user authentication is needed";
129  arg_ainfo.multiplechar = true;
130  arg_ainfo.multiplevalue = false;
131  arg_ainfo.defaultstatus = cgiarginfo::weak;
132  arg_ainfo.argdefault = g_EmptyText;
133  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
134  argsinfo.addarginfo (NULL, arg_ainfo);
135}
136
137void authenaction::configure (const text_t &key, const text_tarray &cfgline) {
138  action::configure (key, cfgline);
139}
140
141bool authenaction::init (ostream &logout) {
142  if (dbhome.empty()) {
143    logout << "ERROR (authenaction::init) dbhome is not set\n";
144    return false;
145  }
146
147  return action::init (logout);
148}
149
150bool authenaction::check_cgiargs (cgiargsinfoclass &/*argsinfo*/, cgiargsclass &/*args*/,
151                  recptprotolistclass * /*protos*/, ostream &/*logout*/) {
152  return true;
153}
154
155// returns false if there is a major problem with the cgi arguments -- not
156// if authentication fails. If the authentication fails "un" will be empty
157bool authenaction::check_external_cgiargs (cgiargsinfoclass &argsinfo,
158                       cgiargsclass &args,
159                       outconvertclass &outconvert,
160                       const text_t &saveconf,
161                       ostream &logout) {
162
163  // no need to go further unless authentication is
164  // required by this page
165  if (args["uan"].empty()) {
166    return true;
167  }
168
169  // failure means we have to redirect to this action to get authentication
170  // (if we are not already doing this)
171
172  userinfo_t thisuser;
173
174  text_t &args_uan = args["uan"];  text_t &args_un = args["un"];
175  text_t &args_pw = args["pw"];    text_t &args_us = args["us"];
176  text_t &args_ug = args["ug"];    text_t &args_ky = args["ky"];
177  text_t &args_ua = args["ua"];    text_t &args_a = args["a"];
178
179  // we must have a username and a password or a key to
180  // do any authentication
181  args_ua.clear(); // default = false;
182  if (args_un.empty() || args_pw.empty()) {
183    args_us = "invalid";
184  }
185  else {
186    args_us = "failed";
187  }
188
189  // make sure we have a username                                                                                                 
190  int status = user_database->get_user_info (args_un, thisuser);
191  if (!args_un.empty() && (status == ERRNO_SUCCEED)) {
192    if (!args_pw.empty()) {
193      // we are authenticating using a password
194      if (user_database->check_passwd (thisuser.username, args_pw) == ERRNO_SUCCEED) {
195    args_ua = "1"; // succeeded
196      }
197     
198    } else if (!args_ky.empty()) {
199      // we are authenticating using a key
200      if (key_database->check_key(thisuser, args_ky, args_ug, keydecay)) {
201    args_ua = "1";
202      }
203      else {
204    args_us = "stalekey";
205      }
206    }
207  }
208
209  args_pw.clear(); // password goes no further
210  if (!args_ua.empty()) {
211    if (thisuser.enabled) {
212      bool haspermission = true;
213
214      // check to make sure the user is in the required group
215      // one group is available only at the moment.
216      // this is what we are changing !
217     
218      if (!args_ug.empty()) {
219
220     // Since we receive a comma separated list
221     // of groups like mygroup,yourgroup,ourgroup
222     // we want to split them into individual groups
223     // and examine them. This is what is done here.
224     
225     text_tset splitgrps;
226     text_t::const_iterator split_here = args_ug.begin();
227     text_t::const_iterator split_end = args_ug.end();
228
229     splitchar(split_here,split_end,',',splitgrps);
230
231     haspermission = false;
232
233     // This examines the current user to be authenticated and
234     // tries to see if he or she is in the group that we have in
235     // thisuser structure. We compare args_ua contents with that
236     // of the user database.
237     
238     text_t::const_iterator group_here = thisuser.groups.begin();
239     text_t::const_iterator group_end = thisuser.groups.end();
240     text_t thisgroup;
241    while (group_here != group_end) {
242       group_here = getdelimitstr (group_here, group_end, ',', thisgroup);
243       if (splitgrps.find(thisgroup) != splitgrps.end() )
244          {
245         haspermission = true;
246         break;
247          }
248    }
249      }
250     
251      if (haspermission) {
252     // succeeded, get info about this user
253     // note: we don't need to set "ug" as it is already set to what it needs to be
254     args_us = "enabled";
255     args_ky = key_database->generate_key(args_un); // new key
256   
257    // delete old keys around every 50 accesses
258    if (rand()%50 == 1) key_database->remove_old_keys(keydecay);
259   
260      } else {
261    // succeeded, however, the user is not in the correct group
262     args_ua.clear();
263     args_us = "permissiondenied";
264     args_ky.clear();
265      }
266     
267    } else {
268       // succeeded, however, the account is disabled
269      args_ua.clear();
270      args_us = "disabled";
271      args_ky.clear();
272    }
273
274  } else {
275    // failure, reset info about the user
276    args_ky.clear();
277  }
278
279  // we will have to redirect the user if authentication is needed,
280  // it failed, and we weren't on our way to be authenticated anyway
281  if ((!args_uan.empty()) && (args_ua.empty()) && (args_a != "a")) {
282    // need to save the current arguments in "er"
283    text_t &arg_er = args["er"];
284    if (!compress_save_args(argsinfo, saveconf, args, arg_er, outconvert, logout)) {
285      arg_er.clear();
286    }
287   
288    // needs to be decoded for use within forms
289    decode_cgi_arg (arg_er);   
290
291    // redirect to this action
292    args_a = "a";
293  }
294
295  return true;
296}
297
298void authenaction::get_cgihead_info (cgiargsclass &/*args*/, recptprotolistclass * /*protos*/,
299                     response_t &response, text_t &response_data,
300                     ostream &/*logout*/) {
301  response = content;
302  response_data = "text/html";
303}
304
305void authenaction::define_internal_macros (displayclass &disp, cgiargsclass &args,
306                       recptprotolistclass * /*protos*/, ostream &/*logout*/) {
307  // sets _authen:messageextra_ based on the value of args["us"]
308  //      _authen:hiddenargs_   to contain all the arguments that were
309  //                            explicitly set
310  disp.setmacro ("messagestatus", "authen", ("_authen:message" + encodeForHTML(args["us"])
311                         + "_"));
312  // change style of header and footer if page is a frame
313  if ((args["p"].empty()) || (args["p"] == "frameset")) {
314    disp.setmacro ("header", "authen", "_status:infoheader_(Log in)");
315    disp.setmacro ("header", "authenok", "_status:infoheader_(Log in)");
316    disp.setmacro ("footer", "authen", "_status:infofooter_(Log in)");
317    disp.setmacro ("footer", "authenok", "_status:infofooter_(Log in)");
318  }
319
320  // get a list of saved configuration arguments (if possible)
321  text_t saveconf;
322  text_tset saveconfset;
323  if (recpt != NULL) {
324    saveconf = recpt->get_configinfo().saveconf;
325    splitchar (saveconf.begin(), saveconf.end(), '-', saveconfset);
326  }
327 
328  text_t hiddenargs;
329  cgiargsclass::const_iterator args_here = args.begin();
330  cgiargsclass::const_iterator args_end = args.end();
331  while (args_here != args_end) {
332    // set this as a hidden argument if it came from the cgi arguments,
333    // its not the compressed arguments, the query string, a user name or
334    // password, or collect.cfg, and if it is not in the compressed arguments
335    if ((*args_here).second.source == cgiarg_t::cgi_arg &&
336    (*args_here).first != "e" && (*args_here).first != "q" &&
337    (*args_here).first != "un" && (*args_here).first != "pw" &&
338    (*args_here).first != "cfgfile" &&
339    saveconfset.find((*args_here).first) == saveconfset.end()) {
340      hiddenargs += "<input type=hidden name=\"" + (*args_here).first +
341    "\" value=\"_cgiarg" + (*args_here).first + "Attrsafe_\">\n";
342    }
343    ++args_here;
344  }
345
346  disp.setmacro ("hiddenargs", "authen", hiddenargs);
347}
348
349bool authenaction::do_action (cgiargsclass &args, recptprotolistclass * /*protos*/,
350                  browsermapclass * /*browsers*/, displayclass &disp,
351                  outconvertclass &outconvert, ostream &textout,
352                  ostream &/*logout*/) {
353  if (args["us"] == "enabled") {
354    // have been authenticated
355    textout << outconvert << disp
356        << "_authenok:header_\n_authenok:content_\n_authenok:footer_\n";
357    return true;
358  }
359 
360  // need to be authenticated
361  textout << outconvert << disp
362      << "_authen:header_\n_authen:content_\n_authen:footer_\n";
363
364  return true;
365}
366
367#endif //GSDL_USE_AUTHEN_ACTION
Note: See TracBrowser for help on using the browser.