From b73bb6408914e49b03748ceb3fc998b4375ef325 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 24 Mar 2021 14:17:58 +0100 Subject: [PATCH 1/8] add send email function in API for password reset and admin email verification code resend Describe new function in detail in docu/login_server.api.md. Made some changes in existing code to have the option to use a custome email text, not only the pre-formulated texte. Use a new state for returned json, state = warning. If function does what expected but still have a error message. Add the new state in frontend/src/apis/loginAPI.js. --- docu/login_server.api.md | 66 +++++++- frontend/src/apis/loginAPI.js | 19 +++ .../cpp/JSONInterface/JsonRequestHandler.cpp | 11 ++ .../cpp/JSONInterface/JsonRequestHandler.h | 1 + .../JsonRequestHandlerFactory.cpp | 7 +- .../src/cpp/JSONInterface/JsonSendEmail.cpp | 149 ++++++++++++++++++ .../src/cpp/JSONInterface/JsonSendEmail.h | 16 ++ .../cpp/controller/EmailVerificationCode.cpp | 18 +++ .../cpp/controller/EmailVerificationCode.h | 2 + login_server/src/cpp/model/email/Email.cpp | 68 +++++++- login_server/src/cpp/model/email/Email.h | 33 ++-- .../src/cpp/model/table/EmailOptIn.cpp | 13 ++ login_server/src/cpp/model/table/EmailOptIn.h | 1 + 13 files changed, 378 insertions(+), 26 deletions(-) create mode 100644 login_server/src/cpp/JSONInterface/JsonSendEmail.cpp create mode 100644 login_server/src/cpp/JSONInterface/JsonSendEmail.h diff --git a/docu/login_server.api.md b/docu/login_server.api.md index 6690abc7f..51e29db96 100644 --- a/docu/login_server.api.md +++ b/docu/login_server.api.md @@ -98,6 +98,7 @@ Update password can only be used if in Login-Server config: ```ini unsecure.allow_passwort_via_json_request = 1 ``` +is set POST http://localhost/login_api/updateUserInfos ```json @@ -207,6 +208,65 @@ return - registerDirect: code generated by register for check email - register: code generated by auto-register via elopage for check email - info can contain additional info strings - - user hasn't password: if user hasn't set a password yet (for example if he was registered via elopage) - - email already activated: if email was already checked -- session_id: session_id for new session \ No newline at end of file + - "user hasn't password": if user hasn't set a password yet (for example if he was registered via elopage) + - "email already activated": if email was already checked +- session_id: session_id for new session + +## Send Emails +Let send Login-Server Password reset E-Mail to User +Can be also used for admin interface to resend email verification code +POST http://localhost/login_api/sendEmail +```json +{"email": "max.musterman@gmail.de", "email_text":7, "email_verification_code_type":"resetPassword" } +``` +also valid: +```json +{"email": "max.musterman@gmail.de", "email_text":"user reset Password", "email_verification_code_type":"resetPassword" } +``` +or: +```json +{"session_id": -127182, "email": "max.musterman@gmail.de", "email_text":5, "email_verification_code_type":"register" } +``` + +- session_id: not needed for resetPassword emails +- email_type: choose which email text should be used (number or string can be used) + - "email user verification code" (2): default text used when new user has registered + - "email user verification code resend" (3): text used when user hasn't activated his account 7 days after register + - "email user verification code resend after long time" (4): text used when user hasn't activated his account more than 7 days after register + - "email admin user verification code" (5): used if admin trigger sending the email with email verification code + - "email admin user verification code resend" (6): used if admin trigger sending the email with email verification code again + - "user reset Password" (7): used for reset password email text + - "email custom tex" (8): used if custom email text should be used (than email_custom_text must also be filled) +- email_verification_code_type + - resetPassword: for password resets, will be deleted immediately, is a only one use code, can be used without session_id + - registerDirect: code generated by register for check email, can only be used by admins for another user + - register: code generated by auto-register via elopage for check email, can only be used by admins for another user +- email_custom_text (optional): can be used to send email with custom text + placeholder for email text, will be replaced + - [first_name] first name + - [last_name] last name + - [duration] time span since user has created account (ex.: 10 Days) with largest unit, day is last unit + - [link] login-server checkEmail link with email verification code (ex.: http://localhost/account/checkEmail/382738273892983) + - [code] email verification code if you like to use your one link +- emailCustomSubject: for custom email the subject + +return +```json +{"state":"success"} +``` +if everything is okay +return +```json +{"state":"warning", "msg":"email already sended"} +``` +if emails was successfully sended but was already sended in past also. + +return with "state":"error" and additional "msg" if error occured (no email sended): +- "email already send less than a hour before": User has already get a password reset email and haven't used the link yet +- "not supported email type": with "email_verification_code_type":"resetPassword" only email type 7 or 8 allowed +- "admin needed": only admins can send email verification emails because this emails normally sended out automaticly +- "invalid email": if email wasn't found in db +- "invalid session": if session wasn't found (only checked if session_id is set and != 0) +- "invalid email type": could not parse email type +- "invalid verification code type": could not parse email verification code type +- "json exception": error parsing input json, more infos can be found in details \ No newline at end of file diff --git a/frontend/src/apis/loginAPI.js b/frontend/src/apis/loginAPI.js index 8756a6840..b9e433730 100644 --- a/frontend/src/apis/loginAPI.js +++ b/frontend/src/apis/loginAPI.js @@ -7,12 +7,31 @@ const EMAIL_TYPE = { ADMIN: 5, // if user was registered by an admin } +const apiGet = async (url) => { + try { + const result = await axios.get(url); + if(result.status !== 200){ + throw new Error('HTTP Status Error '+result.status) + } + + if(result.data.state !== 'success'){ + throw new Error(result.data.msg) + } + return { success: true, result } + } catch(error){ + return { success: false, result: error} + } +} + const apiPost = async (url, payload) => { try { const result = await axios.post(url, payload); if(result.status !== 200){ throw new Error('HTTP Status Error '+result.status) } + if(result.data.state === 'warning') { + return { success: true, result: error } + } if(result.data.state !== 'success'){ throw new Error(result.data.msg) } diff --git a/login_server/src/cpp/JSONInterface/JsonRequestHandler.cpp b/login_server/src/cpp/JSONInterface/JsonRequestHandler.cpp index b66cba495..c8ca8964e 100644 --- a/login_server/src/cpp/JSONInterface/JsonRequestHandler.cpp +++ b/login_server/src/cpp/JSONInterface/JsonRequestHandler.cpp @@ -139,3 +139,14 @@ Poco::JSON::Object* JsonRequestHandler::customStateError(const char* state, cons return result; } +Poco::JSON::Object* JsonRequestHandler::stateWarning(const char* msg, std::string details/* = ""*/) +{ + Poco::JSON::Object* result = new Poco::JSON::Object; + result->set("state", "warning"); + result->set("msg", msg); + if (details != "") { + result->set("details", details); + } + return result; +} + diff --git a/login_server/src/cpp/JSONInterface/JsonRequestHandler.h b/login_server/src/cpp/JSONInterface/JsonRequestHandler.h index 72230bfe4..998c48edf 100644 --- a/login_server/src/cpp/JSONInterface/JsonRequestHandler.h +++ b/login_server/src/cpp/JSONInterface/JsonRequestHandler.h @@ -22,6 +22,7 @@ protected: static Poco::JSON::Object* stateError(const char* msg, std::string details = ""); static Poco::JSON::Object* customStateError(const char* state, const char* msg, std::string details = ""); static Poco::JSON::Object* stateSuccess(); + static Poco::JSON::Object* stateWarning(const char* msg, std::string details = ""); }; diff --git a/login_server/src/cpp/JSONInterface/JsonRequestHandlerFactory.cpp b/login_server/src/cpp/JSONInterface/JsonRequestHandlerFactory.cpp index 5db715db3..2edb3d8b1 100644 --- a/login_server/src/cpp/JSONInterface/JsonRequestHandlerFactory.cpp +++ b/login_server/src/cpp/JSONInterface/JsonRequestHandlerFactory.cpp @@ -11,11 +11,13 @@ #include "JsonGetRunningUserTasks.h" #include "JsonGetUsers.h" #include "JsonLoginViaEmailVerificationCode.h" +#include "JsonLogout.h" +#include "JsonSendEmail.h" #include "JsonAdminEmailVerificationResend.h" #include "JsonGetUserInfos.h" #include "JsonUpdateUserInfos.h" #include "JsonUnsecureLogin.h" -#include "JsonLogout.h" + JsonRequestHandlerFactory::JsonRequestHandlerFactory() : mRemoveGETParameters("^/([a-zA-Z0-9_-]*)"), mLogging(Poco::Logger::get("requestLog")) @@ -71,6 +73,9 @@ Poco::Net::HTTPRequestHandler* JsonRequestHandlerFactory::createRequestHandler(c else if (url_first_part == "/loginViaEmailVerificationCode") { return new JsonLoginViaEmailVerificationCode(client_host); } + else if (url_first_part == "/sendEmail") { + return new JsonSendEmail; + } else if (url_first_part == "/logout") { return new JsonLogout(client_host); } diff --git a/login_server/src/cpp/JSONInterface/JsonSendEmail.cpp b/login_server/src/cpp/JSONInterface/JsonSendEmail.cpp new file mode 100644 index 000000000..f4525b3d7 --- /dev/null +++ b/login_server/src/cpp/JSONInterface/JsonSendEmail.cpp @@ -0,0 +1,149 @@ +#include "JsonSendEmail.h" + +#include "Poco/URI.h" + +#include "../SingletonManager/SessionManager.h" +#include "../SingletonManager/EmailManager.h" +#include "../SingletonManager/LanguageManager.h" + +#include "../controller/User.h" +#include "../controller/EmailVerificationCode.h" + +#include "../lib/DataTypeConverter.h" + +Poco::JSON::Object* JsonSendEmail::handle(Poco::Dynamic::Var params) +{ + int session_id = 0; + std::string email; + model::EmailType emailType = model::EMAIL_DEFAULT; + model::table::EmailOptInType emailVerificationCodeType; + std::string emailCustomText; + std::string emailCustomSubject; + std::string languageCode; + + std::string email_verification_code_type_string; + std::string email_type_string; + int email_type_int = 0; + + // if is json object + if (params.type() == typeid(Poco::JSON::Object::Ptr)) { + Poco::JSON::Object::Ptr paramJsonObject = params.extract(); + /// Throws a RangeException if the value does not fit + /// into the result variable. + /// Throws a NotImplementedException if conversion is + /// not available for the given type. + /// Throws InvalidAccessException if Var is empty. + try { + + if (paramJsonObject->has("session_id")) { + paramJsonObject->get("session_id").convert(session_id); + } + + if (paramJsonObject->has("email_custom_text")) { + paramJsonObject->get("email_custom_text").convert(emailCustomText); + } + if (paramJsonObject->has("emailCustomSubject")) { + paramJsonObject->get("emailCustomSubject").convert(emailCustomSubject); + } + if (paramJsonObject->has("language")) { + paramJsonObject->get("language").convert(languageCode); + } + paramJsonObject->get("email").convert(email); + + paramJsonObject->get("email_verification_code_type").convert(email_verification_code_type_string); + auto email_text = paramJsonObject->get("email_text"); + if (email_text.isString()) { + email_text.convert(email_type_string); + } + else if (email_text.isInteger()) { + email_text.convert(email_type_int); + } + + } + catch (Poco::Exception& ex) { + return stateError("json exception", ex.displayText()); + } + } + // convert types into enum + if (email_type_int > 0 && email_type_int < model::EMAIL_MAX) { + emailType = (model::EmailType)email_type_int; + } + else if (email_type_string != "") { + emailType = model::Email::emailType(email_type_string); + } + emailVerificationCodeType = model::table::EmailOptIn::stringToType(email_verification_code_type_string); + if (model::table::EMAIL_OPT_IN_EMPTY == emailVerificationCodeType) { + return stateError("invalid verification code type"); + } + + switch (emailType) { + case model::EMAIL_DEFAULT: + case model::EMAIL_ERROR: + case model::EMAIL_MAX: return stateError("invalid email type"); + } + + if (0 == session_id && + (model::table::EMAIL_OPT_IN_REGISTER == emailVerificationCodeType || model::table::EMAIL_OPT_IN_REGISTER_DIRECT == emailVerificationCodeType) + ){ + return stateError("login needed"); + } + + auto sm = SessionManager::getInstance(); + auto em = EmailManager::getInstance(); + auto lm = LanguageManager::getInstance(); + + Session* session = nullptr; + if (session_id != 0) { + session = sm->getSession(session_id); + if (nullptr == session) { + return stateError("invalid session"); + } + } + + auto receiver_user = controller::User::create(); + if (1 != receiver_user->load(email)) { + return stateError("invalid email"); + } + auto receiver_user_id = receiver_user->getModel()->getID(); + + if (emailVerificationCodeType == model::table::EMAIL_OPT_IN_RESET_PASSWORD) + { + session = sm->getNewSession(); + if (emailType == model::EMAIL_USER_RESET_PASSWORD) { + auto r = session->sendResetPasswordEmail(receiver_user, false); + if (1 == r) { + return stateWarning("email already sended"); + } + else if (2 == r) { + return stateError("email already send less than a hour before"); + } + } + else if (emailType == model::EMAIL_CUSTOM_TEXT) { + auto email_verification_code_object = controller::EmailVerificationCode::loadOrCreate(receiver_user_id, model::table::EMAIL_OPT_IN_RESET_PASSWORD); + auto email = new model::Email(email_verification_code_object, receiver_user, emailCustomText, emailCustomSubject); + em->addEmail(email); + } + else { + return stateError("not supported email type"); + } + return stateSuccess(); + } + else + { + if (session->getNewUser()->getModel()->getRole() != model::table::ROLE_ADMIN) { + return stateError("admin needed"); + } + + auto email_verification_code_object = controller::EmailVerificationCode::loadOrCreate(receiver_user_id, emailVerificationCodeType); + model::Email* email = nullptr; + if (emailType == model::EMAIL_CUSTOM_TEXT) { + email = new model::Email(email_verification_code_object, receiver_user, emailCustomText, emailCustomSubject); + } + else { + email = new model::Email(email_verification_code_object, receiver_user, emailType); + } + em->addEmail(email); + return stateSuccess(); + } + +} \ No newline at end of file diff --git a/login_server/src/cpp/JSONInterface/JsonSendEmail.h b/login_server/src/cpp/JSONInterface/JsonSendEmail.h new file mode 100644 index 000000000..e1c7d6904 --- /dev/null +++ b/login_server/src/cpp/JSONInterface/JsonSendEmail.h @@ -0,0 +1,16 @@ +#ifndef __JSON_SEND_EMAIL_H +#define __JSON_SEND_EMAIL_H + +#include "JsonRequestHandler.h" + +class JsonSendEmail : public JsonRequestHandler +{ +public: + Poco::JSON::Object* handle(Poco::Dynamic::Var params); + +protected: + + +}; + +#endif // __JSON_SEND_EMAIL_H \ No newline at end of file diff --git a/login_server/src/cpp/controller/EmailVerificationCode.cpp b/login_server/src/cpp/controller/EmailVerificationCode.cpp index e144d57eb..375d80f06 100644 --- a/login_server/src/cpp/controller/EmailVerificationCode.cpp +++ b/login_server/src/cpp/controller/EmailVerificationCode.cpp @@ -67,6 +67,24 @@ namespace controller { return nullptr; } + Poco::AutoPtr EmailVerificationCode::loadOrCreate(int user_id, model::table::EmailOptInType type) + { + model::table::EmailOptIn db; + std::vector fields = { "user_id", "email_opt_in_type_id" }; + std::vector field_values = { user_id, (int)type }; + auto results = db.loadFromDB(fields, field_values); + if (results.size() > 0) { + return Poco::AutoPtr(new EmailVerificationCode(new model::table::EmailOptIn(results[0]))); + } + else { + auto result = create(user_id, type); + result->getModel()->insertIntoDB(false); + return result; + } + + return nullptr; + } + std::vector> EmailVerificationCode::load(int user_id) { auto db = new model::table::EmailOptIn(); diff --git a/login_server/src/cpp/controller/EmailVerificationCode.h b/login_server/src/cpp/controller/EmailVerificationCode.h index 77d607de3..825bbc823 100644 --- a/login_server/src/cpp/controller/EmailVerificationCode.h +++ b/login_server/src/cpp/controller/EmailVerificationCode.h @@ -14,6 +14,8 @@ namespace controller { static Poco::AutoPtr create(int user_id, model::table::EmailOptInType type = model::table::EMAIL_OPT_IN_REGISTER); static Poco::AutoPtr create(model::table::EmailOptInType type = model::table::EMAIL_OPT_IN_REGISTER); + //! try to load code, create if not exist and save into db + static Poco::AutoPtr loadOrCreate(int user_id, model::table::EmailOptInType type); static Poco::AutoPtr load(const Poco::UInt64& code); static std::vector> load(int user_id); diff --git a/login_server/src/cpp/model/email/Email.cpp b/login_server/src/cpp/model/email/Email.cpp index 2efa473e4..e70d19729 100644 --- a/login_server/src/cpp/model/email/Email.cpp +++ b/login_server/src/cpp/model/email/Email.cpp @@ -130,6 +130,16 @@ Gradido Login-Server\n\ : mUser(user), mType(type) { + } + Email::Email( + AutoPtr emailVerification, + AutoPtr user, + const std::string& emailCustomText, + const std::string& customSubject + ) + : mEmailVerificationCode(emailVerification), mUser(user), mType(EMAIL_CUSTOM_TEXT), mCustomText(emailCustomText), mCustomSubject(customSubject) + { + } Email::~Email() @@ -177,6 +187,7 @@ Gradido Login-Server\n\ case EMAIL_USER_REGISTER_OLD_ELOPAGE: case EMAIL_ADMIN_USER_VERIFICATION_CODE: case EMAIL_ADMIN_USER_VERIFICATION_CODE_RESEND: + case EMAIL_CUSTOM_TEXT: if (userTableModel.isNull() || mUser->getModel()->getEmail() == "") { addError(new Error(functionName, "no receiver email set for user email verification email")); return false; @@ -204,14 +215,19 @@ Gradido Login-Server\n\ else if (mType == EMAIL_USER_REGISTER_OLD_ELOPAGE) { messageTemplate = EmailText_emailVerificationOldElopageTransaction; } + else if (mType == EMAIL_CUSTOM_TEXT) { + messageTemplate = mCustomText.data(); + mailMessage->setSubject(mCustomSubject); + } content_string = replaceUserNamesAndLink( langCatalog->gettext(messageTemplate), userTableModel->getFirstName(), userTableModel->getLastName(), - mEmailVerificationCode->getLink() + mEmailVerificationCode->getLink(), + mEmailVerificationCode->getModel()->getCode() ); - if (EMAIL_USER_VERIFICATION_CODE_RESEND_AFTER_LONG_TIME == mType) { + if (EMAIL_USER_VERIFICATION_CODE_RESEND_AFTER_LONG_TIME == mType || EMAIL_CUSTOM_TEXT == mType) { content_string = replaceDuration(content_string, mEmailVerificationCode->getAge(), langCatalog); } mailMessage->addContent(new Poco::Net::StringPartSource(content_string, mt.toString())); @@ -234,7 +250,8 @@ Gradido Login-Server\n\ langCatalog->gettext(EmailText_emailResetPassword), userTableModel->getFirstName(), userTableModel->getLastName(), - mEmailVerificationCode->getLink() + mEmailVerificationCode->getLink(), + mEmailVerificationCode->getModel()->getCode() ), mt.toString()) ); break; @@ -264,8 +281,13 @@ Gradido Login-Server\n\ return true; } - std::string Email::replaceUserNamesAndLink(const char* src, const std::string& first_name, const std::string& last_name, const std::string& link) - { + std::string Email::replaceUserNamesAndLink( + const char* src, + const std::string& first_name, + const std::string& last_name, + const std::string& link, + Poco::UInt64 code + ) { std::string result = src; int findCursor = 0; static const char* functionName = "Email::replaceUserNamesAndLink"; @@ -296,6 +318,12 @@ Gradido Login-Server\n\ else { //addError(new Error(functionName, "no email placeholder found")); } + findPos = result.find("[code]", findCursor); + if (findPos != result.npos) { + auto code_string = std::to_string(code); + findCursor = findPos + code_string.size(); + result.replace(findPos, 6, code_string); + } return result; } @@ -376,12 +404,38 @@ Gradido Login-Server\n\ case EMAIL_ADMIN_USER_VERIFICATION_CODE_RESEND: return "email admin user verification code resend"; case EMAIL_USER_RESET_PASSWORD: return "user reset Password"; case EMAIL_ADMIN_RESET_PASSWORD_REQUEST_WITHOUT_MEMORIZED_PASSPHRASE: return "user reset password without memorized passphrase"; - case EMAIL_NOTIFICATION_TRANSACTION_CREATION: return "email notification transaction creation"; - case EMAIL_NOTIFICATION_TRANSACTION_TRANSFER: return "email notification transaction transfer"; case EMAIL_USER_REGISTER_OLD_ELOPAGE: return "user register automatic throw elopage"; + case EMAIL_CUSTOM_TEXT: return "email custom text"; case EMAIL_MAX: return ""; } return ""; } + EmailType Email::emailType(const std::string& emailTypeString) + { + if ("email user verification code" == emailTypeString) { + return EMAIL_USER_VERIFICATION_CODE; + } + else if ("email user verification code resend" == emailTypeString) { + return EMAIL_USER_VERIFICATION_CODE_RESEND; + } + else if ("email user verification code resend after long time" == emailTypeString) { + return EMAIL_USER_VERIFICATION_CODE_RESEND_AFTER_LONG_TIME; + } + else if ("email admin user verification code") { + return EMAIL_ADMIN_USER_VERIFICATION_CODE; + } + else if ("email admin user verification code resend") { + return EMAIL_ADMIN_USER_VERIFICATION_CODE_RESEND; + } + else if ("user reset Password") { + return EMAIL_USER_RESET_PASSWORD; + } + else if ("email custom text") { + return EMAIL_CUSTOM_TEXT; + } + else { + return EMAIL_ERROR; + } + } } \ No newline at end of file diff --git a/login_server/src/cpp/model/email/Email.h b/login_server/src/cpp/model/email/Email.h index d2887a26c..9dbfb1f07 100644 --- a/login_server/src/cpp/model/email/Email.h +++ b/login_server/src/cpp/model/email/Email.h @@ -22,21 +22,20 @@ namespace model { using namespace Poco; - enum EmailType + enum EmailType { - EMAIL_DEFAULT, - EMAIL_ERROR, - EMAIL_USER_VERIFICATION_CODE, - EMAIL_USER_VERIFICATION_CODE_RESEND, - EMAIL_USER_VERIFICATION_CODE_RESEND_AFTER_LONG_TIME, - EMAIL_ADMIN_USER_VERIFICATION_CODE, - EMAIL_ADMIN_USER_VERIFICATION_CODE_RESEND, - EMAIL_USER_RESET_PASSWORD, - EMAIL_ADMIN_RESET_PASSWORD_REQUEST_WITHOUT_MEMORIZED_PASSPHRASE, - EMAIL_NOTIFICATION_TRANSACTION_CREATION, - EMAIL_NOTIFICATION_TRANSACTION_TRANSFER, - EMAIL_USER_REGISTER_OLD_ELOPAGE, - EMAIL_MAX + EMAIL_DEFAULT = 0, + EMAIL_ERROR = 1, + EMAIL_USER_VERIFICATION_CODE = 2, + EMAIL_USER_VERIFICATION_CODE_RESEND = 3, + EMAIL_USER_VERIFICATION_CODE_RESEND_AFTER_LONG_TIME = 4, + EMAIL_ADMIN_USER_VERIFICATION_CODE = 5, + EMAIL_ADMIN_USER_VERIFICATION_CODE_RESEND = 6, + EMAIL_USER_RESET_PASSWORD = 7, + EMAIL_CUSTOM_TEXT = 8, + EMAIL_ADMIN_RESET_PASSWORD_REQUEST_WITHOUT_MEMORIZED_PASSPHRASE = 9, + EMAIL_USER_REGISTER_OLD_ELOPAGE = 10, + EMAIL_MAX = 11 }; class Email: public ErrorList @@ -44,6 +43,7 @@ namespace model { public: Email(AutoPtr emailVerification, AutoPtr user, EmailType type); Email(AutoPtr user, EmailType type); + Email(AutoPtr emailVerification, AutoPtr user, const std::string& emailCustomText, const std::string& customSubject); //! \param errors copy errors into own memory Email(const std::string& errorHtml, EmailType type); ~Email(); @@ -51,6 +51,7 @@ namespace model { static EmailType convertTypeFromInt(int type); inline EmailType getType() { return mType; } static const char* emailTypeString(EmailType type); + static EmailType emailType(const std::string& emailTypeString); inline controller::User* getUser() { if (!mUser.isNull()) return mUser.get(); return nullptr; } virtual bool draft(Net::MailMessage* mailMessage, LanguageCatalog* langCatalog); @@ -58,7 +59,7 @@ namespace model { protected: - std::string replaceUserNamesAndLink(const char* src, const std::string& first_name, const std::string& last_name, const std::string& link); + std::string replaceUserNamesAndLink(const char* src, const std::string& first_name, const std::string& last_name, const std::string& link, Poco::UInt64 code); std::string replaceEmail(const char* src, const std::string& email); std::string replaceAmount(const char* src, Poco::Int64 gradido_cent); std::string replaceDuration(std::string src, Poco::Timespan duration, LanguageCatalog* lang); @@ -69,6 +70,8 @@ namespace model { EmailType mType; std::queue mAdditionalStringPartSrcs; + std::string mCustomText; + std::string mCustomSubject; }; } diff --git a/login_server/src/cpp/model/table/EmailOptIn.cpp b/login_server/src/cpp/model/table/EmailOptIn.cpp index 09a75ee29..3e89516e8 100644 --- a/login_server/src/cpp/model/table/EmailOptIn.cpp +++ b/login_server/src/cpp/model/table/EmailOptIn.cpp @@ -172,6 +172,19 @@ namespace model { default: return ""; } } + EmailOptInType EmailOptIn::stringToType(const std::string& typeString) + { + if (typeString == "register") { + return EMAIL_OPT_IN_REGISTER; + } + else if (typeString == "resetPassword") { + return EMAIL_OPT_IN_RESET_PASSWORD; + } + else if (typeString == "registerDirect") { + return EMAIL_OPT_IN_REGISTER_DIRECT; + } + return EMAIL_OPT_IN_EMPTY; + } } } diff --git a/login_server/src/cpp/model/table/EmailOptIn.h b/login_server/src/cpp/model/table/EmailOptIn.h index 913c84aa1..c7a21d84c 100644 --- a/login_server/src/cpp/model/table/EmailOptIn.h +++ b/login_server/src/cpp/model/table/EmailOptIn.h @@ -44,6 +44,7 @@ namespace model { size_t addResendCountAndUpdate(); static const char* typeToString(EmailOptInType type); + static EmailOptInType stringToType(const std::string& typeString); protected: Poco::Data::Statement _loadFromDB(Poco::Data::Session session, const std::string& fieldName); Poco::Data::Statement _loadIdFromDB(Poco::Data::Session session); From 913db036f051d7be96f651a59abb5c6cd48b4dd9 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 24 Mar 2021 16:37:58 +0100 Subject: [PATCH 2/8] update field name --- docu/login_server.api.md | 5 +++-- login_server/src/cpp/JSONInterface/JsonSendEmail.cpp | 4 ++-- .../src/cpp/JSONInterface/JsonUpdateUserInfos.cpp | 8 ++++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/docu/login_server.api.md b/docu/login_server.api.md index 51e29db96..e244885ba 100644 --- a/docu/login_server.api.md +++ b/docu/login_server.api.md @@ -215,6 +215,7 @@ return ## Send Emails Let send Login-Server Password reset E-Mail to User Can be also used for admin interface to resend email verification code + POST http://localhost/login_api/sendEmail ```json {"email": "max.musterman@gmail.de", "email_text":7, "email_verification_code_type":"resetPassword" } @@ -236,7 +237,7 @@ or: - "email admin user verification code" (5): used if admin trigger sending the email with email verification code - "email admin user verification code resend" (6): used if admin trigger sending the email with email verification code again - "user reset Password" (7): used for reset password email text - - "email custom tex" (8): used if custom email text should be used (than email_custom_text must also be filled) + - "email custom tex" (8): used if custom email text should be used (than email_custom_text and email_custom_subject must also be filled) - email_verification_code_type - resetPassword: for password resets, will be deleted immediately, is a only one use code, can be used without session_id - registerDirect: code generated by register for check email, can only be used by admins for another user @@ -248,7 +249,7 @@ or: - [duration] time span since user has created account (ex.: 10 Days) with largest unit, day is last unit - [link] login-server checkEmail link with email verification code (ex.: http://localhost/account/checkEmail/382738273892983) - [code] email verification code if you like to use your one link -- emailCustomSubject: for custom email the subject +- email_custom_subject (optional): for custom email the subject return ```json diff --git a/login_server/src/cpp/JSONInterface/JsonSendEmail.cpp b/login_server/src/cpp/JSONInterface/JsonSendEmail.cpp index f4525b3d7..aff4b8c77 100644 --- a/login_server/src/cpp/JSONInterface/JsonSendEmail.cpp +++ b/login_server/src/cpp/JSONInterface/JsonSendEmail.cpp @@ -42,8 +42,8 @@ Poco::JSON::Object* JsonSendEmail::handle(Poco::Dynamic::Var params) if (paramJsonObject->has("email_custom_text")) { paramJsonObject->get("email_custom_text").convert(emailCustomText); } - if (paramJsonObject->has("emailCustomSubject")) { - paramJsonObject->get("emailCustomSubject").convert(emailCustomSubject); + if (paramJsonObject->has("email_custom_subject")) { + paramJsonObject->get("email_custom_subject").convert(emailCustomSubject); } if (paramJsonObject->has("language")) { paramJsonObject->get("language").convert(languageCode); diff --git a/login_server/src/cpp/JSONInterface/JsonUpdateUserInfos.cpp b/login_server/src/cpp/JSONInterface/JsonUpdateUserInfos.cpp index 874ed033d..7add880aa 100644 --- a/login_server/src/cpp/JSONInterface/JsonUpdateUserInfos.cpp +++ b/login_server/src/cpp/JSONInterface/JsonUpdateUserInfos.cpp @@ -68,7 +68,7 @@ Poco::JSON::Object* JsonUpdateUserInfos::handle(Poco::Dynamic::Var params) try { - if ( "User.first_name" == name) { + if ( "User.first_name" == name && value.size() > 0) { if (!value.isString()) { jsonErrorsArray.add("User.first_name isn't a string"); } @@ -77,7 +77,7 @@ Poco::JSON::Object* JsonUpdateUserInfos::handle(Poco::Dynamic::Var params) extractet_values++; } } - else if ("User.last_name" == name) { + else if ("User.last_name" == name && value.size() > 0) { if (!value.isString()) { jsonErrorsArray.add("User.last_name isn't a string"); } @@ -103,7 +103,7 @@ Poco::JSON::Object* JsonUpdateUserInfos::handle(Poco::Dynamic::Var params) jsonErrorsArray.add("User.disabled isn't a boolean or integer"); } } - else if ("User.language" == name) { + else if ("User.language" == name && value.size() > 0) { if (!value.isString()) { jsonErrorsArray.add("User.language isn't a string"); } @@ -118,7 +118,7 @@ Poco::JSON::Object* JsonUpdateUserInfos::handle(Poco::Dynamic::Var params) } } } - else if ("User.password" == name && (ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_PASSWORD_REQUESTS) == ServerConfig::UNSECURE_PASSWORD_REQUESTS) { + else if ("User.password" == name && value.size() > 0 && (ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_PASSWORD_REQUESTS) == ServerConfig::UNSECURE_PASSWORD_REQUESTS) { if (!value.isString()) { jsonErrorsArray.add("User.password isn't string"); } From cb0b3ef90e1c1d4c9094ca17134611d6b1ae71b4 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 24 Mar 2021 19:18:39 +0100 Subject: [PATCH 3/8] move link in activation and password reset emails into login-server config --- configs/login_server/grd_login.properties | 2 ++ login_server/src/cpp/ServerConfig.cpp | 5 ++++- login_server/src/cpp/ServerConfig.h | 1 + login_server/src/cpp/controller/EmailVerificationCode.cpp | 6 ++++-- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/configs/login_server/grd_login.properties b/configs/login_server/grd_login.properties index eccde7fb8..2bbde59db 100644 --- a/configs/login_server/grd_login.properties +++ b/configs/login_server/grd_login.properties @@ -22,6 +22,8 @@ loginServer.db.user = root loginServer.db.password = loginServer.db.port = 3306 +frontend.checkEmailPath = http://localhost/account/checkEmail + email.disable = true #email.username = #email.sender = diff --git a/login_server/src/cpp/ServerConfig.cpp b/login_server/src/cpp/ServerConfig.cpp index f319ea460..89b5e1a2f 100644 --- a/login_server/src/cpp/ServerConfig.cpp +++ b/login_server/src/cpp/ServerConfig.cpp @@ -48,6 +48,7 @@ namespace ServerConfig { Languages g_default_locale; std::string g_php_serverPath; std::string g_php_serverHost; + std::string g_frontend_checkEmailPath; int g_phpServerPort; Poco::Mutex g_TimeMutex; int g_FakeLoginSleepTime = 820; @@ -210,7 +211,7 @@ namespace ServerConfig { DISASM_FALSERET; g_SessionTimeout = cfg.getInt("session.timeout", SESSION_TIMEOUT_DEFAULT); - g_serverPath = cfg.getString("loginServer.path", ""); + g_serverPath = cfg.getString("loginServer.path", "http://localhost/account"); replaceZeroIPWithLocalhostIP(g_serverPath); g_default_locale = LanguageManager::languageFromString(cfg.getString("loginServer.default_locale")); g_serverPort = cfg.getInt("loginServer.port", 0); @@ -231,6 +232,8 @@ namespace ServerConfig { if ("" != app_secret_string) { g_CryptoAppSecret = DataTypeConverter::hexToBin(app_secret_string); } + std::string defaultCheckEmailPath = g_serverPath + "/checkEmail"; + g_frontend_checkEmailPath = cfg.getString("frontend.checkEmailPath", defaultCheckEmailPath); //g_CryptoAppSecret g_gRPCRelayServerFullURL = cfg.getString("grpc.server", ""); diff --git a/login_server/src/cpp/ServerConfig.h b/login_server/src/cpp/ServerConfig.h index aef4ea28d..f74f506a1 100644 --- a/login_server/src/cpp/ServerConfig.h +++ b/login_server/src/cpp/ServerConfig.h @@ -66,6 +66,7 @@ namespace ServerConfig { extern Languages g_default_locale; extern std::string g_php_serverPath; extern std::string g_php_serverHost; + extern std::string g_frontend_checkEmailPath; extern int g_phpServerPort; extern Poco::Mutex g_TimeMutex; extern int g_FakeLoginSleepTime; diff --git a/login_server/src/cpp/controller/EmailVerificationCode.cpp b/login_server/src/cpp/controller/EmailVerificationCode.cpp index 375d80f06..45c38bf70 100644 --- a/login_server/src/cpp/controller/EmailVerificationCode.cpp +++ b/login_server/src/cpp/controller/EmailVerificationCode.cpp @@ -120,8 +120,10 @@ namespace controller { std::string EmailVerificationCode::getLink() { - std::string link = ServerConfig::g_serverPath; - link += "/checkEmail/"; + std::string link = ServerConfig::g_frontend_checkEmailPath; + if (link.data()[link.size() - 1] != '/') { + link += '/'; + } link += std::to_string(getModel()->getCode()); return link; } From c836bde15896109456e0b6beee48c20fb9561929 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 29 Mar 2021 19:17:41 +0200 Subject: [PATCH 4/8] Update frontend/src/apis/loginAPI.js Co-authored-by: Ulf Gebhardt --- frontend/src/apis/loginAPI.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/apis/loginAPI.js b/frontend/src/apis/loginAPI.js index b9e433730..3b8c853a5 100644 --- a/frontend/src/apis/loginAPI.js +++ b/frontend/src/apis/loginAPI.js @@ -29,9 +29,9 @@ const apiPost = async (url, payload) => { if(result.status !== 200){ throw new Error('HTTP Status Error '+result.status) } - if(result.data.state === 'warning') { - return { success: true, result: error } - } + if(result.data.state === 'warning') { + return { success: true, result: error } + } if(result.data.state !== 'success'){ throw new Error(result.data.msg) } @@ -66,4 +66,4 @@ const loginAPI = { }, } -export default loginAPI \ No newline at end of file +export default loginAPI From 21309eada52122c40a49af9ff1ec5c418c655249 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 29 Mar 2021 19:17:49 +0200 Subject: [PATCH 5/8] Update login_server/src/cpp/model/email/Email.h Co-authored-by: Ulf Gebhardt --- login_server/src/cpp/model/email/Email.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/login_server/src/cpp/model/email/Email.h b/login_server/src/cpp/model/email/Email.h index 9dbfb1f07..004854ddf 100644 --- a/login_server/src/cpp/model/email/Email.h +++ b/login_server/src/cpp/model/email/Email.h @@ -70,9 +70,9 @@ namespace model { EmailType mType; std::queue mAdditionalStringPartSrcs; - std::string mCustomText; + std::string mCustomText; std::string mCustomSubject; }; } -#endif //GRADIDO_LOGIN_SERVER_MODEL_EMAIL_INCLUDE \ No newline at end of file +#endif //GRADIDO_LOGIN_SERVER_MODEL_EMAIL_INCLUDE From ad0f11d996926f5d58d1fc174c18501f03356162 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 29 Mar 2021 19:18:03 +0200 Subject: [PATCH 6/8] Update configs/login_server/grd_login.properties Co-authored-by: Ulf Gebhardt --- configs/login_server/grd_login.properties | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/configs/login_server/grd_login.properties b/configs/login_server/grd_login.properties index 2bbde59db..1674e22de 100644 --- a/configs/login_server/grd_login.properties +++ b/configs/login_server/grd_login.properties @@ -22,9 +22,10 @@ loginServer.db.user = root loginServer.db.password = loginServer.db.port = 3306 -frontend.checkEmailPath = http://localhost/account/checkEmail +frontend.checkEmailPath = http://localhost/account/checkEmail email.disable = true + #email.username = #email.sender = #email.admin_receiver = @@ -50,4 +51,4 @@ unsecure.allow_passwort_via_json_request = 1 unsecure.allow_auto_sign_transactions = 1 unsecure.allow_cors_all = 1 # default disable, passwords must contain a number, a lower character, a high character, special character, and be at least 8 characters long -unsecure.allow_all_passwords = 1 \ No newline at end of file +unsecure.allow_all_passwords = 1 From 28c0bc0966b8b46e87d2dff2a4e273ff49ff5a0c Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 30 Mar 2021 13:44:26 +0200 Subject: [PATCH 7/8] fix linting errors --- frontend/src/apis/loginAPI.js | 22 ++++++++++------------ gn | 2 +- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/frontend/src/apis/loginAPI.js b/frontend/src/apis/loginAPI.js index 032d88a22..d422fe3ff 100644 --- a/frontend/src/apis/loginAPI.js +++ b/frontend/src/apis/loginAPI.js @@ -9,17 +9,16 @@ const EMAIL_TYPE = { const apiGet = async (url) => { try { - const result = await axios.get(url); - if(result.status !== 200){ - throw new Error('HTTP Status Error '+result.status) + const result = await axios.get(url) + if (result.status !== 200) { + throw new Error('HTTP Status Error ' + result.status) } - - if(result.data.state !== 'success'){ + if (result.data.state !== 'success') { throw new Error(result.data.msg) } return { success: true, result } - } catch(error){ - return { success: false, result: error} + } catch (error) { + return { success: false, result: error } } } @@ -29,11 +28,10 @@ const apiPost = async (url, payload) => { if (result.status !== 200) { throw new Error('HTTP Status Error ' + result.status) } - if(result.data.state === 'warning') { - return { success: true, result: error } - } - if(result.data.state !== 'success'){ - + if (result.data.state === 'warning') { + return { success: true, result: error } + } + if (result.data.state !== 'success') { throw new Error(result.data.msg) } return { success: true, result } diff --git a/gn b/gn index a61871987..5437e2f88 160000 --- a/gn +++ b/gn @@ -1 +1 @@ -Subproject commit a61871987261614102b11ed58791081be1954d3c +Subproject commit 5437e2f882c54efe4f501f7cd0d97f53806d0b74 From 0e76f5a8a83a9d8e67889c0a3b864b659b7b19cb Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 31 Mar 2021 12:25:24 +0200 Subject: [PATCH 8/8] remove \r from config --- configs/login_server/grd_login.properties | 108 +++++++++++----------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/configs/login_server/grd_login.properties b/configs/login_server/grd_login.properties index 1674e22de..111e0e00d 100644 --- a/configs/login_server/grd_login.properties +++ b/configs/login_server/grd_login.properties @@ -1,54 +1,54 @@ -HTTPServer.port = 1200 -JSONServer.port = 1201 -Gradido.group_id = 1 - -crypto.server_admin_public = f909a866baec97c5460b8d7a93b72d3d4d20cc45d9f15d78bd83944eb9286b7f -crypto.server_key = a51ef8ac7ef1abf162fb7a65261acd7a -# TODO auto-generate in docker build step -crypto.app_secret = 21ffbbc616fe - -# Server admin Passphrase -# nerve execute merit pool talk hockey basic win cargo spin disagree ethics swear price purchase say clutch decrease slow half forest reform cheese able -# - -phpServer.url = http://localhost/ -phpServer.host = nginx - -loginServer.path = http://localhost/account -loginServer.default_locale = de -loginServer.db.host = mariadb -loginServer.db.name = gradido_login -loginServer.db.user = root -loginServer.db.password = -loginServer.db.port = 3306 - -frontend.checkEmailPath = http://localhost/account/checkEmail - -email.disable = true - -#email.username = -#email.sender = -#email.admin_receiver = -#email.password = -#email.smtp.url = -#email.smtp.port = - -# binary is default, for debugging also json is possible -#hedera.consensus.message_format = json -# TESTNET or MAINNET, TESTNET is default -hedera.nettype = TESTNET - -# server setup types: test, staging or production -ServerSetupType=test - - -# Session timeout in minutes -# -session.timeout = 15 - -# Disabling security features for faster develop and testing -unsecure.allow_passwort_via_json_request = 1 -unsecure.allow_auto_sign_transactions = 1 -unsecure.allow_cors_all = 1 -# default disable, passwords must contain a number, a lower character, a high character, special character, and be at least 8 characters long -unsecure.allow_all_passwords = 1 +HTTPServer.port = 1200 +JSONServer.port = 1201 +Gradido.group_id = 1 + +crypto.server_admin_public = f909a866baec97c5460b8d7a93b72d3d4d20cc45d9f15d78bd83944eb9286b7f +crypto.server_key = a51ef8ac7ef1abf162fb7a65261acd7a +# TODO auto-generate in docker build step +crypto.app_secret = 21ffbbc616fe + +# Server admin Passphrase +# nerve execute merit pool talk hockey basic win cargo spin disagree ethics swear price purchase say clutch decrease slow half forest reform cheese able +# + +phpServer.url = http://localhost/ +phpServer.host = nginx + +loginServer.path = http://localhost/account +loginServer.default_locale = de +loginServer.db.host = mariadb +loginServer.db.name = gradido_login +loginServer.db.user = root +loginServer.db.password = +loginServer.db.port = 3306 + +frontend.checkEmailPath = http://localhost/account/checkEmail + +email.disable = true + +#email.username = +#email.sender = +#email.admin_receiver = +#email.password = +#email.smtp.url = +#email.smtp.port = + +# binary is default, for debugging also json is possible +#hedera.consensus.message_format = json +# TESTNET or MAINNET, TESTNET is default +hedera.nettype = TESTNET + +# server setup types: test, staging or production +ServerSetupType=test + + +# Session timeout in minutes +# +session.timeout = 15 + +# Disabling security features for faster develop and testing +unsecure.allow_passwort_via_json_request = 1 +unsecure.allow_auto_sign_transactions = 1 +unsecure.allow_cors_all = 1 +# default disable, passwords must contain a number, a lower character, a high character, special character, and be at least 8 characters long +unsecure.allow_all_passwords = 1