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

Last change on this file since 26198 was 25852, checked in by sjm84, 12 years ago

Fixed email not being properly set in the admin interface. Also increased the maximum password length from 8 to 64. Also made password errors more useful.

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