Added token generation, user activation command and response.

This commit is contained in:
Fabio Bas 2015-05-24 00:37:45 +02:00
parent 42796b0d0e
commit ff1aed717e
19 changed files with 223 additions and 19 deletions

View file

@ -30,6 +30,7 @@ enum ClientStatus {
StatusDisconnecting, StatusDisconnecting,
StatusConnecting, StatusConnecting,
StatusRegistering, StatusRegistering,
StatusActivating,
StatusLoggingIn, StatusLoggingIn,
StatusLoggedIn, StatusLoggedIn,
}; };
@ -60,6 +61,8 @@ signals:
void ignoreListReceived(const QList<ServerInfo_User> &ignoreList); void ignoreListReceived(const QList<ServerInfo_User> &ignoreList);
void replayAddedEventReceived(const Event_ReplayAdded &event); void replayAddedEventReceived(const Event_ReplayAdded &event);
void registerAccepted(); void registerAccepted();
void registerAcceptedNeedsActivate();
void activateAccepted();
void sigQueuePendingCommand(PendingCommand *pend); void sigQueuePendingCommand(PendingCommand *pend);
private: private:
@ -72,7 +75,7 @@ protected slots:
void processProtocolItem(const ServerMessage &item); void processProtocolItem(const ServerMessage &item);
protected: protected:
QMap<int, PendingCommand *> pendingCommands; QMap<int, PendingCommand *> pendingCommands;
QString userName, password, email, country, realName; QString userName, password, email, country, realName, token;
int gender; int gender;
void setStatus(ClientStatus _status); void setStatus(ClientStatus _status);
int getNewCmdId() { return nextCmdId++; } int getNewCmdId() { return nextCmdId++; }

View file

@ -7,6 +7,7 @@
#include "pb/session_commands.pb.h" #include "pb/session_commands.pb.h"
#include "pb/response_login.pb.h" #include "pb/response_login.pb.h"
#include "pb/response_register.pb.h" #include "pb/response_register.pb.h"
#include "pb/response_activate.pb.h"
#include "pb/server_message.pb.h" #include "pb/server_message.pb.h"
#include "pb/event_server_identification.pb.h" #include "pb/event_server_identification.pb.h"
@ -30,6 +31,7 @@ RemoteClient::RemoteClient(QObject *parent)
connect(this, SIGNAL(sigConnectToServer(QString, unsigned int, QString, QString)), this, SLOT(doConnectToServer(QString, unsigned int, QString, QString))); connect(this, SIGNAL(sigConnectToServer(QString, unsigned int, QString, QString)), this, SLOT(doConnectToServer(QString, unsigned int, QString, QString)));
connect(this, SIGNAL(sigDisconnectFromServer()), this, SLOT(doDisconnectFromServer())); connect(this, SIGNAL(sigDisconnectFromServer()), this, SLOT(doDisconnectFromServer()));
connect(this, SIGNAL(sigRegisterToServer(QString, unsigned int, QString, QString, QString, int, QString, QString)), this, SLOT(doRegisterToServer(QString, unsigned int, QString, QString, QString, int, QString, QString))); connect(this, SIGNAL(sigRegisterToServer(QString, unsigned int, QString, QString, QString, int, QString, QString)), this, SLOT(doRegisterToServer(QString, unsigned int, QString, QString, QString, int, QString, QString)));
connect(this, SIGNAL(sigActivateToServer(QString)), this, SLOT(doActivateToServer(QString)));
} }
RemoteClient::~RemoteClient() RemoteClient::~RemoteClient()
@ -81,6 +83,24 @@ void RemoteClient::processServerIdentificationEvent(const Event_ServerIdentifica
return; return;
} }
if(getStatus() == StatusActivating)
{
Command_Activate cmdActivate;
cmdActivate.set_user_name(userName.toStdString());
cmdActivate.set_token(token.toStdString());
PendingCommand *pend = prepareSessionCommand(cmdActivate);
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(activateResponse(Response)));
sendCommand(pend);
return;
}
doLogin();
}
void RemoteClient::doLogin()
{
setStatus(StatusLoggingIn); setStatus(StatusLoggingIn);
Command_Login cmdLogin; Command_Login cmdLogin;
@ -122,13 +142,34 @@ void RemoteClient::loginResponse(const Response &response)
void RemoteClient::registerResponse(const Response &response) void RemoteClient::registerResponse(const Response &response)
{ {
const Response_Register &resp = response.GetExtension(Response_Register::ext); const Response_Register &resp = response.GetExtension(Response_Register::ext);
if (response.response_code() == Response::RespRegistrationAccepted) { switch(response.response_code())
emit registerAccepted(); {
} else { case Response::RespRegistrationAccepted:
emit registerError(response.response_code(), QString::fromStdString(resp.denied_reason_str()), resp.denied_end_time()); emit registerAccepted();
doLogin();
break;
case Response::RespRegistrationAcceptedNeedsActivation:
emit registerAcceptedNeedsActivate();
doLogin();
break;
default:
emit registerError(response.response_code(), QString::fromStdString(resp.denied_reason_str()), resp.denied_end_time());
setStatus(StatusDisconnecting);
doDisconnectFromServer();
break;
}
}
void RemoteClient::activateResponse(const Response &response)
{
const Response_Activate &resp = response.GetExtension(Response_Activate::ext);
if (response.response_code() == Response::RespActivationAccepted) {
emit activateAccepted();
doLogin();
} else {
emit activateError();
} }
setStatus(StatusDisconnecting);
doDisconnectFromServer();
} }
void RemoteClient::readData() void RemoteClient::readData()
@ -201,6 +242,9 @@ void RemoteClient::doConnectToServer(const QString &hostname, unsigned int port,
userName = _userName; userName = _userName;
password = _password; password = _password;
lastHostname = hostname;
lastPort = port;
socket->connectToHost(hostname, port); socket->connectToHost(hostname, port);
setStatus(StatusConnecting); setStatus(StatusConnecting);
} }
@ -215,11 +259,23 @@ void RemoteClient::doRegisterToServer(const QString &hostname, unsigned int port
gender = _gender; gender = _gender;
country = _country; country = _country;
realName = _realname; realName = _realname;
lastHostname = hostname;
lastPort = port;
socket->connectToHost(hostname, port); socket->connectToHost(hostname, port);
setStatus(StatusRegistering); setStatus(StatusRegistering);
} }
void RemoteClient::doActivateToServer(const QString &_token)
{
doDisconnectFromServer();
token = _token;
socket->connectToHost(lastHostname, lastPort);
setStatus(StatusActivating);
}
void RemoteClient::doDisconnectFromServer() void RemoteClient::doDisconnectFromServer()
{ {
timer->stop(); timer->stop();
@ -275,6 +331,11 @@ void RemoteClient::registerToServer(const QString &hostname, unsigned int port,
emit sigRegisterToServer(hostname, port, _userName, _password, _email, _gender, _country, _realname); emit sigRegisterToServer(hostname, port, _userName, _password, _email, _gender, _country, _realname);
} }
void RemoteClient::activateToServer(const QString &_token)
{
emit sigActivateToServer(_token);
}
void RemoteClient::disconnectFromServer() void RemoteClient::disconnectFromServer()
{ {
emit sigDisconnectFromServer(); emit sigDisconnectFromServer();

View file

@ -13,11 +13,13 @@ signals:
void serverTimeout(); void serverTimeout();
void loginError(Response::ResponseCode resp, QString reasonStr, quint32 endTime); void loginError(Response::ResponseCode resp, QString reasonStr, quint32 endTime);
void registerError(Response::ResponseCode resp, QString reasonStr, quint32 endTime); void registerError(Response::ResponseCode resp, QString reasonStr, quint32 endTime);
void activateError();
void socketError(const QString &errorString); void socketError(const QString &errorString);
void protocolVersionMismatch(int clientVersion, int serverVersion); void protocolVersionMismatch(int clientVersion, int serverVersion);
void protocolError(); void protocolError();
void sigConnectToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password); void sigConnectToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password);
void sigRegisterToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password, const QString &_email, const int _gender, const QString &_country, const QString &_realname); void sigRegisterToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password, const QString &_email, const int _gender, const QString &_country, const QString &_realname);
void sigActivateToServer(const QString &_token);
void sigDisconnectFromServer(); void sigDisconnectFromServer();
private slots: private slots:
void slotConnected(); void slotConnected();
@ -28,9 +30,13 @@ private slots:
void processConnectionClosedEvent(const Event_ConnectionClosed &event); void processConnectionClosedEvent(const Event_ConnectionClosed &event);
void loginResponse(const Response &response); void loginResponse(const Response &response);
void registerResponse(const Response &response); void registerResponse(const Response &response);
void activateResponse(const Response &response);
void doConnectToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password); void doConnectToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password);
void doRegisterToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password, const QString &_email, const int _gender, const QString &_country, const QString &_realname); void doRegisterToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password, const QString &_email, const int _gender, const QString &_country, const QString &_realname);
void doLogin();
void doDisconnectFromServer(); void doDisconnectFromServer();
void doActivateToServer(const QString &_token);
private: private:
static const int maxTimeout = 10; static const int maxTimeout = 10;
int timeRunning, lastDataReceived; int timeRunning, lastDataReceived;
@ -42,6 +48,8 @@ private:
QTimer *timer; QTimer *timer;
QTcpSocket *socket; QTcpSocket *socket;
QString lastHostname;
int lastPort;
protected slots: protected slots:
void sendCommandContainer(const CommandContainer &cont); void sendCommandContainer(const CommandContainer &cont);
public: public:
@ -50,7 +58,7 @@ public:
QString peerName() const { return socket->peerName(); } QString peerName() const { return socket->peerName(); }
void connectToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password); void connectToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password);
void registerToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password, const QString &_email, const int _gender, const QString &_country, const QString &_realname); void registerToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password, const QString &_email, const int _gender, const QString &_country, const QString &_realname);
void activateToServer(const QString &_token);
void disconnectFromServer(); void disconnectFromServer();
}; };

View file

@ -126,8 +126,17 @@ void MainWindow::userInfoReceived(const ServerInfo_User &info)
void MainWindow::registerAccepted() void MainWindow::registerAccepted()
{ {
QMessageBox::information(this, tr("Success"), tr("Registration accepted.\nNow check your email for instructions on how to activate your account.")); QMessageBox::information(this, tr("Success"), tr("Registration accepted.\nWill now login."));
actConnect(); }
void MainWindow::registerAcceptedNeedsActivate()
{
// nothing
}
void MainWindow::activateAccepted()
{
QMessageBox::information(this, tr("Success"), tr("Account activation accepted.\nWill now login."));
} }
// Actions // Actions
@ -299,11 +308,20 @@ void MainWindow::loginError(Response::ResponseCode r, QString reasonStr, quint32
actRegister(); actRegister();
} }
break; break;
case Response::RespAccountNotActivated: case Response::RespAccountNotActivated: {
QMessageBox::critical(this, tr("Error"), tr("Your account has not been activated yet.")); bool ok = false;
QString token = QInputDialog::getText(this, tr("Account activation"), tr("Your account has not been activated yet.\n You need to provide the activation token received in the activation email"), QLineEdit::Normal, QString(), &ok);
if(ok && !token.isEmpty())
{
client->activateToServer(token);
return;
}
client->disconnectFromServer();
break; break;
}
default: default:
QMessageBox::critical(this, tr("Error"), tr("Unknown login error: %1").arg(static_cast<int>(r))); QMessageBox::critical(this, tr("Error"), tr("Unknown login error: %1").arg(static_cast<int>(r)));
break;
} }
actConnect(); actConnect();
} }
@ -350,6 +368,13 @@ void MainWindow::registerError(Response::ResponseCode r, QString reasonStr, quin
actRegister(); actRegister();
} }
void MainWindow::activateError()
{
QMessageBox::critical(this, tr("Error"), tr("Account activation failed"));
client->disconnectFromServer();
actConnect();
}
void MainWindow::socketError(const QString &errorStr) void MainWindow::socketError(const QString &errorStr)
{ {
QMessageBox::critical(this, tr("Error"), tr("Socket error: %1").arg(errorStr)); QMessageBox::critical(this, tr("Error"), tr("Socket error: %1").arg(errorStr));
@ -488,7 +513,10 @@ MainWindow::MainWindow(QWidget *parent)
connect(client, SIGNAL(userInfoChanged(const ServerInfo_User &)), this, SLOT(userInfoReceived(const ServerInfo_User &)), Qt::BlockingQueuedConnection); connect(client, SIGNAL(userInfoChanged(const ServerInfo_User &)), this, SLOT(userInfoReceived(const ServerInfo_User &)), Qt::BlockingQueuedConnection);
connect(client, SIGNAL(registerAccepted()), this, SLOT(registerAccepted())); connect(client, SIGNAL(registerAccepted()), this, SLOT(registerAccepted()));
connect(client, SIGNAL(registerAcceptedNeedsActivate()), this, SLOT(registerAcceptedNeedsActivate()));
connect(client, SIGNAL(registerError(Response::ResponseCode, QString, quint32)), this, SLOT(registerError(Response::ResponseCode, QString, quint32))); connect(client, SIGNAL(registerError(Response::ResponseCode, QString, quint32)), this, SLOT(registerError(Response::ResponseCode, QString, quint32)));
connect(client, SIGNAL(activateAccepted()), this, SLOT(activateAccepted()));
connect(client, SIGNAL(activateError()), this, SLOT(activateError()));
clientThread = new QThread(this); clientThread = new QThread(this);
client->moveToThread(clientThread); client->moveToThread(clientThread);

View file

@ -44,10 +44,13 @@ private slots:
void serverTimeout(); void serverTimeout();
void loginError(Response::ResponseCode r, QString reasonStr, quint32 endTime); void loginError(Response::ResponseCode r, QString reasonStr, quint32 endTime);
void registerError(Response::ResponseCode r, QString reasonStr, quint32 endTime); void registerError(Response::ResponseCode r, QString reasonStr, quint32 endTime);
void activateError();
void socketError(const QString &errorStr); void socketError(const QString &errorStr);
void protocolVersionMismatch(int localVersion, int remoteVersion); void protocolVersionMismatch(int localVersion, int remoteVersion);
void userInfoReceived(const ServerInfo_User &userInfo); void userInfoReceived(const ServerInfo_User &userInfo);
void registerAccepted(); void registerAccepted();
void registerAcceptedNeedsActivate();
void activateAccepted();
void localGameEnded(); void localGameEnded();
void pixmapCacheSizeChanged(int newSizeInMBs); void pixmapCacheSizeChanged(int newSizeInMBs);

View file

@ -114,6 +114,7 @@ SET(PROTO_FILES
isl_message.proto isl_message.proto
moderator_commands.proto moderator_commands.proto
move_card_to_zone.proto move_card_to_zone.proto
response_activate.proto
response_deck_download.proto response_deck_download.proto
response_deck_list.proto response_deck_list.proto
response_deck_upload.proto response_deck_upload.proto

View file

@ -32,6 +32,9 @@ message Response {
RespAccountNotActivated = 28; // Client attempted to log into a registered username but the account hasn't been activated RespAccountNotActivated = 28; // Client attempted to log into a registered username but the account hasn't been activated
RespRegistrationDisabled = 29; // Server does not allow clients to register RespRegistrationDisabled = 29; // Server does not allow clients to register
RespRegistrationFailed = 30; // Server accepted a reg request but failed to perform the registration RespRegistrationFailed = 30; // Server accepted a reg request but failed to perform the registration
RespActivationAccepted = 31; // Server accepted a reg user activation token
RespActivationFailed = 32; // Server didn't accept a reg user activation token
RespRegistrationAcceptedNeedsActivation = 33; // Server accepted cient registration, but it will need token activation
} }
enum ResponseType { enum ResponseType {
JOIN_ROOM = 1000; JOIN_ROOM = 1000;
@ -44,6 +47,7 @@ message Response {
DECK_DOWNLOAD = 1007; DECK_DOWNLOAD = 1007;
DECK_UPLOAD = 1008; DECK_UPLOAD = 1008;
REGISTER = 1009; REGISTER = 1009;
ACTIVATE = 1010;
REPLAY_LIST = 1100; REPLAY_LIST = 1100;
REPLAY_DOWNLOAD = 1101; REPLAY_DOWNLOAD = 1101;
} }

View file

@ -0,0 +1,7 @@
import "response.proto";
message Response_Activate {
extend Response {
optional Response_Activate ext = 1010;
}
}

View file

@ -19,6 +19,7 @@ message SessionCommand {
LIST_ROOMS = 1014; LIST_ROOMS = 1014;
JOIN_ROOM = 1015; JOIN_ROOM = 1015;
REGISTER = 1016; REGISTER = 1016;
ACTIVATE = 1017;
REPLAY_LIST = 1100; REPLAY_LIST = 1100;
REPLAY_DOWNLOAD = 1101; REPLAY_DOWNLOAD = 1101;
REPLAY_MODIFY_MATCH = 1102; REPLAY_MODIFY_MATCH = 1102;
@ -115,3 +116,14 @@ message Command_Register {
optional string country = 5; optional string country = 5;
optional string real_name = 6; optional string real_name = 6;
} }
// User wants to activate an account
message Command_Activate {
extend SessionCommand {
optional Command_Activate ext = 1017;
}
// User name client wants to activate
required string user_name = 1;
// Activation token
required string token = 2;
}

View file

@ -208,9 +208,22 @@ RegistrationResult Server::registerUserAccount(const QString &ipAddress, const C
if(password.length() < 6) if(password.length() < 6)
return PasswordTooShort; return PasswordTooShort;
bool regSucceeded = databaseInterface->registerUser(userName, realName, gender, password, emailAddress, country, false); bool regSucceeded = databaseInterface->registerUser(userName, realName, gender, password, emailAddress, country, !requireEmailForRegistration);
return regSucceeded ? Accepted : Failed; if(regSucceeded)
return requireEmailForRegistration ? AcceptedNeedsActivation : Accepted;
else
return Failed;
}
bool Server::activateUserAccount(const Command_Activate &cmd)
{
QString userName = QString::fromStdString(cmd.user_name());
QString token = QString::fromStdString(cmd.token());
Server_DatabaseInterface *databaseInterface = getDatabaseInterface();
return databaseInterface->activateUser(userName, token);
} }
bool Server::tooManyRegistrationAttempts(const QString &ipAddress) bool Server::tooManyRegistrationAttempts(const QString &ipAddress)

View file

@ -29,7 +29,7 @@ class CommandContainer;
class Command_JoinGame; class Command_JoinGame;
enum AuthenticationResult { NotLoggedIn, PasswordRight, UnknownUser, WouldOverwriteOldSession, UserIsBanned, UsernameInvalid, RegistrationRequired, UserIsInactive }; enum AuthenticationResult { NotLoggedIn, PasswordRight, UnknownUser, WouldOverwriteOldSession, UserIsBanned, UsernameInvalid, RegistrationRequired, UserIsInactive };
enum RegistrationResult { Accepted, UserAlreadyExists, EmailRequired, TooManyRequests, InvalidUsername, ClientIsBanned, RegistrationDisabled, Failed, PasswordTooShort }; enum RegistrationResult { Accepted, UserAlreadyExists, EmailRequired, TooManyRequests, InvalidUsername, ClientIsBanned, RegistrationDisabled, Failed, PasswordTooShort, AcceptedNeedsActivation };
class Server : public QObject class Server : public QObject
{ {
@ -57,6 +57,7 @@ public:
* @return RegistrationResult member indicating whether it succeeded or failed. * @return RegistrationResult member indicating whether it succeeded or failed.
*/ */
RegistrationResult registerUserAccount(const QString &ipAddress, const Command_Register &cmd, QString &banReason, int &banSecondsRemaining); RegistrationResult registerUserAccount(const QString &ipAddress, const Command_Register &cmd, QString &banReason, int &banSecondsRemaining);
bool activateUserAccount(const Command_Activate &cmd);
bool tooManyRegistrationAttempts(const QString &ipAddress); bool tooManyRegistrationAttempts(const QString &ipAddress);
const QMap<int, Server_Room *> &getRooms() { return rooms; } const QMap<int, Server_Room *> &getRooms() { return rooms; }

View file

@ -39,6 +39,7 @@ public:
virtual bool getRequireRegistration() { return false; } virtual bool getRequireRegistration() { return false; }
virtual bool registerUser(const QString & /* userName */, const QString & /* realName */, ServerInfo_User_Gender const & /* gender */, const QString & /* password */, const QString & /* emailAddress */, const QString & /* country */, bool /* active = false */) { return false; } virtual bool registerUser(const QString & /* userName */, const QString & /* realName */, ServerInfo_User_Gender const & /* gender */, const QString & /* password */, const QString & /* emailAddress */, const QString & /* country */, bool /* active = false */) { return false; }
virtual bool activateUser(const QString & /* userName */, const QString & /* token */) { return false; }
enum LogMessage_TargetType { MessageTargetRoom, MessageTargetGame, MessageTargetChat, MessageTargetIslRoom }; enum LogMessage_TargetType { MessageTargetRoom, MessageTargetGame, MessageTargetChat, MessageTargetIslRoom };
virtual void logMessage(const int /* senderId */, const QString & /* senderName */, const QString & /* senderIp */, const QString & /* logMessage */, LogMessage_TargetType /* targetType */, const int /* targetId */, const QString & /* targetName */) { }; virtual void logMessage(const int /* senderId */, const QString & /* senderName */, const QString & /* senderIp */, const QString & /* logMessage */, LogMessage_TargetType /* targetType */, const int /* targetId */, const QString & /* targetName */) { };

View file

@ -146,6 +146,7 @@ Response::ResponseCode Server_ProtocolHandler::processSessionCommandContainer(co
case SessionCommand::PING: resp = cmdPing(sc.GetExtension(Command_Ping::ext), rc); break; case SessionCommand::PING: resp = cmdPing(sc.GetExtension(Command_Ping::ext), rc); break;
case SessionCommand::LOGIN: resp = cmdLogin(sc.GetExtension(Command_Login::ext), rc); break; case SessionCommand::LOGIN: resp = cmdLogin(sc.GetExtension(Command_Login::ext), rc); break;
case SessionCommand::REGISTER: resp = cmdRegisterAccount(sc.GetExtension(Command_Register::ext), rc); break; case SessionCommand::REGISTER: resp = cmdRegisterAccount(sc.GetExtension(Command_Register::ext), rc); break;
case SessionCommand::ACTIVATE: resp = cmdActivateAccount(sc.GetExtension(Command_Activate::ext), rc); break;
case SessionCommand::MESSAGE: resp = cmdMessage(sc.GetExtension(Command_Message::ext), rc); break; case SessionCommand::MESSAGE: resp = cmdMessage(sc.GetExtension(Command_Message::ext), rc); break;
case SessionCommand::GET_GAMES_OF_USER: resp = cmdGetGamesOfUser(sc.GetExtension(Command_GetGamesOfUser::ext), rc); break; case SessionCommand::GET_GAMES_OF_USER: resp = cmdGetGamesOfUser(sc.GetExtension(Command_GetGamesOfUser::ext), rc); break;
case SessionCommand::GET_USER_INFO: resp = cmdGetUserInfo(sc.GetExtension(Command_GetUserInfo::ext), rc); break; case SessionCommand::GET_USER_INFO: resp = cmdGetUserInfo(sc.GetExtension(Command_GetUserInfo::ext), rc); break;
@ -439,6 +440,9 @@ Response::ResponseCode Server_ProtocolHandler::cmdRegisterAccount(const Command_
return Response::RespRegistrationDisabled; return Response::RespRegistrationDisabled;
case Accepted: case Accepted:
return Response::RespRegistrationAccepted; return Response::RespRegistrationAccepted;
case AcceptedNeedsActivation:
// TODO SEND EMAIL WITH TOKEN TO THE USER
return Response::RespRegistrationAcceptedNeedsActivation;
case UserAlreadyExists: case UserAlreadyExists:
return Response::RespUserAlreadyExists; return Response::RespUserAlreadyExists;
case EmailRequired: case EmailRequired:
@ -463,6 +467,18 @@ Response::ResponseCode Server_ProtocolHandler::cmdRegisterAccount(const Command_
return Response::RespInvalidCommand; return Response::RespInvalidCommand;
} }
Response::ResponseCode Server_ProtocolHandler::cmdActivateAccount(const Command_Activate &cmd, ResponseContainer &rc)
{
if(server->activateUserAccount(cmd))
{
qDebug() << "Accepted activation for user" << QString::fromStdString(cmd.user_name());
return Response::RespActivationAccepted;
} else {
qDebug() << "Failed activation for user" << QString::fromStdString(cmd.user_name());
return Response::RespActivationFailed;
}
}
Response::ResponseCode Server_ProtocolHandler::cmdMessage(const Command_Message &cmd, ResponseContainer &rc) Response::ResponseCode Server_ProtocolHandler::cmdMessage(const Command_Message &cmd, ResponseContainer &rc)
{ {
if (authState == NotLoggedIn) if (authState == NotLoggedIn)

View file

@ -61,6 +61,7 @@ private:
Response::ResponseCode cmdPing(const Command_Ping &cmd, ResponseContainer &rc); Response::ResponseCode cmdPing(const Command_Ping &cmd, ResponseContainer &rc);
Response::ResponseCode cmdLogin(const Command_Login &cmd, ResponseContainer &rc); Response::ResponseCode cmdLogin(const Command_Login &cmd, ResponseContainer &rc);
Response::ResponseCode cmdRegisterAccount(const Command_Register &cmd, ResponseContainer &rc); Response::ResponseCode cmdRegisterAccount(const Command_Register &cmd, ResponseContainer &rc);
Response::ResponseCode cmdActivateAccount(const Command_Activate &cmd, ResponseContainer &rc);
Response::ResponseCode cmdMessage(const Command_Message &cmd, ResponseContainer &rc); Response::ResponseCode cmdMessage(const Command_Message &cmd, ResponseContainer &rc);
Response::ResponseCode cmdGetGamesOfUser(const Command_GetGamesOfUser &cmd, ResponseContainer &rc); Response::ResponseCode cmdGetGamesOfUser(const Command_GetGamesOfUser &cmd, ResponseContainer &rc);
Response::ResponseCode cmdGetUserInfo(const Command_GetUserInfo &cmd, ResponseContainer &rc); Response::ResponseCode cmdGetUserInfo(const Command_GetUserInfo &cmd, ResponseContainer &rc);

View file

@ -60,7 +60,9 @@ regonly=0
; Enable this feature? Default false. ; Enable this feature? Default false.
;enabled=false ;enabled=false
; Require users to provide an email address in order to register. Default true. ; Require users to provide an email address in order to register. Newly registered users will receive an
; activation token by email, and will be required to input back this token on cockatrice at the first login
; to get their account activated. Default true.
;requireemail=true ;requireemail=true

View file

@ -70,3 +70,8 @@ QString PasswordHasher::generateRandomSalt(const int len)
return ret; return ret;
} }
QString PasswordHasher::generateActivationToken()
{
return QCryptographicHash::hash(generateRandomSalt().toUtf8(), QCryptographicHash::Md5).toBase64().left(16);
}

View file

@ -8,6 +8,7 @@ public:
static void initialize(); static void initialize();
static QString computeHash(const QString &password, const QString &salt); static QString computeHash(const QString &password, const QString &salt);
static QString generateRandomSalt(const int len = 16); static QString generateRandomSalt(const int len = 16);
static QString generateActivationToken();
}; };
#endif #endif

View file

@ -117,11 +117,12 @@ bool Servatrice_DatabaseInterface::registerUser(const QString &userName, const Q
return false; return false;
QString passwordSha512 = PasswordHasher::computeHash(password, PasswordHasher::generateRandomSalt()); QString passwordSha512 = PasswordHasher::computeHash(password, PasswordHasher::generateRandomSalt());
QString token = PasswordHasher::generateActivationToken();
QSqlQuery *query = prepareQuery("insert into {prefix}_users " QSqlQuery *query = prepareQuery("insert into {prefix}_users "
"(name, realname, gender, password_sha512, email, country, registrationDate, active) " "(name, realname, gender, password_sha512, email, country, registrationDate, active, token) "
"values " "values "
"(:userName, :realName, :gender, :password_sha512, :email, :country, UTC_TIMESTAMP(), :active)"); "(:userName, :realName, :gender, :password_sha512, :email, :country, UTC_TIMESTAMP(), :active, :token)");
query->bindValue(":userName", userName); query->bindValue(":userName", userName);
query->bindValue(":realName", realName); query->bindValue(":realName", realName);
query->bindValue(":gender", getGenderChar(gender)); query->bindValue(":gender", getGenderChar(gender));
@ -129,6 +130,7 @@ bool Servatrice_DatabaseInterface::registerUser(const QString &userName, const Q
query->bindValue(":email", emailAddress); query->bindValue(":email", emailAddress);
query->bindValue(":country", country); query->bindValue(":country", country);
query->bindValue(":active", active ? 1 : 0); query->bindValue(":active", active ? 1 : 0);
query->bindValue(":token", token);
if (!execSqlQuery(query)) { if (!execSqlQuery(query)) {
qDebug() << "Failed to insert user: " << query->lastError() << " sql: " << query->lastQuery(); qDebug() << "Failed to insert user: " << query->lastError() << " sql: " << query->lastQuery();
@ -138,6 +140,40 @@ bool Servatrice_DatabaseInterface::registerUser(const QString &userName, const Q
return true; return true;
} }
bool Servatrice_DatabaseInterface::activateUser(const QString &userName, const QString &token)
{
if (!checkSql())
return false;
QSqlQuery *activateQuery = prepareQuery("select name from {prefix}_users where active=0 and name=:username and token=:token");
activateQuery->bindValue(":username", userName);
activateQuery->bindValue(":token", token);
if (!execSqlQuery(activateQuery)) {
qDebug() << "Account activation failed: SQL error." << activateQuery->lastError()<< " sql: " << activateQuery->lastQuery();
return false;
}
if (activateQuery->next()) {
const QString name = activateQuery->value(0).toString();
// redundant check
if(name == userName)
{
QSqlQuery *query = prepareQuery("update {prefix}_users set active=1 where name = :userName");
query->bindValue(":userName", userName);
if (!execSqlQuery(query)) {
qDebug() << "Failed to activate user: " << query->lastError() << " sql: " << query->lastQuery();
return false;
}
return true;
}
}
return false;
}
QChar Servatrice_DatabaseInterface::getGenderChar(ServerInfo_User_Gender const &gender) QChar Servatrice_DatabaseInterface::getGenderChar(ServerInfo_User_Gender const &gender)
{ {
switch (gender) { switch (gender) {

View file

@ -65,6 +65,7 @@ public:
bool getRequireRegistration(); bool getRequireRegistration();
bool registerUser(const QString &userName, const QString &realName, ServerInfo_User_Gender const &gender, const QString &password, const QString &emailAddress, const QString &country, bool active = false); bool registerUser(const QString &userName, const QString &realName, ServerInfo_User_Gender const &gender, const QString &password, const QString &emailAddress, const QString &country, bool active = false);
bool activateUser(const QString &userName, const QString &token);
void logMessage(const int senderId, const QString &senderName, const QString &senderIp, const QString &logMessage, LogMessage_TargetType targetType, const int targetId, const QString &targetName); void logMessage(const int senderId, const QString &senderName, const QString &senderIp, const QString &logMessage, LogMessage_TargetType targetType, const int targetId, const QString &targetName);
}; };