From 0a1abea082cf43cdda937f4bc54e9a06d8c65a14 Mon Sep 17 00:00:00 2001
From: Shamil K Muhammed <noteness@disroot.org>
Date: Mon, 17 Apr 2017 11:57:30 +0530
Subject: [PATCH] Implement the first version of diaspora-auth.

For the time being, it authenticates against the diaspora db.
Many improvements are needed. Like no exception handlers
are registered.

TODO: implement exception handlers.
---
 diaspora_auth_provider.py | 96 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 96 insertions(+)
 create mode 100644 diaspora_auth_provider.py

diff --git a/diaspora_auth_provider.py b/diaspora_auth_provider.py
new file mode 100644
index 0000000..f8a7568
--- /dev/null
+++ b/diaspora_auth_provider.py
@@ -0,0 +1,96 @@
+"""
+synapse-diaspora-auth: A diaspora authenticator for matrix synapse.
+Copyright (C) 2017 Shamil K Muhammed.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+"""
+
+from twisted.internet import defer, threads
+
+import psycopg2
+import bcrypt
+
+import logging
+
+__VERSION__ = "0.0.1"
+
+logger = logging.getLogger(__name__)
+
+
+class DiasporaAuthProvider:
+    __version__ = "0.0.1"
+
+    def __init__(self, config, account_handler):
+        self.account_handler = account_handler
+        self.config = config
+        self.connection = psycopg2.connect(
+            dbname=self.config.db_name,
+            user=self.config.db_username,
+            password=self.config.db_password,
+            host=self.config.db_host,
+            port=self.config.port
+        )
+
+    @defer.inlineCallbacks
+    def check_password(self, user_id, password):
+        # user_id is localpart:hs_bare. we only need the localpart.
+        local_part = user_id.split(':', 1)[0]
+        logger.info("Checking if user {} exists.".format(local_part))
+        with self.connection.cursor() as cursor:
+            yield threads.deferToThread( # Don't think this is needed, but w/e
+                cursor.execute,
+                "SELECT username, encrypted_password FROM users WHERE username=%s",
+                (local_part,)
+            )
+            user = yield threads.deferToThread(
+                cursor.fetchone
+            )
+        # check if the user exists.
+        if not user:
+            logger.info("User {} does not exist. Rejecting auth request".format(local_part))
+            defer.returnValue(False)
+        logger.debug("User {} exists. Checking password".format(local_part))
+        # user exists, check if the password is correct.
+        encrypted_password = user[1]
+        if not bcrypt.hashpw(password, encrypted_password):
+            logger.info("Password given for {} is wrong. Rejecting auth request.".format(local_part))
+            defer.returnValue(False)
+        # Ok, user's password is correct. check if the user exists in the homeserver db.
+        # and create it if doesn't exist.
+        if (yield self.account_handler.check_user_exists(user_id)):
+            logger.info("User {} does exist in synapse db. Authentication complete".format(local_part))
+            defer.returnValue(True)
+        # User not in synapse db. need to create it.
+        logger.info("User {} does not exist in synapse db. creating it.".format(local_part))
+        user_id, access_token = (
+            yield self.account_handler.register(localpart=local_part)
+        )
+        logger.info(
+            "Registration based on diaspora complete. UserID: {}.".format(user_id)
+        )
+        logger.info("Confirming authentication request.")
+        defer.returnValue(True)
+
+    @staticmethod
+    def parse_config(config):
+        class _Conf:
+            pass
+        Conf = _Conf()
+        Conf.db_name = "diaspora_production" if not config['database']['name'] else config['database']['name']
+        Conf.db_host = config['database']['host']
+        Conf.db_port = config['database']['port']
+        Conf.db_username = config['database']['username']
+        Conf.db_password = config['database']['password']
+        return Conf
+
-- 
GitLab