setup github actions with black, flake8, testing and coverage

This commit is contained in:
Hendrik Schneider 2022-04-21 14:15:59 +02:00
parent 04625a8e40
commit a20d77e256
9 changed files with 238 additions and 73 deletions

53
.github/lint-and-test.yml vendored Normal file
View file

@ -0,0 +1,53 @@
name: Lint & Test
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
permissions:
contents: read
jobs:
build:
strategy:
matrix:
django_version: [2.2, 3.0, 3.1, 3.2, 4.0a1]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -q Django==${{ matrix.django_version }}
pip install flake8 coverage black
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Lint with Black
run: |
black --check .
- name: Run tests
run: |
coverage3 run --source='./encrypted_fields' manage.py test
coverage xml
- name: "Upload coverage to Codecov"
uses: codecov/codecov-action@v2
with:
fail_ci_if_error: true

View file

@ -12,15 +12,25 @@ class EncryptedFieldMixin(object):
@cached_property
def keys(self):
keys = []
salt_keys = settings.SALT_KEY if isinstance(settings.SALT_KEY, list) else [settings.SALT_KEY]
salt_keys = (
settings.SALT_KEY
if isinstance(settings.SALT_KEY, list)
else [settings.SALT_KEY]
)
for salt_key in salt_keys:
salt = bytes(salt_key, 'utf-8')
kdf = PBKDF2HMAC(algorithm=hashes.SHA256(),
salt = bytes(salt_key, "utf-8")
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=100000,
backend=default_backend())
keys.append(base64.urlsafe_b64encode(kdf.derive(settings.SECRET_KEY.encode('utf-8'))))
backend=default_backend(),
)
keys.append(
base64.urlsafe_b64encode(
kdf.derive(settings.SECRET_KEY.encode("utf-8"))
)
)
return keys
@cached_property
@ -33,13 +43,13 @@ class EncryptedFieldMixin(object):
"""
To treat everything as text
"""
return 'TextField'
return "TextField"
def get_prep_value(self, value):
if value:
if not isinstance(value, str):
value = str(value)
return self.f.encrypt(bytes(value, 'utf-8')).decode('utf-8')
return self.f.encrypt(bytes(value, "utf-8")).decode("utf-8")
return None
def get_db_prep_value(self, value, connection, prepared=False):
@ -51,9 +61,13 @@ class EncryptedFieldMixin(object):
return self.to_python(value)
def to_python(self, value):
if value is None or not isinstance(value, str) or hasattr(self, '_already_decrypted'):
if (
value is None
or not isinstance(value, str)
or hasattr(self, "_already_decrypted")
):
return value
value = self.f.decrypt(bytes(value, 'utf-8')).decode('utf-8')
value = self.f.decrypt(bytes(value, "utf-8")).decode("utf-8")
return super(EncryptedFieldMixin, self).to_python(value)
def clean(self, value, model_instance):

53
lint-and-test.yml Normal file
View file

@ -0,0 +1,53 @@
name: Lint & Test
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
permissions:
contents: read
jobs:
build:
strategy:
matrix:
django_version: [2.2, 3.0, 3.1, 3.2, 4.0a1]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -q Django==${{ matrix.django_version }}
pip install flake8 coverage black
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Lint with Black
run: |
black --check .
- name: Run tests
run: |
coverage3 run --source='./encrypted_fields' manage.py test
coverage xml
- name: "Upload coverage to Codecov"
uses: codecov/codecov-action@v2
with:
fail_ci_if_error: true

View file

@ -1,8 +1,8 @@
import os
import sys
if __name__ == '__main__':
os.environ['DJANGO_SETTINGS_MODULE'] = 'package_test.settings'
if __name__ == "__main__":
os.environ["DJANGO_SETTINGS_MODULE"] = "package_test.settings"
from django.core.management import execute_from_command_line

View file

@ -1,18 +1,14 @@
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ':memory:',
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": ":memory:",
},
}
SECRET_KEY = 'abc'
SALT_KEY = 'xyz'
SECRET_KEY = "abc"
SALT_KEY = "xyz"
INSTALLED_APPS = (
'encrypted_fields',
'package_test'
)
INSTALLED_APPS = ("encrypted_fields", "package_test")
MIDDLEWARE_CLASSES = []
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"

View file

@ -11,38 +11,38 @@ class FieldTest(TestCase):
def get_db_value(self, field, model_id):
cursor = connection.cursor()
cursor.execute(
'select {0} '
'from package_test_testmodel '
'where id = {1};'.format(field, model_id)
"select {0} "
"from package_test_testmodel "
"where id = {1};".format(field, model_id)
)
return cursor.fetchone()[0]
def test_char_field_encrypted(self):
plaintext = 'Oh hi, test reader!'
plaintext = "Oh hi, test reader!"
model = TestModel()
model.char = plaintext
model.save()
ciphertext = self.get_db_value('char', model.id)
ciphertext = self.get_db_value("char", model.id)
self.assertNotEqual(plaintext, ciphertext)
self.assertTrue('test' not in ciphertext)
self.assertTrue("test" not in ciphertext)
fresh_model = TestModel.objects.get(id=model.id)
self.assertEqual(fresh_model.char, plaintext)
def test_text_field_encrypted(self):
plaintext = 'Oh hi, test reader!' * 10
plaintext = "Oh hi, test reader!" * 10
model = TestModel()
model.text = plaintext
model.save()
ciphertext = self.get_db_value('text', model.id)
ciphertext = self.get_db_value("text", model.id)
self.assertNotEqual(plaintext, ciphertext)
self.assertTrue('test' not in ciphertext)
self.assertTrue("test" not in ciphertext)
fresh_model = TestModel.objects.get(id=model.id)
self.assertEqual(fresh_model.text, plaintext)
@ -54,10 +54,10 @@ class FieldTest(TestCase):
model.datetime = plaintext
model.save()
ciphertext = self.get_db_value('datetime', model.id)
ciphertext = self.get_db_value("datetime", model.id)
# Django's normal date serialization format
self.assertTrue(re.search('^\d\d\d\d-\d\d-\d\d', ciphertext) is None)
self.assertTrue(re.search("^\d\d\d\d-\d\d-\d\d", ciphertext) is None)
fresh_model = TestModel.objects.get(id=model.id)
self.assertEqual(fresh_model.datetime, plaintext)
@ -69,7 +69,7 @@ class FieldTest(TestCase):
model.integer = plaintext
model.save()
ciphertext = self.get_db_value('integer', model.id)
ciphertext = self.get_db_value("integer", model.id)
self.assertNotEqual(plaintext, ciphertext)
self.assertNotEqual(plaintext, str(ciphertext))
@ -84,7 +84,7 @@ class FieldTest(TestCase):
model.date = plaintext
model.save()
ciphertext = self.get_db_value('date', model.id)
ciphertext = self.get_db_value("date", model.id)
fresh_model = TestModel.objects.get(id=model.id)
self.assertNotEqual(ciphertext, plaintext.isoformat())
@ -97,7 +97,7 @@ class FieldTest(TestCase):
model.floating = plaintext
model.save()
ciphertext = self.get_db_value('floating', model.id)
ciphertext = self.get_db_value("floating", model.id)
self.assertNotEqual(plaintext, ciphertext)
self.assertNotEqual(plaintext, str(ciphertext))
@ -106,16 +106,16 @@ class FieldTest(TestCase):
self.assertEqual(fresh_model.floating, plaintext)
def test_email_field_encrypted(self):
plaintext = 'test@gmail.com'
plaintext = "test@gmail.com"
model = TestModel()
model.email = plaintext
model.save()
ciphertext = self.get_db_value('email', model.id)
ciphertext = self.get_db_value("email", model.id)
self.assertNotEqual(plaintext, ciphertext)
self.assertTrue('aron' not in ciphertext)
self.assertTrue("aron" not in ciphertext)
fresh_model = TestModel.objects.get(id=model.id)
self.assertEqual(fresh_model.email, plaintext)
@ -127,13 +127,13 @@ class FieldTest(TestCase):
model.boolean = plaintext
model.save()
ciphertext = self.get_db_value('boolean', model.id)
ciphertext = self.get_db_value("boolean", model.id)
self.assertNotEqual(plaintext, ciphertext)
self.assertNotEqual(True, ciphertext)
self.assertNotEqual('True', ciphertext)
self.assertNotEqual('true', ciphertext)
self.assertNotEqual('1', ciphertext)
self.assertNotEqual("True", ciphertext)
self.assertNotEqual("true", ciphertext)
self.assertNotEqual("1", ciphertext)
self.assertNotEqual(1, ciphertext)
self.assertTrue(not isinstance(ciphertext, bool))
@ -142,27 +142,24 @@ class FieldTest(TestCase):
class RotatedSaltTestCase(TestCase):
@classmethod
@override_settings(SALT_KEY=['abcdefghijklmnopqrstuvwxyz0123456789'])
@override_settings(SALT_KEY=["abcdefghijklmnopqrstuvwxyz0123456789"])
def setUpTestData(cls):
"""Create the initial record using the old salt"""
cls.original = TestModel.objects.create(
text="Oh hi test reader"
)
cls.original = TestModel.objects.create(text="Oh hi test reader")
@override_settings(SALT_KEY=['newkeyhere', 'abcdefghijklmnopqrstuvwxyz0123456789'])
@override_settings(SALT_KEY=["newkeyhere", "abcdefghijklmnopqrstuvwxyz0123456789"])
def test_rotated_salt(self):
"""Chage the salt, keep the old one as the last in the list for reading"""
"""Change the salt, keep the old one as the last in the list for reading"""
plaintext = "Oh hi test reader"
model = TestModel()
model.text = plaintext
model.save()
ciphertext = FieldTest.get_db_value(self, 'text', model.id)
ciphertext = FieldTest.get_db_value(self, "text", model.id)
self.assertNotEqual(plaintext, ciphertext)
self.assertTrue('test' not in ciphertext)
self.assertTrue("test" not in ciphertext)
fresh_model = TestModel.objects.get(id=model.id)
self.assertEqual(fresh_model.text, plaintext)
@ -170,5 +167,6 @@ class RotatedSaltTestCase(TestCase):
old_record = TestModel.objects.get(id=self.original.id)
self.assertEqual(fresh_model.text, old_record.text)
self.assertNotEqual(ciphertext, FieldTest.get_db_value(self, 'text', self.original.pk))
self.assertNotEqual(
ciphertext, FieldTest.get_db_value(self, "text", self.original.pk)
)

View file

@ -2,20 +2,18 @@ from __future__ import print_function
from setuptools import setup
setup(
name='django-fernet-encrypted-fields',
description=(
'This is inspired by django-encrypted-fields.'
),
long_description=open('README.md').read(),
long_description_content_type='text/markdown',
url='http://github.com/frgmt/django-fernet-encrypted-fields/',
license='MIT',
author='fragment.co.jp',
author_email='info@fragment.co.jp',
packages=['encrypted_fields'],
version='0.1.1',
name="django-fernet-encrypted-fields",
description=("This is inspired by django-encrypted-fields."),
long_description=open("README.md").read(),
long_description_content_type="text/markdown",
url="http://github.com/frgmt/django-fernet-encrypted-fields/",
license="MIT",
author="fragment.co.jp",
author_email="info@fragment.co.jp",
packages=["encrypted_fields"],
version="0.1.1",
install_requires=[
'Django>=2.2',
'cryptography>=35.0.0',
"Django>=2.2",
"cryptography>=35.0.0",
],
)

View file

@ -0,0 +1,53 @@
name: Lint & Test
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
permissions:
contents: read
jobs:
build:
strategy:
matrix:
django_version: [2.2, 3.0, 3.1, 3.2, 4.0a1]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -q Django==${{ matrix.django_version }}
pip install flake8 coverage black
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Lint with Black
run: |
black --check .
- name: Run tests
run: |
coverage3 run --source='./encrypted_fields' manage.py test
coverage xml
- name: "Upload coverage to Codecov"
uses: codecov/codecov-action@v2
with:
fail_ci_if_error: true