From bc77bb16d474c7a15c620830f2e211487bce8db5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Sat, 5 Mar 2022 13:07:51 +0100 Subject: [PATCH] add delete route --- api.yml | 12 +++++++++ e2e/zones.py | 25 +++++++++++++++++-- src/dns/api.rs | 1 + src/dns/dns_api.rs | 61 +++++++++++++++++++++++++++++++++++++++++---- src/main.rs | 3 ++- src/routes/zones.rs | 32 ++++++++++++++++++++++-- 6 files changed, 124 insertions(+), 10 deletions(-) diff --git a/api.yml b/api.yml index 13913e4..fdef762 100644 --- a/api.yml +++ b/api.yml @@ -423,3 +423,15 @@ paths: '200': description: '' + delete: + security: + - ApiToken: [] + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/RecordList' + responses: + '200': + description: '' + diff --git a/e2e/zones.py b/e2e/zones.py index 2867df9..d4784de 100644 --- a/e2e/zones.py +++ b/e2e/zones.py @@ -117,7 +117,7 @@ class TestZones(unittest.TestCase): type='TXT' ) - self.api.zones_zone_records_post(zone='example.com.', record_list=RecordList(value=[old_record])) + self.api.zones_zone_records_post(zone='example.com.', record_list=RecordList([old_record])) update_records_request = UpdateRecordsRequest( old_records=RecordList([old_record]), @@ -133,4 +133,25 @@ class TestZones(unittest.TestCase): self.assertEqual(record.text, new_record.text, msg='New record does not have the expected value') found = True - self.assertTrue(found, msg='New record not found in zone records') + self.assertTrue(found, msg='Updated record not found in zone records') + + def test_delete_records(self): + name = random_name('example.com.') + record = RecordTypeTXT( + _class='IN', + ttl=300, + name=name, + text=random_string(32), + type='TXT' + ) + + self.api.zones_zone_records_post(zone='example.com.', record_list=RecordList([record])) + self.api.zones_zone_records_delete(zone='example.com.', record_list=RecordList([record])) + + records = self.api.zones_zone_records_get(zone='example.com.') + found = False + for record in records.value: + if type(record) is RecordTypeTXT and record.name == name: + found = True + + self.assertFalse(found, msg='Delete record found in zone records') \ No newline at end of file diff --git a/src/dns/api.rs b/src/dns/api.rs index 19eda7a..c5a6d84 100644 --- a/src/dns/api.rs +++ b/src/dns/api.rs @@ -11,6 +11,7 @@ pub trait RecordApi { async fn get_records(&mut self, zone: dns::Name, class: dns::DNSClass) -> Result, Self::Error>; async fn add_records(&mut self, zone: dns::Name, class: dns::DNSClass, new_records: Vec) -> Result<(), Self::Error>; async fn update_records(&mut self, zone: dns::Name, class: dns::DNSClass, old_records: Vec, new_records: Vec) -> Result<(), Self::Error>; + async fn delete_records(&mut self, zone: dns::Name, class: dns::DNSClass, records: Vec) -> Result<(), Self::Error>; // delete_records } diff --git a/src/dns/dns_api.rs b/src/dns/dns_api.rs index 9118515..2546ce2 100644 --- a/src/dns/dns_api.rs +++ b/src/dns/dns_api.rs @@ -9,6 +9,9 @@ use super::client::{ClientResponse, DnsClient}; use super::api::{RecordApi, ZoneApi}; +const MAX_PAYLOAD_LEN: u16 = 1232; + + #[derive(Debug)] pub enum DnsApiError { ClientError(ClientError), @@ -82,7 +85,7 @@ impl RecordApi for DnsApiClient { { let edns = message.edns_mut(); - edns.set_max_payload(1232); + edns.set_max_payload(MAX_PAYLOAD_LEN); edns.set_version(0); } @@ -100,12 +103,11 @@ impl RecordApi for DnsApiClient { async fn update_records(&mut self, zone: Name, class: DNSClass, old_records: Vec, new_records: Vec) -> Result<(), Self::Error> { - // Taken from trust_dns_client::op::update_message::compare_and_swap // The original function can not be used as is because it takes a RecordSet and not a Record list // for updates, the query section is used for the zone - let mut zone_query: Query = Query::new(); + let mut zone_query = Query::new(); zone_query.set_name(zone.clone()) .set_query_class(class) .set_query_type(RecordType::SOA); @@ -130,8 +132,8 @@ impl RecordApi for DnsApiClient { // add the delete for the old record let mut delete = old_records; - // the class must be none for delete for record in delete.iter_mut() { + // the class must be none for delete record.set_dns_class(DNSClass::NONE); // the TTL should be 0 record.set_ttl(0); @@ -144,7 +146,7 @@ impl RecordApi for DnsApiClient { // Extended dns { let edns = message.edns_mut(); - edns.set_max_payload(1232); + edns.set_max_payload(MAX_PAYLOAD_LEN); edns.set_version(0); } @@ -159,6 +161,55 @@ impl RecordApi for DnsApiClient { Ok(()) } + + async fn delete_records(&mut self, zone: Name, class: DNSClass, records: Vec) -> Result<(), Self::Error> + { + // for updates, the query section is used for the zone + let mut zone_query = Query::new(); + zone_query.set_name(zone.clone()) + .set_query_class(class) + .set_query_type(RecordType::SOA); + + let mut message: Message = Message::new(); + + // build the message + // TODO: set random / time based id + message + .set_id(0) + .set_message_type(MessageType::Query) + .set_op_code(OpCode::Update) + .set_recursion_desired(false); + message.add_zone(zone_query); + + let mut delete = records; + + for record in delete.iter_mut() { + // the class must be none for delete + record.set_dns_class(DNSClass::NONE); + // the TTL should be 0 + record.set_ttl(0); + } + message.add_updates(delete); + + // Extended dns + { + let edns = message.edns_mut(); + edns.set_max_payload(MAX_PAYLOAD_LEN); + edns.set_version(0); + } + + let response = ClientResponse(self.client.send(message)).await.map_err(|e| DnsApiError::ClientError(e))?; + + if response.response_code() != ResponseCode::NoError { + return Err(DnsApiError::ResponceNotOk { + code: response.response_code(), + zone: zone, + }); + } + + Ok(()) + + } } diff --git a/src/main.rs b/src/main.rs index af24f73..4870eee 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,10 +29,11 @@ async fn rocket() -> rocket::Rocket { get_zone_records, create_zone_records, update_zone_records, + delete_zone_records, get_zones, create_zone, add_member_to_zone, create_auth_token, - create_user + create_user, ]) } diff --git a/src/routes/zones.rs b/src/routes/zones.rs index ef5827f..c10b478 100644 --- a/src/routes/zones.rs +++ b/src/routes/zones.rs @@ -56,7 +56,6 @@ pub async fn create_zone_records( } }).await?; - let mut dns_api = DnsApiClient::new(client); dns_api.add_records( @@ -90,7 +89,6 @@ pub async fn update_zone_records( } }).await?; - let mut dns_api = DnsApiClient::new(client); dns_api.update_records( @@ -103,6 +101,36 @@ pub async fn update_zone_records( return Ok(Json(())); } +#[delete("/zones//records", data = "")] +pub async fn delete_zone_records( + client: DnsClient, + conn: DbConn, + user_info: Result, + zone: models::AbsoluteName, + records: Json +) -> Result, models::ErrorResponse> { + + let user_info = user_info?; + let zone_name = zone.to_utf8(); + + conn.run(move |c| { + if user_info.is_admin() { + models::Zone::get_by_name(c, &zone_name) + } else { + user_info.get_zone(c, &zone_name) + } + }).await?; + + let mut dns_api = DnsApiClient::new(client); + + dns_api.delete_records( + zone.clone(), + models::DNSClass::IN.into(), + records.into_inner().try_into_dns_type(zone.into_inner(), models::DNSClass::IN.into())? + ).await?; + + return Ok(Json(())); +} #[get("/zones")] pub async fn get_zones(