Fixes incorrect migration of legacy encrypted books
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good

This commit is contained in:
2025-08-22 00:11:29 +02:00
parent cbe3a977ac
commit e342f4cff5
5 changed files with 60 additions and 32 deletions

View File

@@ -41,7 +41,7 @@ public class CommandGroupEncrypt extends CommandEncrypt implements TabExecutor {
return false; return false;
} }
EncryptionStyle encryptionStyle = arguments.length == 3 ? EncryptionStyle.getFromString(arguments[2]) : EncryptionStyle.SUBSTITUTION; EncryptionStyle encryptionStyle = arguments.length == 3 ? EncryptionStyle.getFromString(arguments[2]) : EncryptionStyle.AES;
return encryptBook(encryptionStyle, player, arguments[1], arguments[0], false); return encryptBook(encryptionStyle, player, arguments[1], arguments[0], false);
} }

View File

@@ -28,7 +28,9 @@ public enum StaticMessage {
WARNING_USER_BOOK_MIGRATION_IMPOSSIBLE("Unable to migrate player book directory for player {player}"), WARNING_USER_BOOK_MIGRATION_IMPOSSIBLE("Unable to migrate player book directory for player {player}"),
EXCEPTION_BOOK_LOAD_NULL("Text file's first line was null"), EXCEPTION_BOOK_LOAD_NULL("Text file's first line was null"),
EXCEPTION_BOOK_LOAD_FAILED("Unable to read text file"), EXCEPTION_BOOK_LOAD_FAILED("Unable to read text file"),
EXCEPTION_BOOK_UNKNOWN_EXTENSION("Trying to load a book file with an unrecognized extension"); EXCEPTION_BOOK_UNKNOWN_EXTENSION("Trying to load a book file with an unrecognized extension"),
EXCEPTION_UNEXPECTED_ENCRYPTED_BOOK("Attempted to load a normal book, but found an encrypted book instead"),
;
private final @NotNull String messageString; private final @NotNull String messageString;

View File

@@ -95,16 +95,23 @@ public class MigrationQueueThread implements Runnable {
EncryptedBook encryptedBook = null; EncryptedBook encryptedBook = null;
BookMeta loadedBook = null; BookMeta loadedBook = null;
String extension = BookFileHelper.getExtensionFromPath(file.getName()); String extension = BookFileHelper.getExtensionFromPath(file.getName());
if (!extension.equalsIgnoreCase("txt") && !extension.equalsIgnoreCase("yml")) {
BooksWithoutBorders.log(Level.WARNING, "File with unexpected extension " + extension + " encountered!");
return true;
}
// Attempt loading an encrypted book if necessary
if (file.getAbsolutePath().contains("Books" + slash + "Encrypted")) { if (file.getAbsolutePath().contains("Books" + slash + "Encrypted")) {
encryptedBook = BookToFromTextHelper.encryptedBookFromYml(file, bookMeta, "", true); encryptedBook = BookToFromTextHelper.encryptedBookFromYml(file, bookMeta, "", true, true);
if (encryptedBook != null) { if (encryptedBook != null) {
loadedBook = encryptedBook.bookMeta(); loadedBook = encryptedBook.bookMeta();
} }
} else if (extension.equalsIgnoreCase("txt") || extension.equalsIgnoreCase("yml")) { }
// Attempt to load the book normally
if (loadedBook == null) {
loadedBook = BookToFromTextHelper.bookFromFile(file, bookMeta); loadedBook = BookToFromTextHelper.bookFromFile(file, bookMeta);
} else {
BooksWithoutBorders.log(Level.WARNING, "File with unexpected extension " + extension + " encountered!");
return true;
} }
if (loadedBook == null) { if (loadedBook == null) {

View File

@@ -44,7 +44,7 @@ public final class BookToFromTextHelper {
if (file.getName().endsWith(".txt")) { if (file.getName().endsWith(".txt")) {
return bookFromTXT(file, bookMetadata); return bookFromTXT(file, bookMetadata);
} else if (file.getName().endsWith(".yml")) { } else if (file.getName().endsWith(".yml")) {
return bookFromYml(file, bookMetadata); return bookFromYml(file, bookMetadata, false);
} else { } else {
throw new IllegalArgumentException(StaticMessage.EXCEPTION_BOOK_UNKNOWN_EXTENSION.toString()); throw new IllegalArgumentException(StaticMessage.EXCEPTION_BOOK_UNKNOWN_EXTENSION.toString());
} }
@@ -100,7 +100,8 @@ public final class BookToFromTextHelper {
} }
// Make sure the plaintext cannot simply be seen in the file // Make sure the plaintext cannot simply be seen in the file
if (BooksWithoutBorders.getConfiguration().useRealEncryption()) { if (BooksWithoutBorders.getConfiguration().useRealEncryption() &&
encryptedBook.encryptionStyle() == EncryptionStyle.AES) {
bookYml.set("Pages", null); bookYml.set("Pages", null);
} }
@@ -110,19 +111,21 @@ public final class BookToFromTextHelper {
/** /**
* Loads a book from a .yml file * Loads a book from a .yml file
* *
* @param file <p>The path of the file to load</p> * @param file <p>The path of the file to load</p>
* @param bookMetadata <p>Metadata which will be altered with the book's contents</p> * @param bookMetadata <p>Metadata which will be altered with the book's contents</p>
* @param userKey <p>The user-supplied decryption key</p> * @param userKey <p>The user-supplied decryption key</p>
* @param forceDecrypt <p>Whether to use the saved key for decryption, ignoring the supplied key</p> * @param forceDecrypt <p>Whether to use the saved key for decryption, ignoring the supplied key</p>
* @param loadEncrypted <p>Whether to load the book in its encrypted form, even if it cannot be admin decrypted</p>
* @return <p>Metadata for the loaded book</p> * @return <p>Metadata for the loaded book</p>
*/ */
@Nullable @Nullable
public static EncryptedBook encryptedBookFromYml(@NotNull File file, @NotNull BookMeta bookMetadata, public static EncryptedBook encryptedBookFromYml(@NotNull File file, @NotNull BookMeta bookMetadata,
@NotNull String userKey, boolean forceDecrypt) { @NotNull String userKey, boolean forceDecrypt,
boolean loadEncrypted) {
BookMeta meta; BookMeta meta;
try { try {
meta = bookFromYml(file, bookMetadata); meta = bookFromYml(file, bookMetadata, true);
if (meta == null) { if (meta == null) {
return null; return null;
} }
@@ -146,8 +149,12 @@ public final class BookToFromTextHelper {
List<String> data = bookYml.getStringList("Encryption.Data"); List<String> data = bookYml.getStringList("Encryption.Data");
EncryptionStyle encryptionStyle = EncryptionStyle.getFromString(bookYml.getString("Encryption.Style", // If encryption style is null, that means the book was not encrypted in a way this can decrypt
EncryptionStyle.SUBSTITUTION.toString())); String encryptionStyleName = bookYml.getString("Encryption.Style");
if (encryptionStyleName == null) {
return null;
}
EncryptionStyle encryptionStyle = EncryptionStyle.getFromString(encryptionStyleName);
AESConfiguration aesConfiguration = null; AESConfiguration aesConfiguration = null;
if (encryptionStyle == EncryptionStyle.AES) { if (encryptionStyle == EncryptionStyle.AES) {
@@ -174,11 +181,12 @@ public final class BookToFromTextHelper {
userKey, false); userKey, false);
if (decryptedPages != null && !decryptedPages.isEmpty()) { if (decryptedPages != null && !decryptedPages.isEmpty()) {
meta.setPages(decryptedPages); meta.setPages(decryptedPages);
return encryptedBook;
} else if (loadEncrypted) {
return encryptedBook;
} else { } else {
return null; return null;
} }
return encryptedBook;
} }
/** /**
@@ -218,25 +226,36 @@ public final class BookToFromTextHelper {
* *
* @param file <p>The path of the file to load</p> * @param file <p>The path of the file to load</p>
* @param bookMetadata <p>Metadata which will be altered with the book's contents</p> * @param bookMetadata <p>Metadata which will be altered with the book's contents</p>
* @param isEncrypted <p>Whether the book is expected to be encrypted</p>
* @return <p>Metadata for the loaded book</p> * @return <p>Metadata for the loaded book</p>
*/ */
@Nullable @Nullable
private static BookMeta bookFromYml(@NotNull File file, @NotNull BookMeta bookMetadata) { private static BookMeta bookFromYml(@NotNull File file, @NotNull BookMeta bookMetadata, boolean isEncrypted) {
StringFormatter stringFormatter = BooksWithoutBorders.getStringFormatter(); StringFormatter stringFormatter = BooksWithoutBorders.getStringFormatter();
try {
FileConfiguration bookYml = YamlConfiguration.loadConfiguration(file);
bookMetadata.setGeneration(BookMeta.Generation.valueOf(bookYml.getString("Generation", "ORIGINAL"))); FileConfiguration bookYml = YamlConfiguration.loadConfiguration(file);
bookMetadata.setTitle(bookYml.getString("Title",
stringFormatter.getUnFormattedColoredMessage(Formatting.NEUTRAL_UNKNOWN_TITLE))); // Don't allow encrypted books to be loaded unless known to be encrypted
bookMetadata.setAuthor(authorFromUUID(bookYml.getString("Author", String encryptionStyleName = bookYml.getString("Encryption.Style");
stringFormatter.getUnFormattedColoredMessage(Formatting.NEUTRAL_UNKNOWN_AUTHOR)))); if (!isEncrypted && encryptionStyleName != null) {
bookMetadata.setPages(bookYml.getStringList("Pages")); BooksWithoutBorders.log(Level.WARNING, StaticMessage.EXCEPTION_UNEXPECTED_ENCRYPTED_BOOK.toString());
bookMetadata.setLore(bookYml.getStringList("Lore"));
bookMetadata.setDisplayName(bookYml.getString("DisplayName"));
} catch (IllegalArgumentException exception) {
return null; return null;
} }
try {
bookMetadata.setGeneration(BookMeta.Generation.valueOf(bookYml.getString("Generation", "ORIGINAL")));
} catch (IllegalArgumentException exception) {
BooksWithoutBorders.log(Level.FINE, exception.getMessage());
return null;
}
bookMetadata.setTitle(bookYml.getString("Title",
stringFormatter.getUnFormattedColoredMessage(Formatting.NEUTRAL_UNKNOWN_TITLE)));
bookMetadata.setAuthor(authorFromUUID(bookYml.getString("Author",
stringFormatter.getUnFormattedColoredMessage(Formatting.NEUTRAL_UNKNOWN_AUTHOR))));
bookMetadata.setPages(bookYml.getStringList("Pages"));
bookMetadata.setLore(bookYml.getStringList("Lore"));
bookMetadata.setDisplayName(bookYml.getString("DisplayName"));
return bookMetadata; return bookMetadata;
} }

View File

@@ -275,7 +275,7 @@ public final class EncryptionHelper {
return null; return null;
} else { } else {
try { try {
EncryptedBook book = BookToFromTextHelper.encryptedBookFromYml(file, bookMetadata, key, forceDecrypt); EncryptedBook book = BookToFromTextHelper.encryptedBookFromYml(file, bookMetadata, key, forceDecrypt, false);
if (book == null) { if (book == null) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} else { } else {