From cad1165071693b19c83b73c76dd9ec0c65713d64 Mon Sep 17 00:00:00 2001 From: mackristof Date: Thu, 12 May 2016 15:47:49 +0200 Subject: [PATCH] Added DBAuthenticator & Test with H2 DB --- broker/pom.xml | 6 ++ .../spi/impl/security/DBAuthenticator.java | 85 +++++++++++++++++++ .../impl/security/DBAuthenticatorTest.java | 79 +++++++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 broker/src/main/java/io/moquette/spi/impl/security/DBAuthenticator.java create mode 100644 broker/src/test/java/io/moquette/spi/impl/security/DBAuthenticatorTest.java 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); + } + } +}