forked from sorceress/EasyAuth
Merge branch 'leveldb' - switched from SQLite to leveldb database.
This commit is contained in:
commit
30e261c767
|
@ -18,7 +18,7 @@ Requires Fabric API.
|
||||||
## License
|
## License
|
||||||
Libraries that the project is using:
|
Libraries that the project is using:
|
||||||
- `Argon2 (LGPLv3)` https://github.com/phxql/argon2-jvm
|
- `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
|
- `JNA (Apache 2 || LGPLv3)` https://github.com/java-native-access/jna
|
||||||
|
|
||||||
This project is licensed under the `MIT` license.
|
This project is licensed under the `MIT` license.
|
||||||
|
|
|
@ -26,9 +26,12 @@ dependencies {
|
||||||
implementation 'de.mkammerer:argon2-jvm:2.6'
|
implementation 'de.mkammerer:argon2-jvm:2.6'
|
||||||
include 'de.mkammerer:argon2-jvm:2.6'
|
include 'de.mkammerer:argon2-jvm:2.6'
|
||||||
|
|
||||||
// JDBC SQLite Driver
|
// leveldb
|
||||||
implementation group:'org.xerial', name:'sqlite-jdbc', version:'3.8.11.2'
|
implementation group: 'org.iq80.leveldb', name: 'leveldb', version: '0.12'
|
||||||
include group:'org.xerial', name:'sqlite-jdbc', version:'3.8.11.2'
|
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
|
// JNA lib
|
||||||
include 'net.java.dev.jna:jna:5.5.0'
|
include 'net.java.dev.jna:jna:5.5.0'
|
||||||
|
|
|
@ -52,15 +52,14 @@ public class SimpleAuth implements DedicatedServerModInitializer {
|
||||||
LOGGER.info("[SimpleAuth] This mod wouldn't exist without the awesome Fabric Community. TYSM guys!");
|
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)
|
// 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())
|
if (!file.exists() && !file.mkdir())
|
||||||
LOGGER.error("[SimpleAuth] Error creating directory!");
|
LOGGER.error("[SimpleAuth] Error creating directory!");
|
||||||
// Loading config
|
// Loading config
|
||||||
config = AuthConfig.load(new File(gameDirectory + "/mods/SimpleAuth/config.json"));
|
config = AuthConfig.load(new File(gameDirectory + "/mods/SimpleAuth/config.json"));
|
||||||
// Connecting to db
|
// Connecting to db
|
||||||
db.openConnection();
|
db.openConnection();
|
||||||
// Making a table in the database
|
|
||||||
db.makeTable();
|
|
||||||
|
|
||||||
// Registering the commands
|
// Registering the commands
|
||||||
CommandRegistry.INSTANCE.register(false, dispatcher -> {
|
CommandRegistry.INSTANCE.register(false, dispatcher -> {
|
||||||
|
|
|
@ -112,7 +112,6 @@ public class AuthCommand {
|
||||||
|
|
||||||
SimpleAuth.db.update(
|
SimpleAuth.db.update(
|
||||||
uuid,
|
uuid,
|
||||||
username,
|
|
||||||
AuthHelper.hashPass(pass.toCharArray())
|
AuthHelper.hashPass(pass.toCharArray())
|
||||||
);
|
);
|
||||||
if(sender != null)
|
if(sender != null)
|
||||||
|
@ -124,7 +123,7 @@ public class AuthCommand {
|
||||||
}
|
}
|
||||||
private static int removeAccount(ServerCommandSource source, String uuid, String username) {
|
private static int removeAccount(ServerCommandSource source, String uuid, String username) {
|
||||||
Entity sender = source.getEntity();
|
Entity sender = source.getEntity();
|
||||||
SimpleAuth.db.delete(uuid, username);
|
SimpleAuth.db.delete(uuid);
|
||||||
|
|
||||||
// TODO -> Kick player that was unregistered?
|
// TODO -> Kick player that was unregistered?
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ public class ChangepwCommand {
|
||||||
player.sendMessage(cannotChangePassword);
|
player.sendMessage(cannotChangePassword);
|
||||||
return 0;
|
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) {
|
if(newPass.length() < SimpleAuth.config.main.minPasswordChars) {
|
||||||
player.sendMessage(new LiteralText(
|
player.sendMessage(new LiteralText(
|
||||||
String.format(SimpleAuth.config.lang.minPasswordChars, SimpleAuth.config.main.minPasswordChars)
|
String.format(SimpleAuth.config.lang.minPasswordChars, SimpleAuth.config.main.minPasswordChars)
|
||||||
|
@ -69,7 +69,6 @@ public class ChangepwCommand {
|
||||||
}
|
}
|
||||||
SimpleAuth.db.update(
|
SimpleAuth.db.update(
|
||||||
player.getUuidAsString(),
|
player.getUuidAsString(),
|
||||||
null,
|
|
||||||
AuthHelper.hashPass(newPass.toCharArray())
|
AuthHelper.hashPass(newPass.toCharArray())
|
||||||
);
|
);
|
||||||
player.sendMessage(passwordUpdated);
|
player.sendMessage(passwordUpdated);
|
||||||
|
|
|
@ -18,6 +18,7 @@ public class LoginCommand {
|
||||||
private static Text enterPassword = new LiteralText(SimpleAuth.config.lang.enterPassword);
|
private static Text enterPassword = new LiteralText(SimpleAuth.config.lang.enterPassword);
|
||||||
private static Text wrongPassword = new LiteralText(SimpleAuth.config.lang.wrongPassword);
|
private static Text wrongPassword = new LiteralText(SimpleAuth.config.lang.wrongPassword);
|
||||||
private static Text alreadyAuthenticated = new LiteralText(SimpleAuth.config.lang.alreadyAuthenticated);
|
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 loginTriesExceeded = new LiteralText(SimpleAuth.config.lang.loginTriesExceeded);
|
||||||
private static Text successfullyAuthenticated = new LiteralText(SimpleAuth.config.lang.successfullyAuthenticated);
|
private static Text successfullyAuthenticated = new LiteralText(SimpleAuth.config.lang.successfullyAuthenticated);
|
||||||
private static int maxLoginTries = SimpleAuth.config.main.maxLoginTries;
|
private static int maxLoginTries = SimpleAuth.config.main.maxLoginTries;
|
||||||
|
@ -47,15 +48,13 @@ public class LoginCommand {
|
||||||
player.networkHandler.disconnect(loginTriesExceeded);
|
player.networkHandler.disconnect(loginTriesExceeded);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
else if(SimpleAuth.config.main.enableGlobalPassword) {
|
else if (AuthHelper.checkPass(player.getUuidAsString(), pass.toCharArray()) == 1) {
|
||||||
if (AuthHelper.checkPass(null, pass.toCharArray())) {
|
|
||||||
SimpleAuth.authenticatePlayer(player, successfullyAuthenticated);
|
SimpleAuth.authenticatePlayer(player, successfullyAuthenticated);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
else if(AuthHelper.checkPass(player.getUuidAsString(), pass.toCharArray()) == -1) {
|
||||||
else if (AuthHelper.checkPass(player.getUuidAsString(), pass.toCharArray())) {
|
player.sendMessage(notRegistered);
|
||||||
SimpleAuth.authenticatePlayer(player, successfullyAuthenticated);
|
return 0;
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
// Kicking the player out
|
// Kicking the player out
|
||||||
else if(maxLoginTries == 1) {
|
else if(maxLoginTries == 1) {
|
||||||
|
|
|
@ -62,7 +62,7 @@ public class RegisterCommand {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
String hash = AuthHelper.hashPass(pass1.toCharArray());
|
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);
|
SimpleAuth.authenticatePlayer(player, registerSuccess);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,9 +45,9 @@ public class UnregisterCommand {
|
||||||
player.sendMessage(cannotUnregister);
|
player.sendMessage(cannotUnregister);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
else if (AuthHelper.checkPass(player.getUuidAsString(), pass.toCharArray())) {
|
else if (AuthHelper.checkPass(player.getUuidAsString(), pass.toCharArray()) == 1) {
|
||||||
SimpleAuth.deauthenticatePlayer(player);
|
SimpleAuth.deauthenticatePlayer(player);
|
||||||
SimpleAuth.db.delete(player.getUuidAsString(), null);
|
SimpleAuth.db.delete(player.getUuidAsString());
|
||||||
player.sendMessage(accountDeleted);
|
player.sendMessage(accountDeleted);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,146 +2,93 @@ package org.samo_lego.simpleauth.database;
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
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 org.samo_lego.simpleauth.SimpleAuth;
|
||||||
|
|
||||||
import java.sql.*;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
import static org.iq80.leveldb.impl.Iq80DBFactory.bytes;
|
||||||
* Thanks to
|
import static org.iq80.leveldb.impl.Iq80DBFactory.factory;
|
||||||
* @author sqlitetutorial.net
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class SimpleAuthDatabase {
|
public class SimpleAuthDatabase {
|
||||||
private static final Logger LOGGER = LogManager.getLogger();
|
private static final Logger LOGGER = LogManager.getLogger();
|
||||||
|
private DB levelDBStore;
|
||||||
|
|
||||||
// Connects to the DB
|
// Connects to the DB
|
||||||
private Connection conn;
|
|
||||||
|
|
||||||
public void openConnection() {
|
public void openConnection() {
|
||||||
// SQLite connection string
|
|
||||||
String url = "jdbc:sqlite:" + SimpleAuth.gameDirectory + "/mods/SimpleAuth/players.db";
|
|
||||||
try {
|
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());
|
LOGGER.error("[SimpleAuth] " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Closing connection
|
// Closing connection
|
||||||
public void close() {
|
public void close() {
|
||||||
if (conn != null) {
|
if (levelDBStore != null) {
|
||||||
try {
|
try {
|
||||||
conn.close();
|
levelDBStore.close();
|
||||||
LOGGER.info("[SimpleAuth] Database connection closed successfully.");
|
LOGGER.info("[SimpleAuth] Database connection closed successfully.");
|
||||||
} catch (SQLException e) {
|
} catch (Error | IOException e) {
|
||||||
LOGGER.info("[SimpleAuth] Error: " + e.getMessage());
|
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
|
// When player registers, we insert the data into DB
|
||||||
public boolean registerUser(String uuid, String username, String password) {
|
public boolean registerUser(String uuid, 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 {
|
try {
|
||||||
rs.getString("UUID");
|
if(!this.isRegistered(uuid)) {
|
||||||
return false;
|
levelDBStore.put(bytes("UUID:" + uuid), bytes("password:" + password));
|
||||||
} catch(SQLException ignored) {
|
|
||||||
// User is not registered
|
|
||||||
} finally {
|
|
||||||
pstmt.setString(1, uuid);
|
|
||||||
pstmt.setString(2, username);
|
|
||||||
pstmt.setString(3, password);
|
|
||||||
|
|
||||||
pstmt.executeUpdate();
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
} catch (SQLException e) {
|
}
|
||||||
|
return false;
|
||||||
|
} catch (Error e) {
|
||||||
LOGGER.error("[SimpleAuth] Register error: " + e.getMessage());
|
LOGGER.error("[SimpleAuth] Register error: " + e.getMessage());
|
||||||
return false;
|
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
|
// Deletes row containing the username provided
|
||||||
public void delete(String uuid, String username) {
|
public void delete(String uuid) {
|
||||||
String sql = "DELETE FROM users WHERE uuid = ? OR username = ?";
|
try {
|
||||||
|
levelDBStore.delete(bytes("UUID:" + uuid));
|
||||||
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
|
} catch (Error e) {
|
||||||
|
|
||||||
// set the corresponding param
|
|
||||||
pstmt.setString(1, uuid);
|
|
||||||
pstmt.setString(2, username);
|
|
||||||
// execute the delete statement
|
|
||||||
pstmt.executeUpdate();
|
|
||||||
|
|
||||||
} catch (SQLException e) {
|
|
||||||
LOGGER.error("[SimpleAuth] " + e.getMessage());
|
LOGGER.error("[SimpleAuth] " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updates the password of the user
|
// Updates the password of the user
|
||||||
public void update(String uuid, String username, String pass) {
|
public void update(String uuid, String password) {
|
||||||
String sql = "UPDATE users SET password = ? "
|
try {
|
||||||
+ "WHERE uuid = ? OR username = ?";
|
levelDBStore.put(bytes("UUID:" + uuid),bytes("password:" + password));
|
||||||
|
} catch (Error e) {
|
||||||
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) {
|
|
||||||
LOGGER.error("[SimpleAuth] " + e.getMessage());
|
LOGGER.error("[SimpleAuth] " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the hashed password from DB
|
// Gets the hashed password from DB
|
||||||
public String getPassword(String uuid){
|
public String getPassword(String uuid){
|
||||||
String sql = "SELECT UUID, Password "
|
try {
|
||||||
+ "FROM users WHERE UUID = ?";
|
if(this.isRegistered(uuid)) // Gets password from db and removes "password:" prefix from it
|
||||||
String pass = null;
|
return new String(levelDBStore.get(bytes("UUID:" + uuid))).substring(9);
|
||||||
|
} catch (Error e) {
|
||||||
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) {
|
|
||||||
LOGGER.error("[SimpleAuth] Error getting password: " + e.getMessage());
|
LOGGER.error("[SimpleAuth] Error getting password: " + e.getMessage());
|
||||||
}
|
}
|
||||||
return pass;
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -95,6 +95,7 @@ public class AuthConfig {
|
||||||
public String successfulPortalRescue = "§aYou were rescued from nether portal successfully!";
|
public String successfulPortalRescue = "§aYou were rescued from nether portal successfully!";
|
||||||
public String maxPasswordChars = "§6Password can be at most %d characters long!";
|
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 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 Logger LOGGER = LogManager.getLogger();
|
||||||
private static final Gson gson = new GsonBuilder()
|
private static final Gson gson = new GsonBuilder()
|
||||||
|
|
|
@ -12,15 +12,17 @@ public class AuthHelper {
|
||||||
// Creating the instance
|
// Creating the instance
|
||||||
private static Argon2 argon2 = Argon2Factory.create();
|
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) {
|
if(SimpleAuth.config.main.enableGlobalPassword) {
|
||||||
// We have global password enabled
|
// We have global password enabled
|
||||||
try {
|
try {
|
||||||
return argon2.verify(SimpleAuth.config.main.globalPassword, pass);
|
return argon2.verify(SimpleAuth.config.main.globalPassword, pass) ? 1 : 0;
|
||||||
}
|
}
|
||||||
catch (Error e) {
|
catch (Error e) {
|
||||||
LOGGER.error("[SimpleAuth] Argon2 error: " + e);
|
LOGGER.error("[SimpleAuth] Argon2 error: " + e);
|
||||||
return false;
|
return 0;
|
||||||
} finally {
|
} finally {
|
||||||
// Wipe confidential data
|
// Wipe confidential data
|
||||||
argon2.wipeArray(pass);
|
argon2.wipeArray(pass);
|
||||||
|
@ -30,11 +32,13 @@ public class AuthHelper {
|
||||||
try {
|
try {
|
||||||
// Hashed password from DB
|
// Hashed password from DB
|
||||||
String hashed = SimpleAuth.db.getPassword(uuid);
|
String hashed = SimpleAuth.db.getPassword(uuid);
|
||||||
|
if(hashed.equals(""))
|
||||||
|
return -1; // User is not yet registered
|
||||||
// Verify password
|
// Verify password
|
||||||
return argon2.verify(hashed, pass);
|
return argon2.verify(hashed, pass) ? 1 : 0;
|
||||||
} catch (Error e) {
|
} catch (Error e) {
|
||||||
LOGGER.error("[SimpleAuth] error: " + e);
|
LOGGER.error("[SimpleAuth] error: " + e);
|
||||||
return false;
|
return 0;
|
||||||
} finally {
|
} finally {
|
||||||
// Wipe confidential data
|
// Wipe confidential data
|
||||||
argon2.wipeArray(pass);
|
argon2.wipeArray(pass);
|
||||||
|
|
Loading…
Reference in New Issue