register and send verification email

This commit is contained in:
Dario 2019-09-30 14:26:25 +02:00
parent d3b9352d51
commit 2a1938165c
22 changed files with 601 additions and 117 deletions

View File

@ -9,9 +9,11 @@
#include "Poco/Util/HelpFormatter.h"
#include "Poco/Net/ServerSocket.h"
#include "Poco/Net/HTTPServer.h"
#include "Poco/Net/SSLManager.h"
#include "Poco/Environment.h"
#include "MySQL/Poco/Connector.h"
#include <sodium.h>
@ -82,8 +84,14 @@ int Gradido_LoginServer::main(const std::vector<std::string>& args)
return Application::EXIT_CONFIG;
}
ServerConfig::initEMailAccount(config());
// start cpu scheduler
ServerConfig::g_CPUScheduler = new UniLib::controller::CPUSheduler(Poco::Environment::processorCount(), "Login Worker");
unsigned int worker_count = Poco::Environment::processorCount();
if (worker_count > 1) {
worker_count--;
}
ServerConfig::g_CPUScheduler = new UniLib::controller::CPUSheduler(worker_count, "Login Worker");
// load up connection configs
// register MySQL connector
@ -98,6 +106,9 @@ int Gradido_LoginServer::main(const std::vector<std::string>& args)
// put urandom on linux servers
//srand();
Poco::Net::initializeSSL();
ServerConfig::initSSLClientContext();
// set-up a server socket
Poco::Net::ServerSocket svs(port);
// set-up a HTTPServer instance
@ -106,9 +117,11 @@ int Gradido_LoginServer::main(const std::vector<std::string>& args)
srv.start();
// wait for CTRL-C or kill
waitForTerminationRequest();
// Stop the HTTPServer
srv.stop();
ServerConfig::unload();
Poco::Net::uninitializeSSL();
}
return Application::EXIT_OK;
}

View File

@ -7,6 +7,7 @@
#include "RegisterPage.h"
#include "HandleFileRequest.h"
#include "DashboardPage.h"
#include "CheckEmailPage.h"
#include "../SingletonManager/SessionManager.h"
@ -37,6 +38,9 @@ Poco::Net::HTTPRequestHandler* PageRequestHandlerFactory::createRequestHandler(c
} catch (...) {}
auto sm = SessionManager::getInstance();
auto s = sm->getSession(session_id);
if (uri == "/checkEmail") {
return new CheckEmailPage(s);
}
if (s) {
return new DashboardPage(s);

View File

@ -66,31 +66,32 @@ void RegisterPage::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Ne
responseStream << "\t";
#line 48 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\register.cpsp"
if(!form.empty() && userReturned) { responseStream << "\n";
responseStream << "\t\t\n";
responseStream << "\t\t<div class=\"grd_text-max-width\">\n";
responseStream << "\t\t\t<div class=\"grd_text\">\n";
responseStream << "\t\t\t\tSchreibe dir den Merkspruch auf und packe ihn gut weg. Du brauchst ihn um deine Adresse wiederherzustellen. Wenn du ihn verlierst, sind auch deine Gradidos verloren.\n";
responseStream << "\t\t\t</div>\n";
responseStream << "\t\t\t<div class=\"grd_textarea\">\n";
responseStream << "\t\t\t\t";
#line 54 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\register.cpsp"
responseStream << ( session->getPassphrase() );
responseStream << "\n";
responseStream << "\t\t\t\tDeine Anmeldung wird verarbeitet und es wird dir eine E-Mail zugeschickt. \n";
responseStream << "\t\t\t\tWenn sie da ist, befolge ihren Anweisungen. \n";
responseStream << "\t\t\t\tMöchtest du wissen ob die E-Mail schon verschickt wurde? \n";
responseStream << "\t\t\t\tDann klicke einfach hier: \n";
responseStream << "\t\t\t\t<form action=\"/\">\n";
responseStream << "\t\t\t\t\t<input type=\"submit\" value=\"Status überprüfen\">\n";
responseStream << "\t\t\t\t</form>\n";
responseStream << "\t\t\t</div>\n";
responseStream << "\t\t</div>\n";
responseStream << "\t";
#line 57 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\register.cpsp"
#line 61 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\register.cpsp"
} else { responseStream << "\n";
responseStream << "\t<form method=\"POST\">\n";
responseStream << "\t\n";
responseStream << "\t\t";
#line 60 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\register.cpsp"
#line 64 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\register.cpsp"
if(!form.empty() && !userReturned) { responseStream << "\n";
responseStream << "\t\t\t";
#line 61 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\register.cpsp"
#line 65 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\register.cpsp"
responseStream << ( session->getErrorsHtml() );
responseStream << "\n";
responseStream << "\t\t";
#line 62 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\register.cpsp"
#line 66 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\register.cpsp"
} responseStream << "\n";
responseStream << "\t\t<fieldset class=\"grd_container_small\">\n";
responseStream << "\t\t\t<legend>Account anlegen</legend>\n";
@ -98,14 +99,14 @@ void RegisterPage::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Ne
responseStream << "\t\t\t<p class=\"grd_small\">\n";
responseStream << "\t\t\t\t<label for=\"register-name\">Vorname</label>\n";
responseStream << "\t\t\t\t<input id=\"register-name\" type=\"text\" name=\"register-name\" value=\"";
#line 68 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\register.cpsp"
#line 72 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\register.cpsp"
responseStream << ( !form.empty() ? form.get("register-name") : "" );
responseStream << "\"/>\n";
responseStream << "\t\t\t</p>\n";
responseStream << "\t\t\t<p class=\"grd_small\">\n";
responseStream << "\t\t\t\t<label for=\"register-email\">E-Mail</label>\n";
responseStream << "\t\t\t\t<input id=\"register-email\" type=\"email\" name=\"register-email\" value=\"";
#line 72 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\register.cpsp"
#line 76 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\register.cpsp"
responseStream << ( !form.empty() ? form.get("register-email") : "" );
responseStream << "\"/>\n";
responseStream << "\t\t\t</p>\n";
@ -118,7 +119,7 @@ void RegisterPage::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Ne
responseStream << "\t\t\n";
responseStream << "\t</form>\n";
responseStream << "\t";
#line 82 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\register.cpsp"
#line 86 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\register.cpsp"
} responseStream << "\n";
responseStream << "</div>\n";
responseStream << "</body>\n";

View File

@ -3,10 +3,25 @@
#include "Crypto/mnemonic_bip0039.h"
#include "sodium.h"
#include "Poco/Net/SSLManager.h"
#include "Poco/Net/KeyConsoleHandler.h"
#include "Poco/Net/ConsoleCertificateHandler.h"
#include "Poco/SharedPtr.h"
using Poco::Net::SSLManager;
using Poco::Net::Context;
using Poco::Net::KeyConsoleHandler;
using Poco::Net::PrivateKeyPassphraseHandler;
using Poco::Net::InvalidCertificateHandler;
using Poco::Net::ConsoleCertificateHandler;
using Poco::SharedPtr;
namespace ServerConfig {
Mnemonic g_Mnemonic_WordLists[MNEMONIC_MAX];
ObfusArray* g_ServerCryptoKey = nullptr;
UniLib::controller::CPUSheduler* g_CPUScheduler = nullptr;
Context::Ptr g_SSL_CLient_Context = nullptr;
EmailAccount g_EmailAccount;
bool loadMnemonicWordLists()
{
@ -51,6 +66,34 @@ namespace ServerConfig {
return true;
}
bool initEMailAccount(const Poco::Util::LayeredConfiguration& cfg)
{
g_EmailAccount.sender = cfg.getString("email.sender");
g_EmailAccount.username = cfg.getString("email.username");
g_EmailAccount.password = cfg.getString("email.password");
g_EmailAccount.url = cfg.getString("email.smtp.url");
g_EmailAccount.port = cfg.getInt("email.smtp.port");
return true;
}
bool initSSLClientContext()
{
SharedPtr<InvalidCertificateHandler> pCert = new ConsoleCertificateHandler(false); // ask the user via console
/*
Context(Usage usage,
const std::string& certificateNameOrPath,
VerificationMode verMode = VERIFY_RELAXED,
int options = OPT_DEFAULTS,
const std::string& certificateStoreName = CERT_STORE_MY);
*/
g_SSL_CLient_Context = new Context(Context::CLIENT_USE, "", Context::VERIFY_RELAXED, Context::OPT_DEFAULTS);
SSLManager::instance().initializeClient(0, pCert, g_SSL_CLient_Context);
return true;
}
void unload() {
if (g_ServerCryptoKey) {
delete g_ServerCryptoKey;

View File

@ -1,6 +1,7 @@
#include "Crypto/mnemonic.h"
#include "Crypto/Obfus_array.h"
#include "Poco/Util/LayeredConfiguration.h"
#include "Poco/Net/Context.h"
#include "tasks/CPUSheduler.h"
@ -12,12 +13,24 @@ namespace ServerConfig {
MNEMONIC_MAX
};
struct EmailAccount {
std::string sender;
std::string username;
std::string password;
std::string url;
int port;
};
extern Mnemonic g_Mnemonic_WordLists[MNEMONIC_MAX];
extern ObfusArray* g_ServerCryptoKey;
extern UniLib::controller::CPUSheduler* g_CPUScheduler;
extern Poco::Net::Context::Ptr g_SSL_CLient_Context;
extern EmailAccount g_EmailAccount;
bool loadMnemonicWordLists();
bool initServerCrypto(const Poco::Util::LayeredConfiguration& cfg);
bool initEMailAccount(const Poco::Util::LayeredConfiguration& cfg);
bool initSSLClientContext();
void unload();
}
};

View File

@ -1,4 +1,39 @@
#include "ErrorManager.h"
#include "../ServerConfig.h"
#include "Poco/Net/SecureSMTPClientSession.h"
#include "Poco/Net/StringPartSource.h"
SendErrorMessage::~SendErrorMessage()
{
if (mMessage) {
delete mMessage;
mMessage = nullptr;
}
}
int SendErrorMessage::run()
{
auto mailClientSession = new Poco::Net::SecureSMTPClientSession(ServerConfig::g_EmailAccount.url, ServerConfig::g_EmailAccount.port);
mailClientSession->login();
mailClientSession->startTLS(ServerConfig::g_SSL_CLient_Context);
mailClientSession->login(Poco::Net::SMTPClientSession::AUTH_LOGIN, ServerConfig::g_EmailAccount.username, ServerConfig::g_EmailAccount.password);
try {
mMessage->setSender(ServerConfig::g_EmailAccount.sender);
mailClientSession->sendMessage(*mMessage);
mailClientSession->close();
}
catch (Poco::Exception& exc) {
printf("[SendErrorMessage::%s] error sending error message to admin\n", __FUNCTION__);
return -1;
}
return 0;
}
// ------------------------------------------------------------------------------------
ErrorManager* ErrorManager::getInstance()
{
@ -25,6 +60,7 @@ ErrorManager::~ErrorManager()
void ErrorManager::addError(Error* error)
{
DHASH id = DRMakeStringHash(error->getFunctionName());
mWorkingMutex.lock();
auto it = mErrorsMap.find(id);
std::list<Error*>* list = nullptr;
@ -37,10 +73,42 @@ void ErrorManager::addError(Error* error)
list = it->second;
// check if hash collision
if (strcmp((*list->begin())->getFunctionName(), error->getFunctionName()) != 0) {
mWorkingMutex.unlock();
throw "[ErrorManager::addError] hash collision detected";
}
}
list->push_back(error);
mWorkingMutex.unlock();
}
void ErrorManager::sendErrorsAsEmail()
{
auto message = new Poco::Net::MailMessage();
message->setSender("gradido_loginServer@gradido.net");
message->addRecipient(Poco::Net::MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, "***REMOVED***"));
message->setSubject("Error from Gradido Login Server");
std::string content;
mWorkingMutex.lock();
for (auto it1 = mErrorsMap.begin(); it1 != mErrorsMap.end(); it1++) {
auto error_list_functions = it1->second;
content += (*error_list_functions->begin())->getFunctionName();
content += "\n";
for (auto it2 = error_list_functions->begin(); it2 != error_list_functions->end(); it2++) {
content += "\t";
content += (*it2)->getMessage();
delete (*it2);
content += "\n";
}
content += "\n";
error_list_functions->clear();
}
mErrorsMap.clear();
mWorkingMutex.unlock();
message->addContent(new Poco::Net::StringPartSource(content));
UniLib::controller::TaskPtr sendErrorMessageTask(new SendErrorMessage(message, ServerConfig::g_CPUScheduler));
sendErrorMessageTask->scheduleTask(sendErrorMessageTask);
}

View File

@ -17,6 +17,11 @@
#include <cstring>
#include "../Model/Error.h"
#include "../Crypto/DRHash.h"
#include "../tasks/CPUTask.h"
#include "Poco/Mutex.h"
#include "Poco/Net/MailMessage.h"
class ErrorManager : public IErrorCollection
{
@ -28,15 +33,33 @@ public:
// will called delete on error
virtual void addError(Error* error);
virtual void sendErrorsAsEmail();
protected:
ErrorManager();
// access mutex
std::mutex mWorkingMutex;
Poco::Mutex mWorkingMutex;
std::map<DHASH, std::list<Error*>*> mErrorsMap;
// how many errors should be stored
};
class SendErrorMessage : public UniLib::controller::CPUTask
{
public:
SendErrorMessage(Poco::Net::MailMessage* message, UniLib::controller::CPUSheduler* scheduler)
: UniLib::controller::CPUTask(scheduler), mMessage(message) {}
~SendErrorMessage();
virtual int run();
protected:
Poco::Net::MailMessage* mMessage;
};
#endif //DR_LUA_WEB_MODULE_CONNECTION_MANAGER_H

View File

@ -180,4 +180,15 @@ Session* SessionManager::getSession(int handle)
}
mWorkingMutex.unlock();
return result;
}
Session* SessionManager::findByEmailVerificationCode(long long emailVerificationCode)
{
for (auto it = mRequestSessionMap.begin(); it != mRequestSessionMap.end(); it++) {
if (it->second->getEmailVerificationCode() == emailVerificationCode) {
return it->second;
}
}
return nullptr;
}

View File

@ -43,6 +43,7 @@ public:
bool releseSession(int requestHandleSession);
bool isExist(int requestHandleSession);
Session* getSession(int handle);
Session* findByEmailVerificationCode(long long emailVerificationCode);
bool init();
void deinitalize();

View File

@ -1,10 +1,43 @@
#include "Session.h"
#include "../ServerConfig.h"
#include "Poco/RegularExpression.h"
#include "Poco/Net/StringPartSource.h"
#include "../SingletonManager/SessionManager.h"
#include "../SingletonManager/ConnectionManager.h"
#include "../SingletonManager/ErrorManager.h"
#include "../tasks/PrepareEmailTask.h"
#include "../tasks/SendEmailTask.h"
#include "sodium.h"
using namespace Poco::Data::Keywords;
int WriteEmailVerification::run()
{
auto verificationCode = mSession->getEmailVerificationCode();
auto dbSession = ConnectionManager::getInstance()->getConnection(CONNECTION_MYSQL_LOGIN_SERVER);
int user_id = mUser->getDBId();
Poco::Data::Statement insert(dbSession);
insert << "INSERT INTO email_opt_in (user_id, verification_code) VALUES(?,?);",
use(user_id), use(verificationCode);
if (1 != insert.execute()) {
mSession->addError(new Error("WriteEmailVerification", "error inserting email verification code"));
return -1;
}
return 0;
}
// ---------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------
Session::Session(int handle)
: mHandleId(handle), mSessionUser(nullptr)
{
@ -60,27 +93,121 @@ bool Session::createUser(const std::string& name, const std::string& email, cons
//mPassphrase = passphrase;
}*/
// check if user with that email already exist
auto dbConnection = ConnectionManager::getInstance()->getConnection(CONNECTION_MYSQL_LOGIN_SERVER);
Poco::Data::Statement select(dbConnection);
select << "SELECT email from users where email = ?;", useRef(email);
try {
if (select.execute() > 0) {
addError(new Error("E-Mail", "F&uuml;r diese E-Mail Adresse gibt es bereits einen Account"));
return false;
}
}
catch (Poco::Exception& exc) {
printf("mysql exception: %s\n", exc.displayText().data());
}
mSessionUser = new User(email.data(), name.data());
updateTimeout();
// Prepare E-Mail
UniLib::controller::TaskPtr prepareEmail(new PrepareEmailTask(ServerConfig::g_CPUScheduler));
prepareEmail->scheduleTask(prepareEmail);
// create user crypto key
UniLib::controller::TaskPtr cryptoKeyTask(new UserCreateCryptoKey(mSessionUser, password, ServerConfig::g_CPUScheduler));
//cryptoKeyTask->scheduleTask(cryptoKeyTask);
cryptoKeyTask->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_CRYPTO_KEY_GENERATED, this));
cryptoKeyTask->scheduleTask(cryptoKeyTask);
// depends on crypto key
// depends on crypto key, write user record into db
UniLib::controller::TaskPtr writeUserIntoDB(new UserWriteIntoDB(mSessionUser, ServerConfig::g_CPUScheduler, 1));
writeUserIntoDB->setParentTaskPtrInArray(cryptoKeyTask, 0);
writeUserIntoDB->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_USER_WRITTEN, this));
writeUserIntoDB->scheduleTask(writeUserIntoDB);
createEmailVerificationCode();
UniLib::controller::TaskPtr writeEmailVerification(new WriteEmailVerification(mSessionUser, this, ServerConfig::g_CPUScheduler, 1));
writeEmailVerification->setParentTaskPtrInArray(writeUserIntoDB, 0);
writeEmailVerification->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_EMAIL_VERIFICATION_WRITTEN, this));
writeEmailVerification->scheduleTask(writeEmailVerification);
// depends on writeUser because need user_id, write email verification into db
auto message = new Poco::Net::MailMessage;
message->addRecipient(Poco::Net::MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, email));
message->setSubject("Gradido: E-Mail Verification");
std::stringstream ss;
ss << "Hallo " << name << "," << std::endl << std::endl;
ss << "Du oder jemand anderes hat sich soeben mit dieser E-Mail Adresse bei Gradido registriert. " << std::endl;
ss << "Wenn du es warst, klicke bitte auf den Link: https://gradido2.dario-rekowski.de/accounts/checkEmail/" << mEmailVerificationCode << std::endl;
ss << "oder kopiere den Code: " << mEmailVerificationCode << " selbst dort hinein." << std::endl << std::endl;
ss << "Mit freundlichen Grüße" << std::endl;
ss << "Dario, Gradido Server Admin" << std::endl;
message->addContent(new Poco::Net::StringPartSource(ss.str()));
UniLib::controller::TaskPtr sendEmail(new SendEmailTask(message, ServerConfig::g_CPUScheduler, 1));
sendEmail->setParentTaskPtrInArray(prepareEmail, 0);
sendEmail->setParentTaskPtrInArray(writeEmailVerification, 1);
sendEmail->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_EMAIL_VERIFICATION_SEND, this));
sendEmail->scheduleTask(sendEmail);
// write user into db
// generate and write email verification into db
// send email
createEmailVerificationCode();
return true;
}
bool Session::updateEmailVerification(unsigned long long emailVerificationCode)
{
const char* funcName = "Session::updateEmailVerification";
auto em = ErrorManager::getInstance();
if(mEmailVerificationCode == emailVerificationCode) {
if (mSessionUser && mSessionUser->getDBId() == 0) {
//addError(new Error("E-Mail Verification", "Benutzer wurde nicht richtig gespeichert, bitte wende dich an den Server-Admin"));
em->addError(new Error(funcName, "user exist with 0 as id"));
em->sendErrorsAsEmail();
//return false;
}
// load correct user from db
auto dbConnection = ConnectionManager::getInstance()->getConnection(CONNECTION_MYSQL_LOGIN_SERVER);
Poco::Data::Statement update(dbConnection);
update << "UPDATE users SET email_checked=1 where id = (SELECT user_id FROM email_opt_in where verification_code=?)", use(emailVerificationCode);
auto updated_rows = update.execute();
if (updated_rows == 1) {
Poco::Data::Statement delete_row(dbConnection);
delete_row << "DELETE FROM email_opt_in where verification_code = ?", use(emailVerificationCode);
if (delete_row.execute() != 1) {
em->addError(new Error(funcName, "delete from email_opt_in entry didn't work as expected, please check db"));
em->sendErrorsAsEmail();
}
updateState(SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED);
return true;
}
else {
em->addError(new ParamError(funcName, "update user work not like expected, updated row count", updated_rows));
em->sendErrorsAsEmail();
}
if (!updated_rows) {
addError(new Error("E-Mail Verification", "Der Code stimmt nicht, bitte überprüfe ihn nochmal oder registriere dich erneut oder wende dich an den Server-Admin"));
return false;
}
}
else {
addError(new Error("E-Mail Verification", "Falscher Code für aktiven Login"));
return false;
}
return false;
}
bool Session::loadUser(const std::string& email, const std::string& password)
@ -88,13 +215,47 @@ bool Session::loadUser(const std::string& email, const std::string& password)
return true;
}
int Session::createEmailVerificationCode()
void Session::updateState(SessionStates newState)
{
uint32_t* code_p = (uint32_t*)mEmailVerification;
lock();
printf("[Session::%s] newState: %s\n", __FUNCTION__, translateSessionStateToString(newState));
if (newState > mState) {
mState = newState;
}
unlock();
}
const char* Session::getSessionStateString()
{
SessionStates state;
lock();
state = mState;
unlock();
return translateSessionStateToString(state);
}
const char* Session::translateSessionStateToString(SessionStates state)
{
switch (state) {
case SESSION_STATE_CRYPTO_KEY_GENERATED: return "crpyto key generated";
case SESSION_STATE_USER_WRITTEN: return "User saved";
case SESSION_STATE_EMAIL_VERIFICATION_WRITTEN: return "E-Mail verification code saved";
case SESSION_STATE_EMAIL_VERIFICATION_SEND: return "Verification E-Mail sended";
case SESSION_STATE_KEY_PAIR_GENERATED: return "Gradido Address created";
case SESSION_STATE_KEY_PAIR_WRITTEN: return "Gradido Address saved";
default: return "unknown";
}
return "error";
}
void Session::createEmailVerificationCode()
{
uint32_t* code_p = (uint32_t*)&mEmailVerificationCode;
for (int i = 0; i < EMAIL_VERIFICATION_CODE_SIZE / 4; i++) {
code_p[i] = randombytes_random();
}
return 0;
}

View File

@ -13,43 +13,108 @@
#include "ErrorList.h"
#include "User.h"
#include "../tasks/MultithreadContainer.h"
#include "Poco/Thread.h"
#include "Poco/DateTime.h"
#include "Poco/Net/IPAddress.h"
#define EMAIL_VERIFICATION_CODE_SIZE 8
class Session : public ErrorList
class WriteEmailVerification;
enum SessionStates {
SESSION_STATE_CRYPTO_KEY_GENERATED,
SESSION_STATE_USER_WRITTEN,
SESSION_STATE_EMAIL_VERIFICATION_WRITTEN,
SESSION_STATE_EMAIL_VERIFICATION_SEND,
SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED,
SESSION_STATE_KEY_PAIR_GENERATED,
SESSION_STATE_KEY_PAIR_WRITTEN,
SESSION_STATE_COUNT
};
class Session : public ErrorList, public UniLib::lib::MultithreadContainer
{
friend WriteEmailVerification;
public:
Session(int handle);
~Session();
// TODO: automatic redirect after some time, median profiled time for register
// TODO: register state: written into db, mails sended, update state only if new state is higher as old state
bool createUser(const std::string& name, const std::string& email, const std::string& password);
// TODO: check if email exist and if not, fake waiting on password hashing with profiled times of real password hashing
bool loadUser(const std::string& email, const std::string& password);
bool updateEmailVerification(unsigned long long emailVerificationCode);
inline User* getUser() { return mSessionUser; }
inline int getHandle() { return mHandleId; }
inline const char* getPassphrase() { return mPassphrase.data(); }
inline void setClientIp(Poco::Net::IPAddress ip) { mClientLoginIP = ip; }
inline bool isIPValid(Poco::Net::IPAddress ip) { return mClientLoginIP == ip; }
void reset();
void updateState(SessionStates newState);
const char* getSessionStateString();
inline SessionStates getSessionState() { SessionStates s; lock(); s = mState; unlock(); return s; }
inline unsigned long long getEmailVerificationCode() { return mEmailVerificationCode; }
protected:
void updateTimeout();
void createEmailVerificationCode();
int createEmailVerificationCode();
static const char* translateSessionStateToString(SessionStates state);
int mHandleId;
User* mSessionUser;
std::string mPassphrase;
Poco::DateTime mLastActivity;
Poco::Net::IPAddress mClientLoginIP;
unsigned char* mEmailVerification[EMAIL_VERIFICATION_CODE_SIZE];
unsigned long long mEmailVerificationCode;
SessionStates mState;
};
class WriteEmailVerification : public UniLib::controller::CPUTask
{
public:
WriteEmailVerification(User* user, Session* session, UniLib::controller::CPUSheduler* cpuScheduler, size_t taskDependenceCount = 0)
: UniLib::controller::CPUTask(cpuScheduler, taskDependenceCount), mUser(user), mSession(session) {}
virtual const char* getResourceType() const { return "WriteEmailVerification"; };
virtual int run();
private:
User* mUser;
Session* mSession;
};
class SessionStateUpdateCommand : public UniLib::controller::Command
{
public:
SessionStateUpdateCommand(SessionStates state, Session* session)
: mState(state), mSession(session) {}
virtual int taskFinished(UniLib::controller::Task* task) {
mSession->updateState(mState);
return 0;
}
protected:
SessionStates mState;
Session* mSession;
};
#endif // DR_LUA_WEB_MODULE_SESSION_SESSION_H

View File

@ -4,6 +4,12 @@
#include "Poco/Util/Application.h"
#include "../ServerConfig.h"
#include "../SingletonManager/ConnectionManager.h"
#include "Poco/Data/Binding.h"
using namespace Poco::Data::Keywords;
NewUser::NewUser(User* user, const char* password, const char* passphrase)
: mUser(user), mPassword(password), mPassphrase(passphrase)
{
@ -58,6 +64,16 @@ int UserCreateCryptoKey::run()
int UserWriteIntoDB::run()
{
auto cm = ConnectionManager::getInstance();
auto session = cm->getConnection(CONNECTION_MYSQL_LOGIN_SERVER);
Poco::Data::Statement insert = mUser->insertIntoDB(session);
if (1 != insert.execute()) {
mUser->addError(new Error("User::insertIntoDB", "error by inserting data tuple to db"));
return -1;
}
if (!mUser->loadEntryDBId(session)) {
return -2;
}
return 0;
}
@ -65,7 +81,7 @@ int UserWriteIntoDB::run()
User::User(const char* email, const char* name)
: mEmail(email), mFirstName(name), mCryptoKey(nullptr)
: mDBId(0), mEmail(email), mFirstName(name), mCryptoKey(nullptr)
{
//crypto_shorthash(mPasswordHashed, (const unsigned char*)password, strlen(password), *ServerConfig::g_ServerCryptoKey);
memset(mPasswordHashed, 0, crypto_shorthash_BYTES);
@ -143,4 +159,32 @@ void User::createCryptoKey(const std::string& password)
// mCryptoKey
}
Poco::Data::Statement User::insertIntoDB(Poco::Data::Session session)
{
Poco::Data::Statement insert(session);
Poco::Data::BLOB pwd(mPasswordHashed, crypto_shorthash_BYTES);
insert << "INSERT INTO users (email, name, password) VALUES(?, ?, ?);",
use(mEmail), use(mFirstName), bind(pwd);
return insert;
}
bool User::loadEntryDBId(Poco::Data::Session session)
{
Poco::Data::Statement select(session);
select << "SELECT id from users where email = ?;",
into(mDBId), use(mEmail);
if (select.execute() != 1) {
addError(new Error("User::loadEntryDBId", "didn't get expectet row count (1)"));
return false;
}
return true;
}

View File

@ -5,15 +5,18 @@
#include <string>
#include "ErrorList.h"
#include "Poco/Thread.h"
#include "Poco/Data/Session.h"
#include "../tasks/CPUTask.h"
class NewUser;
class UserCreateCryptoKey;
class UserWriteIntoDB;
class User : public ErrorList
{
friend NewUser;
friend UserCreateCryptoKey;
friend UserWriteIntoDB;
public:
// new user
//User(const char* email, const char* name, const char* password);
@ -25,18 +28,21 @@ public:
static std::string generateNewPassphrase(Mnemonic* word_source);
inline bool hasCryptoKey() { lock(); bool bRet = mCryptoKey != nullptr; unlock(); return bRet; }
inline const char* getEmail() { return mEmail.data(); }
inline const char* getName() { return mFirstName.data(); }
inline const char* getEmail() const { return mEmail.data(); }
inline const char* getName() const { return mFirstName.data(); }
inline int getDBId() { return mDBId; }
protected:
void createCryptoKey(const std::string& password);
Poco::Data::Statement insertIntoDB(Poco::Data::Session session);
bool loadEntryDBId(Poco::Data::Session session);
inline void lock() { mWorkingMutex.lock(); }
inline void unlock() { mWorkingMutex.unlock(); }
private:
int mDBId;
std::string mEmail;
std::string mFirstName;
unsigned char mPasswordHashed[crypto_shorthash_BYTES];

View File

@ -1,16 +0,0 @@
#include "PrepaireEmailTask.h"
PrepaireEmailTask::PrepaireEmailTask()
{
}
PrepaireEmailTask::~PrepaireEmailTask()
{
}
int PrepaireEmailTask::run()
{
return 0;
}

View File

@ -1,23 +0,0 @@
#ifndef GRADIDO_LOGIN_SERVER_TASKS_PREPAIRE_EMAIL_TASK_INCLUDE
#define GRADIDO_LOGIN_SERVER_TASKS_PREPAIRE_EMAIL_TASK_INCLUDE
#include "Task.h"
class PrepaireEmailTask : public UniLib::controller::Task
{
public:
PrepaireEmailTask();
virtual ~PrepaireEmailTask();
virtual int run();
virtual const char* getResourceType() const { return "PrepaireEmailTask"; };
protected:
private:
};
#endif //GRADIDO_LOGIN_SERVER_TASKS_PREPAIRE_EMAIL_TASK_INCLUDE

View File

@ -1,16 +1,35 @@
#include "SendEmailTask.h"
#include "PrepareEmailTask.h"
#include "../SingletonManager/ErrorManager.h"
#include "../ServerConfig.h"
SendEmailTask::SendEmailTask()
SendEmailTask::SendEmailTask(Poco::Net::MailMessage* mailMessage, UniLib::controller::CPUSheduler* cpuScheduler, size_t additionalTaskDependenceCount/* = 0*/)
: UniLib::controller::CPUTask(cpuScheduler, additionalTaskDependenceCount+1), mMailMessage(mailMessage)
{
}
SendEmailTask::~SendEmailTask()
{
if (mMailMessage) {
delete mMailMessage;
mMailMessage = nullptr;
}
}
int SendEmailTask::run()
{
auto er = ErrorManager::getInstance();
auto parent = getParent(0);
if (strcmp(parent->getResourceType(), "PrepareEmailTask") != 0) {
er->addError(new Error("SendEmailTask", "first parent isn't PrepareEmailTask"));
return -1;
}
PrepareEmailTask* prepare = (PrepareEmailTask*)&(*parent);
mMailMessage->setSender(ServerConfig::g_EmailAccount.sender);
if (prepare->send(mMailMessage)) {
return -1;
}
return 0;
}

View File

@ -1,13 +1,22 @@
#ifndef GRADIDO_LOGIN_SERVER_TASKS_SEND_EMAIL_TASK_INCLUDE
#define GRADIDO_LOGIN_SERVER_TASKS_SEND_EMAIL_TASK_INCLUDE
#include "Task.h"
#include "CPUTask.h"
#include "Poco/Net/MailMessage.h"
/*
* @author: Dario Rekowski
*
* @date: 29.09.19
* @desc: Task for send an email, the first parent dependence pointer must be a prepare email task
*/
class SendEmailTask : public UniLib::controller::Task
class SendEmailTask : public UniLib::controller::CPUTask
{
public:
SendEmailTask();
SendEmailTask(Poco::Net::MailMessage* mailMessage, UniLib::controller::CPUSheduler* cpuScheduler, size_t additionalTaskDependenceCount = 0);
virtual ~SendEmailTask();
virtual int run();
@ -16,6 +25,7 @@ public:
protected:
private:
Poco::Net::MailMessage* mMailMessage;
};

View File

@ -20,6 +20,9 @@ namespace UniLib {
if (mParentTaskPtrArraySize) {
delete[] mParentTaskPtrArray;
}
if (mFinishCommand) {
delete mFinishCommand;
}
mParentTaskPtrArraySize = 0;
mWorkingMutex.lock();
mDeleted = true;
@ -42,6 +45,14 @@ namespace UniLib {
return allFinished;
}
TaskPtr Task::getParent(int index)
{
if (index < 0 || index >= mParentTaskPtrArraySize) {
return nullptr;
}
return mParentTaskPtrArray[index];
}
void Task::duplicate()
{
mReferenceCount++;
@ -56,5 +67,14 @@ namespace UniLib {
}
void Task::setTaskFinished() {
lock();
mFinished = true;
if (mFinishCommand) {
mFinishCommand->taskFinished(this);
}
unlock();
}
}
}

View File

@ -104,12 +104,13 @@ namespace UniLib {
void duplicate();
void release();
inline void setTaskFinished() { lock(); mFinished = true; unlock(); }
void setTaskFinished();
protected:
// scheduling only once
inline bool isTaskSheduled() {return mTaskScheduled;}
inline void taskScheduled() {mTaskScheduled = true;}
TaskPtr getParent(int index);
bool mTaskScheduled;
Command* mFinishCommand;

View File

@ -1,26 +1,27 @@
<%@ page class="EmailOptInPage" %>
<%@ page class="CheckEmailPage" %>
<%@ page form="true" %>
<%@ page compressed="true" %>
<%!
#include "../SingletonManager/SessionManager.h"
#include "Poco/Net/HTTPCookie.h"
enum PageState
{
PAGE_VERIFICATION_FAILED,
PAGE_ASK_PASSPHRASE,
PAGE_SHOW_PASSPHRASE
};
%>
<%%
auto session = SessionManager::getInstance()->getNewSession();
bool userReturned = false;
if(!form.empty()) {
userReturned = session->createUser(
form.get("register-name"),
form.get("register-email"),
form.get("register-password")
);
if(userReturned) {
auto cookie_id = session->getHandle();
auto user_host = request.clientAddress().toString();
printf("cookie: %d, user_host: %s\n", cookie_id, user_host.data());
response.addCookie(Poco::Net::HTTPCookie("user", std::to_string(cookie_id)));
}
auto sm = SessionManager::getInstance();
bool hasErrors = false;
PageState state = PAGE_ASK_PASSPHRASE;
std::string uri = request.getURI();
printf("uri: %s\n", uri.data());
if(!mSession) {// || mSession->getEmailVerificationCode() != ) {
//mSession = sm->findByEmailVerificationCode();
}
%>
<!DOCTYPE html>
<html>
@ -42,37 +43,24 @@ label:not(.grd_radio_label) {
</head>
<body>
<div class="grd_container">
<% if(hasErrors) {%>
<%= mSession->getErrorsHtml() %>
<%} %>
<h1>Einen neuen Account anlegen</h1>
<% if(!form.empty() && userReturned) {%>
<% if(state == PAGE_SHOW_PASSPHRASE) {%>
<div class="grd_text-max-width">
<div class="grd_text">
Schreibe dir den Merkspruch auf und packe ihn gut weg. Du brauchst ihn um deine Adresse wiederherzustellen. Wenn du ihn verlierst, sind auch deine Gradidos verloren.
</div>
<div class="grd_textarea">
<%= session->getPassphrase() %>
<%= mSession->getPassphrase() %>
</div>
</div>
<% } else { %>
<% } else if(state == PAGE_ASK_PASSPHRASE) { %>
<form method="POST">
<% if(!form.empty() && !userReturned) {%>
<%= session->getErrorsHtml() %>
<%} %>
<fieldset class="grd_container_small">
<legend>Account anlegen</legend>
<p>Bitte gebe deine Daten um einen Account anzulegen</p>
<p class="grd_small">
<label for="register-name">Vorname</label>
<input id="register-name" type="text" name="register-name" value="<%= !form.empty() ? form.get("register-name") : "" %>"/>
</p>
<p class="grd_small">
<label for="register-email">E-Mail</label>
<input id="register-email" type="email" name="register-email" value="<%= !form.empty() ? form.get("register-email") : "" %>"/>
</p>
<p class="grd_small">
<label for="register-password">Passwort</label>
<input id="register-password" type="password" name="register-password"/>
</p>
<p>Hast du schonmal ein Gradido Konto besessen?</p>
<p class="grd_small">
<input id="register-key-new-yes" type="radio" name="register-key" value="yes" checked/>
@ -87,6 +75,23 @@ label:not(.grd_radio_label) {
<input class="grd_bn_succeed" type="submit" name="submit" value="Anmelden">
</form>
<% } else if(state == PAGE_VERIFICATION_FAILED) { %>
<% if(mSession->getSessionState() == SESSION_STATE_EMAIL_VERIFICATION_SEND) { %>
<p>Bitte versuche es erneut</p>
<form method="GET" action="/checkEmail">
<input type="number" name="email-verification-code">
<input class="grd_bn_succeed" type="submit" value="Überprüfe Code">
</form>
<% } else if(mSession->getSessionState() < SESSION_STATE_EMAIL_VERIFICATION_SEND) { %>
<div class="grd_text">Die E-Mail wurde nicht verschickt, bitte habe noch etwas Geduld.</div>
<% } else { %>
<div class="grd_text">Der Account wurde schon freigeschaltet.</div>
<a href="/">Zurück</a>
<% } %>
<% } else { %>
<div class="grd_text">
Ungültige Seite, wenn du das siehst stimmt hier was nicht. Bitte wende dich an den Server-Admin.
</div>
<% } %>
</div>
</body>

View File

@ -1,11 +1,15 @@
<%@ page class="DashboardPage" %>
<%@ page form="true" %>
<%@ page compressed="true" %>
<%!
#include "../SingletonManager/SessionManager.h"
%>
<%
Poco::Net::NameValueCollection cookies;
request.getCookies(cookies);
//Poco::Net::NameValueCollection cookies;
//request.getCookies(cookies);
if(!form.empty()) {
//form.get("email-verification-code")
}
%>
<!DOCTYPE html>
<html>
@ -19,7 +23,14 @@
<body>
<div class="grd_container">
<h1>Willkommen <%= mSession->getUser()->getName() %></h1>
<h3>Status</h3>
<p><%= mSession->getSessionStateString() %></p>
<% if(mSession->getSessionState() == SESSION_STATE_EMAIL_VERIFICATION_SEND) %>
<p>Verification Code E-Mail wurde erfolgreich an dich verschickt, bitte schaue auch in dein Spam-Verzeichnis nach wenn du sie nicht findest und klicke auf den Link den du dort findest oder kopiere den Code hier her:</p>
<form method="GET" action="/checkEmail">
<input type="number" name="email-verification-code">
<input class="grd_bn_succeed" type="submit" value="Überprüfe Code">
</form>
</div>
</body>
</html>

View File

@ -46,12 +46,16 @@ label:not(.grd_radio_label) {
<div class="grd_container">
<h1>Einen neuen Account anlegen</h1>
<% if(!form.empty() && userReturned) {%>
<div class="grd_text-max-width">
<div class="grd_text">
Schreibe dir den Merkspruch auf und packe ihn gut weg. Du brauchst ihn um deine Adresse wiederherzustellen. Wenn du ihn verlierst, sind auch deine Gradidos verloren.
</div>
<div class="grd_textarea">
<%= session->getPassphrase() %>
Deine Anmeldung wird verarbeitet und es wird dir eine E-Mail zugeschickt.
Wenn sie da ist, befolge ihren Anweisungen.
Möchtest du wissen ob die E-Mail schon verschickt wurde?
Dann klicke einfach hier:
<form action="/">
<input type="submit" value="Status überprüfen">
</form>
</div>
</div>
<% } else { %>