diff --git a/h2/src/docsrc/html/tutorial.html b/h2/src/docsrc/html/tutorial.html index 9e718ecbd8..ffab47908a 100644 --- a/h2/src/docsrc/html/tutorial.html +++ b/h2/src/docsrc/html/tutorial.html @@ -443,7 +443,9 @@

Settings of the H2 Console

In addition to those settings, the properties of the last recently used connection diff --git a/h2/src/main/org/h2/api/ErrorCode.java b/h2/src/main/org/h2/api/ErrorCode.java index d9840983f5..680e6c4692 100644 --- a/h2/src/main/org/h2/api/ErrorCode.java +++ b/h2/src/main/org/h2/api/ErrorCode.java @@ -1890,9 +1890,8 @@ public class ErrorCode { /** * The error with code 90125 is thrown when * PreparedStatement.setBigDecimal is called with object that extends the - * class BigDecimal, and the system property h2.allowBigDecimalExtensions is - * not set. Using extensions of BigDecimal is dangerous because the database - * relies on the behavior of BigDecimal. Example of wrong usage: + * class BigDecimal. Using extensions of BigDecimal is dangerous because the + * database relies on the behavior of BigDecimal. Example of wrong usage: *

      * BigDecimal bd = new MyDecimal("$10.3");
      * prep.setBigDecimal(1, bd);
diff --git a/h2/src/main/org/h2/engine/Constants.java b/h2/src/main/org/h2/engine/Constants.java
index 502fa63705..a2012507b3 100644
--- a/h2/src/main/org/h2/engine/Constants.java
+++ b/h2/src/main/org/h2/engine/Constants.java
@@ -472,6 +472,11 @@ public class Constants {
      */
     public static final int QUERY_STATISTICS_MAX_ENTRIES = 100;
 
+    /**
+     * The minimum number of characters in web admin password.
+     */
+    public static final int MIN_WEB_ADMIN_PASSWORD_LENGTH = 12;
+
     /**
      * Announced version for PgServer.
      */
diff --git a/h2/src/main/org/h2/server/web/WebServer.java b/h2/src/main/org/h2/server/web/WebServer.java
index 161ac52ea8..a134634a46 100644
--- a/h2/src/main/org/h2/server/web/WebServer.java
+++ b/h2/src/main/org/h2/server/web/WebServer.java
@@ -162,7 +162,7 @@ public class WebServer implements Service {
     private String externalNames;
     private boolean isDaemon;
     private final Set running =
-            Collections.synchronizedSet(new HashSet());
+            Collections.synchronizedSet(new HashSet<>());
     private boolean ssl;
     private byte[] adminPassword;
     private final HashMap connInfoMap = new HashMap<>();
@@ -926,17 +926,32 @@ void setAdminPassword(String password) {
             adminPassword = null;
             return;
         }
-        if (password.length() == 128) {
-            try {
-                adminPassword = StringUtils.convertHexToBytes(password);
-                return;
-            } catch (Exception ex) {}
+        if (password.length() != 128) {
+            throw new IllegalArgumentException(
+                    "Use result of org.h2.server.web.WebServer.encodeAdminPassword(String)");
+        }
+        adminPassword = StringUtils.convertHexToBytes(password);
+    }
+
+    /**
+     * Generates a random salt and returns it with a hash of specified password
+     * with this salt.
+     *
+     * @param password
+     *            the password
+     * @return a salt and hash of salted password as a hex encoded string to be
+     *         used in configuration file
+     * @throws IllegalArgumentException when password is too short
+     */
+    public static String encodeAdminPassword(String password) {
+        if (password.length() < Constants.MIN_WEB_ADMIN_PASSWORD_LENGTH) {
+            throw new IllegalArgumentException("Min length: " + Constants.MIN_WEB_ADMIN_PASSWORD_LENGTH);
         }
         byte[] salt = MathUtils.secureRandomBytes(32);
         byte[] hash = SHA256.getHashWithSalt(password.getBytes(StandardCharsets.UTF_8), salt);
         byte[] total = Arrays.copyOf(salt, 64);
         System.arraycopy(hash, 0, total, 32, 32);
-        adminPassword = total;
+        return StringUtils.convertBytesToHex(total);
     }
 
     /**
diff --git a/h2/src/main/org/h2/tools/Server.java b/h2/src/main/org/h2/tools/Server.java
index d662e34ec4..be6076fa38 100644
--- a/h2/src/main/org/h2/tools/Server.java
+++ b/h2/src/main/org/h2/tools/Server.java
@@ -29,6 +29,7 @@ public class Server extends Tool implements Runnable, ShutdownHandler {
     private final Service service;
     private Server web, tcp, pg;
     private ShutdownHandler shutdownHandler;
+    private boolean fromCommandLine;
     private boolean started;
 
     public Server() {
@@ -75,7 +76,11 @@ public Server(Service service, String... args) throws SQLException {
      * [-webSSL]
      * Use encrypted (HTTPS) connections
      * [-webAdminPassword]
-     * Password of DB Console administrator
+     * Hash of password of DB Console administrator, can be generated with
+     * {@linkplain WebServer#encodeAdminPassword(String)}. Can be passed only to
+     * the {@link #runTool(String...)} method, this method rejects it. It is
+     * also possible to store this setting in configuration file of H2
+     * Console.
      * [-browser]
      * Start a browser connecting to the web server
      * [-tcp]
@@ -123,7 +128,9 @@ public Server(Service service, String... args) throws SQLException {
      * @throws SQLException on failure
      */
     public static void main(String... args) throws SQLException {
-        new Server().runTool(args);
+        Server server = new Server();
+        server.fromCommandLine = true;
+        server.runTool(args);
     }
 
     private void verifyArgs(String... args) throws SQLException {
@@ -146,6 +153,9 @@ private void verifyArgs(String... args) throws SQLException {
                 } else if ("-webPort".equals(arg)) {
                     i++;
                 } else if ("-webAdminPassword".equals(arg)) {
+                    if (fromCommandLine) {
+                        throwUnsupportedOption(arg);
+                    }
                     i++;
                 } else {
                     throwUnsupportedOption(arg);
@@ -249,6 +259,9 @@ public void runTool(String... args) throws SQLException {
                 } else if ("-webPort".equals(arg)) {
                     i++;
                 } else if ("-webAdminPassword".equals(arg)) {
+                    if (fromCommandLine) {
+                        throwUnsupportedOption(arg);
+                    }
                     i++;
                 } else {
                     showUsageAndThrowUnsupportedOption(arg);
diff --git a/h2/src/test/org/h2/test/server/TestWeb.java b/h2/src/test/org/h2/test/server/TestWeb.java
index 4df6465a6a..df365bedee 100644
--- a/h2/src/test/org/h2/test/server/TestWeb.java
+++ b/h2/src/test/org/h2/test/server/TestWeb.java
@@ -42,6 +42,9 @@
 import org.h2.api.ErrorCode;
 import org.h2.engine.Constants;
 import org.h2.engine.SysProperties;
+import org.h2.jdbc.JdbcSQLFeatureNotSupportedException;
+import org.h2.jdbc.JdbcSQLNonTransientException;
+import org.h2.server.web.WebServer;
 import org.h2.server.web.WebServlet;
 import org.h2.store.fs.FileUtils;
 import org.h2.test.TestBase;
@@ -159,10 +162,25 @@ private void testTools() throws Exception {
         conn.createStatement().execute(
                 "create table test(id int) as select 1");
         conn.close();
+        String hash = WebServer.encodeAdminPassword("1234567890AB");
+        try {
+            Server.main("-web", "-webPort", "8182",
+                    "-properties", "null", "-tcp", "-tcpPort", "9101", "-webAdminPassword", hash);
+            fail("Expected exception");
+        } catch (JdbcSQLFeatureNotSupportedException e) {
+            // Expected
+        }
         Server server = new Server();
         server.setOut(new PrintStream(new ByteArrayOutputStream()));
+        try {
+            server.runTool("-web", "-webPort", "8182",
+                    "-properties", "null", "-tcp", "-tcpPort", "9101", "-webAdminPassword", "123");
+            fail("Expected exception");
+        } catch (JdbcSQLNonTransientException e) {
+            // Expected
+        }
         server.runTool("-web", "-webPort", "8182",
-                "-properties", "null", "-tcp", "-tcpPort", "9101", "-webAdminPassword", "123");
+                "-properties", "null", "-tcp", "-tcpPort", "9101", "-webAdminPassword", hash);
         try {
             String url = "http://localhost:8182";
             WebClient client;
@@ -170,7 +188,7 @@ private void testTools() throws Exception {
             client = new WebClient();
             result = client.get(url);
             client.readSessionId(result);
-            result = client.get(url, "adminLogin.do?password=123");
+            result = client.get(url, "adminLogin.do?password=1234567890AB");
             result = client.get(url, "tools.jsp");
             FileUtils.delete(getBaseDir() + "/backup.zip");
             result = client.get(url, "tools.do?tool=Backup&args=-dir," +
diff --git a/h2/src/tools/org/h2/build/doc/dictionary.txt b/h2/src/tools/org/h2/build/doc/dictionary.txt
index fc1ddcf73b..aa865f0bb3 100644
--- a/h2/src/tools/org/h2/build/doc/dictionary.txt
+++ b/h2/src/tools/org/h2/build/doc/dictionary.txt
@@ -854,4 +854,4 @@ allotted mismatched wise terminator guarding revolves notion piece submission re
 duplicating unnested hardening sticky massacred
 bck clo cur hwm materializedview udca vol connectionpooldatasource xadatasource
 ampm sssssff sstzh tzs yyyysssss newsequentialid solidus openjdk furthermore ssff secons nashorn fractions
-btrim underscores ffl decomposed decomposition subfield infinities retryable
+btrim underscores ffl decomposed decomposition subfield infinities retryable salted