This is an automated email from the ASF dual-hosted git repository.
ChristopherSchultz pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/main by this push:
new e12e023930 Remove JAASRealm
e12e023930 is described below
commit e12e0239309f8e3113f9c531aaa889329a063a6b
Author: Christopher Schultz <[email protected]>
AuthorDate: Tue Oct 13 18:11:52 2026 +0100
Remove JAASRealm
NOTE: This will not be back-ported
---
.../apache/catalina/realm/JAASCallbackHandler.java | 207 -------
java/org/apache/catalina/realm/JAASRealm.java | 666 ---------------------
test/org/apache/catalina/realm/TestJAASRealm.java | 184 ------
webapps/docs/changelog.xml | 3 +
4 files changed, 3 insertions(+), 1057 deletions(-)
diff --git a/java/org/apache/catalina/realm/JAASCallbackHandler.java
b/java/org/apache/catalina/realm/JAASCallbackHandler.java
deleted file mode 100644
index 9ef1e0890d..0000000000
--- a/java/org/apache/catalina/realm/JAASCallbackHandler.java
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * 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.catalina.realm;
-
-
-import java.io.IOException;
-
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.NameCallback;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.callback.TextInputCallback;
-import javax.security.auth.callback.UnsupportedCallbackException;
-
-import org.apache.tomcat.util.res.StringManager;
-
-/**
- * Implementation of the JAAS <code>CallbackHandler</code> interface, used to
negotiate delivery of the username and
- * credentials that were specified to our constructor. No interaction with the
user is required (or possible).
- * <p>
- * The supplied password or client digest is passed to the
<code>LoginModule</code> unchanged. Any digest comparison is
- * expected to be performed by the <code>LoginModule</code> when it calls back
into the <code>Realm</code>.
- * <p>
- * At present, <code>JAASCallbackHandler</code> knows how to handle callbacks
of type
- * <code>javax.security.auth.callback.NameCallback</code>,
- * <code>javax.security.auth.callback.PasswordCallback</code> and
- * <code>javax.security.auth.callback.TextInputCallback</code>.
- */
-public class JAASCallbackHandler implements CallbackHandler {
-
- // ------------------------------------------------------------ Constructor
-
-
- /**
- * Construct a callback handler configured with the specified values.
- *
- * @param realm Our associated JAASRealm instance
- * @param username Username to be authenticated with
- * @param password Password to be authenticated with
- */
- public JAASCallbackHandler(JAASRealm realm, String username, String
password) {
-
- this(realm, username, password, null, null, null, null, null, null,
null, null);
- }
-
-
- /**
- * Construct a callback handler for DIGEST authentication.
- *
- * @param realm Our associated JAASRealm instance
- * @param username Username to be authenticated with
- * @param password Password to be authenticated with. For DIGEST
authentication this is the client digest, which
- * is passed through unchanged since the digest
comparison is performed by the LoginModule
- * when it calls back into the Realm
- * @param nonce Server generated nonce
- * @param nc Nonce count
- * @param cnonce Client generated nonce
- * @param qop Quality of protection applied to the message
- * @param realmName Realm name
- * @param digestA2 Second digest calculated as digest(Method + ":" + uri)
- * @param algorithm The digest algorithm to use
- * @param authMethod The authentication method in use
- */
- public JAASCallbackHandler(JAASRealm realm, String username, String
password, String nonce, String nc,
- String cnonce, String qop, String realmName, String digestA2,
String algorithm, String authMethod) {
- this.realm = realm;
- this.username = username;
- this.password = password;
- this.nonce = nonce;
- this.nc = nc;
- this.cnonce = cnonce;
- this.qop = qop;
- this.realmName = realmName;
- this.digestA2 = digestA2;
- this.authMethod = authMethod;
- this.algorithm = algorithm;
- }
-
- // ----------------------------------------------------- Instance Variables
-
- /**
- * The string manager for this package.
- */
- protected static final StringManager sm =
StringManager.getManager(JAASCallbackHandler.class);
-
- /**
- * The password to be authenticated with.
- */
- protected final String password;
-
-
- /**
- * The associated <code>JAASRealm</code> instance.
- */
- protected final JAASRealm realm;
-
- /**
- * The username to be authenticated with.
- */
- protected final String username;
-
- /**
- * Server generated nonce.
- */
- protected final String nonce;
-
- /**
- * Nonce count.
- */
- protected final String nc;
-
- /**
- * Client generated nonce.
- */
- protected final String cnonce;
-
- /**
- * Quality of protection applied to the message.
- */
- protected final String qop;
-
- /**
- * Realm name.
- */
- protected final String realmName;
-
- /**
- * Second digest.
- */
- protected final String digestA2;
-
- /**
- * The authentication method to be used. If null, assume BASIC/FORM.
- */
- protected final String authMethod;
-
- /**
- * Algorithm.
- */
- protected final String algorithm;
-
- // --------------------------------------------------------- Public Methods
-
-
- /**
- * Retrieve the information requested in the provided
<code>Callbacks</code>. This implementation only recognizes
- * {@link NameCallback}, {@link PasswordCallback} and {@link
TextInputCallback}. {@link TextInputCallback} is used
- * to pass the various additional parameters required for DIGEST
authentication.
- *
- * @param callbacks The set of <code>Callback</code>s to be processed
- *
- * @exception IOException if an input/output error occurs
- * @exception UnsupportedCallbackException if the login method requests an
unsupported callback type
- */
- @Override
- public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
-
- for (Callback callback : callbacks) {
- switch (callback) {
- case NameCallback nameCallback -> {
- if (realm.getContainer().getLogger().isTraceEnabled()) {
-
realm.getContainer().getLogger().trace(sm.getString("jaasCallback.username",
username));
- }
- nameCallback.setName(username);
- }
- case PasswordCallback passwordCallback -> {
- final char[] passwordcontents;
- if (password != null) {
- passwordcontents = password.toCharArray();
- } else {
- passwordcontents = new char[0];
- }
- passwordCallback.setPassword(passwordcontents);
- }
- case TextInputCallback cb -> {
- switch (cb.getPrompt()) {
- case "nonce" -> cb.setText(nonce);
- case "nc" -> cb.setText(nc);
- case "cnonce" -> cb.setText(cnonce);
- case "qop" -> cb.setText(qop);
- case "realmName" -> cb.setText(realmName);
- case "digestA2" -> cb.setText(digestA2);
- case "authMethod" -> cb.setText(authMethod);
- case "algorithm" -> cb.setText(algorithm);
- case "catalinaBase" ->
cb.setText(realm.getContainer().getCatalinaBase().getAbsolutePath());
- default -> throw new
UnsupportedCallbackException(callback);
- }
- }
- case null, default -> throw new
UnsupportedCallbackException(callback);
- }
- }
- }
-}
diff --git a/java/org/apache/catalina/realm/JAASRealm.java
b/java/org/apache/catalina/realm/JAASRealm.java
deleted file mode 100644
index 40f8d0c8e1..0000000000
--- a/java/org/apache/catalina/realm/JAASRealm.java
+++ /dev/null
@@ -1,666 +0,0 @@
-/*
- * 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.catalina.realm;
-
-import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.security.Principal;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.security.auth.Subject;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.login.AccountExpiredException;
-import javax.security.auth.login.Configuration;
-import javax.security.auth.login.CredentialExpiredException;
-import javax.security.auth.login.FailedLoginException;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-
-import jakarta.servlet.http.HttpServletRequest;
-
-import org.apache.catalina.Container;
-import org.apache.catalina.LifecycleException;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.ExceptionUtils;
-import org.apache.tomcat.util.file.ConfigFileLoader;
-import org.apache.tomcat.util.file.ConfigurationSource;
-
-/**
- * <p>
- * Implementation of <b>Realm</b> that authenticates users via the <em>Java
Authentication and Authorization
- * Service</em> (JAAS).
- * </p>
- * <p>
- * The value configured for the <code>appName</code> property is passed to the
- * <code>javax.security.auth.login.LoginContext</code> constructor, to specify
the <em>application name</em> used to
- * select the set of relevant <code>LoginModules</code> required.
- * </p>
- * <p>
- * The JAAS Specification describes the result of a successful login as a
<code>javax.security.auth.Subject</code>
- * instance, which can contain zero or more
<code>java.security.Principal</code> objects in the return value of the
- * <code>Subject.getPrincipals()</code> method. However, it provides no
guidance on how to distinguish Principals that
- * describe the individual user (and are thus appropriate to return as the
value of request.getUserPrincipal() in a web
- * application) from the Principal(s) that describe the authorized roles for
this user. To maintain as much independence
- * as possible from the underlying <code>LoginMethod</code> implementation
executed by JAAS, the following policy is
- * implemented by this Realm:
- * </p>
- * <ul>
- * <li>The JAAS <code>LoginModule</code> is assumed to return a
<code>Subject</code> with at least one
- * <code>Principal</code> instance representing the user himself or herself,
and zero or more separate
- * <code>Principals</code> representing the security roles authorized for this
user.</li>
- * <li>On the <code>Principal</code> representing the user, the Principal name
is an appropriate value to return via the
- * Servlet API method <code>HttpServletRequest.getRemoteUser()</code>.</li>
- * <li>On the <code>Principals</code> representing the security roles, the
name is the name of the authorized security
- * role.</li>
- * <li>This Realm will be configured with two lists of fully qualified Java
class names of classes that implement
- * <code>java.security.Principal</code> - one that identifies class(es)
representing a user, and one that identifies
- * class(es) representing a security role.</li>
- * <li>As this Realm iterates over the <code>Principals</code> returned by
<code>Subject.getPrincipals()</code>, it will
- * identify the first <code>Principal</code> that matches the "user classes"
list as the <code>Principal</code> for this
- * user.</li>
- * <li>As this Realm iterates over the <code>Principals</code> returned by
<code>Subject.getPrincipals()</code>, it will
- * accumulate the set of all <code>Principals</code> matching the "role
classes" list as identifying the security roles
- * for this user.</li>
- * <li>It is a configuration error for the JAAS login method to return a
validated <code>Subject</code> without a
- * <code>Principal</code> that matches the "user classes" list.</li>
- * <li>By default, the enclosing Container's name serves as the application
name used to obtain the JAAS LoginContext
- * ("Catalina" in a default installation). Tomcat must be able to find an
application with this name in the JAAS
- * configuration file. Here is a hypothetical JAAS configuration file entry
for a database-oriented login module that
- * uses a Tomcat-managed JNDI database resource: <blockquote>
- *
- * <pre>
- * Catalina {
- * org.foobar.auth.DatabaseLoginModule REQUIRED
- * JNDI_RESOURCE=jdbc/AuthDB
- * USER_TABLE=users
- * USER_ID_COLUMN=id
- * USER_NAME_COLUMN=name
- * USER_CREDENTIAL_COLUMN=password
- * ROLE_TABLE=roles
- * ROLE_NAME_COLUMN=name
- * PRINCIPAL_FACTORY=org.foobar.auth.impl.SimplePrincipalFactory;
- * };
- * </pre>
- *
- * </blockquote></li>
- * <li>To set the JAAS configuration file location, set the
<code>CATALINA_OPTS</code> environment variable similar to
- * the following:
- *
<blockquote><code>CATALINA_OPTS="-Djava.security.auth.login.config=$CATALINA_HOME/conf/jaas.config"</code></blockquote>
- * </li>
- * <li>As part of the login process, JAASRealm registers its own
<code>CallbackHandler</code>, called (unsurprisingly)
- * <code>JAASCallbackHandler</code>. This handler supplies the HTTP requests'
username and credentials to the
- * user-supplied <code>LoginModule</code></li>
- * <li>As with other <code>Realm</code> implementations, digested passwords
are supported via the configured
- * <code>CredentialHandler</code>. The credentials are passed to the
<code>LoginModule</code> unchanged; any digest
- * comparison is expected to be performed by the <code>LoginModule</code> when
it calls back into the
- * <code>Realm</code></li>
- * </ul>
- */
-public class JAASRealm extends RealmBase {
-
- private static final Log log = LogFactory.getLog(JAASRealm.class);
-
- /**
- * Constructs a new JAASRealm.
- */
- public JAASRealm() {
- }
-
- // ----------------------------------------------------- Instance Variables
-
-
- /**
- * The application name passed to the JAAS <code>LoginContext</code>,
which uses it to select the set of relevant
- * <code>LoginModule</code>s.
- */
- protected String appName = null;
-
-
- /**
- * The list of role class names, split out for easy processing.
- */
- protected final List<String> roleClasses = new ArrayList<>();
-
-
- /**
- * The set of user class names, split out for easy processing.
- */
- protected final List<String> userClasses = new ArrayList<>();
-
-
- /**
- * Whether to use context ClassLoader or default ClassLoader. True means
use context ClassLoader, and True is the
- * default value.
- */
- protected boolean useContextClassLoader = true;
-
-
- /**
- * Path to find a JAAS configuration file, if not set global JVM JAAS
configuration will be used.
- */
- protected String configFile;
-
- /**
- * The loaded JAAS Configuration instance.
- */
- protected volatile Configuration jaasConfiguration;
-
- /**
- * Flag indicating whether the JAAS configuration has been loaded.
- */
- protected volatile boolean jaasConfigurationLoaded = false;
-
- /**
- * Keeps track if JAAS invocation of login modules was successful or not.
By default, it is true unless we detect
- * JAAS login module can't perform the login. This will be used for
realm's {@link #isAvailable()} status so that
- * {@link LockOutRealm} will not lock the user out if JAAS login modules
are unavailable to perform the actual
- * login.
- */
- private volatile boolean invocationSuccess = true;
-
- // ------------------------------------------------------------- Properties
-
- /**
- * Get the path of the JAAS configuration file.
- *
- * @return the path of the JAAS configuration file
- */
- public String getConfigFile() {
- return configFile;
- }
-
- /**
- * Set the JAAS configuration file.
- *
- * @param configFile The JAAS configuration file
- */
- public void setConfigFile(String configFile) {
- this.configFile = configFile;
- }
-
- /**
- * Set the JAAS <code>LoginContext</code> app name.
- *
- * @param name The application name that will be used to retrieve the set
of relevant <code>LoginModule</code>s
- */
- public void setAppName(String name) {
- appName = name;
- }
-
- /**
- * Get the JAAS application name.
- *
- * @return the application name
- */
- public String getAppName() {
- return appName;
- }
-
- /**
- * Sets whether to use the context or default ClassLoader. True means use
context ClassLoader.
- *
- * @param useContext True means use context ClassLoader
- */
- public void setUseContextClassLoader(boolean useContext) {
- useContextClassLoader = useContext;
- }
-
- /**
- * Returns whether to use the context or default ClassLoader. True means
to use the context ClassLoader.
- *
- * @return The value of useContextClassLoader
- */
- public boolean isUseContextClassLoader() {
- return useContextClassLoader;
- }
-
- @Override
- public void setContainer(Container container) {
- super.setContainer(container);
-
- if (appName == null) {
- appName = makeLegalForJAAS(container.getName());
- log.info(sm.getString("jaasRealm.appName", appName));
- }
- }
-
- /**
- * Comma-delimited list of <code>java.security.Principal</code> classes
that represent security roles.
- */
- protected String roleClassNames = null;
-
- /**
- * Get the comma-delimited list of role class names.
- *
- * @return the role class names
- */
- public String getRoleClassNames() {
- return this.roleClassNames;
- }
-
- /**
- * Sets the list of comma-delimited classes that represent roles. The
classes in the list must implement
- * <code>java.security.Principal</code>. The supplied list of classes will
be parsed when {@link #start()} is
- * called.
- *
- * @param roleClassNames The class names list
- */
- public void setRoleClassNames(String roleClassNames) {
- this.roleClassNames = roleClassNames;
- }
-
- /**
- * Parses a comma-delimited list of class names, and store the class names
in the provided List. Each class must
- * implement <code>java.security.Principal</code>.
- *
- * @param classNamesString a comma-delimited list of fully qualified class
names.
- * @param classNamesList the list in which the class names will be
stored. The list is cleared before being
- * populated.
- */
- protected void parseClassNames(String classNamesString, List<String>
classNamesList) {
- classNamesList.clear();
- if (classNamesString == null) {
- return;
- }
-
- ClassLoader loader = this.getClass().getClassLoader();
- if (isUseContextClassLoader()) {
- loader = Thread.currentThread().getContextClassLoader();
- }
-
- String[] classNames = classNamesString.split("[ ]*,[ ]*");
- for (String className : classNames) {
- if (className.isEmpty()) {
- continue;
- }
- try {
- Class<?> principalClass = Class.forName(className, false,
loader);
- if (Principal.class.isAssignableFrom(principalClass)) {
- classNamesList.add(className);
- } else {
- log.error(sm.getString("jaasRealm.notPrincipal",
className));
- }
- } catch (ClassNotFoundException e) {
- log.error(sm.getString("jaasRealm.classNotFound", className));
- }
- }
- }
-
- /**
- * Comma-delimited list of <code>java.security.Principal</code> classes
that represent individual users.
- */
- protected String userClassNames = null;
-
- /**
- * Get the comma-delimited list of user class names.
- *
- * @return the user class names
- */
- public String getUserClassNames() {
- return this.userClassNames;
- }
-
- /**
- * Sets the list of comma-delimited classes that represent individual
users. The classes in the list must implement
- * <code>java.security.Principal</code>. The supplied list of classes will
be parsed when {@link #start()} is
- * called.
- *
- * @param userClassNames The class names list
- */
- public void setUserClassNames(String userClassNames) {
- this.userClassNames = userClassNames;
- }
-
-
- // --------------------------------------------------------- Public Methods
-
- @Override
- public Principal authenticate(String username, String credentials) {
- return authenticate(username, new JAASCallbackHandler(this, username,
credentials));
- }
-
-
- @Override
- public Principal authenticate(String username, String clientDigest, String
nonce, String nc, String cnonce,
- String qop, String realmName, String digestA2, String algorithm) {
- return authenticate(username, new JAASCallbackHandler(this, username,
clientDigest, nonce, nc, cnonce, qop,
- realmName, digestA2, algorithm,
HttpServletRequest.DIGEST_AUTH));
- }
-
-
- // ------------------------------------------------------ Protected Methods
-
- /**
- * Perform the actual JAAS authentication.
- *
- * @param username The username
- * @param callbackHandler The callback handler
- *
- * @return the associated principal, or <code>null</code> if there is none.
- */
- protected Principal authenticate(String username, CallbackHandler
callbackHandler) {
-
- // Establish a LoginContext to use for authentication
- try {
- if (log.isTraceEnabled()) {
- log.trace(sm.getString("jaasRealm.beginLogin", username,
appName));
- }
-
- // What if the LoginModule is in the container class loader ?
- ClassLoader ocl = null;
- Thread currentThread = null;
-
- if (!isUseContextClassLoader()) {
- currentThread = Thread.currentThread();
- ocl = currentThread.getContextClassLoader();
-
currentThread.setContextClassLoader(this.getClass().getClassLoader());
- }
-
- LoginContext loginContext;
- try {
- Configuration config = getConfig();
- loginContext = new LoginContext(appName, null,
callbackHandler, config);
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- log.error(sm.getString("jaasRealm.unexpectedError"), t);
- // There is configuration issue with JAAS so mark the realm as
- // unavailable
- invocationSuccess = false;
- return null;
- } finally {
- if (currentThread != null) {
- currentThread.setContextClassLoader(ocl);
- }
- }
-
- if (log.isTraceEnabled()) {
- log.trace("Login context created " + username);
- }
-
- // Negotiate a login via this LoginContext
- Subject subject;
- try {
- loginContext.login();
- subject = loginContext.getSubject();
- // We were able to perform login successfully so mark JAAS
realm as
- // available as it could have been set to false in prior
attempts.
- // Change invocationSuccess variable only when we know the
outcome
- // of the JAAS operation to keep variable consistent.
- invocationSuccess = true;
- if (subject == null) {
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("jaasRealm.nullSubject",
username));
- }
- silentLogout(loginContext);
- return null;
- }
- } catch (AccountExpiredException e) {
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("jaasRealm.accountExpired",
username), e);
- }
- // JAAS checked LoginExceptions are successful authentication
- // invocations so mark JAAS realm as available
- invocationSuccess = true;
- return null;
- } catch (CredentialExpiredException e) {
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("jaasRealm.credentialExpired",
username), e);
- }
- // JAAS checked LoginExceptions are successful authentication
- // invocations so mark JAAS realm as available
- invocationSuccess = true;
- return null;
- } catch (FailedLoginException e) {
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("jaasRealm.failedLogin", username),
e);
- }
- // JAAS checked LoginExceptions are successful authentication
- // invocations so mark JAAS realm as available
- invocationSuccess = true;
- return null;
- } catch (LoginException e) {
- log.warn(sm.getString("jaasRealm.loginException", username),
e);
- // JAAS checked LoginExceptions are successful authentication
- // invocations so mark JAAS realm as available
- invocationSuccess = true;
- return null;
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- log.error(sm.getString("jaasRealm.unexpectedError"), t);
- // JAAS throws exception different from LoginException so mark
the
- // realm as unavailable
- invocationSuccess = false;
- return null;
- }
-
- if (log.isTraceEnabled()) {
- log.trace(sm.getString("jaasRealm.loginContextCreated",
username));
- }
-
- // Return the appropriate Principal for this authenticated Subject
- Principal principal = createPrincipal(username, subject,
loginContext);
- if (principal == null) {
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("jaasRealm.authenticateFailure",
username));
- }
- silentLogout(loginContext);
- return null;
- }
- if (log.isTraceEnabled()) {
- log.trace(sm.getString("jaasRealm.authenticateSuccess",
username, principal));
- }
-
- return principal;
- } catch (Throwable t) {
- log.error(sm.getString("jaasRealm.unexpectedError"), t);
- // JAAS throws exception different from LoginException so mark the
realm as unavailable
- invocationSuccess = false;
- return null;
- }
- }
-
-
- private void silentLogout(LoginContext loginContext) {
- try {
- loginContext.logout();
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- // Ignore anything else. Caller should have created any necessary
log entries.
- }
- }
-
-
- /**
- * @return the password associated with the given principal's username.
This always returns null as the JAASRealm
- * has no way of obtaining this information.
- */
- @Override
- protected String getPassword(String username) {
- return null;
- }
-
-
- @Override
- protected Principal getPrincipal(String username) {
-
- return authenticate(username, new JAASCallbackHandler(this, username,
null, null, null, null, null, null, null,
- null, HttpServletRequest.CLIENT_CERT_AUTH));
-
- }
-
-
- /**
- * Identify and return a <code>java.security.Principal</code> instance
representing the authenticated user for the
- * specified <code>Subject</code>. The Principal is constructed by
scanning the list of Principals returned by the
- * JAASLoginModule. The first <code>Principal</code> object that matches
one of the class names supplied as a "user
- * class" is the user Principal. This object is returned to the caller.
Any remaining principal objects returned by
- * the LoginModules are mapped to roles, but only if their respective
classes match one of the "role class" classes.
- * If a user Principal cannot be constructed, return <code>null</code>.
- *
- * @param username The associated username
- * @param subject The <code>Subject</code> representing the logged-in
user
- * @param loginContext Associated with the Principal so {@link
LoginContext#logout()} can be called later
- *
- * @return the principal object
- */
- protected Principal createPrincipal(String username, Subject subject,
LoginContext loginContext) {
- // Prepare to scan the Principals for this Subject
-
- List<String> roles = new ArrayList<>();
- Principal userPrincipal = null;
-
- // Scan the Principals for this Subject
- for (Principal principal : subject.getPrincipals()) {
- String principalClass = principal.getClass().getName();
-
- if (log.isTraceEnabled()) {
- log.trace(sm.getString("jaasRealm.checkPrincipal", principal,
principalClass));
- }
-
- if (userPrincipal == null && userClasses.contains(principalClass))
{
- userPrincipal = principal;
- if (log.isTraceEnabled()) {
- log.trace(sm.getString("jaasRealm.userPrincipalSuccess",
principal.getName()));
- }
- }
-
- if (roleClasses.contains(principalClass)) {
- roles.add(principal.getName());
- if (log.isTraceEnabled()) {
- log.trace(sm.getString("jaasRealm.rolePrincipalAdd",
principal.getName()));
- }
- }
- }
-
- // Print failure message if needed
- if (userPrincipal == null) {
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("jaasRealm.userPrincipalFailure"));
- log.debug(sm.getString("jaasRealm.rolePrincipalFailure"));
- }
- return null;
- } else {
- if (roles.isEmpty()) {
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("jaasRealm.rolePrincipalFailure"));
- }
- }
- }
-
- // Return the resulting Principal for our authenticated user
- return new GenericPrincipal(username, roles, userPrincipal,
loginContext);
- }
-
- /**
- * Ensure the given name is legal for JAAS configuration. Added for
Bugzilla 30869, made protected for easy
- * customization in case my implementation is insufficient, which I think
is very likely.
- *
- * @param src The name to validate
- *
- * @return A string that's a valid JAAS realm name
- */
- protected String makeLegalForJAAS(final String src) {
- String result = src;
-
- // Default name is "other" per JAAS spec
- if (result == null) {
- result = "other";
- }
-
- // Strip leading slash if present, as Sun JAAS impl
- // barfs on it (see Bugzilla 30869 bug report).
- if (result.startsWith("/")) {
- result = result.substring(1);
- }
-
- return result;
- }
-
-
- // ------------------------------------------------------ Lifecycle Methods
-
- @Override
- protected void startInternal() throws LifecycleException {
-
- // These need to be called after loading configuration, in case
- // useContextClassLoader appears after them in xml config
- parseClassNames(userClassNames, userClasses);
- parseClassNames(roleClassNames, roleClasses);
-
- super.startInternal();
- }
-
-
- /**
- * Load custom JAAS Configuration.
- *
- * @return the loaded configuration
- */
- protected Configuration getConfig() {
- // Local copy to avoid possible NPE due to concurrent change
- String configFile = this.configFile;
- try {
- if (jaasConfigurationLoaded) {
- return jaasConfiguration;
- }
- synchronized (this) {
- if (configFile == null) {
- jaasConfigurationLoaded = true;
- return null;
- }
- if (jaasConfigurationLoaded) {
- return jaasConfiguration;
- }
- @SuppressWarnings("unchecked")
- Class<Configuration> sunConfigFile =
- (Class<Configuration>)
Class.forName("com.sun.security.auth.login.ConfigFile");
- Constructor<Configuration> constructor =
sunConfigFile.getConstructor(URI.class);
- URL resource =
Thread.currentThread().getContextClassLoader().getResource(configFile);
- Configuration config;
- if (resource == null) {
- try (ConfigurationSource.Resource configFileResource =
-
ConfigFileLoader.getSource().getResource(configFile)) {
- config =
constructor.newInstance(configFileResource.getURI());
- } catch (IOException ioe) {
- throw new
RuntimeException(sm.getString("jaasRealm.configFileNotFound", configFile));
- }
- } else {
- URI uri = resource.toURI();
- config = constructor.newInstance(uri);
- }
- this.jaasConfiguration = config;
- this.jaasConfigurationLoaded = true;
- return this.jaasConfiguration;
- }
- } catch (InvocationTargetException ex) {
- throw new
RuntimeException(ExceptionUtils.unwrapInvocationTargetException(ex));
- } catch (SecurityException | URISyntaxException |
ReflectiveOperationException | IllegalArgumentException ex) {
- throw new RuntimeException(ex);
- }
- }
-
- @Override
- public boolean isAvailable() {
- return invocationSuccess;
- }
-}
diff --git a/test/org/apache/catalina/realm/TestJAASRealm.java
b/test/org/apache/catalina/realm/TestJAASRealm.java
deleted file mode 100644
index 2ce87e8289..0000000000
--- a/test/org/apache/catalina/realm/TestJAASRealm.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * 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.catalina.realm;
-
-import java.io.File;
-import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
-import java.security.MessageDigest;
-import java.security.Principal;
-import java.util.Locale;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-import org.apache.catalina.Context;
-import org.apache.catalina.startup.Tomcat;
-import org.apache.catalina.startup.TomcatBaseTest;
-import org.apache.tomcat.util.buf.HexUtils;
-
-public class TestJAASRealm extends TomcatBaseTest {
-
- private static final String ALGORITHM = "MD5";
-
- private static final String CONFIG =
- "CustomLogin {\n" +
- " org.apache.catalina.realm.TesterLoginModule\n" +
- " sufficient;\n" +
- "};";
-
- private static final String CONFIG_MEMORY =
- "MemoryLogin {\n" +
- " org.apache.catalina.realm.JAASMemoryLoginModule\n" +
- " sufficient pathname=\"tomcat-users-lm.xml\";\n" +
- "};";
-
- @Test
- public void testRealm() throws Exception {
-
- Tomcat tomcat = getTomcatInstance();
-
- // Write login config to the temp path
- File loginConfFile = new File(getTemporaryDirectory(),
"customLoginConfig.conf");
- try (PrintWriter writer = new PrintWriter(loginConfFile)) {
- writer.write(CONFIG);
- }
- addDeleteOnTearDown(loginConfFile);
-
- JAASRealm jaasRealm = new JAASRealm();
- jaasRealm.setAppName("CustomLogin");
- jaasRealm.setCredentialHandler(new MessageDigestCredentialHandler());
- jaasRealm.setUserClassNames(TesterPrincipal.class.getName());
- jaasRealm.setRoleClassNames(TesterRolePrincipal.class.getName());
- jaasRealm.setConfigFile(loginConfFile.getAbsolutePath());
- Context context = tomcat.addContext("/jaastest", null);
- context.setRealm(jaasRealm);
-
- tomcat.start();
-
- Principal p = jaasRealm.authenticate("foo", "bar");
- Assert.assertNull(p);
- p = jaasRealm.authenticate("tomcatuser", "pass");
- Assert.assertNotNull(p);
- Assert.assertTrue(p instanceof GenericPrincipal);
- GenericPrincipal gp = (GenericPrincipal) p;
- Assert.assertTrue(gp.hasRole("role1"));
- }
-
- @Test
- public void testMemoryLoginModule() throws Exception {
- Tomcat tomcat = getTomcatInstance();
-
- File tomcatUsersXml = new File(getTemporaryDirectory(),
"tomcat-users-lm.xml");
- try (PrintWriter writer = new PrintWriter(tomcatUsersXml)) {
- writer.write(TestMemoryRealm.CONFIG);
- }
- addDeleteOnTearDown(tomcatUsersXml);
-
- // Write login config to the temp path
- File loginConfFile = new File(getTemporaryDirectory(),
"memoryLoginConfig.conf");
- try (PrintWriter writer = new PrintWriter(loginConfFile)) {
- String path = tomcatUsersXml.getAbsolutePath();
- if (File.separatorChar == '\\') {
- path = path.replace("\\", "\\\\");
- }
- writer.write(CONFIG_MEMORY.replace("tomcat-users-lm.xml", path));
- }
- addDeleteOnTearDown(loginConfFile);
-
- JAASRealm jaasRealm = new JAASRealm();
- jaasRealm.setAppName("MemoryLogin");
- jaasRealm.setCredentialHandler(new MessageDigestCredentialHandler());
- jaasRealm.setUserClassNames(GenericPrincipal.class.getName());
- jaasRealm.setRoleClassNames(GenericPrincipal.class.getName());
- jaasRealm.setConfigFile(loginConfFile.getAbsolutePath());
- Context context = tomcat.addContext("/jaastest", null);
- context.setRealm(jaasRealm);
-
- tomcat.start();
-
- Principal p = jaasRealm.authenticate("foo", "bar");
- Assert.assertNull(p);
- p = jaasRealm.authenticate("admin", "sekr3t");
- Assert.assertNotNull(p);
- Assert.assertTrue(p instanceof GenericPrincipal);
- GenericPrincipal gp = (GenericPrincipal) p;
- Assert.assertTrue(gp.hasRole("testrole"));
- gp.logout();
- }
-
- @Test
- public void testMemoryLoginModuleDigestAuth() throws Exception {
- Tomcat tomcat = getTomcatInstance();
-
- File tomcatUsersXml = new File(getTemporaryDirectory(),
"tomcat-users-lm-digest.xml");
- try (PrintWriter writer = new PrintWriter(tomcatUsersXml)) {
- writer.write(TestMemoryRealm.CONFIG);
- }
- addDeleteOnTearDown(tomcatUsersXml);
-
- // Write login config to the temp path
- File loginConfFile = new File(getTemporaryDirectory(),
"memoryLoginConfigDigest.conf");
- try (PrintWriter writer = new PrintWriter(loginConfFile)) {
- String path = tomcatUsersXml.getAbsolutePath();
- if (File.separatorChar == '\\') {
- path = path.replace("\\", "\\\\");
- }
- writer.write(CONFIG_MEMORY.replace("tomcat-users-lm.xml", path));
- }
- addDeleteOnTearDown(loginConfFile);
-
- JAASRealm jaasRealm = new JAASRealm();
- jaasRealm.setAppName("MemoryLogin");
- // Use the same algorithm for the realm credential handler as used for
- // the digest authentication
- MessageDigestCredentialHandler credentialHandler = new
MessageDigestCredentialHandler();
- credentialHandler.setAlgorithm(ALGORITHM);
- jaasRealm.setCredentialHandler(credentialHandler);
- jaasRealm.setUserClassNames(GenericPrincipal.class.getName());
- jaasRealm.setRoleClassNames(GenericPrincipal.class.getName());
- jaasRealm.setConfigFile(loginConfFile.getAbsolutePath());
- Context context = tomcat.addContext("/jaastest", null);
- context.setRealm(jaasRealm);
-
- tomcat.start();
-
- String nonce = "test-nonce";
- String nc = "00000001";
- String cnonce = "test-cnonce";
- String qop = "auth";
- String realmName = "test-realm";
-
- MessageDigest md5 = MessageDigest.getInstance(ALGORITHM);
- String digestA2 =
HexUtils.toHexString(md5.digest("GET:/jaastest".getBytes(StandardCharsets.UTF_8)));
- // digestA1 as the client computes it from the stored (clear text in
- // the login module's user file) password
- String digestA1 = HexUtils.toHexString(
- md5.digest(("admin:" + realmName +
":sekr3t").getBytes(StandardCharsets.UTF_8)));
- String clientDigest = HexUtils.toHexString(md5.digest(
- (digestA1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop
+ ":" + digestA2)
- .getBytes(StandardCharsets.UTF_8)));
-
- Principal p = jaasRealm.authenticate("admin",
clientDigest.toLowerCase(Locale.ENGLISH), nonce, nc, cnonce,
- qop, realmName, digestA2, ALGORITHM);
- Assert.assertNotNull(p);
- Assert.assertTrue(p instanceof GenericPrincipal);
- GenericPrincipal gp = (GenericPrincipal) p;
- Assert.assertTrue(gp.hasRole("testrole"));
- gp.logout();
- }
-}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index d5f5d3a36f..95f484dada 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -399,6 +399,9 @@
Connector compression will now enable gzip compression with a request
header of the form <code>Accept-Encoding: *</code>. (markt)
</fix>
+ <fix>
+ Remove JAASRealm. (schultz)
+ </fix>
</changelog>
</subsection>
<subsection name="Coyote">
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]