source: main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/Authentication.java@ 29318

Last change on this file since 29318 was 28966, checked in by kjdon, 10 years ago

Lots of changes. Mainly to do with removing this.doc from everywhere. Document is not thread safe. Now we tend to create a new Document everytime we are starting a new page/message etc. in service this.desc_doc is available as teh document to create service info stuff. But it should only be used for this and not for other messages. newDOM is now static for XMLConverter. method param changes for some GSXML methods.

File size: 39.6 KB
Line 
1package org.greenstone.gsdl3.service;
2
3import java.io.File;
4import java.io.Serializable;
5import java.math.BigInteger;
6import java.sql.SQLException;
7import java.util.ArrayList;
8import java.util.HashMap;
9import java.util.UUID;
10import java.util.Vector;
11import java.util.regex.Pattern;
12
13import net.tanesha.recaptcha.ReCaptchaImpl;
14import net.tanesha.recaptcha.ReCaptchaResponse;
15
16import org.apache.commons.codec.digest.DigestUtils;
17import org.greenstone.gsdl3.util.DerbyWrapper;
18import org.greenstone.gsdl3.util.GSXML;
19import org.greenstone.gsdl3.util.UserQueryResult;
20import org.greenstone.gsdl3.util.UserTermInfo;
21import org.greenstone.gsdl3.util.XMLConverter;
22import org.greenstone.util.GlobalProperties;
23
24import org.w3c.dom.Document;
25import org.w3c.dom.Element;
26import org.w3c.dom.NodeList;
27
28public class Authentication extends ServiceRack
29{
30 //Some useful constants
31 protected static final int USERNAME_MIN_LENGTH = 2;
32 protected static final int USERNAME_MAX_LENGTH = 30;
33 protected static final int PASSWORD_MIN_LENGTH = 3;
34 protected static final int PASSWORD_MAX_LENGTH = 64;
35
36 //Error codes
37 protected static final int NO_ERROR = 0;
38 protected static final int ERROR_REQUEST_HAS_NO_PARAM_LIST = -1;
39 protected static final int ERROR_NOT_LOGGED_IN = -2;
40 protected static final int ERROR_ADMIN_NOT_LOGGED_IN = -3;
41 protected static final int ERROR_COULD_NOT_GET_USER_INFO = -4;
42 protected static final int ERROR_USERNAME_NOT_SPECIFIED = -5;
43 protected static final int ERROR_REQUESTED_USER_NOT_FOUND = -6;
44 protected static final int ERROR_SQL_EXCEPTION = -7;
45 protected static final int ERROR_INVALID_USERNAME = -8;
46 protected static final int ERROR_PASSWORD_NOT_ENTERED = -9;
47 protected static final int ERROR_PASSWORD_TOO_SHORT = -10;
48 protected static final int ERROR_PASSWORD_TOO_LONG = -11;
49 protected static final int ERROR_PASSWORD_USES_ILLEGAL_CHARACTERS = -12;
50 protected static final int ERROR_INCORRECT_PASSWORD = -13;
51 protected static final int ERROR_USER_ALREADY_EXISTS = -14;
52 protected static final int ERROR_ADDING_USER = -15;
53 protected static final int ERROR_REMOVING_USER = -16;
54 protected static final int ERROR_CAPTCHA_DOES_NOT_MATCH = -17;
55 protected static final int ERROR_CAPTCHA_MISSING = -18;
56 protected static final int ERROR_NOT_AUTHORISED = -19;
57
58 protected static final HashMap<Integer, String> _errorMessageMap;
59 static
60 {
61 //Corresponding error messages
62 HashMap<Integer, String> errorMessageMap = new HashMap<Integer, String>();
63 errorMessageMap.put(ERROR_REQUEST_HAS_NO_PARAM_LIST, "The list of parameters for this request was empty.");
64 errorMessageMap.put(ERROR_NOT_LOGGED_IN, "You must be logged in to access this page.");
65 errorMessageMap.put(ERROR_ADMIN_NOT_LOGGED_IN, "You must be logged in as an administrator to access this page.");
66 errorMessageMap.put(ERROR_COULD_NOT_GET_USER_INFO, "There was a error getting the user information.");
67 errorMessageMap.put(ERROR_USERNAME_NOT_SPECIFIED, "No username was specified.");
68 errorMessageMap.put(ERROR_REQUESTED_USER_NOT_FOUND, "The requested user was not found in the database.");
69 errorMessageMap.put(ERROR_SQL_EXCEPTION, "There was an SQL exception while accessing the database.");
70 errorMessageMap.put(ERROR_INVALID_USERNAME, "The username specified was invalid.");
71 errorMessageMap.put(ERROR_PASSWORD_NOT_ENTERED, "No password was entered.");
72 errorMessageMap.put(ERROR_PASSWORD_TOO_SHORT, "The password you entered was too short (minimum of 3 characters).");
73 errorMessageMap.put(ERROR_PASSWORD_TOO_LONG, "The password you entered was too long (maximum of 64 characters).");
74 errorMessageMap.put(ERROR_PASSWORD_USES_ILLEGAL_CHARACTERS, "The password you entered contains illegal characters.");
75 errorMessageMap.put(ERROR_INCORRECT_PASSWORD, "The password specified was incorrect.");
76 errorMessageMap.put(ERROR_USER_ALREADY_EXISTS, "This user already exists and therefore cannot be added.");
77 errorMessageMap.put(ERROR_ADDING_USER, "There was an error adding this user to the database.");
78 errorMessageMap.put(ERROR_REMOVING_USER, "There was an error removing this user from the database.");
79 errorMessageMap.put(ERROR_CAPTCHA_DOES_NOT_MATCH, "The words you entered did not match the image, please try again.");
80 errorMessageMap.put(ERROR_CAPTCHA_MISSING, "The information from the captcha is missing.");
81 errorMessageMap.put(ERROR_NOT_AUTHORISED, "You are not authorised to access this page.");
82
83 _errorMessageMap = errorMessageMap;
84 }
85
86 //Admin-required operations
87 protected static final String LIST_USERS = "ListUsers";
88 protected static final String PERFORM_ADD = "PerformAdd";
89 protected static final String PERFORM_EDIT = "PerformEdit";
90 protected static final String ADD_USER = "AddUser";
91 protected static final String EDIT_USER = "EditUser";
92 protected static final String PERFORM_DELETE_USER = "PerformDeleteUser";
93
94 protected static final ArrayList<String> _adminOpList;
95 static
96 {
97 ArrayList<String> opList = new ArrayList<String>();
98 opList.add(LIST_USERS);
99 opList.add(PERFORM_ADD);
100 opList.add(PERFORM_EDIT);
101 opList.add(EDIT_USER);
102 opList.add(PERFORM_DELETE_USER);
103
104 _adminOpList = opList;
105 }
106
107 //User-required operations
108 protected static final String ACCOUNT_SETTINGS = "AccountSettings";
109 protected static final String PERFORM_ACCOUNT_EDIT = "PerformAccEdit";
110 protected static final String PERFORM_RESET_PASSWORD = "PerformResetPassword";
111 protected static final String PERFORM_CHANGE_PASSWORD = "PerformChangePassword";
112 protected static final String PERFORM_RETRIEVE_PASSWORD = "PerformRetrievePassword";
113 protected static final ArrayList<String> _userOpList;
114 static
115 {
116 ArrayList<String> opList = new ArrayList<String>();
117 opList.add(ACCOUNT_SETTINGS);
118 opList.add(PERFORM_ACCOUNT_EDIT);
119 opList.add(PERFORM_RESET_PASSWORD);
120 opList.addAll(_adminOpList);
121 _userOpList = opList;
122 }
123
124 //Other operations
125 protected static final String REGISTER = "Register";
126 protected static final String PERFORM_REGISTER = "PerformRegister";
127 protected static final String LOGIN = "Login";
128
129 //the services on offer
130 protected static final String AUTHENTICATION_SERVICE = "Authentication";
131 protected static final String GET_USER_INFORMATION_SERVICE = "GetUserInformation";
132 protected static final String CHANGE_USER_EDIT_MODE_SERVICE = "ChangeUserEditMode";
133 protected static final String REMOTE_AUTHENTICATION_SERVICE = "RemoteAuthentication";
134
135 protected static boolean _derbyWrapperDoneForcedShutdown = false;
136
137 protected String _recaptchaPrivateKey = null;
138 protected String _recaptchaPublicKey = null;
139
140 /** constructor */
141 public Authentication()
142 {
143 }
144
145 public void cleanUp()
146 {
147 super.cleanUp();
148
149 if (!_derbyWrapperDoneForcedShutdown)
150 {
151
152 // This boolean is used to ensure we always shutdown the derby server, even if it is never
153 // used by the Authentication server. This is because the Tomcat greenstone3.xml
154 // config file also specifies a connection to the database, which can result in the
155 // server being initialized when the servlet is first accessed. Note also,
156 // Authentication is a ServiceRack, meaning cleanUp() is called for each service
157 // supported, however we only need to shutdown the Derby server once. Again
158 // this boolean variable helps achieve this.
159
160 logger.info("Authentication Service performing forced shutdown of Derby Server ...");
161
162 DerbyWrapper.shutdownDatabaseServer();
163 _derbyWrapperDoneForcedShutdown = true;
164 }
165 }
166
167 public boolean configure(Element info, Element extra_info)
168 {
169 logger.info("Configuring Authentication...");
170 this.config_info = info;
171
172 // set up Authentication service info - for now just has name and type
173 Element authentication_service = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
174 authentication_service.setAttribute(GSXML.TYPE_ATT, "authen");
175 authentication_service.setAttribute(GSXML.NAME_ATT, AUTHENTICATION_SERVICE);
176 this.short_service_info.appendChild(authentication_service);
177
178 Element getUserInformation_service = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
179 getUserInformation_service.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
180 getUserInformation_service.setAttribute(GSXML.NAME_ATT, GET_USER_INFORMATION_SERVICE);
181 this.short_service_info.appendChild(getUserInformation_service);
182
183 Element changeEditMode_service = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
184 changeEditMode_service.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
185 changeEditMode_service.setAttribute(GSXML.NAME_ATT, CHANGE_USER_EDIT_MODE_SERVICE);
186 this.short_service_info.appendChild(changeEditMode_service);
187
188 Element remoteAuthentication_service = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
189 remoteAuthentication_service.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
190 remoteAuthentication_service.setAttribute(GSXML.NAME_ATT, REMOTE_AUTHENTICATION_SERVICE);
191 this.short_service_info.appendChild(remoteAuthentication_service);
192
193
194 DerbyWrapper.createDatabaseIfNeeded();
195
196 NodeList recaptchaElems = info.getElementsByTagName("recaptcha");
197 for (int i = 0; i < recaptchaElems.getLength(); i++)
198 {
199 Element currentElem = (Element) recaptchaElems.item(i);
200 if (currentElem.getAttribute(GSXML.NAME_ATT) != null && currentElem.getAttribute(GSXML.NAME_ATT).equals("public_key"))
201 {
202 if (currentElem.getAttribute(GSXML.VALUE_ATT) != null)
203 {
204 _recaptchaPublicKey = currentElem.getAttribute(GSXML.VALUE_ATT);
205 }
206 }
207 else if (currentElem.getAttribute(GSXML.NAME_ATT) != null && currentElem.getAttribute(GSXML.NAME_ATT).equals("private_key"))
208 {
209 if (currentElem.getAttribute(GSXML.VALUE_ATT) != null)
210 {
211 _recaptchaPrivateKey = currentElem.getAttribute(GSXML.VALUE_ATT);
212 }
213 }
214 }
215
216 return true;
217 }
218
219 protected Element getServiceDescription(Document doc, String service_id, String lang, String subset)
220 {
221
222 Element authen_service = doc.createElement(GSXML.SERVICE_ELEM);
223
224 if (service_id.equals(AUTHENTICATION_SERVICE))
225 {
226 authen_service.setAttribute(GSXML.TYPE_ATT, "authen");
227 authen_service.setAttribute(GSXML.NAME_ATT, AUTHENTICATION_SERVICE);
228 }
229 else if (service_id.equals(GET_USER_INFORMATION_SERVICE))
230 {
231 authen_service.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
232 authen_service.setAttribute(GSXML.NAME_ATT, GET_USER_INFORMATION_SERVICE);
233 }
234 else if (service_id.equals(CHANGE_USER_EDIT_MODE_SERVICE))
235 {
236 authen_service.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
237 authen_service.setAttribute(GSXML.NAME_ATT, CHANGE_USER_EDIT_MODE_SERVICE);
238 }
239 else if (service_id.equals(REMOTE_AUTHENTICATION_SERVICE))
240 {
241 authen_service.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
242 authen_service.setAttribute(GSXML.NAME_ATT, REMOTE_AUTHENTICATION_SERVICE);
243 }
244 else
245 {
246 return null;
247 }
248
249 if (service_id.equals(AUTHENTICATION_SERVICE) && (subset == null || subset.equals(GSXML.DISPLAY_TEXT_ELEM + GSXML.LIST_MODIFIER)))
250 {
251 authen_service.appendChild(GSXML.createDisplayTextElement(doc, GSXML.DISPLAY_TEXT_NAME, getServiceName(service_id, lang)));
252 authen_service.appendChild(GSXML.createDisplayTextElement(doc, GSXML.DISPLAY_TEXT_DESCRIPTION, getServiceDescription(service_id, lang)));
253 }
254 return authen_service;
255 }
256
257 protected String getServiceName(String service_id, String lang)
258 {
259 return getTextString(service_id + ".name", lang);
260 }
261
262 protected String getServiceSubmit(String service_id, String lang)
263 {
264 return getTextString(service_id + ".submit", lang);
265 }
266
267 protected String getServiceDescription(String service_id, String lang)
268 {
269 return getTextString(service_id + ".description", lang);
270 }
271
272 protected Element processChangeUserEditMode(Element request)
273 {
274 // Create a new (empty) result message
275 Document result_doc = XMLConverter.newDOM();
276 Element result = result_doc.createElement(GSXML.RESPONSE_ELEM);
277
278 result.setAttribute(GSXML.FROM_ATT, CHANGE_USER_EDIT_MODE_SERVICE);
279 result.setAttribute(GSXML.TYPE_ATT, GSXML.REQUEST_TYPE_PROCESS);
280
281 Element paramList = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
282 if (paramList == null)
283 {
284 GSXML.addError(result, _errorMessageMap.get(ERROR_REQUEST_HAS_NO_PARAM_LIST));
285 return result;
286 }
287
288 HashMap<String, Serializable> params = GSXML.extractParams(paramList, true);
289
290 String username = (String) params.get("username");
291 String editMode = (String) params.get("enabled");
292
293 if (!editMode.toLowerCase().equals("true") && !editMode.toLowerCase().equals("false"))
294 {
295 editMode = "false";
296 }
297
298 DerbyWrapper dw = openDatabase();
299 dw.addUserData(username, "USER_EDIT_ENABLED", editMode);
300 dw.closeDatabase();
301
302 return result;
303 }
304
305 /**
306 * This method replaces the gliserver.pl code for authenticating a user against the derby database
307 * gliserver.pl needed to instantiate its own JVM to access the derby DB, but the GS3 already has
308 * the Derby DB open and 2 JVMs are not allowed concurrent access to an open embedded Derby DB.
309 * Gliserver.pl now goes through this method (via ServletRealmCheck.java), thereby using the same
310 * connection to the DerbyDB. This method reproduces the same behaviour as gliserver.pl used to,
311 * by returning the user_groups on successful authentication, else returns the specific
312 * "Authentication failed" messages that glisever.pl would produce.
313 * http://remote-host-name:8383/greenstone3/library?a=s&sa=authenticated-ping&excerptid=gs_content&un=admin&pw=<PW>&col=demo
314 */
315 protected Element processRemoteAuthentication(Element request) {
316 //logger.info("*** Authentication::processRemoteAuthentication");
317
318 String message = "";
319
320 Element system = (Element) GSXML.getChildByTagName(request, GSXML.REQUEST_TYPE_SYSTEM);
321 String username = system.hasAttribute("username") ? system.getAttribute("username") : "";
322 String password = system.hasAttribute("password") ? system.getAttribute("password") : "";
323
324
325 // If we're not editing a collection then the user doesn't need to be in a particular group
326 String collection = system.hasAttribute("collection") ? system.getAttribute("collection") : "";
327
328
329 if(username.equals("") || password.equals("")) {
330 message = "Authentication failed: no (username or) password specified.";
331 //logger.error("*** Remote login failed. No username or pwd provided");
332 }
333 else {
334 String storedPassword = retrieveDataForUser(username, "password");
335 if(storedPassword != null && (password.equals(storedPassword) || hashPassword(password).equals(storedPassword))) {
336
337 // gliserver.pl used to return the groups when authentication succeeded
338 String groups = retrieveDataForUser(username, "groups"); //comma-separated list
339
340 if(collection.equals("")) {
341 message = groups;
342 } else {
343
344 if(groups.indexOf("all-collections-editor") != -1) { // Does this user have access to all collections?
345 message = groups;
346 } else if(groups.indexOf("personal-collections-editor") != -1 && collection.startsWith(username+"-")) { // Does this user have access to personal collections, and is this one?
347 message = groups;
348 } else if(groups.indexOf(collection+"-collection-editor") != -1) { // Does this user have access to this collection?
349 message = groups;
350 }
351 else {
352 message = "Authentication failed: user is not in the required group.";
353 //logger.error("*** Remote login failed. Groups did not match for the collection specified");
354 }
355 }
356
357 } else {
358
359 if(storedPassword == null) {
360 message = "Authentication failed: no account for user '" + username + "'";
361 //logger.error("*** Remote login failed. User not found or password not set for user.");
362 } else {
363 message = "Authentication failed: incorrect password.";
364 //logger.error("*** Remote login failed. Password did not match for user");
365 }
366 }
367 }
368 Document result_doc = XMLConverter.newDOM();
369 Element result = result_doc.createElement(GSXML.RESPONSE_ELEM);
370 result.setAttribute(GSXML.FROM_ATT, REMOTE_AUTHENTICATION_SERVICE);
371 result.setAttribute(GSXML.TYPE_ATT, GSXML.REQUEST_TYPE_PROCESS);
372 Element s = GSXML.createTextElement(result_doc, GSXML.STATUS_ELEM, message);
373 result.appendChild(s);
374 return result;
375 }
376
377 protected Element processGetUserInformation(Element request)
378 {
379 // Create a new (empty) result message
380 Document result_doc = XMLConverter.newDOM();
381 Element result = result_doc.createElement(GSXML.RESPONSE_ELEM);
382
383 result.setAttribute(GSXML.FROM_ATT, GET_USER_INFORMATION_SERVICE);
384 result.setAttribute(GSXML.TYPE_ATT, GSXML.REQUEST_TYPE_PROCESS);
385
386 Element paramList = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
387 if (paramList == null)
388 {
389 GSXML.addError(result, _errorMessageMap.get(ERROR_REQUEST_HAS_NO_PARAM_LIST));
390 return result;
391 }
392
393 HashMap<String, Serializable> params = GSXML.extractParams(paramList, true);
394
395 String username = (String) params.get("username");
396
397 if (username == null)
398 {
399 GSXML.addError(result, _errorMessageMap.get(ERROR_USERNAME_NOT_SPECIFIED));
400 return result;
401 }
402
403 DerbyWrapper derbyWrapper = openDatabase();
404
405 UserQueryResult userQueryResult = derbyWrapper.findUser(username);
406 String editEnabled = derbyWrapper.getUserData(username, "USER_EDIT_ENABLED");
407
408 Vector<UserTermInfo> terms = userQueryResult.getUserTerms();
409
410 if (terms.size() == 0)
411 {
412 GSXML.addError(result, _errorMessageMap.get(ERROR_REQUESTED_USER_NOT_FOUND));
413 return result;
414 }
415
416 UserTermInfo userInfo = terms.get(0);
417 Element userInfoList = result_doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
418 result.appendChild(userInfoList);
419
420 Element usernameField = GSXML.createParameter(result_doc, "username", userInfo.username);
421 Element passwordField = GSXML.createParameter(result_doc, "password", userInfo.password);
422 Element groupsField = GSXML.createParameter(result_doc, "groups", userInfo.groups);
423 Element accountStatusField = GSXML.createParameter(result_doc, "accountstatus", userInfo.accountstatus);
424 Element commentField = GSXML.createParameter(result_doc, "comment", userInfo.comment);
425
426 if (editEnabled != null)
427 {
428 Element editEnabledElem = GSXML.createParameter(result_doc, "editEnabled", editEnabled);
429 userInfoList.appendChild(editEnabledElem);
430 }
431
432 userInfoList.appendChild(usernameField);
433 userInfoList.appendChild(passwordField);
434 userInfoList.appendChild(groupsField);
435 userInfoList.appendChild(accountStatusField);
436 userInfoList.appendChild(commentField);
437
438 derbyWrapper.closeDatabase();
439
440 return result;
441 }
442
443 protected Element processAuthentication(Element request)
444 {
445 checkAdminUserExists();
446
447 // Create a new (empty) result message
448 Document result_doc = XMLConverter.newDOM();
449 Element result = result_doc.createElement(GSXML.RESPONSE_ELEM);
450 result.setAttribute(GSXML.FROM_ATT, AUTHENTICATION_SERVICE);
451 result.setAttribute(GSXML.TYPE_ATT, GSXML.REQUEST_TYPE_PROCESS);
452
453 // Create an Authentication node put into the result
454 Element authenNode = result_doc.createElement(GSXML.AUTHEN_NODE_ELEM);
455 result.appendChild(authenNode);
456 result.appendChild(getCollectList(result_doc, this.site_home + File.separatorChar + "collect"));
457
458 // Create a service node added into the Authentication node
459 Element serviceNode = result_doc.createElement(GSXML.SERVICE_ELEM);
460 authenNode.appendChild(serviceNode);
461
462 // Get the parameters of the request
463 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
464 if (param_list == null)
465 {
466 serviceNode.setAttribute("operation", LOGIN);
467 GSXML.addError(result, _errorMessageMap.get(ERROR_REQUEST_HAS_NO_PARAM_LIST));
468 return result; // Return the empty result
469 }
470 HashMap<String, Serializable> paramMap = GSXML.extractParams(param_list, false);
471 String op = (String) paramMap.get("authpage");
472 serviceNode.setAttribute("operation", op);
473
474 String username = null;
475 String groups = null;
476
477 Element userInformation = (Element) GSXML.getChildByTagName(request, GSXML.USER_INFORMATION_ELEM);
478 if (userInformation == null && _userOpList.contains(op))
479 {
480 serviceNode.setAttribute("operation", LOGIN);
481 GSXML.addError(result, _errorMessageMap.get(ERROR_NOT_LOGGED_IN));
482 return result;
483 }
484
485 if (userInformation != null)
486 {
487 username = userInformation.getAttribute(GSXML.USERNAME_ATT);
488 groups = userInformation.getAttribute(GSXML.GROUPS_ATT);
489 }
490
491 if (username == null && _userOpList.contains(op))
492 {
493 serviceNode.setAttribute("operation", LOGIN);
494 GSXML.addError(result, _errorMessageMap.get(ERROR_NOT_LOGGED_IN));
495 return result;
496 }
497
498 if (_adminOpList.contains(op) && (groups == null || !groups.matches(".*\\badministrator\\b.*")))
499 {
500 serviceNode.setAttribute("operation", LOGIN);
501 GSXML.addError(result, _errorMessageMap.get(ERROR_ADMIN_NOT_LOGGED_IN));
502 return result;
503 }
504
505 if (op.equals(LIST_USERS))
506 {
507 int error = addUserInformationToNode(null, serviceNode);
508 if (error != NO_ERROR)
509 {
510 GSXML.addError(result, _errorMessageMap.get(error));
511 }
512 }
513 else if (op.equals(PERFORM_ADD))
514 {
515 String newUsername = (String) paramMap.get("username");
516 String newPassword = (String) paramMap.get("password");
517 String newGroups = (String) paramMap.get("groups");
518 String newStatus = (String) paramMap.get("status");
519 String newComment = (String) paramMap.get("comment");
520 String newEmail = (String) paramMap.get("email");
521
522 //Check the given user name
523 int error;
524 if ((error = checkUsername(newUsername)) != NO_ERROR)
525 {
526 GSXML.addError(result, _errorMessageMap.get(error));
527 return result;
528 }
529
530 //Check the given password
531 if ((error = checkPassword(newPassword)) != NO_ERROR)
532 {
533 GSXML.addError(result, _errorMessageMap.get(error));
534 return result;
535 }
536
537 newPassword = hashPassword(newPassword);
538
539 error = addUser(newUsername, newPassword, newGroups, newStatus, newComment, newEmail);
540 if (error != NO_ERROR)
541 {
542 serviceNode.setAttribute("operation", ADD_USER);
543 GSXML.addError(result, _errorMessageMap.get(error));
544 }
545 else
546 {
547 addUserInformationToNode(null, serviceNode);
548 serviceNode.setAttribute("operation", LIST_USERS);
549 }
550 }
551 else if (op.equals(PERFORM_REGISTER))
552 {
553 String newUsername = (String) paramMap.get("username");
554 String newPassword = (String) paramMap.get("password");
555 String newEmail = (String) paramMap.get("email");
556
557 //Check the given user name
558 int error;
559 if ((error = checkUsername(newUsername)) != NO_ERROR)
560 {
561 GSXML.addError(result, _errorMessageMap.get(error));
562 return result;
563 }
564
565 //Check the given password
566 if ((error = checkPassword(newPassword)) != NO_ERROR)
567 {
568 GSXML.addError(result, _errorMessageMap.get(error));
569 return result;
570 }
571
572 newPassword = hashPassword(newPassword);
573
574 if (_recaptchaPrivateKey != null && _recaptchaPrivateKey.length() > 0)
575 {
576 ReCaptchaImpl reCaptcha = new ReCaptchaImpl();
577 reCaptcha.setPrivateKey(_recaptchaPrivateKey);
578
579 try
580 {
581 //If this line throws an exception then we'll assume the user has a firewall that is too restrictive
582 //(or that they're not connected to the Internet) to allow access to google services.
583 //In this situation we won't use the recaptcha test.
584 reCaptcha.checkAnswer(request.getAttribute("remoteAddress"), "", "");
585
586 String challenge = (String) paramMap.get("recaptcha_challenge_field");
587 String uResponse = (String) paramMap.get("recaptcha_response_field");
588
589 if (challenge == null || uResponse == null)
590 {
591 serviceNode.setAttribute("operation", REGISTER);
592 GSXML.addError(result, _errorMessageMap.get(ERROR_CAPTCHA_MISSING));
593 return result;
594 }
595
596 ReCaptchaResponse reCaptchaResponse = reCaptcha.checkAnswer(request.getAttribute("remoteAddress"), challenge, uResponse);
597
598 if (!reCaptchaResponse.isValid())
599 {
600 serviceNode.setAttribute("operation", REGISTER);
601 GSXML.addError(result, _errorMessageMap.get(ERROR_CAPTCHA_DOES_NOT_MATCH));
602 return result;
603 }
604 }
605 catch (Exception ex)
606 {
607 }
608 }
609
610 error = addUser(newUsername, newPassword, "", "true", "", newEmail);
611 if (error != NO_ERROR)
612 {
613 serviceNode.setAttribute("operation", REGISTER);
614 GSXML.addError(result, _errorMessageMap.get(error));
615 }
616 }
617 else if (op.equals(PERFORM_EDIT))
618 {
619 String previousUsername = (String) paramMap.get("prevUsername");
620 String newUsername = (String) paramMap.get("newUsername");
621 String newPassword = (String) paramMap.get("password");
622 String newGroups = (String) paramMap.get("groups");
623 String newStatus = (String) paramMap.get("status");
624 String newComment = (String) paramMap.get("comment");
625 String newEmail = (String) paramMap.get("newEmail");
626
627 //Check the given user name
628 int error;
629 if ((error = checkUsername(newUsername)) != NO_ERROR)
630 {
631 GSXML.addError(result, _errorMessageMap.get(error));
632 return result;
633 }
634
635 if (newPassword == null)
636 {
637 newPassword = retrieveDataForUser(previousUsername, "password");
638 }
639 else
640 {
641 //Check the given password
642 if ((error = checkPassword(newPassword)) != NO_ERROR)
643 {
644 GSXML.addError(result, _errorMessageMap.get(error));
645 return result;
646 }
647
648 newPassword = hashPassword(newPassword);
649 }
650
651 error = removeUser(previousUsername);
652 if (error != NO_ERROR)
653 {
654 if (error == ERROR_USERNAME_NOT_SPECIFIED)
655 {
656 addUserInformationToNode(null, serviceNode);
657 serviceNode.setAttribute("operation", LIST_USERS);
658 }
659 else
660 {
661 serviceNode.setAttribute("operation", EDIT_USER);
662 GSXML.addError(result, _errorMessageMap.get(error));
663 }
664 return result;
665 }
666
667 error = addUser(newUsername, newPassword, newGroups, newStatus, newComment, newEmail);
668 if (error != NO_ERROR)
669 {
670 serviceNode.setAttribute("operation", EDIT_USER);
671 GSXML.addError(result, _errorMessageMap.get(error));
672 }
673 else
674 {
675 addUserInformationToNode(null, serviceNode);
676 serviceNode.setAttribute("operation", LIST_USERS);
677 }
678 }
679 else if (op.equals(PERFORM_ACCOUNT_EDIT))
680 {
681 String previousUsername = (String) paramMap.get("prevUsername");
682 String newUsername = (String) paramMap.get("newUsername");
683 String oldPassword = (String) paramMap.get("oldPassword");
684 String newPassword = (String) paramMap.get("newPassword");
685 String newEmail = (String) paramMap.get("newEmail");
686
687 //Make sure the user name does not already exist
688 if (!previousUsername.equals(newUsername) && checkUserExists(newUsername))
689 {
690 addUserInformationToNode(previousUsername, serviceNode);
691 serviceNode.setAttribute("operation", ACCOUNT_SETTINGS);
692 GSXML.addError(result, _errorMessageMap.get(ERROR_USER_ALREADY_EXISTS));
693 return result;
694 }
695
696 String prevPassword = retrieveDataForUser(previousUsername, "password");
697
698 if (newPassword != null)
699 {
700 oldPassword = hashPassword(oldPassword);
701
702 if (oldPassword == null || !oldPassword.equals(prevPassword))
703 {
704 addUserInformationToNode(previousUsername, serviceNode);
705 serviceNode.setAttribute("operation", ACCOUNT_SETTINGS);
706 GSXML.addError(result, _errorMessageMap.get(ERROR_INCORRECT_PASSWORD), "Incorrect Password");
707 return result;
708 }
709
710 //Check the given password
711 int error;
712 if ((error = checkPassword(newPassword)) != NO_ERROR)
713 {
714 GSXML.addError(result, _errorMessageMap.get(error));
715 return result;
716 }
717
718 newPassword = hashPassword(newPassword);
719 }
720 else
721 {
722 newPassword = prevPassword;
723 }
724
725 //Check the given user name
726 int error;
727 if ((error = checkUsername(newUsername)) != NO_ERROR)
728 {
729 GSXML.addError(result, _errorMessageMap.get(error));
730 return result;
731 }
732
733 String prevGroups = retrieveDataForUser(previousUsername, "groups");
734 String prevStatus = retrieveDataForUser(previousUsername, "status");
735 String prevComment = retrieveDataForUser(previousUsername, "comment");
736
737 error = removeUser(previousUsername);
738 if (error != NO_ERROR)
739 {
740 if (error == ERROR_USERNAME_NOT_SPECIFIED)
741 {
742 addUserInformationToNode(null, serviceNode);
743 serviceNode.setAttribute("operation", LIST_USERS);
744 }
745 else
746 {
747 addUserInformationToNode(previousUsername, serviceNode);
748 serviceNode.setAttribute("operation", ACCOUNT_SETTINGS);
749 GSXML.addError(result, _errorMessageMap.get(error));
750 }
751 return result;
752 }
753
754 error = addUser(newUsername, newPassword, prevGroups, prevStatus, prevComment, newEmail);
755 if (error != NO_ERROR)
756 {
757 GSXML.addError(result, _errorMessageMap.get(error));
758 }
759
760 addUserInformationToNode(null, serviceNode);
761 serviceNode.setAttribute("operation", LIST_USERS);
762 }
763 else if (op.equals(PERFORM_RETRIEVE_PASSWORD))
764 {
765
766 }
767 else if (op.equals(PERFORM_CHANGE_PASSWORD))
768 {
769 serviceNode.setAttribute("operation", PERFORM_CHANGE_PASSWORD);
770 String user_name = (String) paramMap.get("username");
771 String oldPassword = (String) paramMap.get("oldPassword");
772 String newPassword = (String) paramMap.get("newPassword");
773 if (user_name == null || oldPassword == null || newPassword == null)
774 {
775 GSXML.addError(result, _errorMessageMap.get("missing compulsory parameters: username, oldPassword, or newPassword"));
776 return result;
777 }
778
779 String prevPassword = retrieveDataForUser(user_name, "password");
780 if (!hashPassword(oldPassword).equals(prevPassword))
781 {
782 addUserInformationToNode(user_name, serviceNode);
783 GSXML.addError(result, _errorMessageMap.get(ERROR_INCORRECT_PASSWORD), "Incorrect Password");
784 return result;
785 }
786
787 //Check the given password
788 int error;
789 if ((error = checkPassword(newPassword)) != NO_ERROR)
790 {
791 GSXML.addError(result, _errorMessageMap.get(error));
792 return result;
793 }
794
795 DerbyWrapper derbyWrapper = openDatabase();
796 String chpa_groups = retrieveDataForUser(user_name, "groups");
797 String chpa_comment = "password_changed_by_user";
798 String info = derbyWrapper.modifyUserInfo(user_name, hashPassword(newPassword), chpa_groups, null, chpa_comment, null);
799 derbyWrapper.closeDatabase();
800 if (info != "succeed")
801 {//see DerbyWrapper.modifyUserInfo
802 GSXML.addError(result, _errorMessageMap.get(info));
803 return result;
804 }
805 }
806 else if (op.equals(EDIT_USER))
807 {
808 String editUsername = (String) paramMap.get("username");
809 int error = addUserInformationToNode(editUsername, serviceNode);
810 if (error != NO_ERROR)
811 {
812 GSXML.addError(result, _errorMessageMap.get(error));
813 }
814 }
815 else if (op.equals(ACCOUNT_SETTINGS))
816 {
817 String editUsername = (String) paramMap.get("username");
818
819 if (editUsername == null)
820 {
821 serviceNode.setAttribute("operation", "");
822 GSXML.addError(result, _errorMessageMap.get(ERROR_USERNAME_NOT_SPECIFIED));
823 return result;
824 }
825
826 if (!editUsername.equals(username))
827 {
828 serviceNode.setAttribute("operation", LOGIN);
829 GSXML.addError(result, _errorMessageMap.get(ERROR_NOT_AUTHORISED));
830 return result;
831 }
832 int error = addUserInformationToNode(editUsername, serviceNode);
833 if (error != NO_ERROR)
834 {
835 GSXML.addError(result, _errorMessageMap.get(error));
836 }
837 }
838 else if (op.equals(PERFORM_RESET_PASSWORD))
839 {
840 String passwordResetUser = (String) paramMap.get("username");
841
842 String newPassword = UUID.randomUUID().toString();
843 newPassword = newPassword.substring(0, newPassword.indexOf("-"));
844
845 String email = retrieveDataForUser(passwordResetUser, "email");
846 String from = "[email protected]";
847 String host = request.getAttribute("remoteAddress");
848
849 //TODO: FINISH THIS
850 }
851 else if (op.equals(REGISTER))
852 {
853 if (_recaptchaPrivateKey != null && _recaptchaPrivateKey.length() > 0)
854 {
855 try
856 {
857 ReCaptchaImpl reCaptcha = new ReCaptchaImpl();
858 reCaptcha.setPrivateKey(_recaptchaPrivateKey);
859 reCaptcha.checkAnswer(request.getAttribute("remoteAddress"), "", "");
860 }
861 catch (Exception ex)
862 {
863 return result;
864 }
865 }
866
867 if (_recaptchaPublicKey != null && _recaptchaPrivateKey != null)
868 {
869 Element recaptchaElem = result_doc.createElement("recaptcha");
870 recaptchaElem.setAttribute("publicKey", _recaptchaPublicKey);
871 recaptchaElem.setAttribute("privateKey", _recaptchaPrivateKey);
872 result.appendChild(recaptchaElem);
873 }
874 }
875 else if (op.equals(PERFORM_DELETE_USER))
876 {
877 String usernameToDelete = (String) paramMap.get("username");
878 int error = removeUser(usernameToDelete);
879 if (error != NO_ERROR)
880 {
881 GSXML.addError(result, _errorMessageMap.get(error));
882 }
883 addUserInformationToNode(null, serviceNode);
884 serviceNode.setAttribute("operation", LIST_USERS);
885 }
886
887 return result;
888 }
889
890 public int checkUsernameAndPassword(String username, String password)
891 {
892 int uResult = checkUsername(username);
893 int pResult = checkPassword(password);
894
895 return (uResult != NO_ERROR ? uResult : (pResult != NO_ERROR ? pResult : NO_ERROR));
896 }
897
898 public int checkUsername(String username)
899 {
900 //Check the given user name
901 if ((username == null) || (username.length() < USERNAME_MIN_LENGTH) || (username.length() > USERNAME_MAX_LENGTH) || (!(Pattern.matches("[a-zA-Z0-9//_//.]+", username))))
902 {
903 return ERROR_INVALID_USERNAME;
904 }
905 return NO_ERROR;
906 }
907
908 public int checkPassword(String password)
909 {
910 //Check the given password
911 if (password == null)
912 {
913 return ERROR_PASSWORD_NOT_ENTERED;
914 }
915 else if (password.length() < PASSWORD_MIN_LENGTH)
916 {
917 return ERROR_PASSWORD_TOO_SHORT;
918 }
919 else if (password.length() > PASSWORD_MAX_LENGTH)
920 {
921 return ERROR_PASSWORD_TOO_LONG;
922 }
923 else if (!(Pattern.matches("[\\p{ASCII}]+", password)))
924 {
925 return ERROR_PASSWORD_USES_ILLEGAL_CHARACTERS;
926 }
927 return NO_ERROR;
928 }
929
930 public static String hashPassword(String password)
931 {
932 return DigestUtils.sha1Hex(password);
933 }
934
935 // This method can also be used for printing out the password in hex (in case
936 // the password used the UTF-8 Charset), or the hex values in any unicode string.
937 // From http://stackoverflow.com/questions/923863/converting-a-string-to-hexadecimal-in-java
938 public static String toHex(String arg)
939 {
940 try
941 {
942 return String.format("%x", new BigInteger(arg.getBytes("US-ASCII"))); // set to same charset as used by hashPassword
943 }
944 catch (Exception e)
945 { // UnsupportedEncodingException
946 e.printStackTrace();
947 }
948 return "Unable to print";
949 }
950
951 private void checkAdminUserExists()
952 {
953 DerbyWrapper derbyWrapper = openDatabase();
954 UserQueryResult userQueryResult = derbyWrapper.findUser(null, null);
955 derbyWrapper.closeDatabase();
956
957 if (userQueryResult != null)
958 {
959 Vector userInfo = userQueryResult.users;
960
961 boolean adminFound = false;
962 for (int i = 0; i < userQueryResult.getSize(); i++)
963 {
964 if (((UserTermInfo) userInfo.get(i)).groups != null && ((UserTermInfo) userInfo.get(i)).groups.matches(".*\\badministrator\\b.*"))
965 {
966 adminFound = true;
967 }
968 }
969
970 if (!adminFound)
971 {
972 addUser("admin", "admin", "administrator", "true", "Change the password for this account as soon as possible", "");
973 }
974 }
975 }
976
977 private DerbyWrapper openDatabase()
978 {
979 // check the usersDb database, if it isn't existing, check the etc dir, create the etc dir if it isn't existing, then create the user database and add a "admin" user
980 String usersDB_dir = GlobalProperties.getGSDL3Home() + File.separatorChar + "etc" + File.separatorChar + "usersDB";
981 DerbyWrapper derbyWrapper = new DerbyWrapper(usersDB_dir);
982 return derbyWrapper;
983 }
984
985 private int addUserInformationToNode(String username, Element serviceNode)
986 {
987 DerbyWrapper derbyWrapper = openDatabase();
988 UserQueryResult userQueryResult = derbyWrapper.findUser(username, null);
989 derbyWrapper.closeDatabase();
990
991 if (userQueryResult != null)
992 {
993 Element user_node = getUserNodeList(serviceNode.getOwnerDocument(), userQueryResult);
994 serviceNode.appendChild(user_node);
995 return NO_ERROR;
996 }
997
998 return ERROR_COULD_NOT_GET_USER_INFO;
999 }
1000
1001 private int removeUser(String username)
1002 {
1003 if (username == null)
1004 {
1005 return ERROR_USERNAME_NOT_SPECIFIED;
1006 }
1007
1008 DerbyWrapper derbyWrapper = openDatabase();
1009 boolean success = derbyWrapper.deleteUser(username);
1010 derbyWrapper.closeDatabase();
1011
1012 if (success)
1013 {
1014 return NO_ERROR;
1015 }
1016
1017 return ERROR_REMOVING_USER;
1018 }
1019
1020 private int addUser(String newUsername, String newPassword, String newGroups, String newStatus, String newComment, String newEmail)
1021 {
1022 newGroups = newGroups.replaceAll(" ", "");
1023
1024 //Check if the user already exists
1025 DerbyWrapper derbyWrapper = openDatabase();
1026 UserQueryResult userQueryResult = derbyWrapper.findUser(newUsername, null);
1027
1028 if (userQueryResult != null)
1029 {
1030 derbyWrapper.closeDatabase();
1031 return ERROR_USER_ALREADY_EXISTS;
1032 }
1033 else
1034 {
1035 boolean success = derbyWrapper.addUser(newUsername, newPassword, newGroups, newStatus, newComment, newEmail);
1036 derbyWrapper.closeDatabase();
1037
1038 if (!success)
1039 {
1040 return ERROR_ADDING_USER;
1041 }
1042 }
1043
1044 return NO_ERROR;
1045 }
1046
1047 private boolean checkUserExists(String username)
1048 {
1049 boolean check_status = false;
1050
1051 DerbyWrapper derbyWrapper = openDatabase();
1052 try
1053 {
1054 UserQueryResult result = derbyWrapper.findUser(username);
1055
1056 if (result != null)
1057 {
1058 check_status = true;
1059 }
1060
1061 }
1062 catch (Exception ex)
1063 {
1064 // some error occurred accessing the database
1065 ex.printStackTrace();
1066 }
1067 derbyWrapper.closeDatabase();
1068
1069 return check_status;
1070 }
1071
1072 private String retrieveDataForUser(String username, String dataType)
1073 {
1074 openDatabase();
1075
1076 String data = null;
1077
1078 try
1079 {
1080 DerbyWrapper derbyWrapper = openDatabase();
1081 UserQueryResult result = derbyWrapper.findUser(username);
1082 derbyWrapper.closeDatabase();
1083 Vector userInfo = result.users;
1084
1085 for (int i = 0; i < result.getSize(); i++)
1086 {
1087 if (dataType.equals("password"))
1088 {
1089 data = ((UserTermInfo) userInfo.get(i)).password;
1090 break;
1091 }
1092 else if (dataType.equals("groups"))
1093 {
1094 data = ((UserTermInfo) userInfo.get(i)).groups;
1095 break;
1096 }
1097 else if (dataType.equals("status"))
1098 {
1099 data = ((UserTermInfo) userInfo.get(i)).accountstatus;
1100 break;
1101 }
1102 else if (dataType.equals("comment"))
1103 {
1104 data = ((UserTermInfo) userInfo.get(i)).comment;
1105 break;
1106 }
1107 else if (dataType.equals("email"))
1108 {
1109 data = ((UserTermInfo) userInfo.get(i)).email;
1110 break;
1111 }
1112 }
1113 }
1114 catch (Exception ex)
1115 {
1116 ex.printStackTrace();
1117 }
1118
1119 return data;
1120 }
1121
1122 private Element getUserNodeList(Document doc, UserQueryResult userQueryResult)
1123 {
1124 Element user_list_node = doc.createElement(GSXML.USER_NODE_ELEM + GSXML.LIST_MODIFIER);
1125
1126 Vector userInfo = userQueryResult.users;
1127
1128 for (int i = 0; i < userQueryResult.getSize(); i++)
1129 {
1130 Element user_node = doc.createElement(GSXML.USER_NODE_ELEM);
1131 String username = ((UserTermInfo) userInfo.get(i)).username;
1132 String groups = ((UserTermInfo) userInfo.get(i)).groups;
1133 String accountstatus = ((UserTermInfo) userInfo.get(i)).accountstatus;
1134 String comment = ((UserTermInfo) userInfo.get(i)).comment;
1135 String email = ((UserTermInfo) userInfo.get(i)).email;
1136 user_node.setAttribute("username", username);
1137 user_node.setAttribute("groups", groups);
1138 user_node.setAttribute("status", accountstatus);
1139 user_node.setAttribute("comment", comment);
1140 user_node.setAttribute("email", email);
1141
1142 user_list_node.appendChild(user_node);
1143 }
1144 return user_list_node;
1145 }
1146
1147 private Element getCollectList(Document doc, String collect)
1148 {
1149 Element collect_list_node = doc.createElement(GSXML.COLLECTION_ELEM + GSXML.LIST_MODIFIER);
1150 File[] collect_dir = (new File(collect)).listFiles();
1151 if (collect_dir != null && collect_dir.length > 0)
1152 {
1153 for (int i = 0; i < collect_dir.length; i++)
1154 {
1155 if (collect_dir[i].isDirectory() && (!collect_dir[i].getName().startsWith(".svn")))
1156 {
1157 Element collect_node = doc.createElement(GSXML.COLLECTION_ELEM);
1158 collect_node.setAttribute(GSXML.NAME_ATT, collect_dir[i].getName());
1159 collect_list_node.appendChild(collect_node);
1160 }
1161 }
1162 }
1163 return collect_list_node;
1164 }
1165
1166 // main() method - calls hashPassword() on any String argument, printing this to stdout
1167 // This main() is invoked by gliserver.pl perl code to encrypt passwords identically to Java code.
1168 public static void main(String[] args)
1169 {
1170 if (args.length < 1)
1171 {
1172 System.err.println("Usage: Authentication <string to encrypt>");
1173 System.exit(-1);
1174 }
1175 // just hash the first argument
1176 String hash = Authentication.hashPassword(args[0]);
1177 System.out.println(hash);
1178 }
1179}
Note: See TracBrowser for help on using the repository browser.