mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
345 lines
12 KiB
C++
345 lines
12 KiB
C++
#include "Session.h"
|
||
#include "Profiler.h"
|
||
#include "../ServerConfig.h"
|
||
|
||
#include "Poco/RegularExpression.h"
|
||
#include "Poco/Net/StringPartSource.h"
|
||
|
||
#include "../SingletonManager/SessionManager.h"
|
||
#include "../SingletonManager/ConnectionManager.h"
|
||
#include "../SingletonManager/ErrorManager.h"
|
||
#include "../tasks/PrepareEmailTask.h"
|
||
#include "../tasks/SendEmailTask.h"
|
||
|
||
|
||
#include "sodium.h"
|
||
|
||
using namespace Poco::Data::Keywords;
|
||
|
||
int WriteEmailVerification::run()
|
||
{
|
||
auto verificationCode = mSession->getEmailVerificationCode();
|
||
printf("{[WriteEmailVerification::run] E-Mail Verification Code: %llu\n", verificationCode);
|
||
auto dbSession = ConnectionManager::getInstance()->getConnection(CONNECTION_MYSQL_LOGIN_SERVER);
|
||
int user_id = mUser->getDBId();
|
||
Poco::Data::Statement insert(dbSession);
|
||
insert << "INSERT INTO email_opt_in (user_id, verification_code) VALUES(?,?);",
|
||
use(user_id), bind(verificationCode);
|
||
if (1 != insert.execute()) {
|
||
mSession->addError(new Error("WriteEmailVerification", "error inserting email verification code"));
|
||
return -1;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------------------------------------------
|
||
|
||
|
||
|
||
// --------------------------------------------------------------------------------------------------------------
|
||
|
||
Session::Session(int handle)
|
||
: mHandleId(handle), mSessionUser(nullptr)
|
||
{
|
||
|
||
}
|
||
|
||
Session::~Session()
|
||
{
|
||
|
||
reset();
|
||
}
|
||
|
||
|
||
void Session::reset()
|
||
{
|
||
if (mSessionUser) {
|
||
delete mSessionUser;
|
||
mSessionUser = nullptr;
|
||
}
|
||
updateTimeout();
|
||
mClientLoginIP = Poco::Net::IPAddress();
|
||
}
|
||
|
||
void Session::updateTimeout()
|
||
{
|
||
mLastActivity = Poco::DateTime();
|
||
}
|
||
|
||
bool Session::createUser(const std::string& name, const std::string& email, const std::string& password)
|
||
{
|
||
Profiler usedTime;
|
||
auto sm = SessionManager::getInstance();
|
||
if (!sm->isValid(name, VALIDATE_NAME)) {
|
||
addError(new Error("Vorname", "Bitte gebe einen Namen an. Mindestens 3 Zeichen, keine Sonderzeichen oder Zahlen."));
|
||
return false;
|
||
}
|
||
if (!sm->isValid(email, VALIDATE_EMAIL)) {
|
||
addError(new Error("E-Mail", "Bitte gebe eine gültige E-Mail Adresse an."));
|
||
return false;
|
||
}
|
||
if (!sm->isValid(password, VALIDATE_PASSWORD)) {
|
||
addError(new Error("Password", "Bitte gebe ein gültiges Password ein mit mindestens 8 Zeichen, Groß- und Kleinbuchstaben, mindestens einer Zahl und einem Sonderzeichen"));
|
||
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("E-Mail", "Für diese E-Mail Adresse gibt es bereits einen Account"));
|
||
return false;
|
||
}
|
||
}
|
||
catch (Poco::Exception& exc) {
|
||
printf("mysql exception: %s\n", exc.displayText().data());
|
||
}
|
||
|
||
mSessionUser = new User(email.data(), name.data());
|
||
updateTimeout();
|
||
|
||
// Prepare E-Mail
|
||
UniLib::controller::TaskPtr prepareEmail(new PrepareEmailTask(ServerConfig::g_CPUScheduler));
|
||
prepareEmail->scheduleTask(prepareEmail);
|
||
|
||
// create user crypto key
|
||
UniLib::controller::TaskPtr cryptoKeyTask(new UserCreateCryptoKey(mSessionUser, password, ServerConfig::g_CPUScheduler));
|
||
cryptoKeyTask->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);
|
||
|
||
createEmailVerificationCode();
|
||
|
||
UniLib::controller::TaskPtr writeEmailVerification(new WriteEmailVerification(mSessionUser, this, ServerConfig::g_CPUScheduler, 1));
|
||
writeEmailVerification->setParentTaskPtrInArray(writeUserIntoDB, 0);
|
||
writeEmailVerification->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_EMAIL_VERIFICATION_WRITTEN, this));
|
||
writeEmailVerification->scheduleTask(writeEmailVerification);
|
||
|
||
// depends on writeUser because need user_id, write email verification into db
|
||
auto message = new Poco::Net::MailMessage;
|
||
|
||
message->addRecipient(Poco::Net::MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, email));
|
||
message->setSubject("Gradido: E-Mail Verification");
|
||
std::stringstream ss;
|
||
ss << "Hallo " << name << "," << std::endl << std::endl;
|
||
ss << "Du oder jemand anderes hat sich soeben mit dieser E-Mail Adresse bei Gradido registriert. " << std::endl;
|
||
ss << "Wenn du es warst, klicke bitte auf den Link: https://gradido2.dario-rekowski.de/accounts/checkEmail/" << mEmailVerificationCode << std::endl;
|
||
ss << "oder kopiere den Code: " << mEmailVerificationCode << " selbst dort hinein." << std::endl << std::endl;
|
||
ss << "Mit freundlichen Gr<47><72>e" << std::endl;
|
||
ss << "Dario, Gradido Server Admin" << std::endl;
|
||
|
||
|
||
message->addContent(new Poco::Net::StringPartSource(ss.str()));
|
||
|
||
UniLib::controller::TaskPtr sendEmail(new SendEmailTask(message, ServerConfig::g_CPUScheduler, 1));
|
||
sendEmail->setParentTaskPtrInArray(prepareEmail, 0);
|
||
sendEmail->setParentTaskPtrInArray(writeEmailVerification, 1);
|
||
sendEmail->setFinishCommand(new SessionStateUpdateCommand(SESSION_STATE_EMAIL_VERIFICATION_SEND, this));
|
||
sendEmail->scheduleTask(sendEmail);
|
||
|
||
|
||
// write user into db
|
||
// generate and write email verification into db
|
||
// send email
|
||
|
||
printf("[Session::createUser] time: %s\n", usedTime.string().data());
|
||
|
||
return true;
|
||
}
|
||
|
||
bool Session::updateEmailVerification(unsigned long long emailVerificationCode)
|
||
{
|
||
Profiler usedTime;
|
||
const static char* funcName = "Session::updateEmailVerification";
|
||
auto em = ErrorManager::getInstance();
|
||
if(mEmailVerificationCode == emailVerificationCode) {
|
||
if (mSessionUser && mSessionUser->getDBId() == 0) {
|
||
//addError(new Error("E-Mail Verification", "Benutzer wurde nicht richtig gespeichert, bitte wende dich an den Server-Admin"));
|
||
em->addError(new Error(funcName, "user exist with 0 as id"));
|
||
em->sendErrorsAsEmail();
|
||
//return false;
|
||
}
|
||
|
||
// load correct user from db
|
||
auto dbConnection = ConnectionManager::getInstance()->getConnection(CONNECTION_MYSQL_LOGIN_SERVER);
|
||
Poco::Data::Statement update(dbConnection);
|
||
update << "UPDATE users SET email_checked=1 where id = (SELECT user_id FROM email_opt_in where verification_code=?)", use(emailVerificationCode);
|
||
auto updated_rows = update.execute();
|
||
if (updated_rows == 1) {
|
||
Poco::Data::Statement delete_row(dbConnection);
|
||
delete_row << "DELETE FROM email_opt_in where verification_code = ?", use(emailVerificationCode);
|
||
if (delete_row.execute() != 1) {
|
||
em->addError(new Error(funcName, "delete from email_opt_in entry didn't work as expected, please check db"));
|
||
em->sendErrorsAsEmail();
|
||
}
|
||
updateState(SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED);
|
||
printf("[%s] time: %s\n", funcName, usedTime.string().data());
|
||
return true;
|
||
}
|
||
else {
|
||
em->addError(new ParamError(funcName, "update user work not like expected, updated row count", updated_rows));
|
||
em->sendErrorsAsEmail();
|
||
}
|
||
if (!updated_rows) {
|
||
addError(new Error("E-Mail Verification", "Der Code stimmt nicht, bitte überprüfe ihn nochmal oder registriere dich erneut oder wende dich an den Server-Admin"));
|
||
printf("[%s] time: %s\n", funcName, usedTime.string().data());
|
||
return false;
|
||
}
|
||
|
||
}
|
||
else {
|
||
addError(new Error("E-Mail Verification", "Falscher Code für aktiven Login"));
|
||
printf("[%s] time: %s\n", funcName, usedTime.string().data());
|
||
return false;
|
||
}
|
||
printf("[%s] time: %s\n", funcName, usedTime.string().data());
|
||
return false;
|
||
}
|
||
|
||
|
||
|
||
bool Session::loadUser(const std::string& email, const std::string& password)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
bool Session::loadFromEmailVerificationCode(unsigned long long emailVerificationCode)
|
||
{
|
||
Profiler usedTime;
|
||
const static char* funcName = "Session::loadFromEmailVerificationCode";
|
||
auto em = ErrorManager::getInstance();
|
||
auto dbConnection = ConnectionManager::getInstance()->getConnection(CONNECTION_MYSQL_LOGIN_SERVER);
|
||
|
||
/*Poco::Data::Statement select(dbConnection);
|
||
int user_id = 0;
|
||
select << "SELECT user_id FROM email_opt_in WHERE verification_code=?", into(user_id), use(emailVerificationCode);
|
||
try {
|
||
if (select.execute() == 0) {
|
||
addError(new Error("E-Mail Verification", "Der Code konnte nicht in der Datenbank gefunden werden."));
|
||
return false;
|
||
}
|
||
}
|
||
catch (Poco::Exception& ex) {
|
||
em->addError(new ParamError(funcName, "error selecting verification code entry", ex.displayText().data()));
|
||
em->sendErrorsAsEmail();
|
||
return false;
|
||
}*/
|
||
Poco::Data::Statement select(dbConnection);
|
||
std::string email, name;
|
||
select.reset(dbConnection);
|
||
select << "SELECT email, name FROM users where id = (SELECT user_id FROM email_opt_in WHERE verification_code=?)",
|
||
into(email), into(name), use(emailVerificationCode);
|
||
try {
|
||
size_t rowCount = select.execute();
|
||
if (rowCount != 1) {
|
||
em->addError(new ParamError(funcName, "select user by email verification code work not like expected, selected row count", rowCount));
|
||
em->sendErrorsAsEmail();
|
||
}
|
||
if (rowCount < 0) {
|
||
addError(new Error("E-Mail Verification", "Konnte keinen passenden Account finden."));
|
||
return false;
|
||
}
|
||
|
||
mSessionUser = new User(email.data(), name.data());
|
||
mSessionUser->loadEntryDBId(ConnectionManager::getInstance()->getConnection(CONNECTION_MYSQL_LOGIN_SERVER));
|
||
mEmailVerificationCode = emailVerificationCode;
|
||
updateState(SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED);
|
||
printf("[Session::loadFromEmailVerificationCode] time: %s\n", usedTime.string().data());
|
||
return true;
|
||
}
|
||
catch (const Poco::Exception& ex) {
|
||
em->addError(new ParamError(funcName, "error selecting user from verification code", ex.displayText().data()));
|
||
em->sendErrorsAsEmail();
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
void Session::updateState(SessionStates newState)
|
||
{
|
||
lock();
|
||
printf("[Session::%s] newState: %s\n", __FUNCTION__, translateSessionStateToString(newState));
|
||
if (newState > mState) {
|
||
mState = newState;
|
||
}
|
||
|
||
unlock();
|
||
}
|
||
|
||
const char* Session::getSessionStateString()
|
||
{
|
||
SessionStates state;
|
||
lock();
|
||
state = mState;
|
||
unlock();
|
||
return translateSessionStateToString(state);
|
||
}
|
||
|
||
|
||
const char* Session::translateSessionStateToString(SessionStates state)
|
||
{
|
||
switch (state) {
|
||
case SESSION_STATE_CRYPTO_KEY_GENERATED: return "crpyto key generated";
|
||
case SESSION_STATE_USER_WRITTEN: return "User saved";
|
||
case SESSION_STATE_EMAIL_VERIFICATION_WRITTEN: return "E-Mail verification code saved";
|
||
case SESSION_STATE_EMAIL_VERIFICATION_SEND: return "Verification E-Mail sended";
|
||
case SESSION_STATE_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_KEY_PAIR_GENERATED: return "Gradido Address created";
|
||
case SESSION_STATE_KEY_PAIR_WRITTEN: return "Gradido Address saved";
|
||
default: return "unknown";
|
||
}
|
||
|
||
return "error";
|
||
}
|
||
|
||
void Session::createEmailVerificationCode()
|
||
{
|
||
uint32_t* code_p = (uint32_t*)&mEmailVerificationCode;
|
||
for (int i = 0; i < EMAIL_VERIFICATION_CODE_SIZE / 4; i++) {
|
||
code_p[i] = randombytes_random();
|
||
}
|
||
|
||
}
|
||
/*
|
||
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()
|
||
{
|
||
mPassphrase = User::generateNewPassphrase(&ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER]);
|
||
updateState(SESSION_STATE_PASSPHRASE_GENERATED);
|
||
return true;
|
||
} |