adding new email system, work in progress

This commit is contained in:
Dario 2020-01-02 12:47:32 +01:00
parent a8c4acd81d
commit feb3067f3a
24 changed files with 462 additions and 32 deletions

View File

@ -1,3 +1,4 @@
HTTPInterface/LoginPage.cpp
HTTPInterface/CheckEmailPage.cpp
model/Session.cpp
model/Email.cpp

View File

@ -7,7 +7,7 @@
#include "SingletonManager/ConnectionManager.h"
#include "SingletonManager/SessionManager.h"
#include "SingletonManager/EmailManager.h"
#include "Poco/Util/HelpFormatter.h"
#include "Poco/Net/ServerSocket.h"
@ -161,6 +161,7 @@ int Gradido_LoginServer::main(const std::vector<std::string>& args)
ServerConfig::g_ServerKeySeed->put(1, i1 | (i2 << 8));
ServerConfig::initEMailAccount(config());
EmailManager::getInstance()->init(config());
// start cpu scheduler
uint8_t worker_count = Poco::Environment::processorCount() * 2;

View File

@ -16,7 +16,7 @@ using namespace Poco::Data::Keywords;
#include "../tasks/PrepareEmailTask.h"
#include "../tasks/SendEmailTask.h"
#include "../model/EmailVerificationCode.h"
#include "../controller/EmailVerificationCode.h"
#include "../model/table/ElopageBuy.h"
@ -301,7 +301,7 @@ int HandleElopageRequestTask::run()
}
// email verification code
auto emailVerification = model::EmailVerificationCode::create(user_id);
auto emailVerification = controller::EmailVerificationCode::create(user_id);
//Poco::AutoPtr<model::table::EmailOptIn> emailVerification(new model::table::EmailOptIn(user_id));
// create email verification code

View File

@ -114,7 +114,7 @@ namespace ServerConfig {
g_EmailAccount.url = cfg.getString("email.smtp.url");
g_EmailAccount.port = cfg.getInt("email.smtp.port");
DISASM_FALSERET;
g_ServerKeySeed->put(3, DRRandom::r64());
//g_ServerKeySeed->put(3, DRRandom::r64());
return true;
}

View File

@ -11,6 +11,7 @@
#include "SingletonManager/LanguageManager.h"
#define DISABLE_EMAIL
namespace ServerConfig {
@ -20,9 +21,10 @@ namespace ServerConfig {
MNEMONIC_BIP0039_SORTED_ORDER,
MNEMONIC_MAX
};
// depracted, moved to email manager
struct EmailAccount {
std::string sender;
std::string admin_receiver;
std::string username;
std::string password;
std::string url;

View File

@ -0,0 +1,61 @@
#include "EmailManager.h"
#include "../ServerConfig.h"
#include "../Crypto/Obfus_array.h"
#include "../Crypto/DRRandom.h"
EmailManager::EmailManager()
: mInitalized(false), mDisableEmail(false)
{
}
EmailManager::~EmailManager()
{
exit();
}
EmailManager* EmailManager::getInstance()
{
static EmailManager theOne;
return &theOne;
}
bool EmailManager::init(const Poco::Util::LayeredConfiguration& cfg)
{
try {
mDisableEmail = cfg.getBool("email.disable", false);
mEmailAccount.sender = cfg.getString("email.sender");
mEmailAccount.admin_receiver = cfg.getString("email.admin_receiver");
mEmailAccount.username = cfg.getString("email.username");
mEmailAccount.password = cfg.getString("email.password");
mEmailAccount.url = cfg.getString("email.smtp.url");
mEmailAccount.port = cfg.getInt("email.smtp.port");
}
catch (Poco::Exception& ex) {
printf("email account not set in config: %s\n", ex.displayText().data());
return false;
}
mInitalized = true;
DISASM_FALSERET;
ServerConfig::g_ServerKeySeed->put(3, DRRandom::r64());
return true;
}
void EmailManager::exit()
{
model::Email* email = nullptr;
while (mPendingEmails.pop(email)) {
delete email;
}
mInitalized = false;
}
int EmailManager::ThreadFunction()
{
return 0;
}

View File

@ -0,0 +1,59 @@
/*!
*
* \author: einhornimmond
*
* \date: 02.01.19
*
* \brief: manage emails, send all emails with only one connection to mail server, on after on
*/
#ifndef GRADIDO_LOGIN_SERVER_SINGLETON_MANAGER_EMAIL_MANAGER_H
#define GRADIDO_LOGIN_SERVER_SINGLETON_MANAGER_EMAIL_MANAGER_H
#include "Poco/AutoPtr.h"
#include "Poco/Util/LayeredConfiguration.h"
#include "../lib/MultithreadQueue.h"
#include "../tasks/Thread.h"
#include "../model/Email.h"
class EmailManager : public UniLib::lib::Thread
{
public:
~EmailManager();
static EmailManager* getInstance();
bool init(const Poco::Util::LayeredConfiguration& cfg);
inline const std::string& getAdminReceiver() { return mEmailAccount.admin_receiver; }
inline void addEmail(model::Email* email) { mPendingEmails.push(email); }
protected:
EmailManager();
void exit();
int ThreadFunction();
struct EmailAccount {
std::string sender;
std::string admin_receiver;
std::string username;
std::string password;
std::string url;
int port;
};
EmailAccount mEmailAccount;
bool mInitalized;
bool mDisableEmail;
UniLib::lib::MultithreadQueue<model::Email*> mPendingEmails;
};
#endif //GRADIDO_LOGIN_SERVER_SINGLETON_MANAGER_EMAIL_MANAGER_H

View File

@ -64,6 +64,7 @@ protected:
Languages mThisLanguage;
};
// TODO: max cached catalog count
class LanguageManager : protected UniLib::lib::MultithreadContainer
{

View File

@ -1,4 +1,5 @@
#include "EmailVerificationCode.h"
#include "../ServerConfig.h"
#include "sodium.h"
@ -43,4 +44,12 @@ namespace controller {
}
return resultCode;
}
std::string EmailVerificationCode::getLink()
{
std::string link = ServerConfig::g_serverPath;
link += "/checkEmail/";
link += std::to_string(getModel()->getCode());
return link;
}
}

View File

@ -15,6 +15,8 @@ namespace controller {
static Poco::AutoPtr<EmailVerificationCode> create(int user_id);
inline Poco::AutoPtr<model::table::EmailOptIn> getModel() { return _getModel<model::table::EmailOptIn>(); }
std::string getLink();
protected:
EmailVerificationCode(model::table::EmailOptIn* dbModel);
static Poco::UInt64 createEmailVerificationCode();

View File

@ -14,11 +14,22 @@ namespace controller {
Poco::AutoPtr<User> User::create()
{
/*auto code = createEmailVerificationCode();
auto db = new model::table::EmailOptIn(code, user_id);
auto result = new EmailVerificationCode(db);
return Poco::AutoPtr<EmailVerificationCode>(result);
*/
auto db = new model::table::User();
auto user = new User(db);
return Poco::AutoPtr<User>(user);
}
Poco::AutoPtr<User> User::create(const std::string& email, const std::string& first_name, const std::string& last_name, Poco::UInt64 passwordHashed/* = 0*/, std::string languageKey/* = "de"*/)
{
auto db = new model::table::User(email, first_name, last_name, passwordHashed, languageKey);
auto user = new User(db);
return Poco::AutoPtr<User>(user);
}
int User::load(const unsigned char* pubkey_array)
{
Poco::Data::BLOB pubkey(pubkey_array, 32);
return getModel()->loadFromDB("pubkey", pubkey);
}
}

View File

@ -13,6 +13,11 @@ namespace controller {
~User();
static Poco::AutoPtr<User> create();
static Poco::AutoPtr<User> create(const std::string& email, const std::string& first_name, const std::string& last_name, Poco::UInt64 passwordHashed = 0, std::string languageKey = "de");
inline int load(const std::string& email) { return getModel()->loadFromDB("email", email); }
inline int load(int user_id) { return getModel()->loadFromDB("id", user_id); }
int load(const unsigned char* pubkey_array);
inline Poco::AutoPtr<model::table::User> getModel() { return _getModel<model::table::User>(); }

View File

@ -33,6 +33,7 @@
#define _DR_UNIVERSUM_LIB_LIB_MULTITHREAD_QUEUE_H__
#include "MultithreadContainer.h"
#include <queue>
namespace UniLib {
namespace lib {

186
src/cpp/model/Email.cpp Normal file
View File

@ -0,0 +1,186 @@
#include "Email.h"
#include "../SingletonManager/EmailManager.h"
namespace model {
const static char EmailText_emailVerification[] = {u8"\
Hallo [first_name] [last_name],\n\
\n\
Du oder jemand anderes hat sich soeben mit dieser E-Mail Adresse bei Gradido registriert.\n\
Wenn du es warst, klicke bitte auf den Link: [link]\n\
oder kopiere den obigen Link in Dein Browserfenster.\n\
\n\
Mit freundlichen Grüßen\n\
Dario, Gradido Server Admin\n\
"};
const static char EmailText_emailResetPassword[] = { u8"\
Hallo [first_name] [last_name],\n\
\n\
Du oder jemand anderes hat für dieses Konto ein Passwort Reset angefordert.\n\
Wenn du es warst, klicke bitte auf den Link: [link]\n\
oder kopiere den obigen Link in Dein Browserfenster.\n\
\n\
Mit freundlichen Grüßen\n\
Dario, Gradido Server Admin\n\
" };
const static char EmailText_adminEmailResetPassword[] = { u8"\
Der Benutzer mit der Email-Adresse: [email] hat sein Passwort vergessen.\n\
Außerdem hat er auch seine Passphrase vergessen. \n\
Bitte logge dich im Admin-Bereich um das Problem zu lösen.\n\
\n\
LG \n\
Gradido Login Server\
" };
Email::Email(AutoPtr<controller::EmailVerificationCode> emailVerification, AutoPtr<controller::User> user, EmailType type)
: mEmailVerificationCode(emailVerification), mUser(user), mType(type)
{
}
Email::Email(const std::string& errorHtml, EmailType type)
: mErrorHtml(errorHtml), mType(type)
{
}
bool Email::draft(Net::MailMessage* mailMessage, LanguageCatalog* langCatalog)
{
auto em = EmailManager::getInstance();
auto adminRecipient = Net::MailRecipient(Net::MailRecipient::PRIMARY_RECIPIENT, em->getAdminReceiver());
Poco::AutoPtr<model::table::User> userTableModel;
if (!mUser.isNull()) {
userTableModel = mUser->getModel();
}
static const char* functionName = "Email::draft";
std::string content;
switch (mType) {
case EMAIL_DEFAULT:
mailMessage->addRecipient(adminRecipient);
mailMessage->setSubject(langCatalog->gettext_str("Default Email Subject"));
mailMessage->addContent(new Poco::Net::StringPartSource(langCatalog->gettext_str("Empty Email Content")));
return true;
case EMAIL_ERROR:
mailMessage->addRecipient(adminRecipient);
mailMessage->setSubject(langCatalog->gettext_str("Error from Gradido Login Server"));
mailMessage->addContent(new Poco::Net::StringPartSource(mErrorHtml));
return true;
case EMAIL_USER_VERIFICATION_CODE:
if (userTableModel.isNull() || mUser->getModel()->getEmail() == "") {
addError(new Error(functionName, "no receiver email set for user email verification email"));
return false;
}
if (mEmailVerificationCode.isNull()) {
addError(new Error(functionName, "no email verification code set for user email verification email"));
return false;
}
mailMessage->addRecipient(Poco::Net::MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, mUser->getModel()->getEmail()));
mailMessage->setSubject(langCatalog->gettext_str("Gradido: E-Mail Verification"));
mailMessage->addContent(
new Poco::Net::StringPartSource(replaceUserNamesAndLink(
langCatalog->gettext(EmailText_emailVerification),
userTableModel->getFirstName(),
userTableModel->getLastName(),
mEmailVerificationCode->getLink()
))
);
return true;
case EMAIL_USER_RESET_PASSWORD:
if (userTableModel.isNull() || mUser->getModel()->getEmail() == "") {
addError(new Error(functionName, "no receiver email set for user reset password email"));
return false;
}
if (mEmailVerificationCode.isNull()) {
addError(new Error(functionName, "no email verification code set for user reset password email"));
return false;
}
mailMessage->addRecipient(Poco::Net::MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, mUser->getModel()->getEmail()));
mailMessage->setSubject(langCatalog->gettext_str(u8"Gradido: Passwort zurücksetzen"));
mailMessage->addContent(
new Poco::Net::StringPartSource(replaceUserNamesAndLink(
langCatalog->gettext(EmailText_emailResetPassword),
userTableModel->getFirstName(),
userTableModel->getLastName(),
mEmailVerificationCode->getLink()
))
);
return true;
case EMAIL_ADMIN_RESET_PASSWORD_REQUEST_WITHOUT_MEMORIZED_PASSPHRASE:
if (userTableModel.isNull() || mUser->getModel()->getEmail() == "") {
addError(new Error(functionName, "no user email set for admin reset password email"));
return false;
}
mailMessage->addRecipient(adminRecipient);
mailMessage->setSubject(langCatalog->gettext_str("Reset Password Request without memorized passphrase"));
mailMessage->addContent(
new Poco::Net::StringPartSource(replaceEmail(
EmailText_adminEmailResetPassword,
userTableModel->getEmail()
))
);
return true;
default: return false;
}
return false;
}
std::string Email::replaceUserNamesAndLink(const char* src, const std::string& first_name, const std::string& last_name, const std::string& link)
{
std::string result = src;
int findCursor = 0;
static const char* functionName = "Email::replaceUserNamesAndLink";
int findPos = result.find("[first_name]", findCursor);
if (findPos != result.npos) {
findCursor = findPos + 13;
result.replace(findPos, 12, first_name);
}
else {
addError(new Error(functionName, "no first_name placeholder found"));
}
findPos = result.find("[last_name]", findCursor);
if (findPos != result.npos) {
findCursor = findPos + 12;
result.replace(findPos, 11, last_name);
}
else {
addError(new Error(functionName, "no last_name placeholder found"));
}
findPos = result.find("[link]", findCursor);
if (findPos != result.npos) {
findCursor = findPos + 7;
result.replace(findPos, 6, link);
}
else {
addError(new Error(functionName, "no email placeholder found"));
}
return result;
}
std::string Email::replaceEmail(const char* src, const std::string& email)
{
std::string result = src;
static const char* functionName = "Email::replaceEmail";
int findPos = result.find("[email]");
if (findPos != result.npos) {
result.replace(findPos, 7, email);
}
else {
addError(new Error(functionName, "no email placeholder found"));
}
return result;
}
}

56
src/cpp/model/Email.h Normal file
View File

@ -0,0 +1,56 @@
/*!
*
* \author: einhornimmond
*
* \date: 02.01.20
*
* \brief: store email for
*/
#ifndef GRADIDO_LOGIN_SERVER_MODEL_EMAIL_INCLUDE
#define GRADIDO_LOGIN_SERVER_MODEL_EMAIL_INCLUDE
#include "Poco/Net/MailMessage.h"
#include "../controller/EmailVerificationCode.h"
#include "../controller/User.h"
#include "../SingletonManager/LanguageManager.h"
#include "../lib/ErrorList.h"
namespace model {
using namespace Poco;
enum EmailType
{
EMAIL_DEFAULT,
EMAIL_ERROR,
EMAIL_USER_VERIFICATION_CODE,
EMAIL_USER_RESET_PASSWORD,
EMAIL_ADMIN_RESET_PASSWORD_REQUEST_WITHOUT_MEMORIZED_PASSPHRASE
};
class Email: public ErrorList
{
public:
Email(AutoPtr<controller::EmailVerificationCode> emailVerification, AutoPtr<controller::User> user, EmailType type);
//! \param errors copy errors into own memory
Email(const std::string& errorHtml, EmailType type);
inline EmailType getType() { return mType; }
bool draft(Net::MailMessage* mailMessage, LanguageCatalog* langCatalog);
protected:
std::string replaceUserNamesAndLink(const char* src, const std::string& first_name, const std::string& last_name, const std::string& link);
std::string replaceEmail(const char* src, const std::string& email);
AutoPtr<controller::EmailVerificationCode> mEmailVerificationCode;
AutoPtr<controller::User> mUser;
std::string mErrorHtml;
EmailType mType;
};
}
#endif //GRADIDO_LOGIN_SERVER_MODEL_EMAIL_INCLUDE

View File

@ -84,7 +84,7 @@ int WritePassphraseIntoDB::run()
// --------------------------------------------------------------------------------------------------------------
Session::Session(int handle)
: mHandleId(handle), mSessionUser(nullptr), mEmailVerificationCode(0), mEmailVerificationCodeObject(nullptr), mState(SESSION_STATE_EMPTY), mActive(false)
: mHandleId(handle), mSessionUser(nullptr), mEmailVerificationCode(0), mState(SESSION_STATE_EMPTY), mActive(false)
{
}
@ -106,10 +106,8 @@ void Session::reset()
lock("Session::reset");
mSessionUser = nullptr;
if (mEmailVerificationCodeObject) {
delete mEmailVerificationCodeObject;
mEmailVerificationCodeObject = nullptr;
}
mNewUser = nullptr;
mEmailVerificationCodeObject = nullptr;
// watch out
//updateTimeout();
@ -131,7 +129,7 @@ void Session::updateTimeout()
unlock();
}
controller::EmailVerificationCode* Session::getEmailVerificationCodeObject()
Poco::AutoPtr<controller::EmailVerificationCode> Session::getEmailVerificationCodeObject()
{
lock("Session::getEmailVerificationCodeObject");
auto ret = mEmailVerificationCodeObject;
@ -376,7 +374,9 @@ int Session::updateEmailVerification(Poco::UInt64 emailVerificationCode)
bool Session::createNewEmailVerificationCode()
{
return false;
mEmailVerificationCodeObject = controller::EmailVerificationCode::create(mNewUser->getModel()->getID());
return true;
}
bool Session::startProcessingTransaction(const std::string& proto_message_base64)

View File

@ -12,6 +12,7 @@
#include "../lib/ErrorList.h"
#include "User.h"
#include "../controller/User.h"
#include "../lib/MultithreadContainer.h"
#include "../tasks/ProcessingTransaction.h"
@ -57,7 +58,10 @@ public:
~Session();
// get new model objects
controller::EmailVerificationCode* getEmailVerificationCodeObject();
Poco::AutoPtr<controller::EmailVerificationCode> getEmailVerificationCodeObject();
// set new model objects
inline void setUser(Poco::AutoPtr<controller::User> user) { mNewUser = user; }
// ---------------- User functions ----------------------------
// TODO: automatic redirect after some time, median profiled time for register
@ -69,6 +73,7 @@ public:
inline void setUser(Poco::AutoPtr<User> user) { mSessionUser = user; }
bool deleteUser();
Poco::AutoPtr<User> getUser() {
@ -144,11 +149,12 @@ protected:
private:
int mHandleId;
Poco::AutoPtr<User> mSessionUser;
Poco::AutoPtr<controller::User> mNewUser;
std::string mPassphrase;
Poco::DateTime mLastActivity;
Poco::Net::IPAddress mClientLoginIP;
Poco::UInt64 mEmailVerificationCode;
controller::EmailVerificationCode* mEmailVerificationCodeObject;
Poco::AutoPtr<controller::EmailVerificationCode> mEmailVerificationCodeObject;
SessionStates mState;

View File

@ -67,7 +67,7 @@ namespace model {
}
Poco::Data::Statement ElopageBuy::_loadFromDB(Poco::Data::Session session, std::string& fieldName)
Poco::Data::Statement ElopageBuy::_loadFromDB(Poco::Data::Session session, const std::string& fieldName)
{
// Poco::Data::Statement select(session);

View File

@ -38,7 +38,7 @@ namespace model {
protected:
Poco::Data::Statement _loadFromDB(Poco::Data::Session session, std::string& fieldName);
Poco::Data::Statement _loadFromDB(Poco::Data::Session session, const std::string& fieldName);
Poco::Data::Statement _insertIntoDB(Poco::Data::Session session);
Poco::Int32 mIDs[ELOPAGE_BUY_MAX];

View File

@ -37,7 +37,7 @@ namespace model {
}
Poco::Data::Statement EmailOptIn::_loadFromDB(Poco::Data::Session session, std::string& fieldName)
Poco::Data::Statement EmailOptIn::_loadFromDB(Poco::Data::Session session, const std::string& fieldName)
{
Poco::Data::Statement select(session);

View File

@ -19,8 +19,9 @@ namespace model {
const char* getTableName() { return "email_opt_in"; }
inline Poco::UInt64 getCode() const { return mEmailVerificationCode; }
inline void setCode(Poco::UInt64 code) { mEmailVerificationCode = code; }
protected:
Poco::Data::Statement _loadFromDB(Poco::Data::Session session, std::string& fieldName);
Poco::Data::Statement _loadFromDB(Poco::Data::Session session, const std::string& fieldName);
Poco::Data::Statement _insertIntoDB(Poco::Data::Session session);
// data type must be a multiple of 4

View File

@ -9,6 +9,8 @@
#include "../../MySQL/MysqlTable.h"
//using namespace Poco::Data::Keywords;
namespace model {
namespace table {
@ -21,8 +23,8 @@ namespace model {
virtual const char* getTableName() = 0;
template<class T> size_t updateIntoDB(const std::string& fieldName, T fieldValue );
template<class T> size_t loadFromDB(const std::string& fieldName, T fieldValue);
template<class T> size_t updateIntoDB(const std::string& fieldName, const T& fieldValue );
template<class T> size_t loadFromDB(const std::string& fieldName, const T& fieldValue);
bool insertIntoDB();
inline void setID(int id) { lock(); mID = id; unlock(); }
@ -35,7 +37,7 @@ namespace model {
void release();
protected:
virtual Poco::Data::Statement _loadFromDB(Poco::Data::Session session, std::string& fieldName) = 0;
virtual Poco::Data::Statement _loadFromDB(Poco::Data::Session session, const std::string& fieldName) = 0;
virtual Poco::Data::Statement _insertIntoDB(Poco::Data::Session session) = 0;
int mID;
@ -46,11 +48,11 @@ namespace model {
};
template<class T>
size_t ModelBase::loadFromDB(const std::string& fieldName, T fieldValue)
size_t ModelBase::loadFromDB(const std::string& fieldName, const T& fieldValue)
{
auto cm = ConnectionManager::getInstance();
Poco::Data::Statement select = _loadFromDB(cm->getConnection(CONNECTION_MYSQL_LOGIN_SERVER), fieldName);
select, use(fieldValue);
select, Poco::Data::Keywords::useRef(fieldValue);
size_t resultCount = 0;
try {
@ -66,7 +68,7 @@ namespace model {
}
template<class T>
size_t ModelBase::updateIntoDB(const std::string& fieldName, T fieldValue)
size_t ModelBase::updateIntoDB(const std::string& fieldName, const T& fieldValue)
{
auto cm = ConnectionManager::getInstance();
Poco::Data::Statement update(cm->getConnection(CONNECTION_MYSQL_LOGIN_SERVER));
@ -77,7 +79,7 @@ namespace model {
}
update << "UPDATE " << getTableName() << " SET " << fieldName << " = ? where id = ?",
use(fieldValue), use(mID);
Poco::Data::Keywords::useRef(fieldValue), Poco::Data::Keywords::use(mID);
size_t resultCount = 0;
try {
@ -107,6 +109,32 @@ namespace model {
Poco::AutoPtr<ModelBase> mModel;
};
template <class T>
class ModelUpdateTask : public UniLib::controller::CPUTask
{
public:
ModelUpdateTask(Poco::AutoPtr<ModelBase> model, const std::string& fieldName, const T& fieldValue)
: UniLib::controller::CPUTask(ServerConfig::g_CPUScheduler), mModel(model), mFieldName(fieldName), mFieldValue(fieldValue)
{
#ifdef _UNI_LIB_DEBUG
setName(model->getTableName());
#endif
}
int run() {
mModel->updateIntoDB(mFieldName, mFieldValue);
}
const char* getResourceType() const { return "ModelUpdateTask"; };
protected:
Poco::AutoPtr<ModelBase> mModel;
std::string mFieldName;
T mFieldValue;
};
}
}

View File

@ -61,7 +61,7 @@ namespace model {
return insert;
}
Poco::Data::Statement User::_loadFromDB(Poco::Data::Session session, std::string& fieldName)
Poco::Data::Statement User::_loadFromDB(Poco::Data::Session session, const std::string& fieldName)
{
Poco::Data::Statement select(session);

View File

@ -60,7 +60,7 @@ namespace model {
protected:
Poco::Data::Statement _loadFromDB(Poco::Data::Session session, std::string& fieldName);
Poco::Data::Statement _loadFromDB(Poco::Data::Session session, const std::string& fieldName);
// insert only with email, first_name, last_name, password if exist and language
Poco::Data::Statement _insertIntoDB(Poco::Data::Session session);