adding transaction handling classes

This commit is contained in:
Dario 2019-10-25 18:55:06 +02:00
parent 02b86520e4
commit afdee88dec
17 changed files with 475 additions and 7 deletions

View File

@ -26,11 +26,18 @@ FILE(GLOB MODEL "src/cpp/model/*.h" "src/cpp/model/*.cpp")
FILE(GLOB CRYPTO "src/cpp/Crypto/*.h" "src/cpp/Crypto/*.cpp")
FILE(GLOB MAIN "src/cpp/*.cpp" "src/cpp/*.c" "src/cpp/*.h")
FILE(GLOB MYSQL "src/cpp/MySQL/*.cpp" "src/cpp/MySQL/*.h" "src/cpp/MySQL/Poco/*.h")
SET(LOCAL_SRCS ${TINF} ${MAIN} ${HTTPInterface} ${JSONInterface} ${CRYPTO} ${MODEL} ${SINGLETON_MANAGER} ${MYSQL} ${TASKS})
FILE(GLOB PROTO_GRADIDO "src/cpp/proto/gradido/*.cc" "src/cpp/proto/gradido/*.h")
SET(LOCAL_SRCS
${TINF} ${MAIN} ${HTTPInterface}
${JSONInterface} ${CRYPTO} ${MODEL}
${SINGLETON_MANAGER} ${MYSQL} ${TASKS}
${PROTO_GRADIDO}
)
aux_source_directory("src/cpp" LOCAL_SRCS)
if(MSVC)
# src
source_group("proto" FILES ${PROTO_GRADIDO})
source_group("tinf" FILES ${TINF})
source_group("crypto" FILES ${CRYPTO})
source_group("tasks" FILES ${TASKS})

View File

@ -2,4 +2,8 @@
if [ ! -d "./src/cpp/proto" ] ; then
mkdir ./src/cpp/proto
fi
protoc --cpp_out=./src/cpp/proto --proto_path=./src/proto/gradido ./src/proto/gradido/*.proto
if [ ! -d "./src/cpp/proto/gradido" ] ; then
mkdir ./src/cpp/proto/gradido
fi
protoc --cpp_out=./src/cpp/proto/gradido --proto_path=./src/proto/gradido ./src/proto/gradido/*.proto

@ -1 +1 @@
Subproject commit 1fdf5b6e10be2b1d7118aa3c32dc7acde02cb0cd
Subproject commit 7307ffb8a89d2459f0c07ea5cab27c0d3496df00

@ -1 +1 @@
Subproject commit 947357354a92cbb7c15b1e9087d085fe65f75f5e
Subproject commit 9ba8e32f6d0fe449114d8eb369cf29303257b460

View File

@ -1,8 +1,11 @@
#include "Gradido_LoginServer.h"
#include <sodium.h>
#include "proto/gradido/TransactionBody.pb.h"
int main(int argc, char** argv)
{
GOOGLE_PROTOBUF_VERIFY_VERSION;
if (sodium_init() < 0) {
/* panic! the library couldn't be initialized, it is not safe to use */
printf("error initing sodium, early exit\n");

View File

@ -79,7 +79,7 @@ int WritePassphraseIntoDB::run()
// --------------------------------------------------------------------------------------------------------------
Session::Session(int handle)
: mHandleId(handle), mSessionUser(nullptr), mEmailVerificationCode(0), mState(SESSION_STATE_EMPTY), mActive(false)
: mHandleId(handle), mSessionUser(nullptr), mEmailVerificationCode(0), mState(SESSION_STATE_EMPTY), mActive(false), mProcessingTransaction(nullptr)
{
}
@ -98,6 +98,7 @@ void Session::reset()
lock();
mSessionUser = nullptr;
mProcessingTransaction = nullptr;
// watch out
//updateTimeout();
@ -278,6 +279,39 @@ bool Session::updateEmailVerification(Poco::UInt64 emailVerificationCode)
return false;
}
bool Session::startProcessingTransaction(std::string proto_message_base64)
{
lock();
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("Session::startProcessingTransaction", "transaction already in list"));
unlock();
return false;
}
}
UniLib::controller::TaskPtr processorTask(new ProcessingTransaction(proto_message_base64));
processorTask->scheduleTask(processorTask);
mProcessingTransactions.push_back(processorTask);
unlock();
return true;
}
Poco::AutoPtr<ProcessingTransaction> Session::getNextReadyTransaction()
{
lock();
for (auto it = mProcessingTransactions.begin(); it != mProcessingTransactions.end(); it++) {
if ((*it)->isTaskFinished()) {
}
}
}
bool Session::isPwdValid(const std::string& pwd)
{
if (mSessionUser) {

View File

@ -14,6 +14,7 @@
#include "User.h"
#include "../tasks/MultithreadContainer.h"
#include "../tasks/ProcessingTransaction.h"
#include "Poco/Thread.h"
#include "Poco/Types.h"
@ -64,6 +65,8 @@ public:
bool updateEmailVerification(Poco::UInt64 emailVerificationCode);
bool startProcessingTransaction(std::string proto_message_base64);
Poco::Net::HTTPCookie getLoginCookie();
Poco::AutoPtr<User> getUser() {
@ -95,6 +98,7 @@ public:
inline Poco::DateTime getLastActivity() { return mLastActivity; }
Poco::AutoPtr<ProcessingTransaction> getNextReadyTransaction();
protected:
void updateTimeout();
@ -105,6 +109,8 @@ protected:
void detectSessionState();
static const char* translateSessionStateToString(SessionStates state);
private:
int mHandleId;
Poco::AutoPtr<User> mSessionUser;
@ -116,7 +122,8 @@ private:
SessionStates mState;
bool mActive;
std::list<Poco::AutoPtr<ProcessingTransaction>> mProcessingTransactions;
std::list<Poco::AutoPtr<ProcessingTransaction>>
};

View File

@ -0,0 +1,20 @@
/*!
*
* \author: einhornimmond
*
* \date: 25.10.19
*
* \brief: Interface for Transaction Objects
*/
#ifndef GRADIDO_LOGIN_SERVER_MODEL_TRANSACTION_BASE_INCLUDE
#define GRADIDO_LOGIN_SERVER_MODEL_TRANSACTION_BASE_INCLUDE
#include "ErrorList.h"
class TransactionBase : public ErrorList
{
public:
virtual int prepare() = 0;
};
#endif //GRADIDO_LOGIN_SERVER_MODEL_TRANSACTION_BASE_INCLUDE

View File

@ -0,0 +1,50 @@
#include "TransactionCreation.h"
#include <sodium.h>
TransactionCreation::TransactionCreation(const model::messages::gradido::TransactionCreation& protoCreation)
: mProtoCreation(protoCreation), mReceiverUser(nullptr)
{
memset(mReceiverPublicHex, 0, 65);
}
TransactionCreation::~TransactionCreation()
{
if (mReceiverUser) {
delete mReceiverUser;
mReceiverUser = nullptr;
}
}
int TransactionCreation::prepare()
{
const static char functionName[] = { "TransactionCreation::prepare" };
if (!mProtoCreation.has_receiveramount()) {
addError(new Error(functionName, "hasn't receiver amount"));
return -1;
}
auto receiverAmount = mProtoCreation.receiveramount();
auto receiverPublic = receiverAmount.ed25519_receiver_pubkey();
if (receiverPublic.size() != 32) {
addError(new Error(functionName, "receiver public invalid (size not 32)"));
return -2;
}
mReceiverUser = new User(receiverPublic.data());
getErrors(mReceiverUser);
if (mReceiverUser->getUserState() == USER_EMPTY) {
sodium_bin2hex(mReceiverPublicHex, 64, (const unsigned char*)receiverPublic.data(), receiverPublic.size());
delete mReceiverUser;
mReceiverUser = nullptr;
}
else {
memcpy(mReceiverPublicHex, mReceiverUser->getPublicKeyHex().data(), 64);
if (!mReceiverUser->validateIdentHash(mProtoCreation.ident_hash())) {
addError(new Error(functionName, "ident hash isn't the same"));
return -3;
}
}
//
return 0;
}

View File

@ -0,0 +1,35 @@
/*!
*
* \author: einhornimmond
*
* \date: 25.10.19
*
* \brief: Creation Transaction
*/
#ifndef GRADIDO_LOGIN_SERVER_MODEL_TRANSACTION_CREATION_INCLUDE
#define GRADIDO_LOGIN_SERVER_MODEL_TRANSACTION_CREATION_INCLUDE
#include "TransactionBase.h"
#include "../proto/gradido/TransactionCreation.pb.h"
#include "User.h"
class TransactionCreation : public TransactionBase
{
public:
TransactionCreation(const model::messages::gradido::TransactionCreation& protoCreation);
~TransactionCreation();
int prepare();
inline User* getUser() { return mReceiverUser; }
inline google::protobuf::int64 getAmount() { return mProtoCreation.receiveramount().amount(); }
inline char* getPublicHex() { return mReceiverPublicHex; }
protected:
const model::messages::gradido::TransactionCreation& mProtoCreation;
char mReceiverPublicHex[65];
User* mReceiverUser;
};
#endif //GRADIDO_LOGIN_SERVER_MODEL_TRANSACTION_CREATION_INCLUDE

View File

@ -0,0 +1,14 @@
#include "TransactionTransfer.h"
TransactionTransfer::TransactionTransfer(const model::messages::gradido::Transfer& protoTransfer)
: mProtoTransfer(protoTransfer)
{
}
int TransactionTransfer::prepare()
{
return 0;
}

View File

@ -0,0 +1,26 @@
/*!
*
* \author: einhornimmond
*
* \date: 25.10.19
*
* \brief: Creation Transaction
*/
#ifndef GRADIDO_LOGIN_SERVER_MODEL_TRANSACTION_TRANSFER_INCLUDE
#define GRADIDO_LOGIN_SERVER_MODEL_TRANSACTION_TRANSFER_INCLUDE
#include "TransactionBase.h"
#include "../proto/gradido/Transfer.pb.h"
class TransactionTransfer : public TransactionBase
{
public:
TransactionTransfer(const model::messages::gradido::Transfer& protoTransfer);
int prepare();
protected:
const model::messages::gradido::Transfer& mProtoTransfer;
};
#endif //GRADIDO_LOGIN_SERVER_MODEL_TRANSACTION_TRANSFER_INCLUDE

View File

@ -266,6 +266,50 @@ User::User(int user_id)
}
}
User::User(const unsigned char* pubkey_array)
: mState(USER_EMPTY), mDBId(0), mPasswordHashed(0), mPrivateKey(nullptr), mEmailChecked(false), mCryptoKey(nullptr), mReferenceCount(1)
{
//crypto_shorthash(mPasswordHashed, (const unsigned char*)password, strlen(password), *ServerConfig::g_ServerCryptoKey);
//memset(mPasswordHashed, 0, crypto_shorthash_BYTES);
auto cm = ConnectionManager::getInstance();
auto session = cm->getConnection(CONNECTION_MYSQL_LOGIN_SERVER);
Poco::Data::BLOB pubkey(pubkey_array, 32);
Poco::Nullable<Poco::Data::BLOB> privkey;
Poco::Data::Statement select(session);
int email_checked = 0;
select << "SELECT id, email, first_name, last_name, password, privkey, email_checked from users where pubkey = ?",
into(mDBId), into(mEmail), into(mFirstName), into(mLastName), into(mPasswordHashed), into(privkey), into(email_checked), use(pubkey);
try {
auto result = select.execute();
if (result == 1) {
mState = USER_LOADED_FROM_DB;
if (email_checked == 0) { mState = USER_EMAIL_NOT_ACTIVATED; }
else if (privkey.isNull()) { mState = USER_NO_PRIVATE_KEY; }
else { mState = USER_COMPLETE; }
mEmailChecked = email_checked == 1;
size_t hexSize = pubkey.size() * 2 + 1;
char* hexString = (char*)malloc(hexSize);
memset(hexString, 0, hexSize);
sodium_bin2hex(hexString, hexSize, pubkey.content().data(), pubkey.size());
mPublicHex = hexString;
free(hexString);
if (!privkey.isNull()) {
auto privkey_value = privkey.value();
mPrivateKey = new ObfusArray(privkey_value.size(), privkey_value.content().data());
}
}
}
catch (Poco::Exception& ex) {
addError(new ParamError("User::User", "mysql error", ex.displayText().data()));
}
}
User::~User()
@ -372,7 +416,7 @@ Poco::JSON::Object User::getJson()
userObj.set("public_hex", mPublicHex);
userObj.set("state", userStateToString(mState));
userObj.set("email_checked", mEmailChecked);
userObj.set("ident_hash", DRMakeStringHash(mEmail.data()));
userObj.set("ident_hash", DRMakeStringHash(mEmail.data(), mEmail.size()));
unlock();
return userObj;
}
@ -455,6 +499,14 @@ bool User::validatePwd(const std::string& pwd, ErrorList* validationErrorsToPrin
return false;
}
bool User::validateIdentHash(HASH hash)
{
lock();
HASH local_hash = DRMakeStringHash(mEmail.data(), mEmail.size());
unlock();
return local_hash == hash;
}
bool User::deleteFromDB()
{
auto cm = ConnectionManager::getInstance();

View File

@ -50,6 +50,9 @@ public:
// existing user
User(const char* email);
// existing user by public key
User(const unsigned char* pubkey_array);
User(int user_id);
// login
//User(const std::string& email, const std::string& password);
@ -82,6 +85,7 @@ public:
bool isEmptyPassword();
bool setNewPassword(const std::string& newPassword);
bool validatePwd(const std::string& pwd, ErrorList* validationErrorsToPrint);
bool validateIdentHash(HASH hash);
Poco::Data::BLOB* encrypt(const ObfusArray* data);

View File

@ -0,0 +1,99 @@
#include "ProcessingTransaction.h"
#include <sodium.h>
#include "../model/TransactionCreation.h"
#include "../model/TransactionTransfer.h"
ProcessingTransaction::ProcessingTransaction(std::string proto_message_base64)
: mType(TRANSACTION_NONE), mProtoMessageBase64(proto_message_base64), mTransactionSpecific(nullptr)
{
mHashMutex.lock();
mHash = calculateHash(proto_message_base64);
mHashMutex.unlock();
}
ProcessingTransaction::~ProcessingTransaction()
{
lock();
if (mTransactionSpecific) {
delete mTransactionSpecific;
mTransactionSpecific = nullptr;
}
unlock();
}
HASH ProcessingTransaction::calculateHash(std::string proto_message_base64)
{
return DRMakeStringHash(proto_message_base64.data(), proto_message_base64.size());
}
int ProcessingTransaction::run()
{
lock();
//mTransactionBody.ParseFromString();
unsigned char* binBuffer = (unsigned char*)malloc(mProtoMessageBase64.size());
size_t resultingBinSize = 0;
size_t base64_size = mProtoMessageBase64.size();
if (sodium_base642bin(
binBuffer, base64_size,
mProtoMessageBase64.data(), base64_size,
nullptr, &resultingBinSize, nullptr,
sodium_base64_VARIANT_ORIGINAL))
{
free(binBuffer);
addError(new Error("ProcessingTransaction", "error decoding base64"));
unlock();
return -1;
}
std::string binString((char*)binBuffer, resultingBinSize);
free(binBuffer);
if (!mTransactionBody.ParseFromString(binString)) {
addError(new Error("ProcessingTransaction", "error creating Transaction from binary Message"));
unlock();
return -2;
}
// check Type
if (mTransactionBody.has_creation()) {
mType = TRANSACTION_CREATION;
mTransactionSpecific = new TransactionCreation(mTransactionBody.creation());
}
else if (mTransactionBody.has_transfer()) {
mType = TRANSACTION_TRANSFER;
mTransactionSpecific = new TransactionTransfer(mTransactionBody.transfer());
}
if (mTransactionSpecific) {
if (mTransactionSpecific->prepare()) {
getErrors(mTransactionSpecific);
addError(new Error("ProcessingTransaction", "error preparing"));
unlock();
return -3;
}
}
unlock();
return 0;
}
std::string ProcessingTransaction::getMemo()
{
lock();
if (mTransactionBody.IsInitialized()) {
std::string result(mTransactionBody.memo());
unlock();
return result;
}
unlock();
return "<uninitalized>";
}
TransactionCreation* ProcessingTransaction::getCreationTransaction()
{
return dynamic_cast<TransactionCreation*>(mTransactionSpecific);
}
TransactionTransfer* ProcessingTransaction::getTransferTransaction()
{
return dynamic_cast<TransactionTransfer*>(mTransactionSpecific);
}

View File

@ -0,0 +1,65 @@
#ifndef GRADIDO_LOGIN_SERVER_TASKS_PROCESSING_TRANSACTION_INCLUDE
#define GRADIDO_LOGIN_SERVER_TASKS_PROCESSING_TRANSACTION_INCLUDE
#include "CPUTask.h"
#include "../model/ErrorList.h"
#include "../model/TransactionBase.h"
#include "../proto/gradido/TransactionBody.pb.h"
/*
* @author: Dario Rekowski
*
* @date: 25.10.19
* @desc: Task for processing Transactions
*/
enum TransactionType {
TRANSACTION_NONE,
TRANSACTION_CREATION,
TRANSACTION_TRANSFER
};
class TransactionCreation;
class TransactionTransfer;
class ProcessingTransaction : public UniLib::controller::CPUTask, public ErrorList
{
public:
ProcessingTransaction(std::string proto_message_base64);
virtual ~ProcessingTransaction();
int run();
const char* getResourceType() const { return "ProcessingTransaction"; };
inline TransactionType getType() { lock(); auto t = mType; unlock(); return t; }
std::string getMemo();
// not secured zone, no locking
bool isCreation() { return mType == TRANSACTION_CREATION; }
bool isTransfer() { return mType == TRANSACTION_TRANSFER; }
TransactionCreation* getCreationTransaction();
TransactionTransfer* getTransferTransaction();
static HASH calculateHash(std::string proto_message_base64);
inline HASH getHash() { mHashMutex.lock(); HASH hs = mHash; mHashMutex.unlock(); return hs; }
protected:
TransactionType mType;
std::string mProtoMessageBase64;
model::messages::gradido::TransactionBody mTransactionBody;
TransactionBase* mTransactionSpecific;
HASH mHash;
Poco::Mutex mHashMutex;
private:
};
#endif //GRADIDO_LOGIN_SERVER_TASKS_PROCESSING_TRANSACTION_INCLUDE

View File

@ -0,0 +1,48 @@
<%@ page class="CheckTransactionPage" %>
<%@ page baseClass="SessionHTTPRequestHandler" %>
<%@ page ctorArg="Session*" %>
<%@ header include="SessionHTTPRequestHandler.h" %>
<%@ page form="true" %>
<%@ page compressed="true" %>
<%!
#include "../SingletonManager/SessionManager.h"
%>
<%%
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Gradido Login Server: &Uuml;berpr&uuml;fe Transaktion</title>
<!--<link rel="stylesheet" type="text/css" href="css/styles.min.css">-->
<link rel="stylesheet" type="text/css" href="https://gradido2.dario-rekowski.de/css/styles.css">
<style type="text/css" >
.grd_container
{
max-width:820px;
margin-left:auto;
margin-right:auto;
}
input:not([type='radio']) {
width:200px;
}
label:not(.grd_radio_label) {
width:80px;
display:inline-block;
}
</style>
</head>
<body>
<div class="grd_container">
<h1>Eine Transaktion pr&uuml;fen</h1>
<%= getErrorsHtml() %>
</div>
<div class="grd-time-used">
<%= mTimeProfiler.string() %>
</div>
</body>
</html>