create a new structure for db models, for less duplicate code, finish elopage webhook

This commit is contained in:
Dario 2019-10-11 16:29:59 +02:00
parent 158904f5a1
commit 76448b7e26
15 changed files with 463 additions and 55 deletions

View File

@ -3,9 +3,23 @@
#include "Poco/Net/HTTPServerResponse.h"
#include "Poco/DeflatingStream.h"
#include "Poco/URI.h"
#include "Poco/Data/Binding.h"
using namespace Poco::Data::Keywords;
#include "../SingletonManager/ConnectionManager.h"
#include "../SingletonManager/ErrorManager.h"
#include "../SingletonManager/SessionManager.h"
#include "../ServerConfig.h"
#include "../tasks/PrepareEmailTask.h"
#include "../tasks/SendEmailTask.h"
#include "../model/EmailVerificationCode.h"
void ElopageWebhook::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response)
{
@ -94,12 +108,155 @@ HandleElopageRequestTask::HandleElopageRequestTask(Poco::Net::NameValueCollectio
{
}
bool HandleElopageRequestTask::validateInput()
{
auto sm = SessionManager::getInstance();
if (mEmail == "" || !sm->isValid(mEmail, VALIDATE_EMAIL)) {
addError(new Error(__FUNCTION__, "email is invalid or empty"));
return false;
}
if (mFirstName == "" || !sm->isValid(mFirstName, VALIDATE_NAME)) {
addError(new Error(__FUNCTION__, "first name is invalid or empty"));
return false;
}
if (mLastName == "" || !sm->isValid(mLastName, VALIDATE_NAME)) {
addError(new Error(__FUNCTION__, "last name is invalid or empty"));
return false;
}
return true;
}
void HandleElopageRequestTask::writeUserIntoDB()
{
auto cm = ConnectionManager::getInstance();
auto session = cm->getConnection(CONNECTION_MYSQL_LOGIN_SERVER);
Poco::Data::Statement insert(session);
insert << "INSERT INTO users (email, first_name, last_name) VALUES(?, ?, ?);",
use(mEmail), use(mFirstName), use(mLastName);
try {
insert.execute();
}
catch (Poco::Exception& ex) {
addError(new ParamError(__FUNCTION__, "mysql error", ex.displayText().data()));
}
}
int HandleElopageRequestTask::getUserIdFromDB()
{
auto cm = ConnectionManager::getInstance();
auto session = cm->getConnection(CONNECTION_MYSQL_LOGIN_SERVER);
Poco::Data::Statement select(session);
int user_id = 0;
select << "SELECT id from users where email = ?;",
into(user_id), use(mEmail);
try {
select.execute();
}
catch (Poco::Exception& ex) {
addError(new ParamError(__FUNCTION__, "mysql error selecting from db", ex.displayText().data()));
}
return user_id;
}
bool HandleElopageRequestTask::createEmailVerificationCode()
{
// create email verification code
uint32_t* code_p = (uint32_t*)&mEmailVerificationCode;
for (int i = 0; i < sizeof(mEmailVerificationCode) / 4; i++) {
code_p[i] = randombytes_random();
}
return mEmailVerificationCode != 0;
}
int HandleElopageRequestTask::run()
{
printf("[HandleElopageRequestTask::run]\n");
for (auto it = mRequestData.begin(); it != mRequestData.end(); it++) {
printf("%s => %s\n", it->first.data(), it->second.data());
// get input data
mEmail = mRequestData.get("payer[email]", "");
mFirstName = mRequestData.get("payer[first_name]", "");
mLastName = mRequestData.get("payer[last_name]", "");
std::string order_id = mRequestData.get("order_id", "");
addError(new ParamError("HandleElopageRequestTask", "order_id", order_id.data()));
// validate input
if (!validateInput()) {
// if input is invalid we can stop now
sendErrorsAsEmail();
return -1;
}
printf("[HandleElopageRequestTask::run] end\n");
// if user exist we can stop now
if (getUserIdFromDB()) {
sendErrorsAsEmail();
return -2;
}
// if user with this email didn't exist
// we can create a new user and send a email to him
// prepare email in advance
// create connection to email server
UniLib::controller::TaskPtr prepareEmail(new PrepareEmailTask(ServerConfig::g_CPUScheduler));
prepareEmail->scheduleTask(prepareEmail);
// write user entry into db
writeUserIntoDB();
// get user id from db
int user_id = getUserIdFromDB();
// we didn't get a user_id, something went wrong
if (!user_id) {
addError(new Error("User loadEntryDBId", "user_id is zero"));
sendErrorsAsEmail();
return -3;
}
EmailVerificationCode emailVerification(user_id);
// create email verification code
if (!emailVerification.getCode()) {
// exit if email verification code is empty
addError(new Error("Email verification", "code is empty, error in random?"));
sendErrorsAsEmail();
return -4;
}
// write email verification code into db
UniLib::controller::TaskPtr saveEmailVerificationCode(new ModelInsertTask((ModelBase*)&emailVerification));
saveEmailVerificationCode->scheduleTask(saveEmailVerificationCode);
// send email to user
auto message = new Poco::Net::MailMessage;
message->addRecipient(Poco::Net::MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, mEmail));
message->setSubject("Gradido: E-Mail Verification");
std::stringstream ss;
ss << "Hallo " << mFirstName << " " << mLastName << "," << std::endl << std::endl;
ss << "Du oder jemand anderes hat sich soeben mit dieser E-Mail Adresse bei Elopage für Gradido angemeldet. " << std::endl;
ss << "Um dein Gradido Konto anzulegen und deine E-Mail zu best&auml;tigen," << std::endl;
ss << "klicke bitte auf den Link: https://gradido2.dario-rekowski.de/account/checkEmail/" << mEmailVerificationCode << std::endl;
ss << "oder kopiere den Code: " << mEmailVerificationCode << " selbst dort hinein." << std::endl << std::endl;
ss << "Mit freundlichen Grüße" << std::endl;
ss << "Dario, Gradido Server Admin" << std::endl;
message->addContent(new Poco::Net::StringPartSource(ss.str()));
UniLib::controller::TaskPtr sendEmail(new SendEmailTask(message, ServerConfig::g_CPUScheduler, 1));
sendEmail->setParentTaskPtrInArray(prepareEmail, 0);
sendEmail->setParentTaskPtrInArray(saveEmailVerificationCode, 1);
sendEmail->scheduleTask(sendEmail);
// if errors occured, send via email
//if (errorCount() > 1) {
sendErrorsAsEmail();
//}
return 0;
}

View File

@ -4,6 +4,7 @@
#include "Poco/Net/HTTPRequestHandler.h"
#include "../tasks/CPUTask.h"
#include "../model/ErrorList.h"
#include "Poco/Net/NameValueCollection.h"
@ -13,7 +14,7 @@ public:
void handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response);
};
class HandleElopageRequestTask : public UniLib::controller::CPUTask
class HandleElopageRequestTask : public UniLib::controller::CPUTask, protected ErrorList
{
public:
HandleElopageRequestTask(Poco::Net::NameValueCollection& requestData);
@ -23,7 +24,18 @@ public:
int run();
protected:
Poco::Net::NameValueCollection mRequestData;
// return true if at least one entry in db with this email exist
bool validateInput();
void writeUserIntoDB();
int getUserIdFromDB();
bool createEmailVerificationCode();
Poco::Net::NameValueCollection mRequestData;
std::string mEmail;
std::string mFirstName;
std::string mLastName;
Poco::UInt64 mEmailVerificationCode;
};

View File

@ -4,37 +4,8 @@
#include "Poco/Net/SecureSMTPClientSession.h"
#include "Poco/Net/StringPartSource.h"
SendErrorMessage::~SendErrorMessage()
{
if (mMessage) {
delete mMessage;
mMessage = nullptr;
}
}
#include "../model/ErrorList.h"
int SendErrorMessage::run()
{
auto mailClientSession = new Poco::Net::SecureSMTPClientSession(ServerConfig::g_EmailAccount.url, ServerConfig::g_EmailAccount.port);
mailClientSession->login();
mailClientSession->startTLS(ServerConfig::g_SSL_CLient_Context);
mailClientSession->login(Poco::Net::SMTPClientSession::AUTH_LOGIN, ServerConfig::g_EmailAccount.username, ServerConfig::g_EmailAccount.password);
try {
mMessage->setSender(ServerConfig::g_EmailAccount.sender);
mailClientSession->sendMessage(*mMessage);
mailClientSession->close();
}
catch (Poco::Exception& exc) {
printf("[SendErrorMessage::%s] error sending error message to admin: %s\n",
__FUNCTION__, exc.displayText().data());
return -1;
}
return 0;
}
// ------------------------------------------------------------------------------------
ErrorManager* ErrorManager::getInstance()
{

View File

@ -38,7 +38,6 @@ public:
protected:
ErrorManager();
// access mutex
Poco::Mutex mWorkingMutex;
std::map<DHASH, std::list<Error*>*> mErrorsMap;
@ -46,20 +45,6 @@ protected:
};
class SendErrorMessage : public UniLib::controller::CPUTask
{
public:
SendErrorMessage(Poco::Net::MailMessage* message, UniLib::controller::CPUSheduler* scheduler)
: UniLib::controller::CPUTask(scheduler), mMessage(message) {}
~SendErrorMessage();
virtual int run();
protected:
Poco::Net::MailMessage* mMessage;
};
#endif //DR_LUA_WEB_MODULE_CONNECTION_MANAGER_H

View File

@ -0,0 +1,60 @@
#include "EmailVerificationCode.h"
#include "sodium.h"
using namespace Poco::Data::Keywords;
EmailVerificationCode::EmailVerificationCode(int user_id)
: mUserId(user_id), mEmailVerificationCode(0)
{
createEmailVerificationCode();
}
EmailVerificationCode::EmailVerificationCode()
: mUserId(0), mEmailVerificationCode(0)
{
}
EmailVerificationCode::~EmailVerificationCode()
{
}
void EmailVerificationCode::createEmailVerificationCode()
{
uint32_t* code_p = (uint32_t*)&mEmailVerificationCode;
for (int i = 0; i < sizeof(mEmailVerificationCode) / 4; i++) {
code_p[i] = randombytes_random();
}
}
Poco::Data::Statement EmailVerificationCode::insertIntoDB(Poco::Data::Session session)
{
Poco::Data::Statement insert(session);
lock();
insert << "INSERT INTO " << getTableName()
<< " (user_id, verification_code) VALUES(?,?)"
, bind(mUserId), bind(mEmailVerificationCode);
unlock();
return insert;
}
Poco::Data::Statement EmailVerificationCode::updateIntoDB(Poco::Data::Session session)
{
throw Poco::Exception("EmailVerificationCode::updateIntoDB not implemented");
}
Poco::Data::Statement EmailVerificationCode::loadFromDB(Poco::Data::Session session, std::string& fieldName)
{
Poco::Data::Statement select(session);
select << "SELECT user_id, verification_code FROM " << getTableName()
<< " where " << fieldName << " = ?"
, into(mUserId), into(mEmailVerificationCode);
return select;
}

View File

@ -0,0 +1,33 @@
#ifndef GRADIDO_LOGIN_SERVER_MODEL_EMAIL_VERIFICATION_CODE_INCLUDE
#define GRADIDO_LOGIN_SERVER_MODEL_EMAIL_VERIFICATION_CODE_INCLUDE
#define EMAIL_VERIFICATION_CODE_SIZE 8
#include "ModelBase.h"
#include "Poco/Types.h"
class EmailVerificationCode : public ModelBase
{
public:
EmailVerificationCode(int user_id);
EmailVerificationCode();
~EmailVerificationCode();
// generic db operations
const char* getTableName() { return "email_opt_in"; }
Poco::Data::Statement insertIntoDB(Poco::Data::Session session);
Poco::Data::Statement updateIntoDB(Poco::Data::Session session);
Poco::Data::Statement loadFromDB(Poco::Data::Session session, std::string& fieldName);
inline Poco::UInt64 getCode() const { return mEmailVerificationCode; }
protected:
void createEmailVerificationCode();
// data type must be a multiple of 4
Poco::UInt64 mEmailVerificationCode;
int mUserId;
};
#endif //GRADIDO_LOGIN_SERVER_MODEL_EMAIL_VERIFICATION_CODE_INCLUDE

View File

@ -1,5 +1,42 @@
#include "ErrorList.h"
#include "../ServerConfig.h"
#include "Poco/Net/MailMessage.h"
SendErrorMessage::~SendErrorMessage()
{
if (mMessage) {
delete mMessage;
mMessage = nullptr;
}
}
int SendErrorMessage::run()
{
auto mailClientSession = new Poco::Net::SecureSMTPClientSession(ServerConfig::g_EmailAccount.url, ServerConfig::g_EmailAccount.port);
mailClientSession->login();
mailClientSession->startTLS(ServerConfig::g_SSL_CLient_Context);
mailClientSession->login(Poco::Net::SMTPClientSession::AUTH_LOGIN, ServerConfig::g_EmailAccount.username, ServerConfig::g_EmailAccount.password);
try {
mMessage->setSender(ServerConfig::g_EmailAccount.sender);
mailClientSession->sendMessage(*mMessage);
mailClientSession->close();
}
catch (Poco::Exception& exc) {
printf("[SendErrorMessage::%s] error sending error message to admin: %s\n",
__FUNCTION__, exc.displayText().data());
return -1;
}
return 0;
}
// ------------------------------------------------------------------------------------
ErrorList::ErrorList()
{
@ -79,4 +116,26 @@ std::string ErrorList::getErrorsHtml()
}
res += "</ul>";
return res;
}
void ErrorList::sendErrorsAsEmail()
{
auto message = new Poco::Net::MailMessage();
message->setSender("gradido_loginServer@gradido.net");
message->addRecipient(Poco::Net::MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, "***REMOVED***"));
message->setSubject("Error from Gradido Login Server");
std::string content;
while (mErrorStack.size() > 0) {
auto error = mErrorStack.top();
mErrorStack.pop();
content += error->getString();
delete error;
}
message->addContent(new Poco::Net::StringPartSource(content));
UniLib::controller::TaskPtr sendErrorMessageTask(new SendErrorMessage(message, ServerConfig::g_CPUScheduler));
sendErrorMessageTask->scheduleTask(sendErrorMessageTask);
}

View File

@ -13,6 +13,11 @@
#include "Error.h"
#include <stack>
#include "../tasks/CPUTask.h"
#include "Poco/Net/SecureSMTPClientSession.h"
#include "Poco/Net/StringPartSource.h"
class ErrorList : public IErrorCollection
{
public:
@ -38,8 +43,26 @@ public:
void printErrors();
std::string getErrorsHtml();
void sendErrorsAsEmail();
protected:
std::stack<Error*> mErrorStack;
};
class SendErrorMessage : public UniLib::controller::CPUTask
{
public:
SendErrorMessage(Poco::Net::MailMessage* message, UniLib::controller::CPUSheduler* scheduler)
: UniLib::controller::CPUTask(scheduler), mMessage(message) {}
~SendErrorMessage();
virtual int run();
protected:
Poco::Net::MailMessage* mMessage;
};
#endif // DR_LUA_WEB_MODULE_ERROR_ERROR_LIST_H

View File

@ -0,0 +1,50 @@
#include "ModelBase.h"
#include "../ServerConfig.h"
#include "../SingletonManager/ConnectionManager.h"
ModelInsertTask::ModelInsertTask(Poco::AutoPtr<ModelBase> model)
: UniLib::controller::CPUTask(ServerConfig::g_CPUScheduler), mModel(model)
{
}
int ModelInsertTask::run()
{
auto session = ConnectionManager::getInstance()->getConnection(CONNECTION_MYSQL_LOGIN_SERVER);
auto insert = mModel->insertIntoDB(session);
try {
insert.execute();
}
catch (Poco::Exception& ex) {
mModel->lock();
mModel->addError(new ParamError(mModel->getTableName(), "mysql error by inserting", ex.displayText().data()));
mModel->unlock();
}
return 0;
}
// ---------------------------------------------------------------------------------------------------
void ModelBase::duplicate()
{
lock();
mReferenceCount++;
//printf("[ModelBase::duplicate] new value: %d\n", mReferenceCount);
unlock();
}
void ModelBase::release()
{
lock();
mReferenceCount--;
//printf("[ModelBase::release] new value: %d\n", mReferenceCount);
if (0 == mReferenceCount) {
unlock();
delete this;
return;
}
unlock();
}

51
src/cpp/model/ModelBase.h Normal file
View File

@ -0,0 +1,51 @@
#ifndef GRADIDO_LOGIN_SERVER_MODEL_INTERFACE_INCLUDE
#define GRADIDO_LOGIN_SERVER_MODEL_INTERFACE_INCLUDE
#include "Poco/Data/Session.h"
#include "../tasks/MultithreadContainer.h"
#include "../tasks/CPUTask.h"
#include "../MySQL/MysqlTable.h"
class ModelBase : public UniLib::lib::MultithreadContainer, public ErrorList
{
public:
ModelBase(int id) :mID(id), mReferenceCount(1) {}
ModelBase() : mID(0), mReferenceCount(1) {}
virtual const char* getTableName() = 0;
virtual Poco::Data::Statement insertIntoDB(Poco::Data::Session session) = 0;
virtual Poco::Data::Statement updateIntoDB(Poco::Data::Session session) = 0;
virtual Poco::Data::Statement loadFromDB(Poco::Data::Session session, std::string& fieldName) = 0;
inline void setID(int id) { lock(); mID = id; unlock(); }
inline int getID() { lock(); int id = mID; unlock(); return id; }
// for poco auto ptr
void duplicate();
void release();
protected:
int mID;
// for poco auto ptr
int mReferenceCount;
};
class ModelInsertTask : public UniLib::controller::CPUTask
{
public:
ModelInsertTask(Poco::AutoPtr<ModelBase> model);
int run();
const char* getResourceType() const { return "ModelInsertTask"; };
protected:
Poco::AutoPtr<ModelBase> mModel;
};
#endif //GRADIDO_LOGIN_SERVER_MODEL_INTERFACE_INCLUDE

View File

@ -518,7 +518,7 @@ const char* Session::translateSessionStateToString(SessionStates state)
void Session::createEmailVerificationCode()
{
uint32_t* code_p = (uint32_t*)&mEmailVerificationCode;
for (int i = 0; i < EMAIL_VERIFICATION_CODE_SIZE / 4; i++) {
for (int i = 0; i < sizeof(mEmailVerificationCode) / 4; i++) {
code_p[i] = randombytes_random();
}

View File

@ -21,7 +21,7 @@
#include "Poco/Net/IPAddress.h"
#include "Poco/Net/HTTPCookie.h"
#define EMAIL_VERIFICATION_CODE_SIZE 8
class WriteEmailVerification;

View File

@ -453,8 +453,15 @@ Poco::Data::Statement User::insertIntoDB(Poco::Data::Session session)
//Poco::Data::BLOB pwd(&mPasswordHashed[0], crypto_shorthash_BYTES);
//printf("[User::insertIntoDB] password hashed: %llu\n", mPasswordHashed);
insert << "INSERT INTO users (email, first_name, last_name, password) VALUES(?, ?, ?, ?);",
use(mEmail), use(mFirstName), use(mLastName), bind(mPasswordHashed);
if (mPasswordHashed) {
insert << "INSERT INTO users (email, first_name, last_name, password) VALUES(?, ?, ?, ?);",
use(mEmail), use(mFirstName), use(mLastName), bind(mPasswordHashed);
}
else {
insert << "INSERT INTO users (email, first_name, last_name) VALUES(?, ?, ?);",
use(mEmail), use(mFirstName), use(mLastName);
}
return insert;
}

View File

View File