Skip to content

Commit

Permalink
Merge pull request #2843 from cisagov/es/2589-rdap-api
Browse files Browse the repository at this point in the history
2589: RDAP API endpoint [rh]
  • Loading branch information
erinysong authored Sep 30, 2024
2 parents 65174df + 2ab29b9 commit ab5cd2c
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 27 deletions.
66 changes: 66 additions & 0 deletions src/api/tests/test_rdap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"""Test the domain rdap lookup API."""

import json

from django.contrib.auth import get_user_model
from django.test import RequestFactory
from django.test import TestCase

from ..views import rdap

API_BASE_PATH = "/api/v1/rdap/?domain="


class RdapViewTest(TestCase):
"""Test that the RDAP view function works as expected"""

def setUp(self):
super().setUp()
self.user = get_user_model().objects.create(username="username")
self.factory = RequestFactory()

def test_rdap_get_no_tld(self):
"""RDAP API successfully fetches RDAP for domain without a TLD"""
request = self.factory.get(API_BASE_PATH + "whitehouse")
request.user = self.user
response = rdap(request, domain="whitehouse")
# contains the right text
self.assertContains(response, "rdap")
# can be parsed into JSON with appropriate keys
response_object = json.loads(response.content)
self.assertIn("rdapConformance", response_object)

def test_rdap_invalid_domain(self):
"""RDAP API accepts invalid domain queries and returns JSON response
with appropriate error codes"""
request = self.factory.get(API_BASE_PATH + "whitehouse.com")
request.user = self.user
response = rdap(request, domain="whitehouse.com")

self.assertContains(response, "errorCode")
response_object = json.loads(response.content)
self.assertIn("errorCode", response_object)


class RdapAPITest(TestCase):
"""Test that the API can be called as expected."""

def setUp(self):
super().setUp()
username = "test_user"
first_name = "First"
last_name = "Last"
email = "[email protected]"
title = "title"
phone = "8080102431"
self.user = get_user_model().objects.create(
username=username, title=title, first_name=first_name, last_name=last_name, email=email, phone=phone
)

def test_rdap_get(self):
"""Can call RDAP API"""
self.client.force_login(self.user)
response = self.client.get(API_BASE_PATH + "whitehouse.gov")
self.assertContains(response, "rdap")
response_object = json.loads(response.content)
self.assertIn("rdapConformance", response_object)
44 changes: 18 additions & 26 deletions src/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from django.apps import apps
from django.views.decorators.http import require_http_methods
from django.http import HttpResponse
from django.http import HttpResponse, JsonResponse
from django.utils.safestring import mark_safe

from registrar.templatetags.url_helpers import public_site_url
Expand All @@ -18,7 +18,7 @@
from registrar.utility.s3_bucket import S3ClientError, S3ClientHelper


DOMAIN_FILE_URL = "https://raw.githubusercontent.com/cisagov/dotgov-data/main/current-full.csv"
RDAP_URL = "https://rdap.cloudflareregistry.com/rdap/domain/{domain}"


DOMAIN_API_MESSAGES = {
Expand All @@ -41,30 +41,6 @@
}


# this file doesn't change that often, nor is it that big, so cache the result
# in memory for ten minutes
@ttl_cache(ttl=600)
def _domains():
"""Return a list of the current .gov domains.
Fetch a file from DOMAIN_FILE_URL, parse the CSV for the domain,
lowercase everything and return the list.
"""
DraftDomain = apps.get_model("registrar.DraftDomain")
# 5 second timeout
file_contents = requests.get(DOMAIN_FILE_URL, timeout=5).text
domains = set()
# skip the first line
for line in file_contents.splitlines()[1:]:
# get the domain before the first comma
domain = line.split(",", 1)[0]
# sanity-check the string we got from the file here
if DraftDomain.string_could_be_domain(domain):
# lowercase everything when we put it in domains
domains.add(domain.lower())
return domains


def check_domain_available(domain):
"""Return true if the given domain is available.
Expand Down Expand Up @@ -99,6 +75,22 @@ def available(request, domain=""):
return json_response


@require_http_methods(["GET"])
@login_not_required
# Since we cache domain RDAP data, cache time may need to be re-evaluated this if we encounter any memory issues
@ttl_cache(ttl=600)
def rdap(request, domain=""):
"""Returns JSON dictionary of a domain's RDAP data from Cloudflare API"""
domain = request.GET.get("domain", "")

# If inputted domain doesn't have a TLD, append .gov to it
if "." not in domain:
domain = f"{domain}.gov"

rdap_data = requests.get(RDAP_URL.format(domain=domain), timeout=5).json()
return JsonResponse(rdap_data)


@require_http_methods(["GET"])
@login_not_required
def get_current_full(request, file_name="current-full.csv"):
Expand Down
3 changes: 2 additions & 1 deletion src/registrar/config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
)
from registrar.views.domains_json import get_domains_json
from registrar.views.utility import always_404
from api.views import available, get_current_federal, get_current_full
from api.views import available, rdap, get_current_federal, get_current_full


DOMAIN_REQUEST_NAMESPACE = views.DomainRequestWizard.URL_NAMESPACE
Expand Down Expand Up @@ -194,6 +194,7 @@
path("openid/", include("djangooidc.urls")),
path("request/", include((domain_request_urls, DOMAIN_REQUEST_NAMESPACE))),
path("api/v1/available/", available, name="available"),
path("api/v1/rdap/", rdap, name="rdap"),
path("api/v1/get-report/current-federal", get_current_federal, name="get-current-federal"),
path("api/v1/get-report/current-full", get_current_full, name="get-current-full"),
path(
Expand Down
1 change: 1 addition & 0 deletions src/registrar/tests/test_url_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ class TestURLAuth(TestCase):
"/api/v1/available/",
"/api/v1/get-report/current-federal",
"/api/v1/get-report/current-full",
"/api/v1/rdap/",
"/health",
]

Expand Down

0 comments on commit ab5cd2c

Please sign in to comment.