Hallo,
da jetzt doch schon desöfteren nachgefragt wird, wie man den SA:MP Server mit der aktuellen Woltlab Burning Board Version verbindet, hier mal eine Möglichkeit wie man es machen kann.
Es gibt durchaus, bessere und andere Methoden, welche aber mehr Aufwand bedeuten. Diese hier ist relativ einfach und liefert das gewünschte Ergebnis. Tutorial richtet sich eher an erfahrene Benutzer.
Ihr braucht dafür:
- einen SA:MP Server
- ein installierte Version der aktuellen Woltlab Forensoftware
- etwas Geduld
Also erstes möchte ich Euch kurz erläutern, wie wir dabei vorgehen. Wir senden ein HTTP-Request vom SA:MP Server an unsere PHP-API, welche uns die Daten aus der Forensoftware bereitstellt.
Es sind nur Grundfunktionen enthalten, die aber schnell erweitert werden können.
Installation
Als erstes müsst Ihr das Plugin "SyncHTTP" in Eurem Skript laden.
Dazu oben folgende Code einfügen
Danach fügt Ihr folgenden Code unten bei Euch ins Skript ein:
forward WBB_CheckPassword(playerid, password[]);
public WBB_CheckPassword(playerid, password[]) {
new buffer[128], postData[128];
format(postData, sizeof postData, "&request=wbb_checkPassword&username=%s&password=%s", GetName(playerid), password);
SendWbbApiRequest ( postData, buffer, sizeof buffer);
return strval(buffer);
}
forward WBB_AddUserToGroup(playerid, groupID);
public WBB_AddUserToGroup(playerid, groupID) {
new buffer[128], postData[ 128 ];
format(postData, sizeof postData, "&request=wbb_setUserToGroup&username=%s&groupID=%s", GetName(playerid), groupID);
SendWbbApiRequest(postData, buffer, sizeof buffer);
return buffer[0] == '1' ? true : false;
}
forward WBB_RemoveUserFromGroup(playerid, groupID);
public WBB_RemoveUserFromGroup(playerid, groupID) {
new buffer[128], postData[128];
format(postData , sizeof postData, "&request=wbb_removeUserFromGroup&username=%s&groupID=%s" , GetName(playerid), groupID);
SendWbbApiRequest(postData, buffer , sizeof buffer);
return buffer[0] == '1' ? true : false;
}
forward WBB_GetUserID(playerid);
public WBB_GetUserID(playerid) {
new buffer[128], postData[128];
format(postData ,sizeof postData, "&request=wbb_getId&username=%s", GetName(playerid));
SendWbbApiRequest(postData, buffer, sizeof buffer);
return strval(buffer);
}
SendWbbApiRequest(PostData[],Result[],Length)
{
new httpStatus = SynchronizedHTTP (WBB_CONNECT_URL, PostData , Result , Length , HTTP_GET , 5 , true);
if(httpStatus == 200)
{
return true;
}
else
{
return false;
}
}
Alles anzeigen
Als nächstes noch ein #define für unsere API-Irl
Wer die Funktion "GetName" nicht hat, unten ebenfalls einfügen.
Nun haben wir soweit für das Skript alles eingefügt. Als nächstes müssen wir das PHP-Skript installieren.
Dazu legt Ihr einen Sub-Domain auf einen gewünschten Ordner an. In diesem ladet Ihr alles aus dem Ordner "php" hoch.
Wichtig ist, dass Ihr in dem PHP Skript in der Datei "/php/config.php" Euren eigenen Sicherheitsschlüssel eintragt. Ebenfalls dann im Skript ändern.
Folgender PHP Inhalt
index.php
<?php
//error_reporting(E_ALL);
require_once('php/config.php');
require_once('php/api.class.php');
if (isset($_GET['key']) && $_GET['key'] == key) {
if (isset($_GET['request']) && isset($_GET['username'])) {
$db = new mysqli('', '', '', '');
if ($db->connect_errno > 0) {
die('Unable to connect to database [' . $db->connect_error . ']');
}
$var = new api($_GET['username'], $db);
switch ($_GET['request']) {
case "wbb_getId": echo $var->getWbbId();
break;
case "wbb_checkPassword": echo $var->checkWbbPassword($_GET['password']);
break;
case "wbb_setUserToGroup": echo $var->setUserToGroup($_GET['groupID']);
break;
case "wbb_removeUserFromGroup": echo $var->removeUserFromGroup($_GET['groupID']);
break;
default: echo "0"; break;
}
} else
echo "0";
exit;
} else
echo "0";
exit;
Alles anzeigen
/php/config.php
/php/api.class.php
<?php
class api {
private $username;
private $con;
public function __construct($username, $db_con) {
$this->username = $username;
$this->con = $db_con;
}
public function getWbbId() {
$output = '0';
$sql = "SELECT userID FROM wcf1_user WHERE username = '" . $this->con->real_escape_string($this->username) . "'";
if (!$result = $this->con->query($sql)) {
$output = '' . $this->con->error . '';
} else {
while ($row = $result->fetch_assoc()) {
$output = $row['userID'];
}
}
return $output;
}
public function checkWbbPassword($password) {
$output = '0';
require_once("PasswordUtil.class.php");
$isValid = false;
$password = $this->con->real_escape_string($password);
$sql = "SELECT password FROM wcf1_user WHERE username = '" . $this->con->real_escape_string($this->username) . "'";
if (!$result = $this->con->query($sql)) {
$output = '' . $this->con->error . '';
} else {
while ($row = $result->fetch_assoc()) {
if (PasswordUtil::isBlowfish($row['password'])) {
if (PasswordUtil::secureCompare($row['password'], PasswordUtil::getDoubleSaltedHash($password, $row['password']))) {
$isValid = true;
}
} else {
if (PasswordUtil::checkPassword($name, $password, $row['password'])) {
$isValid = true;
}
}
if ($isValid) $output = "1";
else $output = "0";
}
}
return $output;
}
public function setUserToGroup($groupID = NULL) {
$output = '0';
$alreadyInGroup = $this->isUserInGroup($groupID);
if ($alreadyInGroup == false) {
if (is_numeric($groupID)) {
if ($groupID != NULL) {
$sql = "INSERT INTO wcf1_user_to_group (userID, groupID) VALUES (" . $this->getIdByUserName() . ", " . $groupID . ")";
echo $sql;
if (!$result = $this->con->query($sql)) {
$output = '' . $this->con->error . '';
} else $output = "1";
} else $output = '0';
} else $output = '0';
} else $output = '0';
return $output;
}
public function removeUserFromGroup($groupID = NULL) {
$output = '0';
$alreadyInGroup = $this->isUserInGroup($groupID);
if ($alreadyInGroup == true) {
if (is_numeric($groupID)) {
if ($groupID != NULL) {
$sql = "DELETE FROM wcf1_user_to_group WHERE " . $this->getIdByUserName() . " AND groupID = " . $groupID;
if (!$result = $this->con->query($sql)) {
$output = '' . $this->con->error . '';
} else
$output = "1";
} else $output = '0';
} else $output = '0';
} else $output = '0';
return $output;
}
public function isUserInGroup($group_id = NULL) {
$sql = "SELECT * FROM wcf1_user_to_group WHERE userID = '" . $this->getIdByUserName() . "' AND groupID = " . $group_id;
$result = $this->con->query($sql);
if (!$result) {
return false;
} else {
$row = $result->num_rows;
if ($row > 0) {
return true;
} else {
return false;
}
}
}
public function getIdByUserName() {
$sql = "SELECT userID FROM wcf1_user WHERE username = '" . $this->con->real_escape_string($this->username) . "'";
if (!$result = $this->con->query($sql)) {
return false;
} else {
while ($row = $result->fetch_assoc()) {
return $row['userID'];
}
}
return false;
}
}
Alles anzeigen
/php/PasswordUtil.class.php
<?php
/**
* Provides functions to compute password hashes.
*
* @author Alexander Ebert
* @copyright 2001-2013 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @package com.woltlab.wcf
* @subpackage util
* @category Community Framework
*/
final class PasswordUtil {
/**
* concated list of valid blowfish salt characters
* @var string
*/
private static $blowfishCharacters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./';
/**
* list of supported encryption type by software identifier
* @var array<string>
*/
private static $supportedEncryptionTypes = array(
'ipb2', // Invision Power Board 2.x
'ipb3', // Invision Power Board 3.x
'mybb1', // MyBB 1.x
'smf1', // Simple Machines Forum 1.x
'smf2', // Simple Machines Forum 2.x
'vb3', // vBulletin 3.x
'vb4', // vBulletin 4.x
'vb5', // vBulletin 5.x
'wbb2', // WoltLab Burning Board 2.x
'wcf1', // WoltLab Community Framework 1.x
'wcf2', // WoltLab Community Framework 2.x
'xf1' // XenForo 1.x
);
/**
* blowfish cost factor
* @var string
*/
const BCRYPT_COST = '08';
/**
* blowfish encryption type
* @var string
*/
const BCRYPT_TYPE = '2a';
/**
* Returns true if given encryption type is supported.
*
* @param string $type
* @return boolean
*/
public static function isSupported($type) {
return in_array($type, self::$supportedEncryptionTypes);
}
/**
* Returns true if given hash looks like a valid bcrypt hash.
*
* @param string $hash
* @return boolean
*/
public static function isBlowfish($hash) {
return (preg_match('^\$2[afx]\$^', $hash) ? true : false);
}
/**
* Returns true if given bcrypt hash uses a different cost factor and should be re-computed.
*
* @param string $hash
* @return boolean
*/
public static function isDifferentBlowfish($hash) {
$currentCost = intval(self::BCRYPT_COST);
$hashCost = intval(substr($hash, 4, 2));
if ($currentCost != $hashCost) {
return true;
}
return false;
}
/**
* Validates password against stored hash, encryption type is automatically resolved.
*
* @param string $username
* @param string $password
* @param string $dbHash
* @return boolean
*/
public static function checkPassword($username, $password, $dbHash) {
$type = self::detectEncryption($dbHash);
if ($type === 'unknown') {
throw new Exception("Unable to determine password encryption");
}
// drop type from hash
$dbHash = substr($dbHash, strlen($type));
// check for salt
$salt = '';
if (($pos = strrpos($dbHash, ':')) !== false) {
$salt = substr(substr($dbHash, $pos), 1);
$dbHash = substr($dbHash, 1, ($pos - 1));
}
// compare hash
echo call_user_func('WcfPasswordUtil::'.$type, $username, $password, $salt, $dbHash);
}
/**
* Returns encryption type if possible.
*
* @param string $hash
* @return string
*/
public static function detectEncryption($hash) {
if (($pos = strpos($hash, ':')) !== false) {
$type = substr($hash, 0, $pos);
if (in_array($type, self::$supportedEncryptionTypes)) {
return $type;
}
}
return 'unknown';
}
/**
* Returns a double salted bcrypt hash.
*
* @param string $password
* @param string $salt
* @return string
*/
public static function getDoubleSaltedHash($password, $salt = null) {
if ($salt === null) {
$salt = self::getRandomSalt();
}
return self::getSaltedHash(self::getSaltedHash($password, $salt), $salt);
}
/**
* Returns a simple salted bcrypt hash.
*
* @param string $password
* @param string $salt
* @return string
*/
public static function getSaltedHash($password, $salt = null) {
if ($salt === null) {
$salt = self::getRandomSalt();
}
return crypt($password, $salt);
}
/**
* Returns a random blowfish-compatible salt.
*
* @return string
*/
public static function getRandomSalt() {
$salt = '';
for ($i = 0, $maxIndex = (strlen(self::$blowfishCharacters) - 1); $i < 22; $i++) {
$salt .= self::$blowfishCharacters[mt_rand(0, $maxIndex)];
}
return self::getSalt($salt);
}
/**
* Generates a random user password with the given character length.
*
* @param integer $length
* @return string
*/
public static function getRandomPassword($length = 8) {
$availableCharacters = array(
'abcdefghijklmnopqrstuvwxyz',
'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'0123456789',
'+#-.,;:?!'
);
$password = '';
$type = 0;
for ($i = 0; $i < $length; $i++) {
$type = ($i % 4 == 0) ? 0 : ($type + 1);
$password .= substr($availableCharacters[$type], MathUtil::getRandomValue(0, strlen($availableCharacters[$type]) - 1), 1);
}
return str_shuffle($password);
}
/**
* Compares two password hashes. This function is protected against timing attacks.
*
* @see http://codahale.com/a-lesson-in-timing-attacks/
*
* @param string $hash1
* @param string $hash2
* @return boolean
*/
public static function secureCompare($hash1, $hash2) {
if (strlen($hash1) !== strlen($hash2)) {
return false;
}
$result = 0;
for ($i = 0, $length = strlen($hash1); $i < $length; $i++) {
$result |= ord($hash1[$i]) ^ ord($hash2[$i]);
}
return ($result === 0);
}
/**
* Returns a blowfish salt, e.g. $2a$07$usesomesillystringforsalt$
*
* @param string $salt
* @return string
*/
protected static function getSalt($salt) {
$salt = StringUtil::substring($salt, 0, 22);
return '$' . self::BCRYPT_TYPE . '$' . self::BCRYPT_COST . '$' . $salt;
}
/**
* Validates the password hash for Invision Power Board 2.x (ipb2).
*
* @param string $username
* @param string $password
* @param string $salt
* @param string $dbHash
* @return boolean
*/
protected static function ipb2($username, $password, $salt, $dbHash) {
return self::vb3($username, $password, $salt, $dbHash);
}
/**
* Validates the password hash for Invision Power Board 3.x (ipb3).
*
* @param string $username
* @param string $password
* @param string $salt
* @param string $dbHash
* @return boolean
*/
protected static function ipb3($username, $password, $salt, $dbHash) {
return self::secureCompare($dbHash, md5(md5($salt) . md5($password)));
}
/**
* Validates the password hash for MyBB 1.x (mybb1).
*
* @param string $username
* @param string $password
* @param string $salt
* @param string $dbHash
* @return boolean
*/
protected static function mybb1($username, $password, $salt, $dbHash) {
return self::secureCompare($dbHash, md5(md5($salt) . md5($password)));
}
/**
* Validates the password hash for Simple Machines Forums 1.x (smf1).
*
* @param string $username
* @param string $password
* @param string $salt
* @param string $dbHash
* @return boolean
*/
protected static function smf1($username, $password, $salt, $dbHash) {
return self::secureCompare($dbHash, sha1(StringUtil::toLowerCase($username) . $password));
}
/**
* Validates the password hash for Simple Machines Forums 2.x (smf2).
*
* @param string $username
* @param string $password
* @param string $salt
* @param string $dbHash
* @return boolean
*/
protected static function smf2($username, $password, $salt, $dbHash) {
return self::smf1($username, $password, $salt, $dbHash);
}
/**
* Validates the password hash for vBulletin 3 (vb3).
*
* @param string $username
* @param string $password
* @param string $salt
* @param string $dbHash
* @return boolean
*/
protected static function vb3($username, $password, $salt, $dbHash) {
return self::secureCompare($dbHash, md5(md5($password) . $salt));
}
/**
* Validates the password hash for vBulletin 4 (vb4).
*
* @param string $username
* @param string $password
* @param string $salt
* @param string $dbHash
* @return boolean
*/
protected static function vb4($username, $password, $salt, $dbHash) {
return self::vb3($username, $password, $salt, $dbHash);
}
/**
* Validates the password hash for vBulletin 5 (vb5).
*
* @param string $username
* @param string $password
* @param string $salt
* @param string $dbHash
* @return boolean
*/
protected static function vb5($username, $password, $salt, $dbHash) {
return self::vb3($username, $password, $salt, $dbHash);
}
/**
* Validates the password hash for WoltLab Burning Board 2 (wbb2).
*
* @param string $username
* @param string $password
* @param string $salt
* @param string $dbHash
* @return boolean
*/
protected static function wbb2($username, $password, $salt, $dbHash) {
if (self::secureCompare($dbHash, md5($password))) {
return true;
}
else if (self::secureCompare($dbHash, sha1($password))) {
return true;
}
return false;
}
/**
* Validates the password hash for WoltLab Community Framework 1.x (wcf1).
*
* @param string $username
* @param string $password
* @param string $salt
* @param string $dbHash
* @return boolean
*/
protected static function wcf1($username, $password, $salt, $dbHash) {
return self::secureCompare($dbHash, sha1($salt . sha1($salt . sha1($password))));
}
/**
* Validates the password hash for WoltLab Community Framework 2.x (wcf2).
*
* @param string $username
* @param string $password
* @param string $salt
* @param string $dbHash
* @return boolean
*/
protected static function wcf2($username, $password, $salt, $dbHash) {
return self::secureCompare($dbHash, self::getDoubleSaltedHash($password, $salt));
}
/**
* Validates the password hash for XenForo 1.x with (xf1).
*
* @param string $username
* @param string $password
* @param string $salt
* @param string $dbHash
* @return boolean
*/
protected static function xf1($username, $password, $salt, $dbHash) {
if (self::secureCompare($dbHash, sha1(sha1($password) . $salt))) {
return true;
}
else if (extension_loaded('hash')) {
return self::secureCompare($dbHash, hash('sha256', hash('sha256', $password) . $salt));
}
return false;
}
private function __construct() { }
}
Alles anzeigen
Als Anhang nochmal der gesamte Ordner. Bei Fragen einfach hier fragen. Bin kein Mensch der langen Worte und Tutorials sind auch nicht mein Fachgebiet.
Wichtig für Rückgabewerte
Ihr erhaltet meistens entweder ein "1" oder "0" als Rückgabewert zurück. Diese Rückgabewerte könnte Ihr in der "api.class.php" natürlich Euren Wünschen anpassen. Achtet darauf, dass Ihr dann ebenfalls diese im Pawn-Skript anpassen müsst!