From 0fea20fdd4c8544a0704c753cf491092714945ab Mon Sep 17 00:00:00 2001 From: Dennis Kliban Date: Wed, 24 Apr 2019 07:09:30 -0400 Subject: [PATCH] Problem: ssl fields on remote are hard to use Solution: switch all ssl fields to TextField This patch switches the storage of SSL certs, keys, and CAs from filesystem to database. This patch also introduces a new serializer field that returns a SHA256 digest for GET operations. Required PR: https://github.com/pulp/pulpcore-plugin/pull/91 fixes: #4506 https://pulp.plan.io/issues/4506 re: #4676 https://pulp.plan.io/issues/4676 --- pulpcore/app/migrations/0001_initial.py | 13 ++++++------- pulpcore/app/models/repository.py | 17 +++-------------- pulpcore/app/serializers/__init__.py | 1 + pulpcore/app/serializers/fields.py | 12 ++++++++++++ pulpcore/app/serializers/repository.py | 23 ++++++++++++++--------- 5 files changed, 36 insertions(+), 30 deletions(-) diff --git a/pulpcore/app/migrations/0001_initial.py b/pulpcore/app/migrations/0001_initial.py index 7242e2bdd0..2dbea5510d 100644 --- a/pulpcore/app/migrations/0001_initial.py +++ b/pulpcore/app/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 2.2 on 2019-04-22 17:55 +# Generated by Django 2.2 on 2019-04-26 17:26 from django.conf import settings import django.core.validators @@ -9,7 +9,6 @@ import pulpcore.app.models.content import pulpcore.app.models.fields import pulpcore.app.models.publication -import pulpcore.app.models.repository import pulpcore.app.models.task import uuid @@ -19,8 +18,8 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('contenttypes', '0002_remove_content_type_name'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('contenttypes', '0002_remove_content_type_name'), ] operations = [ @@ -122,9 +121,9 @@ class Migration(migrations.Migration): ('name', models.CharField(db_index=True, max_length=255, unique=True)), ('url', models.TextField()), ('validate', models.BooleanField(default=True)), - ('ssl_ca_certificate', models.FileField(max_length=255, upload_to=pulpcore.app.models.repository.Remote.tls_storage_path)), - ('ssl_client_certificate', models.FileField(max_length=255, upload_to=pulpcore.app.models.repository.Remote.tls_storage_path)), - ('ssl_client_key', models.FileField(max_length=255, upload_to=pulpcore.app.models.repository.Remote.tls_storage_path)), + ('ssl_ca_certificate', models.TextField(null=True)), + ('ssl_client_certificate', models.TextField(null=True)), + ('ssl_client_key', models.TextField(null=True)), ('ssl_validation', models.BooleanField(default=True)), ('proxy_url', models.TextField()), ('username', models.TextField()), @@ -279,7 +278,7 @@ class Migration(migrations.Migration): ('version_removed', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='removed_memberships', to='core.RepositoryVersion')), ], options={ - 'unique_together': {('repository', 'content', 'version_added'), ('repository', 'content', 'version_removed')}, + 'unique_together': {('repository', 'content', 'version_removed'), ('repository', 'content', 'version_added')}, }, ), migrations.AddField( diff --git a/pulpcore/app/models/repository.py b/pulpcore/app/models/repository.py index 374c3e3e18..7f4f859b32 100644 --- a/pulpcore/app/models/repository.py +++ b/pulpcore/app/models/repository.py @@ -12,7 +12,6 @@ from .content import Content from .task import CreatedResource -from pulpcore.app.models.storage import get_tls_path from pulpcore.app.util import get_view_name_for_model from pulpcore.exceptions import ResourceImmutableError @@ -96,24 +95,14 @@ class Remote(MasterModel): 'future requests for that same content to have to be downloaded again.') ) - def tls_storage_path(self, name): - """ - Returns storage path for TLS file - - Args: - name (str): Original name of the uploaded file. - """ - return get_tls_path(self, name) - name = models.CharField(db_index=True, unique=True, max_length=255) url = models.TextField() validate = models.BooleanField(default=True) - ssl_ca_certificate = models.FileField(upload_to=tls_storage_path, max_length=255) - ssl_client_certificate = models.FileField(upload_to=tls_storage_path, - max_length=255) - ssl_client_key = models.FileField(upload_to=tls_storage_path, max_length=255) + ssl_ca_certificate = models.TextField(null=True) + ssl_client_certificate = models.TextField(null=True) + ssl_client_key = models.TextField(null=True) ssl_validation = models.BooleanField(default=True) proxy_url = models.TextField() diff --git a/pulpcore/app/serializers/__init__.py b/pulpcore/app/serializers/__init__.py index 8050716c23..7f33de8eb6 100644 --- a/pulpcore/app/serializers/__init__.py +++ b/pulpcore/app/serializers/__init__.py @@ -17,6 +17,7 @@ BaseURLField, ContentRelatedField, LatestVersionField, + SecretCharField, SingleContentArtifactField, relative_path_validator, ) diff --git a/pulpcore/app/serializers/fields.py b/pulpcore/app/serializers/fields.py index 599c1d91c2..6b7299c93a 100644 --- a/pulpcore/app/serializers/fields.py +++ b/pulpcore/app/serializers/fields.py @@ -1,5 +1,6 @@ from gettext import gettext as _ import os +import hashlib from django.conf import settings from rest_framework import serializers @@ -209,3 +210,14 @@ def to_representation(self, value): prefix.strip('/'), base_path.lstrip('/') )) + + +class SecretCharField(serializers.CharField): + """ + Serializer field for secrets. + + This field accepts text as input and it returns a sha256 digest of the text stored. + """ + + def to_representation(self, value): + return hashlib.sha256(bytes(value, 'utf-8')).hexdigest() diff --git a/pulpcore/app/serializers/repository.py b/pulpcore/app/serializers/repository.py index f855375b2d..0bf445062f 100644 --- a/pulpcore/app/serializers/repository.py +++ b/pulpcore/app/serializers/repository.py @@ -13,6 +13,7 @@ LatestVersionField, MasterModelSerializer, ModelSerializer, + SecretCharField, ) from pulpcore.app.serializers import validate_unknown_fields @@ -59,21 +60,25 @@ class RemoteSerializer(MasterModelSerializer): help_text='If True, the plugin will validate imported artifacts.', required=False, ) - ssl_ca_certificate = serializers.FileField( + ssl_ca_certificate = SecretCharField( help_text='A PEM encoded CA certificate used to validate the server ' - 'certificate presented by the remote server.', - write_only=True, + 'certificate presented by the remote server. Returns SHA256 sum on GET.', + write_only=False, required=False, + allow_blank=True, ) - ssl_client_certificate = serializers.FileField( - help_text='A PEM encoded client certificate used for authentication.', - write_only=True, + ssl_client_certificate = SecretCharField( + help_text='A PEM encoded client certificate used for authentication. Returns SHA256 sum ' + 'on GET.', + write_only=False, required=False, + allow_blank=True, ) - ssl_client_key = serializers.FileField( - help_text='A PEM encoded private key used for authentication.', - write_only=True, + ssl_client_key = SecretCharField( + help_text='A PEM encoded private key used for authentication. Returns SHA256 sum on GET.', + write_only=False, required=False, + allow_blank=True, ) ssl_validation = serializers.BooleanField( help_text='If True, SSL peer validation must be performed.',