Skip to content

Commit

Permalink
A little set of tweaks
Browse files Browse the repository at this point in the history
Things that came out during implementing the library in demo api
  • Loading branch information
poxip committed Aug 3, 2017
1 parent 3d19d8f commit f086e21
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 41 deletions.
11 changes: 2 additions & 9 deletions jwt_devices/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from jwt_devices.models import Device
from jwt_devices.settings import api_settings
from jwt_devices.utils import get_device_details

User = get_user_model()

Expand Down Expand Up @@ -39,15 +40,7 @@ def validate(self, attrs):
"user": user
}
if api_settings.JWT_PERMANENT_TOKEN_AUTH:
headers = self.context["request"].META
device_name = headers.get("HTTP_X_DEVICE_MODEL")
user_agent = headers.get("HTTP_USER_AGENT", "")
if not device_name:
device_name = user_agent
device_details = ""
else:
device_details = user_agent

device_name, device_details = get_device_details(self.context["request"].META)
device = Device.objects.create(
user=user, last_request_datetime=timezone.now(),
name=device_name, details=device_details)
Expand Down
15 changes: 14 additions & 1 deletion jwt_devices/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ def jwt_devices_get_secret_key(payload=None):

def jwt_devices_payload_handler(user, device=None):
payload = jwt_payload_handler(user)
payload["device_id"] = str(device.pk)
if device:
payload["device_id"] = str(device.pk)
return payload


Expand Down Expand Up @@ -61,3 +62,15 @@ def jwt_devices_decode_handler(token):
issuer=rfj_settings.JWT_ISSUER,
algorithms=[rfj_settings.JWT_ALGORITHM]
)


def get_device_details(headers):
device_name = headers.get("HTTP_X_DEVICE_MODEL")
user_agent = headers.get("HTTP_USER_AGENT", "")
if not device_name:
device_name = user_agent
device_details = ""
else:
device_details = user_agent

return device_name, device_details
11 changes: 7 additions & 4 deletions jwt_devices/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from django.utils.translation import ugettext_lazy as _
from rest_framework import mixins, status, viewsets
from rest_framework.exceptions import NotFound
from rest_framework.exceptions import NotFound, ValidationError
from rest_framework.generics import DestroyAPIView
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response
Expand Down Expand Up @@ -57,6 +57,7 @@ def post(self, request, *args, **kwargs):
class DeviceRefreshJSONWebToken(APIView):
"""Refresh JWT token
API View used to refresh JSON Web Token using permanent token.
The DeviceRefreshJSONWebToken view requires the Permanent-Token header to be set in the request headers.
"""
serializer_class = DeviceTokenRefreshSerializer
permission_classes = [AllowAny]
Expand All @@ -69,15 +70,17 @@ def post(self, request):


class DeviceLogout(DestroyAPIView):
"""
Logout user by deleting Device.
"""Logout user by deleting Device.
The DeviceLogout view requires the Device-Id header to be set in the request headers.
"""
queryset = Device.objects.all()
permission_classes = [IsAuthenticated]

def get_object(self):
try:
return self.get_queryset().get(user=self.request.user, id=self.request.META.get("HTTP_DEVICE_ID"))
return self.get_queryset().get(user=self.request.user, id=self.request.META["HTTP_DEVICE_ID"])
except KeyError:
raise ValidationError(_("Device-Id header must be present in the request headers."))
except Device.DoesNotExist:
raise NotFound(_("Device does not exist."))

Expand Down
65 changes: 38 additions & 27 deletions tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,51 +78,62 @@ def setUp(self):
self.second_user = User.objects.create_user(
self.username + "2", self.email + "2", self.password)

def test_logout_view(self):
client = APIClient()

def _create_device(self, client, device_name="Test", user_data=None):
# create device
headers = {"HTTP_X_DEVICE_MODEL": "Android 123"}
headers = {"HTTP_X_DEVICE_MODEL": device_name}
client.credentials(**headers)
response = client.post("/auth-token/", self.data, format="json")
response = client.post("/auth-token/", user_data if user_data else self.data, format="json")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(Device.objects.all().count(), 1)
device_id = response.data["device_id"]
return response.data

headers["HTTP_AUTHORIZATION"] = "JWT {}".format(response.data["token"])
headers["HTTP_DEVICE_ID"] = device_id
client.credentials(**headers)
client.login(**self.data)
def test_empty_device_id(self):
client = APIClient()

token = self._create_device(client)["token"]
client.credentials(HTTP_AUTHORIZATION="JWT {}".format(token))
self.assertEqual(Device.objects.all().count(), 1)
response = client.delete("/device-logout/", format="json")
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
self.assertEqual(Device.objects.all().count(), 0)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(Device.objects.all().count(), 1)

def test_logout_unknown_device(self):
client = APIClient()

# create a few devices
headers = {"HTTP_X_DEVICE_MODEL": "Android 123"}
client.credentials(**headers)
response = client.post("/auth-token/", self.data, format="json")
self.assertEqual(response.status_code, status.HTTP_200_OK)
token = response.data["token"]

headers["HTTP_X_DEVICE_MODEL"] = "Nokia"
client.credentials(**headers)
response = client.post("/auth-token/", {"username": self.second_user.username, "password": self.password},
format="json")
self.assertEqual(response.status_code, status.HTTP_200_OK)
token = self._create_device(client, "Android 123")["token"]
data = self._create_device(
client, "Nokia", user_data={"username": self.second_user.username, "password": self.password})
self.assertEqual(Device.objects.all().count(), 2)
device_id = response.data["device_id"]
device_id = data["device_id"]

headers["HTTP_AUTHORIZATION"] = "JWT {}".format(token)
headers["HTTP_DEVICE_ID"] = device_id
headers = {
"HTTP_AUTHORIZATION": "JWT {}".format(token),
"HTTP_DEVICE_ID": device_id
}
client.credentials(**headers)
client.login(**self.data)
response = client.delete("/device-logout/", format="json")
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
self.assertEqual(Device.objects.all().count(), 2)

def test_logout_view(self):
client = APIClient()

# create device
data = self._create_device(client, "Android 123")
self.assertEqual(Device.objects.all().count(), 1)
device_id = data["device_id"]

headers = {
"HTTP_AUTHORIZATION": "JWT {}".format(data["token"]),
"HTTP_DEVICE_ID": device_id
}
client.credentials(**headers)
client.login(**self.data)
response = client.delete("/device-logout/", format="json")
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
self.assertEqual(Device.objects.all().count(), 0)


class DeviceRefreshTokenViewsTests(BaseTestCase):
def setUp(self):
Expand Down

0 comments on commit f086e21

Please sign in to comment.