-
Notifications
You must be signed in to change notification settings - Fork 97
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update access-control.rst #69
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,28 +5,28 @@ In this chapter, we will add access control to our APIs, | |
and add APIs to create and authenticate users. | ||
|
||
Right now our APIs are completely permissive. Anyone can create, access and delete anything. | ||
We want to add these access controls. | ||
We want to add these access controls: | ||
|
||
|
||
- A user must be authenticated to access a poll or the list of polls. | ||
- Only an authenticated users can create a poll. | ||
- Only an authenticated user can create a poll. | ||
- Only an authenticated user can create a choice. | ||
- Authenticated users can create choices only for polls they have created. | ||
- Authenticated users can delete only polls they have created. | ||
- Only an authenticated user can vote. Users can vote for other people's polls. | ||
- Only an authenticated user can vote. Authenticated users can vote on other people's polls. | ||
|
||
To enable the access control, we need to add two more APIs | ||
To enable the access control, we need to add two more APIs: | ||
|
||
- API to create a user, we will call this endpoint :code:`/users/` | ||
- API to verify a user and get a token to identify them, we will call this endpoint :code:`/login/` | ||
- API to create a user. We will call this endpoint :code:`/users/` | ||
- API to verify a user and get a token to identify them. We will call this endpoint :code:`/login/` | ||
|
||
|
||
|
||
Creating a user | ||
-------------------------- | ||
|
||
|
||
We will add an user serializer, which will allow creating. Add the following code to :code:`serializers.py`. | ||
We will add a user serializer, which will allow creating. Add the following code to :code:`serializers.py`. | ||
|
||
.. code-block:: python | ||
|
||
|
@@ -50,7 +50,7 @@ We will add an user serializer, which will allow creating. Add the following cod | |
user.save() | ||
return user | ||
|
||
We have overriden the ModelSerializer method's :code:`create()` to save the :code:`User` instances. We ensure that we set the password correctly using :code:`user.set_password`, rather than setting the raw password as the hash. We also don't want to get back the password in response which we ensure using :code:`extra_kwargs = {'password': {'write_only': True}}`. | ||
We have overriden the ModelSerializer's :code:`create()` method to save the :code:`User` instances. We ensure that we set the password correctly using :code:`user.set_password()`, rather than setting the raw password as the hash. We also don't want to get back the password in the response which we ensure using :code:`extra_kwargs = {'password': {'write_only': True}}`. | ||
|
||
Let us also add views to the User Serializer for creating the user and connect it to the urls.py | ||
|
||
|
@@ -84,7 +84,7 @@ We can test this api by posting to :code:`/users/` with this json. | |
"password": "FiveThirtyEight" | ||
} | ||
|
||
Which give back this response. | ||
Doing so gives back this response. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
.. code-block:: json | ||
|
||
|
@@ -93,7 +93,7 @@ Which give back this response. | |
"email": "[email protected]" | ||
} | ||
|
||
Try posting the same json, and you will get a error response (HTTP status code 400) | ||
Try posting the same json, and you will get an error response (HTTP status code 400) | ||
|
||
.. code-block:: json | ||
|
||
|
@@ -107,7 +107,7 @@ Try posting the same json, and you will get a error response (HTTP status code 4 | |
Authentication scheme setup | ||
----------------------------- | ||
|
||
With Django Rest Framework, we can set up a default authentication scheme which is applied to all views using :code:`DEFAULT_AUTHENTICATION_CLASSES`. We will use the token authentication in this tutorial. In your settings.py, add this. | ||
With Django Rest Framework, we can set up a default authentication scheme which is applied to all views using :code:`DEFAULT_AUTHENTICATION_CLASSES`. We will use token authentication in this tutorial. In your settings.py, add this. | ||
|
||
.. code-block:: python | ||
|
||
|
@@ -149,13 +149,13 @@ Also, dont forget to give exemption to :code:`UserCreate` view for authenticatio | |
permission_classes = () | ||
serializer_class = UserSerializer | ||
|
||
Note the :code:`authentication_classes = ()` and :code:`permission_classes = ()` to exempt :code:`UserCreate` from global authentication scheme. | ||
Note the :code:`authentication_classes = ()` and :code:`permission_classes = ()` statements which exempt :code:`UserCreate` from the global authentication scheme. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Revert |
||
|
||
We want to ensure that tokens are created when user is created in :code:`UserCreate` view, so we update the :code:`UserSerializer`. Change your :code:`serializers.py` like this | ||
To ensure that tokens are created when a user is created in the :code:`UserCreate` view, we need to update the :code:`UserSerializer`. Edit your :code:`serializers.py` to accomodate this change. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Edit your |
||
|
||
.. code-block:: python | ||
|
||
from rest_framework.authtoken.models import Token | ||
from rest_framework.authtoken.models import Token # new | ||
|
||
class UserSerializer(serializers.ModelSerializer): | ||
|
||
|
@@ -171,15 +171,15 @@ We want to ensure that tokens are created when user is created in :code:`UserCre | |
) | ||
user.set_password(validated_data['password']) | ||
user.save() | ||
Token.objects.create(user=user) | ||
Token.objects.create(user=user) # new | ||
return user | ||
|
||
|
||
|
||
The login API | ||
----------------------------- | ||
|
||
Since we have added :code:`rest_framework.authentication.TokenAuthentication`, we will need to set a header like this :code:`Authorization: Token c2a84953f47288ac1943a3f389a6034e395ad940` to auhenticate. We need an API where a user can give their username and password, and get a token back. | ||
Since we have added :code:`rest_framework.authentication.TokenAuthentication` in **settings.py**, we will need to set a header like this :code:`Authorization: Token c2a84953f47288ac1943a3f389a6034e395ad940` to auhenticate. We need an API where a user can give their username and password, and get a token back. | ||
|
||
We will not be adding a serializer, because we never save a token using this API. | ||
|
||
|
@@ -233,15 +233,15 @@ Do a POST with a correct username and password, and you will get a response like | |
} | ||
|
||
|
||
POST with a incorrect username and password, and you will get a response like this, with a HTTP status of 400. | ||
Do a POST with a incorrect username and password, and you will get a response like this, with a HTTP status of 400. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Revert and update: POST with an incorrect username and password, and you will get a response like this, with a HTTP status of 400. |
||
|
||
.. code-block:: json | ||
|
||
{ | ||
"error": "Wrong Credentials" | ||
} | ||
|
||
Another way to create this login endpoint is using :code:`obtain_auth_token` method provide by DRF | ||
Another way to create this login endpoint is using the :code:`obtain_auth_token` method provided by DRF. | ||
|
||
.. code-block:: python | ||
|
||
|
@@ -299,13 +299,13 @@ We will do that by overriding :code:`PollViewSet.destroy` and :code:`ChoiceList. | |
def post(self, request, *args, **kwargs): | ||
poll = Poll.objects.get(pk=self.kwargs["pk"]) | ||
if not request.user == poll.created_by: | ||
raise PermissionDenied("You can not create choice for this poll.") | ||
raise PermissionDenied("You can not create a choice for this poll.") | ||
return super().post(request, *args, **kwargs) | ||
|
||
In both cases, we are checking :code:`request.user` against the expected user, and raising | ||
a :code:`PermissionDenied` error if it does not match. | ||
|
||
You can check this by doing a DELETE on someone elses :code:`Poll`. You will get an error with :code:`HTTP 403 Forbidden` and response. | ||
You can check this by doing a DELETE on someone elses :code:`Poll`. You will get an error with :code:`HTTP 403 Forbidden` and the following response: | ||
|
||
|
||
.. code-block:: json | ||
|
@@ -315,7 +315,7 @@ You can check this by doing a DELETE on someone elses :code:`Poll`. You will get | |
} | ||
|
||
|
||
Similarly, trying to create choice for someone else's :code:`Poll` will get an error with :code:`HTTP 403 Forbidden` and response | ||
Similarly, trying to create a choice for someone else's :code:`Poll` will get an error with :code:`HTTP 403 Forbidden` and the following response: | ||
|
||
.. code-block:: json | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only an authenticated user can vote. They can also vote on other people's polls