This commit is contained in:
2018-11-22 22:46:51 +01:00
parent e0406a8f65
commit 99f5a817bd
13 changed files with 485 additions and 147 deletions
@@ -4,7 +4,8 @@ import eu.oskar3123.spigot2fa.command.TFACommand;
import eu.oskar3123.spigot2fa.config.Config; import eu.oskar3123.spigot2fa.config.Config;
import eu.oskar3123.spigot2fa.config.ConfigHandler; import eu.oskar3123.spigot2fa.config.ConfigHandler;
import eu.oskar3123.spigot2fa.handler.TFAHandler; import eu.oskar3123.spigot2fa.handler.TFAHandler;
import eu.oskar3123.spigot2fa.listener.JoinQuitListener; import eu.oskar3123.spigot2fa.listener.AuthListener;
import eu.oskar3123.spigot2fa.listener.MapListener;
import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
@@ -41,7 +42,8 @@ public class Main extends JavaPlugin
private void registerListeners() private void registerListeners()
{ {
PluginManager pm = this.getServer().getPluginManager(); PluginManager pm = this.getServer().getPluginManager();
pm.registerEvents(new JoinQuitListener(this), this); pm.registerEvents(new AuthListener(this), this);
pm.registerEvents(new MapListener(this), this);
} }
} }
@@ -2,18 +2,28 @@ package eu.oskar3123.spigot2fa.command;
import eu.oskar3123.spigot2fa.Main; import eu.oskar3123.spigot2fa.Main;
import eu.oskar3123.spigot2fa.handler.TFAHandler; import eu.oskar3123.spigot2fa.handler.TFAHandler;
import eu.oskar3123.spigot2fa.tfa.TFA; import eu.oskar3123.spigot2fa.util.PlayerUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.util.StringUtil;
public class TFACommand implements CommandExecutor import java.util.*;
public class TFACommand implements CommandExecutor, TabCompleter
{ {
private Main plugin; private Main plugin;
private TFAHandler th; private TFAHandler th;
private final Map<String, String[]> TAB_COMPLETE_MAP = new HashMap<>();
{
TAB_COMPLETE_MAP.put("", new String[]{"add", "remove", "<code>"});
TAB_COMPLETE_MAP.put(":remove", new String[]{"[player]"});
}
public TFACommand(Main plugin) public TFACommand(Main plugin)
{ {
@@ -25,29 +35,123 @@ public class TFACommand implements CommandExecutor
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) public boolean onCommand(CommandSender sender, Command command, String label, String[] args)
{ {
Player player = (Player) sender; Player player = (Player) sender;
if (!th.isInProcess(player.getUniqueId())) if (args.length >= 1 && args[0].equalsIgnoreCase("remove"))
{ {
th.startCreating(player); if (!player.hasPermission("2fa.remove"))
return true;
}
else
{
String secret = th.getKey(player.getUniqueId());
String code = TFA.getTOTPCode(secret);
String pCode = StringUtils.join(args);
if (th.matchCode(code, pCode))
{ {
th.creatingSuccess(player); player.sendMessage("You don't have permission to do that");
player.sendMessage("Successfully activated 2FA"); return true;
}
if (args.length >= 2 && player.hasPermission("2fa.remove.other"))
{
OfflinePlayer otherPlayer = PlayerUtils.getOfflinePlayer(args[1]);
if (otherPlayer == null)
{
player.sendMessage("Could not find player \"" + args[1] + "\".");
return true;
}
if (th.hasEnabled2FA(otherPlayer.getUniqueId()))
{
th.remove2FA(otherPlayer.getUniqueId());
player.sendMessage("Removed two-factor authentication for " + otherPlayer.getName() + ".");
return true;
}
else
{
player.sendMessage(otherPlayer.getName() + " does not have two-factor authentication enabled.");
return true;
}
}
if (th.hasEnabled2FA(player.getUniqueId()))
{
th.remove2FA(player.getUniqueId());
player.sendMessage("Removed two-factor authentication.");
return true; return true;
} }
else else
{ {
th.creatingFailed(player); player.sendMessage("You don't have two-factor authentication enabled.");
player.sendMessage("That code is incorrect, aborting"); return true;
}
}
else if (args.length >= 1 && args[0].equalsIgnoreCase("add"))
{
if (!player.hasPermission("2fa.activate"))
{
player.sendMessage("You don't have permission to do that");
return true;
}
if (th.hasEnabled2FA(player.getUniqueId()))
{
player.sendMessage("You have already enabled two-factor authentication.");
return true;
}
if (!th.isInProcess(player.getUniqueId()))
{
boolean canStart = th.startCreating(player);
if (!canStart)
{
player.sendMessage("You can't hold anything when starting the process.");
return true;
}
player.sendMessage("Scan the barcode or enter the secret manually in your 2FA app.");
player.sendMessage("Use /2fa <code> to verify the authenticator.");
return true;
}
else
{
String secret = th.getKey(player.getUniqueId());
String pCode = StringUtils.join(args);
if (th.matchCode(secret, pCode))
{
th.creatingSuccess(player);
player.sendMessage("Successfully activated two-factor authentication.");
return true;
}
else
{
th.creatingFailed(player);
player.sendMessage("That code is incorrect, aborting.");
return true;
}
}
}
else
{
if (th.isInProcess(player.getUniqueId()))
{
player.sendMessage("Confirm the activation by typing /2fa <code>.");
return true;
}
else
{
player.sendMessage("Usage: " + command.getUsage().replace("<command>", label));
return true; return true;
} }
} }
} }
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args)
{
if (args.length < 1)
{
return new ArrayList<>();
}
StringBuilder search = new StringBuilder();
for (int i = 0; i < args.length - 1; i++)
{
search.append(":").append(args[i]);
}
if (!TAB_COMPLETE_MAP.containsKey(search.toString()))
{
return new ArrayList<>();
}
String[] commands = TAB_COMPLETE_MAP.get(search.toString());
final List<String> completions = new ArrayList<>();
StringUtil.copyPartialMatches(args[args.length - 1], Arrays.asList(commands), completions);
Collections.sort(completions);
return completions;
}
} }
@@ -1,6 +1,7 @@
package eu.oskar3123.spigot2fa.config; package eu.oskar3123.spigot2fa.config;
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
@@ -55,18 +56,40 @@ public class ConfigHandler
} }
public void saveConfig(Config config) public void saveConfig(Config config)
{
saveConfig(config, false);
}
public void saveConfig(Config config, boolean async)
{ {
if (config.fileConfig == null || config.file == null) if (config.fileConfig == null || config.file == null)
{ {
return; return;
} }
try if (async)
{ {
getConfig(config).save(config.file); Bukkit.getScheduler().runTaskAsynchronously(plugin, () ->
{
try
{
getConfig(config).save(config.file);
}
catch (IOException e)
{
plugin.getLogger().log(Level.SEVERE, "Could not save config to " + config.file, e);
}
});
} }
catch (IOException ex) else
{ {
plugin.getLogger().log(Level.SEVERE, "Could not save config to " + config.file, ex); try
{
getConfig(config).save(config.file);
}
catch (IOException e)
{
plugin.getLogger().log(Level.SEVERE, "Could not save config to " + config.file, e);
}
} }
} }
@@ -80,6 +103,7 @@ public class ConfigHandler
{ {
plugin.saveResource(config.name + ".yml", false); plugin.saveResource(config.name + ".yml", false);
} }
reloadConfig(config);
} }
} }
@@ -12,6 +12,7 @@ import org.bukkit.inventory.meta.MapMeta;
import org.bukkit.map.MapView; import org.bukkit.map.MapView;
import java.io.IOException; import java.io.IOException;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@@ -27,21 +28,67 @@ public class TFAHandler
this.plugin = plugin; this.plugin = plugin;
} }
public boolean shouldBypassCode(Player player)
{
UUID uuid = player.getUniqueId();
String ip = player.getAddress().getAddress().getHostAddress();
if (!hasEnabled2FA(uuid))
{
return true;
}
long now = new Date().getTime();
long sessionTimeInMs = plugin.configHandler.getConfig(plugin.config).getLong("2fa.sessiontime") * 60000L;
Configuration players = plugin.configHandler.getConfig(plugin.players);
long last = players.getLong(uuid.toString() + ".lastlogin");
String lastIp = players.getString(uuid.toString() + ".lastip");
return last >= now - sessionTimeInMs && ip.equalsIgnoreCase(lastIp);
}
public void setLastLogin(Player player, Long time)
{
UUID uuid = player.getUniqueId();
String ip = player.getAddress().getAddress().getHostAddress();
if (!hasEnabled2FA(uuid))
{
return;
}
long now;
if (time == null)
{
now = new Date().getTime();
}
else
{
now = time;
}
Configuration players = plugin.configHandler.getConfig(plugin.players);
players.set(uuid.toString() + ".lastlogin", now);
players.set(uuid.toString() + ".lastip", ip);
plugin.configHandler.saveConfig(plugin.players, true);
}
public String formatSecret(String secret) public String formatSecret(String secret)
{ {
return secret.toLowerCase().replaceAll("(.{4})(?=.{4})", "$1 "); return secret.toLowerCase().replaceAll("(.{4})(?=.{4})", "$1 ");
} }
public void remove2FA(UUID uuid)
{
plugin.configHandler.getConfig(plugin.players).set(uuid.toString(), null);
plugin.configHandler.saveConfig(plugin.players, true);
}
public boolean hasEnabled2FA(UUID uuid) public boolean hasEnabled2FA(UUID uuid)
{ {
return plugin.configHandler.getConfig(plugin.players).isString(uuid.toString()); return plugin.configHandler.getConfig(plugin.players).isString(uuid.toString() + ".secret");
} }
public void creatingSuccess(Player player) public void creatingSuccess(Player player)
{ {
String secret = remove(player.getUniqueId()); String secret = remove(player.getUniqueId());
plugin.configHandler.getConfig(plugin.players).set(player.getUniqueId().toString(), secret); player.getInventory().setItemInMainHand(new ItemStack(Material.AIR));
plugin.configHandler.saveConfig(plugin.players); plugin.configHandler.getConfig(plugin.players).set(player.getUniqueId().toString() + ".secret", secret);
plugin.configHandler.saveConfig(plugin.players, true);
} }
public void creatingFailed(Player player) public void creatingFailed(Player player)
@@ -49,15 +96,22 @@ public class TFAHandler
remove(player.getUniqueId()); remove(player.getUniqueId());
} }
public boolean matchCode(String code1, String code2) public boolean matchCode(String secret, String code)
{ {
return code1.equalsIgnoreCase(code2); String[] actualCodes = TFA.getTOTPCodes(secret);
for (String actualCode : actualCodes)
{
if (actualCode.equalsIgnoreCase(code))
{
return true;
}
}
return false;
} }
public boolean matchCode(Player player, String code) public boolean matchCode(Player player, String code)
{ {
String actualCode = TFA.getTOTPCode(getKey(player.getUniqueId())); return matchCode(getKey(player.getUniqueId()), code);
return matchCode(actualCode, code);
} }
private String remove(UUID uuid) private String remove(UUID uuid)
@@ -65,33 +119,42 @@ public class TFAHandler
return isInProcess.remove(uuid); return isInProcess.remove(uuid);
} }
public String startCreating(Player player) public boolean startCreating(Player player)
{ {
String key = TFA.getRandomSecretKey(); String key = TFA.getRandomSecretKey();
isInProcess.put(player.getUniqueId(), key); boolean couldShow = showQRCode(player, key);
showQRCode(player, key); if (couldShow)
return key; {
isInProcess.put(player.getUniqueId(), key);
}
return couldShow;
} }
private void showQRCode(Player player, String secret) private boolean showQRCode(Player player, String secret)
{ {
ItemStack inHand = player.getInventory().getItemInMainHand();
if (inHand != null && inHand.getType() != Material.AIR)
{
return false;
}
try try
{ {
ItemStack map = new ItemStack(Material.FILLED_MAP); ItemStack map = new ItemStack(Material.FILLED_MAP);
MapView view = Bukkit.createMap(player.getWorld()); MapView view = Bukkit.createMap(player.getWorld());
view.getRenderers().clear(); view.getRenderers().clear();
view.addRenderer(new QRMapRenderer(secret, player)); view.addRenderer(new QRMapRenderer(plugin, formatSecret(secret), player));
MapMeta mapMeta = (MapMeta) map.getItemMeta(); MapMeta mapMeta = (MapMeta) map.getItemMeta();
mapMeta.setMapId(view.getId()); mapMeta.setMapId(view.getId());
map.setItemMeta(mapMeta); map.setItemMeta(mapMeta);
player.getInventory().setItemInMainHand(map); player.getInventory().setItemInMainHand(map);
player.sendMap(view); player.sendMap(view);
player.sendMessage("Secret Key: " + secret); return true;
} }
catch (IOException e) catch (IOException e)
{ {
e.printStackTrace(); e.printStackTrace();
player.sendMessage("Failed to generate qr code"); player.sendMessage("Failed to generate qr code");
return false;
} }
} }
@@ -107,7 +170,16 @@ public class TFAHandler
return isInProcess.get(uuid); return isInProcess.get(uuid);
} }
Configuration players = plugin.configHandler.getConfig(plugin.players); Configuration players = plugin.configHandler.getConfig(plugin.players);
return players.getString(uuid.toString()); return players.getString(uuid.toString() + ".secret");
}
public void stopCreating(Player player)
{
if (!isInProcess(player.getUniqueId()))
{
return;
}
isInProcess.remove(player.getUniqueId());
} }
} }
@@ -0,0 +1,105 @@
package eu.oskar3123.spigot2fa.listener;
import eu.oskar3123.spigot2fa.Main;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.scheduler.BukkitTask;
import java.util.*;
public class AuthListener implements Listener
{
private class WaitingInfo
{
private BukkitTask kickTask;
private BukkitTask teleportTask;
private WaitingInfo(BukkitTask kickTask, BukkitTask teleportTask)
{
this.kickTask = kickTask;
this.teleportTask = teleportTask;
}
private void cancel(Player player)
{
this.kickTask.cancel();
this.teleportTask.cancel();
player.removePotionEffect(PotionEffectType.BLINDNESS);
}
}
private Main plugin;
private Map<UUID, WaitingInfo> waitingForCode = new HashMap<>();
public AuthListener(Main plugin)
{
this.plugin = plugin;
}
@EventHandler
public void onJoin(PlayerJoinEvent event)
{
if (plugin.tfaHandler.shouldBypassCode(event.getPlayer()))
{
event.getPlayer().sendMessage("bypassed 2fa check");
return;
}
final Player player = event.getPlayer();
BukkitTask task = Bukkit.getScheduler().runTaskLater(plugin, () ->
{
waitingForCode.remove(player.getUniqueId());
player.kickPlayer("You did not enter the 2FA code in time.");
}, 800L);
// 800 ticks == 40 secs
final Location loginLocation = player.getLocation().clone();
BukkitTask tpTask = Bukkit.getScheduler().runTaskTimer(plugin, () ->
{
player.teleport(loginLocation);
player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 45, 0, false, false), true);
}, 0L, 20L);
waitingForCode.put(player.getUniqueId(), new WaitingInfo(task, tpTask));
Bukkit.getScheduler().runTaskLater(plugin, () -> player.sendMessage("Please enter your 2FA code in the chat."), 1L);
}
@EventHandler
public void chat(AsyncPlayerChatEvent event)
{
if (!waitingForCode.containsKey(event.getPlayer().getUniqueId()))
{
return;
}
boolean correct = plugin.tfaHandler.matchCode(event.getPlayer(), event.getMessage());
event.setCancelled(true);
if (!correct)
{
event.getPlayer().sendMessage("Incorrect code, try again.");
}
else
{
waitingForCode.remove(event.getPlayer().getUniqueId()).cancel(event.getPlayer());
plugin.tfaHandler.setLastLogin(event.getPlayer(), null);
event.getPlayer().sendMessage("You have been authorized.");
}
}
@EventHandler
public void command(PlayerCommandPreprocessEvent event)
{
if (!waitingForCode.containsKey(event.getPlayer().getUniqueId()))
{
return;
}
event.setCancelled(true);
event.getPlayer().sendMessage("You have to authorize first.");
}
}
@@ -1,59 +0,0 @@
package eu.oskar3123.spigot2fa.listener;
import eu.oskar3123.spigot2fa.Main;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
public class JoinQuitListener implements Listener
{
private Main plugin;
private Set<UUID> waitingForCode = new HashSet<>();
public JoinQuitListener(Main plugin)
{
this.plugin = plugin;
}
@EventHandler
public void onJoin(PlayerJoinEvent event)
{
boolean enabled = plugin.tfaHandler.hasEnabled2FA(event.getPlayer().getUniqueId());
if (!enabled)
{
return;
}
event.getPlayer().sendMessage("Enter 2FA code");
waitingForCode.add(event.getPlayer().getUniqueId());
}
@EventHandler
public void chat(AsyncPlayerChatEvent event)
{
if (!waitingForCode.contains(event.getPlayer().getUniqueId()))
{
return;
}
boolean correct = plugin.tfaHandler.matchCode(event.getPlayer(), event.getMessage());
waitingForCode.remove(event.getPlayer().getUniqueId());
event.setCancelled(true);
if (!correct)
{
final Player player = event.getPlayer();
Bukkit.getScheduler().runTask(plugin, () -> player.kickPlayer("Wrong code!"));
}
else
{
event.getPlayer().sendMessage("Logged in");
}
}
}
@@ -0,0 +1,100 @@
package eu.oskar3123.spigot2fa.listener;
import eu.oskar3123.spigot2fa.Main;
import org.bukkit.Material;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.*;
import org.bukkit.event.player.PlayerDropItemEvent;
import org.bukkit.event.player.PlayerItemHeldEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.inventory.ItemStack;
public class MapListener implements Listener
{
private Main plugin;
public MapListener(Main plugin)
{
this.plugin = plugin;
}
@EventHandler
public void quit(PlayerQuitEvent event)
{
if (!plugin.tfaHandler.isInProcess(event.getPlayer().getUniqueId()))
{
return;
}
plugin.tfaHandler.stopCreating(event.getPlayer());
event.getPlayer().getInventory().setItemInMainHand(new ItemStack(Material.AIR));
}
@EventHandler
public void drop(PlayerDropItemEvent event)
{
if (!plugin.tfaHandler.isInProcess(event.getPlayer().getUniqueId()))
{
return;
}
if (event.getItemDrop().getItemStack().getType() != Material.FILLED_MAP)
{
return;
}
plugin.tfaHandler.stopCreating(event.getPlayer());
event.getPlayer().sendMessage("Aborted adding two-factor authentication.");
event.getItemDrop().remove();
}
@EventHandler
public void slot(PlayerItemHeldEvent event)
{
if (!plugin.tfaHandler.isInProcess(event.getPlayer().getUniqueId()))
{
return;
}
event.setCancelled(true);
}
@EventHandler
public void open(InventoryOpenEvent event)
{
if (!plugin.tfaHandler.isInProcess(event.getPlayer().getUniqueId()))
{
return;
}
event.setCancelled(true);
}
@EventHandler
public void open(InventoryClickEvent event)
{
if (!plugin.tfaHandler.isInProcess(event.getWhoClicked().getUniqueId()))
{
return;
}
event.setCancelled(true);
}
@EventHandler
public void open(InventoryCreativeEvent event)
{
if (!plugin.tfaHandler.isInProcess(event.getWhoClicked().getUniqueId()))
{
return;
}
event.setCancelled(true);
}
@EventHandler
public void open(InventoryDragEvent event)
{
if (!plugin.tfaHandler.isInProcess(event.getWhoClicked().getUniqueId()))
{
return;
}
event.setCancelled(true);
}
}
@@ -1,47 +0,0 @@
package eu.oskar3123.spigot2fa.map;
import eu.oskar3123.spigot2fa.tfa.TFA;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.MapMeta;
import org.bukkit.map.MapView;
import java.io.IOException;
public class CreateMapRunnable implements Runnable
{
private Player player;
public CreateMapRunnable(Player player)
{
this.player = player;
}
@Override
public void run()
{
try
{
ItemStack map = new ItemStack(Material.FILLED_MAP);
MapView view = Bukkit.createMap(player.getWorld());
view.getRenderers().clear();
String secret = TFA.getRandomSecretKey();
view.addRenderer(new QRMapRenderer(secret, player));
MapMeta mapMeta = (MapMeta) map.getItemMeta();
mapMeta.setMapId(view.getId());
map.setItemMeta(mapMeta);
player.getInventory().setItemInMainHand(map);
player.sendMap(view);
player.sendMessage("Secret Key: " + secret);
}
catch (IOException e)
{
e.printStackTrace();
player.sendMessage("Failed to generate qr code");
}
}
}
@@ -25,10 +25,14 @@ public class QRMapRenderer extends MapRenderer
{ {
this.plugin = plugin; this.plugin = plugin;
this.uuid = player.getUniqueId(); this.uuid = player.getUniqueId();
String[] formattedSecretKey = secretKey .replaceAll("((?:.{4} ){4})", "$1;").split(";"); String[] formattedSecretKey = secretKey.replaceAll("((?:.{4} ){4})", "$1;").split(";");
this.line1 = formattedSecretKey[0].trim(); this.line1 = formattedSecretKey[0].trim();
this.line2 = formattedSecretKey[1].trim(); this.line2 = formattedSecretKey[1].trim();
initImage(TFA.getGoogleAuthenticatorBarCode(secretKey, "Mineworlds", player.getName() + " (" + player.getUniqueId().toString() + ")")); String issuer = plugin.configHandler.getConfig(plugin.config).getString("2fa.issuer");
String account = plugin.configHandler.getConfig(plugin.config).getString("2fa.account")
.replace("{NAME}", player.getName())
.replace("{UUID}", player.getUniqueId().toString());
initImage(TFA.getGoogleAuthenticatorBarCode(secretKey, issuer, account));
} }
private void initImage(String qrValue) throws IOException private void initImage(String qrValue) throws IOException
@@ -22,7 +22,7 @@ public class TFA
return secretKey.toUpperCase(); return secretKey.toUpperCase();
} }
public static String getTOTPCode(String secretKey) public static String[] getTOTPCodes(String secretKey)
{ {
String normalizedBase32Key = secretKey.replace(" ", "").toUpperCase(); String normalizedBase32Key = secretKey.replace(" ", "").toUpperCase();
Base32 base32 = new Base32(); Base32 base32 = new Base32();
@@ -30,7 +30,11 @@ public class TFA
String hexKey = Hex.encodeHexString(bytes); String hexKey = Hex.encodeHexString(bytes);
long time = (System.currentTimeMillis() / 1000) / 30; long time = (System.currentTimeMillis() / 1000) / 30;
String hexTime = Long.toHexString(time); String hexTime = Long.toHexString(time);
return TOTP.generateTOTP(hexKey, hexTime, "6"); String code1 = TOTP.generateTOTP(hexKey, hexTime, "6");
time = time - 1;
hexTime = Long.toHexString(time);
String code2 = TOTP.generateTOTP(hexKey, hexTime, "6");
return new String[] {code1, code2};
} }
public static String getGoogleAuthenticatorBarCode(String secretKey, String issuer, String account) public static String getGoogleAuthenticatorBarCode(String secretKey, String issuer, String account)
@@ -0,0 +1,25 @@
package eu.oskar3123.spigot2fa.util;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
public class PlayerUtils
{
public static OfflinePlayer getOfflinePlayer(String name)
{
for (OfflinePlayer ply : Bukkit.getOfflinePlayers())
{
if (ply == null || ply.getName() == null || ply.getName().isEmpty())
{
continue;
}
if (ply.getName().toLowerCase().startsWith(name.toLowerCase()))
{
return ply;
}
}
return null;
}
}
+2
View File
@@ -1,3 +1,5 @@
2fa: 2fa:
issuer: 'Mineworlds' issuer: 'Mineworlds'
account: '{NAME}' account: '{NAME}'
# session time in minutes
sessiontime: 1440
+2
View File
@@ -5,3 +5,5 @@ authors: [oskar3123]
main: eu.oskar3123.spigot2fa.Main main: eu.oskar3123.spigot2fa.Main
commands: commands:
2fa: 2fa:
usage: '/<command> add/remove/<code>'
description: 'Two-factor authorization command'