[go: nahoru, domu]

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added functionality to validate user via client certificate #5

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
added functionality to validate user via certificate provided in the …
…header
  • Loading branch information
Mariano Billinghurst committed Sep 11, 2020
commit d5805c3e7f65c555c9a940587da32b98961cce7a
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ The variables send via HTTP headers take precedence over environment variables.
- `Ldap-Server-Domain` **(Optional)**
- `Ldap-Required-Groups` **(Optional)**
- `Ldap-Required-Groups-Conditional` **(Optional)**
- `Ldap-Required-Groups-Conditional` **(Optional)**
- `x-forwarded-client-cert` Contains a client certificate ( previously validated by nginx )

### HTTP response headers
- `x-username` Contains the authenticated username
Expand Down
41 changes: 32 additions & 9 deletions files/main.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import logging, sys
import re, logging, sys
from flask import Flask
from flask import request
from flask import g
from flask_httpauth import HTTPBasicAuth
from aldap import Aldap
from cache import Cache
from os import environ
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from urllib.parse import unquote_plus

# --- Logging --------------------------------------------------------------------
loglevel_map = { 'DEBUG': logging.DEBUG,
Expand Down Expand Up @@ -41,9 +44,18 @@

@auth.verify_password
def login(username, password):
if not username or not password:
log.error("Username or password empty.")
return False
log.debug(f"Received Headers: {str(request.headers.keys)}")

# Either Client certificate is in the header (previously validated by nginx) or user and pass
CLIENT_CERT = request.headers.get("x-forwarded-client-cert")
if CLIENT_CERT:
username = getUserFromCertificate(CLIENT_CERT)
password = None
else:
log.info("No client certificate provided in the header")
if not username or not password:
log.error("Username or password empty")
return False

try:
# Get parameters from HTTP headers or from environment variables
Expand Down Expand Up @@ -117,11 +129,9 @@ def login(username, password):
if not validGroups:
return False

# Check if the username and password are valid
# First check inside the cache and then in the LDAP server
if not cache.validate(username, password):
if not aldap.authenticateUser():
return False
# Check authentication: 1st check client certificate, second cache, third authenticate in ldap
if not CLIENT_CERT and not cache.validate(username, password) and not aldap.authenticateUser():
return False

# Include the user in the cache
cache.add(username, password)
Expand All @@ -131,6 +141,19 @@ def login(username, password):
g.matchesGroups = ','.join(matchesGroups) # Set the matches groups to send in the headers response
return True


def getUserFromCertificate(CLIENT_CERT):
log.debug(f'Received Certificate:', CLIENT_CERT)
cert_str = unquote_plus(CLIENT_CERT)
log.debug("Certificate Found")
cert = x509.load_pem_x509_certificate(bytes(cert_str, 'utf-8'), default_backend())
subject = str(cert.subject)
log.debug(f'The issuer of the certificate is: {str(cert.issuer)}')
log.debug(f'The subject of the certificate is: {str(cert.subject)}')

p = re.compile("<Name\(CN=(.*)\)>")
return p.search(subject).group(1)

# Catch-All URL
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
Expand Down