Files
wlan-cloud-owprov/src/RadiusEndpointTypes/GlobalReach.cpp
2023-10-06 12:21:12 -07:00

259 lines
11 KiB
C++

//
// Created by stephane bourque on 2023-09-11.
//
#include "GlobalReach.h"
#include <Poco/JWT/Token.h>
#include <Poco/JWT/Signer.h>
#include <Poco/Net/HTTPSClientSession.h>
#include <Poco/Net/DNS.h>
#include <Poco/URI.h>
#include <Poco/TemporaryFile.h>
#include <Poco/JSON/Object.h>
#include <Poco/JSON/Parser.h>
#include <framework/RESTAPI_Handler.h>
#include <framework/MicroServiceFuncs.h>
#include <StorageService.h>
namespace OpenWifi {
namespace GlobalReach {
int OpenRoaming::Start() {
poco_information(Logger(), "Starting...");
InitCache();
return 0;
}
void OpenRoaming::Stop() {
poco_information(Logger(), "Stopping...");
poco_information(Logger(), "Stopped...");
}
void OpenRoaming::InitCache() {
auto F = [&](const ProvObjects::GLBLRAccountInfo &Info) {
poco_information(Logger(), fmt::format("Adding {} to cache.", Info.info.name));
if (!Info.privateKey.empty() && !Info.GlobalReachAcctId.empty()) {
MakeToken(Info.GlobalReachAcctId, Info.privateKey);
}
return true;
};
StorageService()->GLBLRAccountInfoDB().Iterate(F);
}
bool OpenRoaming::Render(const OpenWifi::ProvObjects::RADIUSEndPoint &RE, const std::string &SerialNumber, Poco::JSON::Object &Result) {
if(RE.UseGWProxy) {
Poco::JSON::Object Auth, Acct, CoA;
Auth.set("host", RE.Index);
Auth.set("port", 1812 );
Auth.set("secret", RE.RadsecServers[0].Secret);
Acct.set("host", RE.Index);
Acct.set("port", 1813);
Acct.set("secret", RE.RadsecServers[0].Secret);
Acct.set("interval", RE.AccountingInterval);
CoA.set("host", RE.Index);
CoA.set("port", 3799);
CoA.set("secret", RE.RadsecServers[0].Secret);
Result.set("nas-identifier", RE.NasIdentifier.empty() ? SerialNumber : RE.NasIdentifier );
Result.set("authentication", Auth);
Result.set("accounting", Acct);
Result.set("dynamic-authorization", CoA);
} else {
}
return false;
}
bool OpenRoaming::CreateRADSECCertificate(
const std::string &GlobalReachAccountId,
const std::string &Name,
const std::string &CSR,
ProvObjects::GLBLRCertificateInfo &NewCertificate) {
try {
std::cout << __LINE__ << ":" << GlobalReachAccountId << std::endl;
auto BearerToken = MakeToken(GlobalReachAccountId);
Poco::URI URI{"https://config.openro.am/v1/radsec/issue"};
std::string Path(URI.getPathAndQuery());
Poco::Net::HTTPRequest Request(Poco::Net::HTTPRequest::HTTP_POST, Path,
Poco::Net::HTTPMessage::HTTP_1_1);
Request.add("Authorization", "Bearer " + BearerToken);
Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort());
Session.setTimeout(Poco::Timespan(10000, 10000));
Poco::JSON::Object CertRequestBody;
CertRequestBody.set("name", Name);
CertRequestBody.set("csr", CSR);
std::ostringstream os;
CertRequestBody.stringify(os);
Request.setContentType("application/json");
Request.setContentLength((long) os.str().size());
auto &Body = Session.sendRequest(Request);
Body << os.str();
Poco::Net::HTTPResponse Response;
std::istream &is = Session.receiveResponse(Response);
if (Response.getStatus() == Poco::Net::HTTPResponse::HTTP_OK) {
Poco::JSON::Parser P;
auto Result = P.parse(is).extract<Poco::JSON::Object::Ptr>();
RESTAPIHandler::AssignIfPresent(Result, "certificate", NewCertificate.certificate);
RESTAPIHandler::AssignIfPresent(Result, "certificate_chain", NewCertificate.certificateChain);
RESTAPIHandler::AssignIfPresent(Result, "certificate_id", NewCertificate.certificateId);
RESTAPIHandler::AssignIfPresent(Result, "expires_at", NewCertificate.expiresAt);
return true;
}
Poco::JSON::Parser P;
std::ostringstream oos;
auto Result = P.parse(is).extract<Poco::JSON::Object::Ptr>();
Result->stringify(oos);
} catch (const Poco::Exception &E) {
poco_error(Logger(),
fmt::format("Could not create a new RADSEC certificate: {},{}", E.name(), E.displayText()));
}
return false;
}
bool OpenRoaming::GetRADSECCertificate(
const std::string &GlobalReachAccountId,
std::string &CertificateId,
ProvObjects::GLBLRCertificateInfo &NewCertificate) {
try {
Poco::URI URI{fmt::format("https://config.openro.am/v1/radsec/cert/{}", CertificateId)};
std::string Path(URI.getPathAndQuery());
Poco::Net::HTTPRequest Request(Poco::Net::HTTPRequest::HTTP_GET, Path,
Poco::Net::HTTPMessage::HTTP_1_1);
auto BearerToken = MakeToken(GlobalReachAccountId);
Request.add("Authorization", "Bearer " + BearerToken);
Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort());
Session.setTimeout(Poco::Timespan(10000, 10000));
Session.sendRequest(Request);
Poco::Net::HTTPResponse Response;
std::istream &is = Session.receiveResponse(Response);
if (Response.getStatus() == Poco::Net::HTTPResponse::HTTP_OK) {
Poco::JSON::Parser P;
auto Result = P.parse(is).extract<Poco::JSON::Object::Ptr>();
RESTAPIHandler::AssignIfPresent(Result, "certificate", NewCertificate.certificate);
RESTAPIHandler::AssignIfPresent(Result, "certificate_chain", NewCertificate.certificateChain);
RESTAPIHandler::AssignIfPresent(Result, "certificate_id", NewCertificate.certificateId);
RESTAPIHandler::AssignIfPresent(Result, "expires_at", NewCertificate.expiresAt);
return true;
}
} catch (const Poco::Exception &E) {
poco_error(Logger(), fmt::format("Could not retrieve the certificate from GlobalReach: {},{}", E.name(),
E.displayText()));
}
return false;
}
std::string
OpenRoaming::MakeToken(const std::string &GlobalReachAccountId, const std::string &PrivateKey) {
try {
Poco::JWT::Token token;
token.setType("JWT");
token.setAlgorithm("ES256");
token.setIssuedAt(std::time(nullptr));
token.payload().set("iss", GlobalReachAccountId);
token.payload().set("iat", (unsigned long) std::time(nullptr));
Poco::SharedPtr<Poco::Crypto::ECKey> Key;
auto KeyHash = Utils::ComputeHash(PrivateKey);
auto KeyHint = PrivateKeys_.find(GlobalReachAccountId);
if (KeyHint != PrivateKeys_.end() && PrivateKey.empty()) {
Key = KeyHint->second.second;
} else {
if (PrivateKey.empty()) {
return "";
}
Poco::TemporaryFile F;
std::ofstream ofs(F.path().c_str(),
std::ios_base::trunc | std::ios_base::out | std::ios_base::binary);
ofs << PrivateKey;
ofs.close();
auto NewKey = Poco::SharedPtr<Poco::Crypto::ECKey>(
new Poco::Crypto::ECKey("", F.path(), ""));
Key = NewKey;
PrivateKeys_[GlobalReachAccountId] = std::make_pair(KeyHash, NewKey);
}
Poco::JWT::Signer Signer;
Signer.setECKey(Key);
Signer.addAllAlgorithms();
return Signer.sign(token, Poco::JWT::Signer::ALGO_ES256);
} catch (const Poco::Exception &E) {
poco_error(Logger(),
fmt::format("Cannot create a Global Reach token: {},{}", E.name(), E.displayText()));
}
return "";
}
bool
OpenRoaming::VerifyAccount(const std::string &GlobalReachAccountId, const std::string &PrivateKey,
std::string &Name) {
auto BearerToken = MakeToken(GlobalReachAccountId, PrivateKey);
Poco::URI URI{"https://config.openro.am/v1/config"};
std::string Path(URI.getPathAndQuery());
Poco::Net::HTTPRequest Request(Poco::Net::HTTPRequest::HTTP_GET, Path,
Poco::Net::HTTPMessage::HTTP_1_1);
Request.add("Authorization", "Bearer " + BearerToken);
Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort());
Session.setTimeout(Poco::Timespan(10000, 10000));
Session.sendRequest(Request);
Poco::Net::HTTPResponse Response;
std::istream &is = Session.receiveResponse(Response);
if (Response.getStatus() == Poco::Net::HTTPResponse::HTTP_OK) {
Poco::JSON::Parser P;
auto Result = P.parse(is).extract<Poco::JSON::Object::Ptr>();
if (Result->has("name")) {
Name = Result->get("name").toString();
}
return true;
}
return false;
}
// static std::string ServiceName{"\"aaa+auth:radius.tls.tcp\""};
static std::string ServiceName{"\"x-openroam:radius.tls.tcp\""};
std::vector<Utils::HostNameServerResult> OpenRoaming::GetServers() {
const std::string &domain = "openro.am";
auto Naptrs = Utils::getNAPTRRecords(domain);
std::vector<Utils::HostNameServerResult> Results;
for(const auto &rec:Naptrs) {
if(rec.service==ServiceName) {
auto Srvs = Utils::getSRVRecords(rec.replacement);
for(const auto &srv:Srvs) {
Utils::HostNameServerResult R{srv.srvname,srv.port};
if(!Utils::ValidIP(srv.srvname)) {
auto Server = Poco::Net::DNS::hostByName(srv.srvname).addresses();
if(!Server.empty()) {
R.Hostname = Server[0].toString();
}
}
Results.emplace_back(R);
}
}
}
return Results;
}
}
} // OpenWifi