prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  "StartTLS is not supported"-Bug in LDAP integrations
  `self` is used in the method `authenticate_ldap_user` which results in the Python exception:
  ```
  NameError: global name 'self' is not defined
  ```
  https://github.com/frappe/frappe/blob/6811f0345e35ea521942db4b29425dd71e403ab8/frappe/integrations/doctype/ldap_settings/ldap_settings.py#L89
  
  Since all exceptions are caught regardless their type, a misleading "StartTLS is not supported" error is thrown:
  https://github.com/frappe/frappe/blob/6811f0345e35ea521942db4b29425dd71e403ab8/frappe/integrations/doctype/ldap_settings/ldap_settings.py#L95
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `frappe/integrations/doctype/ldap_settings/ldap_settings.py`
  Content:
  ```
  1 # -*- coding: utf-8 -*-
  2 # Copyright (c) 2015, Frappe Technologies and contributors
  3 # For license information, please see license.txt
  4 
  5 from __future__ import unicode_literals
  6 import frappe
  7 from frappe import _
  8 from frappe.utils import cstr
  9 from frappe.model.document import Document
  10 
  11 class LDAPSettings(Document):
  12 	def validate(self):
  13 		if not self.flags.ignore_mandatory:
  14 			self.validate_ldap_credentails()
  15 
  16 	def validate_ldap_credentails(self):
  17 		try:
  18 			import ldap
  19 			conn = ldap.initialize(self.ldap_server_url)
  20 			try:
  21 				if self.ssl_tls_mode == 'StartTLS':
  22 					conn.set_option(ldap.OPT_X_TLS_DEMAND, True)
  23 					if self.require_trusted_certificate == 'Yes':
  24 						conn.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND)
  25 					conn.start_tls_s()
  26 			except:
  27 				frappe.throw(_("StartTLS is not supported"))
  28 
  29 			conn.simple_bind_s(self.base_dn, self.get_password(raise_exception=False))
  30 		except ImportError:
  31 			msg = """
  32 				<div>
  33 					{{_("Seems ldap is not installed on system.<br>Guidelines to install ldap dependancies and python package")}},
  34 					<a href="https://discuss.erpnext.com/t/frappe-v-7-1-beta-ldap-dependancies/15841" target="_blank">{{_("Click here")}}</a>,
  35 				</div>
  36 			"""
  37 			frappe.throw(msg, title=_("LDAP Not Installed"))
  38 
  39 		except ldap.LDAPError:
  40 			conn.unbind_s()
  41 			frappe.throw(_("Incorrect UserId or Password"))
  42 
  43 def get_ldap_settings():
  44 	try:
  45 		settings = frappe.get_doc("LDAP Settings")
  46 
  47 		settings.update({
  48 			"method": "frappe.integrations.doctype.ldap_settings.ldap_settings.login"
  49 		})
  50 		return settings
  51 	except Exception:
  52 		# this will return blank settings
  53 		return frappe._dict()
  54 
  55 @frappe.whitelist(allow_guest=True)
  56 def login():
  57 	#### LDAP LOGIN LOGIC #####
  58 	args = frappe.form_dict
  59 	user = authenticate_ldap_user(frappe.as_unicode(args.usr), frappe.as_unicode(args.pwd))
  60 
  61 	frappe.local.login_manager.user = user.name
  62 	frappe.local.login_manager.post_login()
  63 
  64 	# because of a GET request!
  65 	frappe.db.commit()
  66 
  67 def authenticate_ldap_user(user=None, password=None):
  68 	dn = None
  69 	params = {}
  70 	settings = get_ldap_settings()
  71 
  72 	try:
  73 		import ldap
  74 	except:
  75 		msg = """
  76 			<div>
  77 				{{_("Seems ldap is not installed on system.")}}<br>
  78 				<a href"https://discuss.erpnext.com/t/frappe-v-7-1-beta-ldap-dependancies/15841">{{_("Click here")}}</a>,
  79 					{{_("Guidelines to install ldap dependancies and python")}}
  80 			</div>
  81 		"""
  82 		frappe.throw(msg, title=_("LDAP Not Installed"))
  83 
  84 	conn = ldap.initialize(settings.ldap_server_url)
  85 
  86 	try:
  87 		try:
  88 			# set TLS settings for secure connection
  89 			if self.ssl_tls_mode == 'StartTLS':
  90 				conn.set_option(ldap.OPT_X_TLS_DEMAND, True)
  91 				if self.require_trusted_certificate == 'Yes':
  92 					conn.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND)
  93 				conn.start_tls_s()
  94 		except:
  95 			frappe.throw(_("StartTLS is not supported"))
  96 
  97 		# simple_bind_s is synchronous binding to server, it takes two param  DN and password
  98 		conn.simple_bind_s(settings.base_dn, settings.get_password(raise_exception=False))
  99 
  100 		#search for surnames beginning with a
  101 		#available options for how deep a search you want.
  102 		#LDAP_SCOPE_BASE, LDAP_SCOPE_ONELEVEL,LDAP_SCOPE_SUBTREE,
  103 		result = conn.search_s(settings.organizational_unit, ldap.SCOPE_SUBTREE,
  104 			settings.ldap_search_string.format(user))
  105 
  106 		for dn, r in result:
  107 			dn = cstr(dn)
  108 			params["email"] = cstr(r[settings.ldap_email_field][0])
  109 			params["username"] = cstr(r[settings.ldap_username_field][0])
  110 			params["first_name"] = cstr(r[settings.ldap_first_name_field][0])
  111 
  112 		if dn:
  113 			conn.simple_bind_s(dn, frappe.as_unicode(password))
  114 			return create_user(params)
  115 		else:
  116 			frappe.throw(_("Not a valid LDAP user"))
  117 
  118 	except ldap.LDAPError:
  119 		conn.unbind_s()
  120 		frappe.throw(_("Incorrect UserId or Password"))
  121 
  122 def create_user(params):
  123 	if frappe.db.exists("User", params["email"]):
  124 		return frappe.get_doc("User", params["email"])
  125 
  126 	else:
  127 		params.update({
  128 			"doctype": "User",
  129 			"send_welcome_email": 0,
  130 			"language": "",
  131 			"user_type": "System User",
  132 			"roles": [{
  133 				"role": _("Blogger")
  134 			}]
  135 		})
  136 
  137 		user = frappe.get_doc(params).insert(ignore_permissions=True)
  138 		frappe.db.commit()
  139 
  140 		return user
  141
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/frappe/integrations/doctype/ldap_settings/ldap_settings.py b/frappe/integrations/doctype/ldap_settings/ldap_settings.py
  --- a/frappe/integrations/doctype/ldap_settings/ldap_settings.py
  +++ b/frappe/integrations/doctype/ldap_settings/ldap_settings.py
  @@ -86,7 +86,7 @@
   	try:
   		try:
   			# set TLS settings for secure connection
  -			if self.ssl_tls_mode == 'StartTLS':
  +			if settings.ssl_tls_mode == 'StartTLS':
   				conn.set_option(ldap.OPT_X_TLS_DEMAND, True)
   				if self.require_trusted_certificate == 'Yes':
   					conn.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND)
  
verification_info: {"golden_diff": "diff --git a/frappe/integrations/doctype/ldap_settings/ldap_settings.py b/frappe/integrations/doctype/ldap_settings/ldap_settings.py\n--- a/frappe/integrations/doctype/ldap_settings/ldap_settings.py\n+++ b/frappe/integrations/doctype/ldap_settings/ldap_settings.py\n@@ -86,7 +86,7 @@\n \ttry:\n \t\ttry:\n \t\t\t# set TLS settings for secure connection\n-\t\t\tif self.ssl_tls_mode == 'StartTLS':\n+\t\t\tif settings.ssl_tls_mode == 'StartTLS':\n \t\t\t\tconn.set_option(ldap.OPT_X_TLS_DEMAND, True)\n \t\t\t\tif self.require_trusted_certificate == 'Yes':\n \t\t\t\t\tconn.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND)\n", "issue": "\"StartTLS is not supported\"-Bug in LDAP integrations\n`self` is used in the method `authenticate_ldap_user` which results in the Python exception:\r\n```\r\nNameError: global name 'self' is not defined\r\n```\r\nhttps://github.com/frappe/frappe/blob/6811f0345e35ea521942db4b29425dd71e403ab8/frappe/integrations/doctype/ldap_settings/ldap_settings.py#L89\r\n\r\nSince all exceptions are caught regardless their type, a misleading \"StartTLS is not supported\" error is thrown:\r\nhttps://github.com/frappe/frappe/blob/6811f0345e35ea521942db4b29425dd71e403ab8/frappe/integrations/doctype/ldap_settings/ldap_settings.py#L95\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n# Copyright (c) 2015, Frappe Technologies and contributors\n# For license information, please see license.txt\n\nfrom __future__ import unicode_literals\nimport frappe\nfrom frappe import _\nfrom frappe.utils import cstr\nfrom frappe.model.document import Document\n\nclass LDAPSettings(Document):\n\tdef validate(self):\n\t\tif not self.flags.ignore_mandatory:\n\t\t\tself.validate_ldap_credentails()\n\n\tdef validate_ldap_credentails(self):\n\t\ttry:\n\t\t\timport ldap\n\t\t\tconn = ldap.initialize(self.ldap_server_url)\n\t\t\ttry:\n\t\t\t\tif self.ssl_tls_mode == 'StartTLS':\n\t\t\t\t\tconn.set_option(ldap.OPT_X_TLS_DEMAND, True)\n\t\t\t\t\tif self.require_trusted_certificate == 'Yes':\n\t\t\t\t\t\tconn.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND)\n\t\t\t\t\tconn.start_tls_s()\n\t\t\texcept:\n\t\t\t\tfrappe.throw(_(\"StartTLS is not supported\"))\n\n\t\t\tconn.simple_bind_s(self.base_dn, self.get_password(raise_exception=False))\n\t\texcept ImportError:\n\t\t\tmsg = \"\"\"\n\t\t\t\t<div>\n\t\t\t\t\t{{_(\"Seems ldap is not installed on system.<br>Guidelines to install ldap dependancies and python package\")}},\n\t\t\t\t\t<a href=\"https://discuss.erpnext.com/t/frappe-v-7-1-beta-ldap-dependancies/15841\" target=\"_blank\">{{_(\"Click here\")}}</a>,\n\t\t\t\t</div>\n\t\t\t\"\"\"\n\t\t\tfrappe.throw(msg, title=_(\"LDAP Not Installed\"))\n\n\t\texcept ldap.LDAPError:\n\t\t\tconn.unbind_s()\n\t\t\tfrappe.throw(_(\"Incorrect UserId or Password\"))\n\ndef get_ldap_settings():\n\ttry:\n\t\tsettings = frappe.get_doc(\"LDAP Settings\")\n\n\t\tsettings.update({\n\t\t\t\"method\": \"frappe.integrations.doctype.ldap_settings.ldap_settings.login\"\n\t\t})\n\t\treturn settings\n\texcept Exception:\n\t\t# this will return blank settings\n\t\treturn frappe._dict()\n\n@frappe.whitelist(allow_guest=True)\ndef login():\n\t#### LDAP LOGIN LOGIC #####\n\targs = frappe.form_dict\n\tuser = authenticate_ldap_user(frappe.as_unicode(args.usr), frappe.as_unicode(args.pwd))\n\n\tfrappe.local.login_manager.user = user.name\n\tfrappe.local.login_manager.post_login()\n\n\t# because of a GET request!\n\tfrappe.db.commit()\n\ndef authenticate_ldap_user(user=None, password=None):\n\tdn = None\n\tparams = {}\n\tsettings = get_ldap_settings()\n\n\ttry:\n\t\timport ldap\n\texcept:\n\t\tmsg = \"\"\"\n\t\t\t<div>\n\t\t\t\t{{_(\"Seems ldap is not installed on system.\")}}<br>\n\t\t\t\t<a href\"https://discuss.erpnext.com/t/frappe-v-7-1-beta-ldap-dependancies/15841\">{{_(\"Click here\")}}</a>,\n\t\t\t\t\t{{_(\"Guidelines to install ldap dependancies and python\")}}\n\t\t\t</div>\n\t\t\"\"\"\n\t\tfrappe.throw(msg, title=_(\"LDAP Not Installed\"))\n\n\tconn = ldap.initialize(settings.ldap_server_url)\n\n\ttry:\n\t\ttry:\n\t\t\t# set TLS settings for secure connection\n\t\t\tif self.ssl_tls_mode == 'StartTLS':\n\t\t\t\tconn.set_option(ldap.OPT_X_TLS_DEMAND, True)\n\t\t\t\tif self.require_trusted_certificate == 'Yes':\n\t\t\t\t\tconn.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND)\n\t\t\t\tconn.start_tls_s()\n\t\texcept:\n\t\t\tfrappe.throw(_(\"StartTLS is not supported\"))\n\n\t\t# simple_bind_s is synchronous binding to server, it takes two param  DN and password\n\t\tconn.simple_bind_s(settings.base_dn, settings.get_password(raise_exception=False))\n\n\t\t#search for surnames beginning with a\n\t\t#available options for how deep a search you want.\n\t\t#LDAP_SCOPE_BASE, LDAP_SCOPE_ONELEVEL,LDAP_SCOPE_SUBTREE,\n\t\tresult = conn.search_s(settings.organizational_unit, ldap.SCOPE_SUBTREE,\n\t\t\tsettings.ldap_search_string.format(user))\n\n\t\tfor dn, r in result:\n\t\t\tdn = cstr(dn)\n\t\t\tparams[\"email\"] = cstr(r[settings.ldap_email_field][0])\n\t\t\tparams[\"username\"] = cstr(r[settings.ldap_username_field][0])\n\t\t\tparams[\"first_name\"] = cstr(r[settings.ldap_first_name_field][0])\n\n\t\tif dn:\n\t\t\tconn.simple_bind_s(dn, frappe.as_unicode(password))\n\t\t\treturn create_user(params)\n\t\telse:\n\t\t\tfrappe.throw(_(\"Not a valid LDAP user\"))\n\n\texcept ldap.LDAPError:\n\t\tconn.unbind_s()\n\t\tfrappe.throw(_(\"Incorrect UserId or Password\"))\n\ndef create_user(params):\n\tif frappe.db.exists(\"User\", params[\"email\"]):\n\t\treturn frappe.get_doc(\"User\", params[\"email\"])\n\n\telse:\n\t\tparams.update({\n\t\t\t\"doctype\": \"User\",\n\t\t\t\"send_welcome_email\": 0,\n\t\t\t\"language\": \"\",\n\t\t\t\"user_type\": \"System User\",\n\t\t\t\"roles\": [{\n\t\t\t\t\"role\": _(\"Blogger\")\n\t\t\t}]\n\t\t})\n\n\t\tuser = frappe.get_doc(params).insert(ignore_permissions=True)\n\t\tfrappe.db.commit()\n\n\t\treturn user\n", "path": "frappe/integrations/doctype/ldap_settings/ldap_settings.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n# Copyright (c) 2015, Frappe Technologies and contributors\n# For license information, please see license.txt\n\nfrom __future__ import unicode_literals\nimport frappe\nfrom frappe import _\nfrom frappe.utils import cstr\nfrom frappe.model.document import Document\n\nclass LDAPSettings(Document):\n\tdef validate(self):\n\t\tif not self.flags.ignore_mandatory:\n\t\t\tself.validate_ldap_credentails()\n\n\tdef validate_ldap_credentails(self):\n\t\ttry:\n\t\t\timport ldap\n\t\t\tconn = ldap.initialize(self.ldap_server_url)\n\t\t\ttry:\n\t\t\t\tif self.ssl_tls_mode == 'StartTLS':\n\t\t\t\t\tconn.set_option(ldap.OPT_X_TLS_DEMAND, True)\n\t\t\t\t\tif self.require_trusted_certificate == 'Yes':\n\t\t\t\t\t\tconn.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND)\n\t\t\t\t\tconn.start_tls_s()\n\t\t\texcept:\n\t\t\t\tfrappe.throw(_(\"StartTLS is not supported\"))\n\n\t\t\tconn.simple_bind_s(self.base_dn, self.get_password(raise_exception=False))\n\t\texcept ImportError:\n\t\t\tmsg = \"\"\"\n\t\t\t\t<div>\n\t\t\t\t\t{{_(\"Seems ldap is not installed on system.<br>Guidelines to install ldap dependancies and python package\")}},\n\t\t\t\t\t<a href=\"https://discuss.erpnext.com/t/frappe-v-7-1-beta-ldap-dependancies/15841\" target=\"_blank\">{{_(\"Click here\")}}</a>,\n\t\t\t\t</div>\n\t\t\t\"\"\"\n\t\t\tfrappe.throw(msg, title=_(\"LDAP Not Installed\"))\n\n\t\texcept ldap.LDAPError:\n\t\t\tconn.unbind_s()\n\t\t\tfrappe.throw(_(\"Incorrect UserId or Password\"))\n\ndef get_ldap_settings():\n\ttry:\n\t\tsettings = frappe.get_doc(\"LDAP Settings\")\n\n\t\tsettings.update({\n\t\t\t\"method\": \"frappe.integrations.doctype.ldap_settings.ldap_settings.login\"\n\t\t})\n\t\treturn settings\n\texcept Exception:\n\t\t# this will return blank settings\n\t\treturn frappe._dict()\n\n@frappe.whitelist(allow_guest=True)\ndef login():\n\t#### LDAP LOGIN LOGIC #####\n\targs = frappe.form_dict\n\tuser = authenticate_ldap_user(frappe.as_unicode(args.usr), frappe.as_unicode(args.pwd))\n\n\tfrappe.local.login_manager.user = user.name\n\tfrappe.local.login_manager.post_login()\n\n\t# because of a GET request!\n\tfrappe.db.commit()\n\ndef authenticate_ldap_user(user=None, password=None):\n\tdn = None\n\tparams = {}\n\tsettings = get_ldap_settings()\n\n\ttry:\n\t\timport ldap\n\texcept:\n\t\tmsg = \"\"\"\n\t\t\t<div>\n\t\t\t\t{{_(\"Seems ldap is not installed on system.\")}}<br>\n\t\t\t\t<a href\"https://discuss.erpnext.com/t/frappe-v-7-1-beta-ldap-dependancies/15841\">{{_(\"Click here\")}}</a>,\n\t\t\t\t\t{{_(\"Guidelines to install ldap dependancies and python\")}}\n\t\t\t</div>\n\t\t\"\"\"\n\t\tfrappe.throw(msg, title=_(\"LDAP Not Installed\"))\n\n\tconn = ldap.initialize(settings.ldap_server_url)\n\n\ttry:\n\t\ttry:\n\t\t\t# set TLS settings for secure connection\n\t\t\tif settings.ssl_tls_mode == 'StartTLS':\n\t\t\t\tconn.set_option(ldap.OPT_X_TLS_DEMAND, True)\n\t\t\t\tif self.require_trusted_certificate == 'Yes':\n\t\t\t\t\tconn.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND)\n\t\t\t\tconn.start_tls_s()\n\t\texcept:\n\t\t\tfrappe.throw(_(\"StartTLS is not supported\"))\n\n\t\t# simple_bind_s is synchronous binding to server, it takes two param  DN and password\n\t\tconn.simple_bind_s(settings.base_dn, settings.get_password(raise_exception=False))\n\n\t\t#search for surnames beginning with a\n\t\t#available options for how deep a search you want.\n\t\t#LDAP_SCOPE_BASE, LDAP_SCOPE_ONELEVEL,LDAP_SCOPE_SUBTREE,\n\t\tresult = conn.search_s(settings.organizational_unit, ldap.SCOPE_SUBTREE,\n\t\t\tsettings.ldap_search_string.format(user))\n\n\t\tfor dn, r in result:\n\t\t\tdn = cstr(dn)\n\t\t\tparams[\"email\"] = cstr(r[settings.ldap_email_field][0])\n\t\t\tparams[\"username\"] = cstr(r[settings.ldap_username_field][0])\n\t\t\tparams[\"first_name\"] = cstr(r[settings.ldap_first_name_field][0])\n\n\t\tif dn:\n\t\t\tconn.simple_bind_s(dn, frappe.as_unicode(password))\n\t\t\treturn create_user(params)\n\t\telse:\n\t\t\tfrappe.throw(_(\"Not a valid LDAP user\"))\n\n\texcept ldap.LDAPError:\n\t\tconn.unbind_s()\n\t\tfrappe.throw(_(\"Incorrect UserId or Password\"))\n\ndef create_user(params):\n\tif frappe.db.exists(\"User\", params[\"email\"]):\n\t\treturn frappe.get_doc(\"User\", params[\"email\"])\n\n\telse:\n\t\tparams.update({\n\t\t\t\"doctype\": \"User\",\n\t\t\t\"send_welcome_email\": 0,\n\t\t\t\"language\": \"\",\n\t\t\t\"user_type\": \"System User\",\n\t\t\t\"roles\": [{\n\t\t\t\t\"role\": _(\"Blogger\")\n\t\t\t}]\n\t\t})\n\n\t\tuser = frappe.get_doc(params).insert(ignore_permissions=True)\n\t\tfrappe.db.commit()\n\n\t\treturn user\n", "path": "frappe/integrations/doctype/ldap_settings/ldap_settings.py"}]}