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

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

Some fixes for when using the derby database as well as some improvements

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