Remove ed25519, add pynacl, documentation updates and preparation for PyPI
This commit is contained in:
parent
f000e564f9
commit
fff2d3e244
24 changed files with 404 additions and 82 deletions
14
AUTHORS.md
Normal file
14
AUTHORS.md
Normal file
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
title: Credits
|
||||
---
|
||||
|
||||
Development Lead
|
||||
================
|
||||
|
||||
- Miroslav Shubernetskiy - <https://github.com/miki725>
|
||||
|
||||
Contributors
|
||||
============
|
||||
|
||||
- Keaton Brown - <https://gitlab.com/WolfgangAxel>
|
||||
- Python 2 removal, Django 2.2 upgrade
|
13
AUTHORS.rst
Normal file
13
AUTHORS.rst
Normal file
|
@ -0,0 +1,13 @@
|
|||
Credits
|
||||
-------
|
||||
|
||||
Development Lead
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
* Miroslav Shubernetskiy - https://github.com/miki725
|
||||
|
||||
Contributors
|
||||
~~~~~~~~~~~~
|
||||
|
||||
* Keaton Brown - https://gitlab.com/WolfgangAxel
|
||||
* Python 2 removal, Django 2.2 upgrade
|
21
CHANGELOG
21
CHANGELOG
|
@ -1,3 +1,24 @@
|
|||
Sat, 14 Sep 2019 02:38:56 -0500
|
||||
Keaton <kii-chan@tutanota.com>
|
||||
Remove ed25519, add pynacl, documentation updates and preparation for PyPI
|
||||
|
||||
- Fixed typos in readme, fixed formatting to be a little nicer.
|
||||
- Converted `tests` "app". Run tests as follows:
|
||||
|
||||
python3 tests/manage.py test
|
||||
|
||||
- `python-ed25519` lists on its GitHub page that it is depreciated, and that
|
||||
`pynacl` is the recommended alternative. As such, I've converted all calls
|
||||
to the ed25519 library into pynacl calls. All the tests pass, so we *should*
|
||||
be good...
|
||||
- Sidenote, I should really get in the habit of making sure tests pass before
|
||||
committing...
|
||||
- Started adding files needed by PyPI. Honestly I'm not sure that everything
|
||||
is 100% necessary, but I'm not really concerning myself with it for this
|
||||
commit. I just want to push the
|
||||
|
||||
--------------------
|
||||
|
||||
Wed, 04 Sep 2019 21:08:57 -0500
|
||||
Keaton <kii-chan@tutanota.com>
|
||||
Misc updates
|
||||
|
|
13
HISTORY.md
Normal file
13
HISTORY.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
title: History
|
||||
---
|
||||
|
||||
0.2.0 (2019-??-??)
|
||||
==================
|
||||
|
||||
- First release of rewrite on PyPI.
|
||||
|
||||
0.1.0 (2015-05-20)
|
||||
==================
|
||||
|
||||
- First release on PyPI.
|
14
HISTORY.rst
Normal file
14
HISTORY.rst
Normal file
|
@ -0,0 +1,14 @@
|
|||
.. :changelog:
|
||||
|
||||
History
|
||||
-------
|
||||
|
||||
0.2.0 (2019-09-20)
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* First release of rewrite on PyPI.
|
||||
|
||||
0.1.0 (2015-05-20)
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* First release on PyPI.
|
25
LICENSE.md
Normal file
25
LICENSE.md
Normal file
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
title: License
|
||||
---
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015, Miroslav Shubernetskiy
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -22,7 +22,9 @@ so all credit for this working belongs with them.
|
|||
|
||||
First step is to install `django-sqrl-2` which is easies to do using pip:
|
||||
|
||||
$ python3 -m pip install #django-sqrl-2
|
||||
```
|
||||
$ #python3 -m pip install django-sqrl-2
|
||||
```
|
||||
|
||||
### Django settings
|
||||
|
||||
|
@ -30,6 +32,7 @@ Once installed there are a few required changes in Django settings:
|
|||
|
||||
* Make sure that some required Django apps are used:
|
||||
|
||||
```
|
||||
INSTALLED_APPS = [
|
||||
...,
|
||||
'sqrl',
|
||||
|
@ -37,21 +40,26 @@ Once installed there are a few required changes in Django settings:
|
|||
'django.contrib.sessions',
|
||||
'django.contrib.staticfiles',
|
||||
]
|
||||
```
|
||||
|
||||
* Make sure that some required Django middleware are used:
|
||||
|
||||
```
|
||||
MIDDLEWARE_CLASSES = [
|
||||
...
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
]
|
||||
```
|
||||
|
||||
* Change `AUTHENTICATION_BACKENDS` to use SQRL backend vs Django's
|
||||
`ModelBackend` (default):
|
||||
|
||||
```
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
'sqrl.backends.SQRLModelBackend',
|
||||
]
|
||||
```
|
||||
|
||||
* If you are using Django admin, following are required:
|
||||
|
||||
|
@ -59,27 +67,33 @@ Once installed there are a few required changes in Django settings:
|
|||
This allows Django to prioritize `sqrl` templates since `django-sqrl`
|
||||
overwrites some of them.
|
||||
|
||||
```
|
||||
INSTALLED_APPS = [
|
||||
...,
|
||||
'sqrl',
|
||||
'django.contrib.admin',
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
* Make sure to add a custom template directory in settings. `django-sqrl`
|
||||
extends Django admin's `base.html` which by default causes infinite recursion.
|
||||
To solve that, simply add a custom template directory which allows `django-sqrl`
|
||||
to explicitly extend from `django.contrib.admin` `base.html` template:
|
||||
|
||||
```
|
||||
import os
|
||||
import django
|
||||
TEMPLATE_DIRS = [
|
||||
os.path.dirname(django.__file__),
|
||||
]
|
||||
```
|
||||
|
||||
## URLs
|
||||
|
||||
All of SQRL functionality is enabled by adding its URLs to the root URL config:
|
||||
|
||||
```
|
||||
from django.urls import path, include
|
||||
|
||||
urlpatterns = [
|
||||
|
@ -87,18 +101,21 @@ All of SQRL functionality is enabled by adding its URLs to the root URL config:
|
|||
path('sqrl/', include('sqrl.urls', namespace="sqrl")),
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
If you use Django admin, the `/admin/sqrl_manage` will be available to manage
|
||||
If you use Django admin, the `/admin/sqrl_manage` endpoint will be available to manage
|
||||
your site's SQRL identities.
|
||||
|
||||
## Templates
|
||||
|
||||
Now that SQRL is installed in your Django project, you can use it on any login
|
||||
page with three simple template tag:
|
||||
page with three simple template tags:
|
||||
|
||||
```
|
||||
{% load sqrl %}
|
||||
{% sqrl as sqrl_session %}
|
||||
{% sqrl_login_dropin sqrl_session [[a named redirect]] %}
|
||||
```
|
||||
|
||||
The [[named redirect]] is the page that should be redirected to after logging
|
||||
in. Any name that can be resolved by django's `reverse` function will work (i.e.
|
||||
|
@ -112,6 +129,7 @@ These three tags will add a simple element to your login page:
|
|||
If that doesn't suit your fancy, you may build your own template from the
|
||||
following essential tags:
|
||||
|
||||
```
|
||||
{% load sqrl %}
|
||||
{% sqrl as sqrl_session %}
|
||||
<a href="{{ sqrl_session.sqrl_url }}">
|
||||
|
@ -119,6 +137,7 @@ following essential tags:
|
|||
</a>
|
||||
<script>SQRL_NEXT="{{ your desired redirect }}"; SQRL_CHECK_URL="{% sqrl_status_url_script_tag sqrl_session %}"</script>
|
||||
<script type="application/javascript" src="{% static 'sqrl/sqrl.js' %}"></script>
|
||||
```
|
||||
|
||||
## Management Command
|
||||
|
||||
|
@ -126,19 +145,27 @@ SQRL uses server state to keep track of open SQRL transactions in order to
|
|||
mitigate replay attacks. Since this state will constantly grow if not cleared,
|
||||
`django-sqrl` provides a helper management command to clear expired states:
|
||||
|
||||
```
|
||||
$ python3 manage.py clearsqrlnuts
|
||||
```
|
||||
|
||||
It is recommended to run this command as repeating task. Here is an example
|
||||
configuration for `cron`:
|
||||
|
||||
```
|
||||
*/5 * * * * python manage.py clearsqrlnuts >/dev/null 2>&1
|
||||
```
|
||||
|
||||
## ~~Testing~~
|
||||
|
||||
~~To run the tests, you need to install the testing requirements first:~~
|
||||
|
||||
```
|
||||
$ #make install
|
||||
```
|
||||
|
||||
~~Then to run the tests, you can use use the Makefile command:~~
|
||||
|
||||
```
|
||||
$ #make test
|
||||
```
|
12
requirements-dev.txt
Normal file
12
requirements-dev.txt
Normal file
|
@ -0,0 +1,12 @@
|
|||
-r requirements.txt
|
||||
coverage
|
||||
django-extensions
|
||||
django-sslserver
|
||||
flake8
|
||||
mock
|
||||
Sphinx
|
||||
sphinx-autobuild
|
||||
sphinx-rtd-theme
|
||||
tox
|
||||
watchdog
|
||||
Werkzeug
|
|
@ -1,3 +1,3 @@
|
|||
Django
|
||||
django
|
||||
django-braces
|
||||
ed25519
|
||||
pynacl
|
||||
|
|
55
setup.py
Normal file
55
setup.py
Normal file
|
@ -0,0 +1,55 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
from sqrl import __author__, __version__
|
||||
|
||||
|
||||
def read(fname):
|
||||
return open(os.path.join(os.path.dirname(__file__), fname), 'r').read()
|
||||
|
||||
|
||||
authors = read('AUTHORS.md')
|
||||
history = read('HISTORY.md')
|
||||
licence = read('LICENSE.md')
|
||||
readme = read('README.md')
|
||||
|
||||
requirements = read('requirements.txt').splitlines() + [
|
||||
'setuptools',
|
||||
]
|
||||
|
||||
test_requirements = (
|
||||
read('requirements.txt').splitlines()
|
||||
+ read('requirements-dev.txt').splitlines()[1:]
|
||||
)
|
||||
|
||||
setup(
|
||||
name='django-sqrl-2',
|
||||
version=__version__,
|
||||
author=__author__,
|
||||
description='SQRL authentication support for Django',
|
||||
long_description='\n\n'.join([readme, history, authors, licence]),
|
||||
long_description_content_type='text/markdown',
|
||||
url='https://gitlub.com/WolfgangAxel/django-sqrl-2',
|
||||
license='MIT',
|
||||
packages=find_packages(exclude=['tests', 'tests.*']),
|
||||
install_requires=requirements,
|
||||
test_suite='tests',
|
||||
tests_require=test_requirements,
|
||||
keywords=' '.join([
|
||||
'django-sqrl',
|
||||
'django-sqrl-2',
|
||||
'sqrl'
|
||||
]),
|
||||
classifiers=[
|
||||
'Intended Audience :: Developers',
|
||||
'Natural Language :: English',
|
||||
'License :: OSI Approved :: MIT License',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Development Status :: 2 - Pre-Alpha',
|
||||
],
|
||||
python_requires='>=3.7',
|
||||
)
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
__author__ = 'Miroslav Shubernetskiy | Keaton Brown'
|
||||
__email__ = 'miroslav@miki725.com | kii-chan@tutanota.com'
|
||||
__version__ = '0.1.0'
|
||||
__author__ = 'Keaton Brown'
|
||||
__email__ = 'kii-chan@tutanota.com'
|
||||
__version__ = '0.2.0'
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
from collections import OrderedDict
|
||||
import os
|
||||
|
||||
import ed25519
|
||||
from nacl import signing
|
||||
from nacl.exceptions import BadSignatureError
|
||||
from django.utils.crypto import constant_time_compare, salted_hmac
|
||||
|
||||
from .utils import Base64, Encoder
|
||||
|
@ -89,6 +90,8 @@ class Ed25519(object):
|
|||
def __init__(self, public_key, private_key, msg):
|
||||
self.public_key = public_key
|
||||
self.private_key = private_key
|
||||
if private_key and type(private_key) == bytes:
|
||||
self.private_key = private_key[:32]
|
||||
self.msg = msg
|
||||
|
||||
def is_signature_valid(self, other_signature):
|
||||
|
@ -101,10 +104,10 @@ class Ed25519(object):
|
|||
Boolean indicating whether validation has succeeded.
|
||||
"""
|
||||
try:
|
||||
vk = ed25519.VerifyingKey(self.public_key)
|
||||
vk.verify(other_signature, self.msg)
|
||||
vk = signing.VerifyKey(self.public_key)
|
||||
vk.verify(self.msg, other_signature)
|
||||
return True
|
||||
except (AssertionError, ed25519.BadSignatureError):
|
||||
except (AssertionError, BadSignatureError) as e:
|
||||
return False
|
||||
|
||||
def sign_data(self):
|
||||
|
@ -116,8 +119,8 @@ class Ed25519(object):
|
|||
bytes
|
||||
ed25519 signature
|
||||
"""
|
||||
sk = ed25519.SigningKey(self.private_key)
|
||||
return sk.sign(self.msg)
|
||||
sk = signing.SigningKey(self.private_key)
|
||||
return sk.sign(self.msg).signature
|
||||
|
||||
|
||||
def generate_randomness(size=32):
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
</p>
|
||||
|
||||
{% sqrl as sqrl_session %}
|
||||
{% sqrl_login_dropin sqrl_session login %}
|
||||
{% sqrl_login_dropin sqrl_session "sqrl:login" %}
|
||||
{% endblock %}
|
||||
|
||||
{% comment %}
|
||||
|
@ -23,5 +23,6 @@ All necessary data should already be in the context.
|
|||
|
||||
Please note again that this template is for SQRL-exclusive logins.
|
||||
If you would like to add SQRL login to an existing login page,
|
||||
you should rather adjust that template as it is probably way more involved.
|
||||
you should rather adjust that template as it is probably way more involved
|
||||
to add that here.
|
||||
{% endcomment %}
|
||||
|
|
|
@ -15,7 +15,7 @@ def sqrl(context):
|
|||
|
||||
|
||||
@register.inclusion_tag('sqrl/sqrl-dropin.html')
|
||||
def sqrl_login_dropin(sqrl_session, redir=""):
|
||||
def sqrl_login_dropin(sqrl_session, redir="sqrl:login"):
|
||||
"""
|
||||
Creates a drop-in SQRL element in your template pages.
|
||||
Add it to your login template to make it SQRL-aware.
|
||||
|
|
|
@ -4,7 +4,8 @@ from collections import OrderedDict
|
|||
from django.conf import settings
|
||||
import os
|
||||
|
||||
import ed25519
|
||||
from nacl import signing
|
||||
from nacl.exceptions import BadSignatureError
|
||||
import mock
|
||||
|
||||
from ..crypto import HMAC, Ed25519, generate_randomness
|
||||
|
@ -111,11 +112,11 @@ class TestEd25519(unittest.TestCase):
|
|||
b'\x97\x145\x90N[\xb9\xfc\x8e\x8a\x9e\xd2=\xad\x84\xcd\xf1\x93\x06'
|
||||
)
|
||||
|
||||
@mock.patch('ed25519.SigningKey')
|
||||
@mock.patch('nacl.signing.SigningKey')
|
||||
def test_sign_data_mock(self, mock_signing_key):
|
||||
signature = self.sig.sign_data()
|
||||
|
||||
self.assertEqual(signature, mock_signing_key.return_value.sign.return_value)
|
||||
self.assertEqual(signature, mock_signing_key.return_value.sign.return_value.signature)
|
||||
mock_signing_key.assert_called_once_with(self.sig.private_key)
|
||||
mock_signing_key.return_value.sign.assert_called_once_with(self.sig.msg)
|
||||
|
||||
|
@ -125,17 +126,17 @@ class TestEd25519(unittest.TestCase):
|
|||
self.assertTrue(self.sig.is_signature_valid(signature))
|
||||
self.assertFalse(self.sig.is_signature_valid(b'a' + signature[:-1]))
|
||||
|
||||
@mock.patch('ed25519.VerifyingKey')
|
||||
@mock.patch('nacl.signing.VerifyKey')
|
||||
def test_is_signature_mock(self, mock_verifying_key):
|
||||
is_valid = self.sig.is_signature_valid(mock.sentinel.signature)
|
||||
|
||||
self.assertTrue(is_valid)
|
||||
mock_verifying_key.assert_called_once_with(self.sig.public_key)
|
||||
mock_verifying_key.return_value.verify.assert_called_once_with(
|
||||
mock.sentinel.signature, self.data
|
||||
self.data, mock.sentinel.signature
|
||||
)
|
||||
|
||||
@mock.patch('ed25519.VerifyingKey')
|
||||
@mock.patch('nacl.signing.VerifyKey')
|
||||
def test_is_signature_mock_assertion_error(self, mock_verifying_key):
|
||||
mock_verifying_key.return_value.verify.side_effect = AssertionError
|
||||
|
||||
|
@ -144,19 +145,19 @@ class TestEd25519(unittest.TestCase):
|
|||
self.assertFalse(is_valid)
|
||||
mock_verifying_key.assert_called_once_with(self.sig.public_key)
|
||||
mock_verifying_key.return_value.verify.assert_called_once_with(
|
||||
mock.sentinel.signature, self.data
|
||||
self.data, mock.sentinel.signature
|
||||
)
|
||||
|
||||
@mock.patch('ed25519.VerifyingKey')
|
||||
def test_is_signature_mock_bas_signature_error(self, mock_verifying_key):
|
||||
mock_verifying_key.return_value.verify.side_effect = ed25519.BadSignatureError
|
||||
@mock.patch('nacl.signing.VerifyKey')
|
||||
def test_is_signature_mock_bad_signature_error(self, mock_verifying_key):
|
||||
mock_verifying_key.return_value.verify.side_effect = BadSignatureError
|
||||
|
||||
is_valid = self.sig.is_signature_valid(mock.sentinel.signature)
|
||||
|
||||
self.assertFalse(is_valid)
|
||||
mock_verifying_key.assert_called_once_with(self.sig.public_key)
|
||||
mock_verifying_key.return_value.verify.assert_called_once_with(
|
||||
mock.sentinel.signature, self.data
|
||||
self.data, mock.sentinel.signature
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import unittest
|
||||
from collections import OrderedDict
|
||||
|
||||
import ed25519
|
||||
from nacl import signing
|
||||
import mock
|
||||
from django import forms, test
|
||||
from django.contrib.auth import SESSION_KEY, get_user_model
|
||||
|
@ -19,9 +19,10 @@ TESTING_MODULE = 'sqrl.forms'
|
|||
|
||||
class TestRequestForm(test.TestCase):
|
||||
def get_key_pair(self):
|
||||
signing_key, verifying_key = ed25519.create_keypair()
|
||||
signing_key = signing_key.to_bytes()
|
||||
verifying_key = verifying_key.to_bytes()
|
||||
signing_key = signing.SigningKey.generate()
|
||||
verifying_key = signing_key.verify_key
|
||||
signing_key = signing_key._signing_key
|
||||
verifying_key = verifying_key._key
|
||||
|
||||
return signing_key, verifying_key
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ class TestSQRLStatusView(test.TestCase):
|
|||
|
||||
def test_get_success_url_complete_registration(self):
|
||||
self.view.request.GET['url'] = '?next={}'.format(reverse('sqrl:login'))
|
||||
self.view.request.user.is_authenticated.return_value = False
|
||||
self.view.request.user.is_authenticated = False
|
||||
self.view.request.session = {SQRL_IDENTITY_SESSION_KEY: ''}
|
||||
|
||||
self.assertEqual(
|
||||
|
|
1
tests/__init__.py
Normal file
1
tests/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
|
95
tests/settings.py
Normal file
95
tests/settings.py
Normal file
|
@ -0,0 +1,95 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Bare ``settings.py`` for running tests for rest_framework_bulk
|
||||
"""
|
||||
import os
|
||||
import django
|
||||
|
||||
|
||||
DEBUG = True
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
||||
}
|
||||
}
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
]
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [os.path.dirname(django.__file__),],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
INSTALLED_APPS = (
|
||||
'sqrl',
|
||||
'tests',
|
||||
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.staticfiles',
|
||||
)
|
||||
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
'sqrl.backends.SQRLModelBackend',
|
||||
)
|
||||
|
||||
SQRL_SERVER_FRIENDLY_NAME = 'Django SQRL Test Site'
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
SECRET_KEY = 'foo'
|
||||
|
||||
ROOT_URLCONF = 'tests.urls'
|
||||
|
||||
LOGIN_REDIRECT_URL = '/'
|
||||
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'formatters': {
|
||||
'simple': {
|
||||
'format': '%(levelname)s %(message)s'
|
||||
},
|
||||
},
|
||||
'handlers': {
|
||||
'console': {
|
||||
'level': 'DEBUG',
|
||||
'class': 'logging.StreamHandler',
|
||||
'formatter': 'simple'
|
||||
},
|
||||
},
|
||||
'loggers': {
|
||||
'sqrl': {
|
||||
'handlers': ['console'],
|
||||
'level': 'DEBUG',
|
||||
'propagate': True,
|
||||
},
|
||||
'django.request': {
|
||||
'handlers': ['console'],
|
||||
'level': 'ERROR',
|
||||
'propagate': False,
|
||||
},
|
||||
},
|
||||
}
|
4
tests/static/sqrl/jquery.min.js
vendored
Normal file
4
tests/static/sqrl/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -1,5 +1,3 @@
|
|||
{% load static %}
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
|
@ -39,6 +37,12 @@
|
|||
{% endblock content %}
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
{% block footer %}
|
||||
SQRL Test server. Originally created by <a href="https://github.com/miki725">miki725</a>. Revised by <a href="https://gitlab.com/WolfgangAxel">WolfgangAxel</a>
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
||||
{% block scripts %}
|
||||
{% endblock %}
|
||||
|
1
tests/templates/sqrl.html
Normal file
1
tests/templates/sqrl.html
Normal file
|
@ -0,0 +1 @@
|
|||
{% extends 'base.html' %}
|
17
tests/urls.py
Normal file
17
tests/urls.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from django.urls import path, include
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth import urls as auth_urlpatterns
|
||||
from django.contrib.auth.views import LogoutView
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path("", TemplateView.as_view(template_name='sqrl.html'), name='index'),
|
||||
path("admin/", admin.site.urls),
|
||||
path("sqrl/", include('sqrl.urls', namespace='sqrl')),
|
||||
path("logout/", LogoutView.as_view(), {'next_page': 'sqrl:login'}, name='logout'),
|
||||
# Doesn't this not work/break things?
|
||||
path("", include(auth_urlpatterns)),
|
||||
]
|
Loading…
Reference in a new issue