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

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

Reformatting this file ahead of some changes

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