mirror of
https://github.com/webbukkit/dynmap.git
synced 2024-09-27 13:22:34 +02:00
359 lines
12 KiB
Java
359 lines
12 KiB
Java
|
package org.dynmap;
|
||
|
|
||
|
import java.io.File;
|
||
|
import java.io.FileReader;
|
||
|
import java.io.FileWriter;
|
||
|
import java.io.IOException;
|
||
|
import java.io.UnsupportedEncodingException;
|
||
|
import java.security.MessageDigest;
|
||
|
import java.security.NoSuchAlgorithmException;
|
||
|
import java.util.ArrayList;
|
||
|
import java.util.HashMap;
|
||
|
import java.util.HashSet;
|
||
|
import java.util.Properties;
|
||
|
import java.util.Random;
|
||
|
import java.util.Set;
|
||
|
|
||
|
import org.dynmap.common.DynmapCommandSender;
|
||
|
import org.dynmap.common.DynmapPlayer;
|
||
|
import org.dynmap.servlet.LoginServlet;
|
||
|
|
||
|
public class WebAuthManager {
|
||
|
private HashMap<String, String> pwdhash_by_userid = new HashMap<String, String>();
|
||
|
private HashMap<String, String> pending_registrations = new HashMap<String, String>();
|
||
|
private String hashsalt;
|
||
|
private File pfile;
|
||
|
public static final String WEBAUTHFILE = "webauth.txt";
|
||
|
private static final String HASHSALT = "$HASH_SALT$";
|
||
|
private static final String PWDHASH_PREFIX = "hash.";
|
||
|
private Random rnd = new Random();
|
||
|
private DynmapCore core;
|
||
|
|
||
|
public WebAuthManager(DynmapCore core) {
|
||
|
this.core = core;
|
||
|
pfile = new File(core.getDataFolder(), WEBAUTHFILE);
|
||
|
if(pfile.canRead()) {
|
||
|
FileReader rf = null;
|
||
|
try {
|
||
|
rf = new FileReader(pfile);
|
||
|
Properties p = new Properties();
|
||
|
p.load(rf);
|
||
|
hashsalt = p.getProperty(HASHSALT);
|
||
|
for(String k : p.stringPropertyNames()) {
|
||
|
if(k.equals(HASHSALT)) {
|
||
|
hashsalt = p.getProperty(k);
|
||
|
}
|
||
|
else if(k.startsWith(PWDHASH_PREFIX)) { /* Load password hashes */
|
||
|
pwdhash_by_userid.put(k.substring(PWDHASH_PREFIX.length()).toLowerCase(), p.getProperty(k));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} catch (IOException iox) {
|
||
|
Log.severe("Cannot read " + WEBAUTHFILE);
|
||
|
} finally {
|
||
|
if(rf != null) { try { rf.close(); } catch (IOException iox) {} }
|
||
|
}
|
||
|
}
|
||
|
if(hashsalt == null) { /* No hashsalt */
|
||
|
hashsalt = Long.toHexString(rnd.nextLong());
|
||
|
}
|
||
|
}
|
||
|
public boolean save() {
|
||
|
boolean success = false;
|
||
|
FileWriter fw = null;
|
||
|
try {
|
||
|
fw = new FileWriter(pfile);
|
||
|
Properties p = new Properties();
|
||
|
p.setProperty(HASHSALT, hashsalt); /* Save salt */
|
||
|
for(String k : pwdhash_by_userid.keySet()) {
|
||
|
p.setProperty(PWDHASH_PREFIX + k, pwdhash_by_userid.get(k));
|
||
|
}
|
||
|
p.store(fw, "DO NOT EDIT THIS FILE");
|
||
|
success = true;
|
||
|
} catch (IOException iox) {
|
||
|
Log.severe("Error writing " + WEBAUTHFILE);
|
||
|
} finally {
|
||
|
if(fw != null) { try { fw.close(); } catch (IOException iox) {} }
|
||
|
}
|
||
|
if(success)
|
||
|
core.events.trigger("loginupdated", null);
|
||
|
return success;
|
||
|
}
|
||
|
private String makeHash(String pwd) {
|
||
|
String check = hashsalt + pwd;
|
||
|
try {
|
||
|
byte[] checkbytes = check.getBytes("UTF-8");
|
||
|
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||
|
byte[] rslt = md.digest(checkbytes);
|
||
|
String rslthash = "";
|
||
|
for(int i = 0; i < rslt.length; i++) {
|
||
|
rslthash += String.format("%02X", 0xFF & (int)rslt[i]);
|
||
|
}
|
||
|
return rslthash;
|
||
|
} catch (NoSuchAlgorithmException nsax) {
|
||
|
} catch (UnsupportedEncodingException uex) {
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
public boolean checkLogin(String uid, String pwd) {
|
||
|
uid = uid.toLowerCase();
|
||
|
if(uid.equals(LoginServlet.USERID_GUEST)) {
|
||
|
return true;
|
||
|
}
|
||
|
String hash = pwdhash_by_userid.get(uid);
|
||
|
if(hash == null) {
|
||
|
return false;
|
||
|
}
|
||
|
if(core.getServer().isPlayerBanned(uid)) {
|
||
|
return false;
|
||
|
}
|
||
|
String checkhash = makeHash(pwd);
|
||
|
return hash.equals(checkhash);
|
||
|
}
|
||
|
public boolean registerLogin(String uid, String pwd, String passcode) {
|
||
|
uid = uid.toLowerCase();
|
||
|
if(uid.equals(LoginServlet.USERID_GUEST)) {
|
||
|
return false;
|
||
|
}
|
||
|
if(core.getServer().isPlayerBanned(uid)) {
|
||
|
return false;
|
||
|
}
|
||
|
passcode = passcode.toLowerCase();
|
||
|
String kcode = pending_registrations.remove(uid);
|
||
|
if(kcode == null) {
|
||
|
return false;
|
||
|
}
|
||
|
if(!kcode.equals(passcode)) {
|
||
|
return false;
|
||
|
}
|
||
|
String hash = makeHash(pwd);
|
||
|
pwdhash_by_userid.put(uid, hash);
|
||
|
return save();
|
||
|
}
|
||
|
public boolean unregisterLogin(String uid) {
|
||
|
if(uid.equals(LoginServlet.USERID_GUEST)) {
|
||
|
return true;
|
||
|
}
|
||
|
uid = uid.toLowerCase();
|
||
|
pwdhash_by_userid.remove(uid);
|
||
|
return save();
|
||
|
}
|
||
|
public boolean isRegistered(String uid) {
|
||
|
if(uid.equals(LoginServlet.USERID_GUEST)) {
|
||
|
return false;
|
||
|
}
|
||
|
uid = uid.toLowerCase();
|
||
|
return pwdhash_by_userid.containsKey(uid);
|
||
|
}
|
||
|
boolean processCompletedRegister(String uid, String pc, String hash) {
|
||
|
uid = uid.toLowerCase();
|
||
|
if(uid.equals(LoginServlet.USERID_GUEST)) {
|
||
|
return false;
|
||
|
}
|
||
|
if(core.getServer().isPlayerBanned(uid)) {
|
||
|
return false;
|
||
|
}
|
||
|
String kcode = pending_registrations.remove(uid);
|
||
|
if(kcode == null) {
|
||
|
return false;
|
||
|
}
|
||
|
pc = pc.toLowerCase();
|
||
|
if(!kcode.equals(pc)) {
|
||
|
return false;
|
||
|
}
|
||
|
pwdhash_by_userid.put(uid, hash);
|
||
|
return save();
|
||
|
}
|
||
|
public static final boolean checkUserName(String name) {
|
||
|
int nlen = name.length();
|
||
|
if ((nlen > 0) && (nlen <= 16)) {
|
||
|
for (int i = 0; i < nlen; i++) {
|
||
|
if ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_".indexOf(name.charAt(i)) < 0) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
public boolean processWebRegisterCommand(DynmapCore core, DynmapCommandSender sender, DynmapPlayer player, String[] args) {
|
||
|
String uid = null;
|
||
|
boolean other = false;
|
||
|
if(args.length > 1) {
|
||
|
if(!core.checkPlayerPermission(sender, "webregister.other")) {
|
||
|
sender.sendMessage("Not authorized to set web login information for other players");
|
||
|
return true;
|
||
|
}
|
||
|
uid = args[1];
|
||
|
other = true;
|
||
|
}
|
||
|
else if (player == null) { /* Console? */
|
||
|
sender.sendMessage("Must provide user ID to register web login");
|
||
|
return true;
|
||
|
}
|
||
|
else {
|
||
|
uid = player.getName();
|
||
|
}
|
||
|
if (checkUserName(uid) == false) {
|
||
|
sender.sendMessage("Invalid user ID");
|
||
|
return true;
|
||
|
}
|
||
|
String regkey = String.format("%04d-%04d", rnd.nextInt(10000), rnd.nextInt(10000));
|
||
|
pending_registrations.put(uid.toLowerCase(), regkey.toLowerCase());
|
||
|
sender.sendMessage("Registration pending for user ID: " + uid);
|
||
|
sender.sendMessage("Registration code: " + regkey);
|
||
|
sender.sendMessage("Enter ID and code on registration web page (login.html) to complete registration");
|
||
|
if(other) {
|
||
|
DynmapPlayer p = core.getServer().getPlayer(uid);
|
||
|
if(p != null) {
|
||
|
p.sendMessage("The registration of your account for web access has been started.");
|
||
|
p.sendMessage("To complete the process, access the Login page on the Dynmap map");
|
||
|
p.sendMessage("Registration code: " + regkey);
|
||
|
p.sendMessage("The user ID must match your account ID, but the password should NOT be the same.");
|
||
|
}
|
||
|
}
|
||
|
core.events.trigger("loginupdated", null);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
String getLoginPHP(boolean wrap) {
|
||
|
StringBuilder sb = new StringBuilder();
|
||
|
if (wrap) {
|
||
|
sb.append("<?php\n");
|
||
|
}
|
||
|
sb.append("$pwdsalt = '").append(hashsalt).append("';\n");
|
||
|
/* Create password hash */
|
||
|
sb.append("$pwdhash = array(\n");
|
||
|
for(String uid : pwdhash_by_userid.keySet()) {
|
||
|
sb.append(" \'").append(esc(uid)).append("\' => \'").append(esc(pwdhash_by_userid.get(uid))).append("\',\n");
|
||
|
}
|
||
|
sb.append(");\n");
|
||
|
/* Create registration table */
|
||
|
sb.append("$pendingreg = array(\n");
|
||
|
for(String uid : pending_registrations.keySet()) {
|
||
|
sb.append(" \'").append(esc(uid)).append("\' => \'").append(esc(pending_registrations.get(uid))).append("\',\n");
|
||
|
}
|
||
|
sb.append(");\n");
|
||
|
if (wrap) {
|
||
|
sb.append("?>\n");
|
||
|
}
|
||
|
return sb.toString();
|
||
|
}
|
||
|
|
||
|
public static String esc(String s) {
|
||
|
StringBuilder sb = new StringBuilder();
|
||
|
for(int i = 0; i < s.length(); i++) {
|
||
|
char c = s.charAt(i);
|
||
|
if(c == '\\')
|
||
|
sb.append("\\\\");
|
||
|
else if(c == '\'')
|
||
|
sb.append("\\\'");
|
||
|
else
|
||
|
sb.append(c);
|
||
|
}
|
||
|
return sb.toString();
|
||
|
}
|
||
|
|
||
|
String getAccessPHP(boolean wrap) {
|
||
|
StringBuilder sb = new StringBuilder();
|
||
|
if (wrap) {
|
||
|
sb.append("<?php\n");
|
||
|
}
|
||
|
ArrayList<String> mid = new ArrayList<String>();
|
||
|
/* Create world access list */
|
||
|
sb.append("$worldaccess = array(\n");
|
||
|
for(DynmapWorld w : core.getMapManager().getWorlds()) {
|
||
|
if(w.isProtected()) {
|
||
|
String perm = "world." + w.getName();
|
||
|
sb.append(" \'").append(esc(w.getName())).append("\' => \'");
|
||
|
for(String uid : pwdhash_by_userid.keySet()) {
|
||
|
if(core.getServer().checkPlayerPermission(uid, perm)) {
|
||
|
sb.append("[").append(esc(uid)).append("]");
|
||
|
}
|
||
|
}
|
||
|
sb.append("\',\n");
|
||
|
}
|
||
|
for(MapType mt : w.maps) {
|
||
|
if(mt.isProtected()) {
|
||
|
mid.add(w.getName() + "." + mt.getPrefix());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
sb.append(");\n");
|
||
|
|
||
|
/* Create map access list */
|
||
|
sb.append("$mapaccess = array(\n");
|
||
|
for(String id : mid) {
|
||
|
String perm = "map." + id;
|
||
|
sb.append(" \'").append(esc(id)).append("\' => \'");
|
||
|
for(String uid : pwdhash_by_userid.keySet()) {
|
||
|
if(core.getServer().checkPlayerPermission(uid, perm)) {
|
||
|
sb.append("[").append(esc(uid)).append("]");
|
||
|
}
|
||
|
}
|
||
|
sb.append("\',\n");
|
||
|
}
|
||
|
sb.append(");\n");
|
||
|
|
||
|
HashSet<String> cantseeall = new HashSet<String>();
|
||
|
String perm = "playermarkers.seeall";
|
||
|
sb.append("$seeallmarkers = \'");
|
||
|
for(String uid : pwdhash_by_userid.keySet()) {
|
||
|
if(core.getServer().checkPlayerPermission(uid, perm)) {
|
||
|
sb.append("[").append(esc(uid)).append("]");
|
||
|
}
|
||
|
else {
|
||
|
cantseeall.add(uid);
|
||
|
}
|
||
|
}
|
||
|
sb.append("\';\n");
|
||
|
/* Add visibility lists for each player that doesn't see everything */
|
||
|
sb.append("$playervisible = array(\n");
|
||
|
for(String id : cantseeall) {
|
||
|
id = id.toLowerCase();
|
||
|
Set<String> vis = core.getPlayersVisibleToPlayer(id);
|
||
|
if((vis.size() == 1) && vis.contains(id)) continue;
|
||
|
sb.append(" \'").append(esc(id)).append("\' => \'");
|
||
|
for(String uid : vis) {
|
||
|
sb.append("[").append(esc(uid)).append("]");
|
||
|
}
|
||
|
sb.append("\',\n");
|
||
|
}
|
||
|
sb.append(");\n");
|
||
|
|
||
|
core.getDefaultMapStorage().addPaths(sb, core);
|
||
|
|
||
|
if (wrap) {
|
||
|
sb.append("?>\n");
|
||
|
}
|
||
|
|
||
|
return sb.toString();
|
||
|
}
|
||
|
|
||
|
|
||
|
static String getDisabledAccessPHP(DynmapCore core, boolean wrap) {
|
||
|
StringBuilder sb = new StringBuilder();
|
||
|
if (wrap) {
|
||
|
sb.append("<?php\n");
|
||
|
}
|
||
|
|
||
|
core.getDefaultMapStorage().addPaths(sb, core);
|
||
|
|
||
|
if (wrap) {
|
||
|
sb.append("?>\n");
|
||
|
}
|
||
|
|
||
|
return sb.toString();
|
||
|
}
|
||
|
|
||
|
boolean pendingRegisters() {
|
||
|
return (pending_registrations.size() > 0);
|
||
|
}
|
||
|
Set<String> getUserIDs() {
|
||
|
HashSet<String> lst = new HashSet<String>();
|
||
|
lst.addAll(pwdhash_by_userid.keySet());
|
||
|
lst.addAll(pending_registrations.keySet());
|
||
|
return lst;
|
||
|
}
|
||
|
}
|