Skip to content

Commit

Permalink
feat: More security things
Browse files Browse the repository at this point in the history
  • Loading branch information
VidTu committed Feb 8, 2024
1 parent 81c17b2 commit acfce00
Show file tree
Hide file tree
Showing 16 changed files with 209 additions and 60 deletions.
3 changes: 3 additions & 0 deletions .ias/disabled_v1
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# This file will contain any versions with issues critical enough
# to prohibit this mod from working, one version per line.
# Special case: If any of lines is equal to "ALL", then all versions will be disabled.
11 changes: 4 additions & 7 deletions 1.20.4/src/main/java/ru/vidtu/ias/IASMinecraft.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
import ru.vidtu.ias.account.Account;
import ru.vidtu.ias.config.IASConfig;
import ru.vidtu.ias.mixins.MinecraftAccessor;
import ru.vidtu.ias.screen.AccountsScreen;
import ru.vidtu.ias.screen.AccountScreen;
import ru.vidtu.ias.utils.Expression;
import ru.vidtu.ias.utils.IUtils;

Expand Down Expand Up @@ -123,10 +123,7 @@ public static void init(Path gameDir, Path configDir, String loader, String modV
LOGGER.info("IAS: Booting up... (version: {}, loader: {}, loader version: {}, game version: {})", modVersion, loader, loaderVersion, gameVersion);

// Initialize the IAS.
IAS.init(gameDir, configDir);

// Set the UA.
IAS.userAgent(modVersion, loader, loaderVersion, gameVersion);
IAS.init(gameDir, configDir, modVersion, loader, loaderVersion, gameVersion);
}

/**
Expand Down Expand Up @@ -175,7 +172,7 @@ public static void onInit(Minecraft minecraft, Screen screen, Consumer<Button> b
}

// Add the button.
ImageButton button = new ImageButton(x, y, 20, 20, BUTTON, btn -> minecraft.setScreen(new AccountsScreen(screen)), Component.literal("In-Game Account Switcher"));
ImageButton button = new ImageButton(x, y, 20, 20, BUTTON, btn -> minecraft.setScreen(new AccountScreen(screen)), Component.literal("In-Game Account Switcher"));
button.setTooltip(Tooltip.create(button.getMessage()));
button.setTooltipDelay(250);
buttonAdder.accept(button);
Expand Down Expand Up @@ -219,7 +216,7 @@ public static void onInit(Minecraft minecraft, Screen screen, Consumer<Button> b
}

// Add the button.
ImageButton button = new ImageButton(x, y, 20, 20, BUTTON, btn -> minecraft.setScreen(new AccountsScreen(screen)), Component.literal("In-Game Account Switcher"));
ImageButton button = new ImageButton(x, y, 20, 20, BUTTON, btn -> minecraft.setScreen(new AccountScreen(screen)), Component.literal("In-Game Account Switcher"));
button.setTooltip(Tooltip.create(button.getMessage()));
button.setTooltipDelay(250);
buttonAdder.accept(button);
Expand Down
6 changes: 3 additions & 3 deletions 1.20.4/src/main/java/ru/vidtu/ias/screen/AccountList.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ final class AccountList extends ObjectSelectionList<AccountEntry> {
/**
* Parent screen.
*/
private final AccountsScreen screen;
private final AccountScreen screen;

/**
* Creates a new accounts list widget.
Expand All @@ -68,7 +68,7 @@ final class AccountList extends ObjectSelectionList<AccountEntry> {
* @param offset List Y offset
* @param item Entry height
*/
AccountList(AccountsScreen screen, Minecraft minecraft, int width, int height, int offset, int item) {
AccountList(AccountScreen screen, Minecraft minecraft, int width, int height, int offset, int item) {
super(minecraft, width, height, offset, item);
this.screen = screen;
this.update(this.screen.search().getValue());
Expand Down Expand Up @@ -387,7 +387,7 @@ void swapDown(AccountEntry entry) {
*
* @return Parent accounts screen
*/
AccountsScreen screen() {
AccountScreen screen() {
return this.screen;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,24 @@
import net.minecraft.client.gui.components.PlayerSkinWidget;
import net.minecraft.client.gui.components.Tooltip;
import net.minecraft.client.gui.navigation.CommonInputs;
import net.minecraft.client.gui.screens.AlertScreen;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.resources.DefaultPlayerSkin;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.network.chat.Component;
import org.lwjgl.glfw.GLFW;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.vidtu.ias.IAS;
import ru.vidtu.ias.account.Account;
import ru.vidtu.ias.config.IASStorage;

public final class AccountScreen extends Screen {
/**
* Logger for this class.
*/
private static final Logger LOGGER = LoggerFactory.getLogger("IAS/AccountScreen");

public final class AccountsScreen extends Screen {
/**
* Parent screen, {@code null} if none.
*/
Expand Down Expand Up @@ -80,7 +90,7 @@ public final class AccountsScreen extends Screen {
*
* @param parent Parent screen, {@code null} if none
*/
public AccountsScreen(Screen parent) {
public AccountScreen(Screen parent) {
super(Component.translatable("ias.accounts"));
this.parent = parent;
}
Expand All @@ -90,6 +100,30 @@ protected void init() {
// Bruh.
assert this.minecraft != null;

// Disabled check.
if (IAS.disabled()) {
this.minecraft.setScreen(new AlertScreen(this::onClose, Component.translatable("ias.disabled.title").withStyle(ChatFormatting.RED),
Component.translatable("ias.disabled.text"), CommonComponents.GUI_BACK, true));
return;
}

// Disclaimer.
if (!IASStorage.gameDisclaimerShown) {
this.minecraft.setScreen(new AlertScreen(() -> {
// Save disclaimer.
try {
IAS.gameDisclaimerShownStorage();
} catch (Throwable t) {
LOGGER.error("Unable to set or write game disclaimer state.", t);
}

// Set screen.
this.minecraft.setScreen(this);
}, Component.translatable("ias.disclaimer.title").withStyle(ChatFormatting.YELLOW),
Component.translatable("ias.disclaimer.text"), CommonComponents.GUI_CONTINUE, false));
return;
}

// Add search widget.
this.search = new EditBox(this.font, this.width / 2 - 75, 11, 150, 20, this.search, Component.translatable("ias.accounts.search"));
this.search.setHint(this.search.getMessage().copy().withStyle(ChatFormatting.DARK_GRAY));
Expand Down Expand Up @@ -287,7 +321,7 @@ public boolean keyPressed(int key, int scan, int mods) {

@Override
public String toString() {
return "AccountsScreen{" +
return "AccountScreen{" +
"list=" + this.list +
'}';
}
Expand Down
8 changes: 8 additions & 0 deletions 1.20.4/src/main/java/ru/vidtu/ias/screen/ConfigScreen.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import net.minecraft.client.gui.components.Checkbox;
import net.minecraft.client.gui.components.EditBox;
import net.minecraft.client.gui.components.Tooltip;
import net.minecraft.client.gui.screens.AlertScreen;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.resources.sounds.SimpleSoundInstance;
import net.minecraft.network.chat.CommonComponents;
Expand Down Expand Up @@ -121,6 +122,13 @@ protected void init() {
// Bruh.
assert this.minecraft != null;

// Disabled check.
if (IAS.disabled()) {
this.minecraft.setScreen(new AlertScreen(this::onClose, Component.translatable("ias.disabled.title").withStyle(ChatFormatting.RED),
Component.translatable("ias.disabled.text"), CommonComponents.GUI_BACK, true));
return;
}

// Title Text.
Checkbox box = Checkbox.builder(Component.translatable("ias.config.titleText"), this.font)
.pos(5, 20)
Expand Down
29 changes: 14 additions & 15 deletions 1.20.4/src/main/java/ru/vidtu/ias/screen/MicrosoftPopupScreen.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.ChatFormatting;
import net.minecraft.Util;
import net.minecraft.client.KeyboardHandler;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.MultiLineLabel;
Expand All @@ -42,6 +43,7 @@
import ru.vidtu.ias.crypt.PasswordCrypt;
import ru.vidtu.ias.utils.ProbableException;

import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
Expand Down Expand Up @@ -221,6 +223,18 @@ public void onClose() {

@Override
public void removed() {
// Bruh.
assert this.minecraft != null;

// Check if IAS ID is in clipboard.
KeyboardHandler keyboard = this.minecraft.keyboardHandler;
String clipboard = keyboard.getClipboard();

// Null if it is.
if (clipboard.toLowerCase(Locale.ROOT).contains(IAS.CLIENT_ID.toLowerCase(Locale.ROOT))) {
keyboard.setClipboard(" ");
}

// Close the server, if any.
IAS.executor().execute(() -> {
if (this.server != null) {
Expand Down Expand Up @@ -335,21 +349,6 @@ public void success(MicrosoftAccount account) {
// Skip if not current screen.
if (this != this.minecraft.screen) return;

// User cancelled.
if (account == null) {
// Schedule on main.
this.minecraft.execute(() -> {
// Skip if not current screen.
if (this != this.minecraft.screen) return;

// Back to parent screen.
this.minecraft.setScreen(this.parent);
});

// Don't log in.
return;
}

// Write disclaimers.
this.stage(MicrosoftAccount.FINALIZING);

Expand Down
6 changes: 6 additions & 0 deletions docs/TERMS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

These are the terms provided of the In-Game Account Switcher mod.

Last update: **February 8, 2024** (UTC)

## Mod

This mod is redistributed under [GNU LGPLv3](../LICENSE) and its terms apply to the mod usage and distribution.
Expand All @@ -22,3 +24,7 @@ and Microsoft's [Terms of Use](https://www.microsoft.com/en-us/legal/terms-of-us
and [Services Agreement](https://www.microsoft.com/en-us/servicesagreement) apply.
By using this mod, you agree to the Microsoft Terms of Use, Microsoft Privacy Statement,
and Microsoft Usage Guidelines.

## GitHub

This mod connects to the GitHub approximately every two hours.
89 changes: 77 additions & 12 deletions src/main/java/ru/vidtu/ias/IAS.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,18 @@
import ru.vidtu.ias.config.IASConfig;
import ru.vidtu.ias.config.IASStorage;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;

/**
* Main IAS class.
Expand Down Expand Up @@ -78,6 +84,12 @@ public final class IAS {
*/
private static Path configDirectory;

/**
* Whether the mod is disabled remotely.
*/
@SuppressWarnings("NegativelyNamedBooleanVariable") // <- The negative naming is intended.
private static boolean disabled = false;

/**
* An instance of this class cannot be created.
*
Expand All @@ -90,14 +102,22 @@ private IAS() {
/**
* Initializes the IAS.
*
* @param gamePath Game directory
* @param configPath Config directory
* @param gamePath Game directory
* @param configPath Config directory
* @param version Mod version
* @param loader Mod loader
* @param loaderVersion Mod loader version
* @param gameVersion Game version
*/
public static void init(Path gamePath, Path configPath) {
public static void init(Path gamePath, Path configPath, String version, String loader, String loaderVersion, String gameVersion) {
// Initialize the dirs.
gameDirectory = gamePath;
configDirectory = configPath;

// Set up IAS.
userAgent = USER_AGENT_TEMPLATE.formatted(version, SESSION, loader, loaderVersion, gameVersion, Runtime.version().toString());
LOGGER.info("IAS user agent: {}", userAgent);

// Write the disclaimers.
try {
disclaimersStorage();
Expand All @@ -121,6 +141,42 @@ public static void init(Path gamePath, Path configPath) {

// Create the executor.
executor = Executors.newSingleThreadScheduledExecutor(r -> new Thread(r, "IAS Executor Thread"));

// Perform initial loading.
if (!Boolean.getBoolean("ias.skipDisableScanning")) return;
executor.execute(() -> {
// Perform scanning, if allowed.
try {
// Create the client.
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10L))
.version(HttpClient.Version.HTTP_2)
.followRedirects(HttpClient.Redirect.NORMAL)
.executor(Runnable::run)
.priority(1)
.build();

// Send the request.
HttpResponse<Stream<String>> response = client.send(HttpRequest.newBuilder(new URI("https://raw.githubusercontent.com/The-Fireplace-Minecraft-Mods/In-Game-Account-Switcher/main/.ias/disabled_v1"))
.header("User-Agent", userAgent)
.timeout(Duration.ofSeconds(10L))
.GET()
.build(), HttpResponse.BodyHandlers.ofLines());

// Validate the code.
int code = response.statusCode();
if (code < 200 || code > 299) return;

// Check the lines.
disabled = response.body().anyMatch(line -> {
line = line.strip();
if ("ALL".equalsIgnoreCase(line)) return true;
return version.equalsIgnoreCase(line);
});
} catch (Throwable ignored) {
// NO-OP
}
});
}

/**
Expand Down Expand Up @@ -149,7 +205,11 @@ public static void close() {

// Write the disclaimers, if we can.
if (gameDirectory != null) {
disclaimersStorage();
try {
disclaimersStorage();
} catch (Throwable ignored) {
// NO-OP
}
}
}

Expand All @@ -176,16 +236,12 @@ public static String userAgent() {
}

/**
* Sets the user agent for usage in {@link MSAuth}.
* Gets the disabled state.
*
* @param version Mod version
* @param loader Mod loader
* @param loaderVersion Mod loader version
* @param gameVersion Game version
* @return Whether the mod is disabled remotely
*/
public static void userAgent(String version, String loader, String loaderVersion, String gameVersion) {
userAgent = USER_AGENT_TEMPLATE.formatted(version, SESSION, loader, loaderVersion, gameVersion, Runtime.version().toString());
LOGGER.info("IAS user agent: {}", userAgent);
public static boolean disabled() {
return disabled;
}

/**
Expand Down Expand Up @@ -232,4 +288,13 @@ public static void saveStorage() {
public static void disclaimersStorage() {
IASStorage.disclaimers(gameDirectory);
}

/**
* Delegates to {@link IASStorage#gameDisclaimerShown(Path)} with {@link #gameDirectory}.
*
* @throws RuntimeException If unable to set or write game disclaimer shown persistent state
*/
public static void gameDisclaimerShownStorage() {
IASStorage.gameDisclaimerShown(gameDirectory);
}
}
Loading

0 comments on commit acfce00

Please sign in to comment.