mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-08-16 06:24:12 +02:00
Merge branch 'dk/1122-dnssec-rewrite' into dk/1091-dnssec
This commit is contained in:
commit
67345fead4
5 changed files with 392 additions and 98 deletions
|
@ -169,6 +169,7 @@ class DomainDsdataForm(forms.Form):
|
||||||
algorithm = forms.TypedChoiceField(
|
algorithm = forms.TypedChoiceField(
|
||||||
required=True,
|
required=True,
|
||||||
label="Algorithm",
|
label="Algorithm",
|
||||||
|
coerce=int, # need to coerce into int so dsData objects can be compared
|
||||||
choices=[(None, "--Select--")] + ALGORITHM_CHOICES, # type: ignore
|
choices=[(None, "--Select--")] + ALGORITHM_CHOICES, # type: ignore
|
||||||
error_messages={"required": ("Algorithm is required.")},
|
error_messages={"required": ("Algorithm is required.")},
|
||||||
)
|
)
|
||||||
|
@ -176,6 +177,7 @@ class DomainDsdataForm(forms.Form):
|
||||||
digest_type = forms.TypedChoiceField(
|
digest_type = forms.TypedChoiceField(
|
||||||
required=True,
|
required=True,
|
||||||
label="Digest Type",
|
label="Digest Type",
|
||||||
|
coerce=int, # need to coerce into int so dsData objects can be compared
|
||||||
choices=[(None, "--Select--")] + DIGEST_TYPE_CHOICES, # type: ignore
|
choices=[(None, "--Select--")] + DIGEST_TYPE_CHOICES, # type: ignore
|
||||||
error_messages={"required": ("Digest Type is required.")},
|
error_messages={"required": ("Digest Type is required.")},
|
||||||
)
|
)
|
||||||
|
@ -201,6 +203,7 @@ class DomainKeydataForm(forms.Form):
|
||||||
flag = forms.TypedChoiceField(
|
flag = forms.TypedChoiceField(
|
||||||
required=True,
|
required=True,
|
||||||
label="Flag",
|
label="Flag",
|
||||||
|
coerce=int,
|
||||||
choices=FLAG_CHOICES,
|
choices=FLAG_CHOICES,
|
||||||
error_messages={"required": ("Flag is required.")},
|
error_messages={"required": ("Flag is required.")},
|
||||||
)
|
)
|
||||||
|
@ -208,6 +211,7 @@ class DomainKeydataForm(forms.Form):
|
||||||
protocol = forms.TypedChoiceField(
|
protocol = forms.TypedChoiceField(
|
||||||
required=True,
|
required=True,
|
||||||
label="Protocol",
|
label="Protocol",
|
||||||
|
coerce=int,
|
||||||
choices=PROTOCOL_CHOICES,
|
choices=PROTOCOL_CHOICES,
|
||||||
error_messages={"required": ("Protocol is required.")},
|
error_messages={"required": ("Protocol is required.")},
|
||||||
)
|
)
|
||||||
|
@ -215,6 +219,7 @@ class DomainKeydataForm(forms.Form):
|
||||||
algorithm = forms.TypedChoiceField(
|
algorithm = forms.TypedChoiceField(
|
||||||
required=True,
|
required=True,
|
||||||
label="Algorithm",
|
label="Algorithm",
|
||||||
|
coerce=int,
|
||||||
choices=[(None, "--Select--")] + ALGORITHM_CHOICES, # type: ignore
|
choices=[(None, "--Select--")] + ALGORITHM_CHOICES, # type: ignore
|
||||||
error_messages={"required": ("Algorithm is required.")},
|
error_messages={"required": ("Algorithm is required.")},
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,6 +2,7 @@ from itertools import zip_longest
|
||||||
import logging
|
import logging
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from string import digits
|
from string import digits
|
||||||
|
from typing import Optional
|
||||||
from django_fsm import FSMField, transition, TransitionNotAllowed # type: ignore
|
from django_fsm import FSMField, transition, TransitionNotAllowed # type: ignore
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
@ -283,7 +284,7 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
return e.code
|
return e.code
|
||||||
|
|
||||||
@Cache
|
@Cache
|
||||||
def dnssecdata(self) -> extensions.DNSSECExtension:
|
def dnssecdata(self) -> Optional[extensions.DNSSECExtension]:
|
||||||
try:
|
try:
|
||||||
return self._get_property("dnssecdata")
|
return self._get_property("dnssecdata")
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
|
@ -292,21 +293,121 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
logger.info("Domain does not have dnssec data defined %s" % err)
|
logger.info("Domain does not have dnssec data defined %s" % err)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def getDnssecdataChanges(
|
||||||
|
self, _dnssecdata: Optional[extensions.DNSSECExtension]
|
||||||
|
) -> tuple[dict, dict]:
|
||||||
|
"""
|
||||||
|
calls self.dnssecdata, it should pull from cache but may result
|
||||||
|
in an epp call
|
||||||
|
returns tuple of 2 values as follows:
|
||||||
|
addExtension: dict
|
||||||
|
remExtension: dict
|
||||||
|
|
||||||
|
addExtension includes all dsData or keyData to be added
|
||||||
|
remExtension includes all dsData or keyData to be removed
|
||||||
|
|
||||||
|
method operates on dsData OR keyData, never a mix of the two;
|
||||||
|
operates based on which is present in _dnssecdata;
|
||||||
|
if neither is present, addExtension will be empty dict, and
|
||||||
|
remExtension will be all existing dnssecdata to be deleted
|
||||||
|
"""
|
||||||
|
|
||||||
|
oldDnssecdata = self.dnssecdata
|
||||||
|
addDnssecdata: dict = {}
|
||||||
|
remDnssecdata: dict = {}
|
||||||
|
|
||||||
|
if _dnssecdata and _dnssecdata.dsData is not None:
|
||||||
|
# initialize addDnssecdata and remDnssecdata for dsData
|
||||||
|
addDnssecdata["dsData"] = _dnssecdata.dsData
|
||||||
|
|
||||||
|
if oldDnssecdata and len(oldDnssecdata.dsData) > 0:
|
||||||
|
# if existing dsData not in new dsData, mark for removal
|
||||||
|
dsDataForRemoval = [
|
||||||
|
dsData
|
||||||
|
for dsData in oldDnssecdata.dsData
|
||||||
|
if dsData not in _dnssecdata.dsData
|
||||||
|
]
|
||||||
|
if len(dsDataForRemoval) > 0:
|
||||||
|
remDnssecdata["dsData"] = dsDataForRemoval
|
||||||
|
|
||||||
|
# if new dsData not in existing dsData, mark for add
|
||||||
|
dsDataForAdd = [
|
||||||
|
dsData
|
||||||
|
for dsData in _dnssecdata.dsData
|
||||||
|
if dsData not in oldDnssecdata.dsData
|
||||||
|
]
|
||||||
|
if len(dsDataForAdd) > 0:
|
||||||
|
addDnssecdata["dsData"] = dsDataForAdd
|
||||||
|
else:
|
||||||
|
addDnssecdata["dsData"] = None
|
||||||
|
|
||||||
|
elif _dnssecdata and _dnssecdata.keyData is not None:
|
||||||
|
# initialize addDnssecdata and remDnssecdata for keyData
|
||||||
|
addDnssecdata["keyData"] = _dnssecdata.keyData
|
||||||
|
|
||||||
|
if oldDnssecdata and len(oldDnssecdata.keyData) > 0:
|
||||||
|
# if existing keyData not in new keyData, mark for removal
|
||||||
|
keyDataForRemoval = [
|
||||||
|
keyData
|
||||||
|
for keyData in oldDnssecdata.keyData
|
||||||
|
if keyData not in _dnssecdata.keyData
|
||||||
|
]
|
||||||
|
if len(keyDataForRemoval) > 0:
|
||||||
|
remDnssecdata["keyData"] = keyDataForRemoval
|
||||||
|
|
||||||
|
# if new keyData not in existing keyData, mark for add
|
||||||
|
keyDataForAdd = [
|
||||||
|
keyData
|
||||||
|
for keyData in _dnssecdata.keyData
|
||||||
|
if keyData not in oldDnssecdata.keyData
|
||||||
|
]
|
||||||
|
if len(keyDataForAdd) > 0:
|
||||||
|
addDnssecdata["keyData"] = keyDataForAdd
|
||||||
|
else:
|
||||||
|
# there are no new dsData or keyData, remove all
|
||||||
|
remDnssecdata["dsData"] = getattr(oldDnssecdata, "dsData", None)
|
||||||
|
remDnssecdata["keyData"] = getattr(oldDnssecdata, "keyData", None)
|
||||||
|
|
||||||
|
return addDnssecdata, remDnssecdata
|
||||||
|
|
||||||
@dnssecdata.setter # type: ignore
|
@dnssecdata.setter # type: ignore
|
||||||
def dnssecdata(self, _dnssecdata: dict):
|
def dnssecdata(self, _dnssecdata: Optional[extensions.DNSSECExtension]):
|
||||||
updateParams = {
|
_addDnssecdata, _remDnssecdata = self.getDnssecdataChanges(_dnssecdata)
|
||||||
"maxSigLife": _dnssecdata.get("maxSigLife", None),
|
addParams = {
|
||||||
"dsData": _dnssecdata.get("dsData", None),
|
"maxSigLife": _addDnssecdata.get("maxSigLife", None),
|
||||||
"keyData": _dnssecdata.get("keyData", None),
|
"dsData": _addDnssecdata.get("dsData", None),
|
||||||
"remAllDsKeyData": True,
|
"keyData": _addDnssecdata.get("keyData", None),
|
||||||
}
|
}
|
||||||
request = commands.UpdateDomain(name=self.name)
|
remParams = {
|
||||||
extension = commands.UpdateDomainDNSSECExtension(**updateParams)
|
"maxSigLife": _remDnssecdata.get("maxSigLife", None),
|
||||||
request.add_extension(extension)
|
"remDsData": _remDnssecdata.get("dsData", None),
|
||||||
|
"remKeyData": _remDnssecdata.get("keyData", None),
|
||||||
|
}
|
||||||
|
addRequest = commands.UpdateDomain(name=self.name)
|
||||||
|
addExtension = commands.UpdateDomainDNSSECExtension(**addParams)
|
||||||
|
addRequest.add_extension(addExtension)
|
||||||
|
remRequest = commands.UpdateDomain(name=self.name)
|
||||||
|
remExtension = commands.UpdateDomainDNSSECExtension(**remParams)
|
||||||
|
remRequest.add_extension(remExtension)
|
||||||
try:
|
try:
|
||||||
registry.send(request, cleaned=True)
|
if (
|
||||||
|
"dsData" in _addDnssecdata
|
||||||
|
and _addDnssecdata["dsData"] is not None
|
||||||
|
or "keyData" in _addDnssecdata
|
||||||
|
and _addDnssecdata["keyData"] is not None
|
||||||
|
):
|
||||||
|
registry.send(addRequest, cleaned=True)
|
||||||
|
if (
|
||||||
|
"dsData" in _remDnssecdata
|
||||||
|
and _remDnssecdata["dsData"] is not None
|
||||||
|
or "keyData" in _remDnssecdata
|
||||||
|
and _remDnssecdata["keyData"] is not None
|
||||||
|
):
|
||||||
|
registry.send(remRequest, cleaned=True)
|
||||||
except RegistryError as e:
|
except RegistryError as e:
|
||||||
logger.error("Error adding DNSSEC, code was %s error was %s" % (e.code, e))
|
logger.error(
|
||||||
|
"Error updating DNSSEC, code was %s error was %s" % (e.code, e)
|
||||||
|
)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
@nameservers.setter # type: ignore
|
@nameservers.setter # type: ignore
|
||||||
|
|
|
@ -7,7 +7,7 @@ import random
|
||||||
from string import ascii_uppercase
|
from string import ascii_uppercase
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from unittest.mock import MagicMock, Mock, patch
|
from unittest.mock import MagicMock, Mock, patch
|
||||||
from typing import List, Dict, Mapping, Any
|
from typing import List, Dict
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import get_user_model, login
|
from django.contrib.auth import get_user_model, login
|
||||||
|
@ -704,19 +704,27 @@ class MockEppLib(TestCase):
|
||||||
"alg": 1,
|
"alg": 1,
|
||||||
"pubKey": "AQPJ////4Q==",
|
"pubKey": "AQPJ////4Q==",
|
||||||
}
|
}
|
||||||
dnssecExtensionWithDsData: Mapping[Any, Any] = {
|
dnssecExtensionWithDsData = extensions.DNSSECExtension(
|
||||||
"dsData": [common.DSData(**addDsData1)] # type: ignore
|
**{
|
||||||
|
"dsData": [
|
||||||
|
common.DSData(**addDsData1) # type: ignore
|
||||||
|
], # type: ignore
|
||||||
}
|
}
|
||||||
dnssecExtensionWithMultDsData: Mapping[str, Any] = {
|
)
|
||||||
|
dnssecExtensionWithMultDsData = extensions.DNSSECExtension(
|
||||||
|
**{
|
||||||
"dsData": [
|
"dsData": [
|
||||||
common.DSData(**addDsData1), # type: ignore
|
common.DSData(**addDsData1), # type: ignore
|
||||||
common.DSData(**addDsData2), # type: ignore
|
common.DSData(**addDsData2), # type: ignore
|
||||||
],
|
], # type: ignore
|
||||||
}
|
}
|
||||||
dnssecExtensionWithKeyData: Mapping[str, Any] = {
|
)
|
||||||
"maxSigLife": 3215,
|
dnssecExtensionWithKeyData = extensions.DNSSECExtension(
|
||||||
|
**{
|
||||||
"keyData": [common.DNSSECKeyData(**keyDataDict)], # type: ignore
|
"keyData": [common.DNSSECKeyData(**keyDataDict)], # type: ignore
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
dnssecExtensionRemovingDsData = extensions.DNSSECExtension()
|
||||||
|
|
||||||
def mockSend(self, _request, cleaned):
|
def mockSend(self, _request, cleaned):
|
||||||
"""Mocks the registry.send function used inside of domain.py
|
"""Mocks the registry.send function used inside of domain.py
|
||||||
|
@ -758,23 +766,17 @@ class MockEppLib(TestCase):
|
||||||
elif getattr(_request, "name", None) == "dnssec-dsdata.gov":
|
elif getattr(_request, "name", None) == "dnssec-dsdata.gov":
|
||||||
return MagicMock(
|
return MagicMock(
|
||||||
res_data=[self.mockDataInfoDomain],
|
res_data=[self.mockDataInfoDomain],
|
||||||
extensions=[
|
extensions=[self.dnssecExtensionWithDsData],
|
||||||
extensions.DNSSECExtension(**self.dnssecExtensionWithDsData)
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
elif getattr(_request, "name", None) == "dnssec-multdsdata.gov":
|
elif getattr(_request, "name", None) == "dnssec-multdsdata.gov":
|
||||||
return MagicMock(
|
return MagicMock(
|
||||||
res_data=[self.mockDataInfoDomain],
|
res_data=[self.mockDataInfoDomain],
|
||||||
extensions=[
|
extensions=[self.dnssecExtensionWithMultDsData],
|
||||||
extensions.DNSSECExtension(**self.dnssecExtensionWithMultDsData)
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
elif getattr(_request, "name", None) == "dnssec-keydata.gov":
|
elif getattr(_request, "name", None) == "dnssec-keydata.gov":
|
||||||
return MagicMock(
|
return MagicMock(
|
||||||
res_data=[self.mockDataInfoDomain],
|
res_data=[self.mockDataInfoDomain],
|
||||||
extensions=[
|
extensions=[self.dnssecExtensionWithKeyData],
|
||||||
extensions.DNSSECExtension(**self.dnssecExtensionWithKeyData)
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
elif getattr(_request, "name", None) == "dnssec-none.gov":
|
elif getattr(_request, "name", None) == "dnssec-none.gov":
|
||||||
# this case is not necessary, but helps improve readability
|
# this case is not necessary, but helps improve readability
|
||||||
|
|
|
@ -1035,14 +1035,26 @@ class TestRegistrantDNSSEC(MockEppLib):
|
||||||
"""Rule: Registrants may modify their secure DNS data"""
|
"""Rule: Registrants may modify their secure DNS data"""
|
||||||
|
|
||||||
# helper function to create UpdateDomainDNSSECExtention object for verification
|
# helper function to create UpdateDomainDNSSECExtention object for verification
|
||||||
def createUpdateExtension(self, dnssecdata: extensions.DNSSECExtension):
|
def createUpdateExtension(
|
||||||
|
self, dnssecdata: extensions.DNSSECExtension, remove=False
|
||||||
|
):
|
||||||
|
if not remove:
|
||||||
return commands.UpdateDomainDNSSECExtension(
|
return commands.UpdateDomainDNSSECExtension(
|
||||||
maxSigLife=dnssecdata.maxSigLife,
|
maxSigLife=dnssecdata.maxSigLife,
|
||||||
dsData=dnssecdata.dsData,
|
dsData=dnssecdata.dsData,
|
||||||
keyData=dnssecdata.keyData,
|
keyData=dnssecdata.keyData,
|
||||||
remDsData=None,
|
remDsData=None,
|
||||||
remKeyData=None,
|
remKeyData=None,
|
||||||
remAllDsKeyData=True,
|
remAllDsKeyData=False,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return commands.UpdateDomainDNSSECExtension(
|
||||||
|
maxSigLife=dnssecdata.maxSigLife,
|
||||||
|
dsData=None,
|
||||||
|
keyData=None,
|
||||||
|
remDsData=dnssecdata.dsData,
|
||||||
|
remKeyData=dnssecdata.keyData,
|
||||||
|
remAllDsKeyData=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -1061,35 +1073,59 @@ class TestRegistrantDNSSEC(MockEppLib):
|
||||||
|
|
||||||
def test_user_adds_dnssec_data(self):
|
def test_user_adds_dnssec_data(self):
|
||||||
"""
|
"""
|
||||||
Scenario: Registrant adds DNSSEC data.
|
Scenario: Registrant adds DNSSEC ds data.
|
||||||
Verify that both the setter and getter are functioning properly
|
Verify that both the setter and getter are functioning properly
|
||||||
|
|
||||||
This test verifies:
|
This test verifies:
|
||||||
1 - setter calls UpdateDomain command
|
1 - setter initially calls InfoDomain command
|
||||||
2 - setter adds the UpdateDNSSECExtension extension to the command
|
2 - setter then calls UpdateDomain command
|
||||||
3 - setter causes the getter to call info domain on next get from cache
|
3 - setter adds the UpdateDNSSECExtension extension to the command
|
||||||
4 - getter properly parses dnssecdata from InfoDomain response and sets to cache
|
4 - setter causes the getter to call info domain on next get from cache
|
||||||
|
5 - getter properly parses dnssecdata from InfoDomain response and sets to cache
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
domain, _ = Domain.objects.get_or_create(name="dnssec-dsdata.gov")
|
# need to use a separate patcher and side_effect for this test, as
|
||||||
|
# response from InfoDomain must be different for different iterations
|
||||||
|
# of the same command
|
||||||
|
def side_effect(_request, cleaned):
|
||||||
|
if isinstance(_request, commands.InfoDomain):
|
||||||
|
if mocked_send.call_count == 1:
|
||||||
|
return MagicMock(res_data=[self.mockDataInfoDomain])
|
||||||
|
else:
|
||||||
|
return MagicMock(
|
||||||
|
res_data=[self.mockDataInfoDomain],
|
||||||
|
extensions=[self.dnssecExtensionWithDsData],
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return MagicMock(res_data=[self.mockDataInfoHosts])
|
||||||
|
|
||||||
|
patcher = patch("registrar.models.domain.registry.send")
|
||||||
|
mocked_send = patcher.start()
|
||||||
|
mocked_send.side_effect = side_effect
|
||||||
|
|
||||||
|
domain, _ = Domain.objects.get_or_create(name="dnssec-dsdata.gov")
|
||||||
domain.dnssecdata = self.dnssecExtensionWithDsData
|
domain.dnssecdata = self.dnssecExtensionWithDsData
|
||||||
|
|
||||||
# get the DNS SEC extension added to the UpdateDomain command and
|
# get the DNS SEC extension added to the UpdateDomain command and
|
||||||
# verify that it is properly sent
|
# verify that it is properly sent
|
||||||
# args[0] is the _request sent to registry
|
# args[0] is the _request sent to registry
|
||||||
args, _ = self.mockedSendFunction.call_args
|
args, _ = mocked_send.call_args
|
||||||
# assert that the extension matches
|
# assert that the extension on the update matches
|
||||||
self.assertEquals(
|
self.assertEquals(
|
||||||
args[0].extensions[0],
|
args[0].extensions[0],
|
||||||
self.createUpdateExtension(
|
self.createUpdateExtension(self.dnssecExtensionWithDsData),
|
||||||
extensions.DNSSECExtension(**self.dnssecExtensionWithDsData)
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
# test that the dnssecdata getter is functioning properly
|
# test that the dnssecdata getter is functioning properly
|
||||||
dnssecdata_get = domain.dnssecdata
|
dnssecdata_get = domain.dnssecdata
|
||||||
self.mockedSendFunction.assert_has_calls(
|
mocked_send.assert_has_calls(
|
||||||
[
|
[
|
||||||
|
call(
|
||||||
|
commands.InfoDomain(
|
||||||
|
name="dnssec-dsdata.gov",
|
||||||
|
),
|
||||||
|
cleaned=True,
|
||||||
|
),
|
||||||
call(
|
call(
|
||||||
commands.UpdateDomain(
|
commands.UpdateDomain(
|
||||||
name="dnssec-dsdata.gov",
|
name="dnssec-dsdata.gov",
|
||||||
|
@ -1109,9 +1145,9 @@ class TestRegistrantDNSSEC(MockEppLib):
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEquals(
|
self.assertEquals(dnssecdata_get.dsData, self.dnssecExtensionWithDsData.dsData)
|
||||||
dnssecdata_get.dsData, self.dnssecExtensionWithDsData["dsData"]
|
|
||||||
)
|
patcher.stop()
|
||||||
|
|
||||||
def test_dnssec_is_idempotent(self):
|
def test_dnssec_is_idempotent(self):
|
||||||
"""
|
"""
|
||||||
|
@ -1122,12 +1158,33 @@ class TestRegistrantDNSSEC(MockEppLib):
|
||||||
# registry normally sends in this case
|
# registry normally sends in this case
|
||||||
|
|
||||||
This test verifies:
|
This test verifies:
|
||||||
1 - UpdateDomain command called twice
|
1 - InfoDomain command is called first
|
||||||
2 - setter causes the getter to call info domain on next get from cache
|
2 - UpdateDomain command called on the initial setter
|
||||||
3 - getter properly parses dnssecdata from InfoDomain response and sets to cache
|
3 - setter causes the getter to call info domain on next get from cache
|
||||||
|
4 - UpdateDomain command is not called on second setter (no change)
|
||||||
|
5 - getter properly parses dnssecdata from InfoDomain response and sets to cache
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# need to use a separate patcher and side_effect for this test, as
|
||||||
|
# response from InfoDomain must be different for different iterations
|
||||||
|
# of the same command
|
||||||
|
def side_effect(_request, cleaned):
|
||||||
|
if isinstance(_request, commands.InfoDomain):
|
||||||
|
if mocked_send.call_count == 1:
|
||||||
|
return MagicMock(res_data=[self.mockDataInfoDomain])
|
||||||
|
else:
|
||||||
|
return MagicMock(
|
||||||
|
res_data=[self.mockDataInfoDomain],
|
||||||
|
extensions=[self.dnssecExtensionWithDsData],
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return MagicMock(res_data=[self.mockDataInfoHosts])
|
||||||
|
|
||||||
|
patcher = patch("registrar.models.domain.registry.send")
|
||||||
|
mocked_send = patcher.start()
|
||||||
|
mocked_send.side_effect = side_effect
|
||||||
|
|
||||||
domain, _ = Domain.objects.get_or_create(name="dnssec-dsdata.gov")
|
domain, _ = Domain.objects.get_or_create(name="dnssec-dsdata.gov")
|
||||||
|
|
||||||
# set the dnssecdata once
|
# set the dnssecdata once
|
||||||
|
@ -1136,15 +1193,11 @@ class TestRegistrantDNSSEC(MockEppLib):
|
||||||
domain.dnssecdata = self.dnssecExtensionWithDsData
|
domain.dnssecdata = self.dnssecExtensionWithDsData
|
||||||
# test that the dnssecdata getter is functioning properly
|
# test that the dnssecdata getter is functioning properly
|
||||||
dnssecdata_get = domain.dnssecdata
|
dnssecdata_get = domain.dnssecdata
|
||||||
self.mockedSendFunction.assert_has_calls(
|
mocked_send.assert_has_calls(
|
||||||
[
|
[
|
||||||
call(
|
call(
|
||||||
commands.UpdateDomain(
|
commands.InfoDomain(
|
||||||
name="dnssec-dsdata.gov",
|
name="dnssec-dsdata.gov",
|
||||||
nsset=None,
|
|
||||||
keyset=None,
|
|
||||||
registrant=None,
|
|
||||||
auth_info=None,
|
|
||||||
),
|
),
|
||||||
cleaned=True,
|
cleaned=True,
|
||||||
),
|
),
|
||||||
|
@ -1164,12 +1217,18 @@ class TestRegistrantDNSSEC(MockEppLib):
|
||||||
),
|
),
|
||||||
cleaned=True,
|
cleaned=True,
|
||||||
),
|
),
|
||||||
|
call(
|
||||||
|
commands.InfoDomain(
|
||||||
|
name="dnssec-dsdata.gov",
|
||||||
|
),
|
||||||
|
cleaned=True,
|
||||||
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEquals(
|
self.assertEquals(dnssecdata_get.dsData, self.dnssecExtensionWithDsData.dsData)
|
||||||
dnssecdata_get.dsData, self.dnssecExtensionWithDsData["dsData"]
|
|
||||||
)
|
patcher.stop()
|
||||||
|
|
||||||
def test_user_adds_dnssec_data_multiple_dsdata(self):
|
def test_user_adds_dnssec_data_multiple_dsdata(self):
|
||||||
"""
|
"""
|
||||||
|
@ -1184,23 +1243,40 @@ class TestRegistrantDNSSEC(MockEppLib):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# need to use a separate patcher and side_effect for this test, as
|
||||||
|
# response from InfoDomain must be different for different iterations
|
||||||
|
# of the same command
|
||||||
|
def side_effect(_request, cleaned):
|
||||||
|
if isinstance(_request, commands.InfoDomain):
|
||||||
|
if mocked_send.call_count == 1:
|
||||||
|
return MagicMock(res_data=[self.mockDataInfoDomain])
|
||||||
|
else:
|
||||||
|
return MagicMock(
|
||||||
|
res_data=[self.mockDataInfoDomain],
|
||||||
|
extensions=[self.dnssecExtensionWithMultDsData],
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return MagicMock(res_data=[self.mockDataInfoHosts])
|
||||||
|
|
||||||
|
patcher = patch("registrar.models.domain.registry.send")
|
||||||
|
mocked_send = patcher.start()
|
||||||
|
mocked_send.side_effect = side_effect
|
||||||
|
|
||||||
domain, _ = Domain.objects.get_or_create(name="dnssec-multdsdata.gov")
|
domain, _ = Domain.objects.get_or_create(name="dnssec-multdsdata.gov")
|
||||||
|
|
||||||
domain.dnssecdata = self.dnssecExtensionWithMultDsData
|
domain.dnssecdata = self.dnssecExtensionWithMultDsData
|
||||||
# get the DNS SEC extension added to the UpdateDomain command
|
# get the DNS SEC extension added to the UpdateDomain command
|
||||||
# and verify that it is properly sent
|
# and verify that it is properly sent
|
||||||
# args[0] is the _request sent to registry
|
# args[0] is the _request sent to registry
|
||||||
args, _ = self.mockedSendFunction.call_args
|
args, _ = mocked_send.call_args
|
||||||
# assert that the extension matches
|
# assert that the extension matches
|
||||||
self.assertEquals(
|
self.assertEquals(
|
||||||
args[0].extensions[0],
|
args[0].extensions[0],
|
||||||
self.createUpdateExtension(
|
self.createUpdateExtension(self.dnssecExtensionWithMultDsData),
|
||||||
extensions.DNSSECExtension(**self.dnssecExtensionWithMultDsData)
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
# test that the dnssecdata getter is functioning properly
|
# test that the dnssecdata getter is functioning properly
|
||||||
dnssecdata_get = domain.dnssecdata
|
dnssecdata_get = domain.dnssecdata
|
||||||
self.mockedSendFunction.assert_has_calls(
|
mocked_send.assert_has_calls(
|
||||||
[
|
[
|
||||||
call(
|
call(
|
||||||
commands.UpdateDomain(
|
commands.UpdateDomain(
|
||||||
|
@ -1222,12 +1298,103 @@ class TestRegistrantDNSSEC(MockEppLib):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEquals(
|
self.assertEquals(
|
||||||
dnssecdata_get.dsData, self.dnssecExtensionWithMultDsData["dsData"]
|
dnssecdata_get.dsData, self.dnssecExtensionWithMultDsData.dsData
|
||||||
)
|
)
|
||||||
|
|
||||||
|
patcher.stop()
|
||||||
|
|
||||||
|
def test_user_removes_dnssec_data(self):
|
||||||
|
"""
|
||||||
|
Scenario: Registrant removes DNSSEC ds data.
|
||||||
|
Verify that both the setter and getter are functioning properly
|
||||||
|
|
||||||
|
This test verifies:
|
||||||
|
1 - setter initially calls InfoDomain command
|
||||||
|
2 - first setter calls UpdateDomain command
|
||||||
|
3 - second setter calls InfoDomain command again
|
||||||
|
3 - setter then calls UpdateDomain command
|
||||||
|
4 - setter adds the UpdateDNSSECExtension extension to the command with rem
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# need to use a separate patcher and side_effect for this test, as
|
||||||
|
# response from InfoDomain must be different for different iterations
|
||||||
|
# of the same command
|
||||||
|
def side_effect(_request, cleaned):
|
||||||
|
if isinstance(_request, commands.InfoDomain):
|
||||||
|
if mocked_send.call_count == 1:
|
||||||
|
return MagicMock(res_data=[self.mockDataInfoDomain])
|
||||||
|
else:
|
||||||
|
return MagicMock(
|
||||||
|
res_data=[self.mockDataInfoDomain],
|
||||||
|
extensions=[self.dnssecExtensionWithDsData],
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return MagicMock(res_data=[self.mockDataInfoHosts])
|
||||||
|
|
||||||
|
patcher = patch("registrar.models.domain.registry.send")
|
||||||
|
mocked_send = patcher.start()
|
||||||
|
mocked_send.side_effect = side_effect
|
||||||
|
|
||||||
|
domain, _ = Domain.objects.get_or_create(name="dnssec-dsdata.gov")
|
||||||
|
# dnssecdata_get_initial = domain.dnssecdata # call to force initial mock
|
||||||
|
# domain._invalidate_cache()
|
||||||
|
domain.dnssecdata = self.dnssecExtensionWithDsData
|
||||||
|
domain.dnssecdata = self.dnssecExtensionRemovingDsData
|
||||||
|
# get the DNS SEC extension added to the UpdateDomain command and
|
||||||
|
# verify that it is properly sent
|
||||||
|
# args[0] is the _request sent to registry
|
||||||
|
args, _ = mocked_send.call_args
|
||||||
|
# assert that the extension on the update matches
|
||||||
|
self.assertEquals(
|
||||||
|
args[0].extensions[0],
|
||||||
|
self.createUpdateExtension(
|
||||||
|
self.dnssecExtensionWithDsData,
|
||||||
|
remove=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
mocked_send.assert_has_calls(
|
||||||
|
[
|
||||||
|
call(
|
||||||
|
commands.InfoDomain(
|
||||||
|
name="dnssec-dsdata.gov",
|
||||||
|
),
|
||||||
|
cleaned=True,
|
||||||
|
),
|
||||||
|
call(
|
||||||
|
commands.UpdateDomain(
|
||||||
|
name="dnssec-dsdata.gov",
|
||||||
|
nsset=None,
|
||||||
|
keyset=None,
|
||||||
|
registrant=None,
|
||||||
|
auth_info=None,
|
||||||
|
),
|
||||||
|
cleaned=True,
|
||||||
|
),
|
||||||
|
call(
|
||||||
|
commands.InfoDomain(
|
||||||
|
name="dnssec-dsdata.gov",
|
||||||
|
),
|
||||||
|
cleaned=True,
|
||||||
|
),
|
||||||
|
call(
|
||||||
|
commands.UpdateDomain(
|
||||||
|
name="dnssec-dsdata.gov",
|
||||||
|
nsset=None,
|
||||||
|
keyset=None,
|
||||||
|
registrant=None,
|
||||||
|
auth_info=None,
|
||||||
|
),
|
||||||
|
cleaned=True,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
patcher.stop()
|
||||||
|
|
||||||
def test_user_adds_dnssec_keydata(self):
|
def test_user_adds_dnssec_keydata(self):
|
||||||
"""
|
"""
|
||||||
Scenario: Registrant adds DNSSEC data.
|
Scenario: Registrant adds DNSSEC key data.
|
||||||
Verify that both the setter and getter are functioning properly
|
Verify that both the setter and getter are functioning properly
|
||||||
|
|
||||||
This test verifies:
|
This test verifies:
|
||||||
|
@ -1238,23 +1405,40 @@ class TestRegistrantDNSSEC(MockEppLib):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# need to use a separate patcher and side_effect for this test, as
|
||||||
|
# response from InfoDomain must be different for different iterations
|
||||||
|
# of the same command
|
||||||
|
def side_effect(_request, cleaned):
|
||||||
|
if isinstance(_request, commands.InfoDomain):
|
||||||
|
if mocked_send.call_count == 1:
|
||||||
|
return MagicMock(res_data=[self.mockDataInfoDomain])
|
||||||
|
else:
|
||||||
|
return MagicMock(
|
||||||
|
res_data=[self.mockDataInfoDomain],
|
||||||
|
extensions=[self.dnssecExtensionWithKeyData],
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return MagicMock(res_data=[self.mockDataInfoHosts])
|
||||||
|
|
||||||
|
patcher = patch("registrar.models.domain.registry.send")
|
||||||
|
mocked_send = patcher.start()
|
||||||
|
mocked_send.side_effect = side_effect
|
||||||
|
|
||||||
domain, _ = Domain.objects.get_or_create(name="dnssec-keydata.gov")
|
domain, _ = Domain.objects.get_or_create(name="dnssec-keydata.gov")
|
||||||
|
|
||||||
domain.dnssecdata = self.dnssecExtensionWithKeyData
|
domain.dnssecdata = self.dnssecExtensionWithKeyData
|
||||||
# get the DNS SEC extension added to the UpdateDomain command
|
# get the DNS SEC extension added to the UpdateDomain command
|
||||||
# and verify that it is properly sent
|
# and verify that it is properly sent
|
||||||
# args[0] is the _request sent to registry
|
# args[0] is the _request sent to registry
|
||||||
args, _ = self.mockedSendFunction.call_args
|
args, _ = mocked_send.call_args
|
||||||
# assert that the extension matches
|
# assert that the extension matches
|
||||||
self.assertEquals(
|
self.assertEquals(
|
||||||
args[0].extensions[0],
|
args[0].extensions[0],
|
||||||
self.createUpdateExtension(
|
self.createUpdateExtension(self.dnssecExtensionWithKeyData),
|
||||||
extensions.DNSSECExtension(**self.dnssecExtensionWithKeyData)
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
# test that the dnssecdata getter is functioning properly
|
# test that the dnssecdata getter is functioning properly
|
||||||
dnssecdata_get = domain.dnssecdata
|
dnssecdata_get = domain.dnssecdata
|
||||||
self.mockedSendFunction.assert_has_calls(
|
mocked_send.assert_has_calls(
|
||||||
[
|
[
|
||||||
call(
|
call(
|
||||||
commands.UpdateDomain(
|
commands.UpdateDomain(
|
||||||
|
@ -1276,9 +1460,11 @@ class TestRegistrantDNSSEC(MockEppLib):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEquals(
|
self.assertEquals(
|
||||||
dnssecdata_get.keyData, self.dnssecExtensionWithKeyData["keyData"]
|
dnssecdata_get.keyData, self.dnssecExtensionWithKeyData.keyData
|
||||||
)
|
)
|
||||||
|
|
||||||
|
patcher.stop()
|
||||||
|
|
||||||
def test_update_is_unsuccessful(self):
|
def test_update_is_unsuccessful(self):
|
||||||
"""
|
"""
|
||||||
Scenario: An update to the dns data is unsuccessful
|
Scenario: An update to the dns data is unsuccessful
|
||||||
|
|
|
@ -378,8 +378,8 @@ class DomainDsdataView(DomainPermissionView, FormMixin):
|
||||||
def form_valid(self, formset):
|
def form_valid(self, formset):
|
||||||
"""The formset is valid, perform something with it."""
|
"""The formset is valid, perform something with it."""
|
||||||
|
|
||||||
# Set the nameservers from the formset
|
# Set the dnssecdata from the formset
|
||||||
dnssecdata = {"dsData": []}
|
dnssecdata = extensions.DNSSECExtension()
|
||||||
|
|
||||||
for form in formset:
|
for form in formset:
|
||||||
try:
|
try:
|
||||||
|
@ -387,17 +387,17 @@ class DomainDsdataView(DomainPermissionView, FormMixin):
|
||||||
# or form.cleaned_data['delete'] == False:
|
# or form.cleaned_data['delete'] == False:
|
||||||
dsrecord = {
|
dsrecord = {
|
||||||
"keyTag": form.cleaned_data["key_tag"],
|
"keyTag": form.cleaned_data["key_tag"],
|
||||||
"alg": form.cleaned_data["algorithm"],
|
"alg": int(form.cleaned_data["algorithm"]),
|
||||||
"digestType": form.cleaned_data["digest_type"],
|
"digestType": int(form.cleaned_data["digest_type"]),
|
||||||
"digest": form.cleaned_data["digest"],
|
"digest": form.cleaned_data["digest"],
|
||||||
}
|
}
|
||||||
dnssecdata["dsData"].append(common.DSData(**dsrecord))
|
if dnssecdata.dsData is None:
|
||||||
|
dnssecdata.dsData = []
|
||||||
|
dnssecdata.dsData.append(common.DSData(**dsrecord))
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# no server information in this field, skip it
|
# no server information in this field, skip it
|
||||||
pass
|
pass
|
||||||
domain = self.get_object()
|
domain = self.get_object()
|
||||||
if len(dnssecdata["dsData"]) == 0:
|
|
||||||
dnssecdata = {}
|
|
||||||
try:
|
try:
|
||||||
domain.dnssecdata = dnssecdata
|
domain.dnssecdata = dnssecdata
|
||||||
except RegistryError as err:
|
except RegistryError as err:
|
||||||
|
@ -500,25 +500,25 @@ class DomainKeydataView(DomainPermissionView, FormMixin):
|
||||||
"""The formset is valid, perform something with it."""
|
"""The formset is valid, perform something with it."""
|
||||||
|
|
||||||
# Set the nameservers from the formset
|
# Set the nameservers from the formset
|
||||||
dnssecdata = {"keyData": []}
|
dnssecdata = extensions.DNSSECExtension()
|
||||||
|
|
||||||
for form in formset:
|
for form in formset:
|
||||||
try:
|
try:
|
||||||
# if 'delete' not in form.cleaned_data
|
# if 'delete' not in form.cleaned_data
|
||||||
# or form.cleaned_data['delete'] == False:
|
# or form.cleaned_data['delete'] == False:
|
||||||
keyrecord = {
|
keyrecord = {
|
||||||
"flags": form.cleaned_data["flag"],
|
"flags": int(form.cleaned_data["flag"]),
|
||||||
"protocol": form.cleaned_data["protocol"],
|
"protocol": int(form.cleaned_data["protocol"]),
|
||||||
"alg": form.cleaned_data["algorithm"],
|
"alg": int(form.cleaned_data["algorithm"]),
|
||||||
"pubKey": form.cleaned_data["pub_key"],
|
"pubKey": form.cleaned_data["pub_key"],
|
||||||
}
|
}
|
||||||
dnssecdata["keyData"].append(common.DNSSECKeyData(**keyrecord))
|
if dnssecdata.keyData is None:
|
||||||
|
dnssecdata.keyData = []
|
||||||
|
dnssecdata.keyData.append(common.DNSSECKeyData(**keyrecord))
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# no server information in this field, skip it
|
# no server information in this field, skip it
|
||||||
pass
|
pass
|
||||||
domain = self.get_object()
|
domain = self.get_object()
|
||||||
if len(dnssecdata["keyData"]) == 0:
|
|
||||||
dnssecdata = {}
|
|
||||||
try:
|
try:
|
||||||
domain.dnssecdata = dnssecdata
|
domain.dnssecdata = dnssecdata
|
||||||
except RegistryError as err:
|
except RegistryError as err:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue