Подписанная PGP ключом ДС Created сделка сначала должна отправляться по bitmessage, затем должна быть отдана МС.

This commit is contained in:
Преподобный Ален
2022-09-17 15:57:29 +03:00
parent 3b69fc0889
commit 16c1abefe5
4 changed files with 700 additions and 242 deletions

View File

@@ -50,7 +50,6 @@ namespace Apostol {
CWebService::CWebService(CModuleProcess *AProcess): CApostolModule(AProcess, "web service", "module/WebService") {
m_NexServerTime = 0;
m_ServerIndex = -1;
m_SyncPeriod = BPS_DEFAULT_SYNC_PERIOD;
m_Status = psStopped;
@@ -122,125 +121,34 @@ namespace Apostol {
}
//--------------------------------------------------------------------------------------------------------------
void CWebService::FetchOAuth2(CContext &Context) {
Log()->Debug(APP_LOG_DEBUG_CORE, "Trying to fetch a OAuth2 configuration file for module \"%s\" from: %s", Context.Name().c_str(), Context.URL().Origin().c_str());
auto OnRequest = [this, &Context](CHTTPClient *Sender, CHTTPRequest *ARequest) {
ARequest->ContentType = CHTTPRequest::text;
Apostol::PGP::CleartextSignature(
m_pgpPrivateKey,
m_pgpPassphrase,
BPS_PGP_HASH,
Context.Name(),
ARequest->Content);
CHTTPRequest::Prepare(ARequest, "POST", "/api/v1/dm/service");
const auto& caModuleAddress = m_Module["address"];
if (!caModuleAddress.IsEmpty())
ARequest->AddHeader("Module-Address", caModuleAddress);
DebugRequest(ARequest);
};
auto OnExecute = [this, &Context](CTCPConnection *AConnection) {
auto pConnection = dynamic_cast<CHTTPClientConnection *> (AConnection);
if (pConnection != nullptr) {
auto pReply = pConnection->Reply();
DebugReply(pReply);
if (Context.Status() == Context::csPreparing) {
if (pReply->Status == CHTTPReply::ok) {
const CJSON Json(pReply->Content);
Json.SaveToFile(m_OAuth2.c_str());
UpdateOAuth2(m_OAuth2);
} else {
Context.SetStatus(Context::csInitialization);
}
}
pConnection->CloseConnection(true);
}
return true;
};
auto OnException = [&Context](CTCPConnection *AConnection, const Delphi::Exception::Exception &E) {
auto pConnection = dynamic_cast<CHTTPClientConnection *> (AConnection);
if (pConnection != nullptr) {
auto pClient = dynamic_cast<CHTTPClient *> (pConnection->Client());
if (pClient != nullptr) {
Log()->Error(APP_LOG_EMERG, 0, "[%s:%d] %s", pClient->Host().c_str(), pClient->Port(), E.what());
}
DebugReply(pConnection->Reply());
}
Context.SetStatus(Context::csInitialization);
};
Context.SetStatus(Context::csPreparing);
auto pClient = GetClient(Context.URL().hostname, Context.URL().port == 0 ? BPS_SERVER_PORT : Context.URL().port);
pClient->OnRequest(OnRequest);
pClient->OnExecute(OnExecute);
pClient->OnException(OnException);
pClient->AutoFree(true);
pClient->Active(true);
}
//--------------------------------------------------------------------------------------------------------------
void CWebService::UpdateOAuth2(const CString &FileName) {
for (int i = 0; i < m_Servers.Count(); i++) {
auto &Context = m_Servers[i].Value();
if (Context.Status() == Context::csInitialization || Context.Status() == Context::csPreparing) {
if (LoadOAuth2(FileName, Context.Providers())) {
Context.SetStatus(Context::csInitialized);
Context.SetCheckDate(0);
} else {
if (Context.Status() != Context::csPreparing) {
FetchOAuth2(Context);
}
}
}
}
}
//--------------------------------------------------------------------------------------------------------------
bool CWebService::LoadOAuth2(const CString &FileName, CProviders &Providers) {
const auto &caProviderName = CString(SYSTEM_PROVIDER_NAME);
const auto &caApplicationName = CString(SERVICE_APPLICATION_NAME);
if (FileExists(FileName.c_str())) {
CJSONObject Json;
Json.LoadFromFile(FileName.c_str());
int index = Providers.IndexOfName(caProviderName);
if (index == -1)
index = Providers.AddPair(caProviderName, CProvider(caProviderName));
auto& Provider = Providers[index].Value();
Provider.Applications().AddPair(caApplicationName, Json);
return true;
}
return false;
}
//--------------------------------------------------------------------------------------------------------------
void CWebService::CheckKeyForNull(LPCTSTR Key, LPCTSTR Value) {
if (Value == nullptr)
throw ExceptionFrm("Invalid format: key \"%s\" cannot be empty.", Key);
}
//--------------------------------------------------------------------------------------------------------------
void CWebService::UpdateOAuth2(CContext &Context, const CJSONObject &OAuth2) {
if (OAuth2["type"].AsString() == "service_account") {
UpdateProviders(Context.Providers(), OAuth2);
Context.SetCheckDate(0);
Context.SetStatus(Context::csInitialized);
}
}
//--------------------------------------------------------------------------------------------------------------
void CWebService::UpdateProviders(CProviders &Providers, const CJSONObject &Data) {
const auto &caProviderName = CString(SYSTEM_PROVIDER_NAME);
const auto &caApplicationName = CString(SERVICE_APPLICATION_NAME);
int index = Providers.IndexOfName(caProviderName);
if (index == -1)
index = Providers.AddPair(caProviderName, CProvider(caProviderName));
auto& Provider = Providers[index].Value();
Provider.Applications().AddPair(caApplicationName, Data);
}
//--------------------------------------------------------------------------------------------------------------
bool CWebService::FindURLInLine(const CString &Line, CStringList &List) {
CString URL;
@@ -726,7 +634,7 @@ namespace Apostol {
sPayload = caClearText.Text();
} else {
Apostol::PGP::CleartextSignature(
m_pgpPrivateKey,
m_pgpModuleKey,
m_pgpPassphrase,
BPS_PGP_HASH,
caClearText.Text(),
@@ -1174,7 +1082,7 @@ namespace Apostol {
sPayload = caClearText;
} else {
Apostol::PGP::CleartextSignature(
m_pgpPrivateKey,
m_pgpModuleKey,
m_pgpPassphrase,
BPS_PGP_HASH,
caClearText,
@@ -1612,7 +1520,7 @@ namespace Apostol {
void CWebService::FetchPGP(CContext &Context) {
Log()->Debug(APP_LOG_DEBUG_CORE, "Trying to fetch a PGP key \"%s\" from: %s", Context.Name().c_str(), Context.URL().Origin().c_str());
Log()->Notice("[%s] [%s] Trying to fetch a PGP key.", Context.Name().c_str(), Context.URL().Origin().c_str());
auto OnRequest = [&Context](CHTTPClient *Sender, CHTTPRequest *ARequest) {
@@ -1699,28 +1607,348 @@ namespace Apostol {
}
//--------------------------------------------------------------------------------------------------------------
void CWebService::ModuleService(CContext &Context) {
Log()->Notice("[%s] [%s] Trying to fetch a OAuth2 configuration file.", Context.Name().c_str(), Context.URL().Origin().c_str());
auto OnRequest = [this, &Context](CHTTPClient *Sender, CHTTPRequest *ARequest) {
Context.SetStatus(Context::csPreparing);
ARequest->ContentType = CHTTPRequest::text;
Apostol::PGP::CleartextSignature(
m_pgpModuleKey,
m_pgpPassphrase,
BPS_PGP_HASH,
Context.Name(),
ARequest->Content);
CHTTPRequest::Prepare(ARequest, "POST", "/api/v1/dm/service");
const auto &caModuleAddress = m_Module["address"];
if (!caModuleAddress.IsEmpty())
ARequest->AddHeader("Module-Address", caModuleAddress);
DebugRequest(ARequest);
};
auto OnExecute = [&Context](CTCPConnection *AConnection) {
Context.SetStatus(Context::csInitialization);
auto pConnection = dynamic_cast<CHTTPClientConnection *> (AConnection);
if (pConnection != nullptr) {
auto pReply = pConnection->Reply();
DebugReply(pReply);
if (pReply->Status == CHTTPReply::ok) {
const CJSON OAuth2(pReply->Content);
UpdateOAuth2(Context, OAuth2.Object());
}
pConnection->CloseConnection(true);
}
return true;
};
auto OnException = [&Context](CTCPConnection *AConnection, const Delphi::Exception::Exception &E) {
auto pConnection = dynamic_cast<CHTTPClientConnection *> (AConnection);
if (pConnection != nullptr) {
auto pClient = dynamic_cast<CHTTPClient *> (pConnection->Client());
if (pClient != nullptr) {
Log()->Error(APP_LOG_EMERG, 0, "[%s:%d] %s", pClient->Host().c_str(), pClient->Port(), E.what());
}
DebugReply(pConnection->Reply());
}
Context.SetStatus(Context::csInitialization);
};
auto pClient = GetClient(Context.URL().hostname, Context.URL().port == 0 ? BPS_SERVER_PORT : Context.URL().port);
pClient->OnRequest(OnRequest);
pClient->OnExecute(OnExecute);
pClient->OnException(OnException);
pClient->AutoFree(true);
pClient->Active(true);
}
//--------------------------------------------------------------------------------------------------------------
void CWebService::ModuleStatus(CContext &Context) {
Log()->Notice("[%s] [%s] Trying get module status.", Context.Name().c_str(), Context.URL().Origin().c_str());
auto OnRequest = [this, &Context](CHTTPClient *Sender, CHTTPRequest *ARequest) {
Context.SetStatus(Context::csInProgress);
const auto &caModuleAddress = m_Module["address"];
ARequest->ContentType = CHTTPRequest::text;
ARequest->Content.Format(R"({"address": "%s"})", caModuleAddress.c_str());
CHTTPRequest::Prepare(ARequest, "POST", "/api/v1/dm/status");
ARequest->AddHeader("Authorization", "Bearer " + Context.Tokens()["access_token"]);
if (!caModuleAddress.IsEmpty())
ARequest->AddHeader("Module-Address", caModuleAddress);
DebugRequest(ARequest);
};
auto OnExecute = [this, &Context](CTCPConnection *AConnection) {
auto pConnection = dynamic_cast<CHTTPClientConnection *> (AConnection);
if (pConnection != nullptr) {
auto pReply = pConnection->Reply();
DebugReply(pReply);
if (pReply->Status == CHTTPReply::ok) {
ModuleAuthorize(Context);
} else {
ModuleNew(Context);
}
pConnection->CloseConnection(true);
}
return true;
};
auto OnException = [&Context](CTCPConnection *AConnection, const Delphi::Exception::Exception &E) {
auto pConnection = dynamic_cast<CHTTPClientConnection *> (AConnection);
if (pConnection != nullptr) {
auto pClient = dynamic_cast<CHTTPClient *> (pConnection->Client());
if (pClient != nullptr) {
Log()->Error(APP_LOG_EMERG, 0, "[%s:%d] %s", pClient->Host().c_str(), pClient->Port(), E.what());
}
DebugReply(pConnection->Reply());
}
Context.SetStatus(Context::csInitialized);
};
auto pClient = GetClient(Context.URL().hostname, Context.URL().port == 0 ? BPS_SERVER_PORT : Context.URL().port);
pClient->OnRequest(OnRequest);
pClient->OnExecute(OnExecute);
pClient->OnException(OnException);
pClient->AutoFree(true);
pClient->Active(true);
}
//--------------------------------------------------------------------------------------------------------------
void CWebService::ModuleNew(CContext &Context) {
Log()->Notice("[%s] [%s] Trying create new module.", Context.Name().c_str(), Context.URL().Origin().c_str());
auto OnRequest = [this, &Context](CHTTPClient *Sender, CHTTPRequest *ARequest) {
Context.SetStatus(Context::csInProgress);
ARequest->ContentType = CHTTPRequest::json;
const auto &caModuleAddress = m_Module["address"];
CJSON Json(jvtObject);
Json.Object().AddPair("address", caModuleAddress);
Json.Object().AddPair("bitmessage", Context.Name());
const OpenPGP::SecretKey pgpSecret(m_pgpModuleKey.c_str());
const auto &public_key = pgpSecret.get_public();
Json.Object().AddPair("pgp", public_key.write(OpenPGP::PGP::Armored::YES));
ARequest->Content = Json.ToString();
CHTTPRequest::Prepare(ARequest, "POST", "/api/v1/client/new");
ARequest->AddHeader("Authorization", "Bearer " + Context.Tokens()["access_token"]);
if (!caModuleAddress.IsEmpty())
ARequest->AddHeader("Module-Address", caModuleAddress);
DebugRequest(ARequest);
};
auto OnExecute = [this, &Context](CTCPConnection *AConnection) {
Context.SetStatus(Context::csInitialized);
auto pConnection = dynamic_cast<CHTTPClientConnection *> (AConnection);
if (pConnection != nullptr) {
auto pReply = pConnection->Reply();
DebugReply(pReply);
if (pReply->Status == CHTTPReply::ok) {
const CJSON Json(pReply->Content);
if (Json.HasOwnProperty("result")) {
const auto &caResult = Json["result"];
if (caResult.HasOwnProperty("success")) {
if (caResult["success"].AsBoolean()) {
ModuleAuthorize(Context);
} else {
if (caResult.HasOwnProperty("message")) {
Log()->Error(APP_LOG_EMERG, 0, "[%s] [%s] %s", Context.Name().c_str(),
Context.URL().Origin().c_str(), caResult["message"].AsString().c_str());
}
}
}
}
}
pConnection->CloseConnection(true);
}
return true;
};
auto OnException = [&Context](CTCPConnection *AConnection, const Delphi::Exception::Exception &E) {
auto pConnection = dynamic_cast<CHTTPClientConnection *> (AConnection);
if (pConnection != nullptr) {
auto pClient = dynamic_cast<CHTTPClient *> (pConnection->Client());
if (pClient != nullptr) {
Log()->Error(APP_LOG_EMERG, 0, "[%s:%d] %s", pClient->Host().c_str(), pClient->Port(), E.what());
}
DebugReply(pConnection->Reply());
}
Context.SetStatus(Context::csInitialized);
};
auto pClient = GetClient(Context.URL().hostname, Context.URL().port == 0 ? BPS_SERVER_PORT : Context.URL().port);
pClient->OnRequest(OnRequest);
pClient->OnExecute(OnExecute);
pClient->OnException(OnException);
pClient->AutoFree(true);
pClient->Active(true);
}
//--------------------------------------------------------------------------------------------------------------
void CWebService::ModuleAuthorize(CContext &Context) {
Log()->Notice("[%s] [%s] Trying authorize module.", Context.Name().c_str(), Context.URL().Origin().c_str());
auto OnRequest = [this, &Context](CHTTPClient *Sender, CHTTPRequest *ARequest) {
Context.SetStatus(Context::csAuthorization);
ARequest->ContentType = CHTTPRequest::text;
Apostol::PGP::CleartextSignature(
m_pgpModuleKey,
m_pgpPassphrase,
BPS_PGP_HASH,
Context.Name(),
ARequest->Content);
CHTTPRequest::Prepare(ARequest, "POST", "/api/v1/dm/authorize");
ARequest->AddHeader("Authorization", "Bearer " + Context.Tokens()["access_token"]);
const auto &caModuleAddress = m_Module["address"];
if (!caModuleAddress.IsEmpty())
ARequest->AddHeader("Module-Address", caModuleAddress);
DebugRequest(ARequest);
};
auto OnExecute = [&Context](CTCPConnection *AConnection) {
Context.SetStatus(Context::csInitialized);
auto pConnection = dynamic_cast<CHTTPClientConnection *> (AConnection);
if (pConnection != nullptr) {
auto pReply = pConnection->Reply();
DebugReply(pReply);
if (pReply->Status == CHTTPReply::ok) {
const CJSON Json(pReply->Content);
Context.Session() = Json["session"].AsString();
Context.Secret() = Json["secret"].AsString();
Context.Tokens().Values("access_token", Json["access_token"].AsString());
Context.SetFixedDate(0);
Context.SetCheckDate(Now() + (CDateTime) 55 / MinsPerDay); // 55 min
Context.SetStatus(csAuthorized);
}
pConnection->CloseConnection(true);
}
return true;
};
auto OnException = [&Context](CTCPConnection *AConnection, const Delphi::Exception::Exception &E) {
auto pConnection = dynamic_cast<CHTTPClientConnection *> (AConnection);
if (pConnection != nullptr) {
auto pClient = dynamic_cast<CHTTPClient *> (pConnection->Client());
if (pClient != nullptr) {
Log()->Error(APP_LOG_EMERG, 0, "[%s:%d] %s", pClient->Host().c_str(), pClient->Port(), E.what());
}
DebugReply(pConnection->Reply());
}
Context.SetStatus(Context::csInitialized);
};
auto pClient = GetClient(Context.URL().hostname, Context.URL().port == 0 ? BPS_SERVER_PORT : Context.URL().port);
pClient->OnRequest(OnRequest);
pClient->OnExecute(OnExecute);
pClient->OnException(OnException);
pClient->AutoFree(true);
pClient->Active(true);
}
//--------------------------------------------------------------------------------------------------------------
void CWebService::Reload() {
Config()->IniFile().ReadSectionValues("module", &m_Module);
m_OAuth2 = Config()->IniFile().ReadString(CONFIG_SECTION_NAME, "oauth2", "oauth2/service.json");
if (!path_separator(m_OAuth2.front())) {
m_OAuth2 = Config()->Prefix() + m_OAuth2;
CString FileName;
FileName = Config()->IniFile().ReadString(CONFIG_SECTION_NAME, "oauth2", "oauth2/service.json");
if (!path_separator(FileName.front())) {
FileName = Config()->Prefix() + FileName;
}
const auto& caPrivateKey = Config()->IniFile().ReadString("pgp", "private", "module.sec");
const auto& caPublicKey = Config()->IniFile().ReadString("pgp", "public", "dm.pub");
if (FileExists(FileName.c_str())) {
m_OAuth2.LoadFromFile(FileName.c_str());
}
const auto &caPrivateKey = Config()->IniFile().ReadString("pgp", "private", "module.sec");
const auto &caPublicKey = Config()->IniFile().ReadString("pgp", "public", "dm.pub");
m_pgpPassphrase = Config()->IniFile().ReadString("pgp", "passphrase", "");
if (FileExists(caPrivateKey.c_str())) {
m_pgpPrivateKey.LoadFromFile(caPrivateKey.c_str());
m_pgpModuleKey.LoadFromFile(caPrivateKey.c_str());
if (FileExists(caPublicKey.c_str())) {
CString Key;
Key.LoadFromFile(caPublicKey.c_str());
UpdateServerList(Key);
UpdateOAuth2(m_OAuth2);
m_pgpPublicKey.LoadFromFile(caPublicKey.c_str());
UpdateServerList(m_pgpPublicKey);
} else {
Log()->Error(APP_LOG_WARN, 0, APP_FILE_NOT_FOUND, caPublicKey.c_str());
}
@@ -1734,23 +1962,21 @@ namespace Apostol {
for (int i = 0; i < m_Servers.Count(); i++) {
auto &Context = m_Servers[i].Value();
if ((Now >= Context.CheckDate()) && (Context.Status() == Context::csInitialization)) {
Context.SetCheckDate(Now + (CDateTime) 30 / SecsPerDay); // 30 sec
FetchOAuth2(Context);
}
if ((Now >= Context.CheckDate()) && (Context.Status() >= Context::csInitialized)) {
if (Now >= Context.CheckDate()) {
Context.SetCheckDate(Now + (CDateTime) 30 / SecsPerDay); // 30 sec
if (Context.Status() == Context::csInitialized)
Context.SetStatus(Context::csAuthorization);
if (Context.Status() == Context::csInitialization) {
ModuleService(Context);
}
CheckProviders(Now, Context);
FetchProviders(Now, Context);
if (Context.Status() >= Context::csInitialized) {
CheckProviders(Now, Context);
FetchProviders(Now, Context);
}
}
if (Context.Status() == Context::csAuthorized) {
if (Now >= Context.FixedDate()) {
if (Now >= Context.FixedDate()) {
if (Context.Status() == Context::csAuthorized) {
Context.SetFixedDate(Now + (CDateTime) 30 / SecsPerDay); // 30 sec
Context.SetStatus(Context::csInProgress);

View File

@@ -42,11 +42,11 @@ namespace Apostol {
CStringList m_Module;
CString m_OAuth2;
CString m_pgpPrivateKey;
CString m_pgpPassphrase;
CJSON m_OAuth2;
int m_ServerIndex;
CString m_pgpModuleKey;
CString m_pgpPublicKey;
CString m_pgpPassphrase;
CProcessStatus m_Status;
@@ -65,17 +65,19 @@ namespace Apostol {
void InitServerList();
void UpdateServerList(const CString &Key);
void UpdateOAuth2(const CString &FileName);
void FetchProviders(CDateTime Now, CContext &Context);
void CheckProviders(CDateTime Now, CContext &Context);
void CreateAccessToken(const CProvider &Provider, const CString &Application, CContext &Context);
void ParsePGPKey(const CString& Key, CStringPairs& ServerList, CStringList& BTCKeys);
void FetchOAuth2(CContext &Context);
void FetchPGP(CContext &Context);
void ModuleService(CContext &Context);
void ModuleStatus(CContext &Context);
void ModuleNew(CContext &Context);
void ModuleAuthorize(CContext &Context);
protected:
CHTTPProxy *GetProxy(CHTTPServerConnection *AConnection);
@@ -130,6 +132,8 @@ namespace Apostol {
static int VerifyPGPSignature(const CString &ClearText, const CString &Key, CString &Message);
static bool FindURLInLine(const CString &Line, CStringList &List);
static void UpdateOAuth2(CContext &Context, const CJSONObject &OAuth2);
static void UpdateProviders(CProviders &Providers, const CJSONObject &Data);
static bool LoadOAuth2(const CString &FileName, CProviders &Providers);
};
}

View File

@@ -125,29 +125,31 @@ namespace Apostol {
Context.PGP().Name = "PUBLIC";
Context.PGP().Key = Key;
Context.BTCKeys() = Keys;
UpdateOAuth2(Context, m_OAuth2.Object());
}
}
}
//--------------------------------------------------------------------------------------------------------------
void CWebSocketModule::FetchOAuth2(CContext &Context) {
void CWebSocketModule::ModuleStatus(CContext &Context) {
Log()->Debug(APP_LOG_DEBUG_CORE, "Trying to fetch a OAuth2 configuration file for module \"%s\" from: %s", Context.Name().c_str(), Context.URL().Origin().c_str());
Log()->Notice("[%s] [%s] Trying get module status.", Context.Name().c_str(), Context.URL().Origin().c_str());
auto OnRequest = [this, &Context](CHTTPClient *Sender, CHTTPRequest *ARequest) {
ARequest->ContentType = CHTTPRequest::text;
Apostol::PGP::CleartextSignature(
m_pgpPrivateKey,
m_pgpPassphrase,
BPS_PGP_HASH,
Context.Name(),
ARequest->Content);
CHTTPRequest::Prepare(ARequest, "POST", "/api/v1/dm/service");
Context.SetStatus(Context::csInProgress);
const auto &caModuleAddress = m_Module["address"];
ARequest->ContentType = CHTTPRequest::text;
ARequest->Content.Format(R"({"address": "%s"})", caModuleAddress.c_str());
CHTTPRequest::Prepare(ARequest, "POST", "/api/v1/dm/status");
ARequest->AddHeader("Authorization", "Bearer " + Context.Tokens()["access_token"]);
if (!caModuleAddress.IsEmpty())
ARequest->AddHeader("Module-Address", caModuleAddress);
@@ -163,14 +165,82 @@ namespace Apostol {
DebugReply(pReply);
if (Context.Status() == Context::csPreparing) {
if (pReply->Status == CHTTPReply::ok) {
const CJSON Json(pReply->Content);
Json.SaveToFile(m_OAuth2.c_str());
UpdateOAuth2(m_OAuth2);
} else {
Context.SetStatus(Context::csInitialization);
}
if (pReply->Status == CHTTPReply::ok) {
ModuleAuthorize(Context);
} else {
ModuleNew(Context);
}
pConnection->CloseConnection(true);
}
return true;
};
auto OnException = [&Context](CTCPConnection *AConnection, const Delphi::Exception::Exception &E) {
auto pConnection = dynamic_cast<CHTTPClientConnection *> (AConnection);
if (pConnection != nullptr) {
auto pClient = dynamic_cast<CHTTPClient *> (pConnection->Client());
if (pClient != nullptr) {
Log()->Error(APP_LOG_EMERG, 0, "[%s:%d] %s", pClient->Host().c_str(), pClient->Port(), E.what());
}
DebugReply(pConnection->Reply());
}
Context.SetStatus(Context::csInitialized);
};
auto pClient = GetClient(Context.URL().hostname, Context.URL().port == 0 ? BPS_SERVER_PORT : Context.URL().port);
pClient->OnRequest(OnRequest);
pClient->OnExecute(OnExecute);
pClient->OnException(OnException);
pClient->AutoFree(true);
pClient->Active(true);
}
//--------------------------------------------------------------------------------------------------------------
void CWebSocketModule::ModuleService(CContext &Context) {
Log()->Notice("[%s] [%s] Trying to fetch a OAuth2 configuration file.", Context.Name().c_str(), Context.URL().Origin().c_str());
auto OnRequest = [this, &Context](CHTTPClient *Sender, CHTTPRequest *ARequest) {
Context.SetStatus(Context::csPreparing);
ARequest->ContentType = CHTTPRequest::text;
Apostol::PGP::CleartextSignature(
m_pgpModuleKey,
m_pgpPassphrase,
BPS_PGP_HASH,
Context.Name(),
ARequest->Content);
CHTTPRequest::Prepare(ARequest, "POST", "/api/v1/dm/service");
const auto &caModuleAddress = m_Module["address"];
if (!caModuleAddress.IsEmpty())
ARequest->AddHeader("Module-Address", caModuleAddress);
DebugRequest(ARequest);
};
auto OnExecute = [&Context](CTCPConnection *AConnection) {
Context.SetStatus(Context::csInitialization);
auto pConnection = dynamic_cast<CHTTPClientConnection *> (AConnection);
if (pConnection != nullptr) {
auto pReply = pConnection->Reply();
DebugReply(pReply);
if (pReply->Status == CHTTPReply::ok) {
const CJSON OAuth2(pReply->Content);
UpdateOAuth2(Context, OAuth2.Object());
}
pConnection->CloseConnection(true);
@@ -192,7 +262,98 @@ namespace Apostol {
Context.SetStatus(Context::csInitialization);
};
Context.SetStatus(Context::csPreparing);
auto pClient = GetClient(Context.URL().hostname, Context.URL().port == 0 ? BPS_SERVER_PORT : Context.URL().port);
pClient->OnRequest(OnRequest);
pClient->OnExecute(OnExecute);
pClient->OnException(OnException);
pClient->AutoFree(true);
pClient->Active(true);
}
//--------------------------------------------------------------------------------------------------------------
void CWebSocketModule::ModuleNew(CContext &Context) {
Log()->Notice("[%s] [%s] Trying create new module.", Context.Name().c_str(), Context.URL().Origin().c_str());
auto OnRequest = [this, &Context](CHTTPClient *Sender, CHTTPRequest *ARequest) {
Context.SetStatus(Context::csInProgress);
ARequest->ContentType = CHTTPRequest::json;
const auto &caModuleAddress = m_Module["address"];
CJSON Json(jvtObject);
Json.Object().AddPair("address", caModuleAddress);
Json.Object().AddPair("bitmessage", Context.Name());
const OpenPGP::SecretKey pgpSecret(m_pgpModuleKey.c_str());
const auto &public_key = pgpSecret.get_public();
Json.Object().AddPair("pgp", public_key.write(OpenPGP::PGP::Armored::YES));
ARequest->Content = Json.ToString();
CHTTPRequest::Prepare(ARequest, "POST", "/api/v1/client/new");
ARequest->AddHeader("Authorization", "Bearer " + Context.Tokens()["access_token"]);
if (!caModuleAddress.IsEmpty())
ARequest->AddHeader("Module-Address", caModuleAddress);
DebugRequest(ARequest);
};
auto OnExecute = [this, &Context](CTCPConnection *AConnection) {
Context.SetStatus(Context::csInitialized);
auto pConnection = dynamic_cast<CHTTPClientConnection *> (AConnection);
if (pConnection != nullptr) {
auto pReply = pConnection->Reply();
DebugReply(pReply);
if (pReply->Status == CHTTPReply::ok) {
const CJSON Json(pReply->Content);
if (Json.HasOwnProperty("result")) {
const auto &caResult = Json["result"];
if (caResult.HasOwnProperty("success")) {
if (caResult["success"].AsBoolean()) {
ModuleAuthorize(Context);
} else {
if (caResult.HasOwnProperty("message")) {
Log()->Error(APP_LOG_EMERG, 0, "[%s] [%s] %s", Context.Name().c_str(),
Context.URL().Origin().c_str(), caResult["message"].AsString().c_str());
}
}
}
}
}
pConnection->CloseConnection(true);
}
return true;
};
auto OnException = [&Context](CTCPConnection *AConnection, const Delphi::Exception::Exception &E) {
auto pConnection = dynamic_cast<CHTTPClientConnection *> (AConnection);
if (pConnection != nullptr) {
auto pClient = dynamic_cast<CHTTPClient *> (pConnection->Client());
if (pClient != nullptr) {
Log()->Error(APP_LOG_EMERG, 0, "[%s:%d] %s", pClient->Host().c_str(), pClient->Port(), E.what());
}
DebugReply(pConnection->Reply());
}
Context.SetStatus(Context::csInitialized);
};
auto pClient = GetClient(Context.URL().hostname, Context.URL().port == 0 ? BPS_SERVER_PORT : Context.URL().port);
@@ -205,41 +366,108 @@ namespace Apostol {
}
//--------------------------------------------------------------------------------------------------------------
void CWebSocketModule::UpdateOAuth2(const CString &FileName) {
for (int i = 0; i < m_Servers.Count(); i++) {
auto &Context = m_Servers[i].Value();
if (Context.Status() == Context::csInitialization || Context.Status() == Context::csPreparing) {
if (LoadOAuth2(FileName, Context.Providers())) {
Context.SetStatus(Context::csInitialized);
Context.SetCheckDate(0);
} else {
if (Context.Status() != Context::csPreparing) {
FetchOAuth2(Context);
}
void CWebSocketModule::ModuleAuthorize(CContext &Context) {
Log()->Notice("[%s] [%s] Trying authorize module.", Context.Name().c_str(), Context.URL().Origin().c_str());
auto OnRequest = [this, &Context](CHTTPClient *Sender, CHTTPRequest *ARequest) {
Context.SetStatus(Context::csAuthorization);
ARequest->ContentType = CHTTPRequest::text;
Apostol::PGP::CleartextSignature(
m_pgpModuleKey,
m_pgpPassphrase,
BPS_PGP_HASH,
Context.Name(),
ARequest->Content);
CHTTPRequest::Prepare(ARequest, "POST", "/api/v1/dm/authorize");
ARequest->AddHeader("Authorization", "Bearer " + Context.Tokens()["access_token"]);
const auto &caModuleAddress = m_Module["address"];
if (!caModuleAddress.IsEmpty())
ARequest->AddHeader("Module-Address", caModuleAddress);
DebugRequest(ARequest);
};
auto OnExecute = [&Context](CTCPConnection *AConnection) {
Context.SetStatus(Context::csInitialized);
auto pConnection = dynamic_cast<CHTTPClientConnection *> (AConnection);
if (pConnection != nullptr) {
auto pReply = pConnection->Reply();
DebugReply(pReply);
if (pReply->Status == CHTTPReply::ok) {
const CJSON Json(pReply->Content);
Context.Session() = Json["session"].AsString();
Context.Secret() = Json["secret"].AsString();
Context.Tokens().Values("access_token", Json["access_token"].AsString());
Context.SetFixedDate(0);
Context.SetCheckDate(Now() + (CDateTime) 55 / MinsPerDay); // 55 min
Context.SetStatus(csAuthorized);
}
pConnection->CloseConnection(true);
}
return true;
};
auto OnException = [&Context](CTCPConnection *AConnection, const Delphi::Exception::Exception &E) {
auto pConnection = dynamic_cast<CHTTPClientConnection *> (AConnection);
if (pConnection != nullptr) {
auto pClient = dynamic_cast<CHTTPClient *> (pConnection->Client());
if (pClient != nullptr) {
Log()->Error(APP_LOG_EMERG, 0, "[%s:%d] %s", pClient->Host().c_str(), pClient->Port(), E.what());
}
DebugReply(pConnection->Reply());
}
Context.SetStatus(Context::csInitialized);
};
auto pClient = GetClient(Context.URL().hostname, Context.URL().port == 0 ? BPS_SERVER_PORT : Context.URL().port);
pClient->OnRequest(OnRequest);
pClient->OnExecute(OnExecute);
pClient->OnException(OnException);
pClient->AutoFree(true);
pClient->Active(true);
}
//--------------------------------------------------------------------------------------------------------------
void CWebSocketModule::UpdateOAuth2(CContext &Context, const CJSONObject &OAuth2) {
if (OAuth2["type"].AsString() == "service_account") {
UpdateProviders(Context.Providers(), OAuth2);
Context.SetCheckDate(0);
Context.SetStatus(Context::csInitialized);
}
}
//--------------------------------------------------------------------------------------------------------------
bool CWebSocketModule::LoadOAuth2(const CString &FileName, CProviders &Providers) {
void CWebSocketModule::UpdateProviders(CProviders &Providers, const CJSONObject &Data) {
const auto &caProviderName = CString(SYSTEM_PROVIDER_NAME);
const auto &caApplicationName = CString(SERVICE_APPLICATION_NAME);
if (FileExists(FileName.c_str())) {
CJSONObject Json;
Json.LoadFromFile(FileName.c_str());
int index = Providers.IndexOfName(caProviderName);
if (index == -1)
index = Providers.AddPair(caProviderName, CProvider(caProviderName));
auto& Provider = Providers[index].Value();
Provider.Applications().AddPair(caApplicationName, Json);
return true;
}
return false;
int index = Providers.IndexOfName(caProviderName);
if (index == -1)
index = Providers.AddPair(caProviderName, CProvider(caProviderName));
auto& Provider = Providers[index].Value();
Provider.Applications().AddPair(caApplicationName, Data);
}
//--------------------------------------------------------------------------------------------------------------
@@ -355,9 +583,7 @@ namespace Apostol {
pClient->Active(true);
Context.SetFixedDate(0);
if (Context.Status() == Context::csInProgress)
Context.SetStatus(csRunning);
Context.SetStatus(csRunning);
} catch (std::exception &e) {
Log()->Error(APP_LOG_ERR, 0, e.what());
}
@@ -425,7 +651,7 @@ namespace Apostol {
void CWebSocketModule::CreateAccessToken(const CProvider &Provider, const CString &Application, CClientContext &Context) {
auto OnDone = [&Context](CTCPConnection *Sender) {
auto OnDone = [this, &Context](CTCPConnection *Sender) {
auto pConnection = dynamic_cast<CHTTPClientConnection *> (Sender);
auto pReply = pConnection->Reply();
@@ -435,14 +661,12 @@ namespace Apostol {
if (pReply->Status == CHTTPReply::ok) {
const CJSON Json(pReply->Content);
if (Context.Status() == Context::csAuthorization)
Context.SetStatus(csAuthorized);
Context.Session() = Json["session"].AsString();
Context.Secret() = Json["secret"].AsString();
Context.SetFixedDate(0);
Context.SetCheckDate(Now() + (CDateTime) 55 / MinsPerDay); // 55 min
Context.Tokens().Values("access_token", Json["access_token"].AsString());
ModuleStatus(Context);
}
return true;
@@ -639,7 +863,6 @@ namespace Apostol {
if (Response.Payload.HasOwnProperty("data")) {
UpdateServerList(Response.Payload["data"].AsString());
UpdateOAuth2(m_OAuth2);
}
}
//--------------------------------------------------------------------------------------------------------------
@@ -863,7 +1086,7 @@ namespace Apostol {
sPayload = caClearText.Text();
} else {
Apostol::PGP::CleartextSignature(
m_pgpPrivateKey,
m_pgpModuleKey,
m_pgpPassphrase,
BPS_PGP_HASH,
caClearText.Text(),
@@ -1260,7 +1483,7 @@ namespace Apostol {
sPayload = caClearText;
} else {
Apostol::PGP::CleartextSignature(
m_pgpPrivateKey,
m_pgpModuleKey,
m_pgpPassphrase,
BPS_PGP_HASH,
caClearText,
@@ -1517,34 +1740,27 @@ namespace Apostol {
for (int i = 0; i < m_Servers.Count(); i++) {
auto &Context = m_Servers[i].Value();
if ((Now >= Context.CheckDate()) && (Context.Status() == Context::csInitialization)) {
Context.SetCheckDate(Now + (CDateTime) 30 / SecsPerDay); // 30 sec
FetchOAuth2(Context);
}
if ((Now >= Context.CheckDate()) && (Context.Status() >= Context::csInitialized)) {
if (Now >= Context.CheckDate()) {
Context.SetCheckDate(Now + (CDateTime) 30 / SecsPerDay); // 30 sec
if (Context.Status() == Context::csInitialized)
Context.SetStatus(Context::csAuthorization);
if (Context.Status() == Context::csInitialization) {
ModuleService(Context);
}
CheckProviders(Now, Context);
FetchProviders(Now, Context);
}
if (Context.Status() == Context::csAuthorized) {
if (Now >= Context.FixedDate()) {
Context.SetFixedDate(Now + (CDateTime) 30 / SecsPerDay); // 30 sec
Context.SetStatus(Context::csInProgress);
CreateWebSocketClient(Context);
if (Context.Status() >= Context::csInitialized) {
CheckProviders(Now, Context);
FetchProviders(Now, Context);
}
}
if (Context.Status() == Context::csRunning) {
if (Now >= Context.FixedDate()) {
Context.SetFixedDate(Now + (CDateTime) 30 / SecsPerDay); // 30 sec
if (Now >= Context.FixedDate()) {
Context.SetFixedDate(Now + (CDateTime) 30 / SecsPerDay); // 30 sec
if (Context.Status() == Context::csAuthorized) {
CreateWebSocketClient(Context);
}
if (Context.Status() == Context::csRunning) {
for (int j = 0; j < Context.ClientManager().Count(); ++j) {
auto pClient = Context.ClientManager()[j];
@@ -1552,7 +1768,10 @@ namespace Apostol {
pClient->Active(true);
if (!pClient->Connected() && !pClient->Session().IsEmpty()) {
Log()->Notice(_T("[%s] Trying connect to %s."), pClient->Session().c_str(), pClient->URI().href().c_str());
Log()->Notice(_T("[%s] [%s] [%s] Trying to establish a connection."),
Context.Name().c_str(),
Context.URL().Origin().c_str(),
pClient->Session().c_str());
pClient->ConnectStart();
}
}
@@ -1565,9 +1784,15 @@ namespace Apostol {
void CWebSocketModule::Reload() {
Config()->IniFile().ReadSectionValues("module", &m_Module);
m_OAuth2 = Config()->IniFile().ReadString(CONFIG_SECTION_NAME, "oauth2", "oauth2/service.json");
if (!path_separator(m_OAuth2.front())) {
m_OAuth2 = Config()->Prefix() + m_OAuth2;
CString FileName;
FileName = Config()->IniFile().ReadString(CONFIG_SECTION_NAME, "oauth2", "oauth2/service.json");
if (!path_separator(FileName.front())) {
FileName = Config()->Prefix() + FileName;
}
if (FileExists(FileName.c_str())) {
m_OAuth2.LoadFromFile(FileName.c_str());
}
const auto &caPrivateKey = Config()->IniFile().ReadString("pgp", "private", "module.sec");
@@ -1576,14 +1801,11 @@ namespace Apostol {
m_pgpPassphrase = Config()->IniFile().ReadString("pgp", "passphrase", "");
if (FileExists(caPrivateKey.c_str())) {
m_pgpPrivateKey.LoadFromFile(caPrivateKey.c_str());
m_pgpModuleKey.LoadFromFile(caPrivateKey.c_str());
if (FileExists(caPublicKey.c_str())) {
CString Key;
Key.LoadFromFile(caPublicKey.c_str());
UpdateServerList(Key);
UpdateOAuth2(m_OAuth2);
m_pgpPublicKey.LoadFromFile(caPublicKey.c_str());
UpdateServerList(m_pgpPublicKey);
} else {
Log()->Error(APP_LOG_WARN, 0, APP_FILE_NOT_FOUND, caPublicKey.c_str());
}

View File

@@ -69,8 +69,10 @@ namespace Apostol {
CStringList m_Module;
CString m_OAuth2;
CString m_pgpPrivateKey;
CJSON m_OAuth2;
CString m_pgpModuleKey;
CString m_pgpPublicKey;
CString m_pgpPassphrase;
int m_SyncPeriod;
@@ -83,7 +85,6 @@ namespace Apostol {
void InitServerList();
void UpdateServerList(const CString &Key);
void UpdateOAuth2(const CString &FileName);
void FetchProviders(CDateTime Now, CClientContext &Context);
void CheckProviders(CDateTime Now, CClientContext &Context);
@@ -94,7 +95,10 @@ namespace Apostol {
CWebSocketClient *GetWebSocketClient(CClientContext &Context);
void CreateWebSocketClient(CClientContext &Context);
void FetchOAuth2(CContext &Context);
void ModuleService(CContext &Context);
void ModuleStatus(CContext &Context);
void ModuleNew(CContext &Context);
void ModuleAuthorize(CContext &Context);
static CWebSocketClient *GetConnectedClient(const CClientContext &Context);
@@ -147,12 +151,14 @@ namespace Apostol {
void Reload();
static CString ToString(unsigned long Value);
static int CheckFee(const CString& Fee);
static void CheckDeal(const CDeal& Deal);
static void CheckKeyForNull(LPCTSTR key, const CString& Value);
static bool FindURLInLine(const CString &Line, CStringList &List);
static bool LoadOAuth2(const CString &FileName, CProviders &Providers);
static void UpdateOAuth2(CContext &Context, const CJSONObject &OAuth2);
static void UpdateProviders(CProviders &Providers, const CJSONObject &Data);
};
}
}