Aller au contenu Aller au menu principal Aller au menu secondaire Aller au pied de page

Utiliser l’API de .fr en Python (2/4)

Accueil > Observatoire & ressources > Papiers d’experts > Utiliser l’API de .fr en Python (2/4)
Le 31/10/2022

Nous avons présenté le fonctionnement général de l’API de .fr. Voyons maintenant comment l’utiliser en pratique dans différents langages de programmation. Commençons par Python.

Attention, pour simplifier cet article, on omettra une partie du code, notamment le traitement des erreurs. Cela ne serait évidemment pas acceptable dans un programme réel. Une version plus complète du programme figure en https://gitlab.rd.nic.fr/afnic/code-samples/-/tree/main/API/Python.

Les choix techniques

Python dispose, dans sa bibliothèque standard, de moyens de faire des requêtes HTTP. Toutefois, beaucoup de programmeurs Python préfèrent la bibliothèque requests, plus simple d’utilisation. C’est elle que nous utiliserons ici.

L’authentification

def get_token():

    payload = {'client_id': 'registrars-api-client',

               'username': login, 'password': password,

               'grant_type': 'password'}

    r = requests.post("https://login-sandbox.nic.fr/auth/realms/fr/protocol/openid-connect/token",

                      data=payload)

    body = json.loads(r.text)

    return body["access_token"]

Ce code définit une fonction qui va faire une requête HTTP avec la méthode POST pour obtenir le jeton. Elle va pour cela passer une série de couples clé-valeur1 indiquant notamment le mot de passe du client. Le résultat est analysé avec les fonctions du module json de la bibliothèque standard. Une fois obtenu le jeton (c’est un des membres de l’objet JSON renvoyé), on peut faire un dictionnaire des champs nécessaires pour les en-têtes des futures requêtes HTTP :

def get_headers():

    token = get_token()

    return {'Accept': 'application/json',

            'Content-Type': 'application/json',

            'Extensions': 'FRNIC_V2' ,

            'Authorization': "Bearer %s" % token}

Les erreurs qui peuvent se produire

En cas d’erreur, vous recevrez un code de retour HTTP indiquant le problème, et une réponse encodée en JSON qui donnera davantage de détails. Ainsi, vous pouvez traiter les erreurs avec quelque chose comme :

if response.status_code != 200:

    body = json.loads(response.text)

    raise Exception("HTTP status code for delete is %s (reason \"%s\")" % \              

      (response.status_code, body))2

Liste de nos domaines

headers = afnic.get_headers()

response = requests.get("https://api-sandbox.nic.fr/v1/domains",

                        headers=headers)

body = json.loads(response.text)

for domain in body["content"]:

    print(domain["name"])

On fait une requête GET à l’URI de gestion des domaines, en ajoutant les en-têtes nécessaires. Il n’y a pas d’arguments à cette requête. Le résultat est du JSON, qu’on analyse, le transformant en un objet Python. On n’a plus qu’à itérer sur cet objet, affichant la liste des noms de domaines que nous gérons.

Ce code a une limite : la liste des domaines pouvant être longue, il ne récupère que les N premiers3. Pour les récupérer tous, il faut une boucle appelant successivement plusieurs pages de domaines. Un exemple complet figure dans le dépôt Gitlab.

Recherche de disponibilité

On nomme souvent ce service DAS, pour Domain Availability Service.

list = sys.argv[1:]

list = {'names': list}

headers = afnic.get_headers()

response = requests.post("https://api-sandbox.nic.fr/v1/domains/check",

                         headers=headers, data=json.dumps(list))

body = json.loads(response.text)["response"]

for name in body:

    print(name)

La liste des noms de domaine qui nous intéressent est obtenue sur la ligne de commande (le sys.argv). On en fait un dictionnaire Python, que json.dumps encodera plus tard à la syntaxe JSON. La requête est cette fois de méthode HTTP POST. La réponse est un objet JSON, qu’on analyse, avant d’itérer et d’afficher les informations sur chaque nom (qu’on ne cherche pas à analyser plus avant) :

% ./das.py toto.fr nic.fr truc.com

{'name': 'toto.fr', 'available': True}

{'name': 'truc.com', 'available': False, 'reason': 'ZONE_UNKNOWN'}

{'name': 'nic.fr', 'available': False, 'reason': 'IN_USE'}

Un nom est enregistrable, un autre est déjà utilisé, le dernier étant dans un autre domaine de tête.

Création d’un domaine

args = {'name': sys.argv[1],

        "authorizationInformation": "Vachement1234sur",

        "registrantClientId": contact,

        "contacts": [

            {

                "clientId": contact,

                "role": "ADMINISTRATIVE"

            },

            {

                "clientId": contact,

                "role": "TECHNICAL"

            }

        ]}

headers = afnic.get_headers()

response = requests.post("https://api-sandbox.nic.fr/v1/domains",

                         headers=headers, data=json.dumps(args))

body = json.loads(response.text)

print(body)

Ici, le nom de domaine à créer est indiqué sur la ligne de commande (sys.argv[1]). La création d’un nom nécessite de passer un objet JSON plus complexe, où on indique, outre ce nom, le « authinfo » qui servira pour authentifier les transferts, et surtout le titulaire du nom et les contacts. Ici, on a choisi de mettre le même identificateur pour le titulaire, le contact technique, et le contact administratif. Cet identificateur est celui obtenu lors de la création d’un contact (non montrée ici), comme « CTC65093 ».

Suppression d’un domaine

domain = sys.argv[1]

headers = afnic.get_headers()

response = requests.delete("https://api-sandbox.nic.fr/v1/domains/%s" % domain,

                         headers=headers)

body = json.loads(response.text)

print(body)

Les seules nouveautés ici sont l’utilisation de la méthode HTTP DELETE qui, comme son nom l’indique, sert à détruire l’objet visé, et le fait que l’identificateur de cet objet, le nom de domaine, est dans l’URI.

Ajout d’un serveur de noms à tous les domaines

L’intérêt principal de l’API est évidemment la possibilité de faire des opérations sur tout ou partie de vos domaines. Ici, nous allons ajouter un nouveau serveur de noms à tous les domaines du portefeuille4. On va donc récupérer la liste des domaines de ce portefeuille et effectuer ensuite une boucle :

response = requests.get("https://api-sandbox.nic.fr/v1/domains",

                        headers=headers)

if response.status_code != 200:

    raise Exception("HTTP status code for list is %s" % response.status_code)

body = json.loads(response.text)

for domain in body["content"]:

    args = {'name': domain['name'], 'nameServersToAdd': [ns]}

    response = requests.patch("https://api-sandbox.nic.fr/v1/domains/",

                         headers=headers, data=json.dumps(args))

On note l’utilisation de la méthode PATCH pour modifier un objet (ici, un domaine). Notez aussi que vous devez passer comme argument un tableau des serveurs à ajouter, tableau réduit ici à un seul élément5.

Codes d’autorisation

Lorsque vous demandez la création d’un domaine qui est sur la liste des termes soumis à examen préalable, vous devez fournir un code d’autorisation attribué par l’Afnic, après, justement, cet examen préalable. Voici comment solliciter un tel code :

payload = {'domainName': domain, 'registrantClientId': client,

                'justification': "Parce que c'est mon PROOOOJET !"}

response = requests.post("https://api-sandbox.nic.fr/v1/registrar/authorization-code-requests",

                         headers=headers, data=json.dumps(payload))

body = json.loads(response.text)

if response.status_code != 201:

    raise Exception("HTTP status code for auth. code request is %s (reason \"%s\")" % (response.status_code, body))

print("Authorization code %s requested" % body["repositoryObjectId"])

Vous obtenez un identifiant qui vous permettra de suivre les progrès de votre demande :

response = requests.get("https://api-sandbox.nic.fr/v1/registrar/authorization-code-requests/%s" % requestId,

                         headers=headers)

body = json.loads(response.text)

print("Request %s for domain %s is in state %s" % (body["repositoryObjectId"], body["domainName"], body["status"]))

if "code" in body:

    print("Authorization code is %s (valid until %s)" % (body["code"], body["codeExpirationDate"]))

Si la demande est acceptée par le registre, l’état sera ACCEPTED (avec le code d’autorisation dans le champ « code ») et, si elle est refusée par le registre, l’état sera REFUSED.


1 – On n’utilise pas JSON dans la requête.

2 – Cet exemple va donc afficher un message détaillé à l’utilisateur. Ce n’est pas un problème pour un programme en ligne de commande mais, si vous relayez des requêtes d’utilisateurs d’un site Web, les bonnes pratiques de sécurité préconisent pour une application en production de ne pas afficher les erreurs aux utilisateurs finaux, celles(-ci pouvant donner des détails indiscrets sur le système utilisé.

3 – N vaut aujourd’hui 20.

4 – L’API a évidemment les inconvénients de ses avantages : elle permet d’effectuer une opération automatique sur tout son portefeuille. Mais si le programme a une bogue, c’est tout le portefeuille qui sera touché. Relisez donc bien votre code avant de l’exécuter (sauf si vous utilisez le bac à sable).

5 – La méthode PATCH permet également d’autres modifications au domaine, comme de changer son état ou de lui ajouter des clés DNSSEC.