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

Last change on this file since 28235 was 28235, checked in by sjm84, 11 years ago

Removed some print statements

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