Skip to content
This repository has been archived by the owner on May 5, 2020. It is now read-only.

Rest auth #79

Merged
merged 22 commits into from
Sep 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[report]
show_missing = True
exclude_lines =
pragma: no cover
raise NotImplementedError
if __name__ == .__main__.:
if settings.DEBUG:
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,4 @@ target/
tests/local.py
docs/_build/
venv

.python-version
67 changes: 62 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Run this command to install django-nopassword
pip install django-nopassword

### Requirements
Django >= 1.4 (1.5 custom user is supported)
Django >= 1.11 (custom user is supported)

## Usage
Add the app to installed apps
Expand All @@ -22,26 +22,83 @@ INSTALLED_APPS = (
)
```

Set the authentication backend to *EmailBackend*
Add the authentication backend *EmailBackend*

AUTHENTICATION_BACKENDS = ('nopassword.backends.email.EmailBackend',)
```python
AUTHENTICATION_BACKENDS = (
# Needed to login by username in Django admin, regardless of `nopassword`
'django.contrib.auth.backends.ModelBackend',

# Send login codes via email
'nopassword.backends.email.EmailBackend',
)
```

Add urls to your *urls.py*

```python
urlpatterns = patterns('',
...
url(r'^accounts/', include('nopassword.urls', namespace='nopassword')),
url(r'^accounts/', include('nopassword.urls')),
...
)
```

### REST API

To use the REST API, *djangorestframework* must be installed

pip install djangorestframework

Add rest framework to installed apps

```python
INSTALLED_APPS = (
...
'rest_framework',
'rest_framework.authtoken',
'nopassword',
...
)
```

Add *TokenAuthentication* to default authentication classes

```python
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
)
}
```

Add urls to your *urls.py*

```python
urlpatterns = patterns('',
...
url(r'^api/accounts/', include('nopassword.rest.urls')),
...
)
```

You will have the following endpoints available:

- `/api/accounts/login/` (POST)
- username
- next (optional, will be returned in `/api/accounts/login/code/` to be handled by the frontend)
- Sends a login code to the user
- `/api/accounts/login/code/` (POST)
- code
- Returns `key` (authentication token) and `next` (provided by `/api/accounts/login/`)
- `/api/accounts/logout/` (POST)
- Performs logout

### Settings
Information about the available settings can be found in the [docs](http://django-nopassword.readthedocs.org/en/latest/#settings)

## Tests
Run with `python setup.py test`.
To run with sqlite add `USE_SQLITE = True` in tests/local.py

--------
MIT © Rolf Erik Lekang
34 changes: 34 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
Changelog
=========

4.0.0
-----

Added:

- Added ``LoginCodeAdmin``
- Added rest support

Breaking changes:

- Remove support for Django < 1.11
- Add support for Django 2
- ``NoPasswordBackend.authenticate`` doesn't have side effects anymore, it only checks if a login code is valid.
- ``NoPasswordBackend`` now uses the default django method ``user_can_authenticate`` instead of ``verify_user``.
- Changed signature of ``NoPasswordBackend.send_login_code`` to ``send_login_code(code, context, **kwargs)``, to support custom template context.
- ``EmailBackend`` doesn't attach a html message to the email by default. You can provide a template ``registration/login_email.html`` to do so.
- Removed setting ``NOPASSWORD_LOGIN_EMAIL_SUBJECT`` in favor of template ``registration/login_subject.txt``
- Renamed form ``AuthenticationForm`` to ``LoginForm``
- ``LoginForm`` (previously ``AuthenticationForm``) doesn't have side effects anymore while cleaning.
- ``LoginForm`` (previously ``AuthenticationForm``) doesn't check for cookie support anymore.
- Removed methods ``get_user`` and ``get_user_id`` from ``LoginForm`` (previously ``AuthenticationForm``).
- Removed method ``login_url`` and ``send_login_code`` from ``LoginCode`` (previously ``AuthenticationForm``).
- Renamed template ``registration/login.html`` to ``registration/login_form.html``.
- Changed content of default templates.
- Removed views ``login_with_code_and_username``.
- Refactored views to be class based views and to use forms instead of url parameters.
- Changed url paths
- Removed setting ``NOPASSWORD_POST_REDIRECT``, use ``NOPASSWORD_LOGIN_ON_GET`` instead.
- Removed setting ``NOPASSWORD_NAMESPACE``.
- Removed setting ``NOPASSWORD_HIDE_USERNAME``.
- Removed setting ``NOPASSWORD_LOGIN_EMAIL_SUBJECT``.
4 changes: 3 additions & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ Run this command to install django-nopassword::
pip install django-nopassword

Requirements:
Django >= 1.4 (1.5 custom user is supported)
Django >= 1.11 (custom user is supported)

.. include:: usage.rst
.. include:: rest.rst
.. include:: settings.rst
.. include:: changelog.rst
43 changes: 43 additions & 0 deletions docs/rest.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
REST API
--------
To use the REST API, *djangorestframework* must be installed::

pip install djangorestframework

Add rest framework to installed apps::

INSTALLED_APPS = (
...
'rest_framework',
'rest_framework.authtoken',
'nopassword',
...
)

Add *TokenAuthentication* to default authentication classes::

REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
)
}

Add urls to your *urls.py*::

urlpatterns = patterns('',
...
url(r'^api/accounts/', include('nopassword.rest.urls')),
...
)

You will have the following endpoints available:

- `/api/accounts/login/` (POST)
- username
- next (optional, will be returned in ``/api/accounts/login/code/`` to be handled by the frontend)
- Sends a login code to the user
- `/api/accounts/login/code/` (POST)
- code
- Returns ``key`` (authentication token) and ``next`` (provided by ``/api/accounts/login/``)
- `/api/accounts/logout/` (POST)
- Performs logout
34 changes: 3 additions & 31 deletions docs/settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,17 @@ django-nopassword settings

Defines how long a login code is valid in seconds.

.. attribute:: NOPASSWORD_NAMESPACE

Default: ``'nopassword'``

Defines the namespace for the urls, this must match the namespace of the include of
nopassword.urls.

.. attribute:: NOPASSWORD_HIDE_USERNAME

Default: ``False``

If set to True, the login url will not contain the username.

.. attribute:: NOPASSWORD_LOGIN_EMAIL_SUBJECT

Default: ``_('Login code')``

Sets Email Subject for Login Emails.

.. attribute:: NOPASSWORD_HASH_ALGORITHM

Default: ``'sha256'``

Set the algorithm for used in logincode generation. Possible values are those who are supported in hashlib. The value should be set as the name of the attribute in hashlib. Example `hashlib.sha256()` would be `NOPASSWORD_HASH_ALGORITHM = 'sha256'.

.. attribute:: NOPASSWORD_POST_REDIRECT
.. attribute:: NOPASSWORD_LOGIN_ON_GET

Default: ``True``
Default: ``False``

By default, the login code url requires a POST request to authenticate the user. A GET request renders ``registration/login_submit.html``, which contains some Javascript that automatically performs the POST on page load. To authenticate directly inside the initial GET request instead, set this to ``False``.
By default, the login code url requires a POST request to authenticate the user. A GET request renders a form that must be submitted by the user to perform authentication. To authenticate directly inside the initial GET request instead, set this to ``True``.

.. attribute:: NOPASSWORD_CODE_LENGTH

Expand All @@ -66,15 +47,6 @@ django-nopassword settings
Django settings used in django-nopassword
+++++++++++++++++++++++++++++++++++++++++

.. attribute:: SERVER_URL

Default: ``'example.com'``

By default, ``nopassword.views.login`` passes the result of ``result.get_host()`` to
``LoginCode.send_login_code`` to build the login URL. If you write your own view
and/or want to avoid this behavior by not passing a value for host, the
``SERVER_URL`` setting will be used instead.

.. attribute:: DEFAULT_FROM_EMAIL

Default: ``'[email protected]'``
57 changes: 30 additions & 27 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,22 @@ Add the app to installed apps::
'nopassword',
)

Set the authentication backend to *EmailBackend*::
Add the authentication backend *EmailBackend*::

AUTHENTICATION_BACKENDS = ( 'nopassword.backends.email.EmailBackend', )
AUTHENTICATION_BACKENDS = (
# Needed to login by username in Django admin, regardless of `nopassword`
'django.contrib.auth.backends.ModelBackend',

# Send login codes via email
'nopassword.backends.email.EmailBackend',
)

Add urls to your *urls.py*::

urlpatterns = patterns('',
url(r'^accounts/', include('nopassword.urls')),
)

Verify users
~~~~~~~~~~~~
If it is necessary to verify that users still are active in another system. Override
*verify_user(user)* to implement your check. In *NoPasswordBackend* that method checks
whether the user is active in the django app.

Backends
++++++++
There are several predefined backends. Usage of those backends are listed below.
Expand All @@ -30,33 +30,36 @@ There are several predefined backends. Usage of those backends are listed below.

.. class:: EmailBackend
Delivers the code by email. It uses the django send email functionality to send
the emails. It will attach both HTML and plain-text versions of the email.
the emails.

Override the following templates to customize emails:

- ``registration/login_email.txt`` - Plain text message
- ``registration/login_email.html`` - HTML message (note that no default html message is attached)
- ``registration/login_subject.txt`` - Subject

.. currentmodule:: nopassword.backends.sms

.. class:: TwilioBackend
Delivers the code by sms sent through the twilio service.

Override the following template to customize messages:

- ``registration/login_sms.txt`` - SMS message


Custom backends
~~~~~~~~~~~~~~~
In backends.py there is a *NoPasswordBackend*, from which it is possible
to build custom backends. The *EmailBackend* described above inherits from
this backend. Creating your own backend is can be done by creating a subclass
of *NoPasswordBackend* and implementing *send_login_code*. A good example is
the *EmailBackend*::

class EmailBackend(NoPasswordBackend):

def send_login_code(self, code, secure=False, host=None):
subject = getattr(settings, 'NOPASSWORD_LOGIN_EMAIL_SUBJECT', _('Login code'))
to_email = [code.user.email]
from_email = getattr(settings, 'DEFAULT_FROM_EMAIL', '[email protected]')

context = {'url': code.login_url(secure=secure, host=host), 'code': code}
text_content = render_to_string('registration/login_email.txt', context)
html_content = render_to_string('registration/login_email.html', context)

msg = EmailMultiAlternatives(subject, text_content, from_email, to_email)
msg.attach_alternative(html_content, 'text/html')
msg.send()
this backend. Creating your own backend can be done by creating a subclass
of *NoPasswordBackend* and implementing *send_login_code*.::

class CustomBackend(NoPasswordBackend):

def send_login_code(self, code, context, **kwargs):
"""
Use code.user to get contact information
Use context to render a custom template
Use kwargs in case you have a custom view that provides additional configuration
"""
11 changes: 11 additions & 0 deletions nopassword/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
from django.contrib import admin

from nopassword import models


@admin.register(models.LoginCode)
class LoginCodeAdmin(admin.ModelAdmin):
list_display = ('code', 'user', 'timestamp')
ordering = ('-timestamp',)
readonly_fields = ('code', 'user', 'timestamp', 'next')
2 changes: 1 addition & 1 deletion nopassword/backends/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@

try:
from .sms import TwilioBackend # noqa
except ImportError:
except ImportError: # pragma: no cover
pass
Loading