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