[Python-modules-commits] [python-django-x509] 02/06: New upstream version 0.3.4
Michael Fladischer
fladi at moszumanska.debian.org
Fri Jan 5 09:58:20 UTC 2018
This is an automated email from the git hooks/post-receive script.
fladi pushed a commit to branch debian/master
in repository python-django-x509.
commit 8e53bd7f010431a86d0b8f9b6420a4fb0da0bd2c
Author: Michael Fladischer <FladischerMichael at fladi.at>
Date: Fri Jan 5 10:30:39 2018 +0100
New upstream version 0.3.4
---
CHANGES.rst | 13 ++++-
PKG-INFO | 13 +++--
README.rst | 11 +++--
django_x509.egg-info/PKG-INFO | 13 +++--
django_x509.egg-info/SOURCES.txt | 2 +
django_x509/__init__.py | 2 +-
django_x509/admin.py | 11 ++++-
django_x509/base/admin.py | 58 +++++++++++++++++++----
django_x509/base/models.py | 28 ++++++++---
django_x509/migrations/0002_certificate.py | 1 +
django_x509/migrations/0004_auto_20171207_1450.py | 25 ++++++++++
django_x509/static/django-x509/js/switcher.js | 35 ++++++++++++++
django_x509/tests/test_ca.py | 16 ++++++-
django_x509/tests/test_cert.py | 18 +++++--
14 files changed, 209 insertions(+), 37 deletions(-)
diff --git a/CHANGES.rst b/CHANGES.rst
index 0380437..490c8d4 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,12 +1,23 @@
Changelog
=========
+Version 0.3.4 [2017-12-20]
+--------------------------
+
+* [admin] Removed ``serial_number`` from certificate list
+
+Version 0.3.3 [2017-12-20]
+--------------------------
+
+* [models] Reimplemented serial numbers as UUID integers
+* [UX] Import vs New javascript switcher
+
Version 0.3.2 [2017-12-06]
--------------------------
* [requirements] upgraded pyopenssl to 17.5.0 and cryptography to 2.2.0
* [models] Fixed uncaught exception when imported
- PEM `certificate` or `private_key` is invalid
+ PEM ``certificate`` or ``private_key`` is invalid
Version 0.3.1 [2017-12-01]
--------------------------
diff --git a/PKG-INFO b/PKG-INFO
index 58acda0..e48300e 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: django-x509
-Version: 0.3.2
+Version: 0.3.4
Summary: Reusable django app to generate and manage x509 certificates
Home-page: https://github.com/openwisp/django-x509
Author: Federico Capoano
@@ -46,6 +46,8 @@ Description: django-x509
* Certificate revocation
* CRL view (public or protected)
* Possibility to specify x509 extensions on each certificate
+ * Random serial numbers based on uuid4 integers (see `why is this a good idea
+ <https://crypto.stackexchange.com/questions/257/unpredictability-of-x-509-serial-numbers>`_)
Project goals
-------------
@@ -165,21 +167,22 @@ Description: django-x509
.. code-block:: shell
./runtests.py
-
+
Install and run on docker
--------------------------
+
Build from docker file:
.. code-block:: shell
sudo docker build -t openwisp/djangox509 .
-
+
Run the docker container:
.. code-block:: shell
- sudo docker run -it -p 8000:8000 batuhan/django509
-
+ sudo docker run -it -p 8000:8000 openwisp/djangox509
+
Settings
--------
``DJANGO_X509_DEFAULT_CERT_VALIDITY``
diff --git a/README.rst b/README.rst
index 992e9ef..3537172 100644
--- a/README.rst
+++ b/README.rst
@@ -36,6 +36,8 @@ Current features
* Certificate revocation
* CRL view (public or protected)
* Possibility to specify x509 extensions on each certificate
+* Random serial numbers based on uuid4 integers (see `why is this a good idea
+ <https://crypto.stackexchange.com/questions/257/unpredictability-of-x-509-serial-numbers>`_)
Project goals
-------------
@@ -155,21 +157,22 @@ Run tests with:
.. code-block:: shell
./runtests.py
-
+
Install and run on docker
--------------------------
+
Build from docker file:
.. code-block:: shell
sudo docker build -t openwisp/djangox509 .
-
+
Run the docker container:
.. code-block:: shell
- sudo docker run -it -p 8000:8000 batuhan/django509
-
+ sudo docker run -it -p 8000:8000 openwisp/djangox509
+
Settings
--------
``DJANGO_X509_DEFAULT_CERT_VALIDITY``
diff --git a/django_x509.egg-info/PKG-INFO b/django_x509.egg-info/PKG-INFO
index 58acda0..e48300e 100644
--- a/django_x509.egg-info/PKG-INFO
+++ b/django_x509.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: django-x509
-Version: 0.3.2
+Version: 0.3.4
Summary: Reusable django app to generate and manage x509 certificates
Home-page: https://github.com/openwisp/django-x509
Author: Federico Capoano
@@ -46,6 +46,8 @@ Description: django-x509
* Certificate revocation
* CRL view (public or protected)
* Possibility to specify x509 extensions on each certificate
+ * Random serial numbers based on uuid4 integers (see `why is this a good idea
+ <https://crypto.stackexchange.com/questions/257/unpredictability-of-x-509-serial-numbers>`_)
Project goals
-------------
@@ -165,21 +167,22 @@ Description: django-x509
.. code-block:: shell
./runtests.py
-
+
Install and run on docker
--------------------------
+
Build from docker file:
.. code-block:: shell
sudo docker build -t openwisp/djangox509 .
-
+
Run the docker container:
.. code-block:: shell
- sudo docker run -it -p 8000:8000 batuhan/django509
-
+ sudo docker run -it -p 8000:8000 openwisp/djangox509
+
Settings
--------
``DJANGO_X509_DEFAULT_CERT_VALIDITY``
diff --git a/django_x509.egg-info/SOURCES.txt b/django_x509.egg-info/SOURCES.txt
index 3d85b48..4e01f73 100644
--- a/django_x509.egg-info/SOURCES.txt
+++ b/django_x509.egg-info/SOURCES.txt
@@ -26,8 +26,10 @@ django_x509/base/views.py
django_x509/migrations/0001_initial.py
django_x509/migrations/0002_certificate.py
django_x509/migrations/0003_rename_organization_field.py
+django_x509/migrations/0004_auto_20171207_1450.py
django_x509/migrations/__init__.py
django_x509/static/django-x509/css/admin.css
+django_x509/static/django-x509/js/switcher.js
django_x509/templates/admin/django_x509/change_form.html
django_x509/tests/__init__.py
django_x509/tests/test_ca.py
diff --git a/django_x509/__init__.py b/django_x509/__init__.py
index e5f92a7..2af6fd7 100644
--- a/django_x509/__init__.py
+++ b/django_x509/__init__.py
@@ -1,4 +1,4 @@
-VERSION = (0, 3, 2, 'final')
+VERSION = (0, 3, 4, 'final')
__version__ = VERSION # alias
diff --git a/django_x509/admin.py b/django_x509/admin.py
index ab65168..40a22e7 100644
--- a/django_x509/admin.py
+++ b/django_x509/admin.py
@@ -1,7 +1,16 @@
from django.contrib import admin
-from .base.admin import CaAdmin, CertAdmin
+from .base.admin import AbstractCaAdmin, AbstractCertAdmin
from .models import Ca, Cert
+
+class CertAdmin(AbstractCertAdmin):
+ pass
+
+
+class CaAdmin(AbstractCaAdmin):
+ pass
+
+
admin.site.register(Ca, CaAdmin)
admin.site.register(Cert, CertAdmin)
diff --git a/django_x509/base/admin.py b/django_x509/base/admin.py
index 4ab669f..cfb7bad 100644
--- a/django_x509/base/admin.py
+++ b/django_x509/base/admin.py
@@ -1,3 +1,4 @@
+from django import forms
from django.contrib.admin import ModelAdmin
from django.contrib.admin.templatetags.admin_static import static
from django.urls import reverse
@@ -5,6 +6,15 @@ from django.utils.html import format_html
from django.utils.translation import ugettext_lazy as _
+class X509Form(forms.ModelForm):
+ OPERATION_CHOICES = (
+ ('', '----- {0} -----'.format(_('Please select an option'))),
+ ('new', _('Create new')),
+ ('import', _('Import Existing'))
+ )
+ operation_type = forms.ChoiceField(choices=OPERATION_CHOICES)
+
+
class BaseAdmin(ModelAdmin):
"""
ModelAdmin for TimeStampedEditableModel
@@ -17,6 +27,7 @@ class BaseAdmin(ModelAdmin):
search_fields = ['name', 'serial_number', 'common_name']
actions_on_bottom = True
save_on_top = True
+ form = X509Form
# custom attribute
readonly_edit = ['key_length',
'digest',
@@ -55,15 +66,38 @@ class BaseAdmin(ModelAdmin):
return fields
-class CaAdmin(BaseAdmin):
+class AbstractCaAdmin(BaseAdmin):
list_filter = ['key_length', 'digest', 'created']
+ fields = ['operation_type',
+ 'name',
+ 'notes',
+ 'key_length',
+ 'digest',
+ 'validity_start',
+ 'validity_end',
+ 'country_code',
+ 'state',
+ 'city',
+ 'organization_name',
+ 'email',
+ 'common_name',
+ 'extensions',
+ 'serial_number',
+ 'certificate',
+ 'private_key',
+ 'created',
+ 'modified']
+
+ class Media:
+ js = ('django-x509/js/switcher.js',)
-class CertAdmin(BaseAdmin):
+class AbstractCertAdmin(BaseAdmin):
list_filter = ['ca', 'revoked', 'key_length', 'digest', 'created']
list_select_related = ['ca']
readonly_fields = ['revoked', 'revoked_at']
- fields = ['name',
+ fields = ['operation_type',
+ 'name',
'ca',
'notes',
'revoked',
@@ -86,6 +120,9 @@ class CertAdmin(BaseAdmin):
'modified']
actions = ['revoke_action']
+ class Media:
+ js = ('django-x509/js/switcher.js',)
+
def ca_url(self, obj):
url = reverse('admin:{0}_ca_change'.format(self.opts.app_label), args=[obj.ca.id])
return format_html("<a href='{url}'>{text}</a>",
@@ -108,9 +145,12 @@ class CertAdmin(BaseAdmin):
revoke_action.short_description = _('Revoke selected certificates')
-CertAdmin.list_display = BaseAdmin.list_display[:]
-CertAdmin.list_display.insert(1, 'ca_url')
-CertAdmin.list_display.insert(4, 'serial_number')
-CertAdmin.list_display.insert(5, 'revoked')
-CertAdmin.readonly_edit = BaseAdmin.readonly_edit[:]
-CertAdmin.readonly_edit += ('ca',)
+# For backward compatibility
+CaAdmin = AbstractCaAdmin
+CertAdmin = AbstractCertAdmin
+
+AbstractCertAdmin.list_display = BaseAdmin.list_display[:]
+AbstractCertAdmin.list_display.insert(1, 'ca_url')
+AbstractCertAdmin.list_display.insert(5, 'revoked')
+AbstractCertAdmin.readonly_edit = BaseAdmin.readonly_edit[:]
+AbstractCertAdmin.readonly_edit += ('ca',)
diff --git a/django_x509/base/models.py b/django_x509/base/models.py
index 7fa4acc..d5eab6c 100644
--- a/django_x509/base/models.py
+++ b/django_x509/base/models.py
@@ -1,4 +1,5 @@
import collections
+import uuid
from datetime import datetime, timedelta
import OpenSSL
@@ -123,10 +124,13 @@ class BaseX509(models.Model):
help_text=_('additional x509 certificate extensions'),
load_kwargs={'object_pairs_hook': collections.OrderedDict},
dump_kwargs={'indent': 4})
- serial_number = models.PositiveIntegerField(_('serial number'),
- help_text=_('leave blank to determine automatically'),
- blank=True,
- null=True)
+ # serial_number is set to CharField as a UUID integer is too big for a
+ # PositiveIntegerField and an IntegerField on SQLite
+ serial_number = models.CharField(_('serial number'),
+ help_text=_('leave blank to determine automatically'),
+ blank=True,
+ null=True,
+ max_length=39)
certificate = models.TextField(blank=True, help_text='certificate in X.509 PEM format')
private_key = models.TextField(blank=True, help_text='private key in X.509 PEM format')
created = AutoCreatedField(_('created'), editable=True)
@@ -155,6 +159,8 @@ class BaseX509(models.Model):
):
raise ValidationError(_('When importing an existing certificate, both'
'keys (private and public) must be present'))
+ if self.serial_number:
+ self._validate_serial_number()
self._verify_extension_format()
def save(self, *args, **kwargs):
@@ -165,7 +171,7 @@ class BaseX509(models.Model):
if generate:
# automatically determine serial number
if not self.serial_number:
- self.serial_number = self.id
+ self.serial_number = uuid.uuid4().int
self._generate()
kwargs['force_insert'] = False
super(BaseX509, self).save(*args, **kwargs)
@@ -212,6 +218,16 @@ class BaseX509(models.Model):
if errors:
raise ValidationError(errors)
+ def _validate_serial_number(self):
+ """
+ (internal use only)
+ validates serial number if set manually
+ """
+ try:
+ int(self.serial_number)
+ except ValueError:
+ raise ValidationError({'serial_number': _('Serial number must be an integer')})
+
def _generate(self):
"""
(internal use only)
@@ -223,7 +239,7 @@ class BaseX509(models.Model):
subject = self._fill_subject(cert.get_subject())
cert.set_version(0x2) # version 3 (0 indexed counting)
cert.set_subject(subject)
- cert.set_serial_number(self.serial_number)
+ cert.set_serial_number(int(self.serial_number))
cert.set_notBefore(bytes_compat(self.validity_start.strftime(generalized_time)))
cert.set_notAfter(bytes_compat(self.validity_end.strftime(generalized_time)))
# generating certificate for CA
diff --git a/django_x509/migrations/0002_certificate.py b/django_x509/migrations/0002_certificate.py
index 726ae5c..4460a44 100644
--- a/django_x509/migrations/0002_certificate.py
+++ b/django_x509/migrations/0002_certificate.py
@@ -3,6 +3,7 @@
from __future__ import unicode_literals
from django.db import migrations, models
+
import django_x509.base.models
diff --git a/django_x509/migrations/0004_auto_20171207_1450.py b/django_x509/migrations/0004_auto_20171207_1450.py
new file mode 100644
index 0000000..681326e
--- /dev/null
+++ b/django_x509/migrations/0004_auto_20171207_1450.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.8 on 2017-12-07 13:50
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('django_x509', '0003_rename_organization_field'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='ca',
+ name='serial_number',
+ field=models.CharField(blank=True, help_text='leave blank to determine automatically', max_length=39, null=True, verbose_name='serial number'),
+ ),
+ migrations.AlterField(
+ model_name='cert',
+ name='serial_number',
+ field=models.CharField(blank=True, help_text='leave blank to determine automatically', max_length=39, null=True, verbose_name='serial number'),
+ ),
+ ]
diff --git a/django_x509/static/django-x509/js/switcher.js b/django_x509/static/django-x509/js/switcher.js
new file mode 100644
index 0000000..d9ea2a4
--- /dev/null
+++ b/django_x509/static/django-x509/js/switcher.js
@@ -0,0 +1,35 @@
+django.jQuery(function ($) {
+ 'use strict';
+ var operationType = $('.field-operation_type select');
+ // enable switcher only in add forms
+ if (!operationType.length || $('form .deletelink-box').length > 0) {
+ $('.field-operation_type').hide();
+ return;
+ }
+ // function for operation_type switcher
+ var showFields = function () {
+ // define fields for each operation
+ var importFields = $('.form-row:not(.field-certificate, .field-operation_type, ' +
+ '.field-private_key, .field-name, .field-ca)'),
+ newFields = $('.form-row:not(.field-certificate, .field-private_key)'),
+ defaultFields = $('.form-row:not(.field-operation_type)'),
+ allFields = $('.form-row'),
+ value = operationType.val();
+ if (!value) {
+ allFields.show();
+ defaultFields.hide();
+ }
+ if (value === 'new') {
+ allFields.hide();
+ newFields.show();
+ }
+ if (value === 'import') {
+ allFields.show();
+ importFields.hide();
+ }
+ };
+ showFields();
+ operationType.on('change', function (e) {
+ showFields();
+ });
+});
diff --git a/django_x509/tests/test_ca.py b/django_x509/tests/test_ca.py
index a2e4bc9..917c0bb 100644
--- a/django_x509/tests/test_ca.py
+++ b/django_x509/tests/test_ca.py
@@ -60,7 +60,7 @@ WRyKPvMvJzWT
self.assertNotEqual(ca.certificate, '')
self.assertNotEqual(ca.private_key, '')
cert = crypto.load_certificate(crypto.FILETYPE_PEM, ca.certificate)
- self.assertEqual(cert.get_serial_number(), ca.id)
+ self.assertEqual(int(cert.get_serial_number()), int(ca.serial_number))
subject = cert.get_subject()
self.assertEqual(subject.countryName, ca.country_code)
self.assertEqual(subject.stateOrProvinceName, ca.state)
@@ -150,7 +150,7 @@ WRyKPvMvJzWT
self.assertEqual(ca.email, 'contact at acme.com')
self.assertEqual(ca.common_name, 'importtest')
self.assertEqual(ca.name, 'ImportTest')
- self.assertEqual(ca.serial_number, 123456)
+ self.assertEqual(int(ca.serial_number), 123456)
# ensure version is 3
self.assertEqual(cert.get_version(), 3)
ca.delete()
@@ -486,3 +486,15 @@ wZWuZRQLPPTAdiW+drs3gz8w0u3Y9ihgvHQqFcGJ1+j6ANJ0XdE/D5Y=
str(e.message_dict['private_key'][0]))
else:
self.fail('ValidationError not raised')
+
+ def test_create_old_serial_ca(self):
+ ca = self._create_ca(serial_number=3)
+ self.assertEqual(int(ca.serial_number), 3)
+ cert = crypto.load_certificate(crypto.FILETYPE_PEM, ca.certificate)
+ self.assertEqual(int(cert.get_serial_number()), int(ca.serial_number))
+
+ def test_bad_serial_number_ca(self):
+ try:
+ self._create_ca(serial_number='notIntegers')
+ except ValidationError as e:
+ self.assertEqual("Serial number must be an integer", str(e.message_dict['serial_number'][0]))
diff --git a/django_x509/tests/test_cert.py b/django_x509/tests/test_cert.py
index a639f80..a683851 100644
--- a/django_x509/tests/test_cert.py
+++ b/django_x509/tests/test_cert.py
@@ -83,7 +83,7 @@ k9Y1S1C9VB0YsDZTeZUggJNSDN4YrKjIevYZQQIhAOWec6vngM/PlI1adrFndd3d
self.assertNotEqual(cert.certificate, '')
self.assertNotEqual(cert.private_key, '')
x509 = cert.x509
- self.assertEqual(x509.get_serial_number(), cert.id)
+ self.assertEqual(x509.get_serial_number(), cert.serial_number)
subject = x509.get_subject()
# check subject
self.assertEqual(subject.countryName, cert.country_code)
@@ -158,7 +158,7 @@ k9Y1S1C9VB0YsDZTeZUggJNSDN4YrKjIevYZQQIhAOWec6vngM/PlI1adrFndd3d
cert.save()
x509 = cert.x509
# verify attributes
- self.assertEqual(x509.get_serial_number(), 123456)
+ self.assertEqual(int(x509.get_serial_number()), 123456)
subject = x509.get_subject()
self.assertEqual(subject.countryName, None)
self.assertEqual(subject.stateOrProvinceName, None)
@@ -186,7 +186,7 @@ k9Y1S1C9VB0YsDZTeZUggJNSDN4YrKjIevYZQQIhAOWec6vngM/PlI1adrFndd3d
self.assertEqual(cert.organization_name, '')
self.assertEqual(cert.email, '')
self.assertEqual(cert.common_name, '')
- self.assertEqual(cert.serial_number, 123456)
+ self.assertEqual(int(cert.serial_number), 123456)
# ensure version is 3 (indexed 0 based counting)
self.assertEqual(x509.get_version(), 2)
cert.delete()
@@ -397,3 +397,15 @@ k9Y1S1C9VB0YsDZTeZUggJNSDN4YrKjIevYZQQIhAOWec6vngM/PlI1adrFndd3d
str(e.message_dict['private_key'][0]))
else:
self.fail('ValidationError not raised')
+
+ def test_create_old_serial_certificate(self):
+ cert = self._create_cert(serial_number=3)
+ self.assertEqual(int(cert.serial_number), 3)
+ x509 = cert.x509
+ self.assertEqual(int(x509.get_serial_number()), 3)
+
+ def test_bad_serial_number_cert(self):
+ try:
+ self._create_cert(serial_number='notIntegers')
+ except ValidationError as e:
+ self.assertEqual("Serial number must be an integer", str(e.message_dict['serial_number'][0]))
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-django-x509.git
More information about the Python-modules-commits
mailing list