pypubsub_ldap.py 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. #!/usr/bin/env python3
  2. # Licensed to the Apache Software Foundation (ASF) under one
  3. # or more contributor license agreements. See the NOTICE file
  4. # distributed with this work for additional information
  5. # regarding copyright ownership. The ASF licenses this file
  6. # to you under the Apache License, Version 2.0 (the
  7. # "License"); you may not use this file except in compliance
  8. # with the License. You may obtain a copy of the License at
  9. #
  10. # http://www.apache.org/licenses/LICENSE-2.0
  11. #
  12. # Unless required by applicable law or agreed to in writing,
  13. # software distributed under the License is distributed on an
  14. # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. # KIND, either express or implied. See the License for the
  16. # specific language governing permissions and limitations
  17. # under the License.
  18. """ This is the LDAP component of PyPubSub """
  19. import ldap
  20. import asyncio
  21. LDAP_SCHEME = {
  22. 'uri': str,
  23. 'user_dn': str,
  24. 'base_scope': str,
  25. 'membership_patterns': list,
  26. 'acl': dict
  27. }
  28. def vet_settings(lconf):
  29. """ Simple test to vet LDAP settings if present """
  30. if lconf:
  31. for k, v in LDAP_SCHEME.items():
  32. if not isinstance(lconf.get(k), v):
  33. raise Exception(f"LDAP configuration item {k} must be of type {v.__name__}!")
  34. assert ldap.initialize(lconf['uri'])
  35. print("==== LDAP configuration looks kosher, enabling LDAP authentication as fallback ====")
  36. async def get_groups(lconf, user, password):
  37. """ Async fetching of groups an LDAP user belongs to """
  38. bind_dn = lconf['user_dn'] % user
  39. try:
  40. client = ldap.initialize(lconf['uri'])
  41. client.set_option(ldap.OPT_REFERRALS, 0)
  42. client.set_option(ldap.OPT_TIMEOUT, 0)
  43. rv = client.simple_bind(bind_dn, password)
  44. while True:
  45. res = client.result(rv, timeout=0)
  46. if res and res != (None, None):
  47. break
  48. await asyncio.sleep(0.25)
  49. groups = []
  50. for role in lconf['membership_patterns']:
  51. rv = client.search(lconf['base_scope'], ldap.SCOPE_SUBTREE, role % user, ['dn'])
  52. while True:
  53. res = client.result(rv, all=0, timeout=0)
  54. if res:
  55. if res == (None, None):
  56. await asyncio.sleep(0.25)
  57. else:
  58. if not res[1]:
  59. break
  60. for tuples in res[1]:
  61. groups.append(tuples[0])
  62. else:
  63. break
  64. return groups
  65. except Exception as e:
  66. print(f"LDAP Exception for user {user}: {e}")
  67. return []