Compare commits

...

3 Commits

Author SHA1 Message Date
sebivh
690696b496 Add example decode token funktion 2025-10-17 14:02:17 +02:00
sebivh
1115b7378f Add example tocken encryption pyhton function 2025-10-17 14:01:46 +02:00
sebivh
a0157292cc Add the ability to define multiple authentication services 2025-10-17 13:56:15 +02:00
8 changed files with 152 additions and 12 deletions

29
dec_token.py Normal file
View File

@@ -0,0 +1,29 @@
def decode_token(username, token, masterpassword):
# decode the token from hex to bytes
decoded_token = bytes.fromhex(token)
unsername_from_token = bytearray()
for i in range(32):
unsername_from_token.append(decoded_token[i] ^ ord(masterpassword[i]))
# remove padding
unsername_from_token = unsername_from_token.rstrip(':'.encode())
print("Username from token:", unsername_from_token.decode())
return unsername_from_token.decode() == username
if __name__ == "__main__":
token = input("Enter your token: ")
username = input("Enter your username: ")
masterpassword = input("Enter your masterpassword (32 characters): ")
if len(masterpassword) != 32:
print("Masterpassword must be 32 characters long")
exit(1)
if decode_token(username, token, masterpassword):
print("Token is valid")
else:
print("Token is invalid")

View File

@@ -46,7 +46,7 @@ public class Database {
}
}
public void addPlayer(String uuid, String mcusername, LdapUser user) {
public void addPlayer(String uuid, String mcusername, user user) {
String query = "insert into players values (?, ?, ?, ?, ?, ?, ?)";
try(PreparedStatement prep_query = this.statement.getConnection().prepareStatement(query)){
prep_query.setString(1, uuid);

View File

@@ -5,7 +5,7 @@ import javax.naming.directory.Attributes;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class LdapUser {
public class User {
public String firstName;
public String lastName;
public String email;
@@ -13,7 +13,16 @@ public class LdapUser {
public int gid;
public String ldapName;
public LdapUser(Attributes attributes) {
public User(String firstName, String lastName, String email, int uid, int gid, String ldapName) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.uid = uid;
this.gid = gid;
this.ldapName = ldapName;
}
public User(Attributes attributes) {
firstName = trimAttribute(attributes.get("givenname"));
lastName = trimAttribute(attributes.get("sn"));
email = trimAttribute(attributes.get("mail"));

View File

@@ -0,0 +1,8 @@
package de.sebastianvonhelmersen.authentication;
import de.sebastianvonhelmersen.User;
public interface Authenticator {
public boolean authenticate(String username, String token);
public User getUser(String username);
}

View File

@@ -0,0 +1,55 @@
package de.sebastianvonhelmersen.authentication;
import de.sebastianvonhelmersen.User;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
public class FetSite implements Authenticator {
private final byte[] masterPassword;
private final MessageDigest digest;
public FetSite(String masterPassword) {
this.masterPassword = masterPassword.getBytes(StandardCharsets.UTF_8);
// Setup hashing Class
try {
this.digest = MessageDigest.getInstance("SHA-256");
}
catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
/*
Token: base64(hash(<username>) XOR <masterpasswd>)
*/
public boolean authenticate(String username, String token) {
byte[] byteToken = token.getBytes(StandardCharsets.UTF_8);
// Hash username to make comparison
byte[] userNameHash = digest.digest(username.getBytes(StandardCharsets.UTF_8));
// Decode token
// Ensure both have the same length
int length = Math.min(byteToken.length, userNameHash.length);
byte[] result = new byte[length];
// XOR each byte
for (int i = 0; i < length; i++) {
result[i] = (byte) (byteToken[i] ^ this.masterPassword[i]);
}
byte[] dec_token = Base64.getDecoder().decode(result);
// Compare hash and decoded token
return Arrays.equals(dec_token, userNameHash);
}
public User getUser(String username) {
return new User("", "", "", 0, 0, username);
}
}

View File

@@ -1,4 +1,7 @@
package de.sebastianvonhelmersen;
package de.sebastianvonhelmersen.authentication;
import de.sebastianvonhelmersen.User;
import de.sebastianvonhelmersen.user;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
@@ -16,7 +19,7 @@ public class Ldap {
private static final String LDAP_URL = "ldap://juri:389";
private static final String BASE_DN = "dc=fet,dc=htu,dc=tuwien,dc=ac,dc=at";
private static final String ADMIN_DN = "cn=admin,dc=fet,dc=htu,dc=tuwien,dc=ac,dc=at"; // service account
public static LdapUser authenticate(String username, String password) {
public static User authenticate(String username, String password) {
try {
// 1. Bind as admin/service account
@@ -75,7 +78,7 @@ public class Ldap {
new InitialDirContext(authEnv).close(); // bind attempt
return new LdapUser(result.getAttributes()); // success
return new user(result.getAttributes()); // success
} catch (Exception e) {
e.printStackTrace();

View File

@@ -1,5 +1,8 @@
package de.sebastianvonhelmersen;
import de.sebastianvonhelmersen.authentication.Authenticator;
import de.sebastianvonhelmersen.authentication.FetSite;
import de.sebastianvonhelmersen.authentication.Ldap;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
@@ -23,11 +26,13 @@ import java.util.logging.Level;
public class fetmcplugin extends JavaPlugin implements Listener {
private Database db;
private Map<UUID, AuthInfos> allInfos = HashMap.newHashMap(0);
private Authenticator authenticator;
@Override
public void onEnable() {
// Make sure the plugin's data folder exists
File dataFolder = this.getDataFolder();
this.authenticator = new FetSite("MyokpBxpqxw9Eo8IduwH9IFSnBy4qII6");
if (!dataFolder.exists()) {
dataFolder.mkdirs();
@@ -145,10 +150,10 @@ public class fetmcplugin extends JavaPlugin implements Listener {
Component.text("§eBitte gib das Password deines FET Accounts ein!"));
break;
case AUTH_WAITING_PASSWORD:
LdapUser user = checkLdap(info.getUsername(), message.trim());
boolean valid = validate(info.getUsername(), message.trim());
event.setCancelled(true);
event.getRecipients().clear();
if(user != null) {
if(valid) {
allInfos.remove(uuid);
this.db.addPlayer(uuid.toString(), event.getPlayer().getName(), user);
Bukkit.getScheduler().runTask(this, () -> {
@@ -177,11 +182,16 @@ public class fetmcplugin extends JavaPlugin implements Listener {
return;
}
private LdapUser checkLdap(String username, String password) {
/*
* This function uses the defined Authenticator instance to authenticate the User against their
* provided username - token combination
* @param username the username used to authenticate the player with the provider
* @param token some sort of authentication methon. Can be Password, AppPassword or some other form of token the
* provider takes to authenticate the user
*/
private boolean validate(String username, String token) {
getLogger().log(Level.INFO, "Checking LDAP for user " + username);
return Ldap.authenticate(username, password);
//return true;
return authenticator.authenticate(username, token);
}
}

26
token.py Normal file
View File

@@ -0,0 +1,26 @@
# Function that generates a token depending on unsername and masterpassword
# The masterpassword must be 32 characters long!
def create_token(username, masterpassword):
if len(masterpassword) != 32:
print("Masterpassword must be 32 characters long")
exit(1)
padded_username = username.ljust(32, ':')
token = bytearray()
# xor connect with masterpassword to create token
for i in range(32):
token.append(ord(padded_username[i]) ^ ord(masterpassword[i]))
str_token = token.hex()
return str_token
if __name__ == "__main__":
# request username and masterpassword
username = input("Enter your username: ")
masterpassword = input("Enter your masterpassword (32 characters): ")
token = create_token(username, masterpassword)
print("Your token is: " + token)