001/** 002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 003 * 004 * Copyright (c) 2005 Sun Microsystems Inc. All Rights Reserved 005 * 006 * The contents of this file are subject to the terms 007 * of the Common Development and Distribution License 008 * (the License). You may not use this file except in 009 * compliance with the License. 010 * 011 * You can obtain a copy of the License at 012 * https://opensso.dev.java.net/public/CDDLv1.0.html or 013 * opensso/legal/CDDLv1.0.txt 014 * See the License for the specific language governing 015 * permission and limitations under the License. 016 * 017 * When distributing Covered Code, include this CDDL 018 * Header Notice in each file and include the License file 019 * at opensso/legal/CDDLv1.0.txt. 020 * If applicable, add the following below the CDDL Header, 021 * with the fields enclosed by brackets [] replaced by 022 * your own identifying information: 023 * "Portions Copyrighted [year] [name of copyright owner]" 024 * 025 * $Id: AMLoginModule.java,v 1.22 2009/11/21 01:11:56 222713 Exp $ 026 * 027 */ 028 029/* 030 * Portions Copyrighted 2010-2015 ForgeRock AS. 031 */ 032 033package com.sun.identity.authentication.spi; 034 035import static org.forgerock.openam.audit.AuditConstants.EntriesInfoFieldKey.*; 036import static org.forgerock.openam.utils.StringUtils.*; 037 038import com.iplanet.am.sdk.AMException; 039import com.iplanet.am.sdk.AMUser; 040import com.iplanet.am.sdk.AMUserPasswordValidation; 041import com.iplanet.am.util.Misc; 042import com.iplanet.dpro.session.service.InternalSession; 043import com.iplanet.dpro.session.service.SessionConstraint; 044import com.iplanet.dpro.session.service.SessionCount; 045import com.iplanet.sso.SSOException; 046import com.iplanet.sso.SSOToken; 047import com.iplanet.sso.SSOTokenManager; 048import com.sun.identity.authentication.AuthContext; 049import com.sun.identity.authentication.audit.AuthenticationModuleEventAuditor; 050import com.sun.identity.authentication.callbacks.HiddenValueCallback; 051import com.sun.identity.authentication.callbacks.ScriptTextOutputCallback; 052import com.sun.identity.authentication.service.AMAuthErrorCode; 053import com.sun.identity.authentication.service.AuthD; 054import com.sun.identity.authentication.service.AuthException; 055import com.sun.identity.authentication.service.LoginState; 056import com.sun.identity.authentication.service.LoginStateCallback; 057import com.sun.identity.authentication.util.ISAuthConstants; 058import com.sun.identity.authentication.util.ISValidation; 059import com.sun.identity.common.AccountLockoutInfo; 060import com.sun.identity.common.AdministrationServiceListener; 061import com.sun.identity.common.DNUtils; 062import com.sun.identity.common.ISAccountLockout; 063import com.sun.identity.idm.AMIdentity; 064import com.sun.identity.idm.AMIdentityRepository; 065import com.sun.identity.idm.IdRepoException; 066import com.sun.identity.idm.IdType; 067import com.sun.identity.idm.IdUtils; 068import com.sun.identity.shared.Constants; 069import com.sun.identity.shared.datastruct.CollectionHelper; 070import com.sun.identity.shared.debug.Debug; 071import com.sun.identity.shared.locale.AMResourceBundleCache; 072import com.sun.identity.sm.OrganizationConfigManager; 073import com.sun.identity.sm.ServiceSchema; 074import com.sun.identity.sm.ServiceSchemaManager; 075import org.forgerock.guice.core.InjectorHolder; 076import org.forgerock.openam.audit.model.AuthenticationAuditEntry; 077import org.forgerock.openam.ldap.LDAPUtils; 078 079import javax.security.auth.Subject; 080import javax.security.auth.callback.Callback; 081import javax.security.auth.callback.CallbackHandler; 082import javax.security.auth.callback.ChoiceCallback; 083import javax.security.auth.callback.ConfirmationCallback; 084import javax.security.auth.callback.NameCallback; 085import javax.security.auth.callback.PasswordCallback; 086import javax.security.auth.callback.TextInputCallback; 087import javax.security.auth.callback.TextOutputCallback; 088import javax.security.auth.callback.UnsupportedCallbackException; 089import javax.security.auth.login.LoginException; 090import javax.security.auth.spi.LoginModule; 091import javax.servlet.http.HttpServletRequest; 092import javax.servlet.http.HttpServletResponse; 093import java.io.IOException; 094import java.security.Principal; 095import java.util.ArrayList; 096import java.util.Collections; 097import java.util.HashMap; 098import java.util.HashSet; 099import java.util.Iterator; 100import java.util.List; 101import java.util.Map; 102import java.util.ResourceBundle; 103import java.util.Set; 104 105/** 106 * An abstract class which implements JAAS LoginModule, it provides 107 * methods to access OpenAM services and the module 108 * xml configuration. 109 * <p> 110 * Because it is an abstract class, Login Module writers must subclass 111 * and implement init(), process(), getPrincipal() methods. 112 * <p> 113 * The Callback[] for the Login Module is dynamically generated based 114 * on the xml module configuration. The module configuration file name 115 * must be the same as the name of the class (no package name) and have the 116 * extension .xml. 117 * <p> 118 * Here is a sample module configuration file: 119 * <pre> 120 * <ModuleProperties moduleClass="LDAP" version="1.0" > 121 * <Callbacks length="2" order="1" timeout="60" header="LDAP 122 * Authentication" > 123 * <NameCallback> 124 * <Prompt> Enter UserId </Prompt> 125 * </NameCallback> 126 * <PasswordCallback echoPassword="false" > 127 * <Prompt> Enter Password </Prompt> 128 * </PasswordCallback> 129 * </Callbacks> 130 * <Callbacks length="3" order="2" timeout="120" header="Password 131 * Expiring Please Change" > 132 * <PasswordCallback echoPassword="false" > 133 * <Prompt> Enter Current Password </Prompt> 134 * </PasswordCallback> 135 * <PasswordCallback echoPassword="false" > 136 * <Prompt> Enter New Password </Prompt> 137 * </PasswordCallback> 138 * <PasswordCallback echoPassword="false" > 139 * <Prompt> Confirm New Password </Prompt> 140 * </PasswordCallback> 141 * </Callbacks> 142 * </ModuleProperties> 143 * </pre> 144 * Each Callbacks Element corresponds to one login state. 145 * When an authentication process is invoked, there will be Callback[] 146 * generated from user's Login Module for each state. All login state 147 * starts with 1, then module controls the login process, and decides what's 148 * the next state to go in the process() method. 149 * <p> 150 * In the sample module configuration shown above, state one has 151 * three Callbacks, Callback[0] is for module information, Callback[1] is 152 * for user ID, Callback[2] is for user password. When the user fills in the 153 * Callbacks, those Callback[] will be sent to the process() method, where 154 * the module writer gets the submitted Callbacks, validates them and returns. 155 * If user's password is expiring, the module writer will set the next 156 * state to 2. State two has four Callbacks to request user to change 157 * password. The process() routine is again 158 * called after user submits the Callback[]. If the module writer throws an 159 * LoginException, an 'authentication failed' page will be sent to the user. 160 * If no exception is thrown, the user will be redirected to their default 161 * page. 162 * <p> 163 * The optional 'timeout' attribute in each state is used to ensure that the 164 * user responds in a timely manner. If the time between sending the Callbacks 165 * and getting response is greater than the timeout, a timeout page will be 166 * sent. 167 * <p> 168 * There are also optional 'html' and 'image' attribute in each state. The 169 * 'html' attribute allows the module writer to use a custom HTML 170 * page for the Login UI. The 'image' attribute allows the writer to display 171 * a custom background image on each page. 172 * <p> 173 * When multiple states are available to the user, the Callback array from a 174 * previous state may be retrieved by using the <code>getCallbak(int)</code> 175 * methods. The underlying login module keeps the Callback[] from the previous 176 * states until the login process is completed. 177 * <p> 178 * If a module writer need to substitute dynamic text in next state, the writer 179 * could use the <code>getCallback()</code> method to get the Callback[] for the 180 * next state, modify the output text or prompt, then call 181 * <code>replaceCallback()</code> to update the Callback array. This allows a 182 * module writer to dynamically generate challenges, passwords or user IDs. 183 * <p> 184 * Each authentication session will create a new instance of your 185 * Login Module Java class. The reference to the class will be 186 * released once the authentication session has either succeeded 187 * or failed. It is important to note that any static data or 188 * reference to any static data in your Login module 189 * must be thread-safe. 190 * <p> 191 * 192 * For a complete sample, please refer to 193 * <install_root>/SUNWam/samples/authentication/providers 194 * 195 * @supported.api 196 */ 197public abstract class AMLoginModule implements LoginModule { 198 // list which holds both presentation and credential callbacks 199 List internal = null; 200 // list which holds only credential callbacks 201 List external = null; 202 // list which contains the original Callback list from AMModuleProperties 203 List origList = null; 204 // class name 205 private String fileName = null; 206 // if true, means this module does not hava any Callbacks defined, this 207 // is the case for anonymous/cert, which have a size 0 config file 208 boolean noCallbacks = false; 209 210 // constant for empty Callback array 211 private static Callback[] EMPTY_CALLBACK = new Callback[0]; 212 213 // state length for this module 214 private int stateLength = 0; 215 216 // resource bundle 217 private ResourceBundle bundle = null; 218 219 // login state 220 private LoginState loginState =null; 221 222 /** 223 * Holds callback handler object passed in through initialize method 224 */ 225 private CallbackHandler handler = null; 226 /** 227 * Holds subject object passed in through initialize method 228 */ 229 private Subject subject = null; 230 /** 231 * Holds shared state map passed in through initialize method 232 */ 233 private Map sharedState = null; 234 /** 235 * Holds options map passed in through initialize method 236 */ 237 private Map options = null; 238 239 private static Debug debug = Debug.getInstance("amLoginModule"); 240 241 private int currentState = ISAuthConstants.LOGIN_START; 242 243 private final String EMPTY_STRING = ""; 244 private String moduleName = null; 245 private String moduleClass = null; 246 private static final String bundleName = "amAuth"; 247 private static AuthD ad = AuthD.getAuth(); 248 private Principal principal = null; 249 // the authentication status 250 private boolean succeeded = false; 251 252 private boolean forceCallbacksRead = false; 253 254 //use Shared state by default disabled 255 private boolean isSharedState = false; 256 private boolean isStore = true; 257 private String sharedStateBehaviorPattern = ""; 258 259 // variable used in replaceHeader() 260 private String headerWithReplaceTag; 261 private boolean alreadyReplaced = false; 262 private int lastState = 0; 263 264 /** 265 * Holds handle to ResourceBundleCache to quickly get ResourceBundle for 266 * any Locale. 267 */ 268 protected static AMResourceBundleCache amCache = 269 AMResourceBundleCache.getInstance(); 270 271 protected final AuthenticationModuleEventAuditor auditor; 272 273 /** 274 * No argument constructor for {@link AMLoginModule}. 275 */ 276 public AMLoginModule() { 277 auditor = InjectorHolder.getInstance(AuthenticationModuleEventAuditor.class); 278 } 279 280 /** 281 * Clone Callback[], and save it in the internal/external 282 * callbacks list. External callback contains all user defined 283 * Callbacks in the xml module configuration (property file), 284 * internal callback contains the external callbacks plus the 285 * PagePropertiesCallback. Note here, although 286 * Callback[] in internal/external are different, the Callback 287 * instance they pointed are actually same instance 288 * @param index indicates state of callback 289 * @param original original array of callback to be cloned 290 * @return Callback[] returns cloned callback 291 * @exception AuthLoginException if callback can not be cloned 292 */ 293 private Callback[] cloneCallbacks(int index, Callback[] original) 294 throws AuthLoginException { 295 // check if there is any callbacks in original 296 if (original == null || original.length == 0) { 297 // this is the error case where there is no Callbacks 298 // defined for a state 299 debug.error("cloneCallbacks, no callbacks in state " + (index+1)); 300 throw new AuthLoginException(bundleName, "noCallbackState", 301 new Object[]{new Integer(index + 1)}); 302 } 303 304 int len = original.length; 305 // Callback array which hold the cloned Callbacks 306 Callback[] copy = new Callback[len]; 307 // List which contains the external callbacks only 308 List extCallbacks = new ArrayList(); 309 310 // iterate through Callback array, and copy them one by one 311 // if it is an external Callback, add to the extCallback list 312 for (int i = 0; i < len; i++) { 313 if (original[i] instanceof HiddenValueCallback) { 314 final HiddenValueCallback hiddenValueCallback = (HiddenValueCallback) original[i]; 315 String defaultValue = hiddenValueCallback.getDefaultValue(); 316 if (defaultValue != null && defaultValue.length() != 0) { 317 copy[i] = new HiddenValueCallback( 318 hiddenValueCallback.getId(), defaultValue); 319 } else { 320 copy[i] = new HiddenValueCallback( 321 hiddenValueCallback.getId()); 322 } 323 extCallbacks.add(copy[i]); 324 if (debug.messageEnabled()) { 325 debug.message("clone #" + i + " is HiddenValueCallback"); 326 } 327 } else if (original[i] instanceof NameCallback) { 328 String dftName = ((NameCallback) original[i]).getDefaultName(); 329 if (dftName != null && dftName.length() != 0) { 330 copy[i] = new NameCallback( 331 ((NameCallback) original[i]).getPrompt(), dftName); 332 } else { 333 copy[i] = new NameCallback( 334 ((NameCallback) original[i]).getPrompt()); 335 } 336 extCallbacks.add(copy[i]); 337 if (debug.messageEnabled()) { 338 debug.message("clone #" + i + " is NameCallback"); 339 } 340 } else if (original[i] instanceof PasswordCallback) { 341 copy[i] = new PasswordCallback( 342 ((PasswordCallback) original[i]).getPrompt(), 343 ((PasswordCallback) original[i]).isEchoOn()); 344 extCallbacks.add(copy[i]); 345 if (debug.messageEnabled()) { 346 debug.message("clone #" + i + " is PasswordCallback"); 347 } 348 } else if (original[i] instanceof ScriptTextOutputCallback) { 349 copy[i] = new ScriptTextOutputCallback( 350 ((TextOutputCallback) original[i]).getMessage()); 351 extCallbacks.add(copy[i]); 352 if (debug.messageEnabled()) { 353 debug.message("clone #" + i + " is ScriptTextOutputCallback"); 354 } 355 } else if (original[i] instanceof TextOutputCallback) { 356 copy[i] = new TextOutputCallback( 357 ((TextOutputCallback) original[i]).getMessageType(), 358 ((TextOutputCallback) original[i]).getMessage()); 359 extCallbacks.add(copy[i]); 360 if (debug.messageEnabled()) { 361 debug.message("clone #" + i + " is TextOutputCallback"); 362 } 363 } else if (original[i] instanceof PagePropertiesCallback) { 364 // PagePropertiesCallback, no need to add to external callbacks 365 copy[i] = new PagePropertiesCallback( 366 ((PagePropertiesCallback) original[i]).getModuleName(), 367 ((PagePropertiesCallback) original[i]).getHeader(), 368 ((PagePropertiesCallback) original[i]).getImage(), 369 ((PagePropertiesCallback) original[i]).getTimeOutValue(), 370 ((PagePropertiesCallback) original[i]).getTemplateName(), 371 ((PagePropertiesCallback) original[i]).getErrorState(), 372 ((PagePropertiesCallback) original[i]).getPageState()); 373 ((PagePropertiesCallback) copy[i]).setRequire( 374 ((PagePropertiesCallback) original[i]).getRequire()); 375 ((PagePropertiesCallback) copy[i]).setAttribute( 376 ((PagePropertiesCallback) original[i]).getAttribute()); 377 ((PagePropertiesCallback) copy[i]).setInfoText( 378 ((PagePropertiesCallback) original[i]).getInfoText()); 379 if (debug.messageEnabled()) { 380 debug.message("clone #" + i + " is PagePropertiesCallback"); 381 } 382 } else if (original[i] instanceof ChoiceCallback) { 383 384 ChoiceCallback originalChoiceCallback = (ChoiceCallback) original[i]; 385 386 ChoiceCallback clone = new ChoiceCallback( 387 originalChoiceCallback.getPrompt(), 388 originalChoiceCallback.getChoices(), 389 originalChoiceCallback.getDefaultChoice(), 390 originalChoiceCallback.allowMultipleSelections()); 391 392 if (originalChoiceCallback.getSelectedIndexes() != null 393 && originalChoiceCallback.getSelectedIndexes().length > 0) { 394 if (originalChoiceCallback.allowMultipleSelections()) { 395 clone.setSelectedIndexes(originalChoiceCallback.getSelectedIndexes()); 396 } else { 397 clone.setSelectedIndex(originalChoiceCallback.getSelectedIndexes()[0]); 398 } 399 } 400 401 copy[i] = clone; 402 extCallbacks.add(copy[i]); 403 if (debug.messageEnabled()) { 404 debug.message("clone #" + i + " is ChoiceCallback"); 405 } 406 } else if (original[i] instanceof ConfirmationCallback) { 407 ConfirmationCallback temp = (ConfirmationCallback) original[i]; 408 String prompt = temp.getPrompt(); 409 String[] options = temp.getOptions(); 410 if (prompt == null) { 411 // no prompt 412 if (options == null) { 413 // no options 414 copy[i] = new ConfirmationCallback( 415 temp.getMessageType(), 416 temp.getOptionType(), 417 temp.getDefaultOption()); 418 } else { 419 copy[i] = new ConfirmationCallback( 420 temp.getMessageType(), 421 options, 422 temp.getDefaultOption()); 423 } 424 } else { 425 // has prompt 426 if (options == null) { 427 // no options 428 copy[i] = new ConfirmationCallback( 429 prompt, 430 temp.getMessageType(), 431 temp.getOptionType(), 432 temp.getDefaultOption()); 433 } else { 434 copy[i] = new ConfirmationCallback( 435 prompt, 436 temp.getMessageType(), 437 options, 438 temp.getDefaultOption()); 439 } 440 } 441 extCallbacks.add(copy[i]); 442 if (debug.messageEnabled()) { 443 debug.message("clone #" + i + " is ConfirmationCallback"); 444 } 445 } else if (original[i] instanceof TextInputCallback) { 446 copy[i] = new TextInputCallback( 447 ((TextInputCallback) original[i]).getPrompt()); 448 extCallbacks.add(copy[i]); 449 if (debug.messageEnabled()) { 450 debug.message("clone #" + i + " is TextInputCallback"); 451 } 452 } else if (original[i] instanceof HttpCallback) { 453 HttpCallback hc = (HttpCallback) original[i]; 454 copy[i] = new HttpCallback(hc.getAuthorizationHeader(), 455 hc.getNegotiationHeaderName(), 456 hc.getNegotiationHeaderValue(), 457 hc.getNegotiationCode()); 458 extCallbacks.add(copy[i]); 459 } else if (original[i] instanceof RedirectCallback) { 460 RedirectCallback rc = (RedirectCallback) original[i]; 461 copy[i] = new RedirectCallback(rc.getRedirectUrl(), 462 rc.getRedirectData(), 463 rc.getMethod(), 464 rc.getStatusParameter(), 465 rc.getRedirectBackUrlCookieName()); 466 extCallbacks.add(copy[i]); 467 } else { 468 debug.error("unknown callback " + original[i]); 469 } 470 // more callbacks need to be handled here if ... 471 } 472 473 // construct external Callback[] 474 Callback[] ext = new Callback[extCallbacks.size()]; 475 if (!extCallbacks.isEmpty()) { 476 Iterator it = extCallbacks.iterator(); 477 int i = 0; 478 while (it.hasNext()) { 479 ext[i++] = (Callback) it.next(); 480 } 481 } 482 483 // set external/internal callbacks 484 internal.set(index, copy); 485 external.set(index, ext); 486 487 return ext; 488 } 489 490 /** 491 * Returns an administration SSOToken for use the OpenAM APIs. 492 * 493 * <I>NB:</I>This is not the SSOToken that represents the user, if you wish 494 * to set/get user session properties use the <code>setUserSessionProperty</code> 495 * and <code>getUserSessionProperty</code> method respectively. 496 * 497 * @return An administrative <code>SSOToken</code>. 498 * @exception AuthLoginException if the authentication SSO session 499 * is null. 500 * @supported.api 501 */ 502 public SSOToken getSSOSession() throws AuthLoginException { 503 SSOToken sess = AuthD.getAuth().getSSOAuthSession(); 504 if (sess == null) { 505 throw new AuthLoginException(bundleName, "nullSess", null); 506 } 507 return sess; 508 } 509 510 /** 511 * Returns a Callback array for a specific state. 512 * <p> 513 * This method can be used to retrieve Callback[] for any state. All 514 * previous submitted Callback[] information are kept until the login 515 * process is completed. 516 * @param index order of state 517 * @return Callback array for this state, return 0-length Callback array 518 * if there is no Callback defined for this state 519 * @throws AuthLoginException if unable to read the callbacks 520 * @supported.api 521 */ 522 public Callback[] getCallback(int index) throws AuthLoginException { 523 return getCallback(index, false); 524 } 525 526 /** 527 * Return a Callback array for a specific state. 528 * <p> 529 * This method can be used to retrieve Callback[] for any state. All 530 * previous submitted Callback[] information are kept until the login 531 * process is completed. 532 * @param index order of state 533 * @param fetchOrig boolean indicating even if the callbacks for this 534 * state have been previously retrieved, get the original callbacks 535 * from AMModuleProperties, if set to "true". 536 * @return Callback array for this state, return 0-length Callback array 537 * if there is no Callback defined for this state 538 * @throws AuthLoginException if unable to read the callbacks 539 * @supported.api 540 */ 541 public Callback[] getCallback(int index, boolean fetchOrig) 542 throws AuthLoginException 543 { 544 // This method will be called by customer module, so it will 545 // return Callback[] from external callback List 546 // check if there is no callbacks defined for this module 547 if (noCallbacks || ( (isSharedState) && (!forceCallbacksRead) )) { 548 return EMPTY_CALLBACK; 549 } 550 551 if ((internal == null) || ( fetchOrig )) { 552 forceCallbacksInit(); 553 if (origList == null || origList.isEmpty()) { 554 return EMPTY_CALLBACK; 555 } 556 557 if (debug.messageEnabled()) { 558 debug.message("callback size for state " + index + "=" + 559 stateLength); 560 } 561 } 562 563 // get Callback[] for this page 564 // use index-1 as order since page index starts with 1 565 if (index > stateLength) { 566 // invalid login state 567 debug.error("getCallback, state " + index + " > " + stateLength); 568 throw new AuthLoginException(bundleName, "invalidState", 569 new Object[]{new Integer(index)}); 570 } 571 Object temp = external.get(index-1); 572 if (temp != null) { 573 return (Callback[]) temp; 574 } 575 576 // callbacks has not been retrieved for this index yet 577 // need to get it from AMModuleProperties 578 // since the Callbacks could not be shared by different instances 579 // we need to create clone copy here 580 return cloneCallbacks(index-1, (Callback[]) origList.get(index-1)); 581 } 582 583 protected void forceCallbacksInit () throws AuthLoginException { 584 if (internal == null) { 585 // get the callbacks for this class; 586 origList = AMModuleProperties.getModuleProperties(fileName); 587 if (origList == null || origList.isEmpty()) { 588 // we got file whose size is zero, this is the case for 589 // Cert/Anonymous based authentication 590 noCallbacks = true; 591 return; 592 } 593 // instantiate internal/external according to module callback size 594 stateLength = origList.size(); 595 internal = new ArrayList(); 596 external = new ArrayList(); 597 if (debug.messageEnabled()) { 598 debug.message("callback stateLength in file = " + stateLength); 599 } 600 for (int i = 0; i < stateLength; i++) { 601 internal.add(null); 602 external.add(null); 603 } 604 } 605 } 606 607 /** 608 * Replace Callback object for a specific state. 609 * @param state Order of login state 610 * @param index Index of Callback in the Callback array to be replaced 611 * for the specified state. Here index starts with 0, i.e. 0 means the 612 * first Callback in the Callback[], 1 means the second callback. 613 * @param callback Callback instance to be replaced 614 * @exception AuthLoginException if state or index is out of 615 * bound, or callback instance is null. 616 * @supported.api 617 */ 618 public void replaceCallback(int state, int index, Callback callback) 619 throws AuthLoginException { 620 if (debug.messageEnabled()) { 621 debug.message("ReplaceCallback : state=" + state + ", index=" + 622 index + ", callback=" + callback); 623 } 624 // check state length 625 if (state > stateLength) { 626 throw new AuthLoginException(bundleName, "invalidState", 627 new Object[]{new Integer(state)}); 628 } 629 // check callback length for the state 630 Callback[] ext = getCallback(state); 631 if (index < 0 || index >= ext.length) { 632 throw new AuthLoginException(bundleName, "invalidCallbackIndex", 633 new Object[]{new Integer(index)}); 634 } 635 // check callback instance 636 if (callback == null) { 637 throw new AuthLoginException(bundleName, "nullCallback", null); 638 } 639 640 // replace callback in external & internal Callback array 641 ext[index] = callback; 642 // in internal, first Callback is always PagePropertiesCallback 643 // so add one here for the index 644 ((Callback[]) internal.get(state-1))[index + 1] = callback; 645 } 646 647 /** 648 * Replace page header for a specific state. 649 * @param state Order of login state 650 * @param header header messages to be replaced 651 * @throws AuthLoginException if state is out of bound. 652 */ 653 public void replaceHeader(int state, String header) 654 throws AuthLoginException { 655 if (debug.messageEnabled()) { 656 debug.message("ReplaceHeader : state=" + state + ", header=" + 657 header); 658 } 659 660 if (lastState != state) { 661 alreadyReplaced = false; 662 } 663 lastState = state; 664 665 // check state length 666 if (state > stateLength) { 667 throw new AuthLoginException(bundleName, "invalidState", 668 new Object[]{new Integer(state)}); 669 } 670 // check callback length for the state 671 Callback[] ext = getCallback(state, true); 672 if (ext.length<=0) { 673 throw new AuthLoginException(bundleName, "invalidCallbackIndex", 674 null); 675 } 676 677 // in internal, first Callback is always PagePropertiesCallback 678 if ((header!=null)&&(header.length() != 0)) { 679 PagePropertiesCallback pc = 680 (PagePropertiesCallback)((Callback[]) internal.get(state-1))[0]; 681 // retrieve header with REPLACE tag 682 if ( !(alreadyReplaced) ) { 683 headerWithReplaceTag = pc.getHeader(); 684 } 685 // replace string 686 int idx = headerWithReplaceTag.indexOf("#REPLACE#"); 687 if (idx != -1) { 688 String newHeader = headerWithReplaceTag.substring(0, idx) + header; 689 pc.setHeader(newHeader); 690 alreadyReplaced = true; 691 }else{ 692 String newHeader = headerWithReplaceTag.substring(0, 693 headerWithReplaceTag.indexOf("<BR></BR>")) + "<BR></BR>" + header; 694 pc.setHeader(newHeader); 695 } 696 } 697 } 698 699 /** 700 * Allows you to set the info text for a specific callback. Info Text is shown 701 * under the element in the Login page. It is used in the membership module to 702 * implement in-line feedback. 703 * 704 * @param state state in which the Callback[] to be reset 705 * @param callback the callback to associate the info text 706 * @param infoText the infotext for the callback 707 * @throws AuthLoginException if state/callback is out of bounds 708 * @supported.api 709 */ 710 public void substituteInfoText(int state, int callback, String infoText) 711 throws AuthLoginException { 712 if (debug.messageEnabled()) { 713 debug.message("setInfoText : state=" + state + ", infoText=" + infoText); 714 } 715 716 // check state length 717 if (state > stateLength) { 718 throw new AuthLoginException(bundleName, "invalidState", 719 new Object[]{new Integer(state)}); 720 } 721 722 // check callback length for the state 723 Callback[] ext = getCallback(state); 724 if (ext.length<=0) { 725 throw new AuthLoginException(bundleName, "invalidCallbackIndex", null); 726 } 727 728 // in internal, first Callback is always PagePropertiesCallback 729 if ((infoText != null) && (infoText.length() != 0)) { 730 PagePropertiesCallback pc = 731 (PagePropertiesCallback)((Callback[]) internal.get(state - 1))[0]; 732 733 // substitute string 734 List<String> infoTexts = pc.getInfoText(); 735 infoTexts.set(callback, infoText); 736 pc.setInfoText(infoTexts); 737 } 738 } 739 740 /** 741 * Clears the info text for a given callback state 742 * 743 * @param state The state to clear all infotexts 744 * @throws AuthLoginException Invalid state 745 * @supported.api 746 */ 747 public void clearInfoText(int state) 748 throws AuthLoginException { 749 if (debug.messageEnabled()) { 750 debug.message("clearInfoText : state=" + state); 751 } 752 753 // check state length 754 if (state > stateLength) { 755 throw new AuthLoginException(bundleName, "invalidState", 756 new Object[]{new Integer(state)}); 757 } 758 759 // check callback length for the state 760 Callback[] ext = getCallback(state); 761 if (ext.length<=0) { 762 throw new AuthLoginException(bundleName, "invalidCallbackIndex", null); 763 } 764 765 // in internal, first Callback is always PagePropertiesCallback 766 PagePropertiesCallback pc = 767 (PagePropertiesCallback)((Callback[]) internal.get(state - 1))[0]; 768 769 // clear info text 770 List<String> infoTexts = pc.getInfoText(); 771 772 for (int i = 0; i < infoTexts.size(); i++) { 773 infoTexts.set(i, EMPTY_STRING); 774 } 775 776 pc.setInfoText(infoTexts); 777 } 778 779 /** 780 * Use this method to replace the header text from the XML file with new 781 * text. This method can be used multiple times on the same state replacing 782 * text with new text each time. Useful for modules that control their own 783 * error handling. 784 * 785 * @param state state state in which the Callback[] to be reset 786 * @param header The text of the header to be replaced 787 * @throws AuthLoginException if state is out of bounds 788 * @supported.api 789 */ 790 public void substituteHeader(int state, String header) 791 throws AuthLoginException { 792 if (debug.messageEnabled()) { 793 debug.message("substituteHeader : state=" + state + ", header=" + 794 header); 795 } 796 // check state length 797 if (state > stateLength) { 798 throw new AuthLoginException(bundleName, "invalidState", 799 new Object[]{new Integer(state)}); 800 } 801 // check callback length for the state 802 Callback[] ext = getCallback(state); 803 if (ext.length<=0) { 804 throw new AuthLoginException(bundleName, "invalidCallbackIndex", 805 null); 806 } 807 808 // in internal, first Callback is always PagePropertiesCallback 809 if ((header!=null)&&(header.length() != 0)) { 810 PagePropertiesCallback pc = 811 (PagePropertiesCallback)((Callback[]) internal.get(state-1))[0]; 812 813 // substitute string 814 pc.setHeader(header); 815 } 816 } 817 818 /** 819 * Reset a Callback instance to the original Callback for the specified 820 * state and the specified index. This will override change to the Callback 821 * instance by the <code>replaceCallback()</code> method. 822 * @param state state in which the Callback[] to be reset 823 * @param index index order of the Callback in the Callback[], index starts 824 * with 0, i.e. 0 means first callback instance, 1 means 825 * the second callback instance. 826 * @throws AuthLoginException if state or index is out of bound. 827 * @supported.api 828 */ 829 public void resetCallback(int state, int index) 830 throws AuthLoginException { 831 if (debug.messageEnabled()) { 832 debug.message("resetCallback: state=" + state + ",index=" + index); 833 } 834 // check state length 835 if (state > stateLength) { 836 throw new AuthLoginException(bundleName, "invalidState", 837 new Object[]{new Integer(state)}); 838 } 839 // check callback length for the state 840 Callback[] ext = getCallback(state); 841 if (index < 0 || index >= ext.length) { 842 throw new AuthLoginException(bundleName, "invalidCallbackIndex", 843 new Object[]{new Integer(index)}); 844 } 845 846 // get the Callback from AMModuleProperties 847 // add one to index here since first one is the PagePropertiesCallback 848 Callback callback = ((Callback[]) origList.get(state-1))[index+1]; 849 Callback newCallback = null; 850 if (callback instanceof NameCallback) { 851 newCallback = new NameCallback( 852 ((NameCallback) callback).getPrompt()); 853 } else if (callback instanceof PasswordCallback) { 854 newCallback = new PasswordCallback( 855 ((PasswordCallback) callback).getPrompt(), 856 ((PasswordCallback) callback).isEchoOn()); 857 } else if (callback instanceof ChoiceCallback) { 858 int selection = ((ChoiceCallback) callback).getDefaultChoice(); 859 newCallback = new ChoiceCallback( 860 ((ChoiceCallback) callback).getPrompt(), 861 ((ChoiceCallback) callback).getChoices(), 862 selection, 863 ((ChoiceCallback) callback).allowMultipleSelections()); 864 } else { 865 // should never come here since only above three will be supported 866 debug.error("Unsupported call back instance " + callback); 867 throw new AuthLoginException(bundleName, "unknownCallback", null); 868 } 869 870 if (debug.messageEnabled()) { 871 debug.message("original=" + callback + ",new=" + newCallback); 872 } 873 874 // set external & internal callback instance 875 ((Callback[]) internal.get(state-1))[index+1] = newCallback; 876 ((Callback[]) external.get(state-1))[index] = newCallback; 877 } 878 879 /** 880 * Implements initialize() method in JAAS LoginModule class. 881 * <p> 882 * The purpose of this method is to initialize Login Module, 883 * it will call the init() method implemented by user's Login 884 * Module to do initialization. 885 * <p> 886 * This is a final method. 887 * @param subject - the Subject to be authenticated. 888 * @param callbackHandler - a CallbackHandler for communicating with the 889 * end user (prompting for usernames and passwords, for example). 890 * @param sharedState - state shared with other configured LoginModules. 891 * @param options - options specified in the login Configuration for this 892 * particular LoginModule. 893 */ 894 public final void initialize(Subject subject, 895 CallbackHandler callbackHandler, 896 java.util.Map sharedState, 897 java.util.Map options) { 898 this.subject = subject; 899 this.handler = callbackHandler; 900 this.sharedState = sharedState; 901 this.options = options; 902 // get class name 903 String className = this.getClass().getName(); 904 int index = className.lastIndexOf("."); 905 moduleClass = className.substring(index + 1); 906 moduleName = (String) options.get(ISAuthConstants. 907 MODULE_INSTANCE_NAME); 908 909 // get module properties file path 910 911 loginState = getLoginState(); 912 913 fileName = loginState.getFileName(moduleClass+ ".xml"); 914 loginState.setSharedState(sharedState); 915 916 // get resource bundle 917 918 bundle = amCache.getResBundle(bundleName, getLoginLocale()); 919 920 if (debug.messageEnabled()) { 921 debug.message("AMLoginModule resbundle locale="+getLoginLocale()); 922 debug.message("Login, class = " + className + 923 ", module=" + moduleName + ", file=" + fileName); 924 } 925 isSharedState = Boolean.valueOf(CollectionHelper.getMapAttr( 926 options, ISAuthConstants.SHARED_STATE_ENABLED, "false") 927 ).booleanValue(); 928 929 isStore = Boolean.valueOf(CollectionHelper.getMapAttr( 930 options, ISAuthConstants.STORE_SHARED_STATE_ENABLED, "true") 931 ).booleanValue(); 932 933 sharedStateBehaviorPattern = Misc.getMapAttr(options, 934 ISAuthConstants.SHARED_STATE_BEHAVIOR_PATTERN, 935 "tryFirstPass"); 936 937 if (debug.messageEnabled()) { 938 debug.message("AMLoginModule" + 939 ISAuthConstants.SHARED_STATE_BEHAVIOR_PATTERN + 940 " is set to " + sharedStateBehaviorPattern); 941 } 942 943 // Check for composite Advice 944 String compositeAdvice = loginState.getCompositeAdvice(); 945 if (compositeAdvice != null) { 946 if (debug.messageEnabled()) { 947 debug.message("AMLoginModule.initialize: " 948 + "Adding Composite Advice " + compositeAdvice); 949 } 950 sharedState.put(ISAuthConstants.COMPOSITE_ADVICE_XML, 951 compositeAdvice); 952 } 953 // call customer init method 954 init(subject, sharedState, options); 955 } 956 957 /** 958 * Initialize this LoginModule. 959 * <p> 960 * This is an abstract method, must be implemented by user's Login Module 961 * to initialize this LoginModule with the relevant information. If this 962 * LoginModule does not understand any of the data stored in sharedState 963 * or options parameters, they can be ignored. 964 * @param subject - the Subject to be authenticated. 965 * @param sharedState - state shared with other configured LoginModules. 966 * @param options - options specified in the login Configuration for this 967 * particular LoginModule. It contains all the global and organization 968 * attribute configuration for this module. The key of the map is the 969 * attribute name (e.g. <code>iplanet-am-auth-ldap-server</code>) as 970 * String, the value is the value of the corresponding attribute as Set. 971 * @supported.api 972 */ 973 abstract public void init(Subject subject, 974 java.util.Map sharedState, 975 java.util.Map options); 976 977 /** 978 * Abstract method must be implemented by each login module to 979 * control the flow of the login process. 980 * <p> 981 * This method takes an array of sbumitted 982 * Callback, process them and decide the order of next state to go. 983 * Return -1 if the login is successful, return 0 if the 984 * LoginModule should be ignored. 985 * @param callbacks Callback[] for this Login state 986 * @param state Order of state. State order starts with 1. 987 * @return order of next state. return -1 if authentication 988 * is successful, return 0 if the LoginModule should be ignored. 989 * @exception LoginException if login fails. 990 * @supported.api 991 */ 992 abstract public int process(Callback[] callbacks, int state) 993 throws LoginException; 994 995 /** 996 * Abstract method must be implemeted by each login module to 997 * get the user Principal 998 * @return Principal 999 * @supported.api 1000 */ 1001 abstract public java.security.Principal getPrincipal(); 1002 1003 /** 1004 * This method should be overridden by each login module 1005 * to destroy dispensable state fields. 1006 * 1007 * @supported.api 1008 */ 1009 public void destroyModuleState(){}; 1010 1011 /** 1012 * This method should be overridden by each login module 1013 * to do some garbage collection work after the module 1014 * process is done. Typically those class wide global variables 1015 * that will not be used again until a logout call should be nullified. 1016 */ 1017 public void nullifyUsedVars() {}; 1018 1019 /** 1020 * Wrapper for process() to utilize AuthLoginException. 1021 * @param callbacks associated with authentication 1022 * @param state of callbacks 1023 * @return state of auth login 1024 * @exception AuthLoginException if login fails. 1025 */ 1026 private int wrapProcess(Callback[] callbacks, int state) 1027 throws AuthLoginException { 1028 try { 1029 if (callbacks != null) { 1030 for (int i = 0; i < callbacks.length; i++) { 1031 if (callbacks[i] instanceof NameCallback) { 1032 String newUser = null; 1033 try { 1034 newUser = IdUtils.getIdentityName( 1035 ((NameCallback) callbacks[i]).getName(), 1036 getRequestOrg()); 1037 } catch (IdRepoException idRepoExp) { 1038 //Print message and let Auth proceed. 1039 debug.message( 1040 "AMLoginModule.wrapProcess: Cannot get "+ 1041 "username from idrepo. ", idRepoExp); 1042 } 1043 if (newUser != null) { 1044 ((NameCallback) callbacks[i]).setName(newUser); 1045 } 1046 } 1047 } 1048 } 1049 return process(callbacks, state); 1050 } catch (InvalidPasswordException e) { 1051 setFailureState(); 1052 setFailureID(e.getTokenId()); 1053 throw e; 1054 } catch (AuthLoginException e) { 1055 setFailureState(); 1056 throw e; 1057 } catch (LoginException e) { 1058 setFailureState(); 1059 throw new AuthLoginException(e); 1060 } catch (RuntimeException re) { 1061 setFailureState(); 1062 throw re; 1063 } 1064 } 1065 1066 private void setFailureState() { 1067 currentState = ISAuthConstants.LOGIN_IGNORE; 1068 setFailureModuleName(moduleName); 1069 } 1070 1071 /** 1072 * Returns true if a module in authentication chain has already done, either 1073 * succeeded or failed. 1074 * 1075 * @return true if a module in authentication chain has already done, either 1076 * succeeded or failed. 1077 */ 1078 private boolean moduleHasDone() { 1079 return (currentState == ISAuthConstants.LOGIN_SUCCEED) || 1080 (currentState == ISAuthConstants.LOGIN_IGNORE); 1081 } 1082 1083 /** 1084 * Implements login() method in JAAS LoginModule class. 1085 * <p> 1086 * This method is responsible for retrieving corresponding Callback[] for 1087 * current state, send as requirement to user, get the submitted Callback[], 1088 * call the process() method. The process() method will decide the next 1089 * action based on those submitted Callback[]. 1090 * <p> 1091 * This method is final. 1092 * @return <code>true</code> if the authentication succeeded, or 1093 * <code>false</code> if this LoginModule should be ignored. 1094 * @throws AuthLoginException - if the authentication fails 1095 */ 1096 public final boolean login() throws AuthLoginException { 1097 if (moduleHasDone()) { 1098 debug.message("This module has already done."); 1099 if (currentState == ISAuthConstants.LOGIN_SUCCEED) { 1100 return true; 1101 } else { 1102 return false; 1103 } 1104 } else { 1105 if (debug.messageEnabled()) { 1106 debug.message("This module is not done yet. CurrentState: " 1107 + currentState); 1108 } 1109 } 1110 1111 // make one getCallback call to populate first state 1112 // this will set the noCallbacks variable 1113 if (internal == null) { 1114 getCallback(1); 1115 } 1116 // if this module does not define any Callbacks (such as Cert), 1117 // pass control right to module, then check return code from module 1118 if (noCallbacks) { 1119 currentState = wrapProcess(EMPTY_CALLBACK, 1); 1120 // check login status 1121 if (currentState == ISAuthConstants.LOGIN_SUCCEED) { 1122 setSuccessModuleName(moduleName); 1123 succeeded = true; 1124 nullifyUsedVars(); 1125 return true; 1126 } else if (currentState == ISAuthConstants.LOGIN_IGNORE) { 1127 // index = 0; 1128 setFailureModuleName(moduleName); 1129 succeeded = false; 1130 destroyModuleState(); 1131 principal = null; 1132 return false; 1133 } else { 1134 setFailureModuleName(moduleName); 1135 succeeded = false; 1136 cleanup(); 1137 throw new AuthLoginException(bundleName, "invalidCode", 1138 new Object[]{new Integer(currentState)}); 1139 } 1140 } 1141 1142 if (handler == null) { 1143 debug.error("Handler is null"); 1144 throw new AuthLoginException(bundleName, "nullHandler", null); 1145 } 1146 try { 1147 Callback[] lastCallbacks = null; 1148 boolean needToExit = false; 1149 1150 // starting from first page 1151 //currentState = 1; 1152 while (currentState != ISAuthConstants.LOGIN_SUCCEED && 1153 currentState != ISAuthConstants.LOGIN_IGNORE) { 1154 if (debug.messageEnabled()) { 1155 debug.message("Login, state = " + currentState); 1156 } 1157 if (isSharedState) { 1158 currentState = wrapProcess(EMPTY_CALLBACK, 1); 1159 isSharedState = false; 1160 continue; 1161 } 1162 // get current set of callbacks 1163 getCallback(currentState); 1164 // check if this is an error state, if so, throw exception 1165 // to terminate login process 1166 Callback[] cbks = ((Callback[]) internal.get(currentState-1)); 1167 PagePropertiesCallback callback = 1168 (PagePropertiesCallback) cbks[0]; 1169 1170 if (callback.getErrorState()) { 1171 // this is an error state 1172 setFailureModuleName(moduleName); 1173 String template = callback.getTemplateName(); 1174 String errorMessage = callback.getHeader(); 1175 if (template == null || template.length() == 0) { 1176 // this is the case which no error template is 1177 // defined, only exception message in header 1178 throw new MessageLoginException(errorMessage); 1179 } else { 1180 // send error template 1181 //setLoginFailureURL(template); 1182 setModuleErrorTemplate(template); 1183 throw new AuthLoginException(errorMessage); 1184 } 1185 } 1186 // call handler to handle the internal callbacks 1187 handler.handle(cbks); 1188 1189 // Get the page state from the PagePropertiesCallback 1190 Callback[] cbksPrev= ((Callback[])internal.get(currentState-1)); 1191 1192 PagePropertiesCallback callbackPrev = 1193 (PagePropertiesCallback) cbksPrev[0]; 1194 String pageState = callbackPrev.getPageState(); 1195 if ((pageState != null) && 1196 (pageState.length() != 0) && 1197 (!pageState.equals(Integer.toString(currentState)))) { 1198 int loginPage = Integer.parseInt(pageState); 1199 1200 //Set the current page state in PagePropertiesCallback 1201 callbackPrev.setPageState(Integer.toString(currentState)); 1202 1203 currentState = loginPage; 1204 if (debug.messageEnabled()) { 1205 debug.message("currentState from UI " + currentState); 1206 } 1207 } 1208 1209 // Get the last submitted callbacks to auth module and submit 1210 // those callbacks to do DataStore authentication if the incoming 1211 // user is special / internal user and auth module is other than 1212 // "DataStore" and "Application" auth modules. 1213 lastCallbacks = (Callback[])external.get(currentState-1); 1214 if ((!moduleName.equalsIgnoreCase("DataStore")) && 1215 (!moduleName.equalsIgnoreCase("Application"))) { 1216 if (!authenticateToDatastore(lastCallbacks)) { 1217 needToExit = true; 1218 break; 1219 } 1220 } 1221 1222 // send external callback and send to module for processing 1223 currentState = wrapProcess((Callback[]) 1224 external.get(currentState-1), currentState); 1225 1226 if (debug.messageEnabled()) { 1227 debug.message("Login NEXT State : " + currentState); 1228 } 1229 } 1230 1231 if (needToExit) { 1232 nullifyUsedVars(); 1233 throw new AuthLoginException(AMAuthErrorCode.AUTH_MODULE_DENIED); 1234 } 1235 1236 // check login status 1237 if (currentState == ISAuthConstants.LOGIN_SUCCEED) { 1238 setSuccessModuleName(moduleName); 1239 succeeded = true; 1240 nullifyUsedVars(); 1241 return true; 1242 } else { 1243 // currentState = 0; 1244 setFailureModuleName(moduleName); 1245 succeeded = false; 1246 destroyModuleState(); 1247 principal = null; 1248 return false; 1249 } 1250 } catch (IOException e) { 1251 setFailureModuleName(moduleName); 1252 if (e.getMessage().equals(AMAuthErrorCode.AUTH_TIMEOUT)) { 1253 debug.message("login timed out ", e); 1254 } else { 1255 debug.message("login ", e); 1256 } throw new AuthLoginException(e); 1257 } catch (UnsupportedCallbackException e) { 1258 setFailureModuleName(moduleName); 1259 debug.message("Login", e); 1260 throw new AuthLoginException(e); 1261 } 1262 } 1263 1264 /** 1265 * Returns authentication level that has been set for the module 1266 * 1267 * @return authentication level of this authentication session 1268 * @supported.api 1269 */ 1270 public int getAuthLevel() { 1271 // get login state for this authentication session 1272 if (loginState == null) { 1273 loginState = getLoginState(); 1274 if (loginState == null) { 1275 return 0; 1276 } 1277 } 1278 return loginState.getAuthLevel(); 1279 } 1280 1281 /** 1282 * Sets the <code>AuthLevel</code> for this session. 1283 * The authentication level being set cannot be downgraded 1284 * below that set by the module configuration. 1285 * 1286 * @param auth_level authentication level string to be set 1287 * @return <code>true</code> if setting is successful,<code>false</code> 1288 * otherwise 1289 * @supported.api 1290 */ 1291 public boolean setAuthLevel(int auth_level) { 1292 // get login state for this authentication session 1293 if (loginState == null) { 1294 loginState = getLoginState(); 1295 if (loginState == null) { 1296 // may be should throw AuthLoginException here 1297 debug.error("Unable to set auth level : " + auth_level); 1298 return false; 1299 } 1300 } 1301 loginState.setModuleAuthLevel(auth_level); 1302 return true; 1303 } 1304 1305 /** 1306 * Returns the current state in the authentication process. 1307 * 1308 * @return the current state in the authentication process. 1309 * @supported.api 1310 */ 1311 public int getCurrentState() { 1312 return currentState; 1313 } 1314 1315 /** 1316 * Returns the <code>HttpServletRequest</code> object that 1317 * initiated the call to this module. 1318 * 1319 * @return <code>HttpServletRequest</code> for this request, returns null 1320 * if the <code>HttpServletRequest</code> object could not be 1321 * obtained. 1322 * @supported.api 1323 */ 1324 public HttpServletRequest getHttpServletRequest() { 1325 // get login state for this authentication session 1326 if (loginState == null) { 1327 loginState = getLoginState(); 1328 if (loginState == null) { 1329 return null; 1330 } 1331 } 1332 return loginState.getHttpServletRequest(); 1333 } 1334 1335 /** 1336 * Returns the authentication <code>LoginState</code> 1337 * @param methodName Name of the required methd in 1338 * <code>LoginState</code> object 1339 * @return <code>com.sun.identity.authentication.service.LoginState</code> 1340 * for this authentication method. 1341 * @throws AuthLoginException if fails to get the Login state 1342 */ 1343 protected com.sun.identity.authentication.service.LoginState getLoginState( 1344 String methodName) throws AuthLoginException { 1345 if (loginState == null) { 1346 loginState = getLoginState(); 1347 if (loginState == null) { 1348 throw new AuthLoginException(bundleName, "wrongCall", 1349 new Object[]{methodName}); 1350 } 1351 } 1352 return loginState; 1353 } 1354 1355 /** 1356 * Returns the Login <code>Locale</code> for this session 1357 * @return <code>Locale</code> used for localizing text 1358 */ 1359 protected java.util.Locale getLoginLocale() { 1360 try { 1361 String loc = getLocale(); 1362 return com.sun.identity.shared.locale.Locale.getLocale(loc); 1363 } catch (AuthLoginException ex) { 1364 debug.message("unable to determine loginlocale ", ex); 1365 return java.util.Locale.ENGLISH; 1366 } 1367 } 1368 1369 /* 1370 * Returns the Login State object 1371 * @return com.sun.identity.authentication.service.LoginState 1372 */ 1373 private com.sun.identity.authentication.service.LoginState getLoginState() { 1374 Callback[] callbacks = new Callback[1]; 1375 try { 1376 callbacks[0] = new LoginStateCallback(); 1377 if (handler == null) { 1378 return null; 1379 } 1380 handler.handle(callbacks); 1381 return ((LoginStateCallback) callbacks[0]).getLoginState(); 1382 } catch (Exception e) { 1383 debug.message("Error.." ,e ); 1384 return null; 1385 } 1386 } 1387 1388 /** 1389 * Returns the <code>HttpServletResponse</code> object for the servlet 1390 * request that initiated the call to this module. The servlet response 1391 * object will be the response to the <code>HttpServletRequest</code> 1392 * received by the authentication module. 1393 * 1394 * @return <code>HttpServletResponse</code> for this request, returns null 1395 * if the <code>HttpServletResponse</code> object could not be obtained. 1396 * @supported.api 1397 */ 1398 public HttpServletResponse getHttpServletResponse() { 1399 // get login state for this authentication session 1400 if (loginState == null) { 1401 loginState = getLoginState(); 1402 if (loginState == null) { 1403 return null; 1404 } 1405 } 1406 return loginState.getHttpServletResponse(); 1407 } 1408 1409 /** 1410 * Returns the CallbackHandler object for the module. This method 1411 * will be used internally. 1412 * 1413 * @return CallbackHandler for this request, returns null if the 1414 * CallbackHandler object could not be obtained. 1415 */ 1416 public CallbackHandler getCallbackHandler() { 1417 return handler; 1418 } 1419 1420 /** 1421 * Returns the locale for this authentication session. 1422 * 1423 * @return <code>java.util.Locale</code> locale for this authentication 1424 * session. 1425 * @throws AuthLoginException if problem in accessing the 1426 locale. 1427 * @supported.api 1428 */ 1429 public String getLocale() throws AuthLoginException { 1430 // get login state for this authentication session 1431 return getLoginState("getLocale()").getLocale(); 1432 } 1433 1434 /** 1435 * Returns the number of authentication states for this 1436 * login module. 1437 * 1438 * @return the number of authentication states for this login module. 1439 * @supported.api 1440 */ 1441 public int getNumberOfStates() { 1442 return stateLength; 1443 } 1444 1445 /** 1446 * Returns the organization DN for this authentication session. 1447 * 1448 * @return organization DN. 1449 * @supported.api 1450 */ 1451 public String getRequestOrg() { 1452 // get login state for this authentication session 1453 if (loginState == null) { 1454 loginState = getLoginState(); 1455 if (loginState == null) { 1456 return null; 1457 } 1458 } 1459 return loginState.getOrgDN(); 1460 } 1461 1462 /** 1463 * Returns a unique key for this authentication session. 1464 * This key will be unique throughout an entire Web browser session. 1465 * 1466 * @return null is unable to get the key, 1467 * @supported.api 1468 */ 1469 public String getSessionId() { 1470 // get login state for this authentication session 1471 if (loginState == null) { 1472 loginState = getLoginState(); 1473 if (loginState == null) { 1474 return null; 1475 } 1476 } 1477 return loginState.getSid().toString(); 1478/* 1479 InternalSession sess = loginState.getSession(); 1480 if (sess != null) { 1481 return sess.getID().toString(); 1482 } else { 1483 return null; 1484 } 1485*/ 1486 } 1487 1488 /** 1489 * Returns the organization attributes for specified organization. 1490 * 1491 * @param orgDN Requested organization DN. 1492 * @return Map that contains all attribute key/value pairs defined 1493 * in the organization. 1494 * @throws AuthLoginException if cannot get organization profile. 1495 * @supported.api 1496 */ 1497 public Map getOrgProfile(String orgDN) throws AuthLoginException { 1498 Map orgMap = null; 1499 if (orgDN == null || orgDN.length() == 0) { 1500 // get login state for this authentication session 1501 orgDN = getLoginState("getOrgProfile(String)").getOrgDN(); 1502 } 1503 1504 try { 1505 OrganizationConfigManager orgConfigMgr = 1506 AuthD.getAuth().getOrgConfigManager(orgDN); 1507 orgMap = orgConfigMgr.getAttributes( 1508 ISAuthConstants.IDREPO_SVC_NAME); 1509 if (debug.messageEnabled()) { 1510 debug.message("orgMap is : " + orgMap); 1511 } 1512 } catch (Exception ex) { 1513 debug.message("getOrgProfile", ex); 1514 throw new AuthLoginException(ex); 1515 } 1516 return orgMap; 1517 } 1518 1519 /** 1520 * Returns service template attributes defined for the specified 1521 * organization. 1522 * 1523 * @param orgDN Organization DN. 1524 * @param serviceName Requested service name. 1525 * @return Map that contains all attribute key/value pairs defined in the 1526 * organization service template. 1527 * @throws AuthLoginException if cannot get organization service 1528 * template. 1529 * @supported.api 1530 */ 1531 public Map getOrgServiceTemplate(String orgDN, String serviceName) 1532 throws AuthLoginException { 1533 Map orgMap = null; 1534 if (orgDN == null || orgDN.length() == 0) { 1535 // get login state for this authentication session 1536 orgDN = getLoginState( 1537 "getOrgServiceTemplate(String, String)").getOrgDN(); 1538 } 1539 try { 1540 OrganizationConfigManager orgConfigMgr = 1541 AuthD.getAuth().getOrgConfigManager(orgDN); 1542 orgMap = orgConfigMgr.getServiceConfig(serviceName).getAttributes(); 1543 } 1544 catch (Exception ex) { 1545 debug.message("getOrgServiceTemplate", ex); 1546 throw new AuthLoginException(ex); 1547 } 1548 return orgMap; 1549 } 1550 1551 /** 1552 * Checks if dynamic profile creation is enabled. 1553 * 1554 * @return <code>true</code> if dynamic profile creation is enabled. 1555 */ 1556 public boolean isDynamicProfileCreationEnabled() { 1557 // get login state for this authentication session 1558 if (loginState == null) { 1559 loginState = getLoginState(); 1560 if (loginState == null) { 1561 return false; 1562 } 1563 } 1564 return loginState.isDynamicProfileCreationEnabled(); 1565 } 1566 1567 /** 1568 * Returns service configuration attributes. 1569 * @param name Requested service name. 1570 * @return Map that contains all attribute key/value pairs defined in 1571 * the service configuration. 1572 * @throws AuthLoginException if error in accessing the service schema. 1573 * 1574 * @supported.api 1575 */ 1576 public Map getServiceConfig(String name) throws AuthLoginException { 1577 try { 1578 ServiceSchemaManager scm = new ServiceSchemaManager(name, 1579 AuthD.getAuth().getSSOAuthSession()); 1580 ServiceSchema sc = scm.getGlobalSchema(); 1581 HashMap retMap = new HashMap(); 1582 if (sc != null) { 1583 retMap.putAll(sc.getAttributeDefaults()); 1584 } 1585 1586 sc = scm.getOrganizationSchema(); 1587 if (sc != null) { 1588 retMap.putAll(sc.getAttributeDefaults()); 1589 } 1590 1591 sc = scm.getUserSchema(); 1592 if (sc != null) { 1593 retMap.putAll(sc.getAttributeDefaults()); 1594 } 1595 1596 sc = scm.getPolicySchema(); 1597 if (sc != null) { 1598 retMap.putAll(sc.getAttributeDefaults()); 1599 } 1600 1601 return retMap; 1602 } catch (Exception ex) { 1603 debug.message("getServiceConfig", ex); 1604 throw new AuthLoginException(ex); 1605 } 1606 } 1607 1608 /** 1609 * Returns the user profile for the user specified. This 1610 * method may only be called in the validate() method. 1611 * 1612 * @param userDN distinguished name os user. 1613 * @return <code>AMUser</code> object for the user's distinguished name. 1614 * @throws AuthLoginException if it fails to get the user profile for 1615 * <code>userDN</code>. 1616 * @deprecated This method has been deprecated. Please use the 1617 * IdRepo API's to get the AMIdentity object for the user. More 1618 * information on how to use the Identity Repository APIs is 1619 * available in the "Customizing Identity Data Storage" chapter 1620 * of the OpenAM Developer's Guide. 1621 * 1622 * @supported.api 1623 */ 1624 public AMUser getUserProfile(String userDN) throws AuthLoginException{ 1625 AMUser user = null; 1626 try { 1627 user = AuthD.getAuth().getSDK().getUser(userDN); 1628 } catch (Exception ex) { 1629 debug.message("getUserProfile", ex); 1630 throw new AuthLoginException(ex); 1631 } 1632 return user; 1633 } 1634 1635 /** 1636 * Returns the property from the user session. If the session is being force 1637 * upgraded then set on the old session otherwise set on the current session. 1638 * 1639 * @param name The property name. 1640 * @return The property value. 1641 * @throws AuthLoginException if the user session is invalid. 1642 * 1643 * @supported.api 1644 */ 1645 public String getUserSessionProperty(String name) 1646 throws AuthLoginException { 1647 InternalSession sess = null; 1648 1649 if (getLoginState(null).isSessionUpgrade() && 1650 getLoginState(null).getForceFlag()) { 1651 sess = getLoginState(null).getOldSession(); 1652 } else { 1653 sess = getLoginState("getUserSessionProperty()").getSession(); 1654 } 1655 1656 if (sess != null) { 1657 return sess.getProperty(name); 1658 } else { 1659 return null; 1660 } 1661 } 1662 1663 /** 1664 * Sets a property in the user session. If the session is being force 1665 * upgraded then set on the old session otherwise set on the current session. 1666 * 1667 * @param name The property name. 1668 * @param value The property value. 1669 * @throws AuthLoginException if the user session is invalid. 1670 * 1671 * @supported.api 1672 */ 1673 public void setUserSessionProperty(String name, String value) 1674 throws AuthLoginException { 1675 InternalSession sess = null; 1676 1677 if (getLoginState(null).isSessionUpgrade() && 1678 getLoginState(null).getForceFlag()) { 1679 sess = getLoginState(null).getOldSession(); 1680 } else { 1681 sess = getLoginState("setUserSessionProperty()").getSession(); 1682 } 1683 1684 if (sess != null) { 1685 sess.putProperty(name, value); 1686 } else { 1687 throw new AuthLoginException(bundleName, "wrongCall", 1688 new Object[]{" setUserSessionProperty()"}); 1689 } 1690 } 1691 1692 /** 1693 * Returns a set of user IDs generated from the class defined 1694 * in the Core Authentication Service. Returns null if the 1695 * attribute <code>iplanet-am-auth-username-generator-enabled</code> is 1696 * set to false. 1697 * 1698 * @param attributes the keys in the <code>Map</code> contains the 1699 * attribute names and their corresponding values in 1700 * the <code>Map</code> is a <code>Set</code> that 1701 * contains the values for the attribute 1702 * @param num the maximum number of returned user IDs; 0 means there 1703 * is no limit 1704 * @return a set of auto-generated user IDs 1705 * @throws AuthLoginException if the class instantiation failed 1706 * 1707 * @supported.api 1708 */ 1709 public Set getNewUserIDs(Map attributes, int num) 1710 throws AuthLoginException { 1711 boolean enabled = getLoginState( 1712 "getNewUserIDs(Map, int)").isUserIDGeneratorEnabled(); 1713 1714 if (!enabled) { 1715 return null; 1716 } 1717 1718 String className = getLoginState( 1719 "getNewUserIDs(Map, int)").getUserIDGeneratorClassName(); 1720 String orgDN = getLoginState("getNewUserIDs(Map, int)").getOrgDN(); 1721 1722 // if className is null or empty, use the default user ID 1723 // generator class name 1724 if (className == null || className.length() == 0) { 1725 className = ISAuthConstants.DEFAULT_USERID_GENERATOR_CLASS; 1726 } 1727 1728 UserIDGenerator idGenerator = null; 1729 try { 1730 // instantiate the Java class 1731 Class theClass = Class.forName(className); 1732 idGenerator = (UserIDGenerator)theClass.newInstance(); 1733 1734 } catch (Exception e) { 1735 debug.message("getNewUserIDs(): unable to instantiate " + 1736 className, e); 1737 return null; 1738 } 1739 1740 return (idGenerator.generateUserIDs(orgDN, attributes, num)); 1741 } 1742 1743 /** 1744 * Sets the the login failure URL for the user. This method does not 1745 * change the URL in the user's profile. When the user authenticates 1746 * failed, this URL will be used by the authentication for the 1747 * redirect. 1748 * 1749 * @param url URL to go when authentication failed. 1750 * @throws AuthLoginException if unable to set the URL. 1751 * 1752 * @supported.api 1753 */ 1754 public void setLoginFailureURL(String url) throws AuthLoginException { 1755 getLoginState("setLoginFailureURL()").setFailureLoginURL(url); 1756 } 1757 1758 /** 1759 * Sets the error template for the module 1760 * @param templateName the error template for the module 1761 * @throws AuthLoginException when unable to set the template 1762 */ 1763 public void setModuleErrorTemplate(String templateName) 1764 throws AuthLoginException { 1765 getLoginState( 1766 "setModuleTemplate()").setModuleErrorTemplate(templateName); 1767 } 1768 1769 /** 1770 * Sets the the login successful URL for the user. This method does not 1771 * change the URL in the user's profile. When the user authenticates 1772 * successfully, this URL will be used by the authentication for the 1773 * redirect. 1774 * 1775 * @param url <code>URL</code> to go when authentication is successful. 1776 * @throws AuthLoginException if unable to set the URL. 1777 * @supported.api 1778 */ 1779 public void setLoginSuccessURL(String url) throws AuthLoginException { 1780 getLoginState("setLoginSuccessURL()").setSuccessLoginURL(url); 1781 } 1782 1783 /** 1784 * Sets the user organization. This method should only be called when the 1785 * user authenticates successfully. It allows the user authentication 1786 * module to decide in which domain the user profile should be created. 1787 * 1788 * @param orgDN The organization DN. 1789 * @throws AuthLoginException 1790 */ 1791 public void setOrg(String orgDN) throws AuthLoginException { 1792/* TODO 1793 if (orgDN.indexOf("=") == -1) { 1794 throw new AuthLoginException(bundleName, "invalidDN", 1795 new Object[]{orgDN}); 1796 } 1797 getLoginState("setOrg()").setOrg(orgDN); 1798 */ 1799 return; 1800 } 1801 1802 /** 1803 * Checks if a Callback is required to have input. 1804 * @param state Order of state. 1805 * @param index Order of the Callback in the Callback[], the index. 1806 * starts with 0. 1807 * @return <code>true</code> if the callback corresponding to the number 1808 * in the specified state is required to have value, 1809 * <code>false</code> otherwise 1810 * @supported.api 1811 */ 1812 public boolean isRequired(int state, int index) { 1813 // check state 1814 if (state > stateLength) { 1815 // invalid state, return false now 1816 return false; 1817 } 1818 // get internal callbacks for the state 1819 Callback[] callbacks = (Callback[]) internal.get(state - 1); 1820 if (callbacks == null || callbacks.length == 0) { 1821 // no callbacks defined for this state, return false 1822 return false; 1823 } 1824 // check first Callback 1825 Callback callback = callbacks[0]; 1826 if (callback instanceof PagePropertiesCallback) { 1827 List req = ((PagePropertiesCallback) callback).getRequire(); 1828 if (req == null || req.isEmpty() || index >= req.size()) { 1829 return false; 1830 } else { 1831 String tmp = (String) req.get(index); 1832 if (tmp.equalsIgnoreCase("true")) { 1833 return true; 1834 } else { 1835 return false; 1836 } 1837 } 1838 } else { 1839 return false; 1840 } 1841 } 1842 1843 /** 1844 * Returns the info text associated with a specific callback 1845 * 1846 * @param state The state to fetch the info text 1847 * @param index The callback to fetch the info text 1848 * @return The info text 1849 * @supported.api 1850 */ 1851 public String getInfoText(int state, int index) { 1852 // check state 1853 if (state > stateLength) { 1854 // invalid state, return empty string now 1855 return EMPTY_STRING; 1856 } 1857 // get internal callbacks for the state 1858 Callback[] callbacks = (Callback[]) internal.get(state - 1); 1859 if (callbacks == null || callbacks.length == 0) { 1860 // no callbacks defined for this state, return empty string 1861 return EMPTY_STRING; 1862 } 1863 // check first Callback 1864 Callback callback = callbacks[0]; 1865 if (callback instanceof PagePropertiesCallback) { 1866 List<String> infoText = ((PagePropertiesCallback) callback).getAttribute(); 1867 if (infoText == null || infoText.isEmpty() || index >= infoText.size()) { 1868 return EMPTY_STRING; 1869 } else { 1870 return infoText.get(index); 1871 } 1872 } else { 1873 return EMPTY_STRING; 1874 } 1875 } 1876 1877 /** 1878 * Returns the attribute name for the specified callback in the 1879 * specified login state. 1880 * 1881 * @param state Order of state 1882 * @param index Order of the Callback in the Callback[], the index 1883 * starts with 0. 1884 * @return Name of the attribute, empty string will be returned 1885 * if the attribute is not defined. 1886 * @supported.api 1887 */ 1888 public String getAttribute(int state, int index) { 1889 // check state 1890 if (state > stateLength) { 1891 // invalid state, return empty string now 1892 return EMPTY_STRING; 1893 } 1894 // get internal callbacks for the state 1895 Callback[] callbacks = (Callback[]) internal.get(state - 1); 1896 if (callbacks == null || callbacks.length == 0) { 1897 // no callbacks defined for this state, return empty string 1898 return EMPTY_STRING; 1899 } 1900 // check first Callback 1901 Callback callback = callbacks[0]; 1902 if (callback instanceof PagePropertiesCallback) { 1903 List req = ((PagePropertiesCallback) callback).getAttribute(); 1904 if (req == null || req.isEmpty() || index >= req.size()) { 1905 return EMPTY_STRING; 1906 } else { 1907 return (String) req.get(index); 1908 } 1909 } else { 1910 return EMPTY_STRING; 1911 } 1912 } 1913 1914 /** 1915 * Aborts the authentication process. 1916 * <p> 1917 * This JAAS LoginModule method must be implemented by user's module. 1918 * <p> 1919 * This method is called if the overall authentication 1920 * failed. (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL 1921 * LoginModules did not succeed). 1922 * If this LoginModule's own authentication attempt succeeded (checked by 1923 * retrieving the private state saved by the login method), then this 1924 * method cleans up any state that was originally saved. 1925 * 1926 * @return <code>true</code> if this method succeeded,<code>false</code> 1927 * if this LoginModule should be ignored. 1928 * @throws AuthLoginException if the abort fails 1929 * @see javax.security.auth.spi.LoginModule#abort 1930 */ 1931 public final boolean abort() throws AuthLoginException { 1932 debug.message("ABORT return.... false"); 1933 if (succeeded == false) { 1934 return false; 1935 } else { 1936 logout(); 1937 } 1938 return true; 1939 } 1940 1941 /** 1942 * Commit the authentication process (phase 2). 1943 * <p> 1944 * This JAAS LoginModule method must be implemented by user's module. 1945 * <p> 1946 * This method is called if the overall authentication 1947 * succeeded (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL 1948 * LoginModules succeeded). 1949 * <p> 1950 * If this LoginModule's own authentication attempt succeeded (checked by 1951 * retrieving the private state saved by the login method), then this 1952 * method associates relevant Principals and Credentials with the Subject 1953 * located in the LoginModule. If this LoginModule's own authentication 1954 * attempted failed, then this method removes/destroys any state that was 1955 * originally saved. 1956 * 1957 * @return <code>true</code> if this method succeeded, or <code>false</code> 1958 * if this <code>LoginModule</code> should be ignored. 1959 * @throws AuthLoginException if the commit fails 1960 * @see javax.security.auth.spi.LoginModule#commit 1961 */ 1962 public final boolean commit() throws AuthLoginException { 1963 principal = getPrincipal(); 1964 if (debug.messageEnabled()) { 1965 debug.message( 1966 "AMLoginModule.commit():Succeed,principal=" + principal); 1967 } 1968 if (succeeded == false || principal == null) { 1969 return false; 1970 } else if (!subject.getPrincipals().contains(principal)) { 1971 subject.getPrincipals().add(principal); 1972 debug.message("Done added user to principal"); 1973 } 1974 cleanup(); 1975 return true; 1976 } 1977 1978 /** 1979 * Logs out a Subject. 1980 * <p> 1981 * This JAAS LoginModule method must be implemented by user's module. 1982 * <p> 1983 * An implementation of this method might remove/destroy a Subject's 1984 * Principals and Credentials. 1985 * 1986 * @return <code>true</code> if this method succeeded, or <code>false</code> 1987 * if this LoginModule should be ignored. 1988 * @throws AuthLoginException if the logout fails 1989 * @see javax.security.auth.spi.LoginModule#logout 1990 */ 1991 public final boolean logout() throws AuthLoginException { 1992 // logging out 1993 if (subject.getPrincipals().contains(principal)) { 1994 subject.getPrincipals().remove(principal); 1995 } 1996 succeeded = false; 1997 cleanup(); 1998 return true; 1999 } 2000 2001 /** 2002 * Sets the <code>userID</code> of user who failed authentication. 2003 * This <code>userID</code> will be used to log failed authentication in 2004 * the OpenSSO error logs. 2005 * 2006 * @param userID user name of user who failed authentication. 2007 * @supported.api 2008 */ 2009 public void setFailureID(String userID) { 2010 // get login state for this authentication session 2011 if (userID == null) { 2012 return; 2013 } 2014 debug.message("setFailureID : " + userID); 2015 if (loginState == null) { 2016 loginState = getLoginState(); 2017 if (loginState == null) { 2018 // may be should throw AuthLoginException here 2019 debug.error("Unable to set set userId : " + userID); 2020 return; 2021 } 2022 } 2023 loginState.setFailedUserId(userID); 2024 return ; 2025 } 2026 2027 /** 2028 * Sets a Map of attribute value pairs to be used when the authentication 2029 * service is configured to dynamically create a user. 2030 * 2031 * @param attributeValuePairs A map containing the attributes 2032 * and its values. The key is the attribute name and the value 2033 * is a Set of values. 2034 * 2035 * @supported.api 2036 */ 2037 public void setUserAttributes(Map attributeValuePairs) { 2038 // get login state for this authentication session 2039 if (loginState == null) { 2040 loginState = getLoginState(); 2041 if (loginState == null) { 2042 debug.error("Unable to set user attributes"); 2043 return; 2044 } 2045 } 2046 loginState.setUserCreationAttributes(attributeValuePairs); 2047 return; 2048 } 2049 2050 /** 2051 * Validates the given user name by using validation plugin if exists 2052 * else it checks invalid characters in the source string. 2053 * 2054 * @param userName source string which should be validated. 2055 * @param regEx the pattern for which to search. 2056 * @throws UserNamePasswordValidationException if user name is invalid. 2057 * @supported.api 2058 */ 2059 public void validateUserName(String userName, String regEx) 2060 throws UserNamePasswordValidationException { 2061 try { 2062 AMUserPasswordValidation plugin = getUPValidationInstance(); 2063 if (plugin != null) { 2064 debug.message("Validating username..."); 2065 Map envMap = new HashMap(2); 2066 envMap.put( 2067 com.sun.identity.shared.Constants.ORGANIZATION_NAME, 2068 getRequestOrg()); 2069 plugin.validateUserID(userName, envMap); 2070 } else if (regEx != null && (regEx.length() != 0)) { 2071 if (! (ISValidation.validate(userName, regEx, debug))) { 2072 throw new UserNamePasswordValidationException(bundleName, 2073 "invalidChars", null); 2074 } 2075 } 2076 } catch (AMException ame) { 2077 if (debug.messageEnabled()) { 2078 debug.message("User Name validation Failed" + ame.getMessage()); 2079 } 2080 throw new UserNamePasswordValidationException(ame); 2081 } catch (Exception ex) { 2082 debug.message( 2083 "unKnown Exception occured during username validation"); 2084 throw new UserNamePasswordValidationException(ex); 2085 } 2086 } 2087 2088 2089 /** 2090 * Sets the moduleName of successful LoginModule. 2091 * This moduleName will be populated in the session 2092 * property "AuthType" 2093 * @param moduleName name of module 2094 */ 2095 private void setSuccessModuleName(String moduleName) { 2096 // get login state for this authentication session 2097 if (loginState == null) { 2098 loginState = getLoginState(); 2099 if (loginState == null) { 2100 debug.error("Unable to set moduleName : " + moduleName); 2101 return; 2102 } 2103 } 2104 if (debug.messageEnabled()) { 2105 debug.message("SETTING Module name.... :" + moduleName); 2106 } 2107 loginState.setSuccessModuleName(moduleName); 2108 loginState.saveSharedStateAttributes(); 2109 if (getPrincipal() != null && getPrincipal().getName() != null) { 2110 loginState.saveAuthenticatedPrincipal(getPrincipal().getName()); 2111 } 2112 2113 auditor.auditModuleSuccess(loginState, getPrincipal(), getAuditEntryDetail()); 2114 } 2115 2116 /** 2117 * Checks if valid user exists. 2118 * 2119 * @param userDN the distinguished name of the user. 2120 * @return <code>true</code> if user exists,<code>false</code>otherwise 2121 */ 2122 public boolean isValidUserEntry(String userDN) { 2123 // TODO - IdRepo does not have an equivalent of this 2124 // this method is mainly called to validate DSAME Users 2125 // which are going to be processed differently. 2126 2127 boolean isValidUser = false; 2128 try { 2129 isValidUser = 2130 (AuthD.getAuth().getIdentity(IdType.USER, userDN, "/") != null); 2131 } catch (AuthException e) { 2132 debug.message("User Valid :" + isValidUser); 2133 } 2134 return isValidUser; 2135 } 2136 2137 /** 2138 * Checks if distinguished user name is a super admin. 2139 * 2140 * @param userDN the distinguished name of the user. 2141 * @return <code>true</code> if distinguished user name is a super admin. 2142 */ 2143 public boolean isSuperAdmin(String userDN) { 2144 boolean isSuperAdmin = AuthD.getAuth().isSuperAdmin(userDN); 2145 if (debug.messageEnabled()) { 2146 debug.message("is SuperAdmin : " + isSuperAdmin); 2147 } 2148 return isSuperAdmin; 2149 } 2150 2151 /** 2152 * Validate password for the distinguished user, this will use validation 2153 * plugin if exists to validate password 2154 * 2155 * @param userPassword source string which should be validated. 2156 * @throws UserNamePasswordValidationException if user password is invalid. 2157 * 2158 * @supported.api 2159 */ 2160 public void validatePassword(String userPassword) 2161 throws UserNamePasswordValidationException { 2162 AMUserPasswordValidation plugin = getUPValidationInstance(); 2163 try { 2164 if (plugin != null) { 2165 if (debug.messageEnabled()) { 2166 debug.message("Validating password..."); 2167 } 2168 2169 plugin.validatePassword(userPassword); 2170 } else { 2171 if (debug.messageEnabled()) { 2172 debug.message("No plugin found"); 2173 } 2174 } 2175 } catch (AMException ame) { 2176 if (debug.messageEnabled()) { 2177 debug.message("Password validation Failed " + ame.getMessage()); 2178 } 2179 2180 throw new UserNamePasswordValidationException(ame); 2181 } catch (Exception ex) { 2182 if (debug.messageEnabled()) { 2183 debug.message("Unknown Exception occured during password validation"); 2184 } 2185 2186 throw new UserNamePasswordValidationException(ex); 2187 } 2188 } 2189 2190 /* 2191 * this method instantiates and returns plugin object 2192 */ 2193 private AMUserPasswordValidation getUPValidationInstance() { 2194 2195 try { 2196 String className ; 2197 String orgDN = getRequestOrg(); 2198 if (orgDN != null){ 2199 className = getOrgPluginClassName(orgDN); 2200 } 2201 else { 2202 className = getPluginClassName(); 2203 } 2204 2205 if (debug.messageEnabled()) { 2206 debug.message("UserPasswordValidation Class Name is : " + 2207 className); 2208 } 2209 if ( (className == null) || (className.length() == 0)) { 2210 return null; 2211 } 2212 2213 AMUserPasswordValidation userPasswordInstance = 2214 (AMUserPasswordValidation) 2215 (Class.forName(className).newInstance()); 2216 return userPasswordInstance; 2217 } catch (ClassNotFoundException ce) { 2218 if (debug.messageEnabled()) { 2219 debug.message("Class not Found :", ce); 2220 } 2221 return null; 2222 } catch (Exception e) { 2223 if (debug.messageEnabled()) { 2224 debug.message("Error: ", e); 2225 } 2226 return null; 2227 } 2228 } 2229 2230 /* 2231 * this method gets plugin classname from adminstration service for the org 2232 */ 2233 private String getOrgPluginClassName(String orgDN) { 2234 try { 2235 String cachedValue = AdministrationServiceListener. 2236 getOrgPluginNameFromCache(orgDN); 2237 if (cachedValue != null) { 2238 return cachedValue; 2239 } 2240 Map config = 2241 getOrgServiceTemplate(orgDN,ISAuthConstants.ADMINISTRATION_SERVICE); 2242 String className = 2243 Misc.getServerMapAttr(config, 2244 ISAuthConstants.USERID_PASSWORD_VALIDATION_CLASS); 2245 if (debug.messageEnabled()) { 2246 debug.message("Org Plugin Class: " + className); 2247 } 2248 AdministrationServiceListener.setOrgPluginNameInCache( 2249 orgDN, className); 2250 return className; 2251 } catch (Exception ee) { 2252 debug.message("Error while getting UserPasswordValidationClass " ,ee ); 2253 return null; 2254 } 2255 } 2256 2257 /* 2258 * this method gets plugin classname from adminstration service 2259 */ 2260 private String getPluginClassName() throws AuthLoginException { 2261 String cachedValue = AdministrationServiceListener. 2262 getGlobalPluginNameFromCache(); 2263 if (cachedValue != null) { 2264 return cachedValue; 2265 } 2266 Map config = getServiceConfig(ISAuthConstants.ADMINISTRATION_SERVICE); 2267 String className = 2268 CollectionHelper.getServerMapAttr( 2269 config, ISAuthConstants.USERID_PASSWORD_VALIDATION_CLASS); 2270 if (debug.messageEnabled()) { 2271 debug.message("Plugin Class: " + className); 2272 } 2273 AdministrationServiceListener.setGlobalPluginNameInCache( 2274 className); 2275 return className; 2276 } 2277 2278 /** 2279 * Sets the moduleName of failed login module 2280 * @param moduleName - module name of the failed module 2281 */ 2282 2283 private void setFailureModuleName(String moduleName) { 2284 // get login state for this authentication session 2285 if (loginState == null) { 2286 loginState = getLoginState(); 2287 if (loginState == null) { 2288 debug.error("Unable to set moduleName : " + moduleName); 2289 return; 2290 } 2291 } 2292 if (debug.messageEnabled()) { 2293 debug.message("SETTING Failure Module name.... :" + moduleName); 2294 } 2295 loginState.setFailureModuleName(moduleName); 2296 loginState.saveSharedStateAttributes(); 2297 2298 auditor.auditModuleFailure(loginState, getPrincipal(), getAuditEntryDetail()); 2299 } 2300 2301 /** 2302 * Returns JAAS shared state user key. 2303 * 2304 * @return user key. 2305 */ 2306 public String getUserKey() { 2307 return ISAuthConstants.SHARED_STATE_USERNAME; 2308 } 2309 2310 /** 2311 * Returns JAAS shared state password key. 2312 * 2313 * @return password key 2314 */ 2315 public String getPwdKey() { 2316 return ISAuthConstants.SHARED_STATE_PASSWORD; 2317 } 2318 2319 // cleanup method for Auth constants 2320 private void cleanup() { 2321 principal = null; 2322 if (sharedState !=null) { 2323 sharedState.remove(ISAuthConstants.SHARED_STATE_USERNAME); 2324 sharedState.remove(ISAuthConstants.SHARED_STATE_PASSWORD); 2325 } 2326 sharedState = null; 2327 destroyModuleState(); 2328 } 2329 2330 /** 2331 * Stores user name password into shared state map 2332 * this method should be called after successfull 2333 * authentication by each individual modules. 2334 * 2335 * @param user user name 2336 * @param passwd user password 2337 */ 2338 public void storeUsernamePasswd(String user, String passwd) { 2339 // store only if store shared state is enabled 2340 if (isStore && sharedState !=null) { 2341 sharedState.put(ISAuthConstants.SHARED_STATE_USERNAME, user); 2342 sharedState.put(ISAuthConstants.SHARED_STATE_PASSWORD, passwd); 2343 } 2344 } 2345 2346 /** 2347 * Checks if shared state enabled for the module. 2348 * 2349 * @return <code>true</code> if shared state enabled for the module. 2350 */ 2351 public boolean isSharedStateEnabled() { 2352 return isSharedState; 2353 } 2354 2355 /** 2356 * Sets flag to force read call backs in auth chain process. 2357 * @param val - value to force reading call backs 2358 */ 2359 public void setForceCallbacksRead(boolean val) { 2360 forceCallbacksRead = val; 2361 } 2362 2363 /** 2364 * This method returns use first pass enabled or not 2365 * @return return true if use first pass is enabled for the module 2366 */ 2367 public boolean isUseFirstPassEnabled() { 2368 return (sharedStateBehaviorPattern != null) && 2369 sharedStateBehaviorPattern.equals("useFirstPass"); 2370 } 2371 2372 /** 2373 * Returns <code>AMIdentityRepostiory</code> handle for an organization. 2374 * 2375 * @param orgDN the organization name. 2376 * @return <code>AMIdentityRepostiory</code> object 2377 */ 2378 public AMIdentityRepository getAMIdentityRepository(String orgDN) { 2379 return AuthD.getAuth().getAMIdentityRepository(orgDN); 2380 } 2381 2382 /** 2383 * Creates <code>AMIdentity</code> in the repository. 2384 * 2385 * @param userName name of user to be created. 2386 * @param userAttributes Map of default attributes. 2387 * @param userRoles Set of default roles. 2388 * @throws IdRepoException 2389 * @throws SSOException 2390 */ 2391 public void createIdentity( 2392 String userName, 2393 Map userAttributes, 2394 Set userRoles 2395 ) throws IdRepoException, SSOException { 2396 if (loginState == null) { 2397 loginState = getLoginState(); 2398 if (loginState == null) { 2399 debug.error("Unable to create Identity: " + userName); 2400 return ; 2401 } 2402 } 2403 loginState.createUserIdentity(userName,userAttributes,userRoles); 2404 return; 2405 } 2406 2407 /** 2408 * Get the number of failed login attempts for a user when account locking 2409 * is enabled. 2410 * @return number of failed attempts, -1 id account locking is not enabled. 2411 * @throws AuthenticationException if the user name passed in is not valid 2412 * or null, or for any other error condition. 2413 * @supported.api 2414 */ 2415 public int getFailCount(AMIdentity amIdUser) throws AuthenticationException { 2416 AccountLockoutInfo acInfo = null; 2417 if (loginState == null) { 2418 loginState = getLoginState(); 2419 if (loginState == null) { 2420 throw new AuthenticationException(bundleName, "nullLoginState", 2421 null); 2422 } 2423 } 2424 ISAccountLockout isAccountLockout = new ISAccountLockout( 2425 loginState.getLoginFailureLockoutMode(), 2426 loginState.getLoginFailureLockoutTime(), 2427 loginState.getLoginFailureLockoutCount(), 2428 loginState.getLoginLockoutNotification(), 2429 loginState.getLoginLockoutUserWarning(), 2430 loginState.getLoginLockoutAttrName(), 2431 loginState.getLoginLockoutAttrValue(), 2432 loginState.getLoginFailureLockoutDuration(), 2433 loginState.getLoginFailureLockoutMultiplier(), 2434 loginState.getInvalidAttemptsDataAttrName(), 2435 bundleName); 2436 isAccountLockout.setStoreInvalidAttemptsInDS( 2437 loginState.getLoginFailureLockoutStoreInDS()); 2438 2439 try { 2440 if (!isAccountLockout.isLockoutEnabled()) { 2441 debug.message("Failure lockout mode disabled"); 2442 return -1; 2443 } else { 2444 if (debug.messageEnabled()) { 2445 debug.message("AMLogiModule.getFailCount()::" 2446 +"lockout is enabled"); 2447 } 2448 2449 String userDN = null; 2450 userDN = normalizeDN(IdUtils.getDN(amIdUser)); 2451 2452 if (acInfo == null) { 2453 acInfo = isAccountLockout.getAcInfo(userDN,amIdUser); 2454 } 2455 int failCount = acInfo.getFailCount(); 2456 if (debug.messageEnabled()) { 2457 debug.message("AMLoginModule.getFailCount:failCount " 2458 +"returned:" +failCount); 2459 } 2460 return failCount; 2461 } 2462 } catch (Exception ex) { 2463 debug.error("AMLoginModule.getFailCount:Error", ex); 2464 throw new AuthenticationException(ex.getMessage()); 2465 } 2466 } 2467 2468 /** 2469 * Get the maximum number failed login attempts permitted for a user 2470 * before when their account is locked out. 2471 * 2472 * @return the maximum number of failed attempts 2473 * @supported.api 2474 */ 2475 public int getMaximumFailCount() 2476 throws AuthenticationException { 2477 if (loginState == null) { 2478 loginState = getLoginState(); 2479 2480 if (loginState == null) { 2481 throw new AuthenticationException(bundleName, "nullLoginState", 2482 null); 2483 } 2484 } 2485 2486 return loginState.getLoginFailureLockoutCount(); 2487 } 2488 2489 /** 2490 * Increments the fail count for the given user. 2491 * 2492 * @throws AuthenticationException if the user name passed in is not valid 2493 * or null, or for any other error condition. 2494 * @supported.api 2495 */ 2496 public void incrementFailCount(String userName) 2497 throws AuthenticationException { 2498 if (loginState == null) { 2499 loginState = getLoginState(); 2500 2501 if (loginState == null) { 2502 throw new AuthenticationException(bundleName, "nullLoginState", 2503 null); 2504 } 2505 } 2506 2507 loginState.incrementFailCount(userName); 2508 } 2509 2510 /** 2511 * Returns true if the named account is locked out, false otherwise. 2512 * 2513 * @throws AuthenticationException if the user name passed in is not valid 2514 * or null, or for any other error condition. 2515 * @supported.api 2516 */ 2517 public boolean isAccountLocked(String userName) 2518 throws AuthenticationException { 2519 if (loginState == null) { 2520 loginState = getLoginState(); 2521 2522 if (loginState == null) { 2523 throw new AuthenticationException(bundleName, "nullLoginState", 2524 null); 2525 } 2526 } 2527 2528 boolean accountLocked = loginState.isAccountLocked(userName); 2529 2530 if (ad.debug.messageEnabled()) { 2531 ad.debug.message("isAccountLocked for user=" + userName + " :" + accountLocked); 2532 } 2533 2534 return accountLocked; 2535 } 2536 2537 /* returns the normalized DN */ 2538 private String normalizeDN(String userDN) { 2539 String normalizedDN = userDN; 2540 if ((userDN != null) && LDAPUtils.isDN(userDN)) { 2541 normalizedDN = DNUtils.normalizeDN(userDN); 2542 } 2543 if (ad.debug.messageEnabled()) { 2544 ad.debug.message("Original DN is:" + userDN); 2545 ad.debug.message("Normalized DN is:" + normalizedDN); 2546 } 2547 return normalizedDN; 2548 } 2549 2550 /** 2551 * Authenticates to the datastore using idRepo API 2552 * 2553 * @param callbacks Array of last submitted callbacks to the 2554 * authentication module 2555 * @return <code>true</code> if success. <code>false</code> if failure 2556 * @throws <code> AuthLoginException </code> 2557 */ 2558 private boolean authenticateToDatastore(Callback[] callbacks) 2559 throws AuthLoginException { 2560 boolean retval = false; 2561 boolean needToCheck = false; 2562 Callback[] idrepoCallbacks = new Callback[2]; 2563 String userName = null; 2564 char[] password = null; 2565 2566 for (int i = 0; i < callbacks.length; i++) { 2567 if (callbacks[i] instanceof NameCallback) { 2568 NameCallback nc = (NameCallback) callbacks[i]; 2569 userName = nc.getName(); 2570 if (debug.messageEnabled()){ 2571 debug.message("AMLoginModule.authenticateToDatastore:: " 2572 + " user is : " + userName); 2573 debug.message("AMLoginModule.authenticateToDatastore:: " 2574 + " Internal users : " + LoginState.INTERNAL_USERS); 2575 } 2576 2577 if (LoginState.INTERNAL_USERS.contains( 2578 userName.toLowerCase())) { 2579 needToCheck = true; 2580 } else { 2581 break; 2582 } 2583 2584 } else if (callbacks[i] instanceof PasswordCallback) { 2585 PasswordCallback pc = (PasswordCallback) callbacks[i]; 2586 password = pc.getPassword(); 2587 } 2588 } 2589 if (needToCheck == false) { 2590 return true; 2591 } 2592 2593 if (debug.messageEnabled()){ 2594 debug.message("AMLoginModule.authenticateToDatastore:: " 2595 + "Authenticating Internal user to configuration store"); 2596 } 2597 NameCallback nameCallback = new NameCallback("NamePrompt"); 2598 nameCallback.setName(userName); 2599 idrepoCallbacks[0] = nameCallback; 2600 PasswordCallback passwordCallback = new PasswordCallback( 2601 "PasswordPrompt",false); 2602 passwordCallback.setPassword(password); 2603 idrepoCallbacks[1] = passwordCallback; 2604 try { 2605 AMIdentityRepository idrepo = getAMIdentityRepository( 2606 getRequestOrg()); 2607 retval = idrepo.authenticate(idrepoCallbacks); 2608 if (debug.messageEnabled()){ 2609 debug.message("AMLoginModule.authenticateToDatastore:: " + 2610 " IDRepo authentication successful"); 2611 } 2612 } catch (IdRepoException idrepoExp) { 2613 if (debug.messageEnabled()){ 2614 debug.message("AMLoginModule.authenticateToDatastore:: " 2615 + "IdRepo Exception : ", idrepoExp); 2616 } 2617 } catch (InvalidPasswordException ipe) { 2618 throw new AuthLoginException(AMAuthErrorCode.AUTH_MODULE_DENIED); 2619 } 2620 return retval; 2621 2622 } 2623 2624 /** 2625 * Returns true if the user identified by the supplied username has reached 2626 * their session quota.<br> 2627 * <i>NB</i>The existing session count is exclusive of any session created 2628 * as part of the running authentication process 2629 * 2630 * @param userName the username of the user who's session quota will be checked 2631 * @return true if the user session quota is reached, false otherwise 2632 * @supported.api 2633 */ 2634 public boolean isSessionQuotaReached(String userName) { 2635 int sessionCount = -1; 2636 int sessionQuota = -1; 2637 2638 if (userName == null || userName.equals(Constants.EMPTY)) { 2639 debug.error("AMLoginModule.isSessionQuotaReached :: called with null username"); 2640 return false; 2641 } 2642 2643 try { 2644 // Get the universal ID 2645 AMIdentity amIdUser = ad.getIdentity(IdType.USER, userName, 2646 loginState.getOrgDN()); 2647 2648 String univId = IdUtils.getUniversalId(amIdUser); 2649 2650 if (univId != null) { 2651 sessionQuota = getSessionQuota(amIdUser); 2652 sessionCount = SessionCount.getAllSessionsByUUID(univId).size(); 2653 2654 if (debug.messageEnabled()) { 2655 debug.message("AMLoginModule.isSessionQuotaReached :: univId= " 2656 + univId + " - Session Quota Reached = " + (sessionCount >= sessionQuota)); 2657 } 2658 } else { 2659 debug.error("AMLoginModule.isSessionQuotaReached :: " 2660 + "univId is null , amIdUser is " + amIdUser); 2661 return false; 2662 } 2663 } catch (Exception ex) { 2664 debug.error("AMLoginModule.getSessionQuotaLevel:: " 2665 + "Exception : ", ex); 2666 } 2667 2668 return (sessionCount >= sessionQuota); 2669 } 2670 2671 private int getSessionQuota(AMIdentity iden) { 2672 int quota = SessionConstraint.getDefaultSessionQuota(); 2673 2674 if (iden == null) { 2675 debug.error("AMLoginModule.getSessionQuota :: AMIdentity is null, returning default quota"); 2676 return quota; 2677 } 2678 2679 try { 2680 Map serviceAttrs = 2681 iden.getServiceAttributesAscending("iPlanetAMSessionService"); 2682 2683 Set s = (Set)serviceAttrs.get("iplanet-am-session-quota-limit"); 2684 Iterator attrs = s.iterator(); 2685 if (attrs.hasNext()) { 2686 String attr = (String) attrs.next(); 2687 quota = (Integer.valueOf(attr)).intValue(); 2688 } 2689 } catch (Exception ex) { 2690 debug.error("Failed to get the session quota via the "+ 2691 "IDRepo interfaces, => Use the default " + 2692 "value from the dynamic schema instead.", ex); 2693 } 2694 2695 return quota; 2696 } 2697 2698 2699 /** 2700 * Returns the set of SSOTokens for a specified user 2701 * 2702 * @param userName The username to be used to query the sessions 2703 * @return The set of SSOTokens for the user's current sessions, returns null on error 2704 * @supported.api 2705 */ 2706 public Set<SSOToken> getUserSessions(String userName) { 2707 Set<SSOToken> sessions = new HashSet<SSOToken>(); 2708 2709 if (userName == null || userName.equals(Constants.EMPTY)) { 2710 debug.error("AMLoginModule.getUserSessions :: called with null username"); 2711 return null; 2712 } 2713 2714 try { 2715 // Get the universal ID 2716 AMIdentity amIdUser = ad.getIdentity(IdType.USER, userName, loginState.getOrgDN()); 2717 2718 String univId = IdUtils.getUniversalId(amIdUser); 2719 2720 if (univId != null) { 2721 Map<String, String> currentSessions = SessionCount.getAllSessionsByUUID(univId); 2722 SSOTokenManager manager = SSOTokenManager.getInstance(); 2723 2724 for (String tokenID : currentSessions.keySet()) { 2725 sessions.add(manager.createSSOToken(tokenID)); 2726 } 2727 2728 if (debug.messageEnabled()) { 2729 debug.message("AMLoginModule.getUserSessions :: univId= " 2730 + univId + " - found sessions = " + sessions); 2731 } 2732 } else { 2733 debug.error("AMLoginModule.getUserSessions :: " 2734 + "univId is null , amIdUser is " + amIdUser); 2735 return null; 2736 } 2737 } catch (Exception ex) { 2738 debug.error("AMLoginModule.getUserSessions:: " 2739 + "Exception : ", ex); 2740 } 2741 2742 return sessions; 2743 } 2744 2745 /** 2746 * Provides the "Alias Search Attribute Name" list from the Authentication 2747 * Service for the realm. If these attributes are not configured it falls 2748 * back to the User Naming Attribute for the realm 2749 * @return a set containing the attribute names configured 2750 */ 2751 protected Set<String> getUserAliasList() throws AuthLoginException { 2752 final Map<String, Set<String>> orgSvc = getOrgServiceTemplate(getRequestOrg(), ISAuthConstants.AUTH_SERVICE_NAME); 2753 Set<String> aliasAttrNames = orgSvc.get(ISAuthConstants.AUTH_ALIAS_ATTR); 2754 if (debug.messageEnabled()) { 2755 debug.message("AMLoginModule.getUserAliasList: from " + ISAuthConstants.AUTH_ALIAS_ATTR + ": "+ aliasAttrNames); 2756 } 2757 if (aliasAttrNames.isEmpty()) { 2758 aliasAttrNames = orgSvc.get(ISAuthConstants.AUTH_NAMING_ATTR); 2759 if (debug.messageEnabled()) { 2760 debug.message("AMLoginModule.getUserAliasList: from " + ISAuthConstants.AUTH_NAMING_ATTR +": " 2761 + aliasAttrNames); 2762 } 2763 } 2764 return aliasAttrNames; 2765 } 2766 2767 /** 2768 * Returns the principals authenticated in the current authentication process or an empty set if login state is 2769 * unavailable or no authenticated principals are present. 2770 * 2771 * @return a set of authenticated principals. 2772 */ 2773 protected Set<String> getAuthenticatedPrincipals() { 2774 if (loginState == null) { 2775 loginState = getLoginState(); 2776 } 2777 if (loginState == null) { 2778 if (debug.messageEnabled()) { 2779 debug.message("AMLoginModule.getAuthenticatedPrincipals: ubable to get loginState"); 2780 } 2781 return Collections.emptySet(); 2782 } 2783 return loginState.getAuthenticatedPrincipals(); 2784 } 2785 2786 /** 2787 * Supply the additional detail to be logged with this module's completion event. Subclasses can override this 2788 * method to add more specific detail. 2789 * 2790 * @return The audit entry detail. 2791 */ 2792 protected AuthenticationAuditEntry getAuditEntryDetail() { 2793 AuthenticationAuditEntry entryDetail = new AuthenticationAuditEntry(); 2794 entryDetail.setModuleId(moduleName); 2795 2796 String ip = loginState.getClient(); 2797 if (isNotEmpty(ip)) { 2798 entryDetail.addInfo(IP_ADDRESS, ip); 2799 } 2800 AuthContext.IndexType indexType = loginState.getIndexType(); 2801 if (indexType != null) { 2802 entryDetail.addInfo(AUTH_INDEX, indexType.toString()); 2803 } 2804 entryDetail.addInfo(AUTH_LEVEL, String.valueOf(getAuthLevel())); 2805 entryDetail.addInfo(MODULE_CLASS, moduleClass); 2806 2807 return entryDetail; 2808 } 2809}