diff --git a/src/cpp/Gradido_LoginServer.cpp b/src/cpp/Gradido_LoginServer.cpp index 94fb1bdd3..da9481cca 100644 --- a/src/cpp/Gradido_LoginServer.cpp +++ b/src/cpp/Gradido_LoginServer.cpp @@ -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 @@ -82,8 +84,14 @@ int Gradido_LoginServer::main(const std::vector& 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& 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& args) srv.start(); // wait for CTRL-C or kill waitForTerminationRequest(); + // Stop the HTTPServer srv.stop(); ServerConfig::unload(); + Poco::Net::uninitializeSSL(); } return Application::EXIT_OK; } diff --git a/src/cpp/HTTPInterface/PageRequestHandlerFactory.cpp b/src/cpp/HTTPInterface/PageRequestHandlerFactory.cpp index c17df17d6..1a2f66f7d 100644 --- a/src/cpp/HTTPInterface/PageRequestHandlerFactory.cpp +++ b/src/cpp/HTTPInterface/PageRequestHandlerFactory.cpp @@ -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); diff --git a/src/cpp/HTTPInterface/RegisterPage.cpp b/src/cpp/HTTPInterface/RegisterPage.cpp index be62da339..7628cdce1 100644 --- a/src/cpp/HTTPInterface/RegisterPage.cpp +++ b/src/cpp/HTTPInterface/RegisterPage.cpp @@ -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
\n"; responseStream << "\t\t\t
\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
\n"; - responseStream << "\t\t\t
\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
\n"; + responseStream << "\t\t\t\t\t\n"; + responseStream << "\t\t\t\t
\n"; responseStream << "\t\t\t
\n"; responseStream << "\t\t
\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
\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
\n"; responseStream << "\t\t\tAccount anlegen\n"; @@ -98,14 +99,14 @@ void RegisterPage::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Ne responseStream << "\t\t\t

\n"; responseStream << "\t\t\t\t\n"; responseStream << "\t\t\t\t\n"; responseStream << "\t\t\t

\n"; responseStream << "\t\t\t

\n"; responseStream << "\t\t\t\t\n"; responseStream << "\t\t\t\t\n"; responseStream << "\t\t\t

\n"; @@ -118,7 +119,7 @@ void RegisterPage::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Ne responseStream << "\t\t\n"; responseStream << "\t\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 << "\n"; responseStream << "\n"; diff --git a/src/cpp/ServerConfig.cpp b/src/cpp/ServerConfig.cpp index 02e398215..1a2389130 100644 --- a/src/cpp/ServerConfig.cpp +++ b/src/cpp/ServerConfig.cpp @@ -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 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; diff --git a/src/cpp/ServerConfig.h b/src/cpp/ServerConfig.h index cdaa22fc6..bb5c71b51 100644 --- a/src/cpp/ServerConfig.h +++ b/src/cpp/ServerConfig.h @@ -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(); -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/cpp/SingletonManager/ErrorManager.cpp b/src/cpp/SingletonManager/ErrorManager.cpp index f1ceba3a1..f841d32b5 100644 --- a/src/cpp/SingletonManager/ErrorManager.cpp +++ b/src/cpp/SingletonManager/ErrorManager.cpp @@ -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* 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); } \ No newline at end of file diff --git a/src/cpp/SingletonManager/ErrorManager.h b/src/cpp/SingletonManager/ErrorManager.h index e9a45295f..802728ff1 100644 --- a/src/cpp/SingletonManager/ErrorManager.h +++ b/src/cpp/SingletonManager/ErrorManager.h @@ -17,6 +17,11 @@ #include #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*> 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 \ No newline at end of file diff --git a/src/cpp/SingletonManager/SessionManager.cpp b/src/cpp/SingletonManager/SessionManager.cpp index d4547985a..322011826 100644 --- a/src/cpp/SingletonManager/SessionManager.cpp +++ b/src/cpp/SingletonManager/SessionManager.cpp @@ -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; } \ No newline at end of file diff --git a/src/cpp/SingletonManager/SessionManager.h b/src/cpp/SingletonManager/SessionManager.h index 3709f9240..b542ad664 100644 --- a/src/cpp/SingletonManager/SessionManager.h +++ b/src/cpp/SingletonManager/SessionManager.h @@ -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(); diff --git a/src/cpp/model/Session.cpp b/src/cpp/model/Session.cpp index be7728694..19d6a41e0 100644 --- a/src/cpp/model/Session.cpp +++ b/src/cpp/model/Session.cpp @@ -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ü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; } \ No newline at end of file diff --git a/src/cpp/model/Session.h b/src/cpp/model/Session.h index c75719527..9c136fafd 100644 --- a/src/cpp/model/Session.h +++ b/src/cpp/model/Session.h @@ -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 diff --git a/src/cpp/model/User.cpp b/src/cpp/model/User.cpp index a604f5a49..f1d88d68d 100644 --- a/src/cpp/model/User.cpp +++ b/src/cpp/model/User.cpp @@ -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; } \ No newline at end of file diff --git a/src/cpp/model/User.h b/src/cpp/model/User.h index f3b1d1c2c..d88fb7b08 100644 --- a/src/cpp/model/User.h +++ b/src/cpp/model/User.h @@ -5,15 +5,18 @@ #include #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]; diff --git a/src/cpp/tasks/PrepaireEmailTask.cpp b/src/cpp/tasks/PrepaireEmailTask.cpp deleted file mode 100644 index 96b846cca..000000000 --- a/src/cpp/tasks/PrepaireEmailTask.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "PrepaireEmailTask.h" - -PrepaireEmailTask::PrepaireEmailTask() -{ - -} - -PrepaireEmailTask::~PrepaireEmailTask() -{ - -} - -int PrepaireEmailTask::run() -{ - return 0; -} \ No newline at end of file diff --git a/src/cpp/tasks/PrepaireEmailTask.h b/src/cpp/tasks/PrepaireEmailTask.h deleted file mode 100644 index d80f9d1e2..000000000 --- a/src/cpp/tasks/PrepaireEmailTask.h +++ /dev/null @@ -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 \ No newline at end of file diff --git a/src/cpp/tasks/SendEmailTask.cpp b/src/cpp/tasks/SendEmailTask.cpp index f4c7fcdbc..17dd6750b 100644 --- a/src/cpp/tasks/SendEmailTask.cpp +++ b/src/cpp/tasks/SendEmailTask.cpp @@ -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; } \ No newline at end of file diff --git a/src/cpp/tasks/SendEmailTask.h b/src/cpp/tasks/SendEmailTask.h index c5c303b25..d13f9868e 100644 --- a/src/cpp/tasks/SendEmailTask.h +++ b/src/cpp/tasks/SendEmailTask.h @@ -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; }; diff --git a/src/cpp/tasks/Task.cpp b/src/cpp/tasks/Task.cpp index 925a9bb67..f45eee78e 100644 --- a/src/cpp/tasks/Task.cpp +++ b/src/cpp/tasks/Task.cpp @@ -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(); + } + } } \ No newline at end of file diff --git a/src/cpp/tasks/Task.h b/src/cpp/tasks/Task.h index 1c45a98f2..d69ddf4ba 100644 --- a/src/cpp/tasks/Task.h +++ b/src/cpp/tasks/Task.h @@ -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; diff --git a/src/cpsp/emailOptIn.cpsp b/src/cpsp/checkEmail.cpsp similarity index 58% rename from src/cpsp/emailOptIn.cpsp rename to src/cpsp/checkEmail.cpsp index 802014acb..5a85e13ec 100644 --- a/src/cpsp/emailOptIn.cpsp +++ b/src/cpsp/checkEmail.cpsp @@ -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(); } + %> @@ -42,37 +43,24 @@ label:not(.grd_radio_label) {
+ <% if(hasErrors) {%> + <%= mSession->getErrorsHtml() %> + <%} %>

Einen neuen Account anlegen

- <% if(!form.empty() && userReturned) {%> + <% if(state == PAGE_SHOW_PASSPHRASE) {%>
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.
- <%= session->getPassphrase() %> + <%= mSession->getPassphrase() %>
- <% } else { %> + <% } else if(state == PAGE_ASK_PASSPHRASE) { %>
- - <% if(!form.empty() && !userReturned) {%> - <%= session->getErrorsHtml() %> - <%} %>
Account anlegen

Bitte gebe deine Daten um einen Account anzulegen

-

- - "/> -

-

- - "/> -

-

- - -

Hast du schonmal ein Gradido Konto besessen?

@@ -87,6 +75,23 @@ label:not(.grd_radio_label) {

+ <% } else if(state == PAGE_VERIFICATION_FAILED) { %> + <% if(mSession->getSessionState() == SESSION_STATE_EMAIL_VERIFICATION_SEND) { %> +

Bitte versuche es erneut

+
+ + +
+ <% } else if(mSession->getSessionState() < SESSION_STATE_EMAIL_VERIFICATION_SEND) { %> +
Die E-Mail wurde nicht verschickt, bitte habe noch etwas Geduld.
+ <% } else { %> +
Der Account wurde schon freigeschaltet.
+ Zurück + <% } %> + <% } else { %> +
+ Ungültige Seite, wenn du das siehst stimmt hier was nicht. Bitte wende dich an den Server-Admin. +
<% } %>
diff --git a/src/cpsp/dashboard.cpsp b/src/cpsp/dashboard.cpsp index 6ce441fa8..140a75169 100644 --- a/src/cpsp/dashboard.cpsp +++ b/src/cpsp/dashboard.cpsp @@ -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") + } %> @@ -19,7 +23,14 @@

Willkommen <%= mSession->getUser()->getName() %>

- +

Status

+

<%= mSession->getSessionStateString() %>

+ <% if(mSession->getSessionState() == SESSION_STATE_EMAIL_VERIFICATION_SEND) %> +

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:

+
+ + +
diff --git a/src/cpsp/register.cpsp b/src/cpsp/register.cpsp index 60c87eccf..da78ce842 100644 --- a/src/cpsp/register.cpsp +++ b/src/cpsp/register.cpsp @@ -46,12 +46,16 @@ label:not(.grd_radio_label) {

Einen neuen Account anlegen

<% if(!form.empty() && userReturned) {%> +
- 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. -
-
- <%= 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: +
+ +
<% } else { %>