Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
eshaan7 committed Oct 24, 2020
1 parent 556148a commit 96f9590
Show file tree
Hide file tree
Showing 46 changed files with 1,822 additions and 0 deletions.
16 changes: 16 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

[run]
branch = True
source = django-rest-durin
omit =
durin/models.py

[report]
exclude_lines =
if self.debug:
pragma: no cover
raise NotImplementedError
if __name__ == .__main__.:
ignore_errors = True
omit =
tests/*
6 changes: 6 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*venv/
*.tox/
*.egg-info/
*.sqlite*
__pycache__
.vscode/
6 changes: 6 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[flake8]
max-line-length = 88
exclude =
*migrations*,
*venv*
virtualenv
61 changes: 61 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]

venv/

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover

# Translations
*.mo
*.pot

# Django stuff:
*.log

# Sphinx documentation
docs/_build/

# PyBuilder
target/
db.sqlite3
site/
7 changes: 7 additions & 0 deletions .isort.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[settings]
multi_line_output = 3
include_trailing_comma = True
force_grid_wrap = 0
use_parentheses = True
ensure_newline_before_comments = True
line_length = 88
17 changes: 17 additions & 0 deletions .lgtm.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
queries:
- exclude: py/similar-function
- exclude: py/empty-except
- exclude: py/call-to-non-callable
- include: py/undefined-placeholder-variable
- include: py/uninitialized-local-variable
- include: py/request-without-cert-validation
- include: py/return-or-yield-outside-function
- include: py/file-not-closed
- include: py/exit-from-finally
- include: py/ineffectual-statement
- include: py/unused-global-variable
- include: py/hardcoded-credentials
- include: py/import-of-mutable-attribute
- include: py/cyclic-import
- include: py/unnecessary-lambda
- include: py/print-during-import
37 changes: 37 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Disable sudo to speed up the build
sudo: false
language: python
install:
- pip install tox flake8 codecov
matrix:
include:
- python: "3.5"
env: TOX_ENVS=py35-django22
- python: "3.6"
dist: xenial
env: TOX_ENVS=py36-django22,py36-django30,py36-django31
- python: "3.7"
env: TOX_ENVS=py37-django22,py37-django30,py37-django31
dist: xenial
- python: "3.8"
env: TOX_ENVS=py38-django22,py38-django30,py38-django31
- python: "3.9-dev"
env: TOX_ENVS=py39-django22,py39-django30,py39-django31
before_script:
- flake8 . --count
script:
- tox -e $TOX_ENVS
after_success:
- codecov
deploy:
provider: pypi
user: eshaan7
password: testpasswordfortravis
on:
tags: true
repo: eshaan7/django-rest-durin
only:
- main
distributions: "sdist bdist_wheel"
git:
depth: false
6 changes: 6 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"python.formatting.provider": "black",
"python.linting.pylintEnabled": false,
"python.linting.flake8Enabled": true,
"python.linting.enabled": true
}
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
## 0.1.0
- Initial release
3 changes: 3 additions & 0 deletions CONTRIBUTORS
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- James McMahon
- Eshaan Bansal
- Matteo Lodi
4 changes: 4 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
include README.md
include CHANGELOG.md
include LICENSE
include CONTRIBUTORS
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Django-Rest-Durin

[![django-rest-durin on pypi](https://img.shields.io/pypi/v/django-rest-durin)](https://pypi.org/project/django-rest-durin/)
[![Build Status](https://travis-ci.com/Eshaan7/django-rest-durin.svg?branch=main)](https://travis-ci.com/Eshaan7/django-rest-durin)
[![codecov](https://codecov.io/gh/Eshaan7/django-rest-durin/branch/main/graph/badge.svg?token=S9KEI0PU05)](https://codecov.io/gh/Eshaan7/django-rest-durin/)
[![CodeFactor](https://www.codefactor.io/repository/github/eshaan7/django-rest-durin/badge)](https://www.codefactor.io/repository/github/eshaan7/django-rest-durin)
<a href="https://lgtm.com/projects/g/Eshaan7/django-rest-durin/context:python">
<img alt="Language grade: Python" src="https://img.shields.io/lgtm/grade/python/g/Eshaan7/django-rest-durin.svg?logo=lgtm&logoWidth=18"/>
</a>

Per API client token authentication Module for [Django REST Framework](http://www.django-rest-framework.org/).

The idea is to provide one library that does token auth for multiple Web/CLI/Mobile API clients via one interface but allows different token configuration for each client.

Durin authentication is token based, similar to the `TokenAuthentication`
built in to DRF. However, it adds some extra sauce:

- Durin allows multiple tokens per user. But only one token each user per API client.

- Each user token is associated with an API Client. These API Clients are configurable via Django's Admin Interface.

- All Durin tokens have an expiration time. This expiration time can be different per API client.

- Durin provides an option for a logged in user to remove *all*
tokens that the server has - forcing him/her to re-authenticate for all API clients.

- Durin tokens can be renewed to get a fresh expiry.

- Durin provides a `CachedTokenAuthentication` backend as well which uses memoization for faster look ups.

More information can be found in the [Documentation](https://django-rest-durin.readthedocs.io/).

## Django Compatibility Matrix

If your project uses an older verison of Django or Django Rest Framework, you can choose an older version of this project.

| This Project | Python Version | Django Version | Django Rest Framework |
|--------------|----------------|----------------|-----------------------|
| 0.1.* | 3.5 - 3.9 | 2.2, 3.0, 3.1 | 3.7>= |

Make sure to use at least `DRF 3.10` when using `Django 3.0` or newer.

## Changelog / Releases

All releases should be listed in the [releases tab on github](https://github.com/Eshaan7/django-rest-durin/releases).

See [CHANGELOG.md](CHANGELOG.md) for a more detailed listing.

## License

This project is published with the [MIT License](LICENSE). See [https://choosealicense.com/licenses/mit/](https://choosealicense.com/licenses/mit/) for more information about what this means.

## Credits

Durin is inpired by the [django-rest-knox](https://github.com/James1345/django-rest-knox) and [django-rest-multitokenauth](https://github.com/anexia-it/django-rest-multitokenauth) libraries and includes some learnings and code from both.
4 changes: 4 additions & 0 deletions docker-run-tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
black . && flake8 . && isort .
MOUNT_FOLDER=/app
docker run --rm -it -v $(pwd):$MOUNT_FOLDER -w $MOUNT_FOLDER themattrix/tox
108 changes: 108 additions & 0 deletions docs/auth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Authentication `knox.auth`

Knox provides one class to handle authentication.

## TokenAuthentication

This works using [DRF's authentication system](http://www.django-rest-framework.org/api-guide/authentication/).

Knox tokens should be generated using the provided views.
Any `APIView` or `ViewSet` can be accessed using these tokens by adding `TokenAuthentication`
to the View's `authentication_classes`.
To authenticate, the `Authorization` header should be set on the request, with a
value of the word `"Token"`, then a space, then the authentication token provided by
`LoginView`.

Example:
```python
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

from knox.auth import TokenAuthentication

class ExampleView(APIView):
authentication_classes = (TokenAuthentication,)
permission_classes = (IsAuthenticated,)

def get(self, request, format=None):
content = {
'foo': 'bar'
}
return Response(content)
```

Example auth header:

```javascript
Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b9836F45E23A345
```

Tokens expire after a preset time. See settings.


### Global usage on all views

You can activate TokenAuthentication on all your views by adding it to `REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"]`.

If it is your only default authentication class, remember to overwrite knox's LoginView, otherwise it'll not work, since the login view will require a authentication token to generate a new token, rendering it unusable.

For instance, you can authenticate users using Basic Authentication by simply overwriting knox's LoginView and setting BasicAuthentication as one of the acceptable authentication classes, as follows:

```python

views.py:

from knox.views import LoginView as KnoxLoginView
from rest_framework.authentication import BasicAuthentication

class LoginView(KnoxLoginView):
authentication_classes = [BasicAuthentication]

urls.py:

from knox import views as knox_views
from yourapp.api.views import LoginView

urlpatterns = [
url(r'login/', LoginView.as_view(), name='knox_login'),
url(r'logout/', knox_views.LogoutView.as_view(), name='knox_logout'),
url(r'logoutall/', knox_views.LogoutAllView.as_view(), name='knox_logoutall'),
]
```

You can use any number of authentication classes if you want to be able to authenticate using different methods (eg.: Basic and JSON) in the same view. Just be sure not to set TokenAuthentication as your only authentication class on the login view.

If you decide to use Token Authentication as your only authentication class, you can overwrite knox's login view as such:

```python

views.py:

from django.contrib.auth import login

from rest_framework import permissions
from rest_framework.authtoken.serializers import AuthTokenSerializer
from knox.views import LoginView as KnoxLoginView

class LoginView(KnoxLoginView):
permission_classes = (permissions.AllowAny,)

def post(self, request, format=None):
serializer = AuthTokenSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
login(request, user)
return super(LoginView, self).post(request, format=None)

urls.py:

from knox import views as knox_views
from yourapp.api.views import LoginView

urlpatterns = [
url(r'login/', LoginView.as_view(), name='knox_login'),
url(r'logout/', knox_views.LogoutView.as_view(), name='knox_logout'),
url(r'logoutall/', knox_views.LogoutAllView.as_view(), name='knox_logoutall'),
]
```
1 change: 1 addition & 0 deletions docs/changelog.md
31 changes: 31 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Django-Rest-Knox
Knox provides easy to use authentication for [Django REST Framework](http://www.django-rest-framework.org/)
The aim is to allow for common patterns in applications that are REST based,
with little extra effort; and to ensure that connections remain secure.

Knox authentication is token based, similar to the `TokenAuthentication` built
in to DRF. However, it overcomes some problems present in the default implementation:

- DRF tokens are limited to one per user. This does not facilitate securely
signing in from multiple devices, as the token is shared. It also requires
*all* devices to be logged out if a server-side logout is required (i.e. the
token is deleted).

Knox provides one token per call to the login view - allowing
each client to have its own token which is deleted on the server side when the client
logs out. Knox also provides an optional setting to limit the amount of tokens generated
per user.

Knox also provides an option for a logged in client to remove *all* tokens
that the server has - forcing all clients to re-authenticate.

- DRF tokens are stored unencrypted in the database. This would allow an attacker
unrestricted access to an account with a token if the database were compromised.

Knox tokens are only stored in an encrypted form. Even if the database were
somehow stolen, an attacker would not be able to log in with the stolen
credentials.

- DRF tokens track their creation time, but have no inbuilt mechanism for tokens
expiring. Knox tokens can have an expiry configured in the app settings (default is
10 hours.)
Loading

0 comments on commit 96f9590

Please sign in to comment.