v1.4.4
This commit is contained in:
commit
9c94d113d3
10260 changed files with 1237388 additions and 0 deletions
23
attic/authserver/CMakeLists.txt
Normal file
23
attic/authserver/CMakeLists.txt
Normal file
|
@ -0,0 +1,23 @@
|
|||
INCLUDE_DIRECTORIES (
|
||||
${STAR_CORE_INCLUDES}
|
||||
${STAR_GAME_INCLUDES}
|
||||
${STAR_AUTHENTICATION_INCLUDES}
|
||||
)
|
||||
|
||||
SET (authentication_server_HEADERS
|
||||
StarAuthenticationDatabase.hpp
|
||||
StarAuthenticationServer.hpp
|
||||
StarDatabaseConnector.hpp
|
||||
)
|
||||
|
||||
SET (authentication_server_SOURCES
|
||||
StarAuthenticationDatabase.cpp
|
||||
StarAuthenticationServer.cpp
|
||||
StarDatabaseConnector.cpp
|
||||
)
|
||||
|
||||
ADD_EXECUTABLE (starbound_auth_keygen keygen.cpp ${authentication_server_SOURCES} ${authentication_server_HEADERS})
|
||||
ADD_EXECUTABLE (starbound_auth main.cpp ${authentication_server_SOURCES} ${authentication_server_HEADERS})
|
||||
|
||||
TARGET_LINK_LIBRARIES (starbound_auth_keygen star_authentication ${PQ_LIBRARY})
|
||||
TARGET_LINK_LIBRARIES (starbound_auth star_authentication ${PQ_LIBRARY})
|
160
attic/authserver/StarAuthenticationDatabase.cpp
Normal file
160
attic/authserver/StarAuthenticationDatabase.cpp
Normal file
|
@ -0,0 +1,160 @@
|
|||
#include "StarAuthenticationDatabase.hpp"
|
||||
#include "StarLogging.hpp"
|
||||
#include "StarDatabaseConnector.hpp"
|
||||
#include "StarAlgorithm.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Star {
|
||||
namespace Auth {
|
||||
|
||||
|
||||
AuthenticationDatabase::AuthenticationDatabase(String const& connectionString) :m_connectionString(connectionString) {
|
||||
/*
|
||||
if (validateUser("none"))
|
||||
throw StarException("User none should not exist");
|
||||
if (!validateUser("test"))
|
||||
throw StarException("User test should exist");
|
||||
if (!validateUserAndPassword("test", preHashPassword("test", "test")))
|
||||
throw StarException("User test should be able to login using the password 'test'");
|
||||
if (validateUserAndPassword("test", preHashPassword("test", "none")))
|
||||
throw StarException("User test should not be able to login using the password 'none'");
|
||||
*/
|
||||
}
|
||||
|
||||
AuthenticationDatabase::~AuthenticationDatabase() {
|
||||
|
||||
}
|
||||
|
||||
bool AuthenticationDatabase::validateUser(String const& username) {
|
||||
DatabaseConnector conn(m_connectionString);
|
||||
{
|
||||
conn.exec("BEGIN");
|
||||
bool success = false;
|
||||
auto transactionScope = finally([&]() {
|
||||
if (success)
|
||||
conn.exec("END");
|
||||
else
|
||||
conn.exec("ROLLBACK");
|
||||
});
|
||||
auto rows = conn.fetch("SELECT id_user, active FROM users WHERE username=$1", { username });
|
||||
bool result;
|
||||
if (rows.size() == 0) {
|
||||
result = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
starAssert(rows.size() == 1);
|
||||
auto row = rows[0];
|
||||
result = row.get("active").toBool();
|
||||
}
|
||||
conn.exec("INSERT INTO validations (username, timestamp, result) VALUES ($1, $2, $3)", { username, Thread::currentTime(), result });
|
||||
success = true;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
bool AuthenticationDatabase::validateUserAndPassword(String const& username, String const& password) {
|
||||
DatabaseConnector conn(m_connectionString);
|
||||
{
|
||||
conn.exec("BEGIN");
|
||||
bool success = false;
|
||||
auto transactionScope = finally([&]() {
|
||||
if (success)
|
||||
conn.exec("END");
|
||||
else
|
||||
conn.exec("ROLLBACK");
|
||||
});
|
||||
auto rows = conn.fetch("SELECT id_user, active, salt, rounds, hash FROM users WHERE username=$1", { username });
|
||||
bool result;
|
||||
if (rows.size() == 0) {
|
||||
result = false;
|
||||
}
|
||||
else {
|
||||
starAssert(rows.size() == 1);
|
||||
auto row = rows[0];
|
||||
result = row.get("active").toBool();
|
||||
if (result)
|
||||
result = bcryptValidate(password, row.get("salt").toString(), row.get("hash").toString(), row.get("rounds").toInt());
|
||||
}
|
||||
|
||||
conn.exec("INSERT INTO validations (username, timestamp, result) VALUES ($1, $2, $3)", { username, Thread::currentTime(), result });
|
||||
success = true;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
bool AuthenticationDatabase::setUserRecord(String const& username, String const& passwordPreHashed, bool active) {
|
||||
DatabaseConnector conn(m_connectionString);
|
||||
{
|
||||
conn.exec("BEGIN");
|
||||
bool success = false;
|
||||
auto transactionScope = finally([&]() {
|
||||
if (success)
|
||||
conn.exec("END");
|
||||
else
|
||||
conn.exec("ROLLBACK");
|
||||
});
|
||||
String salt = generateKey();
|
||||
int rounds = 0;
|
||||
String hash = bcrypt(passwordPreHashed, salt, rounds);
|
||||
auto rows = conn.fetch("SELECT id_user FROM users WHERE username=$1", { username });
|
||||
if (rows.size() == 1) {
|
||||
conn.exec("UPDATE users SET salt=$2, rounds=$3, hash=$4, active=$5 WHERE id_user=$1", { rows[0].get("id_user"), salt, rounds, hash, active });
|
||||
}
|
||||
else {
|
||||
long since = Thread::currentTime();
|
||||
conn.exec("INSERT INTO users (username, salt, rounds, hash, since, active) VALUES ($1, $2, $3, $4, $5, $6)", { username, salt, rounds, hash, since, active });
|
||||
}
|
||||
success = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool AuthenticationDatabase::activateUserRecord(String const& username, bool active) {
|
||||
DatabaseConnector conn(m_connectionString);
|
||||
{
|
||||
conn.exec("BEGIN");
|
||||
bool success = false;
|
||||
bool result;
|
||||
auto transactionScope = finally([&]() {
|
||||
if (success)
|
||||
conn.exec("END");
|
||||
else
|
||||
conn.exec("ROLLBACK");
|
||||
});
|
||||
auto rows = conn.fetch("SELECT id_user FROM users WHERE username=$1", { username });
|
||||
if (rows.size() == 1) {
|
||||
conn.exec("UPDATE users SET active=$2 WHERE id_user=$1", { rows[0].get("id_user"), active });
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
result = false;
|
||||
success = true;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
int AuthenticationDatabase::usersCount() {
|
||||
DatabaseConnector conn(m_connectionString);
|
||||
{
|
||||
conn.exec("BEGIN");
|
||||
int result = 0;
|
||||
bool success = false;
|
||||
auto transactionScope = finally([&]() {
|
||||
if (success)
|
||||
conn.exec("END");
|
||||
else
|
||||
conn.exec("ROLLBACK");
|
||||
});
|
||||
auto rows = conn.fetch("SELECT count(*) as amount FROM users", {});
|
||||
if (rows.size() == 1) {
|
||||
success = true;
|
||||
result = rows[0].get("amount").toInt();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
34
attic/authserver/StarAuthenticationDatabase.hpp
Normal file
34
attic/authserver/StarAuthenticationDatabase.hpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
#ifndef _STAR_AUTHENTICATOR_DATABASE_HPP_
|
||||
#define _STAR_AUTHENTICATOR_DATABASE_HPP_
|
||||
|
||||
#include "StarAuthenticationKey.hpp"
|
||||
#include "StarAuthenticationService.hpp"
|
||||
#include "StarException.hpp"
|
||||
#include "StarVariant.hpp"
|
||||
#include "StarRoot.hpp"
|
||||
#include "StarTcp.hpp"
|
||||
#include "StarThread.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
namespace Auth {
|
||||
|
||||
class AuthenticationDatabase: public Database {
|
||||
public:
|
||||
AuthenticationDatabase(String const& connectionString);
|
||||
~AuthenticationDatabase();
|
||||
virtual bool validateUser(String const& username) override;
|
||||
virtual bool validateUserAndPassword(String const& username, String const& passwordPreHashed) override;
|
||||
virtual bool setUserRecord(String const& username, String const& passwordPreHashed, bool active) override;
|
||||
virtual bool activateUserRecord(String const& username, bool active) override;
|
||||
virtual int usersCount() override;
|
||||
|
||||
private:
|
||||
String m_connectionString;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
325
attic/authserver/StarAuthenticationServer.cpp
Normal file
325
attic/authserver/StarAuthenticationServer.cpp
Normal file
|
@ -0,0 +1,325 @@
|
|||
#include "StarAuthenticationServer.hpp"
|
||||
#include "StarLogging.hpp"
|
||||
#include "StarAuthenticationDatabase.hpp"
|
||||
|
||||
namespace Star {
|
||||
namespace Auth {
|
||||
|
||||
AuthenticationConnectionHandler::AuthenticationConnectionHandler(TcpSocketPtr socket, AuthenticationServer* server)
|
||||
: Thread("AuthenticationConnectionHandler") {
|
||||
Logger::info("Connection thread constructor.");
|
||||
m_socket = socket;
|
||||
m_server = server;
|
||||
m_stop = false;
|
||||
m_closed = false;
|
||||
}
|
||||
|
||||
AuthenticationConnectionHandler::~AuthenticationConnectionHandler() {
|
||||
Logger::info("Connection thread destructor.");
|
||||
}
|
||||
|
||||
void AuthenticationConnectionHandler::stop() {
|
||||
m_stop = true;
|
||||
try {
|
||||
m_socket->close();
|
||||
} catch (StarException const&) {
|
||||
// bartwe: I'm a jerk, close is not supposed to be called during calls to the socket on other threads, not really seeing a good way to interrupt that i care about enough to implement atm.
|
||||
}
|
||||
}
|
||||
|
||||
bool AuthenticationConnectionHandler::isClosed() {
|
||||
return m_closed;
|
||||
}
|
||||
|
||||
void AuthenticationConnectionHandler::writeResponse(String const& response) {
|
||||
String body = "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Type: text/html; charset=UTF-8\r\nCache-Control: no-cache, no-store\r\n\r\n" + response + "\r\n\r\n";
|
||||
m_socket->write(body.utf8Ptr(), body.utf8Size());
|
||||
Logger::info("Connection thread write response.");
|
||||
}
|
||||
|
||||
void AuthenticationConnectionHandler::writeErrorResponse(String const& response) {
|
||||
String body = "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Type: text/html; charset=UTF-8\r\nCache-Control: no-cache, no-store\r\n\r\n" + response + "\r\n\r\n";
|
||||
m_socket->write(body.utf8Ptr(), body.utf8Size());
|
||||
Logger::info("Connection thread write error.");
|
||||
}
|
||||
|
||||
void AuthenticationConnectionHandler::run() {
|
||||
Logger::info("Connection thread.run");
|
||||
try {
|
||||
m_socket->setNoDelay(true);
|
||||
m_socket->setSocketTimeout(ClientSocketTimeout);
|
||||
|
||||
int64_t deadline = Thread::currentTime() + ClientSocketTimeout;
|
||||
|
||||
bool headerMode = true;
|
||||
Buffer buffer(BufferCapacity);
|
||||
int newlineCounter = 0;
|
||||
while (!m_stop && m_socket->isOpen()) {
|
||||
if (Thread::currentTime() >= deadline) {
|
||||
// client too slow, or a slow lorris attack
|
||||
writeErrorResponse("Client too slow.");
|
||||
break;
|
||||
}
|
||||
char c;
|
||||
if (m_socket->read(&c, 1) != 1)
|
||||
throw StarException("Unexpected result from socket read\n");
|
||||
if (c == '\r')
|
||||
continue; // hello dos
|
||||
buffer.write(&c, 1);
|
||||
if (buffer.pos() >= BufferCapacity) {
|
||||
// request too long
|
||||
writeErrorResponse("Request too long.");
|
||||
break;
|
||||
}
|
||||
if (c == '\n')
|
||||
newlineCounter++;
|
||||
else
|
||||
newlineCounter = 0;
|
||||
if (newlineCounter == 2) {
|
||||
if (headerMode) {
|
||||
String header = String(buffer.ptr(), buffer.pos());
|
||||
if (!header.beginsWith("POST /auth.sb HTTP/1.1\n")) {
|
||||
writeErrorResponse("Page not found.");
|
||||
break;
|
||||
}
|
||||
headerMode = false;
|
||||
buffer.clear();
|
||||
newlineCounter = 0;
|
||||
} else {
|
||||
String body = String(buffer.ptr(), buffer.pos() - 2); // remove trailing newlines, guaranteed to be there
|
||||
auto message = make_shared<MessageToken>(body);
|
||||
Logger::info("Connection thread.message received.");
|
||||
m_server->addMessage(message);
|
||||
if (!message->waitForResponse(ClientProcessingTimeout))
|
||||
writeErrorResponse("Timed out.");
|
||||
writeResponse(message->response());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_socket->flush();
|
||||
}
|
||||
catch (NetworkException const&) {
|
||||
goto finally;
|
||||
}
|
||||
catch (StarException const& e) {
|
||||
Logger::error("AuthenticationConnectionHandler: Exception caught: %s", e.what());
|
||||
}
|
||||
catch (std::exception const& e) {
|
||||
Logger::error("AuthenticationConnectionHandler: Exception caught: %s", e.what());
|
||||
goto finally;
|
||||
}
|
||||
catch (...) {
|
||||
goto finally;
|
||||
}
|
||||
finally: {
|
||||
m_closed = true;
|
||||
if (m_socket->isOpen())
|
||||
m_socket->close();
|
||||
}
|
||||
m_server->addMessage( { });
|
||||
Logger::info("Connection thread.run.end");
|
||||
}
|
||||
|
||||
AuthenticationServer::AuthenticationServer() {
|
||||
m_stop = false;
|
||||
|
||||
String connectionString = Variant::parseJson(File::readFileString("connectionstring.config")).getString("connectionString");
|
||||
|
||||
DatabasePtr db = as<Database>(make_shared<AuthenticationDatabase>(connectionString));
|
||||
m_service = make_shared<AuthenticationService>(db);
|
||||
|
||||
m_requestCount = 0;
|
||||
m_getAuthKeyCounter = 0;
|
||||
m_authorizeClientCounter = 0;
|
||||
m_authorizeClientFailure = 0;
|
||||
m_validateClientCounter = 0;
|
||||
m_validateClientFailure = 0;
|
||||
|
||||
m_munin = make_shared<MuninNode>(Variant::parseJson(File::readFileString("authmunin.config")));
|
||||
m_munin->setCallback([=]() {
|
||||
MutexLocker locker(m_mainLock);
|
||||
m_munin->set("sbauth", "users", db->usersCount());
|
||||
m_munin->set("sbauth", "requests", m_requestCount);
|
||||
m_munin->set("sbauth", "getAuthKey", m_getAuthKeyCounter);
|
||||
m_munin->set("sbauth", "authorizeClient", m_authorizeClientCounter);
|
||||
m_munin->set("sbauth", "authorizeClientFailure", m_authorizeClientFailure);
|
||||
m_munin->set("sbauth", "validateClient", m_validateClientCounter);
|
||||
m_munin->set("sbauth", "validateClientFailure", m_validateClientFailure);
|
||||
});
|
||||
}
|
||||
|
||||
AuthenticationServer::~AuthenticationServer() {}
|
||||
|
||||
void AuthenticationServer::acceptConnection(TcpSocketPtr socket) {
|
||||
MutexLocker locker(m_mainLock);
|
||||
if (m_stop) {
|
||||
socket->close();
|
||||
} else {
|
||||
auto handler = make_shared<AuthenticationConnectionHandler>(socket, this);
|
||||
m_handlers.add(handler);
|
||||
handler->start();
|
||||
}
|
||||
}
|
||||
|
||||
void AuthenticationServer::run() {
|
||||
TcpServer tcpServer(ListenPort);
|
||||
tcpServer.setAcceptCallback(std::bind(&AuthenticationServer::acceptConnection, this, std::placeholders::_1));
|
||||
|
||||
Logger::info("Auth server started.");
|
||||
|
||||
List<AuthenticationConnectionHandlerPtr> cleanup;
|
||||
while (true) {
|
||||
try {
|
||||
MessageTokenPtr message;
|
||||
{
|
||||
MutexLocker locker(m_mainLock);
|
||||
if (m_stop)
|
||||
break;
|
||||
for (auto& handler : m_handlers) {
|
||||
if (handler->isClosed())
|
||||
cleanup.append(handler);
|
||||
}
|
||||
m_handlers.removeAll(cleanup);
|
||||
if (m_messages.empty() && cleanup.empty())
|
||||
m_signal.wait(m_mainLock);
|
||||
if (!m_messages.empty())
|
||||
message = m_messages.takeFirst();
|
||||
}
|
||||
cleanup.clear();
|
||||
if (message) {
|
||||
try {
|
||||
handleMessage(message);
|
||||
if (!message->hasResponse())
|
||||
message->setResponse("");
|
||||
}
|
||||
catch (std::exception const&) {
|
||||
if (!message->hasResponse())
|
||||
message->setResponse("");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::exception const& e) {
|
||||
Logger::error("AuthenticationServer exception caught: %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
Logger::info("Finishing remaining connections.");
|
||||
|
||||
try {
|
||||
MutexLocker locker(m_mainLock);
|
||||
m_stop = true;
|
||||
tcpServer.close();
|
||||
} catch (std::exception const& e) {
|
||||
Logger::error("AuthenticationServer exception caught cleaning up: %s", e.what());
|
||||
}
|
||||
|
||||
{
|
||||
MutexLocker locker(m_mainLock);
|
||||
for (auto & message : m_messages)
|
||||
message->setResponse("stopping");
|
||||
m_messages.clear();
|
||||
}
|
||||
|
||||
// locking should no longer be needed at this point
|
||||
// unused to avoid deadlock scenario where the handler calls addMessage
|
||||
for (auto& handler : m_handlers)
|
||||
handler->stop();
|
||||
m_handlers.clear();
|
||||
}
|
||||
|
||||
void AuthenticationServer::stop() {
|
||||
Logger::info("Auth server stopping.");
|
||||
MutexLocker locker(m_mainLock);
|
||||
m_stop = true;
|
||||
m_signal.signal();
|
||||
}
|
||||
|
||||
void AuthenticationServer::addMessage(MessageTokenPtr const& message) {
|
||||
MutexLocker locker(m_mainLock);
|
||||
m_requestCount++;
|
||||
if (m_stop) {
|
||||
if (message)
|
||||
message->setResponse("stopped");
|
||||
} else {
|
||||
m_messages.append(message);
|
||||
m_signal.signal();
|
||||
}
|
||||
}
|
||||
|
||||
void AuthenticationServer::handleMessage(MessageTokenPtr const& message) {
|
||||
Variant command = Variant::parse(message->request());
|
||||
if (command.getString("command") == "getAuthKey") {
|
||||
m_getAuthKeyCounter++;
|
||||
auto response = m_service->getCertificate();
|
||||
message->setResponse(response.repr(0, true));
|
||||
} else if (command.getString("command") == "authorizeClient") {
|
||||
m_authorizeClientCounter++;
|
||||
try {
|
||||
auto response = m_service->authorizeClient(command.getMap("body"));
|
||||
message->setResponse(response.repr(0, true));
|
||||
}
|
||||
catch (...) {
|
||||
m_authorizeClientFailure++;
|
||||
throw;
|
||||
}
|
||||
} else if (command.getString("command") == "validateClient") {
|
||||
m_validateClientCounter++;
|
||||
try {
|
||||
auto response = m_service->validateClient(command.getMap("body"));
|
||||
message->setResponse(response.repr(0, true));
|
||||
}
|
||||
catch (...) {
|
||||
m_validateClientFailure++;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MessageToken::MessageToken(String const& request) {
|
||||
m_request = request;
|
||||
m_hasResponse = false;
|
||||
}
|
||||
|
||||
bool MessageToken::hasResponse() {
|
||||
MutexLocker locker(m_lock);
|
||||
return m_hasResponse;
|
||||
}
|
||||
|
||||
bool MessageToken::waitForResponse(unsigned millis) {
|
||||
MutexLocker locker(m_lock);
|
||||
int64_t timeout = millis;
|
||||
while (true) {
|
||||
if (m_hasResponse)
|
||||
return true;
|
||||
if (timeout <= 0)
|
||||
return false;
|
||||
int64_t startTime = Thread::currentTime();
|
||||
m_signal.wait(m_lock, (unsigned)timeout);
|
||||
timeout -= Thread::currentTime() - startTime;
|
||||
}
|
||||
}
|
||||
|
||||
void MessageToken::setResponse(String const& response) {
|
||||
{
|
||||
MutexLocker locker(m_lock);
|
||||
starAssert(!m_hasResponse);
|
||||
m_response = response;
|
||||
m_hasResponse = true;
|
||||
}
|
||||
m_signal.signal();
|
||||
}
|
||||
|
||||
String MessageToken::response() {
|
||||
MutexLocker locker(m_lock);
|
||||
starAssert(m_hasResponse);
|
||||
return m_response;
|
||||
}
|
||||
|
||||
String MessageToken::request() {
|
||||
return m_request;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
111
attic/authserver/StarAuthenticationServer.hpp
Normal file
111
attic/authserver/StarAuthenticationServer.hpp
Normal file
|
@ -0,0 +1,111 @@
|
|||
#ifndef _STAR_AUTHENTICATOR_SERVER_HPP_
|
||||
#define _STAR_AUTHENTICATOR_SERVER_HPP_
|
||||
|
||||
#include "StarAuthenticationKey.hpp"
|
||||
#include "StarAuthenticationService.hpp"
|
||||
#include "StarException.hpp"
|
||||
#include "StarVariant.hpp"
|
||||
#include "StarRoot.hpp"
|
||||
#include "StarTcp.hpp"
|
||||
#include "StarThread.hpp"
|
||||
#include "StarMunin.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
namespace Auth {
|
||||
|
||||
//todo, port 80, hide behind a normal httpd, etc.
|
||||
static int const ListenPort = 21027;
|
||||
static unsigned const ClientSocketTimeout = 10000; // 10 seconds
|
||||
static unsigned const ClientProcessingTimeout = 30000; // 30 seconds
|
||||
static int const BufferCapacity = 15*1024;
|
||||
|
||||
class AuthenticationServer;
|
||||
typedef shared_ptr<AuthenticationServer> AuthenticationServerPtr;
|
||||
|
||||
//todo: bad on purpose, replace with a httpd frontend and deliver zmq messages
|
||||
// interops with the main thread using MessageToken, suitable to attach to zmq style, think mongrel httpd
|
||||
class AuthenticationConnectionHandler: public Thread {
|
||||
public:
|
||||
AuthenticationConnectionHandler(TcpSocketPtr socket, AuthenticationServer* server);
|
||||
~AuthenticationConnectionHandler();
|
||||
|
||||
// requests a stop, doesn't actually stop the, for that you'll need to finalize or join()
|
||||
// which may take a long while due to socket operations not being interrupted.
|
||||
void stop();
|
||||
bool isClosed();
|
||||
|
||||
virtual void run();
|
||||
|
||||
private:
|
||||
TcpSocketPtr m_socket;
|
||||
AuthenticationServer* m_server;
|
||||
bool m_stop;
|
||||
bool m_closed;
|
||||
|
||||
Mutex m_lock;
|
||||
|
||||
void writeResponse(String const& response);
|
||||
void writeErrorResponse(String const& response);
|
||||
};
|
||||
typedef shared_ptr<AuthenticationConnectionHandler> AuthenticationConnectionHandlerPtr;
|
||||
|
||||
class MessageToken {
|
||||
public:
|
||||
MessageToken(String const& request);
|
||||
|
||||
bool hasResponse();
|
||||
bool waitForResponse(unsigned millis);
|
||||
|
||||
void setResponse(String const& response);
|
||||
|
||||
String request();
|
||||
String response();
|
||||
|
||||
private:
|
||||
Mutex m_lock;
|
||||
ConditionVariable m_signal;
|
||||
bool m_hasResponse;
|
||||
String m_request;
|
||||
String m_response;
|
||||
};
|
||||
typedef shared_ptr<MessageToken> MessageTokenPtr;
|
||||
|
||||
class AuthenticationServer {
|
||||
public:
|
||||
AuthenticationServer();
|
||||
~AuthenticationServer();
|
||||
|
||||
void run();
|
||||
void stop();
|
||||
|
||||
void addMessage(MessageTokenPtr const& message);
|
||||
|
||||
private:
|
||||
shared_ptr<AuthenticationService> m_service;
|
||||
|
||||
Set<AuthenticationConnectionHandlerPtr> m_handlers;
|
||||
List<MessageTokenPtr> m_messages;
|
||||
|
||||
ConditionVariable m_signal;
|
||||
Mutex m_mainLock;
|
||||
bool m_stop;
|
||||
MuninNodePtr m_munin;
|
||||
int m_requestCount;
|
||||
int m_getAuthKeyCounter;
|
||||
int m_authorizeClientCounter;
|
||||
int m_authorizeClientFailure;
|
||||
int m_validateClientCounter;
|
||||
int m_validateClientFailure;
|
||||
|
||||
void acceptConnection(TcpSocketPtr socket);
|
||||
|
||||
void handleMessage(MessageTokenPtr const& message);
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
83
attic/authserver/StarDatabaseConnector.cpp
Normal file
83
attic/authserver/StarDatabaseConnector.cpp
Normal file
|
@ -0,0 +1,83 @@
|
|||
#include "StarDatabaseConnector.hpp"
|
||||
#include "StarLogging.hpp"
|
||||
|
||||
//todo, ifdef for database type or something ?
|
||||
|
||||
#include <iostream>
|
||||
#include "libpq-fe.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Star {
|
||||
|
||||
class DatabaseConnectionImpl {
|
||||
public:
|
||||
PGconn* conn;
|
||||
};
|
||||
|
||||
DatabaseConnector::DatabaseConnector(String const& connectionString) {
|
||||
m_impl = new DatabaseConnectionImpl;
|
||||
m_impl->conn = PQconnectdb(connectionString.utf8Ptr());
|
||||
if (PQstatus(m_impl->conn) != CONNECTION_OK) {
|
||||
throw StarException(strf("Fail to connect to the database. %s", PQerrorMessage(m_impl->conn)));
|
||||
}
|
||||
}
|
||||
|
||||
DatabaseConnector::~DatabaseConnector() {
|
||||
PQfinish(m_impl->conn);
|
||||
delete m_impl;
|
||||
}
|
||||
|
||||
List<VariantMap> DatabaseConnector::fetch(String const& command, VariantList const& arguments) {
|
||||
int nParams = arguments.size();
|
||||
List<String> paramValuesBacking(nParams);
|
||||
DynamicArray<const char*> paramValues(nParams, (const char*)nullptr);
|
||||
for (int i = 0; i < nParams; i++) {
|
||||
if (arguments[i].type() == Variant::Type::BOOL)
|
||||
paramValuesBacking[i] = arguments[i].toBool()?"t":"f";
|
||||
else
|
||||
paramValuesBacking[i] = arguments[i].toString();
|
||||
paramValues[i] = paramValuesBacking[i].utf8Ptr();
|
||||
}
|
||||
auto res = PQexecParams(m_impl->conn, command.utf8Ptr(), nParams, NULL, paramValues.ptr(), NULL, NULL, 0);
|
||||
if (PQresultStatus(res) == PGRES_COMMAND_OK) {
|
||||
PQclear(res);
|
||||
return {};
|
||||
}
|
||||
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
|
||||
auto message = strf("Command failed. Error: (%d) %s", PQresultStatus(res), PQresultErrorMessage(res));
|
||||
PQclear(res);
|
||||
throw StarException(message);
|
||||
}
|
||||
List<String> columns;
|
||||
int nFields = PQnfields(res);
|
||||
for (int i = 0; i < nFields; i++)
|
||||
columns.append(PQfname(res, i));
|
||||
|
||||
List<VariantMap> result;
|
||||
for (int i = 0; i < PQntuples(res); i++)
|
||||
{
|
||||
result.append({});
|
||||
VariantMap& row = result.last();
|
||||
for (int j = 0; j < nFields; j++)
|
||||
row.insert(columns[j], PQgetvalue(res, i, j));
|
||||
}
|
||||
|
||||
PQclear(res);
|
||||
return result;
|
||||
}
|
||||
|
||||
VariantMap DatabaseConnector::fetchRow(String const& command, VariantList const& arguments) {
|
||||
auto result = fetch(command, arguments);
|
||||
if (result.size() != 1)
|
||||
throw StarException(strf("Command failed. Expected single row. Got %s", result.size()));
|
||||
return result[0];
|
||||
}
|
||||
|
||||
void DatabaseConnector::exec(String const& command, VariantList const& arguments) {
|
||||
auto result = fetch(command, arguments);
|
||||
if (result.size() != 0)
|
||||
throw StarException(strf("Command failed. Expected no result row. Got %s", result.size()));
|
||||
}
|
||||
|
||||
}
|
31
attic/authserver/StarDatabaseConnector.hpp
Normal file
31
attic/authserver/StarDatabaseConnector.hpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
#ifndef _STAR_DATABASE_CONNECTOR_HPP_
|
||||
#define _STAR_DATABASE_CONNECTOR_HPP_
|
||||
|
||||
#include "StarAuthenticationKey.hpp"
|
||||
#include "StarAuthenticationService.hpp"
|
||||
#include "StarException.hpp"
|
||||
#include "StarVariant.hpp"
|
||||
#include "StarRoot.hpp"
|
||||
#include "StarTcp.hpp"
|
||||
#include "StarThread.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
class DatabaseConnectionImpl;
|
||||
|
||||
class DatabaseConnector
|
||||
{
|
||||
public:
|
||||
DatabaseConnector(String const& connectionString);
|
||||
~DatabaseConnector();
|
||||
List<VariantMap> fetch(String const& command, VariantList const& arguments = VariantList());
|
||||
VariantMap fetchRow(String const& command, VariantList const& arguments = VariantList());
|
||||
void exec(String const& command, VariantList const& arguments = VariantList());
|
||||
|
||||
private:
|
||||
DatabaseConnectionImpl* m_impl;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
80
attic/authserver/keygen.cpp
Normal file
80
attic/authserver/keygen.cpp
Normal file
|
@ -0,0 +1,80 @@
|
|||
#include "StarString.hpp"
|
||||
#include "StarThread.hpp"
|
||||
#include "StarRandom.hpp"
|
||||
#include "StarAuthenticationKey.hpp"
|
||||
#include "StarAuthenticationService.hpp"
|
||||
#include "StarAuthenticationDatabase.hpp"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <ctime>
|
||||
|
||||
using namespace std;
|
||||
using namespace Star;
|
||||
|
||||
int usage(int rc) {
|
||||
std::cout
|
||||
<< "USAGE: " << std::endl
|
||||
<< " keygen rootkey <rootPrivateFile> <rootPublicFile>" << std::endl
|
||||
<< " keygen authkey <rootPrivateFile> <authPrivateFile>" << std::endl
|
||||
<< " keygen hashpass <username> <password>" << std::endl
|
||||
<< " keygen insertuser <username> <passhash>" << std::endl
|
||||
<< " keygen enableuser <username>" << std::endl
|
||||
<< " keygen disableuser <username>" << std::endl;
|
||||
return rc;
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv) {
|
||||
try {
|
||||
Random::randu32();
|
||||
StringList args;
|
||||
for (int i = 1; i < argc; i++)
|
||||
args.append(argv[i]);
|
||||
|
||||
String connectionString = Variant::parseJson(File::readFileString("connectionstring.config")).getString("connectionString");
|
||||
|
||||
if (argc == 4 && !String("rootkey").compare(args[0])) {
|
||||
Star::Auth::Key rootKey(true);
|
||||
std::ofstream oFile;
|
||||
oFile.open(args[1].utf8());
|
||||
oFile << rootKey.privateKey();
|
||||
oFile.close();
|
||||
oFile.open(args[2].utf8());
|
||||
oFile << rootKey.publicKey();
|
||||
oFile.close();
|
||||
return 0;
|
||||
} else if (argc == 4 && !String("authkey").compare(args[0])) {
|
||||
std::ifstream iFile(args[1].utf8());
|
||||
String rootPrivateKey;
|
||||
iFile >> rootPrivateKey;
|
||||
iFile.close();
|
||||
std::ofstream oFile(args[2].utf8());
|
||||
oFile << Star::Auth::AuthenticationService::generateAuthenticationConfig(rootPrivateKey, Thread::currentTime() - 1LL * 25LL * 60LL * 60LL * 1000LL, Thread::currentTime() + 38LL * 24LL * 60LL * 60LL * 1000LL).repr(0, true);
|
||||
oFile.close();
|
||||
return 0;
|
||||
} else if (argc == 4 && !String("hashpass").compare(args[0])) {
|
||||
cout << Auth::preHashPassword(args[1], args[2]);
|
||||
} else if (argc == 4 && !String("insertuser").compare(args[0])) {
|
||||
Auth::AuthenticationDatabase database(connectionString);
|
||||
database.setUserRecord(args[1], args[2], true);
|
||||
cout << "Enabled user: " << args[1] << "\n";
|
||||
} else if (argc == 3 && !String("disableuser").compare(args[0])) {
|
||||
Auth::AuthenticationDatabase database(connectionString);
|
||||
if (database.activateUserRecord(args[1], false))
|
||||
cout << "Disabled user: " << args[1] << "\n";
|
||||
else
|
||||
cout << "No such user\n";
|
||||
} else if (argc == 3 && !String("enableuser").compare(args[0])) {
|
||||
Auth::AuthenticationDatabase database(connectionString);
|
||||
if (database.activateUserRecord(args[1], true))
|
||||
cout << "Enabled user: " << args[1] << "\n";
|
||||
else
|
||||
cout << "No such user\n";
|
||||
} else {
|
||||
return usage(-1);
|
||||
}
|
||||
} catch (std::exception const& e) {
|
||||
cerr << e.what() << "\n";
|
||||
return -1;
|
||||
}
|
||||
}
|
40
attic/authserver/main.cpp
Normal file
40
attic/authserver/main.cpp
Normal file
|
@ -0,0 +1,40 @@
|
|||
#include "StarAuthenticationServer.hpp"
|
||||
#include "StarRandom.hpp"
|
||||
#include "StarLexicalCast.hpp"
|
||||
#include "StarLogging.hpp"
|
||||
#include "StarSignalHandler.hpp"
|
||||
|
||||
using namespace Star;
|
||||
using namespace Star::Auth;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
_unused(argc);
|
||||
_unused(argv);
|
||||
SignalHandler signalHandler;
|
||||
try {
|
||||
|
||||
Logger::addSink(make_shared<FileLogSink>("starbound_auth.log", Logger::Debug));
|
||||
|
||||
Thread::prepareThread();
|
||||
Auth::initializeKeyLogic();
|
||||
StarException::initialize();
|
||||
|
||||
Logger::info("Auth server starting.");
|
||||
|
||||
auto authServer = make_shared<AuthenticationServer>();
|
||||
signalHandler.setInterruptHandler([&authServer]() {
|
||||
Logger::info("Interrupt received.");
|
||||
if (authServer)
|
||||
authServer->stop();
|
||||
});
|
||||
|
||||
authServer->run();
|
||||
authServer.reset();
|
||||
|
||||
Logger::info("Server shutdown gracefully");
|
||||
} catch (std::exception const& e) {
|
||||
fatalException(e);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue