Hello community,

here is the log from the commit of package python-django-rest-knox for 
openSUSE:Factory checked in at 2020-04-07 10:28:04
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-django-rest-knox (Old)
 and      /work/SRC/openSUSE:Factory/.python-django-rest-knox.new.3248 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-django-rest-knox"

Tue Apr  7 10:28:04 2020 rev:4 rq:791487 version:4.1.0

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-django-rest-knox/python-django-rest-knox.changes
  2019-10-08 19:57:57.328192969 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-django-rest-knox.new.3248/python-django-rest-knox.changes
        2020-04-07 10:28:30.654308771 +0200
@@ -1,0 +2,12 @@
+Sun Apr  5 07:54:00 UTC 2020 - Tomáš Chvátal <[email protected]>
+
+- Update to 4.1.0:
+  * Expiry format now defaults to whatever is used Django REST framework
+  * The behavior can be overriden via EXPIRY_DATETIME_FORMAT setting
+  * Fully customizable expiry format via format_expiry_datetime
+  * Fully customizable response payload via get_post_response_data
+  * Changes have been made to the create() method on the AuthToken model.
+    It now returns the model instance and the raw token instead of just
+    the token to allow the expiry field to be included in the success response.
+
+-------------------------------------------------------------------

Old:
----
  django-rest-knox-3.6.0.tar.gz

New:
----
  django-rest-knox-4.1.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-django-rest-knox.spec ++++++
--- /var/tmp/diff_new_pack.C9gWv6/_old  2020-04-07 10:28:31.242309425 +0200
+++ /var/tmp/diff_new_pack.C9gWv6/_new  2020-04-07 10:28:31.246309429 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-django-rest-knox
 #
-# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2020 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -19,10 +19,8 @@
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 %define skip_python2 1
 Name:           python-django-rest-knox
-Version:        3.6.0
+Version:        4.1.0
 Release:        0
-# Please, do not update to the current version, 4.x, as it is a breaking
-# change that at least breaks drf-jwt-knox
 Summary:        Authentication for Django REST framework
 License:        MIT
 URL:            https://github.com/James1345/django-rest-knox

++++++ django-rest-knox-3.6.0.tar.gz -> django-rest-knox-4.1.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-rest-knox-3.6.0/.isort.cfg 
new/django-rest-knox-4.1.0/.isort.cfg
--- old/django-rest-knox-3.6.0/.isort.cfg       1970-01-01 01:00:00.000000000 
+0100
+++ new/django-rest-knox-4.1.0/.isort.cfg       2019-06-01 08:24:05.000000000 
+0200
@@ -0,0 +1,7 @@
+[settings]
+combine_as_imports = true
+default_section = THIRDPARTY
+include_trailing_comma = true
+known_first_party = knox
+multi_line_output = 5
+not_skip = __init__.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-rest-knox-3.6.0/.travis.yml 
new/django-rest-knox-4.1.0/.travis.yml
--- old/django-rest-knox-3.6.0/.travis.yml      2018-12-31 12:58:08.000000000 
+0100
+++ new/django-rest-knox-4.1.0/.travis.yml      2019-06-01 08:24:05.000000000 
+0200
@@ -12,8 +12,12 @@
       env: TOX_ENVS=py35-django111,py35-django20,py35-django20,py35-django21
     - python: "3.6"
       env: TOX_ENVS=py36-django111,py36-django20,py36-django21
+    - python: "3.6"
+      dist: xenial
+      sudo: true
+      env: TOX_ENVS=py36-django22
     - python: "3.7"
-      env: TOX_ENVS=py37-django20,py37-django21
+      env: TOX_ENVS=py37-django20,py37-django21,py37-django22
       dist: xenial
       sudo: true
 before_script:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-rest-knox-3.6.0/CHANGELOG.md 
new/django-rest-knox-4.1.0/CHANGELOG.md
--- old/django-rest-knox-3.6.0/CHANGELOG.md     2018-12-31 12:58:08.000000000 
+0100
+++ new/django-rest-knox-4.1.0/CHANGELOG.md     2019-06-01 08:24:05.000000000 
+0200
@@ -1,153 +1,134 @@
-3.6.0
-=====
+## 4.1.0
 
-- The user serializer for each `LoginView`is now dynamic
+- Expiry format now defaults to whatever is used Django REST framework
+- The behavior can be overriden via EXPIRY_DATETIME_FORMAT setting
+- Fully customizable expiry format via format_expiry_datetime
+- Fully customizable response payload via get_post_response_data
 
 
-3.5.0
-=====
+## 4.0.1
 
-- The context, token TTL and tokens per user settings in `LoginView` are now 
dynamic
+- Fix for tox config to build Django 2.2 on python 3.6
+
+## 4.0.0
+
+**BREAKING** This is a major release version because it
+breaks the existing API.
+Changes have been made to the `create()` method on the `AuthToken` model. 
+It now returns the model instance and the raw `token` instead
+of just the `token` to allow the `expiry` field to be included in the
+success response.
+
+Model field of `AuthToken` has been renamed from `expires` to `expiry`
+to remain consistent across the code base. This patch requires you
+to run a migration.
+
+Depending on your usage you might have to adjust your code
+to fit these new changes.
+
+- `AuthToken` model field has been changed from `expires` to `expiry`
+- Successful login now always returns a `expiry` field for when the token 
expires
+
+## 3.6.0
 
+- The user serializer for each `LoginView`is now dynamic
+
+## 3.5.0
 
-3.4.0
-=====
+- The context, token TTL and tokens per user settings in `LoginView` are now 
dynamic
 
+## 3.4.0
 Our release cycle was broken since 3.1.5, hence you can not find the previous 
releases on pypi. We now fixed the problem.
 
 - Adds optional token limit
-- #129, #128 fixed
+- \#129, \#128 fixed
 - Changelog and Readme converted to markdown
 - Auth header prefix is now configurable
 - We ensure not to have flake8 errors in our code during our build
 - MIN_REFRESH_INTERVAL is now a configurable setting
 
+## 3.3.1
+- Ensure compatibility with Django 2.1 up to Python 3.7
 
-3.3.1
-=====
-
--   Ensure compatibility with Django 2.1 up to Python 3.7
-
-3.3.0
-=====
+## 3.3.0
 
 -   **Breaking changes**: Successful authentication **ONLY** returns
     `Token` object by default
     now.`USER_SERIALIZER` must be overridden to return more
     data.
-
+    
 -   Introduce new setting `MIN_REFRESH_INTERVAL` to configure the time
     interval (in seconds) to wait before a token is automatically refreshed.
 
-3.2.1
-=====
-
--   Fix !111: Avoid knox failing if settings are not overwritten
-
-3.2.0
-=====
+## 3.2.1
+- Fix !111: Avoid knox failing if settings are not overwritten
 
--   Introduce new setting AUTO_REFRESH for controlling if token expiry
-    time should be extended automatically
+## 3.2.0
+- Introduce new setting AUTO_REFRESH for controlling if token expiry time 
should be extended automatically
 
-3.1.5
-=====
+## 3.1.5
+- Make AuthTokenAdmin more compatible with big user tables
+- Extend docs regarding usage of Token Authentication as single authentication 
method.
 
--   Make AuthTokenAdmin more compatible with big user tables
--   Extend docs regarding usage of Token Authentication as single
-    authentication method.
+## 3.1.4
+- Fix compability with django-rest-swagger (bad inheritance)
 
-3.1.4
-=====
+## 3.1.3
+- Avoid 500 error response for invalid-length token requests
 
--   Fix compability with django-rest-swagger (bad inheritance)
+## 3.1.2
+- restore compability with Python <2.7.7
 
-3.1.3
-=====
+## 3.1.1
+- use hmac.compare_digest instead of == for comparing hashes for more security
 
--   Avoid 500 error response for invalid-length token requests
+## 3.1.0
+- drop Django 1.8 support as djangorestframework did so too in v.3.7.0
+- build rest-knox on Django 1.11 and 2.0
 
-3.1.2
-=====
+## 3.0.3
+- drop using OpenSSL in favor of urandom
 
--   restore compability with Python <2.7.7
+## 3.0.2
+- Add context to UserSerializer
+- improve docs
 
-3.1.1
-=====
+## 3.0.1
+- improved docs and readme
+- login response better supporting hyperlinked fields
 
--   use hmac.compare_digest instead of == for comparing hashes for more
-    security
+## 3.0.0
+**Please be aware: updating to this version requires applying a database 
migration. All clients will need to reauthenticate.**
 
-3.1.0
-=====
+- Big performance fix: Introduction of token_key field to avoid having to 
compare a login request's token against each and every token in the database 
(issue #21)
+- increased test coverage
 
--   drop Django 1.8 support as djangorestframework did so too in v.3.7.0
--   build rest-knox on Django 1.11 and 2.0
+## 2.2.2
+- Bugfix: invalid token length does no longer trigger a server error
+- Extending documentation
 
-3.0.3
-=====
+## 2.2.1
+**Please be aware: updating to his version requires applying a database 
migration**
 
--   drop using OpenSSL in favor of urandom
-
-3.0.2
-=====
-
--   Add context to UserSerializer
--   improve docs
-
-3.0.1
-=====
-
--   improved docs and readme
--   login response better supporting hyperlinked fields
-
-3.0.3
-=====
-
--   drop using OpenSSL in favor of urandom
-
-3.0.2
-=====
-
--   Add context to UserSerializer
--   improve docs
-
-3.0.1
-=====
-
--   improved docs and readme
--   login response better supporting hyperlinked fields
-
-3.0.0
-=====
-
-**Please be aware: updating to this version requires applying a database
-migration. All clients will need to reauthenticate.**
-
--   Big performance fix: Introduction of token_key field to avoid
-    having to compare a login request's token against each and every
-    token in the database (issue #21)
--   increased test coverage
-
-2.2.2
-=====
+- Introducing token_key to avoid loop over all tokens on login-requests
+- Signals are sent on login/logout
+- Test for invalid token length
+- Cleanup in code and documentation
 
 -   Bugfix: invalid token length does no longer trigger a server error
 -   Extending documentation
 
-2.2.1
-=====
+## 2.2.0
 
-**Please be aware: updating to this version requires applying a database
-migration**
+-   Change to support python 2.7
 
--   Introducing token_key to avoid loop over all tokens on
-    login-requests
--   Signals are sent on login/logout
--   Test for invalid token length
--   Cleanup in code and documentation
+## 2.0.0
+-   Hashing of tokens on the server introduced.
+-   Updating to this version will clean the AuthToken table. In real terms, 
this
+    means all users will be forced to log in again.
 
-2.2.0
-=====
+## 1.1.0
+-   `LoginView` changed to respect `DEFAULT_AUTHENTICATION_CLASSES`
 
--   Change to support python 2.7
+## 1.0.0
+-   Initial release
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-rest-knox-3.6.0/README.md 
new/django-rest-knox-4.1.0/README.md
--- old/django-rest-knox-3.6.0/README.md        2018-12-31 12:58:08.000000000 
+0100
+++ new/django-rest-knox-4.1.0/README.md        2019-06-01 08:24:05.000000000 
+0200
@@ -50,3 +50,31 @@
 
 You could also simply run regular ``tox`` in the root folder as well, but that 
would make testing the matrix of
 Python / Django versions a bit more tricky.
+
+# Work on the documentation
+
+Our documentation is generated by [Mkdocs](https://www.mkdocs.org).
+
+You can refer to their documentation on how to install it locally.
+
+Another option is to use `mkdocs.sh` in this repository.
+It will run mkdocs in a [docker](https://www.docker.com/) container.
+
+Running the script without any params triggers the `serve` command.
+The server is exposed on localhost on port 8000.
+
+To configure the port the `serve` command will be exposing the server to, you
+can use the following env var:
+
+```
+MKDOCS_DEV_PORT="8080"
+```
+
+You can also pass any `mkdocs` command like this:
+
+```
+./mkdocs build
+./mkdocs --help
+```
+
+Check the [Mkdocs documentation](https://www.mkdocs.org/) for more.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-rest-knox-3.6.0/docs/changelog.md 
new/django-rest-knox-4.1.0/docs/changelog.md
--- old/django-rest-knox-3.6.0/docs/changelog.md        1970-01-01 
01:00:00.000000000 +0100
+++ new/django-rest-knox-4.1.0/docs/changelog.md        2020-04-07 
10:28:31.306309495 +0200
@@ -0,0 +1 @@
+symbolic link to ../CHANGELOG.md
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-rest-knox-3.6.0/docs/changes.md 
new/django-rest-knox-4.1.0/docs/changes.md
--- old/django-rest-knox-3.6.0/docs/changes.md  2018-12-31 12:58:08.000000000 
+0100
+++ new/django-rest-knox-4.1.0/docs/changes.md  1970-01-01 01:00:00.000000000 
+0100
@@ -1,92 +0,0 @@
-# Changelog
-
-## 3.6.0
-
-- The user serializer for each `LoginView`is now dynamic
-
-## 3.5.0
-
-- The context, token TTL and tokens per user settings in `LoginView` are now 
dynamic
-
-## 3.4.0
-Our release cycle was broken since 3.1.5, hence you can not find the previous 
releases on pypi. We now fixed the problem.
-
-- Adds optional token limit
-- #129, #128 fixed
-- Changelog and Readme converted to markdown
-- Auth header prefix is now configurable
-- We ensure not to have flake8 errors in our code during our build
-- MIN_REFRESH_INTERVAL is now a configurable setting
-
-## 3.3.1
-- Ensure compatibility with Django 2.1 up to Python 3.7
-
-## 3.3.0
-- **Breaking changes**: Successful authentication **ONLY** returns `Token` 
object by default now.
-`USER_SERIALIZER` must be overridden to return more data.
-
-## 3.2.1
-- Fix !111: Avoid knox failing if settings are not overwritten
-
-## 3.2.0
-- Introduce new setting AUTO_REFRESH for controlling if token expiry time 
should be extended automatically
-
-## 3.1.5
-- Make AuthTokenAdmin more compatible with big user tables
-- Extend docs regarding usage of Token Authentication as single authentication 
method.
-
-## 3.1.4
-- Fix compability with django-rest-swagger (bad inheritance)
-
-## 3.1.3
-- Avoid 500 error response for invalid-length token requests
-
-## 3.1.2
-- restore compability with Python <2.7.7
-
-## 3.1.1
-- use hmac.compare_digest instead of == for comparing hashes for more security
-
-## 3.1.0
-- drop Django 1.8 support as djangorestframework did so too in v.3.7.0
-- build rest-knox on Django 1.11 and 2.0
-
-## 3.0.3
-- drop using OpenSSL in favor of urandom
-
-## 3.0.2
-- Add context to UserSerializer
-- improve docs
-
-## 3.0.1
-- improved docs and readme
-- login response better supporting hyperlinked fields
-
-## 3.0.0
-**Please be aware: updating to this version requires applying a database 
migration. All clients will need to reauthenticate.**
-
-- Big performance fix: Introduction of token_key field to avoid having to 
compare a login request's token against each and every token in the database 
(issue #21)
-- increased test coverage
-
-## 2.2.2
-- Bugfix: invalid token length does no longer trigger a server error
-- Extending documentation
-
-## 2.2.1
-**Please be aware: updating to his version requires applying a database 
migration**
-
-- Introducing token_key to avoid loop over all tokens on login-requests
-- Signals are sent on login/logout
-- Test for invalid token length
-- Cleanup in code and documentation
-
-## 2.0.0
--   Hashing of tokens on the server introduced.
--   Updating to this version will clean the AuthToken table. In real terms, 
this
-    means all users will be forced to log in again.
-
-## 1.1.0
--   `LoginView` changed to respect `DEFAULT_AUTHENTICATION_CLASSES`
-
-## 1.0.0
--   Initial release
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-rest-knox-3.6.0/docs/settings.md 
new/django-rest-knox-4.1.0/docs/settings.md
--- old/django-rest-knox-3.6.0/docs/settings.md 2018-12-31 12:58:08.000000000 
+0100
+++ new/django-rest-knox-4.1.0/docs/settings.md 2019-06-01 08:24:05.000000000 
+0200
@@ -9,13 +9,15 @@
 #...snip...
 # These are the default values if none are set
 from datetime import timedelta
+from rest_framework.settings import api_settings
 REST_KNOX = {
   'SECURE_HASH_ALGORITHM': 'cryptography.hazmat.primitives.hashes.SHA512',
   'AUTH_TOKEN_CHARACTER_LENGTH': 64,
   'TOKEN_TTL': timedelta(hours=10),
   'USER_SERIALIZER': 'knox.serializers.UserSerializer',
   'TOKEN_LIMIT_PER_USER': None,
-  'AUTO_REFRESH': FALSE,
+  'AUTO_REFRESH': False,
+  'EXPIRY_DATETIME_FORMAT': api_settings.DATETME_FORMAT,
 }
 #...snip...
 ```
@@ -74,6 +76,14 @@
 ## AUTH_HEADER_PREFIX
 This is the Authorization header value prefix. The default is `Token`
 
+## EXPIRY_DATETIME_FORMAT
+This is the expiry datetime format returned in the login view. The default is 
the
+[DATETIME_FORMAT][DATETIME_FORMAT] of Django REST framework. May be any of 
`None`, `iso-8601`
+or a Python [strftime format][strftime format] string.
+
+[DATETIME_FORMAT]: 
https://www.django-rest-framework.org/api-guide/settings/#date-and-time-formatting
+[strftime format]: https://docs.python.org/3/library/time.html#time.strftime
+
 # Constants `knox.settings`
 Knox also provides some constants for information. These must not be changed in
 external code; they are used in the model definitions in knox and an error will
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-rest-knox-3.6.0/docs/views.md 
new/django-rest-knox-4.1.0/docs/views.md
--- old/django-rest-knox-3.6.0/docs/views.md    2018-12-31 12:58:08.000000000 
+0100
+++ new/django-rest-knox-4.1.0/docs/views.md    2019-06-01 08:24:05.000000000 
+0200
@@ -15,14 +15,39 @@
 
 It is possible to customize LoginView behaviour by overriding the following
 helper methods:
-- `get_context`, to change the context passed to the `UserSerializer`
-- `get_token_ttl`, to change the token ttl
-- `get_token_limit_per_user`, to change the number of tokens available for a 
user
-- `get_user_serializer_class`, to change the class used for serializing the 
user
+- `get_context(self)`, to change the context passed to the `UserSerializer`
+- `get_token_ttl(self)`, to change the token ttl
+- `get_token_limit_per_user(self)`, to change the number of tokens available 
for a user
+- `get_user_serializer_class(self)`, to change the class used for serializing 
the user
+- `get_expiry_datetime_format(self)`, to change the datetime format used for 
expiry
+- `format_expiry_datetime(self, expiry)`, to format the expiry `datetime` 
object at your convinience
+
+Finally, if none of these helper methods are sufficient, you can also override 
`get_post_response_data`
+to return a fully customized payload.
+
+```python
+...snip...
+    def get_post_response_data(self, request, token, instance):
+        UserSerializer = self.get_user_serializer_class()
+
+        data = {
+            'expiry': self.format_expiry_datetime(instance.expiry),
+            'token': token
+        }
+        if UserSerializer is not None:
+            data["user"] = UserSerializer(
+                request.user,
+                context=self.get_context()
+            ).data
+        return data
+...snip...
+```
 
 ---
-When the endpoint authenticates a request, a json object will be returned 
+When the endpoint authenticates a request, a json object will be returned
 containing the `token` key along with the actual value for the key by default.
+The success response also includes a `expiry` key with a timestamp for when
+the token expires.
 
 > *This is because `USER_SERIALIZER` setting is `None` by default.*
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-rest-knox-3.6.0/knox/admin.py 
new/django-rest-knox-4.1.0/knox/admin.py
--- old/django-rest-knox-3.6.0/knox/admin.py    2018-12-31 12:58:08.000000000 
+0100
+++ new/django-rest-knox-4.1.0/knox/admin.py    2019-06-01 08:24:05.000000000 
+0200
@@ -1,4 +1,5 @@
 from django.contrib import admin
+
 from knox import models
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-rest-knox-3.6.0/knox/auth.py 
new/django-rest-knox-4.1.0/knox/auth.py
--- old/django-rest-knox-3.6.0/knox/auth.py     2018-12-31 12:58:08.000000000 
+0100
+++ new/django-rest-knox-4.1.0/knox/auth.py     2019-06-01 08:24:05.000000000 
+0200
@@ -6,14 +6,12 @@
 
 import binascii
 
-from django.utils.translation import ugettext_lazy as _
-from django.utils import timezone
 from django.contrib.auth import get_user_model
-
+from django.utils import timezone
+from django.utils.translation import ugettext_lazy as _
 from rest_framework import exceptions
 from rest_framework.authentication import (
-    BaseAuthentication,
-    get_authorization_header
+    BaseAuthentication, get_authorization_header,
 )
 
 from knox.crypto import hash_token
@@ -74,19 +72,19 @@
             except (TypeError, binascii.Error):
                 raise exceptions.AuthenticationFailed(msg)
             if compare_digest(digest, auth_token.digest):
-                if knox_settings.AUTO_REFRESH and auth_token.expires:
+                if knox_settings.AUTO_REFRESH and auth_token.expiry:
                     self.renew_token(auth_token)
                 return self.validate_user(auth_token)
         raise exceptions.AuthenticationFailed(msg)
 
     def renew_token(self, auth_token):
-        current_expiry = auth_token.expires
+        current_expiry = auth_token.expiry
         new_expiry = timezone.now() + knox_settings.TOKEN_TTL
-        auth_token.expires = new_expiry
+        auth_token.expiry = new_expiry
         # Throttle refreshing of token to avoid db writes
         delta = (new_expiry - current_expiry).total_seconds()
         if delta > knox_settings.MIN_REFRESH_INTERVAL:
-            auth_token.save(update_fields=('expires',))
+            auth_token.save(update_fields=('expiry',))
 
     def validate_user(self, auth_token):
         if not auth_token.user.is_active:
@@ -99,14 +97,14 @@
 
     def _cleanup_token(self, auth_token):
         for other_token in auth_token.user.auth_token_set.all():
-            if other_token.digest != auth_token.digest and other_token.expires:
-                if other_token.expires < timezone.now():
+            if other_token.digest != auth_token.digest and other_token.expiry:
+                if other_token.expiry < timezone.now():
                     other_token.delete()
                     username = other_token.user.get_username()
                     token_expired.send(sender=self.__class__,
                                        username=username, source="other_token")
-        if auth_token.expires is not None:
-            if auth_token.expires < timezone.now():
+        if auth_token.expiry is not None:
+            if auth_token.expiry < timezone.now():
                 username = auth_token.user.get_username()
                 auth_token.delete()
                 token_expired.send(sender=self.__class__,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-rest-knox-3.6.0/knox/crypto.py 
new/django-rest-knox-4.1.0/knox/crypto.py
--- old/django-rest-knox-3.6.0/knox/crypto.py   2018-12-31 12:58:08.000000000 
+0100
+++ new/django-rest-knox-4.1.0/knox/crypto.py   2019-06-01 08:24:05.000000000 
+0200
@@ -1,10 +1,10 @@
 import binascii
+from os import urandom as generate_bytes
 
 from cryptography.hazmat.backends import default_backend
 from cryptography.hazmat.primitives import hashes
-from os import urandom as generate_bytes
 
-from knox.settings import knox_settings, CONSTANTS
+from knox.settings import CONSTANTS, knox_settings
 
 sha = knox_settings.SECURE_HASH_ALGORITHM
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django-rest-knox-3.6.0/knox/migrations/0001_initial.py 
new/django-rest-knox-4.1.0/knox/migrations/0001_initial.py
--- old/django-rest-knox-3.6.0/knox/migrations/0001_initial.py  2018-12-31 
12:58:08.000000000 +0100
+++ new/django-rest-knox-4.1.0/knox/migrations/0001_initial.py  2019-06-01 
08:24:05.000000000 +0200
@@ -1,8 +1,8 @@
 # -*- coding: utf-8 -*-
 from __future__ import unicode_literals
 
-from django.db import models, migrations
 from django.conf import settings
+from django.db import migrations, models
 
 
 class Migration(migrations.Migration):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django-rest-knox-3.6.0/knox/migrations/0002_auto_20150916_1425.py 
new/django-rest-knox-4.1.0/knox/migrations/0002_auto_20150916_1425.py
--- old/django-rest-knox-3.6.0/knox/migrations/0002_auto_20150916_1425.py       
2018-12-31 12:58:08.000000000 +0100
+++ new/django-rest-knox-4.1.0/knox/migrations/0002_auto_20150916_1425.py       
2019-06-01 08:24:05.000000000 +0200
@@ -1,8 +1,8 @@
 # -*- coding: utf-8 -*-
 from __future__ import unicode_literals
 
-from django.db import models, migrations
 from django.conf import settings
+from django.db import migrations, models
 
 
 class Migration(migrations.Migration):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django-rest-knox-3.6.0/knox/migrations/0003_auto_20150916_1526.py 
new/django-rest-knox-4.1.0/knox/migrations/0003_auto_20150916_1526.py
--- old/django-rest-knox-3.6.0/knox/migrations/0003_auto_20150916_1526.py       
2018-12-31 12:58:08.000000000 +0100
+++ new/django-rest-knox-4.1.0/knox/migrations/0003_auto_20150916_1526.py       
2019-06-01 08:24:05.000000000 +0200
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 from __future__ import unicode_literals
 
-from django.db import models, migrations
+from django.db import migrations, models
 
 
 class Migration(migrations.Migration):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django-rest-knox-3.6.0/knox/migrations/0007_auto_20190111_0542.py 
new/django-rest-knox-4.1.0/knox/migrations/0007_auto_20190111_0542.py
--- old/django-rest-knox-3.6.0/knox/migrations/0007_auto_20190111_0542.py       
1970-01-01 01:00:00.000000000 +0100
+++ new/django-rest-knox-4.1.0/knox/migrations/0007_auto_20190111_0542.py       
2019-06-01 08:24:05.000000000 +0200
@@ -0,0 +1,18 @@
+# Generated by Django 2.1 on 2019-01-11 05:42
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('knox', '0006_auto_20160818_0932'),
+    ]
+
+    operations = [
+        migrations.RenameField(
+            model_name='authtoken',
+            old_name='expires',
+            new_name='expiry',
+        ),
+    ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-rest-knox-3.6.0/knox/models.py 
new/django-rest-knox-4.1.0/knox/models.py
--- old/django-rest-knox-3.6.0/knox/models.py   2018-12-31 12:58:08.000000000 
+0100
+++ new/django-rest-knox-4.1.0/knox/models.py   2019-06-01 08:24:05.000000000 
+0200
@@ -9,19 +9,18 @@
 
 
 class AuthTokenManager(models.Manager):
-    def create(self, user, expires=knox_settings.TOKEN_TTL):
+    def create(self, user, expiry=knox_settings.TOKEN_TTL):
         token = crypto.create_token_string()
         salt = crypto.create_salt_string()
         digest = crypto.hash_token(token, salt)
 
-        if expires is not None:
-            expires = timezone.now() + expires
+        if expiry is not None:
+            expiry = timezone.now() + expiry
 
-        super(AuthTokenManager, self).create(
+        instance = super(AuthTokenManager, self).create(
             token_key=token[:CONSTANTS.TOKEN_KEY_LENGTH], digest=digest,
-            salt=salt, user=user, expires=expires)
-        # Note only the token - not the AuthToken object - is returned
-        return token
+            salt=salt, user=user, expiry=expiry)
+        return instance, token
 
 
 class AuthToken(models.Model):
@@ -37,7 +36,7 @@
     user = models.ForeignKey(User, null=False, blank=False,
                              related_name='auth_token_set', 
on_delete=models.CASCADE)
     created = models.DateTimeField(auto_now_add=True)
-    expires = models.DateTimeField(null=True, blank=True)
+    expiry = models.DateTimeField(null=True, blank=True)
 
     def __str__(self):
         return '%s : %s' % (self.digest, self.user)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-rest-knox-3.6.0/knox/serializers.py 
new/django-rest-knox-4.1.0/knox/serializers.py
--- old/django-rest-knox-3.6.0/knox/serializers.py      2018-12-31 
12:58:08.000000000 +0100
+++ new/django-rest-knox-4.1.0/knox/serializers.py      2019-06-01 
08:24:05.000000000 +0200
@@ -1,5 +1,4 @@
 from django.contrib.auth import get_user_model
-
 from rest_framework import serializers
 
 User = get_user_model()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-rest-knox-3.6.0/knox/settings.py 
new/django-rest-knox-4.1.0/knox/settings.py
--- old/django-rest-knox-3.6.0/knox/settings.py 2018-12-31 12:58:08.000000000 
+0100
+++ new/django-rest-knox-4.1.0/knox/settings.py 2019-06-01 08:24:05.000000000 
+0200
@@ -1,7 +1,8 @@
 from datetime import timedelta
+
 from django.conf import settings
 from django.test.signals import setting_changed
-from rest_framework.settings import APISettings
+from rest_framework.settings import APISettings, api_settings
 
 USER_SETTINGS = getattr(settings, 'REST_KNOX', None)
 
@@ -14,6 +15,7 @@
     'AUTO_REFRESH': False,
     'MIN_REFRESH_INTERVAL': 60,
     'AUTH_HEADER_PREFIX': 'Token',
+    'EXPIRY_DATETIME_FORMAT': api_settings.DATETIME_FORMAT,
 }
 
 IMPORT_STRINGS = {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-rest-knox-3.6.0/knox/views.py 
new/django-rest-knox-4.1.0/knox/views.py
--- old/django-rest-knox-3.6.0/knox/views.py    2018-12-31 12:58:08.000000000 
+0100
+++ new/django-rest-knox-4.1.0/knox/views.py    2019-06-01 08:24:05.000000000 
+0200
@@ -1,9 +1,9 @@
 from django.contrib.auth.signals import user_logged_in, user_logged_out
 from django.utils import timezone
-
 from rest_framework import status
 from rest_framework.permissions import IsAuthenticated
 from rest_framework.response import Response
+from rest_framework.serializers import DateTimeField
 from rest_framework.settings import api_settings
 from rest_framework.views import APIView
 
@@ -28,30 +28,43 @@
     def get_user_serializer_class(self):
         return knox_settings.USER_SERIALIZER
 
+    def get_expiry_datetime_format(self):
+        return knox_settings.EXPIRY_DATETIME_FORMAT
+
+    def format_expiry_datetime(self, expiry):
+        datetime_format = self.get_expiry_datetime_format()
+        return DateTimeField(format=datetime_format).to_representation(expiry)
+
+    def get_post_response_data(self, request, token, instance):
+        UserSerializer = self.get_user_serializer_class()
+
+        data = {
+            'expiry': self.format_expiry_datetime(instance.expiry),
+            'token': token
+        }
+        if UserSerializer is not None:
+            data["user"] = UserSerializer(
+                request.user,
+                context=self.get_context()
+            ).data
+        return data
+
     def post(self, request, format=None):
         token_limit_per_user = self.get_token_limit_per_user()
         if token_limit_per_user is not None:
             now = timezone.now()
-            token = request.user.auth_token_set.filter(expires__gt=now)
+            token = request.user.auth_token_set.filter(expiry__gt=now)
             if token.count() >= token_limit_per_user:
                 return Response(
                     {"error": "Maximum amount of tokens allowed per user 
exceeded."},
                     status=status.HTTP_403_FORBIDDEN
                 )
         token_ttl = self.get_token_ttl()
-        token = AuthToken.objects.create(request.user, token_ttl)
+        instance, token = AuthToken.objects.create(request.user, token_ttl)
         user_logged_in.send(sender=request.user.__class__,
                             request=request, user=request.user)
-        UserSerializer = self.get_user_serializer_class()
-        if UserSerializer is None:
-            return Response({
-                'token': token
-            })
-        context = self.get_context()
-        return Response({
-            'user': UserSerializer(request.user, context=context).data,
-            'token': token,
-        })
+        data = self.get_post_response_data(request, token, instance)
+        return Response(data)
 
 
 class LogoutView(APIView):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-rest-knox-3.6.0/knox_project/views.py 
new/django-rest-knox-4.1.0/knox_project/views.py
--- old/django-rest-knox-3.6.0/knox_project/views.py    2018-12-31 
12:58:08.000000000 +0100
+++ new/django-rest-knox-4.1.0/knox_project/views.py    2019-06-01 
08:24:05.000000000 +0200
@@ -1,8 +1,9 @@
-from knox.auth import TokenAuthentication
 from rest_framework.permissions import IsAuthenticated
 from rest_framework.response import Response
 from rest_framework.views import APIView
 
+from knox.auth import TokenAuthentication
+
 
 class RootView(APIView):
     authentication_classes = (TokenAuthentication,)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-rest-knox-3.6.0/mkdocs.sh 
new/django-rest-knox-4.1.0/mkdocs.sh
--- old/django-rest-knox-3.6.0/mkdocs.sh        1970-01-01 01:00:00.000000000 
+0100
+++ new/django-rest-knox-4.1.0/mkdocs.sh        2019-06-01 08:24:05.000000000 
+0200
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+set -e
+MOUNT_FOLDER=/app
+MKDOCS_DEV_ADDR=${MKDOCS_DEV_ADDR-"0.0.0.0"}
+MKDOCS_DEV_PORT=${MKDOCS_DEV_PORT-"8000"}
+
+docker run --rm -it \
+    -v $(pwd):$MOUNT_FOLDER \
+    -w $MOUNT_FOLDER \
+    -p $MKDOCS_DEV_PORT:$MKDOCS_DEV_PORT \
+    -e MKDOCS_DEV_ADDR="$MKDOCS_DEV_ADDR:$MKDOCS_DEV_PORT" \
+    squidfunk/mkdocs-material:3.2.0 $*
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-rest-knox-3.6.0/mkdocs.yml 
new/django-rest-knox-4.1.0/mkdocs.yml
--- old/django-rest-knox-3.6.0/mkdocs.yml       2018-12-31 12:58:08.000000000 
+0100
+++ new/django-rest-knox-4.1.0/mkdocs.yml       2019-06-01 08:24:05.000000000 
+0200
@@ -1,7 +1,7 @@
 site_name: Django-Rest-Knox
 repo_url: https://github.com/James1345/django-rest-knox
 theme: readthedocs
-pages:
+nav:
   - Home: 'index.md'
   - Installation: 'installation.md'
   - API Guide:
@@ -9,4 +9,6 @@
       - URLs: 'urls.md'
       - Authentication: 'auth.md'
       - Settings: 'settings.md'
-  - Changes: 'changes.md'
+  - Changelog: 'changelog.md'
+
+dev_addr: !!python/object/apply:os.getenv ["MKDOCS_DEV_ADDR"]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-rest-knox-3.6.0/setup.py 
new/django-rest-knox-4.1.0/setup.py
--- old/django-rest-knox-3.6.0/setup.py 2018-12-31 12:58:08.000000000 +0100
+++ new/django-rest-knox-4.1.0/setup.py 2019-06-01 08:24:05.000000000 +0200
@@ -1,9 +1,10 @@
 # Always prefer setuptools over distutils
-from setuptools import setup, find_packages
 # To use a consistent encoding
 from codecs import open
 from os import path
 
+from setuptools import find_packages, setup
+
 here = path.abspath(path.dirname(__file__))
 
 # Get the long description from the relevant file
@@ -16,7 +17,7 @@
     # Versions should comply with PEP440.  For a discussion on single-sourcing
     # the version across setup.py and the project code, see
     # https://packaging.python.org/en/latest/single_source_version.html
-    version='3.6.0',
+    version='4.1.0',
     description='Authentication for django rest framework',
     long_description=long_description,
     long_description_content_type='text/markdown',
@@ -66,7 +67,7 @@
     # your project is installed. For an analysis of "install_requires" vs pip's
     # requirements files see:
     # https://packaging.python.org/en/latest/requirements.html
-    install_requires=['django', 'djangorestframework', 'pyOpenSSL'],
+    install_requires=['django', 'djangorestframework', 'cryptography'],
 
     # List additional groups of dependencies here (e.g. development
     # dependencies). You can install these using the following syntax,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-rest-knox-3.6.0/tests/tests.py 
new/django-rest-knox-4.1.0/tests/tests.py
--- old/django-rest-knox-3.6.0/tests/tests.py   2018-12-31 12:58:08.000000000 
+0100
+++ new/django-rest-knox-4.1.0/tests/tests.py   2019-06-01 08:24:05.000000000 
+0200
@@ -1,10 +1,19 @@
 import base64
 from datetime import datetime, timedelta
 
-from django.utils.six.moves import reload_module
 from django.contrib.auth import get_user_model
 from django.test import override_settings
+from django.utils.six.moves import reload_module
+from freezegun import freeze_time
+from rest_framework.serializers import DateTimeField
+from rest_framework.test import APIRequestFactory, APITestCase as TestCase
+
 from knox import auth, views
+from knox.auth import TokenAuthentication
+from knox.models import AuthToken
+from knox.serializers import UserSerializer
+from knox.settings import CONSTANTS, knox_settings
+from knox.signals import token_expired
 
 try:
     # For django >= 2.0
@@ -13,19 +22,6 @@
     # For django < 2.0
     from django.conf.urls import reverse
 
-from freezegun import freeze_time
-
-from rest_framework.test import (
-    APIRequestFactory,
-    APITestCase as TestCase
-)
-
-from knox.auth import TokenAuthentication
-from knox.signals import token_expired
-from knox.models import AuthToken
-from knox.settings import CONSTANTS, knox_settings
-from knox.serializers import UserSerializer
-
 User = get_user_model()
 root_url = reverse('api-root')
 
@@ -47,6 +43,13 @@
 auth_header_prefix_knox = knox_settings.defaults.copy()
 auth_header_prefix_knox["AUTH_HEADER_PREFIX"] = 'Baerer'
 
+token_no_expiration_knox = knox_settings.defaults.copy()
+token_no_expiration_knox["TOKEN_TTL"] = None
+
+EXPIRY_DATETIME_FORMAT = '%H:%M %d/%m/%y'
+expiry_datetime_format_knox = knox_settings.defaults.copy()
+expiry_datetime_format_knox["EXPIRY_DATETIME_FORMAT"] = EXPIRY_DATETIME_FORMAT
+
 
 class AuthTestCase(TestCase):
 
@@ -103,10 +106,35 @@
         self.assertIn('user', response.data)
         self.assertIn(username_field, response.data['user'])
 
+    def test_login_returns_configured_expiry_datetime_format(self):
+
+        with override_settings(REST_KNOX=expiry_datetime_format_knox):
+            reload_module(views)
+            self.assertEqual(AuthToken.objects.count(), 0)
+            url = reverse('knox_login')
+            self.client.credentials(
+                HTTP_AUTHORIZATION=get_basic_auth_header(self.username, 
self.password)
+            )
+            response = self.client.post(url, {}, format='json')
+            self.assertEqual(
+                expiry_datetime_format_knox["EXPIRY_DATETIME_FORMAT"],
+                EXPIRY_DATETIME_FORMAT
+            )
+        reload_module(views)
+        self.assertEqual(response.status_code, 200)
+        self.assertIn('token', response.data)
+        self.assertNotIn('user', response.data)
+        self.assertEqual(
+            response.data['expiry'],
+            DateTimeField(format=EXPIRY_DATETIME_FORMAT).to_representation(
+                AuthToken.objects.first().expiry
+            )
+        )
+
     def test_logout_deletes_keys(self):
         self.assertEqual(AuthToken.objects.count(), 0)
         for _ in range(2):
-            token = AuthToken.objects.create(user=self.user)
+            instance, token = AuthToken.objects.create(user=self.user)
         self.assertEqual(AuthToken.objects.count(), 2)
 
         url = reverse('knox_logout')
@@ -118,7 +146,7 @@
     def test_logout_all_deletes_keys(self):
         self.assertEqual(AuthToken.objects.count(), 0)
         for _ in range(10):
-            token = AuthToken.objects.create(user=self.user)
+            instance, token = AuthToken.objects.create(user=self.user)
         self.assertEqual(AuthToken.objects.count(), 10)
 
         url = reverse('knox_logoutall')
@@ -129,7 +157,7 @@
     def test_logout_all_deletes_only_targets_keys(self):
         self.assertEqual(AuthToken.objects.count(), 0)
         for _ in range(10):
-            token = AuthToken.objects.create(user=self.user)
+            instance, token = AuthToken.objects.create(user=self.user)
             AuthToken.objects.create(user=self.user2)
         self.assertEqual(AuthToken.objects.count(), 20)
 
@@ -141,8 +169,8 @@
 
     def test_expired_tokens_login_fails(self):
         self.assertEqual(AuthToken.objects.count(), 0)
-        token = AuthToken.objects.create(
-            user=self.user, expires=timedelta(seconds=0))
+        instance, token = AuthToken.objects.create(
+            user=self.user, expiry=timedelta(seconds=0))
         self.client.credentials(HTTP_AUTHORIZATION=('Token %s' % token))
         response = self.client.post(root_url, {}, format='json')
         self.assertEqual(response.status_code, 401)
@@ -152,8 +180,8 @@
         self.assertEqual(AuthToken.objects.count(), 0)
         for _ in range(10):
             # 0 TTL gives an expired token
-            token = AuthToken.objects.create(
-                user=self.user, expires=timedelta(seconds=0))
+            instance, token = AuthToken.objects.create(
+                user=self.user, expiry=timedelta(seconds=0))
         self.assertEqual(AuthToken.objects.count(), 10)
 
         # Attempting a single logout should delete all tokens
@@ -164,14 +192,15 @@
 
     def test_update_token_key(self):
         self.assertEqual(AuthToken.objects.count(), 0)
-        token = AuthToken.objects.create(self.user)
+        instance, token = AuthToken.objects.create(self.user)
         rf = APIRequestFactory()
         request = rf.get('/')
         request.META = {'HTTP_AUTHORIZATION': 'Token {}'.format(token)}
         (self.user, auth_token) = TokenAuthentication().authenticate(request)
         self.assertEqual(
             token[:CONSTANTS.TOKEN_KEY_LENGTH],
-            auth_token.token_key)
+            auth_token.token_key,
+        )
 
     def test_invalid_token_length_returns_401_code(self):
         invalid_token = "1" * (CONSTANTS.TOKEN_KEY_LENGTH - 1)
@@ -181,7 +210,7 @@
         self.assertEqual(response.data, {"detail": "Invalid token."})
 
     def test_invalid_odd_length_token_returns_401_code(self):
-        token = AuthToken.objects.create(self.user)
+        instance, token = AuthToken.objects.create(self.user)
         odd_length_token = token + '1'
         self.client.credentials(HTTP_AUTHORIZATION=('Token %s' % 
odd_length_token))
         response = self.client.post(root_url, {}, format='json')
@@ -193,9 +222,9 @@
         original_time = datetime(2018, 7, 25, 0, 0, 0, 0)
 
         with freeze_time(original_time):
-            token_key = AuthToken.objects.create(user=self.user)
+            instance, token = AuthToken.objects.create(user=self.user)
 
-        self.client.credentials(HTTP_AUTHORIZATION=('Token %s' % token_key))
+        self.client.credentials(HTTP_AUTHORIZATION=('Token %s' % token))
         five_hours_later = original_time + timedelta(hours=5)
         with override_settings(REST_KNOX=auto_refresh_knox):
             reload_module(auth)  # necessary to reload settings in core code
@@ -205,7 +234,7 @@
         self.assertEqual(response.status_code, 200)
 
         # original expiry date was extended:
-        new_expiry = AuthToken.objects.get().expires
+        new_expiry = AuthToken.objects.get().expiry
         expected_expiry = original_time + ttl + timedelta(hours=5)
         self.assertEqual(new_expiry.replace(tzinfo=None), expected_expiry,
                          "Expiry time should have been extended to {} but is 
{}."
@@ -218,7 +247,7 @@
             self.assertEqual(response.status_code, 200)
 
         # token does not work after new expiry:
-        new_expiry = AuthToken.objects.get().expires
+        new_expiry = AuthToken.objects.get().expiry
         with freeze_time(new_expiry + timedelta(seconds=1)):
             response = self.client.get(root_url, {}, format='json')
             self.assertEqual(response.status_code, 401)
@@ -229,25 +258,25 @@
 
         now = datetime.now()
         with freeze_time(now):
-            token_key = AuthToken.objects.create(user=self.user)
+            instance, token = AuthToken.objects.create(user=self.user)
 
-        original_expiry = AuthToken.objects.get().expires
+        original_expiry = AuthToken.objects.get().expiry
 
-        self.client.credentials(HTTP_AUTHORIZATION=('Token %s' % token_key))
+        self.client.credentials(HTTP_AUTHORIZATION=('Token %s' % token))
         with freeze_time(now + timedelta(hours=1)):
             response = self.client.get(root_url, {}, format='json')
 
         self.assertEqual(response.status_code, 200)
-        self.assertEqual(original_expiry, AuthToken.objects.get().expires)
+        self.assertEqual(original_expiry, AuthToken.objects.get().expiry)
 
     def test_token_expiry_is_not_extended_within_MIN_REFRESH_INTERVAL(self):
         now = datetime.now()
         with freeze_time(now):
-            token_key = AuthToken.objects.create(user=self.user)
+            instance, token = AuthToken.objects.create(user=self.user)
 
-        original_expiry = AuthToken.objects.get().expires
+        original_expiry = AuthToken.objects.get().expiry
 
-        self.client.credentials(HTTP_AUTHORIZATION=('Token %s' % token_key))
+        self.client.credentials(HTTP_AUTHORIZATION=('Token %s' % token))
         in_min_interval = now + 
timedelta(seconds=knox_settings.MIN_REFRESH_INTERVAL - 10)
         with override_settings(REST_KNOX=auto_refresh_knox):
             reload_module(auth)  # necessary to reload settings in core code
@@ -256,7 +285,7 @@
         reload_module(auth)  # necessary to reload settings in core code
 
         self.assertEqual(response.status_code, 200)
-        self.assertEqual(original_expiry, AuthToken.objects.get().expires)
+        self.assertEqual(original_expiry, AuthToken.objects.get().expiry)
 
     def test_expiry_signals(self):
         self.signal_was_called = False
@@ -266,7 +295,7 @@
 
         token_expired.connect(handler)
 
-        token = AuthToken.objects.create(user=self.user, expires=timedelta(0))
+        instance, token = AuthToken.objects.create(user=self.user, 
expiry=timedelta(0))
         self.client.credentials(HTTP_AUTHORIZATION=('Token %s' % token))
         self.client.post(root_url, {}, format='json')
 
@@ -294,7 +323,7 @@
             reload_module(views)
             for _ in range(9):
                 AuthToken.objects.create(user=self.user)
-            AuthToken.objects.create(user=self.user, 
expires=timedelta(seconds=0))
+            AuthToken.objects.create(user=self.user, 
expiry=timedelta(seconds=0))
             # now 10 keys, but only 9 valid so request should succeed.
             url = reverse('knox_login')
             self.client.credentials(
@@ -313,11 +342,57 @@
 
         with override_settings(REST_KNOX=auth_header_prefix_knox):
             reload_module(auth)
-            token = AuthToken.objects.create(user=self.user)
+            instance, token = AuthToken.objects.create(user=self.user)
             self.client.credentials(HTTP_AUTHORIZATION=('Token %s' % token))
             failed_response = self.client.get(root_url)
-            self.client.credentials(HTTP_AUTHORIZATION=('Baerer %s' % token))
+            self.client.credentials(
+                HTTP_AUTHORIZATION=(
+                    'Baerer %s' % token
+                )
+            )
             response = self.client.get(root_url)
         reload_module(auth)
         self.assertEqual(failed_response.status_code, 401)
         self.assertEqual(response.status_code, 200)
+
+    def test_expiry_present_also_when_none(self):
+        with override_settings(REST_KNOX=token_no_expiration_knox):
+            reload_module(views)
+            self.assertEqual(AuthToken.objects.count(), 0)
+            url = reverse('knox_login')
+            self.client.credentials(
+                HTTP_AUTHORIZATION=get_basic_auth_header(self.username, 
self.password)
+            )
+            response = self.client.post(
+                url,
+                {},
+                format='json'
+            )
+            self.assertEqual(token_no_expiration_knox["TOKEN_TTL"], None)
+            self.assertEqual(response.status_code, 200)
+            self.assertIn('token', response.data)
+            self.assertIn('expiry', response.data)
+            self.assertEqual(
+                response.data['expiry'],
+                None
+            )
+        reload_module(views)
+
+    def test_expiry_is_present(self):
+        self.assertEqual(AuthToken.objects.count(), 0)
+        url = reverse('knox_login')
+        self.client.credentials(
+            HTTP_AUTHORIZATION=get_basic_auth_header(self.username, 
self.password)
+        )
+        response = self.client.post(
+            url,
+            {},
+            format='json'
+        )
+        self.assertEqual(response.status_code, 200)
+        self.assertIn('token', response.data)
+        self.assertIn('expiry', response.data)
+        self.assertEqual(
+            response.data['expiry'],
+            DateTimeField().to_representation(AuthToken.objects.first().expiry)
+        )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-rest-knox-3.6.0/tox.ini 
new/django-rest-knox-4.1.0/tox.ini
--- old/django-rest-knox-3.6.0/tox.ini  2018-12-31 12:58:08.000000000 +0100
+++ new/django-rest-knox-4.1.0/tox.ini  2019-06-01 08:24:05.000000000 +0200
@@ -1,8 +1,25 @@
 [tox]
 envlist =
-   py{27,34,35,36}-django111,
-   py{34,35,36}-django20,
-   py{35,36,37}-django21,
+    isort
+    flake8,
+    py{27,34,35,36}-django111,
+    py{34,35,36}-django20,
+    py{35,36,37}-django21,
+    py{35,36,37}-django22,
+
+[testenv:flake8]
+deps = flake8
+changedir = {toxinidir}
+commands = flake8 knox
+
+[testenv:isort]
+deps = isort
+changedir = {toxinidir}
+commands = isort --recursive --check-only --diff \ 
+    knox \
+    knox_project/views.py \
+    setup.py \
+    tests
 
 [testenv]
 commands =
@@ -15,12 +32,14 @@
     django111: Django>=1.11,<2.0
     django20: Django>=2.0,<2.1
     django21: Django>=2.1,<2.2
+    django22: Django>=2.2,<2.3
     django-nose
     markdown<3.0
+    isort
     djangorestframework
     freezegun
     mkdocs
-    pyOpenSSL
+    cryptography
     pytest-django
     setuptools
     twine


Reply via email to