Skip to content
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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 21 additions & 21 deletions docs/access-control.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Copy link
Collaborator

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


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

Expand All @@ -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

Expand Down Expand Up @@ -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.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which gives back this response:
or
Which returns the response below


.. code-block:: json

Expand All @@ -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

Expand All @@ -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

Expand Down Expand Up @@ -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.
Copy link
Collaborator

Choose a reason for hiding this comment

The 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.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Edit your serializers.py as explained below:


.. code-block:: python

from rest_framework.authtoken.models import Token
from rest_framework.authtoken.models import Token # new

class UserSerializer(serializers.ModelSerializer):

Expand All @@ -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.

Expand Down Expand Up @@ -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.
Copy link
Collaborator

Choose a reason for hiding this comment

The 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

Expand Down Expand Up @@ -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
Expand All @@ -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

Expand Down