diff --git a/libretroshare/src/jsonapi/jsonapi.cpp b/libretroshare/src/jsonapi/jsonapi.cpp index c219896d4..3dcde2a27 100644 --- a/libretroshare/src/jsonapi/jsonapi.cpp +++ b/libretroshare/src/jsonapi/jsonapi.cpp @@ -165,6 +165,7 @@ JsonApiServer::JsonApiServer(): configMutex("JsonApiServer config"), RsThread::setStopTimeout(10); #endif +#if !RS_VERSION_AT_LEAST(0,6,6) registerHandler("/rsLoginHelper/createLocation", [this](const std::shared_ptr session) { @@ -215,8 +216,9 @@ JsonApiServer::JsonApiServer(): configMutex("JsonApiServer config"), DEFAULT_API_CALL_JSON_RETURN(rb::OK); } ); }, false); +#endif // !RS_VERSION_AT_LEAST(0,6,6) - registerHandler("/rsLoginHelper/attemptLogin", + registerHandler("/rsLoginHelper/createLocationV2", [this](const std::shared_ptr session) { auto reqSize = session->get_request()->get_header("Content-Length", 0); @@ -226,25 +228,51 @@ JsonApiServer::JsonApiServer(): configMutex("JsonApiServer config"), { INITIALIZE_API_CALL_JSON_CONTEXT; - RsPeerId account; + RsPeerId locationId; + RsPgpId pgpId; + std::string locationName; + std::string pgpName; std::string password; + // JSON API only + std::string apiUser; + std::string apiPass; + // deserialize input parameters from JSON { RsGenericSerializer::SerializeContext& ctx(cReq); RsGenericSerializer::SerializeJob j(RsGenericSerializer::FROM_JSON); - RS_SERIAL_PROCESS(account); + RS_SERIAL_PROCESS(locationId); + RS_SERIAL_PROCESS(pgpId); + RS_SERIAL_PROCESS(locationName); + RS_SERIAL_PROCESS(pgpName); RS_SERIAL_PROCESS(password); + + // JSON API only + RS_SERIAL_PROCESS(apiUser); + RS_SERIAL_PROCESS(apiPass); } - // call retroshare C++ API - RsInit::LoadCertificateStatus retval = - rsLoginHelper->attemptLogin(account, password); + std::error_condition retval; + + if(apiUser.empty()) + retval = RsJsonApiErrorNum::TOKEN_FORMAT_INVALID; + + if(!retval) + retval = badApiCredientalsFormat(apiUser, apiPass); + + if(!retval) // call retroshare C++ API + retval = rsLoginHelper->createLocationV2( + locationId, pgpId, locationName, pgpName, password ); + + if(!retval) retval = authorizeUser(apiUser, apiPass); // serialize out parameters and return value to JSON { RsGenericSerializer::SerializeContext& ctx(cAns); RsGenericSerializer::SerializeJob j(RsGenericSerializer::TO_JSON); + RS_SERIAL_PROCESS(locationId); + RS_SERIAL_PROCESS(pgpId); RS_SERIAL_PROCESS(retval); } diff --git a/libretroshare/src/retroshare/rsinit.h b/libretroshare/src/retroshare/rsinit.h index 165649f16..19c0349bb 100644 --- a/libretroshare/src/retroshare/rsinit.h +++ b/libretroshare/src/retroshare/rsinit.h @@ -20,8 +20,7 @@ *******************************************************************************/ #pragma once -/// RetroShare initialization and login API - +/// @file RetroShare initialization and login API // Initialize ok, result >= 0 #define RS_INIT_OK 0 // Initialize ok @@ -32,11 +31,15 @@ #define RS_INIT_NO_KEYRING -3 // Keyring is empty. Need to import it. #define RS_INIT_NO_EXECUTABLE -4 // executable path hasn't been set in config options -#include #include #include #include -#include +#include +#include + +#include "retroshare/rstypes.h" +#include "retroshare/rsversion.h" + class RsLoginHelper; @@ -46,6 +49,71 @@ class RsLoginHelper; */ extern RsLoginHelper* rsLoginHelper; + +enum class RsInitErrorNum : int32_t +{ + ALREADY_LOGGED_IN = 6000, + CANT_ACQUIRE_LOCK = 6001, + INVALID_LOCATION_NAME = 6002, + PGP_NAME_OR_ID_NEEDED = 6003, + PGP_KEY_CREATION_FAILED = 6004, + SSL_KEY_CREATION_FAILED = 6005, + INVALID_SSL_ID = 6006, + LOGIN_FAILED = 6007 +}; + +struct RsInitErrorCategory: std::error_category +{ + const char* name() const noexcept override + { return "RetroShare init"; } + + std::string message(int ev) const override + { + switch (static_cast(ev)) + { + case RsInitErrorNum::ALREADY_LOGGED_IN: + return "Already logged in"; + case RsInitErrorNum::CANT_ACQUIRE_LOCK: + return "Cannot aquire lock on location data. Another instance is " + "already running with this profile?"; + case RsInitErrorNum::INVALID_LOCATION_NAME: + return "Invalid location name"; + case RsInitErrorNum::PGP_NAME_OR_ID_NEEDED: + return "Either PGP name or PGP id is needed"; + case RsInitErrorNum::PGP_KEY_CREATION_FAILED: + return "Failure creating PGP key"; + case RsInitErrorNum::SSL_KEY_CREATION_FAILED: + return "Failure creating SSL key"; + case RsInitErrorNum::INVALID_SSL_ID: + return "Invalid SSL id"; + case RsInitErrorNum::LOGIN_FAILED: + return "Generic login failure"; + default: + return rsErrorNotInCategory(ev, name()); + } + } + + const static RsInitErrorCategory instance; +}; + + +namespace std +{ +/** Register RsJsonApiErrorNum as an error condition enum, must be in std + * namespace */ +template<> struct is_error_condition_enum : true_type {}; +} + +/** Provide RsInitErrorNum conversion to std::error_condition, must be in + * same namespace of RsInitErrorNum */ +inline std::error_condition make_error_condition(RsInitErrorNum e) noexcept +{ + return std::error_condition( + static_cast(e), RsInitErrorCategory::instance ); +}; + + + /** * @brief The RsInitConfig struct * This class contains common configuration options, that executables using libretroshare may want to @@ -85,7 +153,7 @@ struct RsConfigOptions class RsInit { public: - enum LoadCertificateStatus : uint8_t + enum RS_DEPRECATED_FOR(RsInitErrorNum) LoadCertificateStatus : uint8_t { OK, /// Everything go as expected, no error occurred ERR_ALREADY_RUNNING, /// Another istance is running already @@ -317,7 +385,7 @@ public: /** * @brief Normal way to attempt login - * @jsonapi{development,manualwrapper} + * @jsonapi{development,unauthenticated} * @param[in] account Id of the account to which attempt login * @param[in] password Password for the given account * @return RsInit::OK if login attempt success, error code otherwhise @@ -353,6 +421,44 @@ public: void getLocations(std::vector& locations); /** + * @brief Creates a new RetroShare location, and log in once is created + * @jsonapi{development,manualwrapper} + * @param[out] locationId storage for generated location SSL id + * @param[inout] pgpId specify PGP id to use to sign the location, if a null + * id is passed the PGP key is created too and this param is used as + * storage for its id. + * @param[in] password to protect and unlock the associated PGP key + * param[in] apiUser (JSON API only) string containing username for JSON API + * so it can be later used to authenticate JSON API calls. It is passed + * down to @see RsJsonApi::authorizeUser under the hood. + * param[in] apiPass (JSON API only) string containing password for JSON API + * so it can be later used to authenticate JSON API calls. It is passed + * down to @see RsJsonApi::authorizeUser under the hood. + * To improve security we strongly advise to not use the same as the + * password used for the PGP key. + * @return Success or error information + */ + std::error_condition createLocationV2( + RsPeerId& locationId, + RsPgpId& pgpId, + const std::string& locationName, + const std::string& pgpName, + const std::string& password + /* JSON API only + * const std::string& apiUser + * const std::string& apiPass */ ); + + /** + * @brief Check if RetroShare is already logged in, this usually return true + * after a successfull attemptLogin() and before closeSession() + * @jsonapi{development,unauthenticated} + * @return true if already logged in, false otherwise + */ + bool isLoggedIn(); + +#if !RS_VERSION_AT_LEAST(0,6,6) + /** + * @deprecated Use @see createLocationV2 instead * @brief Creates a new RetroShare location, and log in once is created * @jsonapi{development,manualwrapper} * @param[inout] location provide input information to generate the location @@ -365,15 +471,9 @@ public: * Tor hidden location. UNTESTED! * @return true if success, false otherwise */ + RS_DEPRECATED_FOR(createLocationV2) bool createLocation( RsLoginHelper::Location& location, const std::string& password, std::string& errorMessage, bool makeHidden = false, bool makeAutoTor = false ); - - /** - * @brief Check if RetroShare is already logged in, this usually return true - * after a successfull attemptLogin() and before closeSession() - * @jsonapi{development,unauthenticated} - * @return true if already logged in, false otherwise - */ - bool isLoggedIn(); +#endif // !RS_VERSION_AT_LEAST(0,6,6) }; diff --git a/libretroshare/src/retroshare/rsjsonapi.h b/libretroshare/src/retroshare/rsjsonapi.h index 147011787..38e33490e 100644 --- a/libretroshare/src/retroshare/rsjsonapi.h +++ b/libretroshare/src/retroshare/rsjsonapi.h @@ -29,6 +29,7 @@ #include #include +#include "util/rsdebug.h" #include "util/rsmemory.h" class RsJsonApi; @@ -74,8 +75,7 @@ struct RsJsonApiErrorCategory: std::error_category case RsJsonApiErrorNum::NOT_A_MACHINE_GUN: return "Method must not be called in burst"; default: - return "Error message for error: " + std::to_string(ev) + - " not available in category: " + name(); + return rsErrorNotInCategory(ev, name()); } } diff --git a/libretroshare/src/rsserver/rsinit.cc b/libretroshare/src/rsserver/rsinit.cc index 25391fcbe..5dcccfe0c 100644 --- a/libretroshare/src/rsserver/rsinit.cc +++ b/libretroshare/src/rsserver/rsinit.cc @@ -114,6 +114,8 @@ RsLoginHelper* rsLoginHelper = nullptr; RsAccounts* rsAccounts = nullptr; +const RsInitErrorCategory RsInitErrorCategory::instance; + RsConfigOptions::RsConfigOptions() : autoLogin(false), @@ -1950,6 +1952,47 @@ void RsLoginHelper::getLocations(std::vector& store) } } +std::error_condition RsLoginHelper::createLocationV2( + RsPeerId& locationId, RsPgpId& pgpId, + const std::string& locationName, const std::string& pgpName, + const std::string& password ) +{ + if(isLoggedIn()) return RsInitErrorNum::ALREADY_LOGGED_IN; + if(locationName.empty()) return RsInitErrorNum::INVALID_LOCATION_NAME; + if(pgpId.isNull() && pgpName.empty()) + return RsInitErrorNum::PGP_NAME_OR_ID_NEEDED; + + std::string errorMessage; + if(pgpId.isNull() && !RsAccounts::GeneratePGPCertificate( + pgpName, "", password, pgpId, 4096, errorMessage ) ) + { + RS_ERR("Failure creating PGP key: ", errorMessage); + return RsInitErrorNum::PGP_KEY_CREATION_FAILED; + } + + std::string sslPassword = + RsRandom::random_alphaNumericString(RsInit::getSslPwdLen()); + + rsNotify->cachePgpPassphrase(password); + rsNotify->setDisableAskPassword(true); + + bool ret = RsAccounts::createNewAccount( + pgpId, "", locationName, "", false, false, sslPassword, + locationId, errorMessage ); + if(!ret) + { + RS_ERR("Failure creating SSL key: ", errorMessage); + return RsInitErrorNum::SSL_KEY_CREATION_FAILED; + } + + RsInit::LoadPassword(sslPassword); + ret = (RsInit::OK == attemptLogin(locationId, password)); + rsNotify->setDisableAskPassword(false); + + return (ret ? std::error_condition() : RsInitErrorNum::LOGIN_FAILED); +} + +#if !RS_VERSION_AT_LEAST(0,6,6) bool RsLoginHelper::createLocation( RsLoginHelper::Location& l, const std::string& password, std::string& errorMessage, bool makeHidden, bool makeAutoTor ) @@ -1991,6 +2034,7 @@ bool RsLoginHelper::createLocation( rsNotify->setDisableAskPassword(false); return ret; } +#endif // !RS_VERSION_AT_LEAST(0,6,6) bool RsLoginHelper::isLoggedIn() {