[17.4k Lines] Added a QR code image to the two factor authentication system
This commit is contained in:
parent
d360fabcc8
commit
bad226c19b
@ -4,4 +4,5 @@ dependencies {
|
|||||||
compileOnly("com.destroystokyo:paperspigot:1.12.2")
|
compileOnly("com.destroystokyo:paperspigot:1.12.2")
|
||||||
implementation("com.github.cryptomorin:XSeries:7.8.0")
|
implementation("com.github.cryptomorin:XSeries:7.8.0")
|
||||||
implementation("com.warrenstrange:googleauth:1.4.0")
|
implementation("com.warrenstrange:googleauth:1.4.0")
|
||||||
|
implementation("com.google.zxing:javase:3.4.1")
|
||||||
}
|
}
|
@ -2,7 +2,6 @@ package zone.themcgamer.core.account;
|
|||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
import zone.themcgamer.core.account.Account;
|
|
||||||
import zone.themcgamer.core.module.Module;
|
import zone.themcgamer.core.module.Module;
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
package zone.themcgamer.core.twoFactor;
|
package zone.themcgamer.core.twoFactor;
|
||||||
|
|
||||||
|
import com.cryptomorin.xseries.XMaterial;
|
||||||
import com.cryptomorin.xseries.XSound;
|
import com.cryptomorin.xseries.XSound;
|
||||||
import com.warrenstrange.googleauth.GoogleAuthenticator;
|
import com.warrenstrange.googleauth.GoogleAuthenticator;
|
||||||
|
import net.md_5.bungee.api.chat.ClickEvent;
|
||||||
|
import net.md_5.bungee.api.chat.ComponentBuilder;
|
||||||
|
import net.md_5.bungee.api.chat.HoverEvent;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
@ -13,13 +17,18 @@ import org.bukkit.event.entity.EntityDamageEvent;
|
|||||||
import org.bukkit.event.entity.FoodLevelChangeEvent;
|
import org.bukkit.event.entity.FoodLevelChangeEvent;
|
||||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||||
import org.bukkit.event.player.*;
|
import org.bukkit.event.player.*;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.map.MapView;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
import zone.themcgamer.common.EnumUtils;
|
import zone.themcgamer.common.EnumUtils;
|
||||||
import zone.themcgamer.core.account.Account;
|
import zone.themcgamer.core.account.Account;
|
||||||
import zone.themcgamer.core.account.AccountManager;
|
import zone.themcgamer.core.account.AccountManager;
|
||||||
import zone.themcgamer.core.account.MiniAccount;
|
import zone.themcgamer.core.account.MiniAccount;
|
||||||
|
import zone.themcgamer.core.common.ItemBuilder;
|
||||||
import zone.themcgamer.core.common.Style;
|
import zone.themcgamer.core.common.Style;
|
||||||
import zone.themcgamer.core.module.ModuleInfo;
|
import zone.themcgamer.core.module.ModuleInfo;
|
||||||
|
import zone.themcgamer.core.twoFactor.image.QRImageGenerator;
|
||||||
|
import zone.themcgamer.core.twoFactor.image.QRMapRenderer;
|
||||||
import zone.themcgamer.data.Rank;
|
import zone.themcgamer.data.Rank;
|
||||||
import zone.themcgamer.data.mysql.MySQLController;
|
import zone.themcgamer.data.mysql.MySQLController;
|
||||||
|
|
||||||
@ -109,11 +118,25 @@ public class TwoFactorAuthentication extends MiniAccount<TwoFactorClient> {
|
|||||||
String secretKey = googleAuthenticator.createCredentials().getKey();
|
String secretKey = googleAuthenticator.createCredentials().getKey();
|
||||||
twoFactorClient.setSecretKey(secretKey);
|
twoFactorClient.setSecretKey(secretKey);
|
||||||
|
|
||||||
// TODO: 2/20/21 generate a qr map and give it to the player so they can scan it on their twoFactor app
|
// QR Map
|
||||||
|
ItemStack firstSlotItem = player.getInventory().getItem(0);
|
||||||
|
if (firstSlotItem != null)
|
||||||
|
twoFactorClient.setFirstSlotItem(firstSlotItem);
|
||||||
|
|
||||||
|
// Creating the map
|
||||||
|
Bukkit.getScheduler().runTaskAsynchronously(getPlugin(), new QRImageGenerator(player, twoFactorClient.getSecretKey(), image -> {
|
||||||
|
MapView mapView = Bukkit.createMap(player.getWorld());
|
||||||
|
mapView.getRenderers().removeIf(mapView::removeRenderer);
|
||||||
|
mapView.addRenderer(new QRMapRenderer(player, image));
|
||||||
|
player.getInventory().setItem(0, new ItemBuilder(XMaterial.FILLED_MAP, 1, (byte) mapView.getId()).toItemStack());
|
||||||
|
player.getInventory().setHeldItemSlot(0);
|
||||||
|
}));
|
||||||
|
|
||||||
player.sendMessage(Style.main(getName(), "Hey §b" + player.getName() + "§7, you have not setup your two factor authentication yet!"));
|
player.sendMessage(Style.main(getName(), "Hey §b" + player.getName() + "§7, you have not setup your two factor authentication yet!"));
|
||||||
player.sendMessage(Style.main(getName(), "To begin, open your authentication app of choice and insert the secret key"));
|
player.sendMessage(new ComponentBuilder(Style.main(getName(), "To begin, open your authentication app of choice and scan the QR code on the map or enter " +
|
||||||
player.sendMessage(Style.main(getName(), "§f" + secretKey));
|
"the code §6" + secretKey + " §7manually. Once done, type the 6 digit code provided by your authentication app into the chat"))
|
||||||
player.sendMessage(Style.main(getName(), "Once done, type the 6 digit code provided by your authentication app into the chat"));
|
.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("§aClick to copy").create()))
|
||||||
|
.event(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, secretKey)).create());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
player.sendMessage(Style.main(getName(), "§cYou need to re-authenticate!"));
|
player.sendMessage(Style.main(getName(), "§cYou need to re-authenticate!"));
|
||||||
@ -143,13 +166,16 @@ public class TwoFactorAuthentication extends MiniAccount<TwoFactorClient> {
|
|||||||
}
|
}
|
||||||
TwoFactorClient twoFactorClient = optionalTwoFactorClient.get();
|
TwoFactorClient twoFactorClient = optionalTwoFactorClient.get();
|
||||||
String secretKey = twoFactorClient.getSecretKey();
|
String secretKey = twoFactorClient.getSecretKey();
|
||||||
if (!googleAuthenticator.authorize(secretKey, code))
|
if (!googleAuthenticator.authorize(secretKey, code)) // If the provided code is incorrect, show an error to the player
|
||||||
player.sendMessage(Style.main(getName(), "§cInvalid authentication code!"));
|
player.sendMessage(Style.main(getName(), "§cInvalid authentication code!"));
|
||||||
else {
|
else {
|
||||||
|
// If the code is correct, we wanna authenticate the player
|
||||||
twoFactorClient.setLastAuthentication(System.currentTimeMillis());
|
twoFactorClient.setLastAuthentication(System.currentTimeMillis());
|
||||||
repository.authenticate(optionalAccount.get().getId(), secretKey, twoFactorClient.getLastAuthentication());
|
repository.authenticate(optionalAccount.get().getId(), secretKey, twoFactorClient.getLastAuthentication());
|
||||||
authenticating.remove(player.getUniqueId());
|
authenticating.remove(player.getUniqueId());
|
||||||
player.playSound(player.getEyeLocation(), XSound.ENTITY_PLAYER_LEVELUP.parseSound(), 0.9f, 1f);
|
player.playSound(player.getEyeLocation(), XSound.ENTITY_PLAYER_LEVELUP.parseSound(), 0.9f, 1f);
|
||||||
|
if (twoFactorClient.getFirstSlotItem() != null)
|
||||||
|
player.getInventory().setItem(0, twoFactorClient.getFirstSlotItem());
|
||||||
player.sendMessage(Style.main(getName(), "§aAuthenticated!"));
|
player.sendMessage(Style.main(getName(), "§aAuthenticated!"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -194,6 +220,16 @@ public class TwoFactorAuthentication extends MiniAccount<TwoFactorClient> {
|
|||||||
authenticating.remove(event.getPlayer().getUniqueId());
|
authenticating.remove(event.getPlayer().getUniqueId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whetherr or not the provided {@link Player} is authenticating
|
||||||
|
*
|
||||||
|
* @param player the player to check
|
||||||
|
* @return whether or not they're authenticating
|
||||||
|
*/
|
||||||
|
public boolean isAuthenticating(Player player) {
|
||||||
|
return authenticating.contains(player.getUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
private void cancelEvent(Cancellable cancellable) {
|
private void cancelEvent(Cancellable cancellable) {
|
||||||
if (!(cancellable instanceof PlayerEvent))
|
if (!(cancellable instanceof PlayerEvent))
|
||||||
return;
|
return;
|
||||||
|
@ -3,6 +3,7 @@ package zone.themcgamer.core.twoFactor;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@ -14,6 +15,11 @@ public class TwoFactorClient {
|
|||||||
private String secretKey;
|
private String secretKey;
|
||||||
private long lastAuthentication;
|
private long lastAuthentication;
|
||||||
|
|
||||||
|
// This is not stored in MySQL, this is just a copy of the item in the first slot of the player's inventory
|
||||||
|
// when they join. We save this item as we place the QR map in that slot and we give them the item when they
|
||||||
|
// authenticate
|
||||||
|
private ItemStack firstSlotItem;
|
||||||
|
|
||||||
public boolean requiresAuthentication() {
|
public boolean requiresAuthentication() {
|
||||||
return secretKey == null || (System.currentTimeMillis() - lastAuthentication) >= TimeUnit.DAYS.toMillis(1L);
|
return secretKey == null || (System.currentTimeMillis() - lastAuthentication) >= TimeUnit.DAYS.toMillis(1L);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
package zone.themcgamer.core.twoFactor.image;
|
||||||
|
|
||||||
|
import com.google.zxing.BarcodeFormat;
|
||||||
|
import com.google.zxing.WriterException;
|
||||||
|
import com.google.zxing.client.j2se.MatrixToImageWriter;
|
||||||
|
import com.google.zxing.qrcode.QRCodeWriter;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Braydon
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class QRImageGenerator implements Runnable {
|
||||||
|
private static final int MAP_SIZE = 132;
|
||||||
|
|
||||||
|
private final Player player;
|
||||||
|
private final String secretKey;
|
||||||
|
private final Consumer<BufferedImage> imageConsumer;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
QRCodeWriter qrWriter = new QRCodeWriter();
|
||||||
|
try {
|
||||||
|
imageConsumer.accept(MatrixToImageWriter.toBufferedImage(qrWriter.encode(
|
||||||
|
"otpauth://totp/" + player.getName() + "@McGamerZone?secret=" + secretKey,
|
||||||
|
BarcodeFormat.QR_CODE, MAP_SIZE, MAP_SIZE)));
|
||||||
|
} catch (WriterException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package zone.themcgamer.core.twoFactor.image;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.map.MapCanvas;
|
||||||
|
import org.bukkit.map.MapRenderer;
|
||||||
|
import org.bukkit.map.MapView;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Braydon
|
||||||
|
*/
|
||||||
|
public class QRMapRenderer extends MapRenderer {
|
||||||
|
private final Player player;
|
||||||
|
private BufferedImage bufferedImage;
|
||||||
|
|
||||||
|
public QRMapRenderer(Player player, BufferedImage bufferedImage) {
|
||||||
|
super(true);
|
||||||
|
this.player = player;
|
||||||
|
this.bufferedImage = bufferedImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(MapView mapView, MapCanvas mapCanvas, Player player) {
|
||||||
|
if (bufferedImage == null)
|
||||||
|
return;
|
||||||
|
if (this.player.equals(player)) {
|
||||||
|
mapCanvas.drawImage(0, 0, bufferedImage);
|
||||||
|
bufferedImage = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user