diff --git a/README b/README index 993dd4afa..de051e742 100644 --- a/README +++ b/README @@ -21,16 +21,18 @@ cd ../../../ cd dependencies/grpc -mkdir _build -cd _build +mkdir build +cd build cmake .. make # under windows build at least release for protoc.exe and grpc c++ plugin cd ../../../ +./unix_parse_proto.sh # get more dependencies with conan (need conan from https://conan.io/) mkdir build && cd build -conan remote add inexor https://api.bintray.com/conan/inexorgame/inexor-conan +# // not used anymore +# conan remote add inexor https://api.bintray.com/conan/inexorgame/inexor-conan # conan install .. -s build_type=Debug conan install .. diff --git a/skeema/gradido_login/users.sql b/skeema/gradido_login/users.sql index e3c7deec5..783a36211 100644 --- a/skeema/gradido_login/users.sql +++ b/skeema/gradido_login/users.sql @@ -6,6 +6,7 @@ CREATE TABLE `users` ( `password` bigint unsigned NOT NULL, `pubkey` binary(32) DEFAULT NULL, `privkey` binary(80) DEFAULT NULL, + `email_hash` binary(32) DEFAULT NULL, `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `email_checked` tinyint NOT NULL DEFAULT '0', `passphrase_shown` tinyint NOT NULL DEFAULT '0', diff --git a/src/cpp/Gradido_LoginServer.cpp b/src/cpp/Gradido_LoginServer.cpp index 2ad89e19b..3133781ca 100644 --- a/src/cpp/Gradido_LoginServer.cpp +++ b/src/cpp/Gradido_LoginServer.cpp @@ -1,267 +1,263 @@ -#include "Gradido_LoginServer.h" -#include "ServerConfig.h" -#include "HTTPInterface/PageRequestHandlerFactory.h" -#include "JSONInterface/JsonRequestHandlerFactory.h" - -#include "lib/Profiler.h" - -#include "SingletonManager/ConnectionManager.h" -#include "SingletonManager/SessionManager.h" -#include "SingletonManager/EmailManager.h" - -#include "controller/User.h" - -#include "Poco/Util/HelpFormatter.h" -#include "Poco/Net/ServerSocket.h" -#include "Poco/Net/HTTPServer.h" -#include "Poco/Net/SSLManager.h" -#include "Poco/Environment.h" -#include "Poco/Logger.h" -#include "Poco/Path.h" -#include "Poco/AsyncChannel.h" -#include "Poco/SimpleFileChannel.h" -#include "Poco/ConsoleChannel.h" -#include "Poco/SplitterChannel.h" -#include "MySQL/Poco/Connector.h" - - -#include - - - -Gradido_LoginServer::Gradido_LoginServer() - : _helpRequested(false) -{ -} - -Gradido_LoginServer::~Gradido_LoginServer() -{ -} - - -void Gradido_LoginServer::initialize(Application& self) -{ - loadConfiguration(); // load default configuration files, if present - ServerApplication::initialize(self); -} - -void Gradido_LoginServer::uninitialize() -{ - ServerApplication::uninitialize(); -} - -void Gradido_LoginServer::defineOptions(Poco::Util::OptionSet& options) -{ - ServerApplication::defineOptions(options); - - /*options.addOption( - Poco::Util::Option("help", "h", "display help information on command line arguments") - .required(false) - .repeatable(false));*/ - options.addOption( - Poco::Util::Option("config", "c", "use non default config file (default is /etc/grd_login.properties)", false) - .repeatable(false) - .argument("Gradido_LoginServer.properties", true) - .callback(Poco::Util::OptionCallback(this, &Gradido_LoginServer::handleOption))); - -} - -void Gradido_LoginServer::handleOption(const std::string& name, const std::string& value) -{ - //printf("handle option: %s with value: %s\n", name.data(), value.data()); - if (name == "config") { - mConfigPath = value; - return; - } - ServerApplication::handleOption(name, value); - if (name == "help") _helpRequested = true; - -} - -void Gradido_LoginServer::displayHelp() -{ - Poco::Util::HelpFormatter helpFormatter(options()); - helpFormatter.setCommand(commandName()); - helpFormatter.setUsage("OPTIONS"); - helpFormatter.setHeader("Gradido Login Server"); - helpFormatter.format(std::cout); -} - -void Gradido_LoginServer::createConsoleFileAsyncLogger(std::string name, std::string filePath) -{ - Poco::AutoPtr logConsoleChannel(new Poco::ConsoleChannel); - Poco::AutoPtr logFileChannel(new Poco::SimpleFileChannel(filePath)); - logFileChannel->setProperty("rotation", "500 K"); - Poco::AutoPtr logSplitter(new Poco::SplitterChannel); - logSplitter->addChannel(logConsoleChannel); - logSplitter->addChannel(logFileChannel); - - Poco::AutoPtr logAsyncChannel(new Poco::AsyncChannel(logSplitter)); - - Poco::Logger& log = Poco::Logger::get(name); - log.setChannel(logAsyncChannel); - log.setLevel("information"); -} - -int Gradido_LoginServer::main(const std::vector& args) -{ - - Profiler usedTime; - if (_helpRequested) - { - displayHelp(); - } - else - { - // ********** logging ************************************ - std::string log_Path = "/var/log/grd_login/"; -//#ifdef _WIN32 -#if defined(_WIN32) || defined(_WIN64) - log_Path = "./"; -#endif - - // init speed logger - Poco::AutoPtr speedLogFileChannel(new Poco::SimpleFileChannel(log_Path + "speedLog.txt")); - /* - The optional log file rotation mode: - never: no rotation (default) - : rotate if file size exceeds bytes - K: rotate if file size exceeds Kilobytes - M: rotate if file size exceeds Megabytes - */ - speedLogFileChannel->setProperty("rotation", "500 K"); - Poco::AutoPtr speedLogAsyncChannel(new Poco::AsyncChannel(speedLogFileChannel)); - - Poco::Logger& speedLogger = Poco::Logger::get("SpeedLog"); - speedLogger.setChannel(speedLogAsyncChannel); - speedLogger.setLevel("information"); - - // logging for request handling - createConsoleFileAsyncLogger("requestLog", log_Path + "requestLog.txt"); - - // error logging - createConsoleFileAsyncLogger("errorLog", log_Path + "errorLog.txt"); - Poco::Logger& errorLog = Poco::Logger::get("errorLog"); - - createConsoleFileAsyncLogger("emailLog", log_Path + "emailLog.txt"); - - // *************** load from config ******************************************** - - std::string cfg_Path = Poco::Path::config() + "grd_login/grd_login.properties"; - if (mConfigPath != "") { - cfg_Path = mConfigPath; - } - - try { - loadConfiguration(cfg_Path); - } - catch (Poco::Exception& ex) { - errorLog.error("error loading config: %s from path: %s", ex.displayText(), cfg_Path); - } - - unsigned short port = (unsigned short)config().getInt("HTTPServer.port", 9980); - unsigned short json_port = (unsigned short)config().getInt("JSONServer.port", 1201); - - - //printf("show mnemonic list: \n"); - //printf(ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER].getCompleteWordList().data()); - if (!ServerConfig::initServerCrypto(config())) { - //printf("[Gradido_LoginServer::%s] error init server crypto\n", __FUNCTION__); - errorLog.error("[Gradido_LoginServer::main] error init server crypto"); - return Application::EXIT_CONFIG; - } - - // first check time for crypto - auto testUser = new User("email@google.de", "Max", "Mustermann"); - Profiler timeUsed; - testUser->validatePwd("haz27Newpassword", nullptr); - ServerConfig::g_FakeLoginSleepTime = (int)std::round(timeUsed.millis()); - delete testUser; - - Poco::Int64 i1 = randombytes_random(); - Poco::Int64 i2 = randombytes_random(); - ServerConfig::g_ServerKeySeed->put(1, i1 | (i2 << 8)); - - ServerConfig::initEMailAccount(config()); - EmailManager::getInstance()->init(config()); - - // start cpu scheduler - uint8_t worker_count = Poco::Environment::processorCount() * 2; - - ServerConfig::g_CPUScheduler = new UniLib::controller::CPUSheduler(worker_count, "Default Worker"); - ServerConfig::g_CryptoCPUScheduler = new UniLib::controller::CPUSheduler(2, "Crypto Worker"); - - // load up connection configs - // register MySQL connector - Poco::Data::MySQL::Connector::registerConnector(); - //Poco::Data::MySQL::Connector::KEY; - auto conn = ConnectionManager::getInstance(); - //conn->setConnection() - //printf("try connect login server mysql db\n"); - try { - conn->setConnectionsFromConfig(config(), CONNECTION_MYSQL_LOGIN_SERVER); - } - catch (Poco::Exception& ex) { - // maybe we in docker environment and db needs some time to start up - // let's wait 10 seconds - int count = 10; - while (count > 0) { - printf("\rwait on mysql/mariadb %d seconds...", count); - count--; - Poco::Thread::sleep(1000); - } - conn->setConnectionsFromConfig(config(), CONNECTION_MYSQL_LOGIN_SERVER); - } - //printf("try connect php server mysql \n"); - //conn->setConnectionsFromConfig(config(), CONNECTION_MYSQL_PHP_SERVER); - - SessionManager::getInstance()->init(); - // put urandom on linux servers - //srand(); - - Poco::Net::initializeSSL(); - if(!ServerConfig::initSSLClientContext()) { - //printf("[Gradido_LoginServer::%s] error init server SSL Client\n", __FUNCTION__); - errorLog.error("[Gradido_LoginServer::main] error init server SSL Client\n"); - return Application::EXIT_CONFIG; - } - - // schedule email verification resend - controller::User::checkIfVerificationEmailsShouldBeResend(ServerConfig::g_CronJobsTimer); - - // HTTP Interface Server - // set-up a server socket - Poco::Net::ServerSocket svs(port); - // set-up a HTTPServer instance - Poco::ThreadPool& pool = Poco::ThreadPool::defaultPool(); - Poco::Net::HTTPServer srv(new PageRequestHandlerFactory, svs, new Poco::Net::HTTPServerParams); - ServerConfig::g_ServerKeySeed->put(7, 918276611); - - // start the HTTPServer - srv.start(); - - // JSON Interface Server - Poco::Net::ServerSocket json_svs(json_port); - Poco::Net::HTTPServer json_srv(new JsonRequestHandlerFactory, json_svs, new Poco::Net::HTTPServerParams); - - // start the json server - json_srv.start(); - - printf("[Gradido_LoginServer::main] started in %s\n", usedTime.string().data()); - // wait for CTRL-C or kill - waitForTerminationRequest(); - - // Stop the HTTPServer - srv.stop(); - // Stop the json server - json_srv.stop(); - - ServerConfig::unload(); - Poco::Net::uninitializeSSL(); - // Optional: Delete all global objects allocated by libprotobuf. - google::protobuf::ShutdownProtobufLibrary(); - - } - return Application::EXIT_OK; -} - +#include "Gradido_LoginServer.h" +#include "ServerConfig.h" +#include "HTTPInterface/PageRequestHandlerFactory.h" +#include "JSONInterface/JsonRequestHandlerFactory.h" + +#include "lib/Profiler.h" + +#include "SingletonManager/ConnectionManager.h" +#include "SingletonManager/SessionManager.h" +#include "SingletonManager/EmailManager.h" + +#include "controller/User.h" + +#include "Poco/Util/HelpFormatter.h" +#include "Poco/Net/ServerSocket.h" +#include "Poco/Net/HTTPServer.h" +#include "Poco/Net/SSLManager.h" +#include "Poco/Environment.h" +#include "Poco/Logger.h" +#include "Poco/Path.h" +#include "Poco/AsyncChannel.h" +#include "Poco/SimpleFileChannel.h" +#include "Poco/ConsoleChannel.h" +#include "Poco/SplitterChannel.h" +#include "MySQL/Poco/Connector.h" + + +#include + + + +Gradido_LoginServer::Gradido_LoginServer() + : _helpRequested(false) +{ +} + +Gradido_LoginServer::~Gradido_LoginServer() +{ +} + + +void Gradido_LoginServer::initialize(Application& self) +{ + loadConfiguration(); // load default configuration files, if present + ServerApplication::initialize(self); +} + +void Gradido_LoginServer::uninitialize() +{ + ServerApplication::uninitialize(); +} + +void Gradido_LoginServer::defineOptions(Poco::Util::OptionSet& options) +{ + ServerApplication::defineOptions(options); + + /*options.addOption( + Poco::Util::Option("help", "h", "display help information on command line arguments") + .required(false) + .repeatable(false));*/ + options.addOption( + Poco::Util::Option("config", "c", "use non default config file (default is /etc/grd_login.properties)", false) + .repeatable(false) + .argument("Gradido_LoginServer.properties", true) + .callback(Poco::Util::OptionCallback(this, &Gradido_LoginServer::handleOption))); + +} + +void Gradido_LoginServer::handleOption(const std::string& name, const std::string& value) +{ + //printf("handle option: %s with value: %s\n", name.data(), value.data()); + if (name == "config") { + mConfigPath = value; + return; + } + ServerApplication::handleOption(name, value); + if (name == "help") _helpRequested = true; +} + +void Gradido_LoginServer::displayHelp() +{ + Poco::Util::HelpFormatter helpFormatter(options()); + helpFormatter.setCommand(commandName()); + helpFormatter.setUsage("OPTIONS"); + helpFormatter.setHeader("Gradido Login Server"); + helpFormatter.format(std::cout); +} + +void Gradido_LoginServer::createConsoleFileAsyncLogger(std::string name, std::string filePath) +{ + Poco::AutoPtr logConsoleChannel(new Poco::ConsoleChannel); + Poco::AutoPtr logFileChannel(new Poco::SimpleFileChannel(filePath)); + logFileChannel->setProperty("rotation", "500 K"); + Poco::AutoPtr logSplitter(new Poco::SplitterChannel); + logSplitter->addChannel(logConsoleChannel); + logSplitter->addChannel(logFileChannel); + + Poco::AutoPtr logAsyncChannel(new Poco::AsyncChannel(logSplitter)); + + Poco::Logger& log = Poco::Logger::get(name); + log.setChannel(logAsyncChannel); + log.setLevel("information"); +} + +int Gradido_LoginServer::main(const std::vector& args) +{ + Profiler usedTime; + if (_helpRequested) + { + displayHelp(); + } + else + { + // ********** logging ************************************ + std::string log_Path = "/var/log/grd_login/"; +//#ifdef _WIN32 +#if defined(_WIN32) || defined(_WIN64) + log_Path = "./"; +#endif + + // init speed logger + Poco::AutoPtr speedLogFileChannel(new Poco::SimpleFileChannel(log_Path + "speedLog.txt")); + /* + The optional log file rotation mode: + never: no rotation (default) + : rotate if file size exceeds bytes + K: rotate if file size exceeds Kilobytes + M: rotate if file size exceeds Megabytes + */ + speedLogFileChannel->setProperty("rotation", "500 K"); + Poco::AutoPtr speedLogAsyncChannel(new Poco::AsyncChannel(speedLogFileChannel)); + + Poco::Logger& speedLogger = Poco::Logger::get("SpeedLog"); + speedLogger.setChannel(speedLogAsyncChannel); + speedLogger.setLevel("information"); + + // logging for request handling + createConsoleFileAsyncLogger("requestLog", log_Path + "requestLog.txt"); + + // error logging + createConsoleFileAsyncLogger("errorLog", log_Path + "errorLog.txt"); + Poco::Logger& errorLog = Poco::Logger::get("errorLog"); + + createConsoleFileAsyncLogger("emailLog", log_Path + "emailLog.txt"); + + // *************** load from config ******************************************** + + std::string cfg_Path = Poco::Path::config() + "grd_login/"; + try { + loadConfiguration(cfg_Path + "grd_login.properties"); + } + catch (Poco::Exception& ex) { + errorLog.error("error loading config: %s", ex.displayText()); + } + + unsigned short port = (unsigned short)config().getInt("HTTPServer.port", 9980); + unsigned short json_port = (unsigned short)config().getInt("JSONServer.port", 1201); + + + //printf("show mnemonic list: \n"); + //printf(ServerConfig::g_Mnemonic_WordLists[ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER].getCompleteWordList().data()); + if (!ServerConfig::initServerCrypto(config())) { + //printf("[Gradido_LoginServer::%s] error init server crypto\n", __FUNCTION__); + errorLog.error("[Gradido_LoginServer::main] error init server crypto"); + return Application::EXIT_CONFIG; + } + + // first check time for crypto + auto testUser = new User("email@google.de", "Max", "Mustermann"); + Profiler timeUsed; + testUser->validatePwd("haz27Newpassword", nullptr); + ServerConfig::g_FakeLoginSleepTime = (int)std::round(timeUsed.millis()); + delete testUser; + + Poco::Int64 i1 = randombytes_random(); + Poco::Int64 i2 = randombytes_random(); + ServerConfig::g_ServerKeySeed->put(1, i1 | (i2 << 8)); + + ServerConfig::initEMailAccount(config()); + EmailManager::getInstance()->init(config()); + + // start cpu scheduler + uint8_t worker_count = Poco::Environment::processorCount() * 2; + + ServerConfig::g_CPUScheduler = new UniLib::controller::CPUSheduler(worker_count, "Default Worker"); + ServerConfig::g_CryptoCPUScheduler = new UniLib::controller::CPUSheduler(2, "Crypto Worker"); + + // load up connection configs + // register MySQL connector + Poco::Data::MySQL::Connector::registerConnector(); + //Poco::Data::MySQL::Connector::KEY; + auto conn = ConnectionManager::getInstance(); + //conn->setConnection() + //printf("try connect login server mysql db\n"); + try { + conn->setConnectionsFromConfig(config(), CONNECTION_MYSQL_LOGIN_SERVER); + } + catch (Poco::Exception& ex) { + // maybe we in docker environment and db needs some time to start up + // let's wait 10 seconds + int count = 10; + while (count > 0) { + printf("\rwait on mysql/mariadb %d seconds...", count); + count--; + Poco::Thread::sleep(1000); + } + conn->setConnectionsFromConfig(config(), CONNECTION_MYSQL_LOGIN_SERVER); + } + //printf("try connect php server mysql \n"); + //conn->setConnectionsFromConfig(config(), CONNECTION_MYSQL_PHP_SERVER); + + SessionManager::getInstance()->init(); + // put urandom on linux servers + //srand(); + + Poco::Net::initializeSSL(); + if(!ServerConfig::initSSLClientContext()) { + //printf("[Gradido_LoginServer::%s] error init server SSL Client\n", __FUNCTION__); + errorLog.error("[Gradido_LoginServer::main] error init server SSL Client\n"); + return Application::EXIT_CONFIG; + } + + // schedule email verification resend + controller::User::checkIfVerificationEmailsShouldBeResend(ServerConfig::g_CronJobsTimer); + controller::User::addMissingEmailHashes(); + + // HTTP Interface Server + // set-up a server socket + Poco::Net::ServerSocket svs(port); + // set-up a HTTPServer instance + Poco::ThreadPool& pool = Poco::ThreadPool::defaultPool(); + Poco::Net::HTTPServer srv(new PageRequestHandlerFactory, svs, new Poco::Net::HTTPServerParams); + ServerConfig::g_ServerKeySeed->put(7, 918276611); + + // start the HTTPServer + srv.start(); + + // JSON Interface Server + Poco::Net::ServerSocket json_svs(json_port); + Poco::Net::HTTPServer json_srv(new JsonRequestHandlerFactory, json_svs, new Poco::Net::HTTPServerParams); + + // start the json server + json_srv.start(); + + printf("[Gradido_LoginServer::main] started in %s\n", usedTime.string().data()); + // wait for CTRL-C or kill + waitForTerminationRequest(); + + // Stop the HTTPServer + srv.stop(); + // Stop the json server + json_srv.stop(); + + ServerConfig::unload(); + Poco::Net::uninitializeSSL(); + // Optional: Delete all global objects allocated by libprotobuf. + google::protobuf::ShutdownProtobufLibrary(); + + } + return Application::EXIT_OK; +} + +>>>>>>> 419965f (Add email_hash to user db, model::table::user and a json interface to search for publickey by email hash) diff --git a/src/cpp/JSONInterface/JsonRequestHandlerFactory.cpp b/src/cpp/JSONInterface/JsonRequestHandlerFactory.cpp index fc52c3a2c..33ce5c38e 100644 --- a/src/cpp/JSONInterface/JsonRequestHandlerFactory.cpp +++ b/src/cpp/JSONInterface/JsonRequestHandlerFactory.cpp @@ -1,74 +1,78 @@ -#include "JsonRequestHandlerFactory.h" - -#include "Poco/Net/HTTPServerRequest.h" - -#include "../SingletonManager/SessionManager.h" - -#include "JsonCreateUser.h" -#include "JsonGetLogin.h" -#include "JsonUnknown.h" -#include "JsonTransaction.h" -#include "JsonGetRunningUserTasks.h" -#include "JsonGetUsers.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")) -{ -} - -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 == "/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 == "/logout") { - return new JsonLogout(client_host); - } - return new JsonUnknown; -} +#include "JsonRequestHandlerFactory.h" + +#include "Poco/Net/HTTPServerRequest.h" + +#include "../SingletonManager/SessionManager.h" + +#include "JsonCreateUser.h" +#include "JsonGetLogin.h" +#include "JsonUnknown.h" +#include "JsonTransaction.h" +#include "JsonGetRunningUserTasks.h" +#include "JsonGetUsers.h" +#include "JsonAdminEmailVerificationResend.h" +#include "JsonGetUserInfos.h" +#include "JsonUpdateUserInfos.h" +#include "JsonUnsecureLogin.h" +#include "JsonLogout.h" +#include "JsonSearch.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 == "/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 == "/search") { + return new JsonSearch; + } + else if (url_first_part == "/unsecureLogin" && (ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_PASSWORD_REQUESTS)) { + return new JsonUnsecureLogin(client_host); + } + else if (url_first_part == "/logout") { + return new JsonLogout(client_host); + } + return new JsonUnknown; +} diff --git a/src/cpp/JSONInterface/JsonSearch.cpp b/src/cpp/JSONInterface/JsonSearch.cpp new file mode 100644 index 000000000..4b7d82ec6 --- /dev/null +++ b/src/cpp/JSONInterface/JsonSearch.cpp @@ -0,0 +1,89 @@ +#include "JsonSearch.h" + +#include "../lib/DataTypeConverter.h" +#include "../controller/User.h" +#include "../SingletonManager/SessionManager.h" + +Poco::JSON::Object* JsonSearch::handle(Poco::Dynamic::Var params) +{ + /* + 'ask' = ['account_publickey' => ''] + */ + // incoming + + Poco::JSON::Object::Ptr ask; + + // 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 { + ask = paramJsonObject->getObject("ask"); + } + catch (Poco::Exception& ex) { + return stateError("json exception", ex.displayText()); + } + } + else { + return stateError("parameter format unknown"); + } + + + if (ask.isNull()) { + return stateError("ask is zero or not an object"); + } + + + Poco::JSON::Object* result = new Poco::JSON::Object; + result->set("state", "success"); + Poco::JSON::Array jsonErrorsArray; + Poco::JSON::Object result_fields; + auto sm = SessionManager::getInstance(); + auto mm = MemoryManager::getInstance(); + for (auto it = ask->begin(); it != ask->end(); it++) { + std::string name = it->first; + auto value = it->second; + + + try { + if ("account_publickey" == name) { + if (!value.isString()) { + jsonErrorsArray.add("account_publickey isn't a string"); + } + else { + MemoryBin* email_hash = nullptr; + if (sm->isValid(value, VALIDATE_ONLY_HEX)) { + email_hash = DataTypeConverter::hexToBin(value); + } + if (!email_hash) { + email_hash = DataTypeConverter::base64ToBin(value); + } + if (!email_hash) { + jsonErrorsArray.add("account_publickey isn't valid base64 or hex"); + } + else { + auto user = controller::User::create(); + user->load(email_hash); + mm->releaseMemory(email_hash); + auto user_model = user->getModel(); + auto public_key_base64 = DataTypeConverter::binToBase64(user_model->getPublicKey(), user_model->getPublicKeySize()); + result_fields.set("account_publickey", public_key_base64); + } + } + } + } + catch (Poco::Exception& ex) { + jsonErrorsArray.add("update parameter invalid"); + } + } + + result->set("errors", jsonErrorsArray); + result->set("results", result_fields); + result->set("state", "success"); + + return result; +} \ No newline at end of file diff --git a/src/cpp/JSONInterface/JsonSearch.h b/src/cpp/JSONInterface/JsonSearch.h new file mode 100644 index 000000000..c6fa683a8 --- /dev/null +++ b/src/cpp/JSONInterface/JsonSearch.h @@ -0,0 +1,24 @@ +#ifndef __JSON_INTERFACE_JSON_SEARCH_ +#define __JSON_INTERFACE_JSON_SEARCH_ + +#include "JsonRequestHandler.h" + +/*! +* @author Dario Rekowski +* @date 2020-09-28 +* @brief search for public informations (no session_id needed), like account id for email hash +* +* +*/ + +class JsonSearch : public JsonRequestHandler +{ +public: + Poco::JSON::Object* handle(Poco::Dynamic::Var params); + +protected: + + +}; + +#endif // __JSON_INTERFACE_JSON_SEARCH_ \ No newline at end of file diff --git a/src/cpp/SingletonManager/SessionManager.cpp b/src/cpp/SingletonManager/SessionManager.cpp index e3bdbef17..f99e2bd5f 100644 --- a/src/cpp/SingletonManager/SessionManager.cpp +++ b/src/cpp/SingletonManager/SessionManager.cpp @@ -235,7 +235,6 @@ bool SessionManager::releaseSession(int requestHandleSession) mWorkingMutex.unlock(); return false; } - Session* session = it->second; @@ -246,6 +245,7 @@ bool SessionManager::releaseSession(int requestHandleSession) return true; + // check if dead locked if (session->tryLock()) { session->unlock(); diff --git a/src/cpp/controller/User.cpp b/src/cpp/controller/User.cpp index fc94661fb..e5e4916ad 100644 --- a/src/cpp/controller/User.cpp +++ b/src/cpp/controller/User.cpp @@ -83,6 +83,11 @@ namespace controller { return getModel()->loadFromDB("pubkey", pubkey); } + int User::load(MemoryBin* emailHash) + { + Poco::Data::BLOB email_hash(*emailHash, crypto_generichash_BYTES); + return getModel()->loadFromDB("email_hash", email_hash); + } const std::string& User::getPublicHex() { if (mPublicHex != "") { @@ -404,4 +409,60 @@ namespace controller { return 0; } + + int User::addMissingEmailHashes() + { + auto cm = ConnectionManager::getInstance(); + auto em = ErrorManager::getInstance(); + static const char* function_name = "User::addMissingEmailHashes"; + + auto session = cm->getConnection(CONNECTION_MYSQL_LOGIN_SERVER); + Poco::Data::Statement select(session); + std::vector> results; + + select << "select id, email from users " + << "where email_hash IS NULL " + , Poco::Data::Keywords::into(results) + ; + int result_count = 0; + try { + result_count = select.execute(); + } + catch (Poco::Exception& ex) { + em->addError(new ParamError(function_name, "mysql error by select", ex.displayText().data())); + em->sendErrorsAsEmail(); + //return -1; + } + if (0 == result_count) return 0; + std::vector> updates; + // calculate hashes + updates.reserve(results.size()); + unsigned char email_hash[crypto_generichash_BYTES]; + for (auto it = results.begin(); it != results.end(); it++) { + memset(email_hash, 0, crypto_generichash_BYTES); + auto id = it->get<0>(); + auto email = it->get<1>(); + crypto_generichash(email_hash, crypto_generichash_BYTES, + (const unsigned char*)email.data(), email.size(), + NULL, 0); + updates.push_back(Poco::Tuple(Poco::Data::BLOB(email_hash, crypto_generichash_BYTES), id)); + } + + // update db + // reuse connection, I hope it's working + Poco::Data::Statement update(session); + update << "UPDATE users set email_hash = ? where id = ?" + , Poco::Data::Keywords::use(updates); + int updated_count = 0; + try { + updated_count = update.execute(); + } + catch (Poco::Exception& ex) { + em->addError(new ParamError(function_name, "mysql error by update", ex.displayText().data())); + em->sendErrorsAsEmail(); + } + return updated_count; + } + } + diff --git a/src/cpp/controller/User.h b/src/cpp/controller/User.h index 5eb7c8ef6..f627fffc3 100644 --- a/src/cpp/controller/User.h +++ b/src/cpp/controller/User.h @@ -37,6 +37,9 @@ namespace controller { // TODO: instead scheduling all, scheduling only for next day and run this function every day (own task for that) static int checkIfVerificationEmailsShouldBeResend(const Poco::Util::Timer& timer); + //! \brief go through whole db and search for user without email hash and set this in db + static int addMissingEmailHashes(); + //! \brief try to find correct passphrase for this user from db //! //! select entries from user_backups db table belonging to user @@ -53,6 +56,7 @@ namespace controller { //! \return count of found rows, should be 1 or 0 inline size_t load(int user_id) { return getModel()->loadFromDB("id", user_id); } int load(const unsigned char* pubkey_array); + int load(MemoryBin* emailHash); Poco::JSON::Object getJson(); inline Poco::AutoPtr getModel() { return _getModel(); } diff --git a/src/cpp/model/table/User.cpp b/src/cpp/model/table/User.cpp index 745982a25..11584659c 100644 --- a/src/cpp/model/table/User.cpp +++ b/src/cpp/model/table/User.cpp @@ -18,8 +18,9 @@ namespace model { } User::User(const std::string& email, const std::string& first_name, const std::string& last_name, Poco::UInt64 passwordHashed/* = 0*/, std::string languageKey/* = "de"*/) - : mEmail(email), mFirstName(first_name), mLastName(last_name), mPasswordHashed(passwordHashed), mEmailChecked(false), mLanguageKey(languageKey), mDisabled(false), mRole(ROLE_NOT_LOADED) + : mFirstName(first_name), mLastName(last_name), mPasswordHashed(passwordHashed), mEmailChecked(false), mLanguageKey(languageKey), mDisabled(false), mRole(ROLE_NOT_LOADED) { + setEmail(email); } //id, first_name, last_name, email, pubkey, created, email_checked @@ -60,18 +61,31 @@ namespace model { } } + void User::setEmail(const std::string& email) + { + std::unique_lock _lock(mSharedMutex); + mEmail = email; + + unsigned char email_hash[crypto_generichash_BYTES]; + + crypto_generichash(email_hash, crypto_generichash_BYTES, + (const unsigned char*)email.data(), email.size(), + NULL, 0); + mEmailHash = Poco::Nullable(Poco::Data::BLOB(email_hash, crypto_generichash_BYTES)); + } + Poco::Data::Statement User::_insertIntoDB(Poco::Data::Session session) { Poco::Data::Statement insert(session); if (mPasswordHashed) { - insert << "INSERT INTO users (email, first_name, last_name, password, language) VALUES(?,?,?,?,?);", - use(mEmail), use(mFirstName), use(mLastName), bind(mPasswordHashed), use(mLanguageKey); + insert << "INSERT INTO users (email, first_name, last_name, password, email_hash, language) VALUES(?,?,?,?,?,?);", + use(mEmail), use(mFirstName), use(mLastName), bind(mPasswordHashed), use(mEmailHash), use(mLanguageKey); } else { - insert << "INSERT INTO users (email, first_name, last_name, language) VALUES(?,?,?,?);", - use(mEmail), use(mFirstName), use(mLastName), use(mLanguageKey); + insert << "INSERT INTO users (email, first_name, last_name, email_hash, language) VALUES(?,?,?,?,?);", + use(mEmail), use(mFirstName), use(mLastName), use(mEmailHash), use(mLanguageKey); } return insert; @@ -84,12 +98,12 @@ namespace model { _fieldName = getTableName() + std::string(".id"); } Poco::Data::Statement select(session); - select << "SELECT " << getTableName() << ".id, email, first_name, last_name, password, pubkey, privkey, created, email_checked, language, disabled, user_roles.role_id " + select << "SELECT " << getTableName() << ".id, email, first_name, last_name, password, pubkey, privkey, email_hash, created, email_checked, language, disabled, user_roles.role_id " << " FROM " << getTableName() << " LEFT JOIN user_roles ON " << getTableName() << ".id = user_roles.user_id " << " WHERE " << _fieldName << " = ?" , into(mID), into(mEmail), into(mFirstName), into(mLastName), into(mPasswordHashed), - into(mPublicKey), into(mPrivateKey), into(mCreated), into(mEmailChecked), + into(mPublicKey), into(mPrivateKey), into(mEmailHash), into(mCreated), into(mEmailChecked), into(mLanguageKey), into(mDisabled), into(mRole); @@ -266,12 +280,14 @@ namespace model { auto mm = MemoryManager::getInstance(); auto pubkeyHex = mm->getFreeMemory(65); auto privkeyHex = mm->getFreeMemory(161); + auto email_hash = mm->getFreeMemory(crypto_generichash_BYTES+1); //char pubkeyHex[65], privkeyHex[161]; //memset(pubkeyHex, 0, 65); //memset(privkeyHex, 0, 161); memset(*pubkeyHex, 0, 65); memset(*privkeyHex, 0, 161); + memset(*email_hash, 0, crypto_generichash_BYTES + 1); std::stringstream ss; @@ -281,11 +297,16 @@ namespace model { if (!mPrivateKey.isNull()) { sodium_bin2hex(*privkeyHex, 161, mPrivateKey.value().content().data(), mPrivateKey.value().content().size()); } + if (!mEmailHash.isNull()) { + sodium_bin2hex(*email_hash, crypto_generichash_BYTES + 1, mEmailHash.value().content().data(), mEmailHash.value().content().size()); + } + ss << mFirstName << " " << mLastName << " <" << mEmail << ">" << std::endl; ss << "password hash: " << mPasswordHashed << std::endl; ss << "public key: " << (char*)*pubkeyHex << std::endl; ss << "private key: " << (char*)*privkeyHex << std::endl; + ss << "email hash: " << (char*)*email_hash << std::endl; ss << "created: " << Poco::DateTimeFormatter::format(mCreated, "%f.%m.%Y %H:%M:%S") << std::endl; ss << "email checked: " << mEmailChecked << std::endl; ss << "language key: " << mLanguageKey << std::endl; @@ -293,6 +314,7 @@ namespace model { mm->releaseMemory(pubkeyHex); mm->releaseMemory(privkeyHex); + mm->releaseMemory(email_hash); return ss.str(); } @@ -301,8 +323,10 @@ namespace model { { auto mm = MemoryManager::getInstance(); auto pubkeyHex = mm->getFreeMemory(65); + auto email_hash = mm->getFreeMemory(crypto_generichash_BYTES + 1); memset(*pubkeyHex, 0, 65); + memset(*email_hash, 0, crypto_generichash_BYTES + 1); std::stringstream ss; @@ -310,8 +334,13 @@ namespace model { sodium_bin2hex(*pubkeyHex, 65, mPublicKey.value().content().data(), mPublicKey.value().content().size()); } + if (!mEmailHash.isNull()) { + sodium_bin2hex(*email_hash, crypto_generichash_BYTES + 1, mEmailHash.value().content().data(), mEmailHash.value().content().size()); + } + ss << "" << mFirstName << " " << mLastName << " <" << mEmail << ">" << "
"; ss << "public key: " << (char*)*pubkeyHex << "
"; + ss << "email hash: " << (char*)*email_hash << "
"; ss << "created: " << Poco::DateTimeFormatter::format(mCreated, "%f.%m.%Y %H:%M:%S") << "
"; ss << "email checked: " << mEmailChecked << "
"; ss << "language key: " << mLanguageKey << "
"; @@ -319,6 +348,7 @@ namespace model { ss << "disabled: " << mDisabled << "
"; mm->releaseMemory(pubkeyHex); + mm->releaseMemory(email_hash); return ss.str(); } diff --git a/src/cpp/model/table/User.h b/src/cpp/model/table/User.h index 3a6164232..d7523d155 100644 --- a/src/cpp/model/table/User.h +++ b/src/cpp/model/table/User.h @@ -61,17 +61,19 @@ namespace model { inline std::string getNameWithEmail() const { std::shared_lock _lock(mSharedMutex); return mFirstName + " " + mLastName + "<" + mEmail + ">"; } inline const Poco::UInt64 getPasswordHashed() const { std::shared_lock _lock(mSharedMutex); return mPasswordHashed; } inline RoleType getRole() const { std::shared_lock _lock(mSharedMutex); if (mRole.isNull()) return ROLE_NONE; return static_cast(mRole.value()); } - inline const unsigned char* getPublicKey() const { if (mPublicKey.isNull()) return nullptr; return mPublicKey.value().content().data(); } + inline const unsigned char* getPublicKey() const { std::shared_lock _lock(mSharedMutex); if (mPublicKey.isNull()) return nullptr; return mPublicKey.value().content().data(); } + inline size_t getPublicKeySize() const { std::shared_lock _lock(mSharedMutex); if (mPublicKey.isNull()) return 0; return mPublicKey.value().content().size(); } std::string getPublicKeyHex() const; inline bool hasPrivateKeyEncrypted() const { std::shared_lock _lock(mSharedMutex); return !mPrivateKey.isNull(); } + inline bool hasEmailHash() const { std::shared_lock _lock(mSharedMutex); return !mEmailHash.isNull(); } inline const std::vector& getPrivateKeyEncrypted() const { return mPrivateKey.value().content(); } inline bool isEmailChecked() const { std::shared_lock _lock(mSharedMutex); return mEmailChecked; } inline const std::string getLanguageKey() const { std::shared_lock _lock(mSharedMutex); return mLanguageKey; } inline bool isDisabled() const { std::shared_lock _lock(mSharedMutex); return mDisabled; } // default setter unlocked - inline void setEmail(const std::string& email) { std::unique_lock _lock(mSharedMutex); mEmail = email; } + void setEmail(const std::string& email); inline void setFirstName(const std::string& first_name) { std::unique_lock _lock(mSharedMutex); mFirstName = first_name; } inline void setLastName(const std::string& last_name) { std::unique_lock _lock(mSharedMutex); mLastName = last_name; } inline void setPasswordHashed(const Poco::UInt64& passwordHashed) { std::unique_lock _lock(mSharedMutex); mPasswordHashed = passwordHashed; } @@ -102,6 +104,7 @@ namespace model { Poco::Nullable mPublicKey; Poco::Nullable mPrivateKey; + Poco::Nullable mEmailHash; // sodium generic hash (currently blake2b) // created: Mysql DateTime Poco::DateTime mCreated;