[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