Permalink
Browse files

Initial commit

  • Loading branch information...
0 parents commit 2bf513b3e0fd0c7dc725808db3083446ead9afe2 @tomatohater committed Aug 10, 2011
@@ -0,0 +1,4 @@
+*.pyc
+.DS_Store
+*.egg-info
+dist
@@ -0,0 +1 @@
+https://github.com/tomatohater/django-unfriendly/contributors/
@@ -0,0 +1,2 @@
+include AUTHORS
+include README.rst
@@ -0,0 +1,64 @@
+django-unfriendly
+========================
+
+django-unfriendly is a Django app that obfuscates URLs and allows your application to handle with your native Django views.
+
+There is lots of talk about SEO friendly URLs. The trend is towards more and more readable information in your URLs. Django makes it easy to create URLs like::
+
+ http://yoursite.com/music/black-sabbath-is-awesome/
+
+But sometimes these URLs can give too much away. This is where django-unfriendly comes in.
+
+django-unfriendly provides a template filter that obfuscates URLs in your templates, and then provides a URL handler that deobfuscates and executes the original view.
+
+Why?
+****
+
+Perhaps you have a Django application with URLs like the one above. And you don't want anyone tampering with your URLs by guessing other possibilities like this::::
+
+ http://yoursite.com/music/melvins-are-awesome/
+
+You can apply the obfuscation filter to in your template tag which might result in a URL like this::
+
+ http://yoursite.com/u/E5v4uxuNSA8I2is33c6V8lqFTcdv_IxPLDGG/
+
+
+Installation
+************
+
+1. ``easy_install django-unfriendly`` or ``pip install django-unfriendly``
+
+2. Add ``unfriendly`` to your ``INSTALLED_APPS``
+
+3. Add ``unfriendly.urls`` to your ``urls.py``::
+
+ urlpatterns = patterns('',
+ url(r'^u/', include('unfriendly.urls')),
+ )
+
+
+Usage
+******
+Load this tag library into any templates where you want to use django-unfriendly::
+
+ {% load unfriendly_tags %}
+
+Then apply the obfuscate filter to any URL you'd like to hide::
+
+ <a href="{{ "/music/black-sabbath-is-awesome/"|obfuscate }}">Sabbath awesome</a>
+
+Or with the ``url`` reversal::
+
+ {% url path.to.view as melvins_url %}
+ <a href="{{ melvins_url|obfuscate }}">Melvins awesome</a>
+
+If SEO is still important to you, you can pass some SEO juice to the filter::
+
+ <a href="{{ melvins_url|obfuscate:"King Buzzo rocks" }}">Melvins awesome</a>
+
+
+Credits
+********
+`Drew Engelson`_
+
+.. _`Drew Engelson`: http://github.com/tomatohater
@@ -0,0 +1,32 @@
+from setuptools import setup, find_packages
+import unfriendly
+import os
+
+def read(fname):
+ return open(os.path.join(os.path.dirname(__file__), fname)).read()
+
+README = read('README.rst')
+
+setup(
+ name = "django-unfriendly",
+ version = unfriendly.__version__,
+ description = 'The unfriendliest urls in town.',
+ long_description = README,
+ url = 'http://github.com/tomatohater/django-unfriendly',
+ author = 'Drew Engelson',
+ author_email = 'drew@engelson.net',
+ license = 'BSD',
+ zip_safe = False,
+ packages = find_packages(),
+ include_package_data = True,
+ package_data = {},
+ classifiers = [
+ 'Environment :: Web Environment',
+ 'Framework :: Django',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: BSD License',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python',
+ 'Topic :: Internet :: WWW/HTTP',
+ ]
+)
@@ -0,0 +1,2 @@
+VERSION = (0, 1, '1dev')
+__version__ = '.'.join(map(str, VERSION))
@@ -0,0 +1,7 @@
+from django.conf import settings
+
+"""
+UNFRIENDLY_SECRET is used for obfuscation and encryption.
+"""
+UNFRIENDLY_SECRET = getattr(settings, 'UNFRIENDLY_SECRET',
+ getattr(settings, 'SECRET_KEY', 'hush-hush'))
No changes.
@@ -0,0 +1,34 @@
+from django import template
+from django.core.urlresolvers import reverse
+from django.template.defaultfilters import slugify
+
+from unfriendly import settings
+from unfriendly.utils import Obfuscator
+
+
+register = template.Library()
+
+
+@register.filter
+def obfuscate(value, juice=None):
+ """
+ Template filter that obfuscates whatever text it is applied to. The text is
+ supposed to be a URL, but it will obfuscate any text.
+
+ Usage:
+ Extremely unfriendly URL:
+ {{ "/my-site-path/"|obfuscate }}
+
+ Include some SEO juice:
+ {{ "/my-site-path/"|obfuscate:"some SEO friendly text" }}
+ """
+ obfuscator = Obfuscator(settings.UNFRIENDLY_SECRET)
+
+ kwargs = {
+ 'key': obfuscator.obfuscate(*[value]),
+ }
+
+ if juice:
+ kwargs['juice'] = slugify(juice)
+
+ return reverse('unfriendly-deobfuscate', kwargs=kwargs)
@@ -0,0 +1,9 @@
+from django.conf.urls.defaults import *
+
+urlpatterns = patterns('unfriendly.views',
+ # Mostly unfriendly URL (but with SEO juice).
+ url(r'^(?P<juice>.+)/(?P<key>.+)/$', 'deobfuscate', name='unfriendly-deobfuscate'),
+
+ # Extremely unfriendly URL (no SEO juice).
+ url(r'^(?P<key>.+)/$', 'deobfuscate', name='unfriendly-deobfuscate'),
+)
@@ -0,0 +1,47 @@
+import base64
+
+from django.utils.hashcompat import sha_constructor
+from django.conf import settings
+
+
+class Obfuscator(object):
+ """
+ Handles string obfuscation and deobfuscation.
+
+ Usage:
+ >>> o = Obfuscator('secret-key')
+ >>> o.obfuscate('cleartext')
+ 'vJqxni-3Mrcx'
+ >>> o.deobfuscate('vJqxni-3Mrcx')
+ 'cleartext'
+ """
+ obfuscation_key = None
+
+ def __init__(self, secret):
+ self.obfuscation_key = (sha_constructor(secret).digest() +
+ sha_constructor(secret[::-1]).digest())
+
+
+ def xor_map_string(self, data):
+ """
+ Returns obfuscated string (also deobfuscates).
+ """
+ key = self.obfuscation_key * (len(data)//len(self.obfuscation_key) + 1)
+ xor_gen = (chr(ord(t) ^ ord(k)) for t, k in zip(data, key))
+ return ''.join(xor_gen)
+
+
+ def obfuscate(self, data):
+ """
+ Obfuscates string in url-safe fashion.
+ """
+ return base64.urlsafe_b64encode(
+ self.xor_map_string(data)).replace('=', '')
+
+
+ def deobfuscate(self, data):
+ """
+ Deobfuscates string.
+ """
+ return self.xor_map_string(
+ base64.urlsafe_b64decode(data + ('=' * (len(data) % 4))))
@@ -0,0 +1,32 @@
+from urlparse import urlparse
+
+from django.core.urlresolvers import resolve, Resolver404
+from django.http import HttpResponse, HttpResponseNotFound, QueryDict
+
+from unfriendly import settings
+from unfriendly.utils import Obfuscator
+
+
+def deobfuscate(request, key, juice=None):
+ """
+ Returns HttpResponse from original obfuscated view.
+ SEO juice is ignored since it is only for URL display purposes.
+ """
+ obfuscator = Obfuscator(settings.UNFRIENDLY_SECRET)
+ try:
+ url = obfuscator.deobfuscate(str(key))
+ except:
+ return HttpResponseNotFound()
+ url_parts = urlparse(url)
+ path = url_parts.path
+ query = url_parts.query
+
+ try:
+ view, args, kwargs = resolve(path)
+ except Resolver404:
+ return HttpResponseNotFound()
+
+ if query:
+ request.GET = QueryDict(query)
+
+ return view(request, *args, **kwargs)

0 comments on commit 2bf513b

Please sign in to comment.