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 2019/06/24 18:06:06 UTC
[tomcat] 02/02: Fix
https://bz.apache.org/bugzilla/show_bug.cgi?id=58590
This is an automated email from the ASF dual-hosted git repository.
markt pushed a commit to branch 8.5.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit f2dfcb07f3aa00676f97e9a9da9ed4f1349e2ab2
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Mon Jun 24 18:01:31 2019 +0100
Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=58590
Add the ability for a MemoryUserDatabase to monitor the backing XML file
for changes and reload the source file if a change in the last modified
time is detected. This is enabled by default meaning that changes to
$CATALINA_BASE/conf/tomcat-users.xml will now take effect a short time
after the file is saved.
---
.../apache/catalina/realm/UserDatabaseRealm.java | 9 ++
.../apache/catalina/users/LocalStrings.properties | 5 +
.../catalina/users/LocalStrings_es.properties | 2 +
.../catalina/users/LocalStrings_fr.properties | 14 +-
.../catalina/users/LocalStrings_ja.properties | 12 ++
.../apache/catalina/users/MemoryUserDatabase.java | 156 +++++++++++++++------
.../catalina/users/MemoryUserDatabaseFactory.java | 5 +
webapps/docs/changelog.xml | 7 +
webapps/docs/jndi-resources-howto.xml | 8 +-
9 files changed, 176 insertions(+), 42 deletions(-)
diff --git a/java/org/apache/catalina/realm/UserDatabaseRealm.java b/java/org/apache/catalina/realm/UserDatabaseRealm.java
index 064ac59..38f8822 100644
--- a/java/org/apache/catalina/realm/UserDatabaseRealm.java
+++ b/java/org/apache/catalina/realm/UserDatabaseRealm.java
@@ -29,6 +29,7 @@ import org.apache.catalina.Role;
import org.apache.catalina.User;
import org.apache.catalina.UserDatabase;
import org.apache.catalina.Wrapper;
+import org.apache.catalina.users.MemoryUserDatabase;
import org.apache.tomcat.util.ExceptionUtils;
/**
@@ -151,6 +152,14 @@ public class UserDatabaseRealm extends RealmBase {
}
+ @Override
+ public void backgroundProcess() {
+ if (database instanceof MemoryUserDatabase) {
+ ((MemoryUserDatabase) database).backgroundProcess();
+ }
+ }
+
+
/**
* Return the password associated with the given principal's user name.
*/
diff --git a/java/org/apache/catalina/users/LocalStrings.properties b/java/org/apache/catalina/users/LocalStrings.properties
index 7b76a1d..84d2754 100644
--- a/java/org/apache/catalina/users/LocalStrings.properties
+++ b/java/org/apache/catalina/users/LocalStrings.properties
@@ -13,13 +13,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+memoryUserDatabase.fileClose=Failed to close [{0}]
+memoryUserDatabase.fileDelete=Failed to delete [{0}]
memoryUserDatabase.fileNotFound=The specified user database [{0}] could not be found
memoryUserDatabase.notPersistable=User database is not persistable - no write permissions on directory
memoryUserDatabase.nullGroup=Null or zero length group name specified. The group will be ignored.
memoryUserDatabase.nullRole=Null or zero length role name specified. The role will be ignored.
memoryUserDatabase.nullUser=Null or zero length user name specified. The user will be ignored.
memoryUserDatabase.readOnly=User database has been configured to be read only. Changes cannot be saved
+memoryUserDatabase.reload=Reloading memory user database [{0}] from updated source [{1}]
+memoryUserDatabase.reloadError=Error reloading memory user database [{0}] from updated source [{1}]
memoryUserDatabase.renameNew=Cannot rename new file to [{0}]
memoryUserDatabase.renameOld=Cannot rename original file to [{0}]
+memoryUserDatabase.restoreOrig=Cannot restore [{0}] to original file
memoryUserDatabase.writeException=IOException writing to [{0}]
memoryUserDatabase.xmlFeatureEncoding=Exception configuring digester to permit java encoding names in XML files. Only IANA encoding names will be supported.
diff --git a/java/org/apache/catalina/users/LocalStrings_es.properties b/java/org/apache/catalina/users/LocalStrings_es.properties
index 40d5426..e9ab723 100644
--- a/java/org/apache/catalina/users/LocalStrings_es.properties
+++ b/java/org/apache/catalina/users/LocalStrings_es.properties
@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+memoryUserDatabase.fileNotFound=El usuario de base de datos especificado [{0}] no pudo ser encontrado
memoryUserDatabase.notPersistable=La base de datos de usuario no es persistible - no hay permisos de grabación sobre el directorio
memoryUserDatabase.nullGroup=Se ha especificado un nombre de grupo nulo o de tamaño cero. Se ignora el grupo.
memoryUserDatabase.nullRole=Se ha especificado un nombre rol nulo o de tamaño cero. Se ignora el rol.
@@ -20,5 +21,6 @@ memoryUserDatabase.nullUser=Se ha especificado un nombre de usuario nulo o de ta
memoryUserDatabase.readOnly=User database has been configured to be read only. Changes cannot be saved
memoryUserDatabase.renameNew=Imposible de renombrar el archivo nuevo a [{0}]
memoryUserDatabase.renameOld=Imposible de renombrar el archivo original a [{0}]
+memoryUserDatabase.restoreOrig=No se puede restablecer [{0}] al archivo original
memoryUserDatabase.writeException=IOException durante la escritura hacia [{0}]
memoryUserDatabase.xmlFeatureEncoding=Excepción al configurar el resumidor para permitir nombres codificados en java en los ficheros XML. Sólo se soportarán los nombres con codificación IANA.
diff --git a/java/org/apache/catalina/users/LocalStrings_fr.properties b/java/org/apache/catalina/users/LocalStrings_fr.properties
index a2f8b7d..e31177a 100644
--- a/java/org/apache/catalina/users/LocalStrings_fr.properties
+++ b/java/org/apache/catalina/users/LocalStrings_fr.properties
@@ -13,6 +13,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+memoryUserDatabase.fileClose=Echec de fermeture [{0}]
+memoryUserDatabase.fileDelete=Impossible d''effacer [{0}]
+memoryUserDatabase.fileNotFound=La base d''utilisateurs spécifiée [{0}] n''a pas été trouvée
+memoryUserDatabase.notPersistable=La base de donnée des utilisateurs ne peut pas être persistée, il n'y a pas de permissions d'écriture sur le répertoire
+memoryUserDatabase.nullGroup=Un nom de groupe nul ou vide a été spécifié, le groupe sera ignoré
+memoryUserDatabase.nullRole=Le nom du rôle spécifié est nul ou a une taille de zéro. Le rôle sera ignoré.
+memoryUserDatabase.nullUser=Le nom d'utilisateur est null ou a une longueur de zéro, il sera ignoré
+memoryUserDatabase.readOnly=La base de donnée utilisateurs a été configurée en mode lecture seule, les modifications ne peuvent être sauvegardées
+memoryUserDatabase.reload=Rechargement de la base de données des utilisateurs [{0}] à partir de la source mise à jour [{1}]
+memoryUserDatabase.reloadError=Erreur de rechargement de la base de donnée utilisateurs [{0}] à partir de la source mise à jour [{1}]
memoryUserDatabase.renameNew=Impossible de renommer le nouveau fichier en [{0}]
-memoryUserDatabase.renameOld=Impossible de renommer le fichier original en [{0}]
+memoryUserDatabase.renameOld=Impossible de renommer le fichier d''origine en [{0}]
+memoryUserDatabase.restoreOrig=Impossible de restaurer [{0}] vers le fichier d''origine
memoryUserDatabase.writeException=IOException lors de l''écriture vers [{0}]
+memoryUserDatabase.xmlFeatureEncoding=Exception lors de la configuration du Digester pour permettre des noms d'encodage Java dans les fichiers XML, seuls le noms IANA seront supportés
diff --git a/java/org/apache/catalina/users/LocalStrings_ja.properties b/java/org/apache/catalina/users/LocalStrings_ja.properties
index 279f966..6179f1f 100644
--- a/java/org/apache/catalina/users/LocalStrings_ja.properties
+++ b/java/org/apache/catalina/users/LocalStrings_ja.properties
@@ -13,6 +13,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+memoryUserDatabase.fileClose=[{0}]のクローズに失敗しました
+memoryUserDatabase.fileDelete=[{0}]を削除できませんでした
+memoryUserDatabase.fileNotFound=ユーザー情報データベースとして指定された [{0}] は存在しません。
+memoryUserDatabase.notPersistable=ユーザーデータベースは永続的ではありません - ディレクトリに対する書き込み権限がありません。
+memoryUserDatabase.nullGroup=Nullまたは長さゼロのグループ名が指定されています。 グループは無視されます。
+memoryUserDatabase.nullRole=NULLまたは長さゼロのロール名が指定されています。 ロールは無視されます。
+memoryUserDatabase.nullUser=Nullまたは長さゼロのユーザー名が指定されています。 ユーザーは無視されます。
+memoryUserDatabase.readOnly=ユーザー情報データベースは読み取り専用になっています。変更を保存できません。
+memoryUserDatabase.reload=更新されたソース[{1}]からメモリユーザーデータベース[{0}]を再ロードしています
+memoryUserDatabase.reloadError=更新されたソース[{1}]からメモリユーザーデータベース[{0}]を再ロード中にエラーが発生しました。
memoryUserDatabase.renameNew=新しいファイル名を [{0}] に変更できません
memoryUserDatabase.renameOld=元のファイル名を [{0}] に変更できません
+memoryUserDatabase.restoreOrig=[{0}]を元のファイルに復元できません
memoryUserDatabase.writeException=[{0}] に書き込み中のIOExceptionです
+memoryUserDatabase.xmlFeatureEncoding=XMLファイルのJavaエンコーディング名を許可するためにdigesterを設定する際の例外。 IANAのエンコーディング名のみがサポートされます。
diff --git a/java/org/apache/catalina/users/MemoryUserDatabase.java b/java/org/apache/catalina/users/MemoryUserDatabase.java
index dccfff0..45e846c 100644
--- a/java/org/apache/catalina/users/MemoryUserDatabase.java
+++ b/java/org/apache/catalina/users/MemoryUserDatabase.java
@@ -22,6 +22,10 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
@@ -148,6 +152,9 @@ public class MemoryUserDatabase implements UserDatabase {
private final Lock readLock = dbLock.readLock();
private final Lock writeLock = dbLock.writeLock();
+ private volatile long lastModified = 0;
+ private boolean watchSource = true;
+
// ------------------------------------------------------------- Properties
@@ -212,6 +219,17 @@ public class MemoryUserDatabase implements UserDatabase {
}
+ public boolean getWatchSource() {
+ return watchSource;
+ }
+
+
+
+ public void setWatchSource(boolean watchSource) {
+ this.watchSource = watchSource;
+ }
+
+
/**
* @return the set of {@link Role}s defined in this user database.
*/
@@ -405,7 +423,16 @@ public class MemoryUserDatabase implements UserDatabase {
roles.clear();
String pathName = getPathname();
- try (InputStream is = ConfigFileLoader.getInputStream(getPathname())) {
+ URI uri = ConfigFileLoader.getURI(pathName);
+ URLConnection uConn = null;
+
+ try {
+ URL url = uri.toURL();
+ uConn = url.openConnection();
+
+ InputStream is = uConn.getInputStream();
+ this.lastModified = uConn.getLastModified();
+
// Construct a digester to read the XML input file
Digester digester = new Digester();
try {
@@ -431,6 +458,15 @@ public class MemoryUserDatabase implements UserDatabase {
groups.clear();
roles.clear();
throw e;
+ } finally {
+ if (uConn != null) {
+ try {
+ // Can't close a uConn directly. Have to do it like this.
+ uConn.getInputStream().close();
+ } catch (IOException ioe) {
+ log.warn(sm.getString("memoryUserDatabase.fileClose", pathname), ioe);
+ }
+ }
}
} finally {
writeLock.unlock();
@@ -542,25 +578,22 @@ public class MemoryUserDatabase implements UserDatabase {
if (!fileNew.isAbsolute()) {
fileNew = new File(System.getProperty(Globals.CATALINA_BASE_PROP), pathnameNew);
}
- PrintWriter writer = null;
+
+ writeLock.lock();
try {
+ try (FileOutputStream fos = new FileOutputStream(fileNew);
+ OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
+ PrintWriter writer = new PrintWriter(osw)) {
+
+ // Print the file prolog
+ writer.println("<?xml version='1.0' encoding='utf-8'?>");
+ writer.println("<tomcat-users xmlns=\"http://tomcat.apache.org/xml\"");
+ writer.print(" ");
+ writer.println("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
+ writer.print(" ");
+ writer.println("xsi:schemaLocation=\"http://tomcat.apache.org/xml tomcat-users.xsd\"");
+ writer.println(" version=\"1.0\">");
- // Configure our PrintWriter
- FileOutputStream fos = new FileOutputStream(fileNew);
- OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF8");
- writer = new PrintWriter(osw);
-
- // Print the file prolog
- writer.println("<?xml version='1.0' encoding='utf-8'?>");
- writer.println("<tomcat-users xmlns=\"http://tomcat.apache.org/xml\"");
- writer.print(" ");
- writer.println("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
- writer.print(" ");
- writer.println("xsi:schemaLocation=\"http://tomcat.apache.org/xml tomcat-users.xsd\"");
- writer.println(" version=\"1.0\">");
-
- writeLock.lock();
- try {
// Print entries for each defined role, group, and user
Iterator<?> values = null;
values = getRoles();
@@ -578,27 +611,24 @@ public class MemoryUserDatabase implements UserDatabase {
writer.print(" ");
writer.println(((MemoryUser) values.next()).toXml());
}
- } finally {
- writeLock.unlock();
- }
- // Print the file epilog
- writer.println("</tomcat-users>");
+ // Print the file epilog
+ writer.println("</tomcat-users>");
- // Check for errors that occurred while printing
- if (writer.checkError()) {
- writer.close();
- fileNew.delete();
- throw new IOException(sm.getString("memoryUserDatabase.writeException",
- fileNew.getAbsolutePath()));
- }
- writer.close();
- } catch (IOException e) {
- if (writer != null) {
- writer.close();
+ // Check for errors that occurred while printing
+ if (writer.checkError()) {
+ throw new IOException(sm.getString("memoryUserDatabase.writeException",
+ fileNew.getAbsolutePath()));
+ }
+ } catch (IOException e) {
+ if (fileNew.exists() && !fileNew.delete()) {
+ log.warn(sm.getString("memoryUserDatabase.fileDelete", fileNew));
+ }
+ throw e;
}
- fileNew.delete();
- throw e;
+ this.lastModified = fileNew.lastModified();
+ } finally {
+ writeLock.unlock();
}
// Perform the required renames to permanently save this file
@@ -606,13 +636,14 @@ public class MemoryUserDatabase implements UserDatabase {
if (!fileOld.isAbsolute()) {
fileOld = new File(System.getProperty(Globals.CATALINA_BASE_PROP), pathnameOld);
}
- fileOld.delete();
+ if (fileOld.exists() && !fileOld.delete()) {
+ throw new IOException(sm.getString("memoryUserDatabase.fileDelete", fileOld));
+ }
File fileOrig = new File(pathname);
if (!fileOrig.isAbsolute()) {
fileOrig = new File(System.getProperty(Globals.CATALINA_BASE_PROP), pathname);
}
if (fileOrig.exists()) {
- fileOld.delete();
if (!fileOrig.renameTo(fileOld)) {
throw new IOException(sm.getString("memoryUserDatabase.renameOld",
fileOld.getAbsolutePath()));
@@ -620,13 +651,58 @@ public class MemoryUserDatabase implements UserDatabase {
}
if (!fileNew.renameTo(fileOrig)) {
if (fileOld.exists()) {
- fileOld.renameTo(fileOrig);
+ if (!fileOld.renameTo(fileOrig)) {
+ log.warn(sm.getString("memoryUserDatabase.restoreOrig", fileOld));
+ }
}
throw new IOException(sm.getString("memoryUserDatabase.renameNew",
fileOrig.getAbsolutePath()));
}
- fileOld.delete();
+ if (fileOld.exists() && !fileOld.delete()) {
+ throw new IOException(sm.getString("memoryUserDatabase.fileDelete", fileOld));
+ }
+ }
+
+
+ public void backgroundProcess() {
+ if (!watchSource) {
+ return;
+ }
+ URI uri = ConfigFileLoader.getURI(getPathname());
+ URLConnection uConn = null;
+ try {
+ URL url = uri.toURL();
+ uConn = url.openConnection();
+
+ if (this.lastModified != uConn.getLastModified()) {
+ writeLock.lock();
+ try {
+ long detectedLastModified = uConn.getLastModified();
+ // Last modified as a resolution of 1s. Ensure that a write
+ // to the file is not in progress by ensuring that the last
+ // modified time is at least 2 seconds ago.
+ if (this.lastModified != detectedLastModified &&
+ detectedLastModified + 2000 < System.currentTimeMillis()) {
+ log.info(sm.getString("memoryUserDatabase.reload", id, uri));
+ open();
+ }
+ } finally {
+ writeLock.unlock();
+ }
+ }
+ } catch (Exception ioe) {
+ log.error(sm.getString("memoryUserDatabase.reloadError", id, uri), ioe);
+ } finally {
+ if (uConn != null) {
+ try {
+ // Can't close a uConn directly. Have to do it like this.
+ uConn.getInputStream().close();
+ } catch (IOException ioe) {
+ log.warn(sm.getString("memoryUserDatabase.fileClose", pathname), ioe);
+ }
+ }
+ }
}
diff --git a/java/org/apache/catalina/users/MemoryUserDatabaseFactory.java b/java/org/apache/catalina/users/MemoryUserDatabaseFactory.java
index ac75a54..6d01ac1 100644
--- a/java/org/apache/catalina/users/MemoryUserDatabaseFactory.java
+++ b/java/org/apache/catalina/users/MemoryUserDatabaseFactory.java
@@ -98,6 +98,11 @@ public class MemoryUserDatabaseFactory implements ObjectFactory {
database.setReadonly(Boolean.parseBoolean(ra.getContent().toString()));
}
+ ra = ref.get("watchSource");
+ if (ra != null) {
+ database.setWatchSource(Boolean.parseBoolean(ra.getContent().toString()));
+ }
+
// Return the configured database instance
database.open();
// Don't try something we know won't work
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 32c49c4..2385a68 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -73,6 +73,13 @@
<fix>Fix typo in UTF-32LE charset name. Patch by zhanhb vi Github.
(fschumacher)
</fix>
+ <add>
+ <bug>58590</bug>: Add the ability for a UserDatabase to monitor the
+ backing XML file for changes and reload the source file if a change in
+ the last modified time is detected. This is enabled by default meaning
+ that changes to <code>$CATALINA_BASE/conf/tomcat-users.xml</code> will
+ now take effect a short time after the file is saved. (markt)
+ </add>
</changelog>
</subsection>
<subsection name="Jasper">
diff --git a/webapps/docs/jndi-resources-howto.xml b/webapps/docs/jndi-resources-howto.xml
index d1bde74..4898619 100644
--- a/webapps/docs/jndi-resources-howto.xml
+++ b/webapps/docs/jndi-resources-howto.xml
@@ -25,7 +25,7 @@
<properties>
<author email="craigmcc@apache.org">Craig R. McClanahan</author>
<author email="yoavs@apache.org">Yoav Shapira</author>
- <title>JNDI Resources HOW-TO</title>
+ <title>JNDI Resources How-To</title>
</properties>
<body>
@@ -482,6 +482,12 @@ public class MyBean2 {
is running as. Ensure that these are appropriate to maintain the security
of your installation.</p>
+ <p>If referenced in a Realm, the MemoryUserDatabse will, by default, monitor
+ <code>pathname</code> for changes and reload the file if a change in the
+ last modified time is observed. This can be disabled by setting the
+ <code>watchSource</code> attribute to <code>false</code>.
+ </p>
+
<h5>3. Configure the Realm</h5>
<p>Configure a UserDatabase Realm to use this resource as described in the
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org