From 38e48bef8445eef8b27bd62662b4daded004fbe3 Mon Sep 17 00:00:00 2001 From: Michael Porokhovnichenko Date: Mon, 19 Jan 2015 00:38:15 +0600 Subject: [PATCH 01/12] prepare to next version (ci) --- coverage.rc => .coveragerc | 0 .travis.yml | 10 +- CHANGELOG.rst | 20 ++ Makefile | 13 +- disguise/__init__.py | 6 + disguise/compat.py | 24 ++ disguise/const.py | 14 + disguise/forms.py | 41 ++- disguise/middleware.py | 98 +----- disguise/models.py | 31 +- disguise/signals.py | 10 + .../static/disguise/js/disguise-angular.js | 11 + .../static/disguise/js/disguise-jquery.js | 5 + disguise/templates/disguise/form.html | 61 ++-- disguise/templates/disguise/tests/index.html | 14 +- disguise/templatetags/__init__.py | 6 + disguise/templatetags/disguise_tags.py | 24 ++ disguise/tests/__init__.py | 329 ++++++++++++------ disguise/tests/urls.py | 18 - disguise/urls.py | 6 +- disguise/utils.py | 104 ++++++ disguise/views.py | 113 +++--- setup.cfg | 3 + setup.py | 19 +- tests.py | 8 +- 25 files changed, 642 insertions(+), 346 deletions(-) rename coverage.rc => .coveragerc (100%) create mode 100644 CHANGELOG.rst create mode 100644 disguise/compat.py create mode 100644 disguise/const.py create mode 100644 disguise/signals.py create mode 100644 disguise/static/disguise/js/disguise-angular.js create mode 100644 disguise/static/disguise/js/disguise-jquery.js create mode 100644 disguise/templatetags/__init__.py create mode 100644 disguise/templatetags/disguise_tags.py delete mode 100644 disguise/tests/urls.py create mode 100644 disguise/utils.py create mode 100644 setup.cfg diff --git a/coverage.rc b/.coveragerc similarity index 100% rename from coverage.rc rename to .coveragerc diff --git a/.travis.yml b/.travis.yml index f395564..50be9e0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,10 +8,10 @@ python: env: - - DJANGO=django==1.4.15 - - DJANGO=django==1.5.10 - - DJANGO=django==1.6.7 - - DJANGO=django==1.7 + - DJANGO=django==1.4.18 + - DJANGO=django==1.5.13 + - DJANGO=django==1.6.10 + - DJANGO=django==1.7.3 matrix: @@ -33,7 +33,7 @@ before_script: - make flake8 script: - - make test + - make coverage after_success: - make coveralls diff --git a/CHANGELOG.rst b/CHANGELOG.rst new file mode 100644 index 0000000..d961509 --- /dev/null +++ b/CHANGELOG.rst @@ -0,0 +1,20 @@ +Changes +======= + +0.1 +--- + +* Permissions for disguise now linked with User model; +* Using django system check framework in newest versions; +* Disguise widget become a template tag; earlier it added into page with middleware; +* Migrated to CBV views; +* Code imporvements (pep8); +* Added coverage support; +* Added signals; +* Removed the ``update_user_login`` feature prior to custom signal handling; + + +0.0.3 +----- + +* Travis CI integration diff --git a/Makefile b/Makefile index 49bfac5..0b2a70f 100644 --- a/Makefile +++ b/Makefile @@ -1,23 +1,26 @@ test: python setup.py test + release: python setup.py sdist --format=zip,bztar,gztar register upload + python setup.py bdist_wheel register upload flake8: - flake8 --ignore=E501 disguise - flake8 --ignore=E501 tests.py - flake8 --ignore=E501 setup.py + flake8 \ + disguise \ + tests.py \ + setup.py coverage: - coverage run --include=disguise/*.py setup.py test + coverage run --rcfile=.coveragerc --include=disguise/*.py setup.py test coverage html coveralls: - coveralls --config_file=coverage.rc + coveralls clean: rm -rf *.egg *.egg-info diff --git a/disguise/__init__.py b/disguise/__init__.py index e69de29..1f6693a 100644 --- a/disguise/__init__.py +++ b/disguise/__init__.py @@ -0,0 +1,6 @@ +# coding: utf-8 + +from __future__ import unicode_literals +from __future__ import print_function +from __future__ import absolute_import +from __future__ import division diff --git a/disguise/compat.py b/disguise/compat.py new file mode 100644 index 0000000..258219a --- /dev/null +++ b/disguise/compat.py @@ -0,0 +1,24 @@ +# coding: utf-8 + +from __future__ import unicode_literals +from __future__ import print_function +from __future__ import absolute_import +from __future__ import division + + +try: + from django.contrib.auth import get_user_model +except ImportError: + from django.contrib.auth.models import User + get_user_model = lambda: User + +try: + from django.core import checks +except ImportError: + from django.core.exceptions import ImproperlyConfigured + + class Checks(object): + class Error(object): + def __init__(self, message, hint='', error=''): + raise ImproperlyConfigured(message) + checks = Checks() diff --git a/disguise/const.py b/disguise/const.py new file mode 100644 index 0000000..27a7c8d --- /dev/null +++ b/disguise/const.py @@ -0,0 +1,14 @@ +# coding: utf-8 + +from __future__ import unicode_literals +from __future__ import print_function +from __future__ import absolute_import +from __future__ import division +from django.contrib.auth import SESSION_KEY, BACKEND_SESSION_KEY + +KEYNAME = 'django_disguise:original_user' +TAGNAME = 'body' +BACKEND = 'django.contrib.auth.backends.ModelBackend' + +__all__ = ['KEYNAME', 'TAGNAME', 'SESSION_KEY', 'BACKEND_SESSION_KEY', + 'BACKEND'] diff --git a/disguise/forms.py b/disguise/forms.py index a3d2afe..8adec72 100644 --- a/disguise/forms.py +++ b/disguise/forms.py @@ -1,36 +1,37 @@ +# coding: utf-8 + +from __future__ import unicode_literals +from __future__ import print_function +from __future__ import absolute_import +from __future__ import division from django import forms from django.utils.translation import ugettext_lazy as _ +from disguise.compat import get_user_model + -try: - from django.contrib.auth import get_user_model - User = get_user_model() -except ImportError: - from django.contrib.auth.models import User +User = get_user_model() class DisguiseForm(forms.Form): """ Disguise form """ - username = forms.CharField(label=_('User name'), - required=False) - user_id = forms.IntegerField(label=_('User ID'), - required=False) - update_last_login = forms.BooleanField(label=_('Update last login'), - required=False) + username = forms.CharField(label=_('User name'), required=False) + user_id = forms.IntegerField(label=_('User ID'), required=False) def clean_username(self): """ Cleans username field """ username = self.cleaned_data.get('username') + if not username: return None qset = User.objects.filter(username=username) - if len(qset) == 1: - return qset[0] + if qset.exists(): + return qset.get() raise forms.ValidationError(_('No such username')) def clean_user_id(self): @@ -43,16 +44,21 @@ def clean_user_id(self): return None qset = User.objects.filter(pk=user_id) - if len(qset) == 1: - return qset[0] + if qset.exists(): + return qset.get() raise forms.ValidationError(_('No such user id')) def clean(self): """ Clears whole form totally """ - if not getattr(self, 'cleaned_data'): - raise forms.ValidationError(_('No such username or user id')) + cleaned_data = getattr(self, 'cleaned_data', {}) + + if not cleaned_data.get('user_id') and \ + not cleaned_data.get('username'): + raise forms.ValidationError( + _('Please enter either username or user id') + ) return self.cleaned_data def get_user(self): @@ -67,4 +73,3 @@ def get_user(self): if not isinstance(user, User): continue return user - raise ValueError('Cannot retrieve user') diff --git a/disguise/middleware.py b/disguise/middleware.py index eab7596..a65f49c 100644 --- a/disguise/middleware.py +++ b/disguise/middleware.py @@ -1,39 +1,12 @@ # coding: utf-8 -from django.template import RequestContext -from django.template.loader import render_to_string -from disguise.forms import DisguiseForm -import warnings - - -try: - from django.utils.encoding import smart_unicode as smart_text -except ImportError: - from django.utils.encoding import smart_text - - -try: - from django.contrib.auth import get_user_model -except ImportError: - from django.contrib.auth.models import User - get_user_model = lambda: User - - -KEYNAME = 'django_disguise:original_user' -TAGNAME = '' - - -def replace_insensitive(string, target, replacement): - """ - Similar to string.replace() but is case insensitive - Code borrowed from: http://forums.devshed.com/python-programming-11/case-insensitive-string-replace-490921.html - """ - no_case = string.lower() - index = no_case.rfind(target.lower()) - if index >= 0: - return string[:index] + replacement + string[index + len(target):] - else: # no results so return the original string - return string +from __future__ import unicode_literals +from __future__ import print_function +from __future__ import absolute_import +from __future__ import division +from disguise.compat import get_user_model +from disguise.utils import can_disguise +from disguise.const import KEYNAME class DisguiseMiddleware(object): @@ -41,18 +14,6 @@ class DisguiseMiddleware(object): Disguise user middleware """ - def test_disguise(self, request): - if KEYNAME in request.session: - return True - - if hasattr(request, 'original_user'): - return True - - if request.user.has_perm('disguire.can_disguise'): - return True - - return False - def get_original_user(self, request): if KEYNAME in request.session: return get_user_model().objects.get(pk=request.session[KEYNAME]) @@ -60,51 +21,10 @@ def get_original_user(self, request): def process_request(self, request): """ - Runs during each request + Injects the `original_user` attribute into HttpRequest object """ - if not hasattr(request, 'user'): - warnings.warn("DisguiseMiddleware must be installed after " - "django.contrib.auth.middleware.AuthenticationMiddleware") - return - - if not hasattr(request, 'session'): - warnings.warn("DisguiseMiddleware must be installed after " - "django.contrib.sessions.middleware.SessionMiddleware") - return - if not request.user.is_authenticated(): return - - if not self.test_disguise(request): + if not can_disguise(request): return - request.original_user = self.get_original_user(request) - - def process_response(self, request, response): - """ - Runs during responding - """ - if not request.user.is_authenticated(): - return response - - if not response.status_code == 200: - return response - - if not response.get('content-type', '').startswith('text/html'): - return response - - if self.test_disguise(request): - # Render HTML code that helps to select disguise - html = render_to_string('disguise/form.html', { - 'form': DisguiseForm(), - 'original_user': getattr(request, 'original_user', None), - 'disguise_user': getattr(request, 'user'), - }, RequestContext(request)) - - # Insert this code before - response.content = replace_insensitive( - smart_text(response.content), # Subject - TAGNAME, # Search - smart_text(html + TAGNAME) # Replace - ) - return response diff --git a/disguise/models.py b/disguise/models.py index 884fd8d..fb79cea 100644 --- a/disguise/models.py +++ b/disguise/models.py @@ -1,26 +1,31 @@ +# coding: utf-8 + +from __future__ import unicode_literals +from __future__ import print_function +from __future__ import absolute_import +from __future__ import division from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.db.models.signals import post_save, post_syncdb +from django.utils.translation import ugettext_lazy as _ +from disguise.compat import get_user_model def create_perms(sender, **kwargs): perms = ( - ('can_disguise', 'Can disguise'), + ('can_disguise', _('Can disguise')), ) - # create a content type for the app if it doesn't already exist - content_type, created = ContentType.objects.get_or_create( - model='', - app_label='disguise', - defaults={'name': 'disguise'}) + content_type = ContentType.objects.get_for_model(get_user_model()) + for codename, title in perms: - # create a permission if it doesn't already exist - Permission.objects.get_or_create(codename=codename, - content_type__pk=content_type.id, - defaults={ - 'name': title, - 'content_type': content_type - }) + Permission.objects.get_or_create( + codename=codename, + content_type=content_type, + defaults={ + 'name': title, + }) + post_save.connect(create_perms, Permission) post_syncdb.connect(create_perms, sender=__import__('disguise')) diff --git a/disguise/signals.py b/disguise/signals.py new file mode 100644 index 0000000..6daf5e2 --- /dev/null +++ b/disguise/signals.py @@ -0,0 +1,10 @@ +# coding: utf-8 + +from __future__ import unicode_literals +from __future__ import print_function +from __future__ import absolute_import +from __future__ import division +from django.core.signals import Signal + +disguise_applied = Signal(providing_args=['original_user', 'new_user']) +disguise_disapplied = Signal(providing_args=['original_user', 'old_user']) diff --git a/disguise/static/disguise/js/disguise-angular.js b/disguise/static/disguise/js/disguise-angular.js new file mode 100644 index 0000000..0707895 --- /dev/null +++ b/disguise/static/disguise/js/disguise-angular.js @@ -0,0 +1,11 @@ +(function(angular, undefined){ + "use strict"; + angular + .module("ngDjangoDisguise", []) + .directive("disguise", { + restrict: "A", + scope: true, + function($scope, element, attrs, ctrl) { + } + }) +})(angular) diff --git a/disguise/static/disguise/js/disguise-jquery.js b/disguise/static/disguise/js/disguise-jquery.js new file mode 100644 index 0000000..ee1ce49 --- /dev/null +++ b/disguise/static/disguise/js/disguise-jquery.js @@ -0,0 +1,5 @@ +(function($, undefined){ + "use strict"; + $(document).on('#disguise', 'submit', function(e){ + }); +})((django ? django.jQuery : false) || jQuery); diff --git a/disguise/templates/disguise/form.html b/disguise/templates/disguise/form.html index 061cff5..5eaff48 100644 --- a/disguise/templates/disguise/form.html +++ b/disguise/templates/disguise/form.html @@ -1,38 +1,45 @@ {% load i18n %} -{% load url from future %} -