Unit Test Using Forcing Authentication Django Rest Framework - django

I'm building RESTful API services using django rest framework, I've reached the point where i have to create an automated test for my RESTful API services.
The sessionList api require token authentication, in case the user doesn't have the token he won't be able to access the session collection.
The api worked fine when I've tested it using POSTMAN and the real browser.
SessionList:
class SessionList(generics.ListCreateAPIView):
authentication_classes = [TokenAuthentication, ]
permission_classes = [IsAuthenticated, ]
throttle_scope = 'session'
throttle_classes = (ScopedRateThrottle,)
name = 'session-list'
filter_class = SessionFilter
serializer_class = SessionSerializer
ordering_fields = (
'distance_in_miles',
'speed'
)
def get_queryset(self):
return Session.objects.filter(owner=self.request.user)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
Then i've created an automated test using DRF test
RunningSessionTest:
class RunningSessionTest(APITestCase):
def test_get_sessions(self):
factory = APIRequestFactory()
view = views.SessionList.as_view()
user = User.objects.create_user(
'user01', 'user01#example.com', 'user01P4ssw0rD')
request = factory.get('http://localhost:8000/sessions/')
force_authenticate(request, user=user)
response = view(request)
assert Response.status_code == status.HTTP_200_OK
def test_get_sessions_not_authenticated_user(self):
factory = APIRequestFactory()
view = views.SessionList.as_view()
user = User.objects.create_user(
'user01', 'user01#example.com', 'user01P4ssw0rD')
request = factory.get('http://localhost:8000/sessions/')
response = view(request)
assert Response.status_code == status.HTTP_401_UNAUTHORIZED
The issue: in both cases, if the user has the token or not the response value is HTTP_200_OK
I've tried to solve the problem by trying different methods to implement the test. I've used APIRequestFactory, Also i've used the APIClient but i got the same result. To be honest after reading the document many times i couldn't understand the differences between the APIClient and the APIRequestFactory.
The result of the test :
Traceback (most recent call last):
File "C:\python_work\DjnagoREST\01\restful01\RunKeeper\tests.py", line 67, in test_get_sessions_not_authenticated_user
assert Response.status_code == status.HTTP_401_UNAUTHORIZED
AssertionError
I will be grateful to your help.

I guess you will need to change Response.status_code to response.status_code.
As it turned out Response.status_code (as from rest_framework.response.Response)
is equal to 200 :D

Related

Testing a custom auth backend with Django RestFramework

I created a custom authentication backend for my DRF application.
I can't figure out how to test it.
Calling the client.post calls my authenticate function (cause that's in my view)
But I need to mock an internal method in my ModelBackend.
Kinda confused how to go about this?
View:
class Web3UserToken(APIView):
authentication_classes = []
permission_classes = []
def post(self, request, **kwargs):
public_address = request.data["public_address"]
web3 = Web3Backend()
user, token = web3.authenticate(request)
if token:
return JsonResponse({'token': token})
else:
return Response({'message': 'Missing token'}, status=400)
Test:
class TestWeb3AuthBackend(APITestCase):
def setUp(self):
#TODO: set up test user
self.client = APIClient()
#self.factory = APIRequestFactory()
def test_authenticatepasseswithexistinguser(self):
self.user = Web3User(public_address=TEST_PUBLIC_ADDRESS)
auth_backend = Web3Backend()
import ipdb; ipdb.sset_trace()
request = self.client.post('/api/token/', {'public_address': TEST_PUBLIC_ADDRESS, 'nonce': '0xsomething_random'},follow=True)
with mock.patch.object(auth_backend, '_check_nonce', return_value=True) as method:
token, user = auth_backend.authenticate(request)
self.assertTrue(token)
self.assertTrue(user)
I suggest using RequestFactory for creating a request and passing it to authenticate method, instead of sending a request via Django's test client. This is a unit test and its aim is to test authenticate method of Web3Backend. You don't need to test this functionality through an api call.

Django module tests: Failing to attach a non-anonymous user to a request

I am trying to test that my custom permissions in DRF work properly, so I need to imitate some requests made by users with different rights (the rights are stored as attributes in the db in a table linked to the default User table).
I am using RequestFactory for setting the request user, but it doesn't seem to work properly, as it still sees the request user as AnonymousUser, and returns AttributeError when checking the permission.
Here's an example of my testcase:
class TestSingleClient(TestCase):
def setUp(self):
self.factory = RequestFactory()
self.user = User.objects.create(
username='admin',
password='admin',
first_name='Name',
last_name='Surname',
email='user#gmail.com',
is_staff=True
)
AccountProfile.objects.create(
phone='8999999999',
occupation='tst_occupation',
user=self.user
)
Client.objects.create(
name='Client1',
type=0)
def test_get_single_client(self):
client = Client.objects.get(name='Client1')
request = self.factory.get('/system/clients/{}/'.format(client.id))
request.user = self.user
response = ClientView.as_view()(request, id=client.id)
client_data = Client.objects.get(id=client.id)
serializer = ClientSerializer(client_data)
self.assertEqual(response.data, serializer.data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
which results in:
File "***/core/extra.py", line 10, in my_permissions_func
roles = AccessRoles.objects.filter(user_groups=user.user_profile.group_id, can_read=True).\
AttributeError: 'AnonymousUser' object has no attribute 'user_profile'
Any suggestions for fixing that?
P.S. A similar topic was discussed here: Django RequestFactory doesn't store a User, which didn't bring me any closer to the answer.

request.POST returns old values after updating it in custom middleware - django 1.11.9

I am using django 1.11.9
I want to add client_id and client_secret to the django POST request.
Here is how my middleware.py file looks like:
class LoginMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# auth_header = get_authorization_header(request)
# Code to be executed for each request before
# the view (and later middleware) are called.
#Add Django authentication app client data to the request
request.POST = request.POST.copy()
request.POST['client_id'] = '12345678'
request.POST['client_secret'] = '12345678'
response = self.get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
Middleware is being successfully processed when I check it with a debugger. Thought when a view is called the 'client_id' and 'client_secret' fields are missing in the request.
After some experimenting i figure out that request is not getting updated and when it is called in a different view, it returns old values.
I am later using request in rest_framework_social_oauth2. And this is the point when 'client_id' and 'client_secret' disappear.
class ConvertTokenView(CsrfExemptMixin, OAuthLibMixin, APIView):
"""
Implements an endpoint to convert a provider token to an access token
The endpoint is used in the following flows:
* Authorization code
* Client credentials
"""
server_class = SocialTokenServer
validator_class = oauth2_settings.OAUTH2_VALIDATOR_CLASS
oauthlib_backend_class = KeepRequestCore
permission_classes = (permissions.AllowAny,)
def post(self, request, *args, **kwargs):
import pdb ; pdb.set_trace()
# Use the rest framework `.data` to fake the post body of the django request.
request._request.POST = request._request.POST.copy()
for key, value in request.data.items():
request._request.POST[key] = value
url, headers, body, status = self.create_token_response(request._request)
response = Response(data=json.loads(body), status=status)
for k, v in headers.items():
response[k] = v
return response
I need to add client_id and client_secret to the request body, so it can be later used by rest_framework_social_oauth2.
What could be the problem? How to properly update the request?
As you're working with request and processing a request, you have to implement process_request method, so the result will be something like:
class LoginMiddleware(object):
def process_request(self, request):
request.session['client_id'] = '12345678'
and then in your view:
def your_view(request):
client_id = request.session['client_id']

Authentication on Post DRF

I got an error on my DRF. when I try to authenticate using a post method.
the token is the correct for the admin user. when I use a safe method it is sucessfull, but with the post method no, it doesn't authenticate
my view
class SpecialistListView(ListCreateAPIView):
authentication_classes = (OAuth2Authentication,)
permission_classes = (permissions.IsAdminUser,)
queryset = Specialist.objects.all()
serializer_class = SpecialistSerializer
I don't understand why the status of the code returned is HTTP 401 Unauthorized.
It does'nt work on testing. I check that in postman is succesfull.
I'm using APIClient from rest_framework.test import APIClient
my test_client
client = APIClient()
client.credentials(HTTP_AUTHORIZATION='Bearer EGsnU4Cz3Mx5bUCuLrc2hmup51sSGz')
class CreateSpecialist(APITestCase):
fixtures = ['data','data2']
def setUp(self):
self.valid_payload = {
'username': 'julia',
'nick': 'julia',
'password': 'intel12345',
"first_name": "juliana",
"last_name": "garzon"
}
def test_create_specialist(self):
response = self.client.post(
reverse('specialists'),
data=json.dumps(self.valid_payload),
content_type='application/json'
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
I just add the credentials before the post and it works! :D
def test_create_specialist(self):
self.client.credentials(HTTP_AUTHORIZATION='Bearer EGsnU4Cz3Mx5bUCuLrc2hmup51sSGz')
response = self.client.post(
reverse('specialists'),
data=json.dumps(self.valid_payload),
content_type='application/json'
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

rest-framework permission testing

I am working with the latest django-rest-framework and want to create some tests. I have a ModelViewSet and a custom permission which accesses request.GET. This all works well, but in my unittest the GET dictionary is empty.
Here is my code:
class MyModelViewSet(ModelViewSet):
...
permission_classes = [IsAuthenticated, CustomPermission]
...
permissions.py:
class CustomPermission(permissions.BasePermission):
def has_permission(self, request, view):
# here I access the GET to check permissions
id = request.GET.get('id')
obj = MyModel.objects.get(id=id)
return request.user == obj.owner
This all works as expected in the browsable api.
But now I wrote a unittest:
class ModelTestCase(APITestCase):
def setUp(self):
self.obj = mommy.make('MyModel')
self.user = mommy.make('CustomUser')
def test_list(self):
self.client.force_authenticate(user=self.user)
url = '%s?id=%s' % (reverse('mymodel-list'), self.obj.id)
r = self.client.get(url) # this raises the exception
And here I get an exception:
models.DoesNotExist: MyModel matching query does not exist.
While debugging I realized that request.GET is empty in has_permission.
Has anyone an idea why this is working in "production" but not in the unittest?
Updating to the newest release (3.2.1) fixed this issue.