mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
1017 lines
32 KiB
C++
1017 lines
32 KiB
C++
#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/AuthenticatedEncryptionCreateKeyTask.h"
|
|
#include "../tasks/VerificationEmailResendTask.h"
|
|
|
|
#include "../lib/JsonRequest.h"
|
|
|
|
#include "../Crypto/Passphrase.h"
|
|
|
|
|
|
#include "../controller/User.h"
|
|
#include "../controller/UserBackup.h"
|
|
#include "../controller/EmailVerificationCode.h"
|
|
|
|
#include "table/UserRole.h"
|
|
|
|
#include "table/ModelBase.h"
|
|
|
|
|
|
#include "sodium.h"
|
|
|
|
using namespace Poco::Data::Keywords;
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------------------------------------------
|
|
|
|
Session::Session(int handle)
|
|
: mHandleId(handle), 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<std::shared_mutex> _lock(mSharedMutex);
|
|
mNewUser.assign(nullptr);
|
|
mEmailVerificationCodeObject.assign(nullptr);
|
|
|
|
// watch out
|
|
//updateTimeout();
|
|
mLastActivity = Poco::DateTime();
|
|
|
|
mState = SESSION_STATE_EMPTY;
|
|
|
|
mPassphrase = "";
|
|
mLastExternReferer = "";
|
|
mClientLoginIP = Poco::Net::IPAddress();
|
|
unlock();
|
|
|
|
|
|
//printf("[Session::reset] finished\n");
|
|
}
|
|
|
|
int Session::isActive()
|
|
{
|
|
int ret = 0;
|
|
|
|
if(!mWorkMutex.tryLock(100)) {
|
|
return -1;
|
|
}
|
|
|
|
ret = (int)mActive;
|
|
|
|
try {
|
|
unlock();
|
|
} catch(Poco::SystemException& ex) {
|
|
addError(new ParamError("Session::isActive", "exception unlocking mutex", ex.what()));
|
|
return -1;
|
|
}
|
|
return ret;
|
|
|
|
}
|
|
|
|
bool Session::isDeadLocked()
|
|
{
|
|
if(!mWorkMutex.tryLock(200)) {
|
|
return true;
|
|
};
|
|
unlock();
|
|
return false;
|
|
}
|
|
|
|
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<controller::EmailVerificationCode> Session::getEmailVerificationCodeObject()
|
|
{
|
|
lock("Session::getEmailVerificationCodeObject");
|
|
std::shared_lock<std::shared_mutex> _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, int group_id, const std::string &baseUrl)
|
|
{
|
|
Profiler usedTime;
|
|
auto user_model = mNewUser->getModel();
|
|
if (user_model->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 (user_model->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, group_id);
|
|
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);
|
|
email_verification_code->setBaseUrl(baseUrl);
|
|
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<std::shared_mutex> _lock(mSharedMutex);
|
|
mEmailVerificationCodeObject = email_verification_code;
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool Session::createUserDirect(const std::string& first_name, const std::string& last_name, const std::string& email, const std::string& password, const std::string &baseUrl)
|
|
{
|
|
std::unique_lock<std::shared_mutex> _lock(mSharedMutex);
|
|
static const char* function_name = "Session::createUserDirect";
|
|
auto sm = SessionManager::getInstance();
|
|
auto em = ErrorManager::getInstance();
|
|
auto email_manager = EmailManager::getInstance();
|
|
|
|
if (mLanguageCatalog.isNull()) {
|
|
mLanguageCatalog = LanguageManager::getInstance()->getFreeCatalog(LANG_DE);
|
|
}
|
|
|
|
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, mLanguageCatalog)) {
|
|
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
|
|
int group_id = 0;
|
|
if (ServerConfig::g_devDefaultGroup != "") {
|
|
auto groups = controller::Group::load(ServerConfig::g_devDefaultGroup);
|
|
if (groups.size() == 1) {
|
|
group_id = groups[0]->getModel()->getID();
|
|
}
|
|
}
|
|
mNewUser = controller::User::create(email, first_name, last_name, group_id);
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
// if it gets id 1, it's the first user, so we should give him the admin role
|
|
if (user_id == 1) {
|
|
Poco::AutoPtr<model::table::UserRole> user_role(new model::table::UserRole(user_id, model::table::ROLE_ADMIN));
|
|
user_role->insertIntoDB(false);
|
|
mNewUser->reload();
|
|
}
|
|
|
|
|
|
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->setBaseUrl(baseUrl + "/checkEmail");
|
|
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<Poco::Mutex> _lock(mWorkMutex);
|
|
// new mutex, will replace the Poco Mutex complete in the future
|
|
std::unique_lock<std::shared_mutex> _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)
|
|
{
|
|
// 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()) {
|
|
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;
|
|
|
|
}
|
|
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<controller::User> user, bool passphraseMemorized, const std::string& baseUrl)
|
|
{
|
|
mNewUser = user;
|
|
auto em = EmailManager::getInstance();
|
|
|
|
std::unique_lock<std::shared_mutex> _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;
|
|
}
|
|
if (baseUrl.size() > 0) {
|
|
mEmailVerificationCodeObject->setBaseUrl(baseUrl);
|
|
}
|
|
auto email_verification_model = mEmailVerificationCodeObject->getModel();
|
|
if (email_already_send) {
|
|
auto time_elapsed = Poco::DateTime() - email_verification_model->getUpdated();
|
|
if (time_elapsed.totalMinutes() < 10) {
|
|
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, const Mnemonic* wordSource)
|
|
{
|
|
|
|
static const char* functionName = "Session::comparePassphraseWithSavedKeys";
|
|
if (!wordSource) {
|
|
addError(new Error(functionName, "wordSource is empty"));
|
|
sendErrorsAsEmail();
|
|
return -2;
|
|
}
|
|
auto passphrase = Passphrase::create(inputPassphrase, wordSource);
|
|
// if (!keys.generateFromPassphrase(inputPassphrase.data(), wordSource)) {
|
|
if (passphrase.isNull() || !passphrase->checkIfValid()) {
|
|
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;
|
|
}
|
|
}
|
|
auto keys = KeyPairEd25519::create(passphrase);
|
|
if (keys) {
|
|
auto cmp_result = memcmp(userModel->getPublicKey(), keys->getPublicKey(), crypto_sign_PUBLICKEYBYTES);
|
|
delete keys;
|
|
keys = nullptr;
|
|
if (0 == cmp_result) {
|
|
mPassphrase = inputPassphrase;
|
|
return 1;
|
|
}
|
|
}
|
|
addError(new Error(gettext("Passphrase"), gettext("Das ist nicht die richtige Passphrase.")), false);
|
|
return 0;
|
|
}
|
|
|
|
UserState Session::loadUser(const std::string& email, const std::string& password)
|
|
{
|
|
static const char* functionName = "Session::loadUser";
|
|
auto observer = SingletonTaskObserver::getInstance();
|
|
auto sm = SessionManager::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);
|
|
Poco::ScopedLock<Poco::Mutex> _lock(mWorkMutex);
|
|
|
|
//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;
|
|
}
|
|
if (mNewUser->getUserState() >= USER_LOADED_FROM_DB) {
|
|
|
|
NotificationList pwd_errors;
|
|
auto lm = LanguageManager::getInstance();
|
|
auto lang_key = mNewUser->getModel()->getLanguageKey();
|
|
if (mLanguageCatalog.isNull()) {
|
|
mLanguageCatalog = lm->getFreeCatalog(lm->languageFromString(lang_key));
|
|
}
|
|
|
|
if (!sm->checkPwdValidation(password, &pwd_errors, mLanguageCatalog))
|
|
{
|
|
Poco::Thread::sleep(ServerConfig::g_FakeLoginSleepTime);
|
|
return USER_PASSWORD_INCORRECT;
|
|
}
|
|
|
|
int loginResult = mNewUser->login(password);
|
|
int exitCount = 0;
|
|
if (loginResult == -3)
|
|
{
|
|
do
|
|
{
|
|
Poco::Thread::sleep(100);
|
|
loginResult = mNewUser->login(password);
|
|
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;
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
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::UserBackup::load(user_model->getID());
|
|
for (auto it = user_backups.begin(); it != user_backups.end(); it++) {
|
|
auto key = std::unique_ptr<KeyPairEd25519>((*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;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
else {
|
|
Poco::Thread::sleep(ServerConfig::g_FakeLoginSleepTime);
|
|
}
|
|
|
|
detectSessionState();
|
|
if (0 == mNewUser->getModel()->getGroupId()) {
|
|
return USER_NO_GROUP;
|
|
}
|
|
|
|
return mNewUser->getUserState();
|
|
}
|
|
|
|
bool Session::deleteUser()
|
|
{
|
|
lock("Session::deleteUser");
|
|
bool bResult = false;
|
|
if(!mNewUser.isNull()) {
|
|
JsonRequest phpServerRequest(ServerConfig::g_php_serverHost, 443);
|
|
Poco::Net::NameValueCollection payload;
|
|
auto user_model = mNewUser->getModel();
|
|
payload.add("user", user_model->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 = user_model->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 (mNewUser.isNull() || !mNewUser->getModel() || mNewUser->getPassword().isNull()) {
|
|
return;
|
|
}
|
|
UserState userState = mNewUser->getUserState();
|
|
|
|
int checkEmail = -1, resetPasswd = -1;
|
|
try {
|
|
auto emailVerificationCodeObjects = controller::EmailVerificationCode::load(mNewUser->getModel()->getID());
|
|
|
|
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<std::shared_mutex> _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 = mNewUser->getModel()->getID();
|
|
auto userBackups = controller::UserBackup::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++) {
|
|
auto passphrase = (*it)->getModel()->getPassphrase();
|
|
Mnemonic* wordSource = nullptr;
|
|
auto passphrase_obj = Passphrase::create(passphrase, wordSource);
|
|
if (!passphrase_obj.isNull() && passphrase_obj->checkIfValid()) {
|
|
auto key_pair = KeyPairEd25519::create(passphrase_obj);
|
|
if (key_pair && key_pair->isTheSame(mNewUser->getModel()->getPublicKey())) {
|
|
correctPassphraseFound = true;
|
|
//break;
|
|
}
|
|
if (key_pair) {
|
|
delete key_pair;
|
|
}
|
|
if (correctPassphraseFound) {
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
cryptedPassphrase = true;
|
|
}
|
|
}
|
|
/*
|
|
auto dbConnection = ConnectionManager::getInstance()->getConnection(CONNECTION_MYSQL_LOGIN_SERVER);
|
|
Poco::Data::Statement select(dbConnection);
|
|
Poco::Nullable<Poco::Data::BLOB> 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<std::shared_mutex> _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;
|
|
}
|
|
// TODO: Maybe update language key by user, is session has another, or update only with options-menu
|
|
|
|
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::lastTransactionTheSame(Poco::AutoPtr<model::gradido::Transaction> newTransaction)
|
|
{
|
|
assert(!newTransaction.isNull());
|
|
Poco::ScopedLock<Poco::Mutex> _lock(mWorkMutex);
|
|
if (mLastTransaction.isNull()) {
|
|
return false;
|
|
}
|
|
bool result = mLastTransaction->isTheSameTransaction(newTransaction);
|
|
return result;
|
|
}
|
|
|
|
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::UserBackup::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;
|
|
*/
|
|
}
|