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)
# This code is under the MIT license.
# Inspired by this StackOverflow question:
http://stackoverflow.com/questions/3295405/creating-django-objects-with-a-random-primary-key
import struct
from Crypto.Cipher import DES
from django.db import models
def base36encode(number):
"""Encode number to string of alphanumeric characters (0 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 argument and set pk argument to the appropriate value
encryption_obj = DES.new(self.model.PK_SECRET_KEY) # This 8 character secret key should be changed!
kwargs['pk'] = struct.unpack('<Q', encryption_obj.decrypt(
struct.pack('<Q', base36decode(encrypted_pk))
))[0]
return super(EncryptedPKManager, self).get(*args, **kwargs)
class EncryptedPKModel(models.Model):
"""Adds encrypted_pk property to children which returns the encrypted value of the primary key."""
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):
encryption_obj = DES.new(self.PK_SECRET_KEY) # This 8 character secret key should be changed!
return base36encode(struct.unpack('<Q', 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):
PK_SECRET_KEY = '8charkey'
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