diff --git a/openapi/openroaming_globalreach.yaml b/openapi/openroaming_globalreach.yaml index 56c7b42..c71e471 100644 --- a/openapi/openroaming_globalreach.yaml +++ b/openapi/openroaming_globalreach.yaml @@ -89,7 +89,7 @@ paths: get: tags: - OpenRoaming-Global Reach - operationId: getOpenRoaminGlobalReachAccountList + operationId: getOpenRoamingGlobalReachAccountList summary: Retrieve account list. parameters: - in: query @@ -132,7 +132,7 @@ paths: get: tags: - OpenRoaming-Global Reach - operationId: getOpenRoaminGlobalReachAccount + operationId: getOpenRoamingGlobalReachAccount summary: Retrieve account information. parameters: - in: path @@ -154,7 +154,7 @@ paths: delete: tags: - OpenRoaming-Global Reach - operationId: deleteOpenRoaminGlobalReachAccount + operationId: deleteOpenRoamingGlobalReachAccount summary: Delete account information. parameters: - in: path @@ -176,7 +176,7 @@ paths: post: tags: - OpenRoaming-Global Reach - operationId: createOpenRoaminGlobalReachAccount + operationId: createOpenRoamingGlobalReachAccount summary: Create account information. parameters: - in: path @@ -203,7 +203,7 @@ paths: put: tags: - OpenRoaming-Global Reach - operationId: modifyOpenRoaminGlobalReachAccount + operationId: modifyOpenRoamingGlobalReachAccount summary: Modify account information. parameters: - in: path @@ -231,7 +231,7 @@ paths: get: tags: - OpenRoaming-Global Reach Certificate - operationId: getOpenRoaminGlobalReachCertificateList + operationId: getOpenRoamingGlobalReachCertificateList summary: Retrieve certificate list. parameters: - in: path @@ -280,17 +280,17 @@ paths: get: tags: - OpenRoaming-Global Reach Certificate - operationId: getOpenRoaminGlobalReachCertificate + operationId: getOpenRoamingGlobalReachCertificate summary: Retrieve certificate information. parameters: - in: path - description: The account name + description: The account name - this is the provisioning ID for the account. Not the GlobalReach ID. name: account schema: type: string required: true - in: path - description: The certificate id + description: The certificate id in provisioning - not the certificate_id from GlobalReach name: id schema: type: string @@ -308,17 +308,17 @@ paths: delete: tags: - OpenRoaming-Global Reach Certificate - operationId: deleteOpenRoaminGlobalReachCertificate + operationId: deleteOpenRoamingGlobalReachCertificate summary: Delete certificate information. parameters: - in: path - description: The account name + description: The account name - this is the provisioning ID for the account. Not the GlobalReach ID. name: account schema: type: string required: true - in: path - description: The certificate id + description: The certificate id in provisioning - not the certificate_id from GlobalReach name: id schema: type: string @@ -336,17 +336,17 @@ paths: post: tags: - OpenRoaming-Global Reach Certificate - operationId: createOpenRoaminGlobalReachCertificate + operationId: createOpenRoamingGlobalReachCertificate summary: Create certificate information. parameters: - in: path - description: The account name + description: The account name - this is the provisioning ID for the account. Not the GlobalReach ID. name: account schema: type: string required: true - in: path - description: The certificate id + description: Must be set to "0" name: id schema: type: string @@ -366,36 +366,3 @@ paths: 404: $ref: '#/components/responses/NotFound' - put: - tags: - - OpenRoaming-Global Reach Certificate - operationId: modifyOpenRoaminGlobalReachCertificate - summary: Modify certificate information. - parameters: - - in: path - description: The account name - name: account - schema: - type: string - required: true - - in: path - description: The certificate id - name: id - schema: - type: string - required: true - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/GLBLRAccountInfo' - responses: - 200: - $ref: '#/components/schemas/GLBLRAccountInfo' - 400: - $ref: '#/components/responses/BadRequest' - 403: - $ref: '#/components/responses/Unauthorized' - 404: - $ref: '#/components/responses/NotFound' - diff --git a/src/OpenRoamin_GlobalReach.cpp b/src/OpenRoamin_GlobalReach.cpp index d98aaa7..16bf43b 100644 --- a/src/OpenRoamin_GlobalReach.cpp +++ b/src/OpenRoamin_GlobalReach.cpp @@ -12,11 +12,13 @@ #include #include +#include namespace OpenWifi { int OpenRoaming_GlobalReach::Start() { poco_information(Logger(), "Starting..."); + InitCache(); return 0; } @@ -25,111 +27,150 @@ namespace OpenWifi { poco_information(Logger(), "Stopped..."); } - bool OpenRoaming_GlobalReach::GetAccountInfo( - [[maybe_unused]] const std::string &AccountName, - [[maybe_unused]] ProvObjects::GLBLRAccountInfo &Account) { -/* Poco::URI URI{"https://config.openro.am/v1/config"}; + void OpenRoaming_GlobalReach::InitCache() { - 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); + 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; + }; - Poco::Net::HTTPResponse Response; - std::istream &is = Session.receiveResponse(Response); - Poco::JSON::Parser P; - Result= P.parse(is).extract(); - - std::cout << Response.getStatus() << " : " ; - Result->stringify(std::cout); - std::cout << std::endl; - */ - return true; + StorageService()->GLBLRAccountInfoDB().Iterate(F); } - bool OpenRoaming_GlobalReach::CreateRadsecCertificate( - [[maybe_unused]] const std::string &AccountName, - [[maybe_unused]] ProvObjects::GLBLRCertificateInfo &NewCertificate) { -/* - 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)); - - std::ostringstream os; - Body.stringify(os); - Request.setContentType("application/json"); - Request.setContentLength(os.str().size()); - - auto &body = Session.sendRequest(Request); - body << os.str(); - - Poco::Net::HTTPResponse Response; - std::istream &is = Session.receiveResponse(Response); - Poco::JSON::Parser P; - Result= P.parse(is).extract(); - - std::cout << Response.getStatus() << " : " ; - Result->stringify(std::cout); - std::cout << std::endl; -*/ - return true; + bool OpenRoaming_GlobalReach::CreateRADSECCertificate( + const std::string &GlobalReachAccountId, + const std::string &Name, + const std::string &CSR, + ProvObjects::GLBLRCertificateInfo &NewCertificate) { + + try { + 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(); + NewCertificate.csr = Result->get("csr").toString(); + NewCertificate.certificate = Result->get("certificate").toString(); + NewCertificate.name = Result->get("name").toString(); + NewCertificate.certificateChain = Result->get("certificate_chain").toString(); + NewCertificate.certificateId = Result->get("certificate_id").toString(); + NewCertificate.expiresAt = Result->get("expires_at"); + return true; + } + } 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_GlobalReach::GetRadsecCertificate( - [[maybe_unused]] const std::string &AccountName, - [[maybe_unused]] std::string &CertificateId, - [[maybe_unused]] ProvObjects::GLBLRCertificateInfo &NewCertificate) { - return true; + bool OpenRoaming_GlobalReach::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(); + NewCertificate.csr = Result->get("csr").toString(); + NewCertificate.certificate = Result->get("certificate").toString(); + NewCertificate.name = Result->get("name").toString(); + NewCertificate.certificateChain = Result->get("certificate_chain").toString(); + NewCertificate.certificateId = Result->get("certificate_id").toString(); + NewCertificate.expiresAt = Result->get("expires_at"); + std::cout << Response.getStatus() << " : "; + Result->stringify(std::cout); + std::cout << std::endl; + 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_GlobalReach::MakeToken(const std::string &GlobalReachAccountId, const std::string &PrivateKey) { - 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 Key; - auto KeyHash = Utils::ComputeHash(PrivateKey); - auto KeyHint = PrivateKeys_.find(KeyHash); - if(KeyHint!=PrivateKeys_.end()) { - Key = KeyHint->second; - } else { - 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( - new Poco::Crypto::ECKey("", F.path(),"")); - Key = PrivateKeys_[KeyHash] = NewKey; - } + 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 Key; + auto KeyHash = Utils::ComputeHash(PrivateKey); + auto KeyHint = PrivateKeys_.find(GlobalReachAccountId); + if (KeyHint != PrivateKeys_.end() && KeyHint->first == KeyHash) { + 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( + 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); + 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_GlobalReach::VerifyAccount(const std::string &GlobalReachAccountId, const std::string &PrivateKey, [[ - maybe_unused]] std::string &Name) { + bool OpenRoaming_GlobalReach::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"}; diff --git a/src/OpenRoamin_GlobalReach.h b/src/OpenRoamin_GlobalReach.h index 3cfea01..6cb009e 100644 --- a/src/OpenRoamin_GlobalReach.h +++ b/src/OpenRoamin_GlobalReach.h @@ -19,15 +19,18 @@ namespace OpenWifi { int Start() override; void Stop() override; - bool GetAccountInfo(const std::string &AccountName, ProvObjects::GLBLRAccountInfo &Account); - bool CreateRadsecCertificate(const std::string &AccountName, ProvObjects::GLBLRCertificateInfo &NewCertificate); - bool GetRadsecCertificate(const std::string &AccountName, std::string & CertificateId, ProvObjects::GLBLRCertificateInfo &NewCertificate); + bool CreateRADSECCertificate(const std::string &AccountName, + const std::string &Name, + const std::string &CSR, + ProvObjects::GLBLRCertificateInfo &NewCertificate); + bool GetRADSECCertificate(const std::string &AccountName, std::string & CertificateId, ProvObjects::GLBLRCertificateInfo &NewCertificate); bool VerifyAccount(const std::string &GlobalReachAccountId, const std::string &PrivateKey, std::string &Name); + void InitCache(); private: - std::string MakeToken(const std::string &GlobalReachAccountId, const std::string &PrivateKey); + std::string MakeToken(const std::string &GlobalReachAccountId, const std::string &PrivateKey=""); - std::map> PrivateKeys_; + std::map>> PrivateKeys_; OpenRoaming_GlobalReach() noexcept : SubSystemServer("OpenRoaming_GlobalReach", "GLBL-REACH", "globalreach") { diff --git a/src/RESTAPI/RESTAPI_openroaming_gr_cert_handler.cpp b/src/RESTAPI/RESTAPI_openroaming_gr_cert_handler.cpp index e7aa4e1..2f39cb8 100644 --- a/src/RESTAPI/RESTAPI_openroaming_gr_cert_handler.cpp +++ b/src/RESTAPI/RESTAPI_openroaming_gr_cert_handler.cpp @@ -3,23 +3,78 @@ // #include "RESTAPI_openroaming_gr_cert_handler.h" +#include namespace OpenWifi { void RESTAPI_openroaming_gr_cert_handler::DoGet() { - return BadRequest(RESTAPI::Errors::NotImplemented); + auto Account = GetBinding("account",""); + auto Id = GetBinding("id",""); + + if(Account.empty() || Id.empty()) { + return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); + } + + if(!StorageService()->GLBLRAccountInfoDB().Exists("id",Account)) { + return NotFound(); + } + + std::vector Certificates; + DB_.GetRecords(0,1,Certificates,fmt::format(" accountId='{}' and id='{}' ", Account, Id)); + if(Certificates.empty()) { + return NotFound(); + } + return ReturnObject(Certificates[0]); } void RESTAPI_openroaming_gr_cert_handler::DoDelete() { - return BadRequest(RESTAPI::Errors::NotImplemented); + auto Account = GetBinding("account",""); + auto Id = GetBinding("id",""); + if(Account.empty() || Id.empty()) { + return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); + } + + if(!StorageService()->GLBLRAccountInfoDB().Exists("id",Account)) { + return NotFound(); + } + + DB_.DeleteRecords(fmt::format(" accountId='{}' and id='{}' ", Account, Id)); + return OK(); } void RESTAPI_openroaming_gr_cert_handler::DoPost() { - return BadRequest(RESTAPI::Errors::NotImplemented); - } + auto Account = GetBinding("account",""); + auto Id = GetBinding("id",""); + + if(Account.empty() || Id.empty()) { + return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); + } + + const auto &RawObject = ParsedBody_; + ProvObjects::GLBLRCertificateInfo NewObject; + if( !NewObject.from_json(RawObject)) { + return BadRequest(OpenWifi::RESTAPI::Errors::InvalidJSONDocument); + } + + if(NewObject.name.empty()) { + return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); + } + + ProvObjects::GLBLRAccountInfo AccountInfo; + if(!StorageService()->GLBLRAccountInfoDB().GetRecord("id",Account, AccountInfo)) { + return BadRequest(RESTAPI::Errors::InvalidGlobalReachAccount); + } + + if(OpenRoaming_GlobalReach()->CreateRADSECCertificate(AccountInfo.GlobalReachAcctId,NewObject.name,AccountInfo.CSR, NewObject)) { + NewObject.id = MicroServiceCreateUUID(); + NewObject.accountId = Account; + DB_.CreateRecord(NewObject); + ProvObjects::GLBLRCertificateInfo CreatedObject; + DB_.GetRecord("id",NewObject.id,CreatedObject); + return ReturnObject(CreatedObject); + } - void RESTAPI_openroaming_gr_cert_handler::DoPut() { - return BadRequest(RESTAPI::Errors::NotImplemented); + return BadRequest(RESTAPI::Errors::RecordNotCreated); } } // OpenWifi \ No newline at end of file diff --git a/src/RESTAPI/RESTAPI_openroaming_gr_cert_handler.h b/src/RESTAPI/RESTAPI_openroaming_gr_cert_handler.h index c79ad62..d562f78 100644 --- a/src/RESTAPI/RESTAPI_openroaming_gr_cert_handler.h +++ b/src/RESTAPI/RESTAPI_openroaming_gr_cert_handler.h @@ -15,7 +15,6 @@ namespace OpenWifi { : RESTAPIHandler(bindings, L, std::vector{Poco::Net::HTTPRequest::HTTP_GET, Poco::Net::HTTPRequest::HTTP_DELETE, - Poco::Net::HTTPRequest::HTTP_PUT, Poco::Net::HTTPRequest::HTTP_POST, Poco::Net::HTTPRequest::HTTP_OPTIONS}, Server, TransactionId, Internal) {} @@ -25,7 +24,7 @@ namespace OpenWifi { GLBLRCertsDB &DB_ = StorageService()->GLBLRCertsDB(); void DoGet() final; void DoPost() final; - void DoPut() final; + void DoPut() final {}; void DoDelete() final; }; } // namespace OpenWifi