mirror of
https://github.com/element-hq/synapse.git
synced 2025-12-13 01:50:46 +00:00
Compare commits
5 Commits
devon/depr
...
anoa/testb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc0b59e51a | ||
|
|
9915139bef | ||
|
|
fa8bc0ba39 | ||
|
|
62ac8b9c0d | ||
|
|
422d40e82f |
22
docker/Dockerfile-workers
Normal file
22
docker/Dockerfile-workers
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# Inherit from the official Synapse docker image
|
||||||
|
FROM matrixdotorg/synapse
|
||||||
|
|
||||||
|
# Install deps
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get install -y supervisor redis nginx
|
||||||
|
|
||||||
|
# Copy the worker process and log configuration files
|
||||||
|
COPY ./docker/workers /conf/workers/
|
||||||
|
|
||||||
|
# Expose nginx listener port
|
||||||
|
EXPOSE 8008/tcp
|
||||||
|
|
||||||
|
# Volume for user-editable config files, logs etc.
|
||||||
|
VOLUME ["/data"]
|
||||||
|
|
||||||
|
# A script to read environment variables and create the necessary
|
||||||
|
# files to run the desired worker configuration. Will start supervisord.
|
||||||
|
COPY ./docker/configure_workers_and_start.py /configure_workers_and_start.py
|
||||||
|
ENTRYPOINT ["/configure_workers_and_start.py"]
|
||||||
|
|
||||||
|
# TODO: Healthcheck? Which worker to ask? Can we ask supervisord?
|
||||||
35
docker/MoveToComplement.Dockerfile
Normal file
35
docker/MoveToComplement.Dockerfile
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# This dockerfile builds on top of Dockerfile-worker and includes a built-in postgres instance.
|
||||||
|
# It is intended to be used for Complement testing
|
||||||
|
|
||||||
|
FROM matrixdotorg/synapse:workers
|
||||||
|
|
||||||
|
# Install postgres
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get install -y postgres
|
||||||
|
|
||||||
|
# Create required databases in postgres
|
||||||
|
|
||||||
|
# Create a user without a password
|
||||||
|
RUN sudo -u postgres createuser -w synapse_user
|
||||||
|
|
||||||
|
# Then set their password
|
||||||
|
RUN sudo -u postgres psql -c "ALTER USER postgres PASSWORD 'somesecret';"
|
||||||
|
|
||||||
|
# Create the synapse database
|
||||||
|
RUN sudo -u postgres psql -c "CREATE DATABASE synapse \
|
||||||
|
ENCODING 'UTF8' \
|
||||||
|
LC_COLLATE='C' \
|
||||||
|
LC_CTYPE='C' \
|
||||||
|
template=template0 \
|
||||||
|
OWNER synapse_user;"
|
||||||
|
|
||||||
|
# Modify Synapse's database config to point to the local postgres
|
||||||
|
COPY ./docker/synapse_use_local_postgres.py /synapse_use_local_postgres.py
|
||||||
|
RUN /synapse_use_local_postgres.py
|
||||||
|
|
||||||
|
VOLUME ["/data"]
|
||||||
|
|
||||||
|
EXPOSE 8008/tcp 8009/tcp 8448/tcp
|
||||||
|
|
||||||
|
# Start supervisord
|
||||||
|
CMD ["/usr/bin/supervisord"]
|
||||||
@@ -27,8 +27,7 @@ log_config: "{{ SYNAPSE_LOG_CONFIG }}"
|
|||||||
|
|
||||||
listeners:
|
listeners:
|
||||||
{% if not SYNAPSE_NO_TLS %}
|
{% if not SYNAPSE_NO_TLS %}
|
||||||
-
|
- port: 8448
|
||||||
port: 8448
|
|
||||||
bind_addresses: ['::']
|
bind_addresses: ['::']
|
||||||
type: http
|
type: http
|
||||||
tls: true
|
tls: true
|
||||||
@@ -52,6 +51,15 @@ listeners:
|
|||||||
- names: [federation]
|
- names: [federation]
|
||||||
compress: false
|
compress: false
|
||||||
|
|
||||||
|
{% if SYNAPSE_WORKERS %}
|
||||||
|
# The HTTP replication port
|
||||||
|
- port: 9093
|
||||||
|
bind_address: '127.0.0.1'
|
||||||
|
type: http
|
||||||
|
resources:
|
||||||
|
- names: [replication]
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
## Database ##
|
## Database ##
|
||||||
|
|
||||||
{% if POSTGRES_PASSWORD %}
|
{% if POSTGRES_PASSWORD %}
|
||||||
|
|||||||
347
docker/configure_workers_and_start.py
Executable file
347
docker/configure_workers_and_start.py
Executable file
@@ -0,0 +1,347 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
# This script reads environment variables and generates a shared Synapse worker,
|
||||||
|
# nginx and supervisord configs depending on the workers requested
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import jinja2
|
||||||
|
|
||||||
|
|
||||||
|
# Utility functions
|
||||||
|
def log(txt):
|
||||||
|
print(txt)
|
||||||
|
|
||||||
|
|
||||||
|
def error(txt):
|
||||||
|
log(txt)
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
|
||||||
|
def convert(src, dst, environ):
|
||||||
|
"""Generate a file from a template
|
||||||
|
|
||||||
|
Args:
|
||||||
|
src (str): path to input file
|
||||||
|
dst (str): path to file to write
|
||||||
|
environ (dict): environment dictionary, for replacement mappings.
|
||||||
|
"""
|
||||||
|
with open(src) as infile:
|
||||||
|
template = infile.read()
|
||||||
|
rendered = jinja2.Template(template, autoescape=True).render(**environ)
|
||||||
|
with open(dst, "w") as outfile:
|
||||||
|
outfile.write(rendered)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_base_homeserver_config():
|
||||||
|
"""Starts Synapse and generates a basic homeserver config, which will later be
|
||||||
|
modified for worker support.
|
||||||
|
|
||||||
|
Raises: CalledProcessError if calling start.py return a non-zero exit code.
|
||||||
|
"""
|
||||||
|
# start.py already does this for us, so just call that.
|
||||||
|
# note that this script is copied in in the official, monolith dockerfile
|
||||||
|
subprocess.check_output(["/usr/local/bin/python", "/start.py", "generate"])
|
||||||
|
|
||||||
|
|
||||||
|
def generate_worker_files(environ, config_path: str, data_dir: str):
|
||||||
|
"""Read the desired list of workers from environment variables and generate
|
||||||
|
shared homeserver, nginx and supervisord configs.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
environ: _Environ[str]
|
||||||
|
config_path: Where to output the generated Synapse main worker config file.
|
||||||
|
data_dir: The location of the synapse data directory. Where log and
|
||||||
|
user-facing config files live.
|
||||||
|
"""
|
||||||
|
# Note that yaml cares about indentation, so care should be taken to insert lines
|
||||||
|
# into files at the correct indentation below.
|
||||||
|
|
||||||
|
# The contents of a Synapse config file that will be added alongside the generated
|
||||||
|
# config when running the main Synapse process.
|
||||||
|
# It is intended mainly for disabling functionality when certain workers are spun up.
|
||||||
|
homeserver_config = """
|
||||||
|
redis:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# TODO: remove before prod
|
||||||
|
suppress_key_server_warning: true
|
||||||
|
"""
|
||||||
|
|
||||||
|
# The supervisord config
|
||||||
|
supervisord_config = """
|
||||||
|
[supervisord]
|
||||||
|
nodaemon=true
|
||||||
|
|
||||||
|
[program:nginx]
|
||||||
|
command=/usr/sbin/nginx -g "daemon off;"
|
||||||
|
priority=900
|
||||||
|
stdout_logfile=/dev/stdout
|
||||||
|
stdout_logfile_maxbytes=0
|
||||||
|
stderr_logfile=/dev/stderr
|
||||||
|
stderr_logfile_maxbytes=0
|
||||||
|
username=www-data
|
||||||
|
autorestart=true
|
||||||
|
|
||||||
|
[program:synapse_main]
|
||||||
|
command=/usr/local/bin/python -m synapse.app.homeserver \
|
||||||
|
--config-path="%s" \
|
||||||
|
--config-path=/conf/workers/shared.yaml
|
||||||
|
|
||||||
|
# Log startup failures to supervisord's stdout/err
|
||||||
|
# Regular synapse logs will still go in the configured data directory
|
||||||
|
stdout_logfile=/dev/stdout
|
||||||
|
stdout_logfile_maxbytes=0
|
||||||
|
stderr_logfile=/dev/stderr
|
||||||
|
stderr_logfile_maxbytes=0
|
||||||
|
autorestart=unexpected
|
||||||
|
exitcodes=0
|
||||||
|
|
||||||
|
""" % (config_path,)
|
||||||
|
|
||||||
|
# An nginx site config. Will live in /etc/nginx/conf.d
|
||||||
|
nginx_config_template_header = """
|
||||||
|
server {
|
||||||
|
# Listen on Synapse's default HTTP port number
|
||||||
|
listen 8008;
|
||||||
|
listen [::]:8008;
|
||||||
|
|
||||||
|
server_name localhost;
|
||||||
|
"""
|
||||||
|
nginx_config_body = "" # to modify below
|
||||||
|
nginx_config_template_end = """
|
||||||
|
# Send all other traffic to the main process
|
||||||
|
location ~* ^(\/_matrix|\/_synapse) {
|
||||||
|
proxy_pass http://localhost:18008;
|
||||||
|
proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
|
|
||||||
|
# TODO: Can we move this to the default nginx.conf so all locations are
|
||||||
|
# affected?
|
||||||
|
#
|
||||||
|
# Nginx by default only allows file uploads up to 1M in size
|
||||||
|
# Increase client_max_body_size to match max_upload_size defined in homeserver.yaml
|
||||||
|
client_max_body_size 50M;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Read desired worker configuration from environment
|
||||||
|
if "SYNAPSE_WORKERS" not in environ:
|
||||||
|
error("Environment variable 'SYNAPSE_WORKERS' is mandatory.")
|
||||||
|
|
||||||
|
worker_types = environ.get("SYNAPSE_WORKERS")
|
||||||
|
worker_types = worker_types.split(",")
|
||||||
|
|
||||||
|
for worker_type in worker_types:
|
||||||
|
worker_type = worker_type.strip()
|
||||||
|
|
||||||
|
if worker_type == "pusher":
|
||||||
|
# Disable push handling from the main process
|
||||||
|
homeserver_config += """
|
||||||
|
start_pushers: false
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Enable the pusher worker in supervisord
|
||||||
|
supervisord_config += """
|
||||||
|
[program:synapse_pusher]
|
||||||
|
command=/usr/local/bin/python -m synapse.app.pusher \
|
||||||
|
--config-path="%s" \
|
||||||
|
--config-path=/conf/workers/shared.yaml \
|
||||||
|
--config-path=/conf/workers/pusher.yaml
|
||||||
|
autorestart=unexpected
|
||||||
|
exitcodes=0
|
||||||
|
stdout_logfile=/dev/stdout
|
||||||
|
stdout_logfile_maxbytes=0
|
||||||
|
stderr_logfile=/dev/stderr
|
||||||
|
stderr_logfile_maxbytes=0
|
||||||
|
""" % (config_path,)
|
||||||
|
|
||||||
|
# This worker does not handle any REST endpoints
|
||||||
|
|
||||||
|
elif worker_type == "appservice":
|
||||||
|
# Disable appservice traffic sending from the main process
|
||||||
|
homeserver_config += """
|
||||||
|
notify_appservices: false
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Enable the pusher worker in supervisord
|
||||||
|
supervisord_config += """
|
||||||
|
[program:synapse_appservice]
|
||||||
|
command=/usr/local/bin/python -m synapse.app.appservice \
|
||||||
|
--config-path="%s" \
|
||||||
|
--config-path=/conf/workers/shared.yaml \
|
||||||
|
--config-path=/conf/workers/appservice.yaml
|
||||||
|
autorestart=unexpected
|
||||||
|
exitcodes=0
|
||||||
|
stdout_logfile=/dev/stdout
|
||||||
|
stdout_logfile_maxbytes=0
|
||||||
|
stderr_logfile=/dev/stderr
|
||||||
|
stderr_logfile_maxbytes=0
|
||||||
|
""" % (config_path,)
|
||||||
|
|
||||||
|
# This worker does not handle any REST endpoints
|
||||||
|
|
||||||
|
elif worker_type == "user_dir":
|
||||||
|
# Disable user directory updates on the main process
|
||||||
|
homeserver_config += """
|
||||||
|
update_user_directory: false
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Enable the user directory worker in supervisord
|
||||||
|
supervisord_config += """
|
||||||
|
[program:synapse_user_dir]
|
||||||
|
command=/usr/local/bin/python -m synapse.app.user_dir \
|
||||||
|
--config-path="%s" \
|
||||||
|
--config-path=/conf/workers/shared.yaml \
|
||||||
|
--config-path=/conf/workers/user_dir.yaml
|
||||||
|
autorestart=unexpected
|
||||||
|
exitcodes=0
|
||||||
|
stdout_logfile=/dev/stdout
|
||||||
|
stdout_logfile_maxbytes=0
|
||||||
|
stderr_logfile=/dev/stderr
|
||||||
|
stderr_logfile_maxbytes=0
|
||||||
|
""" % (config_path,)
|
||||||
|
|
||||||
|
# Route user directory requests to this worker
|
||||||
|
nginx_config_body += """
|
||||||
|
location ~* ^/_matrix/client/(api/v1|r0|unstable)/user_directory/search$ {
|
||||||
|
proxy_pass http://localhost:8010;
|
||||||
|
proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
elif worker_type == "federation_sender":
|
||||||
|
# Disable user directory updates on the main process
|
||||||
|
homeserver_config += """
|
||||||
|
send_federation: False
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Enable the user directory worker in supervisord
|
||||||
|
supervisord_config += """
|
||||||
|
[program:synapse_user_dir]
|
||||||
|
command=/usr/local/bin/python -m synapse.app.user_dir \
|
||||||
|
--config-path="%s" \
|
||||||
|
--config-path=/conf/workers/shared.yaml \
|
||||||
|
--config-path=/conf/workers/user_dir.yaml
|
||||||
|
autorestart=unexpected
|
||||||
|
exitcodes=0
|
||||||
|
stdout_logfile=/dev/stdout
|
||||||
|
stdout_logfile_maxbytes=0
|
||||||
|
stderr_logfile=/dev/stderr
|
||||||
|
stderr_logfile_maxbytes=0
|
||||||
|
""" % (config_path,)
|
||||||
|
|
||||||
|
# This worker does not handle any REST endpoints
|
||||||
|
|
||||||
|
elif worker_type == "media_repository":
|
||||||
|
# Disable user directory updates on the main process
|
||||||
|
homeserver_config += """
|
||||||
|
update_user_directory: false
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Enable the user directory worker in supervisord
|
||||||
|
supervisord_config += """
|
||||||
|
[program:synapse_user_dir]
|
||||||
|
command=/usr/local/bin/python -m synapse.app.user_dir \
|
||||||
|
--config-path="%s" \
|
||||||
|
--config-path=/conf/workers/shared.yaml \
|
||||||
|
--config-path=/conf/workers/user_dir.yaml
|
||||||
|
autorestart=unexpected
|
||||||
|
exitcodes=0
|
||||||
|
stdout_logfile=/dev/stdout
|
||||||
|
stdout_logfile_maxbytes=0
|
||||||
|
stderr_logfile=/dev/stderr
|
||||||
|
stderr_logfile_maxbytes=0
|
||||||
|
""" % (config_path,)
|
||||||
|
|
||||||
|
# Route user directory requests to this worker
|
||||||
|
nginx_config_body += """
|
||||||
|
location ~* (^/_matrix/media/.*$|^/_synapse/admin/v1/(purge_media_cache$|(room|user)/.*/media.*$|media/.*$|quarantine_media/.*$) {
|
||||||
|
proxy_pass http://localhost:8010;
|
||||||
|
proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Write out the config files
|
||||||
|
|
||||||
|
# Shared homeserver config
|
||||||
|
print(homeserver_config)
|
||||||
|
with open("/conf/workers/shared.yaml", "w") as f:
|
||||||
|
f.write(homeserver_config)
|
||||||
|
|
||||||
|
# Nginx config
|
||||||
|
print()
|
||||||
|
print(nginx_config_template_header)
|
||||||
|
print(nginx_config_body)
|
||||||
|
print(nginx_config_template_end)
|
||||||
|
with open("/etc/nginx/conf.d/matrix-synapse.conf", "w") as f:
|
||||||
|
f.write(nginx_config_template_header)
|
||||||
|
f.write(nginx_config_body)
|
||||||
|
f.write(nginx_config_template_end)
|
||||||
|
|
||||||
|
# Supervisord config
|
||||||
|
print()
|
||||||
|
print(supervisord_config)
|
||||||
|
with open("/etc/supervisor/conf.d/supervisord.conf", "w") as f:
|
||||||
|
f.write(supervisord_config)
|
||||||
|
|
||||||
|
# Generate worker log config files from the templates.
|
||||||
|
# The templates are mainly there so that we can inject some environment variable
|
||||||
|
# values into them.
|
||||||
|
log_config_template_dir = "/conf/workers/log_config_templates/"
|
||||||
|
log_config_dir = "/conf/workers/"
|
||||||
|
for log_config_filename in os.listdir(log_config_template_dir):
|
||||||
|
template_path = log_config_template_dir + log_config_filename
|
||||||
|
out_path = log_config_dir + log_config_filename
|
||||||
|
|
||||||
|
convert(template_path, out_path, environ)
|
||||||
|
|
||||||
|
# Ensure the logging directory exists
|
||||||
|
log_dir = data_dir + "/logs"
|
||||||
|
if not os.path.exists(log_dir):
|
||||||
|
os.mkdir(log_dir)
|
||||||
|
|
||||||
|
|
||||||
|
def start_supervisord():
|
||||||
|
"""Starts up supervisord which then starts and monitors all other necessary processes
|
||||||
|
|
||||||
|
Raises: CalledProcessError if calling start.py return a non-zero exit code.
|
||||||
|
"""
|
||||||
|
subprocess.check_output(["/usr/bin/supervisord"])
|
||||||
|
|
||||||
|
|
||||||
|
def main(args, environ):
|
||||||
|
config_dir = environ.get("SYNAPSE_CONFIG_DIR", "/data")
|
||||||
|
config_path = environ.get("SYNAPSE_CONFIG_PATH", config_dir + "/homeserver.yaml")
|
||||||
|
data_dir = environ.get("SYNAPSE_DATA_DIR", "/data")
|
||||||
|
|
||||||
|
# Generate the base homeserver config if one does not yet exist
|
||||||
|
if not os.path.exists(config_path):
|
||||||
|
log("Generating base homeserver config")
|
||||||
|
generate_base_homeserver_config()
|
||||||
|
|
||||||
|
# Always regenerate all other config files
|
||||||
|
generate_worker_files(environ, config_path, data_dir)
|
||||||
|
|
||||||
|
# Start supervisord, which will start Synapse, all of the configured worker
|
||||||
|
# processes, redis, nginx etc. according to the config we created above.
|
||||||
|
start_supervisord()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main(sys.argv, os.environ)
|
||||||
@@ -177,7 +177,6 @@ def run_generate_config(environ, ownership):
|
|||||||
else:
|
else:
|
||||||
os.execv("/usr/local/bin/python", args)
|
os.execv("/usr/local/bin/python", args)
|
||||||
|
|
||||||
|
|
||||||
def main(args, environ):
|
def main(args, environ):
|
||||||
mode = args[1] if len(args) > 1 else "run"
|
mode = args[1] if len(args) > 1 else "run"
|
||||||
desired_uid = int(environ.get("UID", "991"))
|
desired_uid = int(environ.get("UID", "991"))
|
||||||
|
|||||||
30
docker/workers/log_config_templates/pusher_log.yaml
Normal file
30
docker/workers/log_config_templates/pusher_log.yaml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
version: 1
|
||||||
|
|
||||||
|
formatters:
|
||||||
|
precise:
|
||||||
|
format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s'
|
||||||
|
|
||||||
|
handlers:
|
||||||
|
file:
|
||||||
|
class: logging.handlers.TimedRotatingFileHandler
|
||||||
|
formatter: precise
|
||||||
|
filename: "{{ SYNAPSE_DATA_DIR or '/data' }}/logs/pusher1.log"
|
||||||
|
when: midnight
|
||||||
|
backupCount: 3 # Does not include the current log file.
|
||||||
|
encoding: utf8
|
||||||
|
|
||||||
|
console:
|
||||||
|
class: logging.StreamHandler
|
||||||
|
formatter: precise
|
||||||
|
|
||||||
|
loggers:
|
||||||
|
synapse.storage.SQL:
|
||||||
|
# beware: increasing this to DEBUG will make synapse log sensitive
|
||||||
|
# information such as access tokens.
|
||||||
|
level: INFO
|
||||||
|
|
||||||
|
root:
|
||||||
|
level: {{ SYNAPSE_LOG_LEVEL or "INFO" }}
|
||||||
|
handlers: [console]
|
||||||
|
|
||||||
|
disable_existing_loggers: false
|
||||||
30
docker/workers/log_config_templates/user_dir_log.yaml
Normal file
30
docker/workers/log_config_templates/user_dir_log.yaml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
version: 1
|
||||||
|
|
||||||
|
formatters:
|
||||||
|
precise:
|
||||||
|
format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s'
|
||||||
|
|
||||||
|
handlers:
|
||||||
|
file:
|
||||||
|
class: logging.handlers.TimedRotatingFileHandler
|
||||||
|
formatter: precise
|
||||||
|
filename: "{{ SYNAPSE_DATA_DIR or '/data' }}/logs/pusher1.log"
|
||||||
|
when: midnight
|
||||||
|
backupCount: 3 # Does not include the current log file.
|
||||||
|
encoding: utf8
|
||||||
|
|
||||||
|
console:
|
||||||
|
class: logging.StreamHandler
|
||||||
|
formatter: precise
|
||||||
|
|
||||||
|
loggers:
|
||||||
|
synapse.storage.SQL:
|
||||||
|
# beware: increasing this to DEBUG will make synapse log sensitive
|
||||||
|
# information such as access tokens.
|
||||||
|
level: INFO
|
||||||
|
|
||||||
|
root:
|
||||||
|
level: {{ SYNAPSE_LOG_LEVEL or "INFO" }}
|
||||||
|
handlers: [console]
|
||||||
|
|
||||||
|
disable_existing_loggers: false
|
||||||
13
docker/workers/pusher.yaml
Normal file
13
docker/workers/pusher.yaml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
worker_app: synapse.app.pusher
|
||||||
|
worker_name: pusher
|
||||||
|
|
||||||
|
# The replication listener on the main synapse process.
|
||||||
|
worker_replication_host: 127.0.0.1
|
||||||
|
worker_replication_http_port: 9093
|
||||||
|
|
||||||
|
worker_listeners:
|
||||||
|
- type: http
|
||||||
|
port: 8083
|
||||||
|
resources: []
|
||||||
|
|
||||||
|
worker_log_config: /conf/workers/pusher_log.yaml
|
||||||
13
docker/workers/user_dir.yaml
Normal file
13
docker/workers/user_dir.yaml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
worker_app: synapse.app.user_dir
|
||||||
|
worker_name: user_dir
|
||||||
|
|
||||||
|
# The replication listener on the main synapse process.
|
||||||
|
worker_replication_host: 127.0.0.1
|
||||||
|
worker_replication_http_port: 9093
|
||||||
|
|
||||||
|
worker_listeners:
|
||||||
|
- type: http
|
||||||
|
port: 8084
|
||||||
|
resources: []
|
||||||
|
|
||||||
|
worker_log_config: /conf/workers/user_dir_log.yaml
|
||||||
@@ -1111,15 +1111,8 @@ url_preview_accept_language:
|
|||||||
#turn_allow_guests: true
|
#turn_allow_guests: true
|
||||||
|
|
||||||
|
|
||||||
## Registration ##
|
## Account Validity ##
|
||||||
#
|
#
|
||||||
# Registration can be rate-limited using the parameters in the "Ratelimiting"
|
|
||||||
# section of this file.
|
|
||||||
|
|
||||||
# Enable registration for new users.
|
|
||||||
#
|
|
||||||
#enable_registration: false
|
|
||||||
|
|
||||||
# Optional account validity configuration. This allows for accounts to be denied
|
# Optional account validity configuration. This allows for accounts to be denied
|
||||||
# any request after a given period.
|
# any request after a given period.
|
||||||
#
|
#
|
||||||
@@ -1131,57 +1124,67 @@ url_preview_accept_language:
|
|||||||
# after that the validity period changes and Synapse is restarted, the users'
|
# after that the validity period changes and Synapse is restarted, the users'
|
||||||
# expiration dates won't be updated unless their account is manually renewed. This
|
# expiration dates won't be updated unless their account is manually renewed. This
|
||||||
# date will be randomly selected within a range [now + period - d ; now + period],
|
# date will be randomly selected within a range [now + period - d ; now + period],
|
||||||
# where d is equal to 10% of the validity period.
|
# where d is equal to 10%% of the validity period.
|
||||||
#
|
#
|
||||||
account_validity:
|
account_validity:
|
||||||
# The account validity feature is disabled by default. Uncomment the
|
# The account validity feature is disabled by default. Uncomment the
|
||||||
# following line to enable it.
|
# following line to enable it.
|
||||||
#
|
#
|
||||||
#enabled: true
|
#enabled: true
|
||||||
|
|
||||||
# The period after which an account is valid after its registration. When
|
# The period after which an account is valid after its registration. When
|
||||||
# renewing the account, its validity period will be extended by this amount
|
# renewing the account, its validity period will be extended by this amount
|
||||||
# of time. This parameter is required when using the account validity
|
# of time. This parameter is required when using the account validity
|
||||||
# feature.
|
# feature.
|
||||||
#
|
#
|
||||||
#period: 6w
|
#period: 6w
|
||||||
|
|
||||||
# The amount of time before an account's expiry date at which Synapse will
|
# The amount of time before an account's expiry date at which Synapse will
|
||||||
# send an email to the account's email address with a renewal link. By
|
# send an email to the account's email address with a renewal link. By
|
||||||
# default, no such emails are sent.
|
# default, no such emails are sent.
|
||||||
#
|
#
|
||||||
# If you enable this setting, you will also need to fill out the 'email' and
|
# If you enable this setting, you will also need to fill out the 'email' and
|
||||||
# 'public_baseurl' configuration sections.
|
# 'public_baseurl' configuration sections.
|
||||||
#
|
#
|
||||||
#renew_at: 1w
|
#renew_at: 1w
|
||||||
|
|
||||||
# The subject of the email sent out with the renewal link. '%(app)s' can be
|
# The subject of the email sent out with the renewal link. '%%(app)s' can be
|
||||||
# used as a placeholder for the 'app_name' parameter from the 'email'
|
# used as a placeholder for the 'app_name' parameter from the 'email'
|
||||||
# section.
|
# section.
|
||||||
#
|
#
|
||||||
# Note that the placeholder must be written '%(app)s', including the
|
# Note that the placeholder must be written '%%(app)s', including the
|
||||||
# trailing 's'.
|
# trailing 's'.
|
||||||
#
|
#
|
||||||
# If this is not set, a default value is used.
|
# If this is not set, a default value is used.
|
||||||
#
|
#
|
||||||
#renew_email_subject: "Renew your %(app)s account"
|
#renew_email_subject: "Renew your %%(app)s account"
|
||||||
|
|
||||||
# Directory in which Synapse will try to find templates for the HTML files to
|
# Directory in which Synapse will try to find templates for the HTML files to
|
||||||
# serve to the user when trying to renew an account. If not set, default
|
# serve to the user when trying to renew an account. If not set, default
|
||||||
# templates from within the Synapse package will be used.
|
# templates from within the Synapse package will be used.
|
||||||
#
|
#
|
||||||
#template_dir: "res/templates"
|
#template_dir: "res/templates"
|
||||||
|
|
||||||
# File within 'template_dir' giving the HTML to be displayed to the user after
|
# File within 'template_dir' giving the HTML to be displayed to the user after
|
||||||
# they successfully renewed their account. If not set, default text is used.
|
# they successfully renewed their account. If not set, default text is used.
|
||||||
#
|
#
|
||||||
#account_renewed_html_path: "account_renewed.html"
|
#account_renewed_html_path: "account_renewed.html"
|
||||||
|
|
||||||
# File within 'template_dir' giving the HTML to be displayed when the user
|
# File within 'template_dir' giving the HTML to be displayed when the user
|
||||||
# tries to renew an account with an invalid renewal token. If not set,
|
# tries to renew an account with an invalid renewal token. If not set,
|
||||||
# default text is used.
|
# default text is used.
|
||||||
#
|
#
|
||||||
#invalid_token_html_path: "invalid_token.html"
|
#invalid_token_html_path: "invalid_token.html"
|
||||||
|
|
||||||
|
|
||||||
|
## Registration ##
|
||||||
|
#
|
||||||
|
# Registration can be rate-limited using the parameters in the "Ratelimiting"
|
||||||
|
# section of this file.
|
||||||
|
|
||||||
|
# Enable registration for new users.
|
||||||
|
#
|
||||||
|
#enable_registration: false
|
||||||
|
|
||||||
# Time that a user's session remains valid for, after they log in.
|
# Time that a user's session remains valid for, after they log in.
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
# Log configuration for Synapse.
|
|
||||||
#
|
|
||||||
# This is a YAML file containing a standard Python logging configuration
|
|
||||||
# dictionary. See [1] for details on the valid settings.
|
|
||||||
#
|
|
||||||
# Synapse also supports structured logging for machine readable logs which can
|
|
||||||
# be ingested by ELK stacks. See [2] for details.
|
|
||||||
#
|
|
||||||
# [1]: https://docs.python.org/3.7/library/logging.config.html#configuration-dictionary-schema
|
|
||||||
# [2]: https://github.com/matrix-org/synapse/blob/master/docs/structured_logging.md
|
|
||||||
|
|
||||||
version: 1
|
|
||||||
|
|
||||||
formatters:
|
|
||||||
precise:
|
|
||||||
format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s'
|
|
||||||
|
|
||||||
handlers:
|
|
||||||
file:
|
|
||||||
class: logging.handlers.TimedRotatingFileHandler
|
|
||||||
formatter: precise
|
|
||||||
filename: /var/log/matrix-synapse/homeserver.log
|
|
||||||
when: midnight
|
|
||||||
backupCount: 3 # Does not include the current log file.
|
|
||||||
encoding: utf8
|
|
||||||
|
|
||||||
# Default to buffering writes to log file for efficiency. This means that
|
|
||||||
# will be a delay for INFO/DEBUG logs to get written, but WARNING/ERROR
|
|
||||||
# logs will still be flushed immediately.
|
|
||||||
buffer:
|
|
||||||
class: logging.handlers.MemoryHandler
|
|
||||||
target: file
|
|
||||||
# The capacity is the number of log lines that are buffered before
|
|
||||||
# being written to disk. Increasing this will lead to better
|
|
||||||
# performance, at the expensive of it taking longer for log lines to
|
|
||||||
# be written to disk.
|
|
||||||
capacity: 10
|
|
||||||
flushLevel: 30 # Flush for WARNING logs as well
|
|
||||||
|
|
||||||
# A handler that writes logs to stderr. Unused by default, but can be used
|
|
||||||
# instead of "buffer" and "file" in the logger handlers.
|
|
||||||
console:
|
|
||||||
class: logging.StreamHandler
|
|
||||||
formatter: precise
|
|
||||||
|
|
||||||
loggers:
|
|
||||||
synapse.storage.SQL:
|
|
||||||
# beware: increasing this to DEBUG will make synapse log sensitive
|
|
||||||
# information such as access tokens.
|
|
||||||
level: INFO
|
|
||||||
|
|
||||||
twisted:
|
|
||||||
# We send the twisted logging directly to the file handler,
|
|
||||||
# to work around https://github.com/matrix-org/synapse/issues/3471
|
|
||||||
# when using "buffer" logger. Use "console" to log to stderr instead.
|
|
||||||
handlers: [file]
|
|
||||||
propagate: false
|
|
||||||
|
|
||||||
root:
|
|
||||||
level: INFO
|
|
||||||
|
|
||||||
# Write logs to the `buffer` handler, which will buffer them together in memory,
|
|
||||||
# then write them to a file.
|
|
||||||
#
|
|
||||||
# Replace "buffer" with "console" to log to stderr instead. (Note that you'll
|
|
||||||
# also need to update the configuration for the `twisted` logger above, in
|
|
||||||
# this case.)
|
|
||||||
#
|
|
||||||
handlers: [buffer]
|
|
||||||
|
|
||||||
disable_existing_loggers: false
|
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ class Auth:
|
|||||||
|
|
||||||
self._auth_blocking = AuthBlocking(self.hs)
|
self._auth_blocking = AuthBlocking(self.hs)
|
||||||
|
|
||||||
self._account_validity = hs.config.account_validity
|
self._account_validity_enabled = hs.config.account_validity_enabled
|
||||||
self._track_appservice_user_ips = hs.config.track_appservice_user_ips
|
self._track_appservice_user_ips = hs.config.track_appservice_user_ips
|
||||||
self._macaroon_secret_key = hs.config.macaroon_secret_key
|
self._macaroon_secret_key = hs.config.macaroon_secret_key
|
||||||
|
|
||||||
@@ -219,7 +219,7 @@ class Auth:
|
|||||||
shadow_banned = user_info.shadow_banned
|
shadow_banned = user_info.shadow_banned
|
||||||
|
|
||||||
# Deny the request if the user account has expired.
|
# Deny the request if the user account has expired.
|
||||||
if self._account_validity.enabled and not allow_expired:
|
if self._account_validity_enabled and not allow_expired:
|
||||||
if await self.store.is_account_expired(
|
if await self.store.is_account_expired(
|
||||||
user_info.user_id, self.clock.time_msec()
|
user_info.user_id, self.clock.time_msec()
|
||||||
):
|
):
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from typing import Any, Iterable, List, Optional
|
from typing import Any, Iterable, List, Optional
|
||||||
|
|
||||||
from synapse.config import (
|
from synapse.config import (
|
||||||
|
account_validity,
|
||||||
api,
|
api,
|
||||||
appservice,
|
appservice,
|
||||||
captcha,
|
captcha,
|
||||||
@@ -55,6 +56,7 @@ class RootConfig:
|
|||||||
media: repository.ContentRepositoryConfig
|
media: repository.ContentRepositoryConfig
|
||||||
captcha: captcha.CaptchaConfig
|
captcha: captcha.CaptchaConfig
|
||||||
voip: voip.VoipConfig
|
voip: voip.VoipConfig
|
||||||
|
accountvalidity: account_validity.AccountValidityConfig
|
||||||
registration: registration.RegistrationConfig
|
registration: registration.RegistrationConfig
|
||||||
metrics: metrics.MetricsConfig
|
metrics: metrics.MetricsConfig
|
||||||
api: api.ApiConfig
|
api: api.ApiConfig
|
||||||
|
|||||||
171
synapse/config/account_validity.py
Normal file
171
synapse/config/account_validity.py
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
import os
|
||||||
|
|
||||||
|
import pkg_resources
|
||||||
|
|
||||||
|
from synapse.config._base import Config, ConfigError
|
||||||
|
|
||||||
|
|
||||||
|
class AccountValidityConfig(Config):
|
||||||
|
section = "accountvalidity"
|
||||||
|
|
||||||
|
def read_config(self, config, **kwargs):
|
||||||
|
account_validity_config = config.get("account_validity", {})
|
||||||
|
self.account_validity_enabled = account_validity_config.get("enabled", False)
|
||||||
|
self.account_validity_renew_by_email_enabled = (
|
||||||
|
"renew_at" in account_validity_config
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.account_validity_enabled:
|
||||||
|
if "period" in config:
|
||||||
|
self.account_validity_period = self.parse_duration(config["period"])
|
||||||
|
else:
|
||||||
|
raise ConfigError("'period' is required when using account validity")
|
||||||
|
|
||||||
|
if "renew_at" in config:
|
||||||
|
self.account_validity_renew_at = self.parse_duration(config["renew_at"])
|
||||||
|
|
||||||
|
if "renew_email_subject" in config:
|
||||||
|
self.account_validity_renew_email_subject = config[
|
||||||
|
"renew_email_subject"
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
self.account_validity_renew_email_subject = "Renew your %(app)s account"
|
||||||
|
|
||||||
|
self.account_validity_startup_job_max_delta = self.period * 10.0 / 100.0
|
||||||
|
|
||||||
|
if self.account_validity_renew_by_email_enabled:
|
||||||
|
if not self.public_baseurl:
|
||||||
|
raise ConfigError("Can't send renewal emails without 'public_baseurl'")
|
||||||
|
|
||||||
|
template_dir = config.get("template_dir")
|
||||||
|
|
||||||
|
if not template_dir:
|
||||||
|
template_dir = pkg_resources.resource_filename("synapse", "res/templates")
|
||||||
|
|
||||||
|
if "account_renewed_html_path" in config:
|
||||||
|
file_path = os.path.join(template_dir, config["account_renewed_html_path"])
|
||||||
|
|
||||||
|
self.account_validity_account_renewed_html_content = self.read_file(
|
||||||
|
file_path, "account_validity.account_renewed_html_path"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.account_validity_account_renewed_html_content = (
|
||||||
|
"<html><body>Your account has been successfully renewed.</body><html>"
|
||||||
|
)
|
||||||
|
|
||||||
|
if "invalid_token_html_path" in config:
|
||||||
|
file_path = os.path.join(template_dir, config["invalid_token_html_path"])
|
||||||
|
|
||||||
|
self.account_validity_invalid_token_html_content = self.read_file(
|
||||||
|
file_path, "account_validity.invalid_token_html_path"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.account_validity_invalid_token_html_content = (
|
||||||
|
"<html><body>Invalid renewal token.</body><html>"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Load account validity templates.
|
||||||
|
# We do this here instead of in AccountValidityConfig as read_templates
|
||||||
|
# relies on state that hasn't been initialised in AccountValidityConfig
|
||||||
|
account_renewed_template_filename = config.get(
|
||||||
|
"account_renewed_html_path", "account_renewed.html"
|
||||||
|
)
|
||||||
|
account_previously_renewed_template_filename = config.get(
|
||||||
|
"account_previously_renewed_html_path", "account_previously_renewed.html"
|
||||||
|
)
|
||||||
|
invalid_token_template_filename = config.get(
|
||||||
|
"invalid_token_html_path", "invalid_token.html"
|
||||||
|
)
|
||||||
|
(
|
||||||
|
self.account_validity.account_renewed_template,
|
||||||
|
self.account_validity.account_previously_renewed_template,
|
||||||
|
self.account_validity.invalid_token_template,
|
||||||
|
) = self.read_templates(
|
||||||
|
[
|
||||||
|
account_renewed_template_filename,
|
||||||
|
account_previously_renewed_template_filename,
|
||||||
|
invalid_token_template_filename,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
def generate_config_section(self, **kwargs):
|
||||||
|
return """\
|
||||||
|
## Account Validity ##
|
||||||
|
#
|
||||||
|
# Optional account validity configuration. This allows for accounts to be denied
|
||||||
|
# any request after a given period.
|
||||||
|
#
|
||||||
|
# Once this feature is enabled, Synapse will look for registered users without an
|
||||||
|
# expiration date at startup and will add one to every account it found using the
|
||||||
|
# current settings at that time.
|
||||||
|
# This means that, if a validity period is set, and Synapse is restarted (it will
|
||||||
|
# then derive an expiration date from the current validity period), and some time
|
||||||
|
# after that the validity period changes and Synapse is restarted, the users'
|
||||||
|
# expiration dates won't be updated unless their account is manually renewed. This
|
||||||
|
# date will be randomly selected within a range [now + period - d ; now + period],
|
||||||
|
# where d is equal to 10%% of the validity period.
|
||||||
|
#
|
||||||
|
account_validity:
|
||||||
|
# The account validity feature is disabled by default. Uncomment the
|
||||||
|
# following line to enable it.
|
||||||
|
#
|
||||||
|
#enabled: true
|
||||||
|
|
||||||
|
# The period after which an account is valid after its registration. When
|
||||||
|
# renewing the account, its validity period will be extended by this amount
|
||||||
|
# of time. This parameter is required when using the account validity
|
||||||
|
# feature.
|
||||||
|
#
|
||||||
|
#period: 6w
|
||||||
|
|
||||||
|
# The amount of time before an account's expiry date at which Synapse will
|
||||||
|
# send an email to the account's email address with a renewal link. By
|
||||||
|
# default, no such emails are sent.
|
||||||
|
#
|
||||||
|
# If you enable this setting, you will also need to fill out the 'email' and
|
||||||
|
# 'public_baseurl' configuration sections.
|
||||||
|
#
|
||||||
|
#renew_at: 1w
|
||||||
|
|
||||||
|
# The subject of the email sent out with the renewal link. '%%(app)s' can be
|
||||||
|
# used as a placeholder for the 'app_name' parameter from the 'email'
|
||||||
|
# section.
|
||||||
|
#
|
||||||
|
# Note that the placeholder must be written '%%(app)s', including the
|
||||||
|
# trailing 's'.
|
||||||
|
#
|
||||||
|
# If this is not set, a default value is used.
|
||||||
|
#
|
||||||
|
#renew_email_subject: "Renew your %%(app)s account"
|
||||||
|
|
||||||
|
# Directory in which Synapse will try to find templates for the HTML files to
|
||||||
|
# serve to the user when trying to renew an account. If not set, default
|
||||||
|
# templates from within the Synapse package will be used.
|
||||||
|
#
|
||||||
|
#template_dir: "res/templates"
|
||||||
|
|
||||||
|
# File within 'template_dir' giving the HTML to be displayed to the user after
|
||||||
|
# they successfully renewed their account. If not set, default text is used.
|
||||||
|
#
|
||||||
|
#account_renewed_html_path: "account_renewed.html"
|
||||||
|
|
||||||
|
# File within 'template_dir' giving the HTML to be displayed when the user
|
||||||
|
# tries to renew an account with an invalid renewal token. If not set,
|
||||||
|
# default text is used.
|
||||||
|
#
|
||||||
|
#invalid_token_html_path: "invalid_token.html"
|
||||||
|
"""
|
||||||
@@ -13,8 +13,8 @@
|
|||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from ._base import RootConfig
|
from ._base import RootConfig
|
||||||
|
from .account_validity import AccountValidityConfig
|
||||||
from .api import ApiConfig
|
from .api import ApiConfig
|
||||||
from .appservice import AppServiceConfig
|
from .appservice import AppServiceConfig
|
||||||
from .cache import CacheConfig
|
from .cache import CacheConfig
|
||||||
@@ -66,6 +66,7 @@ class HomeServerConfig(RootConfig):
|
|||||||
ContentRepositoryConfig,
|
ContentRepositoryConfig,
|
||||||
CaptchaConfig,
|
CaptchaConfig,
|
||||||
VoipConfig,
|
VoipConfig,
|
||||||
|
AccountValidityConfig,
|
||||||
RegistrationConfig,
|
RegistrationConfig,
|
||||||
MetricsConfig,
|
MetricsConfig,
|
||||||
ApiConfig,
|
ApiConfig,
|
||||||
|
|||||||
@@ -13,75 +13,14 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import os
|
|
||||||
from distutils.util import strtobool
|
from distutils.util import strtobool
|
||||||
|
|
||||||
import pkg_resources
|
|
||||||
|
|
||||||
from synapse.api.constants import RoomCreationPreset
|
from synapse.api.constants import RoomCreationPreset
|
||||||
from synapse.config._base import Config, ConfigError
|
from synapse.config._base import Config, ConfigError
|
||||||
from synapse.types import RoomAlias, UserID
|
from synapse.types import RoomAlias, UserID
|
||||||
from synapse.util.stringutils import random_string_with_symbols
|
from synapse.util.stringutils import random_string_with_symbols
|
||||||
|
|
||||||
|
|
||||||
class AccountValidityConfig(Config):
|
|
||||||
section = "accountvalidity"
|
|
||||||
|
|
||||||
def __init__(self, config, synapse_config):
|
|
||||||
if config is None:
|
|
||||||
return
|
|
||||||
super().__init__()
|
|
||||||
self.enabled = config.get("enabled", False)
|
|
||||||
self.renew_by_email_enabled = "renew_at" in config
|
|
||||||
|
|
||||||
if self.enabled:
|
|
||||||
if "period" in config:
|
|
||||||
self.period = self.parse_duration(config["period"])
|
|
||||||
else:
|
|
||||||
raise ConfigError("'period' is required when using account validity")
|
|
||||||
|
|
||||||
if "renew_at" in config:
|
|
||||||
self.renew_at = self.parse_duration(config["renew_at"])
|
|
||||||
|
|
||||||
if "renew_email_subject" in config:
|
|
||||||
self.renew_email_subject = config["renew_email_subject"]
|
|
||||||
else:
|
|
||||||
self.renew_email_subject = "Renew your %(app)s account"
|
|
||||||
|
|
||||||
self.startup_job_max_delta = self.period * 10.0 / 100.0
|
|
||||||
|
|
||||||
if self.renew_by_email_enabled:
|
|
||||||
if "public_baseurl" not in synapse_config:
|
|
||||||
raise ConfigError("Can't send renewal emails without 'public_baseurl'")
|
|
||||||
|
|
||||||
template_dir = config.get("template_dir")
|
|
||||||
|
|
||||||
if not template_dir:
|
|
||||||
template_dir = pkg_resources.resource_filename("synapse", "res/templates")
|
|
||||||
|
|
||||||
if "account_renewed_html_path" in config:
|
|
||||||
file_path = os.path.join(template_dir, config["account_renewed_html_path"])
|
|
||||||
|
|
||||||
self.account_renewed_html_content = self.read_file(
|
|
||||||
file_path, "account_validity.account_renewed_html_path"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self.account_renewed_html_content = (
|
|
||||||
"<html><body>Your account has been successfully renewed.</body><html>"
|
|
||||||
)
|
|
||||||
|
|
||||||
if "invalid_token_html_path" in config:
|
|
||||||
file_path = os.path.join(template_dir, config["invalid_token_html_path"])
|
|
||||||
|
|
||||||
self.invalid_token_html_content = self.read_file(
|
|
||||||
file_path, "account_validity.invalid_token_html_path"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self.invalid_token_html_content = (
|
|
||||||
"<html><body>Invalid renewal token.</body><html>"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class RegistrationConfig(Config):
|
class RegistrationConfig(Config):
|
||||||
section = "registration"
|
section = "registration"
|
||||||
|
|
||||||
@@ -94,10 +33,6 @@ class RegistrationConfig(Config):
|
|||||||
strtobool(str(config["disable_registration"]))
|
strtobool(str(config["disable_registration"]))
|
||||||
)
|
)
|
||||||
|
|
||||||
self.account_validity = AccountValidityConfig(
|
|
||||||
config.get("account_validity") or {}, config
|
|
||||||
)
|
|
||||||
|
|
||||||
self.registrations_require_3pid = config.get("registrations_require_3pid", [])
|
self.registrations_require_3pid = config.get("registrations_require_3pid", [])
|
||||||
self.allowed_local_3pids = config.get("allowed_local_3pids", [])
|
self.allowed_local_3pids = config.get("allowed_local_3pids", [])
|
||||||
self.enable_3pid_lookup = config.get("enable_3pid_lookup", True)
|
self.enable_3pid_lookup = config.get("enable_3pid_lookup", True)
|
||||||
@@ -211,69 +146,6 @@ class RegistrationConfig(Config):
|
|||||||
#
|
#
|
||||||
#enable_registration: false
|
#enable_registration: false
|
||||||
|
|
||||||
# Optional account validity configuration. This allows for accounts to be denied
|
|
||||||
# any request after a given period.
|
|
||||||
#
|
|
||||||
# Once this feature is enabled, Synapse will look for registered users without an
|
|
||||||
# expiration date at startup and will add one to every account it found using the
|
|
||||||
# current settings at that time.
|
|
||||||
# This means that, if a validity period is set, and Synapse is restarted (it will
|
|
||||||
# then derive an expiration date from the current validity period), and some time
|
|
||||||
# after that the validity period changes and Synapse is restarted, the users'
|
|
||||||
# expiration dates won't be updated unless their account is manually renewed. This
|
|
||||||
# date will be randomly selected within a range [now + period - d ; now + period],
|
|
||||||
# where d is equal to 10%% of the validity period.
|
|
||||||
#
|
|
||||||
account_validity:
|
|
||||||
# The account validity feature is disabled by default. Uncomment the
|
|
||||||
# following line to enable it.
|
|
||||||
#
|
|
||||||
#enabled: true
|
|
||||||
|
|
||||||
# The period after which an account is valid after its registration. When
|
|
||||||
# renewing the account, its validity period will be extended by this amount
|
|
||||||
# of time. This parameter is required when using the account validity
|
|
||||||
# feature.
|
|
||||||
#
|
|
||||||
#period: 6w
|
|
||||||
|
|
||||||
# The amount of time before an account's expiry date at which Synapse will
|
|
||||||
# send an email to the account's email address with a renewal link. By
|
|
||||||
# default, no such emails are sent.
|
|
||||||
#
|
|
||||||
# If you enable this setting, you will also need to fill out the 'email' and
|
|
||||||
# 'public_baseurl' configuration sections.
|
|
||||||
#
|
|
||||||
#renew_at: 1w
|
|
||||||
|
|
||||||
# The subject of the email sent out with the renewal link. '%%(app)s' can be
|
|
||||||
# used as a placeholder for the 'app_name' parameter from the 'email'
|
|
||||||
# section.
|
|
||||||
#
|
|
||||||
# Note that the placeholder must be written '%%(app)s', including the
|
|
||||||
# trailing 's'.
|
|
||||||
#
|
|
||||||
# If this is not set, a default value is used.
|
|
||||||
#
|
|
||||||
#renew_email_subject: "Renew your %%(app)s account"
|
|
||||||
|
|
||||||
# Directory in which Synapse will try to find templates for the HTML files to
|
|
||||||
# serve to the user when trying to renew an account. If not set, default
|
|
||||||
# templates from within the Synapse package will be used.
|
|
||||||
#
|
|
||||||
#template_dir: "res/templates"
|
|
||||||
|
|
||||||
# File within 'template_dir' giving the HTML to be displayed to the user after
|
|
||||||
# they successfully renewed their account. If not set, default text is used.
|
|
||||||
#
|
|
||||||
#account_renewed_html_path: "account_renewed.html"
|
|
||||||
|
|
||||||
# File within 'template_dir' giving the HTML to be displayed when the user
|
|
||||||
# tries to renew an account with an invalid renewal token. If not set,
|
|
||||||
# default text is used.
|
|
||||||
#
|
|
||||||
#invalid_token_html_path: "invalid_token.html"
|
|
||||||
|
|
||||||
# Time that a user's session remains valid for, after they log in.
|
# Time that a user's session remains valid for, after they log in.
|
||||||
#
|
#
|
||||||
# Note that this is not currently compatible with guest logins.
|
# Note that this is not currently compatible with guest logins.
|
||||||
|
|||||||
@@ -40,11 +40,18 @@ class AccountValidityHandler:
|
|||||||
self.sendmail = self.hs.get_sendmail()
|
self.sendmail = self.hs.get_sendmail()
|
||||||
self.clock = self.hs.get_clock()
|
self.clock = self.hs.get_clock()
|
||||||
|
|
||||||
self._account_validity = self.hs.config.account_validity
|
self._account_validity_period = self.hs.config.account_validity_period
|
||||||
|
self._account_validity_enabled = self.hs.config.account_validity_enabled
|
||||||
|
self._account_validity_renew_email_subject = (
|
||||||
|
self.hs.config.account_validity_renew_email_subject
|
||||||
|
)
|
||||||
|
self._account_validity_renew_by_email_enabled = (
|
||||||
|
self.hs.config.account_validity_renew_by_email_enabled
|
||||||
|
)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
self._account_validity.enabled
|
self._account_validity_enabled
|
||||||
and self._account_validity.renew_by_email_enabled
|
and self._account_validity_renew_by_email_enabled
|
||||||
):
|
):
|
||||||
# Don't do email-specific configuration if renewal by email is disabled.
|
# Don't do email-specific configuration if renewal by email is disabled.
|
||||||
self._template_html = self.config.account_validity_template_html
|
self._template_html = self.config.account_validity_template_html
|
||||||
@@ -53,14 +60,14 @@ class AccountValidityHandler:
|
|||||||
try:
|
try:
|
||||||
app_name = self.hs.config.email_app_name
|
app_name = self.hs.config.email_app_name
|
||||||
|
|
||||||
self._subject = self._account_validity.renew_email_subject % {
|
self._subject = self._account_validity_renew_email_subject % {
|
||||||
"app": app_name
|
"app": app_name
|
||||||
}
|
}
|
||||||
|
|
||||||
self._from_string = self.hs.config.email_notif_from % {"app": app_name}
|
self._from_string = self.hs.config.email_notif_from % {"app": app_name}
|
||||||
except Exception:
|
except Exception:
|
||||||
# If substitution failed, fall back to the bare strings.
|
# If substitution failed, fall back to the bare strings.
|
||||||
self._subject = self._account_validity.renew_email_subject
|
self._subject = self._account_validity_renew_email_subject
|
||||||
self._from_string = self.hs.config.email_notif_from
|
self._from_string = self.hs.config.email_notif_from
|
||||||
|
|
||||||
self._raw_from = email.utils.parseaddr(self._from_string)[1]
|
self._raw_from = email.utils.parseaddr(self._from_string)[1]
|
||||||
@@ -258,7 +265,7 @@ class AccountValidityHandler:
|
|||||||
milliseconds since epoch.
|
milliseconds since epoch.
|
||||||
"""
|
"""
|
||||||
if expiration_ts is None:
|
if expiration_ts is None:
|
||||||
expiration_ts = self.clock.time_msec() + self._account_validity.period
|
expiration_ts = self.clock.time_msec() + self._account_validity_period
|
||||||
|
|
||||||
await self.store.set_account_validity_for_user(
|
await self.store.set_account_validity_for_user(
|
||||||
user_id=user_id, expiration_ts=expiration_ts, email_sent=email_sent
|
user_id=user_id, expiration_ts=expiration_ts, email_sent=email_sent
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ class DeactivateAccountHandler(BaseHandler):
|
|||||||
if hs.config.run_background_tasks:
|
if hs.config.run_background_tasks:
|
||||||
hs.get_reactor().callWhenRunning(self._start_user_parting)
|
hs.get_reactor().callWhenRunning(self._start_user_parting)
|
||||||
|
|
||||||
self._account_validity_enabled = hs.config.account_validity.enabled
|
self._account_validity_enabled = hs.config.account_validity_enabled
|
||||||
|
|
||||||
async def deactivate_account(
|
async def deactivate_account(
|
||||||
self, user_id: str, erase_data: bool, id_server: Optional[str] = None
|
self, user_id: str, erase_data: bool, id_server: Optional[str] = None
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ class PusherPool:
|
|||||||
self.store = self.hs.get_datastore()
|
self.store = self.hs.get_datastore()
|
||||||
self.clock = self.hs.get_clock()
|
self.clock = self.hs.get_clock()
|
||||||
|
|
||||||
self._account_validity = hs.config.account_validity
|
self._account_validity_enabled = hs.config.account_validity_enabled
|
||||||
|
|
||||||
# We shard the handling of push notifications by user ID.
|
# We shard the handling of push notifications by user ID.
|
||||||
self._pusher_shard_config = hs.config.push.pusher_shard_config
|
self._pusher_shard_config = hs.config.push.pusher_shard_config
|
||||||
@@ -223,7 +223,7 @@ class PusherPool:
|
|||||||
|
|
||||||
for u in users_affected:
|
for u in users_affected:
|
||||||
# Don't push if the user account has expired
|
# Don't push if the user account has expired
|
||||||
if self._account_validity.enabled:
|
if self._account_validity_enabled:
|
||||||
expired = await self.store.is_account_expired(
|
expired = await self.store.is_account_expired(
|
||||||
u, self.clock.time_msec()
|
u, self.clock.time_msec()
|
||||||
)
|
)
|
||||||
@@ -251,7 +251,7 @@ class PusherPool:
|
|||||||
|
|
||||||
for u in users_affected:
|
for u in users_affected:
|
||||||
# Don't push if the user account has expired
|
# Don't push if the user account has expired
|
||||||
if self._account_validity.enabled:
|
if self._account_validity_enabled:
|
||||||
expired = await self.store.is_account_expired(
|
expired = await self.store.is_account_expired(
|
||||||
u, self.clock.time_msec()
|
u, self.clock.time_msec()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ class AccountValidityRenewServlet(RestServlet):
|
|||||||
self.hs = hs
|
self.hs = hs
|
||||||
self.account_activity_handler = hs.get_account_validity_handler()
|
self.account_activity_handler = hs.get_account_validity_handler()
|
||||||
self.auth = hs.get_auth()
|
self.auth = hs.get_auth()
|
||||||
self.success_html = hs.config.account_validity.account_renewed_html_content
|
self.success_html = hs.config.account_validity_account_renewed_html_content
|
||||||
self.failure_html = hs.config.account_validity.invalid_token_html_content
|
self.failure_html = hs.config.account_validity_invalid_token_html_content
|
||||||
|
|
||||||
async def on_GET(self, request):
|
async def on_GET(self, request):
|
||||||
if b"token" not in request.args:
|
if b"token" not in request.args:
|
||||||
|
|||||||
@@ -82,8 +82,13 @@ class RegistrationWorkerStore(CacheInvalidationWorkerStore):
|
|||||||
database.engine, find_max_generated_user_id_localpart, "user_id_seq",
|
database.engine, find_max_generated_user_id_localpart, "user_id_seq",
|
||||||
)
|
)
|
||||||
|
|
||||||
self._account_validity = hs.config.account_validity
|
self._account_validity_enabled = hs.config.account_validity_enabled
|
||||||
if hs.config.run_background_tasks and self._account_validity.enabled:
|
self._account_validity_period = hs.config.account_validity_period
|
||||||
|
self._account_validity_renew_at = hs.config.account_validity_renew_at
|
||||||
|
self._account_validity_startup_job_max_delta = (
|
||||||
|
hs.config.account_validity_startup_job_max_delta
|
||||||
|
)
|
||||||
|
if hs.config.run_background_tasks and self._account_validity_enabled:
|
||||||
self._clock.call_later(
|
self._clock.call_later(
|
||||||
0.0, self._set_expiration_date_when_missing,
|
0.0, self._set_expiration_date_when_missing,
|
||||||
)
|
)
|
||||||
@@ -291,7 +296,7 @@ class RegistrationWorkerStore(CacheInvalidationWorkerStore):
|
|||||||
"get_users_expiring_soon",
|
"get_users_expiring_soon",
|
||||||
select_users_txn,
|
select_users_txn,
|
||||||
self._clock.time_msec(),
|
self._clock.time_msec(),
|
||||||
self.config.account_validity.renew_at,
|
self.config.account_validity_renew_at,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def set_renewal_mail_status(self, user_id: str, email_sent: bool) -> None:
|
async def set_renewal_mail_status(self, user_id: str, email_sent: bool) -> None:
|
||||||
@@ -902,11 +907,11 @@ class RegistrationWorkerStore(CacheInvalidationWorkerStore):
|
|||||||
delta equal to 10% of the validity period.
|
delta equal to 10% of the validity period.
|
||||||
"""
|
"""
|
||||||
now_ms = self._clock.time_msec()
|
now_ms = self._clock.time_msec()
|
||||||
expiration_ts = now_ms + self._account_validity.period
|
expiration_ts = now_ms + self._account_validity_period
|
||||||
|
|
||||||
if use_delta:
|
if use_delta:
|
||||||
expiration_ts = self.rand.randrange(
|
expiration_ts = self.rand.randrange(
|
||||||
expiration_ts - self._account_validity.startup_job_max_delta,
|
expiration_ts - self._account_validity_startup_job_max_delta,
|
||||||
expiration_ts,
|
expiration_ts,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1306,7 +1311,7 @@ class RegistrationStore(StatsStore, RegistrationBackgroundUpdateStore):
|
|||||||
except self.database_engine.module.IntegrityError:
|
except self.database_engine.module.IntegrityError:
|
||||||
raise StoreError(400, "User ID already taken.", errcode=Codes.USER_IN_USE)
|
raise StoreError(400, "User ID already taken.", errcode=Codes.USER_IN_USE)
|
||||||
|
|
||||||
if self._account_validity.enabled:
|
if self._account_validity_enabled:
|
||||||
self.set_expiration_date_for_user_txn(txn, user_id)
|
self.set_expiration_date_for_user_txn(txn, user_id)
|
||||||
|
|
||||||
if create_profile_with_displayname:
|
if create_profile_with_displayname:
|
||||||
|
|||||||
Reference in New Issue
Block a user