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

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

Should now create a database on server start

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