Repository: privacyidea/privacyidea

Issue:
Enhance validate/check logic
Check that a token belongs to a user if `user` and `serial` were given.


Discussion:

Resolving patch:
diff --git a/privacyidea/api/validate.py b/privacyidea/api/validate.py
--- a/privacyidea/api/validate.py
+++ b/privacyidea/api/validate.py
@@ -323,6 +323,10 @@ def check():
                         "realm": user.realm})
 
     if serial:
+        if user:
+            # check if the given token belongs to the user
+            if not get_tokens(user=user, serial=serial, count=True):
+                raise ParameterError('Given serial does not belong to given user!')
         if not otp_only:
             result, details = check_serial_pass(serial, password, options=options)
         else:


Test patch:
diff --git a/tests/test_api_validate.py b/tests/test_api_validate.py
--- a/tests/test_api_validate.py
+++ b/tests/test_api_validate.py
@@ -2533,6 +2533,44 @@ def test_33_auth_cache(self):
             self.assertTrue(result.get("status"))
             self.assertFalse(result.get("value"))
 
+    def test_34_validate_user_and_serial(self):
+        # create a new token
+        db_token = Token(self.serials[1], tokentype="hotp")
+        db_token.update_otpkey(self.otpkey)
+        db_token.save()
+        token = HotpTokenClass(db_token)
+        self.assertEqual(token.token.serial, self.serials[1], token)
+        # try to authenticate a given user with a given unassigned token serial
+        with self.app.test_request_context('/validate/check',
+                                           method='POST',
+                                           data={"user": "cornelius",
+                                                 "realm": self.realm1,
+                                                 "serial": self.serials[1],
+                                                 "pass": OTPs[3]}):
+            res = self.app.full_dispatch_request()
+            self.assertEqual(res.status_code, 400, res)
+            result = res.json['result']
+            self.assertEqual(result['error']['message'],
+                             u"ERR905: Given serial does not belong to given user!",
+                             result)
+
+        # try to authenticate with a token assigned to a different user
+        token.add_user(User(u"nönäscii", self.realm2))
+        token.set_pin("pin")
+        self.assertEqual(token.token.owners.first().user_id, "1116")
+        with self.app.test_request_context('/validate/check',
+                                           method='POST',
+                                           data={"user": "cornelius",
+                                                 "realm": self.realm1,
+                                                 "serial": self.serials[1],
+                                                 "pass": OTPs[3]}):
+            res = self.app.full_dispatch_request()
+            result = res.json['result']
+            self.assertEqual(result['error']['message'],
+                             u"ERR905: Given serial does not belong to given user!",
+                             result)
+            self.assertEqual(res.status_code, 400, res)
+
 
 class RegistrationValidity(MyApiTestCase):