diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index ac42c38..0000000 --- a/.coveragerc +++ /dev/null @@ -1,7 +0,0 @@ -[run] -omit = - /*/test* - /tests - /*/__init__.py - /setup.py - /*/migrations/* diff --git a/.github/dependabot.yml b/.github/dependabot.yml index db95827..c893f4a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -11,3 +11,9 @@ updates: interval: "monthly" commit-message: prefix: "[deps] " + - package-ecosystem: "github-actions" # Check for GitHub Actions updates + directory: "/" # The root directory where the Ansible role is located + schedule: + interval: "monthly" # Check for updates weekly + commit-message: + prefix: "[ci] " diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6af0f95..bed5361 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: - name: Install Dependencies id: deps run: | - pip install -U "pip==20.2.4" setuptools wheel + pip install -U pip setuptools wheel sudo npm install -g jslint pip install -U -r requirements-test.txt pip install -U -e . @@ -50,26 +50,25 @@ jobs: - name: Tests if: ${{ !cancelled() && steps.deps.conclusion == 'success' }} run: | - coverage run --source=django_x509 runtests.py - SAMPLE_APP=1 ./runtests.py --parallel --keepdb + coverage run runtests.py --parallel + SAMPLE_APP=1 coverage run ./runtests.py --parallel + coverage combine + coverage xml - name: Upload Coverage if: ${{ success() }} - run: coveralls --service=github - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - COVERALLS_FLAG_NAME: python-${{ matrix.python-version }}-${{ matrix.django-version }} - COVERALLS_PARALLEL: true + uses: coverallsapp/github-action@v2 + with: + parallel: true + format: cobertura + flag-name: python-${{ matrix.env.env }} + github-token: ${{ secrets.GITHUB_TOKEN }} coveralls: - name: Finish Coveralls needs: build runs-on: ubuntu-latest - container: python:3-slim steps: - - name: Finished - run: | - pip3 install --upgrade coveralls - coveralls --finish - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Coveralls Finished + uses: coverallsapp/github-action@v2 + with: + parallel-finished: true diff --git a/.gitignore b/.gitignore index b2fe60b..ba41d0b 100644 --- a/.gitignore +++ b/.gitignore @@ -35,7 +35,7 @@ pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ -.coverage +.coverage* .cache nosetests.xml coverage.xml @@ -68,4 +68,3 @@ Pipfile # IDE specific files .idea - diff --git a/CHANGES.rst b/CHANGES.rst index 52d2e70..fad277e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -26,8 +26,9 @@ Version 1.0.0 [2022-02-25] Bugfixes ~~~~~~~~ -- Fixed `bug in admin `_ - for creating CA with blank ``key_length`` and ``digest`` fields +- Fixed `bug in admin + `_ for creating CA + with blank ``key_length`` and ``digest`` fields Changes ~~~~~~~ @@ -51,23 +52,23 @@ Version 0.9.3 [2021-03-16] -------------------------- - [deps] Updated pyopenssl range to allow 20.0.x -- [deps] Removed django-model-utils in favour of openwisp-utils - which centralizes several dependencies used by all the OpenWISP modules +- [deps] Removed django-model-utils in favour of openwisp-utils which + centralizes several dependencies used by all the OpenWISP modules (including ``django-model-utils``) Version 0.9.2 [2020-12-09] -------------------------- - [deps] Pinned django-model-utils>=4.0.0,<4.1.0 -- [deps] Pin cryptography to ~=3.2 because version 3.3 - contains backward incompatible changes +- [deps] Pin cryptography to ~=3.2 because version 3.3 contains backward + incompatible changes Version 0.9.1 [2020-11-13] -------------------------- - [deps] Updated cryptography minimum version to 3.2 for security reasons - The version range has also been relaxed to avoid having to update it too often, - future versions up to but excluding 4.0 will be accepted + The version range has also been relaxed to avoid having to update it too + often, future versions up to but excluding 4.0 will be accepted - [fix] Removed ``static()`` call from admin media - [tests] Updated openwisp-utils[qa] to 0.7 @@ -88,15 +89,20 @@ Features ~~~~~~~~ - Added swappable models, improved extensibility -- Improved documentation on `how to extend django-x509 `_ +- Improved documentation on `how to extend django-x509 + `_ Changes ~~~~~~~ -- **Breaking change**: systems using django-x509 as a library must set ``DJANGO_X509_CA_MODEL`` - & ``DJANGO_X509_CERT_MODEL`` values in their settings.py when upgrading or an exception like the following one will be raised: +- **Breaking change**: systems using django-x509 as a library must set + ``DJANGO_X509_CA_MODEL`` & ``DJANGO_X509_CERT_MODEL`` values in their + settings.py when upgrading or an exception like the following one will + be raised: + + ``django.core.exceptions.ImproperlyConfigured: Could not find + django_x509.Ca!`` - ``django.core.exceptions.ImproperlyConfigured: Could not find django_x509.Ca!`` - Added support for django 3.1 - Added support for cryptography 3.0.0 @@ -132,135 +138,136 @@ Version 0.6.0 [2020-01-15] Version 0.5.1 [2019-12-23] -------------------------- -- [fix] Use ``self.pk`` instead of ``self.id`` to allow more - flexible override of primary key +- [fix] Use ``self.pk`` instead of ``self.id`` to allow more flexible + override of primary key - Fixed jQuery init issue on django 2.2 Version 0.5.0 [2019-11-20] -------------------------- -* `#36 `_: +- `#36 `_: [requirements] Added support for django 2.1 -* `#44 `_: - [models] Improved error message format #44 -* `#61 `_: - Bumped supported Django version to 2.2 and Python version to 3.7 -* `#63 `_: - [bug] Load model after registration in apps -* Bumped cryptography version to 2.8.0, pyopenssl to 19.0.0 +- `#44 `_: [models] + Improved error message format #44 +- `#61 `_: Bumped + supported Django version to 2.2 and Python version to 3.7 +- `#63 `_: [bug] Load + model after registration in apps +- Bumped cryptography version to 2.8.0, pyopenssl to 19.0.0 Version 0.4.1 [2018-09-05] -------------------------- -* [admin] Fixed UI bug that prevented changing Cert and CA -* [requirements] cryptography>=2.3.0,<2.4.0 -* [requirements] pyopenssl>=17.5.0,<18.1.0 -* `#41 `_: - [requirements] Added support for django 2.1 -* [admin] Fixed involuntary permanent modification of field list +- [admin] Fixed UI bug that prevented changing Cert and CA +- [requirements] cryptography>=2.3.0,<2.4.0 +- [requirements] pyopenssl>=17.5.0,<18.1.0 +- `#41 `_: [requirements] + Added support for django 2.1 +- [admin] Fixed involuntary permanent modification of field list Version 0.4.0 [2018-02-19] -------------------------- -* `#24 `_: - [qa] Added django 2.0 & dropped django 1.10 -* `#25 `_: - [admin] Automatically select ``certificate`` and ``private_key`` on click -* `#33 `_: - [models] Added ``organizational_unit_name`` in ``Cert`` and ``Ca`` +- `#24 `_: [qa] Added + django 2.0 & dropped django 1.10 +- `#25 `_: [admin] + Automatically select ``certificate`` and ``private_key`` on click +- `#33 `_: [models] + Added ``organizational_unit_name`` in ``Cert`` and ``Ca`` Version 0.3.4 [2017-12-20] -------------------------- -* [admin] Removed ``serial_number`` from certificate list +- [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 +- [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 +- [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 Version 0.3.1 [2017-12-01] -------------------------- -* temporarily downgraded cryptography and pyopenssl versions - to avoid segmentation faults +- temporarily downgraded cryptography and pyopenssl versions to avoid + segmentation faults Version 0.3.0 [2017-11-03] -------------------------- -* [models] Avoided possible double insertion in ``Base.save`` -* [requirements] pyopenssl>=17.1.0,<17.4.0 -* [admin] Fixed preformatted look of certificate and private-key fields -* [models] Allow importing certs with invalid country codes -* [models] Allow importing certificate with empty common name -* [tests] Updated data for import test to fix pyOpenSSL issue -* [models] Renamed ``organization`` field to ``organization_name`` +- [models] Avoided possible double insertion in ``Base.save`` +- [requirements] pyopenssl>=17.1.0,<17.4.0 +- [admin] Fixed preformatted look of certificate and private-key fields +- [models] Allow importing certs with invalid country codes +- [models] Allow importing certificate with empty common name +- [tests] Updated data for import test to fix pyOpenSSL issue +- [models] Renamed ``organization`` field to ``organization_name`` Version 0.2.4 [2017-07-04] -------------------------- -* [models] added ``digest`` argument to ``CRL.export`` -* [requirements] pyopenssl>=17.1.0,<17.2.0 +- [models] added ``digest`` argument to ``CRL.export`` +- [requirements] pyopenssl>=17.1.0,<17.2.0 Version 0.2.3 [2017-05-15] -------------------------- -* [migrations] Updated ``validity_start`` on ``Cert`` model +- [migrations] Updated ``validity_start`` on ``Cert`` model Version 0.2.2 [2017-05-11] -------------------------- -* [models] Set ``validity_start`` to 1 day before the current date (at 00:00) +- [models] Set ``validity_start`` to 1 day before the current date (at + 00:00) Version 0.2.1 [2017-05-02] -------------------------- -* [django] added support for django 1.11 +- [django] added support for django 1.11 Version 0.2.0 [2017-01-11] -------------------------- -* [models] improved reusability by providing abstract models -* [admin] improved reusability by providing abstract admin classes -* [views] provided a base view that can be reused by third party apps -* [docs] documented how to extend models and admin -* [docs] documented hard dependencies +- [models] improved reusability by providing abstract models +- [admin] improved reusability by providing abstract admin classes +- [views] provided a base view that can be reused by third party apps +- [docs] documented how to extend models and admin +- [docs] documented hard dependencies Version 0.1.3 [2016-09-22] -------------------------- -* [model] avoid import error if any imported field is ``NULL`` -* [admin] added ``serial_number`` to ``list_display`` in ``Cert`` admin -* [model] avoid exception if x509 subject attributes are empty +- [model] avoid import error if any imported field is ``NULL`` +- [admin] added ``serial_number`` to ``list_display`` in ``Cert`` admin +- [model] avoid exception if x509 subject attributes are empty Version 0.1.2 [2016-09-08] -------------------------- -* improved general ``verbose_name`` of the app -* added official compatibility with django 1.10 -* [admin] show link to CA in cert admin -* [admin] added ``key_length`` and ``digest`` to available filters +- improved general ``verbose_name`` of the app +- added official compatibility with django 1.10 +- [admin] show link to CA in cert admin +- [admin] added ``key_length`` and ``digest`` to available filters Version 0.1.1 [2016-08-03] -------------------------- -* fixed x509 certificate version -* renamed ``public_key`` field to more appropiate ``certificate`` -* show x509 text dump in admin when editing objects +- fixed x509 certificate version +- renamed ``public_key`` field to more appropiate ``certificate`` +- show x509 text dump in admin when editing objects Version 0.1 [2016-07-18] ------------------------ -* CA and end entity certificate generation -* import existing certificates -* x509 extensions -* revocation -* CRL +- CA and end entity certificate generation +- import existing certificates +- x509 extensions +- revocation +- CRL diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 8204960..e1b58e4 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -1 +1,2 @@ -Please refer to the `OpenWISP Contribution Guidelines `_. +Please refer to the `OpenWISP Contribution Guidelines +`_. diff --git a/README.rst b/README.rst index 75e403e..8c935bd 100644 --- a/README.rst +++ b/README.rst @@ -2,39 +2,39 @@ django-x509 =========== .. image:: https://github.com/openwisp/django-x509/workflows/Django-x509%20Build/badge.svg?branch=master - :target: https://github.com/openwisp/django-x509/actions?query=workflow%3A"Django-x509+Build%22" - :alt: CI build status + :target: https://github.com/openwisp/django-x509/actions?query=workflow%3A"Django-x509+Build%22" + :alt: CI build status .. image:: https://coveralls.io/repos/openwisp/django-x509/badge.svg - :target: https://coveralls.io/r/openwisp/django-x509 - :alt: Test Coverage + :target: https://coveralls.io/r/openwisp/django-x509 + :alt: Test Coverage .. image:: https://img.shields.io/librariesio/release/github/openwisp/django-x509 - :target: https://libraries.io/github/openwisp/django-x509#repository_dependencies - :alt: Dependency monitoring + :target: https://libraries.io/github/openwisp/django-x509#repository_dependencies + :alt: Dependency monitoring .. image:: https://img.shields.io/gitter/room/nwjs/nw.js.svg - :target: https://gitter.im/openwisp/general - :alt: chat + :target: https://gitter.im/openwisp/general + :alt: chat .. image:: https://badge.fury.io/py/django-x509.svg - :target: http://badge.fury.io/py/django-x509 - :alt: Pypi Version + :target: http://badge.fury.io/py/django-x509 + :alt: Pypi Version .. image:: https://pepy.tech/badge/django-x509 - :target: https://pepy.tech/project/django-x509 - :alt: downloads + :target: https://pepy.tech/project/django-x509 + :alt: downloads .. image:: https://img.shields.io/badge/code%20style-black-000000.svg - :target: https://pypi.org/project/black/ - :alt: code style: black + :target: https://pypi.org/project/black/ + :alt: code style: black ------------- +---- .. image:: https://github.com/openwisp/django-x509/raw/master/docs/demo_x509.gif - :alt: demo + :alt: demo ------------- +---- Simple reusable django app implementing x509 PKI certificates management. @@ -42,42 +42,45 @@ Simple reusable django app implementing x509 PKI certificates management. `_. .. image:: https://raw.githubusercontent.com/openwisp/openwisp2-docs/master/assets/design/openwisp-logo-black.svg - :target: http://openwisp.org + :target: http://openwisp.org ------------- +---- .. contents:: **Table of Contents**: - :backlinks: none - :depth: 3 + :backlinks: none + :depth: 3 ------------- +---- Current features ---------------- -* CA generation -* Import existing CAs -* End entity certificate generation -* Import existing certificates -* 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 +- CA generation +- Import existing CAs +- End entity certificate generation +- Import existing certificates +- 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 `_) -* Possibility to generate and import passphrase protected x509 certificates/CAs -* Passphrase protected x509 content will be shown encrypted in the web UI +- Possibility to generate and import passphrase protected x509 + certificates/CAs +- Passphrase protected x509 content will be shown encrypted in the web UI Project goals ------------- -* provide a simple and reusable x509 PKI management django app -* provide abstract models that can be imported and extended in larger django projects +- provide a simple and reusable x509 PKI management django app +- provide abstract models that can be imported and extended in larger + django projects Dependencies ------------ -* Python >= 3.8 -* OpenSSL +- Python >= 3.8 +- OpenSSL Install stable version from pypi -------------------------------- @@ -120,7 +123,7 @@ Add ``django_x509`` to ``INSTALLED_APPS``: INSTALLED_APPS = [ # other apps - 'django_x509', + "django_x509", ] Add the URLs to your main ``urls.py``: @@ -131,8 +134,7 @@ Add the URLs to your main ``urls.py``: urlpatterns = [ # ... other urls in your project ... - - url(r'admin/', admin.site.urls), + url(r"admin/", admin.site.urls), ] Then run: @@ -187,184 +189,180 @@ Run tests with: ./runtests.py Install and run on docker --------------------------- +------------------------- Build from docker file: .. code-block:: shell - sudo docker build -t openwisp/djangox509 . + sudo docker build -t openwisp/djangox509 . Run the docker container: .. code-block:: shell - sudo docker run -it -p 8000:8000 openwisp/djangox509 + sudo docker run -it -p 8000:8000 openwisp/djangox509 Settings -------- + ``DJANGO_X509_DEFAULT_CERT_VALIDITY`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+--------------+-------------+ -| **type**: | ``int`` | -+--------------+-------------+ -| **default**: | ``365`` | -+--------------+-------------+ +============ ======= +**type**: ``int`` +**default**: ``365`` +============ ======= Default validity period (in days) when creating new x509 certificates. - ``DJANGO_X509_DEFAULT_CA_VALIDITY`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+--------------+-------------+ -| **type**: | ``int`` | -+--------------+-------------+ -| **default**: | ``3650`` | -+--------------+-------------+ +============ ======== +**type**: ``int`` +**default**: ``3650`` +============ ======== -Default validity period (in days) when creating new Certification Authorities. +Default validity period (in days) when creating new Certification +Authorities. ``DJANGO_X509_DEFAULT_KEY_LENGTH`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+--------------+-------------+ -| **type**: | ``int`` | -+--------------+-------------+ -| **default**: | ``2048`` | -+--------------+-------------+ +============ ======== +**type**: ``int`` +**default**: ``2048`` +============ ======== Default key length for new CAs and new certificates. Must be one of the following values: -* ``512`` -* ``1024`` -* ``2048`` -* ``4096`` +- ``512`` +- ``1024`` +- ``2048`` +- ``4096`` ``DJANGO_X509_DEFAULT_DIGEST_ALGORITHM`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+--------------+-------------+ -| **type**: | ``str`` | -+--------------+-------------+ -| **default**: | ``sha256`` | -+--------------+-------------+ +============ ========== +**type**: ``str`` +**default**: ``sha256`` +============ ========== Default digest algorithm for new CAs and new certificates. Must be one of the following values: -* ``sha1`` -* ``sha224`` -* ``sha256`` -* ``sha384`` -* ``sha512`` +- ``sha1`` +- ``sha224`` +- ``sha256`` +- ``sha384`` +- ``sha512`` ``DJANGO_X509_CA_BASIC_CONSTRAINTS_CRITICAL`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+--------------+-----------+ -| **type**: | ``bool`` | -+--------------+-----------+ -| **default**: | ``True`` | -+--------------+-----------+ +============ ======== +**type**: ``bool`` +**default**: ``True`` +============ ======== -Whether the ``basicConstraint`` x509 extension must be flagged as critical when creating new CAs. +Whether the ``basicConstraint`` x509 extension must be flagged as critical +when creating new CAs. ``DJANGO_X509_CA_BASIC_CONSTRAINTS_PATHLEN`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+--------------+---------------------+ -| **type**: | ``int`` or ``None`` | -+--------------+---------------------+ -| **default**: | ``0`` | -+--------------+---------------------+ +============ =================== +**type**: ``int`` or ``None`` +**default**: ``0`` +============ =================== -Value of the ``pathLenConstraint`` of ``basicConstraint`` x509 extension used when creating new CAs. +Value of the ``pathLenConstraint`` of ``basicConstraint`` x509 extension +used when creating new CAs. -When this value is a positive ``int`` it represents the maximum number of non-self-issued -intermediate certificates that may follow the generated certificate in a valid certification path. +When this value is a positive ``int`` it represents the maximum number of +non-self-issued intermediate certificates that may follow the generated +certificate in a valid certification path. Set this value to ``None`` to avoid imposing any limit. ``DJANGO_X509_CA_KEYUSAGE_CRITICAL`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+--------------+--------------------------+ -| **type**: | ``bool`` | -+--------------+--------------------------+ -| **default**: | ``True`` | -+--------------+--------------------------+ +============ ======== +**type**: ``bool`` +**default**: ``True`` +============ ======== -Whether the ``keyUsage`` x509 extension should be flagged as "critical" for new CAs. +Whether the ``keyUsage`` x509 extension should be flagged as "critical" +for new CAs. ``DJANGO_X509_CA_KEYUSAGE_VALUE`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+--------------+--------------------------+ -| **type**: | ``str`` | -+--------------+--------------------------+ -| **default**: | ``cRLSign, keyCertSign`` | -+--------------+--------------------------+ +============ ======================== +**type**: ``str`` +**default**: ``cRLSign, keyCertSign`` +============ ======================== Value of the ``keyUsage`` x509 extension for new CAs. ``DJANGO_X509_CERT_KEYUSAGE_CRITICAL`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+--------------+--------------------------+ -| **type**: | ``bool`` | -+--------------+--------------------------+ -| **default**: | ``False`` | -+--------------+--------------------------+ +============ ========= +**type**: ``bool`` +**default**: ``False`` +============ ========= -Whether the ``keyUsage`` x509 extension should be flagged as "critical" for new -end-entity certificates. +Whether the ``keyUsage`` x509 extension should be flagged as "critical" +for new end-entity certificates. ``DJANGO_X509_CERT_KEYUSAGE_VALUE`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+--------------+---------------------------------------+ -| **type**: | ``str`` | -+--------------+---------------------------------------+ -| **default**: | ``digitalSignature, keyEncipherment`` | -+--------------+---------------------------------------+ +============ ===================================== +**type**: ``str`` +**default**: ``digitalSignature, keyEncipherment`` +============ ===================================== Value of the ``keyUsage`` x509 extension for new end-entity certificates. ``DJANGO_X509_CRL_PROTECTED`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+--------------+-----------+ -| **type**: | ``bool`` | -+--------------+-----------+ -| **default**: | ``False`` | -+--------------+-----------+ +============ ========= +**type**: ``bool`` +**default**: ``False`` +============ ========= -Whether the view for downloading Certificate Revocation Lists should -be protected with authentication or not. +Whether the view for downloading Certificate Revocation Lists should be +protected with authentication or not. Extending django-x509 --------------------- -One of the core values of the OpenWISP project is `Software Reusability `_, -for this reason *django-x509* provides a set of base classes -which can be imported, extended and reused to create derivative apps. +One of the core values of the OpenWISP project is `Software Reusability +`_, +for this reason *django-x509* provides a set of base classes which can be +imported, extended and reused to create derivative apps. -In order to implement your custom version of *django-x509*, -you need to perform the steps described in this section. +In order to implement your custom version of *django-x509*, you need to +perform the steps described in this section. -When in doubt, the code in the `test project `_ -and the `sample app `_ -will serve you as source of truth: -just replicate and adapt that code to get a basic derivative of -*django-x509* working. +When in doubt, the code in the `test project +`_ +and the `sample app +`_ +will serve you as source of truth: just replicate and adapt that code to +get a basic derivative of *django-x509* working. -**Premise**: if you plan on using a customized version of this module, -we suggest to start with it since the beginning, because migrating your data +**Premise**: if you plan on using a customized version of this module, we +suggest to start with it since the beginning, because migrating your data from the default module to your extended version may be time consuming. 1. Initialize your custom module @@ -373,35 +371,41 @@ from the default module to your extended version may be time consuming. The first thing you need to do is to create a new django app which will contain your custom version of *django-x509*. -A django app is nothing more than a -`python package `_ -(a directory of python scripts), in the following examples we'll call this django app -``myx509``, but you can name it how you want:: +A django app is nothing more than a `python package +`_ (a directory +of python scripts), in the following examples we'll call this django app +``myx509``, but you can name it how you want: + +:: django-admin startapp myx509 -Keep in mind that the command mentioned above must be called from a directory -which is available in your `PYTHON_PATH `_ -so that you can then import the result into your project. +Keep in mind that the command mentioned above must be called from a +directory which is available in your `PYTHON_PATH +`_ so that +you can then import the result into your project. -Now you need to add ``myx509`` to ``INSTALLED_APPS`` in your ``settings.py``, -ensuring also that ``django_x509`` has been removed: +Now you need to add ``myx509`` to ``INSTALLED_APPS`` in your +``settings.py``, ensuring also that ``django_x509`` has been removed: .. code-block:: python INSTALLED_APPS = [ # ... other apps ... # 'django_x509' <-- comment out or delete this line - 'myx509' + "myx509" ] -For more information about how to work with django projects and django apps, -please refer to the `django documentation `_. +For more information about how to work with django projects and django +apps, please refer to the `django documentation +`_. 2. Install ``django-x509`` & ``openwisp-utils`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Install (and add to the requirement of your project):: +Install (and add to the requirement of your project): + +:: pip install django-x509 openwisp-utils @@ -412,7 +416,7 @@ Add the following to your ``settings.py``: .. code-block:: python - EXTENDED_APPS = ['django_x509'] + EXTENDED_APPS = ["django_x509"] 4. Add ``openwisp_utils.staticfiles.DependencyFinder`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -423,32 +427,33 @@ Add ``openwisp_utils.staticfiles.DependencyFinder`` to .. code-block:: python STATICFILES_FINDERS = [ - 'django.contrib.staticfiles.finders.FileSystemFinder', - 'django.contrib.staticfiles.finders.AppDirectoriesFinder', - 'openwisp_utils.staticfiles.DependencyFinder', + "django.contrib.staticfiles.finders.FileSystemFinder", + "django.contrib.staticfiles.finders.AppDirectoriesFinder", + "openwisp_utils.staticfiles.DependencyFinder", ] 5. Add ``openwisp_utils.loaders.DependencyLoader`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Add ``openwisp_utils.loaders.DependencyLoader`` to ``TEMPLATES`` in your ``settings.py``: +Add ``openwisp_utils.loaders.DependencyLoader`` to ``TEMPLATES`` in your +``settings.py``: .. code-block:: python TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'OPTIONS': { - 'loaders': [ - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', - 'openwisp_utils.loaders.DependencyLoader', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "OPTIONS": { + "loaders": [ + "django.template.loaders.filesystem.Loader", + "django.template.loaders.app_directories.Loader", + "openwisp_utils.loaders.DependencyLoader", ], - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", ], }, } @@ -459,89 +464,107 @@ Add ``openwisp_utils.loaders.DependencyLoader`` to ``TEMPLATES`` in your ``setti Please refer to the following files in the sample app of the test project: -- `sample_x509/__init__.py `_. -- `sample_x509/apps.py `_. +- `sample_x509/__init__.py + `_. +- `sample_x509/apps.py + `_. You have to replicate and adapt that code in your project. -For more information regarding the concept of ``AppConfig`` please refer to -the `"Applications" section in the django documentation `_. +For more information regarding the concept of ``AppConfig`` please refer +to the `"Applications" section in the django documentation +`_. 7. Create your custom models ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Here we provide an example of how to extend the base models of -*django-x509*. We added a simple "details" field to the -models for demostration of modification: +*django-x509*. We added a simple "details" field to the models for +demostration of modification: .. code-block:: python from django.db import models from django_x509.base.models import AbstractCa, AbstractCert + class DetailsModel(models.Model): details = models.CharField(max_length=64, blank=True, null=True) class Meta: abstract = True + class Ca(DetailsModel, AbstractCa): """ Concrete Ca model """ + class Meta(AbstractCa.Meta): abstract = False + class Cert(DetailsModel, AbstractCert): """ Concrete Cert model """ + class Meta(AbstractCert.Meta): abstract = False You can add fields in a similar way in your ``models.py`` file. -**Note**: for doubts regarding how to use, extend or develop models please refer to -the `"Models" section in the django documentation `_. +**Note**: for doubts regarding how to use, extend or develop models please +refer to the `"Models" section in the django documentation +`_. 8. Add swapper configurations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Once you have created the models, add the following to your ``settings.py``: +Once you have created the models, add the following to your +``settings.py``: .. code-block:: python # Setting models for swapper module - DJANGO_X509_CA_MODEL = 'myx509.Ca' - DJANGO_X509_CERT_MODEL = 'myx509.Cert' + DJANGO_X509_CA_MODEL = "myx509.Ca" + DJANGO_X509_CERT_MODEL = "myx509.Cert" Substitute ``myx509`` with the name you chose in step 1. 9. Create database migrations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Create and apply database migrations:: +Create and apply database migrations: + +:: ./manage.py makemigrations ./manage.py migrate -For more information, refer to the -`"Migrations" section in the django documentation `_. +For more information, refer to the `"Migrations" section in the django +documentation +`_. 10. Create the admin ~~~~~~~~~~~~~~~~~~~~ -Refer to the `admin.py file of the sample app `_. +Refer to the `admin.py file of the sample app +`_. -To introduce changes to the admin, you can do it in two main ways which are described below. +To introduce changes to the admin, you can do it in two main ways which +are described below. -**Note**: for more information regarding how the django admin works, or how it can be customized, -please refer to `"The django admin site" section in the django documentation `_. +**Note**: for more information regarding how the django admin works, or +how it can be customized, please refer to `"The django admin site" section +in the django documentation +`_. 1. Monkey patching -################## +++++++++++++++++++ -If the changes you need to add are relatively small, you can resort to monkey patching. +If the changes you need to add are relatively small, you can resort to +monkey patching. For example: @@ -549,14 +572,18 @@ For example: from django_x509.admin import CaAdmin, CertAdmin - # CaAdmin.list_display.insert(1, 'my_custom_field') <-- your custom change example - # CertAdmin.list_display.insert(1, 'my_custom_field') <-- your custom change example + CaAdmin.list_display.insert( + 1, "my_custom_field" + ) # <-- your custom change example + CertAdmin.list_display.insert( + 1, "my_custom_field" + ) # <-- your custom change example 2. Inheriting admin classes -########################### ++++++++++++++++++++++++++++ -If you need to introduce significant changes and/or you don't want to resort to -monkey patching, you can proceed as follows: +If you need to introduce significant changes and/or you don't want to +resort to monkey patching, you can proceed as follows: .. code-block:: python @@ -565,59 +592,75 @@ monkey patching, you can proceed as follows: from django_x509.base.admin import AbstractCaAdmin, AbstractCertAdmin - Ca = load_model('django_x509', 'Ca') - Cert = load_model('django_x509', 'Cert') + Ca = load_model("django_x509", "Ca") + Cert = load_model("django_x509", "Cert") + class CertAdmin(AbstractCertAdmin): + pass # add your changes here + class CaAdmin(AbstractCaAdmin): + pass # add your changes here + admin.site.register(Ca, CaAdmin) admin.site.register(Cert, CertAdmin) 11. Create root URL configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Please refer to the `urls.py `_ +Please refer to the `urls.py +`_ file in the test project. -For more information about URL configuration in django, please refer to the -`"URL dispatcher" section in the django documentation `_. +For more information about URL configuration in django, please refer to +the `"URL dispatcher" section in the django documentation +`_. 12. Import the automated tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When developing a custom application based on this module, it's a good -idea to import and run the base tests too, so that you can be sure the changes -you're introducing are not breaking some of the existing features of *django-x509*. +idea to import and run the base tests too, so that you can be sure the +changes you're introducing are not breaking some of the existing features +of *django-x509*. -In case you need to add breaking changes, you can overwrite the tests defined -in the base classes to test your own behavior. +In case you need to add breaking changes, you can overwrite the tests +defined in the base classes to test your own behavior. .. code-block:: python from django.test import TestCase from django_x509.tests.base import TestX509Mixin - from django_x509.tests.test_admin import ModelAdminTests as BaseModelAdminTests + from django_x509.tests.test_admin import ( + ModelAdminTests as BaseModelAdminTests, + ) from django_x509.tests.test_ca import TestCa as BaseTestCa from django_x509.tests.test_cert import TestCert as BaseTestCert + class ModelAdminTests(BaseModelAdminTests): - app_label = 'myx509' + app_label = "myx509" + class TestCert(BaseTestCert): pass + class TestCa(BaseTestCa): pass + del BaseModelAdminTests del BaseTestCa del BaseTestCert -Now, you can then run tests with:: +Now, you can then run tests with: + +:: # the --parallel flag is optional ./manage.py test --parallel myx509 @@ -625,12 +668,14 @@ Now, you can then run tests with:: Substitute ``myx509`` with the name you chose in step 1. For more information about automated tests in django, please refer to -`"Testing in Django" `_. +`"Testing in Django" +`_. Contributing ------------ -Please refer to the `OpenWISP contributing guidelines `_. +Please refer to the `OpenWISP contributing guidelines +`_. Support ------- @@ -640,9 +685,11 @@ See `OpenWISP Support Channels `_. Changelog --------- -See `CHANGES `_. +See `CHANGES +`_. License ------- -See `LICENSE `_. +See `LICENSE +`_. diff --git a/django_x509/migrations/0001_initial.py b/django_x509/migrations/0001_initial.py index 41cdce9..931a3a1 100644 --- a/django_x509/migrations/0001_initial.py +++ b/django_x509/migrations/0001_initial.py @@ -11,7 +11,6 @@ class Migration(migrations.Migration): - initial = True dependencies = [] diff --git a/django_x509/migrations/0002_certificate.py b/django_x509/migrations/0002_certificate.py index a39b1ba..b3a1226 100644 --- a/django_x509/migrations/0002_certificate.py +++ b/django_x509/migrations/0002_certificate.py @@ -6,7 +6,6 @@ class Migration(migrations.Migration): - dependencies = [('django_x509', '0001_initial')] operations = [ diff --git a/django_x509/migrations/0003_rename_organization_field.py b/django_x509/migrations/0003_rename_organization_field.py index 8c780ee..aaa12fe 100644 --- a/django_x509/migrations/0003_rename_organization_field.py +++ b/django_x509/migrations/0003_rename_organization_field.py @@ -4,7 +4,6 @@ class Migration(migrations.Migration): - dependencies = [('django_x509', '0002_certificate')] operations = [ diff --git a/django_x509/migrations/0004_auto_20171207_1450.py b/django_x509/migrations/0004_auto_20171207_1450.py index f3bf6df..ed22681 100644 --- a/django_x509/migrations/0004_auto_20171207_1450.py +++ b/django_x509/migrations/0004_auto_20171207_1450.py @@ -4,7 +4,6 @@ class Migration(migrations.Migration): - dependencies = [('django_x509', '0003_rename_organization_field')] operations = [ diff --git a/django_x509/migrations/0005_organizational_unit_name.py b/django_x509/migrations/0005_organizational_unit_name.py index 7608853..68e7dd4 100644 --- a/django_x509/migrations/0005_organizational_unit_name.py +++ b/django_x509/migrations/0005_organizational_unit_name.py @@ -4,7 +4,6 @@ class Migration(migrations.Migration): - dependencies = [('django_x509', '0004_auto_20171207_1450')] operations = [ diff --git a/django_x509/migrations/0006_passphrase_field.py b/django_x509/migrations/0006_passphrase_field.py index 3662cc2..c8819ef 100644 --- a/django_x509/migrations/0006_passphrase_field.py +++ b/django_x509/migrations/0006_passphrase_field.py @@ -4,7 +4,6 @@ class Migration(migrations.Migration): - dependencies = [('django_x509', '0005_organizational_unit_name')] operations = [ diff --git a/django_x509/migrations/0007_serial_number_max_length.py b/django_x509/migrations/0007_serial_number_max_length.py index bec53a4..8431568 100644 --- a/django_x509/migrations/0007_serial_number_max_length.py +++ b/django_x509/migrations/0007_serial_number_max_length.py @@ -4,7 +4,6 @@ class Migration(migrations.Migration): - dependencies = [('django_x509', '0006_passphrase_field')] operations = [ diff --git a/django_x509/migrations/0008_common_name_max_length.py b/django_x509/migrations/0008_common_name_max_length.py index 8a12636..cb30e2c 100644 --- a/django_x509/migrations/0008_common_name_max_length.py +++ b/django_x509/migrations/0008_common_name_max_length.py @@ -4,7 +4,6 @@ class Migration(migrations.Migration): - dependencies = [ ('django_x509', '0007_serial_number_max_length'), ] diff --git a/django_x509/migrations/0009_alter_ca_digest_alter_ca_key_length_and_more.py b/django_x509/migrations/0009_alter_ca_digest_alter_ca_key_length_and_more.py index 9e48788..e5f2549 100644 --- a/django_x509/migrations/0009_alter_ca_digest_alter_ca_key_length_and_more.py +++ b/django_x509/migrations/0009_alter_ca_digest_alter_ca_key_length_and_more.py @@ -5,7 +5,6 @@ class Migration(migrations.Migration): - dependencies = [ ('django_x509', '0008_common_name_max_length'), ] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..8733b5b --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,22 @@ +[tool.coverage.run] +source = ["django_x509"] +parallel = true +concurrency = ["multiprocessing"] +omit = [ + "django_x509/__init__.py", + "*/tests/*" +] + +[tool.docstrfmt] +extend_exclude = ["**/*.py"] + +[tool.isort] +known_third_party = ["django"] +known_first_party = ["django_x509"] +default_section = "THIRDPARTY" +line_length = 88 +multi_line_output = 3 +use_parentheses = true +include_trailing_comma = true +force_grid_wrap = 0 +skip = ["migrations"] diff --git a/setup.cfg b/setup.cfg index 26c2980..861750b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,20 +1,10 @@ [bdist_wheel] universal=1 -[isort] -skip = migrations -known_first_party=django_x509 -default_section = THIRDPARTY -line_length=88 -multi_line_output=3 -use_parentheses=True -include_trailing_comma=True -force_grid_wrap=0 - [flake8] # W503: line break before or after operator # W504: line break after or after operator # W605: invalid escape sequence ignore = W605, W503, W504 -exclude = *migrations/0*,, +exclude = *migrations/0* max-line-length = 88 diff --git a/setup.py b/setup.py index 759f61b..2bd8098 100644 --- a/setup.py +++ b/setup.py @@ -56,7 +56,7 @@ def get_install_requires(): zip_safe=False, install_requires=get_install_requires(), classifiers=[ - 'Development Status :: 3 - Alpha', + 'Development Status :: 5 - Production/Stable ', 'Environment :: Web Environment', 'Topic :: Internet :: WWW/HTTP', 'Intended Audience :: Developers', diff --git a/tests/openwisp2/sample_x509/migrations/0001_initial.py b/tests/openwisp2/sample_x509/migrations/0001_initial.py index cfa051e..e2d1d8f 100644 --- a/tests/openwisp2/sample_x509/migrations/0001_initial.py +++ b/tests/openwisp2/sample_x509/migrations/0001_initial.py @@ -10,7 +10,6 @@ class Migration(migrations.Migration): - initial = True dependencies = [] diff --git a/tests/openwisp2/sample_x509/migrations/0002_common_name_max_length.py b/tests/openwisp2/sample_x509/migrations/0002_common_name_max_length.py index 319c22c..8050069 100644 --- a/tests/openwisp2/sample_x509/migrations/0002_common_name_max_length.py +++ b/tests/openwisp2/sample_x509/migrations/0002_common_name_max_length.py @@ -4,7 +4,6 @@ class Migration(migrations.Migration): - dependencies = [ ('sample_x509', '0001_initial'), ]