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

Last change on this file since 26233 was 26233, checked in by xiao, 12 years ago

added user required operation PerformChangePassword

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