diff --git a/configs/login_server/grd_login.properties b/configs/login_server/grd_login.properties index 853dfe6af..00bd8df52 100644 --- a/configs/login_server/grd_login.properties +++ b/configs/login_server/grd_login.properties @@ -22,7 +22,10 @@ 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 = @@ -48,4 +51,6 @@ 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 + diff --git a/docu/login_server.api.md b/docu/login_server.api.md index fadd2de60..c221b03e8 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,10 +208,70 @@ 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 + - "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 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 + - 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 +- email_custom_subject (optional): 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 + ## Check Running Transactions / password encryption Check if transactions on login-server for user are processed diff --git a/frontend/src/apis/loginAPI.js b/frontend/src/apis/loginAPI.js index e10a19c3f..d422fe3ff 100644 --- a/frontend/src/apis/loginAPI.js +++ b/frontend/src/apis/loginAPI.js @@ -7,12 +7,30 @@ 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 3f9bd35b3..2cbf7f634 100644 --- a/login_server/src/cpp/JSONInterface/JsonRequestHandler.cpp +++ b/login_server/src/cpp/JSONInterface/JsonRequestHandler.cpp @@ -1,141 +1,153 @@ -#include "JsonRequestHandler.h" - -#include "Poco/Net/HTTPServerRequest.h" -#include "Poco/Net/HTTPServerResponse.h" - -#include "Poco/URI.h" -#include "Poco/DeflatingStream.h" - -#include "Poco/JSON/Parser.h" - -#include "../ServerConfig.h" - -#include "../lib/DataTypeConverter.h" -#include "../SingletonManager/SessionManager.h" - - -void JsonRequestHandler::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response) -{ - - response.setChunkedTransferEncoding(false); - response.setContentType("application/json"); - if (ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_CORS_ALL) { - response.set("Access-Control-Allow-Origin", "*"); - response.set("Access-Control-Allow-Headers", "Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers"); - } - //bool _compressResponse(request.hasToken("Accept-Encoding", "gzip")); - //if (_compressResponse) response.set("Content-Encoding", "gzip"); - - std::ostream& responseStream = response.send(); - //Poco::DeflatingOutputStream _gzipStream(_responseStream, Poco::DeflatingStreamBuf::STREAM_GZIP, 1); - //std::ostream& responseStream = _compressResponse ? _gzipStream : _responseStream; - - auto method = request.getMethod(); - std::istream& request_stream = request.stream(); - Poco::JSON::Object* json_result = nullptr; - if (method == "POST" || method == "PUT") { - // extract parameter from request - Poco::Dynamic::Var parsedResult = parseJsonWithErrorPrintFile(request_stream); - - if (parsedResult.size() != 0) { - json_result = handle(parsedResult); - } - else { - json_result = stateError("empty body"); - } - - } - else if(method == "GET") { - Poco::URI uri(request.getURI()); - auto queryParameters = uri.getQueryParameters(); - json_result = handle(queryParameters); - } - - if (json_result) { - if (!json_result->isNull("session_id")) { - int session_id = 0; - try { - json_result->get("session_id").convert(session_id); - } - catch (Poco::Exception& e) { - ErrorList erros; - erros.addError(new Error("json request", "invalid session_id")); - erros.sendErrorsAsEmail(); - } - if (session_id) { - auto session = SessionManager::getInstance()->getSession(session_id); - response.addCookie(session->getLoginCookie()); - } - } - json_result->stringify(responseStream); - delete json_result; - } - - //if (_compressResponse) _gzipStream.close(); -} - - -Poco::Dynamic::Var JsonRequestHandler::parseJsonWithErrorPrintFile(std::istream& request_stream, ErrorList* errorHandler /* = nullptr*/, const char* functionName /* = nullptr*/) -{ - // debugging answer - - std::stringstream responseStringStream; - for (std::string line; std::getline(request_stream, line); ) { - responseStringStream << line << std::endl; - } - - // extract parameter from request - Poco::JSON::Parser jsonParser; - Poco::Dynamic::Var parsedJson; - try { - parsedJson = jsonParser.parse(responseStringStream.str()); - - return parsedJson; - } - catch (Poco::Exception& ex) { - if (errorHandler) { - errorHandler->addError(new ParamError(functionName, "error parsing request answer", ex.displayText().data())); - errorHandler->sendErrorsAsEmail(responseStringStream.str()); - } - std::string dateTimeString = Poco::DateTimeFormatter::format(Poco::DateTime(), "%d_%m_%yT%H_%M_%S"); - std::string filename = dateTimeString + "_response.html"; - FILE* f = fopen(filename.data(), "wt"); - if (f) { - std::string responseString = responseStringStream.str(); - fwrite(responseString.data(), 1, responseString.size(), f); - fclose(f); - } - return Poco::Dynamic::Var(); - } - return Poco::Dynamic::Var(); -} - -Poco::JSON::Object* JsonRequestHandler::stateError(const char* msg, std::string details) -{ - Poco::JSON::Object* result = new Poco::JSON::Object; - result->set("state", "error"); - result->set("msg", msg); - if (details != "") { - result->set("details", details); - } - return result; -} - -Poco::JSON::Object* JsonRequestHandler::stateSuccess() -{ - Poco::JSON::Object* result = new Poco::JSON::Object; - result->set("state", "success"); - return result; -} - -Poco::JSON::Object* JsonRequestHandler::customStateError(const char* state, const char* msg, std::string details/* = ""*/) -{ - Poco::JSON::Object* result = new Poco::JSON::Object; - result->set("state", state); - result->set("msg", msg); - if (details != "") { - result->set("details", details); - } - return result; -} - +#include "JsonRequestHandler.h" + +#include "Poco/Net/HTTPServerRequest.h" +#include "Poco/Net/HTTPServerResponse.h" + +#include "Poco/URI.h" +#include "Poco/DeflatingStream.h" + +#include "Poco/JSON/Parser.h" + +#include "../ServerConfig.h" + +#include "../lib/DataTypeConverter.h" +#include "../SingletonManager/SessionManager.h" + + +void JsonRequestHandler::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response) +{ + + response.setChunkedTransferEncoding(false); + response.setContentType("application/json"); + if (ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_CORS_ALL) { + response.set("Access-Control-Allow-Origin", "*"); + response.set("Access-Control-Allow-Headers", "Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers"); + } + //bool _compressResponse(request.hasToken("Accept-Encoding", "gzip")); + //if (_compressResponse) response.set("Content-Encoding", "gzip"); + + std::ostream& responseStream = response.send(); + //Poco::DeflatingOutputStream _gzipStream(_responseStream, Poco::DeflatingStreamBuf::STREAM_GZIP, 1); + //std::ostream& responseStream = _compressResponse ? _gzipStream : _responseStream; + + auto method = request.getMethod(); + std::istream& request_stream = request.stream(); + Poco::JSON::Object* json_result = nullptr; + if (method == "POST" || method == "PUT") { + // extract parameter from request + Poco::Dynamic::Var parsedResult = parseJsonWithErrorPrintFile(request_stream); + + if (parsedResult.size() != 0) { + json_result = handle(parsedResult); + } + else { + json_result = stateError("empty body"); + } + + } + else if(method == "GET") { + Poco::URI uri(request.getURI()); + auto queryParameters = uri.getQueryParameters(); + json_result = handle(queryParameters); + } + + if (json_result) { + if (!json_result->isNull("session_id")) { + int session_id = 0; + try { + json_result->get("session_id").convert(session_id); + } + catch (Poco::Exception& e) { + ErrorList erros; + erros.addError(new Error("json request", "invalid session_id")); + erros.sendErrorsAsEmail(); + } + if (session_id) { + auto session = SessionManager::getInstance()->getSession(session_id); + response.addCookie(session->getLoginCookie()); + } + } + json_result->stringify(responseStream); + delete json_result; + } + + //if (_compressResponse) _gzipStream.close(); +} + + +Poco::Dynamic::Var JsonRequestHandler::parseJsonWithErrorPrintFile(std::istream& request_stream, ErrorList* errorHandler /* = nullptr*/, const char* functionName /* = nullptr*/) +{ + // debugging answer + + std::stringstream responseStringStream; + for (std::string line; std::getline(request_stream, line); ) { + responseStringStream << line << std::endl; + } + + // extract parameter from request + Poco::JSON::Parser jsonParser; + Poco::Dynamic::Var parsedJson; + try { + parsedJson = jsonParser.parse(responseStringStream.str()); + + return parsedJson; + } + catch (Poco::Exception& ex) { + if (errorHandler) { + errorHandler->addError(new ParamError(functionName, "error parsing request answer", ex.displayText().data())); + errorHandler->sendErrorsAsEmail(responseStringStream.str()); + } + std::string dateTimeString = Poco::DateTimeFormatter::format(Poco::DateTime(), "%d_%m_%yT%H_%M_%S"); + std::string filename = dateTimeString + "_response.html"; + FILE* f = fopen(filename.data(), "wt"); + if (f) { + std::string responseString = responseStringStream.str(); + fwrite(responseString.data(), 1, responseString.size(), f); + fclose(f); + } + return Poco::Dynamic::Var(); + } + return Poco::Dynamic::Var(); +} + +Poco::JSON::Object* JsonRequestHandler::stateError(const char* msg, std::string details) +{ + Poco::JSON::Object* result = new Poco::JSON::Object; + result->set("state", "error"); + result->set("msg", msg); + if (details != "") { + result->set("details", details); + } + return result; +} + +Poco::JSON::Object* JsonRequestHandler::stateSuccess() +{ + Poco::JSON::Object* result = new Poco::JSON::Object; + result->set("state", "success"); + return result; +} + +Poco::JSON::Object* JsonRequestHandler::customStateError(const char* state, const char* msg, std::string details/* = ""*/) +{ + Poco::JSON::Object* result = new Poco::JSON::Object; + result->set("state", state); + result->set("msg", msg); + if (details != "") { + result->set("details", details); + } + 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 d6eff52f9..534a9c371 100644 --- a/login_server/src/cpp/JSONInterface/JsonRequestHandlerFactory.cpp +++ b/login_server/src/cpp/JSONInterface/JsonRequestHandlerFactory.cpp @@ -1,82 +1,90 @@ -#include "JsonRequestHandlerFactory.h" - -#include "Poco/Net/HTTPServerRequest.h" - -#include "../SingletonManager/SessionManager.h" - -#include "JsonAdminEmailVerificationResend.h" -#include "JsonCheckSessionState.h" -#include "JsonCreateUser.h" -#include "JsonGetLogin.h" -#include "JsonUnknown.h" -#include "JsonTransaction.h" -#include "JsonGetRunningUserTasks.h" -#include "JsonGetUsers.h" -#include "JsonLoginViaEmailVerificationCode.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")) -{ -} - -Poco::Net::HTTPRequestHandler* JsonRequestHandlerFactory::createRequestHandler(const Poco::Net::HTTPServerRequest& request) -{ - std::string uri = request.getURI(); - std::string url_first_part; - std::stringstream logStream; - - mRemoveGETParameters.extract(uri, url_first_part); - - std::string dateTimeString = Poco::DateTimeFormatter::format(Poco::DateTime(), "%d.%m.%y %H:%M:%S"); - logStream << dateTimeString << " call " << uri; - - mLogging.information(logStream.str()); - - auto client_host = request.clientAddress().host(); - //auto client_ip = request.clientAddress(); - // X-Real-IP forwarded ip from nginx config - auto client_host_string = request.get("X-Real-IP", client_host.toString()); - client_host = Poco::Net::IPAddress(client_host_string); - - if (url_first_part == "/login") { - return new JsonGetLogin; - } - else if (url_first_part == "/checkSessionState") { - return new JsonCheckSessionState; - } - else if (url_first_part == "/checkTransaction") { - return new JsonTransaction; - } - else if (url_first_part == "/getRunningUserTasks") { - return new JsonGetRunningUserTasks; - } - else if (url_first_part == "/getUsers") { - return new JsonGetUsers; - } - else if (url_first_part == "/createUser") { - return new JsonCreateUser(client_host); - } - else if (url_first_part == "/adminEmailVerificationResend") { - return new JsonAdminEmailVerificationResend; - } - else if (url_first_part == "/getUserInfos") { - return new JsonGetUserInfos; - } - else if (url_first_part == "/updateUserInfos") { - return new JsonUpdateUserInfos; - } - else if (url_first_part == "/unsecureLogin" && (ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_PASSWORD_REQUESTS)) { - return new JsonUnsecureLogin(client_host); - } - else if (url_first_part == "/loginViaEmailVerificationCode") { - return new JsonLoginViaEmailVerificationCode(client_host); - } - else if (url_first_part == "/logout") { - return new JsonLogout(client_host); - } - return new JsonUnknown; -} + +#include "JsonRequestHandlerFactory.h" + +#include "Poco/Net/HTTPServerRequest.h" + +#include "../SingletonManager/SessionManager.h" + +#include "JsonAdminEmailVerificationResend.h" +#include "JsonCheckSessionState.h" +#include "JsonCreateUser.h" +#include "JsonGetLogin.h" +#include "JsonUnknown.h" +#include "JsonTransaction.h" +#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" + + +JsonRequestHandlerFactory::JsonRequestHandlerFactory() + : mRemoveGETParameters("^/([a-zA-Z0-9_-]*)"), mLogging(Poco::Logger::get("requestLog")) +{ +} + +Poco::Net::HTTPRequestHandler* JsonRequestHandlerFactory::createRequestHandler(const Poco::Net::HTTPServerRequest& request) +{ + std::string uri = request.getURI(); + std::string url_first_part; + std::stringstream logStream; + + mRemoveGETParameters.extract(uri, url_first_part); + + std::string dateTimeString = Poco::DateTimeFormatter::format(Poco::DateTime(), "%d.%m.%y %H:%M:%S"); + logStream << dateTimeString << " call " << uri; + + mLogging.information(logStream.str()); + + auto client_host = request.clientAddress().host(); + //auto client_ip = request.clientAddress(); + // X-Real-IP forwarded ip from nginx config + auto client_host_string = request.get("X-Real-IP", client_host.toString()); + client_host = Poco::Net::IPAddress(client_host_string); + + if (url_first_part == "/login") { + return new JsonGetLogin; + } + else if (url_first_part == "/checkSessionState") { + return new JsonCheckSessionState; + } + else if (url_first_part == "/checkTransaction") { + return new JsonTransaction; + } + else if (url_first_part == "/getRunningUserTasks") { + return new JsonGetRunningUserTasks; + } + else if (url_first_part == "/getUsers") { + return new JsonGetUsers; + } + else if (url_first_part == "/createUser") { + return new JsonCreateUser(client_host); + } + else if (url_first_part == "/adminEmailVerificationResend") { + return new JsonAdminEmailVerificationResend; + } + else if (url_first_part == "/getUserInfos") { + return new JsonGetUserInfos; + } + else if (url_first_part == "/updateUserInfos") { + return new JsonUpdateUserInfos; + } + else if (url_first_part == "/unsecureLogin" && (ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_PASSWORD_REQUESTS)) { + return new JsonUnsecureLogin(client_host); + } + 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); + } + return new JsonUnknown; +} + diff --git a/login_server/src/cpp/JSONInterface/JsonSendEmail.cpp b/login_server/src/cpp/JSONInterface/JsonSendEmail.cpp new file mode 100644 index 000000000..aff4b8c77 --- /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("email_custom_subject")) { + paramJsonObject->get("email_custom_subject").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/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"); } diff --git a/login_server/src/cpp/ServerConfig.cpp b/login_server/src/cpp/ServerConfig.cpp index d37480a80..89b5e1a2f 100644 --- a/login_server/src/cpp/ServerConfig.cpp +++ b/login_server/src/cpp/ServerConfig.cpp @@ -1,349 +1,352 @@ -#include "ServerConfig.h" -#include "Crypto/mnemonic_german.h" -#include "Crypto/mnemonic_german2.h" -#include "Crypto/mnemonic_bip0039.h" -#include "Crypto/DRRandom.h" -#include "lib/DataTypeConverter.h" -#include "sodium.h" - - -#include "Poco/Net/SSLManager.h" -#include "Poco/Net/KeyConsoleHandler.h" -#include "Poco/Net/RejectCertificateHandler.h" -#include "Poco/Net/DNS.h" -#include "Poco/SharedPtr.h" - -#include "Poco/Mutex.h" -#include "Poco/Path.h" -#include "Poco/FileStream.h" -#include "Poco/LocalDateTime.h" -#include "Poco/DateTimeFormat.h" -#include "Poco/DateTimeFormatter.h" - - -using Poco::Net::SSLManager; -using Poco::Net::Context; -using Poco::Net::KeyConsoleHandler; -using Poco::Net::PrivateKeyPassphraseHandler; -using Poco::Net::InvalidCertificateHandler; -using Poco::Net::RejectCertificateHandler; -using Poco::SharedPtr; - -namespace ServerConfig { - -#define SESSION_TIMEOUT_DEFAULT 10 - - Mnemonic g_Mnemonic_WordLists[MNEMONIC_MAX]; - ObfusArray* g_ServerCryptoKey = nullptr; - ObfusArray* g_ServerKeySeed = nullptr; -// std::string g_ServerAdminPublic; - UniLib::controller::CPUSheduler* g_CPUScheduler = nullptr; - UniLib::controller::CPUSheduler* g_CryptoCPUScheduler = nullptr; - Context::Ptr g_SSL_CLient_Context = nullptr; - Poco::Util::Timer g_CronJobsTimer; - EmailAccount g_EmailAccount; - int g_SessionTimeout = SESSION_TIMEOUT_DEFAULT; - std::string g_serverPath; - int g_serverPort = 0; - Languages g_default_locale; - std::string g_php_serverPath; - std::string g_php_serverHost; - int g_phpServerPort; - Poco::Mutex g_TimeMutex; - int g_FakeLoginSleepTime = 820; - std::string g_versionString = ""; - bool g_disableEmail = false; - ServerSetupType g_ServerSetupType = SERVER_TYPE_PRODUCTION; - std::string g_gRPCRelayServerFullURL; - MemoryBin* g_CryptoAppSecret = nullptr; - AllowUnsecure g_AllowUnsecureFlags = NOT_UNSECURE; - -#ifdef __linux__ -#include -#include -#include -#include -#include -#include -#endif //#ifdef __linux__ - - std::string getHostIpString() - { -#ifdef __linux__ - struct ifaddrs * ifAddrStruct = NULL; - struct ifaddrs * ifa = NULL; - void * tmpAddrPtr = NULL; - - getifaddrs(&ifAddrStruct); - std::string ipAddressString; - - for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) { - if (!ifa->ifa_addr) { - continue; - } - if (ifa->ifa_addr->sa_family == AF_INET) { // check it is IP4 - // is a valid IP4 Address - tmpAddrPtr = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; - char addressBuffer[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN); - ipAddressString = addressBuffer; - printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); - } - else if (ifa->ifa_addr->sa_family == AF_INET6) { // check it is IP6 - // is a valid IP6 Address - tmpAddrPtr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; - char addressBuffer[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN); - printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); - } - } - if (ifAddrStruct != NULL) freeifaddrs(ifAddrStruct); - return ipAddressString; -#else //__linux__ - std::string ipAddressString = ""; - auto host = Poco::Net::DNS::thisHost(); - for (auto it = host.addresses().begin(); it != host.addresses().end(); it++) { - auto ipAddress = *it; - if (!ipAddress.isIPv4Compatible() && !ipAddress.isIPv4Mapped()) { - continue; - } - if (ipAddress.isLoopback()) { - continue; - } - ipAddressString = ipAddress.toString(); - //isIPv4Compatible - //!isLoopback - //printf("ipaddress: %s\n", ipAddressString.data()); - break; - //break; - } - return ipAddressString; -#endif // __linux__ - } - - bool replaceZeroIPWithLocalhostIP(std::string& url) - { - auto pos = url.find("0.0.0.0", 0); - if (pos != std::string::npos) { - std::string ipAddressString = getHostIpString(); - if ("" != ipAddressString) { - url.replace(pos, 7, ipAddressString); - } - } - - //printf("ipaddress: %s\n", ipAddress.data()); - - return true; - } - - ServerSetupType getServerSetupTypeFromString(const std::string& serverSetupTypeString) { - if ("test" == serverSetupTypeString) { - return SERVER_TYPE_TEST; - } - if ("staging" == serverSetupTypeString) { - return SERVER_TYPE_STAGING; - } - if ("production" == serverSetupTypeString) { - return SERVER_TYPE_PRODUCTION; - } - return SERVER_TYPE_PRODUCTION; - } - - - bool loadMnemonicWordLists() - { - for (int i = 0; i < MNEMONIC_MAX; i++) { - int iResult = 0; - switch (i) { - case MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER: - iResult = g_Mnemonic_WordLists[i].init(populate_mnemonic_german, g_mnemonic_german_original_size, g_mnemonic_german_compressed_size); - if (iResult) { - printf("[%s] error init german mnemonic set, error nr: %d\n", __FUNCTION__, iResult); - return false; - } - g_Mnemonic_WordLists[i].printToFile("de_words.txt"); - break; - case MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER_FIXED_CASES: - iResult = g_Mnemonic_WordLists[i].init(populate_mnemonic_german2, g_mnemonic_german2_original_size, g_mnemonic_german2_compressed_size); - if (iResult) { - printf("[%s] error init german mnemonic set 2, error nr: %d\n", __FUNCTION__, iResult); - return false; - } - g_Mnemonic_WordLists[i].printToFile("de_words2.txt"); - break; - case MNEMONIC_BIP0039_SORTED_ORDER: - iResult = g_Mnemonic_WordLists[i].init(populate_mnemonic_bip0039, g_mnemonic_bip0039_original_size, g_mnemonic_bip0039_compressed_size); - if (iResult) { - printf("[%s] error init bip0039 mnemonic set, error nr: %d\n", __FUNCTION__, iResult); - return false; - } - //g_Mnemonic_WordLists[i].printToFile("en_words.txt"); - break; - default: printf("[%s] unknown MnemonicType\n", __FUNCTION__); return false; - } - } - return true; - } - - bool initServerCrypto(const Poco::Util::LayeredConfiguration& cfg) - { - auto serverKey = cfg.getString("crypto.server_key"); - unsigned char key[crypto_shorthash_KEYBYTES]; - size_t realBinSize = 0; - NULLPAD_10; - if (sodium_hex2bin(key, crypto_shorthash_KEYBYTES, serverKey.data(), serverKey.size(), nullptr, &realBinSize, nullptr)) { - printf("[%s] serverKey isn't valid hex: %s\n", __FUNCTION__, serverKey.data()); - return false; - } - if (realBinSize != crypto_shorthash_KEYBYTES) { - printf("[%s] serverKey hasn't valid size, expecting: %u, get: %lu\n", - __FUNCTION__, crypto_shorthash_KEYBYTES, realBinSize); - return false; - } - g_ServerCryptoKey = new ObfusArray(realBinSize, key); - g_ServerKeySeed = new ObfusArray(9*8); - Poco::Int64 i1 = randombytes_random(); - Poco::Int64 i2 = randombytes_random(); - g_ServerKeySeed->put(0, i1 | (i2 << 8)); - - //g_ServerAdminPublic = cfg.getString("crypto.server_admin_public"); - - DISASM_FALSERET; - g_SessionTimeout = cfg.getInt("session.timeout", SESSION_TIMEOUT_DEFAULT); - g_serverPath = cfg.getString("loginServer.path", ""); - replaceZeroIPWithLocalhostIP(g_serverPath); - g_default_locale = LanguageManager::languageFromString(cfg.getString("loginServer.default_locale")); - g_serverPort = cfg.getInt("loginServer.port", 0); - g_phpServerPort = cfg.getInt("phpServer.port", 0); - // replace 0.0.0.0 with actual server ip - - g_php_serverPath = cfg.getString("phpServer.url", ""); - replaceZeroIPWithLocalhostIP(g_php_serverPath); - g_php_serverHost = cfg.getString("phpServer.host", ""); - replaceZeroIPWithLocalhostIP(g_php_serverHost); - //g_ServerSetupType - auto serverSetupTypeString = cfg.getString("ServerSetupType", ""); - g_ServerSetupType = getServerSetupTypeFromString(serverSetupTypeString); - - // app secret for encrypt user private keys - // TODO: encrypt with server admin key - auto app_secret_string = cfg.getString("crypto.app_secret", ""); - if ("" != app_secret_string) { - g_CryptoAppSecret = DataTypeConverter::hexToBin(app_secret_string); - } - //g_CryptoAppSecret - - g_gRPCRelayServerFullURL = cfg.getString("grpc.server", ""); - - // unsecure flags - //g_AllowUnsecureFlags - if (cfg.getInt("unsecure.allow_passwort_via_json_request", 0) == 1) { - g_AllowUnsecureFlags = (AllowUnsecure)(g_AllowUnsecureFlags | UNSECURE_PASSWORD_REQUESTS); - } - if (cfg.getInt("unsecure.allow_auto_sign_transactions", 0) == 1) { - g_AllowUnsecureFlags = (AllowUnsecure)(g_AllowUnsecureFlags | UNSECURE_AUTO_SIGN_TRANSACTIONS); - } - if (cfg.getInt("unsecure.allow_cors_all", 0) == 1) { - g_AllowUnsecureFlags = (AllowUnsecure)(g_AllowUnsecureFlags | UNSECURE_CORS_ALL); - } - if (cfg.getInt("unsecure.allow_all_passwords", 0) == 1) { - g_AllowUnsecureFlags = (AllowUnsecure)(g_AllowUnsecureFlags | UNSECURE_ALLOW_ALL_PASSWORDS); - } - - return true; - } - - bool initEMailAccount(const Poco::Util::LayeredConfiguration& cfg) - { - g_disableEmail = cfg.getBool("email.disable", false); - if (g_disableEmail) { - printf("Email is disabled!\n"); - } - else { - g_EmailAccount.sender = cfg.getString("email.sender"); - g_EmailAccount.username = cfg.getString("email.username"); - g_EmailAccount.password = cfg.getString("email.password"); - g_EmailAccount.url = cfg.getString("email.smtp.url"); - g_EmailAccount.port = cfg.getInt("email.smtp.port"); - } - DISASM_FALSERET; - //g_ServerKeySeed->put(3, DRRandom::r64()); - return true; - } - - bool initSSLClientContext() - { - SharedPtr pCert = new RejectCertificateHandler(false); // reject invalid certificates - /* - Context(Usage usage, - const std::string& certificateNameOrPath, - VerificationMode verMode = VERIFY_RELAXED, - int options = OPT_DEFAULTS, - const std::string& certificateStoreName = CERT_STORE_MY); - */ - try { -#ifdef POCO_NETSSL_WIN - g_SSL_CLient_Context = new Context(Context::CLIENT_USE, "cacert.pem", Context::VERIFY_RELAXED, Context::OPT_DEFAULTS); -#else - - g_SSL_CLient_Context = new Context(Context::CLIENT_USE, "", "", Poco::Path::config() + "grd_login/cacert.pem", Context::VERIFY_RELAXED, 9, true, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"); -#endif - } catch(Poco::Exception& ex) { - printf("[ServerConfig::initSSLClientContext] error init ssl context, maybe no cacert.pem found?\nPlease make sure you have cacert.pem (CA/root certificates) next to binary from https://curl.haxx.se/docs/caextract.html\n"); - return false; - } - DISASM_FALSERET; - SSLManager::instance().initializeClient(0, pCert, g_SSL_CLient_Context); - - g_ServerKeySeed->put(5, DRRandom::r64()); - - return true; - } - - void unload() { - if (g_ServerCryptoKey) { - delete g_ServerCryptoKey; - } - if (g_ServerKeySeed) { - delete g_ServerKeySeed; - } - if (g_CPUScheduler) { - delete g_CPUScheduler; - } - - if (g_CryptoCPUScheduler) { - delete g_CryptoCPUScheduler; - } - if (g_CryptoAppSecret) { - MemoryManager::getInstance()->releaseMemory(g_CryptoAppSecret); - g_CryptoAppSecret = nullptr; - } - } - - void writeToFile(std::istream& datas, std::string fileName) - { - static Poco::Mutex mutex; - - mutex.lock(); - - Poco::FileOutputStream file(fileName, std::ios::out | std::ios::app); - - if (!file.good()) { - printf("[ServerConfig::writeToFile] error creating file with name: %s\n", fileName.data()); - mutex.unlock(); - return; - } - - Poco::LocalDateTime now; - - std::string dateTimeStr = Poco::DateTimeFormatter::format(now, Poco::DateTimeFormat::ISO8601_FORMAT); - file << dateTimeStr << std::endl; - - for (std::string line; std::getline(datas, line); ) { - file << line << std::endl; - } - file << std::endl; - file.close(); - mutex.unlock(); - } +#include "ServerConfig.h" +#include "Crypto/mnemonic_german.h" +#include "Crypto/mnemonic_german2.h" +#include "Crypto/mnemonic_bip0039.h" +#include "Crypto/DRRandom.h" +#include "lib/DataTypeConverter.h" +#include "sodium.h" + + +#include "Poco/Net/SSLManager.h" +#include "Poco/Net/KeyConsoleHandler.h" +#include "Poco/Net/RejectCertificateHandler.h" +#include "Poco/Net/DNS.h" +#include "Poco/SharedPtr.h" + +#include "Poco/Mutex.h" +#include "Poco/Path.h" +#include "Poco/FileStream.h" +#include "Poco/LocalDateTime.h" +#include "Poco/DateTimeFormat.h" +#include "Poco/DateTimeFormatter.h" + + +using Poco::Net::SSLManager; +using Poco::Net::Context; +using Poco::Net::KeyConsoleHandler; +using Poco::Net::PrivateKeyPassphraseHandler; +using Poco::Net::InvalidCertificateHandler; +using Poco::Net::RejectCertificateHandler; +using Poco::SharedPtr; + +namespace ServerConfig { + +#define SESSION_TIMEOUT_DEFAULT 10 + + Mnemonic g_Mnemonic_WordLists[MNEMONIC_MAX]; + ObfusArray* g_ServerCryptoKey = nullptr; + ObfusArray* g_ServerKeySeed = nullptr; +// std::string g_ServerAdminPublic; + UniLib::controller::CPUSheduler* g_CPUScheduler = nullptr; + UniLib::controller::CPUSheduler* g_CryptoCPUScheduler = nullptr; + Context::Ptr g_SSL_CLient_Context = nullptr; + Poco::Util::Timer g_CronJobsTimer; + EmailAccount g_EmailAccount; + int g_SessionTimeout = SESSION_TIMEOUT_DEFAULT; + std::string g_serverPath; + int g_serverPort = 0; + 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; + std::string g_versionString = ""; + bool g_disableEmail = false; + ServerSetupType g_ServerSetupType = SERVER_TYPE_PRODUCTION; + std::string g_gRPCRelayServerFullURL; + MemoryBin* g_CryptoAppSecret = nullptr; + AllowUnsecure g_AllowUnsecureFlags = NOT_UNSECURE; + +#ifdef __linux__ +#include +#include +#include +#include +#include +#include +#endif //#ifdef __linux__ + + std::string getHostIpString() + { +#ifdef __linux__ + struct ifaddrs * ifAddrStruct = NULL; + struct ifaddrs * ifa = NULL; + void * tmpAddrPtr = NULL; + + getifaddrs(&ifAddrStruct); + std::string ipAddressString; + + for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) { + continue; + } + if (ifa->ifa_addr->sa_family == AF_INET) { // check it is IP4 + // is a valid IP4 Address + tmpAddrPtr = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; + char addressBuffer[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN); + ipAddressString = addressBuffer; + printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); + } + else if (ifa->ifa_addr->sa_family == AF_INET6) { // check it is IP6 + // is a valid IP6 Address + tmpAddrPtr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; + char addressBuffer[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN); + printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); + } + } + if (ifAddrStruct != NULL) freeifaddrs(ifAddrStruct); + return ipAddressString; +#else //__linux__ + std::string ipAddressString = ""; + auto host = Poco::Net::DNS::thisHost(); + for (auto it = host.addresses().begin(); it != host.addresses().end(); it++) { + auto ipAddress = *it; + if (!ipAddress.isIPv4Compatible() && !ipAddress.isIPv4Mapped()) { + continue; + } + if (ipAddress.isLoopback()) { + continue; + } + ipAddressString = ipAddress.toString(); + //isIPv4Compatible + //!isLoopback + //printf("ipaddress: %s\n", ipAddressString.data()); + break; + //break; + } + return ipAddressString; +#endif // __linux__ + } + + bool replaceZeroIPWithLocalhostIP(std::string& url) + { + auto pos = url.find("0.0.0.0", 0); + if (pos != std::string::npos) { + std::string ipAddressString = getHostIpString(); + if ("" != ipAddressString) { + url.replace(pos, 7, ipAddressString); + } + } + + //printf("ipaddress: %s\n", ipAddress.data()); + + return true; + } + + ServerSetupType getServerSetupTypeFromString(const std::string& serverSetupTypeString) { + if ("test" == serverSetupTypeString) { + return SERVER_TYPE_TEST; + } + if ("staging" == serverSetupTypeString) { + return SERVER_TYPE_STAGING; + } + if ("production" == serverSetupTypeString) { + return SERVER_TYPE_PRODUCTION; + } + return SERVER_TYPE_PRODUCTION; + } + + + bool loadMnemonicWordLists() + { + for (int i = 0; i < MNEMONIC_MAX; i++) { + int iResult = 0; + switch (i) { + case MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER: + iResult = g_Mnemonic_WordLists[i].init(populate_mnemonic_german, g_mnemonic_german_original_size, g_mnemonic_german_compressed_size); + if (iResult) { + printf("[%s] error init german mnemonic set, error nr: %d\n", __FUNCTION__, iResult); + return false; + } + g_Mnemonic_WordLists[i].printToFile("de_words.txt"); + break; + case MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER_FIXED_CASES: + iResult = g_Mnemonic_WordLists[i].init(populate_mnemonic_german2, g_mnemonic_german2_original_size, g_mnemonic_german2_compressed_size); + if (iResult) { + printf("[%s] error init german mnemonic set 2, error nr: %d\n", __FUNCTION__, iResult); + return false; + } + g_Mnemonic_WordLists[i].printToFile("de_words2.txt"); + break; + case MNEMONIC_BIP0039_SORTED_ORDER: + iResult = g_Mnemonic_WordLists[i].init(populate_mnemonic_bip0039, g_mnemonic_bip0039_original_size, g_mnemonic_bip0039_compressed_size); + if (iResult) { + printf("[%s] error init bip0039 mnemonic set, error nr: %d\n", __FUNCTION__, iResult); + return false; + } + //g_Mnemonic_WordLists[i].printToFile("en_words.txt"); + break; + default: printf("[%s] unknown MnemonicType\n", __FUNCTION__); return false; + } + } + return true; + } + + bool initServerCrypto(const Poco::Util::LayeredConfiguration& cfg) + { + auto serverKey = cfg.getString("crypto.server_key"); + unsigned char key[crypto_shorthash_KEYBYTES]; + size_t realBinSize = 0; + NULLPAD_10; + if (sodium_hex2bin(key, crypto_shorthash_KEYBYTES, serverKey.data(), serverKey.size(), nullptr, &realBinSize, nullptr)) { + printf("[%s] serverKey isn't valid hex: %s\n", __FUNCTION__, serverKey.data()); + return false; + } + if (realBinSize != crypto_shorthash_KEYBYTES) { + printf("[%s] serverKey hasn't valid size, expecting: %u, get: %lu\n", + __FUNCTION__, crypto_shorthash_KEYBYTES, realBinSize); + return false; + } + g_ServerCryptoKey = new ObfusArray(realBinSize, key); + g_ServerKeySeed = new ObfusArray(9*8); + Poco::Int64 i1 = randombytes_random(); + Poco::Int64 i2 = randombytes_random(); + g_ServerKeySeed->put(0, i1 | (i2 << 8)); + + //g_ServerAdminPublic = cfg.getString("crypto.server_admin_public"); + + DISASM_FALSERET; + g_SessionTimeout = cfg.getInt("session.timeout", SESSION_TIMEOUT_DEFAULT); + 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); + g_phpServerPort = cfg.getInt("phpServer.port", 0); + // replace 0.0.0.0 with actual server ip + + g_php_serverPath = cfg.getString("phpServer.url", ""); + replaceZeroIPWithLocalhostIP(g_php_serverPath); + g_php_serverHost = cfg.getString("phpServer.host", ""); + replaceZeroIPWithLocalhostIP(g_php_serverHost); + //g_ServerSetupType + auto serverSetupTypeString = cfg.getString("ServerSetupType", ""); + g_ServerSetupType = getServerSetupTypeFromString(serverSetupTypeString); + + // app secret for encrypt user private keys + // TODO: encrypt with server admin key + auto app_secret_string = cfg.getString("crypto.app_secret", ""); + 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", ""); + + // unsecure flags + //g_AllowUnsecureFlags + if (cfg.getInt("unsecure.allow_passwort_via_json_request", 0) == 1) { + g_AllowUnsecureFlags = (AllowUnsecure)(g_AllowUnsecureFlags | UNSECURE_PASSWORD_REQUESTS); + } + if (cfg.getInt("unsecure.allow_auto_sign_transactions", 0) == 1) { + g_AllowUnsecureFlags = (AllowUnsecure)(g_AllowUnsecureFlags | UNSECURE_AUTO_SIGN_TRANSACTIONS); + } + if (cfg.getInt("unsecure.allow_cors_all", 0) == 1) { + g_AllowUnsecureFlags = (AllowUnsecure)(g_AllowUnsecureFlags | UNSECURE_CORS_ALL); + } + if (cfg.getInt("unsecure.allow_all_passwords", 0) == 1) { + g_AllowUnsecureFlags = (AllowUnsecure)(g_AllowUnsecureFlags | UNSECURE_ALLOW_ALL_PASSWORDS); + } + + return true; + } + + bool initEMailAccount(const Poco::Util::LayeredConfiguration& cfg) + { + g_disableEmail = cfg.getBool("email.disable", false); + if (g_disableEmail) { + printf("Email is disabled!\n"); + } + else { + g_EmailAccount.sender = cfg.getString("email.sender"); + g_EmailAccount.username = cfg.getString("email.username"); + g_EmailAccount.password = cfg.getString("email.password"); + g_EmailAccount.url = cfg.getString("email.smtp.url"); + g_EmailAccount.port = cfg.getInt("email.smtp.port"); + } + DISASM_FALSERET; + //g_ServerKeySeed->put(3, DRRandom::r64()); + return true; + } + + bool initSSLClientContext() + { + SharedPtr pCert = new RejectCertificateHandler(false); // reject invalid certificates + /* + Context(Usage usage, + const std::string& certificateNameOrPath, + VerificationMode verMode = VERIFY_RELAXED, + int options = OPT_DEFAULTS, + const std::string& certificateStoreName = CERT_STORE_MY); + */ + try { +#ifdef POCO_NETSSL_WIN + g_SSL_CLient_Context = new Context(Context::CLIENT_USE, "cacert.pem", Context::VERIFY_RELAXED, Context::OPT_DEFAULTS); +#else + + g_SSL_CLient_Context = new Context(Context::CLIENT_USE, "", "", Poco::Path::config() + "grd_login/cacert.pem", Context::VERIFY_RELAXED, 9, true, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"); +#endif + } catch(Poco::Exception& ex) { + printf("[ServerConfig::initSSLClientContext] error init ssl context, maybe no cacert.pem found?\nPlease make sure you have cacert.pem (CA/root certificates) next to binary from https://curl.haxx.se/docs/caextract.html\n"); + return false; + } + DISASM_FALSERET; + SSLManager::instance().initializeClient(0, pCert, g_SSL_CLient_Context); + + g_ServerKeySeed->put(5, DRRandom::r64()); + + return true; + } + + void unload() { + if (g_ServerCryptoKey) { + delete g_ServerCryptoKey; + } + if (g_ServerKeySeed) { + delete g_ServerKeySeed; + } + if (g_CPUScheduler) { + delete g_CPUScheduler; + } + + if (g_CryptoCPUScheduler) { + delete g_CryptoCPUScheduler; + } + if (g_CryptoAppSecret) { + MemoryManager::getInstance()->releaseMemory(g_CryptoAppSecret); + g_CryptoAppSecret = nullptr; + } + } + + void writeToFile(std::istream& datas, std::string fileName) + { + static Poco::Mutex mutex; + + mutex.lock(); + + Poco::FileOutputStream file(fileName, std::ios::out | std::ios::app); + + if (!file.good()) { + printf("[ServerConfig::writeToFile] error creating file with name: %s\n", fileName.data()); + mutex.unlock(); + return; + } + + Poco::LocalDateTime now; + + std::string dateTimeStr = Poco::DateTimeFormatter::format(now, Poco::DateTimeFormat::ISO8601_FORMAT); + file << dateTimeStr << std::endl; + + for (std::string line; std::getline(datas, line); ) { + file << line << std::endl; + } + file << std::endl; + file.close(); + mutex.unlock(); + } } \ No newline at end of file diff --git a/login_server/src/cpp/ServerConfig.h b/login_server/src/cpp/ServerConfig.h index 444fb524c..6d19e25f0 100644 --- a/login_server/src/cpp/ServerConfig.h +++ b/login_server/src/cpp/ServerConfig.h @@ -1,90 +1,92 @@ -#ifndef __GRADIDO_LOGIN_SERVER_SERVER_CONFIG__ -#define __GRADIDO_LOGIN_SERVER_SERVER_CONFIG__ - -#include "Crypto/mnemonic.h" -#include "Crypto/Obfus_array.h" -#include "Poco/Util/LayeredConfiguration.h" -#include "Poco/Net/Context.h" -#include "Poco/Types.h" -#include "Poco/Util/Timer.h" - -#include "tasks/CPUSheduler.h" - -#include "SingletonManager/LanguageManager.h" -#include "SingletonManager/MemoryManager.h" - -#define DISABLE_EMAIL - -namespace ServerConfig { - - enum Mnemonic_Types { - MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER, - MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER_FIXED_CASES, - MNEMONIC_BIP0039_SORTED_ORDER, - MNEMONIC_MAX - }; - // depracted, moved to email manager - struct EmailAccount { - std::string sender; - std::string admin_receiver; - std::string username; - std::string password; - std::string url; - int port; - }; - - enum ServerSetupType { - SERVER_TYPE_TEST, - SERVER_TYPE_STAGING, - SERVER_TYPE_PRODUCTION - }; - - // used with bit-operators, so only use numbers with control exactly one bit (1,2,4,8,16...) - enum AllowUnsecure { - NOT_UNSECURE = 0, - UNSECURE_PASSWORD_REQUESTS = 1, - UNSECURE_AUTO_SIGN_TRANSACTIONS = 2, - UNSECURE_CORS_ALL = 4, - UNSECURE_ALLOW_ALL_PASSWORDS = 8 - }; - - - extern Mnemonic g_Mnemonic_WordLists[MNEMONIC_MAX]; - - extern ObfusArray* g_ServerCryptoKey; - extern ObfusArray* g_ServerKeySeed; - - //extern unsigned char g_ServerAdminPublic[]; - extern UniLib::controller::CPUSheduler* g_CPUScheduler; - extern UniLib::controller::CPUSheduler* g_CryptoCPUScheduler; - extern Poco::Net::Context::Ptr g_SSL_CLient_Context; - extern Poco::Util::Timer g_CronJobsTimer; - extern EmailAccount g_EmailAccount; - extern int g_SessionTimeout; - extern std::string g_serverPath; - extern int g_serverPort; - extern Languages g_default_locale; - extern std::string g_php_serverPath; - extern std::string g_php_serverHost; - extern int g_phpServerPort; - extern Poco::Mutex g_TimeMutex; - extern int g_FakeLoginSleepTime; - extern std::string g_versionString; - extern bool g_disableEmail; - extern ServerSetupType g_ServerSetupType; - extern std::string g_gRPCRelayServerFullURL; - extern MemoryBin* g_CryptoAppSecret; - extern AllowUnsecure g_AllowUnsecureFlags; - - bool loadMnemonicWordLists(); - bool initServerCrypto(const Poco::Util::LayeredConfiguration& cfg); - bool initEMailAccount(const Poco::Util::LayeredConfiguration& cfg); - bool initSSLClientContext(); - - - void writeToFile(std::istream& datas, std::string fileName); - - void unload(); -}; - +#ifndef __GRADIDO_LOGIN_SERVER_SERVER_CONFIG__ +#define __GRADIDO_LOGIN_SERVER_SERVER_CONFIG__ + +#include "Crypto/mnemonic.h" +#include "Crypto/Obfus_array.h" +#include "Poco/Util/LayeredConfiguration.h" +#include "Poco/Net/Context.h" +#include "Poco/Types.h" +#include "Poco/Util/Timer.h" + +#include "tasks/CPUSheduler.h" + +#include "SingletonManager/LanguageManager.h" +#include "SingletonManager/MemoryManager.h" + +#define DISABLE_EMAIL + +namespace ServerConfig { + + enum Mnemonic_Types { + MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER, + MNEMONIC_GRADIDO_BOOK_GERMAN_RANDOM_ORDER_FIXED_CASES, + MNEMONIC_BIP0039_SORTED_ORDER, + MNEMONIC_MAX + }; + // depracted, moved to email manager + struct EmailAccount { + std::string sender; + std::string admin_receiver; + std::string username; + std::string password; + std::string url; + int port; + }; + + enum ServerSetupType { + SERVER_TYPE_TEST, + SERVER_TYPE_STAGING, + SERVER_TYPE_PRODUCTION + }; + + // used with bit-operators, so only use numbers with control exactly one bit (1,2,4,8,16...) + enum AllowUnsecure { + NOT_UNSECURE = 0, + UNSECURE_PASSWORD_REQUESTS = 1, + UNSECURE_AUTO_SIGN_TRANSACTIONS = 2, + UNSECURE_CORS_ALL = 4, + UNSECURE_ALLOW_ALL_PASSWORDS = 8 + }; + + + extern Mnemonic g_Mnemonic_WordLists[MNEMONIC_MAX]; + + extern ObfusArray* g_ServerCryptoKey; + extern ObfusArray* g_ServerKeySeed; + + //extern unsigned char g_ServerAdminPublic[]; + extern UniLib::controller::CPUSheduler* g_CPUScheduler; + extern UniLib::controller::CPUSheduler* g_CryptoCPUScheduler; + extern Poco::Net::Context::Ptr g_SSL_CLient_Context; + extern Poco::Util::Timer g_CronJobsTimer; + extern EmailAccount g_EmailAccount; + extern int g_SessionTimeout; + extern std::string g_serverPath; + extern int g_serverPort; + 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; + extern std::string g_versionString; + extern bool g_disableEmail; + extern ServerSetupType g_ServerSetupType; + extern std::string g_gRPCRelayServerFullURL; + extern MemoryBin* g_CryptoAppSecret; + extern AllowUnsecure g_AllowUnsecureFlags; + + bool loadMnemonicWordLists(); + bool initServerCrypto(const Poco::Util::LayeredConfiguration& cfg); + bool initEMailAccount(const Poco::Util::LayeredConfiguration& cfg); + bool initSSLClientContext(); + + + void writeToFile(std::istream& datas, std::string fileName); + + void unload(); +}; + + #endif //__GRADIDO_LOGIN_SERVER_SERVER_CONFIG__ \ 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..45c38bf70 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(); @@ -102,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; } 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 c04ab417c..6b4b2eb90 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..004854ddf 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,7 +70,9 @@ namespace model { EmailType mType; std::queue mAdditionalStringPartSrcs; + 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 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);