Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
No results found
Show changes
Commits on Source (4)
......@@ -92,60 +92,3 @@ Then run these queries:
And you will be good to go!
Email Authentication
~~~~~~~~~~~~~~~~~~~~
While this module helps in authenticating with diaspora, we need to set up mxisd_ for supporting
authentication through email.
Installation
^^^^^^^^^^^^
Follow the instructions `here <https://github.com/kamax-io/mxisd/blob/master/docs/getting-started.md#install>`_
Configuration & Setup
^^^^^^^^^^^^^^^^^^^^^
Follow `this <https://github.com/kamax-io/mxisd/blob/master/docs/getting-started.md#configure>`_.
Basically, if you used the debian package, you just need to set up the ``matrix.domain`` first.
And then, add these lines to ``mxisd.yaml``:
.. code:: yaml
sql:
enabled: true
type: mysql
connection: "//<HOST>/<DATABASE>?user=<USERNAME>&password=<PASSWORD>"
identity:
type: 'uid'
query: "select (case when ?='email' then username else null end) as uid from users where email=?"
Where ``<HOST>``, ``<DATABASE>``, ``<USERNAME>`` and ``<PASSWORD>`` are your database host, diaspora database, user and password you created when you set up database for synapse-diaspora-auth
Now follow the steps `given here <https://github.com/kamax-io/mxisd/blob/master/docs/features/authentication.md#advanced>`_. ie, forward the ``/_matrix/client/r0/login`` endpoint to mxisd and add
.. code:: yaml
dns.overwrite.homeserver.client:
- name: '<DOMAIN>'
value: 'http://localhost:8008'
where ``<DOMAIN>`` is your matrix server name.
An Apache2 reverse proxy example is already `provided here <https://github.com/kamax-io/mxisd/blob/master/docs/features/authentication.md#apache2>`_. An example nginx configuration would be this:
.. code::
location /_matrix/client/r0/login {
proxy_pass http://localhost:8090/_matrix/client/r0/login;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
Make sure to put this above other matrix reverse proxy directives. And Congrats! You now have a competely integrated synapse - diaspora setup! :D
.. _mxisd: https://github.com/kamax-io/mxisd
\ No newline at end of file
......@@ -16,17 +16,20 @@ 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
from collections.abc import Awaitable, Callable
from typing import Any
import asyncio
import synapse
from synapse import module_api
import bcrypt
import logging
from pkg_resources import parse_version
__VERSION__ = "0.2.2"
__VERSION__ = "0.4.0"
logger = logging.getLogger(__name__)
......@@ -34,53 +37,78 @@ logger = logging.getLogger(__name__)
class DiasporaAuthProvider:
__version__ = __VERSION__
def __init__(self, config, account_handler):
self.account_handler = account_handler
def __init__(self, config: dict, api: module_api):
self.config = config
self.auth_handler = self.account_handler._auth_handler
self.api = api
if self.config.engine == "mysql":
import pymysql
import aiomysql
self.module = pymysql
self.module = aiomysql
elif self.config.engine == "postgres":
import psycopg2
import aiopg
self.module = psycopg2
self.module = aiopg
@defer.inlineCallbacks
def exec_query(self, query, *args):
self.connection = self.module.connect(
database=self.config.db_name,
api.register_password_auth_provider_callbacks(
auth_checkers={
("m.login.password", ("password",)): self.check_pass,
},
)
async def exec_query(
self,
loop: asyncio.AbstractEventLoop,
query: str,
*args: list[str],
) -> tuple[tuple[str]] | None:
pool = await self.module.create_pool(
db=self.config.db_name,
user=self.config.db_username,
password=self.config.db_password,
host=self.config.db_host,
port=self.config.db_port,
loop=loop,
)
try:
with self.connection:
with self.connection.cursor() as cursor:
yield threads.deferToThread( # Don't think this is needed, but w/e
cursor.execute,
query,
args,
)
results = yield threads.deferToThread(cursor.fetchall)
cursor.close()
defer.returnValue(results)
async with pool.acquire() as connection:
async with connection.cursor() as cursor:
await cursor.execute(query, args)
results = cursor.fetchall()
return results
except self.module.Error as e:
logger.warning("Error during execution of query: {}: {}".format(query, e))
defer.returnValue(None)
return None
finally:
self.connection.close()
@defer.inlineCallbacks
def check_password(self, user_id, password):
pool.close()
await pool.wait_closed()
async def check_pass(
self,
username: str,
login_type: str,
login_dict: "synapse.module_api.JsonDict",
) -> (
tuple[
str,
Callable[["synapse.module_api.LoginResponse"], Awaitable[None]] | None,
]
| None
):
if login_type != "m.login.password":
return None
password = login_dict.get("password")
if not password:
defer.returnValue(False)
local_part = user_id.split(":", 1)[0][1:]
users = yield self.exec_query(
"SELECT username, encrypted_password, email FROM users WHERE username=%s",
local_part,
return None
local_part = username.split(":", 1)[0][1:] if ":" in username else username
loop = asyncio.get_event_loop()
users = await loop.run_until_complete(
self.exec_query(
loop,
"SELECT username, encrypted_password, email FROM users WHERE username=%s",
local_part,
)
)
# user_id is @localpart:hs_bare. we only need the localpart.
logger.info("Checking if user {} exists.".format(local_part))
......@@ -90,10 +118,11 @@ class DiasporaAuthProvider:
logger.info(
"User {} does not exist. Rejecting auth request".format(local_part)
)
defer.returnValue(False)
user = users[0]
return None
logger.debug("User {} exists. Checking password".format(local_part))
# user exists, check if the password is correct.
user = users[0]
encrypted_password = user[1]
email = user[2]
peppered_pass = "{}{}".format(password, self.config.pepper)
......@@ -108,70 +137,8 @@ class DiasporaAuthProvider:
local_part
)
)
defer.returnValue(False)
self.register_user(local_part, email)
logger.info("Confirming authentication request.")
defer.returnValue(True)
@defer.inlineCallbacks
def check_3pid_auth(self, medium, address, password):
logger.info(medium, address, password)
if medium != "email":
defer.returnValue(None)
logger.debug("Searching for email {} in diaspora db.".format(address))
users = yield self.exec_query(
"SELECT username FROM users WHERE email=%s", address
)
if not users:
defer.returnValue(None)
username = users[0][0]
logger.debug("Found username! {}".format(username))
logger.debug("Registering user {}".format(username))
self.register_user(username, address)
logger.debug("Registration complete")
defer.returnValue(username)
@defer.inlineCallbacks
def register_user(self, local_part, email):
if (yield self.account_handler.check_user_exists(local_part)):
yield self.sync_email(local_part, email)
defer.returnValue(local_part)
else:
user_id = yield self.account_handler.register_user(
localpart=local_part, emails=[email]
)
defer.returnValue(user_id)
@defer.inlineCallbacks
def sync_email(self, user_id, email):
logger.info("Syncing emails of {}".format(user_id))
email = email.lower()
store = self.account_handler._store # Need access to datastore
threepids = yield store.user_get_threepids(user_id)
if not threepids:
logger.info("No 3pids found.")
yield self.add_email(user_id, email)
for threepid in threepids:
if not threepid["medium"] == "email":
logger.debug("Not an email: {}".format(str(threepid)))
pass
address = threepid["address"]
if address != email:
logger.info(
"Existing 3pid doesn't match {} != {}. Deleting".format(
address, email
)
)
yield self.auth_handler.delete_threepid(user_id, "email", address)
yield self.add_email(user_id, email)
break
logger.info("Sync completed.")
@defer.inlineCallbacks
def add_email(self, user_id, email):
logger.info("Adding 3pid: {} for {}".format(email, user_id))
validated_at = self.account_handler._hs.get_clock().time_msec()
yield self.auth_handler.add_threepid(user_id, "email", email, validated_at)
return None
return (self.api.get_qualified_user_id(username), None)
@staticmethod
def parse_config(config):
......
......@@ -49,9 +49,9 @@ setup(
description="A Diaspora* auth provider for Synapse",
install_requires=[
"Twisted>=15.1.0",
"psycopg2",
"bcrypt",
"pymysql"
"aiomysql",
"aiopg"
],
long_description=read("README.rst"),
classifiers=[
......