From 55922753a7ffd9552be132501d744da491c409b5 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 22 Feb 2021 20:11:54 +0100 Subject: [PATCH] Add auto-sign Transaction functionality --- src/cpp/JSONInterface/JsonTransaction.cpp | 325 +-- src/cpp/JSONInterface/JsonTransaction.h | 33 +- src/cpp/model/Session.cpp | 2627 +++++++++++---------- src/cpp/model/Session.h | 563 ++--- 4 files changed, 1790 insertions(+), 1758 deletions(-) diff --git a/src/cpp/JSONInterface/JsonTransaction.cpp b/src/cpp/JSONInterface/JsonTransaction.cpp index 777fca295..9e0d0aff9 100644 --- a/src/cpp/JSONInterface/JsonTransaction.cpp +++ b/src/cpp/JSONInterface/JsonTransaction.cpp @@ -1,158 +1,169 @@ -#include "JsonTransaction.h" -#include "Poco/URI.h" -#include "Poco/Dynamic/Struct.h" - -#include "../SingletonManager/SessionManager.h" - -Poco::JSON::Object* JsonTransaction::handle(Poco::Dynamic::Var params) -{ - Poco::JSON::Object* result = new Poco::JSON::Object; - int session_id = 0; - - // if is json object - if (params.type() == typeid(Poco::JSON::Object::Ptr)) { - Poco::JSON::Object::Ptr paramJsonObject = params.extract(); - - try { - /// Throws a RangeException if the value does not fit - /// into the result variable. - /// Throws a NotImplementedException if conversion is - /// not available for the given type. - /// Throws InvalidAccessException if Var is empty. - paramJsonObject->get("session_id").convert(session_id); - auto sm = SessionManager::getInstance(); - if (session_id != 0) { - auto session = sm->getSession(session_id); - if (!session) { - result->set("state", "error"); - result->set("msg", "session not found"); - return result; - } - - int balance = 0; - if (!paramJsonObject->isNull("balance")) { - paramJsonObject->get("balance").convert(balance); - if (balance) { - auto u = session->getUser(); - if (u) { - u->setBalance(balance); - } - auto nu = session->getNewUser(); - if (!nu.isNull()) { - nu->setBalance(balance); - } - } - } - - std::string transactionBase64String; - Poco::Dynamic::Var transaction_base64 = paramJsonObject->get("transaction_base64"); - int alreadyEnlisted = 0; - if (transaction_base64.isString()) { - paramJsonObject->get("transaction_base64").convert(transactionBase64String); - - if (!session->startProcessingTransaction(transactionBase64String)) { - auto lastError = session->getLastError(); - if (lastError) delete lastError; - result->set("state", "error"); - result->set("msg", "already enlisted"); - return result; - } - - } else { - Poco::DynamicStruct ds = *paramJsonObject; - for (int i = 0; i < ds["transaction_base64"].size(); i++) { - ds["transaction_base64"][i].convert(transactionBase64String); - if (!session->startProcessingTransaction(transactionBase64String)) { - auto lastError = session->getLastError(); - if (lastError) delete lastError; - alreadyEnlisted++; - } - } - - if (alreadyEnlisted > 0) { - result->set("state", "warning"); - result->set("msg", std::to_string(alreadyEnlisted) + " already enlisted"); - return result; - } - } - - - - result->set("state", "success"); - return result; - } - - } - catch (Poco::Exception& ex) { - printf("[JsonTransaction::handle] try to use params as jsonObject: %s\n", ex.displayText().data()); - result->set("state", "error"); - result->set("msg", "json exception"); - result->set("details", ex.displayText()); - return result; - } - } - else if (params.isVector()) { - const Poco::URI::QueryParameters queryParams = params.extract(); - auto transactionIT = queryParams.begin(); - for (auto it = queryParams.begin(); it != queryParams.end(); it++) { - if (it->first == "session_id") { - session_id = stoi(it->second); - //break; - } - else if (it->first == "transaction_base64") { - transactionIT = it; - } - } - if (session_id) { - auto sm = SessionManager::getInstance(); - auto session = sm->getSession(session_id); - if (!session) { - result->set("state", "error"); - result->set("msg", "session not found"); - return result; - } - if (!session->startProcessingTransaction(transactionIT->second)) { - auto lastError = session->getLastError(); - if (lastError) delete lastError; - result->set("state", "error"); - result->set("msg", "already enlisted"); - return result; - } - result->set("state", "success"); - return result; - } - else { - result->set("state", "error"); - result->set("msg", "session id not set"); - return result; - } - } - else if (params.isStruct()) { - result->set("state", "error"); - result->set("msg", "struct not implemented yet"); - } - else if (params.isArray()) { - result->set("state", "error"); - result->set("msg", "array not implemented yet"); - } - else if (params.isList()) { - result->set("state", "error"); - result->set("msg", "list not implemented yet"); - } - else if (params.isString()) { - result->set("state", "error"); - result->set("msg", "string not implemented yet"); - } - else if (params.isDeque()) { - result->set("state", "error"); - result->set("meg", "deque not implemented yet"); - } - else { - - result->set("state", "error"); - result->set("msg", "format not implemented"); - result->set("details", std::string(params.type().name())); - } - - return result; +#include "JsonTransaction.h" +#include "Poco/URI.h" +#include "Poco/Dynamic/Struct.h" + +#include "../SingletonManager/SessionManager.h" +#include "../ServerConfig.h" + +Poco::JSON::Object* JsonTransaction::handle(Poco::Dynamic::Var params) +{ + Poco::JSON::Object* result = new Poco::JSON::Object; + int session_id = 0; + + // if is json object + if (params.type() == typeid(Poco::JSON::Object::Ptr)) { + Poco::JSON::Object::Ptr paramJsonObject = params.extract(); + + try { + /// Throws a RangeException if the value does not fit + /// into the result variable. + /// Throws a NotImplementedException if conversion is + /// not available for the given type. + /// Throws InvalidAccessException if Var is empty. + paramJsonObject->get("session_id").convert(session_id); + auto sm = SessionManager::getInstance(); + if (session_id != 0) { + auto session = sm->getSession(session_id); + if (!session) { + result->set("state", "error"); + result->set("msg", "session not found"); + return result; + } + + int balance = 0; + if (!paramJsonObject->isNull("balance")) { + paramJsonObject->get("balance").convert(balance); + if (balance) { + auto u = session->getUser(); + if (u) { + u->setBalance(balance); + } + auto nu = session->getNewUser(); + if (!nu.isNull()) { + nu->setBalance(balance); + } + } + } + + std::string transactionBase64String; + Poco::Dynamic::Var transaction_base64 = paramJsonObject->get("transaction_base64"); + + if (transaction_base64.isString()) { + paramJsonObject->get("transaction_base64").convert(transactionBase64String); + + if (!session->startProcessingTransaction(transactionBase64String)) { + auto lastError = session->getLastError(); + if (lastError) delete lastError; + result->set("state", "error"); + result->set("msg", "already enlisted"); + return result; + } + + } else { + Poco::DynamicStruct ds = *paramJsonObject; + int alreadyEnlisted = 0; + + for (int i = 0; i < ds["transaction_base64"].size(); i++) { + ds["transaction_base64"][i].convert(transactionBase64String); + if (!session->startProcessingTransaction(transactionBase64String)) { + auto lastError = session->getLastError(); + if (lastError) delete lastError; + alreadyEnlisted++; + } + } + + if (alreadyEnlisted > 0) { + result->set("state", "warning"); + result->set("msg", std::to_string(alreadyEnlisted) + " already enlisted"); + return result; + } + } + + + + result->set("state", "success"); + return result; + } + + } + catch (Poco::Exception& ex) { + printf("[JsonTransaction::handle] try to use params as jsonObject: %s\n", ex.displayText().data()); + result->set("state", "error"); + result->set("msg", "json exception"); + result->set("details", ex.displayText()); + return result; + } + } + else if (params.isVector()) { + const Poco::URI::QueryParameters queryParams = params.extract(); + auto transactionIT = queryParams.begin(); + for (auto it = queryParams.begin(); it != queryParams.end(); it++) { + if (it->first == "session_id") { + session_id = stoi(it->second); + //break; + } + else if (it->first == "transaction_base64") { + transactionIT = it; + } + } + if (session_id) { + auto sm = SessionManager::getInstance(); + auto session = sm->getSession(session_id); + if (!session) { + result->set("state", "error"); + result->set("msg", "session not found"); + return result; + } + if (!session->startProcessingTransaction(transactionIT->second)) { + auto lastError = session->getLastError(); + if (lastError) delete lastError; + result->set("state", "error"); + result->set("msg", "already enlisted"); + return result; + } + result->set("state", "success"); + return result; + } + else { + result->set("state", "error"); + result->set("msg", "session id not set"); + return result; + } + } + else if (params.isStruct()) { + result->set("state", "error"); + result->set("msg", "struct not implemented yet"); + } + else if (params.isArray()) { + result->set("state", "error"); + result->set("msg", "array not implemented yet"); + } + else if (params.isList()) { + result->set("state", "error"); + result->set("msg", "list not implemented yet"); + } + else if (params.isString()) { + result->set("state", "error"); + result->set("msg", "string not implemented yet"); + } + else if (params.isDeque()) { + result->set("state", "error"); + result->set("meg", "deque not implemented yet"); + } + else { + + result->set("state", "error"); + result->set("msg", "format not implemented"); + result->set("details", std::string(params.type().name())); + } + + return result; +} + +bool JsonTransaction::startProcessingTransaction(Session* session, const std::string& transactionBase64) +{ + if ((ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_AUTO_SIGN_TRANSACTIONS) == ServerConfig::UNSECURE_AUTO_SIGN_TRANSACTIONS) { + + } + return session->startProcessingTransaction(transactionBase64); } \ No newline at end of file diff --git a/src/cpp/JSONInterface/JsonTransaction.h b/src/cpp/JSONInterface/JsonTransaction.h index 7a93cbbca..7d842fac0 100644 --- a/src/cpp/JSONInterface/JsonTransaction.h +++ b/src/cpp/JSONInterface/JsonTransaction.h @@ -1,16 +1,19 @@ -#ifndef __JSON_INTERFACE_JSON_TRANSACTION_ -#define __JSON_INTERFACE_JSON_TRANSACTION_ - -#include "JsonRequestHandler.h" - -class JsonTransaction : public JsonRequestHandler -{ -public: - Poco::JSON::Object* handle(Poco::Dynamic::Var params); - -protected: - - -}; - +#ifndef __JSON_INTERFACE_JSON_TRANSACTION_ +#define __JSON_INTERFACE_JSON_TRANSACTION_ + +#include "JsonRequestHandler.h" + +class Session; + +class JsonTransaction : public JsonRequestHandler +{ +public: + Poco::JSON::Object* handle(Poco::Dynamic::Var params); + +protected: + bool startProcessingTransaction(Session* session, const std::string& transactionBase64); + + +}; + #endif // __JSON_INTERFACE_JSON_TRANSACTION_ \ No newline at end of file diff --git a/src/cpp/model/Session.cpp b/src/cpp/model/Session.cpp index baec70cbc..8e98532df 100644 --- a/src/cpp/model/Session.cpp +++ b/src/cpp/model/Session.cpp @@ -1,1305 +1,1322 @@ -#include "Session.h" -#include "../lib/Profiler.h" -#include "../ServerConfig.h" - -#include "Poco/RegularExpression.h" -#include "Poco/Net/StringPartSource.h" -#include "Poco/Net/MediaType.h" - -#include "../SingletonManager/SessionManager.h" -#include "../SingletonManager/ConnectionManager.h" -#include "../SingletonManager/ErrorManager.h" -#include "../SingletonManager/EmailManager.h" -#include "../SingletonManager/SingletonTaskObserver.h" - -#include "../tasks/PrepareEmailTask.h" -#include "../tasks/SendEmailTask.h" -#include "../tasks/SigningTransaction.h" -#include "../tasks/AuthenticatedEncryptionCreateKeyTask.h" -#include "../tasks/VerificationEmailResendTask.h" - -#include "../lib/JsonRequest.h" - -#include "../Crypto/Passphrase.h" - - -#include "../controller/User.h" -#include "../controller/UserBackups.h" -#include "../controller/EmailVerificationCode.h" - -#include "table/ModelBase.h" - - -#include "sodium.h" - -using namespace Poco::Data::Keywords; - -int WriteEmailVerification::run() -{ - auto em = ErrorManager::getInstance(); - - mEmailVerificationCode->getModel()->setUserId(mUser->getDBId()); - auto emailVerificationModel = mEmailVerificationCode->getModel(); - emailVerificationModel->setUserId(mUser->getDBId()); - if (!emailVerificationModel->insertIntoDB(true) || emailVerificationModel->errorCount() > 0) { - emailVerificationModel->sendErrorsAsEmail(); - return -1; - } - - return 0; -} - -// --------------------------------------------------------------------------------------------------------------- - -int WritePassphraseIntoDB::run() -{ - Profiler timeUsed; - - // TODO: encrypt passphrase, need server admin crypto box pubkey - //int crypto_box_seal(unsigned char *c, const unsigned char *m, - //unsigned long long mlen, const unsigned char *pk); - size_t mlen = mPassphrase.size(); - size_t crypto_size = crypto_box_SEALBYTES + mlen; - - auto em = ErrorManager::getInstance(); - - auto dbSession = ConnectionManager::getInstance()->getConnection(CONNECTION_MYSQL_LOGIN_SERVER); - Poco::Data::Statement insert(dbSession); - insert << "INSERT INTO user_backups (user_id, passphrase) VALUES(?,?)", - use(mUserId), use(mPassphrase); - try { - if (insert.execute() != 1) { - em->addError(new ParamError("WritePassphraseIntoDB::run", "inserting passphrase for user failed", std::to_string(mUserId))); - em->sendErrorsAsEmail(); - } - } - catch (Poco::Exception& ex) { - em->addError(new ParamError("WritePassphraseIntoDB::run", "insert passphrase mysql error", ex.displayText().data())); - em->sendErrorsAsEmail(); - } - - //printf("[WritePassphraseIntoDB] timeUsed: %s\n", timeUsed.string().data()); - return 0; -} - - -// -------------------------------------------------------------------------------------------------------------- - -Session::Session(int handle) - : mHandleId(handle), mSessionUser(nullptr), mState(SESSION_STATE_EMPTY), mActive(false) -{ - -} - -Session::~Session() -{ - //printf("[Session::~Session] \n"); - if (tryLock()) { - unlock(); - reset(); - } - - - //printf("[Session::~Session] finished \n"); -} - - -void Session::reset() -{ - //printf("[Session::reset]\n"); - lock("Session::reset"); - std::unique_lock _lock(mSharedMutex); - mSessionUser.assign(nullptr); - mNewUser.assign(nullptr); - mEmailVerificationCodeObject.assign(nullptr); - - // watch out - //updateTimeout(); - mLastActivity = Poco::DateTime(); - - mState = SESSION_STATE_EMPTY; - - mPassphrase = ""; - mLastExternReferer = ""; - mClientLoginIP = Poco::Net::IPAddress(); - unlock(); - - // reset transactions - mCurrentActiveProcessingTransaction = nullptr; - mProcessingTransactions.clear(); - - //printf("[Session::reset] finished\n"); -} - -int Session::isActive() -{ - int ret = 0; - try { - mWorkMutex.tryLock(100); - } - catch (Poco::TimeoutException &ex) { - return -1; - } - ret = (int)mActive; - unlock(); - return ret; - -} - -bool Session::isDeadLocked() -{ - try { - mWorkMutex.tryLock(200); - unlock(); - return false; - } - catch (Poco::Exception& ex) { - - } - return true; -} - -bool Session::setActive(bool active) -{ - try { - mWorkMutex.tryLock(100); - } - catch (Poco::TimeoutException &ex) { - return false; - } - mActive = active; - unlock(); - return true; -} - -void Session::updateTimeout() -{ - lock("Session::updateTimeout"); - mLastActivity = Poco::DateTime(); - unlock(); -} - -Poco::AutoPtr Session::getEmailVerificationCodeObject() -{ - lock("Session::getEmailVerificationCodeObject"); - std::shared_lock _lock(mSharedMutex); - auto ret = mEmailVerificationCodeObject; - unlock(); - return ret; -} - -bool Session::adminCreateUser(const std::string& first_name, const std::string& last_name, const std::string& email) -{ - Profiler usedTime; - - if (mNewUser->getModel()->getRole() != model::table::ROLE_ADMIN) { - addError(new Error(gettext("Benutzer"), gettext("Eingeloggter Benutzer ist kein Admin")), false); - return false; - } - - auto sm = SessionManager::getInstance(); - if (!sm->isValid(first_name, VALIDATE_NAME)) { - addError(new Error(gettext("Vorname"), gettext("Bitte gebe einen Namen an. Mindestens 3 Zeichen, keines folgender Zeichen <>&;")), false); - return false; - } - if (!sm->isValid(last_name, VALIDATE_NAME)) { - addError(new Error(gettext("Nachname"), gettext("Bitte gebe einen Namen an. Mindestens 3 Zeichen, keines folgender Zeichen <>&;")), false); - return false; - } - if (!sm->isValid(email, VALIDATE_EMAIL)) { - addError(new Error(gettext("E-Mail"), gettext("Bitte gebe eine gültige E-Mail Adresse an.")), false); - return false; - } - - - // check if user with that email already exist - if (mNewUser->getModel()->isExistInDB("email", email)) { - addError(new Error(gettext("E-Mail"), gettext("Für diese E-Mail Adresse gibt es bereits einen Account")), false); - return false; - } - - auto newUser = controller::User::create(email, first_name, last_name); - updateTimeout(); - - - auto newUserModel = newUser->getModel(); - if (!newUserModel->insertIntoDB(true)) { - addError(new Error(gettext("Benutzer"), gettext("Fehler beim speichern!"))); - return 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(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; - auto sm = SessionManager::getInstance(); - if (!sm->isValid(first_name, VALIDATE_NAME)) { - addError(new Error(gettext("Vorname"), gettext("Bitte gebe einen Namen an. Mindestens 3 Zeichen, keines folgender Zeichen <>&;")), false); - return false; - } - if (!sm->isValid(last_name, VALIDATE_NAME)) { - addError(new Error(gettext("Nachname"), gettext("Bitte gebe einen Namen an. Mindestens 3 Zeichen, keines folgender Zeichen <>&;")), false); - return false; - } - if (!sm->isValid(email, VALIDATE_EMAIL)) { - addError(new Error(gettext("E-Mail"), gettext("Bitte gebe eine gültige E-Mail Adresse an.")), false); - return false; - } - if (!sm->checkPwdValidation(password, this)) { - return false; - } - /*if (passphrase.size() > 0 && !sm->isValid(passphrase, VALIDATE_PASSPHRASE)) { - addError(new Error("Merkspruch", "Der Merkspruch ist nicht gültig, er besteht aus 24 Wörtern, mit Komma getrennt.")); - return false; - } - if (passphrase.size() == 0) { - //mPassphrase = User::generateNewPassphrase(&ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER]); - mPassphrase = User::generateNewPassphrase(&ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER]); - } - else { - //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(gettext("E-Mail"), gettext("Für diese E-Mail Adresse gibt es bereits einen Account")), false); - return false; - } - } - catch (Poco::Exception& exc) { - printf("mysql exception: %s\n", exc.displayText().data()); - } - - mSessionUser = new User(email.data(), first_name.data(), last_name.data()); - mNewUser = controller::User::create(email, first_name, last_name); - 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, mNewUser, password, ServerConfig::g_CryptoCPUScheduler)); - cryptoKeyTask->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_CRYPTO_KEY_GENERATED, this)); - cryptoKeyTask->scheduleTask(cryptoKeyTask); - - // 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); - - 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)); - - writeEmailVerification->setParentTaskPtrInArray(writeUserIntoDB, 0); - writeEmailVerification->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_EMAIL_VERIFICATION_WRITTEN, this)); - writeEmailVerification->scheduleTask(writeEmailVerification); - - - /*printf("LastName: %s\n", last_name.data()); - for (int i = 0; i < last_name.size(); i++) { - char c = last_name.data()[i]; - //printf("%d ", c); - } - //printf("\n\n"); - */ - - // depends on writeUser because need user_id, write email verification into db - /*auto message = new Poco::Net::MailMessage; - Poco::Net::MediaType mt("text", "plain"); - mt.setParameter("charset", "utf-8"); - message->setContentType(mt); - - message->addRecipient(Poco::Net::MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, email)); - message->setSubject(gettext("Gradido: E-Mail Verification")); - std::stringstream ss; - ss << "Hallo " << first_name << " " << last_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: " << ServerConfig::g_serverPath << "/checkEmail/" << mEmailVerificationCode << std::endl; - //ss << "oder kopiere den Code: " << mEmailVerificationCode << " selbst dort hinein." << std::endl; - ss << "oder kopiere den obigen Link in Dein Browserfenster." << std::endl; - ss << std::endl; - ss << "Mit freundlichen " << u8"Grüßen" << 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)); - //Email(AutoPtr emailVerification, AutoPtr user, EmailType type); - UniLib::controller::TaskPtr sendEmail(new SendEmailTask(new model::Email(mEmailVerificationCodeObject, mNewUser, model::EMAIL_USER_VERIFICATION_CODE), ServerConfig::g_CPUScheduler, 1)); - //sendEmail->setParentTaskPtrInArray(prepareEmail, 0); - sendEmail->setParentTaskPtrInArray(writeEmailVerification, 0); - 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 - - //printf("[Session::createUser] time: %s\n", usedTime.string().data()); - - return true; -} - -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(); - auto email_manager = EmailManager::getInstance(); - - if (!sm->isValid(first_name, VALIDATE_NAME)) { - addError(new Error(gettext("Vorname"), gettext("Bitte gebe einen Namen an. Mindestens 3 Zeichen, keines folgender Zeichen <>&;")), false); - return false; - } - if (!sm->isValid(last_name, VALIDATE_NAME)) { - addError(new Error(gettext("Nachname"), gettext("Bitte gebe einen Namen an. Mindestens 3 Zeichen, keines folgender Zeichen <>&;")), false); - return false; - } - if (!sm->isValid(email, VALIDATE_EMAIL)) { - addError(new Error(gettext("E-Mail"), gettext("Bitte gebe eine gültige E-Mail Adresse an.")), false); - return false; - } - if (!sm->checkPwdValidation(password, this)) { - return false; - } - - // check if email already exist - auto user = controller::User::create(); - if (user->load(email) >= 1) { - addError(new Error(gettext("E-Mail"), gettext("Für diese E-Mail Adresse gibt es bereits ein Konto")), false); - return false; - } - - // user - mNewUser = controller::User::create(email, first_name, last_name); - auto user_model = mNewUser->getModel(); - user_model->insertIntoDB(true); - auto user_id = user_model->getID(); - - - // one retry in case of connection error - if (!user_id) { - user_model->insertIntoDB(true); - auto user_id = user_model->getID(); - if (!user_id) { - em->addError(new ParamError(function_name, "error saving new user in db, after one retry with email", email)); - em->sendErrorsAsEmail(); - addError(new Error(gettext("Server"), gettext("Fehler beim speichen des Kontos bitte versuche es später noch einmal")), false); - return false; - } - } - - generateKeys(true, true); - - // calculate encryption key, could need some time, will save encrypted privkey to db - UniLib::controller::TaskPtr create_authenticated_encrypten_key = new AuthenticatedEncryptionCreateKeyTask(mNewUser, password); - create_authenticated_encrypten_key->scheduleTask(create_authenticated_encrypten_key); - - // 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())); - - email_manager->addEmail(new model::Email(email_verification, mNewUser, model::EMAIL_USER_VERIFICATION_CODE)); - - return true; -} - -bool Session::ifUserExist(const std::string& email) -{ - auto em = ErrorManager::getInstance(); - const char* funcName = "Session::ifUserExist"; - auto dbConnection = ConnectionManager::getInstance()->getConnection(CONNECTION_MYSQL_LOGIN_SERVER); - Poco::Data::Statement select(dbConnection); - bool emailChecked = false; - int userId = 0; - select << "SELECT email_checked, id from users where email = ? and email_checked = 1", - into(emailChecked), into(userId), useRef(email); - - try { - if(select.execute() == 1) return true; - } - catch (Poco::Exception& ex) { - em->addError(new ParamError(funcName, "select user from email verification code mysql error ", ex.displayText().data())); - em->sendErrorsAsEmail(); - } - return false; -} - -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(); - if (mEmailVerificationCodeObject.isNull()) { - em->addError(new Error(funcName, "email verification object is zero")); - em->sendErrorsAsEmail(); - - return -2; - } - auto email_verification_code_model = mEmailVerificationCodeObject->getModel(); - assert(email_verification_code_model); - if(email_verification_code_model->getCode() == 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; - return -2; - } - - // load correct user from db - if (mNewUser.isNull() || !mNewUser->getModel() || mNewUser->getModel()->getID() != email_verification_code_model->getUserId()) { - mNewUser = controller::User::create(); - if (1 != mNewUser->load(email_verification_code_model->getUserId())) { - em->addError(new ParamError(funcName, "user load didn't return 1 with user_id ", email_verification_code_model->getUserId())); - em->sendErrorsAsEmail(); - - return -2; - } - } - - auto user_model = mNewUser->getModel(); - assert(user_model); - bool first_email_activation = false; - auto verification_type = email_verification_code_model->getType(); - if (model::table::EMAIL_OPT_IN_REGISTER == verification_type || - model::table::EMAIL_OPT_IN_EMPTY == verification_type || - model::table::EMAIL_OPT_IN_REGISTER_DIRECT == verification_type) { - first_email_activation = true; - } - if (first_email_activation && user_model->isEmailChecked()) { - mSessionUser = new User(mNewUser); - addError(new Error(gettext("E-Mail Verification"), gettext("Du hast dein Konto bereits aktiviert!")), false); - - return 1; - } - if (first_email_activation) { - user_model->setEmailChecked(true); - - user_model->updateIntoDB("email_checked", 1); - if (user_model->errorCount() > 0) { - user_model->sendErrorsAsEmail(); - } - - // no find all active sessions - - updateState(SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED); - return 0; - } - - if (email_verification_code_model->getType() == model::table::EMAIL_OPT_IN_RESET_PASSWORD) { - - if (mEmailVerificationCodeObject->deleteFromDB()) { - mEmailVerificationCodeObject.assign(nullptr); - } - else { - em->getErrors(mEmailVerificationCodeObject->getModel()); - em->addError(new Error(funcName, "error deleting email verification code")); - em->sendErrorsAsEmail(); - return -2; - } - updateState(SESSION_STATE_RESET_PASSWORD_REQUEST); - return 0; - } - - em->addError(new Error(funcName, "invalid code path")); - em->sendErrorsAsEmail(); - - return -2; - - /*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(); - } - if (mSessionUser) { - mSessionUser->setEmailChecked(); - mSessionUser->setLanguage(getLanguage()); - } - updateState(SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED); - //printf("[%s] time: %s\n", funcName, usedTime.string().data()); - unlock(); - return true; - } - else { - em->addError(new ParamError(funcName, "update user work not like expected, updated row count", updated_rows)); - em->sendErrorsAsEmail(); - }*/ - - - } - else { - addError(new Error(gettext("E-Mail Verification"), gettext("Falscher Code für aktiven Login"))); - //printf("[%s] time: %s\n", funcName, usedTime.string().data()); - - return -1; - } - //printf("[%s] time: %s\n", funcName, usedTime.string().data()); - - return 0; -} - - -int Session::sendResetPasswordEmail(Poco::AutoPtr user, bool passphraseMemorized) -{ - mNewUser = user; - 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 - // check if email was already send shortly before - bool frequent_resend = false; - bool email_already_send = false; - - 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 { - 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 (!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; -} - -int Session::comparePassphraseWithSavedKeys(const std::string& inputPassphrase, Mnemonic* wordSource) -{ - KeyPair keys; - static const char* functionName = "Session::comparePassphraseWithSavedKeys"; - if (!wordSource) { - addError(new Error(functionName, "wordSource is empty")); - sendErrorsAsEmail(); - return -2; - } - if (!keys.generateFromPassphrase(inputPassphrase.data(), wordSource)) { - addError(new ParamError(functionName, "invalid passphrase", inputPassphrase)); - if (!mNewUser.isNull() && mNewUser->getModel()) { - addError(new ParamError(functionName, "user email", mNewUser->getModel()->getEmail())); - } - sendErrorsAsEmail(); - addError(new Error(gettext("Passphrase"), gettext("Deine Passphrase ist ungütig")), false); - return 0; - } - auto userModel = mNewUser->getModel(); - auto existingPublic = userModel->getPublicKey(); - if (!existingPublic) { - userModel->loadFromDB("email", userModel->getEmail()); - existingPublic = userModel->getPublicKey(); - if (!existingPublic) { - addError(new Error(functionName, "cannot load existing public key from db")); - addError(new ParamError(functionName, "user email", userModel->getEmail())); - sendErrorsAsEmail(); - addError(new Error(gettext("Passphrase"), gettext("Ein Fehler trat auf, bitte versuche es erneut")), false); - return -1; - } - } - if (0 == memcmp(userModel->getPublicKey(), keys.getPublicKey(), crypto_sign_PUBLICKEYBYTES)) { - mPassphrase = inputPassphrase; - return 1; - } - addError(new Error(gettext("Passphrase"), gettext("Das ist nicht die richtige Passphrase.")), false); - return 0; -} - -bool Session::startProcessingTransaction(const std::string& proto_message_base64) -{ - static const char* funcName = "Session::startProcessingTransaction"; - lock(funcName); - HASH hs = ProcessingTransaction::calculateHash(proto_message_base64); - // check if it is already running or waiting - for (auto it = mProcessingTransactions.begin(); it != mProcessingTransactions.end(); it++) { - if (it->isNull()) { - it = mProcessingTransactions.erase(it); - } - if (hs == (*it)->getHash()) { - addError(new Error(funcName, "transaction already in list")); - unlock(); - return false; - } - } - if (mSessionUser.isNull() || !mSessionUser->getEmail()) { - addError(new Error(funcName, "user is zero")); - unlock(); - return false; - } - - Poco::AutoPtr processorTask( - new ProcessingTransaction( - proto_message_base64, - DRMakeStringHash(mSessionUser->getEmail()), - mSessionUser->getLanguage()) - ); - processorTask->scheduleTask(processorTask); - mProcessingTransactions.push_back(processorTask); - unlock(); - return true; - -} - -Poco::AutoPtr Session::getNextReadyTransaction(size_t* working/* = nullptr*/) -{ - lock("Session::getNextReadyTransaction"); - if (working) { - *working = 0; - } - else if (!mCurrentActiveProcessingTransaction.isNull()) - { - unlock(); - return mCurrentActiveProcessingTransaction; - } - for (auto it = mProcessingTransactions.begin(); it != mProcessingTransactions.end(); it++) { - if (working && !(*it)->isTaskFinished()) { - (*working)++; - } - if (mCurrentActiveProcessingTransaction.isNull() && (*it)->isTaskFinished()) { - if (!working) { - mCurrentActiveProcessingTransaction = *it; - unlock(); - return mCurrentActiveProcessingTransaction; - } - // no early exit - else { - mCurrentActiveProcessingTransaction = *it; - } - - } - } - unlock(); - return mCurrentActiveProcessingTransaction; -} - -bool Session::finalizeTransaction(bool sign, bool reject) -{ - int result = -1; - lock("Session::finalizeTransaction"); - if (mCurrentActiveProcessingTransaction.isNull()) { - unlock(); - return false; - } - mProcessingTransactions.remove(mCurrentActiveProcessingTransaction); - - if (!reject) { - if (sign) { - Poco::AutoPtr signingTransaction(new SigningTransaction(mCurrentActiveProcessingTransaction, mNewUser)); - //signingTransaction->scheduleTask(signingTransaction); - result = signingTransaction->run(); - } - } - mCurrentActiveProcessingTransaction.assign(nullptr); - unlock(); - return result == 0; -} - -size_t Session::getProcessingTransactionCount() -{ - size_t count = 0; - lock("Session::getProcessingTransactionCount"); - - for (auto it = mProcessingTransactions.begin(); it != mProcessingTransactions.end(); it++) { - - (*it)->lock(); - if ((*it)->errorCount() > 0) { - (*it)->sendErrorsAsEmail(); - (*it)->unlock(); - it = mProcessingTransactions.erase(it); - if (it == mProcessingTransactions.end()) break; - } - else { - (*it)->unlock(); - } - - } - count = mProcessingTransactions.size(); - unlock(); - return count; -} - -bool Session::isPwdValid(const std::string& pwd) -{ - if (mSessionUser) { - return mSessionUser->validatePwd(pwd, this); - } - return false; -} - -UserStates Session::loadUser(const std::string& email, const std::string& password) -{ - static const char* functionName = "Session::loadUser"; - auto observer = SingletonTaskObserver::getInstance(); - if (email != "") { - if (observer->getTaskCount(email, TASK_OBSERVER_PASSWORD_CREATION) > 0) { - return USER_PASSWORD_ENCRYPTION_IN_PROCESS; - } - } - //Profiler usedTime; - //printf("before lock\n"); - lock(functionName); - //printf("locked \n"); - if (!mSessionUser.isNull() && mSessionUser->getEmail() != email) { - mSessionUser.assign(nullptr); - mNewUser.assign(nullptr); - //printf("user nullptr assigned\n"); - } - //printf("after checking if session user is null\n"); - //if (!mSessionUser) { - if (mNewUser.isNull()) { - //printf("new user is null\n"); - mNewUser = controller::User::create(); - //printf("new user created\n"); - // load user for email only once from db - mNewUser->load(email); - //printf("load new user from db with email: %s\n", email.data()); - mSessionUser = new User(mNewUser); - //mSessionUser = new User(email.data()); - - //printf("user loaded from email\n"); - } - //printf("before get model\n"); - auto user_model = mNewUser->getModel(); - if (user_model && user_model->isDisabled()) { - return USER_DISABLED; - } - //printf("before if login\n"); - if (!mSessionUser.isNull() && mSessionUser->getUserState() >= USER_LOADED_FROM_DB) { - //printf("before login\n"); - int loginResult = 0; - int exitCount = 0; - do { - loginResult = mNewUser->login(password); - Poco::Thread::sleep(100); - exitCount++; - } while (-3 == loginResult && exitCount < 15); - if (exitCount > 1) { - addError(new ParamError(functionName, "login succeed, retrys: ", exitCount)); - addError(new ParamError(functionName, "email: ", email)); - sendErrorsAsEmail(); - } - - if (exitCount >= 15) - { - auto running_password_creations = observer->getTasksCount(TASK_OBSERVER_PASSWORD_CREATION); - - addError(new ParamError(functionName, "login failed after 15 retrys and 100 ms sleep between, currently running passwort creation tasks: ", running_password_creations)); - addError(new ParamError(functionName, "email: ", email)); - sendErrorsAsEmail(); - return USER_PASSWORD_ENCRYPTION_IN_PROCESS; - } - - //printf("new user login with result: %d\n", loginResult); - - if (-1 == loginResult) { - addError(new Error(functionName, "error in user data set, saved pubkey didn't match extracted pubkey from private key")); - addError(new ParamError(functionName, "user email", mNewUser->getModel()->getEmail())); - sendErrorsAsEmail(); - //unlock(); - //return USER_KEYS_DONT_MATCH; - } - if (0 == loginResult) { - unlock(); - return USER_PASSWORD_INCORRECT; - } - // error decrypting private key - if (-2 == loginResult) { - // check if we have access to the passphrase, if so we can reencrypt the private key - printf("try reencrypting key\n"); - auto user_model = mNewUser->getModel(); - auto user_backups = controller::UserBackups::load(user_model->getID()); - for (auto it = user_backups.begin(); it != user_backups.end(); it++) { - auto key = std::unique_ptr((*it)->createGradidoKeyPair()); - if (key->isTheSame(user_model->getPublicKey())) - { - - // set valid key pair - if (1 == mNewUser->setGradidoKeyPair(key.release())) { - // save new encrypted private key - user_model->updatePrivkey(); - } - else { - auto em = ErrorManager::getInstance(); - em->addError(new Error(functionName, "error reencrypt private key")); - em->addError(new ParamError(functionName, "for user with email", user_model->getEmail())); - em->sendErrorsAsEmail(); - } - break; - } - } - } - // can be removed if session user isn't used any more - // don't calculate password two times anymore - mSessionUser->login(mNewUser); - //printf("after old user login\n"); - /*if (mNewUser->getModel()->getPasswordHashed() && !mSessionUser->validatePwd(password, this)) { - unlock(); - return USER_PASSWORD_INCORRECT; - }*/ - } - else { - //printf("before sleep\n"); - User::fakeCreateCryptoKey(); - } - - /*if (!mSessionUser->validatePwd(password, this)) { - addError(new Error("Login", "E-Mail oder Passwort nicht korrekt, bitte versuche es erneut!")); - unlock(); - return false; - } - if (!mSessionUser->isEmailChecked()) { - addError(new Error("Account", "E-Mail Adresse wurde noch nicht bestätigt, hast du schon eine E-Mail erhalten?")); - unlock(); - return false; - }*/ - //printf("before detect session state\n"); - detectSessionState(); - unlock(); - //printf("before return user state\n"); - return mSessionUser->getUserState(); -} - -bool Session::deleteUser() -{ - lock("Session::deleteUser"); - bool bResult = false; - if(mSessionUser) { - JsonRequest phpServerRequest(ServerConfig::g_php_serverHost, 443); - Poco::Net::NameValueCollection payload; - payload.add("user", std::string(mSessionUser->getPublicKeyHex())); - //auto ret = phpServerRequest.request("userDelete", payload); - JsonRequestReturn ret = JSON_REQUEST_RETURN_OK; - if (ret == JSON_REQUEST_RETURN_ERROR) { - addError(new Error("Session::deleteUser", "php server error")); - getErrors(&phpServerRequest); - sendErrorsAsEmail(); - } - else if (ret == JSON_REQUEST_RETURN_OK) { - bResult = mSessionUser->deleteFromDB(); - } - else { - addError(new Error(gettext("Benutzer"), gettext("Konnte Community Server nicht erreichen. E-Mail an den Admin ist raus."))); - unlock(); - return false; - } - } - if(!bResult) { - addError(new Error(gettext("Benutzer"), gettext("Fehler beim Löschen des Accounts. Bitte logge dich erneut ein und versuche es nochmal."))); - } - unlock(); - return bResult; -} - -void Session::setLanguage(Languages lang) -{ - //printf("[Session::setLanguage] new language: %d\n", lang); - lock("Session::setLanguage"); - if (mLanguageCatalog.isNull() || mLanguageCatalog->getLanguage() != lang) { - auto lm = LanguageManager::getInstance(); - mLanguageCatalog = lm->getFreeCatalog(lang); - } - unlock(); -} - -Languages Session::getLanguage() -{ - Languages lang = LANG_NULL; - lock("Session::getLanguage"); - if (!mLanguageCatalog.isNull()) { - lang = mLanguageCatalog->getLanguage(); - } - unlock(); - return lang; -} - - -/* -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_PASSPHRASE_GENERATED, -SESSION_STATE_PASSPHRASE_SHOWN, -SESSION_STATE_PASSPHRASE_WRITTEN, -SESSION_STATE_KEY_PAIR_GENERATED, -SESSION_STATE_KEY_PAIR_WRITTEN, -SESSION_STATE_COUNT -*/ -void Session::detectSessionState() -{ - if (mSessionUser.isNull() || !mSessionUser->hasCryptoKey()) { - return; - } - UserStates userState = mSessionUser->getUserState(); - - int checkEmail = -1, resetPasswd = -1; - try { - auto emailVerificationCodeObjects = controller::EmailVerificationCode::load(mSessionUser->getDBId()); - - for (int i = 0; i < emailVerificationCodeObjects.size(); i++) { - auto type = emailVerificationCodeObjects[i]->getModel()->getType(); - if (type == model::table::EMAIL_OPT_IN_EMPTY || type == model::table::EMAIL_OPT_IN_REGISTER) { - checkEmail = i; - } - else if (type == model::table::EMAIL_OPT_IN_RESET_PASSWORD) { - resetPasswd = i; - } - } - std::unique_lock _lock_shared(mSharedMutex); - if (resetPasswd != -1) { - mEmailVerificationCodeObject = emailVerificationCodeObjects[resetPasswd]; - } - else if (checkEmail != -1) { - mEmailVerificationCodeObject = emailVerificationCodeObjects[checkEmail]; - } - - } - catch (Poco::Exception& ex) { - printf("[Session::detectSessionState] exception: %s\n", ex.displayText().data()); - //return; - } - - if (userState <= USER_EMAIL_NOT_ACTIVATED) { - - if (checkEmail != -1) { - updateState(SESSION_STATE_EMAIL_VERIFICATION_WRITTEN); - return; - } - - updateState(SESSION_STATE_USER_WRITTEN); - return; - } - - if (USER_NO_KEYS == userState) { - - auto user_id = mSessionUser->getDBId(); - auto userBackups = controller::UserBackups::load(user_id); - - // check passphrase, only possible while passphrase isn't crypted in db - bool correctPassphraseFound = false; - // always trigger SESSION_STATE_PASSPHRASE_WRITTEN, else lost of data possible - bool cryptedPassphrase = userBackups.size() > 0; - for (auto it = userBackups.begin(); it != userBackups.end(); it++) { - KeyPair keys; - auto passphrase = (*it)->getModel()->getPassphrase(); - Mnemonic* wordSource = nullptr; - if (User::validatePassphrase(passphrase, &wordSource)) { - if (keys.generateFromPassphrase((*it)->getModel()->getPassphrase().data(), wordSource)) { - if (sodium_memcmp(mSessionUser->getPublicKey(), keys.getPublicKey(), ed25519_pubkey_SIZE) == 0) { - correctPassphraseFound = true; - break; - } - } - } - else { - cryptedPassphrase = true; - } - } - /* - auto dbConnection = ConnectionManager::getInstance()->getConnection(CONNECTION_MYSQL_LOGIN_SERVER); - Poco::Data::Statement select(dbConnection); - Poco::Nullable passphrase; - - select << "SELECT passphrase from user_backups where user_id = ?;", - into(passphrase), use(user_id); - try { - if (select.execute() == 1 && !passphrase.isNull()) { - //KeyPair keys;keys.generateFromPassphrase(passphrase.value().rawContent()) - updateState(SESSION_STATE_PASSPHRASE_WRITTEN); - return; - } - } - catch (Poco::Exception& exc) { - printf("[Session::detectSessionState] 2 mysql exception: %s\n", exc.displayText().data()); - }*/ - if (correctPassphraseFound || cryptedPassphrase) { - updateState(SESSION_STATE_PASSPHRASE_WRITTEN); - return; - } - if (mPassphrase != "") { - updateState(SESSION_STATE_PASSPHRASE_GENERATED); - return; - } - updateState(SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED); - return; - } - - updateState(SESSION_STATE_KEY_PAIR_WRITTEN); - - if (resetPasswd != -1) { - // don't go to reset password screen after login, only throw checkEmail - //updateState(SESSION_STATE_RESET_PASSWORD_REQUEST); - return; - } - -} - -Poco::Net::HTTPCookie Session::getLoginCookie() -{ - auto keks = Poco::Net::HTTPCookie("GRADIDO_LOGIN", std::to_string(mHandleId)); - // prevent reading or changing cookie with js - keks.setHttpOnly(); - keks.setPath("/"); - // send cookie only via https, on linux, except in test builds -#ifndef WIN32 - if (ServerConfig::SERVER_TYPE_PRODUCTION == ServerConfig::g_ServerSetupType || - ServerConfig::SERVER_TYPE_STAGING == ServerConfig::g_ServerSetupType) { - keks.setSecure(true); - } -#endif - - return keks; -} - -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."))); - return false; - } - - mNewUser = controller::User::create(); - assert(mEmailVerificationCodeObject->getModel() && mEmailVerificationCodeObject->getModel()->getUserId()); - mNewUser->load(mEmailVerificationCodeObject->getModel()->getUserId()); - if (mNewUser->getModel()->errorCount() > 0) { - mNewUser->getModel()->sendErrorsAsEmail(); - addError(new Error(gettext("E-Mail Verification"), gettext("Fehler beim laden des Benutzers."))); - return false; - } - mSessionUser = new User(mNewUser); - mSessionUser->setLanguage(getLanguage()); - - auto verificationType = mEmailVerificationCodeObject->getModel()->getType(); - if (verificationType == model::table::EMAIL_OPT_IN_RESET_PASSWORD) { - updateState(SESSION_STATE_RESET_PASSWORD_REQUEST); - } - else { - updateState(SESSION_STATE_EMAIL_VERIFICATION_WRITTEN); - } - - return true; -} - -void Session::updateState(SessionStates newState) -{ - lock("Session::updateState"); - if (!mActive) { - unlock(); - return; - } - updateTimeout(); - //printf("[%s] newState: %s\n", __FUNCTION__, translateSessionStateToString(newState)); - if (newState > mState) { - mState = newState; - } - - unlock(); -} - -const char* Session::getSessionStateString() -{ - SessionStates state; - lock("Session::getSessionStateString"); - state = mState; - unlock(); - return translateSessionStateToString(state); -} - - -const char* Session::translateSessionStateToString(SessionStates state) -{ - switch (state) { - case SESSION_STATE_EMPTY: return "uninitalized"; - 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_EMAIL_VERIFICATION_CODE_CHECKED: return "Verification Code checked"; - case SESSION_STATE_PASSPHRASE_GENERATED: return "Passphrase generated"; - case SESSION_STATE_PASSPHRASE_SHOWN: return "Passphrase shown"; - case SESSION_STATE_PASSPHRASE_WRITTEN: return "Passphrase written"; - case SESSION_STATE_KEY_PAIR_GENERATED: return "Gradido Address created"; - case SESSION_STATE_KEY_PAIR_WRITTEN: return "Gradido Address saved"; - case SESSION_STATE_RESET_PASSWORD_REQUEST: return "Passwort reset requested"; - case SESSION_STATE_RESET_PASSWORD_SUCCEED: return "Passwort reset succeeded"; - default: return "unknown"; - } - - return "error"; -} - - -/* -bool Session::useOrGeneratePassphrase(const std::string& passphase) -{ - if (passphase != "" && User::validatePassphrase(passphase)) { - // passphrase is valid - setPassphrase(passphase); - updateState(SESSION_STATE_PASSPHRASE_SHOWN); - return true; - } - else { - mPassphrase = User::generateNewPassphrase(&ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER]); - updateState(SESSION_STATE_PASSPHRASE_GENERATED); - return true; - } -} -*/ -bool Session::generatePassphrase() -{ - if (mNewUser.isNull()) return false; - - auto lang = getLanguage(); - if (lang == LANG_EN) { - mPassphrase = User::generateNewPassphrase(&ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER]); - } - else { - mPassphrase = User::generateNewPassphrase(&ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER]); - } - //mPassphrase = User::generateNewPassphrase(&ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER]); - updateState(SESSION_STATE_PASSPHRASE_GENERATED); - return true; -} - -bool Session::generateKeys(bool savePrivkey, bool savePassphrase) -{ - if (mNewUser.isNull()) { - addError(new Error(gettext("Benutzer"), gettext("Kein gültiger Benutzer, bitte logge dich erneut ein."))); - return false; - } - static const char* function_name = "Session::generateKeys"; - auto lang = getLanguage(); - auto user_model = mNewUser->getModel(); - auto mnemonic_type = ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER; - /*if (LANG_DE == lang) { - mnemonic_type = ServerConfig::MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER_FIXED_CASES; - }*/ - - auto passphrase = Passphrase::generate(&ServerConfig::g_Mnemonic_WordLists[mnemonic_type]); - if (!passphrase) { - addError(new ParamError(function_name, "Error generating passphrase with mnemonic: ", mnemonic_type)); - addError(new ParamError(function_name, "user email: ", mNewUser->getModel()->getEmail())); - sendErrorsAsEmail(); - addError(new Error(gettext("Benutzer"), gettext("Fehler beim generieren der Passphrase, der Admin bekommt eine E-Mail. "))); - return false; - } - - if (savePassphrase) { - auto user_backup = controller::UserBackups::create(user_model->getID(), passphrase->getString(), mnemonic_type); - // sync version - //user_backup->getModel()->insertIntoDB(false); - - // async version - UniLib::controller::TaskPtr save_user_backup_task = new model::table::ModelInsertTask(user_backup->getModel(), false, true); - save_user_backup_task->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_PASSPHRASE_WRITTEN, this)); - save_user_backup_task->scheduleTask(save_user_backup_task); - } - - // keys - auto gradido_key_pair = KeyPairEd25519::create(passphrase); - auto set_key_result = mNewUser->setGradidoKeyPair(gradido_key_pair); - size_t result_save_key = 0; - if (1 == set_key_result && savePrivkey) { - // save public key and private key in db - result_save_key = user_model->updatePubkeyAndPrivkey(); - } - else { - // save public key in db - result_save_key = user_model->updatePublickey(); - } - if (!result_save_key) { - user_model->addError(new Error(function_name, "Error saving new generated pubkey")); - user_model->addError(new ParamError(function_name, "e-mail: ", user_model->getEmail())); - user_model->sendErrorsAsEmail(); - //addError(new Error(gettext("Benutzer"), gettext("Fehler beim Speichern der Keys, der Admin bekommt eine E-Mail. Evt. nochmal versuchen oder abwarten!"))); - return false; - } - return true; - /* - - bool validUser = true; - if (mSessionUser) { - if (!mSessionUser->generateKeys(savePrivkey, mPassphrase, this)) { - validUser = false; - } - else { - if (savePassphrase) { - //printf("[Session::generateKeys] create save passphrase task\n"); - UniLib::controller::TaskPtr savePassphrase(new WritePassphraseIntoDB(mSessionUser->getDBId(), mPassphrase)); - savePassphrase->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_PASSPHRASE_WRITTEN, this)); - savePassphrase->scheduleTask(savePassphrase); - } - } - } - else { - validUser = false; - } - if (!validUser) { - addError(new Error(gettext("Benutzer"), gettext("Kein gültiger Benutzer, bitte logge dich erneut ein."))); - return false; - } - // delete passphrase after all went well - mPassphrase.clear(); - - return true; - */ -} +#include "Session.h" +#include "../lib/Profiler.h" +#include "../ServerConfig.h" + +#include "Poco/RegularExpression.h" +#include "Poco/Net/StringPartSource.h" +#include "Poco/Net/MediaType.h" + +#include "../SingletonManager/SessionManager.h" +#include "../SingletonManager/ConnectionManager.h" +#include "../SingletonManager/ErrorManager.h" +#include "../SingletonManager/EmailManager.h" +#include "../SingletonManager/SingletonTaskObserver.h" + +#include "../tasks/PrepareEmailTask.h" +#include "../tasks/SendEmailTask.h" +#include "../tasks/SigningTransaction.h" +#include "../tasks/AuthenticatedEncryptionCreateKeyTask.h" +#include "../tasks/VerificationEmailResendTask.h" + +#include "../lib/JsonRequest.h" + +#include "../Crypto/Passphrase.h" + + +#include "../controller/User.h" +#include "../controller/UserBackups.h" +#include "../controller/EmailVerificationCode.h" + +#include "table/ModelBase.h" + + +#include "sodium.h" + +using namespace Poco::Data::Keywords; + +int WriteEmailVerification::run() +{ + auto em = ErrorManager::getInstance(); + + mEmailVerificationCode->getModel()->setUserId(mUser->getDBId()); + auto emailVerificationModel = mEmailVerificationCode->getModel(); + emailVerificationModel->setUserId(mUser->getDBId()); + if (!emailVerificationModel->insertIntoDB(true) || emailVerificationModel->errorCount() > 0) { + emailVerificationModel->sendErrorsAsEmail(); + return -1; + } + + return 0; +} + +// --------------------------------------------------------------------------------------------------------------- + +int WritePassphraseIntoDB::run() +{ + Profiler timeUsed; + + // TODO: encrypt passphrase, need server admin crypto box pubkey + //int crypto_box_seal(unsigned char *c, const unsigned char *m, + //unsigned long long mlen, const unsigned char *pk); + size_t mlen = mPassphrase.size(); + size_t crypto_size = crypto_box_SEALBYTES + mlen; + + auto em = ErrorManager::getInstance(); + + auto dbSession = ConnectionManager::getInstance()->getConnection(CONNECTION_MYSQL_LOGIN_SERVER); + Poco::Data::Statement insert(dbSession); + insert << "INSERT INTO user_backups (user_id, passphrase) VALUES(?,?)", + use(mUserId), use(mPassphrase); + try { + if (insert.execute() != 1) { + em->addError(new ParamError("WritePassphraseIntoDB::run", "inserting passphrase for user failed", std::to_string(mUserId))); + em->sendErrorsAsEmail(); + } + } + catch (Poco::Exception& ex) { + em->addError(new ParamError("WritePassphraseIntoDB::run", "insert passphrase mysql error", ex.displayText().data())); + em->sendErrorsAsEmail(); + } + + //printf("[WritePassphraseIntoDB] timeUsed: %s\n", timeUsed.string().data()); + return 0; +} + + +// -------------------------------------------------------------------------------------------------------------- + +Session::Session(int handle) + : mHandleId(handle), mSessionUser(nullptr), mState(SESSION_STATE_EMPTY), mActive(false) +{ + +} + +Session::~Session() +{ + //printf("[Session::~Session] \n"); + if (tryLock()) { + unlock(); + reset(); + } + + + //printf("[Session::~Session] finished \n"); +} + + +void Session::reset() +{ + //printf("[Session::reset]\n"); + lock("Session::reset"); + std::unique_lock _lock(mSharedMutex); + mSessionUser.assign(nullptr); + mNewUser.assign(nullptr); + mEmailVerificationCodeObject.assign(nullptr); + + // watch out + //updateTimeout(); + mLastActivity = Poco::DateTime(); + + mState = SESSION_STATE_EMPTY; + + mPassphrase = ""; + mLastExternReferer = ""; + mClientLoginIP = Poco::Net::IPAddress(); + unlock(); + + // reset transactions + mCurrentActiveProcessingTransaction = nullptr; + mProcessingTransactions.clear(); + + //printf("[Session::reset] finished\n"); +} + +int Session::isActive() +{ + int ret = 0; + try { + mWorkMutex.tryLock(100); + } + catch (Poco::TimeoutException &ex) { + return -1; + } + ret = (int)mActive; + unlock(); + return ret; + +} + +bool Session::isDeadLocked() +{ + try { + mWorkMutex.tryLock(200); + unlock(); + return false; + } + catch (Poco::Exception& ex) { + + } + return true; +} + +bool Session::setActive(bool active) +{ + try { + mWorkMutex.tryLock(100); + } + catch (Poco::TimeoutException &ex) { + return false; + } + mActive = active; + unlock(); + return true; +} + +void Session::updateTimeout() +{ + lock("Session::updateTimeout"); + mLastActivity = Poco::DateTime(); + unlock(); +} + +Poco::AutoPtr Session::getEmailVerificationCodeObject() +{ + lock("Session::getEmailVerificationCodeObject"); + std::shared_lock _lock(mSharedMutex); + auto ret = mEmailVerificationCodeObject; + unlock(); + return ret; +} + +bool Session::adminCreateUser(const std::string& first_name, const std::string& last_name, const std::string& email) +{ + Profiler usedTime; + + if (mNewUser->getModel()->getRole() != model::table::ROLE_ADMIN) { + addError(new Error(gettext("Benutzer"), gettext("Eingeloggter Benutzer ist kein Admin")), false); + return false; + } + + auto sm = SessionManager::getInstance(); + if (!sm->isValid(first_name, VALIDATE_NAME)) { + addError(new Error(gettext("Vorname"), gettext("Bitte gebe einen Namen an. Mindestens 3 Zeichen, keines folgender Zeichen <>&;")), false); + return false; + } + if (!sm->isValid(last_name, VALIDATE_NAME)) { + addError(new Error(gettext("Nachname"), gettext("Bitte gebe einen Namen an. Mindestens 3 Zeichen, keines folgender Zeichen <>&;")), false); + return false; + } + if (!sm->isValid(email, VALIDATE_EMAIL)) { + addError(new Error(gettext("E-Mail"), gettext("Bitte gebe eine gültige E-Mail Adresse an.")), false); + return false; + } + + + // check if user with that email already exist + if (mNewUser->getModel()->isExistInDB("email", email)) { + addError(new Error(gettext("E-Mail"), gettext("Für diese E-Mail Adresse gibt es bereits einen Account")), false); + return false; + } + + auto newUser = controller::User::create(email, first_name, last_name); + updateTimeout(); + + + auto newUserModel = newUser->getModel(); + if (!newUserModel->insertIntoDB(true)) { + addError(new Error(gettext("Benutzer"), gettext("Fehler beim speichern!"))); + return 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(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; + auto sm = SessionManager::getInstance(); + if (!sm->isValid(first_name, VALIDATE_NAME)) { + addError(new Error(gettext("Vorname"), gettext("Bitte gebe einen Namen an. Mindestens 3 Zeichen, keines folgender Zeichen <>&;")), false); + return false; + } + if (!sm->isValid(last_name, VALIDATE_NAME)) { + addError(new Error(gettext("Nachname"), gettext("Bitte gebe einen Namen an. Mindestens 3 Zeichen, keines folgender Zeichen <>&;")), false); + return false; + } + if (!sm->isValid(email, VALIDATE_EMAIL)) { + addError(new Error(gettext("E-Mail"), gettext("Bitte gebe eine gültige E-Mail Adresse an.")), false); + return false; + } + if (!sm->checkPwdValidation(password, this)) { + return false; + } + /*if (passphrase.size() > 0 && !sm->isValid(passphrase, VALIDATE_PASSPHRASE)) { + addError(new Error("Merkspruch", "Der Merkspruch ist nicht gültig, er besteht aus 24 Wörtern, mit Komma getrennt.")); + return false; + } + if (passphrase.size() == 0) { + //mPassphrase = User::generateNewPassphrase(&ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER]); + mPassphrase = User::generateNewPassphrase(&ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER]); + } + else { + //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(gettext("E-Mail"), gettext("Für diese E-Mail Adresse gibt es bereits einen Account")), false); + return false; + } + } + catch (Poco::Exception& exc) { + printf("mysql exception: %s\n", exc.displayText().data()); + } + + mSessionUser = new User(email.data(), first_name.data(), last_name.data()); + mNewUser = controller::User::create(email, first_name, last_name); + 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, mNewUser, password, ServerConfig::g_CryptoCPUScheduler)); + cryptoKeyTask->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_CRYPTO_KEY_GENERATED, this)); + cryptoKeyTask->scheduleTask(cryptoKeyTask); + + // 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); + + 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)); + + writeEmailVerification->setParentTaskPtrInArray(writeUserIntoDB, 0); + writeEmailVerification->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_EMAIL_VERIFICATION_WRITTEN, this)); + writeEmailVerification->scheduleTask(writeEmailVerification); + + + /*printf("LastName: %s\n", last_name.data()); + for (int i = 0; i < last_name.size(); i++) { + char c = last_name.data()[i]; + //printf("%d ", c); + } + //printf("\n\n"); + */ + + // depends on writeUser because need user_id, write email verification into db + /*auto message = new Poco::Net::MailMessage; + Poco::Net::MediaType mt("text", "plain"); + mt.setParameter("charset", "utf-8"); + message->setContentType(mt); + + message->addRecipient(Poco::Net::MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, email)); + message->setSubject(gettext("Gradido: E-Mail Verification")); + std::stringstream ss; + ss << "Hallo " << first_name << " " << last_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: " << ServerConfig::g_serverPath << "/checkEmail/" << mEmailVerificationCode << std::endl; + //ss << "oder kopiere den Code: " << mEmailVerificationCode << " selbst dort hinein." << std::endl; + ss << "oder kopiere den obigen Link in Dein Browserfenster." << std::endl; + ss << std::endl; + ss << "Mit freundlichen " << u8"Grüßen" << 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)); + //Email(AutoPtr emailVerification, AutoPtr user, EmailType type); + UniLib::controller::TaskPtr sendEmail(new SendEmailTask(new model::Email(mEmailVerificationCodeObject, mNewUser, model::EMAIL_USER_VERIFICATION_CODE), ServerConfig::g_CPUScheduler, 1)); + //sendEmail->setParentTaskPtrInArray(prepareEmail, 0); + sendEmail->setParentTaskPtrInArray(writeEmailVerification, 0); + 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 + + //printf("[Session::createUser] time: %s\n", usedTime.string().data()); + + return true; +} + +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(); + auto email_manager = EmailManager::getInstance(); + + if (!sm->isValid(first_name, VALIDATE_NAME)) { + addError(new Error(gettext("Vorname"), gettext("Bitte gebe einen Namen an. Mindestens 3 Zeichen, keines folgender Zeichen <>&;")), false); + return false; + } + if (!sm->isValid(last_name, VALIDATE_NAME)) { + addError(new Error(gettext("Nachname"), gettext("Bitte gebe einen Namen an. Mindestens 3 Zeichen, keines folgender Zeichen <>&;")), false); + return false; + } + if (!sm->isValid(email, VALIDATE_EMAIL)) { + addError(new Error(gettext("E-Mail"), gettext("Bitte gebe eine gültige E-Mail Adresse an.")), false); + return false; + } + if (!sm->checkPwdValidation(password, this)) { + return false; + } + + // check if email already exist + auto user = controller::User::create(); + if (user->load(email) >= 1) { + addError(new Error(gettext("E-Mail"), gettext("Für diese E-Mail Adresse gibt es bereits ein Konto")), false); + return false; + } + + // user + mNewUser = controller::User::create(email, first_name, last_name); + auto user_model = mNewUser->getModel(); + user_model->insertIntoDB(true); + auto user_id = user_model->getID(); + + + // one retry in case of connection error + if (!user_id) { + user_model->insertIntoDB(true); + auto user_id = user_model->getID(); + if (!user_id) { + em->addError(new ParamError(function_name, "error saving new user in db, after one retry with email", email)); + em->sendErrorsAsEmail(); + addError(new Error(gettext("Server"), gettext("Fehler beim speichen des Kontos bitte versuche es später noch einmal")), false); + return false; + } + } + + generateKeys(true, true); + + // calculate encryption key, could need some time, will save encrypted privkey to db + UniLib::controller::TaskPtr create_authenticated_encrypten_key = new AuthenticatedEncryptionCreateKeyTask(mNewUser, password); + create_authenticated_encrypten_key->scheduleTask(create_authenticated_encrypten_key); + + // 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())); + + email_manager->addEmail(new model::Email(email_verification, mNewUser, model::EMAIL_USER_VERIFICATION_CODE)); + + return true; +} + +bool Session::ifUserExist(const std::string& email) +{ + auto em = ErrorManager::getInstance(); + const char* funcName = "Session::ifUserExist"; + auto dbConnection = ConnectionManager::getInstance()->getConnection(CONNECTION_MYSQL_LOGIN_SERVER); + Poco::Data::Statement select(dbConnection); + bool emailChecked = false; + int userId = 0; + select << "SELECT email_checked, id from users where email = ? and email_checked = 1", + into(emailChecked), into(userId), useRef(email); + + try { + if(select.execute() == 1) return true; + } + catch (Poco::Exception& ex) { + em->addError(new ParamError(funcName, "select user from email verification code mysql error ", ex.displayText().data())); + em->sendErrorsAsEmail(); + } + return false; +} + +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(); + if (mEmailVerificationCodeObject.isNull()) { + em->addError(new Error(funcName, "email verification object is zero")); + em->sendErrorsAsEmail(); + + return -2; + } + auto email_verification_code_model = mEmailVerificationCodeObject->getModel(); + assert(email_verification_code_model); + if(email_verification_code_model->getCode() == 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; + return -2; + } + + // load correct user from db + if (mNewUser.isNull() || !mNewUser->getModel() || mNewUser->getModel()->getID() != email_verification_code_model->getUserId()) { + mNewUser = controller::User::create(); + if (1 != mNewUser->load(email_verification_code_model->getUserId())) { + em->addError(new ParamError(funcName, "user load didn't return 1 with user_id ", email_verification_code_model->getUserId())); + em->sendErrorsAsEmail(); + + return -2; + } + } + + auto user_model = mNewUser->getModel(); + assert(user_model); + bool first_email_activation = false; + auto verification_type = email_verification_code_model->getType(); + if (model::table::EMAIL_OPT_IN_REGISTER == verification_type || + model::table::EMAIL_OPT_IN_EMPTY == verification_type || + model::table::EMAIL_OPT_IN_REGISTER_DIRECT == verification_type) { + first_email_activation = true; + } + if (first_email_activation && user_model->isEmailChecked()) { + mSessionUser = new User(mNewUser); + addError(new Error(gettext("E-Mail Verification"), gettext("Du hast dein Konto bereits aktiviert!")), false); + + return 1; + } + if (first_email_activation) { + user_model->setEmailChecked(true); + + user_model->updateIntoDB("email_checked", 1); + if (user_model->errorCount() > 0) { + user_model->sendErrorsAsEmail(); + } + + // no find all active sessions + + updateState(SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED); + return 0; + } + + if (email_verification_code_model->getType() == model::table::EMAIL_OPT_IN_RESET_PASSWORD) { + + if (mEmailVerificationCodeObject->deleteFromDB()) { + mEmailVerificationCodeObject.assign(nullptr); + } + else { + em->getErrors(mEmailVerificationCodeObject->getModel()); + em->addError(new Error(funcName, "error deleting email verification code")); + em->sendErrorsAsEmail(); + return -2; + } + updateState(SESSION_STATE_RESET_PASSWORD_REQUEST); + return 0; + } + + em->addError(new Error(funcName, "invalid code path")); + em->sendErrorsAsEmail(); + + return -2; + + /*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(); + } + if (mSessionUser) { + mSessionUser->setEmailChecked(); + mSessionUser->setLanguage(getLanguage()); + } + updateState(SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED); + //printf("[%s] time: %s\n", funcName, usedTime.string().data()); + unlock(); + return true; + } + else { + em->addError(new ParamError(funcName, "update user work not like expected, updated row count", updated_rows)); + em->sendErrorsAsEmail(); + }*/ + + + } + else { + addError(new Error(gettext("E-Mail Verification"), gettext("Falscher Code für aktiven Login"))); + //printf("[%s] time: %s\n", funcName, usedTime.string().data()); + + return -1; + } + //printf("[%s] time: %s\n", funcName, usedTime.string().data()); + + return 0; +} + + +int Session::sendResetPasswordEmail(Poco::AutoPtr user, bool passphraseMemorized) +{ + mNewUser = user; + 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 + // check if email was already send shortly before + bool frequent_resend = false; + bool email_already_send = false; + + 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 { + 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 (!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; +} + +int Session::comparePassphraseWithSavedKeys(const std::string& inputPassphrase, Mnemonic* wordSource) +{ + KeyPair keys; + static const char* functionName = "Session::comparePassphraseWithSavedKeys"; + if (!wordSource) { + addError(new Error(functionName, "wordSource is empty")); + sendErrorsAsEmail(); + return -2; + } + if (!keys.generateFromPassphrase(inputPassphrase.data(), wordSource)) { + addError(new ParamError(functionName, "invalid passphrase", inputPassphrase)); + if (!mNewUser.isNull() && mNewUser->getModel()) { + addError(new ParamError(functionName, "user email", mNewUser->getModel()->getEmail())); + } + sendErrorsAsEmail(); + addError(new Error(gettext("Passphrase"), gettext("Deine Passphrase ist ungütig")), false); + return 0; + } + auto userModel = mNewUser->getModel(); + auto existingPublic = userModel->getPublicKey(); + if (!existingPublic) { + userModel->loadFromDB("email", userModel->getEmail()); + existingPublic = userModel->getPublicKey(); + if (!existingPublic) { + addError(new Error(functionName, "cannot load existing public key from db")); + addError(new ParamError(functionName, "user email", userModel->getEmail())); + sendErrorsAsEmail(); + addError(new Error(gettext("Passphrase"), gettext("Ein Fehler trat auf, bitte versuche es erneut")), false); + return -1; + } + } + if (0 == memcmp(userModel->getPublicKey(), keys.getPublicKey(), crypto_sign_PUBLICKEYBYTES)) { + mPassphrase = inputPassphrase; + return 1; + } + addError(new Error(gettext("Passphrase"), gettext("Das ist nicht die richtige Passphrase.")), false); + return 0; +} + +bool Session::startProcessingTransaction(const std::string& proto_message_base64) +{ + static const char* funcName = "Session::startProcessingTransaction"; + lock(funcName); + HASH hs = ProcessingTransaction::calculateHash(proto_message_base64); + // check if it is already running or waiting + for (auto it = mProcessingTransactions.begin(); it != mProcessingTransactions.end(); it++) { + if (it->isNull()) { + it = mProcessingTransactions.erase(it); + } + if (hs == (*it)->getHash()) { + addError(new Error(funcName, "transaction already in list")); + unlock(); + return false; + } + } + if (mSessionUser.isNull() || !mSessionUser->getEmail()) { + addError(new Error(funcName, "user is zero")); + unlock(); + return false; + } + + Poco::AutoPtr processorTask( + new ProcessingTransaction( + proto_message_base64, + DRMakeStringHash(mSessionUser->getEmail()), + mSessionUser->getLanguage()) + ); + if ((ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_AUTO_SIGN_TRANSACTIONS) == ServerConfig::UNSECURE_AUTO_SIGN_TRANSACTIONS) { + if (processorTask->run() != 0) { + getErrors(processorTask); + unlock(); + return false; + } + Poco::AutoPtr signingTransaction(new SigningTransaction(processorTask, mNewUser)); + //signingTransaction->scheduleTask(signingTransaction); + if (signingTransaction->run() != 0) { + getErrors(signingTransaction); + unlock(); + return false; + } + + } + else { + processorTask->scheduleTask(processorTask); + mProcessingTransactions.push_back(processorTask); + } + unlock(); + return true; + +} + +Poco::AutoPtr Session::getNextReadyTransaction(size_t* working/* = nullptr*/) +{ + lock("Session::getNextReadyTransaction"); + if (working) { + *working = 0; + } + else if (!mCurrentActiveProcessingTransaction.isNull()) + { + unlock(); + return mCurrentActiveProcessingTransaction; + } + for (auto it = mProcessingTransactions.begin(); it != mProcessingTransactions.end(); it++) { + if (working && !(*it)->isTaskFinished()) { + (*working)++; + } + if (mCurrentActiveProcessingTransaction.isNull() && (*it)->isTaskFinished()) { + if (!working) { + mCurrentActiveProcessingTransaction = *it; + unlock(); + return mCurrentActiveProcessingTransaction; + } + // no early exit + else { + mCurrentActiveProcessingTransaction = *it; + } + + } + } + unlock(); + return mCurrentActiveProcessingTransaction; +} + +bool Session::finalizeTransaction(bool sign, bool reject) +{ + int result = -1; + lock("Session::finalizeTransaction"); + if (mCurrentActiveProcessingTransaction.isNull()) { + unlock(); + return false; + } + mProcessingTransactions.remove(mCurrentActiveProcessingTransaction); + + if (!reject) { + if (sign) { + Poco::AutoPtr signingTransaction(new SigningTransaction(mCurrentActiveProcessingTransaction, mNewUser)); + //signingTransaction->scheduleTask(signingTransaction); + result = signingTransaction->run(); + } + } + mCurrentActiveProcessingTransaction.assign(nullptr); + unlock(); + return result == 0; +} + +size_t Session::getProcessingTransactionCount() +{ + size_t count = 0; + lock("Session::getProcessingTransactionCount"); + + for (auto it = mProcessingTransactions.begin(); it != mProcessingTransactions.end(); it++) { + + (*it)->lock(); + if ((*it)->errorCount() > 0) { + (*it)->sendErrorsAsEmail(); + (*it)->unlock(); + it = mProcessingTransactions.erase(it); + if (it == mProcessingTransactions.end()) break; + } + else { + (*it)->unlock(); + } + + } + count = mProcessingTransactions.size(); + unlock(); + return count; +} + +bool Session::isPwdValid(const std::string& pwd) +{ + if (mSessionUser) { + return mSessionUser->validatePwd(pwd, this); + } + return false; +} + +UserStates Session::loadUser(const std::string& email, const std::string& password) +{ + static const char* functionName = "Session::loadUser"; + auto observer = SingletonTaskObserver::getInstance(); + if (email != "") { + if (observer->getTaskCount(email, TASK_OBSERVER_PASSWORD_CREATION) > 0) { + return USER_PASSWORD_ENCRYPTION_IN_PROCESS; + } + } + //Profiler usedTime; + //printf("before lock\n"); + lock(functionName); + //printf("locked \n"); + if (!mSessionUser.isNull() && mSessionUser->getEmail() != email) { + mSessionUser.assign(nullptr); + mNewUser.assign(nullptr); + //printf("user nullptr assigned\n"); + } + //printf("after checking if session user is null\n"); + //if (!mSessionUser) { + if (mNewUser.isNull()) { + //printf("new user is null\n"); + mNewUser = controller::User::create(); + //printf("new user created\n"); + // load user for email only once from db + mNewUser->load(email); + //printf("load new user from db with email: %s\n", email.data()); + mSessionUser = new User(mNewUser); + //mSessionUser = new User(email.data()); + + //printf("user loaded from email\n"); + } + //printf("before get model\n"); + auto user_model = mNewUser->getModel(); + if (user_model && user_model->isDisabled()) { + return USER_DISABLED; + } + //printf("before if login\n"); + if (!mSessionUser.isNull() && mSessionUser->getUserState() >= USER_LOADED_FROM_DB) { + //printf("before login\n"); + int loginResult = 0; + int exitCount = 0; + do { + loginResult = mNewUser->login(password); + Poco::Thread::sleep(100); + exitCount++; + } while (-3 == loginResult && exitCount < 15); + if (exitCount > 1) { + addError(new ParamError(functionName, "login succeed, retrys: ", exitCount)); + addError(new ParamError(functionName, "email: ", email)); + sendErrorsAsEmail(); + } + + if (exitCount >= 15) + { + auto running_password_creations = observer->getTasksCount(TASK_OBSERVER_PASSWORD_CREATION); + + addError(new ParamError(functionName, "login failed after 15 retrys and 100 ms sleep between, currently running passwort creation tasks: ", running_password_creations)); + addError(new ParamError(functionName, "email: ", email)); + sendErrorsAsEmail(); + return USER_PASSWORD_ENCRYPTION_IN_PROCESS; + } + + //printf("new user login with result: %d\n", loginResult); + + if (-1 == loginResult) { + addError(new Error(functionName, "error in user data set, saved pubkey didn't match extracted pubkey from private key")); + addError(new ParamError(functionName, "user email", mNewUser->getModel()->getEmail())); + sendErrorsAsEmail(); + //unlock(); + //return USER_KEYS_DONT_MATCH; + } + if (0 == loginResult) { + unlock(); + return USER_PASSWORD_INCORRECT; + } + // error decrypting private key + if (-2 == loginResult) { + // check if we have access to the passphrase, if so we can reencrypt the private key + printf("try reencrypting key\n"); + auto user_model = mNewUser->getModel(); + auto user_backups = controller::UserBackups::load(user_model->getID()); + for (auto it = user_backups.begin(); it != user_backups.end(); it++) { + auto key = std::unique_ptr((*it)->createGradidoKeyPair()); + if (key->isTheSame(user_model->getPublicKey())) + { + + // set valid key pair + if (1 == mNewUser->setGradidoKeyPair(key.release())) { + // save new encrypted private key + user_model->updatePrivkey(); + } + else { + auto em = ErrorManager::getInstance(); + em->addError(new Error(functionName, "error reencrypt private key")); + em->addError(new ParamError(functionName, "for user with email", user_model->getEmail())); + em->sendErrorsAsEmail(); + } + break; + } + } + } + // can be removed if session user isn't used any more + // don't calculate password two times anymore + mSessionUser->login(mNewUser); + //printf("after old user login\n"); + /*if (mNewUser->getModel()->getPasswordHashed() && !mSessionUser->validatePwd(password, this)) { + unlock(); + return USER_PASSWORD_INCORRECT; + }*/ + } + else { + //printf("before sleep\n"); + User::fakeCreateCryptoKey(); + } + + /*if (!mSessionUser->validatePwd(password, this)) { + addError(new Error("Login", "E-Mail oder Passwort nicht korrekt, bitte versuche es erneut!")); + unlock(); + return false; + } + if (!mSessionUser->isEmailChecked()) { + addError(new Error("Account", "E-Mail Adresse wurde noch nicht bestätigt, hast du schon eine E-Mail erhalten?")); + unlock(); + return false; + }*/ + //printf("before detect session state\n"); + detectSessionState(); + unlock(); + //printf("before return user state\n"); + return mSessionUser->getUserState(); +} + +bool Session::deleteUser() +{ + lock("Session::deleteUser"); + bool bResult = false; + if(mSessionUser) { + JsonRequest phpServerRequest(ServerConfig::g_php_serverHost, 443); + Poco::Net::NameValueCollection payload; + payload.add("user", std::string(mSessionUser->getPublicKeyHex())); + //auto ret = phpServerRequest.request("userDelete", payload); + JsonRequestReturn ret = JSON_REQUEST_RETURN_OK; + if (ret == JSON_REQUEST_RETURN_ERROR) { + addError(new Error("Session::deleteUser", "php server error")); + getErrors(&phpServerRequest); + sendErrorsAsEmail(); + } + else if (ret == JSON_REQUEST_RETURN_OK) { + bResult = mSessionUser->deleteFromDB(); + } + else { + addError(new Error(gettext("Benutzer"), gettext("Konnte Community Server nicht erreichen. E-Mail an den Admin ist raus."))); + unlock(); + return false; + } + } + if(!bResult) { + addError(new Error(gettext("Benutzer"), gettext("Fehler beim Löschen des Accounts. Bitte logge dich erneut ein und versuche es nochmal."))); + } + unlock(); + return bResult; +} + +void Session::setLanguage(Languages lang) +{ + //printf("[Session::setLanguage] new language: %d\n", lang); + lock("Session::setLanguage"); + if (mLanguageCatalog.isNull() || mLanguageCatalog->getLanguage() != lang) { + auto lm = LanguageManager::getInstance(); + mLanguageCatalog = lm->getFreeCatalog(lang); + } + unlock(); +} + +Languages Session::getLanguage() +{ + Languages lang = LANG_NULL; + lock("Session::getLanguage"); + if (!mLanguageCatalog.isNull()) { + lang = mLanguageCatalog->getLanguage(); + } + unlock(); + return lang; +} + + +/* +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_PASSPHRASE_GENERATED, +SESSION_STATE_PASSPHRASE_SHOWN, +SESSION_STATE_PASSPHRASE_WRITTEN, +SESSION_STATE_KEY_PAIR_GENERATED, +SESSION_STATE_KEY_PAIR_WRITTEN, +SESSION_STATE_COUNT +*/ +void Session::detectSessionState() +{ + if (mSessionUser.isNull() || !mSessionUser->hasCryptoKey()) { + return; + } + UserStates userState = mSessionUser->getUserState(); + + int checkEmail = -1, resetPasswd = -1; + try { + auto emailVerificationCodeObjects = controller::EmailVerificationCode::load(mSessionUser->getDBId()); + + for (int i = 0; i < emailVerificationCodeObjects.size(); i++) { + auto type = emailVerificationCodeObjects[i]->getModel()->getType(); + if (type == model::table::EMAIL_OPT_IN_EMPTY || type == model::table::EMAIL_OPT_IN_REGISTER) { + checkEmail = i; + } + else if (type == model::table::EMAIL_OPT_IN_RESET_PASSWORD) { + resetPasswd = i; + } + } + std::unique_lock _lock_shared(mSharedMutex); + if (resetPasswd != -1) { + mEmailVerificationCodeObject = emailVerificationCodeObjects[resetPasswd]; + } + else if (checkEmail != -1) { + mEmailVerificationCodeObject = emailVerificationCodeObjects[checkEmail]; + } + + } + catch (Poco::Exception& ex) { + printf("[Session::detectSessionState] exception: %s\n", ex.displayText().data()); + //return; + } + + if (userState <= USER_EMAIL_NOT_ACTIVATED) { + + if (checkEmail != -1) { + updateState(SESSION_STATE_EMAIL_VERIFICATION_WRITTEN); + return; + } + + updateState(SESSION_STATE_USER_WRITTEN); + return; + } + + if (USER_NO_KEYS == userState) { + + auto user_id = mSessionUser->getDBId(); + auto userBackups = controller::UserBackups::load(user_id); + + // check passphrase, only possible while passphrase isn't crypted in db + bool correctPassphraseFound = false; + // always trigger SESSION_STATE_PASSPHRASE_WRITTEN, else lost of data possible + bool cryptedPassphrase = userBackups.size() > 0; + for (auto it = userBackups.begin(); it != userBackups.end(); it++) { + KeyPair keys; + auto passphrase = (*it)->getModel()->getPassphrase(); + Mnemonic* wordSource = nullptr; + if (User::validatePassphrase(passphrase, &wordSource)) { + if (keys.generateFromPassphrase((*it)->getModel()->getPassphrase().data(), wordSource)) { + if (sodium_memcmp(mSessionUser->getPublicKey(), keys.getPublicKey(), ed25519_pubkey_SIZE) == 0) { + correctPassphraseFound = true; + break; + } + } + } + else { + cryptedPassphrase = true; + } + } + /* + auto dbConnection = ConnectionManager::getInstance()->getConnection(CONNECTION_MYSQL_LOGIN_SERVER); + Poco::Data::Statement select(dbConnection); + Poco::Nullable passphrase; + + select << "SELECT passphrase from user_backups where user_id = ?;", + into(passphrase), use(user_id); + try { + if (select.execute() == 1 && !passphrase.isNull()) { + //KeyPair keys;keys.generateFromPassphrase(passphrase.value().rawContent()) + updateState(SESSION_STATE_PASSPHRASE_WRITTEN); + return; + } + } + catch (Poco::Exception& exc) { + printf("[Session::detectSessionState] 2 mysql exception: %s\n", exc.displayText().data()); + }*/ + if (correctPassphraseFound || cryptedPassphrase) { + updateState(SESSION_STATE_PASSPHRASE_WRITTEN); + return; + } + if (mPassphrase != "") { + updateState(SESSION_STATE_PASSPHRASE_GENERATED); + return; + } + updateState(SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED); + return; + } + + updateState(SESSION_STATE_KEY_PAIR_WRITTEN); + + if (resetPasswd != -1) { + // don't go to reset password screen after login, only throw checkEmail + //updateState(SESSION_STATE_RESET_PASSWORD_REQUEST); + return; + } + +} + +Poco::Net::HTTPCookie Session::getLoginCookie() +{ + auto keks = Poco::Net::HTTPCookie("GRADIDO_LOGIN", std::to_string(mHandleId)); + // prevent reading or changing cookie with js + keks.setHttpOnly(); + keks.setPath("/"); + // send cookie only via https, on linux, except in test builds +#ifndef WIN32 + if (ServerConfig::SERVER_TYPE_PRODUCTION == ServerConfig::g_ServerSetupType || + ServerConfig::SERVER_TYPE_STAGING == ServerConfig::g_ServerSetupType) { + keks.setSecure(true); + } +#endif + + return keks; +} + +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."))); + return false; + } + + mNewUser = controller::User::create(); + assert(mEmailVerificationCodeObject->getModel() && mEmailVerificationCodeObject->getModel()->getUserId()); + mNewUser->load(mEmailVerificationCodeObject->getModel()->getUserId()); + if (mNewUser->getModel()->errorCount() > 0) { + mNewUser->getModel()->sendErrorsAsEmail(); + addError(new Error(gettext("E-Mail Verification"), gettext("Fehler beim laden des Benutzers."))); + return false; + } + mSessionUser = new User(mNewUser); + mSessionUser->setLanguage(getLanguage()); + + auto verificationType = mEmailVerificationCodeObject->getModel()->getType(); + if (verificationType == model::table::EMAIL_OPT_IN_RESET_PASSWORD) { + updateState(SESSION_STATE_RESET_PASSWORD_REQUEST); + } + else { + updateState(SESSION_STATE_EMAIL_VERIFICATION_WRITTEN); + } + + return true; +} + +void Session::updateState(SessionStates newState) +{ + lock("Session::updateState"); + if (!mActive) { + unlock(); + return; + } + updateTimeout(); + //printf("[%s] newState: %s\n", __FUNCTION__, translateSessionStateToString(newState)); + if (newState > mState) { + mState = newState; + } + + unlock(); +} + +const char* Session::getSessionStateString() +{ + SessionStates state; + lock("Session::getSessionStateString"); + state = mState; + unlock(); + return translateSessionStateToString(state); +} + + +const char* Session::translateSessionStateToString(SessionStates state) +{ + switch (state) { + case SESSION_STATE_EMPTY: return "uninitalized"; + 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_EMAIL_VERIFICATION_CODE_CHECKED: return "Verification Code checked"; + case SESSION_STATE_PASSPHRASE_GENERATED: return "Passphrase generated"; + case SESSION_STATE_PASSPHRASE_SHOWN: return "Passphrase shown"; + case SESSION_STATE_PASSPHRASE_WRITTEN: return "Passphrase written"; + case SESSION_STATE_KEY_PAIR_GENERATED: return "Gradido Address created"; + case SESSION_STATE_KEY_PAIR_WRITTEN: return "Gradido Address saved"; + case SESSION_STATE_RESET_PASSWORD_REQUEST: return "Passwort reset requested"; + case SESSION_STATE_RESET_PASSWORD_SUCCEED: return "Passwort reset succeeded"; + default: return "unknown"; + } + + return "error"; +} + + +/* +bool Session::useOrGeneratePassphrase(const std::string& passphase) +{ + if (passphase != "" && User::validatePassphrase(passphase)) { + // passphrase is valid + setPassphrase(passphase); + updateState(SESSION_STATE_PASSPHRASE_SHOWN); + return true; + } + else { + mPassphrase = User::generateNewPassphrase(&ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER]); + updateState(SESSION_STATE_PASSPHRASE_GENERATED); + return true; + } +} +*/ +bool Session::generatePassphrase() +{ + if (mNewUser.isNull()) return false; + + auto lang = getLanguage(); + if (lang == LANG_EN) { + mPassphrase = User::generateNewPassphrase(&ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER]); + } + else { + mPassphrase = User::generateNewPassphrase(&ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER]); + } + //mPassphrase = User::generateNewPassphrase(&ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER]); + updateState(SESSION_STATE_PASSPHRASE_GENERATED); + return true; +} + +bool Session::generateKeys(bool savePrivkey, bool savePassphrase) +{ + if (mNewUser.isNull()) { + addError(new Error(gettext("Benutzer"), gettext("Kein gültiger Benutzer, bitte logge dich erneut ein."))); + return false; + } + static const char* function_name = "Session::generateKeys"; + auto lang = getLanguage(); + auto user_model = mNewUser->getModel(); + auto mnemonic_type = ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER; + /*if (LANG_DE == lang) { + mnemonic_type = ServerConfig::MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER_FIXED_CASES; + }*/ + + auto passphrase = Passphrase::generate(&ServerConfig::g_Mnemonic_WordLists[mnemonic_type]); + if (!passphrase) { + addError(new ParamError(function_name, "Error generating passphrase with mnemonic: ", mnemonic_type)); + addError(new ParamError(function_name, "user email: ", mNewUser->getModel()->getEmail())); + sendErrorsAsEmail(); + addError(new Error(gettext("Benutzer"), gettext("Fehler beim generieren der Passphrase, der Admin bekommt eine E-Mail. "))); + return false; + } + + if (savePassphrase) { + auto user_backup = controller::UserBackups::create(user_model->getID(), passphrase->getString(), mnemonic_type); + // sync version + //user_backup->getModel()->insertIntoDB(false); + + // async version + UniLib::controller::TaskPtr save_user_backup_task = new model::table::ModelInsertTask(user_backup->getModel(), false, true); + save_user_backup_task->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_PASSPHRASE_WRITTEN, this)); + save_user_backup_task->scheduleTask(save_user_backup_task); + } + + // keys + auto gradido_key_pair = KeyPairEd25519::create(passphrase); + auto set_key_result = mNewUser->setGradidoKeyPair(gradido_key_pair); + size_t result_save_key = 0; + if (1 == set_key_result && savePrivkey) { + // save public key and private key in db + result_save_key = user_model->updatePubkeyAndPrivkey(); + } + else { + // save public key in db + result_save_key = user_model->updatePublickey(); + } + if (!result_save_key) { + user_model->addError(new Error(function_name, "Error saving new generated pubkey")); + user_model->addError(new ParamError(function_name, "e-mail: ", user_model->getEmail())); + user_model->sendErrorsAsEmail(); + //addError(new Error(gettext("Benutzer"), gettext("Fehler beim Speichern der Keys, der Admin bekommt eine E-Mail. Evt. nochmal versuchen oder abwarten!"))); + return false; + } + return true; + /* + + bool validUser = true; + if (mSessionUser) { + if (!mSessionUser->generateKeys(savePrivkey, mPassphrase, this)) { + validUser = false; + } + else { + if (savePassphrase) { + //printf("[Session::generateKeys] create save passphrase task\n"); + UniLib::controller::TaskPtr savePassphrase(new WritePassphraseIntoDB(mSessionUser->getDBId(), mPassphrase)); + savePassphrase->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_PASSPHRASE_WRITTEN, this)); + savePassphrase->scheduleTask(savePassphrase); + } + } + } + else { + validUser = false; + } + if (!validUser) { + addError(new Error(gettext("Benutzer"), gettext("Kein gültiger Benutzer, bitte logge dich erneut ein."))); + return false; + } + // delete passphrase after all went well + mPassphrase.clear(); + + return true; + */ +} diff --git a/src/cpp/model/Session.h b/src/cpp/model/Session.h index 7044ff2c3..e70c24e14 100644 --- a/src/cpp/model/Session.h +++ b/src/cpp/model/Session.h @@ -1,281 +1,282 @@ -/*! -* -* \author: einhornimmond -* -* \date: 02.03.19 -* -* \brief: store session data -*/ - -#ifndef DR_LUA_WEB_MODULE_SESSION_SESSION_H -#define DR_LUA_WEB_MODULE_SESSION_SESSION_H - -#include "../lib/ErrorList.h" -#include "User.h" -#include "../controller/User.h" - -#include "../lib/MultithreadContainer.h" -#include "../tasks/ProcessingTransaction.h" - -#include "../SingletonManager/LanguageManager.h" - -#include "../controller/EmailVerificationCode.h" - -#include "Poco/Thread.h" -#include "Poco/Types.h" -#include "Poco/DateTime.h" -#include "Poco/Net/IPAddress.h" -#include "Poco/Net/HTTPCookie.h" - -#include - - -class WriteEmailVerification; - -enum SessionStates { - SESSION_STATE_EMPTY, - 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_PASSPHRASE_GENERATED, - SESSION_STATE_PASSPHRASE_SHOWN, - SESSION_STATE_PASSPHRASE_WRITTEN, - SESSION_STATE_KEY_PAIR_GENERATED, - SESSION_STATE_KEY_PAIR_WRITTEN, - SESSION_STATE_RESET_PASSWORD_REQUEST, - SESSION_STATE_RESET_PASSWORD_SUCCEED, - SESSION_STATE_COUNT -}; - -class SessionManager; -class UpdateUserPasswordPage; -class PassphrasePage; -class RepairDefectPassphrase; - -class Session : public ErrorList, public UniLib::lib::MultithreadContainer -{ - friend WriteEmailVerification; - friend SessionManager; - friend UpdateUserPasswordPage; - friend PassphrasePage; - friend RepairDefectPassphrase; -public: - Session(int handle); - ~Session(); - - // get new model objects - Poco::AutoPtr getEmailVerificationCodeObject(); - - // set new model objects - inline void setUser(Poco::AutoPtr user) { mNewUser = user; } - inline Poco::AutoPtr getNewUser() { return mNewUser; } - - // ---------------- User functions ---------------------------- - // TODO: register state: written into db, mails sended, update state only if new state is higher as old state - // create User send e-mail activation link - bool createUser(const std::string& first_name, const std::string& last_name, const std::string& email, const std::string& password); - - //! \brief new register function, without showing user pubkeys, using controller/user - bool createUserDirect(const std::string& first_name, const std::string& last_name, const std::string& email, const std::string& password); - - - // adminRegister without passwort - bool adminCreateUser(const std::string& first_name, const std::string& last_name, const std::string& email); - - // TODO: check if email exist and if not, fake waiting on password hashing with profiled times of real password hashing - UserStates loadUser(const std::string& email, const std::string& password); - bool ifUserExist(const std::string& email); - - inline void setUser(Poco::AutoPtr user) { mSessionUser = user; } - - - bool deleteUser(); - - Poco::AutoPtr getUser() { - return mSessionUser; - } - - // ------------------------- Email Verification Code functions ------------------------------- - - bool loadFromEmailVerificationCode(Poco::UInt64 emailVerificationCode); - - //! \return 1 = konto already exist - //! -1 = invalid code - //! -2 = critical error - //! 0 = ok - int updateEmailVerification(Poco::UInt64 emailVerificationCode); - - // 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); - // - //! \return 0 = not the same - //! \return 1 = same - //! \return -1 = error - //! \return -2 = critical error - int comparePassphraseWithSavedKeys(const std::string& inputPassphrase, Mnemonic* wordSource); - - Poco::Net::HTTPCookie getLoginCookie(); - - - inline int getHandle() { return mHandleId; } - - // ------------------------ Passphrase functions ---------------------------- - - inline void setPassphrase(Poco::AutoPtr passphrase) { mNewPassphrase = passphrase; } - inline Poco::AutoPtr getPassphrase() { return mNewPassphrase; } - - inline void setPassphrase(const std::string& passphrase) { mPassphrase = passphrase; } - - inline const std::string& getOldPassphrase() { return mPassphrase; } - bool generatePassphrase(); - bool generateKeys(bool savePrivkey, bool savePassphrase); - - inline void setClientIp(Poco::Net::IPAddress ip) { mClientLoginIP = ip; } - inline Poco::Net::IPAddress getClientIp() { return mClientLoginIP; } - - inline bool isIPValid(Poco::Net::IPAddress ip) { return mClientLoginIP == ip; } - bool isPwdValid(const std::string& pwd); - void reset(); - - void updateState(SessionStates newState); - const char* getSessionStateString(); - inline SessionStates getSessionState() { SessionStates s; lock("Session::getSessionState"); s = mState; unlock(); return s; } - - 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; - } - return mEmailVerificationCodeObject->getModel()->getType(); - } - - //! \return -1 if session is locked - //! \return 1 if session is active - //! \return 0 - int isActive(); - //! \return false if session is locked - bool setActive(bool active); - - bool isDeadLocked(); - - inline Poco::DateTime getLastActivity() { return mLastActivity; } - - // ------------------------ transactions functions ---------------------------- - - //! \return true if succeed - bool startProcessingTransaction(const std::string& proto_message_base64); - //! \param working if set will filled with transaction running - Poco::AutoPtr getNextReadyTransaction(size_t* working = nullptr); - bool finalizeTransaction(bool sign, bool reject); - size_t getProcessingTransactionCount(); - - inline LanguageCatalog* getLanguageCatalog() { return mLanguageCatalog.isNull() ? nullptr : mLanguageCatalog; } - void setLanguage(Languages lang); - inline void setLanguageCatalog(Poco::AutoPtr languageCatalog) { mLanguageCatalog = languageCatalog; } - Languages getLanguage(); - inline const char* gettext(const char* text) { if (mLanguageCatalog.isNull()) return text; return mLanguageCatalog->gettext(text); } - - // last referer - inline void setLastReferer(const std::string& lastReferer) { mLastExternReferer = lastReferer; } - inline const std::string& getLastReferer() const { return mLastExternReferer; } - -protected: - void updateTimeout(); - inline void setHandle(int newHandle) { mHandleId = newHandle; } - - void detectSessionState(); - static const char* translateSessionStateToString(SessionStates state); - - inline const std::string& getPassphrase() const { return mPassphrase; } - - -private: - int mHandleId; - Poco::AutoPtr mSessionUser; - Poco::AutoPtr mNewUser; - std::string mPassphrase; - Poco::AutoPtr mNewPassphrase; - Poco::DateTime mLastActivity; - Poco::Net::IPAddress mClientLoginIP; - std::string mLastExternReferer; - Poco::AutoPtr mEmailVerificationCodeObject; - std::shared_mutex mSharedMutex; - - - SessionStates mState; - - bool mActive; - std::list> mProcessingTransactions; - Poco::AutoPtr mCurrentActiveProcessingTransaction; - - Poco::AutoPtr mLanguageCatalog; -}; - - -class WriteEmailVerification : public UniLib::controller::CPUTask -{ -public: - WriteEmailVerification(Poco::AutoPtr user, Poco::AutoPtr emailVerificationCode, UniLib::controller::CPUSheduler* cpuScheduler, size_t taskDependenceCount = 0) - : UniLib::controller::CPUTask(cpuScheduler, taskDependenceCount), mUser(user), mEmailVerificationCode(emailVerificationCode) { -#ifdef _UNI_LIB_DEBUG - setName(user->getEmail()); -#endif - } - - virtual const char* getResourceType() const { return "WriteEmailVerification"; }; - virtual int run(); - -private: - Poco::AutoPtr mUser; - Poco::AutoPtr mEmailVerificationCode; - -}; - -class WritePassphraseIntoDB : public UniLib::controller::CPUTask -{ -public: - WritePassphraseIntoDB(int userId, const std::string& passphrase) - : mUserId(userId), mPassphrase(passphrase) { -#ifdef _UNI_LIB_DEBUG - setName(std::to_string(userId).data()); -#endif - } - - - virtual int run(); - virtual const char* getResourceType() const { return "WritePassphraseIntoDB"; }; - -protected: - int mUserId; - std::string mPassphrase; -}; - -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 +/*! +* +* \author: einhornimmond +* +* \date: 02.03.19 +* +* \brief: store session data +*/ + +#ifndef DR_LUA_WEB_MODULE_SESSION_SESSION_H +#define DR_LUA_WEB_MODULE_SESSION_SESSION_H + +#include "../lib/ErrorList.h" +#include "User.h" +#include "../controller/User.h" + +#include "../lib/MultithreadContainer.h" +#include "../tasks/ProcessingTransaction.h" + +#include "../SingletonManager/LanguageManager.h" + +#include "../controller/EmailVerificationCode.h" + +#include "Poco/Thread.h" +#include "Poco/Types.h" +#include "Poco/DateTime.h" +#include "Poco/Net/IPAddress.h" +#include "Poco/Net/HTTPCookie.h" + +#include + + +class WriteEmailVerification; + +enum SessionStates { + SESSION_STATE_EMPTY, + 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_PASSPHRASE_GENERATED, + SESSION_STATE_PASSPHRASE_SHOWN, + SESSION_STATE_PASSPHRASE_WRITTEN, + SESSION_STATE_KEY_PAIR_GENERATED, + SESSION_STATE_KEY_PAIR_WRITTEN, + SESSION_STATE_RESET_PASSWORD_REQUEST, + SESSION_STATE_RESET_PASSWORD_SUCCEED, + SESSION_STATE_COUNT +}; + +class SessionManager; + +class UpdateUserPasswordPage; +class PassphrasePage; +class RepairDefectPassphrase; + +class Session : public ErrorList, public UniLib::lib::MultithreadContainer +{ + friend WriteEmailVerification; + friend SessionManager; + friend UpdateUserPasswordPage; + friend PassphrasePage; + friend RepairDefectPassphrase; +public: + Session(int handle); + ~Session(); + + // get new model objects + Poco::AutoPtr getEmailVerificationCodeObject(); + + // set new model objects + inline void setUser(Poco::AutoPtr user) { mNewUser = user; } + inline Poco::AutoPtr getNewUser() { return mNewUser; } + + // ---------------- User functions ---------------------------- + // TODO: register state: written into db, mails sended, update state only if new state is higher as old state + // create User send e-mail activation link + bool createUser(const std::string& first_name, const std::string& last_name, const std::string& email, const std::string& password); + + //! \brief new register function, without showing user pubkeys, using controller/user + bool createUserDirect(const std::string& first_name, const std::string& last_name, const std::string& email, const std::string& password); + + + // adminRegister without passwort + bool adminCreateUser(const std::string& first_name, const std::string& last_name, const std::string& email); + + // TODO: check if email exist and if not, fake waiting on password hashing with profiled times of real password hashing + UserStates loadUser(const std::string& email, const std::string& password); + bool ifUserExist(const std::string& email); + + inline void setUser(Poco::AutoPtr user) { mSessionUser = user; } + + + bool deleteUser(); + + Poco::AutoPtr getUser() { + return mSessionUser; + } + + // ------------------------- Email Verification Code functions ------------------------------- + + bool loadFromEmailVerificationCode(Poco::UInt64 emailVerificationCode); + + //! \return 1 = konto already exist + //! -1 = invalid code + //! -2 = critical error + //! 0 = ok + int updateEmailVerification(Poco::UInt64 emailVerificationCode); + + // 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); + // + //! \return 0 = not the same + //! \return 1 = same + //! \return -1 = error + //! \return -2 = critical error + int comparePassphraseWithSavedKeys(const std::string& inputPassphrase, Mnemonic* wordSource); + + Poco::Net::HTTPCookie getLoginCookie(); + + + inline int getHandle() { return mHandleId; } + + // ------------------------ Passphrase functions ---------------------------- + + inline void setPassphrase(Poco::AutoPtr passphrase) { mNewPassphrase = passphrase; } + inline Poco::AutoPtr getPassphrase() { return mNewPassphrase; } + + inline void setPassphrase(const std::string& passphrase) { mPassphrase = passphrase; } + + inline const std::string& getOldPassphrase() { return mPassphrase; } + bool generatePassphrase(); + bool generateKeys(bool savePrivkey, bool savePassphrase); + + inline void setClientIp(Poco::Net::IPAddress ip) { mClientLoginIP = ip; } + inline Poco::Net::IPAddress getClientIp() { return mClientLoginIP; } + + inline bool isIPValid(Poco::Net::IPAddress ip) { return mClientLoginIP == ip; } + bool isPwdValid(const std::string& pwd); + void reset(); + + void updateState(SessionStates newState); + const char* getSessionStateString(); + inline SessionStates getSessionState() { SessionStates s; lock("Session::getSessionState"); s = mState; unlock(); return s; } + + 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; + } + return mEmailVerificationCodeObject->getModel()->getType(); + } + + //! \return -1 if session is locked + //! \return 1 if session is active + //! \return 0 + int isActive(); + //! \return false if session is locked + bool setActive(bool active); + + bool isDeadLocked(); + + inline Poco::DateTime getLastActivity() { return mLastActivity; } + + // ------------------------ transactions functions ---------------------------- + + //! \return true if succeed + bool startProcessingTransaction(const std::string& proto_message_base64); + //! \param working if set will filled with transaction running + Poco::AutoPtr getNextReadyTransaction(size_t* working = nullptr); + bool finalizeTransaction(bool sign, bool reject); + size_t getProcessingTransactionCount(); + + inline LanguageCatalog* getLanguageCatalog() { return mLanguageCatalog.isNull() ? nullptr : mLanguageCatalog; } + void setLanguage(Languages lang); + inline void setLanguageCatalog(Poco::AutoPtr languageCatalog) { mLanguageCatalog = languageCatalog; } + Languages getLanguage(); + inline const char* gettext(const char* text) { if (mLanguageCatalog.isNull()) return text; return mLanguageCatalog->gettext(text); } + + // last referer + inline void setLastReferer(const std::string& lastReferer) { mLastExternReferer = lastReferer; } + inline const std::string& getLastReferer() const { return mLastExternReferer; } + +protected: + void updateTimeout(); + inline void setHandle(int newHandle) { mHandleId = newHandle; } + + void detectSessionState(); + static const char* translateSessionStateToString(SessionStates state); + + inline const std::string& getPassphrase() const { return mPassphrase; } + + +private: + int mHandleId; + Poco::AutoPtr mSessionUser; + Poco::AutoPtr mNewUser; + std::string mPassphrase; + Poco::AutoPtr mNewPassphrase; + Poco::DateTime mLastActivity; + Poco::Net::IPAddress mClientLoginIP; + std::string mLastExternReferer; + Poco::AutoPtr mEmailVerificationCodeObject; + std::shared_mutex mSharedMutex; + + + SessionStates mState; + + bool mActive; + std::list> mProcessingTransactions; + Poco::AutoPtr mCurrentActiveProcessingTransaction; + + Poco::AutoPtr mLanguageCatalog; +}; + + +class WriteEmailVerification : public UniLib::controller::CPUTask +{ +public: + WriteEmailVerification(Poco::AutoPtr user, Poco::AutoPtr emailVerificationCode, UniLib::controller::CPUSheduler* cpuScheduler, size_t taskDependenceCount = 0) + : UniLib::controller::CPUTask(cpuScheduler, taskDependenceCount), mUser(user), mEmailVerificationCode(emailVerificationCode) { +#ifdef _UNI_LIB_DEBUG + setName(user->getEmail()); +#endif + } + + virtual const char* getResourceType() const { return "WriteEmailVerification"; }; + virtual int run(); + +private: + Poco::AutoPtr mUser; + Poco::AutoPtr mEmailVerificationCode; + +}; + +class WritePassphraseIntoDB : public UniLib::controller::CPUTask +{ +public: + WritePassphraseIntoDB(int userId, const std::string& passphrase) + : mUserId(userId), mPassphrase(passphrase) { +#ifdef _UNI_LIB_DEBUG + setName(std::to_string(userId).data()); +#endif + } + + + virtual int run(); + virtual const char* getResourceType() const { return "WritePassphraseIntoDB"; }; + +protected: + int mUserId; + std::string mPassphrase; +}; + +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