Refactor LDAP Client

* Enable LDAP traffic logging
* Specify CA certificates file
* Move client creation to actual parent
* Fix OpenLDAP container certificate generation
This commit is contained in:
crudo 2017-12-18 20:28:54 +01:00
parent ced97f549c
commit 9ee69fb907
7 changed files with 56 additions and 18 deletions

View File

@ -6,12 +6,13 @@ core:
ldap: ldap:
host: 127.0.0.1 host: localhost
port: 389 port: 389
encryption: TLSv1.2 # Can either be None or TLSv1.2. Default: None encryption: TLSv1.2 # Can either be None or TLSv1.2. Default: None
ciphers: "HIGH" 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 username: uid=phi,ou=Services,dc=unit,dc=macaomilano,dc=org
password: phi password: phi
@ -42,3 +43,6 @@ logging:
aiohttp: aiohttp:
level: DEBUG level: DEBUG
handlers: [console, file] handlers: [console, file]
ldap3:
level: DEBUG
handlers: [console, file]

View File

@ -9,7 +9,7 @@ build: gen-cert
gen-cert: gen-cert:
openssl req \ openssl req \
-x509 -nodes -days 365 -sha256 \ -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 -newkey rsa:2048 -keyout key.pem -out cert.pem
.PHONY: clean .PHONY: clean

View File

@ -1,14 +1,31 @@
from aiohttp import web from aiohttp import web
from phi.logging import get_logger from phi.logging import get_logger
from phi.ldap.client import Client
from phi.api.routes import api_routes from phi.api.routes import api_routes
log = get_logger(__name__) 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.") log.info("Initializing API sub-app.")
app = web.Application() app = web.Application()
app.router.add_routes(api_routes)
ldap_client = Client(**config.get('ldap', {}))
app['ldap_client'] = ldap_client 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 return app

View File

@ -1,5 +1,12 @@
from aiohttp.web import Response from aiohttp.web import json_response
def status(request): 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)

View File

@ -2,7 +2,6 @@ from asyncio import get_event_loop
from aiohttp import web from aiohttp import web
from phi.api.app import api_app from phi.api.app import api_app
from phi.ldap.client import Client
def setup_app(config): def setup_app(config):
@ -11,14 +10,17 @@ def setup_app(config):
app = web.Application(loop=loop) app = web.Application(loop=loop)
app['config'] = config 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) app.add_subapp('/api', api)
return app return app
def shutdown_app(app):
app['ldap_client'].close()
def run_app(app): def run_app(app):
web.run_app(app, web.run_app(app,
host=app['config']['core']['listen'].get('host', '127.0.0.1'), host=app['config']['core']['listen'].get('host', '127.0.0.1'),

View File

@ -1,24 +1,26 @@
from threading import Lock 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.logging import get_logger
from phi.ldap.connection import make_connection from phi.ldap.connection import make_connection
from phi.ldap.connection import open_connection, close_connection from phi.ldap.connection import open_connection, close_connection
log = get_logger(__name__) log = get_logger(__name__)
set_library_log_detail_level(PROTOCOL) set_library_log_detail_level(BASIC)
class Client: class Client:
def __init__(self, host=None, port=389, def __init__(self, host=None, port=389,
encryption=None, ciphers=None, validate=False, encryption=None, ciphers=None, validate=False,
username=None, password=None, ca_certs=None, username=None, password=None,
base_dn=None): base_dn=None):
log.info("Initializing LDAP Client.")
self.host = host self.host = host
self.port = port self.port = port
self.encryption = encryption self.encryption = encryption
self.ciphers = ciphers self.ciphers = ciphers
self.validate = validate self.validate = validate
self.ca_certs = ca_certs
self.username = username self.username = username
self.password = password self.password = password
self.base_dn = base_dn self.base_dn = base_dn
@ -28,6 +30,7 @@ class Client:
encryption=self.encryption, encryption=self.encryption,
ciphers=self.ciphers, ciphers=self.ciphers,
validate=self.validate, validate=self.validate,
ca_certs=self.ca_certs,
username=self.username, username=self.username,
password=self.password) password=self.password)
@ -35,11 +38,10 @@ class Client:
self.connection_lock.acquire() self.connection_lock.acquire()
if self.connection.closed is True: if self.connection.closed is True:
open_connection(self.connection) open_connection(self.connection)
self.connection_lock.release()
else: else:
self.connection_lock.release() self.connection_lock.release()
raise Exception("Connection is already open. " raise Exception("Trying to open a connection, but it is already open.")
"Cannot open again.")
self.connection_lock.release()
def close(self): def close(self):
self.connection_lock.acquire() self.connection_lock.acquire()

View File

@ -8,10 +8,10 @@ log = get_logger(__name__)
def make_connection(host=None, port=389, def make_connection(host=None, port=389,
encryption=None, ciphers=None, validate=False, 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 # TLSv1.2 is supported since Python 3.4
if encryption is None: 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 tls = None
elif encryption == "TLSv1.2": elif encryption == "TLSv1.2":
log.info("The connection to the LDAP server will use 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.") "remote hostname.")
tls.validate = CERT_REQUIRED 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) server = Server(host=host, port=port, tls=tls)
connection = Connection(server, user=username, password=password, connection = Connection(server, user=username, password=password,
client_strategy=ASYNC) client_strategy=ASYNC)
@ -49,5 +54,6 @@ def open_connection(connection):
def close_connection(connection): def close_connection(connection):
log.info("Closing connection to LDAP server.")
log.info("Issuing UNBIND command.") log.info("Issuing UNBIND command.")
connection.unbind() connection.unbind()