add delete route

This commit is contained in:
Hannaeko 2022-03-05 13:07:51 +01:00
parent 5f18e32615
commit bc77bb16d4
6 changed files with 124 additions and 10 deletions

12
api.yml
View file

@ -423,3 +423,15 @@ paths:
'200': '200':
description: '' description: ''
delete:
security:
- ApiToken: []
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/RecordList'
responses:
'200':
description: ''

View file

@ -117,7 +117,7 @@ class TestZones(unittest.TestCase):
type='TXT' 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( update_records_request = UpdateRecordsRequest(
old_records=RecordList([old_record]), 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') self.assertEqual(record.text, new_record.text, msg='New record does not have the expected value')
found = True 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')

View file

@ -11,6 +11,7 @@ pub trait RecordApi {
async fn get_records(&mut self, zone: dns::Name, class: dns::DNSClass) -> Result<Vec<dns::Record>, Self::Error>; async fn get_records(&mut self, zone: dns::Name, class: dns::DNSClass) -> Result<Vec<dns::Record>, Self::Error>;
async fn add_records(&mut self, zone: dns::Name, class: dns::DNSClass, new_records: Vec<dns::Record>) -> Result<(), Self::Error>; async fn add_records(&mut self, zone: dns::Name, class: dns::DNSClass, new_records: Vec<dns::Record>) -> Result<(), Self::Error>;
async fn update_records(&mut self, zone: dns::Name, class: dns::DNSClass, old_records: Vec<dns::Record>, new_records: Vec<dns::Record>) -> Result<(), Self::Error>; async fn update_records(&mut self, zone: dns::Name, class: dns::DNSClass, old_records: Vec<dns::Record>, new_records: Vec<dns::Record>) -> Result<(), Self::Error>;
async fn delete_records(&mut self, zone: dns::Name, class: dns::DNSClass, records: Vec<dns::Record>) -> Result<(), Self::Error>;
// delete_records // delete_records
} }

View file

@ -9,6 +9,9 @@ use super::client::{ClientResponse, DnsClient};
use super::api::{RecordApi, ZoneApi}; use super::api::{RecordApi, ZoneApi};
const MAX_PAYLOAD_LEN: u16 = 1232;
#[derive(Debug)] #[derive(Debug)]
pub enum DnsApiError { pub enum DnsApiError {
ClientError(ClientError), ClientError(ClientError),
@ -82,7 +85,7 @@ impl RecordApi for DnsApiClient {
{ {
let edns = message.edns_mut(); let edns = message.edns_mut();
edns.set_max_payload(1232); edns.set_max_payload(MAX_PAYLOAD_LEN);
edns.set_version(0); 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<Record>, new_records: Vec<Record>) -> Result<(), Self::Error> async fn update_records(&mut self, zone: Name, class: DNSClass, old_records: Vec<Record>, new_records: Vec<Record>) -> Result<(), Self::Error>
{ {
// Taken from trust_dns_client::op::update_message::compare_and_swap // 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 // 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 // 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()) zone_query.set_name(zone.clone())
.set_query_class(class) .set_query_class(class)
.set_query_type(RecordType::SOA); .set_query_type(RecordType::SOA);
@ -130,8 +132,8 @@ impl RecordApi for DnsApiClient {
// add the delete for the old record // add the delete for the old record
let mut delete = old_records; let mut delete = old_records;
// the class must be none for delete
for record in delete.iter_mut() { for record in delete.iter_mut() {
// the class must be none for delete
record.set_dns_class(DNSClass::NONE); record.set_dns_class(DNSClass::NONE);
// the TTL should be 0 // the TTL should be 0
record.set_ttl(0); record.set_ttl(0);
@ -144,7 +146,7 @@ impl RecordApi for DnsApiClient {
// Extended dns // Extended dns
{ {
let edns = message.edns_mut(); let edns = message.edns_mut();
edns.set_max_payload(1232); edns.set_max_payload(MAX_PAYLOAD_LEN);
edns.set_version(0); edns.set_version(0);
} }
@ -159,6 +161,55 @@ impl RecordApi for DnsApiClient {
Ok(()) Ok(())
} }
async fn delete_records(&mut self, zone: Name, class: DNSClass, records: Vec<Record>) -> 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(())
}
} }

View file

@ -29,10 +29,11 @@ async fn rocket() -> rocket::Rocket {
get_zone_records, get_zone_records,
create_zone_records, create_zone_records,
update_zone_records, update_zone_records,
delete_zone_records,
get_zones, get_zones,
create_zone, create_zone,
add_member_to_zone, add_member_to_zone,
create_auth_token, create_auth_token,
create_user create_user,
]) ])
} }

View file

@ -56,7 +56,6 @@ pub async fn create_zone_records(
} }
}).await?; }).await?;
let mut dns_api = DnsApiClient::new(client); let mut dns_api = DnsApiClient::new(client);
dns_api.add_records( dns_api.add_records(
@ -90,7 +89,6 @@ pub async fn update_zone_records(
} }
}).await?; }).await?;
let mut dns_api = DnsApiClient::new(client); let mut dns_api = DnsApiClient::new(client);
dns_api.update_records( dns_api.update_records(
@ -103,6 +101,36 @@ pub async fn update_zone_records(
return Ok(Json(())); return Ok(Json(()));
} }
#[delete("/zones/<zone>/records", data = "<records>")]
pub async fn delete_zone_records(
client: DnsClient,
conn: DbConn,
user_info: Result<models::UserInfo, models::ErrorResponse>,
zone: models::AbsoluteName,
records: Json<models::RecordList>
) -> Result<Json<()>, 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")] #[get("/zones")]
pub async fn get_zones( pub async fn get_zones(