You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2020/10/07 19:32:32 UTC
[tomcat] branch 9.0.x updated: Fix BZ 55559. Add local JNDI support
to the UserDatabaseRealm
This is an automated email from the ASF dual-hosted git repository.
markt pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/9.0.x by this push:
new 9324206 Fix BZ 55559. Add local JNDI support to the UserDatabaseRealm
9324206 is described below
commit 932420649009920337aa8dfb0ebd35f555fa2458
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Wed Oct 7 20:21:56 2020 +0100
Fix BZ 55559. Add local JNDI support to the UserDatabaseRealm
---
.../apache/catalina/realm/UserDatabaseRealm.java | 116 ++++++++++++++++-----
.../apache/catalina/realm/mbeans-descriptors.xml | 4 +
webapps/docs/changelog.xml | 11 ++
webapps/docs/config/realm.xml | 7 ++
4 files changed, 114 insertions(+), 24 deletions(-)
diff --git a/java/org/apache/catalina/realm/UserDatabaseRealm.java b/java/org/apache/catalina/realm/UserDatabaseRealm.java
index 64957a9..bffe246 100644
--- a/java/org/apache/catalina/realm/UserDatabaseRealm.java
+++ b/java/org/apache/catalina/realm/UserDatabaseRealm.java
@@ -29,13 +29,14 @@ import org.apache.catalina.Role;
import org.apache.catalina.User;
import org.apache.catalina.UserDatabase;
import org.apache.catalina.Wrapper;
+import org.apache.naming.ContextBindings;
import org.apache.tomcat.util.ExceptionUtils;
/**
* Implementation of {@link org.apache.catalina.Realm} that is based on an
- * implementation of {@link UserDatabase} made available through the global JNDI
+ * implementation of {@link UserDatabase} made available through the JNDI
* resources configured for this instance of Catalina. Set the
- * <code>resourceName</code> parameter to the global JNDI resources name for the
+ * <code>resourceName</code> parameter to the JNDI resources name for the
* configured instance of <code>UserDatabase</code> that we should consult.
*
* @author Craig R. McClanahan
@@ -49,7 +50,8 @@ public class UserDatabaseRealm extends RealmBase {
* The <code>UserDatabase</code> we will use to authenticate users and
* identify associated roles.
*/
- protected UserDatabase database = null;
+ protected volatile UserDatabase database = null;
+ private final Object databaseLock = new Object();
/**
* The global JNDI name of the <code>UserDatabase</code> resource we will be
@@ -57,6 +59,11 @@ public class UserDatabaseRealm extends RealmBase {
*/
protected String resourceName = "UserDatabase";
+ /**
+ * Obtain the UserDatabase from the context (rather than global) JNDI.
+ */
+ private boolean localJndiResource = false;
+
// ------------------------------------------------------------- Properties
@@ -80,6 +87,31 @@ public class UserDatabaseRealm extends RealmBase {
}
+ /**
+ * Determines whether this Realm is configured to obtain the associated
+ * {@link UserDatabase} from the global JNDI context or a local (web
+ * application) JNDI context.
+ *
+ * @return {@code true} if a local JNDI context will be used, {@code false}
+ * if the the global JNDI context will be used
+ */
+ public boolean getLocalJndiResource() {
+ return localJndiResource;
+ }
+
+
+ /**
+ * Configure whether this Realm obtains the associated {@link UserDatabase}
+ * from the global JNDI context or a local (web application) JNDI context.
+ *
+ * @param localJndiResource {@code true} to use a local JNDI context,
+ * {@code false} to use the global JNDI context
+ */
+ public void setLocalJndiResource(boolean localJndiResource) {
+ this.localJndiResource = localJndiResource;
+ }
+
+
// --------------------------------------------------------- Public Methods
/**
@@ -94,6 +126,12 @@ public class UserDatabaseRealm extends RealmBase {
*/
@Override
public boolean hasRole(Wrapper wrapper, Principal principal, String role) {
+
+ UserDatabase database = getUserDatabase();
+ if (database == null) {
+ return false;
+ }
+
// Check for a role alias defined in a <security-role-ref> element
if (wrapper != null) {
String realRole = wrapper.findSecurityReference(role);
@@ -140,7 +178,10 @@ public class UserDatabaseRealm extends RealmBase {
@Override
public void backgroundProcess() {
- database.backgroundProcess();
+ UserDatabase database = getUserDatabase();
+ if (database != null) {
+ database.backgroundProcess();
+ }
}
@@ -149,6 +190,11 @@ public class UserDatabaseRealm extends RealmBase {
*/
@Override
protected String getPassword(String username) {
+ UserDatabase database = getUserDatabase();
+ if (database == null) {
+ return null;
+ }
+
User user = database.findUser(username);
if (user == null) {
@@ -164,6 +210,10 @@ public class UserDatabaseRealm extends RealmBase {
*/
@Override
protected Principal getPrincipal(String username) {
+ UserDatabase database = getUserDatabase();
+ if (database == null) {
+ return null;
+ }
User user = database.findUser(username);
if (user == null) {
@@ -189,30 +239,48 @@ public class UserDatabaseRealm extends RealmBase {
}
+ /*
+ * Can't do this in startInternal() with local JNDI as the local JNDI
+ * context won't be initialised at this point.
+ */
+ private UserDatabase getUserDatabase() {
+ // DCL so database MUST be volatile
+ if (database == null) {
+ synchronized (databaseLock) {
+ if (database == null) {
+ try {
+ Context context = null;
+ if (localJndiResource) {
+ context = ContextBindings.getClassLoader();
+ context = (Context) context.lookup("comp/env");
+ } else {
+ context = getServer().getGlobalNamingContext();
+ }
+ database = (UserDatabase) context.lookup(resourceName);
+ } catch (Throwable e) {
+ ExceptionUtils.handleThrowable(e);
+ containerLog.error(sm.getString("userDatabaseRealm.lookup", resourceName), e);
+ database = null;
+ }
+ }
+ }
+ }
+ return database;
+ }
+
+
// ------------------------------------------------------ Lifecycle Methods
- /**
- * Prepare for the beginning of active use of the public methods of this
- * component and implement the requirements of
- * {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
- *
- * @exception LifecycleException if this component detects a fatal error
- * that prevents this component from being used
- */
@Override
protected void startInternal() throws LifecycleException {
-
- try {
- Context context = getServer().getGlobalNamingContext();
- database = (UserDatabase) context.lookup(resourceName);
- } catch (Throwable e) {
- ExceptionUtils.handleThrowable(e);
- containerLog.error(sm.getString("userDatabaseRealm.lookup", resourceName), e);
- database = null;
- }
- if (database == null) {
- throw new LifecycleException(
- sm.getString("userDatabaseRealm.noDatabase", resourceName));
+ // If the JNDI resource is global, check it here and fail the context
+ // start if it is not valid. Local JNDI resources can't be validated
+ // this way because the JNDI context isn't available at Realm start.
+ if (!localJndiResource) {
+ UserDatabase database = getUserDatabase();
+ if (database == null) {
+ throw new LifecycleException(sm.getString("userDatabaseRealm.noDatabase", resourceName));
+ }
}
super.startInternal();
diff --git a/java/org/apache/catalina/realm/mbeans-descriptors.xml b/java/org/apache/catalina/realm/mbeans-descriptors.xml
index a75a66d..031afce 100644
--- a/java/org/apache/catalina/realm/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/realm/mbeans-descriptors.xml
@@ -400,6 +400,10 @@
description="The global JNDI name of the UserDatabase resource to use"
type="java.lang.String"/>
+ <attribute name="localJndiResource"
+ description="Configures if the UserDatabase JNDI definition is local to the webapp"
+ type="boolean"/>
+
<attribute name="realmPath"
description="The realm path"
type="java.lang.String"/>
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 8ad8194..4175940 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -45,6 +45,17 @@
issues do not "pop up" wrt. others).
-->
<section name="Tomcat 9.0.40 (markt)" rtext="in development">
+ <subsection name="Catalina">
+ <changelog>
+ <fix>
+ <bug>55559</bug>: Add a new attribute, <code>localJndiResource</code>,
+ that allows a UserDatabaseRealm to obtain a UserDatabase instance from
+ the local (web application) JNDI context rather than the global JNDI
+ context. This option is only useful when the Realm is defined on the
+ Context. (markt)
+ </fix>
+ </changelog>
+ </subsection>
</section>
<section name="Tomcat 9.0.39 (markt)" rtext="release in progress">
<subsection name="Catalina">
diff --git a/webapps/docs/config/realm.xml b/webapps/docs/config/realm.xml
index 08e4480..628b186 100644
--- a/webapps/docs/config/realm.xml
+++ b/webapps/docs/config/realm.xml
@@ -647,6 +647,13 @@
one of those roles.</p>
</attribute>
+ <attribute name="localJndiResource" required="false">
+ <p>When the realm is nested inside a Context element, this allows the
+ realm to use a UserDatabase defined for the Context rather than a global
+ UserDatabase. If not specified, the default is <code>false</code>: use a
+ global UserDatabase.</p>
+ </attribute>
+
<attribute name="resourceName" required="true">
<p>The name of the global <code>UserDatabase</code> resource
that this realm will use for user, password and role information.</p>
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org