Jan Dittberner
3f4e005cf3
- add a cats.authentication.ClientCertificateBackend authentication backend implementation that extracts the used fields from a client certificate - configure the AUTHENTICATION_BACKENDS setting to use the ClientCertificateBackend - add cryptography dependency to parse certificate data - add gunicorn as production dependency - add a development configuration for Gunicorn - document how to pass client certificate information via nginx reverse proxy - add a certificate_login view and a basic home_page view and add corresponding URL patterns - ignore PEM encoded files and temporary gunicorn files
42 lines
1.7 KiB
Python
42 lines
1.7 KiB
Python
from urllib.parse import unquote_to_bytes
|
|
|
|
from cryptography import x509
|
|
from cryptography.x509 import load_pem_x509_certificate, SubjectAlternativeName
|
|
from django.contrib.auth.backends import BaseBackend
|
|
|
|
|
|
class ClientCertificateUser:
|
|
def __init__(self, serial_number, issuer_name, subject_name, emails):
|
|
self.serial_number = serial_number
|
|
self.issuer_name = issuer_name
|
|
self.subject_name = subject_name
|
|
self.emails = emails
|
|
|
|
def __str__(self):
|
|
return (f"<ClientCertificateUser[serial_number={self.serial_number}, issuer_name={self.issuer_name},"
|
|
f" subject_name={self.subject_name}, emails={self.emails}]>")
|
|
|
|
|
|
class ClientCertificateBackend(BaseBackend):
|
|
def authenticate(self, request, encoded_certificate=None):
|
|
"""
|
|
encoded_certificate is expected to be a URL encoded PEM certificate as sent by nginx when using the
|
|
$ssl_client_escaped_cert
|
|
"""
|
|
if encoded_certificate is None:
|
|
return None
|
|
|
|
pem_data = unquote_to_bytes(encoded_certificate)
|
|
certificate_data = load_pem_x509_certificate(pem_data)
|
|
|
|
subject_alternative_name = certificate_data.extensions.get_extension_for_class(SubjectAlternativeName)
|
|
emails = subject_alternative_name.value.get_values_for_type(x509.RFC822Name)
|
|
|
|
serial_number = f"{certificate_data.serial_number:X}"
|
|
if len(serial_number) % 2 == 1:
|
|
serial_number = "0" + serial_number
|
|
|
|
issuer_name = certificate_data.issuer.get_attributes_for_oid(x509.OID_COMMON_NAME)[0].value
|
|
subject_name = certificate_data.subject.get_attributes_for_oid(x509.OID_COMMON_NAME)[0].value
|
|
|
|
return ClientCertificateUser(serial_number, issuer_name, subject_name, emails)
|