Skip to content

Commit

Permalink
Add AuthMe integration
Browse files Browse the repository at this point in the history
  • Loading branch information
MrMicky-FR committed Mar 13, 2022
1 parent 333a9e0 commit 8dfb04c
Show file tree
Hide file tree
Showing 9 changed files with 213 additions and 10 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
allprojects {
group 'com.azuriom'
version '1.0.2'
version '1.1.0'
}

subprojects {
Expand Down
2 changes: 2 additions & 0 deletions bukkit/build.gradle
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
repositories {
maven { url 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' }
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
maven { url 'https://repo.codemc.io/repository/maven-public/' }
}

dependencies {
implementation project(':azlink-common')
compileOnly 'org.spigotmc:spigot-api:1.15.2-R0.1-SNAPSHOT'
compileOnly 'io.netty:netty-all:4.1.25.Final'
compileOnly 'fr.xephi:authme:5.6.0-beta2'
}

processResources {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.azuriom.azlink.bukkit.command.BukkitCommandExecutor;
import com.azuriom.azlink.bukkit.command.BukkitCommandSender;
import com.azuriom.azlink.bukkit.injector.InjectedHttpServer;
import com.azuriom.azlink.bukkit.integrations.AuthMeIntegration;
import com.azuriom.azlink.common.AzLinkPlatform;
import com.azuriom.azlink.common.AzLinkPlugin;
import com.azuriom.azlink.common.command.CommandSender;
Expand Down Expand Up @@ -72,6 +73,11 @@ protected HttpServer createHttpServer() {
getServer().getScheduler().runTaskTimer(this, this.tpsTask, 0, 1);

saveDefaultConfig();

if (getConfig().getBoolean("authme-integration")
&& getServer().getPluginManager().getPlugin("AuthMe") != null) {
getServer().getPluginManager().registerEvents(new AuthMeIntegration(this), this);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package com.azuriom.azlink.bukkit.integrations;

import com.azuriom.azlink.bukkit.AzLinkBukkitPlugin;
import fr.xephi.authme.api.v3.AuthMeApi;
import fr.xephi.authme.api.v3.AuthMePlayer;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.events.EmailChangedEvent;
import fr.xephi.authme.events.PasswordEncryptionEvent;
import fr.xephi.authme.events.RegisterEvent;
import fr.xephi.authme.security.crypts.EncryptionMethod;
import fr.xephi.authme.security.crypts.HashedPassword;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;

import java.io.IOException;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;

public class AuthMeIntegration implements Listener {

private final Map<String, String> passwords = new HashMap<>();

private final AzLinkBukkitPlugin plugin;
private DataSource dataSource;

public AuthMeIntegration(AzLinkBukkitPlugin plugin) {
this.plugin = plugin;

this.plugin.getLoggerAdapter().info("AuthMe integration enabled.");

this.plugin.getServer().getScheduler().runTask(this.plugin, () -> {
try {
Field field = AuthMeApi.class.getDeclaredField("dataSource");
field.setAccessible(true);

this.dataSource = (DataSource) field.get(AuthMeApi.getInstance());
} catch (ReflectiveOperationException e) {
throw new UnsupportedOperationException("Unable to register AuthMe integration", e);
}
});
}

@EventHandler(priority = EventPriority.HIGHEST)
public void onPasswordEncryption(PasswordEncryptionEvent event) {
event.setMethod(new ForwardingEncryptionMethod(event.getMethod()));
}

@EventHandler
public void onEmailChanged(EmailChangedEvent event) {
Player player = event.getPlayer();

runAsync(event, () -> {
try {
this.plugin.getPlugin().getHttpClient().updateEmail(player.getUniqueId(), event.getNewEmail());
} catch (IOException e) {
this.plugin.getLoggerAdapter().error("Unable to update email for " + player.getName(), e);
}
});
}

@EventHandler
public void onRegister(RegisterEvent event) {
Player player = event.getPlayer();
HashedPassword hashedPassword = this.dataSource.getPassword(event.getPlayer().getName());
String password = this.passwords.remove(hashedPassword.getHash());
String email = AuthMeApi.getInstance().getPlayerInfo(player.getName())
.flatMap(AuthMePlayer::getEmail)
.orElse(null);
InetSocketAddress address = player.getAddress();
InetAddress ip = address != null ? address.getAddress() : null;

if (password == null) {
this.plugin.getLoggerAdapter().warn("Unable to get password of " + player.getName());
return;
}

runAsync(event, () -> {
try {
this.plugin.getPlugin()
.getHttpClient()
.registerUser(player.getName(), email, player.getUniqueId(), password, ip);
} catch (IOException e) {
this.plugin.getLoggerAdapter().error("Unable to register " + player.getName(), e);
}
});
}

private void runAsync(Event event, Runnable runnable) {
if (event.isAsynchronous()) {
runnable.run();
return;
}

this.plugin.getSchedulerAdapter().executeAsync(runnable);
}

public class ForwardingEncryptionMethod implements EncryptionMethod {

private final EncryptionMethod method;

public ForwardingEncryptionMethod(EncryptionMethod method) {
this.method = method;
}

@Override
public HashedPassword computeHash(String password, String name) {
HashedPassword hash = this.method.computeHash(password, name);
AuthMeIntegration.this.passwords.put(hash.getHash(), password);
return hash;
}

@Override
public String computeHash(String password, String salt, String name) {
String hash = this.method.computeHash(password, salt, name);
AuthMeIntegration.this.passwords.put(hash, password);
return hash;
}

@Override
public boolean comparePassword(String password, HashedPassword hashedPassword, String name) {
return this.method.comparePassword(password, hashedPassword, name);
}

@Override
public String generateSalt() {
return this.method.generateSalt();
}

@Override
public boolean hasSeparateSalt() {
return this.method.hasSeparateSalt();
}

@Override
public boolean equals(Object o) {
return this.method.equals(o);
}

@Override
public int hashCode() {
return this.method.hashCode();
}
}
}
6 changes: 6 additions & 0 deletions bukkit/src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,9 @@
# included in players count and won't receive commands while vanished.
# Try disabling this option if you have some issues.
ignore-vanished-players: false

# When enabled, new users registered with the AuthMe plugin will automatically be registered on the website
# WARNING: You need AuthMeReloaded version 5.6.0-beta2 or newer AND Azuriom version 1.0.0 or newer!
# If you are using multiples serves, you should use the same database for AuthMe to prevent users
# from registering multiple times
authme-integration: false
1 change: 1 addition & 0 deletions bukkit/src/main/resources/plugin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ description: The plugin to link your Azuriom website with your server.
website: https://azuriom.com
main: com.azuriom.azlink.bukkit.AzLinkBukkitPlugin
api-version: 1.13
loadbefore: [AuthMe]
commands:
azlink:
description: Manage the AzLink plugin.
Expand Down
4 changes: 2 additions & 2 deletions common/build.gradle
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
dependencies {
implementation 'com.squareup.okhttp3:okhttp:3.14.9'

compileOnly 'org.slf4j:slf4j-api:1.7.32'
compileOnly 'com.google.code.gson:gson:2.8.9'
compileOnly 'org.slf4j:slf4j-api:1.7.36'
compileOnly 'com.google.code.gson:gson:2.9.0'
compileOnly 'io.netty:netty-all:4.1.25.Final'

testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ public void execute(CommandSender sender, String[] args) {
return;
}

setup(sender, args[1], args[2]);
plugin.getScheduler().executeAsync(() -> setup(sender, args[1], args[2]));
return;
}

if (args[0].equalsIgnoreCase("status")) {
showStatus(sender);
plugin.getScheduler().executeAsync(() -> showStatus(sender));
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.azuriom.azlink.common.AzLinkPlugin;
import com.azuriom.azlink.common.data.ServerData;
import com.azuriom.azlink.common.data.WebsiteResponse;
import com.google.gson.JsonObject;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
Expand All @@ -12,12 +13,16 @@

import java.io.BufferedReader;
import java.io.IOException;
import java.net.InetAddress;
import java.time.Duration;
import java.util.UUID;

public class HttpClient {

public static final MediaType JSON_TYPE = MediaType.parse("application/json; charset=utf-8");

private final OkHttpClient httpClient = new OkHttpClient.Builder()
.callTimeout(Duration.ofSeconds(5))
.addInterceptor(chain -> chain.proceed(addHeadersToRequest(chain.request())))
.build();

Expand All @@ -28,9 +33,35 @@ public HttpClient(AzLinkPlugin plugin) {
}

public void verifyStatus() throws IOException {
try (Response response = getStatus()) {
// success
}
makeCallAndClose(new Request.Builder().url(getSiteUrl()).build());
}

public void registerUser(String name, String email, UUID uuid, String password, InetAddress address)
throws IOException {
JsonObject body = new JsonObject();
body.addProperty("name", name);
body.addProperty("email", email);
body.addProperty("game_id", uuid.toString());
body.addProperty("password", password);
body.addProperty("ip", address != null ? address.getHostAddress() : null);

Request request = new Request.Builder().url(getSiteUrl() + "/register")
.post(RequestBody.create(JSON_TYPE, body.toString()))
.build();

makeCallAndClose(request);
}

public void updateEmail(UUID uuid, String email) throws IOException {
JsonObject body = new JsonObject();
body.addProperty("game_id", uuid.toString());
body.addProperty("email", email);

Request request = new Request.Builder().url(getSiteUrl() + "/email")
.post(RequestBody.create(JSON_TYPE, body.toString()))
.build();

makeCallAndClose(request);
}

public Response getStatus() throws IOException {
Expand All @@ -46,7 +77,7 @@ public WebsiteResponse postData(ServerData data) throws IOException {
ResponseBody body = response.body();

if (body == null) {
throw new RuntimeException("No body in response");
throw new IllegalStateException("No body in response");
}

try (BufferedReader reader = new BufferedReader(body.charStream())) {
Expand All @@ -55,6 +86,12 @@ public WebsiteResponse postData(ServerData data) throws IOException {
}
}

public void makeCallAndClose(Request request) throws IOException {
try (Response response = makeCall(request)) {
// ignore
}
}

public Response makeCall(Request request) throws IOException {
Response response = this.httpClient.newCall(request).execute();

Expand All @@ -72,7 +109,8 @@ public OkHttpClient getHttpClient() {

private Request addHeadersToRequest(Request request) {
return request.newBuilder()
.header("Authorization", "Bearer " + this.plugin.getConfig().getSiteKey())
.header("Accept", "application/json")
.header("Content-Type", "application/json")
.header("Azuriom-Link-Token", this.plugin.getConfig().getSiteKey())
.header("User-Agent", "AzLink v" + this.plugin.getPlatform().getPluginVersion())
.build();
Expand Down

0 comments on commit 8dfb04c

Please sign in to comment.