Sfoglia il codice sorgente

HUE-3287 [core] Django 1.11 upgrade
- Upgrading django-axes-1.5.0 => django-axes-2.2.0 module

Prakash Ranade 8 anni fa
parent
commit
9fd49bd3ad
53 ha cambiato i file con 1544 aggiunte e 800 eliminazioni
  1. 0 11
      desktop/core/ext-py/django-axes-1.5.0/.gitignore
  2. 0 22
      desktop/core/ext-py/django-axes-1.5.0/.travis.yml
  3. 0 8
      desktop/core/ext-py/django-axes-1.5.0/MANIFEST.in
  4. 0 13
      desktop/core/ext-py/django-axes-1.5.0/Makefile
  5. 0 10
      desktop/core/ext-py/django-axes-1.5.0/axes/__init__.py
  6. 0 13
      desktop/core/ext-py/django-axes-1.5.0/axes/management/commands/axes_list_attempts.py
  7. 0 37
      desktop/core/ext-py/django-axes-1.5.0/axes/middleware.py
  8. 0 6
      desktop/core/ext-py/django-axes-1.5.0/axes/test_urls.py
  9. 0 20
      desktop/core/ext-py/django-axes-1.5.0/axes/utils.py
  10. 0 1
      desktop/core/ext-py/django-axes-1.5.0/axes/views.py
  11. 0 31
      desktop/core/ext-py/django-axes-1.5.0/examples/README.rst
  12. 0 0
      desktop/core/ext-py/django-axes-1.5.0/examples/example/foo/__init__.py
  13. 0 0
      desktop/core/ext-py/django-axes-1.5.0/examples/example/foo/management/__init__.py
  14. 0 0
      desktop/core/ext-py/django-axes-1.5.0/examples/example/foo/management/commands/__init__.py
  15. 0 41
      desktop/core/ext-py/django-axes-1.5.0/examples/example/foo/management/commands/axes_create_test_data.py
  16. 0 0
      desktop/core/ext-py/django-axes-1.5.0/examples/example/foo/models.py
  17. 0 26
      desktop/core/ext-py/django-axes-1.5.0/examples/example/local_settings.example
  18. 0 10
      desktop/core/ext-py/django-axes-1.5.0/examples/example/manage.py
  19. 0 242
      desktop/core/ext-py/django-axes-1.5.0/examples/example/settings.py
  20. 0 17
      desktop/core/ext-py/django-axes-1.5.0/examples/example/urls.py
  21. 0 32
      desktop/core/ext-py/django-axes-1.5.0/examples/example/wsgi.py
  22. 0 7
      desktop/core/ext-py/django-axes-1.5.0/examples/install.sh
  23. 0 3
      desktop/core/ext-py/django-axes-1.5.0/examples/reinstall.sh
  24. 0 4
      desktop/core/ext-py/django-axes-1.5.0/examples/test.sh
  25. 0 5
      desktop/core/ext-py/django-axes-1.5.0/examples/uninstall.sh
  26. 16 0
      desktop/core/ext-py/django-axes-2.2.0/.travis.yml
  27. 104 1
      desktop/core/ext-py/django-axes-2.2.0/CHANGES.txt
  28. 0 0
      desktop/core/ext-py/django-axes-2.2.0/LICENSE
  29. 4 0
      desktop/core/ext-py/django-axes-2.2.0/MANIFEST.in
  30. 216 0
      desktop/core/ext-py/django-axes-2.2.0/Makefile
  31. 568 0
      desktop/core/ext-py/django-axes-2.2.0/PKG-INFO
  32. 77 32
      desktop/core/ext-py/django-axes-2.2.0/README.rst
  33. 7 0
      desktop/core/ext-py/django-axes-2.2.0/axes/__init__.py
  34. 0 2
      desktop/core/ext-py/django-axes-2.2.0/axes/admin.py
  35. 11 0
      desktop/core/ext-py/django-axes-2.2.0/axes/apps.py
  36. 133 168
      desktop/core/ext-py/django-axes-2.2.0/axes/decorators.py
  37. 0 0
      desktop/core/ext-py/django-axes-2.2.0/axes/management/__init__.py
  38. 0 0
      desktop/core/ext-py/django-axes-2.2.0/axes/management/commands/__init__.py
  39. 16 0
      desktop/core/ext-py/django-axes-2.2.0/axes/management/commands/axes_list_attempts.py
  40. 5 4
      desktop/core/ext-py/django-axes-2.2.0/axes/management/commands/axes_reset.py
  41. 51 0
      desktop/core/ext-py/django-axes-2.2.0/axes/migrations/0001_initial.py
  42. 54 0
      desktop/core/ext-py/django-axes-2.2.0/axes/migrations/0002_auto_20151217_2044.py
  43. 59 0
      desktop/core/ext-py/django-axes-2.2.0/axes/migrations/0003_auto_20160322_0929.py
  44. 0 0
      desktop/core/ext-py/django-axes-2.2.0/axes/migrations/__init__.py
  45. 9 1
      desktop/core/ext-py/django-axes-2.2.0/axes/models.py
  46. 3 10
      desktop/core/ext-py/django-axes-2.2.0/axes/signals.py
  47. 23 2
      desktop/core/ext-py/django-axes-2.2.0/axes/test_settings.py
  48. 16 0
      desktop/core/ext-py/django-axes-2.2.0/axes/test_urls.py
  49. 105 13
      desktop/core/ext-py/django-axes-2.2.0/axes/tests.py
  50. 41 0
      desktop/core/ext-py/django-axes-2.2.0/axes/utils.py
  51. 15 0
      desktop/core/ext-py/django-axes-2.2.0/runtests.py
  52. 5 0
      desktop/core/ext-py/django-axes-2.2.0/setup.cfg
  53. 6 8
      desktop/core/ext-py/django-axes-2.2.0/setup.py

+ 0 - 11
desktop/core/ext-py/django-axes-1.5.0/.gitignore

@@ -1,11 +0,0 @@
-django_axes.egg-info
-*.pyc
-build
-dist
-.hg
-.DS_Store
-examples/db/
-examples/logs/
-examples/media/
-examples/static/
-examples/example/local_settings.py

+ 0 - 22
desktop/core/ext-py/django-axes-1.5.0/.travis.yml

@@ -1,22 +0,0 @@
-language: python
-
-python:
-  - 2.7
-  - 3.3
-
-env:
-  - DJANGO=Django==1.4.15
-  - DJANGO=Django==1.5.10
-  - DJANGO=Django==1.6.7
-  - DJANGO=Django==1.7
-
-install:
-  - pip install -q $DJANGO
-
-script:
-  - PYTHONPATH=$PYTHONPATH:$PWD django-admin.py test axes --settings=axes.test_settings
-
-matrix:
-    exclude:
-        - python: 3.3
-          env: DJANGO=Django==1.4.15

+ 0 - 8
desktop/core/ext-py/django-axes-1.5.0/MANIFEST.in

@@ -1,8 +0,0 @@
-include LICENSE README.rst CHANGES.txt
-recursive-include axes *.py
-include .travis.yml
-recursive-include examples *.example
-recursive-include examples *.py
-recursive-include examples *.rst
-recursive-include examples *.sh
-include Makefile

+ 0 - 13
desktop/core/ext-py/django-axes-1.5.0/Makefile

@@ -1,13 +0,0 @@
-all: clean check-manifest pyroma viewdoc
-clean:
-	if [ -e .long-description.html ]; then rm .long-description.html ; fi
-check-manifest:
-	check-manifest
-viewdoc:
-	viewdoc
-pyroma:
-	pyroma .
-testrelease:
-	python setup.py sdist --format=zip upload -r test
-release:
-	python setup.py sdist --format=zip upload

+ 0 - 10
desktop/core/ext-py/django-axes-1.5.0/axes/__init__.py

@@ -1,10 +0,0 @@
-try:
-    __version__ = __import__('pkg_resources').get_distribution(
-        'django-axes'
-    ).version
-except:
-    __version__ = ''
-
-
-def get_version():
-    return __version__

+ 0 - 13
desktop/core/ext-py/django-axes-1.5.0/axes/management/commands/axes_list_attempts.py

@@ -1,13 +0,0 @@
-from django.core.management.base import BaseCommand
-from django.core.management.base import CommandError
-
-from axes.models import AccessAttempt
-
-class Command(BaseCommand):
-    args = ''
-    help = ("List login attempts")
-
-    def handle(self, *args, **kwargs):
-        for at in  AccessAttempt.objects.all():
-            print ("%s %s %s" % (at.ip_address,  at.username, at.failures))
-

+ 0 - 37
desktop/core/ext-py/django-axes-1.5.0/axes/middleware.py

@@ -1,37 +0,0 @@
-from django.conf import settings
-from django.contrib.auth import views as auth_views
-
-from axes.decorators import watch_login
-
-
-class FailedLoginMiddleware(object):
-    def __init__(self, *args, **kwargs):
-        super(FailedLoginMiddleware, self).__init__(*args, **kwargs)
-
-        # watch the auth login
-        auth_views.login = watch_login(auth_views.login)
-
-
-class ViewDecoratorMiddleware(object):
-    """
-    When the django_axes middleware is installed, by default it watches the
-    django.auth.views.login.
-
-    This middleware allows adding protection to other views without the need
-    to change any urls or dectorate them manually.
-
-    Add this middleware to your MIDDLEWARE settings after
-    `axes.middleware.FailedLoginMiddleware` and before the django
-    flatpages middleware.
-    """
-    watched_logins = getattr(
-        settings, 'AXES_PROTECTED_LOGINS', (
-            '/accounts/login/',
-        )
-    )
-
-    def process_view(self, request, view_func, view_args, view_kwargs):
-        if request.path in self.watched_logins:
-            return watch_login(view_func)(request, *view_args, **view_kwargs)
-
-        return None

+ 0 - 6
desktop/core/ext-py/django-axes-1.5.0/axes/test_urls.py

@@ -1,6 +0,0 @@
-from django.conf.urls import patterns, include
-from django.contrib import admin
-
-urlpatterns = patterns('',
-    (r'^admin/', include(admin.site.urls)),
-)

+ 0 - 20
desktop/core/ext-py/django-axes-1.5.0/axes/utils.py

@@ -1,20 +0,0 @@
-from axes.models import AccessAttempt
-
-
-def reset(ip=None, username=None):
-    """Reset records that match ip or username, and
-    return the count of removed attempts.
-    """
-    count = 0
-
-    attempts = AccessAttempt.objects.all()
-    if ip:
-        attempts = attempts.filter(ip_address=ip)
-    if username:
-        attempts = attempts.filter(username=username)
-
-    if attempts:
-        count = attempts.count()
-        attempts.delete()
-
-    return count

+ 0 - 1
desktop/core/ext-py/django-axes-1.5.0/axes/views.py

@@ -1 +0,0 @@
-# Create your views here.

+ 0 - 31
desktop/core/ext-py/django-axes-1.5.0/examples/README.rst

@@ -1,31 +0,0 @@
-================================
-Example project for django-axes
-================================
-
-Installation
-================================
-1. Run the install.sh script:
-
-    $ ./install.sh
-
-2. Run the server:
-
-    $ ./manage.py runserver
-
-3. Try the app:
-
-There are two admin accounts created:
-
-- admin:test
-- test:test
-
-Open the http://localhost:8000/admin/axes/accessattempt/ URL and log in using admin:admin.
-
-In another browser open http://localhost:8000/admin/ URL and try to log in using test:1 (wrong
-password). After your 3-rd wrong login attempt, your account would be locked out.
-
-Testing
-================================
-To test the app in an easy way do as follows:
-
-    $ ./test.sh

+ 0 - 0
desktop/core/ext-py/django-axes-1.5.0/examples/example/foo/__init__.py


+ 0 - 0
desktop/core/ext-py/django-axes-1.5.0/examples/example/foo/management/__init__.py


+ 0 - 0
desktop/core/ext-py/django-axes-1.5.0/examples/example/foo/management/commands/__init__.py


+ 0 - 41
desktop/core/ext-py/django-axes-1.5.0/examples/example/foo/management/commands/axes_create_test_data.py

@@ -1,41 +0,0 @@
-from __future__ import print_function
-
-from django.core.management.base import BaseCommand
-
-from django.contrib.auth.models import User
-
-def create_admin_user(username, password):
-    """
-    Create a user for testing the admin.
-
-    :param string username:
-    :param strring password:
-    """
-    u = User()
-    u.username = username
-    u.email = '{0}@dev.mail.example.com'.format(username)
-    u.is_superuser = True
-    u.is_staff = True
-    u.set_password(password)
-
-    try:
-        u.save()
-        print("Created user {0} with password {1}.".format(username, password))
-    except Exception as e:
-        #print("Failed to create user {0} with password {1}. Reason: {2}".format(username, password, str(e)))
-        pass
-
-class Command(BaseCommand):
-    def handle(self, *args, **options):
-        """
-        Creates test data.
-        """
-        try:
-            create_admin_user('admin', 'test')
-        except Exception as e:
-            pass
-
-        try:
-            create_admin_user('test', 'test')
-        except Exception as e:
-            pass

+ 0 - 0
desktop/core/ext-py/django-axes-1.5.0/examples/example/foo/models.py


+ 0 - 26
desktop/core/ext-py/django-axes-1.5.0/examples/example/local_settings.example

@@ -1,26 +0,0 @@
-import os
-PROJECT_DIR = lambda base : os.path.abspath(os.path.join(os.path.dirname(__file__), base).replace('\\','/'))
-
-DEBUG = True
-DEBUG_TOOLBAR = not True
-TEMPLATE_DEBUG = DEBUG
-
-DATABASES = {
-    'default': {
-        'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
-        'NAME': PROJECT_DIR('../db/example.db'),                      # Or path to database file if using sqlite3.
-
-        # The following settings are not used with sqlite3:
-        'USER': '',
-        'PASSWORD': '',
-        'HOST': '',                      # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP.
-        'PORT': '',                      # Set to empty string for default.
-    }
-}
-
-INTERNAL_IPS = ('127.0.0.1',)
-
-EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
-EMAIL_FILE_PATH = PROJECT_DIR('../tmp')
-
-DEFAULT_FROM_EMAIL = '<no-reply@example.com>'

+ 0 - 10
desktop/core/ext-py/django-axes-1.5.0/examples/example/manage.py

@@ -1,10 +0,0 @@
-#!/usr/bin/env python
-import os
-import sys
-
-if __name__ == "__main__":
-    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
-
-    from django.core.management import execute_from_command_line
-
-    execute_from_command_line(sys.argv)

+ 0 - 242
desktop/core/ext-py/django-axes-1.5.0/examples/example/settings.py

@@ -1,242 +0,0 @@
-# Django settings for example project.
-import os
-PROJECT_DIR = lambda base : os.path.abspath(os.path.join(os.path.dirname(__file__), base).replace('\\','/'))
-gettext = lambda s: s
-
-DEBUG = False
-DEBUG_TOOLBAR = False
-TEMPLATE_DEBUG = DEBUG
-
-ADMINS = (
-    # ('Your Name', 'your_email@example.com'),
-)
-
-MANAGERS = ADMINS
-
-DATABASES = {
-    'default': {
-        'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
-        'NAME': PROJECT_DIR('../db/example.db'),                      # Or path to database file if using sqlite3.
-        # The following settings are not used with sqlite3:
-        'USER': '',
-        'PASSWORD': '',
-        'HOST': '',                      # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP.
-        'PORT': '',                      # Set to empty string for default.
-    }
-}
-
-# Hosts/domain names that are valid for this site; required if DEBUG is False
-# See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
-ALLOWED_HOSTS = []
-
-# Local time zone for this installation. Choices can be found here:
-# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
-# although not all choices may be available on all operating systems.
-# In a Windows environment this must be set to your system time zone.
-TIME_ZONE = 'America/Chicago'
-
-# Language code for this installation. All choices can be found here:
-# http://www.i18nguy.com/unicode/language-identifiers.html
-#LANGUAGE_CODE = 'en-us'
-
-SITE_ID = 1
-
-# If you set this to False, Django will make some optimizations so as not
-# to load the internationalization machinery.
-USE_I18N = True
-
-# If you set this to False, Django will not format dates, numbers and
-# calendars according to the current locale.
-USE_L10N = True
-
-# If you set this to False, Django will not use timezone-aware datetimes.
-USE_TZ = True
-
-# Absolute filesystem path to the directory that will hold user-uploaded files.
-# Example: "/var/www/example.com/media/"
-MEDIA_ROOT = PROJECT_DIR(os.path.join('..', 'media'))
-
-# URL that handles the media served from MEDIA_ROOT. Make sure to use a
-# trailing slash.
-# Examples: "http://example.com/media/", "http://media.example.com/"
-MEDIA_URL = '/media/'
-
-# Absolute path to the directory static files should be collected to.
-# Don't put anything in this directory yourself; store your static files
-# in apps' "static/" subdirectories and in STATICFILES_DIRS.
-# Example: "/var/www/example.com/static/"
-STATIC_ROOT = PROJECT_DIR(os.path.join('..', 'static'))
-
-# URL prefix for static files.
-# Example: "http://example.com/static/", "http://static.example.com/"
-STATIC_URL = '/static/'
-
-# Additional locations of static files
-STATICFILES_DIRS = (
-    # Put strings here, like "/home/html/static" or "C:/www/django/static".
-    # Always use forward slashes, even on Windows.
-    # Don't forget to use absolute paths, not relative paths.
-    PROJECT_DIR(os.path.join('..', 'media', 'static')),
-)
-
-# List of finder classes that know how to find static files in
-# various locations.
-STATICFILES_FINDERS = (
-    'django.contrib.staticfiles.finders.FileSystemFinder',
-    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
-#    'django.contrib.staticfiles.finders.DefaultStorageFinder',
-)
-
-# Make this unique, and don't share it with anybody.
-SECRET_KEY = '6sf18c*w971i8a-m^1coasrmur2k6+q5_kyn*)s@(*_dk5q3&r'
-
-# List of callables that know how to import templates from various sources.
-TEMPLATE_LOADERS = (
-    'django.template.loaders.filesystem.Loader',
-    'django.template.loaders.app_directories.Loader',
-    'django.template.loaders.eggs.Loader',
-)
-
-MIDDLEWARE_CLASSES = (
-    'django.contrib.sessions.middleware.SessionMiddleware',
-    'django.middleware.common.CommonMiddleware',
-    'django.middleware.csrf.CsrfViewMiddleware',
-    'django.contrib.auth.middleware.AuthenticationMiddleware',
-    'django.contrib.messages.middleware.MessageMiddleware',
-    # Uncomment the next line for simple clickjacking protection:
-    # 'django.middleware.clickjacking.XFrameOptionsMiddleware',
-    'axes.middleware.FailedLoginMiddleware'
-)
-
-ROOT_URLCONF = 'urls'
-
-# Python dotted path to the WSGI application used by Django's runserver.
-WSGI_APPLICATION = 'wsgi.application'
-
-TEMPLATE_CONTEXT_PROCESSORS = (
-    "django.contrib.auth.context_processors.auth",
-    "django.core.context_processors.debug",
-    "django.core.context_processors.i18n",
-    "django.core.context_processors.media",
-    "django.core.context_processors.static",
-    "django.core.context_processors.tz",
-    "django.contrib.messages.context_processors.messages",
-    "django.core.context_processors.request"
-)
-
-TEMPLATE_DIRS = (
-    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
-    # Always use forward slashes, even on Windows.
-    # Don't forget to use absolute paths, not relative paths.
-    PROJECT_DIR('templates')
-)
-
-INSTALLED_APPS = (
-    # Django core and contrib apps
-    'django.contrib.auth',
-    'django.contrib.contenttypes',
-    'django.contrib.sessions',
-    'django.contrib.sites',
-    'django.contrib.messages',
-    'django.contrib.staticfiles',
-    'django.contrib.admin',
-    'django.contrib.sitemaps',
-
-    'axes',
-
-    # Test app
-    'foo',
-)
-
-LOGIN_REDIRECT_URL = '/admin'
-
-# ******************** django-axes settings *********************
-# Max number of login attemts within the ``AXES_COOLOFF_TIME``
-AXES_LOGIN_FAILURE_LIMIT = 3
-
-from datetime import timedelta
-AXES_COOLOFF_TIME=timedelta(seconds = 200)
-# ******************** /django-axes settings *********************
-
-# A sample logging configuration. The only tangible logging
-# performed by this configuration is to send an email to
-# the site admins on every HTTP 500 error when DEBUG=False.
-# See http://docs.djangoproject.com/en/dev/topics/logging for
-# more details on how to customize your logging configuration.
-LOGGING = {
-    'version': 1,
-    'disable_existing_loggers': False,
-    'filters': {
-        'require_debug_false': {
-            '()': 'django.utils.log.RequireDebugFalse'
-        }
-    },
-    'formatters': {
-        'verbose': {
-            'format': '%(levelname)s %(asctime)s [%(pathname)s:%(lineno)s] %(message)s'
-        },
-        'simple': {
-            'format': '%(levelname)s %(message)s'
-        },
-    },
-    'handlers': {
-        'mail_admins': {
-            'level': 'ERROR',
-            'filters': ['require_debug_false'],
-            'class': 'django.utils.log.AdminEmailHandler'
-        },
-        'console': {
-            'level': 'DEBUG',
-            'class': 'logging.StreamHandler',
-            'formatter': 'verbose'
-        },
-        'django_log': {
-            'level':'DEBUG',
-            'class':'logging.handlers.RotatingFileHandler',
-            'filename': PROJECT_DIR("../logs/django.log"),
-            'maxBytes': 1048576,
-            'backupCount': 99,
-            'formatter': 'verbose',
-        },
-        'axes_log': {
-            'level':'DEBUG',
-            'class':'logging.handlers.RotatingFileHandler',
-            'filename': PROJECT_DIR("../logs/axes.log"),
-            'maxBytes': 1048576,
-            'backupCount': 99,
-            'formatter': 'verbose',
-        },
-    },
-    'loggers': {
-        'django': {
-            'handlers': ['django_log'],
-            'level': 'ERROR',
-            'propagate': True,
-        },
-        'axes': {
-            'handlers': ['console', 'axes_log'],
-            'level': 'DEBUG',
-            'propagate': True,
-        },
-    },
-}
-
-# Do not put any settings below this line
-try:
-    from local_settings import *
-except:
-    pass
-
-if DEBUG and DEBUG_TOOLBAR:
-    # debug_toolbar
-    MIDDLEWARE_CLASSES += (
-        'debug_toolbar.middleware.DebugToolbarMiddleware',
-	)
-
-    INSTALLED_APPS += (
-        'debug_toolbar',
-    )
-
-    DEBUG_TOOLBAR_CONFIG = {
-        'INTERCEPT_REDIRECTS': False,
-    }

+ 0 - 17
desktop/core/ext-py/django-axes-1.5.0/examples/example/urls.py

@@ -1,17 +0,0 @@
-from django.conf.urls import patterns, include, url
-
-from django.conf import settings
-from django.contrib import admin
-from django.contrib.staticfiles.urls import staticfiles_urlpatterns
-from django.conf.urls.static import static
-
-admin.autodiscover()
-
-urlpatterns = patterns('',
-    # Uncomment the next line to enable the admin:
-    url(r'^admin/', include(admin.site.urls)),
-)
-
-if settings.DEBUG:
-    urlpatterns += staticfiles_urlpatterns()
-    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

+ 0 - 32
desktop/core/ext-py/django-axes-1.5.0/examples/example/wsgi.py

@@ -1,32 +0,0 @@
-"""
-WSGI config for example project.
-
-This module contains the WSGI application used by Django's development server
-and any production WSGI deployments. It should expose a module-level variable
-named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
-this application via the ``WSGI_APPLICATION`` setting.
-
-Usually you will have the standard Django WSGI application here, but it also
-might make sense to replace the whole Django WSGI application with a custom one
-that later delegates to the Django one. For example, you could introduce WSGI
-middleware here, or combine a Django application with an application of another
-framework.
-
-"""
-import os
-
-# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks
-# if running multiple sites in the same mod_wsgi process. To fix this, use
-# mod_wsgi daemon mode with each site in its own daemon process, or use
-# os.environ["DJANGO_SETTINGS_MODULE"] = "example.settings"
-os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings")
-
-# This application object is used by any WSGI server configured to use this
-# file. This includes Django's development server, if the WSGI_APPLICATION
-# setting points here.
-from django.core.wsgi import get_wsgi_application
-application = get_wsgi_application()
-
-# Apply WSGI middleware here.
-# from helloworld.wsgi import HelloWorldApplication
-# application = HelloWorldApplication(application)

+ 0 - 7
desktop/core/ext-py/django-axes-1.5.0/examples/install.sh

@@ -1,7 +0,0 @@
-pip install django==1.5.5
-pip install django-axes
-mkdir -p logs db media media/static
-cp example/local_settings.example example/local_settings.py
-python example/manage.py collectstatic --noinput
-python example/manage.py syncdb --noinput
-python example/manage.py axes_create_test_data

+ 0 - 3
desktop/core/ext-py/django-axes-1.5.0/examples/reinstall.sh

@@ -1,3 +0,0 @@
-reset
-./uninstall.sh
-./install.sh

+ 0 - 4
desktop/core/ext-py/django-axes-1.5.0/examples/test.sh

@@ -1,4 +0,0 @@
-reset
-./uninstall.sh
-./install.sh
-python example/manage.py test axes --traceback

+ 0 - 5
desktop/core/ext-py/django-axes-1.5.0/examples/uninstall.sh

@@ -1,5 +0,0 @@
-pip uninstall django-axes -y
-rm build -rf
-rm dist -rf
-rm django_axes.egg-info -rf
-rm django-axes.egg-info -rf

+ 16 - 0
desktop/core/ext-py/django-axes-2.2.0/.travis.yml

@@ -0,0 +1,16 @@
+language: python
+
+python:
+  - 2.7
+  - 3.5
+
+env:
+  - DJANGO="Django>=1.8,<1.9"
+  - DJANGO="Django>=1.9,<1.10"
+  - DJANGO="Django --pre"
+
+install:
+  - pip install -q $DJANGO
+
+script:
+  - ./runtests.py

+ 104 - 1
desktop/core/ext-py/django-axes-1.5.0/CHANGES.txt → desktop/core/ext-py/django-axes-2.2.0/CHANGES.txt

@@ -1,6 +1,109 @@
 Changes
 =======
 
+2.2.0 (2016-07-20)
+------------------
+
+- Improve the logic when using a reverse proxy to avoid possible attacks.
+  [camilonova]
+
+
+2.1.0 (2016-07-14)
+------------------
+
+- Add `default_app_config` so you can just use `axes` in `INSTALLED_APPS`
+  [vdboor]
+
+
+2.0.0 (2016-06-24)
+------------------
+
+- Removed middleware to use app_config
+  [camilonova]
+
+- Lots of cleaning
+  [camilonova]
+
+- Improved test suite and versions
+  [camilonova]
+
+
+1.7.0 (2016-06-10)
+------------------
+
+- Use render shortcut for rendering LOCKOUT_TEMPLATE
+  [Radosław Luter]
+
+- Added app_label for RemovedInDjango19Warning
+  [yograterol]
+
+- Add iso8601 translator.
+  [mullakhmetov]
+
+- Edit json response. Context now contains ISO 8601 formatted cooloff time
+  [mullakhmetov]
+
+- Add json response and iso8601 tests.
+  [mullakhmetov]
+
+- Fixes issue 162: UnicodeDecodeError on pip install
+  [joeribekker]
+
+- Added AXES_NEVER_LOCKOUT_WHITELIST option to prevent certain IPs from being locked out.
+  [joeribekker]
+
+
+1.6.1 (2016-05-13)
+------------------
+
+- Fixes whitelist check when BEHIND_REVERSE_PROXY
+  [Patrick Hagemeister]
+
+- Made migrations py3 compatible
+  [mvdwaeter]
+
+- Fixing #126, possibly breaking compatibility with Django<=1.7
+  [int-ua]
+
+- Add note for upgrading users about new migration files
+  [kelseyq]
+
+- Fixes #148
+  [camilonova]
+
+- Decorate auth_views.login only once
+  [teeberg]
+
+- Set IP public/private classifier to be compliant with RFC 1918.
+  [SilasX]
+
+- Issue #155. Lockout response status code changed to 403.
+  [Артур Муллахметов]
+
+- BUGFIX: Missing migration
+  [smeinel]
+
+1.6.0 (2016-01-07)
+------------------
+
+- Stopped using render_to_response so that other template engines work
+  [tarkatronic]
+
+- Improved performance & DoS prevention on query2str
+  [tarkatronic]
+
+- Immediately return from is_already_locked if the user is not lockable
+  [jdunck]
+
+- Iterate over ip addresses only once
+  [annp89]
+
+- added initial migration files to support django 1.7 &up. Upgrading users should run migrate --fake-initial after update
+  [ibaguio]
+
+- Add db indexes to CommonAccess model
+  [Schweigi]
+
 1.5.0 (2015-09-11)
 ------------------
 
@@ -101,7 +204,7 @@ Changes
 
 - Fixes object type issue, response is not an string
   [camilonova]
-    
+
 - Python 3 compatibility fix for db_reset
   [nicois]
 

+ 0 - 0
desktop/core/ext-py/django-axes-1.5.0/LICENSE → desktop/core/ext-py/django-axes-2.2.0/LICENSE


+ 4 - 0
desktop/core/ext-py/django-axes-2.2.0/MANIFEST.in

@@ -0,0 +1,4 @@
+include LICENSE README.rst CHANGES.txt
+recursive-include axes *.py
+include .travis.yml
+include Makefile

+ 216 - 0
desktop/core/ext-py/django-axes-2.2.0/Makefile

@@ -0,0 +1,216 @@
+# https://github.com/aclark4life/project-makefile
+#
+# The MIT License (MIT)
+#
+# Copyright (c) 2016 Alex Clark
+#
+# 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.
+
+.DEFAULT_GOAL=git-commit-edit-push
+
+APP=app
+MESSAGE="Update"
+PROJECT=project
+TMP:=$(shell echo `tmp`)
+
+commit: git-commit-auto-push
+co: git-checkout-branches
+db: django-migrate django-su
+db-clean: django-db-clean-postgres
+django-start: django-init
+fe-init: npm-init npm-install grunt-init grunt-serve
+fe: npm-install grunt-serve
+freeze: python-pip-freeze
+heroku: heroku-push
+install: python3.5-virtualenv python-pip-install
+lint: python-flake python-yapf python-wc
+migrate: django-migrate
+push: git-push
+plone-start: plone-init
+readme: python-package-readme-test
+release: python-package-release
+releasetest: python-package-release-test
+serve: python-serve
+sphinx-start: sphinx-init
+static: django-static
+test: django-test
+vm: vagrant-up
+vm-down: vagrant-suspend
+
+
+# Django
+django-db-clean-postgres:
+	-dropdb $(PROJECT)-$(APP)
+	-createdb $(PROJECT)-$(APP)
+django-db-clean-sqlite:
+	-rm -f $(PROJECT)-$(APP).sqlite3
+django-init:
+	-mkdir -p $(PROJECT)/$(APP)
+	-django-admin startproject $(PROJECT) .
+	-django-admin startapp $(APP) $(PROJECT)/$(APP)
+django-install:
+	$(MAKE) python-virtualenv
+	bin/pip install Django
+django-migrate:
+	python manage.py migrate
+django-migrations:
+	python manage.py makemigrations $(APP)
+django-migrations-clean:
+	rm -rf $(PROJECT)/$(APP)/migrations
+	$(MAKE) django-migrations
+django-serve:
+	python manage.py runserver
+django-test:
+	python manage.py test
+django-shell:
+	python manage.py shell
+django-static:
+	python manage.py collectstatic --noinput
+django-su:
+	python manage.py createsuperuser
+
+# Git
+REMOTE_BRANCHES=`git branch -a |\
+	grep remote |\
+	grep -v HEAD |\
+	grep -v master`
+git-checkout-branches:
+	-for i in $(REMOTE_BRANCHES) ; do \
+        git checkout -t $$i ; done
+git-commit-auto-push:
+	git commit -a -m $(MESSAGE)
+	$(MAKE) git-push
+git-commit-edit-push:
+	git commit -a
+	$(MAKE) git-push
+git-push:
+	git push
+
+# Heroku
+heroku-debug-on:
+	heroku config:set DEBUG=1
+heroku-debug-off:
+	heroku config:unset DEBUG
+heroku-push:
+	git push heroku
+heroku-shell:
+	heroku run bash
+
+# Misc
+help:
+	@echo "\nPlease run \`make\` with one of these targets:\n"
+	@$(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F:\
+        '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}'\
+        | sort | egrep -v -e '^[^[:alnum:]]' -e '^$@$$' | xargs | tr ' ' '\n' | awk\
+        '{print "    - "$$0}'
+	@echo "\n"
+review:
+	open -a "Sublime Text 2" `find $(PROJECT) -name \*.py | grep -v __init__.py`\
+        `find $(PROJECT) -name \*.html`
+
+# Node
+npm-init:
+	npm init
+npm-install:
+	npm install
+grunt-init:
+	npm install grunt
+	grunt-init Gruntfile
+grunt-serve:
+	grunt serve
+
+# Plone
+plone-heroku:
+	-@createuser -s plone > /dev/null 2>&1
+	-@createdb -U plone plone > /dev/null 2>&1
+	@export PORT=8080 && \
+		export USERNAME=admin && \
+		export PASSWORD=admin && \
+		bin/buildout -c heroku.cfg
+plone-init:
+	plock --force --no-cache --no-virtualenv .
+plone-install:
+	$(MAKE) install
+	bin/buildout
+plone-db-sync:
+	bin/buildout -c database.cfg
+plone-serve:
+	@echo "Zope about to handle requests here:\n\n\thttp://localhost:8080\n"
+	@bin/plone fg
+
+# Python
+python-clean-pyc:
+	find . -name \*.pyc | xargs rm -v
+python-flake:
+	-flake8 *.py
+	-flake8 $(PROJECT)/*.py
+	-flake8 $(PROJECT)/$(APP)/*.py
+python-package-check:
+	check-manifest
+	pyroma .
+python-package-readme-test:
+	rst2html.py README.rst > readme.html; open readme.html
+python-package-release:
+	python setup.py sdist --format=gztar,zip upload
+python-package-release-test:
+	python setup.py sdist --format=gztar,zip upload -r test
+python-package-test:
+	python setup.py test
+python-pip-freeze:
+	bin/pip freeze | sort > $(TMP)/requirements.txt
+	mv -f $(TMP)/requirements.txt .
+python-pip-install:
+	bin/pip install -r requirements.txt
+python-serve:
+	@echo "\n\tServing HTTP on http://0.0.0.0:8000\n"
+	python -m SimpleHTTPServer
+python-virtualenv:
+	virtualenv .
+python-yapf:
+	-yapf -i *.py
+	-yapf -i -e $(PROJECT)/urls.py $(PROJECT)/*.py
+	-yapf -i $(PROJECT)/$(APP)/*.py
+python-wc:
+	-wc -l *.py
+	-wc -l $(PROJECT)/*.py
+	-wc -l $(PROJECT)/$(APP)/*.py
+
+# Sphinx
+sphinx-init:
+	sphinx-quickstart -q -p "Python Project" -a "Alex Clark" -v 0.0.1 doc
+sphinx-serve:
+	@echo "\nServing HTTP on http://0.0.0.0:8085 ...\n"
+	pushd _build/html; python -m SimpleHTTPServer 8085; popd
+
+# Vagrant
+vagrant-box-update:
+	vagrant box update
+vagrant-clean:
+	vagrant destroy
+vagrant-down:
+	vagrant suspend
+vagrant-init:
+	vagrant init ubuntu/trusty64
+	vagrant up --provider virtualbox
+vagrant-up:
+	vagrant up --provision
+
+# Django-axes
+python3.5-virtualenv:
+	virtualenv-3.5 .

+ 568 - 0
desktop/core/ext-py/django-axes-2.2.0/PKG-INFO

@@ -0,0 +1,568 @@
+Metadata-Version: 1.1
+Name: django-axes
+Version: 2.2.0
+Summary: Keep track of failed login attempts in Django-powered sites.
+Home-page: https://github.com/django-pci/django-axes
+Author: Alex Clark
+Author-email: aclark@aclark.net
+License: MIT
+Description: Django Axes
+        ===========
+        
+        .. image:: https://secure.travis-ci.org/django-pci/django-axes.png?branch=master
+            :alt: Build Status
+            :target: http://travis-ci.org/django-pci/django-axes
+        
+        ``django-axes`` is a very simple way for you to keep track of failed login
+        attempts, both for the Django admin and for the rest of your site. The name is
+        sort of a geeky pun, since ``axes`` can be read interpreted as:
+        
+        * "access", as in monitoring access attempts
+        * "axes", as in tools you can use hack (generally on wood). In this case,
+          however, the "hacking" part of it can be taken a bit further: ``django-axes``
+          is intended to help you *stop* people from hacking (popular media
+          definition) your website. Hilarious, right? That's what I thought too!
+        
+        
+        Requirements
+        ============
+        
+        ``django-axes`` requires a supported Django version. The application is
+        intended to work around the Django admin and the regular
+        ``django.contrib.auth`` login-powered pages.
+        Look at https://www.djangoproject.com/download/ to check if your version
+        is supported.
+        
+        
+        Installation
+        ============
+        
+        You can install the latest stable package running this command::
+        
+            $ pip install django-axes
+        
+        
+        Development
+        ===========
+        
+        You can contribute to this project forking it from github and sending pull requests.
+        
+        Running tests
+        -------------
+        
+        Clone the repository and install the django version you want. Then run::
+        
+            $ ./runtests.py
+        
+        
+        Configuration
+        =============
+        
+        Just add `axes` to your ``INSTALLED_APPS``::
+        
+            INSTALLED_APPS = (
+                'django.contrib.admin',
+                'django.contrib.auth',
+                'django.contrib.contenttypes',
+                'django.contrib.sessions',
+                'django.contrib.sites',
+                ...
+                'axes',
+                ...
+            )
+        
+        Remember to run ``python manage.py migrate`` to sync the database.
+        
+        
+        Customizing Axes
+        ----------------
+        
+        You have a couple options available to you to customize ``django-axes`` a bit.
+        These should be defined in your ``settings.py`` file.
+        
+        * ``AXES_LOGIN_FAILURE_LIMIT``: The number of login attempts allowed before a
+          record is created for the failed logins.  Default: ``3``
+        * ``AXES_LOCK_OUT_AT_FAILURE``: After the number of allowed login attempts
+          are exceeded, should we lock out this IP (and optional user agent)?
+          Default: ``True``
+        * ``AXES_USE_USER_AGENT``: If ``True``, lock out / log based on an IP address
+          AND a user agent.  This means requests from different user agents but from
+          the same IP are treated differently.  Default: ``False``
+        * ``AXES_COOLOFF_TIME``: If set, defines a period of inactivity after which
+          old failed login attempts will be forgotten. Can be set to a python
+          timedelta object or an integer. If an integer, will be interpreted as a
+          number of hours.  Default: ``None``
+        * ``AXES_LOGGER``: If set, specifies a logging mechanism for axes to use.
+          Default: ``'axes.watch_login'``
+        * ``AXES_LOCKOUT_TEMPLATE``: If set, specifies a template to render when a
+          user is locked out. Template receives cooloff_time and failure_limit as
+          context variables. Default: ``None``
+        * ``AXES_LOCKOUT_URL``: If set, specifies a URL to redirect to on lockout. If
+          both AXES_LOCKOUT_TEMPLATE and AXES_LOCKOUT_URL are set, the template will
+          be used. Default: ``None``
+        * ``AXES_VERBOSE``: If ``True``, you'll see slightly more logging for Axes.
+          Default: ``True``
+        * ``AXES_USERNAME_FORM_FIELD``: the name of the form field that contains your
+          users usernames. Default: ``username``
+        * ``AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP``: If ``True`` prevents to login
+          from IP under particular user if attempts limit exceed, otherwise lock out
+          based on IP.
+          Default: ``False``
+        * ``AXES_NEVER_LOCKOUT_WHITELIST``: If ``True``, users can always login from
+          whitelisted IP addresses.
+          Default: ``False``
+        * ``AXES_BEHIND_REVERSE_PROXY``: If ``True``, it will look for the IP address from the header defined at ``AXES_REVERSE_PROXY_HEADER``. Please make sure if you enable this setting to configure your proxy to set the correct value for the header, otherwise you could be attacked by setting this header directly in every request.
+          Default: ``False``
+        * ``AXES_REVERSE_PROXY_HEADER``: If ``AXES_BEHIND_REVERSE_PROXY`` is ``True``, it will look for the IP address from this header.
+          Default: ``HTTP_X_FORWARDED_FOR``
+        
+        
+        Usage
+        =====
+        
+        Using ``django-axes`` is extremely simple. All you need to do is periodically
+        check the Access Attempts section of the admin.
+        
+        By default, django-axes will lock out repeated attempts from the same IP
+        address. You can allow this IP to attempt again by deleting the relevant
+        ``AccessAttempt`` records in the admin.
+        
+        You can also use the ``axes_reset`` management command using Django's
+        ``manage.py``.
+        
+        * ``manage.py axes_reset`` will reset all lockouts and access records.
+        * ``manage.py axes_reset ip`` will clear lockout/records for ip
+        
+        In your code, you can use ``from axes.utils import reset``.
+        
+        * ``reset()`` will reset all lockouts and access records.
+        * ``reset(ip=ip)`` will clear lockout/records for ip
+        * ``reset(username=username)`` will clear lockout/records for a username
+        
+        
+        Issues
+        ======
+        
+        Not being locked out after failed attempts
+        ------------------------------------------
+        
+        You may find that Axes is not capturing your failed login attempts. It may
+        be that you need to manually add watch_login to your login url.
+        
+        For example, in your urls.py::
+        
+            ...
+            from my.custom.app import login
+            from axes.decorators import watch_login
+            ...
+            urlpatterns = patterns('',
+                (r'^login/$', watch_login(login)),
+            ...
+        
+        
+        Locked out without reason
+        -------------------------
+        
+        It may happen that you have suddenly become locked out without a single failed
+        attempt. One possible reason is that you are using some custom login form and the
+        username field is named something different than "username", e.g. "email". This
+        leads to all users attempts being lumped together. To fix this add the following
+        to your settings:
+        
+            AXES_USERNAME_FORM_FIELD = "email"
+        
+        
+        Using a captcha
+        ===============
+        
+        Using https://github.com/mbi/django-simple-captcha you do the following:
+        
+        1. Change axes lockout url in ``settings.py``::
+        
+            AXES_LOCKOUT_URL = '/locked'
+        
+        2. Add the url in ``urls.py``::
+        
+            url(r'^locked/$', locked_out, name='locked_out'),
+        
+        3. Create a captcha form::
+        
+            class AxesCaptchaForm(forms.Form):
+                captcha = CaptchaField()
+        
+        4. Create a captcha view for the above url that resets on captcha success and redirects::
+        
+            def locked_out(request):
+                if request.POST:
+                    form = AxesCaptchaForm(request.POST)
+                    if form.is_valid():
+                        ip = get_ip_address_from_request(request)
+                        reset(ip=ip)
+                        return HttpResponseRedirect(reverse_lazy('signin'))
+                else:
+                    form = AxesCaptchaForm()
+        
+                return render_to_response('locked_out.html', dict(form=form), context_instance=RequestContext(request))
+        
+        5. Add a captcha template::
+        
+            <form action="" method="post">
+                {% csrf_token %}
+        
+                {{ form.captcha.errors }}
+                {{ form.captcha }}
+        
+                <div class="form-actions">
+                    <input type="submit" value="Submit" />
+                </div>
+            </form>
+        
+        Done.
+        
+        Changes
+        =======
+        
+        2.2.0 (2016-07-20)
+        ------------------
+        
+        - Improve the logic when using a reverse proxy to avoid possible attacks.
+          [camilonova]
+        
+        
+        2.1.0 (2016-07-14)
+        ------------------
+        
+        - Add `default_app_config` so you can just use `axes` in `INSTALLED_APPS`
+          [vdboor]
+        
+        
+        2.0.0 (2016-06-24)
+        ------------------
+        
+        - Removed middleware to use app_config
+          [camilonova]
+        
+        - Lots of cleaning
+          [camilonova]
+        
+        - Improved test suite and versions
+          [camilonova]
+        
+        
+        1.7.0 (2016-06-10)
+        ------------------
+        
+        - Use render shortcut for rendering LOCKOUT_TEMPLATE
+          [Radosław Luter]
+        
+        - Added app_label for RemovedInDjango19Warning
+          [yograterol]
+        
+        - Add iso8601 translator.
+          [mullakhmetov]
+        
+        - Edit json response. Context now contains ISO 8601 formatted cooloff time
+          [mullakhmetov]
+        
+        - Add json response and iso8601 tests.
+          [mullakhmetov]
+        
+        - Fixes issue 162: UnicodeDecodeError on pip install
+          [joeribekker]
+        
+        - Added AXES_NEVER_LOCKOUT_WHITELIST option to prevent certain IPs from being locked out.
+          [joeribekker]
+        
+        
+        1.6.1 (2016-05-13)
+        ------------------
+        
+        - Fixes whitelist check when BEHIND_REVERSE_PROXY
+          [Patrick Hagemeister]
+        
+        - Made migrations py3 compatible
+          [mvdwaeter]
+        
+        - Fixing #126, possibly breaking compatibility with Django<=1.7
+          [int-ua]
+        
+        - Add note for upgrading users about new migration files
+          [kelseyq]
+        
+        - Fixes #148
+          [camilonova]
+        
+        - Decorate auth_views.login only once
+          [teeberg]
+        
+        - Set IP public/private classifier to be compliant with RFC 1918.
+          [SilasX]
+        
+        - Issue #155. Lockout response status code changed to 403.
+          [Артур Муллахметов]
+        
+        - BUGFIX: Missing migration
+          [smeinel]
+        
+        1.6.0 (2016-01-07)
+        ------------------
+        
+        - Stopped using render_to_response so that other template engines work
+          [tarkatronic]
+        
+        - Improved performance & DoS prevention on query2str
+          [tarkatronic]
+        
+        - Immediately return from is_already_locked if the user is not lockable
+          [jdunck]
+        
+        - Iterate over ip addresses only once
+          [annp89]
+        
+        - added initial migration files to support django 1.7 &up. Upgrading users should run migrate --fake-initial after update
+          [ibaguio]
+        
+        - Add db indexes to CommonAccess model
+          [Schweigi]
+        
+        1.5.0 (2015-09-11)
+        ------------------
+        
+        - Fix #_get_user_attempts to include username when filtering AccessAttempts if AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP is True
+          [afioca]
+        
+        1.4.0 (2015-08-09)
+        ------------------
+        
+        - Send the user_locked_out signal. Fixes #94.
+          [toabi]
+        
+        1.3.9 (2015-02-11)
+        ------------------
+        
+        - Python 3 fix (#104)
+        
+        1.3.8 (2014-10-07)
+        ------------------
+        
+        - Rename GitHub organization from django-security to django-pci to emphasize focus on providing assistance with building PCI compliant websites with Django.
+          [aclark4life]
+        
+        1.3.7 (2014-10-05)
+        ------------------
+        
+        - Explain common issues where Axes fails silently
+          [cericoda]
+        
+        - Allow for user-defined username field for lookup in POST data
+          [SteveByerly]
+        
+        - Log out only if user was logged in
+          [zoten]
+        
+        - Support for floats in cooloff time (i.e: 0.1 == 6 minutes)
+          [marianov]
+        
+        - Limit amount of POST data logged (#73). Limiting the length of value is not enough, as there could be arbitrary number of them, or very long key names.
+          [peterkuma]
+        
+        - Improve get_ip to try for real ip address
+          [7wonders]
+        
+        - Change IPAddressField to GenericIPAddressField. When using a PostgreSQL database and the client does not pass an IP address you get an inet error. This is a known problem with PostgreSQL and the IPAddressField. https://code.djangoproject.com/ticket/5622. It can be fixed by using a GenericIPAddressField instead.
+          [polvoblanco]
+        
+        - Get first X-Forwarded-For IP
+          [tutumcloud]
+        
+        - White listing IP addresses behind reverse proxy. Allowing some IP addresses to have direct access to the app even if they are behind a reverse proxy. Those IP addresses must still be on a white list.
+          [ericbulloch]
+        
+        - Reduce logging of reverse proxy IP lookup and use configured logger. Fixes #76. Instead of logging the notice that django.axes looks for a HTTP header set by a reverse proxy on each attempt, just log it one-time on first module import. Also use the configured logger (by default axes.watch_login) for the message to be more consistent in logging.
+          [eht16]
+        
+        - Limit the length of the values logged into the database. Refs #73
+          [camilonova]
+        
+        - Refactored tests to be more stable and faster
+          [camilonova]
+        
+        - Clean client references
+          [camilonova]
+        
+        - Fixed admin login url
+          [camilonova]
+        
+        - Added django 1.7 for testing
+          [camilonova]
+        
+        - Travis file cleanup
+          [camilonova]
+        
+        - Remove hardcoded url path
+          [camilonova]
+        
+        - Fixing tests for django 1.7
+          [Andrew-Crosio]
+        
+        - Fix for django 1.7 exception not existing
+          [Andrew-Crosio]
+        
+        - Removed python 2.6 from testing
+          [camilonova]
+        
+        - Use django built-in six version
+          [camilonova]
+        
+        - Added six as requirement
+          [camilonova]
+        
+        - Added python 2.6 for travis testing
+          [camilonova]
+        
+        - Replaced u string literal prefixes with six.u() calls
+          [amrhassan]
+        
+        - Fixes object type issue, response is not an string
+          [camilonova]
+        
+        - Python 3 compatibility fix for db_reset
+          [nicois]
+        
+        - Added example project and helper scripts
+          [barseghyanartur]
+        
+        - Admin command to list login attemps
+          [marianov]
+        
+        - Replaced six imports with django.utils.six ones
+          [amrhassan]
+        
+        - Replaced u string literal prefixes with six.u() calls to make it compatible with Python 3.2
+          [amrhassan]
+        
+        - Replaced `assertIn`s and `assertNotIn`s with `assertContains` and `assertNotContains`
+          [fcurella]
+        
+        - Added py3k to travis
+          [fcurella]
+        
+        - Update test cases to be python3 compatible
+          [nicois]
+        
+        - Python 3 compatibility fix for db_reset
+          [nicois]
+        
+        - Removed trash from example urls
+          [barseghyanartur]
+        
+        - Added django installer
+          [barseghyanartur]
+        
+        - Added example project and helper scripts
+          [barseghyanartur]
+        
+        
+        1.3.6 (2013-11-23)
+        ------------------
+        
+        - Added AttributeError in case get_profile doesn't exist [camilonova]
+        - Improved axes_reset command [camilonova]
+        
+        
+        1.3.5 (2013-11-01)
+        ------------------
+        
+        - Fix an issue with __version__ loading the wrong version [graingert]
+        
+        
+        1.3.4 (2013-11-01)
+        ------------------
+        
+        - Update README.rst for PyPI [marty] [camilonova] [graingert]
+        - Add cooloff period [visualspace]
+        
+        
+        1.3.3 (2013-07-05)
+        ------------------
+        
+        - Added 'username' field to the Admin table [bkvirendra]
+        - Removed fallback logging creation since logging cames by default on django 1.4 or later, if you don't have it is because you explicitly wanted. Fixes #45 [camilonova]
+        
+        
+        1.3.2 (2013-03-28)
+        ------------------
+        
+        - Fix an issue when a user logout [camilonova]
+        - Match pypi version [camilonova]
+        - Better User model import method [camilonova]
+        - Use only one place to get the version number [camilonova]
+        - Fixed an issue when a user on django 1.4 logout [camilonova]
+        - Handle exception if there is not user profile model set [camilonova]
+        - Made some cleanup and remove a pokemon exception handling [camilonova]
+        - Improved tests so it really looks for the rabbit in the hole [camilonova]
+        - Match pypi version [camilonova]
+        
+        
+        1.3.1 (2013-03-19)
+        ------------------
+        
+        - Add support for Django 1.5 [camilonova]
+        
+        
+        1.3.0 (2013-02-27)
+        ------------------
+        
+        - Bug fix: get_version() format string [csghormley]
+        
+        
+        1.2.9 (2013-02-20)
+        ------------------
+        
+        - Add to and improve test cases [camilonova]
+        
+        
+        1.2.8 (2013-01-23)
+        ------------------
+        
+        - Increased http accept header length [jslatts]
+        
+        
+        1.2.7 (2013-01-17)
+        ------------------
+        
+        - Reverse proxy support [rmagee]
+        - Clean up README [martey]
+        
+        
+        1.2.6 (2012-12-04)
+        ------------------
+        
+        - Remove unused import [aclark4life]
+        
+        
+        1.2.5 (2012-11-28)
+        ------------------
+        
+        - Fix setup.py [aclark4life]
+        - Added ability to flag user accounts as unlockable. [kencochrane]
+        - Added ipaddress as a param to the user_locked_out signal. [kencochrane]
+        - Added a signal receiver for user_logged_out. [kencochrane]
+        - Added a signal for when a user gets locked out. [kencochrane]
+        - Added AccessLog model to log all access attempts. [kencochrane]
+        
+Keywords: authentication,django,pci,security
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Web Environment
+Classifier: Framework :: Django
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: System Administrators
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Topic :: Internet :: Log Analysis
+Classifier: Topic :: Security
+Classifier: Topic :: System :: Logging

+ 77 - 32
desktop/core/ext-py/django-axes-1.5.0/README.rst → desktop/core/ext-py/django-axes-2.2.0/README.rst

@@ -6,22 +6,24 @@ Django Axes
     :target: http://travis-ci.org/django-pci/django-axes
 
 ``django-axes`` is a very simple way for you to keep track of failed login
-attempts, both for the Django admin and for the rest of your site.  The name is
+attempts, both for the Django admin and for the rest of your site. The name is
 sort of a geeky pun, since ``axes`` can be read interpreted as:
 
 * "access", as in monitoring access attempts
-* "axes", as in tools you can use hack (generally on wood).  In this case,
+* "axes", as in tools you can use hack (generally on wood). In this case,
   however, the "hacking" part of it can be taken a bit further: ``django-axes``
   is intended to help you *stop* people from hacking (popular media
-  definition) your website.  Hilarious, right?  That's what I thought too!
+  definition) your website. Hilarious, right? That's what I thought too!
 
 
 Requirements
 ============
 
-``django-axes`` requires Django 1.5 or later. The application is intended to
-work around the Django admin and the regular ``django.contrib.auth``
-login-powered pages.
+``django-axes`` requires a supported Django version. The application is
+intended to work around the Django admin and the regular
+``django.contrib.auth`` login-powered pages.
+Look at https://www.djangoproject.com/download/ to check if your version
+is supported.
 
 
 Installation
@@ -31,9 +33,6 @@ You can install the latest stable package running this command::
 
     $ pip install django-axes
 
-Also you can install the development version running this command::
-
-    $ pip install -e git+http://github.com/django-pci/django-axes.git#egg=django_axes-dev
 
 Development
 ===========
@@ -43,16 +42,15 @@ You can contribute to this project forking it from github and sending pull reque
 Running tests
 -------------
 
-Tests can be run, after you clone the repository and having django installed, like::
+Clone the repository and install the django version you want. Then run::
 
-    $ PYTHONPATH=$PYTHONPATH:$PWD django-admin.py test axes --settings=axes.test_settings
+    $ ./runtests.py
 
 
 Configuration
 =============
 
-First of all, you must add this project to your list of ``INSTALLED_APPS`` in
-``settings.py``::
+Just add `axes` to your ``INSTALLED_APPS``::
 
     INSTALLED_APPS = (
         'django.contrib.admin',
@@ -65,17 +63,8 @@ First of all, you must add this project to your list of ``INSTALLED_APPS`` in
         ...
     )
 
-Next, install the ``FailedLoginMiddleware`` middleware::
-
-    MIDDLEWARE_CLASSES = (
-        'django.middleware.common.CommonMiddleware',
-        'django.contrib.sessions.middleware.SessionMiddleware',
-        'django.contrib.auth.middleware.AuthenticationMiddleware',
-        'axes.middleware.FailedLoginMiddleware'
-    )
+Remember to run ``python manage.py migrate`` to sync the database.
 
-Run ``python manage.py syncdb``.  This creates the appropriate tables in your database
-that are necessary for operation.
 
 Customizing Axes
 ----------------
@@ -107,22 +96,27 @@ These should be defined in your ``settings.py`` file.
   Default: ``True``
 * ``AXES_USERNAME_FORM_FIELD``: the name of the form field that contains your
   users usernames. Default: ``username``
-
 * ``AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP``: If ``True`` prevents to login
   from IP under particular user if attempts limit exceed, otherwise lock out
   based on IP.
   Default: ``False``
+* ``AXES_NEVER_LOCKOUT_WHITELIST``: If ``True``, users can always login from
+  whitelisted IP addresses.
+  Default: ``False``
+* ``AXES_BEHIND_REVERSE_PROXY``: If ``True``, it will look for the IP address from the header defined at ``AXES_REVERSE_PROXY_HEADER``. Please make sure if you enable this setting to configure your proxy to set the correct value for the header, otherwise you could be attacked by setting this header directly in every request.
+  Default: ``False``
+* ``AXES_REVERSE_PROXY_HEADER``: If ``AXES_BEHIND_REVERSE_PROXY`` is ``True``, it will look for the IP address from this header.
+  Default: ``HTTP_X_FORWARDED_FOR``
 
 
 Usage
 =====
 
-Using ``django-axes`` is extremely simple.  Once you install the application
-and the middleware, all you need to do is periodically check the Access
-Attempts section of the admin.
+Using ``django-axes`` is extremely simple. All you need to do is periodically
+check the Access Attempts section of the admin.
 
 By default, django-axes will lock out repeated attempts from the same IP
-address.  You can allow this IP to attempt again by deleting the relevant
+address. You can allow this IP to attempt again by deleting the relevant
 ``AccessAttempt`` records in the admin.
 
 You can also use the ``axes_reset`` management command using Django's
@@ -135,7 +129,8 @@ In your code, you can use ``from axes.utils import reset``.
 
 * ``reset()`` will reset all lockouts and access records.
 * ``reset(ip=ip)`` will clear lockout/records for ip
-* ``reset(username=username)`` will clear lockout/records for username
+* ``reset(username=username)`` will clear lockout/records for a username
+
 
 Issues
 ======
@@ -143,15 +138,17 @@ Issues
 Not being locked out after failed attempts
 ------------------------------------------
 
-You may find that Axes is not capturing your failed login attempts. It may be that you need to manually add watch_login to your login url.
+You may find that Axes is not capturing your failed login attempts. It may
+be that you need to manually add watch_login to your login url.
+
 For example, in your urls.py::
 
     ...
-    from django.contrib.auth.views import login, logout, password_change
+    from my.custom.app import login
     from axes.decorators import watch_login
     ...
     urlpatterns = patterns('',
-        (r'^login/$', watch_login(login), {'template_name': 'auth/login.html'}),
+        (r'^login/$', watch_login(login)),
     ...
 
 
@@ -165,3 +162,51 @@ leads to all users attempts being lumped together. To fix this add the following
 to your settings:
 
     AXES_USERNAME_FORM_FIELD = "email"
+
+
+Using a captcha
+===============
+
+Using https://github.com/mbi/django-simple-captcha you do the following:
+
+1. Change axes lockout url in ``settings.py``::
+
+    AXES_LOCKOUT_URL = '/locked'
+
+2. Add the url in ``urls.py``::
+
+    url(r'^locked/$', locked_out, name='locked_out'),
+
+3. Create a captcha form::
+
+    class AxesCaptchaForm(forms.Form):
+        captcha = CaptchaField()
+
+4. Create a captcha view for the above url that resets on captcha success and redirects::
+
+    def locked_out(request):
+        if request.POST:
+            form = AxesCaptchaForm(request.POST)
+            if form.is_valid():
+                ip = get_ip_address_from_request(request)
+                reset(ip=ip)
+                return HttpResponseRedirect(reverse_lazy('signin'))
+        else:
+            form = AxesCaptchaForm()
+
+        return render_to_response('locked_out.html', dict(form=form), context_instance=RequestContext(request))
+
+5. Add a captcha template::
+
+    <form action="" method="post">
+        {% csrf_token %}
+
+        {{ form.captcha.errors }}
+        {{ form.captcha }}
+
+        <div class="form-actions">
+            <input type="submit" value="Submit" />
+        </div>
+    </form>
+
+Done.

+ 7 - 0
desktop/core/ext-py/django-axes-2.2.0/axes/__init__.py

@@ -0,0 +1,7 @@
+__version__ = '2.2.0'
+
+default_app_config = 'axes.apps.AppConfig'
+
+
+def get_version():
+    return __version__

+ 0 - 2
desktop/core/ext-py/django-axes-1.5.0/axes/admin.py → desktop/core/ext-py/django-axes-2.2.0/axes/admin.py

@@ -16,7 +16,6 @@ class AccessAttemptAdmin(admin.ModelAdmin):
 
     list_filter = [
         'attempt_time',
-        'ip_address',
         'username',
         'path_info',
     ]
@@ -58,7 +57,6 @@ class AccessLogAdmin(admin.ModelAdmin):
     list_filter = [
         'attempt_time',
         'logout_time',
-        'ip_address',
         'username',
         'path_info',
     ]

+ 11 - 0
desktop/core/ext-py/django-axes-2.2.0/axes/apps.py

@@ -0,0 +1,11 @@
+from django import apps
+
+
+class AppConfig(apps.AppConfig):
+    name = 'axes'
+
+    def ready(self):
+        from django.contrib.auth import views as auth_views
+        from axes.decorators import watch_login
+
+        auth_views.login = watch_login(auth_views.login)

+ 133 - 168
desktop/core/ext-py/django-axes-1.5.0/axes/decorators.py → desktop/core/ext-py/django-axes-2.2.0/axes/decorators.py

@@ -1,35 +1,22 @@
+import json
 import logging
-import socket
 
 from datetime import timedelta
 
 from django.conf import settings
+from django.contrib.auth import get_user_model
 from django.contrib.auth import logout
-from django.core.exceptions import ObjectDoesNotExist
 from django.http import HttpResponse
 from django.http import HttpResponseRedirect
-from django.shortcuts import render_to_response
-from django.template import RequestContext
+from django.shortcuts import render
+from django.utils import six
 from django.utils import timezone as datetime
-from django.utils.translation import ugettext_lazy
-
-try:
-    from django.contrib.auth import get_user_model
-except ImportError:  # django < 1.5
-    from django.contrib.auth.models import User
-else:
-    User = get_user_model()
-
-try:
-    from django.contrib.auth.models import SiteProfileNotAvailable
-except ImportError: # django >= 1.7
-    SiteProfileNotAvailable = type('SiteProfileNotAvailable', (Exception,), {})
 
-from axes.models import AccessLog
 from axes.models import AccessAttempt
+from axes.models import AccessLog
 from axes.signals import user_locked_out
+from axes.utils import iso8601
 import axes
-from django.utils import six
 
 
 # see if the user has overridden the failure limit
@@ -49,33 +36,36 @@ PASSWORD_FORM_FIELD = getattr(settings, 'AXES_PASSWORD_FORM_FIELD', 'password')
 # see if the django app is sitting behind a reverse proxy
 BEHIND_REVERSE_PROXY = getattr(settings, 'AXES_BEHIND_REVERSE_PROXY', False)
 
-# see if the django app is sitting behind a reverse proxy but can be accessed directly
-BEHIND_REVERSE_PROXY_WITH_DIRECT_ACCESS = getattr(settings, 'AXES_BEHIND_REVERSE_PROXY_WITH_DIRECT_ACCESS', False)
-
 # if the django app is behind a reverse proxy, look for the ip address using this HTTP header value
-REVERSE_PROXY_HEADER = getattr(settings, 'AXES_REVERSE_PROXY_HEADER', 'HTTP_X_FORWARDED_FOR')
+REVERSE_PROXY_HEADER = \
+    getattr(settings, 'AXES_REVERSE_PROXY_HEADER', 'HTTP_X_FORWARDED_FOR')
 
 # lock out user from particular IP based on combination USER+IP
-def should_lock_out_by_combination_user_and_ip():
-    return getattr(settings, 'AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP', False)
+LOCK_OUT_BY_COMBINATION_USER_AND_IP = \
+    getattr(settings, 'AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP', False)
 
 COOLOFF_TIME = getattr(settings, 'AXES_COOLOFF_TIME', None)
-if (isinstance(COOLOFF_TIME, int) or isinstance(COOLOFF_TIME, float) ):
+if (isinstance(COOLOFF_TIME, int) or isinstance(COOLOFF_TIME, float)):
     COOLOFF_TIME = timedelta(hours=COOLOFF_TIME)
 
 LOGGER = getattr(settings, 'AXES_LOGGER', 'axes.watch_login')
 
 LOCKOUT_TEMPLATE = getattr(settings, 'AXES_LOCKOUT_TEMPLATE', None)
+
+LOCKOUT_URL = getattr(settings, 'AXES_LOCKOUT_URL', None)
+
 VERBOSE = getattr(settings, 'AXES_VERBOSE', True)
 
 # whitelist and blacklist
-# todo: convert the strings to IPv4 on startup to avoid type conversion during processing
+# TODO: convert the strings to IPv4 on startup to avoid type conversion during processing
+NEVER_LOCKOUT_WHITELIST = \
+    getattr(settings, 'AXES_NEVER_LOCKOUT_WHITELIST', False)
+
 ONLY_WHITELIST = getattr(settings, 'AXES_ONLY_ALLOW_WHITELIST', False)
+
 IP_WHITELIST = getattr(settings, 'AXES_IP_WHITELIST', None)
-IP_BLACKLIST = getattr(settings, 'AXES_IP_BLACKLIST', None)
 
-ERROR_MESSAGE = ugettext_lazy("Please enter a correct username and password. "
-                              "Note that both fields are case-sensitive.")
+IP_BLACKLIST = getattr(settings, 'AXES_IP_BLACKLIST', None)
 
 
 log = logging.getLogger(LOGGER)
@@ -85,67 +75,25 @@ if VERBOSE:
 
 
 if BEHIND_REVERSE_PROXY:
-    log.debug('Axes is configured to be behind reverse proxy...looking for header value %s', REVERSE_PROXY_HEADER)
-
-
-def is_valid_ip(ip_address):
-    """ Check Validity of an IP address """
-    valid = True
-    try:
-        socket.inet_aton(ip_address.strip())
-    except:
-        valid = False
-    return valid
-
-
-def get_ip_address_from_request(request):
-    """ Makes the best attempt to get the client's real IP or return the loopback """
-    PRIVATE_IPS_PREFIX = ('10.', '172.', '192.', '127.')
-    ip_address = ''
-    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR', '')
-    if x_forwarded_for and ',' not in x_forwarded_for:
-        # Hue's LB can be hosted on Private IP address.
-        if is_valid_ip(x_forwarded_for):
-            ip_address = x_forwarded_for.strip()
-    elif x_forwarded_for and ',' in x_forwarded_for:
-        # Hue Server may have more than one reverse proxy in front of it.
-        x_forwarded_for_first_ip = x_forwarded_for.split(',')[0]
-        if is_valid_ip(x_forwarded_for_first_ip):
-            ip_address = x_forwarded_for_first_ip.strip()
-    else:
-        ips = [ip.strip() for ip in x_forwarded_for.split(',')]
-        for ip in ips:
-            if ip.startswith(PRIVATE_IPS_PREFIX):
-                continue
-            elif not is_valid_ip(ip):
-                continue
-            else:
-                ip_address = ip
-                break
-    if not ip_address:
-        x_real_ip = request.META.get('HTTP_X_REAL_IP', '')
-        if x_real_ip:
-            if not x_real_ip.startswith(PRIVATE_IPS_PREFIX) and is_valid_ip(x_real_ip):
-                ip_address = x_real_ip.strip()
-    if not ip_address:
-        remote_addr = request.META.get('REMOTE_ADDR', '')
-        if remote_addr:
-            if not remote_addr.startswith(PRIVATE_IPS_PREFIX) and is_valid_ip(remote_addr):
-                ip_address = remote_addr.strip()
-            if remote_addr.startswith(PRIVATE_IPS_PREFIX) and is_valid_ip(remote_addr):
-                ip_address = remote_addr.strip()
-    if not ip_address:
-            ip_address = '127.0.0.1'
-    return ip_address
+    log.debug('Axes is configured to be behind reverse proxy')
+    log.debug('Looking for header value %s', REVERSE_PROXY_HEADER)
 
 
 def get_ip(request):
-    ip = get_ip_address_from_request(request)
-    return ip
-
+    ip = request.META.get('REMOTE_ADDR', '')
+
+    if BEHIND_REVERSE_PROXY:
+        ip = request.META.get(REVERSE_PROXY_HEADER, '').split(',', 1)[0]
+        ip = ip.strip()
+        if not ip:
+            raise Warning(
+                'Axes is configured for operation behind a reverse proxy '
+                'but could not find an HTTP header value. Check your proxy '
+                'server settings to make sure this header value is being '
+                'passed. Header value {0}'.format(REVERSE_PROXY_HEADER)
+            )
 
-def get_lockout_url():
-    return getattr(settings, 'AXES_LOCKOUT_URL', None)
+    return ip
 
 
 def query2str(items, max_length=1024):
@@ -153,15 +101,13 @@ def query2str(items, max_length=1024):
 
     If there's a field called "password" it will be excluded from the output.
 
-    The length of the output is limited to max_length to avoid a DoS attack.
+    The length of the output is limited to max_length to avoid a DoS attack
+    via excessively large payloads.
     """
-
-    kvs = []
-    for k, v in items:
-        if k != PASSWORD_FORM_FIELD:
-            kvs.append(six.u('%s=%s') % (k, v))
-
-    return '\n'.join(kvs)[:max_length]
+    return '\n'.join([
+        '%s=%s' % (k, v) for k, v in six.iteritems(items)
+        if k != PASSWORD_FORM_FIELD
+    ][:int(max_length / 2)])[:max_length]
 
 
 def ip_in_whitelist(ip):
@@ -184,41 +130,30 @@ def is_user_lockable(request):
     and doesn't get their account locked out
     """
     try:
-        field = getattr(User, 'USERNAME_FIELD', 'username')
+        field = getattr(get_user_model(), 'USERNAME_FIELD', 'username')
         kwargs = {
             field: request.POST.get(USERNAME_FORM_FIELD)
         }
-        user = User.objects.get(**kwargs)
-    except User.DoesNotExist:
+        user = get_user_model().objects.get(**kwargs)
+
+        if hasattr(user, 'nolockout'):
+            # need to invert since we need to return
+            # false for users that can't be blocked
+            return not user.nolockout
+
+    except get_user_model().DoesNotExist:
         # not a valid user
         return True
 
-    if hasattr(user, 'nolockout'):
-        # need to revert since we need to return
-        # false for users that can't be blocked
-        return not user.nolockout
-
-    elif hasattr(settings, 'AUTH_PROFILE_MODULE'):
-        try:
-            profile = user.get_profile()
-            if hasattr(profile, 'nolockout'):
-                # need to revert since we need to return
-                # false for users that can't be blocked
-                return not profile.nolockout
-
-        except (SiteProfileNotAvailable, ObjectDoesNotExist, AttributeError):
-            # no profile
-            return True
-
     # Default behavior for a user to be lockable
     return True
 
+
 def _get_user_attempts(request):
     """Returns access attempt record if it exists.
     Otherwise return None.
     """
     ip = get_ip(request)
-
     username = request.POST.get(USERNAME_FORM_FIELD, None)
 
     if USE_USER_AGENT:
@@ -235,13 +170,14 @@ def _get_user_attempts(request):
         params = {'ip_address': ip, 'trusted': False}
         if USE_USER_AGENT:
             params['user_agent'] = ua
-        if should_lock_out_by_combination_user_and_ip():
+        if LOCK_OUT_BY_COMBINATION_USER_AND_IP:
             params['username'] = username
 
         attempts = AccessAttempt.objects.filter(**params)
 
     return attempts
 
+
 def get_user_attempts(request):
     objects_deleted = False
     attempts = _get_user_attempts(request)
@@ -269,6 +205,10 @@ def watch_login(func):
     Used to decorate the django.contrib.admin.site.login method.
     """
 
+    # Don't decorate multiple times
+    if func.__name__ == 'decorated_login':
+        return func
+
     def decorated_login(request, *args, **kwargs):
         # share some useful information
         if func.__name__ != 'decorated_login' and VERBOSE:
@@ -305,19 +245,21 @@ def watch_login(func):
 
         if request.method == 'POST':
             # see if the login was successful
-
             login_unsuccessful = (
                 response and
                 not response.has_header('location') and
                 response.status_code != 302
             )
 
-            access_log = AccessLog.objects.create(
-                user_agent=request.META.get('HTTP_USER_AGENT', '<unknown>')[:255],
+            user_agent = request.META.get('HTTP_USER_AGENT', '<unknown>')[:255]
+            http_accept = request.META.get('HTTP_ACCEPT', '<unknown>')[:1025]
+            path_info = request.META.get('PATH_INFO', '<unknown>')[:255]
+            AccessLog.objects.create(
+                user_agent=user_agent,
                 ip_address=get_ip(request),
                 username=request.POST.get(USERNAME_FORM_FIELD, None),
-                http_accept=request.META.get('HTTP_ACCEPT', '<unknown>'),
-                path_info=request.META.get('PATH_INFO', '<unknown>'),
+                http_accept=http_accept,
+                path_info=path_info,
                 trusted=not login_unsuccessful,
             )
             if check_request(request, login_unsuccessful):
@@ -331,41 +273,57 @@ def watch_login(func):
 
 
 def lockout_response(request):
-    if LOCKOUT_TEMPLATE:
-        context = {
-            'cooloff_time': COOLOFF_TIME,
-            'failure_limit': FAILURE_LIMIT,
-            'username': request.POST.get(USERNAME_FORM_FIELD, '')
-        }
-        return render_to_response(LOCKOUT_TEMPLATE, context,
-                                  context_instance=RequestContext(request))
+    context = {
+        'failure_limit': FAILURE_LIMIT,
+        'username': request.POST.get(USERNAME_FORM_FIELD, '')
+    }
+
+    if request.is_ajax():
+        if COOLOFF_TIME:
+            context.update({'cooloff_time': iso8601(COOLOFF_TIME)})
+
+        return HttpResponse(
+            json.dumps(context),
+            content_type='application/json',
+            status=403,
+        )
 
-    LOCKOUT_URL = get_lockout_url()
-    if LOCKOUT_URL:
+    elif LOCKOUT_TEMPLATE:
+        if COOLOFF_TIME:
+            context.update({'cooloff_time': iso8601(COOLOFF_TIME)})
+
+        return render(request, LOCKOUT_TEMPLATE, context, status=403)
+
+    elif LOCKOUT_URL:
         return HttpResponseRedirect(LOCKOUT_URL)
 
-    if COOLOFF_TIME:
-        return HttpResponse("Account locked: too many login attempts.  "
-                            "Please try again later.")
     else:
-        return HttpResponse("Account locked: too many login attempts.  "
-                            "Contact an admin to unlock your account.")
+        msg = 'Account locked: too many login attempts. {0}'
+        if COOLOFF_TIME:
+            msg = msg.format('Please try again later.')
+        else:
+            msg = msg.format('Contact an admin to unlock your account.')
+
+        return HttpResponse(msg, status=403)
 
 
 def is_already_locked(request):
     ip = get_ip(request)
 
-    if ONLY_WHITELIST:
-        if not ip_in_whitelist(ip):
-            return True
+    if NEVER_LOCKOUT_WHITELIST and ip_in_whitelist(ip):
+        return False
+
+    if ONLY_WHITELIST and not ip_in_whitelist(ip):
+        return True
 
     if ip_in_blacklist(ip):
         return True
 
-    attempts = get_user_attempts(request)
-    user_lockable = is_user_lockable(request)
-    for attempt in attempts:
-        if attempt.failures_since_start >= FAILURE_LIMIT and LOCK_OUT_AT_FAILURE and user_lockable:
+    if not is_user_lockable(request):
+        return False
+
+    for attempt in get_user_attempts(request):
+        if attempt.failures_since_start >= FAILURE_LIMIT and LOCK_OUT_AT_FAILURE:
             return True
 
     return False
@@ -390,20 +348,23 @@ def check_request(request, login_unsuccessful):
             for attempt in attempts:
                 attempt.get_data = '%s\n---------\n%s' % (
                     attempt.get_data,
-                    query2str(request.GET.items()),
+                    query2str(request.GET),
                 )
                 attempt.post_data = '%s\n---------\n%s' % (
                     attempt.post_data,
-                    query2str(request.POST.items())
+                    query2str(request.POST)
                 )
-                attempt.http_accept = request.META.get('HTTP_ACCEPT', '<unknown>')
-                attempt.path_info = request.META.get('PATH_INFO', '<unknown>')
+                attempt.http_accept = \
+                    request.META.get('HTTP_ACCEPT', '<unknown>')[:1025]
+                attempt.path_info = \
+                    request.META.get('PATH_INFO', '<unknown>')[:255]
                 attempt.failures_since_start = failures
                 attempt.attempt_time = datetime.now()
                 attempt.save()
-                log.info('AXES: Repeated login failure by %s. Updating access '
-                         'record. Count = %s' %
-                         (attempt.ip_address, failures))
+                log.info(
+                    'AXES: Repeated login failure by %s. Updating access '
+                    'record. Count = %s' % (attempt.ip_address, failures)
+                )
         else:
             create_new_failure_records(request, failures)
     else:
@@ -421,6 +382,9 @@ def check_request(request, login_unsuccessful):
         if trusted_record_exists is False:
             create_new_trusted_record(request)
 
+    if NEVER_LOCKOUT_WHITELIST and ip_in_whitelist(ip_address):
+        return True
+
     user_lockable = is_user_lockable(request)
     # no matter what, we want to lock them out if they're past the number of
     # attempts allowed, unless the user is set to notlockable
@@ -429,10 +393,13 @@ def check_request(request, login_unsuccessful):
         # password
         if hasattr(request, 'user') and request.user.is_authenticated():
             logout(request)
-        log.warn('AXES: locked out %s after repeated login attempts.' %
-                 (ip_address,))
+        log.warn(
+            'AXES: locked out %s after repeated login attempts.' % ip_address
+        )
         # send signal when someone is locked out.
-        user_locked_out.send("axes", request=request, username=username, ip_address=ip_address)
+        user_locked_out.send(
+            'axes', request=request, username=username, ip_address=ip_address
+        )
 
         # if a trusted login has violated lockout, revoke trust
         for attempt in [a for a in attempts if a.trusted]:
@@ -449,18 +416,16 @@ def create_new_failure_records(request, failures):
     ua = request.META.get('HTTP_USER_AGENT', '<unknown>')[:255]
     username = request.POST.get(USERNAME_FORM_FIELD, None)
 
-    params = {
-        'user_agent': ua,
-        'ip_address': ip,
-        'username': username,
-        'get_data': query2str(request.GET.items()),
-        'post_data': query2str(request.POST.items()),
-        'http_accept': request.META.get('HTTP_ACCEPT', '<unknown>'),
-        'path_info': request.META.get('PATH_INFO', '<unknown>'),
-        'failures_since_start': failures,
-    }
-
-    AccessAttempt.objects.create(**params)
+    AccessAttempt.objects.create(
+        user_agent=ua,
+        ip_address=ip,
+        username=username,
+        get_data=query2str(request.GET),
+        post_data=query2str(request.POST),
+        http_accept=request.META.get('HTTP_ACCEPT', '<unknown>'),
+        path_info=request.META.get('PATH_INFO', '<unknown>'),
+        failures_since_start=failures,
+    )
 
     log.info('AXES: New login failure by %s. Creating access record.' % (ip,))
 
@@ -477,8 +442,8 @@ def create_new_trusted_record(request):
         user_agent=ua,
         ip_address=ip,
         username=username,
-        get_data=query2str(request.GET.items()),
-        post_data=query2str(request.POST.items()),
+        get_data=query2str(request.GET),
+        post_data=query2str(request.POST),
         http_accept=request.META.get('HTTP_ACCEPT', '<unknown>'),
         path_info=request.META.get('PATH_INFO', '<unknown>'),
         failures_since_start=0,

+ 0 - 0
desktop/core/ext-py/django-axes-1.5.0/axes/management/__init__.py → desktop/core/ext-py/django-axes-2.2.0/axes/management/__init__.py


+ 0 - 0
desktop/core/ext-py/django-axes-1.5.0/axes/management/commands/__init__.py → desktop/core/ext-py/django-axes-2.2.0/axes/management/commands/__init__.py


+ 16 - 0
desktop/core/ext-py/django-axes-2.2.0/axes/management/commands/axes_list_attempts.py

@@ -0,0 +1,16 @@
+from django.core.management.base import BaseCommand
+
+from axes.models import AccessAttempt
+
+
+class Command(BaseCommand):
+    args = ''
+    help = ('List registered login attempts')
+
+    def handle(self, *args, **kwargs):
+        for obj in AccessAttempt.objects.all():
+            print('{ip}\t{username}\t{failures}'.format(
+                ip=obj.ip_address,
+                username=obj.username,
+                failures=obj.failures,
+            ))

+ 5 - 4
desktop/core/ext-py/django-axes-1.5.0/axes/management/commands/axes_reset.py → desktop/core/ext-py/django-axes-2.2.0/axes/management/commands/axes_reset.py

@@ -1,18 +1,19 @@
 from django.core.management.base import BaseCommand
-from django.core.management.base import CommandError
 
 from axes.utils import reset
 
 
 class Command(BaseCommand):
-    args = ''
     help = ("resets any lockouts or failed login records. If called with an "
             "IP, resets only for that IP")
 
+    def add_arguments(self, parser):
+        parser.add_argument('ip', nargs='+')
+
     def handle(self, *args, **kwargs):
         count = 0
-        if args:
-            for ip in args:
+        if kwargs:
+            for ip in kwargs['ip']:
                 count += reset(ip=ip)
         else:
             count = reset()

+ 51 - 0
desktop/core/ext-py/django-axes-2.2.0/axes/migrations/0001_initial.py

@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='AccessAttempt',
+            fields=[
+                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+                ('user_agent', models.CharField(max_length=255)),
+                ('ip_address', models.GenericIPAddressField(null=True, verbose_name='IP Address')),
+                ('username', models.CharField(max_length=255, null=True)),
+                ('trusted', models.BooleanField(default=False)),
+                ('http_accept', models.CharField(max_length=1025, verbose_name='HTTP Accept')),
+                ('path_info', models.CharField(max_length=255, verbose_name='Path')),
+                ('attempt_time', models.DateTimeField(auto_now_add=True)),
+                ('get_data', models.TextField(verbose_name='GET Data')),
+                ('post_data', models.TextField(verbose_name='POST Data')),
+                ('failures_since_start', models.PositiveIntegerField(verbose_name='Failed Logins')),
+            ],
+            options={
+                'ordering': ['-attempt_time'],
+                'abstract': False,
+            },
+        ),
+        migrations.CreateModel(
+            name='AccessLog',
+            fields=[
+                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+                ('user_agent', models.CharField(max_length=255)),
+                ('ip_address', models.GenericIPAddressField(null=True, verbose_name='IP Address')),
+                ('username', models.CharField(max_length=255, null=True)),
+                ('trusted', models.BooleanField(default=False)),
+                ('http_accept', models.CharField(max_length=1025, verbose_name='HTTP Accept')),
+                ('path_info', models.CharField(max_length=255, verbose_name='Path')),
+                ('attempt_time', models.DateTimeField(auto_now_add=True)),
+                ('logout_time', models.DateTimeField(null=True, blank=True)),
+            ],
+            options={
+                'ordering': ['-attempt_time'],
+                'abstract': False,
+            },
+        ),
+    ]

+ 54 - 0
desktop/core/ext-py/django-axes-2.2.0/axes/migrations/0002_auto_20151217_2044.py

@@ -0,0 +1,54 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('axes', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='accessattempt',
+            name='ip_address',
+            field=models.GenericIPAddressField(db_index=True, null=True, verbose_name='IP Address'),
+        ),
+        migrations.AlterField(
+            model_name='accessattempt',
+            name='trusted',
+            field=models.BooleanField(db_index=True, default=False),
+        ),
+        migrations.AlterField(
+            model_name='accessattempt',
+            name='user_agent',
+            field=models.CharField(db_index=True, max_length=255),
+        ),
+        migrations.AlterField(
+            model_name='accessattempt',
+            name='username',
+            field=models.CharField(db_index=True, max_length=255, null=True),
+        ),
+        migrations.AlterField(
+            model_name='accesslog',
+            name='ip_address',
+            field=models.GenericIPAddressField(db_index=True, null=True, verbose_name='IP Address'),
+        ),
+        migrations.AlterField(
+            model_name='accesslog',
+            name='trusted',
+            field=models.BooleanField(db_index=True, default=False),
+        ),
+        migrations.AlterField(
+            model_name='accesslog',
+            name='user_agent',
+            field=models.CharField(db_index=True, max_length=255),
+        ),
+        migrations.AlterField(
+            model_name='accesslog',
+            name='username',
+            field=models.CharField(db_index=True, max_length=255, null=True),
+        ),
+    ]

+ 59 - 0
desktop/core/ext-py/django-axes-2.2.0/axes/migrations/0003_auto_20160322_0929.py

@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('axes', '0002_auto_20151217_2044'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='accessattempt',
+            name='failures_since_start',
+            field=models.PositiveIntegerField(verbose_name='Failed Logins'),
+        ),
+        migrations.AlterField(
+            model_name='accessattempt',
+            name='get_data',
+            field=models.TextField(verbose_name='GET Data'),
+        ),
+        migrations.AlterField(
+            model_name='accessattempt',
+            name='http_accept',
+            field=models.CharField(verbose_name='HTTP Accept', max_length=1025),
+        ),
+        migrations.AlterField(
+            model_name='accessattempt',
+            name='ip_address',
+            field=models.GenericIPAddressField(null=True, verbose_name='IP Address', db_index=True),
+        ),
+        migrations.AlterField(
+            model_name='accessattempt',
+            name='path_info',
+            field=models.CharField(verbose_name='Path', max_length=255),
+        ),
+        migrations.AlterField(
+            model_name='accessattempt',
+            name='post_data',
+            field=models.TextField(verbose_name='POST Data'),
+        ),
+        migrations.AlterField(
+            model_name='accesslog',
+            name='http_accept',
+            field=models.CharField(verbose_name='HTTP Accept', max_length=1025),
+        ),
+        migrations.AlterField(
+            model_name='accesslog',
+            name='ip_address',
+            field=models.GenericIPAddressField(null=True, verbose_name='IP Address', db_index=True),
+        ),
+        migrations.AlterField(
+            model_name='accesslog',
+            name='path_info',
+            field=models.CharField(verbose_name='Path', max_length=255),
+        ),
+    ]

+ 0 - 0
desktop/core/ext-py/django-axes-1.5.0/examples/example/__init__.py → desktop/core/ext-py/django-axes-2.2.0/axes/migrations/__init__.py


+ 9 - 1
desktop/core/ext-py/django-axes-1.5.0/axes/models.py → desktop/core/ext-py/django-axes-2.2.0/axes/models.py

@@ -1,25 +1,30 @@
 from django.db import models
 from django.utils import six
 
+
 class CommonAccess(models.Model):
     user_agent = models.CharField(
         max_length=255,
+        db_index=True,
     )
 
     ip_address = models.GenericIPAddressField(
         verbose_name='IP Address',
         null=True,
+        db_index=True,
     )
 
     username = models.CharField(
         max_length=255,
         null=True,
+        db_index=True,
     )
 
     # Once a user logs in from an ip, that combination is trusted and not
     # locked out in case of a distributed attack
     trusted = models.BooleanField(
         default=False,
+        db_index=True,
     )
 
     http_accept = models.CharField(
@@ -37,6 +42,7 @@ class CommonAccess(models.Model):
     )
 
     class Meta:
+        app_label = 'axes'
         abstract = True
         ordering = ['-attempt_time']
 
@@ -69,4 +75,6 @@ class AccessLog(CommonAccess):
     )
 
     def __unicode__(self):
-        return six.u('Access Log for %s @ %s') % (self.username, self.attempt_time)
+        return six.u('Access Log for %s @ %s') % (
+            self.username, self.attempt_time
+        )

+ 3 - 10
desktop/core/ext-py/django-axes-1.5.0/axes/signals.py → desktop/core/ext-py/django-axes-2.2.0/axes/signals.py

@@ -2,7 +2,6 @@ from django.dispatch import receiver
 from django.dispatch import Signal
 from django.utils.timezone import now
 from django.contrib.auth.signals import user_logged_out
-from django.core.exceptions import ObjectDoesNotExist
 
 from axes.models import AccessLog
 
@@ -17,18 +16,12 @@ def log_user_lockout(sender, request, user, signal, *args, **kwargs):
     if not user:
         return
 
-    try:
-        username = user.get_username()
-    except AttributeError:
-        # Django < 1.5
-        username = user.username
-
     access_logs = AccessLog.objects.filter(
-        username=username,
+        username=user.get_username(),
         logout_time__isnull=True,
     ).order_by('-attempt_time')
 
-    if access_logs:
-        access_log = access_logs[0]
+    if access_logs.exists():
+        access_log = access_logs.first()
         access_log.logout_time = now()
         access_log.save()

+ 23 - 2
desktop/core/ext-py/django-axes-1.5.0/axes/test_settings.py → desktop/core/ext-py/django-axes-2.2.0/axes/test_settings.py

@@ -13,7 +13,6 @@ MIDDLEWARE_CLASSES = (
     'django.middleware.common.CommonMiddleware',
     'django.contrib.sessions.middleware.SessionMiddleware',
     'django.contrib.auth.middleware.AuthenticationMiddleware',
-    'axes.middleware.FailedLoginMiddleware'
 )
 
 ROOT_URLCONF = 'axes.test_urls'
@@ -26,11 +25,33 @@ INSTALLED_APPS = (
     'django.contrib.messages',
     'django.contrib.admin',
 
-    'axes',
+    'axes.apps.AppConfig',
 )
 
+TEMPLATES = [
+    {
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+        'DIRS': [],
+        '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',
+            ],
+        },
+    },
+]
+
 SECRET_KEY = 'too-secret-for-test'
 
+USE_I18N = False
+
+USE_L10N = False
+
+USE_TZ = False
+
 LOGIN_REDIRECT_URL = '/admin/'
 
 AXES_LOGIN_FAILURE_LIMIT = 10

+ 16 - 0
desktop/core/ext-py/django-axes-2.2.0/axes/test_urls.py

@@ -0,0 +1,16 @@
+from django.conf.urls import url
+from django.contrib import admin
+
+try:
+    # django < 1.10
+    from django.conf.urls import patterns
+    from django.conf.urls import include
+
+    urlpatterns = patterns(
+        '',
+        url(r'^admin/', include(admin.site.urls)),
+    )
+except ImportError:
+    urlpatterns = [
+        url(r'^admin/', admin.site.urls),
+    ]

+ 105 - 13
desktop/core/ext-py/django-axes-1.5.0/axes/tests.py → desktop/core/ext-py/django-axes-2.2.0/axes/tests.py

@@ -1,18 +1,22 @@
 import random
 import string
 import time
+import json
+import datetime
 
 from django.test import TestCase
 from django.test.utils import override_settings
 from django.contrib.auth.models import User
 from django.core.urlresolvers import NoReverseMatch
 from django.core.urlresolvers import reverse
+from django.utils import six
 
 from axes.decorators import COOLOFF_TIME
 from axes.decorators import FAILURE_LIMIT
-from axes.models import AccessLog
+from axes.decorators import is_valid_ip
+from axes.models import AccessAttempt, AccessLog
 from axes.signals import user_locked_out
-from axes.utils import reset
+from axes.utils import reset, iso8601
 
 
 class AccessAttemptTest(TestCase):
@@ -23,7 +27,8 @@ class AccessAttemptTest(TestCase):
     LOCKED_MESSAGE = 'Account locked: too many login attempts.'
     LOGIN_FORM_KEY = '<input type="submit" value="Log in" />'
 
-    def _login(self, is_valid_username=False, is_valid_password=False, user_agent='test-browser'):
+    def _login(self, is_valid_username=False, is_valid_password=False,
+               is_json=False, **kwargs):
         """Login a user. A valid credential is used when is_valid_username is True,
         otherwise it will use a random string to make a failed login.
         """
@@ -45,11 +50,23 @@ class AccessAttemptTest(TestCase):
         else:
             password = 'invalid-password'
 
-        response = self.client.post(admin_login, {
+        headers = {
+            'user_agent': 'test-browser'
+        }
+        post_data = {
             'username': username,
             'password': password,
             'this_is_the_login_form': 1,
-        }, HTTP_USER_AGENT=user_agent)
+        }
+        post_data.update(kwargs)
+
+        if is_json:
+            headers.update({
+                'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest',
+                'content_type': 'application/json',
+            })
+            post_data = json.dumps(post_data)
+        response = self.client.post(admin_login, post_data, **headers)
 
         return response
 
@@ -66,7 +83,8 @@ class AccessAttemptTest(TestCase):
         """Tests the login lock trying to login one more time
         than failure limit
         """
-        for i in range(1, FAILURE_LIMIT):  # test until one try before the limit
+        # test until one try before the limit
+        for i in range(1, FAILURE_LIMIT):
             response = self._login()
             # Check if we are in the same login page
             self.assertContains(response, self.LOGIN_FORM_KEY)
@@ -74,7 +92,7 @@ class AccessAttemptTest(TestCase):
         # So, we shouldn't have gotten a lock-out yet.
         # But we should get one now
         response = self._login()
-        self.assertContains(response, self.LOCKED_MESSAGE)
+        self.assertContains(response, self.LOCKED_MESSAGE, status_code=403)
 
     def test_failure_limit_many(self):
         """Tests the login lock trying to login a lot of times more
@@ -89,7 +107,7 @@ class AccessAttemptTest(TestCase):
         # We should get a locked message each time we try again
         for i in range(0, random.randrange(1, FAILURE_LIMIT)):
             response = self._login()
-            self.assertContains(response, self.LOCKED_MESSAGE)
+            self.assertContains(response, self.LOCKED_MESSAGE, status_code=403)
 
     def test_valid_login(self):
         """Tests a valid login for a real username
@@ -131,7 +149,11 @@ class AccessAttemptTest(TestCase):
         """Tests if can handle a long user agent
         """
         long_user_agent = 'ie6' * 1024
-        response = self._login(is_valid_username=True, is_valid_password=True, user_agent=long_user_agent)
+        response = self._login(
+            is_valid_username=True,
+            is_valid_password=True,
+            user_agent=long_user_agent,
+        )
         self.assertNotContains(response, self.LOGIN_FORM_KEY, status_code=302)
 
     def test_long_user_agent_not_valid(self):
@@ -141,7 +163,7 @@ class AccessAttemptTest(TestCase):
         for i in range(0, FAILURE_LIMIT + 1):
             response = self._login(user_agent=long_user_agent)
 
-        self.assertContains(response, self.LOCKED_MESSAGE)
+        self.assertContains(response, self.LOCKED_MESSAGE, status_code=403)
 
     def test_reset_ip(self):
         """Tests if can reset an ip address
@@ -196,12 +218,82 @@ class AccessAttemptTest(TestCase):
         """Tests the login lock with a valid username and invalid password
         when AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP is True
         """
-        for i in range(1, FAILURE_LIMIT):  # test until one try before the limit
-            response = self._login(is_valid_username=True, is_valid_password=False)
+        # test until one try before the limit
+        for i in range(1, FAILURE_LIMIT):
+            response = self._login(
+                is_valid_username=True,
+                is_valid_password=False,
+            )
             # Check if we are in the same login page
             self.assertContains(response, self.LOGIN_FORM_KEY)
 
         # So, we shouldn't have gotten a lock-out yet.
         # But we should get one now
         response = self._login(is_valid_username=True, is_valid_password=False)
-        self.assertContains(response, self.LOCKED_MESSAGE)
+        self.assertContains(response, self.LOCKED_MESSAGE, status_code=403)
+
+    def test_log_data_truncated(self):
+        """Tests that query2str properly truncates data to the max_length (default 1024)
+        """
+        # An impossibly large post dict
+        extra_data = {string.ascii_letters * x: x for x in range(0, 1000)}
+        self._login(**extra_data)
+        self.assertEquals(
+            len(AccessAttempt.objects.latest('id').post_data), 1024
+        )
+
+    def test_json_response(self):
+        """Tests response content type and status code for the ajax request
+        """
+        self.test_failure_limit_once()
+        response = self._login(is_json=True)
+        self.assertEquals(response.status_code, 403)
+        self.assertEquals(response.get('Content-Type'), 'application/json')
+
+
+class IPClassifierTest(TestCase):
+    def test_classify_private_ips(self):
+        """Tests whether is_valid_public_ip correctly classifies IPs as being
+        bot public and valid
+        """
+        EXPECTED = {
+            'foobar': False,  # invalid - not octects
+            '192.168.0': False,  # invalid - only 3 octets
+            '192.168.0.0': False,  # private
+            '192.168.165.1': False,  # private
+            '192.249.19.1': True,  # public but 192 prefix
+            '10.0.201.13': False,  # private
+            '172.15.12.1': True,  # public but 172 prefix
+            '172.16.12.1': False,  # private
+            '172.31.12.1': False,  # private
+            '172.32.0.1': True,  # public but 127 prefix
+            '200.150.23.5': True,  # normal public
+        }
+        for ip_address, is_valid_public in six.iteritems(EXPECTED):
+            self.assertEqual(is_valid_ip(ip_address), is_valid_public)
+
+
+class UtilsTest(TestCase):
+    def test_iso8601(self):
+        """Tests iso8601 correctly translates datetime.timdelta to ISO 8601
+        formatted duration."""
+        EXPECTED = {
+            datetime.timedelta(days=1, hours=25, minutes=42, seconds=8):
+                'P2DT1H42M8S',
+            datetime.timedelta(days=7, seconds=342):
+                'P7DT5M42S',
+            datetime.timedelta(days=0, hours=2, minutes=42):
+                'PT2H42M',
+            datetime.timedelta(hours=20, seconds=42):
+                'PT20H42S',
+            datetime.timedelta(seconds=300):
+                'PT5M',
+            datetime.timedelta(seconds=9005):
+                'PT2H30M5S',
+            datetime.timedelta(minutes=9005):
+                'P6DT6H5M',
+            datetime.timedelta(days=15):
+                'P15D'
+        }
+        for timedelta, iso_duration in six.iteritems(EXPECTED):
+            self.assertEqual(iso8601(timedelta), iso_duration)

+ 41 - 0
desktop/core/ext-py/django-axes-2.2.0/axes/utils.py

@@ -0,0 +1,41 @@
+from axes.models import AccessAttempt
+
+
+def reset(ip=None, username=None):
+    """Reset records that match ip or username, and
+    return the count of removed attempts.
+    """
+    count = 0
+
+    attempts = AccessAttempt.objects.all()
+    if ip:
+        attempts = attempts.filter(ip_address=ip)
+    if username:
+        attempts = attempts.filter(username=username)
+
+    if attempts:
+        count = attempts.count()
+        attempts.delete()
+
+    return count
+
+
+def iso8601(timestamp):
+    """Returns datetime.timedelta translated to ISO 8601 formatted duration.
+    """
+    seconds = timestamp.total_seconds()
+    minutes, seconds = divmod(seconds, 60)
+    hours, minutes = divmod(minutes, 60)
+    days, hours = divmod(hours, 24)
+
+    date = '{:.0f}D'.format(days) if days else ''
+
+    time_values = hours, minutes, seconds
+    time_designators = 'H', 'M', 'S'
+
+    time = ''.join([
+        ('{:.0f}'.format(value) + designator)
+        for value, designator in zip(time_values, time_designators)
+        if value]
+    )
+    return u'P' + date + (u'T' + time if time else '')

+ 15 - 0
desktop/core/ext-py/django-axes-2.2.0/runtests.py

@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+import os
+import sys
+
+import django
+from django.conf import settings
+from django.test.utils import get_runner
+
+if __name__ == "__main__":
+    os.environ['DJANGO_SETTINGS_MODULE'] = 'axes.test_settings'
+    django.setup()
+    TestRunner = get_runner(settings)
+    test_runner = TestRunner()
+    failures = test_runner.run_tests(["axes"])
+    sys.exit(bool(failures))

+ 5 - 0
desktop/core/ext-py/django-axes-2.2.0/setup.cfg

@@ -0,0 +1,5 @@
+[egg_info]
+tag_build = 
+tag_date = 0
+tag_svn_revision = 0
+

+ 6 - 8
desktop/core/ext-py/django-axes-1.5.0/setup.py → desktop/core/ext-py/django-axes-2.2.0/setup.py

@@ -1,19 +1,18 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
-
-import os
+import codecs
 from setuptools import setup, find_packages
 
-VERSION = '1.5.0'
+from axes import get_version
 
 setup(
     name='django-axes',
-    version=VERSION,
+    version=get_version(),
     description="Keep track of failed login attempts in Django-powered sites.",
     long_description=(
-        open("README.rst").read() + '\n' +
-        open("CHANGES.txt").read()),
-    keywords='authentication, django, pci, security',
+        codecs.open("README.rst", encoding='utf-8').read() + '\n' +
+        codecs.open("CHANGES.txt", encoding='utf-8').read()),
+    keywords='authentication django pci security'.split(),
     author='Josh VanderLinden, Philip Neustrom, Michael Blume, Camilo Nova',
     author_email='codekoala@gmail.com',
     maintainer='Alex Clark',
@@ -35,7 +34,6 @@ setup(
         'Programming Language :: Python :: 2.7',
         'Programming Language :: Python :: 3',
         'Topic :: Internet :: Log Analysis',
-        'Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware',
         'Topic :: Security',
         'Topic :: System :: Logging',
     ],