From 9ee69fb90747dc9c5fea938c92db6dac4cdc0b4e Mon Sep 17 00:00:00 2001 From: crudo Date: Mon, 18 Dec 2017 20:28:54 +0100 Subject: [PATCH] Refactor LDAP Client * Enable LDAP traffic logging * Specify CA certificates file * Move client creation to actual parent * Fix OpenLDAP container certificate generation --- config.yml | 8 ++++++-- openldap/Makefile | 2 +- src/phi/api/app.py | 21 +++++++++++++++++++-- src/phi/api/rest.py | 11 +++++++++-- src/phi/app.py | 8 +++++--- src/phi/ldap/client.py | 14 ++++++++------ src/phi/ldap/connection.py | 10 ++++++++-- 7 files changed, 56 insertions(+), 18 deletions(-) diff --git a/config.yml b/config.yml index f684038..aca8c90 100644 --- a/config.yml +++ b/config.yml @@ -6,12 +6,13 @@ core: ldap: - host: 127.0.0.1 + host: localhost port: 389 encryption: TLSv1.2 # Can either be None or TLSv1.2. Default: None ciphers: "HIGH" - validate: False # Can either be True or False. Default: False + validate: True # Can either be True or False. Default: False + ca_certs: openldap/cert.pem username: uid=phi,ou=Services,dc=unit,dc=macaomilano,dc=org password: phi @@ -42,3 +43,6 @@ logging: aiohttp: level: DEBUG handlers: [console, file] + ldap3: + level: DEBUG + handlers: [console, file] diff --git a/openldap/Makefile b/openldap/Makefile index 4c0add4..d88e671 100644 --- a/openldap/Makefile +++ b/openldap/Makefile @@ -9,7 +9,7 @@ build: gen-cert gen-cert: openssl req \ -x509 -nodes -days 365 -sha256 \ - -subj '/C=IT/ST=Lombardia/L=Milano/CN=unit.macaomilano.org' \ + -subj '/C=IT/ST=Lombardia/L=Milano/CN=localhost' \ -newkey rsa:2048 -keyout key.pem -out cert.pem .PHONY: clean diff --git a/src/phi/api/app.py b/src/phi/api/app.py index 111ac95..4eb3d96 100644 --- a/src/phi/api/app.py +++ b/src/phi/api/app.py @@ -1,14 +1,31 @@ from aiohttp import web from phi.logging import get_logger +from phi.ldap.client import Client from phi.api.routes import api_routes log = get_logger(__name__) -def api_app(ldap_client=None): +def api_startup(app): + app['ldap_client'].open() + + +def api_shutdown(app): + app['ldap_client'].close() + + +def api_app(config): log.info("Initializing API sub-app.") + app = web.Application() - app.router.add_routes(api_routes) + + ldap_client = Client(**config.get('ldap', {})) app['ldap_client'] = ldap_client + + app.on_startup.append(api_startup) + app.on_shutdown.append(api_shutdown) + + app.router.add_routes(api_routes) + return app diff --git a/src/phi/api/rest.py b/src/phi/api/rest.py index 5ebd19d..9a06925 100644 --- a/src/phi/api/rest.py +++ b/src/phi/api/rest.py @@ -1,5 +1,12 @@ -from aiohttp.web import Response +from aiohttp.web import json_response def status(request): - Response(text="Status: it's working!") + response = { + 'ldap': { + 'connected': not request.app['ldap_client'].connection.closed, + 'bound': request.app['ldap_client'].connection.bound + } + } + + return json_response(response) diff --git a/src/phi/app.py b/src/phi/app.py index 4a9369b..691fedb 100644 --- a/src/phi/app.py +++ b/src/phi/app.py @@ -2,7 +2,6 @@ from asyncio import get_event_loop from aiohttp import web from phi.api.app import api_app -from phi.ldap.client import Client def setup_app(config): @@ -11,14 +10,17 @@ def setup_app(config): app = web.Application(loop=loop) app['config'] = config - ldap_client = Client(**config.get('ldap', {})) - api = api_app(ldap_client=ldap_client) + api = api_app(config) app.add_subapp('/api', api) return app +def shutdown_app(app): + app['ldap_client'].close() + + def run_app(app): web.run_app(app, host=app['config']['core']['listen'].get('host', '127.0.0.1'), diff --git a/src/phi/ldap/client.py b/src/phi/ldap/client.py index b5c6e54..429f2ef 100644 --- a/src/phi/ldap/client.py +++ b/src/phi/ldap/client.py @@ -1,24 +1,26 @@ from threading import Lock -from ldap3.utils.log import set_library_log_detail_level, PROTOCOL +from ldap3.utils.log import set_library_log_detail_level, BASIC from phi.logging import get_logger from phi.ldap.connection import make_connection from phi.ldap.connection import open_connection, close_connection log = get_logger(__name__) -set_library_log_detail_level(PROTOCOL) +set_library_log_detail_level(BASIC) class Client: def __init__(self, host=None, port=389, encryption=None, ciphers=None, validate=False, - username=None, password=None, + ca_certs=None, username=None, password=None, base_dn=None): + log.info("Initializing LDAP Client.") self.host = host self.port = port self.encryption = encryption self.ciphers = ciphers self.validate = validate + self.ca_certs = ca_certs self.username = username self.password = password self.base_dn = base_dn @@ -28,6 +30,7 @@ class Client: encryption=self.encryption, ciphers=self.ciphers, validate=self.validate, + ca_certs=self.ca_certs, username=self.username, password=self.password) @@ -35,11 +38,10 @@ class Client: self.connection_lock.acquire() if self.connection.closed is True: open_connection(self.connection) + self.connection_lock.release() else: self.connection_lock.release() - raise Exception("Connection is already open. " - "Cannot open again.") - self.connection_lock.release() + raise Exception("Trying to open a connection, but it is already open.") def close(self): self.connection_lock.acquire() diff --git a/src/phi/ldap/connection.py b/src/phi/ldap/connection.py index 238a121..79af89b 100644 --- a/src/phi/ldap/connection.py +++ b/src/phi/ldap/connection.py @@ -8,10 +8,10 @@ log = get_logger(__name__) def make_connection(host=None, port=389, encryption=None, ciphers=None, validate=False, - username=None, password=None): + ca_certs=None, username=None, password=None): # TLSv1.2 is supported since Python 3.4 if encryption is None: - log.info("The connection to the LDAP server will not be encrypted.") + log.warning("The connection to the LDAP server will not be encrypted.") tls = None elif encryption == "TLSv1.2": log.info("The connection to the LDAP server will use TLSv1.2.") @@ -29,6 +29,11 @@ def make_connection(host=None, port=389, "remote hostname.") tls.validate = CERT_REQUIRED + if encryption is not None and validate is True and ca_certs is not None: + log.info("Using the following CA certificates: {}" + .format(ca_certs)) + tls.ca_certs_file = ca_certs + server = Server(host=host, port=port, tls=tls) connection = Connection(server, user=username, password=password, client_strategy=ASYNC) @@ -49,5 +54,6 @@ def open_connection(connection): def close_connection(connection): + log.info("Closing connection to LDAP server.") log.info("Issuing UNBIND command.") connection.unbind()