blob: 834baf83081382231d505aefa1d5e50efdf410d9 [file] [log] [blame]
#!/usr/bin/env python3
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
""" This is the LDAP component of PyPubSub """
import ldap
import asyncio
class LDAPConnection:
def __init__(self, yml: dict):
self.uri = yml.get('uri')
assert isinstance(self.uri, str), "LDAP URI must be a string."
self.user = yml.get('user_dn')
assert isinstance(self.user, str) or self.user is None, "LDAP user DN must be a string or absent."
self.base = yml.get('base_scope')
assert isinstance(self.base, str), "LDAP Base scope must be a string"
self.patterns: list = yml.get('membership_patterns', [])
assert isinstance(self.patterns, list), "LDAP membership patterns must be a list of pattern strings"
self.acl = yml.get('acl')
assert isinstance(self.acl, dict), "LDAP ACL must be a dictionary (hash) of ACLs"
assert ldap.initialize(self.uri)
print("==== LDAP configuration looks kosher, enabling LDAP authentication as fallback ====")
async def get_groups(self, user: str, password: str):
"""Async fetching of groups an LDAP user belongs to"""
bind_dn = self.user % user # Interpolate user DN with username
try:
client = ldap.initialize(self.uri)
client.set_option(ldap.OPT_REFERRALS, 0)
client.set_option(ldap.OPT_TIMEOUT, 0)
rv = client.simple_bind(bind_dn, password)
while True:
res = client.result(rv, timeout=0)
if res and res != (None, None):
break
await asyncio.sleep(0.25)
groups = []
for role in self.patterns:
rv = client.search(self.base, ldap.SCOPE_SUBTREE, role % user, ['dn'])
while True:
res = client.result(rv, all=0, timeout=0)
if res:
if res == (None, None):
await asyncio.sleep(0.25)
else:
if not res[1]:
break
for tuples in res[1]:
groups.append(tuples[0])
else:
break
return groups
except Exception as e:
print(f"LDAP Exception for user {user}: {e}")
return []