Skip to content

Instantly share code, notes, and snippets.

@parhammmm
Forked from treyhunner/models.py
Created December 8, 2012 19:20
Show Gist options
  • Save parhammmm/4241508 to your computer and use it in GitHub Desktop.
Save parhammmm/4241508 to your computer and use it in GitHub Desktop.
Encrypt and decrypt Django model primary key values (useful for publicly viewable unique identifiers)
import struct
from Crypto.Cipher import DES
from django.db import models
def base36encode(number):
"""Encode number to string of characters in the range 0 9 and a to z. (Code taken from Wikipedia)."""
if not isinstance(number, (int, long)):
raise TypeError('number must be an integer')
if number < 0:
raise ValueError('number must be positive')
alphabet = '0123456789abcdefghijklmnopqrstuvwxyz'
base36 = ''
while number:
number, i = divmod(number, 36)
base36 = alphabet[i] + base36
return base36 or alphabet[0]
def base36decode(numstr):
"""Convert a base-36 string (made of alphanumeric characters) to its numeric value."""
return int(numstr,36)
class EncryptedPKModelManager(models.Manager):
"""This manager allows models to be identified based on their encrypted_pk value."""
def get(self, *args, **kwargs):
encrypted_pk = kwargs.pop('encrypted_pk', None)
if encrypted_pk:
# If found, decrypt encrypted_pk keyword argument and set pk argument to the appropriate value
kwargs['pk'] = struct.unpack('<Q', self.model.encryption_obj.decrypt(
struct.pack('<Q', base36decode(encrypted_pk))
))[0]
return super(EncryptedPKModelManager, self).get(*args, **kwargs)
class EncryptedPKModel(models.Model):
"""Adds encrypted_pk property to children which returns the encrypted value of the primary key."""
encryption_obj = DES.new('8charkey') # This 8 character secret key should be changed!
def __init__(self, *args, **kwargs):
super(EncryptedPKModel, self).__init__(*args, **kwargs)
setattr(
self.__class__,
"encrypted_%s" % (self._meta.pk.name,),
property(self.__class__._encrypted_pk)
)
def _encrypted_pk(self):
return base36encode(struct.unpack('<Q', self.encryption_obj.encrypt(
str(struct.pack('<Q', self.pk))
))[0])
encrypted_pk = property(_encrypted_pk)
class Meta:
abstract = True
class ExampleModelManager(EncryptedPKModelManager):
pass
class ExampleModel(EncryptedPKModel):
objects = ExampleModelManager()
example_field = models.CharField(max_length=32)
# Example usage:
# example_instance = ExampleModel.objects.get(pk=1)
# url_pk = example_instance.encrypted_pk
# ExampleModel.objects.get(encrypted_pk=url_pk)
@tomgruner
Copy link

Nice! Thanks for the gist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment