Implement JSON API HTTP Basic authentication

jsonapi-generator is now capable of generating API for headers outside
  retroshare/ directory
jsonapi-generator do a bit of methods parameter sanity check
JsonApiServer is now integrated in the rsinit hell like other services
Add *::exportGPGKeyPairToString to a bunch of classes in cascade
RsControl is now capable of calling back a function when retroshare is almost
  completely stopped, this is useful when running retroshare toghether with
  externally managed runloop such as QCoreApplication
Expose a bunch of methods through JSON API
retroshare-nogui remove some dead code and fix stopping from the RetroShare API
This commit is contained in:
Gioacchino Mazzurco 2018-09-19 21:28:26 +02:00
parent ac9350d375
commit eb77f921ec
No known key found for this signature in database
GPG Key ID: A1FBCA3872E87051
32 changed files with 816 additions and 398 deletions

View File

@ -55,7 +55,7 @@ extern RsGxsChannels* rsGxsChannels;
.Calling the JSON API with curl on the terminal
[source,bash]
--------------------------------------------------------------------------------
curl --data @paramethers.json http://127.0.0.1:9092/rsGxsChannels/createGroup
curl -u $API_USER --data @paramethers.json http://127.0.0.1:9092/rsGxsChannels/createGroup
--------------------------------------------------------------------------------
.JSON API call result
@ -76,7 +76,7 @@ least in two different ways.
.Calling the JSON API with GET method with curl on the terminal
[source,bash]
--------------------------------------------------------------------------------
curl --get --data-urlencode jsonData@paramethers.json \
curl -u $API_USER --get --data-urlencode jsonData@paramethers.json \
http://127.0.0.1:9092/rsGxsChannels/createGroup
--------------------------------------------------------------------------------
@ -85,12 +85,53 @@ equivalent to the following.
.Calling the JSON API with GET method and pre-encoded data with curl on the terminal
--------------------------------------------------------------------------------
curl http://127.0.0.1:9092/rsGxsChannels/createGroup?jsonData=%7B%0A%20%20%20%20%22group%22%3A%7B%0A%20%20%20%20%20%20%20%20%22mMeta%22%3A%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22mGroupName%22%3A%22JSON%20test%20group%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22mGroupFlags%22%3A4%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22mSignFlags%22%3A520%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%22mDescription%22%3A%22JSON%20test%20group%20description%22%0A%20%20%20%20%7D%2C%0A%20%20%20%20%22caller_data%22%3A%22Here%20can%20go%20any%20kind%20of%20JSON%20data%20%28even%20objects%29%20that%20the%20caller%20want%20to%20get%20back%20together%20with%20the%20response%22%0A%7D
curl -u $API_USER http://127.0.0.1:9092/rsGxsChannels/createGroup?jsonData=%7B%0A%20%20%20%20%22group%22%3A%7B%0A%20%20%20%20%20%20%20%20%22mMeta%22%3A%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22mGroupName%22%3A%22JSON%20test%20group%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22mGroupFlags%22%3A4%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22mSignFlags%22%3A520%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%22mDescription%22%3A%22JSON%20test%20group%20description%22%0A%20%20%20%20%7D%2C%0A%20%20%20%20%22caller_data%22%3A%22Here%20can%20go%20any%20kind%20of%20JSON%20data%20%28even%20objects%29%20that%20the%20caller%20want%20to%20get%20back%20together%20with%20the%20response%22%0A%7D
--------------------------------------------------------------------------------
Note that using +GET+ method +?jsonData=+ and then the JSON data URL encoded are
added after the path in the HTTP request.
== JSON API authentication
Most of JSON API methods require authentication as they give access to
RetroShare user data, and we don't want any application running on the system
eventually by other users be able to access private data indiscriminately.
JSON API support HTTP Basic as authentication scheme, this is enough as JSON API
server is intented for usage on the same system (127.0.0.1) not over an
untrusted network.
If you need to use JSON API over an untrusted network consider using a reverse
proxy with HTTPS such as NGINX in front of JSON API server.
If RetroShare login has been effectuated through the JSON API you can use your
location SSLID as username and your PGP password as credential for the JSON API,
but we suggests you use specific meaningful and human readable credentials for
each JSON API client so the human user can have better control over which client
can access the JSON API.
.NewToken.json
[source,json]
--------------------------------------------------------------------------------
{
"token": "myNewUser:myNewPassword"
}
--------------------------------------------------------------------------------
.An authenticated client can authorize new tokens like this
--------------------------------------------------------------------------------
curl -u $API_USER --data @NewToken.json http://127.0.0.1:9092/jsonApiServer/authorizeToken
--------------------------------------------------------------------------------
.An unauthenticated JSON API client can request access with
--------------------------------------------------------------------------------
curl --data @NewToken.json http://127.0.0.1:9092/jsonApiServer/requestNewTokenAutorization
--------------------------------------------------------------------------------
When an unauthenticated client request his token to be authorized, JSON API
server will try to ask confirmation to the human user if possible through
+mNewAccessRequestCallback+, if it is not possible or the user didn't authorized
the token +false+ is returned.
== Offer new RetroShare services through JSON API
To offer a retroshare service through the JSON API, first of all one need find
@ -231,6 +272,17 @@ data: {"results":[{"size":668,"hash":"e8845280912ebf3779e400000000000000000000",
--------------------------------------------------------------------------------
By default JSON API methods requires client authentication and their wrappers
are automatically generated by +json-api-generator+.
In some cases methods need do be accessible without authentication such as
+rsLoginHelper/getLocations+ so in the doxygen documentaion they have the custom
command +@jsonapi{RS_VERSION,unauthenticated}+.
Other methods such as +/rsControl/rsGlobalShutDown+ need special care so they
are marked with the custom doxygen command +@jsonapi{RS_VERSION,manualwrapper}+
and their wrappers are not automatically generated but written manually into
+JsonApiServer::JsonApiServer(...)+.
== A bit of history
=== First writings about this

View File

@ -61,5 +61,5 @@ $%outputParamsSerialization%$
session->yield(message.str());
$%sessionDelayedClose%$
} );
});
}, $%requiresAuth%$);

View File

@ -2,6 +2,7 @@ DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "libretroshare"
ALIASES += jsonapi{1}="\xmlonly<jsonapi minversion=\"\1\"/>\endxmlonly"
ALIASES += jsonapi{2}="\xmlonly<jsonapi minversion=\"\1\" access=\"\2\"/>\endxmlonly"
# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
# documentation from any documented member that it re-implements.

View File

@ -22,6 +22,8 @@
#include <QDirIterator>
#include <QFileInfo>
#include <iterator>
#include <functional>
#include <QVariant>
struct MethodParam
{
@ -40,7 +42,10 @@ struct MethodParam
int main(int argc, char *argv[])
{
if(argc != 3)
qFatal("Usage: jsonapi-generator SOURCE_PATH OUTPUT_PATH");
{
qDebug() << "Usage: jsonapi-generator SOURCE_PATH OUTPUT_PATH";
return EINVAL;
}
QString sourcePath(argv[1]);
QString outputPath(argv[2]);
@ -59,6 +64,20 @@ int main(int argc, char *argv[])
qFatal(QString("Can't open: " + cppApiIncludesFilePath).toLatin1().data());
QSet<QString> cppApiIncludesSet;
auto fatalError = [&](
std::initializer_list<QVariant> errors, int ernum = EINVAL )
{
QString errorMsg;
for(const QVariant& error: errors)
errorMsg += error.toString() + " ";
errorMsg.chop(1);
QByteArray cppError(QString("#error "+errorMsg+"\n").toLocal8Bit());
wrappersDefFile.write(cppError);
cppApiIncludesFile.write(cppError);
qDebug() << errorMsg;
return ernum;
};
QDirIterator it(doxPrefix, QStringList() << "*8h.xml", QDir::Files);
while(it.hasNext())
{
@ -74,9 +93,10 @@ int main(int argc, char *argv[])
continue;
}
QFileInfo hfi(hFile);
QString headerFileName(hfi.fileName());
headerFileName.replace(QString("_8h.xml"), QString(".h"));
QFileInfo headerFileInfo(hDoc.elementsByTagName("location").at(0)
.attributes().namedItem("file").nodeValue());
QString headerRelPath = headerFileInfo.dir().dirName() + "/" +
headerFileInfo.fileName();
QDomNodeList sectiondefs = hDoc.elementsByTagName("memberdef");
for(int j = 0; j < sectiondefs.size(); ++j)
@ -112,6 +132,7 @@ int main(int argc, char *argv[])
QDomNode member = members.item(i);
QString refid(member.attributes().namedItem("refid").nodeValue());
QString methodName(member.firstChildElement("name").toElement().text());
bool requiresAuth = true;
QString defFilePath(doxPrefix + refid.split('_')[0] + ".xml");
qDebug() << "Looking for" << typeName << methodName << "into"
@ -134,10 +155,21 @@ int main(int argc, char *argv[])
QDomElement tmpMBD = memberdefs.item(k).toElement();
QString tmpId = tmpMBD.attributes().namedItem("id").nodeValue();
QString tmpKind = tmpMBD.attributes().namedItem("kind").nodeValue();
bool hasJsonApi = !tmpMBD.elementsByTagName("jsonapi").isEmpty();
if( tmpId == refid && tmpKind == "function" && hasJsonApi )
QDomNodeList tmpJsonApiTagList(tmpMBD.elementsByTagName("jsonapi"));
if( tmpJsonApiTagList.count() && tmpId == refid &&
tmpKind == "function")
{
QDomElement tmpJsonApiTag = tmpJsonApiTagList.item(0).toElement();
QString tmpAccessValue;
if(tmpJsonApiTag.hasAttribute("access"))
tmpAccessValue = tmpJsonApiTag.attributeNode("access").nodeValue();
requiresAuth = "unauthenticated" != tmpAccessValue;
if("manualwrapper" != tmpAccessValue)
memberdef = tmpMBD;
break;
}
}
@ -189,6 +221,7 @@ int main(int argc, char *argv[])
pType.replace(QString("&"), QString());
pType.replace(QString(" "), QString());
}
paramsMap.insert(tmpParam.name, tmpParam);
orderedParamNames.push_back(tmpParam.name);
}
@ -211,6 +244,16 @@ int main(int argc, char *argv[])
}
}
// Params sanity check
for( const MethodParam& pm : paramsMap)
if( !(pm.isMultiCallback || pm.isSingleCallback
|| pm.in || pm.out) )
return fatalError(
{ "Parameter:", pm.name, "of:", apiPath,
"declared in:", headerRelPath,
"miss doxygen parameter direction attribute!",
defFile.fileName() });
QString functionCall("\t\t");
if(retvalType != "void")
{
@ -325,7 +368,6 @@ int main(int argc, char *argv[])
substitutionsMap.insert("inputParamsDeserialization", inputParamsDeserialization);
substitutionsMap.insert("outputParamsSerialization", outputParamsSerialization);
substitutionsMap.insert("instanceName", instanceName);
substitutionsMap.insert("headerFileName", headerFileName);
substitutionsMap.insert("functionCall", functionCall);
substitutionsMap.insert("apiPath", apiPath);
substitutionsMap.insert("sessionEarlyClose", sessionEarlyClose);
@ -334,6 +376,7 @@ int main(int argc, char *argv[])
substitutionsMap.insert("callbackName", callbackName);
substitutionsMap.insert("callbackParams", callbackParams);
substitutionsMap.insert("callbackParamsSerialization", callbackParamsSerialization);
substitutionsMap.insert("requiresAuth", requiresAuth ? "true" : "false");
QString templFilePath(sourcePath);
if(hasMultiCallback || hasSingleCallback)
@ -351,7 +394,7 @@ int main(int argc, char *argv[])
wrappersDefFile.write(wrapperDef.toLocal8Bit());
cppApiIncludesSet.insert("#include \"retroshare/" + headerFileName + "\"\n");
cppApiIncludesSet.insert("#include \"" + headerRelPath + "\"\n");
}
}
}

View File

@ -44,5 +44,5 @@ $%outputParamsSerialization%$
// return them to the API caller
DEFAULT_API_CALL_JSON_RETURN(rb::OK);
} );
});
}, $%requiresAuth%$);

View File

@ -432,8 +432,7 @@ void RsControlModule::handleLogin(Request &req, Response &resp)
void RsControlModule::handleShutdown(Request &, Response &resp)
{
RS_STACK_MUTEX(mExitFlagMtx); // ********** LOCKED **********
mProcessShouldExit = true;
requestShutdown();
resp.setOk();
}
@ -582,6 +581,12 @@ bool RsControlModule::askForDeferredSelfSignature(const void *data, const uint32
}
}
bool RsControlModule::requestShutdown()
{
RS_STACK_MUTEX(mExitFlagMtx);
mProcessShouldExit = true;
}
void RsControlModule::setRunState(RunState s, std::string errstr)
{
RS_STACK_MUTEX(mDataMtx); // ********** LOCKED **********

View File

@ -62,6 +62,8 @@ public:
virtual bool askForPassword(const std::string &title, const std::string& key_details, bool prev_is_bad , std::string& password,bool& canceled) override;
virtual bool askForDeferredSelfSignature(const void *data, const uint32_t len, unsigned char *sign, unsigned int *signlen,int& signature_result, std::string reason = "") override;
virtual bool requestShutdown();
protected:
// from RsThread
// wee need a thread to call into things which block like askForPassword()

View File

@ -18,6 +18,7 @@
#include "jsonapi.h"
#include <string>
#include <sstream>
#include <memory>
#include <restbed>
@ -27,10 +28,17 @@
#include "retroshare/rsfiles.h"
#include "util/radix64.h"
#include "retroshare/rsversion.h"
#include "retroshare/rsinit.h"
#include "util/rsnet.h"
#include "retroshare/rsiface.h"
#include "retroshare/rsinit.h"
#include "util/rsurl.h"
// Generated at compile time
#include "jsonapi-includes.inl"
/*extern*/ JsonApiServer* jsonApiServer = nullptr;
#define INITIALIZE_API_CALL_JSON_CONTEXT \
RsGenericSerializer::SerializeContext cReq( \
nullptr, 0, \
@ -71,10 +79,10 @@ static bool checkRsServicePtrReady(
{
if(serviceInstance) return true;
std::string jsonApiError;
std::string jsonApiError = __PRETTY_FUNCTION__;
jsonApiError += "Service: ";
jsonApiError += serviceName;
jsonApiError += " not initialized! Are you sure you logged in already?";
jsonApiError += " not initialized!";
RsGenericSerializer::SerializeJob j(RsGenericSerializer::TO_JSON);
RS_SERIAL_PROCESS(jsonApiError);
@ -84,13 +92,53 @@ static bool checkRsServicePtrReady(
return false;
}
JsonApiServer::JsonApiServer(
uint16_t port, const std::string& bindAddress,
const std::function<void(int)> shutdownCallback ) :
mPort(port), mBindAddress(bindAddress), mShutdownCallback(shutdownCallback)
JsonApiServer::JsonApiServer(uint16_t port, const std::string& bindAddress,
const std::function<bool(const std::string&)> newAccessRequestCallback ) :
mPort(port), mBindAddress(bindAddress),
mNewAccessRequestCallback(newAccessRequestCallback),
configMutex("JsonApiServer config")
{
registerHandler("/jsonApiServer/shutdown",
registerHandler("/rsLoginHelper/attemptLogin",
[this](const std::shared_ptr<rb::Session> session)
{
size_t reqSize = session->get_request()->get_header("Content-Length", 0);
session->fetch( reqSize, [this](
const std::shared_ptr<rb::Session> session,
const rb::Bytes& body )
{
INITIALIZE_API_CALL_JSON_CONTEXT;
RsPeerId account;
std::string password;
// deserialize input parameters from JSON
{
RsGenericSerializer::SerializeContext& ctx(cReq);
RsGenericSerializer::SerializeJob j(RsGenericSerializer::FROM_JSON);
RS_SERIAL_PROCESS(account);
RS_SERIAL_PROCESS(password);
}
// call retroshare C++ API
RsInit::LoadCertificateStatus retval =
rsLoginHelper->attemptLogin(account, password);
if( retval == RsInit::OK )
authorizeToken(account.toStdString()+":"+password);
// serialize out parameters and return value to JSON
{
RsGenericSerializer::SerializeContext& ctx(cAns);
RsGenericSerializer::SerializeJob j(RsGenericSerializer::TO_JSON);
RS_SERIAL_PROCESS(retval);
}
// return them to the API caller
DEFAULT_API_CALL_JSON_RETURN(rb::OK);
} );
}, false);
registerHandler("/rsControl/rsGlobalShutDown",
[this](const std::shared_ptr<rb::Session> session)
{
size_t reqSize = session->get_request()->get_header("Content-Length", 0);
@ -100,37 +148,9 @@ JsonApiServer::JsonApiServer(
{
INITIALIZE_API_CALL_JSON_CONTEXT;
DEFAULT_API_CALL_JSON_RETURN(rb::OK);
shutdown();
} );
});
registerHandler("/jsonApiServer/version",
[](const std::shared_ptr<rb::Session> session)
{
size_t reqSize = session->get_request()->get_header("Content-Length", 0);
session->fetch( reqSize, [](
const std::shared_ptr<rb::Session> session,
const rb::Bytes& body )
{
INITIALIZE_API_CALL_JSON_CONTEXT;
uint32_t major = RS_MAJOR_VERSION;
uint32_t minor = RS_MINOR_VERSION;
uint32_t mini = RS_MINI_VERSION;
std::string extra = RS_EXTRA_VERSION;
std::string human = RS_HUMAN_READABLE_VERSION;
RsGenericSerializer::SerializeContext& ctx(cAns);
RsGenericSerializer::SerializeJob j(RsGenericSerializer::TO_JSON);
RS_SERIAL_PROCESS(major);
RS_SERIAL_PROCESS(minor);
RS_SERIAL_PROCESS(mini);
RS_SERIAL_PROCESS(extra);
RS_SERIAL_PROCESS(human);
DEFAULT_API_CALL_JSON_RETURN(rb::OK);
} );
rsControl->rsGlobalShutDown();
} );
}, true);
registerHandler("/rsFiles/getFileData",
[](const std::shared_ptr<rb::Session> session)
@ -186,7 +206,7 @@ JsonApiServer::JsonApiServer(
DEFAULT_API_CALL_JSON_RETURN(rb::OK);
} );
});
}, true);
// Generated at compile time
#include "jsonapi-wrappers.inl"
@ -198,22 +218,173 @@ void JsonApiServer::run()
settings->set_port(mPort);
settings->set_bind_address(mBindAddress);
settings->set_default_header("Cache-Control", "no-cache");
{
sockaddr_storage tmp;
sockaddr_storage_inet_pton(tmp, mBindAddress);
sockaddr_storage_setport(tmp, mPort);
sockaddr_storage_ipv6_to_ipv4(tmp);
RsUrl tmpUrl(sockaddr_storage_tostring(tmp));
tmpUrl.setScheme("http");
std::cerr << "JSON API listening on " << tmpUrl.toString()
<< std::endl;
}
mService.start(settings);
}
void JsonApiServer::registerHandler(
const std::string& path,
const std::function<void (const std::shared_ptr<restbed::Session>)>& handler)
const std::function<void (const std::shared_ptr<restbed::Session>)>& handler,
bool requiresAutentication )
{
std::shared_ptr<restbed::Resource> resource(new rb::Resource);
resource->set_path(path);
resource->set_method_handler("GET", handler);
resource->set_method_handler("POST", handler);
if(requiresAutentication)
resource->set_authentication_handler(
[this](
const std::shared_ptr<rb::Session> session,
const std::function<void (const std::shared_ptr<rb::Session>)>& callback )
{
if(!rsLoginHelper->isLoggedIn())
{
session->close(rb::CONFLICT);
return;
}
std::istringstream authHeader;
authHeader.str(session->get_request()->get_header("Authorization"));
std::string authToken;
std::getline(authHeader, authToken, ' ');
if(authToken != "Basic")
{
session->close(rb::UNAUTHORIZED);
return;
}
std::getline(authHeader, authToken, ' ');
authToken = decodeToken(authToken);
if(isAuthTokenValid(authToken)) callback(session);
else session->close(rb::UNAUTHORIZED);
} );
mService.publish(resource);
}
void JsonApiServer::shutdown(int exitCode)
void JsonApiServer::setNewAccessRequestCallback(
const std::function<bool (const std::string&)>& callback )
{ mNewAccessRequestCallback = callback; }
void JsonApiServer::shutdown() { mService.stop(); }
bool JsonApiServer::requestNewTokenAutorization(const std::string& token)
{
mService.stop();
mShutdownCallback(exitCode);
if(rsLoginHelper->isLoggedIn() && mNewAccessRequestCallback(token))
return authorizeToken(token);
return false;
}
bool JsonApiServer::isAuthTokenValid(const std::string& token)
{
RS_STACK_MUTEX(configMutex);
return mAuthTokenStorage.mAuthorizedTokens.count(token);
}
std::set<std::string> JsonApiServer::getAuthorizedTokens()
{
RS_STACK_MUTEX(configMutex);
return mAuthTokenStorage.mAuthorizedTokens;
}
bool JsonApiServer::revokeAuthToken(const std::string& token)
{
RS_STACK_MUTEX(configMutex);
if(mAuthTokenStorage.mAuthorizedTokens.erase(token))
{
IndicateConfigChanged();
return true;
}
return false;
}
bool JsonApiServer::authorizeToken(const std::string& token)
{
if(token.empty()) return false;
RS_STACK_MUTEX(configMutex);
if(mAuthTokenStorage.mAuthorizedTokens.insert(token).second)
{
IndicateConfigChanged();
return true;
}
return false;
}
/*static*/ std::string JsonApiServer::decodeToken(const std::string& token)
{
std::vector<uint8_t> decodedVect(Radix64::decode(token));
std::string decodedToken(
reinterpret_cast<const char*>(&decodedVect[0]),
decodedVect.size() );
return decodedToken;
}
/*static*/ std::string JsonApiServer::encondeToken(const std::string& token)
{
std::string encoded;
Radix64::encode( reinterpret_cast<const uint8_t*>(token.c_str()),
token.length(), encoded );
return encoded;
}
/*static*/ void JsonApiServer::version(
uint32_t& major, uint32_t& minor, uint32_t& mini, std::string& extra,
std::string& human )
{
major = RS_MAJOR_VERSION;
minor = RS_MINOR_VERSION;
mini = RS_MINI_VERSION;
extra = RS_EXTRA_VERSION;
human = RS_HUMAN_READABLE_VERSION;
}
RsSerialiser* JsonApiServer::setupSerialiser()
{
RsSerialiser* rss = new RsSerialiser;
rss->addSerialType(new JsonApiConfigSerializer);
return rss;
}
bool JsonApiServer::saveList(bool& cleanup, std::list<RsItem*>& saveItems)
{
cleanup = false;
configMutex.lock();
saveItems.push_back(&mAuthTokenStorage);
return true;
}
bool JsonApiServer::loadList(std::list<RsItem*>& loadList)
{
for(RsItem* it : loadList)
switch (static_cast<JsonApiItemsType>(it->PacketSubType()))
{
case JsonApiItemsType::AuthTokenItem:
mAuthTokenStorage = *static_cast<JsonApiServerAuthTokenStorage*>(it);
delete it;
break;
default:
delete it;
break;
}
return true;
}
void JsonApiServer::saveDone() { configMutex.unlock(); }

View File

@ -15,36 +15,59 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <string>
#include <memory>
#include <restbed>
#include <cstdint>
#include "util/rsthreads.h"
#include "pqi/p3cfgmgr.h"
#include "rsitems/rsitem.h"
#include "jsonapi/jsonapiitems.h"
#include "util/rsthreads.h"
namespace rb = restbed;
struct JsonApiServer;
/**
* Pointer to global instance of JsonApiServer
* @jsonapi{development}
*/
extern JsonApiServer* jsonApiServer;
/**
* Simple usage
* \code{.cpp}
* JsonApiServer jas(9092);
* jsonApiServer = &jas;
* jas.start("JsonApiServer");
* \endcode
* Uses p3Config to securely store persistent JSON API authorization tokens
*/
struct JsonApiServer : RsSingleJobThread
struct JsonApiServer : RsSingleJobThread, p3Config
{
/**
* @brief construct a JsonApiServer instance with given parameters
* @param[in] port listening port fpt the JSON API socket
* @param[in] bindAddress binding address for the JSON API socket
* @param newAccessRequestCallback called when a new auth token is asked to
* be authorized via JSON API, the auth token is passed as parameter, and
* the callback should return true if the new token get access granted and
* false otherwise, this usually requires user interacion to confirm access
*/
JsonApiServer(
uint16_t port = 9092,
const std::string& bindAddress = "127.0.0.1",
const std::function<void(int)> shutdownCallback = [](int){} );
/// @see RsSingleJobThread
virtual void run();
const std::function<bool(const std::string&)> newAccessRequestCallback = [](const std::string&){return false;} );
/**
* @param[in] path Path itno which publish the API call
* @param[in] handler function which will be called to handle the requested
* @param[in] requiresAutentication specify if the API call must be
* autenticated or not
* path, the function must be declared like:
* \code{.cpp}
* void functionName(const shared_ptr<restbed::Session> session)
@ -52,24 +75,118 @@ struct JsonApiServer : RsSingleJobThread
*/
void registerHandler(
const std::string& path,
const std::function<void(const std::shared_ptr<rb::Session>)>& handler );
const std::function<void(const std::shared_ptr<rb::Session>)>& handler,
bool requiresAutentication = true );
/**
* @brief Shutdown the JSON API server and call shutdownCallback
* @jsonapi{development}
* Beware that this method shout down only the JSON API server instance not
* the whole RetroShare instance, this behaviour can be altered via
* shutdownCallback paramether of @see JsonApiServer::JsonApiServer
* This method is made available also via JSON API with path
* /jsonApiServer/shutdown
* @param exitCode just passed down to the shutdownCallback
* @brief Set new access request callback
* @param callback function to call when a new JSON API access is requested
*/
void shutdown(int exitCode = 0);
void setNewAccessRequestCallback(
const std::function<bool(const std::string&)>& callback );
/**
* @brief Shutdown the JSON API server
* Beware that this method shout down only the JSON API server instance not
*/
void shutdown();
/**
* @brief This function should be used by JSON API clients that aren't
* authenticated yet, to ask their token to be authorized, the success or
* failure will depend on mNewAccessRequestCallback return value, and it
* will likely need human user interaction in the process.
* @jsonapi{development,unauthenticated}
* @param[in] token token to autorize
* @return true if authorization succeded, false otherwise.
*/
bool requestNewTokenAutorization(const std::string& token);
/**
* @brief Check if given JSON API auth token is authorized
* @jsonapi{development}
* @param[in] token decoded
* @return tru if authorized, false otherwise
*/
bool isAuthTokenValid(const std::string& token);
/**
* @brief Get uthorized tokens
* @jsonapi{development}
* @return the set of authorized encoded tokens
*/
std::set<std::string> getAuthorizedTokens();
/**
* @brief Revoke given auth token
* @jsonapi{development}
* @param[in] token decoded
* @return true if the token has been revoked, false otherwise
*/
bool revokeAuthToken(const std::string& token);
/**
* @brief Add new auth token to the authorized set
* @jsonapi{development}
* @param[in] token toke to autorize decoded
* @return true if the token has been added to authorized, false if error
* occurred or if the token was already authorized
*/
bool authorizeToken(const std::string& token);
/**
* @brief Get decoded version of the given encoded token
* @jsonapi{development,unauthenticated}
* @param[in] token encoded
* @return token decoded
*/
static std::string decodeToken(const std::string& token);
/**
* @brief Get encoded version of the given decoded token
* @jsonapi{development,unauthenticated}
* @param[in] token decoded
* @return token encoded
*/
static std::string encondeToken(const std::string& token);
/**
* @brief Write version information to given paramethers
* @jsonapi{development,unauthenticated}
* @param[out] major storage
* @param[out] minor storage
* @param[out] mini storage
* @param[out] extra storage
* @param[out] human storage
*/
static void version( uint32_t& major, uint32_t& minor, uint32_t& mini,
std::string& extra, std::string&human );
/// @see RsSingleJobThread
virtual void run();
private:
/// @see p3Config::setupSerialiser
virtual RsSerialiser* setupSerialiser();
/// @see p3Config::saveList
virtual bool saveList(bool &cleanup, std::list<RsItem *>& saveItems);
/// @see p3Config::loadList
virtual bool loadList(std::list<RsItem *>& loadList);
/// @see p3Config::saveDone
virtual void saveDone();
const uint16_t mPort;
const std::string mBindAddress;
rb::Service mService;
const std::function<void(int)> mShutdownCallback;
/// Called when new JSON API auth token is requested to be authorized
std::function<bool(const std::string&)> mNewAccessRequestCallback;
/// Encrypted persistent storage for authorized JSON API tokens
JsonApiServerAuthTokenStorage mAuthTokenStorage;
RsMutex configMutex;
};

View File

@ -0,0 +1,69 @@
/*
* RetroShare JSON API
* Copyright (C) 2018 Gioacchino Mazzurco <gio@eigenlab.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <string>
#include <ctime>
#include <cstdint>
#include <set>
#include "rsitems/rsitem.h"
#include "rsitems/rsserviceids.h"
#include "serialiser/rsserializer.h"
#include "serialiser/rsserializable.h"
enum class JsonApiItemsType : uint8_t { AuthTokenItem = 0 };
struct JsonApiServerAuthTokenStorage : RsItem
{
JsonApiServerAuthTokenStorage() :
RsItem( RS_PKT_VERSION_SERVICE, RS_SERVICE_TYPE_JSONAPI,
static_cast<uint8_t>(JsonApiItemsType::AuthTokenItem) ) {}
/// @see RsSerializable
virtual void serial_process(RsGenericSerializer::SerializeJob j,
RsGenericSerializer::SerializeContext& ctx)
{
RS_SERIAL_PROCESS(mAuthorizedTokens);
}
/// @see RsItem
virtual void clear() { mAuthorizedTokens.clear(); }
std::set<std::string> mAuthorizedTokens;
};
struct JsonApiConfigSerializer : RsServiceSerializer
{
JsonApiConfigSerializer() : RsServiceSerializer(RS_SERVICE_TYPE_JSONAPI) {}
virtual ~JsonApiConfigSerializer() {}
RsItem* create_item(uint16_t service_id, uint8_t item_sub_id) const
{
if(service_id != RS_SERVICE_TYPE_JSONAPI) return nullptr;
switch(static_cast<JsonApiItemsType>(item_sub_id))
{
case JsonApiItemsType::AuthTokenItem: return new JsonApiServerAuthTokenStorage();
default: return nullptr;
}
}
};

View File

@ -909,7 +909,7 @@ rs_jsonapi {
# Force recalculation of libretroshare dependencies see https://stackoverflow.com/a/47884045
QMAKE_EXTRA_TARGETS += libretroshare
HEADERS += jsonapi/jsonapi.h
HEADERS += jsonapi/jsonapi.h jsonapi/jsonapiitems.h
SOURCES += jsonapi/jsonapi.cpp
}

View File

@ -709,6 +709,36 @@ bool PGPHandler::exportGPGKeyPair(const std::string& filename,const RsPgpId& exp
return true ;
}
bool PGPHandler::exportGPGKeyPairToString(
std::string& data, const RsPgpId& exportedKeyId,
bool includeSignatures, std::string& errorMsg ) const
{
RS_STACK_MUTEX(pgphandlerMtx);
const ops_keydata_t *pubkey = locked_getPublicKey(exportedKeyId,false);
if(!pubkey)
{
errorMsg = "Cannot output key " + exportedKeyId.toStdString() +
": not found in public keyring.";
return false;
}
const ops_keydata_t *seckey = locked_getSecretKey(exportedKeyId);
if(!seckey)
{
errorMsg = "Cannot output key " + exportedKeyId.toStdString() +
": not found in secret keyring.";
return false;
}
data = makeRadixEncodedPGPKey(pubkey, includeSignatures);
data += "\n";
data += makeRadixEncodedPGPKey(seckey, includeSignatures);
data += "\n";
return true;
}
bool PGPHandler::getGPGDetailsFromBinaryBlock(const unsigned char *mem_block,size_t mem_size,RsPgpId& key_id, std::string& name, std::list<RsPgpId>& signers) const
{
ops_keyring_t *tmp_keyring = allocateOPSKeyring();

View File

@ -99,6 +99,9 @@ class PGPHandler
bool importGPGKeyPair(const std::string& filename,RsPgpId& imported_id,std::string& import_error) ;
bool importGPGKeyPairFromString(const std::string& data,RsPgpId& imported_id,std::string& import_error) ;
bool exportGPGKeyPair(const std::string& filename,const RsPgpId& exported_id) const ;
bool exportGPGKeyPairToString(
std::string& data, const RsPgpId& exportedKeyId,
bool includeSignatures, std::string& errorMsg ) const;
bool availableGPGCertificatesWithPrivateKeys(std::list<RsPgpId>& ids);
bool GeneratePGPCertificate(const std::string& name, const std::string& email, const std::string& passwd, RsPgpId& pgpId, const int keynumbits, std::string& errString) ;

View File

@ -327,6 +327,14 @@ bool AuthGPG::exportProfile(const std::string& fname,const RsPgpId& exported_id)
return PGPHandler::exportGPGKeyPair(fname,exported_id) ;
}
bool AuthGPG::exportIdentityToString(
std::string& data, const RsPgpId& pgpId, bool includeSignatures,
std::string& errorMsg )
{
return PGPHandler::exportGPGKeyPairToString(
data, pgpId, includeSignatures, errorMsg);
}
bool AuthGPG::importProfile(const std::string& fname,RsPgpId& imported_id,std::string& import_error)
{
return PGPHandler::importGPGKeyPair(fname,imported_id,import_error) ;
@ -337,7 +345,6 @@ bool AuthGPG::importProfileFromString(const std::string &data, RsPgpId &gpg_id,
return PGPHandler::importGPGKeyPairFromString(data, gpg_id, import_error);
}
bool AuthGPG::active()
{
RsStackMutex stack(gpgMtxData); /******* LOCKED ******/

View File

@ -158,6 +158,9 @@ public:
virtual bool importProfile(const std::string& filename,RsPgpId& gpg_id,std::string& import_error) ;
virtual bool importProfileFromString(const std::string& data,RsPgpId& gpg_id,std::string& import_error) ;
virtual bool exportProfile(const std::string& filename,const RsPgpId& gpg_id) ;
virtual bool exportIdentityToString(
std::string& data, const RsPgpId& pgpId, bool includeSignatures,
std::string& errorMsg );
virtual bool removeKeysFromPGPKeyring(const std::set<RsPgpId> &pgp_ids,std::string& backup_file,uint32_t& error_code) ;

View File

@ -987,9 +987,9 @@ void p3ServiceControl::filterChangeAdded_locked(const RsPeerId &peerId, uint32_t
void p3ServiceControl::getPeersConnected(const uint32_t serviceId, std::set<RsPeerId> &peerSet)
void p3ServiceControl::getPeersConnected(uint32_t serviceId, std::set<RsPeerId> &peerSet)
{
RsStackMutex stack(mCtrlMtx); /***** LOCK STACK MUTEX ****/
RS_STACK_MUTEX(mCtrlMtx);
std::map<uint32_t, std::set<RsPeerId> >::iterator mit;
mit = mServicePeerMap.find(serviceId);
@ -1004,9 +1004,9 @@ void p3ServiceControl::getPeersConnected(const uint32_t serviceId, std::set<RsPe
}
bool p3ServiceControl::isPeerConnected(const uint32_t serviceId, const RsPeerId &peerId)
bool p3ServiceControl::isPeerConnected(uint32_t serviceId, const RsPeerId &peerId)
{
RsStackMutex stack(mCtrlMtx); /***** LOCK STACK MUTEX ****/
RS_STACK_MUTEX(mCtrlMtx);
std::map<uint32_t, std::set<RsPeerId> >::iterator mit;
mit = mServicePeerMap.find(serviceId);

View File

@ -95,8 +95,8 @@ virtual bool getServicePermissions(uint32_t serviceId, RsServicePermissions &per
virtual bool updateServicePermissions(uint32_t serviceId, const RsServicePermissions &permissions);
// Get List of Peers using this Service.
virtual void getPeersConnected(const uint32_t serviceId, std::set<RsPeerId> &peerSet);
virtual bool isPeerConnected(const uint32_t serviceId, const RsPeerId &peerId);
virtual void getPeersConnected(uint32_t serviceId, std::set<RsPeerId> &peerSet);
virtual bool isPeerConnected(uint32_t serviceId, const RsPeerId &peerId);
// Gets the list of items used by that service
virtual bool getServiceItemNames(uint32_t serviceId,std::map<uint8_t,std::string>& names) ;

View File

@ -204,7 +204,7 @@ public:
* as available or a sensible maximum. Expect a block size of around 1MiB.
* To get more data, call this function repeatedly with different offsets.
*
* jsonapi{development}
* @jsonapi{development,manualwrapper}
* note the missing @ the wrapper for this is written manually not
* autogenerated @see JsonApiServer.
*

View File

@ -200,8 +200,8 @@ public:
* @brief Share channel publishing key
* This can be used to authorize other peers to post on the channel
* @jsonapi{development}
* param[in] groupId Channel id
* param[in] peers peers to which share the key
* @param[in] groupId Channel id
* @param[in] peers peers to which share the key
* @return false on error, true otherwise
*/
virtual bool groupShareKeys(

View File

@ -19,24 +19,22 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
#ifndef RETROSHARE_GUI_INTERFACE_H
#define RETROSHARE_GUI_INTERFACE_H
#pragma once
#include "retroshare/rsnotify.h"
#include "rstypes.h"
#include <map>
#include <functional>
class NotifyBase;
class RsServer;
class RsInit;
class RsPeerCryptoParams;
struct TurtleFileInfo ;
/* RsInit -> Configuration Parameters for RetroShare Startup
*/
class RsControl;
/// RsInit -> Configuration Parameters for RetroShare Startup
RsInit* InitRsConfig();
/* extract various options for GUI */
const char *RsConfigDirectory(RsInit *config);
bool RsConfigStartMinimised(RsInit *config);
@ -46,35 +44,50 @@ void CleanupRsConfig(RsInit *);
// Called First... (handles comandline options)
int InitRetroShare(int argc, char **argv, RsInit *config);
class RsControl /* The Main Interface Class - for controlling the server */
/**
* Pointer to global instance of RsControl needed to expose JSON API
* @jsonapi{development}
*/
extern RsControl* rsControl;
/** The Main Interface Class - for controlling the server */
class RsControl
{
public:
/// TODO: This should return a reference instead of a pointer!
static RsControl* instance();
static void earlyInitNotificationSystem() { instance(); }
static void earlyInitNotificationSystem() { rsControl = instance(); }
/* Real Startup Fn */
virtual int StartupRetroShare() = 0;
/** Check if core is fully ready, true only after
* StartupRetroShare() finish and before rsGlobalShutDown() begin
/**
* @brief Check if core is fully ready, true only after StartupRetroShare()
* finish and before rsGlobalShutDown() begin
* @jsonapi{development}
*/
virtual bool isReady() = 0;
/****************************************/
/* Config */
virtual void ConfigFinalSave() = 0;
/**
* @brief Turn off RetroShare
* @jsonapi{development,manualwrapper}
*/
virtual void rsGlobalShutDown() = 0;
/****************************************/
virtual bool getPeerCryptoDetails(const RsPeerId& ssl_id,RsPeerCryptoParams& params) = 0;
virtual bool getPeerCryptoDetails(
const RsPeerId& ssl_id,RsPeerCryptoParams& params ) = 0;
virtual void getLibraries(std::list<RsLibraryInfo> &libraries) = 0;
/**
* @brief Set shutdown callback, useful if main runlop is controlled by
* another entity such as QCoreApplication
* @param callback function to call when shutdown is almost complete
*/
virtual void setShutdownCallback(const std::function<void(int)>& callback) = 0;
protected:
RsControl() {} // should not be used, hence it's private.
};
#endif

View File

@ -166,7 +166,12 @@ public:
static std::string systemDataDirectory(bool check = true);
static std::string PGPDirectory();
// PGP Accounts.
/**
* @brief Get available PGP identities id list
* @jsonapi{development,unauthenticated}
* @param[out] pgpIds storage for PGP id list
* @return true on success, false otherwise
*/
static int GetPGPLogins(std::list<RsPgpId> &pgpIds);
static int GetPGPLoginDetails(const RsPgpId& id, std::string &name, std::string &email);
static bool GeneratePGPCertificate(const std::string&, const std::string& email, const std::string& passwd, RsPgpId &pgpId, const int keynumbits, std::string &errString);
@ -174,7 +179,32 @@ public:
// PGP Support Functions.
static bool ExportIdentity(const std::string& fname,const RsPgpId& pgp_id) ;
static bool ImportIdentity(const std::string& fname,RsPgpId& imported_pgp_id,std::string& import_error) ;
static bool ImportIdentityFromString(const std::string& data,RsPgpId& imported_pgp_id,std::string& import_error) ;
/**
* @brief Import full encrypted PGP identity from string
* @jsonapi{development,unauthenticated}
* @param[in] data certificate string
* @param[out] pgpId storage for the PGP fingerprint of the imported key
* @param[out] errorMsg storage for eventual human readable error message
* @return true on success, false otherwise
*/
static bool ImportIdentityFromString(
const std::string& data, RsPgpId& pgpId,
std::string& errorMsg );
/**
* @brief Export full encrypted PGP identity to string
* @jsonapi{development}
* @param[out] data storage for certificate string
* @param[in] pgpId PGP id to export
* @param[in] includeSignatures true to include signatures
* @param[out] errorMsg storage for eventual human readable error message
* @return true on success, false otherwise
*/
static bool exportIdentityToString(
std::string& data, const RsPgpId& pgpId, std::string& errorMsg,
bool includeSignatures = true );
static void GetUnsupportedKeys(std::map<std::string,std::vector<std::string> > &unsupported_keys);
static bool CopyGnuPGKeyrings() ;
@ -233,7 +263,7 @@ struct RsLoginHelper
{
/**
* @brief Normal way to attempt login
* @jsonapi{development}
* @jsonapi{development,manualwrapper}
* @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
@ -255,14 +285,14 @@ struct RsLoginHelper
/**
* @brief Get locations and associated information
* @jsonapi{development}
* @jsonapi{development,unauthenticated}
* @param[out] locations storage for the retrived locations
*/
void getLocations(std::vector<RsLoginHelper::Location>& locations);
/**
* @brief Creates a new RetroShare location, and log in once is created
* @jsonapi{development}
* @jsonapi{development,unauthenticated}
* @param[inout] location provide input information to generate the location
* and storage to output the data of the generated location
* @param[in] password to protect and unlock the associated PGP key
@ -280,7 +310,7 @@ struct RsLoginHelper
/**
* @brief Check if RetroShare is already logged in, this usually return true
* after a successfull attemptLogin() and before closeSession()
* @jsonapi{development}
* @jsonapi{development,unauthenticated}
* @return true if already logged in, false otherwise
*/
bool isLoggedIn();

View File

@ -131,9 +131,9 @@ public:
virtual ~RsServiceControl(){}
/**
* @brief getOwnServices return a map off all services.
* @brief get a map off all services.
* @jsonapi{development}
* @param[out] info
* @param[out] info storage for service information
* @return always true
*/
virtual bool getOwnServices(RsPeerServiceInfo &info) = 0;
@ -197,7 +197,8 @@ public:
* @param[in] serviceId service to look up.
* @param[out] peerSet set of peers using this service.
*/
virtual void getPeersConnected(const uint32_t serviceId, std::set<RsPeerId> &peerSet) = 0;
virtual void getPeersConnected( uint32_t serviceId,
std::set<RsPeerId>& peerSet ) = 0;
};
#endif

View File

@ -79,6 +79,7 @@ const uint16_t RS_SERVICE_GXS_TYPE_GXSCIRCLE = 0x0218;
const uint16_t RS_SERVICE_GXS_TYPE_REPUTATION = 0x0219;
const uint16_t RS_SERVICE_TYPE_GXS_RECOGN = 0x0220;
const uint16_t RS_SERVICE_TYPE_GXS_TRANS = 0x0230;
const uint16_t RS_SERVICE_TYPE_JSONAPI = 0x0240;
const uint16_t RS_SERVICE_GXS_TYPE_FORUMS_CONFIG = 0x0315;
const uint16_t RS_SERVICE_GXS_TYPE_CHANNELS_CONFIG = 0x0317;

View File

@ -33,7 +33,7 @@
#include "retroshare/rsinit.h"
#include "plugins/pluginmanager.h"
#include "util/rsdebug.h"
//const int p3facemsgzone = 11453;
#include "jsonapi/jsonapi.h"
#include <sys/time.h>
#include <time.h>
@ -92,6 +92,10 @@ void RsServer::rsGlobalShutDown()
mNetMgr->shutdown(); /* Handles UPnP */
#ifdef RS_JSONAPI
if(jsonApiServer) jsonApiServer->shutdown();
#endif
rsAutoProxyMonitor::instance()->stopAllRSShutdown();
fullstop() ;
@ -117,4 +121,6 @@ void RsServer::rsGlobalShutDown()
// #endif
AuthGPG::exit();
mShutdownCallback(0);
}

View File

@ -55,6 +55,9 @@ int rsserverzone = 101;
#include <sys/timeb.h>
#endif
/*extern*/ RsControl* rsControl = nullptr;
static double getCurrentTS()
{

View File

@ -19,8 +19,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
#ifndef MRK_P3RS_INTERFACE_H
#define MRK_P3RS_INTERFACE_H
#pragma once
#include <functional>
//#include "server/filedexserver.h"
#include "ft/ftserver.h"
@ -75,18 +76,18 @@ class RsPluginManager;
class RsServer: public RsControl, public RsTickingThread
{
public:
/****************************************/
/* p3face-startup.cc: init... */
virtual int StartupRetroShare();
RsServer();
virtual ~RsServer();
/****************************************/
/* p3face.cc: main loop / util fns / locking. */
virtual int StartupRetroShare();
/// @see RsControl::isReady()
virtual bool isReady() { return coreReady; }
RsServer() ;
virtual ~RsServer();
/// @see RsControl::setShutdownCallback
void setShutdownCallback(const std::function<void(int)>& callback)
{ mShutdownCallback = callback; }
/* Thread Fn: Run the Core */
virtual void data_tick();
@ -202,6 +203,9 @@ class RsServer: public RsControl, public RsTickingThread
static const double maxTimeDelta;
static const double kickLimit;
/// @see RsControl::setShutdownCallback
std::function<void(int)> mShutdownCallback;
/** Keep track of the core being fully ready, true only after
* StartupRetroShare() finish and before rsGlobalShutDown() begin
*/
@ -213,5 +217,3 @@ class RsServer: public RsControl, public RsTickingThread
*/
std::string make_path_unix(std::string winpath);
#endif

View File

@ -930,6 +930,14 @@ bool RsAccountsDetail::importIdentityFromString(const std::string &data, RsPgpId
return AuthGPG::getAuthGPG()->importProfileFromString(data, imported_pgp_id, import_error);
}
bool RsAccountsDetail::exportIdentityToString(
std::string& data, const RsPgpId& pgpId, bool includeSignatures,
std::string& errorMsg )
{
return AuthGPG::getAuthGPG()->exportIdentityToString(
data, pgpId, includeSignatures, errorMsg );
}
bool RsAccountsDetail::copyGnuPGKeyrings()
{
std::string pgp_dir = PathPGPDirectory() ;
@ -1344,6 +1352,14 @@ bool RsAccounts::ImportIdentityFromString(const std::string& data,RsPgpId& im
return rsAccountsDetails->importIdentityFromString(data,imported_pgp_id,import_error);
}
/*static*/ bool RsAccounts::exportIdentityToString(
std::string& data, const RsPgpId& pgpId, std::string& errorMsg,
bool includeSignatures )
{
return rsAccountsDetails->exportIdentityToString(
data, pgpId, includeSignatures, errorMsg);
}
void RsAccounts::GetUnsupportedKeys(std::map<std::string,std::vector<std::string> > &unsupported_keys)
{
return rsAccountsDetails->getUnsupportedKeys(unsupported_keys);

View File

@ -99,6 +99,9 @@ class RsAccountsDetail
// PGP Support Functions.
bool exportIdentity(const std::string& fname,const RsPgpId& pgp_id) ;
bool importIdentity(const std::string& fname,RsPgpId& imported_pgp_id,std::string& import_error) ;
bool exportIdentityToString(
std::string& data, const RsPgpId& pgpId, bool includeSignatures,
std::string& errorMsg );
bool importIdentityFromString(const std::string& data,RsPgpId& imported_pgp_id,std::string& import_error) ;
void getUnsupportedKeys(std::map<std::string,std::vector<std::string> > &unsupported_keys);
bool copyGnuPGKeyrings() ;

View File

@ -94,6 +94,10 @@ RsDht *rsDht = NULL ;
# include "gxstrans/p3gxstrans.h"
#endif
#ifdef RS_JSONAPI
# include "jsonapi/jsonapi.h"
#endif
// #define GPG_DEBUG
// #define AUTHSSL_DEBUG
// #define FIM_DEBUG
@ -105,9 +109,9 @@ RsLoginHelper* rsLoginHelper = nullptr;
RsAccounts* rsAccounts = nullptr;
class RsInitConfig
struct RsInitConfig
{
public:
RsInitConfig() : jsonApiPort(0), jsonApiBindAddress("127.0.0.1") {}
RsFileHash main_executable_hash;
@ -146,24 +150,14 @@ class RsInitConfig
int debugLevel;
std::string logfname;
bool load_trustedpeer;
std::string load_trustedpeer_file;
bool udpListenerOnly;
std::string opModeStr;
uint16_t jsonApiPort;
std::string jsonApiBindAddress;
};
static RsInitConfig *rsInitConfig = NULL;
//const int p3facestartupzone = 47238;
// initial configuration bootstrapping...
//static const std::string configInitFile = "default_cert.txt";
//static const std::string configConfFile = "config.rs";
//static const std::string configCertDir = "friends";
//static const std::string configKeyDir = "keys";
//static const std::string configCaFile = "cacerts.pem";
//static const std::string configHelpName = "retro.htm";
static RsInitConfig* rsInitConfig = nullptr;
static const std::string configLogFileName = "retro.log";
static const int SSLPWD_LEN = 64;
@ -172,6 +166,10 @@ void RsInit::InitRsConfig()
{
rsInitConfig = new RsInitConfig;
/* TODO almost all of this should be moved to RsInitConfig::RsInitConfig
* initializers */
/* Directories */
#ifdef WINDOWS_SYS
rsInitConfig->portable = false;
@ -181,14 +179,13 @@ void RsInit::InitRsConfig()
rsInitConfig->hiddenNodeSet = false;
// This doesn't seems a configuration...
#ifndef WINDOWS_SYS
rsInitConfig->lockHandle = -1;
#else
rsInitConfig->lockHandle = NULL;
#endif
rsInitConfig->load_trustedpeer = false;
rsInitConfig->port = 0 ;
rsInitConfig->forceLocalAddr = false;
rsInitConfig->haveLogFile = false;
@ -204,9 +201,6 @@ void RsInit::InitRsConfig()
rsInitConfig->udpListenerOnly = false;
rsInitConfig->opModeStr = std::string("");
/* setup the homePath (default save location) */
// rsInitConfig->homePath = getHomePath();
#ifdef WINDOWS_SYS
// test for portable version
if (GetFileAttributes(L"portable") != (DWORD) -1) {
@ -243,31 +237,7 @@ void RsInit::InitRsConfig()
}
#endif
/* Setup the Debugging */
// setup debugging for desired zones.
setOutputLevel(RsLog::Warning); // default to Warnings.
// For Testing purposes.
// We can adjust everything under Linux.
//setZoneLevel(PQL_DEBUG_BASIC, 38422); // pqipacket.
//setZoneLevel(PQL_DEBUG_BASIC, 96184); // pqinetwork;
//setZoneLevel(PQL_DEBUG_BASIC, 82371); // pqiperson.
//setZoneLevel(PQL_DEBUG_BASIC, 34283); // pqihandler.
//setZoneLevel(PQL_DEBUG_BASIC, 44863); // discItems.
//setZoneLevel(PQL_DEBUG_BASIC, 1728); // pqi/p3proxy
//setZoneLevel(PQL_DEBUG_BASIC, 1211); // sslroot.
//setZoneLevel(PQL_DEBUG_BASIC, 37714); // pqissl.
//setZoneLevel(PQL_DEBUG_BASIC, 8221); // pqistreamer.
//setZoneLevel(PQL_DEBUG_BASIC, 9326); // pqiarchive
//setZoneLevel(PQL_DEBUG_BASIC, 3334); // p3channel.
//setZoneLevel(PQL_DEBUG_BASIC, 354); // pqipersongrp.
//setZoneLevel(PQL_DEBUG_BASIC, 6846); // pqiudpproxy
//setZoneLevel(PQL_DEBUG_BASIC, 3144); // pqissludp;
//setZoneLevel(PQL_DEBUG_BASIC, 86539); // pqifiler.
//setZoneLevel(PQL_DEBUG_BASIC, 91393); // Funky_Browser.
//setZoneLevel(PQL_DEBUG_BASIC, 25915); // fltkserver
//setZoneLevel(PQL_DEBUG_BASIC, 47659); // fldxsrvr
//setZoneLevel(PQL_DEBUG_BASIC, 49787); // pqissllistener
setOutputLevel(RsLog::Warning);
}
/********
@ -294,68 +264,59 @@ bool doPortRestrictions = false;
int RsInit::InitRetroShare(int argc, char **argv, bool /* strictCheck */)
{
#ifdef DEBUG_RSINIT
for(int i=0; i<argc; i++)
printf("%d: %s\n", i, argv[i]);
for(int i=0; i<argc; i++) printf("%d: %s\n", i, argv[i]);
#endif
/* for static PThreads under windows... we need to init the library... */
#ifdef PTW32_STATIC_LIB
// for static PThreads under windows... we need to init the library...
pthread_win32_process_attach_np();
#endif
/******************************** WINDOWS/UNIX SPECIFIC PART ******************/
std::string prefUserString = "";
std::string opt_base_dir;
/* getopt info: every availiable option is listed here. if it is followed by a ':' it
needs an argument. If it is followed by a '::' the argument is optional.
*/
//rsInitConfig->logfname = "" ;
//rsInitConfig->inet = "" ;
#ifdef __APPLE__
// TODO: is this still needed with argstream?
/* HACK to avoid stupid OSX Finder behaviour
* remove the commandline arguments - if we detect we are launched from Finder,
* and we have the unparsable "-psn_0_12332" option.
* this is okay, as you cannot pass commandline arguments via Finder anyway
*/
if ((argc >= 2) && (0 == strncmp(argv[1], "-psn", 4)))
{
argc = 1;
}
if ((argc >= 2) && (0 == strncmp(argv[1], "-psn", 4))) argc = 1;
#endif
argstream as(argc,argv);
as
#ifdef RS_AUTOLOGIN
>> option('a',"auto-login" ,rsInitConfig->autoLogin ,"AutoLogin (Windows Only) + StartMinimised")
#endif
>> option('m',"minimized" ,rsInitConfig->startMinimised ,"Start minimized." )
as >> option('m',"minimized" ,rsInitConfig->startMinimised ,"Start minimized." )
>> option('s',"stderr" ,rsInitConfig->outStderr ,"output to stderr instead of log file." )
>> option('u',"udp" ,rsInitConfig->udpListenerOnly,"Only listen to UDP." )
>> option('e',"external-port" ,rsInitConfig->forceExtPort ,"Use a forwarded external port." )
>> parameter('l',"log-file" ,rsInitConfig->logfname ,"logfile" ,"Set Log filename." ,false)
>> parameter('d',"debug-level" ,rsInitConfig->debugLevel ,"level" ,"Set debug level." ,false)
#ifdef TO_REMOVE
// This was removed because it is not used anymore.
>> parameter('w',"password" ,rsInitConfig->passwd ,"password" ,"Set Login Password." ,false)
#endif
>> parameter('i',"ip-address" ,rsInitConfig->inet ,"nnn.nnn.nnn.nnn", "Force IP address to use (if cannot be detected)." ,false)
>> parameter('o',"opmode" ,rsInitConfig->opModeStr ,"opmode" ,"Set Operating mode (Full, NoTurtle, Gaming, Minimal)." ,false)
>> parameter('p',"port" ,rsInitConfig->port ,"port", "Set listenning port to use." ,false)
>> parameter('c',"base-dir" ,opt_base_dir ,"directory", "Set base directory." ,false)
>> parameter('U',"user-id" ,prefUserString ,"ID", "[ocation Id] Sets Account to Use, Useful when Autologin is enabled.",false)
// by rshare 'r' "link" "Link" "Open RsLink with protocol retroshare://"
// by rshare 'f' "rsfile" "RsFile" "Open RsFile like RsCollection"
#ifdef LOCALNET_TESTING
>> parameter('R',"restrict-port" ,portRestrictions ,"port1-port2","Apply port restriction" ,false)
#endif
>> help('h',"help","Display this Help") ;
>> parameter('U',"user-id" ,prefUserString ,"ID", "[ocation Id] Sets Account to Use, Useful when Autologin is enabled.",false);
#ifdef RS_JSONAPI
as >> parameter(
"jsonApiPort", rsInitConfig->jsonApiPort, "jsonApiPort",
"Enable JSON API on the specified port", false )
>> parameter(
"jsonApiBindAddress", rsInitConfig->jsonApiBindAddress,
"jsonApiBindAddress", "JSON API Bind Address.", false);
#endif // ifdef RS_JSONAPI
#ifdef LOCALNET_TESTING
as >> parameter('R',"restrict-port" ,portRestrictions ,"port1-port2","Apply port restriction" ,false);
#endif // ifdef LOCALNET_TESTING
#ifdef RS_AUTOLOGIN
as >> option('a',"auto-login" ,rsInitConfig->autoLogin ,"AutoLogin (Windows Only) + StartMinimised");
#endif // ifdef RS_AUTOLOGIN
as >> help('h',"help","Display this Help");
as.defaultErrorHandling(true,true);
if(rsInitConfig->autoLogin) rsInitConfig->startMinimised = true ;
@ -477,6 +438,16 @@ int RsInit::InitRetroShare(int argc, char **argv, bool /* strictCheck */)
}
#endif
#ifdef RS_JSONAPI
if(rsInitConfig->jsonApiPort)
{
jsonApiServer = new JsonApiServer(
rsInitConfig->jsonApiPort,
rsInitConfig->jsonApiBindAddress );
jsonApiServer->start("JSON API Server");
}
#endif // ifdef RS_JSONAPI
return RS_INIT_OK;
}
@ -1277,6 +1248,14 @@ int RsServer::StartupRetroShare()
//
mPluginsManager->loadPlugins(programatically_inserted_plugins) ;
#ifdef RS_JSONAPI
{
mConfigMgr->addConfiguration("jsonApi.cfg", jsonApiServer);
RsFileHash dummyHash;
jsonApiServer->loadConfiguration(dummyHash);
}
#endif
/**** Reputation system ****/
p3GxsReputation *mReputations = new p3GxsReputation(mLinkMgr) ;
@ -1940,6 +1919,7 @@ int RsServer::StartupRetroShare()
RsInit::LoadCertificateStatus RsLoginHelper::attemptLogin(
const RsPeerId& account, const std::string& password)
{
if(isLoggedIn()) return RsInit::ERR_ALREADY_RUNNING;
if(!rsNotify->cachePgpPassphrase(password)) return RsInit::ERR_UNKOWN;
if(!rsNotify->setDisableAskPassword(true)) return RsInit::ERR_UNKOWN;
if(!RsAccounts::SelectAccount(account)) return RsInit::ERR_UNKOWN;
@ -1973,6 +1953,8 @@ bool RsLoginHelper::createLocation(
RsLoginHelper::Location& l, const std::string& password,
bool makeHidden, bool makeAutoTor, std::string& errorMessage )
{
if(isLoggedIn()) return (errorMessage="Already Running", false);
if(l.mLocationName.empty())
{
errorMessage = "Location name is needed";

View File

@ -90,6 +90,7 @@ int main(int argc, char *argv[])
RsInit::InitRsConfig();
RsInit::InitRetroShare(argc, argv, true);
RsControl::earlyInitNotificationSystem();
rsControl->setShutdownCallback(QCoreApplication::exit);
QObject::connect(
&app, &QCoreApplication::aboutToQuit,
[](){
@ -97,71 +98,5 @@ int main(int argc, char *argv[])
RsControl::instance()->rsGlobalShutDown(); } );
#endif // ifdef LIBRESAPI_LOCAL_SERVER
#ifdef RS_JSONAPI
uint16_t jsonApiPort = 9092;
std::string jsonApiBindAddress = "127.0.0.1";
{
QCommandLineOption jsonApiPortOpt(
"jsonApiPort", "JSON API listening port.", "port", "9092");
QCommandLineOption jsonApiBindAddressOpt(
"jsonApiBindAddress", "JSON API Bind Address.",
"IP Address", "127.0.0.1");
QCommandLineParser cmdParser;
cmdParser.addHelpOption();
cmdParser.addOption(jsonApiPortOpt);
cmdParser.addOption(jsonApiBindAddressOpt);
cmdParser.parse(app.arguments());
if(cmdParser.isSet(jsonApiPortOpt))
{
QString jsonApiPortStr = cmdParser.value(jsonApiPortOpt);
bool portOk;
jsonApiPort = jsonApiPortStr.toUShort(&portOk);
if(!portOk)
{
std::cerr << "ERROR: jsonApiPort option value must be a valid "
<< "TCP port!" << std::endl;
cmdParser.showHelp();
QCoreApplication::exit(EINVAL);
}
}
if(cmdParser.isSet(jsonApiBindAddressOpt))
{
sockaddr_storage tmp;
jsonApiBindAddress =
cmdParser.value(jsonApiBindAddressOpt).toStdString();
if(!sockaddr_storage_inet_pton(tmp, jsonApiBindAddress))
{
std::cerr << "ERROR: jsonApiBindAddress option value must "
<< "be a valid IP address!" << std::endl;
cmdParser.showHelp();
QCoreApplication::exit(EINVAL);
}
}
}
JsonApiServer jas( jsonApiPort, jsonApiBindAddress,
[](int ec) { QCoreApplication::exit(ec); } );
jas.start();
{
sockaddr_storage tmp;
sockaddr_storage_inet_pton(tmp, jsonApiBindAddress);
sockaddr_storage_setport(tmp, jsonApiPort);
sockaddr_storage_ipv6_to_ipv4(tmp);
RsUrl tmpUrl(sockaddr_storage_tostring(tmp));
tmpUrl.setScheme("http");
std::cerr << "JSON API listening on "
<< tmpUrl.toString()
<< std::endl;
}
#endif // ifdef RS_JSONAPI
return app.exec();
}

View File

@ -24,15 +24,15 @@
*
*/
#include <retroshare/rsiface.h> /* definition of iface */
#include <retroshare/rsinit.h> /* definition of iface */
#include "retroshare/rsiface.h"
#include "retroshare/rsinit.h"
#include "notifytxt.h"
#include <unistd.h>
#include "util/argstream.h"
#include "util/rstime.h"
#include <unistd.h>
#include <iostream>
#ifdef WINDOWS_SYS
#include <winsock2.h>
#endif
@ -43,18 +43,12 @@
#ifdef ENABLE_WEBUI
#include <stdarg.h>
#include <csignal>
#include "api/ApiServerMHD.h"
#include "api/RsControlModule.h"
#include "TerminalApiClient.h"
#endif
#ifdef RS_JSONAPI
# include <csignal>
# include "jsonapi/jsonapi.h"
# include "util/rsnet.h"
# include "util/rsurl.h"
#endif // RS_JSONAPI
/* Basic instructions for running libretroshare as background thread.
* ******************************************************************* *
* This allows your program to communicate with authenticated peers.
@ -67,45 +61,6 @@
int main(int argc, char **argv)
{
#ifdef RS_JSONAPI
JsonApiServer* jsonApiServer = nullptr;
uint16_t jsonApiPort = 0;
std::string jsonApiBindAddress = "127.0.0.1";
{
argstream jsonApiArgs(argc, argv);
jsonApiArgs >> parameter(
"jsonApiPort", jsonApiPort, "jsonApiPort",
"Enable JSON API on the specified port", false );
jsonApiArgs >> parameter(
"jsonApiBindAddress", jsonApiBindAddress,
"jsonApiBindAddress", "JSON API Bind Address.", false);
jsonApiArgs >> help('h', "help", "Display this Help");
if (jsonApiArgs.helpRequested())
std::cerr << jsonApiArgs.usage() << std::endl;
}
if(jsonApiPort)
{
jsonApiServer = new JsonApiServer(
jsonApiPort, jsonApiBindAddress,
[](int /*ec*/) { std::raise(SIGTERM); } );
jsonApiServer->start("JSON API Server");
sockaddr_storage tmp;
sockaddr_storage_inet_pton(tmp, jsonApiBindAddress);
sockaddr_storage_setport(tmp, jsonApiPort);
sockaddr_storage_ipv6_to_ipv4(tmp);
RsUrl tmpUrl(sockaddr_storage_tostring(tmp));
tmpUrl.setScheme("http");
std::cerr << "JSON API listening on " << tmpUrl.toString()
<< std::endl;
}
#endif // RS_JSONAPI
#ifdef ENABLE_WEBUI
std::string docroot = resource_api::getDefaultDocroot();
@ -146,11 +101,14 @@ int main(int argc, char **argv)
httpd->start();
}
RsControl::earlyInitNotificationSystem();
rsControl->setShutdownCallback([](int){std::raise(SIGTERM);});
resource_api::TerminalApiClient tac(&api);
tac.start();
bool already = false ;
while(ctrl_mod.processShouldExit() == false)
while(!ctrl_mod.processShouldExit())
{
rstime::rs_usleep(1000*1000);
@ -170,28 +128,6 @@ int main(int argc, char **argv)
return 0;
#endif
/* Retroshare startup is configured using an RsInit object.
* This is an opaque class, which the user cannot directly tweak
* If you want to peek at whats happening underneath look in
* libretroshare/src/rsserver/p3face-startup.cc
*
* You create it with InitRsConfig(), and delete with CleanupRsConfig()
* InitRetroshare(argv, argc, config) parses the command line options,
* and initialises the config paths.
*
* *** There are several functions that I should add to modify
* **** the config the moment these can only be set via the commandline
* - RsConfigDirectory(...) is probably the most useful.
* - RsConfigNetAddr(...) for setting port, etc.
* - RsConfigOutput(...) for logging and debugging.
*
* Next you need to worry about loading your certificate, or making
* a new one:
*
* RsGenerateCertificate(...) To create a new key, certificate
* LoadPassword(...) set password for existing certificate.
**/
bool strictCheck = true;
RsInit::InitRsConfig();
int initResult = RsInit::InitRetroShare(argc, argv, strictCheck);
@ -220,9 +156,12 @@ int main(int argc, char **argv)
* if you want to receive notifications of events */
// This is needed to allocate rsNotify, so that it can be used to ask for PGP passphrase
//
RsControl::earlyInitNotificationSystem();
// an atomic might be safer but is probably unneded for this simple usage
bool keepRunning = true;
rsControl->setShutdownCallback([&](int){keepRunning = false;});
NotifyTxt *notify = new NotifyTxt();
rsNotify->registerNotifyClient(notify);
@ -250,29 +189,13 @@ int main(int argc, char **argv)
RsIntroServer rsIS;
#endif
/* pass control to the GUI */
while(1)
while(keepRunning)
{
//std::cerr << "GUI Tick()" << std::endl;
#ifdef RS_INTRO_SERVER
rsIS.tick();
#endif
int rt = 0;
// If we have a MenuTerminal ...
// only want to sleep if there is no input. (rt == 0).
if (rt == 0)
{
#ifndef WINDOWS_SYS
sleep(1);
#else
Sleep(1000);
#endif
rstime::rs_usleep(10*1000);
}
rstime::rs_usleep(1000);
}
return 1;
return 0;
}

View File

@ -15,7 +15,7 @@ class FakeServiceControl: public p3ServiceControl
{
}
virtual void getPeersConnected(const uint32_t serviceId, std::set<RsPeerId> &peerSet)
virtual void getPeersConnected(uint32_t serviceId, std::set<RsPeerId> &peerSet)
{
(void) serviceId;
std::list<RsPeerId> ids ;