How to create an easy Restful API for a simple model in Django(Part III)

How to create an easy Restful API for a simple model in Django(Part III)

Following with previous post we can go now to authentication and permissions.

Everything is well documented in Django Rest Framework web site:

http://www.django-rest-framework.org/api-guide/authentication/

http://www.django-rest-framework.org/api-guide/permissions/

They even have this beautiful tutorial: http://www.django-rest-framework.org/tutorial/quickstart/

However I will explain a litte bit in the basic but pay attention to a corner case: when we don't need to authenticate but at the same time you need to be sure that only from specific IPs the API can be used.

Suppose you need some other external partner to post to your APIs. In our case we need to receive leads from Boberdoo(a Lead Distribution system) and receive information from Salesforce too.

We decided we should have a model with IPs that we should allow in our system and we have some APIs using a custom permission class that will reject the request. 

Our first move was to install ipcalc:

pip instal ipcalc==1.1.3

Then we created a model called TrustedIP

import ipcalc

network_help_txt
= 'Insert one ip address or a network mask (i.e. 10.1.1.0/24) to include a whole network.'


class TrustedIP(models.Model):
title = models.CharField('Title', max_length=100)
network = models.CharField('IP network', max_length=18, help_text=network_help_txt)

def __unicode__(self):
return self.title

def get_network(self):
return ipcalc.Network(self.network)

class Meta:
verbose_name = 'IP mask to include'
verbose_name_plural = 'IP masks to include'

We did:

> ./manage.py makemigrations

> ./manage.py migrate

We added the model to the admin:

@admin.register(models.TrustedIP)
class TrustedIP(admin.ModelAdmin):
list_display = ('title', 'network', )
search_fields = ('title', 'network')

In order to test, you must insert IPs to the table so we can see that is working.

We are ready now to create custom permission class:

from rest_framework import permissions

from . import models

def get_ip(req):
ip = req.META['REMOTE_ADDR']
# forwarded proxy fix for proxy passing setups
if (not ip or ip == '127.0.0.1') and req.META.has_key('HTTP_X_FORWARDED_FOR'):
ip = req.META['HTTP_X_FORWARDED_FOR']
return ip


def is_ip_in_nets(ip, nets):
for net in nets:
if ip in net:
return True
return False
class WhiteListPermission(permissions.BasePermission):
"""
Global permission check for white listed IPs.
"""
def has_permission(self, request, view):
request_ip = get_ip(request)
trusted_ips = [i.get_network() for i in models.TrustedIP.objects.only('network')]
return is_ip_in_nets(request_ip, trusted_ips)

As you can see you only have to inherit from permissions.BasePermission and create has_permission method.

But we still are missing the link between the view and the permission class. You can do that using rest framework decorators for example.

from rest_framework.decorators import permission_classes
@permission_classes((WhiteListorAPIPermission,))
class LeadViewSet(viewsets.ModelViewSet):
model = Lead
serializer_class = LeadSerializer
queryset = Lead.objects.all()

Finally, after these little tricks we are ready to insert Boberdoo and Salesforce IPs into TrustedIP model and the API start working smoothly.

Let me know if it was helpful.

Thanks.

Current rating: 5