Skip to content

feat: Add Minecraft Account linking endpoints. #10

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@ trim_trailing_whitespace = true # Gets rid of unnecessary indentation, and IJ ig
# Note: Turn on Smart Tabs!
ij_java_continuation_indent_size = 8 # Should be twice the tab width (8 for 4 columns, 16 for 8)
ij_json_continuation_indent_size = 4 # Should be the tab size
ij_toml_continuation_indent_size = 4 # Should be the tab size
ij_toml_continuation_indent_size = 4 # Should be the tab size

[*.key]
insert_final_newline = false # Don't do this for any publickeys.
3 changes: 3 additions & 0 deletions src/main/java/net/modgarden/backend/ModGardenBackend.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.net.http.HttpClient;
import java.security.NoSuchAlgorithmException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
Expand Down Expand Up @@ -108,6 +109,7 @@ public static void main(String[] args) {
app.error(422, BackendError::handleError);
app.error(500, BackendError::handleError);
app.start(7070);

LOG.info("Mod Garden Backend Started!");
}

Expand Down Expand Up @@ -138,6 +140,7 @@ public static void v1(Javalin app) {

get(app, 1, "discord/oauth/modrinth", DiscordBotOAuthHandler::authModrinthAccount);
get(app, 1, "discord/oauth/minecraft", DiscordBotOAuthHandler::authMinecraftAccount);
get(app, 1, "discord/oauth/minecraft/challenge", DiscordBotOAuthHandler::getMicrosoftCodeChallenge);

post(app, 1, "discord/submission/create/modrinth", DiscordBotSubmissionHandler::submitModrinth);
post(app, 1, "discord/submission/delete", DiscordBotSubmissionHandler::unsubmit);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
package net.modgarden.backend.handler.v1.discord;

import com.mojang.serialization.Codec;
import com.mojang.serialization.JsonOps;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import io.javalin.http.Context;
import net.modgarden.backend.ModGardenBackend;
import net.modgarden.backend.data.LinkCode;
import net.modgarden.backend.data.profile.User;
import net.modgarden.backend.util.ExtraCodecs;

import java.math.BigInteger;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.UUID;

public class DiscordBotLinkHandler {
public static void link(Context ctx) {
Expand Down Expand Up @@ -49,19 +43,20 @@ public static void link(Context ctx) {
deleteStatement.execute();
if (accountId == null) {
ctx.result("Invalid link code for " + capitalisedService + ".");
ctx.status(422);
ctx.status(400);
return;
}

if (body.service.equals(LinkCode.Service.MODRINTH.serializedName())) {
handleModrinth(ctx, connection, body.discordId, accountId, capitalisedService);
handleModrinth(ctx, connection, body.discordId, accountId);
return;
} else if (body.service.equals(LinkCode.Service.MINECRAFT.serializedName())) {
handleMinecraft(ctx, connection, body.discordId, accountId, capitalisedService);
handleMinecraft(ctx, connection, body.discordId, accountId);
DiscordBotOAuthHandler.invalidateFromUuid(body.linkCode);
return;
}
ctx.result("Invalid link code service '" + capitalisedService + "'.");
ctx.status(422);
ctx.status(400);
} catch (SQLException ex) {
ModGardenBackend.LOG.error("Exception in SQL query.", ex);
ctx.result("Internal Error.");
Expand All @@ -72,78 +67,66 @@ public static void link(Context ctx) {
private static void handleModrinth(Context ctx,
Connection connection,
String discordId,
String accountId,
String capitalisedService) throws SQLException {
String accountId) throws SQLException {
try (var accountCheckStatement = connection.prepareStatement("SELECT 1 FROM users WHERE modrinth_id = ?");
var userCheckStatement = connection.prepareStatement("SELECT 1 FROM users WHERE discord_id = ? AND modrinth_id IS NOT NULL");
var insertStatement = connection.prepareStatement("UPDATE users SET modrinth_id = ? WHERE discord_id = ?")) {
accountCheckStatement.setString(1, accountId);
ResultSet accountCheckResult = accountCheckStatement.executeQuery();
if (accountCheckResult.isBeforeFirst() && accountCheckResult.getBoolean(1)) {
ctx.result("The specified " + capitalisedService + " account has already been linked to a Mod Garden account.");
ctx.status(422);
ctx.result("The specified Modrinth account has already been linked to a Mod Garden account.");
ctx.status(400);
return;
}

userCheckStatement.setString(1, discordId);
ResultSet userCheckResult = userCheckStatement.executeQuery();
if (userCheckResult.isBeforeFirst() && userCheckResult.getBoolean(1)) {
ctx.result("The specified Mod Garden account is already linked with " + capitalisedService + ".");
ctx.status(422);
ctx.result("The specified Mod Garden account is already linked with Modrinth.");
ctx.status(400);
return;
}

insertStatement.setString(1, accountId);
insertStatement.setString(2, discordId);
insertStatement.execute();

ctx.result("Successfully linked " + capitalisedService + " account to Mod Garden account associated with Discord ID '" + discordId + "'.");
ctx.result("Successfully linked Modrinth account to Mod Garden account associated with Discord ID '" + discordId + "'.");
ctx.status(201);
}
}

private static void handleMinecraft(Context ctx,
Connection connection,
String discordId,
String uuid,
String capitalisedService) throws SQLException {
try (var accountCheckStatement = connection.prepareStatement("SELECT 1 FROM users WHERE instr(minecraft_accounts, ?) > 0");
var insertStatement = connection.prepareStatement("UPDATE users SET minecraft_accounts = ? WHERE discord_id = ?")) {
accountCheckStatement.setString(1, uuid);
ResultSet accountCheckResult = accountCheckStatement.executeQuery();
if (accountCheckResult.isBeforeFirst() && accountCheckResult.getBoolean(1)) {
ctx.result("The specified " + capitalisedService + " account has already been linked to a Mod Garden account.");
ctx.status(422);
return;
}

Connection connection,
String discordId,
String uuid) throws SQLException {
try (var accountCheckStatement = connection.prepareStatement("SELECT user_id FROM minecraft_accounts WHERE uuid = ?");
var insertStatement = connection.prepareStatement("INSERT INTO minecraft_accounts (uuid, user_id) VALUES (?, ?)")) {
User user = User.query(discordId, "discord");
if (user == null) {
ctx.result("Could not find user from Discord ID '" + discordId + "'.");
ctx.status(422);
ctx.status(400);
return;
}

List<UUID> uuids = new ArrayList<>(user.minecraftAccounts());
uuids.add(new UUID(
new BigInteger(uuid.substring(0, 16), 16).longValue(),
new BigInteger(uuid.substring(16), 16).longValue()
));

var dataResult = ExtraCodecs.UUID_CODEC.listOf().encodeStart(JsonOps.INSTANCE, uuids);

if (!dataResult.hasResultOrPartial()) {
ModGardenBackend.LOG.error("Failed to create Minecraft account data. {}", dataResult.error().orElseThrow().message());
ctx.result("Failed to create Minecraft account data.");
ctx.status(500);
accountCheckStatement.setString(1, uuid);
ResultSet accountCheckResult = accountCheckStatement.executeQuery();
if (accountCheckResult.isBeforeFirst() && accountCheckResult.getString(1) != null) {
if (accountCheckResult.getString(1).equals(user.id())) {
ctx.result("Your Minecraft account is already linked to your Mod Garden account.");
ctx.status(200);
return;
}
ctx.result("The specified Minecraft account has already been linked to a Mod Garden account.");
ctx.status(400);
return;
}

accountCheckStatement.setString(1, dataResult.getOrThrow().toString());
accountCheckStatement.setString(2, discordId);
insertStatement.setString(1, uuid);
insertStatement.setString(2, user.id());
insertStatement.execute();

ctx.result("Successfully linked " + capitalisedService + " account to Mod Garden account associated with Discord ID '" + discordId + "'.");
ctx.result("Successfully linked Minecraft account to Mod Garden account associated with Discord ID '" + discordId + "'.");
ctx.status(201);
}
}
Expand Down
Loading