Skip to content

Commit

Permalink
Merge pull request svetlyak40wt#50 from nautilebleu/master
Browse files Browse the repository at this point in the history
Add support for django 1.10
  • Loading branch information
svetlyak40wt authored Dec 31, 2016
2 parents 43b8bbf + d84bf54 commit efef8e8
Show file tree
Hide file tree
Showing 10 changed files with 258 additions and 186 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ parts/
test.sqlite
env
pip-log.txt
.tox
19 changes: 18 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,19 @@
language: python
script: ./run-tests.sh
python:
- "3.5"
- "3.4"
- "2.7"
sudo: false
env:
- TOX_ENV=django19
- TOX_ENV=django18
- TOX_ENV=django110
- TOX_ENV=djangomaster
matrix:
fast_finish: true
allow_failures:
- env: TOX_ENV=djangomaster
install:
- pip install tox
script:
- tox -e $TOX_ENV
5 changes: 0 additions & 5 deletions requirements.txt

This file was deleted.

10 changes: 0 additions & 10 deletions run-tests.sh

This file was deleted.

33 changes: 19 additions & 14 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,21 @@
version = '0.3.0'

setup(
name = 'django-fields',
version = version,
description = 'Django-fields is an application which includes different kinds of models fields.',
keywords = 'django apps tools collection',
license = 'New BSD License',
author = 'Alexander Artemenko',
author_email = 'svetlyak.40wt@gmail.com',
url = 'http://github.com/svetlyak40wt/django-fields/',
install_requires = ['pycrypto', ],
name='django-fields',
version=version,
description='Django-fields is an application which includes different kinds of models fields.',
keywords='django apps tools collection',
license='New BSD License',
author='Alexander Artemenko',
author_email='svetlyak.40wt@gmail.com',
url='http://github.com/svetlyak40wt/django-fields/',
install_requires=[
'django',
'pycrypto',
'nose',
'django-nose==1.4.4',
'tox',
],
classifiers=[
'Development Status :: 2 - Pre-Alpha',
'Environment :: Plugins',
Expand All @@ -21,9 +27,8 @@
'Programming Language :: Python',
'Topic :: Software Development :: Libraries :: Python Modules',
],
package_dir = {'': 'src'},
packages = ['django_fields'],
include_package_data = True,
package_dir={'': 'src'},
packages=['django_fields'],
include_package_data=True,
test_suite="runtests.runtests",
)


123 changes: 88 additions & 35 deletions src/django_fields/fields.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import binascii
import codecs
import datetime
import string
import sys
Expand All @@ -8,7 +9,6 @@
from django.forms import fields
from django.db import models
from django.conf import settings
from django.utils.encoding import smart_str, force_unicode
from django.utils.translation import ugettext_lazy as _
from Crypto import Random
from Crypto.Random import random
Expand All @@ -28,6 +28,14 @@
except:
import pickle

if sys.version_info[0] == 3:
PYTHON3 = True
from django.utils.encoding import smart_str, force_text as force_unicode
else:
PYTHON3 = False
from django.utils.encoding import smart_str, force_unicode


class BaseEncryptedField(models.Field):
'''This code is based on the djangosnippet #1095
You can find the original at http://www.djangosnippets.org/snippets/1095/'''
Expand Down Expand Up @@ -76,15 +84,20 @@ def __init__(self, *args, **kwargs):
super(BaseEncryptedField, self).__init__(*args, **kwargs)

def _is_encrypted(self, value):
return isinstance(value, basestring) and value.startswith(self.prefix)
if PYTHON3 is True:
is_enc = isinstance(value, str) and value.startswith(self.prefix)
return is_enc
else:
return isinstance(value, basestring) and value.startswith(
self.prefix)

def _get_padding(self, value):
# We always want at least 2 chars of padding (including zero byte),
# so we could have up to block_size + 1 chars.
mod = (len(value) + 2) % self.cipher.block_size
return self.cipher.block_size - mod + 2

def to_python(self, value):
def from_db_value(self, value, expression, connection, context):
if self._is_encrypted(value):
if self.block_type:
self.iv = binascii.a2b_hex(value[len(self.prefix):])[:len(self.iv)]
Expand All @@ -96,31 +109,51 @@ def to_python(self, value):
else:
decrypt_value = binascii.a2b_hex(value[len(self.prefix):])
return force_unicode(
self.cipher.decrypt(decrypt_value).split('\0')[0]
self.cipher.decrypt(decrypt_value).split(b'\0')[0]
)
return value

def get_db_prep_value(self, value, connection=None, prepared=False):
if value is None:
return None

value = smart_str(value)
if PYTHON3 is True:
value = bytes(value.encode('utf-8'))
else:
value = smart_str(value)

if not self._is_encrypted(value):
padding = self._get_padding(value)
if padding > 0:
value += "\0" + ''.join([
random.choice(string.printable)
for index in range(padding-1)
])
if PYTHON3 is True:
value += bytes("\0".encode('utf-8')) + bytes(
''.encode('utf-8')).join([
bytes(random.choice(
string.printable).encode('utf-8'))
for index in range(padding - 1)])
else:
value += "\0" + ''.join([
random.choice(string.printable)
for index in range(padding - 1)
])
if self.block_type:
self.cipher = self.cipher_object.new(
self.secret_key,
getattr(self.cipher_object, self.block_type),
self.iv)
value = self.prefix + binascii.b2a_hex(self.iv + self.cipher.encrypt(value))
if PYTHON3 is True:
value = self.prefix + binascii.b2a_hex(
self.iv + self.cipher.encrypt(value)).decode('utf-8')
else:
value = self.prefix + binascii.b2a_hex(
self.iv + self.cipher.encrypt(value))
else:
value = self.prefix + binascii.b2a_hex(self.cipher.encrypt(value))
if PYTHON3 is True:
value = self.prefix + binascii.b2a_hex(
self.cipher.encrypt(value)).decode('utf-8')
else:
value = self.prefix + binascii.b2a_hex(
self.cipher.encrypt(value))
return value

def deconstruct(self):
Expand All @@ -136,7 +169,6 @@ def deconstruct(self):


class EncryptedTextField(BaseEncryptedField):
__metaclass__ = models.SubfieldBase

def get_internal_type(self):
return 'TextField'
Expand All @@ -148,7 +180,6 @@ def formfield(self, **kwargs):


class EncryptedCharField(BaseEncryptedField):
__metaclass__ = models.SubfieldBase

def get_internal_type(self):
return "CharField"
Expand Down Expand Up @@ -191,6 +222,9 @@ def formfield(self, **kwargs):
return super(BaseEncryptedDateField, self).formfield(**defaults)

def to_python(self, value):
return self.from_db_value(value)

def from_db_value(self, value, expression, connection, context):
# value is either a date or a string in the format "YYYY:MM:DD"

if value in fields.EMPTY_VALUES:
Expand All @@ -199,7 +233,8 @@ def to_python(self, value):
if isinstance(value, self.date_class):
date_value = value
else:
date_text = super(BaseEncryptedDateField, self).to_python(value)
date_text = super(BaseEncryptedDateField, self).from_db_value(
value, expression, connection, context)
date_value = self.date_class(*map(int, date_text.split(':')))
return date_value

Expand All @@ -218,7 +253,6 @@ def get_db_prep_value(self, value, connection=None, prepared=False):


class EncryptedDateField(BaseEncryptedDateField):
__metaclass__ = models.SubfieldBase
form_widget = forms.DateInput
form_field = forms.DateField
save_format = "%Y:%m:%d"
Expand All @@ -228,7 +262,6 @@ class EncryptedDateField(BaseEncryptedDateField):

class EncryptedDateTimeField(BaseEncryptedDateField):
# FIXME: This doesn't handle time zones, but Python doesn't really either.
__metaclass__ = models.SubfieldBase
form_widget = forms.DateTimeInput
form_field = forms.DateTimeField
save_format = "%Y:%m:%d:%H:%M:%S:%f"
Expand All @@ -248,11 +281,15 @@ def get_internal_type(self):
return 'CharField'

def to_python(self, value):
return self.from_db_value(value)

def from_db_value(self, value, expression, connection, context):
# value is either an int or a string of an integer
if isinstance(value, self.number_type) or value == '':
number = value
else:
number_text = super(BaseEncryptedNumberField, self).to_python(value)
number_text = super(BaseEncryptedNumberField, self).from_db_value(
value, expression, connection, context)
number = self.number_type(number_text)
return number

Expand All @@ -267,47 +304,67 @@ def get_db_prep_value(self, value, connection=None, prepared=False):


class EncryptedIntField(BaseEncryptedNumberField):
__metaclass__ = models.SubfieldBase
max_raw_length = len(str(-sys.maxint - 1))
if PYTHON3 is True:
max_raw_length = len(str(-sys.maxsize - 1))
else:
max_raw_length = len(str(-sys.maxint - 1))
number_type = int
format_string = "%d"


class EncryptedLongField(BaseEncryptedNumberField):
__metaclass__ = models.SubfieldBase
max_raw_length = None # no limit
number_type = long
if PYTHON3 is True:
number_type = int
else:
number_type = long
format_string = "%d"

def get_internal_type(self):
return 'TextField'


class EncryptedFloatField(BaseEncryptedNumberField):
__metaclass__ = models.SubfieldBase
max_raw_length = 150 # arbitrary, but should be sufficient
number_type = float
# If this format is too long for some architectures, change it.
format_string = "%0.66f"


class PickleField(models.TextField):
__metaclass__ = models.SubfieldBase

editable = False
serialize = False

def get_db_prep_value(self, value, connection=None, prepared=False):
return pickle.dumps(value)
if PYTHON3 is True:
# When PYTHON3, we convert data to base64 to prevent errors when
# unpickling.
val = codecs.encode(pickle.dumps(value), 'base64').decode()
return val
else:
return pickle.dumps(value)

def to_python(self, value):
if not isinstance(value, basestring):
return value
return self.from_db_value(value)

def from_db_value(self, value, expression, connection, context):
if PYTHON3 is True:
if not isinstance(value, str):
return value
else:
if not isinstance(value, basestring):
return value

# Tries to convert unicode objects to string, cause loads pickle from
# unicode excepts ugly ``KeyError: '\x00'``.
try:
return pickle.loads(smart_str(value))
if PYTHON3 is True:
# When PYTHON3, data are in base64 to prevent errors when
# unpickling.
val = pickle.loads(codecs.decode(value.encode(), "base64"))
return val
else:
return pickle.loads(smart_str(value))
# If pickle could not loads from string it's means that it's Python
# string saved to PickleField.
except ValueError:
Expand All @@ -317,14 +374,12 @@ def to_python(self, value):


class EncryptedUSPhoneNumberField(BaseEncryptedField):
__metaclass__ = models.SubfieldBase

def get_internal_type(self):
return "CharField"

def formfield(self, **kwargs):
try:
from localflavor.us.forms import USPhoneNumberField
from localflavor.us.forms import USPhoneNumberField
except ImportError:
from django.contrib.localflavor.us.forms import USPhoneNumberField

Expand All @@ -334,23 +389,21 @@ def formfield(self, **kwargs):


class EncryptedUSSocialSecurityNumberField(BaseEncryptedField):
__metaclass__ = models.SubfieldBase

def get_internal_type(self):
return "CharField"

def formfield(self, **kwargs):
try:
from localflavor.us.forms import USSocialSecurityNumberField
except ImportError:
from django.contrib.localflavor.us.forms import USSocialSecurityNumberField
from django.contrib.localflavor.us.forms import USSocialSecurityNumberField

defaults = {'form_class': USSocialSecurityNumberField}
defaults.update(kwargs)
return super(EncryptedUSSocialSecurityNumberField, self).formfield(**defaults)


class EncryptedEmailField(BaseEncryptedField):
__metaclass__ = models.SubfieldBase
description = _("E-mail address")

def get_internal_type(self):
Expand Down
Loading

0 comments on commit efef8e8

Please sign in to comment.