From e1612df79ef98592f4c3f0ea57c87fb8c5ae2148 Mon Sep 17 00:00:00 2001 From: Konstantin Kochin Date: Fri, 12 Oct 2018 09:59:04 +0000 Subject: [PATCH 1/2] Update supported django versions Changes: - drop django support 1.7 or lower. - add support of the django 1.8-2.1 - replace south migration by django migration - move tests into separate directory --- requirements.txt | 1 + rest_framework_digestauth/authentication.py | 39 +++--- rest_framework_digestauth/backends.py | 9 +- .../migrations/0001_initial.py | 70 ++++------ rest_framework_digestauth/models.py | 3 +- rest_framework_digestauth/utils.py | 16 +-- test/conftest.py | 9 ++ test/test_app/__init__.py | 0 testsettings.py => test/test_app/settings.py | 9 +- test/test_app/urls.py | 12 ++ test/test_app/views.py | 7 + .../tests.py => test/test_auth.py | 42 +++--- tox.ini | 128 ++++++++++++++++++ 13 files changed, 244 insertions(+), 101 deletions(-) create mode 100644 test/conftest.py create mode 100644 test/test_app/__init__.py rename testsettings.py => test/test_app/settings.py (53%) create mode 100644 test/test_app/urls.py create mode 100644 test/test_app/views.py rename rest_framework_digestauth/tests.py => test/test_auth.py (86%) create mode 100644 tox.ini diff --git a/requirements.txt b/requirements.txt index 5ccb1b0..a1bb46d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ Django>=1.4.3 djangorestframework>=2.1.15 +six diff --git a/rest_framework_digestauth/authentication.py b/rest_framework_digestauth/authentication.py index 2a7b3c7..9d929ec 100644 --- a/rest_framework_digestauth/authentication.py +++ b/rest_framework_digestauth/authentication.py @@ -1,26 +1,31 @@ -import os import hashlib +import os from importlib import import_module -from django.core.signing import Signer -from django.contrib.auth import get_user_model from django.conf import settings +from django.contrib.auth import get_user_model +from django.core.signing import Signer from django.utils import six - -from rest_framework import exceptions from rest_framework import authentication +from rest_framework import exceptions from rest_framework_digestauth.utils import parse_dict_header +__all__ = ['DigestAuthentication'] + +_User = get_user_model() + + +def _load_backed_class(): + backend_path = getattr( + settings, + 'DIGESTAUTH_BACKEND', + 'rest_framework_digestauth.backends.DatabaseBackend', + ).split('.') + return getattr(import_module('.'.join(backend_path[:-1])), backend_path[-1]) -User = get_user_model() -backend_path = getattr( - settings, - 'DIGESTAUTH_BACKEND', - 'rest_framework_digestauth.backends.DatabaseBackend', -).split('.') -DigestBackend = getattr(import_module('.'.join(backend_path[:-1])), backend_path[-1]) +_DigestBackend = _load_backed_class() class DigestAuthentication(authentication.BaseAuthentication): @@ -46,7 +51,7 @@ def authenticate(self, request): return None self.check_authorization_request_header() user = self.get_user() - self.backend = DigestBackend(user) + self.backend = _DigestBackend(user) password = self.backend.get_password() if self.check_digest_auth(request, password): return user, None @@ -107,11 +112,11 @@ def get_user(self): username = self.auth_header['username'] try: username_field = 'username' - if hasattr(User, 'USERNAME_FIELD'): - username_field = User.USERNAME_FIELD + if hasattr(_User, 'USERNAME_FIELD'): + username_field = _User.USERNAME_FIELD args = {username_field: username} - user = User.objects.get(**args) - except (User.DoesNotExist, User.MultipleObjectsReturned): + user = _User.objects.get(**args) + except (_User.DoesNotExist, _User.MultipleObjectsReturned): raise exceptions.PermissionDenied return user diff --git a/rest_framework_digestauth/backends.py b/rest_framework_digestauth/backends.py index 8e49ffc..966cc1e 100644 --- a/rest_framework_digestauth/backends.py +++ b/rest_framework_digestauth/backends.py @@ -1,13 +1,15 @@ - from rest_framework import exceptions -from rest_framework.authentication import TokenAuthentication +from rest_framework.authtoken.models import Token + from rest_framework_digestauth.models import DigestAuthCounter +__all__ = ['AbstractDigestBackend', 'DatabaseBackend'] + class AbstractDigestBackend(object): def __init__(self, user): - self.user = user # This user is *unauthenticated*, beware. + self.user = user # This user is *unauthenticated*, beware. def get_password(self): """ @@ -35,7 +37,6 @@ def set_counter(self, server_nonce, client_nonce, counter): class DatabaseBackend(AbstractDigestBackend): def get_password(self): - Token = TokenAuthentication.model try: token = Token.objects.get(user=self.user) except (Token.DoesNotExist, diff --git a/rest_framework_digestauth/migrations/0001_initial.py b/rest_framework_digestauth/migrations/0001_initial.py index 0c31603..c76b510 100644 --- a/rest_framework_digestauth/migrations/0001_initial.py +++ b/rest_framework_digestauth/migrations/0001_initial.py @@ -1,44 +1,28 @@ # -*- coding: utf-8 -*- -from south.utils import datetime_utils as datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - - -class Migration(SchemaMigration): - - def forwards(self, orm): - # Adding model 'DigestAuthCounter' - db.create_table(u'rest_framework_digestauth_digestauthcounter', ( - (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('server_nonce', self.gf('django.db.models.fields.TextField')()), - ('client_nonce', self.gf('django.db.models.fields.TextField')()), - ('client_counter', self.gf('django.db.models.fields.IntegerField')(null=True)), - ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), - )) - db.send_create_signal(u'rest_framework_digestauth', ['DigestAuthCounter']) - - # Adding unique constraint on 'DigestAuthCounter', fields ['server_nonce', 'client_nonce'] - db.create_unique(u'rest_framework_digestauth_digestauthcounter', ['server_nonce', 'client_nonce']) - - - def backwards(self, orm): - # Removing unique constraint on 'DigestAuthCounter', fields ['server_nonce', 'client_nonce'] - db.delete_unique(u'rest_framework_digestauth_digestauthcounter', ['server_nonce', 'client_nonce']) - - # Deleting model 'DigestAuthCounter' - db.delete_table(u'rest_framework_digestauth_digestauthcounter') - - - models = { - u'rest_framework_digestauth.digestauthcounter': { - 'Meta': {'unique_together': "(('server_nonce', 'client_nonce'),)", 'object_name': 'DigestAuthCounter'}, - 'client_counter': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), - 'client_nonce': ('django.db.models.fields.TextField', [], {}), - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'server_nonce': ('django.db.models.fields.TextField', [], {}) - } - } - - complete_apps = ['rest_framework_digestauth'] \ No newline at end of file +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='DigestAuthCounter', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('server_nonce', models.TextField()), + ('client_nonce', models.TextField()), + ('client_counter', models.IntegerField(null=True)), + ('created', models.DateTimeField(auto_now_add=True)), + ], + ), + migrations.AlterUniqueTogether( + name='digestauthcounter', + unique_together={('server_nonce', 'client_nonce')}, + ), + ] diff --git a/rest_framework_digestauth/models.py b/rest_framework_digestauth/models.py index 48e9257..a482abd 100644 --- a/rest_framework_digestauth/models.py +++ b/rest_framework_digestauth/models.py @@ -1,6 +1,7 @@ - from django.db import models +__all__ = ['DigestAuthCounter'] + class DigestAuthCounter(models.Model): server_nonce = models.TextField() diff --git a/rest_framework_digestauth/utils.py b/rest_framework_digestauth/utils.py index 8d9a812..d4e2c89 100644 --- a/rest_framework_digestauth/utils.py +++ b/rest_framework_digestauth/utils.py @@ -1,18 +1,6 @@ -import sys +from six.moves.urllib.request import parse_http_list as _parse_list_header - -_ver = sys.version_info - -#: Python 2.x? -is_py2 = (_ver[0] == 2) - -#: Python 3.x? -is_py3 = (_ver[0] == 3) - -if is_py2: - from urllib2 import parse_http_list as _parse_list_header -elif is_py3: - from urllib.request import parse_http_list as _parse_list_header # NOQA +__all__ = ['parse_dict_header', 'unquote_header_value'] # From mitsuhiko/werkzeug (used with permission). diff --git a/test/conftest.py b/test/conftest.py new file mode 100644 index 0000000..af5e58b --- /dev/null +++ b/test/conftest.py @@ -0,0 +1,9 @@ +def pytest_configure(): + import logging + + logging.basicConfig(level=logging.INFO) + + import os + import django + os.environ['DJANGO_SETTINGS_MODULE'] = 'test_app.settings' + django.setup() diff --git a/test/test_app/__init__.py b/test/test_app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/testsettings.py b/test/test_app/settings.py similarity index 53% rename from testsettings.py rename to test/test_app/settings.py index a8446b7..6e2416f 100644 --- a/testsettings.py +++ b/test/test_app/settings.py @@ -12,6 +12,13 @@ 'rest_framework_digestauth', ) -ROOT_URLCONF = 'rest_framework_digestauth.tests' +MIDDLEWARE = [] + +ROOT_URLCONF = 'test_app.urls' SECRET_KEY = 'DIGESTAUTH' + +REST_FRAMEWORK = { + 'DEFAULT_AUTHENTICATION_CLASSES': ['rest_framework_digestauth.authentication.DigestAuthentication'], + "DEFAULT_PERMISSION_CLASSES": ['rest_framework.permissions.IsAuthenticated'] +} diff --git a/test/test_app/urls.py b/test/test_app/urls.py new file mode 100644 index 0000000..ee2c915 --- /dev/null +++ b/test/test_app/urls.py @@ -0,0 +1,12 @@ +try: + # django 2.0 or higher + from django.urls import re_path +except ImportError: + # django 1.11 or lower + from django.conf.urls import url as re_path + +from .views import mock_view + +urlpatterns = [ + re_path(r'^digest-auth/$', mock_view, name='digest_auth') +] diff --git a/test/test_app/views.py b/test/test_app/views.py new file mode 100644 index 0000000..3dc8b12 --- /dev/null +++ b/test/test_app/views.py @@ -0,0 +1,7 @@ +from rest_framework.decorators import api_view +from rest_framework.response import Response + + +@api_view(['GET', 'POST']) +def mock_view(request): + return Response({"test": "test"}) diff --git a/rest_framework_digestauth/tests.py b/test/test_auth.py similarity index 86% rename from rest_framework_digestauth/tests.py rename to test/test_auth.py index c62e903..d3cdc16 100644 --- a/rest_framework_digestauth/tests.py +++ b/test/test_auth.py @@ -1,26 +1,23 @@ import base64 +import hashlib import os import time -import hashlib from django.contrib.auth import get_user_model from django.test import Client, TestCase - from rest_framework import HTTP_HEADER_ENCODING -from rest_framework.tests.test_authentication import MockView from rest_framework.authtoken.models import Token -from rest_framework.compat import patterns -from rest_framework_digestauth.authentication import DigestAuthentication -from rest_framework_digestauth.utils import parse_dict_header from rest_framework_digestauth.backends import DatabaseBackend +from rest_framework_digestauth.utils import parse_dict_header -User = get_user_model() +try: + from django.urls import reverse +except ImportError: + # for django 1.9 or lower + from django.core.urlresolvers import reverse -urlpatterns = patterns( - '', - (r'^digest-auth/$', - MockView.as_view(authentication_classes=[DigestAuthentication]))) +User = get_user_model() def build_basic_header(username, password): @@ -43,6 +40,7 @@ def md5_utf8(x): if isinstance(x, str): x = x.encode('utf-8') return hashlib.md5(x).hexdigest() + hash_utf8 = md5_utf8 KD = lambda s, d: hash_utf8("%s:%s" % (s, d)) @@ -62,7 +60,7 @@ def md5_utf8(x): noncebit = "%s:%s:%s:%s:%s" % (nonce, ncvalue, cnonce, qop, hash_utf8(A2)) respdig = KD(hash_utf8(A1), noncebit) - base = 'username="%s", realm="%s", nonce="%s", uri="%s", '\ + base = 'username="%s", realm="%s", nonce="%s", uri="%s", ' \ 'response="%s", algorithm="MD5"' base = base % (username, realm, nonce, path, respdig) @@ -88,14 +86,16 @@ def setUp(self): self.key = 'abcd1234' self.token = Token.objects.create(key=self.key, user=self.user) + self.mock_view_url = reverse('digest_auth') + def test_challenge(self): - response = self.csrf_client.post('/digest-auth/', + response = self.csrf_client.post(self.mock_view_url, {'example': 'example'}) self.assertEqual(response.status_code, 401) self.assertTrue('WWW-Authenticate' in response) def test_access(self): - response = self.csrf_client.post('/digest-auth/', + response = self.csrf_client.post(self.mock_view_url, {'example': 'example'}) self.assertEqual(response.status_code, 401) self.assertTrue('WWW-Authenticate' in response) @@ -104,13 +104,13 @@ def test_access(self): response['WWW-Authenticate'], 'POST', '/digest-auth/') - response = self.csrf_client.post('/digest-auth/', + response = self.csrf_client.post(self.mock_view_url, {'example': 'example'}, HTTP_AUTHORIZATION=auth) self.assertEqual(response.status_code, 200) def test_replay_attack(self): - response = self.csrf_client.post('/digest-auth/') + response = self.csrf_client.post(self.mock_view_url) self.assertEqual(response.status_code, 401) self.assertTrue('WWW-Authenticate' in response) @@ -124,18 +124,18 @@ def test_replay_attack(self): } auth = build_digest_header(**auth_kwargs) - response = self.csrf_client.post('/digest-auth/', + response = self.csrf_client.post(self.mock_view_url, HTTP_AUTHORIZATION=auth) self.assertEqual(response.status_code, 200) - response = self.csrf_client.post('/digest-auth/', + response = self.csrf_client.post(self.mock_view_url, HTTP_AUTHORIZATION=auth) self.assertEqual(response.status_code, 401) self.assertTrue('WWW-Authenticate' in response) for nonce_count in range(2, 4): auth = build_digest_header(nonce_count=nonce_count, **auth_kwargs) - response = self.csrf_client.post('/digest-auth/', + response = self.csrf_client.post(self.mock_view_url, HTTP_AUTHORIZATION=auth) self.assertEqual(response.status_code, 200) @@ -160,10 +160,10 @@ def test_database_backend(self): def test_basic_access(self): """Test if a basic access attempt results in another 401.""" - response = self.csrf_client.post('/digest-auth/', + response = self.csrf_client.post(self.mock_view_url, {'example': 'example'}) auth = build_basic_header('john', 'abcd1234') - response = self.csrf_client.post('/digest-auth/', + response = self.csrf_client.post(self.mock_view_url, {'example': 'example'}, HTTP_AUTHORIZATION=auth) self.assertEqual(response.status_code, 401) diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..6f38995 --- /dev/null +++ b/tox.ini @@ -0,0 +1,128 @@ +# Tox (http://tox.testrun.org/) is a tool for running tests +# in multiple virtualenvs. This configuration file will run the +# test suite on all supported python versions. To use it, "pip install tox" +# and then run "tox" from this directory. + +[lib] +python_package_namespace = rest_framework_digestauth +python_package_folder = rest_framework_digestauth +top_package_folder = rest_framework_digestauth +default_python_version_short = py36 + + +[tox] +envlist = py{27,36}-django{18,19,110,111},py36-django{20,21} + + +[coverage] +prepare_commands = + /usr/bin/env bash -c '[ -f {toxinidir}/.coveragerc ] || printf "[run]\nbranch=True" > {toxinidir}/.coveragerc' + + +[testenv] +deps = + pyhamcrest + mock + coverage + py27: pylint<2 + !py27: pylint + flake8 + pytest + pytest-cov + pytest-timeout + pytest-django + django21: django>=2.1,<2.2 + django20: django>=2.0,<2.1 + django111: django>=1.11,<1.12 + django110: django>=1.10,<1.11 + django19: django>=1.9,<1.10 + django18: django>=1.8,<1.9 + django{18,19}: djangorestframework<3.7 +install_command = pip install {opts} {packages} +# This is the main testing command, intended for comprehensive testing. +# It's supposed to fail (exit code != 0) if tests don't pass +commands = + pytest {posargs:\ + {envsitepackagesdir}/{[lib]python_package_folder} \ + test \ + --durations=10} + + +[testenv:check_code] +envdir = {toxworkdir}/{[lib]default_python_version_short} +# This is a command performing differents check of the code, +# and saving results in files for further processing (visualization, for inst.). +# It is designed to ignore errors, and should not be used for testing per se. +commands = + {[coverage]prepare_commands} + pytest \ + {[lib]python_package_folder} \ + test \ + --doctest-modules \ + --junit-xml={toxinidir}nosetests.xml \ + --cov={[lib]python_package_namespace} \ + --cov-report xml:{toxinidir}/coverage.xml + pylint -f parseable --files-output=y {[lib]top_package_folder} + flake8 --output-file=flake8.txt {[lib]top_package_folder} +ignore_errors = True +ignore_outcome = True + + +[testenv:doctests] +envdir = {toxworkdir}/{[lib]default_python_version_short} +commands = + pytest \ + {[lib]python_package_folder} \ + --doctest-modules \ + --durations=10 + + +[testenv:test_fast] +envdir = {toxworkdir}/{[lib]default_python_version_short} +commands = + pytest -v \ + test \ + -m "not slow" \ + --durations=10 +ignore_outcome = True + + +[testenv:test_slow] +envdir = {toxworkdir}/{[lib]default_python_version_short} +commands = + pytest \ + -m slow \ + --durations=0 +ignore_outcome = True + + +[testenv:pylint] +envdir = {toxworkdir}/{[lib]default_python_version_short} +commands = + pylint {[lib]top_package_folder} +ignore_outcome = True + + +[testenv:flake8] +envdir = {toxworkdir}/{[lib]default_python_version_short} +commands = + flake8 {[lib]top_package_folder} +ignore_outcome = True + + +[testenv:coverage_html] +envdir = {toxworkdir}/{[lib]default_python_version_short} +commands = + {[coverage]prepare_commands} + pytest \ + {[lib]python_package_folder} \ + test \ + --doctest-modules \ + --cov={[lib]python_package_namespace} \ + --cov-report html:{toxinidir}/coverage_html +ignore_outcome = True + + +[flake8] +max-line-length = 120 + From 9b071c7b93f457374f8b924990a0c474227eef4b Mon Sep 17 00:00:00 2001 From: Konstantin Kochin Date: Fri, 12 Oct 2018 09:59:05 +0000 Subject: [PATCH 2/2] Update .travis.ci and setup.py Changes: - simplify setup.py script - updated .travis.ci matrix - updated changelog --- .travis.yml | 39 ++++++++----- README.md | 12 +++- requirements.txt | 3 - rest_framework_digestauth/__init__.py | 2 +- rest_framework_digestauth/_version.py | 1 + setup.py | 82 ++++++--------------------- 6 files changed, 57 insertions(+), 82 deletions(-) delete mode 100644 requirements.txt create mode 100644 rest_framework_digestauth/_version.py diff --git a/.travis.yml b/.travis.yml index 033b51b..cd94988 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,28 @@ language: python -python: - - "2.6" - - "2.7" - - "3.3" -env: - - DJANGO_VERSION=1.3 - - DJANGO_VERSION=1.4 - - DJANGO_VERSION=1.5 - - DJANGO_VERSION=1.6 +matrix: + include: + - python: 2.7 + env: TOXENV=py27-django18 + - python: 2.7 + env: TOXENV=py27-django19 + - python: 2.7 + env: TOXENV=py27-django110 + - python: 2.7 + env: TOXENV=py27-django111 + + - python: 3.6 + env: TOXENV=py36-django18 + - python: 3.6 + env: TOXENV=py36-django19 + - python: 3.6 + env: TOXENV=py36-django110 + - python: 3.6 + env: TOXENV=py36-django111 + - python: 3.6 + env: TOXENV=py36-django20 + - python: 3.6 + env: TOXENV=py36-django21 + install: - - if [[ $TRAVIS_PYTHON_VERSION == 2.6 ]]; then pip install --use-mirrors importlib unittest2; fi - - pip install -q Django==$DJANGO_VERSION --use-mirrors - - pip install -q -r requirements.txt --use-mirrors -script: python manage.py test rest_framework_digestauth + - pip install tox +script: tox diff --git a/README.md b/README.md index e49289e..19de650 100644 --- a/README.md +++ b/README.md @@ -63,10 +63,20 @@ Once you've implimented a new backend, you can use it with the `DIGESTAUTH_BACKE ## Running the tests To run the tests against the current environment: - $ ./manage.py test rest_framework_digestauth + $ pytest test + +The tests with different environments can be run using tox: + + $ tox ## Changelog +### Unreleased + +- Drop support of the django 1.6 or lower +- Add support of the django 1.8-2.1 +- Switch from *south* to *django* migration system + ### 1.1.0 **31th Jan 2014** diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index a1bb46d..0000000 --- a/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -Django>=1.4.3 -djangorestframework>=2.1.15 -six diff --git a/rest_framework_digestauth/__init__.py b/rest_framework_digestauth/__init__.py index 1a72d32..8dee4bf 100644 --- a/rest_framework_digestauth/__init__.py +++ b/rest_framework_digestauth/__init__.py @@ -1 +1 @@ -__version__ = '1.1.0' +from ._version import __version__ diff --git a/rest_framework_digestauth/_version.py b/rest_framework_digestauth/_version.py new file mode 100644 index 0000000..1a72d32 --- /dev/null +++ b/rest_framework_digestauth/_version.py @@ -0,0 +1 @@ +__version__ = '1.1.0' diff --git a/setup.py b/setup.py index 445b9ce..61c6600 100644 --- a/setup.py +++ b/setup.py @@ -1,79 +1,33 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from __future__ import print_function - -from setuptools import setup -import re -import os -import sys - - -def get_version(package): - """ - Return package version as listed in `__version__` in `init.py`. - """ - init_py = open(os.path.join(package, '__init__.py')).read() - return re.search( - "^__version__ = ['\"]([^'\"]+)['\"]", - init_py, re.MULTILINE).group(1) - - -def get_packages(package): - """ - Return root package and all sub-packages. - """ - return [dirpath - for dirpath, dirnames, filenames in os.walk(package) - if os.path.exists(os.path.join(dirpath, '__init__.py'))] - - -def get_package_data(package): - """ - Return all files under the root package, that are not in a - package themselves. - """ - walk = [(dirpath.replace(package + os.sep, '', 1), filenames) - for dirpath, dirnames, filenames in os.walk(package) - if not os.path.exists(os.path.join(dirpath, '__init__.py'))] - - filepaths = [] - for base, filenames in walk: - filepaths.extend([os.path.join(base, filename) - for filename in filenames]) - return {package: filepaths} - - -package = 'rest_framework_digestauth' -version = get_version(package) -install_requires = open('requirements.txt').read().split('\n') - -try: - import importlib -except ImportError: - install_requires.append('importlib') - - -if sys.argv[-1] == 'publish': - os.system("python setup.py sdist upload") - args = {'version': version} - print("You probably want to also tag the version now:") - print(" git tag -a %(version)s -m 'version %(version)s'" % args) - print(" git push --tags") - sys.exit() +from setuptools import setup, find_packages +# Version info -- read without importing +_locals = {} +with open('rest_framework_digestauth/_version.py') as fp: + exec(fp.read(), None, _locals) +__version__ = _locals['__version__'] setup( name='djangorestframework-digestauth', - version=version, + version=__version__, url='http://github.com/juanriaza/django-rest-framework-digestauth', license='BSD', description='Digest HTTP auth support for Django REST framework', author='Juan Riaza', author_email='juanriaza@gmail.com', - packages=get_packages(package), - package_data=get_package_data(package), - install_requires=install_requires, + packages=find_packages(include="rest_framework_digestauth.*"), + include_package_data=True, + install_requires=[ + 'Django>=1.7', + 'djangorestframework', + 'six' + ], + tests_require=[ + 'pytest', + 'pytest-django' + ], classifiers=[ 'Development Status :: 4 - Beta', 'Environment :: Web Environment',