mirror of
https://github.com/GravitLauncher/Launcher
synced 2024-12-22 16:41:46 +03:00
[FEATURE] PasswordVerifier and MySQLCoreProvider
This commit is contained in:
parent
9da0ca8604
commit
d9f8b20a71
9 changed files with 271 additions and 2 deletions
|
@ -11,6 +11,7 @@
|
|||
import pro.gravit.launcher.request.auth.GetAvailabilityAuthRequest;
|
||||
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
|
||||
import pro.gravit.launchserver.auth.handler.AuthHandler;
|
||||
import pro.gravit.launchserver.auth.password.PasswordVerifier;
|
||||
import pro.gravit.launchserver.auth.protect.ProtectHandler;
|
||||
import pro.gravit.launchserver.auth.protect.hwid.HWIDProvider;
|
||||
import pro.gravit.launchserver.auth.provider.AuthProvider;
|
||||
|
@ -203,6 +204,7 @@ public static void initGson(LaunchServerModulesManager modulesManager) {
|
|||
@SuppressWarnings("deprecation")
|
||||
public static void registerAll() {
|
||||
AuthCoreProvider.registerProviders();
|
||||
PasswordVerifier.registerProviders();
|
||||
AuthHandler.registerHandlers();
|
||||
AuthProvider.registerProviders();
|
||||
TextureProvider.registerProviders();
|
||||
|
|
|
@ -24,6 +24,7 @@ public abstract class AuthCoreProvider implements AutoCloseable {
|
|||
public static void registerProviders() {
|
||||
if (!registredProviders) {
|
||||
providers.register("reject", RejectAuthCoreProvider.class);
|
||||
providers.register("mysql", MySQLCoreProvider.class);
|
||||
registredProviders = true;
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +34,7 @@ public static void registerProviders() {
|
|||
public abstract PasswordVerifyReport verifyPassword(User user, AuthRequest.AuthPasswordInterface password);
|
||||
public abstract void init(LaunchServer server);
|
||||
// Auth Handler methods
|
||||
protected abstract boolean updateAuth(User user) throws IOException;
|
||||
protected abstract boolean updateAuth(User user, String accessToken) throws IOException;
|
||||
protected abstract boolean updateServerID(User user, String serverID) throws IOException;
|
||||
|
||||
public List<GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails> getDetails(Client client) {
|
||||
|
|
|
@ -0,0 +1,185 @@
|
|||
package pro.gravit.launchserver.auth.core;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import pro.gravit.launcher.ClientPermissions;
|
||||
import pro.gravit.launcher.request.auth.AuthRequest;
|
||||
import pro.gravit.launcher.request.auth.password.AuthPlainPassword;
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.auth.AuthException;
|
||||
import pro.gravit.launchserver.auth.MySQLSourceConfig;
|
||||
import pro.gravit.launchserver.auth.handler.CachedAuthHandler;
|
||||
import pro.gravit.launchserver.auth.password.DigestPasswordVerifier;
|
||||
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.UUID;
|
||||
|
||||
public class MySQLCoreProvider extends AuthCoreProvider {
|
||||
private transient final Logger logger = LogManager.getLogger();
|
||||
public MySQLSourceConfig mySQLHolder;
|
||||
|
||||
public String uuidColumn;
|
||||
public String usernameColumn;
|
||||
public String accessTokenColumn;
|
||||
public String passwordColumn;
|
||||
public String serverIDColumn;
|
||||
public String table;
|
||||
public DigestPasswordVerifier passwordVerifier;
|
||||
|
||||
// Prepared SQL queries
|
||||
private transient String queryByUUIDSQL;
|
||||
private transient String queryByUsernameSQL;
|
||||
private transient String updateAuthSQL;
|
||||
private transient String updateServerIDSQL;
|
||||
|
||||
@Override
|
||||
public User getUserByUsername(String username) {
|
||||
try {
|
||||
return query(queryByUsernameSQL, username);
|
||||
} catch (IOException e) {
|
||||
logger.error("SQL error", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUserByUUID(UUID uuid) {
|
||||
try {
|
||||
return query(queryByUUIDSQL, uuid.toString());
|
||||
} catch (IOException e) {
|
||||
logger.error("SQL error", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verifyAuth(AuthResponse.AuthContext context) throws AuthException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public PasswordVerifyReport verifyPassword(User user, AuthRequest.AuthPasswordInterface password) {
|
||||
if(passwordVerifier.check(((MySQLUser)user).password, ((AuthPlainPassword)password).password)) {
|
||||
return new PasswordVerifyReport(true);
|
||||
} else {
|
||||
return PasswordVerifyReport.FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(LaunchServer server) {
|
||||
if (mySQLHolder == null) logger.error("mySQLHolder cannot be null");
|
||||
if (uuidColumn == null) logger.error("uuidColumn cannot be null");
|
||||
if (usernameColumn == null) logger.error("usernameColumn cannot be null");
|
||||
if (accessTokenColumn == null) logger.error("accessTokenColumn cannot be null");
|
||||
if (serverIDColumn == null) logger.error("serverIDColumn cannot be null");
|
||||
if (table == null) logger.error("table cannot be null");
|
||||
// Prepare SQL queries
|
||||
queryByUUIDSQL = String.format("SELECT %s, %s, %s, %s, %s FROM %s WHERE %s=? LIMIT 1",
|
||||
uuidColumn, usernameColumn, accessTokenColumn, serverIDColumn, passwordColumn, table, uuidColumn);
|
||||
queryByUsernameSQL = String.format("SELECT %s, %s, %s, %s, %s FROM %s WHERE %s=? LIMIT 1",
|
||||
uuidColumn, usernameColumn, accessTokenColumn, serverIDColumn, passwordColumn, table, usernameColumn);
|
||||
|
||||
updateAuthSQL = String.format("UPDATE %s SET %s=?, %s=NULL WHERE %s=? LIMIT 1",
|
||||
table, accessTokenColumn, serverIDColumn, uuidColumn);
|
||||
updateServerIDSQL = String.format("UPDATE %s SET %s=? WHERE %s=? LIMIT 1",
|
||||
table, serverIDColumn, uuidColumn);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean updateAuth(User user, String accessToken) throws IOException {
|
||||
try (Connection c = mySQLHolder.getConnection()) {
|
||||
PreparedStatement s = c.prepareStatement(updateAuthSQL);
|
||||
s.setString(1, accessToken);
|
||||
s.setString(2, user.getUUID().toString());
|
||||
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
|
||||
return s.executeUpdate() > 0;
|
||||
} catch (SQLException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean updateServerID(User user, String serverID) throws IOException {
|
||||
try (Connection c = mySQLHolder.getConnection()) {
|
||||
PreparedStatement s = c.prepareStatement(updateServerIDSQL);
|
||||
s.setString(1, serverID);
|
||||
s.setString(2, user.getUUID().toString());
|
||||
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
|
||||
return s.executeUpdate() > 0;
|
||||
} catch (SQLException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
mySQLHolder.close();
|
||||
}
|
||||
|
||||
private MySQLUser constructUser(ResultSet set) throws SQLException {
|
||||
return set.next() ? new MySQLUser(UUID.fromString(set.getString(uuidColumn)), set.getString(usernameColumn),
|
||||
set.getString(accessTokenColumn), set.getString(serverIDColumn), set.getString(passwordColumn), new ClientPermissions()) : null;
|
||||
}
|
||||
|
||||
private User query(String sql, String value) throws IOException {
|
||||
try (Connection c = mySQLHolder.getConnection()) {
|
||||
PreparedStatement s = c.prepareStatement(sql);
|
||||
s.setString(1, value);
|
||||
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
|
||||
try (ResultSet set = s.executeQuery()) {
|
||||
return constructUser(set);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static class MySQLUser implements User {
|
||||
private final UUID uuid;
|
||||
private final String username;
|
||||
private final String accessToken;
|
||||
private final String serverId;
|
||||
private final String password;
|
||||
private final ClientPermissions permissions;
|
||||
|
||||
public MySQLUser(UUID uuid, String username, String accessToken, String serverId, String password, ClientPermissions permissions) {
|
||||
this.uuid = uuid;
|
||||
this.username = username;
|
||||
this.accessToken = accessToken;
|
||||
this.serverId = serverId;
|
||||
this.password = password;
|
||||
this.permissions = permissions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUUID() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerId() {
|
||||
return serverId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAccessToken() {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientPermissions getPermissions() {
|
||||
return permissions;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -35,7 +35,7 @@ public void init(LaunchServer server) {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean updateAuth(User user) throws IOException {
|
||||
protected boolean updateAuth(User user, String accessToken) throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package pro.gravit.launchserver.auth.password;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import pro.gravit.utils.helper.SecurityHelper;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class DigestPasswordVerifier extends PasswordVerifier {
|
||||
private transient final Logger logger = LogManager.getLogger();
|
||||
public String algo;
|
||||
@Override
|
||||
public boolean check(String encryptedPassword, String password) {
|
||||
try {
|
||||
MessageDigest digest = MessageDigest.getInstance(algo);
|
||||
byte[] bytes = SecurityHelper.fromHex(encryptedPassword);
|
||||
return Arrays.equals(password.getBytes(StandardCharsets.UTF_8), digest.digest(bytes));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
logger.error("Digest algorithm {} not supported", algo);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package pro.gravit.launchserver.auth.password;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import pro.gravit.utils.helper.SecurityHelper;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class DoubleDigestPasswordVerifier extends PasswordVerifier {
|
||||
private transient final Logger logger = LogManager.getLogger();
|
||||
public String algo;
|
||||
public boolean toHexMode;
|
||||
@Override
|
||||
public boolean check(String encryptedPassword, String password) {
|
||||
try {
|
||||
MessageDigest digest = MessageDigest.getInstance(algo);
|
||||
byte[] bytes = SecurityHelper.fromHex(encryptedPassword);
|
||||
byte[] firstDigest = digest.digest(bytes);
|
||||
return Arrays.equals(password.getBytes(StandardCharsets.UTF_8), toHexMode ? digest.digest(SecurityHelper.toHex(firstDigest).getBytes(StandardCharsets.UTF_8)) : digest.digest(firstDigest));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
logger.error("Digest algorithm {} not supported", algo);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package pro.gravit.launchserver.auth.password;
|
||||
|
||||
import pro.gravit.utils.ProviderMap;
|
||||
|
||||
public abstract class PasswordVerifier {
|
||||
public static final ProviderMap<PasswordVerifier> providers = new ProviderMap<>("PasswordVerifier");
|
||||
private static boolean registeredProviders = false;
|
||||
public static void registerProviders() {
|
||||
if(!registeredProviders) {
|
||||
providers.register("plain", PlainPasswordVerifier.class);
|
||||
providers.register("digest", DigestPasswordVerifier.class);
|
||||
providers.register("doubleDigest", DoubleDigestPasswordVerifier.class);
|
||||
registeredProviders = true;
|
||||
}
|
||||
}
|
||||
public abstract boolean check(String encryptedPassword, String password);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package pro.gravit.launchserver.auth.password;
|
||||
|
||||
public class PlainPasswordVerifier extends PasswordVerifier {
|
||||
@Override
|
||||
public boolean check(String encryptedPassword, String password) {
|
||||
return encryptedPassword.equals(password);
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@
|
|||
import pro.gravit.launcher.request.auth.GetAvailabilityAuthRequest;
|
||||
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
|
||||
import pro.gravit.launchserver.auth.handler.AuthHandler;
|
||||
import pro.gravit.launchserver.auth.password.PasswordVerifier;
|
||||
import pro.gravit.launchserver.auth.protect.ProtectHandler;
|
||||
import pro.gravit.launchserver.auth.protect.hwid.HWIDProvider;
|
||||
import pro.gravit.launchserver.auth.provider.AuthProvider;
|
||||
|
@ -39,6 +40,7 @@ public void registerAdapters(GsonBuilder builder) {
|
|||
builder.registerTypeAdapter(TextureProvider.class, new UniversalJsonAdapter<>(TextureProvider.providers));
|
||||
builder.registerTypeAdapter(AuthHandler.class, new UniversalJsonAdapter<>(AuthHandler.providers));
|
||||
builder.registerTypeAdapter(AuthCoreProvider.class, new UniversalJsonAdapter<>(AuthCoreProvider.providers));
|
||||
builder.registerTypeAdapter(PasswordVerifier.class, new UniversalJsonAdapter<>(PasswordVerifier.providers));
|
||||
builder.registerTypeAdapter(Component.class, new UniversalJsonAdapter<>(Component.providers));
|
||||
builder.registerTypeAdapter(ProtectHandler.class, new UniversalJsonAdapter<>(ProtectHandler.providers));
|
||||
builder.registerTypeAdapter(DaoProvider.class, new UniversalJsonAdapter<>(DaoProvider.providers));
|
||||
|
|
Loading…
Reference in a new issue