From 875632e892f714a0ac0653ceaf3f0284c846fbf7 Mon Sep 17 00:00:00 2001 From: Dario Date: Fri, 10 Jul 2020 11:22:52 +0200 Subject: [PATCH] update session manager get session by email verification code to work with better new email verification object --- src/cpp/HTTPInterface/ResetPassword.cpp | 60 +++++++++--------- src/cpp/SingletonManager/SessionManager.cpp | 37 +++++++---- src/cpp/SingletonManager/SessionManager.h | 4 +- src/cpp/controller/EmailVerificationCode.cpp | 2 +- src/cpp/controller/EmailVerificationCode.h | 2 +- src/cpp/model/Session.cpp | 64 +++++++++++++------- src/cpp/model/Session.h | 14 ++++- src/cpsp/resetPassword.cpsp | 8 ++- 8 files changed, 121 insertions(+), 70 deletions(-) diff --git a/src/cpp/HTTPInterface/ResetPassword.cpp b/src/cpp/HTTPInterface/ResetPassword.cpp index 5016c703d..78d10c055 100644 --- a/src/cpp/HTTPInterface/ResetPassword.cpp +++ b/src/cpp/HTTPInterface/ResetPassword.cpp @@ -95,11 +95,13 @@ void ResetPassword::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::N if(!errorCount()) { // send reset password email + int result = 0; if(user_exist) { - session->sendResetPasswordEmail(user, sendUserEmail); + result = session->sendResetPasswordEmail(user, sendUserEmail); } - - if(sendUserEmail) { + if(2 == result) { + state = PAGE_EMAIL_ALREADY_SEND; + } else if(sendUserEmail) { state = PAGE_WAIT_EMAIL; } else { state = PAGE_WAIT_ADMIN; @@ -225,34 +227,34 @@ void ResetPassword::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::N responseStream << "\n"; responseStream << "\n"; responseStream << " "; -#line 122 "F:\\Gradido\\gradido_login_server\\src\\cpsp\\resetPassword.cpsp" +#line 124 "F:\\Gradido\\gradido_login_server\\src\\cpsp\\resetPassword.cpsp" if(state == PAGE_EMAIL_ASK) { responseStream << "\n"; responseStream << "\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"; responseStream << "\t\t\t \n"; responseStream << "\t\t\t
\n"; responseStream << "\t\t
\n"; responseStream << "\t"; -#line 132 "F:\\Gradido\\gradido_login_server\\src\\cpsp\\resetPassword.cpsp" +#line 134 "F:\\Gradido\\gradido_login_server\\src\\cpsp\\resetPassword.cpsp" } else if(state == PAGE_ASK) { responseStream << "\n"; responseStream << "\t\t"; // begin include flags.cpsp @@ -291,35 +293,35 @@ void ResetPassword::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::N // end include flags.cpsp responseStream << "\n"; responseStream << "\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\t \n"; responseStream << "\t\t\t\t\n"; responseStream << "\t\t\t\t
\n"; responseStream << "\t\t\t\t\t
\n"; responseStream << "\t\t\t\t\t \n"; @@ -327,7 +329,7 @@ void ResetPassword::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::N responseStream << "\t\t\t\t\t
\n"; responseStream << "\t\t\t\t\t \n"; @@ -335,45 +337,45 @@ void ResetPassword::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::N responseStream << "\t\t\t\t
\n"; responseStream << "\t\t\t
\n"; responseStream << "\t\t\t \n"; responseStream << "\t\t\t
\n"; responseStream << "\t\t\n"; responseStream << "\t "; -#line 157 "F:\\Gradido\\gradido_login_server\\src\\cpsp\\resetPassword.cpsp" +#line 159 "F:\\Gradido\\gradido_login_server\\src\\cpsp\\resetPassword.cpsp" } else if(state == PAGE_WAIT_EMAIL) { responseStream << "\n"; responseStream << "\t\t\t"; -#line 158 "F:\\Gradido\\gradido_login_server\\src\\cpsp\\resetPassword.cpsp" +#line 160 "F:\\Gradido\\gradido_login_server\\src\\cpsp\\resetPassword.cpsp" responseStream << ( langCatalog->gettext("Dir wird eine E-Mail zugeschickt um dein Passwort zurückzusetzen.") ); responseStream << "\n"; responseStream << "\t "; -#line 159 "F:\\Gradido\\gradido_login_server\\src\\cpsp\\resetPassword.cpsp" +#line 161 "F:\\Gradido\\gradido_login_server\\src\\cpsp\\resetPassword.cpsp" } else if(state == PAGE_WAIT_ADMIN) { responseStream << "\n"; responseStream << "\t\t\t"; -#line 160 "F:\\Gradido\\gradido_login_server\\src\\cpsp\\resetPassword.cpsp" +#line 162 "F:\\Gradido\\gradido_login_server\\src\\cpsp\\resetPassword.cpsp" responseStream << ( langCatalog->gettext("Der Admin hat eine E-Mail bekommen und wird sich bei dir melden.") ); responseStream << "\n"; responseStream << "\t "; -#line 161 "F:\\Gradido\\gradido_login_server\\src\\cpsp\\resetPassword.cpsp" +#line 163 "F:\\Gradido\\gradido_login_server\\src\\cpsp\\resetPassword.cpsp" } else if(state == PAGE_EMAIL_ALREADY_SEND) { responseStream << "\n"; responseStream << "\t\t\t

"; -#line 162 "F:\\Gradido\\gradido_login_server\\src\\cpsp\\resetPassword.cpsp" +#line 164 "F:\\Gradido\\gradido_login_server\\src\\cpsp\\resetPassword.cpsp" responseStream << ( langCatalog->gettext("Du hast bereits eine E-Mail bekommen. Bitte schau auch in dein Spam-Verzeichnis nach. ") ); responseStream << "

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

"; -#line 163 "F:\\Gradido\\gradido_login_server\\src\\cpsp\\resetPassword.cpsp" +#line 165 "F:\\Gradido\\gradido_login_server\\src\\cpsp\\resetPassword.cpsp" responseStream << ( langCatalog->gettext("Du hast wirklich keine E-Mail erhalten und auch schon ein paar Minuten gewartet?") ); responseStream << "

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

"; -#line 164 "F:\\Gradido\\gradido_login_server\\src\\cpsp\\resetPassword.cpsp" +#line 166 "F:\\Gradido\\gradido_login_server\\src\\cpsp\\resetPassword.cpsp" responseStream << ( langCatalog->gettext("E-Mail an Support schicken")); responseStream << "

\n"; responseStream << "\t "; -#line 165 "F:\\Gradido\\gradido_login_server\\src\\cpsp\\resetPassword.cpsp" +#line 167 "F:\\Gradido\\gradido_login_server\\src\\cpsp\\resetPassword.cpsp" } responseStream << "\n"; responseStream << "
\n"; responseStream << "
\n"; diff --git a/src/cpp/SingletonManager/SessionManager.cpp b/src/cpp/SingletonManager/SessionManager.cpp index 51197785a..eb471c212 100644 --- a/src/cpp/SingletonManager/SessionManager.cpp +++ b/src/cpp/SingletonManager/SessionManager.cpp @@ -2,6 +2,7 @@ #include "ErrorManager.h" #include "../ServerConfig.h" #include "../Crypto/DRRandom.h" +#include "../controller/EmailVerificationCode.h" #include @@ -342,30 +343,42 @@ Session* SessionManager::getSession(int handle) return result; } -Session* SessionManager::findByEmailVerificationCode(long long emailVerificationCode) +Session* SessionManager::findByEmailVerificationCode(const Poco::UInt64& emailVerificationCode) { - Session* result = nullptr; + + auto email_verification = controller::EmailVerificationCode::load(emailVerificationCode); + if (email_verification.isNull()) return nullptr; + auto email_verification_model = email_verification->getModel(); + assert(email_verification_model->getUserId() > 0); + + auto session = findByUserId(email_verification_model->getUserId()); + if (session) { + session->setEmailVerificationCodeObject(email_verification); + } + + return session; +} + +Session* SessionManager::findByUserId(int userId) +{ + assert(userId > 0); try { Poco::Mutex::ScopedLock _lock(mWorkingMutex, 500); } catch (Poco::TimeoutException &ex) { - printf("[SessionManager::findByEmailVerificationCode] exception timout mutex: %s\n", ex.displayText().data()); - return result; + printf("[SessionManager::findByUserId] exception timout mutex: %s\n", ex.displayText().data()); + return nullptr; } //mWorkingMutex.lock(); for (auto it = mRequestSessionMap.begin(); it != mRequestSessionMap.end(); it++) { - if (it->second->getEmailVerificationCode() == emailVerificationCode) { - result = it->second; - if (!result->isActive()) { - result = nullptr; - continue; - } - break; + auto user = it->second->getNewUser(); + if (userId == user->getModel()->getID()) { + return it->second; } } //mWorkingMutex.unlock(); - return result; + return nullptr; } void SessionManager::checkTimeoutSession() diff --git a/src/cpp/SingletonManager/SessionManager.h b/src/cpp/SingletonManager/SessionManager.h index 17a259973..c50309da0 100644 --- a/src/cpp/SingletonManager/SessionManager.h +++ b/src/cpp/SingletonManager/SessionManager.h @@ -36,6 +36,7 @@ enum SessionValidationTypes { // TODO: cleanup timeouted sessions +// TODO: only one session per user allowed, delete active session by new login? class SessionManager { public: @@ -55,7 +56,8 @@ public: // try to find existing session, return nullptr if not found Session* getSession(int handle); Session* getSession(const Poco::Net::HTTPServerRequest& request); - Session* findByEmailVerificationCode(long long emailVerificationCode); + Session* findByEmailVerificationCode(const Poco::UInt64& emailVerificationCode); + Session* findByUserId(int userId); bool init(); void deinitalize(); diff --git a/src/cpp/controller/EmailVerificationCode.cpp b/src/cpp/controller/EmailVerificationCode.cpp index 21a1b5372..57022b191 100644 --- a/src/cpp/controller/EmailVerificationCode.cpp +++ b/src/cpp/controller/EmailVerificationCode.cpp @@ -41,7 +41,7 @@ namespace controller { return Poco::AutoPtr(new EmailVerificationCode(db)); } - Poco::AutoPtr EmailVerificationCode::load(Poco::UInt64 code) + Poco::AutoPtr EmailVerificationCode::load(const Poco::UInt64& code) { auto db = new model::table::EmailOptIn(); if (db->loadFromDB("verification_code", code) == 1) { diff --git a/src/cpp/controller/EmailVerificationCode.h b/src/cpp/controller/EmailVerificationCode.h index b8fc49b14..77d607de3 100644 --- a/src/cpp/controller/EmailVerificationCode.h +++ b/src/cpp/controller/EmailVerificationCode.h @@ -15,7 +15,7 @@ namespace controller { static Poco::AutoPtr create(int user_id, model::table::EmailOptInType type = model::table::EMAIL_OPT_IN_REGISTER); static Poco::AutoPtr create(model::table::EmailOptInType type = model::table::EMAIL_OPT_IN_REGISTER); - static Poco::AutoPtr load(Poco::UInt64 code); + static Poco::AutoPtr load(const Poco::UInt64& code); static std::vector> load(int user_id); static Poco::AutoPtr load(int user_id, model::table::EmailOptInType type); diff --git a/src/cpp/model/Session.cpp b/src/cpp/model/Session.cpp index 0139309cf..42924319a 100644 --- a/src/cpp/model/Session.cpp +++ b/src/cpp/model/Session.cpp @@ -108,7 +108,7 @@ void Session::reset() { //printf("[Session::reset]\n"); lock("Session::reset"); - + std::unique_lock _lock(mSharedMutex); mSessionUser.assign(nullptr); mNewUser.assign(nullptr); mEmailVerificationCodeObject.assign(nullptr); @@ -141,6 +141,7 @@ void Session::updateTimeout() Poco::AutoPtr Session::getEmailVerificationCodeObject() { lock("Session::getEmailVerificationCodeObject"); + std::shared_lock _lock(mSharedMutex); auto ret = mEmailVerificationCodeObject; unlock(); return ret; @@ -186,18 +187,21 @@ bool Session::adminCreateUser(const std::string& first_name, const std::string& return false; } - auto emailVerificationCode = controller::EmailVerificationCode::create(newUserModel->getID(), model::table::EMAIL_OPT_IN_REGISTER); - if (!emailVerificationCode->getModel()->insertIntoDB(false)) { + auto email_verification_code = controller::EmailVerificationCode::create(newUserModel->getID(), model::table::EMAIL_OPT_IN_REGISTER); + if (!email_verification_code->getModel()->insertIntoDB(false)) { addError(new Error(gettext("Email Verification Code"), gettext("Fehler beim speichern!"))); return false; } - EmailManager::getInstance()->addEmail(new model::Email(emailVerificationCode, newUser, model::EMAIL_ADMIN_USER_VERIFICATION_CODE)); + EmailManager::getInstance()->addEmail(new model::Email(email_verification_code, newUser, model::EMAIL_ADMIN_USER_VERIFICATION_CODE)); + + std::unique_lock _lock(mSharedMutex); + mEmailVerificationCodeObject = email_verification_code; return true; } - +// bool Session::createUser(const std::string& first_name, const std::string& last_name, const std::string& email, const std::string& password) { Profiler usedTime; @@ -263,7 +267,7 @@ bool Session::createUser(const std::string& first_name, const std::string& last_ writeUserIntoDB->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_USER_WRITTEN, this)); writeUserIntoDB->scheduleTask(writeUserIntoDB); - + std::unique_lock _lock(mSharedMutex); mEmailVerificationCodeObject = controller::EmailVerificationCode::create(model::table::EMAIL_OPT_IN_REGISTER); UniLib::controller::TaskPtr writeEmailVerification(new WriteEmailVerification(mSessionUser, mEmailVerificationCodeObject, ServerConfig::g_CPUScheduler, 1)); @@ -320,6 +324,7 @@ bool Session::createUser(const std::string& first_name, const std::string& last_ bool Session::createUserDirect(const std::string& first_name, const std::string& last_name, const std::string& email, const std::string& password) { + std::unique_lock _lock(mSharedMutex); static const char* function_name = "Session::createUserDirect"; auto sm = SessionManager::getInstance(); auto em = ErrorManager::getInstance(); @@ -376,6 +381,7 @@ bool Session::createUserDirect(const std::string& first_name, const std::string& // email verification code auto email_verification = controller::EmailVerificationCode::create(user_id, model::table::EMAIL_OPT_IN_REGISTER_DIRECT); email_verification->getModel()->insertIntoDB(false); + mEmailVerificationCodeObject = email_verification; auto _7days_later = Poco::DateTime() + Poco::Timespan(7, 0, 0, 0, 0); ServerConfig::g_CronJobsTimer.schedule(new VerificationEmailResendTimerTask(user_id), Poco::Timestamp(_7days_later.timestamp())); @@ -411,6 +417,8 @@ int Session::updateEmailVerification(Poco::UInt64 emailVerificationCode) const static char* funcName = "Session::updateEmailVerification"; Poco::ScopedLock _lock(mWorkMutex); + // new mutex, will replace the Poco Mutex complete in the future + std::unique_lock _lock_shared(mSharedMutex); Profiler usedTime; auto em = ErrorManager::getInstance(); @@ -529,28 +537,42 @@ int Session::sendResetPasswordEmail(Poco::AutoPtr user, bool p mSessionUser = new User(user); auto em = EmailManager::getInstance(); + std::unique_lock _lock(mSharedMutex); + // creating email verification code also for user without passphrase // first check if already exist - mEmailVerificationCodeObject = controller::EmailVerificationCode::create(mNewUser->getModel()->getID(), model::table::EMAIL_OPT_IN_RESET_PASSWORD); - auto foundCount = mEmailVerificationCodeObject->load(user->getModel()->getID(), model::table::EMAIL_OPT_IN_RESET_PASSWORD); - auto emailVerificationModel = mEmailVerificationCodeObject->getModel(); + // check if email was already send shortly before + bool frequent_resend = false; + bool email_already_send = false; - UniLib::controller::TaskPtr insertEmailVerificationCode( - new model::table::ModelInsertTask(emailVerificationModel, true, true) - ); - insertEmailVerificationCode->scheduleTask(insertEmailVerificationCode); - - if (passphraseMemorized) { - em->addEmail(new model::Email(mEmailVerificationCodeObject, mNewUser, model::EMAIL_USER_RESET_PASSWORD)); + mEmailVerificationCodeObject = controller::EmailVerificationCode::load(user->getModel()->getID(), model::table::EMAIL_OPT_IN_RESET_PASSWORD); + if (mEmailVerificationCodeObject.isNull()) { + mEmailVerificationCodeObject = controller::EmailVerificationCode::create(mNewUser->getModel()->getID(), model::table::EMAIL_OPT_IN_RESET_PASSWORD); + mEmailVerificationCodeObject->getModel()->insertIntoDB(false); } else { - em->addEmail(new model::Email(user, model::EMAIL_ADMIN_RESET_PASSWORD_REQUEST_WITHOUT_MEMORIZED_PASSPHRASE)); + email_already_send = true; + } + auto email_verification_model = mEmailVerificationCodeObject->getModel(); + if (email_already_send) { + auto time_elapsed = Poco::DateTime() - email_verification_model->getUpdated(); + if (time_elapsed.totalHours() < 1) { + frequent_resend = true; + } } - if (foundCount) { - return 1; + if (!frequent_resend) { + if (passphraseMemorized) { + em->addEmail(new model::Email(mEmailVerificationCodeObject, mNewUser, model::EMAIL_USER_RESET_PASSWORD)); + } + else { + em->addEmail(new model::Email(user, model::EMAIL_ADMIN_RESET_PASSWORD_REQUEST_WITHOUT_MEMORIZED_PASSPHRASE)); + } } + if (frequent_resend) return 2; + if (email_already_send) return 1; + return 0; } @@ -882,7 +904,7 @@ void Session::detectSessionState() resetPasswd = i; } } - + std::unique_lock _lock_shared(mSharedMutex); if (resetPasswd != -1) { mEmailVerificationCodeObject = emailVerificationCodeObjects[resetPasswd]; } @@ -992,7 +1014,7 @@ bool Session::loadFromEmailVerificationCode(Poco::UInt64 emailVerificationCode) { Profiler usedTime; auto em = ErrorManager::getInstance(); - + std::unique_lock _lock(mSharedMutex); mEmailVerificationCodeObject = controller::EmailVerificationCode::load(emailVerificationCode); if (mEmailVerificationCodeObject.isNull()) { addError(new Error(gettext("E-Mail Verification"), gettext("Konnte kein passendes Konto finden."))); diff --git a/src/cpp/model/Session.h b/src/cpp/model/Session.h index 2e747e2c3..69a9612e0 100644 --- a/src/cpp/model/Session.h +++ b/src/cpp/model/Session.h @@ -27,7 +27,7 @@ #include "Poco/Net/IPAddress.h" #include "Poco/Net/HTTPCookie.h" - +#include class WriteEmailVerification; @@ -109,6 +109,7 @@ public: // called from page with same name //! \return 1 = reset password email already send + //! \return 2 = reset password email already shortly before //! \return 0 = ok int sendResetPasswordEmail(Poco::AutoPtr user, bool passphraseMemorized); // @@ -145,8 +146,16 @@ public: const char* getSessionStateString(); inline SessionStates getSessionState() { SessionStates s; lock("Session::getSessionState"); s = mState; unlock(); return s; } - inline Poco::UInt64 getEmailVerificationCode() { if (mEmailVerificationCodeObject.isNull()) return 0; return mEmailVerificationCodeObject->getModel()->getCode(); } + inline Poco::UInt64 getEmailVerificationCode() { + std::shared_lock _lock(mSharedMutex); + if (mEmailVerificationCodeObject.isNull()) return 0; return mEmailVerificationCodeObject->getModel()->getCode(); + } + inline void setEmailVerificationCodeObject(Poco::AutoPtr emailVerficationObject) { + std::unique_lock _lock(mSharedMutex); + mEmailVerificationCodeObject = emailVerficationObject; + } inline model::table::EmailOptInType getEmailVerificationType() { + std::shared_lock _lock(mSharedMutex); if (mEmailVerificationCodeObject.isNull()) { return model::table::EMAIL_OPT_IN_EMPTY; } @@ -197,6 +206,7 @@ private: Poco::Net::IPAddress mClientLoginIP; std::string mLastExternReferer; Poco::AutoPtr mEmailVerificationCodeObject; + std::shared_mutex mSharedMutex; SessionStates mState; diff --git a/src/cpsp/resetPassword.cpsp b/src/cpsp/resetPassword.cpsp index a329a6e8f..bdb48c251 100644 --- a/src/cpsp/resetPassword.cpsp +++ b/src/cpsp/resetPassword.cpsp @@ -78,11 +78,13 @@ enum PageState { if(!errorCount()) { // send reset password email + int result = 0; if(user_exist) { - session->sendResetPasswordEmail(user, sendUserEmail); + result = session->sendResetPasswordEmail(user, sendUserEmail); } - - if(sendUserEmail) { + if(2 == result) { + state = PAGE_EMAIL_ALREADY_SEND; + } else if(sendUserEmail) { state = PAGE_WAIT_EMAIL; } else { state = PAGE_WAIT_ADMIN;