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

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

Fixed a silly mistake that was causing non-admins to not be able to change their account settings

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