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

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

Hopefully fixed the hashing algorithm so that it produces the same result on 32-bit and 64-bit machines

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