mirror of
https://github.com/instant-labs/instant-epp.git
synced 2025-02-12 20:02:01 +00:00
feat: support <update>
DNSSEC extension
This commit is contained in:
parent
4ad8d64f59
commit
378a7afb46
@ -86,6 +86,114 @@ impl Extension for InfoData {
|
||||
type Response = InfoDataResponse<'static>;
|
||||
}
|
||||
|
||||
pub use update::{RemoveData, UpdateData};
|
||||
mod update {
|
||||
use super::*;
|
||||
|
||||
impl Transaction<UpdateData<'_>> for crate::domain::update::DomainUpdate<'_> {}
|
||||
|
||||
impl Extension for UpdateData<'_> {
|
||||
type Response = NoExtension;
|
||||
}
|
||||
|
||||
// NOTE: Per RFC 5910 (paraphased): "At least one add, rem, or change element MUST be provided."
|
||||
// Feels excessive to enforce this with the type system, this will do for now.
|
||||
#[derive(Debug)]
|
||||
pub struct UpdateData<'a> {
|
||||
/// Ask the server operator to process the request with high priority
|
||||
///
|
||||
/// "High priority" is relative to standard server operator policies
|
||||
/// that are determined using an out-of-band mechanism
|
||||
// NOTE: Option<bool> in RFC
|
||||
pub urgent: bool,
|
||||
/// Remove security information from a delegation
|
||||
pub remove: Option<RemoveData<'a>>,
|
||||
/// Add security information to a delegation
|
||||
pub add: Option<DsOrKeyData<'a>>,
|
||||
/// Change existing security information
|
||||
// NOTE: Should be Option<Option<Max...>> for full RFC compliance
|
||||
pub change: Option<MaximumSignatureLifeTime>,
|
||||
}
|
||||
|
||||
impl ToXml for UpdateData<'_> {
|
||||
fn serialize<W: std::fmt::Write + ?Sized>(
|
||||
&self,
|
||||
field: Option<Id<'_>>,
|
||||
serializer: &mut Serializer<W>,
|
||||
) -> Result<(), Error> {
|
||||
const ELEMENT: &str = "update";
|
||||
// Opening tag
|
||||
serializer.write_start(ELEMENT, XMLNS)?;
|
||||
if self.urgent {
|
||||
serializer.write_attr("urgent", serializer.default_ns(), "true")?;
|
||||
}
|
||||
let old_context = serializer.push(instant_xml::ser::Context {
|
||||
default_ns: XMLNS,
|
||||
prefixes: [instant_xml::ser::Prefix {
|
||||
prefix: "",
|
||||
ns: XMLNS,
|
||||
}],
|
||||
})?;
|
||||
serializer.end_start()?;
|
||||
|
||||
serialize_nested("rem", self.remove.as_ref(), field, serializer)?;
|
||||
serialize_nested("add", self.add.as_ref(), field, serializer)?;
|
||||
serialize_nested("chg", self.change.as_ref(), field, serializer)?;
|
||||
|
||||
serializer.pop(old_context);
|
||||
|
||||
serializer.write_close(None, ELEMENT)?;
|
||||
|
||||
return Ok(());
|
||||
|
||||
fn serialize_nested<T: ToXml, W: std::fmt::Write + ?Sized>(
|
||||
element_name: &str,
|
||||
action: Option<&T>,
|
||||
field: Option<Id<'_>>,
|
||||
serializer: &mut Serializer<W>,
|
||||
) -> Result<(), Error> {
|
||||
if let Some(action_data) = action {
|
||||
serializer.write_start(element_name, serializer.default_ns())?;
|
||||
serializer.end_start()?;
|
||||
action_data.serialize(field, serializer)?;
|
||||
serializer.write_close(None, element_name)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RemoveData<'a> {
|
||||
All,
|
||||
DsOrKey(DsOrKeyData<'a>),
|
||||
}
|
||||
|
||||
impl ToXml for RemoveData<'_> {
|
||||
fn serialize<W: std::fmt::Write + ?Sized>(
|
||||
&self,
|
||||
field: Option<Id<'_>>,
|
||||
serializer: &mut Serializer<W>,
|
||||
) -> Result<(), Error> {
|
||||
match self {
|
||||
RemoveData::All => {
|
||||
const ELEMENT: &str = "all";
|
||||
serializer.write_start(ELEMENT, XMLNS)?;
|
||||
serializer.end_start()?;
|
||||
serializer.write_str("true")?;
|
||||
serializer.write_close(None, ELEMENT)?;
|
||||
}
|
||||
RemoveData::DsOrKey(ds_or_key_data) => {
|
||||
ds_or_key_data.serialize(field, serializer)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Struct supporting either the `dsData` or the `keyData` interface.
|
||||
#[derive(Debug, ToXml, FromXml)]
|
||||
#[xml(transparent)]
|
||||
@ -633,4 +741,109 @@ mod tests {
|
||||
"response/extensions/secdns_info_key.xml",
|
||||
);
|
||||
}
|
||||
|
||||
mod update {
|
||||
use std::borrow::Cow;
|
||||
|
||||
use domain::DomainUpdate;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn add_ds_remove_ds() {
|
||||
let ds = [mock_ds_data(None)];
|
||||
let ds_other = [mock_ds_other()];
|
||||
|
||||
let extension = UpdateData {
|
||||
urgent: false,
|
||||
remove: Some(RemoveData::DsOrKey(DsOrKeyData::DsData(Cow::Borrowed(&ds)))),
|
||||
add: Some(DsOrKeyData::DsData(Cow::Borrowed(&ds_other))),
|
||||
change: None,
|
||||
};
|
||||
|
||||
assert_serialized(
|
||||
"request/extensions/secdns_update_add_ds_rem_ds.xml",
|
||||
(&mock_domain_update(), &extension),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change() {
|
||||
let extension = UpdateData {
|
||||
urgent: false,
|
||||
remove: None,
|
||||
add: None,
|
||||
change: mock_sig_life(),
|
||||
};
|
||||
|
||||
assert_serialized(
|
||||
"request/extensions/secdns_update_chg.xml",
|
||||
(&mock_domain_update(), &extension),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_all_urgent() {
|
||||
let extension = UpdateData {
|
||||
urgent: true,
|
||||
remove: Some(RemoveData::All),
|
||||
add: None,
|
||||
change: None,
|
||||
};
|
||||
|
||||
assert_serialized(
|
||||
"request/extensions/secdns_update_rem_all_urgent.xml",
|
||||
(&mock_domain_update(), &extension),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_ks_add_ks_change() {
|
||||
let ks = [mock_key_data()];
|
||||
let ks_other = [mock_key_other()];
|
||||
|
||||
let extension = UpdateData {
|
||||
urgent: false,
|
||||
remove: Some(RemoveData::DsOrKey(DsOrKeyData::KeyData(Cow::Borrowed(
|
||||
&ks_other,
|
||||
)))),
|
||||
add: Some(DsOrKeyData::KeyData(Cow::Borrowed(&ks))),
|
||||
change: mock_sig_life(),
|
||||
};
|
||||
|
||||
assert_serialized(
|
||||
"request/extensions/secdns_update_rem_ks_add_ks_chg.xml",
|
||||
(&mock_domain_update(), &extension),
|
||||
);
|
||||
}
|
||||
|
||||
fn mock_domain_update() -> DomainUpdate<'static> {
|
||||
DomainUpdate {
|
||||
domain: domain::update::DomainUpdateRequestData {
|
||||
name: "example.com",
|
||||
add: None,
|
||||
remove: None,
|
||||
change_info: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn mock_key_other() -> KeyDataType<'static> {
|
||||
KeyDataType {
|
||||
public_key: Cow::Borrowed("AQPJ////4QQQ"),
|
||||
..mock_key_data()
|
||||
}
|
||||
}
|
||||
|
||||
fn mock_ds_other() -> DsDataType<'static> {
|
||||
DsDataType {
|
||||
key_tag: 12346,
|
||||
..mock_ds_data(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn mock_sig_life() -> Option<MaximumSignatureLifeTime> {
|
||||
Some(MaximumSignatureLifeTime(Duration::from_secs(605900)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<update>
|
||||
<update xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<name>example.com</name>
|
||||
</update>
|
||||
</update>
|
||||
<extension>
|
||||
<update xmlns="urn:ietf:params:xml:ns:secDNS-1.1">
|
||||
<rem>
|
||||
<dsData>
|
||||
<keyTag>12345</keyTag>
|
||||
<alg>3</alg>
|
||||
<digestType>1</digestType>
|
||||
<digest>49FD46E6C4B45C55D4AC</digest>
|
||||
</dsData>
|
||||
</rem>
|
||||
<add>
|
||||
<dsData>
|
||||
<keyTag>12346</keyTag>
|
||||
<alg>3</alg>
|
||||
<digestType>1</digestType>
|
||||
<digest>49FD46E6C4B45C55D4AC</digest>
|
||||
</dsData>
|
||||
</add>
|
||||
</update>
|
||||
</extension>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
</epp>
|
19
tests/resources/request/extensions/secdns_update_chg.xml
Normal file
19
tests/resources/request/extensions/secdns_update_chg.xml
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<update>
|
||||
<update xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<name>example.com</name>
|
||||
</update>
|
||||
</update>
|
||||
<extension>
|
||||
<update xmlns="urn:ietf:params:xml:ns:secDNS-1.1">
|
||||
<chg>
|
||||
<maxSigLife>605900</maxSigLife>
|
||||
</chg>
|
||||
</update>
|
||||
</extension>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
|
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<update>
|
||||
<update xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<name>example.com</name>
|
||||
</update>
|
||||
</update>
|
||||
<extension>
|
||||
<update xmlns="urn:ietf:params:xml:ns:secDNS-1.1" urgent="true">
|
||||
<rem>
|
||||
<all>true</all>
|
||||
</rem>
|
||||
</update>
|
||||
</extension>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
</epp>
|
@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<update>
|
||||
<update xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<name>example.com</name>
|
||||
</update>
|
||||
</update>
|
||||
<extension>
|
||||
<update xmlns="urn:ietf:params:xml:ns:secDNS-1.1">
|
||||
<rem>
|
||||
<keyData>
|
||||
<flags>257</flags>
|
||||
<protocol>3</protocol>
|
||||
<alg>1</alg>
|
||||
<pubKey>AQPJ////4QQQ</pubKey>
|
||||
</keyData>
|
||||
</rem>
|
||||
<add>
|
||||
<keyData>
|
||||
<flags>257</flags>
|
||||
<protocol>3</protocol>
|
||||
<alg>1</alg>
|
||||
<pubKey>AQPJ////4Q==</pubKey>
|
||||
</keyData>
|
||||
</add>
|
||||
<chg>
|
||||
<maxSigLife>605900</maxSigLife>
|
||||
</chg>
|
||||
</update>
|
||||
</extension>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
</epp>
|
Loading…
Reference in New Issue
Block a user