From: Aki Tuomi Date: Fri, 18 Aug 2023 08:13:54 +0000 (+0300) Subject: ws-auth.cc: Split apiServerZoneDetail to GET, PATCH, PUT and DELETE variants X-Git-Tag: auth-4.9.0-alpha1~42^2~14 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b62707f99ea1b9bec98bf88de2dc4833e03172b8;p=thirdparty%2Fpdns.git ws-auth.cc: Split apiServerZoneDetail to GET, PATCH, PUT and DELETE variants Enables us to specify method routes for this later. --- diff --git a/pdns/ws-auth.cc b/pdns/ws-auth.cc index 9aa661fab7..0645efcd33 100644 --- a/pdns/ws-auth.cc +++ b/pdns/ws-auth.cc @@ -2022,137 +2022,153 @@ static void apiServerZones(HttpRequest* req, HttpResponse* resp) { throw HttpMethodNotAllowedException(); } -static void apiServerZoneDetail(HttpRequest* req, HttpResponse* resp) { +static void apiServerZoneDetailPUT(HttpRequest* req, HttpResponse* resp) { zoneFromId(req); - if(req->method == "PUT") { - // update domain contents and/or settings - auto document = req->json(); + // update domain contents and/or settings + const auto& document = req->json(); - auto rrsets = document["rrsets"]; - bool zoneWasModified = false; - DomainInfo::DomainKind newKind = di.kind; - if (document["kind"].is_string()) { - newKind = DomainInfo::stringToKind(stringFromJson(document, "kind")); - } + auto rrsets = document["rrsets"]; + bool zoneWasModified = false; + DomainInfo::DomainKind newKind = di.kind; + if (document["kind"].is_string()) { + newKind = DomainInfo::stringToKind(stringFromJson(document, "kind")); + } - // if records/comments are given, load, check and insert them - if (rrsets.is_array()) { - zoneWasModified = true; - bool haveSoa = false; - string soaEditApiKind; - string soaEditKind; - di.backend->getDomainMetadataOne(zonename, "SOA-EDIT-API", soaEditApiKind); - di.backend->getDomainMetadataOne(zonename, "SOA-EDIT", soaEditKind); + // if records/comments are given, load, check and insert them + if (rrsets.is_array()) { + zoneWasModified = true; + bool haveSoa = false; + string soaEditApiKind; + string soaEditKind; + di.backend->getDomainMetadataOne(zonename, "SOA-EDIT-API", soaEditApiKind); + di.backend->getDomainMetadataOne(zonename, "SOA-EDIT", soaEditKind); - vector new_records; - vector new_comments; + vector new_records; + vector new_comments; - try { - for (const auto& rrset : rrsets.array_items()) { - DNSName qname = apiNameToDNSName(stringFromJson(rrset, "name")); - apiCheckQNameAllowedCharacters(qname.toString()); - QType qtype; - qtype = stringFromJson(rrset, "type"); - if (qtype.getCode() == 0) { - throw ApiException("RRset "+qname.toString()+" IN "+stringFromJson(rrset, "type")+": unknown type given"); - } - if (rrset["records"].is_array()) { - uint32_t ttl = uintFromJson(rrset, "ttl"); - gatherRecords(rrset, qname, qtype, ttl, new_records); - } - if (rrset["comments"].is_array()) { - gatherComments(rrset, qname, qtype, new_comments); - } + try { + for (const auto& rrset : rrsets.array_items()) { + DNSName qname = apiNameToDNSName(stringFromJson(rrset, "name")); + apiCheckQNameAllowedCharacters(qname.toString()); + QType qtype; + qtype = stringFromJson(rrset, "type"); + if (qtype.getCode() == 0) { + throw ApiException("RRset "+qname.toString()+" IN "+stringFromJson(rrset, "type")+": unknown type given"); } - } - catch (const JsonException& e) { - throw ApiException("New RRsets are invalid: " + string(e.what())); - } - - for(auto& rr : new_records) { - rr.qname.makeUsLowerCase(); - if (!rr.qname.isPartOf(zonename) && rr.qname != zonename) { - throw ApiException("RRset "+rr.qname.toString()+" IN "+rr.qtype.toString()+": Name is out of zone"); + if (rrset["records"].is_array()) { + uint32_t ttl = uintFromJson(rrset, "ttl"); + gatherRecords(rrset, qname, qtype, ttl, new_records); } - apiCheckQNameAllowedCharacters(rr.qname.toString()); - - if (rr.qtype.getCode() == QType::SOA && rr.qname == zonename) { - haveSoa = true; + if (rrset["comments"].is_array()) { + gatherComments(rrset, qname, qtype, new_comments); } } + } + catch (const JsonException& exc) { + throw ApiException("New RRsets are invalid: " + string(exc.what())); + } - if (!haveSoa && newKind != DomainInfo::Secondary && newKind != DomainInfo::Consumer) { - // Require SOA if this is a primary zone. - throw ApiException("Must give SOA record for zone when replacing all RR sets"); - } - if (newKind == DomainInfo::Consumer && !new_records.empty()) { - // Allow deleting all RRsets, just not modifying them. - throw ApiException("Modifying RRsets in Consumer zones is unsupported"); + for(auto& rr : new_records) { // NOLINT(readability-identifier-length) + rr.qname.makeUsLowerCase(); + if (!rr.qname.isPartOf(zonename) && rr.qname != zonename) { + throw ApiException("RRset "+rr.qname.toString()+" IN "+rr.qtype.toString()+": Name is out of zone"); } + apiCheckQNameAllowedCharacters(rr.qname.toString()); - checkNewRecords(new_records, zonename); - - di.backend->startTransaction(zonename, static_cast(di.id)); - for(auto& rr : new_records) { - rr.domain_id = static_cast(di.id); - di.backend->feedRecord(rr, DNSName()); - } - for(Comment& c : new_comments) { - c.domain_id = static_cast(di.id); - di.backend->feedComment(c); + if (rr.qtype.getCode() == QType::SOA && rr.qname == zonename) { + haveSoa = true; } + } - if (!haveSoa && (newKind == DomainInfo::Secondary || newKind == DomainInfo::Consumer)) { - di.backend->setStale(di.id); - } - } else { - // avoid deleting current zone contents - di.backend->startTransaction(zonename, -1); + if (!haveSoa && newKind != DomainInfo::Secondary && newKind != DomainInfo::Consumer) { + // Require SOA if this is a primary zone. + throw ApiException("Must give SOA record for zone when replacing all RR sets"); + } + if (newKind == DomainInfo::Consumer && !new_records.empty()) { + // Allow deleting all RRsets, just not modifying them. + throw ApiException("Modifying RRsets in Consumer zones is unsupported"); } - // updateDomainSettingsFromDocument will rectify the zone and update SOA serial. - updateDomainSettingsFromDocument(B, di, zonename, document, zoneWasModified); - di.backend->commitTransaction(); + checkNewRecords(new_records, zonename); - purgeAuthCaches(zonename.toString() + "$"); + di.backend->startTransaction(zonename, static_cast(di.id)); + for(auto& rr : new_records) { // NOLINT(readability-identifier-length) + rr.domain_id = static_cast(di.id); + di.backend->feedRecord(rr, DNSName()); + } + for(Comment& comment : new_comments) { + comment.domain_id = static_cast(di.id); + di.backend->feedComment(comment); + } - resp->body = ""; - resp->status = 204; // No Content, but indicate success - return; + if (!haveSoa && (newKind == DomainInfo::Secondary || newKind == DomainInfo::Consumer)) { + di.backend->setStale(di.id); + } + } else { + // avoid deleting current zone contents + di.backend->startTransaction(zonename, -1); } - else if(req->method == "DELETE") { - // delete domain - di.backend->startTransaction(zonename, -1); - try { - if(!di.backend->deleteDomain(zonename)) - throw ApiException("Deleting domain '"+zonename.toString()+"' failed: backend delete failed/unsupported"); + // updateDomainSettingsFromDocument will rectify the zone and update SOA serial. + updateDomainSettingsFromDocument(B, di, zonename, document, zoneWasModified); + di.backend->commitTransaction(); - di.backend->commitTransaction(); + purgeAuthCaches(zonename.toString() + "$"); - g_zoneCache.remove(zonename); - } catch (...) { - di.backend->abortTransaction(); - throw; + resp->body = ""; + resp->status = 204; // No Content, but indicate success +} + +static void apiServerZoneDetailDELETE(HttpRequest* req, HttpResponse* resp) { + zoneFromId(req); + + // delete domain + + di.backend->startTransaction(zonename, -1); + try { + if(!di.backend->deleteDomain(zonename)) { + throw ApiException("Deleting domain '"+zonename.toString()+"' failed: backend delete failed/unsupported"); } - // clear caches - DNSSECKeeper::clearCaches(zonename); - purgeAuthCaches(zonename.toString() + "$"); + di.backend->commitTransaction(); - // empty body on success - resp->body = ""; - resp->status = 204; // No Content: declare that the zone is gone now - return; - } else if (req->method == "PATCH") { - patchZone(B, zonename, di, req, resp); - return; - } else if (req->method == "GET") { - fillZone(B, zonename, resp, req); - return; + purgeAuthCaches(zonename.toString() + "$"); + } catch (...) { + di.backend->abortTransaction(); + throw; } - throw HttpMethodNotAllowedException(); + + // clear caches + DNSSECKeeper::clearCaches(zonename); + purgeAuthCaches(zonename.toString() + "$"); + + // empty body on success + resp->body = ""; + resp->status = 204; // No Content: declare that the zone is gone now +} + +static void apiServerZoneDetailPATCH(HttpRequest* req, HttpResponse* resp) { + zoneFromId(req); + patchZone(B, zonename, di, req, resp); +} + +static void apiServerZoneDetailGET(HttpRequest* req, HttpResponse* resp) { + zoneFromId(req); + fillZone(B, zonename, resp, req); +} + +static void apiServerZoneDetail(HttpRequest* req, HttpResponse* resp) { + if (req->method == "GET") + apiServerZoneDetailGET(req, resp); + else if (req->method == "PATCH") + apiServerZoneDetailPATCH(req, resp); + else if (req->method == "PUT") + apiServerZoneDetailPUT(req, resp); + else if (req->method == "DELETE") + apiServerZoneDetailDELETE(req, resp); + else + throw HttpMethodNotAllowedException(); } static void apiServerZoneExport(HttpRequest* req, HttpResponse* resp) {