Repository: privacyidea/privacyidea

Issue:
Import HOTP tokens with counter
**Is your feature request related to a problem? Please describe.**

SAM Exports contain the counter of HOTP tokens.
We should be able to import HOTP tokens with counter values.

**Describe the solution you'd like**

The CSV should contain:

    serial, seed, hotp, otplen, counter

Also: The import should be possible via command line using a tool, maybe ``pi-manage`` or an explicit new tool.


Discussion:

Resolving patch:
diff --git a/privacyidea/lib/importotp.py b/privacyidea/lib/importotp.py
--- a/privacyidea/lib/importotp.py
+++ b/privacyidea/lib/importotp.py
@@ -113,8 +113,16 @@ def parseOATHcsv(csv):
     This function parses CSV data for oath token.
     The file format is
 
-        serial, key, [hotp,totp], [6,8], [30|60],
+    for HOTP
+        serial, key, hotp, [6|8], [counter]
+
+    for TOTP
+        serial, key, totp, [6|8], [30|60]
+
+    for OCRA
         serial, key, ocra, [ocra-suite]
+
+    for TAN
         serial, key, tan, tan1 tan2 tan3 tan4
 
     It imports sha1 hotp or totp token.
diff --git a/privacyidea/lib/tokens/hotptoken.py b/privacyidea/lib/tokens/hotptoken.py
--- a/privacyidea/lib/tokens/hotptoken.py
+++ b/privacyidea/lib/tokens/hotptoken.py
@@ -736,3 +736,19 @@ def generate_symmetric_key(self, server_component, client_component,
                         rounds,
                         keysize)
         return hexlify_and_unicode(secret)
+
+    @staticmethod
+    def get_import_csv(l):
+        """
+        Read the list from a csv file and return a dictionary, that can be used
+        to do a token_init.
+
+        :param l: The list of the line of a csv file
+        :type l: list
+        :return: A dictionary of init params
+        """
+        params = TokenClass.get_import_csv(l)
+        # get Counter
+        if len(l) >= 5:
+            params["counter"] = int(l[4].strip())
+        return params


Test patch:
diff --git a/tests/test_lib_importotp.py b/tests/test_lib_importotp.py
--- a/tests/test_lib_importotp.py
+++ b/tests/test_lib_importotp.py
@@ -474,6 +474,8 @@
         # this is a comment
         # a serial without an OTP key will not create a token
         serialX
+        # A HOTP token with a counter
+        tok6, 1212, hotp, 6, 345
         '''
 OATHCSV_USER = """# version:2
 user1, resolver1, realm1, tok1, 1212, hotp
@@ -519,16 +521,21 @@
 """
 
 
-
 class ImportOTPTestCase(MyTestCase):
 
     def test_00_import_oath(self):
         tokens = parseOATHcsv(OATHCSV)
-        self.assertTrue(len(tokens) == 5, len(tokens))
+        self.assertTrue(len(tokens) == 6, len(tokens))
         self.assertTrue("tok1" in tokens, tokens)
         self.assertTrue("tok2" in tokens, tokens)
         self.assertTrue("tok3" in tokens, tokens)
         self.assertTrue("tok4" in tokens, tokens)
+        self.assertTrue("tok5" in tokens, tokens)
+        self.assertTrue("tok6" in tokens, tokens)
+        self.assertEqual(tokens["tok6"]["counter"], 345)
+        # The TOTP token does contain the timeStep but no counter
+        self.assertEqual(tokens["tok4"]["timeStep"], 60)
+        self.assertNotIn("counter", tokens["tok4"])
 
     def test_00_import_oath_user(self):
         tokens = parseOATHcsv(OATHCSV_USER)