Commit 756fc0fc authored by Ondřej Kuzník's avatar Ondřej Kuzník
Browse files

Add a way to configure TLS and auth for connections

parent 2dc2734a
......@@ -23,5 +23,6 @@ setup(
install_requires=[
'ldap0 >= 1.1.0',
'urwid',
'pyyaml',
],
)
# -*- coding: utf-8 -*-
# This work is part of OpenLDAP Software <http://www.openldap.org/>.
#
# Copyright 2018-2020 The OpenLDAP Foundation.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted only as authorized by the OpenLDAP
# Public License.
#
# A copy of this license is available in the file LICENSE in the
# top-level directory of the distribution or, alternatively, at
# <http://www.OpenLDAP.org/license.html>.
#
# ACKNOWLEDGEMENTS:
# This work was initially developed by Ondřej Kuzník
# for inclusion in OpenLDAP Software.
"""
Connection set up
"""
import logging
logger = logging.getLogger(__name__)
from .ldap_wrapper import AsyncClient
import ldap0 as ldap
from ldap0.ldapurl import LDAPUrl
def connect_and_setup(uri, config):
config = config or {}
uri = LDAPUrl(uri)
conn = AsyncClient(uri)
conn.protocol_version = ldap.VERSION3
reset_tls_ctx = False
for option_name, value in config.get('options', {}).items():
option = getattr(ldap, 'OPT_X_' + option_name.upper(), None)
if option:
logger.debug("Setting option %s(%s) to %s", option_name, option, value)
conn.set_option(option, value)
if option_name.upper().startswith('TLS_'):
reset_tls_ctx = True
if reset_tls_ctx:
logger.debug("A TLS option was set, resetting the TLS context")
conn.set_option(ldap.OPT_X_TLS_NEWCTX, 0)
if uri.urlscheme == 'ldap':
starttls = config.get('starttls', 'yes')
if starttls:
try:
conn.start_tls_s()
except ldap.SERVER_DOWN:
if starttls == 'hard':
logger.exception("Cannot set up a TLS layer")
raise
logger.warning("Cannot set up a TLS layer, "
"configuration says to proceed anyway")
auth = config.get('auth')
if auth:
mechanism = auth.get('mechanism', 'simple').upper()
if mechanism == 'SIMPLE':
if auth.get('authz_id'):
raise NotImplementedError
logger.debug("binding as %s on %s", auth['binddn'], uri)
conn.bind_s(auth['binddn'], auth['password'])
elif mechanism in ('EXTERNAL', 'GSSAPI'):
conn.sasl_non_interactive_bind_s(mechanism, auth.get('authz_id'))
else:
raise NotImplementedError
return conn
......@@ -28,7 +28,7 @@ import ldap0.controls
import ldap0.controls.syncrepl
from .cookie import SyncreplCookie
from .ldap_wrapper import AsyncClient
from .connection import connect_and_setup
from .signals import Signal
from .syncrepl_observer import SyncreplObserver
from .watchdog import Watchdog
......@@ -53,8 +53,9 @@ class Provider:
cookie_updated = Signal()
state_changed = Signal()
def __init__(self, uri, searchbase, scope=ldap0.SCOPE_BASE, cookie=None, mode='refreshAndPersist'):
def __init__(self, uri, config, searchbase, scope=ldap0.SCOPE_BASE, cookie=None, mode='refreshAndPersist'):
self.uri = uri
self.config = config
self.base = searchbase
self.scope = scope
self.cookie = SyncreplCookie(cookie)
......@@ -71,7 +72,7 @@ class Provider:
def set_up(self):
self.state = ReplicaState.CONNECTING
self.state_changed(self.state)
self.client = AsyncClient(self.uri)
self.client = connect_and_setup(self.uri, self.config)
control = ldap0.controls.syncrepl.SyncRequestControl(
cookie=self.cookie, mode=self.mode)
......@@ -146,14 +147,18 @@ class Provider:
class SyncreplEnvironment:
cookie_updated = Signal()
def __init__(self, uris, searchbase, scope=ldap0.SCOPE_BASE, cookie=None):
def __init__(self, uris, searchbase, scope=ldap0.SCOPE_BASE, cookie=None, config=None):
self.base = searchbase
self.scope = scope
self.cookie = SyncreplCookie(cookie)
self.config = config
self.providers = {}
for uri in uris:
provider = Provider(uri, searchbase, scope, cookie, mode='refreshAndPersist')
provider_config = None
if config:
provider_config = config.get(uri, config)
provider = Provider(uri, provider_config, searchbase, scope, cookie, mode='refreshAndPersist')
provider.cookie_updated.connect(self.update_cookie)
self.cookie_updated.connect(provider.environment_update)
......
......@@ -35,6 +35,8 @@ logger = logging.getLogger(__name__)
parser = argparse.ArgumentParser()
parser.add_argument('-f', '--config', type=argparse.FileType('r'),
help='Authentication and connection config')
parser.add_argument('-b', '--base', required=True, help='Search base')
parser.add_argument('-p', '--persist', action='store_const',
const='refreshAndPersist', default='refreshOnly',
......@@ -73,7 +75,8 @@ class App(urwid.Frame):
self.options = options
self.environment = SyncreplEnvironment(options.args, options.base,
cookie=options.cookie)
cookie=options.cookie,
config=options.config)
header = urwid.Text(options.base)
self.body = urwid.Filler(urwid.Pile([ProviderEntry(provider) for uri, provider
......@@ -98,6 +101,10 @@ async def run(args=None):
options = parser.parse_args(args)
if options.config:
import yaml
options.config = yaml.full_load(options.config.read())
level = None
if options.verbose:
level = logging.DEBUG
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment