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

Last change on this file since 26914 was 26914, checked in by davidb, 11 years ago

Fixed two silly typos

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