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

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

Check and make sure a user is authenticated before allowing changes to be made

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