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
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 DerbyWrapper _derbyWrapper = null;
130
131 protected String _recaptchaPrivateKey = null;
132 protected String _recaptchaPublicKey = null;
133
134 /** constructor */
135 public Authentication()
136 {
137 }
138
139 public boolean configure(Element info, Element extra_info)
140 {
141 logger.info("Configuring Authentication...");
142 this.config_info = info;
143
144 // set up Authentication service info - for now just has name and type
145 Element authentication_service = this.doc.createElement(GSXML.SERVICE_ELEM);
146 authentication_service.setAttribute(GSXML.TYPE_ATT, "authen");
147 authentication_service.setAttribute(GSXML.NAME_ATT, AUTHENTICATION_SERVICE);
148 this.short_service_info.appendChild(authentication_service);
149
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
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
177 return true;
178 }
179
180 protected Element getServiceDescription(String service_id, String lang, String subset)
181 {
182
183 Element authen_service = this.doc.createElement(GSXML.SERVICE_ELEM);
184
185 if (service_id.equals(AUTHENTICATION_SERVICE))
186 {
187 authen_service.setAttribute(GSXML.TYPE_ATT, "authen");
188 authen_service.setAttribute(GSXML.NAME_ATT, AUTHENTICATION_SERVICE);
189 }
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 }
195 else
196 {
197 return null;
198 }
199
200 if (service_id.equals(AUTHENTICATION_SERVICE) && (subset == null || subset.equals(GSXML.DISPLAY_TEXT_ELEM + GSXML.LIST_MODIFIER)))
201 {
202 authen_service.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_NAME, getServiceName(service_id, lang)));
203 authen_service.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_DESCRIPTION, getServiceDescription(service_id, lang)));
204 }
205 return authen_service;
206 }
207
208 protected String getServiceName(String service_id, String lang)
209 {
210 return getTextString(service_id + ".name", lang);
211 }
212
213 protected String getServiceSubmit(String service_id, String lang)
214 {
215 return getTextString(service_id + ".submit", lang);
216 }
217
218 protected String getServiceDescription(String service_id, String lang)
219 {
220 return getTextString(service_id + ".description", lang);
221 }
222
223 protected void addCustomParams(String service, Element param_list, String lang)
224 {
225 }
226
227 protected void createParameter(String name, Element param_list, String lang)
228 {
229 }
230
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 {
242 GSXML.addError(this.doc, result, _errorMessageMap.get(ERROR_REQUEST_HAS_NO_PARAM_LIST));
243 return result;
244 }
245
246 HashMap<String, Serializable> params = GSXML.extractParams(paramList, true);
247
248 String username = (String) params.get("username");
249
250 if (username == null)
251 {
252 GSXML.addError(this.doc, result, _errorMessageMap.get(ERROR_USERNAME_NOT_SPECIFIED));
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 {
269 GSXML.addError(this.doc, result, _errorMessageMap.get(ERROR_REQUESTED_USER_NOT_FOUND));
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
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);
282
283 userInfoList.appendChild(usernameField);
284 userInfoList.appendChild(passwordField);
285 userInfoList.appendChild(groupsField);
286 userInfoList.appendChild(accountStatusField);
287 userInfoList.appendChild(commentField);
288 }
289 catch (SQLException ex)
290 {
291 GSXML.addError(this.doc, result, _errorMessageMap.get(ERROR_SQL_EXCEPTION));
292 ex.printStackTrace();
293 }
294
295 return result;
296 }
297
298 protected Element processAuthentication(Element request)
299 {
300 checkAdminUserExists();
301
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);
306
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
316 // Get the parameters of the request
317 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
318 if (param_list == null)
319 {
320 serviceNode.setAttribute("operation", LOGIN);
321 GSXML.addError(this.doc, result, _errorMessageMap.get(ERROR_REQUEST_HAS_NO_PARAM_LIST));
322 return result; // Return the empty result
323 }
324 HashMap<String, Serializable> paramMap = GSXML.extractParams(param_list, false);
325 String op = (String) paramMap.get("authpage");
326 serviceNode.setAttribute("operation", op);
327
328 String username = null;
329 String groups = null;
330
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 }
338
339 if (userInformation != null)
340 {
341 username = userInformation.getAttribute(GSXML.USERNAME_ATT);
342 groups = userInformation.getAttribute(GSXML.GROUPS_ATT);
343 }
344
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 }
351
352 if (_adminOpList.contains(op) && (groups == null || !groups.matches(".*\\badministrator\\b.*")))
353 {
354 serviceNode.setAttribute("operation", LOGIN);
355 GSXML.addError(this.doc, result, _errorMessageMap.get(ERROR_ADMIN_NOT_LOGGED_IN));
356 return result;
357 }
358
359 if (op.equals(LIST_USERS))
360 {
361 int error = addUserInformationToNode(null, serviceNode);
362 if (error != NO_ERROR)
363 {
364 GSXML.addError(this.doc, result, _errorMessageMap.get(error));
365 }
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");
375
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 }
383
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);
394 if (error != NO_ERROR)
395 {
396 serviceNode.setAttribute("operation", ADD_USER);
397 GSXML.addError(this.doc, result, _errorMessageMap.get(error));
398 }
399 else
400 {
401 addUserInformationToNode(null, serviceNode);
402 serviceNode.setAttribute("operation", LIST_USERS);
403 }
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");
410
411 //Check the given user name
412 int error;
413 if ((error = checkUsername(newUsername)) != NO_ERROR)
414 {
415 GSXML.addError(this.doc, result, _errorMessageMap.get(error));
416 return result;
417 }
418
419 //Check the given password
420 if ((error = checkPassword(newPassword)) != NO_ERROR)
421 {
422 GSXML.addError(this.doc, result, _errorMessageMap.get(error));
423 return result;
424 }
425
426 newPassword = hashPassword(newPassword);
427
428 if (_recaptchaPrivateKey != null && _recaptchaPrivateKey.length() > 0)
429 {
430 ReCaptchaImpl reCaptcha = new ReCaptchaImpl();
431 reCaptcha.setPrivateKey(_recaptchaPrivateKey);
432
433 try
434 {
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.
438 reCaptcha.checkAnswer(request.getAttribute("remoteAddress"), "", "");
439
440 String challenge = (String) paramMap.get("recaptcha_challenge_field");
441 String uResponse = (String) paramMap.get("recaptcha_response_field");
442
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)
460 {
461 }
462 }
463
464 error = addUser(newUsername, newPassword, "", "true", "", newEmail);
465 if (error != NO_ERROR)
466 {
467 serviceNode.setAttribute("operation", REGISTER);
468 GSXML.addError(this.doc, result, _errorMessageMap.get(error));
469 }
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");
479 String newEmail = (String) paramMap.get("newEmail");
480
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
489 if (newPassword == null)
490 {
491 newPassword = retrieveDataForUser(previousUsername, "password");
492 }
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 }
501
502 newPassword = hashPassword(newPassword);
503 }
504
505 error = removeUser(previousUsername);
506 if (error != NO_ERROR)
507 {
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;
519 }
520
521 error = addUser(newUsername, newPassword, newGroups, newStatus, newComment, newEmail);
522 if (error != NO_ERROR)
523 {
524 serviceNode.setAttribute("operation", EDIT_USER);
525 GSXML.addError(this.doc, result, _errorMessageMap.get(error));
526 }
527 else
528 {
529 addUserInformationToNode(null, serviceNode);
530 serviceNode.setAttribute("operation", LIST_USERS);
531 }
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))
543 {
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;
548 }
549
550 String prevPassword = retrieveDataForUser(previousUsername, "password");
551
552 if (newPassword != null)
553 {
554 oldPassword = hashPassword(oldPassword);
555
556 if (oldPassword == null || !oldPassword.equals(prevPassword))
557 {
558 addUserInformationToNode(previousUsername, serviceNode);
559 serviceNode.setAttribute("operation", ACCOUNT_SETTINGS);
560 GSXML.addError(this.doc, result, _errorMessageMap.get(ERROR_INCORRECT_PASSWORD), "Incorrect Password");
561 return result;
562 }
563
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 }
571
572 newPassword = hashPassword(newPassword);
573 }
574 else
575 {
576 newPassword = prevPassword;
577 }
578
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 }
586
587 String prevGroups = retrieveDataForUser(previousUsername, "groups");
588 String prevStatus = retrieveDataForUser(previousUsername, "status");
589 String prevComment = retrieveDataForUser(previousUsername, "comment");
590
591 error = removeUser(previousUsername);
592 if (error != NO_ERROR)
593 {
594 if (error == ERROR_USERNAME_NOT_SPECIFIED)
595 {
596 addUserInformationToNode(null, serviceNode);
597 serviceNode.setAttribute("operation", LIST_USERS);
598 }
599 else
600 {
601 addUserInformationToNode(previousUsername, serviceNode);
602 serviceNode.setAttribute("operation", ACCOUNT_SETTINGS);
603 GSXML.addError(this.doc, result, _errorMessageMap.get(error));
604 }
605 return result;
606 }
607
608 error = addUser(newUsername, newPassword, prevGroups, prevStatus, prevComment, newEmail);
609 if (error != NO_ERROR)
610 {
611 GSXML.addError(this.doc, result, _errorMessageMap.get(error));
612 }
613
614 addUserInformationToNode(null, serviceNode);
615 serviceNode.setAttribute("operation", LIST_USERS);
616 }
617 else if (op.equals(PERFORM_RETRIEVE_PASSWORD))
618 {
619
620 }
621 else if (op.equals(PERFORM_CHANGE_PASSWORD))
622 {
623 serviceNode.setAttribute("operation", PERFORM_CHANGE_PASSWORD);
624 String user_name = (String) paramMap.get("username");
625 String oldPassword = (String) paramMap.get("oldPassword");
626 String newPassword = (String) paramMap.get("newPassword");
627 if (user_name == null || oldPassword == null || newPassword == null)
628 {
629 GSXML.addError(this.doc, result, _errorMessageMap.get("missing compulsory parameters: username, oldPassword, or newPassword"));
630 return result;
631 }
632
633 String prevPassword = retrieveDataForUser(user_name, "password");
634 if (!hashPassword(oldPassword).equals(prevPassword))
635 {
636 addUserInformationToNode(user_name, serviceNode);
637 GSXML.addError(this.doc, result, _errorMessageMap.get(ERROR_INCORRECT_PASSWORD), "Incorrect Password");
638 return result;
639 }
640
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 }
648
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);
652 if (info != "succeed")
653 {//see DerbyWrapper.modifyUserInfo
654 GSXML.addError(this.doc, result, _errorMessageMap.get(info));
655 return result;
656 }
657 }
658 else if (op.equals(EDIT_USER))
659 {
660 String editUsername = (String) paramMap.get("username");
661 int error = addUserInformationToNode(editUsername, serviceNode);
662 if (error != NO_ERROR)
663 {
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");
670
671 if (editUsername == null)
672 {
673 serviceNode.setAttribute("operation", "");
674 GSXML.addError(this.doc, result, _errorMessageMap.get(ERROR_USERNAME_NOT_SPECIFIED));
675 return result;
676 }
677
678 if (!editUsername.equals(username))
679 {
680 serviceNode.setAttribute("operation", LOGIN);
681 GSXML.addError(this.doc, result, _errorMessageMap.get(ERROR_NOT_AUTHORISED));
682 return result;
683 }
684 int error = addUserInformationToNode(editUsername, serviceNode);
685 if (error != NO_ERROR)
686 {
687 GSXML.addError(this.doc, result, _errorMessageMap.get(error));
688 }
689 }
690 else if (op.equals(PERFORM_RESET_PASSWORD))
691 {
692 String passwordResetUser = (String) paramMap.get("username");
693
694 String newPassword = UUID.randomUUID().toString();
695 newPassword = newPassword.substring(0, newPassword.indexOf("-"));
696
697 String email = retrieveDataForUser(passwordResetUser, "email");
698 String from = "[email protected]";
699 String host = request.getAttribute("remoteAddress");
700
701 //TODO: FINISH THIS
702 }
703 else if (op.equals(REGISTER))
704 {
705 if (_recaptchaPrivateKey != null && _recaptchaPrivateKey.length() > 0)
706 {
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 }
717 }
718
719 if (_recaptchaPublicKey != null && _recaptchaPrivateKey != null)
720 {
721 Element recaptchaElem = this.doc.createElement("recaptcha");
722 recaptchaElem.setAttribute("publicKey", _recaptchaPublicKey);
723 recaptchaElem.setAttribute("privateKey", _recaptchaPrivateKey);
724 result.appendChild(recaptchaElem);
725 }
726 }
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)
732 {
733 GSXML.addError(this.doc, result, _errorMessageMap.get(error));
734 }
735 addUserInformationToNode(null, serviceNode);
736 serviceNode.setAttribute("operation", LIST_USERS);
737 }
738
739 return result;
740 }
741
742 public int checkUsernameAndPassword(String username, String password)
743 {
744 int uResult = checkUsername(username);
745 int pResult = checkPassword(password);
746
747 return (uResult != NO_ERROR ? uResult : (pResult != NO_ERROR ? pResult : NO_ERROR));
748 }
749
750 public int checkUsername(String username)
751 {
752 //Check the given user name
753 if ((username == null) || (username.length() < USERNAME_MIN_LENGTH) || (username.length() > USERNAME_MAX_LENGTH) || (!(Pattern.matches("[a-zA-Z0-9//_//.]+", username))))
754 {
755 return ERROR_INVALID_USERNAME;
756 }
757 return NO_ERROR;
758 }
759
760 public int checkPassword(String password)
761 {
762 //Check the given password
763 if (password == null)
764 {
765 return ERROR_PASSWORD_NOT_ENTERED;
766 }
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 }
779 return NO_ERROR;
780 }
781
782 public static String hashPassword(String password)
783 {
784 return DigestUtils.sha1Hex(password);
785 }
786
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 }
802
803 private void checkAdminUserExists()
804 {
805 if (_derbyWrapper == null)
806 {
807 openDatabase();
808 }
809
810 UserQueryResult userQueryResult = _derbyWrapper.findUser(null, null);
811 closeDatabase();
812
813 if (userQueryResult != null)
814 {
815 Vector userInfo = userQueryResult.users;
816
817 boolean adminFound = false;
818 for (int i = 0; i < userQueryResult.getSize(); i++)
819 {
820 if (((UserTermInfo) userInfo.get(i)).groups != null && ((UserTermInfo) userInfo.get(i)).groups.matches(".*\\badministrator\\b.*"))
821 {
822 adminFound = true;
823 }
824 }
825
826 if (!adminFound)
827 {
828 addUser("admin", "admin", "administrator", "true", "Change the password for this account as soon as possible", "");
829 }
830 }
831
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())
843 {
844 String etc_dir = this.site_home + File.separatorChar + "etc";
845 File etc_file = new File(etc_dir);
846 if (!etc_file.exists())
847 {
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 }
854 }
855 _derbyWrapper.connectDatabase(usersDB_dir, true);
856 _derbyWrapper.createDatabase();
857 }
858 else
859 {
860 _derbyWrapper.connectDatabase(usersDB_dir, false);
861 }
862 return true;
863 }
864
865 private void closeDatabase()
866 {
867 if (_derbyWrapper != null)
868 {
869 _derbyWrapper.closeDatabase();
870 _derbyWrapper = null;
871 }
872 }
873
874 private int addUserInformationToNode(String username, Element serviceNode)
875 {
876 if (_derbyWrapper == null)
877 {
878 openDatabase();
879 }
880
881 UserQueryResult userQueryResult = _derbyWrapper.findUser(username, null);
882 closeDatabase();
883
884 if (userQueryResult != null)
885 {
886 Element user_node = getUserNode(userQueryResult);
887 serviceNode.appendChild(user_node);
888 closeDatabase();
889 return NO_ERROR;
890 }
891
892 closeDatabase();
893 return ERROR_COULD_NOT_GET_USER_INFO;
894 }
895
896 private int removeUser(String username)
897 {
898 if (username == null)
899 {
900 return ERROR_USERNAME_NOT_SPECIFIED;
901 }
902
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 {
932 closeDatabase();
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)
940 {
941 closeDatabase();
942 return ERROR_ADDING_USER;
943 }
944 }
945 closeDatabase();
946 return NO_ERROR;
947 }
948
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)
961 {
962 return true;
963 }
964 else
965 {
966 return false;
967 }
968
969 }
970 catch (Exception ex)
971 {
972 return false;
973 }
974 finally
975 {
976 closeDatabase();
977 }
978 }
979
980 private String retrieveDataForUser(String username, String dataType)
981 {
982 if (_derbyWrapper == null)
983 {
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++)
995 {
996 if (dataType.equals("password"))
997 {
998 return ((UserTermInfo) userInfo.get(i)).password;
999 }
1000 else if (dataType.equals("groups"))
1001 {
1002 return ((UserTermInfo) userInfo.get(i)).groups;
1003 }
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 }
1016 }
1017 }
1018 catch (Exception ex)
1019 {
1020 ex.printStackTrace();
1021 }
1022
1023 closeDatabase();
1024 return password;
1025 }
1026
1027 private Element getUserNode(UserQueryResult userQueryResult)
1028 {
1029 Element user_list_node = this.doc.createElement(GSXML.USER_NODE_ELEM + "List");
1030
1031 Vector userInfo = userQueryResult.users;
1032
1033 for (int i = 0; i < userQueryResult.getSize(); i++)
1034 {
1035 Element user_node = this.doc.createElement(GSXML.USER_NODE_ELEM);
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);
1046
1047 user_list_node.appendChild(user_node);
1048 }
1049 return user_list_node;
1050 }
1051
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 {
1062 Element collect_node = this.doc.createElement(GSXML.COLLECTION_ELEM);
1063 collect_node.setAttribute("name", collect_dir[i].getName());
1064 collect_list_node.appendChild(collect_node);
1065 }
1066 }
1067 }
1068 return collect_list_node;
1069 }
1070
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);
1083 }
1084}
Note: See TracBrowser for help on using the repository browser.