Skip to content

Instantly share code, notes, and snippets.

@webtweakers
Forked from parhammmm/models.py
Last active April 1, 2021 21:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save webtweakers/b75b98cfa22cfcfa9fe479ba67fed851 to your computer and use it in GitHub Desktop.
Save webtweakers/b75b98cfa22cfcfa9fe479ba67fed851 to your computer and use it in GitHub Desktop.
Encrypt and decrypt Django model primary key values (useful for publicly viewable unique identifiers) [python3 version]
# 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
# Python 3.x version
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)
if not isinstance(number, int):
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(value):
# convert a base-36 alphanumeric string to its numeric value
return int(value, 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:
# decrypt encrypted_pk argument and set pk argument to the appropriate value
key = bytes(getattr(self.model, 'PK_SECRET_KEY'), 'utf8')
encryption_obj = DES.new(key, DES.MODE_ECB)
kwargs['pk'] = struct.unpack('<Q', encryption_obj.decrypt(
struct.pack('<Q', base36decode(encrypted_pk))
))[0]
return super().get(*args, **kwargs)
class EncryptedPKModel(models.Model):
"""
Adds encrypted_pk property to children which returns the encrypted value of the primary key.
"""
PK_SECRET_KEY = 'default1'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
setattr(
self.__class__,
"encrypted_%s" % (self._meta.pk.name,),
property(self.__class__._encrypted_pk)
)
def _encrypted_pk(self):
key = bytes(getattr(self, 'PK_SECRET_KEY'), 'utf8')
encryption_obj = DES.new(key, DES.MODE_ECB)
return base36encode(struct.unpack('<Q', encryption_obj.encrypt(
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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment