Added optional authURL support, as well as example PHP file for SMF 2.X.

This commit is contained in:
Travis Burtrum 2011-07-04 19:20:16 -04:00
parent a18a4cb336
commit 7cd36ff247
6 changed files with 176 additions and 2 deletions

View File

@ -27,7 +27,7 @@ public class LoginCommand implements CommandExecutor {
if (args.length < 1) {
xAuthMessages.send("loginUsage", player);
return true;
} else if (!xPlayer.isRegistered()) {
} else if (!xAuthSettings.authURLEnabled && !xPlayer.isRegistered()) {
xAuthMessages.send("loginErrRegistered", player);
return true;
} else if (xPlayer.hasSession()) {
@ -36,6 +36,10 @@ public class LoginCommand implements CommandExecutor {
}
Account account = xPlayer.getAccount();
if(xAuthSettings.authURLEnabled && account == null){
account = new Account(player.getName(), "authURL", null);
xPlayer.setAccount(account);
}
String password = args[0];
if (!plugin.checkPassword(account, password)) {

View File

@ -61,7 +61,7 @@ public class xAuthPlayerListener extends PlayerListener {
final String fieldName;
plugin.createGuest(xPlayer);
if (isRegistered)
if (isRegistered || xAuthSettings.authURLEnabled)
fieldName = "joinLogin";
else
fieldName = "joinRegister";

View File

@ -12,6 +12,7 @@ import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
@ -20,6 +21,15 @@ import org.bukkit.inventory.PlayerInventory;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
public class xAuth extends JavaPlugin {
public static PluginDescriptionFile desc;
public static File dataFolder;
@ -241,6 +251,19 @@ public class xAuth extends JavaPlugin {
}
public boolean checkPassword(Account account, String checkPass) {
if(xAuthSettings.authURLEnabled){
StringBuilder sb = new StringBuilder();
boolean result = checkAuthURLPass(account.getPlayerName(), checkPass, sb);
// if true, tell whole server a player logged in
// else, send the returned string (error message) to the user
if(result)
Bukkit.getServer().broadcastMessage("Player '" + account.getPlayerName() + "' logged in with forum name '"+sb.toString()+"'");
else
account.getPlayer().sendMessage(sb.toString());
return result;
}
String realPass = account.getPassword();
// check for old encryption (md5 or whirlpool)
@ -269,6 +292,39 @@ public class xAuth extends JavaPlugin {
return (hash.substring(0, saltPos) + salt + hash.substring(saltPos)).equals(realPass);
}
public boolean checkAuthURLPass(String user, String pass, StringBuilder response) {
try {
user = URLEncoder.encode(user, "UTF-8");
pass = URLEncoder.encode(pass, "UTF-8");
HttpURLConnection.setFollowRedirects(false);
HttpURLConnection uc = (HttpURLConnection) new URL(xAuthSettings.authURL).openConnection();
uc.setRequestMethod("POST");
uc.setDoInput(true);
uc.setDoOutput(true);
uc.setUseCaches(false);
uc.setAllowUserInteraction(false);
uc.setInstanceFollowRedirects(false);
uc.setRequestProperty("User-Agent", "Mozilla/5.0 xAuth/" + desc.getVersion());
uc.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
DataOutputStream out = new DataOutputStream(uc.getOutputStream());
out.writeBytes("user=" + user + "&pass=" + pass);
out.flush();
out.close();
BufferedReader in = new BufferedReader(new InputStreamReader(uc.getInputStream()));
String line = in.readLine();
boolean success = line != null && line.equals("YES");
response.append(in.readLine());
in.close();
return success;
} catch (Exception e) {
response.append(e.getMessage());
return false;
}
}
public Location getLocationToTeleport(World world) {
TeleLocation teleLocation = dataManager.getTeleLocation(world.getName());
return (teleLocation == null ? world.getSpawnLocation() : teleLocation.getLocation());

View File

@ -16,6 +16,10 @@ public class xAuthSettings {
public static boolean autoDisable = true;
public static boolean reverseESS = true;
// authURL
public static boolean authURLEnabled = false;
public static String authURL = "http://127.0.0.1/auth.php?field=minecra";
// mysql
public static String mysqlHost = "localhost";
public static int mysqlPort = 3306;
@ -110,6 +114,9 @@ public class xAuthSettings {
autoDisable = getBool("main.auto-disable", autoDisable);
reverseESS = getBool("main.reverse-enforce-single-session", reverseESS);
authURLEnabled = getBool("authurl.enabled", authURLEnabled);
authURL = getString("authurl.url", authURL);
mysqlHost = getString("mysql.host", mysqlHost);
mysqlPort = getInt("mysql.port", mysqlPort);
mysqlUser = getString("mysql.username", mysqlUser);
@ -171,6 +178,14 @@ public class xAuthSettings {
rstrDmgTaken = getBool("restrict.damage-taken", rstrDmgTaken);
rstrDmgGiven = getBool("restrict.damage-given", rstrDmgGiven);
rstrMobTarget = getBool("restrict.mob-target", rstrMobTarget);*/
// authURL doesn't allow for registration, or password management, so automatically disable it
if (authURLEnabled) {
regEnabled = false;
regForced = true;
pwAllowChange = false;
activation = false;
}
}
private static String getString(String key, String def) {

View File

@ -12,6 +12,14 @@ main:
# already online, the player connecting will be kicked instead of the online player
reverse-enforce-single-session: [reverseESS]
authurl:
# Send user/pass data to authURL for authentication instead of the database
# This option disables registration and password changes
enabled: [authURLEnabled]
# The URL to send user/pass data to, look at the example auth.php for an
# example that works with SMF forums.
url: [authURL]
mysql:
# Location of the MySQL server. Can be either a host name or IP address
host: [mysqlHost]

91
src/smf_auth.php Normal file
View File

@ -0,0 +1,91 @@
<?php
// this script is tested with SMF 2.X
/* The format is pretty simple, and always returns exactly 2 lines.
if successful, return this:
YES
forum_name
if not successful, return this:
ERROR
String to return to user describing error
*/
// $localaddr should be the IP your webserver is listening on, if this page isn't being visited by the same IP ($_SERVER['REMOTE_ADDR'])
// then errors are logged and a warning email is sent to the email configured in done() so no one tries to use this to bruteforce
// passwords, you really should just restrict this to only the server accessing it, I only make it accessible over localhost or to
// my home address over SSL only.
$localaddr = "127.0.0.1";
if($_SERVER['REMOTE_ADDR'] != $localaddr && $_SERVER['REMOTE_ADDR'] != gethostbyname('an.allowed.hostname') && $_SERVER['REMOTE_ADDR'] != '192.168.1.212' ) die("Access Denied!");
function writeToFile($message, $fname = 'auth.log', $mode = 'a'){
$fp = fopen($fname, $mode);
fwrite($fp, time().': '.$message."\n");
fclose($fp);
}
function done($msg, $template = "ERROR\n%s"){
printf($template, $msg);
global $localaddr;
if($_SERVER['REMOTE_ADDR'] != $localaddr){
$result = sprintf(str_replace("\n", ", ", $template), $msg);
writeToFile("result: ".$result);
// only if it's a bad pass, text me
if(strpos($msg, 'assword') === FALSE)
exit;
$to = "YOUR_EMAIL_ADDRESS_IF_REQUIRED";
$subject = "auth alert";
$message .= $result."\n";
$message .= $_SERVER['REMOTE_ADDR']." user: ".$_REQUEST['user'].", field: ".$_REQUEST['field'].", pass length: ".strlen($_REQUEST['pass']);
$from = "EMAIL_TO_SEND_FROM";
$headers = "From: $from";
$sendmail_params = "-f $from -r $from";
writeToFile("mail sent: ".(mail($to,$subject,$message,$headers, $sendmail_params) ? 'true' : 'false'));
}
exit;
}
if(($_SERVER['REMOTE_ADDR'] != $localaddr && !isset($_SERVER['HTTPS']))
|| !isset($_REQUEST['pass']) || !isset($_REQUEST['user']) || !isset($_REQUEST['field'])
|| ($_REQUEST['field'] != 'minecra'))
die("Access Denied!");
$user = $_REQUEST['user'];
$pass = $_REQUEST['pass'];
$field = 'cust_'.$_REQUEST['field'];
if($_SERVER['REMOTE_ADDR'] != $localaddr)
writeToFile($_SERVER['REMOTE_ADDR']." user: $user, field: $field, pass length: ".strlen($pass));
$db_server = 'localhost';
$db_name = 'smf';
$db_user = 'smfadmin';
$db_passwd = 'your_db_pass';
$db_prefix = 'smf_';
$mysqli = new mysqli($db_server, $db_user, $db_passwd, $db_name);
$stmt = $mysqli->prepare("SELECT `member_name`, `passwd`, `real_name` FROM `smf_members` WHERE `is_activated` = '1' AND `id_member` = (SELECT `id_member` FROM `smf_themes` WHERE `value` = ? AND `variable` = ?) LIMIT 1") or done('MySQL Error');
$stmt->bind_param("ss", $user, $field);
$stmt->execute();
// bind result variables
$stmt->bind_result($member_name, $pass_hash, $display_name);
$success = $stmt->fetch();
$stmt->close();
$mysqli->close();
if(!$success)
done('Name not registered, must put in profile on forum: URL_TO_YOUR_FORUM');
// hash password
$sha_passwd = sha1(strtolower($member_name) . htmlspecialchars_decode($pass));
if($sha_passwd != $pass_hash)
done('Incorrect Password, make sure you use your forum password.');
done($display_name, "YES\n%s");
?>