Repository: privacyidea/privacyidea

Issue:
Raise error when the token already has a different owner on token_init
If you issue an init request 

`/token/init type=spass serial=MYSPASS pin=123 user=test realm=test`

but the token with serial MYSPASS is already assigned to a different user, the request succeeds, but the assignment will not happen, of course.

We may log a warning to make this behavior transparent.


Discussion:

Resolving patch:
diff --git a/privacyidea/lib/tokenclass.py b/privacyidea/lib/tokenclass.py
--- a/privacyidea/lib/tokenclass.py
+++ b/privacyidea/lib/tokenclass.py
@@ -219,9 +219,18 @@ def add_user(self, user, report=None):
         :return: None
         """
         (uid, resolvertype, resolvername) = user.get_user_identifiers()
-        r = TokenOwner(token_id=self.token.id,
-                       user_id=uid, resolver=resolvername,
-                       realmname=user.realm).save()
+        # prevent to init update a token changing the token owner
+        # FIXME: We need to remove this, if we one day want to assign several users to one token
+        if self.user and self.user != user:
+            log.info(u"The token with serial {0!s} is already assigned "
+                     u"to user {1!s}. Can not assign to {2!s}.".format(self.token.serial, self.user, user))
+            raise TokenAdminError("This token is already assigned to another user.")
+
+        if not self.user:
+            # If the tokenowner is not set yet, set it / avoid setting the same tokenowner multiple times
+            r = TokenOwner(token_id=self.token.id,
+                           user_id=uid, resolver=resolvername,
+                           realmname=user.realm).save()
         # set the tokenrealm
         self.set_realms([user.realm])
 
diff --git a/privacyidea/lib/user.py b/privacyidea/lib/user.py
--- a/privacyidea/lib/user.py
+++ b/privacyidea/lib/user.py
@@ -143,6 +143,15 @@ def __eq__(self, other):
         return isinstance(other, type(self)) and (self.login == other.login) and (
                 self.resolver == other.resolver) and (self.realm == other.realm)
 
+    def __ne__(self, other):
+        """
+        Compare two user objects and return true, if they are not equal
+
+        :param other: The other User objkect
+        :return: True or False
+        """
+        return not self.__eq__(other)
+
     def __hash__(self):
         return hash((type(self), self.login, self.resolver, self.realm))
 


Test patch:
diff --git a/tests/test_lib_token.py b/tests/test_lib_token.py
--- a/tests/test_lib_token.py
+++ b/tests/test_lib_token.py
@@ -318,6 +318,13 @@ def test_15_init_token(self):
                                                         "serial": "NEW001",
                                                         "type": "totp"})
 
+        # try to update the same token with another user assignment
+        self.assertRaises(TokenAdminError, init_token, {"otpkey": "1234",
+                                                        "serial": "NEW001"},
+                          user=User(login="hans", realm=self.realm1))
+        # test that token was not assigned to the other user
+        self.assertTrue(tokenobject.user == User(login="cornelius", realm=self.realm1))
+
         # update the existing token
         self.assertTrue(tokenobject.token.otplen == 6, tokenobject.token.otplen)
         tokenobject = init_token({"serial": "NEW001", "type": "hotp",
diff --git a/tests/test_lib_tokens_tiqr.py b/tests/test_lib_tokens_tiqr.py
--- a/tests/test_lib_tokens_tiqr.py
+++ b/tests/test_lib_tokens_tiqr.py
@@ -397,6 +397,11 @@ def test_00_users(self):
         self.setUp_user_realms()
 
     def _test_create_token(self, user):
+        try:
+            # Try to remove the token, if it exists
+            remove_token("TIQR1")
+        except Exception:
+            pass
         pin = "test"
         token = init_token({"type": "tiqr",
                             "pin": pin,