We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Choose more secure configuration defaults
The configuration for SMTP server, privacyIDEA server or LDAP resolvers default to not using TLS and no certificate verification.
Better defaults or additional documentation may reduce the chance of accidental misconfiguration by the admins.

We have the following occurances:

* [x] Config -> SMTP Server: ``Use TLS``, check it by default.
* [x] Config -> privacyIDEA Server ``Verify the certificate``, check it by default
* [x] Config -> SMS Gateways (HTTP Gateway), default ``Check_SSL`` to "yes".
* ~~Config->User->New LDAP server: already defaults to ``verify TLS``.~~

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `privacyidea/lib/smsprovider/HttpSMSProvider.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 #
3 #    E-mail: info@privacyidea.org
4 #    Contact: www.privacyidea.org
5 #
6 #    2018-06-15 Pascal Fuks <pascal@foxit.pro>
7 #               Added REGEXP parameter on phone number
8 #    2018-01-10 Cornelius Kölbel <cornelius.koelbel@netknights.it>
9 #               Fix type cast for timeout
10 #    2016-06-14 Cornelius Kölbel <cornelius@privacyidea.org>
11 #               Add properties for new SMS provider model
12 #    2016-04-08 Cornelius Kölbel <cornelius@privacyidea.org>
13 #               Remote "None" as redundant 2nd argument to get
14 #    2016-01-13 Cornelius Kölbel <cornelius@privacyidea.org>
15 #               omit data object in GET request
16 #               omit params in POST request
17 #
18 #    privacyIDEA is a fork of LinOTP
19 #    May 28, 2014 Cornelius Kölbel
20 #    2015-01-30 Rewrite for migration to flask
21 #               Cornelius Kölbel <cornelius@privacyidea.org>
22 #
23 #
24 #    Copyright (C) LinOTP: 2010 - 2014 LSE Leading Security Experts GmbH
25 #
26 #    This program is free software: you can redistribute it and/or
27 #    modify it under the terms of the GNU Affero General Public
28 #    License, version 3, as published by the Free Software Foundation.
29 #
30 #    This program is distributed in the hope that it will be useful,
31 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
32 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
33 #    GNU Affero General Public License for more details.
34 #
35 #    You should have received a copy of the
36 #               GNU Affero General Public License
37 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
38 #
39 #
40 
41 __doc__ = """This is the SMSClass to send SMS via HTTP Gateways
42 It can handle HTTP/HTTPS PUT and GET requests also with Proxy support
43 
44 The code is tested in tests/test_lib_smsprovider
45 """
46 
47 from privacyidea.lib.smsprovider.SMSProvider import (ISMSProvider, SMSError)
48 from privacyidea.lib import _
49 import requests
50 from six.moves.urllib.parse import urlparse
51 import re
52 import logging
53 log = logging.getLogger(__name__)
54 
55 
56 class HttpSMSProvider(ISMSProvider):
57 
58     @staticmethod
59     def _mangle_phone(phone, config):
60         regexp = config.get("REGEXP")
61         if regexp:
62             try:
63                 m = re.match("^/(.*)/(.*)/$", regexp)
64                 if m:
65                     phone = re.sub(m.group(1), m.group(2), phone)
66             except re.error:
67                 log.warning(u"Can not mangle phone number. "
68                             u"Please check your REGEXP: {0!s}".format(regexp))
69 
70         return phone
71 
72     def submit_message(self, phone, message):
73         """
74         send a message to a phone via an http sms gateway
75 
76         :param phone: the phone number
77         :param message: the message to submit to the phone
78         :return:
79         """
80         log.debug("submitting message {0!r} to {1!s}".format(message, phone))
81         parameter = {}
82         headers = {}
83         if self.smsgateway:
84             phone = self._mangle_phone(phone, self.smsgateway.option_dict)
85             url = self.smsgateway.option_dict.get("URL")
86             method = self.smsgateway.option_dict.get("HTTP_METHOD", "GET")
87             username = self.smsgateway.option_dict.get("USERNAME")
88             password = self.smsgateway.option_dict.get("PASSWORD")
89             ssl_verify = self.smsgateway.option_dict.get("CHECK_SSL",
90                                                          "yes") == "yes"
91             # FIXME: The Proxy option is deprecated and will be removed a version > 2.21
92             proxy = self.smsgateway.option_dict.get("PROXY")
93             http_proxy = self.smsgateway.option_dict.get('HTTP_PROXY')
94             https_proxy = self.smsgateway.option_dict.get('HTTPS_PROXY')
95             timeout = self.smsgateway.option_dict.get("TIMEOUT") or 3
96             for k, v in self.smsgateway.option_dict.items():
97                 if k not in self.parameters().get("parameters"):
98                     # This is an additional option
99                     parameter[k] = v.format(otp=message, phone=phone)
100             headers = self.smsgateway.header_dict
101         else:
102             phone = self._mangle_phone(phone, self.config)
103             url = self.config.get('URL')
104             method = self.config.get('HTTP_Method', 'GET')
105             username = self.config.get('USERNAME')
106             password = self.config.get('PASSWORD')
107             ssl_verify = self.config.get('CHECK_SSL', True)
108             # FIXME: The Proxy option is deprecated and will be removed a version > 2.21
109             proxy = self.config.get('PROXY')
110             http_proxy = self.config.get('HTTP_PROXY')
111             https_proxy = self.config.get('HTTPS_PROXY')
112             parameter = self._get_parameters(message, phone)
113             timeout = self.config.get("TIMEOUT") or 3
114 
115         if url is None:
116             log.warning("can not submit message. URL is missing.")
117             raise SMSError(-1, "No URL specified in the provider config.")
118         basic_auth = None
119 
120         # there might be the basic authentication in the request url
121         # like http://user:passw@hostname:port/path
122         if password is None and username is None:
123             parsed_url = urlparse(url)
124             if "@" in parsed_url[1]:
125                 puser, server = parsed_url[1].split('@')
126                 username, password = puser.split(':')
127 
128         if username and password is not None:
129             basic_auth = (username, password)
130 
131         proxies = {}
132         if http_proxy:
133             proxies["http"] = http_proxy
134         if https_proxy:
135             proxies["https"] = https_proxy
136         if not proxies and proxy:
137             # No new proxy config but only the old one.
138             protocol = proxy.split(":")[0]
139             proxies = {protocol: proxy}
140 
141         # url, parameter, username, password, method
142         requestor = requests.get
143         params = parameter
144         data = {}
145         if method == "POST":
146             requestor = requests.post
147             params = {}
148             data = parameter
149 
150         log.debug(u"issuing request with parameters {0!s} headers {1!s} and method {2!s} and"
151                   "authentication {3!s} to url {4!s}.".format(params, headers, method,
152                                                               basic_auth, url))
153         # Todo: drop basic auth if Authorization-Header is given?
154         r = requestor(url, params=params, headers=headers,
155                       data=data,
156                       verify=ssl_verify,
157                       auth=basic_auth,
158                       timeout=float(timeout),
159                       proxies=proxies)
160         log.debug("queued SMS on the HTTP gateway. status code returned: {0!s}".format(
161                   r.status_code))
162 
163         # We assume, that all gateways return with HTTP Status Code 200,
164         # 201 or 202
165         if r.status_code not in [200, 201, 202]:
166             raise SMSError(r.status_code, "SMS could not be "
167                                           "sent: %s" % r.status_code)
168         success = self._check_success(r)
169         return success
170 
171     def _get_parameters(self, message, phone):
172 
173         urldata = {}
174         # transfer the phone key
175         phoneKey = self.config.get('SMS_PHONENUMBER_KEY', "phone")
176         urldata[phoneKey] = phone
177         # transfer the sms key
178         messageKey = self.config.get('SMS_TEXT_KEY', "sms")
179         urldata[messageKey] = message
180         params = self.config.get('PARAMETER', {})
181         urldata.update(params)
182         log.debug("[getParameters] urldata: {0!s}".format(urldata))
183         return urldata
184 
185     def _check_success(self, response):
186         """
187         Check the success according to the reply
188         1. if RETURN_SUCCESS is defined
189         2. if RETURN_FAIL is defined
190         :response reply: A response object.
191         """
192         reply = response.text
193         ret = False
194         if self.smsgateway:
195             return_success = self.smsgateway.option_dict.get("RETURN_SUCCESS")
196             return_fail = self.smsgateway.option_dict.get("RETURN_FAIL")
197         else:
198             return_success = self.config.get("RETURN_SUCCESS")
199             return_fail = self.config.get("RETURN_FAIL")
200 
201         if return_success:
202             if return_success in reply:
203                 log.debug("sending sms success")
204                 ret = True
205             else:
206                 log.warning("failed to send sms. Reply %s does not match "
207                             "the RETURN_SUCCESS definition" % reply)
208                 raise SMSError(response.status_code,
209                                "We received a none success reply from the "
210                                "SMS Gateway: {0!s} ({1!s})".format(reply,
211                                                                    return_success))
212 
213         elif return_fail:
214             if return_fail in reply:
215                 log.warning("sending sms failed. %s was not found "
216                             "in %s" % (return_fail, reply))
217                 raise SMSError(response.status_code,
218                                "We received the predefined error from the "
219                                "SMS Gateway.")
220             else:
221                 log.debug("sending sms success")
222                 ret = True
223         else:
224             ret = True
225         return ret
226 
227     @classmethod
228     def parameters(cls):
229         """
230         Return a dictionary, that describes the parameters and options for the
231         SMS provider.
232         Parameters are required keys to values.
233 
234         :return: dict
235         """
236         params = {"options_allowed": True,
237                   "headers_allowed": True,
238                   "parameters": {
239                       "URL": {
240                           "required": True,
241                           "description": _("The base URL of the HTTP Gateway")},
242                       "HTTP_METHOD": {
243                           "required": True,
244                           "description": _("Should the HTTP Gateway be "
245                                            "connected via an HTTP GET or POST "
246                                            "request."),
247                           "values": ["GET", "POST"]},
248                       "RETURN_SUCCESS": {
249                           "description": _("Specify a substring, "
250                                            "that indicates, that the SMS was "
251                                            "delivered successfully.")},
252                       "RETURN_FAIL": {
253                           "description": _("Specify a substring, "
254                                            "that indicates, that the SMS "
255                                            "failed to be delivered.")},
256                       "USERNAME": {
257                           "description": _("Username in case of basic "
258                                            "authentication.")
259                       },
260                       "PASSWORD": {
261                           "description": _("Password in case of basic "
262                                            "authentication.")
263                       },
264                       "CHECK_SSL": {
265                           "description": _("Should the SSL certificate be "
266                                            "verified."),
267                           "values": ["yes", "no"]
268                       },
269                       "REGEXP": {
270                           "description": _("Regular expression to modify the phone number "                 
271                                            "to make it compatible with provider. "
272                                            "Enter something like '/[\\+/]//' to remove "
273                                            "pluses and slashes.")
274                       },
275                       "PROXY": {"description": _("An optional proxy string. DEPRECATED. Do not use "
276                                                  "this anymore. Rather use HTTP_PROXY for http "
277                                                  "connections and HTTPS_PROXY for https "
278                                                  "connection. The PROXY option will be removed in "
279                                                  "future.")},
280                       "HTTP_PROXY": {"description": _("Proxy setting for HTTP connections.")},
281                       "HTTPS_PROXY": {"description": _("Proxy setting for HTTPS connections.")},
282                       "TIMEOUT": {"description": _("The timeout in seconds.")}
283                   }
284                   }
285         return params
286
```
--- 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()

```

diff --git a/privacyidea/lib/smsprovider/HttpSMSProvider.py b/privacyidea/lib/smsprovider/HttpSMSProvider.py
--- a/privacyidea/lib/smsprovider/HttpSMSProvider.py
+++ b/privacyidea/lib/smsprovider/HttpSMSProvider.py
@@ -262,6 +262,7 @@
                                            "authentication.")
                       },
                       "CHECK_SSL": {
+                          "required": True,
                           "description": _("Should the SSL certificate be "
                                            "verified."),
                           "values": ["yes", "no"]

{"golden_diff": "diff --git a/privacyidea/lib/smsprovider/HttpSMSProvider.py b/privacyidea/lib/smsprovider/HttpSMSProvider.py\n--- a/privacyidea/lib/smsprovider/HttpSMSProvider.py\n+++ b/privacyidea/lib/smsprovider/HttpSMSProvider.py\n@@ -262,6 +262,7 @@\n                                            \"authentication.\")\n                       },\n                       \"CHECK_SSL\": {\n+                          \"required\": True,\n                           \"description\": _(\"Should the SSL certificate be \"\n                                            \"verified.\"),\n                           \"values\": [\"yes\", \"no\"]\n", "issue": "Choose more secure configuration defaults\nThe configuration for SMTP server, privacyIDEA server or LDAP resolvers default to not using TLS and no certificate verification.\r\nBetter defaults or additional documentation may reduce the chance of accidental misconfiguration by the admins.\r\n\r\nWe have the following occurances:\r\n\r\n* [x] Config -> SMTP Server: ``Use TLS``, check it by default.\r\n* [x] Config -> privacyIDEA Server ``Verify the certificate``, check it by default\r\n* [x] Config -> SMS Gateways (HTTP Gateway), default ``Check_SSL`` to \"yes\".\r\n* ~~Config->User->New LDAP server: already defaults to ``verify TLS``.~~\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n#\n#    E-mail: info@privacyidea.org\n#    Contact: www.privacyidea.org\n#\n#    2018-06-15 Pascal Fuks <pascal@foxit.pro>\n#               Added REGEXP parameter on phone number\n#    2018-01-10 Cornelius K\u00f6lbel <cornelius.koelbel@netknights.it>\n#               Fix type cast for timeout\n#    2016-06-14 Cornelius K\u00f6lbel <cornelius@privacyidea.org>\n#               Add properties for new SMS provider model\n#    2016-04-08 Cornelius K\u00f6lbel <cornelius@privacyidea.org>\n#               Remote \"None\" as redundant 2nd argument to get\n#    2016-01-13 Cornelius K\u00f6lbel <cornelius@privacyidea.org>\n#               omit data object in GET request\n#               omit params in POST request\n#\n#    privacyIDEA is a fork of LinOTP\n#    May 28, 2014 Cornelius K\u00f6lbel\n#    2015-01-30 Rewrite for migration to flask\n#               Cornelius K\u00f6lbel <cornelius@privacyidea.org>\n#\n#\n#    Copyright (C) LinOTP: 2010 - 2014 LSE Leading Security Experts GmbH\n#\n#    This program is free software: you can redistribute it and/or\n#    modify it under the terms of the GNU Affero General Public\n#    License, version 3, as published by the Free Software Foundation.\n#\n#    This program is distributed in the hope that it will be useful,\n#    but WITHOUT ANY WARRANTY; without even the implied warranty of\n#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n#    GNU Affero General Public License for more details.\n#\n#    You should have received a copy of the\n#               GNU Affero General Public License\n#    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n#\n#\n\n__doc__ = \"\"\"This is the SMSClass to send SMS via HTTP Gateways\nIt can handle HTTP/HTTPS PUT and GET requests also with Proxy support\n\nThe code is tested in tests/test_lib_smsprovider\n\"\"\"\n\nfrom privacyidea.lib.smsprovider.SMSProvider import (ISMSProvider, SMSError)\nfrom privacyidea.lib import _\nimport requests\nfrom six.moves.urllib.parse import urlparse\nimport re\nimport logging\nlog = logging.getLogger(__name__)\n\n\nclass HttpSMSProvider(ISMSProvider):\n\n    @staticmethod\n    def _mangle_phone(phone, config):\n        regexp = config.get(\"REGEXP\")\n        if regexp:\n            try:\n                m = re.match(\"^/(.*)/(.*)/$\", regexp)\n                if m:\n                    phone = re.sub(m.group(1), m.group(2), phone)\n            except re.error:\n                log.warning(u\"Can not mangle phone number. \"\n                            u\"Please check your REGEXP: {0!s}\".format(regexp))\n\n        return phone\n\n    def submit_message(self, phone, message):\n        \"\"\"\n        send a message to a phone via an http sms gateway\n\n        :param phone: the phone number\n        :param message: the message to submit to the phone\n        :return:\n        \"\"\"\n        log.debug(\"submitting message {0!r} to {1!s}\".format(message, phone))\n        parameter = {}\n        headers = {}\n        if self.smsgateway:\n            phone = self._mangle_phone(phone, self.smsgateway.option_dict)\n            url = self.smsgateway.option_dict.get(\"URL\")\n            method = self.smsgateway.option_dict.get(\"HTTP_METHOD\", \"GET\")\n            username = self.smsgateway.option_dict.get(\"USERNAME\")\n            password = self.smsgateway.option_dict.get(\"PASSWORD\")\n            ssl_verify = self.smsgateway.option_dict.get(\"CHECK_SSL\",\n                                                         \"yes\") == \"yes\"\n            # FIXME: The Proxy option is deprecated and will be removed a version > 2.21\n            proxy = self.smsgateway.option_dict.get(\"PROXY\")\n            http_proxy = self.smsgateway.option_dict.get('HTTP_PROXY')\n            https_proxy = self.smsgateway.option_dict.get('HTTPS_PROXY')\n            timeout = self.smsgateway.option_dict.get(\"TIMEOUT\") or 3\n            for k, v in self.smsgateway.option_dict.items():\n                if k not in self.parameters().get(\"parameters\"):\n                    # This is an additional option\n                    parameter[k] = v.format(otp=message, phone=phone)\n            headers = self.smsgateway.header_dict\n        else:\n            phone = self._mangle_phone(phone, self.config)\n            url = self.config.get('URL')\n            method = self.config.get('HTTP_Method', 'GET')\n            username = self.config.get('USERNAME')\n            password = self.config.get('PASSWORD')\n            ssl_verify = self.config.get('CHECK_SSL', True)\n            # FIXME: The Proxy option is deprecated and will be removed a version > 2.21\n            proxy = self.config.get('PROXY')\n            http_proxy = self.config.get('HTTP_PROXY')\n            https_proxy = self.config.get('HTTPS_PROXY')\n            parameter = self._get_parameters(message, phone)\n            timeout = self.config.get(\"TIMEOUT\") or 3\n\n        if url is None:\n            log.warning(\"can not submit message. URL is missing.\")\n            raise SMSError(-1, \"No URL specified in the provider config.\")\n        basic_auth = None\n\n        # there might be the basic authentication in the request url\n        # like http://user:passw@hostname:port/path\n        if password is None and username is None:\n            parsed_url = urlparse(url)\n            if \"@\" in parsed_url[1]:\n                puser, server = parsed_url[1].split('@')\n                username, password = puser.split(':')\n\n        if username and password is not None:\n            basic_auth = (username, password)\n\n        proxies = {}\n        if http_proxy:\n            proxies[\"http\"] = http_proxy\n        if https_proxy:\n            proxies[\"https\"] = https_proxy\n        if not proxies and proxy:\n            # No new proxy config but only the old one.\n            protocol = proxy.split(\":\")[0]\n            proxies = {protocol: proxy}\n\n        # url, parameter, username, password, method\n        requestor = requests.get\n        params = parameter\n        data = {}\n        if method == \"POST\":\n            requestor = requests.post\n            params = {}\n            data = parameter\n\n        log.debug(u\"issuing request with parameters {0!s} headers {1!s} and method {2!s} and\"\n                  \"authentication {3!s} to url {4!s}.\".format(params, headers, method,\n                                                              basic_auth, url))\n        # Todo: drop basic auth if Authorization-Header is given?\n        r = requestor(url, params=params, headers=headers,\n                      data=data,\n                      verify=ssl_verify,\n                      auth=basic_auth,\n                      timeout=float(timeout),\n                      proxies=proxies)\n        log.debug(\"queued SMS on the HTTP gateway. status code returned: {0!s}\".format(\n                  r.status_code))\n\n        # We assume, that all gateways return with HTTP Status Code 200,\n        # 201 or 202\n        if r.status_code not in [200, 201, 202]:\n            raise SMSError(r.status_code, \"SMS could not be \"\n                                          \"sent: %s\" % r.status_code)\n        success = self._check_success(r)\n        return success\n\n    def _get_parameters(self, message, phone):\n\n        urldata = {}\n        # transfer the phone key\n        phoneKey = self.config.get('SMS_PHONENUMBER_KEY', \"phone\")\n        urldata[phoneKey] = phone\n        # transfer the sms key\n        messageKey = self.config.get('SMS_TEXT_KEY', \"sms\")\n        urldata[messageKey] = message\n        params = self.config.get('PARAMETER', {})\n        urldata.update(params)\n        log.debug(\"[getParameters] urldata: {0!s}\".format(urldata))\n        return urldata\n\n    def _check_success(self, response):\n        \"\"\"\n        Check the success according to the reply\n        1. if RETURN_SUCCESS is defined\n        2. if RETURN_FAIL is defined\n        :response reply: A response object.\n        \"\"\"\n        reply = response.text\n        ret = False\n        if self.smsgateway:\n            return_success = self.smsgateway.option_dict.get(\"RETURN_SUCCESS\")\n            return_fail = self.smsgateway.option_dict.get(\"RETURN_FAIL\")\n        else:\n            return_success = self.config.get(\"RETURN_SUCCESS\")\n            return_fail = self.config.get(\"RETURN_FAIL\")\n\n        if return_success:\n            if return_success in reply:\n                log.debug(\"sending sms success\")\n                ret = True\n            else:\n                log.warning(\"failed to send sms. Reply %s does not match \"\n                            \"the RETURN_SUCCESS definition\" % reply)\n                raise SMSError(response.status_code,\n                               \"We received a none success reply from the \"\n                               \"SMS Gateway: {0!s} ({1!s})\".format(reply,\n                                                                   return_success))\n\n        elif return_fail:\n            if return_fail in reply:\n                log.warning(\"sending sms failed. %s was not found \"\n                            \"in %s\" % (return_fail, reply))\n                raise SMSError(response.status_code,\n                               \"We received the predefined error from the \"\n                               \"SMS Gateway.\")\n            else:\n                log.debug(\"sending sms success\")\n                ret = True\n        else:\n            ret = True\n        return ret\n\n    @classmethod\n    def parameters(cls):\n        \"\"\"\n        Return a dictionary, that describes the parameters and options for the\n        SMS provider.\n        Parameters are required keys to values.\n\n        :return: dict\n        \"\"\"\n        params = {\"options_allowed\": True,\n                  \"headers_allowed\": True,\n                  \"parameters\": {\n                      \"URL\": {\n                          \"required\": True,\n                          \"description\": _(\"The base URL of the HTTP Gateway\")},\n                      \"HTTP_METHOD\": {\n                          \"required\": True,\n                          \"description\": _(\"Should the HTTP Gateway be \"\n                                           \"connected via an HTTP GET or POST \"\n                                           \"request.\"),\n                          \"values\": [\"GET\", \"POST\"]},\n                      \"RETURN_SUCCESS\": {\n                          \"description\": _(\"Specify a substring, \"\n                                           \"that indicates, that the SMS was \"\n                                           \"delivered successfully.\")},\n                      \"RETURN_FAIL\": {\n                          \"description\": _(\"Specify a substring, \"\n                                           \"that indicates, that the SMS \"\n                                           \"failed to be delivered.\")},\n                      \"USERNAME\": {\n                          \"description\": _(\"Username in case of basic \"\n                                           \"authentication.\")\n                      },\n                      \"PASSWORD\": {\n                          \"description\": _(\"Password in case of basic \"\n                                           \"authentication.\")\n                      },\n                      \"CHECK_SSL\": {\n                          \"description\": _(\"Should the SSL certificate be \"\n                                           \"verified.\"),\n                          \"values\": [\"yes\", \"no\"]\n                      },\n                      \"REGEXP\": {\n                          \"description\": _(\"Regular expression to modify the phone number \"                 \n                                           \"to make it compatible with provider. \"\n                                           \"Enter something like '/[\\\\+/]//' to remove \"\n                                           \"pluses and slashes.\")\n                      },\n                      \"PROXY\": {\"description\": _(\"An optional proxy string. DEPRECATED. Do not use \"\n                                                 \"this anymore. Rather use HTTP_PROXY for http \"\n                                                 \"connections and HTTPS_PROXY for https \"\n                                                 \"connection. The PROXY option will be removed in \"\n                                                 \"future.\")},\n                      \"HTTP_PROXY\": {\"description\": _(\"Proxy setting for HTTP connections.\")},\n                      \"HTTPS_PROXY\": {\"description\": _(\"Proxy setting for HTTPS connections.\")},\n                      \"TIMEOUT\": {\"description\": _(\"The timeout in seconds.\")}\n                  }\n                  }\n        return params\n", "path": "privacyidea/lib/smsprovider/HttpSMSProvider.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n#\n#    E-mail: info@privacyidea.org\n#    Contact: www.privacyidea.org\n#\n#    2018-06-15 Pascal Fuks <pascal@foxit.pro>\n#               Added REGEXP parameter on phone number\n#    2018-01-10 Cornelius K\u00f6lbel <cornelius.koelbel@netknights.it>\n#               Fix type cast for timeout\n#    2016-06-14 Cornelius K\u00f6lbel <cornelius@privacyidea.org>\n#               Add properties for new SMS provider model\n#    2016-04-08 Cornelius K\u00f6lbel <cornelius@privacyidea.org>\n#               Remote \"None\" as redundant 2nd argument to get\n#    2016-01-13 Cornelius K\u00f6lbel <cornelius@privacyidea.org>\n#               omit data object in GET request\n#               omit params in POST request\n#\n#    privacyIDEA is a fork of LinOTP\n#    May 28, 2014 Cornelius K\u00f6lbel\n#    2015-01-30 Rewrite for migration to flask\n#               Cornelius K\u00f6lbel <cornelius@privacyidea.org>\n#\n#\n#    Copyright (C) LinOTP: 2010 - 2014 LSE Leading Security Experts GmbH\n#\n#    This program is free software: you can redistribute it and/or\n#    modify it under the terms of the GNU Affero General Public\n#    License, version 3, as published by the Free Software Foundation.\n#\n#    This program is distributed in the hope that it will be useful,\n#    but WITHOUT ANY WARRANTY; without even the implied warranty of\n#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n#    GNU Affero General Public License for more details.\n#\n#    You should have received a copy of the\n#               GNU Affero General Public License\n#    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n#\n#\n\n__doc__ = \"\"\"This is the SMSClass to send SMS via HTTP Gateways\nIt can handle HTTP/HTTPS PUT and GET requests also with Proxy support\n\nThe code is tested in tests/test_lib_smsprovider\n\"\"\"\n\nfrom privacyidea.lib.smsprovider.SMSProvider import (ISMSProvider, SMSError)\nfrom privacyidea.lib import _\nimport requests\nfrom six.moves.urllib.parse import urlparse\nimport re\nimport logging\nlog = logging.getLogger(__name__)\n\n\nclass HttpSMSProvider(ISMSProvider):\n\n    @staticmethod\n    def _mangle_phone(phone, config):\n        regexp = config.get(\"REGEXP\")\n        if regexp:\n            try:\n                m = re.match(\"^/(.*)/(.*)/$\", regexp)\n                if m:\n                    phone = re.sub(m.group(1), m.group(2), phone)\n            except re.error:\n                log.warning(u\"Can not mangle phone number. \"\n                            u\"Please check your REGEXP: {0!s}\".format(regexp))\n\n        return phone\n\n    def submit_message(self, phone, message):\n        \"\"\"\n        send a message to a phone via an http sms gateway\n\n        :param phone: the phone number\n        :param message: the message to submit to the phone\n        :return:\n        \"\"\"\n        log.debug(\"submitting message {0!r} to {1!s}\".format(message, phone))\n        parameter = {}\n        headers = {}\n        if self.smsgateway:\n            phone = self._mangle_phone(phone, self.smsgateway.option_dict)\n            url = self.smsgateway.option_dict.get(\"URL\")\n            method = self.smsgateway.option_dict.get(\"HTTP_METHOD\", \"GET\")\n            username = self.smsgateway.option_dict.get(\"USERNAME\")\n            password = self.smsgateway.option_dict.get(\"PASSWORD\")\n            ssl_verify = self.smsgateway.option_dict.get(\"CHECK_SSL\",\n                                                         \"yes\") == \"yes\"\n            # FIXME: The Proxy option is deprecated and will be removed a version > 2.21\n            proxy = self.smsgateway.option_dict.get(\"PROXY\")\n            http_proxy = self.smsgateway.option_dict.get('HTTP_PROXY')\n            https_proxy = self.smsgateway.option_dict.get('HTTPS_PROXY')\n            timeout = self.smsgateway.option_dict.get(\"TIMEOUT\") or 3\n            for k, v in self.smsgateway.option_dict.items():\n                if k not in self.parameters().get(\"parameters\"):\n                    # This is an additional option\n                    parameter[k] = v.format(otp=message, phone=phone)\n            headers = self.smsgateway.header_dict\n        else:\n            phone = self._mangle_phone(phone, self.config)\n            url = self.config.get('URL')\n            method = self.config.get('HTTP_Method', 'GET')\n            username = self.config.get('USERNAME')\n            password = self.config.get('PASSWORD')\n            ssl_verify = self.config.get('CHECK_SSL', True)\n            # FIXME: The Proxy option is deprecated and will be removed a version > 2.21\n            proxy = self.config.get('PROXY')\n            http_proxy = self.config.get('HTTP_PROXY')\n            https_proxy = self.config.get('HTTPS_PROXY')\n            parameter = self._get_parameters(message, phone)\n            timeout = self.config.get(\"TIMEOUT\") or 3\n\n        if url is None:\n            log.warning(\"can not submit message. URL is missing.\")\n            raise SMSError(-1, \"No URL specified in the provider config.\")\n        basic_auth = None\n\n        # there might be the basic authentication in the request url\n        # like http://user:passw@hostname:port/path\n        if password is None and username is None:\n            parsed_url = urlparse(url)\n            if \"@\" in parsed_url[1]:\n                puser, server = parsed_url[1].split('@')\n                username, password = puser.split(':')\n\n        if username and password is not None:\n            basic_auth = (username, password)\n\n        proxies = {}\n        if http_proxy:\n            proxies[\"http\"] = http_proxy\n        if https_proxy:\n            proxies[\"https\"] = https_proxy\n        if not proxies and proxy:\n            # No new proxy config but only the old one.\n            protocol = proxy.split(\":\")[0]\n            proxies = {protocol: proxy}\n\n        # url, parameter, username, password, method\n        requestor = requests.get\n        params = parameter\n        data = {}\n        if method == \"POST\":\n            requestor = requests.post\n            params = {}\n            data = parameter\n\n        log.debug(u\"issuing request with parameters {0!s} headers {1!s} and method {2!s} and\"\n                  \"authentication {3!s} to url {4!s}.\".format(params, headers, method,\n                                                              basic_auth, url))\n        # Todo: drop basic auth if Authorization-Header is given?\n        r = requestor(url, params=params, headers=headers,\n                      data=data,\n                      verify=ssl_verify,\n                      auth=basic_auth,\n                      timeout=float(timeout),\n                      proxies=proxies)\n        log.debug(\"queued SMS on the HTTP gateway. status code returned: {0!s}\".format(\n                  r.status_code))\n\n        # We assume, that all gateways return with HTTP Status Code 200,\n        # 201 or 202\n        if r.status_code not in [200, 201, 202]:\n            raise SMSError(r.status_code, \"SMS could not be \"\n                                          \"sent: %s\" % r.status_code)\n        success = self._check_success(r)\n        return success\n\n    def _get_parameters(self, message, phone):\n\n        urldata = {}\n        # transfer the phone key\n        phoneKey = self.config.get('SMS_PHONENUMBER_KEY', \"phone\")\n        urldata[phoneKey] = phone\n        # transfer the sms key\n        messageKey = self.config.get('SMS_TEXT_KEY', \"sms\")\n        urldata[messageKey] = message\n        params = self.config.get('PARAMETER', {})\n        urldata.update(params)\n        log.debug(\"[getParameters] urldata: {0!s}\".format(urldata))\n        return urldata\n\n    def _check_success(self, response):\n        \"\"\"\n        Check the success according to the reply\n        1. if RETURN_SUCCESS is defined\n        2. if RETURN_FAIL is defined\n        :response reply: A response object.\n        \"\"\"\n        reply = response.text\n        ret = False\n        if self.smsgateway:\n            return_success = self.smsgateway.option_dict.get(\"RETURN_SUCCESS\")\n            return_fail = self.smsgateway.option_dict.get(\"RETURN_FAIL\")\n        else:\n            return_success = self.config.get(\"RETURN_SUCCESS\")\n            return_fail = self.config.get(\"RETURN_FAIL\")\n\n        if return_success:\n            if return_success in reply:\n                log.debug(\"sending sms success\")\n                ret = True\n            else:\n                log.warning(\"failed to send sms. Reply %s does not match \"\n                            \"the RETURN_SUCCESS definition\" % reply)\n                raise SMSError(response.status_code,\n                               \"We received a none success reply from the \"\n                               \"SMS Gateway: {0!s} ({1!s})\".format(reply,\n                                                                   return_success))\n\n        elif return_fail:\n            if return_fail in reply:\n                log.warning(\"sending sms failed. %s was not found \"\n                            \"in %s\" % (return_fail, reply))\n                raise SMSError(response.status_code,\n                               \"We received the predefined error from the \"\n                               \"SMS Gateway.\")\n            else:\n                log.debug(\"sending sms success\")\n                ret = True\n        else:\n            ret = True\n        return ret\n\n    @classmethod\n    def parameters(cls):\n        \"\"\"\n        Return a dictionary, that describes the parameters and options for the\n        SMS provider.\n        Parameters are required keys to values.\n\n        :return: dict\n        \"\"\"\n        params = {\"options_allowed\": True,\n                  \"headers_allowed\": True,\n                  \"parameters\": {\n                      \"URL\": {\n                          \"required\": True,\n                          \"description\": _(\"The base URL of the HTTP Gateway\")},\n                      \"HTTP_METHOD\": {\n                          \"required\": True,\n                          \"description\": _(\"Should the HTTP Gateway be \"\n                                           \"connected via an HTTP GET or POST \"\n                                           \"request.\"),\n                          \"values\": [\"GET\", \"POST\"]},\n                      \"RETURN_SUCCESS\": {\n                          \"description\": _(\"Specify a substring, \"\n                                           \"that indicates, that the SMS was \"\n                                           \"delivered successfully.\")},\n                      \"RETURN_FAIL\": {\n                          \"description\": _(\"Specify a substring, \"\n                                           \"that indicates, that the SMS \"\n                                           \"failed to be delivered.\")},\n                      \"USERNAME\": {\n                          \"description\": _(\"Username in case of basic \"\n                                           \"authentication.\")\n                      },\n                      \"PASSWORD\": {\n                          \"description\": _(\"Password in case of basic \"\n                                           \"authentication.\")\n                      },\n                      \"CHECK_SSL\": {\n                          \"required\": True,\n                          \"description\": _(\"Should the SSL certificate be \"\n                                           \"verified.\"),\n                          \"values\": [\"yes\", \"no\"]\n                      },\n                      \"REGEXP\": {\n                          \"description\": _(\"Regular expression to modify the phone number \"                 \n                                           \"to make it compatible with provider. \"\n                                           \"Enter something like '/[\\\\+/]//' to remove \"\n                                           \"pluses and slashes.\")\n                      },\n                      \"PROXY\": {\"description\": _(\"An optional proxy string. DEPRECATED. Do not use \"\n                                                 \"this anymore. Rather use HTTP_PROXY for http \"\n                                                 \"connections and HTTPS_PROXY for https \"\n                                                 \"connection. The PROXY option will be removed in \"\n                                                 \"future.\")},\n                      \"HTTP_PROXY\": {\"description\": _(\"Proxy setting for HTTP connections.\")},\n                      \"HTTPS_PROXY\": {\"description\": _(\"Proxy setting for HTTPS connections.\")},\n                      \"TIMEOUT\": {\"description\": _(\"The timeout in seconds.\")}\n                  }\n                  }\n        return params\n", "path": "privacyidea/lib/smsprovider/HttpSMSProvider.py"}]}