/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jackrabbit.core.security.authentication; import java.io.IOException; import java.security.Principal; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import javax.jcr.Credentials; import javax.jcr.GuestCredentials; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.SimpleCredentials; import javax.security.auth.Subject; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.auth.login.FailedLoginException; import javax.security.auth.login.LoginException; import javax.security.auth.spi.LoginModule; import org.apache.jackrabbit.api.security.principal.PrincipalIterator; import org.apache.jackrabbit.core.config.LoginModuleConfig; import org.apache.jackrabbit.core.security.SecurityConstants; import org.apache.jackrabbit.core.security.principal.PrincipalProvider; import org.apache.jackrabbit.core.security.principal.PrincipalProviderRegistry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * <code>AbstractLoginModule</code> provides the means for the common * authentication tasks within the Repository. * <p> * On successful authentication it associates the credentials to principals * using the {@link PrincipalProvider} configured for this LoginModule * <p> * Jackrabbit distinguishes between Login and Impersonation dispatching the * the corresponding Repository/Session methods to * {@link #authenticate(java.security.Principal, javax.jcr.Credentials)} and * {@link #impersonate(java.security.Principal, javax.jcr.Credentials)}, respectively. * <br> * This LoginModule implements default behavior for either method. * * @see LoginModule */ public abstract class AbstractLoginModule implements LoginModule { private static final Logger log = LoggerFactory.getLogger(AbstractLoginModule.class); private static final String KEY_CREDENTIALS = "org.apache.jackrabbit.credentials"; private static final String KEY_LOGIN_NAME = "javax.security.auth.login.name"; /** * The name of the login module configuration option providing the name * of the SimpleCredentials attribute used to identify a pre-authenticated * login. * * @see #isPreAuthenticated(Credentials) * @deprecated For security reasons this configuration option has been * deprecated and will no longer be supported in a subsequent release. * See also <a href="https://issues.apache.org/jira/browse/JCR-3293">JCR-3293</a> */ private static final String PRE_AUTHENTICATED_ATTRIBUTE_OPTION = "trust_credentials_attribute"; private String principalProviderClassName; private boolean initialized; protected String adminId; protected String anonymousId; /** * The name of the credentials attribute providing a hint that the * credentials should be taken as is and the user requesting access * has already been authenticated outside of this LoginModule. * * @see #getPreAuthAttributeName() * @deprecated For security reasons the support for the preAuth attribute * has been deprecated and will no longer be available in a subsequent release. * See also <a href="https://issues.apache.org/jira/browse/JCR-3293">JCR-3293</a> */ private String preAuthAttributeName; protected CallbackHandler callbackHandler; protected Principal principal; protected SimpleCredentials credentials; protected Subject subject; protected PrincipalProvider principalProvider; protected Map sharedState; /** * Initialize this LoginModule and sets the following fields for later usage: * <ul> * <li>{@link PrincipalProvider} for user-{@link Principal} resolution.</li> * <li>{@link LoginModuleConfig#PARAM_ADMIN_ID} option is evaluated</li> * <li>{@link LoginModuleConfig#PARAM_ANONYMOUS_ID} option is evaluated</li> * </ul> * Implementations are called via * {@link #doInit(CallbackHandler, Session, Map)} to implement * additional initialization * * @param subject the <code>Subject</code> to be authenticated. <p> * @param callbackHandler a <code>CallbackHandler</code> for communicating * with the end user (prompting for usernames and * passwords, for example). <p> * @param sharedState state shared with other configured * LoginModules.<p> * @param options options specified in the login <code>Configuration</code> * for this particular <code>LoginModule</code>. * @see LoginModule#initialize(Subject, CallbackHandler, Map, Map) * @see #doInit(CallbackHandler, Session, Map) * @see #isInitialized() */ public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String,?> sharedState, Map<String,?> options) { // common jaas state variables this.callbackHandler = callbackHandler; this.subject = subject; this.sharedState = sharedState; // initialize the login module try { log.debug("Initialize LoginModule: "); RepositoryCallback repositoryCb = new RepositoryCallback(); callbackHandler.handle(new Callback[]{repositoryCb}); PrincipalProviderRegistry registry = repositoryCb.getPrincipalProviderRegistry(); // check if the class name of a PrincipalProvider implementation // is present with the module configuration. if (options.containsKey(LoginModuleConfig.PARAM_PRINCIPAL_PROVIDER_CLASS)) { Object pcOption = options.get(LoginModuleConfig.PARAM_PRINCIPAL_PROVIDER_CLASS); if (pcOption != null) { principalProviderClassName = pcOption.toString(); } } if (principalProviderClassName == null) { // try compatibility parameters if (options.containsKey(LoginModuleConfig.COMPAT_PRINCIPAL_PROVIDER_NAME)) { principalProviderClassName = options.get(LoginModuleConfig.COMPAT_PRINCIPAL_PROVIDER_NAME).toString(); } else if (options.containsKey(LoginModuleConfig.COMPAT_PRINCIPAL_PROVIDER_CLASS)) { principalProviderClassName = options.get(LoginModuleConfig.COMPAT_PRINCIPAL_PROVIDER_CLASS).toString(); } } if (principalProviderClassName != null) { principalProvider = registry.getProvider(principalProviderClassName); } if (principalProvider == null) { principalProvider = registry.getDefault(); if (principalProvider == null) { return; // abort. not even a default principal provider } } log.debug("- PrincipalProvider -> '" + principalProvider.getClass().getName() + "'"); // call implementation for additional setup doInit(callbackHandler, repositoryCb.getSession(), options); // adminId: if not present in options -> retrieve from callback if (options.containsKey(LoginModuleConfig.PARAM_ADMIN_ID)) { adminId = (String) options.get(LoginModuleConfig.PARAM_ADMIN_ID); } if (adminId == null) { adminId = repositoryCb.getAdminId(); } // anonymousId: if not present in options -> retrieve from callback if (options.containsKey(LoginModuleConfig.PARAM_ANONYMOUS_ID)) { anonymousId = (String) options.get(LoginModuleConfig.PARAM_ANONYMOUS_ID); } if (anonymousId == null) { anonymousId = repositoryCb.getAnonymousId(); } // trusted credentials attribute name (may be missing to not // support) (normalized to null aka missing aka unset if an empty // string) preAuthAttributeName = (String) options.get(PRE_AUTHENTICATED_ATTRIBUTE_OPTION); if (preAuthAttributeName != null && preAuthAttributeName.length() == 0) { preAuthAttributeName = null; } //log config values for debug if (log.isDebugEnabled()) { for (String option : options.keySet()) { log.debug("- Option: " + option + " -> '" + options.get(option) + "'"); } } initialized = (this.subject != null); } catch (Exception e) { log.error("LoginModule failed to initialize.", e); } } /** * Implementations may set-up their own state. * * @param callbackHandler as passed by {@link javax.security.auth.login.LoginContext} * @param session to security-workspace of Jackrabbit * @param options options from LoginModule config * @throws LoginException in case initialization fails. */ protected abstract void doInit(CallbackHandler callbackHandler, Session session, Map options) throws LoginException; /** * Returns <code>true</code> if this module has been successfully initialized. * * @return <code>true</code> if this module has been successfully initialized. * @see LoginModule#initialize(Subject, CallbackHandler, Map, Map) */ protected boolean isInitialized() { return initialized; } /** * Method to authenticate a <code>Subject</code> (phase 1). * <p> * The login is divided into 3 Phases: * <p> * <b>1) User-ID resolution</b><br> * In a first step it is tried to resolve a User-ID for further validation. * As for JCR the identification is marked with the {@link Credentials} * interface, credentials are accessed in this phase.<br> * If no User-ID can be found, anonymous access is granted with the ID of * the anonymous user (as defined in the security configuration). * Anonymous access can be switched off removing the configuration entry. * <br> This implementation uses two helper-methods, which allow for * customization: * <ul> * <li>{@link #getCredentials()} and</li> * <li>{@link #getUserID(Credentials)}</li> * </ul> * <p> * * <b>2) User-Principal resolution </b><br> * In a second step it is tested, if the resolved User-ID belongs to a User * known to the system, i.e. if the {@link PrincipalProvider} has a principal * for the given ID and the principal can be found via * {@link PrincipalProvider#findPrincipals(String)}.<br> * The provider implementation can be set by the LoginModule configuration. * If the option is missing, the system default principal provider will * be used. * <p> * <b>3) Verification</b><br> * There are four cases, how the User-ID can be verified: * The login is anonymous, pre-authenticated or the login is the result of * an impersonation request (see {@link javax.jcr.Session#impersonate(Credentials)} * or of a login to the Repository ({@link javax.jcr.Repository#login(Credentials)}). * The concrete implementation of the LoginModule is responsible for all * four cases: * <ul> * <li>{@link #isAnonymous(Credentials)}</li> * <li>{@link #isPreAuthenticated(Credentials)}</li> * <li>{@link #authenticate(Principal, Credentials)}</li> * <li>{@link #impersonate(Principal, Credentials)}</li> * </ul> * * Under the following conditions, the login process is aborted and the * module is marked to be ignored: * <ul> * <li>No User-ID could be resolve, and anonymous access is switched off</li> * <li>No Principal is found for the User-ID resolved</li> * </ul> * * Under the following conditions, the login process is marked to be invalid * by throwing an LoginException: * <ul> * <li>It is an impersonation request, but the impersonator is not allowed * to impersonate to the requested User-ID</li> * <li>The user tries to login, but the Credentials can not be verified.</li> * </ul> * <p> * The LoginModule keeps the Credentials and the Principal as instance fields, * to mark that login has been successful. * * @return true if the authentication succeeded, or false if this * <code>LoginModule</code> should be ignored. * @throws LoginException if the authentication fails * @see javax.security.auth.spi.LoginModule#login() * @see #getCredentials() * @see #getUserID(Credentials) * @see #getImpersonatorSubject(Credentials) */ public boolean login() throws LoginException { if (!isInitialized()) { log.warn("Unable to perform login: initialization not completed."); return false; } // check the availability and validity of Credentials Credentials creds = getCredentials(); if (creds == null) { log.debug("No credentials available -> try default (anonymous) authentication."); } else if (!supportsCredentials(creds)) { log.debug("Unsupported credentials implementation : " + creds.getClass().getName()); return false; } try { Principal userPrincipal = getPrincipal(creds); if (userPrincipal == null) { // unknown or disabled user or a group log.debug("No valid user -> ignore."); return false; } boolean authenticated; // test for anonymous, pre-authentication, impersonation or common authentication. if (isAnonymous(creds) || isPreAuthenticated(creds)) { authenticated = true; } else if (isImpersonation(creds)) { authenticated = impersonate(userPrincipal, creds); } else { authenticated = authenticate(userPrincipal, creds); } // process authenticated user if (authenticated) { if (creds instanceof SimpleCredentials) { credentials = (SimpleCredentials) creds; } else { credentials = new SimpleCredentials(getUserID(creds), new char[0]); } principal = userPrincipal; return true; } } catch (RepositoryException e) { log.error("Login failed:", e); } return false; } /** * Method to commit the authentication process (phase 2). * <p> * This method is called if the LoginContext's overall authentication * succeeded (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL * LoginModules succeeded). * <p> * If this LoginModule's own authentication attempt succeeded (checked * by retrieving the private state saved by the <code>login</code> method), * then this method associates relevant Principals and Credentials with the * <code>Subject</code> located in the <code>LoginModule</code>. If this * LoginModule's own authentication attempted failed, then this method * removes/destroys any state that was originally saved. * <p> * The login is considered as succeeded if there is a principal set. * <p> * The implementation stores the principal associated to the UserID and all * the Groups it is member of with the Subject and in addition adds an * instance of (#link SimpleCredentials} to the Subject's public credentials. * * @return true if this method succeeded, or false if this * <code>LoginModule</code> should be ignored. * @throws LoginException if the commit fails * @see javax.security.auth.spi.LoginModule#commit() */ public boolean commit() throws LoginException { if (!isInitialized() || principal == null) { return false; } Set<Principal> principals = getPrincipals(); subject.getPrincipals().addAll(principals); subject.getPublicCredentials().add(credentials); return true; } /** * Method to abort the authentication process (phase 2). * <p> * <p> This method is called if the LoginContext's overall authentication * failed. (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL * LoginModules did not succeed). * <p> * <p> If this LoginModule's own authentication attempt succeeded (checked * by retrieving the private state saved by the <code>login</code> method), * then this method cleans up any state that was originally saved. * <p> * <p> * * @return true if this method succeeded, or false if this * <code>LoginModule</code> should be ignored. * @throws LoginException if the abort fails * @see javax.security.auth.spi.LoginModule#abort() */ public boolean abort() throws LoginException { if (!isInitialized()) { return false; } else { sharedState.remove(KEY_CREDENTIALS); callbackHandler = null; principal = null; credentials = null; return logout(); } } /** * @return <code>true</code> if this method succeeded, * or <code>false</code> if this <code>LoginModule</code> should be ignored. * @throws LoginException if the logout fails * @see javax.security.auth.spi.LoginModule#logout() */ public boolean logout() throws LoginException { if (subject.getPrincipals().isEmpty() || subject.getPublicCredentials(Credentials.class).isEmpty()) { return false; } else { // clear subject if not readonly if (!subject.isReadOnly()) { subject.getPrincipals().clear(); subject.getPublicCredentials().clear(); } return true; } } /** * @param principal Principal used to retrieve the <code>Authentication</code> * object. * @param credentials Credentials used for the authentication. * @return <code>true</code> if Credentials authenticate, * <code>false</code> if no <code>Authentication</code> can handle * the given <code>Credentials</code> * @throws javax.security.auth.login.FailedLoginException * if the authentication failed. * @throws RepositoryException If another error occurs. * @see AbstractLoginModule#getAuthentication(java.security.Principal, javax.jcr.Credentials) * @see AbstractLoginModule#authenticate(java.security.Principal, javax.jcr.Credentials) */ protected boolean authenticate(Principal principal, Credentials credentials) throws FailedLoginException, RepositoryException { Authentication auth = getAuthentication(principal, credentials); if (auth == null) { return false; } else if (auth.authenticate(credentials)) { return true; } throw new FailedLoginException(); } /** * Test if the current request is an Impersonation attempt. The default * implementation returns <code>true</code> if an * {@link #getImpersonatorSubject(Credentials) subject} for the * impersonation can be retrieved. * * @param credentials potentially containing impersonation data * @return true if this is an impersonation attempt * @see #getImpersonatorSubject(Credentials) */ protected boolean isImpersonation(Credentials credentials) { return getImpersonatorSubject(credentials) != null; } /** * Handles the impersonation of given Credentials. * * @param principal Principal to impersonate. * @param credentials Credentials used to create the impersonation subject. * @return false, if there is no User to impersonate, * true if impersonation is allowed * @throws LoginException If credentials don't allow to impersonate to principal. * @throws RepositoryException If another error occurs. */ protected abstract boolean impersonate(Principal principal, Credentials credentials) throws RepositoryException, LoginException; /** * Retrieve the <code>Authentication</code>. * * @param principal A principal. * @param creds The Credentials used for the login. * @return Authentication object for the given principal / credentials. * @throws RepositoryException If an error occurs. */ protected abstract Authentication getAuthentication(Principal principal, Credentials creds) throws RepositoryException; /** * Method tries to acquire an Impersonator in the following order: * <ul> * <li> Try to access it from the {@link Credentials} via {@link SimpleCredentials#getAttribute(String)}</li> * <li> Ask CallbackHandler for Impersonator with use of {@link ImpersonationCallback}.</li> * </ul> * * @param credentials which, may contain an impersonation Subject * @return impersonation subject or null if non contained * @see #login() * @see #impersonate(java.security.Principal, javax.jcr.Credentials) */ protected Subject getImpersonatorSubject(Credentials credentials) { Subject impersonator = null; if (credentials == null) { try { ImpersonationCallback impers = new ImpersonationCallback(); callbackHandler.handle(new Callback[]{impers}); impersonator = impers.getImpersonator(); } catch (UnsupportedCallbackException e) { log.warn(e.getCallback().getClass().getName() + " not supported: Unable to perform Impersonation."); } catch (IOException e) { log.error("Impersonation-Callback failed: " + e.getMessage() + ": Unable to perform Impersonation."); } } else if (credentials instanceof SimpleCredentials) { SimpleCredentials sc = (SimpleCredentials) credentials; impersonator = (Subject) sc.getAttribute(SecurityConstants.IMPERSONATOR_ATTRIBUTE); } return impersonator; } /** * Method tries to resolve the {@link Credentials} used for login. It takes * authentication-extension of an already authenticated {@link Subject} into * account. * <p> * Therefore the credentials are retrieved as follows: * <ol> * <li>Test if the shared state contains credentials.</li> * <li>Ask CallbackHandler for Credentials with using a {@link * CredentialsCallback}. Expects {@link CredentialsCallback#getCredentials} * to return an instance of {@link Credentials}.</li> * <li>Ask the Subject for its public <code>SimpleCredentials</code> see * {@link Subject#getPublicCredentials(Class)}, thus enabling to * pre-authenticate the Subject.</li> * </ol> * * @return Credentials or null if not found * @see #login() */ protected Credentials getCredentials() { Credentials credentials = null; if (sharedState.containsKey(KEY_CREDENTIALS)) { credentials = (Credentials) sharedState.get(KEY_CREDENTIALS); } else { try { CredentialsCallback callback = new CredentialsCallback(); callbackHandler.handle(new Callback[]{callback}); credentials = callback.getCredentials(); if (credentials != null && supportsCredentials(credentials)) { sharedState.put(KEY_CREDENTIALS, credentials); } } catch (UnsupportedCallbackException e) { log.warn("Credentials-Callback not supported try Name-Callback"); } catch (IOException e) { log.error("Credentials-Callback failed: " + e.getMessage() + ": try Name-Callback"); } } // if still no credentials -> try to retrieve them from the subject. if (null == credentials) { // try if subject contains SimpleCredentials Set<SimpleCredentials> preAuthCreds = subject.getPublicCredentials(SimpleCredentials.class); if (!preAuthCreds.isEmpty()) { credentials = preAuthCreds.iterator().next(); } } if (null == credentials) { // try if subject contains GuestCredentials Set<GuestCredentials> preAuthCreds = subject.getPublicCredentials(GuestCredentials.class); if (!preAuthCreds.isEmpty()) { credentials = preAuthCreds.iterator().next(); } } return credentials; } /** * Return a flag indicating whether the credentials are supported by * this login module. Default implementation supports * {@link SimpleCredentials} and {@link GuestCredentials}. * * @param creds credentials * @return <code>true</code> if the credentials are supported; * <code>false</code> otherwise */ protected boolean supportsCredentials(Credentials creds) { return creds instanceof SimpleCredentials || creds instanceof GuestCredentials; } /** * Method supports tries to acquire a UserID in the following order: * <ol> * <li>If passed credentials are {@link GuestCredentials} the anonymous user id * is returned.</li> * <li>Try to access it from the {@link Credentials} via {@link * SimpleCredentials#getUserID()}</li> * <li>Ask CallbackHandler for User-ID with use of {@link NameCallback}.</li> * <li>Test if the 'sharedState' contains a login name.</li> * <li>Fallback: return the anonymous UserID.</li> * </ol> * * @param credentials which, may contain a User-ID * @return The userId retrieved from the credentials or by any other means * described above. * @see #login() */ protected String getUserID(Credentials credentials) { String userId = null; if (credentials != null) { if (credentials instanceof GuestCredentials) { userId = anonymousId; } else if (credentials instanceof SimpleCredentials) { userId = ((SimpleCredentials) credentials).getUserID(); } else { try { NameCallback callback = new NameCallback("User-ID: "); callbackHandler.handle(new Callback[]{callback}); userId = callback.getName(); } catch (UnsupportedCallbackException e) { log.warn("Credentials- or NameCallback must be supported"); } catch (IOException e) { log.error("Name-Callback failed: " + e.getMessage()); } } } if (userId == null && sharedState.containsKey(KEY_LOGIN_NAME)) { userId = (String) sharedState.get(KEY_LOGIN_NAME); } // still no userId -> anonymousID if its has been defined. // TODO: check again if correct when used with 'extendedAuth' if (userId == null) { userId = anonymousId; } return userId; } /** * Indicate if the given Credentials are considered to be anonymous. * * @param credentials The Credentials to be tested. * @return <code>true</code> if is anonymous; <code>false</code> otherwise. */ protected boolean isAnonymous(Credentials credentials) { if (credentials instanceof GuestCredentials) { return true; } else { // TODO: review again. former simple-login-module treated 'null' as anonymous (probably wrong). String userId = getUserID(credentials); return (anonymousId == null) ? userId == null : anonymousId.equals(userId); } } /** * Authentication process associates a Principal to Credentials<br> * This method resolves the Principal for the given Credentials. If no valid * Principal can be determined, the LoginModule should be ignored. * * @param credentials Credentials used for to login. * @return the principal associated with the given credentials or <code>null</code>. */ protected abstract Principal getPrincipal(Credentials credentials); /** * @return a Collection of principals that contains the current user * principal and all groups it is member of. */ protected Set<Principal> getPrincipals() { // use linked HashSet instead of HashSet in order to maintain the order // of principals (as in the Subject). Set<Principal> principals = new LinkedHashSet<Principal>(); principals.add(principal); PrincipalIterator groups = principalProvider.getGroupMembership(principal); while (groups.hasNext()) { principals.add(groups.nextPrincipal()); } return principals; } //-------------------------------------------------------------------------- /** * Returns the admin user id. * * @return admin user id */ public String getAdminId() { return adminId; } /** * Sets the administrator's user id. * * @param adminId the administrator's user id. */ public void setAdminId(String adminId) { this.adminId = adminId; } /** * Returns the anonymous user id. * * @return anonymous user id */ public String getAnonymousId() { return anonymousId; } /** * Sets the anonymous user id. * * @param anonymousId anonymous user id */ public void setAnonymousId(String anonymousId) { this.anonymousId = anonymousId; } /** * Returns the configured name of the principal provider class. * * @return name of the principal provider class. */ public String getPrincipalProvider() { return principalProviderClassName; } /** * Sets the configured name of the principal provider class * * @param principalProvider Name of the principal provider class. */ public void setPrincipalProvider(String principalProvider) { this.principalProviderClassName = principalProvider; } /** * The name of the credentials attribute providing a hint that the * credentials should be taken as is and the user requesting access * has already been authenticated outside of this LoginModule. * <p> * This name is configured as the value of the LoginModule configuration * parameter <code>trust_credentials_attribute</code>. If the configuration * parameter is missing (or empty) the name is not set and this method * returns <code>null</code>. * * @see #isPreAuthenticated(Credentials) * @deprecated For security reasons the support for the preAuth attribute * has been deprecated and will no longer be available in a subsequent release. * See also <a href="https://issues.apache.org/jira/browse/JCR-3293">JCR-3293</a> */ protected final String getPreAuthAttributeName() { return preAuthAttributeName; } /** * Returns <code>true</code> if the credentials should be considered as * pre-authenticated and a password check is not required. * <p> * This base class implementation returns <code>true</code> if the * <code>creds</code> object is a SimpleCredentials instance and the * configured {@link #getPreAuthAttributeName() trusted * credentials property} is set to a non-<code>null</code> value in the * credentials attributes. * <p> * Extensions of this class may overwrite this method to apply more or * different checks to the credentials. * * @param creds The Credentials to check * * @see #getPreAuthAttributeName() * @deprecated For security reasons the support for the preAuth attribute * has been deprecated and will no longer be available in a subsequent release. * See also <a href="https://issues.apache.org/jira/browse/JCR-3293">JCR-3293</a> */ protected boolean isPreAuthenticated(final Credentials creds) { final String preAuthAttrName = getPreAuthAttributeName(); boolean isPreAuth = preAuthAttrName != null && (creds instanceof SimpleCredentials) && ((SimpleCredentials) creds).getAttribute(preAuthAttrName) != null; if (isPreAuth) { log.warn("Usage of deprecated 'trust_credentials_attribute' option. " + "Please note that for security reasons this feature will not" + "be supported in future releases."); } return isPreAuth; } }