Skip to content

Commit

Permalink
Merge 6b17cb2 into 7445910
Browse files Browse the repository at this point in the history
  • Loading branch information
maxwellgithinji committed Oct 31, 2018
2 parents 7445910 + 6b17cb2 commit 362ef0e
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 16 deletions.
78 changes: 65 additions & 13 deletions authors/apps/authentication/backends.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,69 @@
# import jwt
#
# from django.conf import settings
#
# from rest_framework import authentication, exceptions
#
# from .models import User
"""
JWT Configuration module
"""
import jwt
import json

"""Configure JWT Here"""
from django.conf import settings
from django.http import HttpResponse

from rest_framework import authentication, exceptions
from rest_framework.authentication import get_authorization_header, BaseAuthentication

class JWTAuthentication:
def authenticate(self, argument2):
pass
from .models import User
from authors.settings import SECRET_KEY

def authenticate_header(self, argument2):
pass

class JWTAuthentication(BaseAuthentication):
"""
JWT Authentication helper class
:param: BaseAuthentication:
"""
def authenticate(self, request):
"""
:param: request: request object to decode and authenticate
"""

auth = get_authorization_header(request).split()

#Check if token and is present
if not auth or auth[0].lower() != b'bearer':
return None

#Check if the correct credemtials were passed in 'request' parameter
if len(auth) == 1:
msg = 'Invalid token header. No credentials provided.'
raise exceptions.AuthenticationFailed(msg)
#Check if token header has correct number of segments
elif len(auth) > 2:
msg = 'Invalid token header'
raise exceptions.AuthenticationFailed(msg)

#Decode token sucessfully, else catch some errors;
#errors can be due to expired signature, in decoding
#token validity or a user not existing

token = auth[1]
try:
payload = jwt.decode(token, SECRET_KEY)
email = payload['email']

user = User.objects.get(
email=email,
is_active=True
)
#Except cuustom errors while using the token
except Exception as e:
if e.__class__.__name__ == 'DecodeError':
raise exceptions.AuthenticationFailed('cannot decode token')
if e.__class__.__name__ == 'ExpiredSignatureError':
raise exceptions.AuthenticationFailed('Token has expired')
return (user, token)


def authenticate_header(self, request):
"""
Authenticate header prefix
:param: request: object request to add prefix
"""
return 'Bearer'
18 changes: 16 additions & 2 deletions authors/apps/authentication/serializers.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import jwt

from datetime import datetime, timedelta

from django.contrib.auth import authenticate

from rest_framework import serializers
from rest_framework.validators import UniqueValidator

from .models import User

from authors.settings import SECRET_KEY

class RegistrationSerializer(serializers.ModelSerializer):
"""Serializers registration requests and creates a new user."""
Expand Down Expand Up @@ -58,6 +62,7 @@ class LoginSerializer(serializers.Serializer):
email = serializers.CharField(max_length=255)
username = serializers.CharField(max_length=255, read_only=True)
password = serializers.CharField(max_length=128, write_only=True)
token = serializers.CharField(read_only=True)


def validate(self, data):
Expand Down Expand Up @@ -105,13 +110,22 @@ def validate(self, data):
'This user has been deactivated.'
)

#Create a token by encoding email
#jwt consist of `header`, `payload` and `secret`
payload = {
'email': user.email,
'iat': datetime.utcnow(),
'exp': datetime.utcnow() + timedelta(days=7)
}
jwt_token = {'token': jwt.encode(payload, SECRET_KEY).decode('UTF-8')}

# The `validate` method should return a dictionary of validated data.
# This is the data that is passed to the `create` and `update` methods
# that we will see later on.
return {
'email': user.email,
'username': user.username,

'token': jwt_token,
}


Expand Down
1 change: 1 addition & 0 deletions authors/apps/authentication/tests/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def setUp(self):
"""Define the test client"""
self.SIGN_IN_URL = '/api/users/login/'
self.SIGN_UP_URL = '/api/users/'
self.USER_URL = '/api/user/'
self.client = APIClient()
self.user_data = {
'email': 'zawi@gmail.com',
Expand Down
2 changes: 1 addition & 1 deletion authors/apps/authentication/tests/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def test_login_user_wrong_password(self):
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

def test_login_user_non_existent(self):
"""Test if user is registered"""
"""Test if user does not exist"""
response = self.client.post(
self.SIGN_UP_URL,
self.user_data,
Expand Down
99 changes: 99 additions & 0 deletions authors/apps/authentication/tests/test_token.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
"""
Token related tests module
"""
import json

from rest_framework.views import status

from .base import BaseTest

class TokenTestcase(BaseTest):
"""Test tokens testcase"""


def test_token_without_bearer(self):
"""
test token must have a 'Bearer' prefix
"""
self.client.credentials(HTTP_AUTHORIZATION="Token akldjfakjdlfjs")

get_user = self.client.get(
self.USER_URL
)
self.output = json.loads(get_user.content)['user']['detail']
self.assertEqual(get_user.status_code, status.HTTP_401_UNAUTHORIZED)
self.assertEqual(self.output, 'Authentication credentials were not provided.')


def test_token_without_string_token(self):
"""
test token must have a token string
"""
self.client.credentials(HTTP_AUTHORIZATION="Bearer ")

get_user = self.client.get(
self.USER_URL
)
self.output = json.loads(get_user.content)['user']['detail']
self.assertEqual(get_user.status_code, status.HTTP_401_UNAUTHORIZED)
self.assertEqual(self.output, 'Invalid token header. No credentials provided.')


def test_token_with_invalid_content(self):
"""
test token must not have invalid content
"""
self.client.credentials(HTTP_AUTHORIZATION="Bearer kksdjkskjjds dsjkdksjd ")

get_user = self.client.get(
self.USER_URL
)
self.output = json.loads(get_user.content)['user']['detail']
self.assertEqual(get_user.status_code, status.HTTP_401_UNAUTHORIZED)
self.assertEqual(self.output, 'Invalid token header')



def test_authenticates_user(self):
"""
Test sucessful token generation on login
"""
register = self.client.post(
self.SIGN_UP_URL,
self.user_data,
format="json")
login = self.client.post(
self.SIGN_IN_URL,
self.user_data,
format="json")
token = json.loads(login.content)['user']['token']
self.client.credentials(HTTP_AUTHORIZATION="Bearer "+token)
self.assertIn('token', login.data)
self.assertEqual(token, login.data['token'])


def test_invalid_token(self):
"""
Test secured endpoint must have a valid token
"""
register = self.client.post(
self.SIGN_UP_URL,
self.user_data,
format="json",
)
login = self.client.post(
self.SIGN_IN_URL,
self.user_data,
format="json")

token = json.loads(login.content)['user']['token']

#tamper with the token authorizarion header
self.client.credentials(HTTP_AUTHORIZATION="Bearer " + 'token')

#try acessing a secured endpoint
get_user = self.client.get(
self.USER_URL
)

self.assertTrue('cannot decode token', json.loads(get_user.content)['user']['detail'])

0 comments on commit 362ef0e

Please sign in to comment.