source: main/trunk/greenstone3/src/java/org/greenstone/gsdl3/LibraryServlet.java

Last change on this file was 38374, checked in by kjdon, 6 months ago

if we are page action (used as default when no action was specified), and no subaction specified, if collection is specified, use about page, otherwise use home page. This is to allow urls like library/collection/lucene-demo. This means that LibraryServlet must only set a=p as teh default, not sa=home

  • Property svn:keywords set to Author Date Id Revision
File size: 54.7 KB
Line 
1/*
2 * LibraryServlet.java
3 * Copyright (C) 2008 New Zealand Digital Library, http://www.nzdl.org
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20package org.greenstone.gsdl3;
21
22import java.io.File;
23import java.io.IOException;
24import java.io.PrintWriter;
25import java.io.Serializable;
26import java.lang.reflect.Type;
27import java.util.ArrayList;
28import java.util.Arrays;
29import java.util.Enumeration;
30import java.util.HashMap;
31import java.util.Hashtable;
32import java.util.Iterator;
33import java.util.List;
34import java.util.Map;
35
36import javax.servlet.ServletConfig;
37import javax.servlet.ServletContext;
38import javax.servlet.ServletException;
39import javax.servlet.http.Cookie;
40import javax.servlet.http.HttpServletRequest;
41import javax.servlet.http.HttpServletResponse;
42import javax.servlet.http.HttpSession;
43import javax.servlet.http.HttpSessionBindingEvent;
44import javax.servlet.http.HttpSessionBindingListener;
45
46import org.apache.commons.fileupload.FileItem;
47import org.apache.commons.fileupload.disk.DiskFileItemFactory;
48import org.apache.commons.fileupload.servlet.ServletFileUpload;
49import org.apache.commons.lang3.StringUtils;
50import org.apache.log4j.Logger;
51import org.greenstone.gsdl3.action.PageAction;
52import org.greenstone.gsdl3.comms.Communicator;
53import org.greenstone.gsdl3.comms.SOAPCommunicator;
54import org.greenstone.gsdl3.core.DefaultReceptionist;
55import org.greenstone.gsdl3.core.MessageRouter;
56import org.greenstone.gsdl3.core.Receptionist;
57import org.greenstone.gsdl3.service.Authentication;
58import org.greenstone.gsdl3.util.Dictionary;
59import org.greenstone.gsdl3.util.GSConstants;
60import org.greenstone.gsdl3.util.GSParams;
61import org.greenstone.gsdl3.util.GSXML;
62import org.greenstone.gsdl3.util.UserContext;
63import org.greenstone.gsdl3.util.XMLConverter;
64import org.greenstone.util.GlobalProperties;
65import org.json.JSONObject;
66import org.w3c.dom.Document;
67import org.w3c.dom.Element;
68import org.w3c.dom.Node;
69import org.w3c.dom.NodeList;
70
71import com.google.gson.Gson;
72import com.google.gson.reflect.TypeToken;
73
74/**
75 * a servlet to serve the greenstone library - we are using servlets instead of
76 * cgi
77 * the init method is called only once - the first time the servlet classes
78 * are loaded. Each time a request comes in to the servlet, the session() method
79 * is called in a new thread (calls doGet/doPut etc) takes the a=p&p=home type
80 * args and builds a simple request to send to its receptionist, which returns a
81 * result in html, cos output=html is set in the request
82 *
83 * 18/Jul/07 xiao modify to make the cached parameters collection-specific. Most
84 * of the work is done in doGet(), except adding an inner class
85 * UserSessionCache.
86 *
87 * @see Receptionist
88 */
89public class LibraryServlet extends BaseGreenstoneServlet
90{
91 protected static final String LOGIN_MESSAGE_PARAM = "loginMessage";
92 protected static final String REDIRECT_URL_PARAM = "redirectURL";
93 /** the receptionist to send messages to */
94 protected Receptionist recept = null;
95
96 /** We record the library name for later */
97 protected String library_name = null;
98 /** Whether or not client-side XSLT support should be exposed */
99 protected boolean supports_client_xslt = false;
100
101 /**
102 * the default language - is specified by setting a servlet param, otherwise
103 * DEFAULT_LANG is used
104 */
105 protected String default_lang = null;
106 /**
107 * The default default - used if a default lang is not specified in the
108 * servlet params
109 */
110 protected final String DEFAULT_LANG = "en";
111
112 /**
113 * the cgi stuff - the Receptionist can add new args to this
114 *
115 * its used by the servlet to determine what args to save,
116 * and to set default values
117 */
118 protected GSParams gs_params = null;
119
120 /**
121 * a hash that contains all the active session IDs mapped to the cached
122 * items It is updated whenever the whole site or a particular collection is
123 * reconfigured using the command a=s&sa=c or a=s&sa=c&c=xxx It is in the
124 * form: sid -> (UserSessionCache object)
125 */
126 protected Hashtable<String, UserSessionCache> session_ids_table = new Hashtable<String, UserSessionCache>();
127
128 /**
129 * the maximum interval that the cached info remains in session_ids_table
130 * (in seconds) This is set in web.xml
131 */
132 protected int session_expiration = 1800;
133
134 static Logger logger = Logger.getLogger(org.greenstone.gsdl3.LibraryServlet.class.getName());
135
136 /** initialise the servlet */
137 public void init(ServletConfig config) throws ServletException
138 {
139 // always call super.init;
140 super.init(config);
141 // disable preferences - does this work anyway??
142 //System.setProperty("java.util.prefs.PreferencesFactory", "org.greenstone.gsdl3.util.DisabledPreferencesFactory");
143
144 // Look through all the init params
145 // these are the ones we are expecting
146 String interface_name = null;
147 String useXslt = null;
148 String sess_expire = null;
149 String site_name = null;
150 String recept_name = null;
151
152 // may have these instead of site_name
153 String remote_site_name = null;
154 String remote_site_type = null;
155 String remote_site_address = null;
156
157 // the stored config params
158 HashMap<String, Object> config_params = new HashMap<String, Object>();
159 Enumeration<String> params = config.getInitParameterNames();
160 while (params.hasMoreElements()) {
161 String name = params.nextElement();
162 String value = config.getInitParameter(name);
163 if (name.equals(GSConstants.LIBRARY_NAME)) {
164 library_name = value;
165 } else if (name.equals(GSConstants.INTERFACE_NAME)) {
166 interface_name = value;
167 } else if (name.equals(GSConstants.USE_CLIENT_SIDE_XSLT)) {
168 useXslt = value;
169 } else if (name.equals(GSConstants.DEFAULT_LANG)) {
170 this.default_lang = value;
171 } else if (name.equals(GSXML.SESSION_EXPIRATION)) {
172 sess_expire = value;
173 } else if (name.equals(GSConstants.SITE_NAME)) {
174 site_name = value;
175 } else if (name.equals(GSConstants.REMOTE_SITE_NAME)) {
176 remote_site_name = value;
177 } else if (name.equals(GSConstants.REMOTE_SITE_TYPE)) {
178 remote_site_type = value;
179 } else if (name.equals(GSConstants.REMOTE_SITE_ADDRESS)) {
180 remote_site_address = value;
181 } else if (name.equals(GSConstants.RECEPTIONIST_CLASS)) {
182 recept_name = value;
183 } else {
184 // If there is a param we are not expecting, just add to the list. That way users can add new servlet params which will go through to the XML source without modifying code.
185 config_params.put(name, value);
186 }
187 }
188 if (library_name == null || interface_name == null) {
189
190 // must have these
191 System.err.println("initialisation parameters not all set!");
192 System.err.println(" you must have libraryname and interfacename");
193 System.exit(1);
194 }
195
196 // If we don't have a site, then we must be using a Communicator, and all the remote params must be set
197 if (site_name == null && (remote_site_name == null || remote_site_type == null || remote_site_address == null)) {
198
199 System.err.println("initialisation paramters not all set!");
200 System.err.println("if site_name is not set, then you must have remote_site_name, remote_site_type and remote_site_address set");
201 System.exit(1);
202 }
203
204 supports_client_xslt = useXslt != null && useXslt.equals("true");
205 if (sess_expire != null && !sess_expire.equals(""))
206 {
207 this.session_expiration = Integer.parseInt(sess_expire);
208 }
209 if (this.default_lang == null)
210 {
211 // choose english
212 this.default_lang = DEFAULT_LANG;
213 }
214
215 config_params.put(GSConstants.LIBRARY_NAME, library_name);
216 config_params.put(GSConstants.INTERFACE_NAME, interface_name);
217 config_params.put(GSConstants.USE_CLIENT_SIDE_XSLT, supports_client_xslt);
218
219 if (site_name != null)
220 {
221 config_params.put(GSConstants.SITE_NAME, site_name);
222 }
223
224 // The following is now superceeded by the for-loop below
225 //String greenstone_context = GlobalProperties.getGSDL3WebAddress();
226 //config_params.put(GSConstants.SERVLET_CONTEXT, greenstone_context);
227
228 String[] servlet_url_parts = { GSConstants.SERVLET_PROTOCOL, GSConstants.SERVLET_DOMAIN, GSConstants.SERVLET_OPT_PORT, GSConstants.SERVLET_CONTEXT };
229 String[] servlet_url_props = { "server.protocol", "tomcat.server", "tomcat.port", "tomcat.context" };
230 String[] revproxy_url_props = { "revproxy.protocol", "revproxy.domain", "revproxy.opt_port", "revproxy.context" };
231
232 boolean explicit_revproxy_domain = false;
233
234 for (int i=0; i<servlet_url_parts.length; i++) {
235 String servlet_part_name = servlet_url_parts[i];
236
237 String revproxy_prop_name = revproxy_url_props[i];
238 String servlet_prop_name = servlet_url_props[i];
239
240 String revproxy_prop_val = GlobalProperties.getProperty(revproxy_prop_name);
241 String servlet_prop_val = GlobalProperties.getProperty(servlet_prop_name);
242
243 String unassigned_prop_val = "${" + revproxy_prop_name + "}";
244
245 if (revproxy_prop_val.equals(unassigned_prop_val)) {
246 // default to SERVLET version of val
247 // with a special exception if the port
248 if (revproxy_prop_name.equals("revproxy.opt_port")) {
249 if (explicit_revproxy_domain) {
250 // explicitly set revproxy.domain, but provided no revproxy.opt_port
251 // => set to be the empty string
252 config_params.put(servlet_part_name, "");
253 }
254 else {
255 // No explicity revproxy domain => default back to the servlet provied port
256 config_params.put(servlet_part_name, servlet_prop_val);
257 }
258 }
259 else {
260 // Regular case => default back to the value provided for 'servlet'
261 config_params.put(servlet_part_name, servlet_prop_val);
262 }
263 }
264 else {
265 // Have a non-trival revproxy part val => use that (public facing) value to embed into web browser pages
266 config_params.put(servlet_part_name, revproxy_prop_val);
267
268 if (revproxy_prop_name.equals("revproxy.domain")) {
269 explicit_revproxy_domain = true;
270 }
271 }
272 }
273
274
275 // the receptionist -the servlet will talk to this
276 if (recept_name == null)
277 {
278 this.recept = new DefaultReceptionist();
279 }
280 else
281 {
282 try
283 {
284 this.recept = (Receptionist) Class.forName("org.greenstone.gsdl3.core." + recept_name).getDeclaredConstructor().newInstance();
285 }
286 catch (Exception e)
287 { // cant use this new one, so use normal one
288 System.err.println("LibraryServlet configure exception when trying to use a new Receptionist " + recept_name + ": " + e.getMessage());
289 e.printStackTrace();
290 this.recept = new DefaultReceptionist();
291 }
292 }
293 this.recept.setConfigParams(config_params);
294
295 // the params arg thingy
296
297 String params_name = (String) config.getInitParameter("params_class");
298 if (params_name == null)
299 {
300 this.gs_params = new GSParams();
301 }
302 else
303 {
304 try
305 {
306 this.gs_params = (GSParams) Class.forName("org.greenstone.gsdl3.util." + params_name).getDeclaredConstructor().newInstance();
307 }
308 catch (Exception e)
309 {
310 System.err.println("LibraryServlet configure exception when trying to use a new params thing " + params_name + ": " + e.getMessage());
311 e.printStackTrace();
312 this.gs_params = new GSParams();
313 }
314 }
315
316 // the receptionist uses a MessageRouter or Communicator to send its requests to. We either create a MessageRouter here for the designated site (if site_name set), or we create a Communicator for a remote site. The is given to the Receptionist, and the servlet never talks to it again directly.
317 if (site_name != null)
318 {
319 String mr_name = (String) config.getInitParameter("messagerouter_class");
320 MessageRouter message_router = null;
321 if (mr_name == null)
322 { // just use the normal MR
323 message_router = new MessageRouter();
324 }
325 else
326 { // try the specified one
327 try
328 {
329 message_router = (MessageRouter) Class.forName("org.greenstone.gsdl3.core." + mr_name).getDeclaredConstructor().newInstance();
330 }
331 catch (Exception e)
332 { // cant use this new one, so use normal one
333 System.err.println("LibraryServlet configure exception when trying to use a new MessageRouter " + mr_name + ": " + e.getMessage());
334 e.printStackTrace();
335 message_router = new MessageRouter();
336 }
337 }
338
339 message_router.setSiteName(site_name);
340 message_router.setLibraryName(library_name);
341 message_router.setParams(this.gs_params);
342 message_router.configure();
343 this.recept.setMessageRouter(message_router);
344 }
345 else
346 {
347 // TODO: what do we do about params here?
348 // talking to a remote site, create a communicator
349 Communicator communicator = null;
350 // we need to create the XML to configure the communicator
351 Document doc = XMLConverter.newDOM();
352 Element site_elem = doc.createElement(GSXML.SITE_ELEM);
353 site_elem.setAttribute(GSXML.TYPE_ATT, remote_site_type);
354 site_elem.setAttribute(GSXML.NAME_ATT, remote_site_name);
355 site_elem.setAttribute(GSXML.ADDRESS_ATT, remote_site_address);
356
357 if (remote_site_type.equals(GSXML.COMM_TYPE_SOAP_JAVA))
358 {
359 communicator = new SOAPCommunicator();
360 }
361 else
362 {
363 System.err.println("LibraryServlet.init Error: invalid Communicator type: " + remote_site_type);
364 System.exit(1);
365 }
366
367 if (!communicator.configure(site_elem))
368 {
369 System.err.println("LibraryServlet.init Error: Couldn't configure communicator");
370 System.exit(1);
371 }
372 this.recept.setMessageRouter(communicator);
373 }
374
375 // pass params to the receptionist
376 this.recept.setParams(this.gs_params);
377 this.recept.configure();
378
379 //Allow the message router and the document to be accessed from anywhere in this servlet context
380 this.getServletContext().setAttribute(library_name+"Router", this.recept.getMessageRouter());
381 }
382
383 private void logUsageInfo(HttpServletRequest request, Map<String, String[]> queryMap)
384 {
385 String usageInfo = "";
386
387 //session-info: get params stored in the session
388 HttpSession session = request.getSession(true);
389 Enumeration attributeNames = session.getAttributeNames();
390 while (attributeNames.hasMoreElements())
391 {
392 String name = (String) attributeNames.nextElement();
393 usageInfo += name + "=" + session.getAttribute(name) + " ";
394 }
395
396 String queryParamStr = "";
397 if (queryMap != null)
398 {
399 Iterator<String> queryIter = queryMap.keySet().iterator();
400 while (queryIter.hasNext()) {
401 String q = queryIter.next();
402 String[] vals = queryMap.get(q);
403 queryParamStr += q +"="+StringUtils.join(vals, ",")+" ";
404 }
405 }
406 //logged info = general-info + session-info
407 usageInfo = request.getServletPath() + " " + //serlvet
408 "[" + queryParamStr.trim()+"]"+" " + // query map params
409 "session:[" + usageInfo.trim() + "]" + " " + // params stored in a session
410 request.getRemoteAddr() + " " + //remote address
411 request.getRequestedSessionId() + " " + //session id
412 request.getHeader("user-agent") + " "; //the remote brower info
413
414 logger.info(usageInfo);
415
416 }
417
418 public class UserSessionCache implements HttpSessionBindingListener
419 {
420 String session_id = "";
421
422 /**
423 * a hash that maps the session ID to a hashtable that maps the
424 * coll_name to its parameters coll_name -> Hashtable (param_name ->
425 * param_value)
426 */
427 protected Hashtable<String, Hashtable<String, String>> coll_name_params_table = null;
428
429 public UserSessionCache(String id, Hashtable<String, Hashtable<String, String>> table)
430 {
431 session_id = id;
432 coll_name_params_table = (table == null) ? new Hashtable() : table;
433 }
434
435 protected void cleanupCache(String coll_name)
436 {
437 if (coll_name_params_table.containsKey(coll_name))
438 {
439 coll_name_params_table.remove(coll_name);
440 }
441 }
442
443 protected Hashtable<String, Hashtable<String, String>> getParamsTable()
444 {
445 return coll_name_params_table;
446 }
447
448 public void valueBound(HttpSessionBindingEvent event)
449 {
450 // Do nothing
451 }
452
453 // if session cache id removed from session, this triggers this valueUnbound method on the value=this object
454 public void valueUnbound(HttpSessionBindingEvent event)
455 {
456 if (session_ids_table.containsKey(session_id))
457 {
458 session_ids_table.remove(session_id);
459 }
460 }
461
462 public int tableSize()
463 {
464 return (coll_name_params_table == null) ? 0 : coll_name_params_table.size();
465 }
466 }
467
468 public void destroy()
469 {
470 recept.cleanUp();
471 }
472
473 public void doGetOrPost(HttpServletRequest request, HttpServletResponse response, Map<String, String[]> queryMap) throws ServletException, IOException
474 {
475 logUsageInfo(request, queryMap);
476
477 if (processRedirectRequest(request, response, queryMap)) {
478 // this method will do the redirect if needed and return true if it has
479 // done so.
480 // e.g. el=direct/framed&rl=0&href=http://newurl.com
481 return;
482 }
483
484 // Nested Diagnostic Configurator to identify the client for
485 HttpSession session = request.getSession(true);
486 session.setMaxInactiveInterval(session_expiration);
487 String uid = session.getId();
488 request.setCharacterEncoding("UTF-8");
489 response.setContentType("text/html;charset=UTF-8");
490 PrintWriter out = response.getWriter();
491
492 String lang = getFirstParam(GSParams.LANGUAGE, queryMap);
493 if (lang == null || lang.equals(""))
494 {
495 // try the session cached lang
496 lang = (String) session.getAttribute(GSParams.LANGUAGE);
497 if (lang == null || lang.equals(""))
498 {
499 // still not set, use the default
500 lang = this.default_lang;
501 }
502 }
503
504 // set the lang in the session
505 session.setAttribute(GSParams.LANGUAGE, lang);
506
507 String output = getFirstParam(GSParams.OUTPUT, queryMap);
508 if (output == null || output.equals(""))
509 {
510 output = "html"; // uses html by default
511 }
512
513 String requestedURL = request.getRequestURL().toString();
514 String baseURL = "";
515 //logger.error("requested URL = "+requestedURL);
516 if (requestedURL.indexOf(library_name) != -1)
517 {
518 // we need to work out the baseURL and set it
519 baseURL = requestedURL;
520 int protocol_boundary_pos = baseURL.indexOf("://");
521 if (protocol_boundary_pos>=1) {
522 baseURL = baseURL.substring(protocol_boundary_pos+1); // change things like http:// or https:// to //
523 }
524 // The baseURL is everything up to the library_name.
525 // eg https://community.greenstone.org/greenstone3/library/collection/twso/page/about
526 // baseURL is //community.greenstone.org/greenstone3
527 // Issues: the library_name may occur in more than one place - eg if its part of the domain name mylibrary.heritage.nz/greenstone3/library
528 // or if a collection name is the same as the library name eg localhost:8585/greenstone3/twso/collection/twso/page/about.
529 // So can't just use teh first or last occurrence. Look for /library-name, and check its not the first position (eg //library.nzdl.org/greenstone3/library)
530 int library_name_index = baseURL.indexOf("/"+library_name);
531 if (library_name_index == 1) {
532 // we have library name at the start of the url - need to use the second one
533 library_name_index = baseURL.indexOf("/"+library_name, 2);
534 }
535 baseURL = baseURL.substring(0, library_name_index+1);
536 //logger.error("new base url = "+baseURL);
537
538 }
539
540 String fullURL;
541 if (request.getQueryString() != null)
542 {
543 fullURL = requestedURL + "?" + request.getQueryString();
544 }
545 else
546 {
547 fullURL = requestedURL;
548 }
549
550 UserContext userContext = new UserContext();
551 userContext.setLanguage(lang);
552 userContext.setUserID(uid);
553
554 // If GoogleSignin is operational (due to googlesignin_client_id specified in servlet.xml)
555 // AND a Google Client ID Token is provided via googlesignin_id_token then
556 // initiate authentication via Google Signin, which is then tied into Greenstone3 through
557 // the customized Realm written for Greenstone3.
558 //
559 // This customized Realm, GoogleSigninJDBCRealm, works by overriding 'authenticate()'
560 // in Realm.
561
562 // 1. If the username 'googlesign' is provided, then the credentials
563 // passed in needs to be a valid verifyable Google Client Id Token.
564 // 1a. From this verified token, the email address that is registered with
565 // Google for that user account is then used to find a match in the
566 // Greenstone3 JDBC-specified Username table.
567 // 1b. Assuming that an email address match is found, then the customized Realm
568 // completes the authentication process by making the found username, the
569 // one that is autheticated.
570 // 2. If the username is anything but 'googlesignin' then the authentication process
571 // continues as with the regular JDBCRealm process
572
573 //String googlesignin_id_token = getFirstParam("googlesignin_id_token",queryMap);
574 //if ((googlesignin_id_token != null) && (!googlesignin_id_token.equals(""))) {
575
576 String googleidentity_signin = getFirstParam(GSParams.GOOGLE_SIGNIN,queryMap);
577 if ((googleidentity_signin != null) && (googleidentity_signin.equals("1"))) {
578 queryMap.put(GSParams.USERNAME, new String[] { GoogleSigninJDBCRealm.GOOGLESIGNIN_USERNAME_BRIDGE });
579
580 String googlesignin_credential = getFirstParam("credential",queryMap);
581 queryMap.put(GSParams.PASSWORD, new String[] { googlesignin_credential });
582 // logger.info("**** googlesignin_credenital (aka id_token) = '" + googlesignin_credential +"'");
583 }
584
585
586 if (!processLoginChanges(request, userContext, out, baseURL, queryMap, lang, output)) {
587 // any invalid login attempt will redirect to a new login page and return false
588 return;
589 }
590
591
592 if (request.getAuthType() != null)
593 {
594 // sets username, groups etc into usercontext
595 updateUserContextWithAuthenticatedInfo(request, userContext);
596 }
597
598 // If server output, force a switch to traditional interface
599 //output = (output.equals("server")) ? "html" : output;
600
601 // Force change the output mode if client-side XSLT is supported - server vs. client
602 // BUT only if the library allows client-side transforms
603 if (supports_client_xslt)
604 {
605 // MUST be done before the xml_message is built
606 Cookie[] cookies = request.getCookies();
607 Cookie xsltCookie = null;
608
609 // The client has cookies enabled and a value set - use it!
610 if (cookies != null)
611 {
612 for (Cookie c : cookies)
613 {
614 if (c.getName().equals("supportsXSLT"))
615 {
616 xsltCookie = c;
617 break;
618 }
619 }
620 output = (xsltCookie != null && xsltCookie.getValue().equals("true") && output.equals("html")) ? "xsltclient" : output;
621 }
622 }
623
624
625 String action = getFirstParam(GSParams.ACTION, queryMap);
626 String subaction = getFirstParam(GSParams.SUBACTION, queryMap);
627 String collection = getFirstParam(GSParams.COLLECTION, queryMap);
628 String document = getFirstParam(GSParams.DOCUMENT, queryMap);
629 String service = getFirstParam(GSParams.SERVICE, queryMap);
630 String specified_cache_key = getFirstParam(GSParams.CACHE_KEY, queryMap);
631
632 if (collection != null && !collection.equals("")) {
633 //is this user allowed to access this collection/document?
634 if (!runCollectionSecurityCheck(request, userContext, out, baseURL, collection, document, lang, output)) {
635 return;
636 }
637 }
638 // We clean up the cache session_ids_table if system
639 // commands are issued, and also don't need to do caching for these request requests
640 boolean should_cache = true;
641 if (action != null && action.equals(GSParams.SYSTEM_ACTION)
642 && !subaction.equals(GSXML.SYSTEM_TYPE_PING)
643 && !subaction.equals(GSXML.SYSTEM_TYPE_AUTHENTICATED_PING)) // don't 'clean' anything on a mere ping
644 {
645 should_cache = false;
646
647 // System commands now need to be logged in/have permissions
648 // like configuring, activation and deactivation
649 if(!configureSecurityCheck(request, userContext, out, baseURL, lang, output, queryMap)) {
650 return;
651 }
652
653 // we may want to remove all collection cache info, or just a specific collection
654 boolean clean_all = true;
655 String clean_collection = null;
656 // system commands are to activate/deactivate stuff
657 // collection param is in the sc parameter.
658 // don't like the fact that it is hard coded here
659 String coll = getFirstParam(GSParams.SYSTEM_CLUSTER, queryMap);
660 if (coll != null && !coll.equals(""))
661 {
662 clean_all = false;
663 clean_collection = coll;
664 }
665 else
666 {
667 // check other system types
668 if (subaction.equals("a") || subaction.equals("d"))
669 {
670 String module_name = getFirstParam("sn", queryMap);
671 if (module_name != null && !module_name.equals(""))
672 {
673 clean_all = false;
674 clean_collection = module_name;
675 }
676 }
677 }
678 if (clean_all)
679 {
680 // TODO
681 session_ids_table = new Hashtable<String, UserSessionCache>();
682 session.removeAttribute(GSParams.USER_SESSION_CACHE); // triggers valueUnbound(), which removes the session id from the session_ids_table
683 }
684 else
685 {
686 // just clean up info for clean_collection
687 ArrayList<UserSessionCache> cache_list = new ArrayList<UserSessionCache>(session_ids_table.values());
688 for (int i = 0; i < cache_list.size(); i++)
689 {
690 UserSessionCache cache = cache_list.get(i);
691 cache.cleanupCache(clean_collection);
692 }
693
694 }
695 }
696
697 // cache_key is the collection name, or service name
698 String cache_key = specified_cache_key;
699 if (cache_key == null || cache_key.equals(""))
700 {
701 cache_key = collection;
702 }
703 if (cache_key == null || cache_key.equals(""))
704 {
705 cache_key = service;
706 }
707
708 //clear the collection-specific cache in the session, since we have no way to know whether this page is
709 //about the same collection as the last page or not.
710 Enumeration attributeNames = session.getAttributeNames();
711 while (attributeNames.hasMoreElements())
712 {
713 String name = (String) attributeNames.nextElement();
714 if (!name.equals(GSParams.USER_SESSION_CACHE) && !name.equals(GSParams.LANGUAGE) && !name.equals(GSXML.USER_ID_ATT) && !this.gs_params.isGlobal(name))
715 {
716 session.removeAttribute(name);
717 }
718 }
719
720 UserSessionCache session_cache = null;
721 Hashtable<String, Hashtable<String, String>> param_table = null;
722 Hashtable<String, String> table = null;
723 boolean new_table = false;
724 String sid = session.getId();
725 boolean new_session = false;
726 if (!session_ids_table.containsKey(sid)) {
727 new_session = true;
728 }
729 if (should_cache == true && cache_key != null && !cache_key.equals(""))
730 {
731 if (!new_session)
732 {
733 session_cache = session_ids_table.get(sid);
734 param_table = session_cache.getParamsTable();
735 if (param_table.containsKey(cache_key))
736 {
737 table = param_table.get(cache_key);
738 }
739 else
740 {
741 table = new Hashtable<String, String>();
742 param_table.put(cache_key, table);
743 new_table = true;
744 }
745 }
746 else
747 {
748
749 param_table = new Hashtable<String, Hashtable<String, String>>();
750 table = new Hashtable<String, String>();
751 param_table.put(cache_key, table);
752 session_cache = new UserSessionCache(sid, param_table);
753 session_ids_table.put(sid, session_cache);
754 session.setAttribute(GSParams.USER_SESSION_CACHE, session_cache);
755 new_table = true;
756 }
757
758 }
759
760 // here we add in default params values if need be - if we have a new session, or if we have a new table
761 // in an existing session
762 // don't override any existing values
763 if (new_session || new_table) {
764
765 Iterator i = this.gs_params.getParamsWithDefaults().iterator();
766 while (i.hasNext()) {
767 String p = (String)i.next();
768 String v = this.gs_params.getParamDefault(p);
769 if (this.gs_params.isGlobal(p)) {
770 //need to add in to session unless its already there
771 if (session.getAttribute(p) == null) {
772 session.setAttribute(p, v);
773 }
774 } else {
775 // add to table unless its already there
776 if (new_table && !table.containsKey(p)) {
777 table.put(p,v);
778 }
779 }
780
781 }
782 }
783
784 // the request to the receptionist
785 Document msg_doc = XMLConverter.newDOM();
786 Element xml_message = msg_doc.createElement(GSXML.MESSAGE_ELEM);
787 Element xml_request = GSXML.createBasicRequest(msg_doc, GSXML.REQUEST_TYPE_PAGE, "", userContext);
788 xml_request.setAttribute(GSXML.OUTPUT_ATT, output);
789
790 xml_message.appendChild(xml_request);
791
792 if (request.getAuthType() != null) {
793 // lots of classes are using the <userInformation> element. But all its info is now in userContext, so maybe we can get rid of this one day?
794 appendUserInformation(xml_request, userContext);
795 }
796
797 if (action == null || action.equals(""))
798 {
799 // use page action as the default
800 xml_request.setAttribute(GSXML.ACTION_ATT, "p");
801
802 }
803 else
804 {
805 xml_request.setAttribute(GSXML.ACTION_ATT, action);
806 if (subaction != null)
807 {
808 xml_request.setAttribute(GSXML.SUBACTION_ATT, subaction);
809 }
810 }
811
812 // create the param list for the greenstone request - includes
813 // the params from the current request and any others from the saved session
814 Element xml_param_list = msg_doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
815 xml_request.appendChild(xml_param_list);
816
817 if (queryMap.containsKey("s1.collection") || queryMap.containsKey("s1.group")){
818 table.remove("s1.collection");
819 table.remove("s1.group");
820 }
821 for (String name : queryMap.keySet())
822 {
823 if (!name.equals(GSParams.ACTION) && !name.equals(GSParams.SUBACTION) && !name.equals(GSParams.LANGUAGE) && !name.equals(GSParams.OUTPUT))
824 {// we have already dealt with these
825
826 String value = "";
827 String[] values = queryMap.get(name);
828 value = values[0];
829 if (values.length > 1)
830 {
831 for (int i = 1; i < values.length; i++)
832 {
833 value += "," + values[i];
834 }
835 }
836 // if we need to save the value, then add it to the session/cache table.
837 // otherwise add directly to the paramList
838 if (this.gs_params.shouldSave(name)) {
839 if (this.gs_params.isGlobal(name) || table == null) {
840 session.setAttribute(name, value);
841 } else {
842 table.put(name, value);
843 }
844 }
845 else
846 {
847 // If logging out as the very next step after logging in, then
848 // the 'password' param was found to be null
849 //
850 // This then causes GSXML.xsmSafe(value) to throw an exception
851 // For now, the coding decision is to test for null, and skip
852 // adding the param if it is null
853 //
854 // Could be that it is more meaningful to store the values as
855 // the empty string. In which case updating GSXML.xsmlSafe to
856 // test for null and treat it as an empty string would be
857 // a better way to go
858
859 if (value != null) {
860 Element param = msg_doc.createElement(GSXML.PARAM_ELEM);
861 param.setAttribute(GSXML.NAME_ATT, name);
862 param.setAttribute(GSXML.VALUE_ATT, GSXML.xmlSafe(value));
863 if (this.gs_params.isSensitive(name)) {
864 param.setAttribute(GSXML.SENSITIVE_ATT, "true");
865 }
866 xml_param_list.appendChild(param);
867 }
868
869 }
870 }
871 }
872 //put everything in the table into the session
873 if (table != null)
874 {
875 Enumeration<String> keys = table.keys();
876 while (keys.hasMoreElements())
877 {
878 String name = keys.nextElement();
879 session.setAttribute(name, table.get(name));
880 }
881 }
882
883 // put in all the params from the session cache
884 Enumeration params = session.getAttributeNames();
885 while (params.hasMoreElements())
886 {
887 String name = (String) params.nextElement();
888 if (!name.equals(GSParams.USER_SESSION_CACHE) && !name.equals(GSParams.LANGUAGE) && !name.equals(GSXML.USER_ID_ATT))
889 {
890
891 // lang and uid are stored but we dont want it in the param list cos its already in the request
892 Element param = msg_doc.createElement(GSXML.PARAM_ELEM);
893 param.setAttribute(GSXML.NAME_ATT, name);
894 String value = GSXML.xmlSafe((String) session.getAttribute(name));
895
896 // ugly hack to undo : escaping
897 value = StringUtils.replace(value, "%3A", "\\:");
898 param.setAttribute(GSXML.VALUE_ATT, value);
899 xml_param_list.appendChild(param);
900 }
901 }
902
903
904 if (output.equals("json"))
905 {
906 response.setContentType("application/json");
907 }
908 else if (!output.equals("html") && !output.equals("server") && !output.equals("xsltclient"))
909 {
910 response.setContentType("text/xml"); // for now use text
911 }
912
913 //Add custom HTTP headers if requested
914 String httpHeadersParam = getFirstParam(GSParams.HTTP_HEADER_FIELDS, queryMap);
915 if (httpHeadersParam != null && httpHeadersParam.length() > 0)
916 {
917 Gson gson = new Gson();
918 Type type = new TypeToken<List<Map<String, String>>>()
919 {
920 }.getType();
921 List<Map<String, String>> httpHeaders = gson.fromJson(httpHeadersParam, type);
922 if (httpHeaders != null && httpHeaders.size() > 0)
923 {
924
925 for (int j = 0; j < httpHeaders.size(); j++)
926 {
927 Map nameValueMap = httpHeaders.get(j);
928 String name = (String) nameValueMap.get("name");
929 String value = (String) nameValueMap.get("value");
930
931 if (name != null && value != null)
932 {
933 response.setHeader(name, value);
934 }
935 }
936 }
937 }
938
939
940 xml_request.setAttribute("remoteAddress", request.getRemoteAddr());
941 xml_request.setAttribute("fullURL", fullURL.replace("&", "&amp;"));
942 xml_request.setAttribute("baseURL", baseURL);
943
944 // logger.error("about to process this message");
945 // logger.error(XMLConverter.getPrettyString(xml_message));
946 Node xml_result = this.recept.process(xml_message);
947 encodeURLs(xml_result, response);
948
949 String xml_string = XMLConverter.getPrettyString(xml_result);
950
951 if (output.equals("json"))
952 {
953 try
954 {
955 JSONObject json_obj = org.json.XML.toJSONObject(xml_string);
956
957 out.println(json_obj.toString());
958 }
959 catch (Exception e)
960 {
961 e.printStackTrace();
962 out.println("Error: failed to convert output XML to JSON format");
963 }
964 }
965 else
966 {
967 out.println(xml_string);
968 }
969
970 displaySize(session_ids_table);
971 }
972
973 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
974 {
975 doGetOrPost(request, response, request.getParameterMap());
976 } //end of doGet(HttpServletRequest, HttpServletResponse)
977
978 private boolean processRedirectRequest(HttpServletRequest request, HttpServletResponse response, Map<String, String[]> queryMap) throws IOException
979 {
980 if (queryMap != null)
981 {
982 Iterator<String> queryIter = queryMap.keySet().iterator();
983 boolean redirect = false;
984 String href = null;
985 String rl = null;
986 String el = null;
987 String collection = null;
988
989 while (queryIter.hasNext())
990 {
991 String q = queryIter.next();
992 if (q.equals(GSParams.COLLECTION))
993 {
994 collection = queryMap.get(q)[0];
995 }
996 else if (q.equals(GSParams.EXTERNAL_LINK_TYPE))
997 {
998 el = queryMap.get(q)[0];
999 }
1000 else if (q.equals(GSParams.HREF))
1001 {
1002 href = queryMap.get(q)[0];
1003 href = StringUtils.replace(href, "%2f", "/");
1004 href = StringUtils.replace(href, "%7e", "~");
1005 href = StringUtils.replace(href, "%3f", "?");
1006 href = StringUtils.replace(href, "%3A", "\\:");
1007 }
1008 else if (q.equals(GSParams.RELATIVE_LINK))
1009 {
1010 rl = queryMap.get(q)[0];
1011 }
1012 }
1013
1014 //if query_string contains "el=direct", an href is specified, and its not a relative link, then the web page will be redirected to the external URl, otherwise a greenstone page with an external URL will be displayed
1015 //"rl=0" this is an external link
1016 //"rl=1" this is an internal link
1017 if ((href != null) && (rl.equals("0")))
1018 {// This is an external link,
1019
1020 if (el.equals("framed"))
1021 {
1022 // framed means we are linking to an external page inside our greenstone page
1023 HttpSession session = request.getSession();
1024 ServletContext context = session.getServletContext();
1025 String new_url = context.getContextPath()+"/"+ library_name+"?a=p&sa=html&url="+href;
1026 if (collection != null && !collection.equals("")) {
1027 new_url += "&c="+collection;
1028 }
1029 response.sendRedirect(new_url);
1030 }
1031 else
1032 {
1033 // el = '' or direct
1034 //the web page is re-directed to the external URL (&el=&rl=0&href="http://...")
1035 response.sendRedirect(href);
1036 }
1037 return true;
1038 }
1039 }
1040 return false;
1041 }
1042
1043 private void generateLoginPage(String redirect_url_string, String error_message, UserContext userContext, PrintWriter out, String baseURL, String output) {
1044
1045 Document loginPageDoc = XMLConverter.newDOM();
1046 Element loginPageMessage = loginPageDoc.createElement(GSXML.MESSAGE_ELEM);
1047 Element loginPageRequest = GSXML.createBasicRequest(loginPageDoc, GSXML.REQUEST_TYPE_PAGE, "", userContext);
1048 loginPageRequest.setAttribute(GSXML.ACTION_ATT, "p");
1049 loginPageRequest.setAttribute(GSXML.SUBACTION_ATT, "login");
1050 loginPageRequest.setAttribute(GSXML.OUTPUT_ATT, output);
1051 loginPageRequest.setAttribute(GSXML.BASE_URL, baseURL);
1052
1053 loginPageMessage.appendChild(loginPageRequest);
1054
1055 Element paramList = loginPageDoc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
1056 loginPageRequest.appendChild(paramList);
1057
1058 Element messageParam = loginPageDoc.createElement(GSXML.PARAM_ELEM);
1059 messageParam.setAttribute(GSXML.NAME_ATT, LOGIN_MESSAGE_PARAM);
1060 messageParam.setAttribute(GSXML.VALUE_ATT, error_message);
1061 paramList.appendChild(messageParam);
1062
1063 Element urlParam = loginPageDoc.createElement(GSXML.PARAM_ELEM);
1064 urlParam.setAttribute(GSXML.NAME_ATT, REDIRECT_URL_PARAM);
1065
1066 if (redirect_url_string.matches("^[a-z]+://.*$")) {
1067 int protocol_boundary_pos = redirect_url_string.indexOf("://");
1068 redirect_url_string = redirect_url_string.substring(protocol_boundary_pos+1); // change things like http:// or https:// to //
1069 }
1070
1071 urlParam.setAttribute(GSXML.VALUE_ATT, redirect_url_string);
1072 paramList.appendChild(urlParam);
1073
1074 Node loginPageResponse = this.recept.process(loginPageMessage);
1075 out.println(XMLConverter.getPrettyString(loginPageResponse));
1076 }
1077
1078 private boolean processLoginChanges(HttpServletRequest request, UserContext userContext, PrintWriter out, String baseURL, Map<String, String[]> queryMap, String lang, String output) throws ServletException {
1079
1080 //logger.info("Start of LibraryServlet::processLoginChanges()");
1081
1082 //Check if we need to login or logout
1083 String username = getFirstParam(GSParams.USERNAME, queryMap);
1084 String password = getFirstParam(GSParams.PASSWORD, queryMap);
1085 String logout = getFirstParam(GSParams.LOGOUT, queryMap);
1086
1087 if (logout != null)
1088 {
1089 //logger.info("LibraryServlet::processLoginChanges() logging out (logout cgi param non-null)");
1090 request.logout();
1091 }
1092
1093 if (username != null && password != null)
1094 {
1095 //logger.info("LibrarySerlvet::processLoginChagnes(): username and password not null");
1096
1097 //We are changing to another user, so log out first
1098 if (request.getAuthType() != null)
1099 {
1100 //logger.info("Logging out (preparing to change to another user) ");
1101 request.logout();
1102 }
1103
1104 //This try/catch block catches when the login request fails (e.g. The user enters an incorrect password).
1105 try
1106 {
1107 //Try a global login first, and then go on to site-login if throws exception
1108 if (!username.equals(GoogleSigninJDBCRealm.GOOGLESIGNIN_USERNAME_BRIDGE)) {
1109 // Hashing password used by a direct to Greenstone3 authentication text,
1110 // but not when a Google Signin
1111 password = Authentication.hashPassword(password);
1112 }
1113
1114 request.login(username, password);
1115 //logger.info("Global Login successful");
1116 }
1117 catch (Exception ex)
1118 {
1119 try
1120 {
1121 //If the global login fails then try a site-level login
1122 String siteName = (String) this.recept.getConfigParams().get(GSConstants.SITE_NAME);
1123 String siteUserName = siteName + "-" + username;
1124 //logger.info("Global login unsuccessful, trying site-level login with: " + siteUserName);
1125 request.login(siteUserName, password);
1126 //logger.info("Site-wide login successful");
1127 }
1128 catch (Exception exc)
1129 {
1130 //logger.info("Site-wide login unsuccessful => generating login page");
1131
1132 //The user entered in either the wrong username or the wrong password
1133
1134 HttpSession session = request.getSession();
1135 ServletContext context = session.getServletContext();
1136 String redirect_url_string = request.getRequestURI().replaceFirst(context.getContextPath() + "/", "");
1137
1138 if ((request.getQueryString() != null) && (request.getQueryString().length() > 0))
1139 {
1140 redirect_url_string += "?" + request.getQueryString().replace("&", "&amp;");
1141 }
1142
1143 generateLoginPage(redirect_url_string, getTextString("auth.error.un_or_pw_err", lang), userContext, out, baseURL, output);
1144 return false;
1145 }
1146 }
1147 }
1148 return true;
1149
1150
1151 }
1152
1153 private void updateUserContextWithAuthenticatedInfo(HttpServletRequest request, UserContext userContext)
1154 {
1155 //logger.info("Start of updateUserContextWithAutenticatedInfo");
1156
1157 //Get the username
1158 String user_name = request.getUserPrincipal().getName();
1159 //logger.info(" getUserPrincipal user_name = " + user_name);
1160
1161 userContext.setUsername(user_name);
1162
1163 //Get the groups for the user
1164 Document msg_doc = XMLConverter.newDOM();
1165 Element userInfoMessage = msg_doc.createElement(GSXML.MESSAGE_ELEM);
1166 Element userInfoRequest = GSXML.createBasicRequest(msg_doc, GSXML.REQUEST_TYPE_PROCESS, "GetUserInformation", userContext);
1167 userInfoMessage.appendChild(userInfoRequest);
1168
1169 Element paramList = msg_doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
1170 userInfoRequest.appendChild(paramList);
1171 paramList.appendChild(GSXML.createParameter(msg_doc, GSXML.USERNAME_ATT, user_name));
1172
1173 Element userInfoResponseMessage = (Element) this.recept.process(userInfoMessage);
1174 Element userInfoResponse = (Element) GSXML.getChildByTagName(userInfoResponseMessage, GSXML.RESPONSE_ELEM);
1175 Element respParamList = (Element) GSXML.getChildByTagName(userInfoResponse, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
1176
1177 if (respParamList != null)
1178 {
1179 HashMap<String, Serializable> params = GSXML.extractParams(respParamList, false);
1180 String groups = (String) params.get("groups");
1181 String editEnabled = (String) params.get("editEnabled");
1182 userContext.setGroups(groups.split(","));
1183 userContext.setEditEnabled((editEnabled != null) ? editEnabled : "false");
1184 }
1185 }
1186
1187 private void appendUserInformation(Element xml_request, UserContext userContext)
1188 {
1189 Element userInformation = xml_request.getOwnerDocument().createElement(GSXML.USER_INFORMATION_ELEM);
1190 userInformation.setAttribute(GSXML.USERNAME_ATT, userContext.getUsername());
1191
1192
1193 userInformation.setAttribute(GSXML.GROUPS_ATT, userContext.getGroupsString());
1194 userInformation.setAttribute(GSXML.EDIT_ENABLED_ATT, userContext.getEditEnabled());
1195 xml_request.appendChild(userInformation);
1196 }
1197
1198 /**
1199 * request.getRemoteAddr() returns the IP of the client *or* of a proxy.
1200 * We want the client IP, so we can check if it's truly local or not, since proxies can be
1201 * local, thus masking a client connecting from an external IP.
1202 * The following code is from
1203 * https://www.javaprogramto.com/2020/11/how-to-get-client-ip-address-in-java.html
1204 */
1205 public String getClientIpAddress(HttpServletRequest request)
1206 {
1207 final String[] VALID_IP_HEADER_CANDIDATES = {
1208 "X-Forwarded-For",
1209 "Proxy-Client-IP",
1210 "WL-Proxy-Client-IP",
1211 "HTTP_X_FORWARDED_FOR",
1212 "HTTP_X_FORWARDED",
1213 "HTTP_X_CLUSTER_CLIENT_IP",
1214 "HTTP_CLIENT_IP",
1215 "HTTP_FORWARDED_FOR",
1216 "HTTP_FORWARDED",
1217 "HTTP_VIA",
1218 "REMOTE_ADDR"
1219 };
1220
1221
1222 for (String header : VALID_IP_HEADER_CANDIDATES) {
1223 String ipAddress = request.getHeader(header);
1224 if (ipAddress != null && ipAddress.length() != 0 && !"unknown".equalsIgnoreCase(ipAddress)) {
1225 return ipAddress;
1226 }
1227 }
1228 return request.getRemoteAddr(); // fallback to whatever is the incoming IP (could be proxy)
1229 }
1230
1231
1232 /**
1233 * Clients on the same machine as the GS3 server can reconfigure and activate/deactivate,
1234 * Users in the administrator group also have the right to do such system commands.
1235 * For collection-level system commands, users with permissions to edit all collections or
1236 * the specific collection have a right to activate/deactivate a collection.
1237 * Useful links:
1238 * https://stackoverflow.com/questions/13563351/how-to-restrict-acces-to-specific-servlet-by-ip-via-container-configuration
1239 * https://stackoverflow.com/questions/35015514/restricting-access-to-localhost-for-java-servlet-endpoint
1240 * Reverse of: https://stackoverflow.com/questions/2539461/how-to-access-java-servlet-running-on-my-pc-from-outside
1241 */
1242 private boolean configureSecurityCheck(HttpServletRequest request, UserContext userContext,
1243 PrintWriter out, String baseURL, String lang,
1244 String output, Map<String, String[]> queryMap)
1245 {
1246 // if request emanates on same machine as GS server/from local machine, let it go through
1247 String ipOfClient = getClientIpAddress(request); //request.getRemoteAddr();
1248
1249 // Load the global.properties file, get the tomcat.server.IPregex and check for any of it
1250 // matching against the local IP
1251 String tomcatServerIPregex = GlobalProperties.getProperty("tomcat.server.IPregex", "");
1252 if(ipOfClient.equals(request.getLocalAddr()) || ipOfClient.matches(tomcatServerIPregex)) {
1253 return true;
1254 }
1255
1256 // TODO: Want regex for localhost situations and then make it a property in build.props
1257 // In build.props: comment, you might like to leave the IP of your server machine
1258 // at the end of the line
1259 // Check with another machine: browser, retest cmdline and activate.pl
1260 // Final phase is testing with apache set up
1261
1262 // if we have sc=colname, user needs to be colname-|all-collection-editor
1263 // if we have st=collection&sn=colname, same as above
1264 // otherwise user needs to be administrator
1265
1266 // We can have MODULE_TYPE=collection And MODULE_NAME=collname,
1267 // OR SYSTEM_CLUSTER=collname
1268 // If none of these specified, then assume the user should be administrator
1269 // If not a collection level operation, the user should be administrator
1270
1271 String coll = null;
1272 String module_type = getFirstParam(GSParams.SYSTEM_MODULE_TYPE, queryMap);
1273 if(module_type != null && module_type.equals("collection")) {
1274 // To deactivate demo:library?excerptid=gs_content&a=s&sa=d&sc=lucene-jdbm-demo
1275 coll = getFirstParam(GSParams.SYSTEM_MODULE_NAME, queryMap);
1276 } else {
1277 // To reconfigure demo: library?excerptid=gs_content&a=s&sa=c&sc=lucene-jdbm-demo
1278 coll = getFirstParam(GSParams.SYSTEM_CLUSTER, queryMap);
1279 if (coll != null && coll.equals("")) {
1280 coll = null;
1281 }
1282 }
1283 String collname_editor = (coll == null) ? null : (coll+"-collections-editor");
1284
1285 for (String group : userContext.getGroups()) {
1286
1287 // if user is admin, all is good: they can do reconfigure (on service, collection or
1288 // any module) or activate/deactivate a collection or any module
1289 if (group.equals("administrator")) {
1290 return true;
1291 }
1292
1293 // if collection level operation, user must have permissions for the collection
1294 // so the user must be in at least one of all-|personal-|collname-collections-editor
1295 if (coll != null) { // collname_editor won't be null
1296 if(group.equals("all-collections-editor") || group.equals(collname_editor)) {
1297 return true; // do we add || group.equals("personal-collections-editor")?
1298 }
1299 }
1300
1301 }
1302
1303 errorAndLoginPage(request, userContext, out, baseURL, lang, output);
1304 return false;
1305 }
1306
1307 private boolean runCollectionSecurityCheck(HttpServletRequest request, UserContext userContext, PrintWriter out, String baseURL, String collection, String document, String lang, String output) {
1308
1309 // logger.info("LibraryServlet::runCollectionSecurityCheck(): start of check for collection:"+collection);
1310
1311 //Get the security info for this collection
1312 Document msg_doc = XMLConverter.newDOM();
1313 Element securityMessage = msg_doc.createElement(GSXML.MESSAGE_ELEM);
1314 Element securityRequest = GSXML.createBasicRequest(msg_doc, GSXML.REQUEST_TYPE_SECURITY, collection, userContext);
1315 securityMessage.appendChild(securityRequest);
1316 if (document != null && !document.equals(""))
1317 {
1318 securityRequest.setAttribute(GSXML.NODE_OID, document);
1319 }
1320
1321 Element securityResponse = (Element) GSXML.getChildByTagName(this.recept.process(securityMessage), GSXML.RESPONSE_ELEM);
1322 if (securityResponse == null)
1323 {
1324 return false;
1325 }
1326
1327 // Groups mentioned in collectionConfig.xml's Security element, se_groups
1328 ArrayList<String> se_groups = GSXML.getGroupsFromSecurityResponse(securityResponse);
1329
1330 //If guests are not allowed to access this page then check to see if the user is in a group that is allowed to access the page
1331 if (!se_groups.contains(""))
1332 {
1333 boolean found = false;
1334 for (String se_group : se_groups)
1335 {
1336 if (request.isUserInRole(se_group))
1337 {
1338 found = true;
1339 break;
1340 }
1341 }
1342
1343 //The current user is not allowed to access the page so produce a login page
1344 if (!found)
1345 {
1346 errorAndLoginPage(request, userContext, out, baseURL, lang, output);
1347 return false;
1348 }
1349 }
1350 return true;
1351 }
1352
1353
1354 private void errorAndLoginPage(HttpServletRequest request, UserContext userContext,
1355 PrintWriter out, String baseURL, String lang, String output)
1356 {
1357 String error_message = "";
1358 if (request.getAuthType() == null)
1359 {
1360 error_message = getTextString("auth.error.login", lang);
1361 }
1362 else
1363 {
1364 error_message = getTextString("auth.error.incorrect_login", lang);
1365 }
1366
1367 HttpSession session = request.getSession();
1368 ServletContext context = session.getServletContext();
1369 String redirect_url_string = request.getRequestURI().replaceFirst(context.getContextPath() + "/", "");
1370
1371 if (request.getQueryString() != null && request.getQueryString().length() > 0)
1372 {
1373 redirect_url_string += "?" + request.getQueryString().replace("&", "&amp;");
1374 }
1375
1376 generateLoginPage(redirect_url_string, error_message, userContext, out, baseURL, output);
1377 }
1378
1379
1380 private String getTextString(String key, String lang) {
1381 return Dictionary.getTextString(key, lang, null, null, null, null, null);
1382
1383 }
1384 //a debugging method
1385 private void displaySize(Hashtable<String, UserSessionCache> table)
1386 {
1387 if (table == null)
1388 {
1389 //logger.info("cached table is null");
1390 return;
1391 }
1392 if (table.size() == 0)
1393 {
1394 //logger.info("cached table size is zero");
1395 return;
1396 }
1397 int num_cached_coll = 0;
1398 ArrayList<UserSessionCache> cache_list = new ArrayList<UserSessionCache>(table.values());
1399 for (int i = 0; i < cache_list.size(); i++)
1400 {
1401 num_cached_coll += cache_list.get(i).tableSize();
1402 }
1403 //logger.info("Number of sessions : total number of cached collection info = " + table.size() + " : " + num_cached_coll);
1404 }
1405
1406 /** merely a debugging method! */
1407 private String tableToString(Hashtable<String, Hashtable<String, String>> table)
1408 {
1409 String str = "";
1410 Enumeration<String> keys = table.keys();
1411 while (keys.hasMoreElements())
1412 {
1413 String name = keys.nextElement();
1414 str += name + ", ";
1415 }
1416 return str;
1417 }
1418
1419 /**
1420 * this goes through each URL and adds in a session id if needed-- its
1421 * needed if the browser doesn't accept cookies also escapes things if
1422 * needed
1423 */
1424 protected void encodeURLs(Node dataNode, HttpServletResponse response)
1425 {
1426 if (dataNode == null)
1427 {
1428 return;
1429 }
1430
1431 Element data = null;
1432
1433 short nodeType = dataNode.getNodeType();
1434 if (nodeType == Node.DOCUMENT_NODE)
1435 {
1436 Document docNode = (Document) dataNode;
1437 data = docNode.getDocumentElement();
1438 }
1439 else if (nodeType == Node.ELEMENT_NODE)
1440 {
1441 data = (Element) dataNode;
1442 }
1443
1444 // Normally 'dataNode' is an Element or Document, but it is possible for it to be one of the other Node types
1445 // (e.g., Node.TEXT_NODE, which is generated when using excerptid-text=xxxx)
1446 // For these simpler cases, there is no URL encoding work to be done, so leaving 'data' as null is sufficient
1447
1448 if (data != null)
1449 {
1450
1451 // get all the <a> elements
1452 NodeList hrefs = data.getElementsByTagName("a");
1453 // Instead of calculating each iteration...
1454 int hrefscount = hrefs.getLength();
1455
1456 for (int i = 0; hrefs != null && i < hrefscount; i++)
1457 {
1458 Element a = (Element) hrefs.item(i);
1459 // ugly hack to get rid of : in the args - interferes with session handling
1460 String href = a.getAttribute("href");
1461 if (!href.equals(""))
1462 {
1463 if (href.indexOf("?") != -1)
1464 {
1465 String[] parts = StringUtils.split(href, "\\?", -1);
1466 if (parts.length == 1)
1467 {
1468 parts[0] = StringUtils.replace(parts[0], ":", "%3A");
1469 href = "?" + parts[0];
1470 }
1471 else
1472 {
1473 parts[1] = StringUtils.replace(parts[1], ":", "%3A");
1474 href = parts[0] + "?" + parts[1];
1475 }
1476
1477 }
1478 a.setAttribute("href", response.encodeURL(href));
1479 }
1480 }
1481
1482 // now find any submit bits - get all the <form> elements
1483 NodeList forms = data.getElementsByTagName("form");
1484 int formscount = forms.getLength();
1485 for (int i = 0; forms != null && i < formscount; i++)
1486 {
1487 Element form = (Element) forms.item(i);
1488 form.setAttribute("action", response.encodeURL(form.getAttribute("action")));
1489 }
1490 // are these the only cases where URLs occur??
1491 // we should only do this for greenstone urls?
1492 }
1493
1494 }
1495
1496 protected String getFirstParam(String name, Map<String, String[]> map)
1497 {
1498 String[] val = map.get(name);
1499 if (val == null || val.length == 0)
1500 {
1501 return null;
1502 }
1503
1504 return val[0];
1505 }
1506
1507 public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
1508 {
1509 //Check if we need to process a file upload
1510 if (ServletFileUpload.isMultipartContent(request))
1511 {
1512 DiskFileItemFactory fileItemFactory = new DiskFileItemFactory();
1513
1514 int sizeLimit = System.getProperties().containsKey("servlet.upload.filesize.limit") ? Integer.parseInt(System.getProperty("servlet.upload.filesize.limit")) : 100 * 1024 * 1024;
1515
1516 File tempDir = new File(GlobalProperties.getGSDL3Home() + File.separator + "tmp");
1517 if (!tempDir.exists())
1518 {
1519 tempDir.mkdirs();
1520 }
1521
1522 //We want all files to be stored on disk (hence the 0)
1523 fileItemFactory.setSizeThreshold(0);
1524 fileItemFactory.setRepository(tempDir);
1525
1526 ServletFileUpload uploadHandler = new ServletFileUpload(fileItemFactory);
1527 uploadHandler.setFileSizeMax(sizeLimit);
1528
1529 HashMap<String, String[]> queryMap = new HashMap<String, String[]>();
1530 try
1531 {
1532 List items = uploadHandler.parseRequest(request);
1533 Iterator iter = items.iterator();
1534 while (iter.hasNext())
1535 {
1536 FileItem current = (FileItem) iter.next();
1537 if (current.isFormField())
1538 {
1539 queryMap.put(current.getFieldName(), new String[] { current.getString() });
1540 }
1541 else if (current.getName() != null && !current.getName().equals(""))
1542 {
1543 File file = new File(tempDir, current.getName());
1544 current.write(file);
1545
1546 queryMap.put("md___ex.Filesize", new String[] { "" + file.length() });
1547 queryMap.put("md___ex.Filename", new String[] { "" + current.getName() });
1548 }
1549 }
1550 }
1551 catch (Exception e)
1552 {
1553 e.printStackTrace();
1554 }
1555
1556 doGetOrPost(request, response, queryMap);
1557 }
1558 else
1559 {
1560 doGetOrPost(request, response, request.getParameterMap());
1561 }
1562 }
1563}
Note: See TracBrowser for help on using the repository browser.