gooderp18绿色标准版
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

256 lines
9.3KB

  1. ##########################################################################
  2. #
  3. # pgAdmin 4 - PostgreSQL Tools
  4. #
  5. # Copyright (C) 2013 - 2020, The pgAdmin Development Team
  6. # This software is released under the PostgreSQL Licence
  7. #
  8. ##########################################################################
  9. """A blueprint module implementing the ldap authentication."""
  10. import ssl
  11. import config
  12. from ldap3 import Connection, Server, Tls, ALL, ALL_ATTRIBUTES, ANONYMOUS,\
  13. SIMPLE
  14. from ldap3.core.exceptions import LDAPSocketOpenError, LDAPBindError,\
  15. LDAPInvalidScopeError, LDAPAttributeError, LDAPInvalidFilterError,\
  16. LDAPStartTLSError, LDAPSSLConfigurationError
  17. from flask_babelex import gettext
  18. from urllib.parse import urlparse
  19. from .internal import BaseAuthentication
  20. from pgadmin.model import User, ServerGroup, db, Role
  21. from flask import current_app
  22. from pgadmin.tools.user_management import create_user
  23. ERROR_SEARCHING_LDAP_DIRECTORY = "Error searching the LDAP directory: {}"
  24. class LDAPAuthentication(BaseAuthentication):
  25. """Ldap Authentication Class"""
  26. def get_friendly_name(self):
  27. return gettext("ldap")
  28. def authenticate(self, form):
  29. self.username = form.data['email']
  30. self.password = form.data['password']
  31. self.dedicated_user = True
  32. self.start_tls = False
  33. user_email = None
  34. # Check the dedicated ldap user
  35. self.bind_user = getattr(config, 'LDAP_BIND_USER', None)
  36. self.bind_pass = getattr(config, 'LDAP_BIND_PASSWORD', None)
  37. # Check for the anonymous binding
  38. self.anonymous_bind = getattr(config, 'LDAP_ANONYMOUS_BIND', False)
  39. if self.bind_user and not self.bind_pass:
  40. return False, "LDAP configuration error: Set the bind password."
  41. # if no dedicated ldap user is configured then use the login
  42. # username and password
  43. if not self.bind_user and not self.bind_pass and\
  44. self.anonymous_bind is False:
  45. user_dn = "{0}={1},{2}".format(config.LDAP_USERNAME_ATTRIBUTE,
  46. self.username,
  47. config.LDAP_BASE_DN
  48. )
  49. self.bind_user = user_dn
  50. self.bind_pass = self.password
  51. self.dedicated_user = False
  52. # Connect ldap server
  53. status, msg = self.connect()
  54. if not status:
  55. return status, msg
  56. status, ldap_user = self.search_ldap_user()
  57. if not status:
  58. return status, ldap_user
  59. # If dedicated user is configured
  60. if self.dedicated_user:
  61. # Get the user DN from the user ldap entry
  62. self.bind_user = ldap_user.entry_dn
  63. self.bind_pass = self.password
  64. self.anonymous_bind = False
  65. status, msg = self.connect()
  66. if not status:
  67. return status, msg
  68. if 'mail' in ldap_user:
  69. user_email = ldap_user['mail'].value
  70. return self.__auto_create_user(user_email)
  71. def connect(self):
  72. """Setup the connection to the LDAP server and authenticate the user.
  73. """
  74. status, server = self._configure_server()
  75. if not status:
  76. return status, server
  77. # Create the connection
  78. try:
  79. if self.anonymous_bind:
  80. self.conn = Connection(server,
  81. auto_bind=True,
  82. authentication=ANONYMOUS
  83. )
  84. else:
  85. self.conn = Connection(server,
  86. user=self.bind_user,
  87. password=self.bind_pass,
  88. auto_bind=True,
  89. authentication=SIMPLE
  90. )
  91. except LDAPSocketOpenError as e:
  92. current_app.logger.exception(
  93. "Error connecting to the LDAP server: {}\n".format(e))
  94. return False, "Error connecting to the LDAP server:" \
  95. " {}\n".format(e.args[0])
  96. except LDAPBindError as e:
  97. current_app.logger.exception(
  98. "Error binding to the LDAP server.")
  99. return False, "Error binding to the LDAP server."
  100. except Exception as e:
  101. current_app.logger.exception(
  102. "Error connecting to the LDAP server: {}\n".format(e))
  103. return False, "Error connecting to the LDAP server:" \
  104. " {}\n".format(e.args[0])
  105. # Enable TLS if STARTTLS is configured
  106. if self.start_tls:
  107. try:
  108. self.conn.start_tls()
  109. except LDAPStartTLSError as e:
  110. current_app.logger.exception(
  111. "Error starting TLS: {}\n".format(e))
  112. return False, "Error starting TLS: {}\n".format(e.args[0])
  113. return True, None
  114. def __auto_create_user(self, user_email):
  115. """Add the ldap user to the internal SQLite database."""
  116. if config.LDAP_AUTO_CREATE_USER:
  117. user = User.query.filter_by(
  118. username=self.username).first()
  119. if user is None:
  120. return create_user({
  121. 'username': self.username,
  122. 'email': user_email,
  123. 'role': 2,
  124. 'active': True,
  125. 'auth_source': 'ldap'
  126. })
  127. return True, None
  128. def __configure_tls(self):
  129. ca_cert_file = getattr(config, 'LDAP_CA_CERT_FILE', None)
  130. cert_file = getattr(config, 'LDAP_CERT_FILE', None)
  131. key_file = getattr(config, 'LDAP_KEY_FILE', None)
  132. cert_validate = ssl.CERT_NONE
  133. if ca_cert_file and cert_file and key_file:
  134. cert_validate = ssl.CERT_REQUIRED
  135. try:
  136. tls = Tls(
  137. local_private_key_file=key_file,
  138. local_certificate_file=cert_file,
  139. validate=cert_validate,
  140. version=ssl.PROTOCOL_TLSv1_2,
  141. ca_certs_file=ca_cert_file)
  142. except LDAPSSLConfigurationError as e:
  143. current_app.logger.exception(
  144. "LDAP configuration error: {}\n".format(e))
  145. return False, "LDAP configuration error: {}\n".format(
  146. e.args[0])
  147. return True, tls
  148. def _configure_server(self):
  149. # Parse the server URI
  150. uri = getattr(config, 'LDAP_SERVER_URI', None)
  151. if uri:
  152. uri = urlparse(uri)
  153. # Create the TLS configuration object if required
  154. tls = None
  155. if type(uri) == str:
  156. return False, "LDAP configuration error: Set the proper LDAP URI."
  157. if uri.scheme == 'ldaps' or config.LDAP_USE_STARTTLS:
  158. status, tls = self.__configure_tls()
  159. if not status:
  160. return status, tls
  161. if uri.scheme != 'ldaps' and config.LDAP_USE_STARTTLS:
  162. self.start_tls = True
  163. try:
  164. # Create the server object
  165. server = Server(uri.hostname,
  166. port=uri.port,
  167. use_ssl=(uri.scheme == 'ldaps'),
  168. get_info=ALL,
  169. tls=tls,
  170. connect_timeout=config.LDAP_CONNECTION_TIMEOUT)
  171. except ValueError as e:
  172. return False, "LDAP configuration error: {}.".format(e)
  173. return True, server
  174. def search_ldap_user(self):
  175. """Get a list of users from the LDAP server based on config
  176. search criteria."""
  177. try:
  178. search_base_dn = config.LDAP_SEARCH_BASE_DN
  179. if (not search_base_dn or search_base_dn == '<Search-Base-DN>')\
  180. and (self.anonymous_bind or self.dedicated_user):
  181. return False, "LDAP configuration error:" \
  182. " Set the Search Domain."
  183. elif not search_base_dn or search_base_dn == '<Search-Base-DN>':
  184. search_base_dn = config.LDAP_BASE_DN
  185. self.conn.search(search_base=search_base_dn,
  186. search_filter=config.LDAP_SEARCH_FILTER,
  187. search_scope=config.LDAP_SEARCH_SCOPE,
  188. attributes=ALL_ATTRIBUTES
  189. )
  190. except LDAPInvalidScopeError as e:
  191. current_app.logger.exception(
  192. ERROR_SEARCHING_LDAP_DIRECTORY.format(e.args[0])
  193. )
  194. return False, ERROR_SEARCHING_LDAP_DIRECTORY.format(e.args[0])
  195. except LDAPAttributeError as e:
  196. current_app.logger.exception(
  197. ERROR_SEARCHING_LDAP_DIRECTORY.format(e)
  198. )
  199. return False, ERROR_SEARCHING_LDAP_DIRECTORY.format(e.args[0])
  200. except LDAPInvalidFilterError as e:
  201. current_app.logger.exception(
  202. ERROR_SEARCHING_LDAP_DIRECTORY.format(e)
  203. )
  204. return False, ERROR_SEARCHING_LDAP_DIRECTORY.format(e.args[0])
  205. for entry in self.conn.entries:
  206. if config.LDAP_USERNAME_ATTRIBUTE in entry and self.username == \
  207. entry[config.LDAP_USERNAME_ATTRIBUTE].value:
  208. return True, entry
  209. return False, ERROR_SEARCHING_LDAP_DIRECTORY.format(
  210. "Could not find the specified user.")
上海开阖软件有限公司 沪ICP备12045867号-1