diff --git a/README.md b/README.md index b47dbd6..101db29 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Requires Fabric API. ## License Libraries that the project is using: - `Argon2 (LGPLv3)` https://github.com/phxql/argon2-jvm -- `JDBC (Apache 2)` https://github.com/xerial/sqlite-jdbc +- `leveldb (BSD-3-Clause)` https://github.com/google/leveldb - `JNA (Apache 2 || LGPLv3)` https://github.com/java-native-access/jna This project is licensed under the `MIT` license. diff --git a/build.gradle b/build.gradle index 98772bd..8b89ebe 100644 --- a/build.gradle +++ b/build.gradle @@ -26,9 +26,12 @@ dependencies { implementation 'de.mkammerer:argon2-jvm:2.6' include 'de.mkammerer:argon2-jvm:2.6' - // JDBC SQLite Driver - implementation group:'org.xerial', name:'sqlite-jdbc', version:'3.8.11.2' - include group:'org.xerial', name:'sqlite-jdbc', version:'3.8.11.2' + // leveldb + implementation group: 'org.iq80.leveldb', name: 'leveldb', version: '0.12' + implementation group: 'org.iq80.leveldb', name: 'leveldb-api', version: '0.12' + include group: 'org.iq80.leveldb', name: 'leveldb', version: '0.12' + include group: 'org.iq80.leveldb', name: 'leveldb-api', version: '0.12' + // JNA lib include 'net.java.dev.jna:jna:5.5.0' diff --git a/src/main/java/org/samo_lego/simpleauth/SimpleAuth.java b/src/main/java/org/samo_lego/simpleauth/SimpleAuth.java index bd40a99..525d2a3 100644 --- a/src/main/java/org/samo_lego/simpleauth/SimpleAuth.java +++ b/src/main/java/org/samo_lego/simpleauth/SimpleAuth.java @@ -52,15 +52,14 @@ public class SimpleAuth implements DedicatedServerModInitializer { LOGGER.info("[SimpleAuth] This mod wouldn't exist without the awesome Fabric Community. TYSM guys!"); // Creating data directory (database and config files are stored there) - File file = new File(gameDirectory + "/mods/SimpleAuth"); + File file = new File(gameDirectory + "/mods/SimpleAuth/levelDBStore"); if (!file.exists() && !file.mkdir()) LOGGER.error("[SimpleAuth] Error creating directory!"); // Loading config config = AuthConfig.load(new File(gameDirectory + "/mods/SimpleAuth/config.json")); // Connecting to db db.openConnection(); - // Making a table in the database - db.makeTable(); + // Registering the commands CommandRegistry.INSTANCE.register(false, dispatcher -> { diff --git a/src/main/java/org/samo_lego/simpleauth/commands/AuthCommand.java b/src/main/java/org/samo_lego/simpleauth/commands/AuthCommand.java index 6d892d1..c23f082 100644 --- a/src/main/java/org/samo_lego/simpleauth/commands/AuthCommand.java +++ b/src/main/java/org/samo_lego/simpleauth/commands/AuthCommand.java @@ -112,7 +112,6 @@ public class AuthCommand { SimpleAuth.db.update( uuid, - username, AuthHelper.hashPass(pass.toCharArray()) ); if(sender != null) @@ -124,7 +123,7 @@ public class AuthCommand { } private static int removeAccount(ServerCommandSource source, String uuid, String username) { Entity sender = source.getEntity(); - SimpleAuth.db.delete(uuid, username); + SimpleAuth.db.delete(uuid); // TODO -> Kick player that was unregistered? diff --git a/src/main/java/org/samo_lego/simpleauth/commands/ChangepwCommand.java b/src/main/java/org/samo_lego/simpleauth/commands/ChangepwCommand.java index 24b7753..8ff5321 100644 --- a/src/main/java/org/samo_lego/simpleauth/commands/ChangepwCommand.java +++ b/src/main/java/org/samo_lego/simpleauth/commands/ChangepwCommand.java @@ -54,7 +54,7 @@ public class ChangepwCommand { player.sendMessage(cannotChangePassword); return 0; } - else if (AuthHelper.checkPass(player.getUuidAsString(), oldPass.toCharArray())) { + else if (AuthHelper.checkPass(player.getUuidAsString(), oldPass.toCharArray()) == 1) { if(newPass.length() < SimpleAuth.config.main.minPasswordChars) { player.sendMessage(new LiteralText( String.format(SimpleAuth.config.lang.minPasswordChars, SimpleAuth.config.main.minPasswordChars) @@ -69,7 +69,6 @@ public class ChangepwCommand { } SimpleAuth.db.update( player.getUuidAsString(), - null, AuthHelper.hashPass(newPass.toCharArray()) ); player.sendMessage(passwordUpdated); diff --git a/src/main/java/org/samo_lego/simpleauth/commands/LoginCommand.java b/src/main/java/org/samo_lego/simpleauth/commands/LoginCommand.java index ff1900a..c27bd19 100644 --- a/src/main/java/org/samo_lego/simpleauth/commands/LoginCommand.java +++ b/src/main/java/org/samo_lego/simpleauth/commands/LoginCommand.java @@ -18,6 +18,7 @@ public class LoginCommand { private static Text enterPassword = new LiteralText(SimpleAuth.config.lang.enterPassword); private static Text wrongPassword = new LiteralText(SimpleAuth.config.lang.wrongPassword); private static Text alreadyAuthenticated = new LiteralText(SimpleAuth.config.lang.alreadyAuthenticated); + private static Text notRegistered = new LiteralText(SimpleAuth.config.lang.notRegistered); private static Text loginTriesExceeded = new LiteralText(SimpleAuth.config.lang.loginTriesExceeded); private static Text successfullyAuthenticated = new LiteralText(SimpleAuth.config.lang.successfullyAuthenticated); private static int maxLoginTries = SimpleAuth.config.main.maxLoginTries; @@ -47,16 +48,14 @@ public class LoginCommand { player.networkHandler.disconnect(loginTriesExceeded); return 0; } - else if(SimpleAuth.config.main.enableGlobalPassword) { - if (AuthHelper.checkPass(null, pass.toCharArray())) { - SimpleAuth.authenticatePlayer(player, successfullyAuthenticated); - return 1; - } - } - else if (AuthHelper.checkPass(player.getUuidAsString(), pass.toCharArray())) { + else if (AuthHelper.checkPass(player.getUuidAsString(), pass.toCharArray()) == 1) { SimpleAuth.authenticatePlayer(player, successfullyAuthenticated); return 1; } + else if(AuthHelper.checkPass(player.getUuidAsString(), pass.toCharArray()) == -1) { + player.sendMessage(notRegistered); + return 0; + } // Kicking the player out else if(maxLoginTries == 1) { player.networkHandler.disconnect(wrongPassword); diff --git a/src/main/java/org/samo_lego/simpleauth/commands/RegisterCommand.java b/src/main/java/org/samo_lego/simpleauth/commands/RegisterCommand.java index 519ab52..a55f798 100644 --- a/src/main/java/org/samo_lego/simpleauth/commands/RegisterCommand.java +++ b/src/main/java/org/samo_lego/simpleauth/commands/RegisterCommand.java @@ -62,7 +62,7 @@ public class RegisterCommand { return 0; } String hash = AuthHelper.hashPass(pass1.toCharArray()); - if (SimpleAuth.db.registerUser(player.getUuidAsString(), source.getName(), hash)) { + if (SimpleAuth.db.registerUser(player.getUuidAsString(), hash)) { SimpleAuth.authenticatePlayer(player, registerSuccess); return 1; } diff --git a/src/main/java/org/samo_lego/simpleauth/commands/UnregisterCommand.java b/src/main/java/org/samo_lego/simpleauth/commands/UnregisterCommand.java index 9e056df..b5c8677 100644 --- a/src/main/java/org/samo_lego/simpleauth/commands/UnregisterCommand.java +++ b/src/main/java/org/samo_lego/simpleauth/commands/UnregisterCommand.java @@ -45,9 +45,9 @@ public class UnregisterCommand { player.sendMessage(cannotUnregister); return 0; } - else if (AuthHelper.checkPass(player.getUuidAsString(), pass.toCharArray())) { + else if (AuthHelper.checkPass(player.getUuidAsString(), pass.toCharArray()) == 1) { SimpleAuth.deauthenticatePlayer(player); - SimpleAuth.db.delete(player.getUuidAsString(), null); + SimpleAuth.db.delete(player.getUuidAsString()); player.sendMessage(accountDeleted); return 1; } diff --git a/src/main/java/org/samo_lego/simpleauth/database/SimpleAuthDatabase.java b/src/main/java/org/samo_lego/simpleauth/database/SimpleAuthDatabase.java index c32bd04..40b4990 100644 --- a/src/main/java/org/samo_lego/simpleauth/database/SimpleAuthDatabase.java +++ b/src/main/java/org/samo_lego/simpleauth/database/SimpleAuthDatabase.java @@ -2,146 +2,93 @@ package org.samo_lego.simpleauth.database; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.iq80.leveldb.DB; +import org.iq80.leveldb.DBException; +import org.iq80.leveldb.Options; import org.samo_lego.simpleauth.SimpleAuth; -import java.sql.*; +import java.io.File; +import java.io.IOException; -/** - * Thanks to - * @author sqlitetutorial.net - */ +import static org.iq80.leveldb.impl.Iq80DBFactory.bytes; +import static org.iq80.leveldb.impl.Iq80DBFactory.factory; public class SimpleAuthDatabase { private static final Logger LOGGER = LogManager.getLogger(); + private DB levelDBStore; // Connects to the DB - private Connection conn; - public void openConnection() { - // SQLite connection string - String url = "jdbc:sqlite:" + SimpleAuth.gameDirectory + "/mods/SimpleAuth/players.db"; try { - conn = DriverManager.getConnection(url); - } catch (SQLException e) { + + Options options = new Options(); + levelDBStore = factory.open(new File(SimpleAuth.gameDirectory + "/mods/SimpleAuth/levelDBStore"), options); + } catch (Error | IOException e) { LOGGER.error("[SimpleAuth] " + e.getMessage()); } } // Closing connection public void close() { - if (conn != null) { + if (levelDBStore != null) { try { - conn.close(); + levelDBStore.close(); LOGGER.info("[SimpleAuth] Database connection closed successfully."); - } catch (SQLException e) { + } catch (Error | IOException e) { LOGGER.info("[SimpleAuth] Error: " + e.getMessage()); } } } - // If the mod runs for the first time, we need to create the DB table - public void makeTable() { - try { - // Creating database table if it doesn't exist yet - String sql = "CREATE TABLE IF NOT EXISTS users (\n" + - " `UserID` INTEGER PRIMARY KEY AUTOINCREMENT,\n" + - " `UUID` BINARY(16) NOT NULL,\n" + - " `Username` VARCHAR(16) NOT NULL,\n" + - " `Password` VARCHAR(64) NOT NULL,\n" + - " UNIQUE (`UUID`)\n" + - " UNIQUE (`Username`)\n" + - ");"; - Statement stmt = conn.createStatement(); - stmt.execute(sql); - } catch (SQLException e) { - LOGGER.error("[SimpleAuth] Error: " + e.getMessage()); - } - } - // When player registers, we insert the data into DB - public boolean registerUser(String uuid, String username, String password) { - String sql = "INSERT INTO users(uuid, username, password) VALUES(?,?,?)"; - String sqlCheck = "SELECT UUID " - + "FROM users WHERE UUID = ?"; - - try (PreparedStatement pstmt = conn.prepareStatement(sql); - PreparedStatement pstmtCheck = conn.prepareStatement(sqlCheck)) { - - pstmtCheck.setString(1, uuid); - ResultSet rs = pstmtCheck.executeQuery(); - - // Getting the password - //String dbUuid = null; - try { - rs.getString("UUID"); - return false; - } catch(SQLException ignored) { - // User is not registered - } finally { - pstmt.setString(1, uuid); - pstmt.setString(2, username); - pstmt.setString(3, password); - - pstmt.executeUpdate(); + public boolean registerUser(String uuid, String password) { + try { + if(!this.isRegistered(uuid)) { + levelDBStore.put(bytes("UUID:" + uuid), bytes("password:" + password)); + return true; } - return true; - } catch (SQLException e) { + return false; + } catch (Error e) { LOGGER.error("[SimpleAuth] Register error: " + e.getMessage()); return false; } } + // Checks if user is registered + private boolean isRegistered(String uuid) { + try { + return levelDBStore.get(bytes("UUID:" + uuid)) != null; + } catch (DBException e) { + LOGGER.error("[SimpleAuth] " + e.getMessage()); + } + return false; + } + // Deletes row containing the username provided - public void delete(String uuid, String username) { - String sql = "DELETE FROM users WHERE uuid = ? OR username = ?"; - - try (PreparedStatement pstmt = conn.prepareStatement(sql)) { - - // set the corresponding param - pstmt.setString(1, uuid); - pstmt.setString(2, username); - // execute the delete statement - pstmt.executeUpdate(); - - } catch (SQLException e) { + public void delete(String uuid) { + try { + levelDBStore.delete(bytes("UUID:" + uuid)); + } catch (Error e) { LOGGER.error("[SimpleAuth] " + e.getMessage()); } } // Updates the password of the user - public void update(String uuid, String username, String pass) { - String sql = "UPDATE users SET password = ? " - + "WHERE uuid = ? OR username = ?"; - - try (PreparedStatement pstmt = conn.prepareStatement(sql)) { - - // set the corresponding param - pstmt.setString(1, pass); - pstmt.setString(2, uuid); - pstmt.setString(3, username); - - // update - pstmt.executeUpdate(); - } catch (SQLException e) { + public void update(String uuid, String password) { + try { + levelDBStore.put(bytes("UUID:" + uuid),bytes("password:" + password)); + } catch (Error e) { LOGGER.error("[SimpleAuth] " + e.getMessage()); } } // Gets the hashed password from DB public String getPassword(String uuid){ - String sql = "SELECT UUID, Password " - + "FROM users WHERE UUID = ?"; - String pass = null; - - try (PreparedStatement pstmt = conn.prepareStatement(sql)) { - // Setting statement - pstmt.setString(1, uuid); - ResultSet rs = pstmt.executeQuery(); - - // Getting the password - pass = rs.getString("Password"); - } catch (SQLException e) { + try { + if(this.isRegistered(uuid)) // Gets password from db and removes "password:" prefix from it + return new String(levelDBStore.get(bytes("UUID:" + uuid))).substring(9); + } catch (Error e) { LOGGER.error("[SimpleAuth] Error getting password: " + e.getMessage()); } - return pass; + return ""; } } \ No newline at end of file diff --git a/src/main/java/org/samo_lego/simpleauth/utils/AuthConfig.java b/src/main/java/org/samo_lego/simpleauth/utils/AuthConfig.java index bb55c9a..2186977 100644 --- a/src/main/java/org/samo_lego/simpleauth/utils/AuthConfig.java +++ b/src/main/java/org/samo_lego/simpleauth/utils/AuthConfig.java @@ -95,6 +95,7 @@ public class AuthConfig { public String successfulPortalRescue = "§aYou were rescued from nether portal successfully!"; public String maxPasswordChars = "§6Password can be at most %d characters long!"; public String minPasswordChars = "§6Password needs to be at least %d characters long!"; + public String notRegistered = "§6This account is not yet registered! Type `/register` first"; } private static final Logger LOGGER = LogManager.getLogger(); private static final Gson gson = new GsonBuilder() diff --git a/src/main/java/org/samo_lego/simpleauth/utils/AuthHelper.java b/src/main/java/org/samo_lego/simpleauth/utils/AuthHelper.java index bd8dcc1..45cbf2e 100644 --- a/src/main/java/org/samo_lego/simpleauth/utils/AuthHelper.java +++ b/src/main/java/org/samo_lego/simpleauth/utils/AuthHelper.java @@ -12,15 +12,17 @@ public class AuthHelper { // Creating the instance private static Argon2 argon2 = Argon2Factory.create(); - public static boolean checkPass(String uuid, char[] pass) { + // Returns 1 if password is correct, 0 if not + // and -1 if user is not registered yet + public static int checkPass(String uuid, char[] pass) { if(SimpleAuth.config.main.enableGlobalPassword) { // We have global password enabled try { - return argon2.verify(SimpleAuth.config.main.globalPassword, pass); + return argon2.verify(SimpleAuth.config.main.globalPassword, pass) ? 1 : 0; } catch (Error e) { LOGGER.error("[SimpleAuth] Argon2 error: " + e); - return false; + return 0; } finally { // Wipe confidential data argon2.wipeArray(pass); @@ -30,11 +32,13 @@ public class AuthHelper { try { // Hashed password from DB String hashed = SimpleAuth.db.getPassword(uuid); + if(hashed.equals("")) + return -1; // User is not yet registered // Verify password - return argon2.verify(hashed, pass); + return argon2.verify(hashed, pass) ? 1 : 0; } catch (Error e) { LOGGER.error("[SimpleAuth] error: " + e); - return false; + return 0; } finally { // Wipe confidential data argon2.wipeArray(pass);