diff --git a/broker/pom.xml b/broker/pom.xml
index c1e22d44..2838e6e9 100644
--- a/broker/pom.xml
+++ b/broker/pom.xml
@@ -137,6 +137,12 @@
9.2.0.M1
test
+
+ com.h2database
+ h2
+ 1.4.191
+ test
+
diff --git a/broker/src/main/java/io/moquette/spi/impl/security/DBAuthenticator.java b/broker/src/main/java/io/moquette/spi/impl/security/DBAuthenticator.java
new file mode 100644
index 00000000..36584720
--- /dev/null
+++ b/broker/src/main/java/io/moquette/spi/impl/security/DBAuthenticator.java
@@ -0,0 +1,85 @@
+package io.moquette.spi.impl.security;
+
+import io.moquette.spi.security.IAuthenticator;
+import org.apache.commons.codec.binary.Hex;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.sql.*;
+
+/**
+ * Load user credentials from a SQL database.
+ * sql driver must be provided at runtime
+ *
+ * @author mackristof
+ */
+
+public class DBAuthenticator implements IAuthenticator {
+
+ private static final Logger LOG = LoggerFactory.getLogger(DBAuthenticator.class);
+
+ private final String sqlQuery;
+ private final MessageDigest messageDigest;
+ private final PreparedStatement preparedStatement;
+
+
+ /** provide authenticator from SQL database
+ * @param driver : jdbc driver class like : "org.postgresql.Driver"
+ * @param jdbcUrl : jdbc url like : "jdbc:postgresql://host:port/dbname"
+ * @param sqlQuery : sql query like : "SELECT PASSWORD FROM USER WHERE LOGIN=?"
+ * @param digestMethod : password encoding algorithm : "MD5", "SHA-1", "SHA-256"
+ */
+ public DBAuthenticator(String driver, String jdbcUrl, String sqlQuery, String digestMethod){
+
+ this.sqlQuery = sqlQuery;
+ try {
+ Class.forName(driver);
+ final Connection connection = DriverManager.getConnection(jdbcUrl);
+ this.messageDigest = MessageDigest.getInstance(digestMethod);
+ this.preparedStatement = connection.prepareStatement(sqlQuery);
+ } catch (ClassNotFoundException cnfe){
+ LOG.error(String.format("Can't find driver %s", driver), cnfe);
+ throw new RuntimeException(cnfe);
+ } catch (SQLException sqle) {
+ LOG.error(String.format("Can't connect to %s", jdbcUrl), sqle);
+ throw new RuntimeException(sqle);
+ } catch (NoSuchAlgorithmException nsaex) {
+ LOG.error(String.format("Can't find %s for password encoding", digestMethod), nsaex);
+ throw new RuntimeException(nsaex);
+ }
+ }
+
+ @Override
+ public synchronized boolean checkValid(String username, byte[] password) {
+ // Check Username / Password in DB using sqlQuery
+ if (username == null || password == null) {
+ LOG.info("username or password was null");
+ return false;
+ }
+ ResultSet r= null;
+ try {
+ this.preparedStatement.setString(1,username);
+ r = this.preparedStatement.executeQuery();
+ if(r.next()) {
+ final String foundPwq = r.getString(1);
+ messageDigest.update(password);
+ byte[] digest = messageDigest.digest();
+ String encodedPasswd = new String(Hex.encodeHex(digest));
+ return foundPwq.equals(encodedPasswd);
+ }
+ r.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return false;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ this.preparedStatement.close();
+ this.preparedStatement.getConnection().close();
+ super.finalize();
+ }
+}
diff --git a/broker/src/test/java/io/moquette/spi/impl/security/DBAuthenticatorTest.java b/broker/src/test/java/io/moquette/spi/impl/security/DBAuthenticatorTest.java
new file mode 100644
index 00000000..8c561713
--- /dev/null
+++ b/broker/src/test/java/io/moquette/spi/impl/security/DBAuthenticatorTest.java
@@ -0,0 +1,79 @@
+package io.moquette.spi.impl.security;
+
+import org.apache.commons.codec.binary.Hex;
+import org.junit.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.sql.*;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Created by mackristof on 12/05/2016.
+ */
+public class DBAuthenticatorTest {
+
+ private static final Logger LOG = LoggerFactory.getLogger(DBAuthenticatorTest.class);
+
+ public static final String ORG_H2_DRIVER = "org.h2.Driver";
+ public static final String JDBC_H2_MEM_TEST = "jdbc:h2:mem:test";
+ public static final String SHA_256 = "SHA-256";
+ private Connection connection;
+
+
+ @Before
+ public void setup() throws ClassNotFoundException, SQLException, NoSuchAlgorithmException {
+ Class.forName(ORG_H2_DRIVER);
+ this.connection = DriverManager.getConnection(JDBC_H2_MEM_TEST);
+ Statement statement = this.connection.createStatement();
+ try {
+ statement.execute("DROP TABLE USER");
+ } catch(SQLException sqle) {
+ LOG.info("Table not found, not dropping",sqle);
+ }
+ MessageDigest digest = MessageDigest.getInstance(SHA_256);
+ String hash = new String(Hex.encodeHex(digest.digest("password".getBytes(StandardCharsets.UTF_8))));
+ try {
+ if (statement.execute("CREATE TABLE PUBLIC.USER ( LOGIN VARCHAR(64), PASSWORD VARCHAR(256))")){
+ throw new SQLException("can't create USER table");
+ }
+ if (statement.execute("INSERT INTO PUBLIC.USER ( LOGIN , PASSWORD ) VALUES ('testuser', '"+hash+"')")){
+ throw new SQLException("can't insert in USER table");
+ }
+ } catch(SQLException sqle) {
+ LOG.error("Table not created, not inserted",sqle);
+ return;
+ }
+ LOG.info("Table User created");
+ statement.close();
+
+
+ }
+
+ @Test
+ public void Db_verifyValid() {
+ final DBAuthenticator dbAuthenticator = new DBAuthenticator(ORG_H2_DRIVER, JDBC_H2_MEM_TEST, "SELECT PASSWORD FROM USER WHERE LOGIN=?", SHA_256);
+ assertTrue(dbAuthenticator.checkValid("testuser","password".getBytes()));
+ }
+
+ @Test
+ public void Db_verifyInvalid(){
+ final DBAuthenticator dbAuthenticator = new DBAuthenticator(ORG_H2_DRIVER, JDBC_H2_MEM_TEST, "SELECT PASSWORD FROM USER WHERE LOGIN=?", SHA_256);
+ assertFalse(dbAuthenticator.checkValid("testuser2","password".getBytes()));
+ }
+
+
+ @After
+ public void teardown(){
+ try {
+ this.connection.close();
+ } catch (SQLException e) {
+ LOG.error("can't close connection",e);
+ }
+ }
+}