Add support for 'use-player-login-ip', 'require-player-login-ip' - map player login IP addresses to web chat addresses

This commit is contained in:
Mike Primm 2011-12-29 14:19:03 +08:00 committed by mikeprimm
parent 09fbf312ac
commit e009aefb18
7 changed files with 187 additions and 12 deletions

View File

@ -6,11 +6,13 @@ import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -86,6 +88,8 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI {
private int config_hashcode; /* Used to signal need to reload web configuration (world changes, config update, etc) */
private int fullrenderplayerlimit; /* Number of online players that will cause fullrender processing to pause */
private boolean didfullpause;
private Map<String, LinkedList<String>> ids_by_ip = new HashMap<String, LinkedList<String>>();
private boolean persist_ids_by_ip = false;
public enum CompassMode {
PRE19, /* Default for 1.8 and earlier (east is Z+) */
@ -278,6 +282,10 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI {
compassmode = CompassMode.PRE19;
/* Load full render processing player limit */
fullrenderplayerlimit = configuration.getInteger("fullrenderplayerlimit", 0);
/* If we're persisting ids-by-ip, load it */
persist_ids_by_ip = configuration.getBoolean("persist-ids-by-ip", true);
if(persist_ids_by_ip)
loadIDsByIP();
loadDebuggers();
@ -290,6 +298,7 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI {
playerList.load();
PlayerListener pl = new PlayerListener() {
public void onPlayerJoin(PlayerJoinEvent evt) {
Player p = evt.getPlayer();
playerList.updateOnlinePlayers(null);
if(fullrenderplayerlimit > 0) {
if((getServer().getOnlinePlayers().length+1) >= fullrenderplayerlimit) {
@ -300,6 +309,23 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI {
}
}
}
/* Add player info to IP-to-ID table */
InetSocketAddress addr = p.getAddress();
if(addr != null) {
String ip = addr.getAddress().getHostAddress();
LinkedList<String> ids = ids_by_ip.get(ip);
if(ids == null) {
ids = new LinkedList<String>();
ids_by_ip.put(ip, ids);
}
String pid = p.getName();
/* See if not first in list */
int idx = ids.indexOf(pid);
if(idx > 0) {
ids.remove(idx);
}
ids.addFirst(pid); /* Put us first on list */
}
}
public void onPlayerQuit(PlayerQuitEvent evt) {
playerList.updateOnlinePlayers(evt.getPlayer());
@ -412,6 +438,8 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI {
@Override
public void onDisable() {
if(persist_ids_by_ip)
saveIDsByIP();
if (componentManager != null) {
int componentCount = componentManager.components.size();
for(Component component : componentManager.components) {
@ -767,7 +795,9 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI {
"resetstats",
"sendtoweb",
"pause",
"purgequeue" }));
"purgequeue",
"ids-for-ip",
"ips-for-id" }));
@Override
public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) {
@ -954,6 +984,31 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI {
msg += args[i] + " ";
}
this.sendBroadcastToWeb("dynmap", msg);
} else if(c.equals("ids-for-ip") && checkPlayerPermission(sender, "ids-for-ip")) {
if(args.length > 1) {
List<String> ids = getIDsForIP(args[1]);
sender.sendMessage("IDs logged in from address " + args[1] + " (most recent to least):");
if(ids != null) {
for(String id : ids)
sender.sendMessage(" " + id);
}
}
else {
sender.sendMessage("IP address required as parameter");
}
} else if(c.equals("ips-for-id") && checkPlayerPermission(sender, "ips-for-id")) {
if(args.length > 1) {
sender.sendMessage("IP addresses logged for player " + args[1] + ":");
for(String ip: ids_by_ip.keySet()) {
LinkedList<String> ids = ids_by_ip.get(ip);
if((ids != null) && ids.contains(args[1])) {
sender.sendMessage(" " + ip);
}
}
}
else {
sender.sendMessage("Player ID required as parameter");
}
}
return true;
}
@ -1536,4 +1591,56 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI {
mapManager.pushUpdate(new Client.PlayerQuitMessage(player.getDisplayName(), player.getName()));
}
}
/**
* Get list of IDs seen on give IP (most recent to least recent)
*/
public List<String> getIDsForIP(InetAddress addr) {
return getIDsForIP(addr.getHostAddress());
}
/**
* Get list of IDs seen on give IP (most recent to least recent)
*/
public List<String> getIDsForIP(String ip) {
LinkedList<String> ids = ids_by_ip.get(ip);
if(ids != null)
return new ArrayList<String>(ids);
return null;
}
private void loadIDsByIP() {
File f = new File(getDataFolder(), "ids-by-ip.txt");
if(f.exists() == false)
return;
YamlConfiguration fc = new YamlConfiguration();
try {
fc.load(new File(getDataFolder(), "ids-by-ip.txt"));
ids_by_ip.clear();
Map<String,Object> v = fc.getValues(false);
for(String k : v.keySet()) {
List<String> ids = fc.getStringList(k);
if(ids != null) {
k = k.replace("_", ".");
ids_by_ip.put(k, new LinkedList<String>(ids));
}
}
} catch (Exception iox) {
Log.severe("Error loading " + f.getPath() + " - " + iox.getMessage());
}
}
private void saveIDsByIP() {
File f = new File(getDataFolder(), "ids-by-ip.txt");
YamlConfiguration fc = new YamlConfiguration();
for(String k : ids_by_ip.keySet()) {
List<String> v = ids_by_ip.get(k);
if(v != null) {
k = k.replace(".", "_");
fc.set(k, v);
}
}
try {
fc.save(f);
} catch (Exception x) {
Log.severe("Error saving " + f.getPath() + " - " + x.getMessage());
}
}
}

View File

@ -8,11 +8,13 @@ import static org.dynmap.JSONUtils.*;
public class InternalClientUpdateComponent extends ClientUpdateComponent {
public InternalClientUpdateComponent(DynmapPlugin plugin, final ConfigurationNode configuration) {
public InternalClientUpdateComponent(final DynmapPlugin plugin, final ConfigurationNode configuration) {
super(plugin, configuration);
final Boolean allowwebchat = configuration.getBoolean("allowwebchat", false);
final Boolean hidewebchatip = configuration.getBoolean("hidewebchatip", false);
final Boolean trust_client_name = configuration.getBoolean("trustclientname", false);
final boolean allowwebchat = configuration.getBoolean("allowwebchat", false);
final boolean hidewebchatip = configuration.getBoolean("hidewebchatip", false);
final boolean trust_client_name = configuration.getBoolean("trustclientname", false);
final boolean useplayerloginip = configuration.getBoolean("use-player-login-ip", true);
final boolean requireplayerloginip = configuration.getBoolean("require-player-login-ip", false);
final float webchatInterval = configuration.getFloat("webchat-interval", 1);
final String spammessage = plugin.configuration.getString("spammessage", "You may only chat once every %interval% seconds.");
@ -31,7 +33,10 @@ public class InternalClientUpdateComponent extends ClientUpdateComponent {
maximumMessageInterval = (int)(webchatInterval * 1000);
spamMessage = "\""+spammessage+"\"";
hideip = hidewebchatip;
this.plug_in = plugin;
this.trustclientname = trust_client_name;
this.use_player_login_ip = useplayerloginip;
this.require_player_login_ip = requireplayerloginip;
onMessageReceived.addListener(new Listener<SendMessageHandler.Message>() {
@Override
public void triggered(Message t) {

View File

@ -9,6 +9,7 @@ import java.io.Reader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
@ -26,8 +27,11 @@ public class JsonFileClientUpdateComponent extends ClientUpdateComponent {
protected long currentTimestamp = 0;
protected long lastTimestamp = 0;
protected JSONParser parser = new JSONParser();
private Boolean hidewebchatip;
private boolean hidewebchatip;
private boolean useplayerloginip;
private boolean requireplayerloginip;
private boolean trust_client_name;
private HashMap<String,String> useralias = new HashMap<String,String>();
private int aliasindex = 1;
private long last_confighash;
@ -38,6 +42,10 @@ public class JsonFileClientUpdateComponent extends ClientUpdateComponent {
final boolean allowwebchat = configuration.getBoolean("allowwebchat", false);
jsonInterval = (long)(configuration.getFloat("writeinterval", 1) * 1000);
hidewebchatip = configuration.getBoolean("hidewebchatip", false);
useplayerloginip = configuration.getBoolean("use-player-login-ip", true);
requireplayerloginip = configuration.getBoolean("require-player-login-ip", false);
trust_client_name = configuration.getBoolean("trustclientname", false);
MapManager.scheduleDelayedJob(new Runnable() {
@Override
public void run() {
@ -211,7 +219,24 @@ public class JsonFileClientUpdateComponent extends ClientUpdateComponent {
if(ts.equals("null")) ts = "0";
if (Long.parseLong(ts) >= (lastTimestamp)) {
String name = String.valueOf(o.get("name"));
if(hidewebchatip) {
String ip = String.valueOf(o.get("ip"));
boolean isip = true;
if((!trust_client_name) || (name == null) || (name.equals(""))) {
if(ip != null)
name = ip;
}
if(useplayerloginip) { /* Try to match using IPs of player logins */
List<String> ids = plugin.getIDsForIP(name);
if(ids != null) {
name = ids.get(0);
isip = false;
}
else if(requireplayerloginip) {
Log.info("Ignore message from '" + name + "' - no matching player login recorded");
return;
}
}
if(hidewebchatip && isip) {
String n = useralias.get(name);
if(n == null) { /* Make ID */
n = String.format("web-%03d", aliasindex);

View File

@ -4,9 +4,12 @@ import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Logger;
import org.dynmap.DynmapPlugin;
import org.dynmap.Event;
import org.dynmap.Log;
import org.dynmap.web.HttpField;
import org.dynmap.web.HttpHandler;
import org.dynmap.web.HttpMethod;
@ -25,6 +28,9 @@ public class SendMessageHandler implements HttpHandler {
public int maximumMessageInterval = 1000;
public boolean hideip = false;
public boolean trustclientname = false;
public boolean use_player_login_ip = false;
public boolean require_player_login_ip = false;
public DynmapPlugin plug_in;
public String spamMessage = "\"You may only chat once every %interval% seconds.\"";
private HashMap<String, WebUser> disallowedUsers = new HashMap<String, WebUser>();
private LinkedList<WebUser> disallowedUserQueue = new LinkedList<WebUser>();
@ -45,10 +51,12 @@ public class SendMessageHandler implements HttpHandler {
JSONObject o = (JSONObject)parser.parse(reader);
final Message message = new Message();
message.name = "";
if(trustclientname) {
message.name = String.valueOf(o.get("name"));
}
else {
boolean isip = true;
if((message.name == null) || message.name.equals("")) {
/* If proxied client address, get original */
if(request.fields.containsKey("X-Forwarded-For"))
message.name = request.fields.get("X-Forwarded-For");
@ -58,7 +66,18 @@ public class SendMessageHandler implements HttpHandler {
else
message.name = request.rmtaddr.getAddress().getHostAddress();
}
if(hideip) { /* If hiding IP, find or assign alias */
if(use_player_login_ip) {
List<String> ids = plug_in.getIDsForIP(message.name);
if(ids != null) {
message.name = ids.get(0);
isip = false;
}
else if(require_player_login_ip) {
Log.info("Ignore message from '" + message.name + "' - no matching player login recorded");
return;
}
}
if(hideip && isip) { /* If hiding IP, find or assign alias */
synchronized(disallowedUsersLock) {
String n = useralias.get(message.name);
if(n == null) { /* Make ID */

View File

@ -27,6 +27,10 @@ components:
hidewebchatip: false
trustclientname: false
includehiddenplayers: false
# (optional) if true, player login IDs will be used for web chat when their IPs match
use-player-login-ip: true
# (optional) if use-player-login-ip is true, setting this to true will cause chat messages not matching a known player IP to be ignored
require-player-login-ip: false
# # Optional - make players hidden when they are inside/underground/in shadows (#=light level: 0=full shadow,15=sky)
# hideifshadow: 4
# # Optional - make player hidden when they are under cover (#=sky light level,0=underground,15=open to sky)
@ -39,8 +43,10 @@ components:
# webchat-interval: 5
# hidewebchatip: false
# includehiddenplayers: false
# hideifshadow: 4
# hideifundercover: 14
# use-player-login-ip: false
# require-player-login-ip: false
# hideifshadow: 0
# hideifundercover: 0
- class: org.dynmap.SimpleWebChatComponent
allowchat: true
@ -270,6 +276,9 @@ defaultmap: flat
# Option to enable workaround for incorrectly encoded unicode in Cyrillic MC/Bukkit (not good for other code pages)
cyrillic-support: false
# If true, make persistent record of IP addresses used by player logins, to support web IP to player matching
persist-ids-by-ip: true
# NOTE: the 'templates' section is now found in the 'templates' directory
# Templates CAN still be defined in configuration.txt, as before 0.20
templates:

View File

@ -30,6 +30,7 @@ commands:
/<command> purgequeue - Set tile update queue to empty
/<command> pause - Show render pause state
/<command> pause <all|none|full|update> - Set render pause state
/<command> ids-for-ip <ipaddress> - Show player IDs that have logged in from given IP address
dmarker:
description: Manipulate map markers
@ -83,6 +84,8 @@ permissions:
dynmap.resetstats: true
dynmap.sendtoweb: true
dynmap.purgequeue: true
dynmap.ids-for-ip: true
dynmap.ips-for-id: true
dynmap.pause: true
dynmap.marker.add: true
dynmap.marker.update: true
@ -144,6 +147,12 @@ permissions:
dynmap.pause:
description: Allows /dynmap pause
default: op
dynmap.ids-for-ip:
description: Allows /dynmap ids-for-ip
default: op
dynmap.ips-for-id:
description: Allows /dynmap ips-for-id
default: op
dynmap.marker.add:
description: Allows /dmarker add
default: op

View File

@ -11,6 +11,7 @@ if($_SERVER['REQUEST_METHOD'] == 'POST' && $_SESSION['lastchat'] < time())
$data = json_decode(trim(file_get_contents('php://input')));
$data->timestamp = $timestamp;
$data->ip = $_SERVER['REMOTE_ADDR'];
$old_messages = json_decode(file_get_contents('dynmap_webchat.json'), true);
if(!empty($old_messages))
{