Repository: privacyidea/privacyidea

Issue:
Authentication fails against userstore of ownCLoud 10.0.9
In the current master branch if you define a SQL resolver against owncloud, authentication at the web ui fails.

Could the password format be changed? 
A password looks like:

    | cornelius | NULL        | 1|$2y$10$o5nv09TSzlv9LNagLhVgg.rQ18UT8UvdWCoRjWeEO4lVi56rYwU8y |

The users where created in ownCloud 10.0.9 with PHP7.0




Discussion:
The passlib.hash.bcrypt would support the bcryp hash, which is indicated by 2y.

https://passlib.readthedocs.io/en/latest/lib/passlib.hash.bcrypt.html

But note: There is a difference between 1.6.x and 1.7.x. Only 1.7 seems to support 2y.
bcrypt.verify works the same for 1.6 and 1.7

Resolving patch:
diff --git a/privacyidea/lib/resolvers/SQLIdResolver.py b/privacyidea/lib/resolvers/SQLIdResolver.py
--- a/privacyidea/lib/resolvers/SQLIdResolver.py
+++ b/privacyidea/lib/resolvers/SQLIdResolver.py
@@ -45,6 +45,7 @@
 from privacyidea.lib.crypto import urandom, geturandom
 from privacyidea.lib.utils import (is_true, hash_password, PasswordHash,
                                    check_sha, check_ssha, otrs_sha256, check_crypt, censor_connect_string)
+from passlib.hash import bcrypt
 
 log = logging.getLogger(__name__)
 ENCODING = "utf-8"
@@ -151,6 +152,7 @@ def checkPass(self, uid, password):
         -         false if password does not match
 
         """
+
         res = False
         userinfo = self.getUserInfo(uid)
         if isinstance(password, unicode):
@@ -178,6 +180,12 @@ def checkPass(self, uid, password):
             res = otrs_sha256(database_pw, password)
         elif database_pw[:3] in ["$1$", "$6$"]:
             res = check_crypt(database_pw, password)
+        elif database_pw[:4] in ["$2a$", "$2b$", "$2y$"]:
+            # Do bcrypt hashing
+            res = bcrypt.verify(password, database_pw)
+        elif database_pw[:6] in ["1|$2a$", "1|$2b$", "1|$2y$"]:
+            # Do bcrypt hashing with some owncloud format
+            res = bcrypt.verify(password, database_pw[2:])
 
         return res
 


Test patch:
diff --git a/tests/test_lib_resolver.py b/tests/test_lib_resolver.py
--- a/tests/test_lib_resolver.py
+++ b/tests/test_lib_resolver.py
@@ -129,10 +129,12 @@
                                 'oid': "1"}}
                        ]
 
+
 class SQLResolverTestCase(MyTestCase):
     """
     Test the SQL Resolver
     """
+    num_users = 11
     parameters = {'Driver': 'sqlite',
                   'Server': '/tests/testdata/',
                   'Database': "testuser.sqlite",
@@ -164,7 +166,7 @@ def test_01_sqlite_resolver(self):
         y.loadConfig(self.parameters)
 
         userlist = y.getUserList()
-        self.assertTrue(len(userlist) == 9, len(userlist))
+        self.assertTrue(len(userlist) == self.num_users, len(userlist))
 
         user = "cornelius"
         user_id = y.getUserId(user)
@@ -202,19 +204,19 @@ def test_01_where_tests(self):
         y.loadConfig(dict(self.parameters.items() + {"Where": "givenname like "
                                                               "hans"}.items()))
         userlist = y.getUserList()
-        self.assertTrue(len(userlist) == 1, userlist)
+        self.assertEqual(len(userlist), 1)
 
         y = SQLResolver()
         y.loadConfig(
             dict(self.parameters.items() + {"Where": "id > 2"}.items()))
         userlist = y.getUserList()
-        self.assertTrue(len(userlist) == 7, userlist)
+        self.assertEqual(len(userlist), self.num_users - 2)
 
         y = SQLResolver()
         y.loadConfig(dict(self.parameters.items() + {"Where": "id < "
                                                               "5"}.items()))
         userlist = y.getUserList()
-        self.assertTrue(len(userlist) == 4, userlist)
+        self.assertEqual(len(userlist), 4)
 
     def test_02_check_passwords(self):
         y = SQLResolver()
@@ -256,11 +258,17 @@ def test_02_check_passwords(self):
         result = y.checkPass(9, "dunno")
         self.assertTrue(result)
 
+        # bcrypt hashes
+        self.assertTrue(y.checkPass(10, "test"))
+        self.assertFalse(y.checkPass(10, "testw"))
+        self.assertTrue(y.checkPass(11, "test"))
+        self.assertFalse(y.checkPass(11, "testw"))
+
     def test_03_testconnection(self):
         y = SQLResolver()
         result = y.testconnection(self.parameters)
-        self.assertEqual(result[0], 9)
-        self.assertTrue('Found 9 users.' in result[1])
+        self.assertEqual(result[0], self.num_users)
+        self.assertTrue('Found {0!s} users.'.format(self.num_users) in result[1])
 
     def test_05_add_user_update_delete(self):
         y = SQLResolver()
diff --git a/tests/testdata/testuser.sqlite b/tests/testdata/testuser.sqlite
Binary files a/tests/testdata/testuser.sqlite and b/tests/testdata/testuser.sqlite differ