diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..952a853 --- /dev/null +++ b/.gitignore @@ -0,0 +1,204 @@ +# Created by .ignore support plugin (hsz.mobi) +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +*.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Python template +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + diff --git a/README.md b/README.md index 5e5c91e..48de92d 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,6 @@ - [Configure](#configure) - [Integrate](#integrate) - [Support](#support) ---- - -**This project is no longer maintained.** - ---- ## Overview This synapse's password provider allows you to validate a password for a given username and return a user profile using an existing backend, like: @@ -18,7 +13,7 @@ This synapse's password provider allows you to validate a password for a given u - CRMs (Wordpress, ...) - self-hosted clouds (Nextcloud, ownCloud, ...) -It is mainly used with [mxisd](https://github.com/kamax-matrix/mxisd), the Federated Matrix Identity Server, to provide +It is mainly used with [ma1sd](https://github.com/ma1uta/ma1sd), the Federated Matrix Identity Server, to provide missing features and offer a fully integrated solution (directory, authentication, search). **NOTE:** This module doesn't provide direct integration with any backend. If you do not use mxisd, you will need to write @@ -31,7 +26,7 @@ Copy in whichever directory python3.x can pick it up as a module. If you installed synapse using the Matrix debian repos: ``` -sudo curl https://raw.githubusercontent.com/kamax-matrix/matrix-synapse-rest-auth/master/rest_auth_provider.py -o /opt/venvs/matrix-synapse/lib/python3.5/site-packages/rest_auth_provider.py +sudo curl https://raw.githubusercontent.com/ma1uta/matrix-synapse-rest-password-provider/master/rest_auth_provider.py -o /opt/venvs/matrix-synapse/lib/python3.7/site-packages/rest_auth_provider.py ``` If the command fail, double check that the python version still matches. If not, please let us know by opening an issue. @@ -40,7 +35,7 @@ Copy in whichever directory python2.x can pick it up as a module. If you installed synapse using the Matrix debian repos: ``` -sudo curl https://raw.githubusercontent.com/kamax-matrix/matrix-synapse-rest-auth/master/rest_auth_provider.py -o /usr/lib/python2.7/dist-packages/rest_auth_provider.py +sudo curl https://raw.githubusercontent.com/ma1uta/matrix-synapse-rest-password-provider/master/rest_auth_provider.py -o /usr/lib/python2.7/dist-packages/rest_auth_provider.py ``` If the command fail, double check that the python version still matches. If not, please let us know by opening an issue. diff --git a/rest_auth_provider.py b/rest_auth_provider.py index 909a5a3..5f6f583 100644 --- a/rest_auth_provider.py +++ b/rest_auth_provider.py @@ -23,9 +23,11 @@ import logging from twisted.internet import defer import requests import json +import time logger = logging.getLogger(__name__) + class RestAuthProvider(object): def __init__(self, config, account_handler): @@ -37,15 +39,15 @@ class RestAuthProvider(object): self.endpoint = config.endpoint self.regLower = config.regLower self.config = config - + logger.info('Endpoint: %s', self.endpoint) logger.info('Enforce lowercase username during registration: %s', self.regLower) @defer.inlineCallbacks def check_password(self, user_id, password): logger.info("Got password check for " + user_id) - data = {'user':{'id':user_id, 'password':password}} - r = requests.post(self.endpoint + '/_matrix-internal/identity/v1/check_credentials', json = data) + data = {'user': {'id': user_id, 'password': password}} + r = requests.post(self.endpoint + '/_matrix-internal/identity/v1/check_credentials', json=data) r.raise_for_status() r = r.json() if not r["auth"]: @@ -64,11 +66,11 @@ class RestAuthProvider(object): registration = False if not (yield self.account_handler.check_user_exists(user_id)): logger.info("User %s does not exist yet, creating...", user_id) - + if localpart != localpart.lower() and self.regLower: logger.info('User %s was cannot be created due to username lowercase policy', localpart) defer.returnValue(False) - + user_id, access_token = (yield self.account_handler.register(localpart=localpart)) registration = True logger.info("Registration based on REST data was successful for %s", user_id) @@ -79,14 +81,19 @@ class RestAuthProvider(object): logger.info("Handling profile data") profile = auth["profile"] - store = yield self.account_handler.hs.get_profile_handler().store + # fixme: temporary fix + try: + store = yield self.account_handler._hs.get_profile_handler().store # for synapse >= 1.9.0 + except AttributeError: + store = yield self.account_handler.hs.get_profile_handler().store # for synapse < 1.9.0 + if "display_name" in profile and ((registration and self.config.setNameOnRegister) or (self.config.setNameOnLogin)): display_name = profile["display_name"] logger.info("Setting display name to '%s' based on profile data", display_name) yield store.set_profile_displayname(localpart, display_name) else: logger.info("Display name was not set because it was not given or policy restricted it") - + if (self.config.updateThreepid): if "three_pids" in profile: logger.info("Handling 3PIDs") @@ -98,7 +105,7 @@ class RestAuthProvider(object): external_3pids.append({"medium": medium, "address": address}) logger.info("Looking for 3PID %s:%s in user profile", medium, address) - validated_at = self.account_handler.hs.get_clock().time_msec() + validated_at = time_msec() if not (yield store.get_user_id_by_threepid(medium, address)): logger.info("3PID is not present, adding") yield store.user_add_threepid( @@ -123,7 +130,6 @@ class RestAuthProvider(object): address ) - else: logger.info("3PIDs were not updated due to policy") else: @@ -164,7 +170,7 @@ class RestAuthProvider(object): except KeyError: # we don't care pass - + try: rest_config.setNameOnLogin = config['policy']['login']['profile']['name'] except TypeError: @@ -194,6 +200,7 @@ class RestAuthProvider(object): return rest_config + def _require_keys(config, required): missing = [key for key in required if key not in config] if missing: @@ -202,3 +209,9 @@ def _require_keys(config, required): ", ".join(missing) ) ) + + +def time_msec(): + """Get the current timestamp in milliseconds + """ + return int(time.time() * 1000)