1 19 package org.openharmonise.rm.security.authentication; 20 21 import java.sql.*; 22 import java.util.*; 23 import java.util.Date ; 24 import java.util.logging.*; 25 26 import org.openharmonise.commons.cache.*; 27 import org.openharmonise.commons.dsi.*; 28 import org.openharmonise.commons.dsi.dml.*; 29 import org.openharmonise.rm.DataAccessException; 30 import org.openharmonise.rm.config.*; 31 import org.openharmonise.rm.dsi.DataStoreInterfaceFactory; 32 import org.openharmonise.rm.factory.*; 33 import org.openharmonise.rm.resources.AbstractObject; 34 import org.openharmonise.rm.resources.lifecycle.*; 35 import org.openharmonise.rm.resources.users.User; 36 import org.openharmonise.rm.security.authorization.*; 37 38 39 49 public class UserAuthenticatorImpl implements UserAuthenticator, EditEventListener { 50 51 public static final String PNAME_PWD_MIN_LENGTH = "PWD_MIN_LENGTH"; 52 public static final String PNAME_PWD_FORCE_ALPHA_CHAR = 53 "PWD_FORCE_ALPHA_CHAR"; 54 public static final String PNAME_PWD_FORCE_NUM_CHAR = "PWD_FORCE_NUM_CHAR"; 55 public static final String PNAME_PWD_FORCE_MIX_CASE = "PWD_FORCE_MIX_CASE"; 56 public static final String PNAME_PWD_RETRY_LIMIT = "PWD_RETRY_LIMIT"; 57 public static final String PNAME_PWD_CHANGE_PERIOD = "PWD_CHANGE_PERIOD"; 58 public static final String PNAME_PWD_HIST_CHECK_SIZE = 59 "PWD_HIST_CHECK_SIZE"; 60 61 public static final int INVALID_PWD_LENGTH = 1; 62 public static final int INVALID_PWD_NO_ALPHA = 2; 63 public static final int INVALID_PWD_NO_NUM = 3; 64 public static final int INVALID_PWD_NO_CASE_MIX = 4; 65 public static final int INVALID_PWD_REPEAT = 5; 66 public static final int INVALID_USER_STATE = 6; 67 68 private static final String CLMN_FAIL_COUNT = "login_fail_count"; 69 private static final String CLMN_PWD_CHANGE_DATE = "pwd_change_date"; 70 71 74 private static Logger m_logger = Logger.getLogger(UserAuthenticatorImpl.class.getName()); 75 76 AbstractDataStoreInterface m_dsi = null; 77 78 private Map m_userCache = Collections.synchronizedMap(new HashMap()); 79 private PasswordHelper helper; 80 81 84 public UserAuthenticatorImpl() { 85 super(); 86 try { 87 m_dsi = DataStoreInterfaceFactory.getDataStoreInterface(); 88 } catch (DataStoreException e) { 89 m_logger.log(Level.WARNING, e.getLocalizedMessage(), e); 90 throw new IllegalStateException ( 91 "Error obtaining datastore interface:" 92 + e.getLocalizedMessage()); 93 94 } 95 } 96 97 public UserAuthenticatorImpl(PasswordHelper helper) { 98 this(); 99 this.helper = helper; 100 } 101 102 105 public boolean authenticate(User usr, String pwd) 106 throws UserAuthenticationException { 107 boolean bAuth = false; 108 109 if (usr != null) { 110 try { 111 AbstractDataStoreInterface dsi = 112 DataStoreInterfaceFactory.getDataStoreInterface(); 113 ColumnRef failCountCol = 114 new ColumnRef( 115 usr.getDBTableName(), 116 CLMN_FAIL_COUNT, 117 ColumnRef.NUMBER); 118 119 SelectStatement select = new SelectStatement(); 120 121 select.addSelectColumn(failCountCol); 122 123 ColumnRef keyCol = 124 usr.getInstanceColumnRef(AbstractObject.ATTRIB_KEY, false); 125 126 select.addWhereCondition(keyCol, "=", usr.getKey()); 127 128 ResultSet rs = dsi.execute(select); 129 int nFailCount = 0; 130 if (rs.next()) { 131 nFailCount = rs.getInt(1); 132 if (nFailCount < 0) { 133 nFailCount = 0; 134 } 135 } 136 137 rs.close(); 138 139 int nFailRetryLimit = 140 ConfigSettings.getIntProperty(PNAME_PWD_RETRY_LIMIT, "-1"); 141 142 if (nFailRetryLimit > 0 && nFailCount >= nFailRetryLimit) { 143 if(m_logger.isLoggable(Level.FINE)) { 144 m_logger.logp(Level.FINE, this.getClass().getName(), "authenticate", "Login fail limit has been reached for user " + usr.getId() + ", password attempt - " + pwd); 145 } 146 throw new LoginRetryLimitException(); 147 } 148 149 bAuth = helper.compare(pwd, usr.getPassword(), usr.getSalt()); 151 153 UpdateStatement update = new UpdateStatement(); 154 155 if (bAuth == true && nFailCount != 0) { 156 update.addColumnValue(failCountCol, 0); 157 update.addWhereCondition(keyCol, "=", usr.getKey()); 158 dsi.execute(update); 159 160 } else if(bAuth == false) { 161 update.addColumnValue(failCountCol, nFailCount + 1); 162 update.addWhereCondition(keyCol, "=", usr.getKey()); 163 dsi.execute(update); 164 } 165 166 if (bAuth == true) { 167 if(hasPasswordExpired(usr) == true) { 168 if(m_logger.isLoggable(Level.FINE)) { 169 m_logger.logp(Level.FINE, this.getClass().getName(), "authenticate", "Password has expired for user " + usr.getId() + " and password " + pwd); 170 } 171 throw new PasswordExpiredException(); 172 } 173 } 174 } catch (DataAccessException e) { 175 throw new UserAuthenticationException(e); 176 } catch (DataStoreException e) { 177 throw new UserAuthenticationException(e); 178 } catch (SQLException e) { 179 throw new UserAuthenticationException(e); 180 } catch (ConfigException e) { 181 throw new UserAuthenticationException(e); 182 } 183 184 if(m_logger.isLoggable(Level.FINER)) { 185 m_logger.logp(Level.FINER, this.getClass().getName(), "authenticate", "Authenticating user " + usr.getId() + " with password '" + pwd + "'"); 186 } 187 } 188 189 return bAuth; 190 } 191 192 195 public int setPassword( 196 User authUsr, 197 User pwdUsr, 198 String authPwd, 199 String newPwd) 200 throws UserAuthenticationException { 201 int nCode = -1; 202 203 try { 204 205 boolean bAuth = false; 206 207 try { 208 bAuth = authenticate(authUsr, authPwd); 209 } catch (PasswordExpiredException exp_e) { 210 bAuth = true; 211 } 212 213 if (bAuth == true) { 214 if (authUsr.equals(pwdUsr) == true 215 || AuthorizationValidator.isSuperUser(authUsr) == true) { 216 217 if ((pwdUsr.isPendingVersion() == true 218 && pwdUsr.getLiveVersion() != null) 219 || pwdUsr.getPendingVersions().size() > 0) { 220 nCode = INVALID_USER_STATE; 221 } else { 222 nCode = validatePassword(pwdUsr, newPwd); 223 224 if (nCode == PWD_OK) { 225 boolean bIsApproved = 226 (pwdUsr.getStatus() == Status.APPROVED); 227 228 User newPwdUsr = (User) pwdUsr.createNewVersion(); 229 230 newPwdUsr.setPassword(helper.getNewPassword(newPwd, pwdUsr.getSalt())); 233 234 newPwdUsr = (User) newPwdUsr.save(); 235 236 if (bIsApproved) { 237 newPwdUsr.changeStatus(Status.APPROVED); 238 } 239 240 } 241 } 242 243 } else { 244 nCode = AUTHENTICATION_FAIL; 245 } 246 } else { 247 nCode = AUTHENTICATION_FAIL; 248 } 249 } catch (EditException e) { 250 throw new UserAuthenticationException( 251 "Error occured while changing password", 252 e); 253 } catch (AuthorizationException e) { 254 throw new UserAuthenticationException( 255 "Error occured while checking super user status", 256 e); 257 } catch (DataAccessException e) { 258 throw new UserAuthenticationException( 259 "Error occured while checking user pending status", 260 e); 261 } 262 263 return nCode; 264 } 265 266 269 public User getUser(String sUser, String sPwd) 270 throws UserAuthenticationException { 271 User usr = getUser(sUser); 272 273 try { 274 if (helper.compare(sPwd, usr.getPassword(), usr.getSalt()) == false) { 276 usr = null; 277 } 278 } catch (DataAccessException e) { 282 throw new UserAuthenticationException(e); 283 } 284 285 return usr; 286 } 287 288 296 public int validatePassword(User usr, String sPassword) 297 throws UserAuthenticationException { 298 boolean bIsValid = true; 299 int nStatus = PWD_OK; 300 301 try { 302 ConfigSettings config = ConfigSettings.getInstance(); 303 304 int nMinLen = 306 ConfigSettings.getIntProperty(PNAME_PWD_MIN_LENGTH, "-1"); 307 308 if (nMinLen > 0 && sPassword.length() < nMinLen) { 309 bIsValid = false; 310 nStatus = INVALID_PWD_LENGTH; 311 } 312 313 if (bIsValid == true) { 315 boolean bAlphaReq = 316 ConfigSettings.getBoolProperty( 317 PNAME_PWD_FORCE_ALPHA_CHAR, 318 "false"); 319 boolean bAlphaMix = 320 ConfigSettings.getBoolProperty( 321 PNAME_PWD_FORCE_MIX_CASE, 322 "false"); 323 324 if (bAlphaReq == true || bAlphaMix == true) { 325 boolean bHasUpperAlpha = false; 326 boolean bHasLowerAlpha = false; 327 328 for (int i = 0; i < sPassword.length(); i++) { 329 char c = sPassword.charAt(i); 330 331 if (Character.isLowerCase(c)) { 332 bHasLowerAlpha = true; 333 } 334 335 if (Character.isUpperCase(c)) { 336 bHasUpperAlpha = true; 337 } 338 } 339 340 if (bAlphaMix == true) { 341 bIsValid = bHasUpperAlpha && bHasLowerAlpha; 342 343 if (bIsValid == false) { 344 if(bAlphaReq == true 345 && bHasLowerAlpha == false 346 && bHasLowerAlpha == false) { 347 nStatus = INVALID_PWD_NO_ALPHA; 348 } else { 349 nStatus = INVALID_PWD_NO_CASE_MIX; 350 } 351 } 352 } else { 353 bIsValid = bHasUpperAlpha || bHasLowerAlpha; 354 if (bIsValid == false) { 355 nStatus = INVALID_PWD_NO_ALPHA; 356 } 357 } 358 359 } 360 } 361 362 if (bIsValid == true) { 364 boolean bNumReq = 365 ConfigSettings.getBoolProperty( 366 PNAME_PWD_FORCE_NUM_CHAR, 367 "false"); 368 369 if (bNumReq == true) { 370 boolean bHasNum = false; 371 for (int i = 0; 372 i < sPassword.length() && bHasNum == false; 373 i++) { 374 char c = sPassword.charAt(i); 375 376 if (Character.isDigit(c)) { 377 bHasNum = true; 378 } 379 } 380 bIsValid = bHasNum; 381 382 if (bIsValid == false) { 383 nStatus = INVALID_PWD_NO_NUM; 384 } 385 } 386 } 387 388 if (bIsValid == true) { 390 int pwdNum = 391 ConfigSettings.getIntProperty( 392 PNAME_PWD_HIST_CHECK_SIZE, 393 "1"); 394 395 if (sPassword.equals(usr.getPassword())) { 396 nStatus = INVALID_PWD_REPEAT; 397 bIsValid = false; 398 } 399 400 if (pwdNum > 0 && bIsValid == true) { 401 402 SelectStatement select = new SelectStatement(); 403 404 select.addSelectColumn( 405 usr.getInstanceColumnRef(User.TAG_PASSWORD, true)); 406 ColumnRef verCol = 407 usr.getInstanceColumnRef(User.TAG_VERSION, true); 408 409 select.addSelectColumn(verCol); 410 411 select.addWhereCondition( 412 usr.getInstanceColumnRef(User.ATTRIB_ID, true), 413 "=", 414 usr.getId()); 415 416 select.setOrderBy(verCol); 417 select.setDistinct(true); 418 419 ResultSet rs = m_dsi.execute(select); 420 421 int i = 0; 422 boolean bFound = false; 423 while (rs.next() && i < pwdNum && bFound == false) { 424 String sOldPwd = rs.getString(1); 425 426 if (sOldPwd.equals(sPassword) == true) { 427 bFound = true; 428 } 429 430 i++; 431 } 432 433 rs.close(); 434 435 if (bFound == true) { 436 nStatus = INVALID_PWD_REPEAT; 437 bIsValid = false; 438 } 439 440 } 441 } 442 443 } catch (ConfigException e) { 444 throw new UserAuthenticationException(e.getLocalizedMessage(), e); 445 } catch (SQLException e) { 446 throw new UserAuthenticationException(e.getLocalizedMessage(), e); 447 } catch (DataStoreException e) { 448 throw new UserAuthenticationException(e.getLocalizedMessage(), e); 449 } catch (DataAccessException e) { 450 throw new UserAuthenticationException(e.getLocalizedMessage(), e); 451 } 452 453 if(m_logger.isLoggable(Level.FINE)) { 454 m_logger.logp(Level.FINE, this.getClass().getName(),"validatePassword", "Validated password '" + sPassword + "' for user " + usr.getId() + ", returning status - " + nStatus); 455 } 456 457 return nStatus; 458 } 459 460 463 public User getUser(String sUser) throws UserAuthenticationException { 464 User usr = null; 465 466 467 ResultSet rs = null; 468 469 470 try { 471 CachePointer uptr = (CachePointer) m_userCache.get(sUser); 472 473 if(uptr == null) { 474 475 SelectStatement select = new SelectStatement(); 476 477 ColumnRef idCol = 478 User.getObjectColumnRef( 479 User.TBL_USER, 480 AbstractObject.ATTRIB_ID); 481 ColumnRef nameCol = 482 User.getObjectColumnRef(User.TBL_USER, AbstractObject.TAG_NAME); 483 484 select.addSelectColumn(idCol); 485 select.addWhereCondition(nameCol, "=", sUser); 486 487 rs = m_dsi.execute(select); 488 489 if (rs.next()) { 490 int nId = rs.getInt(1); 491 492 usr = 493 (User) HarmoniseObjectFactory.instantiateHarmoniseObject( 494 m_dsi, 495 User.class.getName(), 496 nId); 497 } 498 499 if(usr != null && sUser.equals(usr.getName())) { 500 CachePointer ptr = CacheHandler.getInstance(m_dsi).getCachePointer(usr); 501 m_userCache.put(sUser, ptr); 502 } else { 503 usr = null; 505 } 506 } else { 507 usr = (User) uptr.getObject(); 508 } 509 510 } catch (DataStoreException e) { 511 throw new UserAuthenticationException(e); 512 } catch (SQLException e) { 513 throw new UserAuthenticationException(e); 514 } catch (HarmoniseFactoryException e) { 515 throw new UserAuthenticationException(e); 516 } catch (CacheException e) { 517 throw new UserAuthenticationException(e); 518 } catch (DataAccessException e) { 519 throw new UserAuthenticationException(e); 520 } finally { 521 if (rs != null) { 522 try { 523 524 rs.close(); 525 } catch (SQLException sql_e) { 526 throw new UserAuthenticationException( 527 sql_e.getLocalizedMessage(), 528 sql_e); 529 } 530 } 531 } 532 533 return usr; 534 } 535 536 539 public boolean isUserLockedOut(String sUserName) 540 throws UserAuthenticationException { 541 boolean bIsLockedOut = false; 542 SelectStatement select = new SelectStatement(); 543 544 ResultSet rs = null; 545 try { 546 int nFailRetryLimit = 547 ConfigSettings.getIntProperty(PNAME_PWD_RETRY_LIMIT, "-1"); 548 549 ColumnRef idCol = 550 User.getObjectColumnRef( 551 User.TBL_USER, 552 AbstractObject.ATTRIB_ID); 553 ColumnRef nameCol = 554 User.getObjectColumnRef(User.TBL_USER, AbstractObject.TAG_NAME); 555 ColumnRef failCountCol = 556 new ColumnRef(User.TBL_USER, CLMN_FAIL_COUNT, ColumnRef.NUMBER); 557 558 select.addSelectColumn(idCol); 559 select.addSelectColumn(failCountCol); 560 select.addWhereCondition(nameCol, "=", sUserName); 561 562 rs = m_dsi.execute(select); 563 564 if (rs.next()) { 565 int nId = rs.getInt(1); 566 567 int nFails = rs.getInt(2); 568 569 if (nFailRetryLimit > 0 && nFails >= nFailRetryLimit) { 570 bIsLockedOut = true; 571 } 572 } 573 574 } catch (DataStoreException e) { 575 throw new UserAuthenticationException(e.getLocalizedMessage(), e); 576 } catch (SQLException e) { 577 throw new UserAuthenticationException(e.getLocalizedMessage(), e); 578 } catch (ConfigException e) { 579 throw new UserAuthenticationException(e.getLocalizedMessage(), e); 580 } finally { 581 if (rs != null) { 582 try { 583 584 rs.close(); 585 } catch (SQLException sql_e) { 586 throw new UserAuthenticationException( 587 sql_e.getLocalizedMessage(), 588 sql_e); 589 } 590 } 591 } 592 return bIsLockedOut; 593 } 594 595 598 public boolean hasPasswordExpired(User usr) throws UserAuthenticationException { 599 boolean bExpired = false; 600 601 try { 602 int nPwdExpryLimit = 604 ConfigSettings.getIntProperty(PNAME_PWD_CHANGE_PERIOD, "-1"); 605 606 m_logger.log(Level.FINE, "password change period - " + nPwdExpryLimit); 607 608 if (nPwdExpryLimit > 0) { 609 AbstractDataStoreInterface dsi = 610 DataStoreInterfaceFactory.getDataStoreInterface(); 611 612 SelectStatement exprySelect = new SelectStatement(); 613 614 ColumnRef pwdChangeCol = 615 new ColumnRef( 616 usr.getDBTableName(), 617 CLMN_PWD_CHANGE_DATE, 618 ColumnRef.DATE); 619 ColumnRef verDateCol = 620 usr.getInstanceColumnRef(User.TAG_VERSION_DATE, false); 621 622 exprySelect.addSelectColumn(pwdChangeCol); 623 exprySelect.addSelectColumn(verDateCol); 624 625 exprySelect.addWhereCondition( 626 usr.getInstanceColumnRef(AbstractObject.ATTRIB_KEY, false), 627 "=", 628 usr.getKey()); 629 630 ResultSet expryRS = dsi.execute(exprySelect); 631 Date pwdDate = null; 632 if (expryRS.next()) { 633 pwdDate = expryRS.getDate(1); 634 if (pwdDate == null) { 635 pwdDate = expryRS.getDate(2); 636 } 637 } 638 639 expryRS.close(); 640 641 GregorianCalendar cal = new GregorianCalendar(); 642 643 cal.setTime(pwdDate); 644 645 cal.add(Calendar.DATE, nPwdExpryLimit); 646 647 Date expiryDate = cal.getTime(); 648 649 Date now = new Date (); 650 651 if(m_logger.getLevel() == Level.FINE) { 652 m_logger.log(Level.FINE, 653 "expiry - " 654 + expiryDate 655 + ", now - " 656 + now 657 + ", pwd date - " 658 + pwdDate); 659 } 660 661 662 663 bExpired = now.after(expiryDate); 664 } 665 } catch (ConfigException e) { 666 throw new UserAuthenticationException(e); 667 } catch (DataAccessException e) { 668 throw new UserAuthenticationException(e); 669 } catch (DataStoreException e) { 670 throw new UserAuthenticationException(e); 671 } catch (SQLException e) { 672 throw new UserAuthenticationException(e); 673 } 674 675 return bExpired; 676 } 677 678 681 public void workflowObjectSaved(EditEvent event) { 682 User usr = (User) event.getSource(); 683 User result = (User) event.getResult(); 684 try { 685 if(usr.getName() != result.getName()) { 687 m_userCache.remove(usr.getName()); 688 } 689 690 } catch (DataAccessException e) { 691 m_logger.log(Level.WARNING, e.getLocalizedMessage(), e); 692 } 693 } 694 695 698 public void workflowObjectStatusChanged(EditEvent event) { 699 700 } 701 702 705 public void workflowObjectArchived(EditEvent event) { 706 User usr = (User) event.getSource(); 707 708 try { 709 m_userCache.remove(usr.getName()); 710 } catch (DataAccessException e) { 711 m_logger.log(Level.WARNING, e.getLocalizedMessage(), e); 712 } 713 } 714 715 718 721 public void workflowObjectReactivated(EditEvent event) { 722 723 } 724 725 728 public void workflowObjectLocked(EditEvent event) { 729 730 } 731 732 735 public void workflowObjectUnlocked(EditEvent event) { 736 737 } 738 739 } 740 | Popular Tags |