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

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

operation PerformChangePassword, now uses modifyUserInfo instead of removeUser then addUser

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