diff --git a/README.md b/README.md index a0c6d8c..fcdab57 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,11 @@ ## Setup -1. Clone the repository. +1. Clone the repository. Run the following commands: +``` +./gradlew genSources +./gradlew idea +``` 2. Edit the code you want. 3. To build run the following command: @@ -11,5 +15,8 @@ ``` ## License +Libraries that the project is using: +- `LGPLv3 (Argon2)` https://github.com/phxql/argon2-jvm +- `Apache 2 (JDBC)` https://github.com/xerial/sqlite-jdbc -This project is licensed under the MIT license. +This project is licensed under the `MIT` license. diff --git a/build.gradle b/build.gradle index 3af8ea4..919c806 100644 --- a/build.gradle +++ b/build.gradle @@ -25,9 +25,6 @@ dependencies { // Argon2 library for password hashing compile 'de.mkammerer:argon2-jvm:2.6' - // IDEA stuff - implementation 'org.jetbrains:annotations:15.0' - // JDBC SQLite Driver compile group:'org.xerial', name:'sqlite-jdbc', version:'3.8.11.2' diff --git a/src/main/java/org/samo_lego/simpleauth/SimpleAuth.java b/src/main/java/org/samo_lego/simpleauth/SimpleAuth.java index e9e9fed..3d4a116 100644 --- a/src/main/java/org/samo_lego/simpleauth/SimpleAuth.java +++ b/src/main/java/org/samo_lego/simpleauth/SimpleAuth.java @@ -1,28 +1,31 @@ package org.samo_lego.simpleauth; -import de.mkammerer.argon2.Argon2; -import de.mkammerer.argon2.Argon2Factory; import net.fabricmc.api.DedicatedServerModInitializer; import net.fabricmc.fabric.api.event.player.AttackBlockCallback; import net.fabricmc.fabric.api.registry.CommandRegistry; import net.minecraft.server.network.ServerPlayerEntity; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.samo_lego.simpleauth.commands.AuthCommand; +import org.samo_lego.simpleauth.commands.ChangepwCommand; import org.samo_lego.simpleauth.commands.LoginCommand; import org.samo_lego.simpleauth.commands.RegisterCommand; import org.samo_lego.simpleauth.database.SimpleAuthDatabase; import org.samo_lego.simpleauth.event.AuthEventHandler; import org.samo_lego.simpleauth.event.block.BreakBlockCallback; import org.samo_lego.simpleauth.event.block.InteractBlockCallback; -import org.samo_lego.simpleauth.event.entity.player.*; +import org.samo_lego.simpleauth.event.entity.player.InteractItemCallback; +import org.samo_lego.simpleauth.event.entity.player.PlayerJoinServerCallback; +import org.samo_lego.simpleauth.event.entity.player.PlayerLeaveServerCallback; import java.io.File; import java.util.HashSet; public class SimpleAuth implements DedicatedServerModInitializer { private static final Logger LOGGER = LogManager.getLogger(); + public static SimpleAuthDatabase db = new SimpleAuthDatabase(); - @Override + @Override public void onInitializeServer() { // Info I guess :D LOGGER.info("SimpleAuth mod by samo_lego."); @@ -39,6 +42,8 @@ public class SimpleAuth implements DedicatedServerModInitializer { CommandRegistry.INSTANCE.register(false, dispatcher -> { RegisterCommand.registerCommand(dispatcher); LoginCommand.registerCommand(dispatcher); + ChangepwCommand.registerCommand(dispatcher); + AuthCommand.registerCommand(dispatcher); }); // Registering the events @@ -48,9 +53,7 @@ public class SimpleAuth implements DedicatedServerModInitializer { PlayerJoinServerCallback.EVENT.register(AuthEventHandler::onPlayerJoin); PlayerLeaveServerCallback.EVENT.register(AuthEventHandler::onPlayerLeave); BreakBlockCallback.EVENT.register((world, pos, state, player) -> AuthEventHandler.onBlockBroken(player)); - - // Connection to database - SimpleAuthDatabase.main(); + db.makeTable(); } public static HashSet authenticatedUsers = new HashSet<>(); diff --git a/src/main/java/org/samo_lego/simpleauth/commands/AuthCommand.java b/src/main/java/org/samo_lego/simpleauth/commands/AuthCommand.java new file mode 100644 index 0000000..a7c7f9a --- /dev/null +++ b/src/main/java/org/samo_lego/simpleauth/commands/AuthCommand.java @@ -0,0 +1,90 @@ +package org.samo_lego.simpleauth.commands; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import de.mkammerer.argon2.Argon2; +import de.mkammerer.argon2.Argon2Factory; +import net.minecraft.entity.Entity; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.LiteralText; +import net.minecraft.text.Text; +import net.minecraft.text.TranslatableText; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.samo_lego.simpleauth.SimpleAuth; + +import static com.mojang.brigadier.arguments.StringArgumentType.*; +import static net.minecraft.server.command.CommandManager.argument; +import static net.minecraft.server.command.CommandManager.literal; + +public class AuthCommand { + private static final Logger LOGGER = LogManager.getLogger(); + + private static TranslatableText userdataDeleted = new TranslatableText("command.simpleauth.userdataDeleted"); + private static TranslatableText userdataUpdated = new TranslatableText("command.simpleauth.userdataUpdated"); + + public static void registerCommand(CommandDispatcher dispatcher) { + // Registering the "/auth" command + dispatcher.register(literal("auth") + .requires(source -> source.hasPermissionLevel(4)) + .then(literal("update") + .then(argument("uuid", word()) + .then(argument("password", word()) + .executes( ctx -> updatePass( + ctx.getSource(), + getString(ctx, "uuid"), + getString(ctx, "password") + )) + ) + ) + ) + .then(literal("remove") + .then(argument("uuid", word()) + .executes( ctx -> removeAccount( + ctx.getSource(), + getString(ctx, "uuid") + )) + ) + ) + ); + } + + // Method called for checking the password + private static int updatePass(ServerCommandSource source, String uuid, String pass) { + // Getting the player who send the command + Entity sender = source.getEntity(); + + if(uuid == null) + return -1; + // Create instance + Argon2 argon2 = Argon2Factory.create(); + char[] password = pass.toCharArray(); + try { + // Hashed password from DB + String hashed = SimpleAuth.db.getPassword(uuid); + + // Writing into DB + SimpleAuth.db.update(uuid, hashed); + if(sender != null) + sender.sendMessage(userdataUpdated); + else + LOGGER.info(userdataUpdated); + } finally { + // Wipe confidential data + argon2.wipeArray(password); + } + + return 1; // Success + } + private static int removeAccount(ServerCommandSource source, String uuid) { + // Getting the player who send the command + Entity sender = source.getEntity(); + SimpleAuth.db.delete(uuid); + if(sender != null) + sender.sendMessage(userdataDeleted); + else + LOGGER.info(userdataDeleted); + return 1; // Success + } +} diff --git a/src/main/java/org/samo_lego/simpleauth/commands/ChangepwCommand.java b/src/main/java/org/samo_lego/simpleauth/commands/ChangepwCommand.java new file mode 100644 index 0000000..7cd61ef --- /dev/null +++ b/src/main/java/org/samo_lego/simpleauth/commands/ChangepwCommand.java @@ -0,0 +1,76 @@ +package org.samo_lego.simpleauth.commands; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import de.mkammerer.argon2.Argon2; +import de.mkammerer.argon2.Argon2Factory; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.TranslatableText; +import org.samo_lego.simpleauth.SimpleAuth; + +import static com.mojang.brigadier.arguments.StringArgumentType.getString; +import static com.mojang.brigadier.arguments.StringArgumentType.word; +import static net.minecraft.server.command.CommandManager.argument; +import static net.minecraft.server.command.CommandManager.literal; + +public class ChangepwCommand { + private static TranslatableText enterNewPassword = new TranslatableText("command.simpleauth.passwordNew"); + private static TranslatableText enterPassword = new TranslatableText("command.simpleauth.password"); + private static TranslatableText wrongPassword = new TranslatableText("command.simpleauth.wrongPassword"); + private static TranslatableText passwordUpdated = new TranslatableText("command.simpleauth.passwordUpdated"); + + public static void registerCommand(CommandDispatcher dispatcher) { + // Registering the "/changepw" command + dispatcher.register(literal("changepw") + .executes(ctx -> { + ctx.getSource().getPlayer().sendMessage(enterPassword); + return 1; + }) + .then(argument("oldPassword", word()) + .executes(ctx -> { + ctx.getSource().getPlayer().sendMessage(enterNewPassword); + return 1; + }) + .then(argument("newPassword", word()) + .executes( ctx -> changepw( + ctx.getSource(), + getString(ctx, "oldPassword"), + getString(ctx, "newPassword") + ) + ) + ) + ) + ); + } + + // Method called for checking the password and then changing it + private static int changepw(ServerCommandSource source, String oldPass, String newPass) throws CommandSyntaxException { + // Getting the player who send the command + ServerPlayerEntity player = source.getPlayer(); + + // Create instance + Argon2 argon2 = Argon2Factory.create(); + // Read password from user + char[] password = oldPass.toCharArray(); + + try { + // Hashed password from DB + String hashedOld = SimpleAuth.db.getPassword(player.getUuidAsString()); + + // Verify password + if (argon2.verify(hashedOld, password)) { + String hash = argon2.hash(10, 65536, 1, newPass.toCharArray()); + // Writing into DB + SimpleAuth.db.update(player.getUuidAsString(), hash); + player.sendMessage(passwordUpdated); + } + else + player.sendMessage(wrongPassword); + } finally { + // Wipe confidential data + argon2.wipeArray(password); + } + return 1; + } +} 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 3aa973b..998fbb3 100644 --- a/src/main/java/org/samo_lego/simpleauth/commands/LoginCommand.java +++ b/src/main/java/org/samo_lego/simpleauth/commands/LoginCommand.java @@ -51,7 +51,7 @@ public class LoginCommand { try { // Hashed password from DB - String hashed = argon2.hash(10, 65536, 1, password); + String hashed = SimpleAuth.db.getPassword(player.getUuidAsString()); // Verify password if (argon2.verify(hashed, password)) { 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 41aae99..a4d21dd 100644 --- a/src/main/java/org/samo_lego/simpleauth/commands/RegisterCommand.java +++ b/src/main/java/org/samo_lego/simpleauth/commands/RegisterCommand.java @@ -9,8 +9,9 @@ import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.LiteralText; import net.minecraft.text.TranslatableText; import org.samo_lego.simpleauth.SimpleAuth; -import org.samo_lego.simpleauth.database.SimpleAuthDatabase; + import java.util.Objects; + import static com.mojang.brigadier.arguments.StringArgumentType.getString; import static com.mojang.brigadier.arguments.StringArgumentType.word; import static net.minecraft.server.command.CommandManager.argument; @@ -21,6 +22,7 @@ public class RegisterCommand { private static TranslatableText pleaseRegister = new TranslatableText("§4Type /register to login."); private static TranslatableText enterPassword = new TranslatableText("command.simpleauth.passwordTwice"); private static TranslatableText alreadyAuthenticated = new TranslatableText("command.simpleauth.alreadyAuthenticated"); + private static TranslatableText alreadyRegistered = new TranslatableText("command.simpleauth.alreadyRegistered"); public static void registerCommand(CommandDispatcher dispatcher) { @@ -53,12 +55,14 @@ public class RegisterCommand { // Hash password String hash = argon2.hash(10, 65536, 1, password); // Writing into database - SimpleAuthDatabase.insert(Objects.requireNonNull(source.getEntity()).getUuidAsString(), source.getName(), hash); + SimpleAuth.db.insert(Objects.requireNonNull(source.getEntity()).getUuidAsString(), source.getName(), hash); SimpleAuth.authenticatedUsers.add(player); // Letting the player know it was successful player.sendMessage( new LiteralText(source.getName() + ", you have registered successfully!") ); + } catch (Error e) { + player.sendMessage(alreadyRegistered); } finally { // Wipe confidential data argon2.wipeArray(password); 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 25e660d..14aa11c 100644 --- a/src/main/java/org/samo_lego/simpleauth/database/SimpleAuthDatabase.java +++ b/src/main/java/org/samo_lego/simpleauth/database/SimpleAuthDatabase.java @@ -4,51 +4,31 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.sql.*; -import java.util.UUID; /** + * Thanks to * @author sqlitetutorial.net */ public class SimpleAuthDatabase { private static final Logger LOGGER = LogManager.getLogger(); - /*public static void connect() { - Connection conn = null; - try { - // db parameters - String url = "jdbc:sqlite:mods/SimpleAuth/players.db"; - // create a connection to the database - conn = DriverManager.getConnection(url); - // Creating database table - 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" + - ");"; - Statement stmt = conn.createStatement(); - stmt.execute(sql); - } catch (SQLException e) { - System.out.println(e.getMessage()); - } finally { - //Main? - try { - if (conn != null) { - conn.close(); - } - } catch (SQLException ex) { - System.out.println(ex.getMessage()); - } - } - }*/ - private static Connection connect() { + // Connects to the DB + private Connection connect() { // SQLite connection string String url = "jdbc:sqlite:mods/SimpleAuth/players.db"; Connection conn = null; try { conn = DriverManager.getConnection(url); + } catch (SQLException e) { + LOGGER.error(e.getMessage()); + } + return conn; + } + + // If the mod runs for the first time, we need to create the DB table + public void makeTable() { + try (Connection conn = this.connect()) { // Creating database table if it doesn't exist yet String sql = "CREATE TABLE IF NOT EXISTS users (\n" + " `UserID` INTEGER PRIMARY KEY AUTOINCREMENT,\n" + @@ -60,55 +40,81 @@ public class SimpleAuthDatabase { ");"; Statement stmt = conn.createStatement(); stmt.execute(sql); - } catch (SQLException e) { LOGGER.error(e.getMessage()); } - return conn; } - public static void insert(String uuid, String username, String password) { + // When player registers, we insert the data into DB + public void insert(String uuid, String username, String password) { String sql = "INSERT INTO users(uuid, username, password) VALUES(?,?,?)"; - - try (Connection conn = connect(); + try ( + Connection conn = this.connect(); PreparedStatement pstmt = conn.prepareStatement(sql)) { + pstmt.setString(1, uuid); pstmt.setString(2, username); pstmt.setString(3, password); + pstmt.executeUpdate(); } catch (SQLException e) { LOGGER.error(e.getMessage()); } } - public void selectAll(){ - String sql = "SELECT id, name, capacity FROM users"; + + // Deletes row containing the username provided + public void delete(String uuid) { + String sql = "DELETE FROM users WHERE uuid = ?"; try (Connection conn = this.connect(); - Statement stmt = conn.createStatement(); - ResultSet rs = stmt.executeQuery(sql)){ + PreparedStatement pstmt = conn.prepareStatement(sql)) { + + // set the corresponding param + pstmt.setString(1, uuid); + // execute the delete statement + pstmt.executeUpdate(); - // loop through the result set - while (rs.next()) { - System.out.println(rs.getInt("id") + "\t" + - rs.getString("name") + "\t" + - rs.getDouble("capacity")); - } } catch (SQLException e) { - System.out.println(e.getMessage()); - } - } - private static void disconnect(Connection conn) { - try { - if (conn != null) { - conn.close(); - } - } catch (SQLException ex) { - LOGGER.error(ex.getMessage()); + LOGGER.error(e.getMessage()); } } - public static void main() { - Connection conn = connect(); + // Updates the password of the user + public void update(String uuid, String pass) { + String sql = "UPDATE users SET password = ? " + + "WHERE uuid = ?"; + + try (Connection conn = this.connect(); + PreparedStatement pstmt = conn.prepareStatement(sql)) { + + // set the corresponding param + pstmt.setString(1, pass); + pstmt.setString(2, uuid); + + // update + pstmt.executeUpdate(); + } catch (SQLException e) { + LOGGER.error(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 (Connection conn = this.connect(); + 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(e.getMessage()); + } + return pass; + } } diff --git a/src/main/resources/assets/simpleauth/lang/en_us.lang b/src/main/resources/assets/simpleauth/lang/en_us.json similarity index 82% rename from src/main/resources/assets/simpleauth/lang/en_us.lang rename to src/main/resources/assets/simpleauth/lang/en_us.json index 967c9f1..36d0892 100644 --- a/src/main/resources/assets/simpleauth/lang/en_us.lang +++ b/src/main/resources/assets/simpleauth/lang/en_us.json @@ -3,6 +3,7 @@ "command.simpleauth.passwordTwice": "§6You need to enter your password twice!", "command.simpleauth.passwordMatch": "§6Passwords must match!", "command.simpleauth.passwordWrong": "§4Wrong password!", + "command.simpleauth.alreadyRegistered": "§6This account name is already registered!", "command.simpleauth.notAuthenticated": "§4You are not authenticated!", "command.simpleauth.authenticated": "§aYou are now authenticated." } \ No newline at end of file diff --git a/src/main/resources/create.sql b/src/main/resources/create.sql deleted file mode 100644 index 611f878..0000000 --- a/src/main/resources/create.sql +++ /dev/null @@ -1,7 +0,0 @@ -CREATE TABLE IF NOT EXISTS users ( - `UserID` INTEGER PRIMARY KEY AUTOINCREMENT, - `UUID` BINARY(16) NOT NULL, - `Username` VARCHAR(16) NOT NULL, - `Password` VARCHAR(64) NOT NULL, - UNIQUE (`UUID`) -); \ No newline at end of file