internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: privacyidea/privacyidea

Issue:
Created tokens from pre-Script-Eventhandler don't show up in token list
If a Script-Eventhandler is run as a pre-event, a token created with the script does not show up during this request. A reload does confirm the token was successfully created.
Due to the [databse isolation](https://en.wikipedia.org/wiki/Isolation_%28database_systems%29) the running request will not be aware of a newly created token from the script which is run as a sub-process.
Finishing the running transaction just before the script process is started fixes this.
More on this can be found here: https://docs.sqlalchemy.org/en/13/faq/sessions.html#i-m-re-loading-data-with-my-session-but-it-isn-t-seeing-changes-that-i-committed-elsewhere


Discussion:

Resolving patch:
diff --git a/privacyidea/lib/eventhandler/scripthandler.py b/privacyidea/lib/eventhandler/scripthandler.py
--- a/privacyidea/lib/eventhandler/scripthandler.py
+++ b/privacyidea/lib/eventhandler/scripthandler.py
@@ -36,6 +36,7 @@
 from privacyidea.lib.framework import get_app_config_value
 from privacyidea.lib.error import ServerError
 from privacyidea.lib import _
+from privacyidea.app import db
 import logging
 import subprocess
 import os
@@ -189,6 +190,7 @@ def do(self, action, options=None):
         rcode = 0
         try:
             log.info("Starting script {script!r}.".format(script=script_name))
+            db.session.commit()
             p = subprocess.Popen(proc_args, cwd=self.script_directory, universal_newlines=True)
             if handler_options.get("background") == SCRIPT_WAIT:
                 rcode = p.wait()


Test patch:
Repository: privacyidea/privacyidea

Issue:
Do not raise on missing Origin in check_otp() for WebAuthn
Currently a missing Origin HTTP header in a request to authenticate with a WebAuthn token will cause a ValueError to be raised and a 500 response to be returned. As this is a client-side error in the response to the challenge, we should instead return a 200 response with a message indicating the response to the challenge was not valid.


Discussion:

Resolving patch:
diff --git a/privacyidea/lib/tokens/webauthntoken.py b/privacyidea/lib/tokens/webauthntoken.py
--- a/privacyidea/lib/tokens/webauthntoken.py
+++ b/privacyidea/lib/tokens/webauthntoken.py
@@ -1138,14 +1138,14 @@ def check_otp(self, otpval, counter=None, window=None, options=None):
 
             uv_req = getParam(options, WEBAUTHNACTION.USER_VERIFICATION_REQUIREMENT, optional)
 
-            try:
-                http_origin = getParam(options, "HTTP_ORIGIN", required, allow_empty=False)
-            except ParameterError:
-                raise ValueError("The ORIGIN HTTP header must be included, when authenticating with a WebAuthn token")
-
             challenge = binascii.unhexlify(getParam(options, "challenge", required))
 
             try:
+                try:
+                    http_origin = getParam(options, "HTTP_ORIGIN", required, allow_empty=False)
+                except ParameterError:
+                    raise AuthenticationRejectedException('HTTP Origin header missing.')
+
                 # This does the heavy lifting.
                 #
                 # All data is parsed and verified. If any errors occur, an exception


Test patch:
diff --git a/tests/test_lib_tokens_webauthn.py b/tests/test_lib_tokens_webauthn.py
--- a/tests/test_lib_tokens_webauthn.py
+++ b/tests/test_lib_tokens_webauthn.py
@@ -271,6 +271,20 @@ def test_05_authorization(self):
                                           })
         self.assertTrue(sign_count > 0)
 
+    def test_06_missing_origin(self):
+        self.challenge_options['nonce'] = webauthn_b64_decode(ASSERTION_CHALLENGE)
+        self._create_challenge()
+        sign_count = self.token.check_otp(otpval=None,
+                                          options={
+                                              "credentialid": CRED_ID,
+                                              "authenticatordata": ASSERTION_RESPONSE_TMPL['authData'],
+                                              "clientdata": ASSERTION_RESPONSE_TMPL['clientData'],
+                                              "signaturedata": ASSERTION_RESPONSE_TMPL['signature'],
+                                              "user": self.user,
+                                              "challenge": hexlify_and_unicode(self.challenge_options['nonce']),
+                                              "HTTP_ORIGIN": '',
+                                          })
+        self.assertTrue(sign_count == -1)
 
 class WebAuthnTestCase(unittest.TestCase):
     @staticmethod
Repository: privacyidea/privacyidea

Issue:
Remove lightning from default WebAuthn transports
Apple has in the meantime finished their implementation of WebAuthn and they consider lightning tokens as having usb transport. Thus we should remove lightning from the default value for WEBAUTHN_ALLOWED_TRANSPORTS.


Discussion:

Resolving patch:
diff --git a/privacyidea/lib/tokens/webauthn.py b/privacyidea/lib/tokens/webauthn.py
--- a/privacyidea/lib/tokens/webauthn.py
+++ b/privacyidea/lib/tokens/webauthn.py
@@ -273,7 +273,6 @@ class TRANSPORT(object):
     BLE = 'ble'
     NFC = 'nfc'
     INTERNAL = 'internal'
-    LIGHTNING = 'lightning'
 
 
 TRANSPORTS = (
@@ -281,7 +280,6 @@ class TRANSPORT(object):
     TRANSPORT.BLE,
     TRANSPORT.NFC,
     TRANSPORT.INTERNAL,
-    TRANSPORT.LIGHTNING
 )
 
 
diff --git a/privacyidea/lib/tokens/webauthntoken.py b/privacyidea/lib/tokens/webauthntoken.py
--- a/privacyidea/lib/tokens/webauthntoken.py
+++ b/privacyidea/lib/tokens/webauthntoken.py
@@ -410,7 +410,7 @@
 DEFAULT_DESCRIPTION = _(u'Generic WebAuthn Token')
 
 # Policy defaults
-DEFAULT_ALLOWED_TRANSPORTS = "usb ble nfc internal lightning"
+DEFAULT_ALLOWED_TRANSPORTS = "usb ble nfc internal"
 DEFAULT_TIMEOUT = 60
 DEFAULT_USER_VERIFICATION_REQUIREMENT = 'preferred'
 DEFAULT_AUTHENTICATOR_ATTACHMENT = 'either'
@@ -561,7 +561,7 @@ def get_class_info(key=None, ret='all'):
                     WEBAUTHNACTION.ALLOWED_TRANSPORTS: {
                         'type': 'str',
                         'desc': _("A list of transports to prefer to communicate with WebAuthn tokens. "
-                                  "Default: usb ble nfc internal lightning (All standard transports)")
+                                  "Default: usb ble nfc internal (All standard transports)")
                     },
                     WEBAUTHNACTION.TIMEOUT: {
                         'type': 'int',


Test patch:
Repository: privacyidea/privacyidea

Issue:
Fix url_decode()
The length-calculation for the padding of b64-encoded strings in u2f- and WebAuthn-Tokens fails to account for whitespace in the encoded string.


Discussion:

Resolving patch:
diff --git a/privacyidea/lib/tokens/u2f.py b/privacyidea/lib/tokens/u2f.py
--- a/privacyidea/lib/tokens/u2f.py
+++ b/privacyidea/lib/tokens/u2f.py
@@ -17,6 +17,8 @@
 # You should have received a copy of the GNU Affero General Public
 # License along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
+
+import re
 from OpenSSL import crypto
 import binascii
 from hashlib import sha256
@@ -49,7 +51,10 @@ def url_decode(url):
     :return: the decoded string
     :rtype: bytes
     """
-    pad_len = -len(url) % 4
+    # remove all non base64 characters (newline, CR) from the string before
+    # calculating the padding length
+    pad_len = -len(re.sub('[^A-Za-z0-9-_+/]+', '', to_unicode(url))) % 4
+
     padding = pad_len * "="
     res = base64.urlsafe_b64decode(to_bytes(url) + to_bytes(padding))
     return res


Test patch:
diff --git a/tests/test_lib_tokens_webauthn.py b/tests/test_lib_tokens_webauthn.py
--- a/tests/test_lib_tokens_webauthn.py
+++ b/tests/test_lib_tokens_webauthn.py
@@ -143,6 +143,11 @@
 PUB_KEY = 'a401020326215820319ea01f1125ce6232947365800ae5d9ddc874247c55d1516bad3ca3ca32075c'\
           '22582059f1f07f3b2f86c0a51e0cfa13dc57e7c77a110e796f8a0b27741fe58663cb3a'
 
+URL_DECODE_TEST_STRING = 'MEQCIBxR_Zn2XNp8yp4gVaFWU7xdpdAjkBXpXPphKPrgc_4uAiBAB0oVN-8ryLRfo-koEF5NLn1J\r\n'\
+                         'Cj8yyeCsp1U7mhR32A'
+URL_DECODE_EXPECTED_RESULT = b'0D\x02 \x1cQ\xfd\x99\xf6\\\xda|\xca\x9e U\xa1VS\xbc]\xa5\xd0#\x90\x15\xe9\\\xfaa(\xfa'\
+                             b'\xe0s\xfe.\x02 @\x07J\x157\xef+\xc8\xb4_\xa3\xe9(\x10^M.}I\n?2\xc9\xe0\xac\xa7U;\x9a'\
+                             b'\x14w\xd8'
 NONE_ATTESTATION_REGISTRATION_RESPONSE_TMPL = {
     'clientData': b'eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiZkszN3hLZGhXSmhVNmQyVEFH'
                   b'VllESEttZHNwb3JsanNSb1daMDlaaVRYTSIsIm9yaWdpbiI6Imh0dHBzOi8vd2ViYXV0aG4uaW8i'
@@ -448,3 +453,6 @@ def test_05_no_user_presence_fail_assertion(self):
         # TODO Sign an authenticator data statement with UP=0 and test against that so that the signature is valid.
         with self.assertRaises(AuthenticationRejectedException):
             webauthn_assertion_response.verify()
+
+    def test_06_webauthn_b64_decode(self):
+        self.assertEqual(webauthn_b64_decode(URL_DECODE_TEST_STRING), URL_DECODE_EXPECTED_RESULT)
Repository: privacyidea/privacyidea

Issue:
Update requirements for upcoming version 3.5
Push requirements to newest versions according to https://github.com/privacyidea/privacyidea/wiki/Development-workflow#requirements



Discussion:

Resolving patch:
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -50,6 +50,7 @@ def get_file_list(file_path):
                     "oauth2client>=2.0.1",
                     "passlib[bcrypt]>=1.7.0",
                     "Pillow>=6.2.1",
+                    "pydash>=4.7.4",
                     "PyJWT>=1.3.0",
                     "PyMySQL>=0.6.6",
                     "pyOpenSSL>=17.5",


Test patch:
diff --git a/tests/requirements.txt b/tests/requirements.txt
--- a/tests/requirements.txt
+++ b/tests/requirements.txt
@@ -1,28 +1,24 @@
 -r ../requirements.txt
-atomicwrites==1.3.0; python_version < '3.0'
-attrs==19.3.0
+atomicwrites==1.4.0; python_version < '3.0'
+attrs==20.2.0
 configparser==4.0.2; python_version < '3.0'
-contextlib2==0.6.0.post1; python_version < '3.0'
 cookies==2.2.1; python_version < '3.0'
-coverage==5.1
+coverage==5.3
 funcsigs==1.0.2; python_version < '3.0'
-importlib-metadata==1.6.0
-mock==4.0.2; python_version > '3.5'
+importlib-metadata==2.0.0
+iniconfig==1.0.1; python_version > '3.0'
 mock==3.0.5; python_version <= '3.5'
+mock==4.0.2; python_version > '3.5'
 more-itertools==5.0.0; python_version < '3.0'
-more-itertools==8.2.0; python_version > '3.0'
-packaging==20.3
-pathlib2==2.3.5; python_version < '3.0'
+more-itertools==8.5.0; python_version > '3.0'
+packaging==20.4
 pluggy==0.13.1
-py==1.8.1
+py==1.9.0
 pyparsing==2.4.7
-pytest==4.6.9; python_version < '3.0'
-pytest==5.4.1; python_version > '3.0'
-pytest-cov==2.8.1
-pytest-subtests==0.3.1; python_version > '3.0'
-responses==0.10.12
-scandir==1.10.0; python_version < '3.0'
-testfixtures==6.14.0
-wcwidth==0.1.9
-zipp==3.1.0; python_version > '3.5'
-zipp==1.2.0; python_version <= '3.5'
+pytest==4.6.11; python_version < '3.0'
+pytest==6.1.0; python_version > '3.0'
+pytest-cov==2.10.1
+responses==0.12.0
+testfixtures==6.14.2
+toml==0.10.1; python_version > '3.0'
+wcwidth==0.2.5; python_version < '3.0'
diff --git a/tests/test_lib_caconnector.py b/tests/test_lib_caconnector.py
--- a/tests/test_lib_caconnector.py
+++ b/tests/test_lib_caconnector.py
@@ -186,7 +186,7 @@ def tearDownClass(cls):
             try:
                 os.remove("{0!s}/{1!s}".format(WORKINGDIR, f))
             except OSError:
-                print("File {1!s} could not be deleted.".format(f))
+                print("File {0!s} could not be deleted.".format(f))
 
         # restore backup of index.txt and serial
         shutil.copyfile("{0!s}/serial.orig".format(WORKINGDIR),
Repository: privacyidea/privacyidea

Issue:
Allow PIN change at WebUI

### Top-level intent

Use the pin change during validate when logging in to the webui

### Steps to reproduce

1. configure ``change-pin-on-first-use``
2. configure ``login_mode=privacyIDEA``
3. Login to webui with 2FA
4. The webui will not display any challenge

### Expected outcome

The user should be able to change his PIN

### Actual outcome

Nothing is displayed

I think the problem is, that the input field is "ng-hidden" by some tokentype in the response. The PIN-change-challenge probably is missing some fields in the JSON response, so that dialog is hidden.


Discussion:

Resolving patch:
diff --git a/privacyidea/lib/challengeresponsedecorators.py b/privacyidea/lib/challengeresponsedecorators.py
--- a/privacyidea/lib/challengeresponsedecorators.py
+++ b/privacyidea/lib/challengeresponsedecorators.py
@@ -59,6 +59,7 @@ def _create_pin_reset_challenge(token_obj, message, challenge_data=None):
     reply_dict = {}
     reply_dict["multi_challenge"] = [{"transaction_id": db_challenge.transaction_id,
                                       "message": message,
+                                      "attributes": None,
                                       "serial": token_obj.token.serial,
                                       "type": token_obj.token.tokentype}]
     reply_dict["message"] = message


Test patch:
Repository: privacyidea/privacyidea

Issue:
Nightly SQL database tests on MySQL and others
We should run our tests also with MySQL and Postgres.

This could be done in a daily/nightly run in CircleCI.

Tests have to be adapted, since currently some tests have hardcoded sqlite databases like some audit tests.

See #2435 


Discussion:

Resolving patch:
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -104,7 +104,7 @@ def get_scripts(dir):
     author_email='cornelius@privacyidea.org',
     url='http://www.privacyidea.org',
     keywords='OTP, two factor authentication, management, security',
-    python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.9.*',
+    python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*',
     packages=find_packages(),
     scripts=["pi-manage"] + get_scripts("tools"),
     extras_require={
@@ -148,7 +148,8 @@ def get_scripts(dir):
                  'Programming Language :: Python :: 3.5',
                  'Programming Language :: Python :: 3.6',
                  'Programming Language :: Python :: 3.7',
-                 'Programming Language :: Python :: 3.8'
+                 'Programming Language :: Python :: 3.8',
+                 'Programming Language :: Python :: 3.9'
                  ],
     zip_safe=False,
     long_description=get_file_contents('README.rst')


Test patch:
diff --git a/tests/test_api_auth.py b/tests/test_api_auth.py
--- a/tests/test_api_auth.py
+++ b/tests/test_api_auth.py
@@ -572,7 +572,7 @@ def test_01_admin_and_user_same_name(self):
                 self.assertIn('token', result.get("value"), result)
                 # role should be 'admin'
                 self.assertEqual('admin', result['value']['role'], result)
-            mock_log.assert_called_with("Local admin 'testadmin' successfully logged in.")
+            mock_log.assert_any_call("Local admin 'testadmin' successfully logged in.")
 
         # If a user logs in, with the same name as the admin, this event is logged in warning
         with mock.patch("logging.Logger.warning") as mock_log:
Repository: privacyidea/privacyidea

Issue:
argon2 hash too long for authcache column authorization
With the new argon2 hashing, introduced by #2413, the hashes may exeed VARCHAR(64) as e.g. in the authcache table in the authorization column.

We have to increase that size.

- [x] Check for the length of the argon2 hashes as used by us - the hash generated by test_06_hash_pepper has 77 characters. We will do VARCHAR(255)
- [x] Check if this affects not only the authcache table
- [x] Add migration script (-> v3.5)

This bug may cause validate/check requests to fail with exception if the auth cache is enabled.


Discussion:
The Token PINs and Admin passwords are hashed with argon2. The table comlumsn are 512 and 255 chars and manually and successfully habe been tested with mysql. (So you can check the 2nd checkbox)

The length of the argon hash depends on the parameters like rounds, salt, memory costs, hash length...

But in our scenario it will not get much longer than 80. I would recommend a ``VARCHAR(255)`` for the  ``authentication`` column in the authcache table.

Resolving patch:
diff --git a/migrations/versions/8d40dbcfda25_.py b/migrations/versions/8d40dbcfda25_.py
new file mode 100644
--- /dev/null
+++ b/migrations/versions/8d40dbcfda25_.py
@@ -0,0 +1,35 @@
+"""Update AuthCache.authentication to VARCHAR(255) to accommodate argon2 hashes.
+
+Revision ID: 8d40dbcfda25
+Revises: 140ba0ca4f07
+Create Date: 2020-11-02 17:57:12.493515
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '8d40dbcfda25'
+down_revision = '140ba0ca4f07'
+
+from alembic import op
+import sqlalchemy as sa
+
+def upgrade():
+    try:
+        # ### commands auto generated by Alembic - please adjust! ###
+        op.alter_column('authcache', 'authentication',
+                        existing_type=sa.Unicode(length=64),
+                        type_=sa.Unicode(length=255),
+                        existing_nullable=True)
+        # ### end Alembic commands ###
+    except Exception as exx:
+        print('Modifying of authcache column "authentication" failed: {!r}'.format(exx))
+        print('This is expected behavior if the columns have already been modified.')
+
+
+def downgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.alter_column('authcache', 'authentication',
+                    existing_type=sa.Unicode(length=255),
+                    type_=sa.Unicode(length=64),
+                    existing_nullable=True)
+    # ### end Alembic commands ###
diff --git a/privacyidea/models.py b/privacyidea/models.py
--- a/privacyidea/models.py
+++ b/privacyidea/models.py
@@ -2675,7 +2675,7 @@ class AuthCache(MethodsMixin, db.Model):
     user_agent = db.Column(db.Unicode(120), default=u"")
     # We can hash the password like this:
     # binascii.hexlify(hashlib.sha256("secret123456").digest())
-    authentication = db.Column(db.Unicode(64), default=u"")
+    authentication = db.Column(db.Unicode(255), default=u"")
 
     def __init__(self, username, realm, resolver, authentication,
                  first_auth=None, last_auth=None):


Test patch:
Repository: privacyidea/privacyidea

Issue:
db get method cannot handle values containing equal sign ('=')
You may set a policy which contains an equal sign as URLs frequently contain this.

The policy is correctly setup in the database, but the method get in models.py splits the value by '=' and fails to get the whole value.
https://github.com/privacyidea/privacyidea/blob/89562c9a8e4a395624424954f218cca9bbf7b8bf/privacyidea/models.py#L1474

action_list may be something like this
```python
action_list = "login_text=\'test my=text\', show_custom_authenticator=https://myurl?id=test"
```

A simple change to split only the first '=' is this
```python
        action_list = [x.strip().split("=", 1) for x in (self.action or "").split(",")]
```


Discussion:

Resolving patch:
diff --git a/privacyidea/models.py b/privacyidea/models.py
--- a/privacyidea/models.py
+++ b/privacyidea/models.py
@@ -1471,7 +1471,7 @@ def get(self, key=None):
              "time": self.time,
              "conditions": self.get_conditions_tuples(),
              "priority": self.priority}
-        action_list = [x.strip().split("=") for x in (self.action or "").split(
+        action_list = [x.strip().split("=", 1) for x in (self.action or "").split(
             ",")]
         action_dict = {}
         for a in action_list:


Test patch:
Repository: privacyidea/privacyidea

Issue:
Container audit fails in 3.5
The container audit will fail in version 3.5 due to a missing parameter in the constructor.

https://community.privacyidea.org/t/logging-error-after-update-to-3-5/1811/2


Discussion:

Resolving patch:
diff --git a/privacyidea/lib/auditmodules/containeraudit.py b/privacyidea/lib/auditmodules/containeraudit.py
--- a/privacyidea/lib/auditmodules/containeraudit.py
+++ b/privacyidea/lib/auditmodules/containeraudit.py
@@ -46,14 +46,15 @@ class Audit(AuditBase):
     to a list of audit modules.
     """
 
-    def __init__(self, config=None):
-        super(Audit, self).__init__(config)
+    def __init__(self, config=None, startdate=None):
+        super(Audit, self).__init__(config, startdate)
         self.name = "containeraudit"
         write_conf = self.config.get('PI_AUDIT_CONTAINER_WRITE')
         read_conf = self.config.get('PI_AUDIT_CONTAINER_READ')
         # Initialize all modules
-        self.write_modules = [get_module_class(audit_module, "Audit", "log")(config) for audit_module in write_conf]
-        self.read_module = get_module_class(read_conf, "Audit", "log")(config)
+        self.write_modules = [get_module_class(audit_module, "Audit", "log")(config, startdate)
+                              for audit_module in write_conf]
+        self.read_module = get_module_class(read_conf, "Audit", "log")(config, startdate)
         if not self.read_module.is_readable:
             log.warning(u"The specified PI_AUDIT_CONTAINER_READ {0!s} is not readable.".format(self.read_module))
 


Test patch:
diff --git a/tests/test_lib_audit.py b/tests/test_lib_audit.py
--- a/tests/test_lib_audit.py
+++ b/tests/test_lib_audit.py
@@ -413,7 +413,8 @@ def test_10_container_audit(self):
                                                          "privacyidea.lib.auditmodules.sqlaudit"],
                             "PI_AUDIT_CONTAINER_READ": "privacyidea.lib.auditmodules.sqlaudit",
                             "PI_AUDIT_NO_SIGN": True,
-                            "PI_AUDIT_SQL_URI": 'sqlite:///' + os.path.join(basedir, 'data-test.sqlite')})
+                            "PI_AUDIT_SQL_URI": 'sqlite:///' + os.path.join(basedir, 'data-test.sqlite')},
+                           startdate=None)
         self.assertFalse(a.has_data)
         a.log({"action": "something_test_30"})
         self.assertTrue(a.has_data)
Repository: privacyidea/privacyidea

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``.~~


Discussion:
Maybe we can also change the default of the API layer or reverse the logic.
~~~~javascript
    $scope.identifier = $stateParams.identifier;
    if ($scope.identifier) {
        // We are editing an existing SMTP Server
        $scope.getSmtpServers($scope.identifier);
    } else {
        // This is a new SMTP Server
        $scope.params = {
            tls: true,
            port: 25
        }
    }
~~~~
The "CHECK_SSL" set here as "required" so the admin has to deal with it and consciously choose yes / no.
It was decided not to change the API defaults.



Resolving patch:
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 @@ def parameters(cls):
                                            "authentication.")
                       },
                       "CHECK_SSL": {
+                          "required": True,
                           "description": _("Should the SSL certificate be "
                                            "verified."),
                           "values": ["yes", "no"]


Test patch: