Refactor HomeserverConfig so it can be typechecked (#6137)
This commit is contained in:
parent
def5413480
commit
f743108a94
|
@ -0,0 +1 @@
|
||||||
|
Refactor configuration loading to allow better typechecking.
|
16
mypy.ini
16
mypy.ini
|
@ -4,10 +4,6 @@ plugins=mypy_zope:plugin
|
||||||
follow_imports=skip
|
follow_imports=skip
|
||||||
mypy_path=stubs
|
mypy_path=stubs
|
||||||
|
|
||||||
[mypy-synapse.config.homeserver]
|
|
||||||
# this is a mess because of the metaclass shenanigans
|
|
||||||
ignore_errors = True
|
|
||||||
|
|
||||||
[mypy-zope]
|
[mypy-zope]
|
||||||
ignore_missing_imports = True
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
@ -52,3 +48,15 @@ ignore_missing_imports = True
|
||||||
|
|
||||||
[mypy-signedjson.*]
|
[mypy-signedjson.*]
|
||||||
ignore_missing_imports = True
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-prometheus_client.*]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-service_identity.*]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-daemonize]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-sentry_sdk]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
|
@ -18,7 +18,9 @@
|
||||||
import argparse
|
import argparse
|
||||||
import errno
|
import errno
|
||||||
import os
|
import os
|
||||||
|
from collections import OrderedDict
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
|
from typing import Any, MutableMapping, Optional
|
||||||
|
|
||||||
from six import integer_types
|
from six import integer_types
|
||||||
|
|
||||||
|
@ -51,7 +53,56 @@ Missing mandatory `server_name` config option.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def path_exists(file_path):
|
||||||
|
"""Check if a file exists
|
||||||
|
|
||||||
|
Unlike os.path.exists, this throws an exception if there is an error
|
||||||
|
checking if the file exists (for example, if there is a perms error on
|
||||||
|
the parent dir).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if the file exists; False if not.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
os.stat(file_path)
|
||||||
|
return True
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno != errno.ENOENT:
|
||||||
|
raise e
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class Config(object):
|
class Config(object):
|
||||||
|
"""
|
||||||
|
A configuration section, containing configuration keys and values.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
section (str): The section title of this config object, such as
|
||||||
|
"tls" or "logger". This is used to refer to it on the root
|
||||||
|
logger (for example, `config.tls.some_option`). Must be
|
||||||
|
defined in subclasses.
|
||||||
|
"""
|
||||||
|
|
||||||
|
section = None
|
||||||
|
|
||||||
|
def __init__(self, root_config=None):
|
||||||
|
self.root = root_config
|
||||||
|
|
||||||
|
def __getattr__(self, item: str) -> Any:
|
||||||
|
"""
|
||||||
|
Try and fetch a configuration option that does not exist on this class.
|
||||||
|
|
||||||
|
This is so that existing configs that rely on `self.value`, where value
|
||||||
|
is actually from a different config section, continue to work.
|
||||||
|
"""
|
||||||
|
if item in ["generate_config_section", "read_config"]:
|
||||||
|
raise AttributeError(item)
|
||||||
|
|
||||||
|
if self.root is None:
|
||||||
|
raise AttributeError(item)
|
||||||
|
else:
|
||||||
|
return self.root._get_unclassed_config(self.section, item)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_size(value):
|
def parse_size(value):
|
||||||
if isinstance(value, integer_types):
|
if isinstance(value, integer_types):
|
||||||
|
@ -88,22 +139,7 @@ class Config(object):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def path_exists(cls, file_path):
|
def path_exists(cls, file_path):
|
||||||
"""Check if a file exists
|
return path_exists(file_path)
|
||||||
|
|
||||||
Unlike os.path.exists, this throws an exception if there is an error
|
|
||||||
checking if the file exists (for example, if there is a perms error on
|
|
||||||
the parent dir).
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: True if the file exists; False if not.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
os.stat(file_path)
|
|
||||||
return True
|
|
||||||
except OSError as e:
|
|
||||||
if e.errno != errno.ENOENT:
|
|
||||||
raise e
|
|
||||||
return False
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def check_file(cls, file_path, config_name):
|
def check_file(cls, file_path, config_name):
|
||||||
|
@ -136,42 +172,106 @@ class Config(object):
|
||||||
with open(file_path) as file_stream:
|
with open(file_path) as file_stream:
|
||||||
return file_stream.read()
|
return file_stream.read()
|
||||||
|
|
||||||
def invoke_all(self, name, *args, **kargs):
|
|
||||||
"""Invoke all instance methods with the given name and arguments in the
|
class RootConfig(object):
|
||||||
class's MRO.
|
"""
|
||||||
|
Holder of an application's configuration.
|
||||||
|
|
||||||
|
What configuration this object holds is defined by `config_classes`, a list
|
||||||
|
of Config classes that will be instantiated and given the contents of a
|
||||||
|
configuration file to read. They can then be accessed on this class by their
|
||||||
|
section name, defined in the Config or dynamically set to be the name of the
|
||||||
|
class, lower-cased and with "Config" removed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
config_classes = []
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._configs = OrderedDict()
|
||||||
|
|
||||||
|
for config_class in self.config_classes:
|
||||||
|
if config_class.section is None:
|
||||||
|
raise ValueError("%r requires a section name" % (config_class,))
|
||||||
|
|
||||||
|
try:
|
||||||
|
conf = config_class(self)
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception("Failed making %s: %r" % (config_class.section, e))
|
||||||
|
self._configs[config_class.section] = conf
|
||||||
|
|
||||||
|
def __getattr__(self, item: str) -> Any:
|
||||||
|
"""
|
||||||
|
Redirect lookups on this object either to config objects, or values on
|
||||||
|
config objects, so that `config.tls.blah` works, as well as legacy uses
|
||||||
|
of things like `config.server_name`. It will first look up the config
|
||||||
|
section name, and then values on those config classes.
|
||||||
|
"""
|
||||||
|
if item in self._configs.keys():
|
||||||
|
return self._configs[item]
|
||||||
|
|
||||||
|
return self._get_unclassed_config(None, item)
|
||||||
|
|
||||||
|
def _get_unclassed_config(self, asking_section: Optional[str], item: str):
|
||||||
|
"""
|
||||||
|
Fetch a config value from one of the instantiated config classes that
|
||||||
|
has not been fetched directly.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
name (str): Name of function to invoke
|
asking_section: If this check is coming from a Config child, which
|
||||||
|
one? This section will not be asked if it has the value.
|
||||||
|
item: The configuration value key.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AttributeError if no config classes have the config key. The body
|
||||||
|
will contain what sections were checked.
|
||||||
|
"""
|
||||||
|
for key, val in self._configs.items():
|
||||||
|
if key == asking_section:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if item in dir(val):
|
||||||
|
return getattr(val, item)
|
||||||
|
|
||||||
|
raise AttributeError(item, "not found in %s" % (list(self._configs.keys()),))
|
||||||
|
|
||||||
|
def invoke_all(self, func_name: str, *args, **kwargs) -> MutableMapping[str, Any]:
|
||||||
|
"""
|
||||||
|
Invoke a function on all instantiated config objects this RootConfig is
|
||||||
|
configured to use.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
func_name: Name of function to invoke
|
||||||
*args
|
*args
|
||||||
**kwargs
|
**kwargs
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list: The list of the return values from each method called
|
ordered dictionary of config section name and the result of the
|
||||||
|
function from it.
|
||||||
"""
|
"""
|
||||||
results = []
|
res = OrderedDict()
|
||||||
for cls in type(self).mro():
|
|
||||||
if name in cls.__dict__:
|
for name, config in self._configs.items():
|
||||||
results.append(getattr(cls, name)(self, *args, **kargs))
|
if hasattr(config, func_name):
|
||||||
return results
|
res[name] = getattr(config, func_name)(*args, **kwargs)
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def invoke_all_static(cls, name, *args, **kargs):
|
def invoke_all_static(cls, func_name: str, *args, **kwargs):
|
||||||
"""Invoke all static methods with the given name and arguments in the
|
"""
|
||||||
class's MRO.
|
Invoke a static function on config objects this RootConfig is
|
||||||
|
configured to use.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
name (str): Name of function to invoke
|
func_name: Name of function to invoke
|
||||||
*args
|
*args
|
||||||
**kwargs
|
**kwargs
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list: The list of the return values from each method called
|
ordered dictionary of config section name and the result of the
|
||||||
|
function from it.
|
||||||
"""
|
"""
|
||||||
results = []
|
for config in cls.config_classes:
|
||||||
for c in cls.mro():
|
if hasattr(config, func_name):
|
||||||
if name in c.__dict__:
|
getattr(config, func_name)(*args, **kwargs)
|
||||||
results.append(getattr(c, name)(*args, **kargs))
|
|
||||||
return results
|
|
||||||
|
|
||||||
def generate_config(
|
def generate_config(
|
||||||
self,
|
self,
|
||||||
|
@ -187,7 +287,8 @@ class Config(object):
|
||||||
tls_private_key_path=None,
|
tls_private_key_path=None,
|
||||||
acme_domain=None,
|
acme_domain=None,
|
||||||
):
|
):
|
||||||
"""Build a default configuration file
|
"""
|
||||||
|
Build a default configuration file
|
||||||
|
|
||||||
This is used when the user explicitly asks us to generate a config file
|
This is used when the user explicitly asks us to generate a config file
|
||||||
(eg with --generate_config).
|
(eg with --generate_config).
|
||||||
|
@ -242,6 +343,7 @@ class Config(object):
|
||||||
Returns:
|
Returns:
|
||||||
str: the yaml config file
|
str: the yaml config file
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return "\n\n".join(
|
return "\n\n".join(
|
||||||
dedent(conf)
|
dedent(conf)
|
||||||
for conf in self.invoke_all(
|
for conf in self.invoke_all(
|
||||||
|
@ -257,7 +359,7 @@ class Config(object):
|
||||||
tls_certificate_path=tls_certificate_path,
|
tls_certificate_path=tls_certificate_path,
|
||||||
tls_private_key_path=tls_private_key_path,
|
tls_private_key_path=tls_private_key_path,
|
||||||
acme_domain=acme_domain,
|
acme_domain=acme_domain,
|
||||||
)
|
).values()
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -444,7 +546,7 @@ class Config(object):
|
||||||
)
|
)
|
||||||
|
|
||||||
(config_path,) = config_files
|
(config_path,) = config_files
|
||||||
if not cls.path_exists(config_path):
|
if not path_exists(config_path):
|
||||||
print("Generating config file %s" % (config_path,))
|
print("Generating config file %s" % (config_path,))
|
||||||
|
|
||||||
if config_args.data_directory:
|
if config_args.data_directory:
|
||||||
|
@ -469,7 +571,7 @@ class Config(object):
|
||||||
open_private_ports=config_args.open_private_ports,
|
open_private_ports=config_args.open_private_ports,
|
||||||
)
|
)
|
||||||
|
|
||||||
if not cls.path_exists(config_dir_path):
|
if not path_exists(config_dir_path):
|
||||||
os.makedirs(config_dir_path)
|
os.makedirs(config_dir_path)
|
||||||
with open(config_path, "w") as config_file:
|
with open(config_path, "w") as config_file:
|
||||||
config_file.write("# vim:ft=yaml\n\n")
|
config_file.write("# vim:ft=yaml\n\n")
|
||||||
|
@ -518,7 +620,7 @@ class Config(object):
|
||||||
|
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
def parse_config_dict(self, config_dict, config_dir_path, data_dir_path):
|
def parse_config_dict(self, config_dict, config_dir_path=None, data_dir_path=None):
|
||||||
"""Read the information from the config dict into this Config object.
|
"""Read the information from the config dict into this Config object.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
@ -607,3 +709,6 @@ def find_config_files(search_paths):
|
||||||
else:
|
else:
|
||||||
config_files.append(config_path)
|
config_files.append(config_path)
|
||||||
return config_files
|
return config_files
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["Config", "RootConfig"]
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
from typing import Any, List, Optional
|
||||||
|
|
||||||
|
from synapse.config import (
|
||||||
|
api,
|
||||||
|
appservice,
|
||||||
|
captcha,
|
||||||
|
cas,
|
||||||
|
consent_config,
|
||||||
|
database,
|
||||||
|
emailconfig,
|
||||||
|
groups,
|
||||||
|
jwt_config,
|
||||||
|
key,
|
||||||
|
logger,
|
||||||
|
metrics,
|
||||||
|
password,
|
||||||
|
password_auth_providers,
|
||||||
|
push,
|
||||||
|
ratelimiting,
|
||||||
|
registration,
|
||||||
|
repository,
|
||||||
|
room_directory,
|
||||||
|
saml2_config,
|
||||||
|
server,
|
||||||
|
server_notices_config,
|
||||||
|
spam_checker,
|
||||||
|
stats,
|
||||||
|
third_party_event_rules,
|
||||||
|
tls,
|
||||||
|
tracer,
|
||||||
|
user_directory,
|
||||||
|
voip,
|
||||||
|
workers,
|
||||||
|
)
|
||||||
|
|
||||||
|
class ConfigError(Exception): ...
|
||||||
|
|
||||||
|
MISSING_REPORT_STATS_CONFIG_INSTRUCTIONS: str
|
||||||
|
MISSING_REPORT_STATS_SPIEL: str
|
||||||
|
MISSING_SERVER_NAME: str
|
||||||
|
|
||||||
|
def path_exists(file_path: str): ...
|
||||||
|
|
||||||
|
class RootConfig:
|
||||||
|
server: server.ServerConfig
|
||||||
|
tls: tls.TlsConfig
|
||||||
|
database: database.DatabaseConfig
|
||||||
|
logging: logger.LoggingConfig
|
||||||
|
ratelimit: ratelimiting.RatelimitConfig
|
||||||
|
media: repository.ContentRepositoryConfig
|
||||||
|
captcha: captcha.CaptchaConfig
|
||||||
|
voip: voip.VoipConfig
|
||||||
|
registration: registration.RegistrationConfig
|
||||||
|
metrics: metrics.MetricsConfig
|
||||||
|
api: api.ApiConfig
|
||||||
|
appservice: appservice.AppServiceConfig
|
||||||
|
key: key.KeyConfig
|
||||||
|
saml2: saml2_config.SAML2Config
|
||||||
|
cas: cas.CasConfig
|
||||||
|
jwt: jwt_config.JWTConfig
|
||||||
|
password: password.PasswordConfig
|
||||||
|
email: emailconfig.EmailConfig
|
||||||
|
worker: workers.WorkerConfig
|
||||||
|
authproviders: password_auth_providers.PasswordAuthProviderConfig
|
||||||
|
push: push.PushConfig
|
||||||
|
spamchecker: spam_checker.SpamCheckerConfig
|
||||||
|
groups: groups.GroupsConfig
|
||||||
|
userdirectory: user_directory.UserDirectoryConfig
|
||||||
|
consent: consent_config.ConsentConfig
|
||||||
|
stats: stats.StatsConfig
|
||||||
|
servernotices: server_notices_config.ServerNoticesConfig
|
||||||
|
roomdirectory: room_directory.RoomDirectoryConfig
|
||||||
|
thirdpartyrules: third_party_event_rules.ThirdPartyRulesConfig
|
||||||
|
tracer: tracer.TracerConfig
|
||||||
|
|
||||||
|
config_classes: List = ...
|
||||||
|
def __init__(self) -> None: ...
|
||||||
|
def invoke_all(self, func_name: str, *args: Any, **kwargs: Any): ...
|
||||||
|
@classmethod
|
||||||
|
def invoke_all_static(cls, func_name: str, *args: Any, **kwargs: Any) -> None: ...
|
||||||
|
def __getattr__(self, item: str): ...
|
||||||
|
def parse_config_dict(
|
||||||
|
self,
|
||||||
|
config_dict: Any,
|
||||||
|
config_dir_path: Optional[Any] = ...,
|
||||||
|
data_dir_path: Optional[Any] = ...,
|
||||||
|
) -> None: ...
|
||||||
|
read_config: Any = ...
|
||||||
|
def generate_config(
|
||||||
|
self,
|
||||||
|
config_dir_path: str,
|
||||||
|
data_dir_path: str,
|
||||||
|
server_name: str,
|
||||||
|
generate_secrets: bool = ...,
|
||||||
|
report_stats: Optional[str] = ...,
|
||||||
|
open_private_ports: bool = ...,
|
||||||
|
listeners: Optional[Any] = ...,
|
||||||
|
database_conf: Optional[Any] = ...,
|
||||||
|
tls_certificate_path: Optional[str] = ...,
|
||||||
|
tls_private_key_path: Optional[str] = ...,
|
||||||
|
acme_domain: Optional[str] = ...,
|
||||||
|
): ...
|
||||||
|
@classmethod
|
||||||
|
def load_or_generate_config(cls, description: Any, argv: Any): ...
|
||||||
|
@classmethod
|
||||||
|
def load_config(cls, description: Any, argv: Any): ...
|
||||||
|
@classmethod
|
||||||
|
def add_arguments_to_parser(cls, config_parser: Any) -> None: ...
|
||||||
|
@classmethod
|
||||||
|
def load_config_with_parser(cls, parser: Any, argv: Any): ...
|
||||||
|
def generate_missing_files(
|
||||||
|
self, config_dict: dict, config_dir_path: str
|
||||||
|
) -> None: ...
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
root: RootConfig
|
||||||
|
def __init__(self, root_config: Optional[RootConfig] = ...) -> None: ...
|
||||||
|
def __getattr__(self, item: str, from_root: bool = ...): ...
|
||||||
|
@staticmethod
|
||||||
|
def parse_size(value: Any): ...
|
||||||
|
@staticmethod
|
||||||
|
def parse_duration(value: Any): ...
|
||||||
|
@staticmethod
|
||||||
|
def abspath(file_path: Optional[str]): ...
|
||||||
|
@classmethod
|
||||||
|
def path_exists(cls, file_path: str): ...
|
||||||
|
@classmethod
|
||||||
|
def check_file(cls, file_path: str, config_name: str): ...
|
||||||
|
@classmethod
|
||||||
|
def ensure_directory(cls, dir_path: str): ...
|
||||||
|
@classmethod
|
||||||
|
def read_file(cls, file_path: str, config_name: str): ...
|
||||||
|
|
||||||
|
def read_config_files(config_files: List[str]): ...
|
||||||
|
def find_config_files(search_paths: List[str]): ...
|
|
@ -18,6 +18,8 @@ from ._base import Config
|
||||||
|
|
||||||
|
|
||||||
class ApiConfig(Config):
|
class ApiConfig(Config):
|
||||||
|
section = "api"
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
self.room_invite_state_types = config.get(
|
self.room_invite_state_types = config.get(
|
||||||
"room_invite_state_types",
|
"room_invite_state_types",
|
||||||
|
|
|
@ -30,6 +30,8 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class AppServiceConfig(Config):
|
class AppServiceConfig(Config):
|
||||||
|
section = "appservice"
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
self.app_service_config_files = config.get("app_service_config_files", [])
|
self.app_service_config_files = config.get("app_service_config_files", [])
|
||||||
self.notify_appservices = config.get("notify_appservices", True)
|
self.notify_appservices = config.get("notify_appservices", True)
|
||||||
|
|
|
@ -16,6 +16,8 @@ from ._base import Config
|
||||||
|
|
||||||
|
|
||||||
class CaptchaConfig(Config):
|
class CaptchaConfig(Config):
|
||||||
|
section = "captcha"
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
self.recaptcha_private_key = config.get("recaptcha_private_key")
|
self.recaptcha_private_key = config.get("recaptcha_private_key")
|
||||||
self.recaptcha_public_key = config.get("recaptcha_public_key")
|
self.recaptcha_public_key = config.get("recaptcha_public_key")
|
||||||
|
|
|
@ -22,6 +22,8 @@ class CasConfig(Config):
|
||||||
cas_server_url: URL of CAS server
|
cas_server_url: URL of CAS server
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
section = "cas"
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
cas_config = config.get("cas_config", None)
|
cas_config = config.get("cas_config", None)
|
||||||
if cas_config:
|
if cas_config:
|
||||||
|
|
|
@ -73,6 +73,9 @@ DEFAULT_CONFIG = """\
|
||||||
|
|
||||||
|
|
||||||
class ConsentConfig(Config):
|
class ConsentConfig(Config):
|
||||||
|
|
||||||
|
section = "consent"
|
||||||
|
|
||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
super(ConsentConfig, self).__init__(*args)
|
super(ConsentConfig, self).__init__(*args)
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,8 @@ from ._base import Config
|
||||||
|
|
||||||
|
|
||||||
class DatabaseConfig(Config):
|
class DatabaseConfig(Config):
|
||||||
|
section = "database"
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
self.event_cache_size = self.parse_size(config.get("event_cache_size", "10K"))
|
self.event_cache_size = self.parse_size(config.get("event_cache_size", "10K"))
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,8 @@ from ._base import Config, ConfigError
|
||||||
|
|
||||||
|
|
||||||
class EmailConfig(Config):
|
class EmailConfig(Config):
|
||||||
|
section = "email"
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
# TODO: We should separate better the email configuration from the notification
|
# TODO: We should separate better the email configuration from the notification
|
||||||
# and account validity config.
|
# and account validity config.
|
||||||
|
|
|
@ -17,6 +17,8 @@ from ._base import Config
|
||||||
|
|
||||||
|
|
||||||
class GroupsConfig(Config):
|
class GroupsConfig(Config):
|
||||||
|
section = "groups"
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
self.enable_group_creation = config.get("enable_group_creation", False)
|
self.enable_group_creation = config.get("enable_group_creation", False)
|
||||||
self.group_creation_prefix = config.get("group_creation_prefix", "")
|
self.group_creation_prefix = config.get("group_creation_prefix", "")
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
# 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 .api import ApiConfig
|
from .api import ApiConfig
|
||||||
from .appservice import AppServiceConfig
|
from .appservice import AppServiceConfig
|
||||||
from .captcha import CaptchaConfig
|
from .captcha import CaptchaConfig
|
||||||
|
@ -46,7 +47,9 @@ from .voip import VoipConfig
|
||||||
from .workers import WorkerConfig
|
from .workers import WorkerConfig
|
||||||
|
|
||||||
|
|
||||||
class HomeServerConfig(
|
class HomeServerConfig(RootConfig):
|
||||||
|
|
||||||
|
config_classes = [
|
||||||
ServerConfig,
|
ServerConfig,
|
||||||
TlsConfig,
|
TlsConfig,
|
||||||
DatabaseConfig,
|
DatabaseConfig,
|
||||||
|
@ -77,5 +80,4 @@ class HomeServerConfig(
|
||||||
RoomDirectoryConfig,
|
RoomDirectoryConfig,
|
||||||
ThirdPartyRulesConfig,
|
ThirdPartyRulesConfig,
|
||||||
TracerConfig,
|
TracerConfig,
|
||||||
):
|
]
|
||||||
pass
|
|
||||||
|
|
|
@ -23,6 +23,8 @@ MISSING_JWT = """Missing jwt library. This is required for jwt login.
|
||||||
|
|
||||||
|
|
||||||
class JWTConfig(Config):
|
class JWTConfig(Config):
|
||||||
|
section = "jwt"
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
jwt_config = config.get("jwt_config", None)
|
jwt_config = config.get("jwt_config", None)
|
||||||
if jwt_config:
|
if jwt_config:
|
||||||
|
|
|
@ -92,6 +92,8 @@ class TrustedKeyServer(object):
|
||||||
|
|
||||||
|
|
||||||
class KeyConfig(Config):
|
class KeyConfig(Config):
|
||||||
|
section = "key"
|
||||||
|
|
||||||
def read_config(self, config, config_dir_path, **kwargs):
|
def read_config(self, config, config_dir_path, **kwargs):
|
||||||
# the signing key can be specified inline or in a separate file
|
# the signing key can be specified inline or in a separate file
|
||||||
if "signing_key" in config:
|
if "signing_key" in config:
|
||||||
|
|
|
@ -84,6 +84,8 @@ root:
|
||||||
|
|
||||||
|
|
||||||
class LoggingConfig(Config):
|
class LoggingConfig(Config):
|
||||||
|
section = "logging"
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
self.log_config = self.abspath(config.get("log_config"))
|
self.log_config = self.abspath(config.get("log_config"))
|
||||||
self.no_redirect_stdio = config.get("no_redirect_stdio", False)
|
self.no_redirect_stdio = config.get("no_redirect_stdio", False)
|
||||||
|
|
|
@ -34,6 +34,8 @@ class MetricsFlags(object):
|
||||||
|
|
||||||
|
|
||||||
class MetricsConfig(Config):
|
class MetricsConfig(Config):
|
||||||
|
section = "metrics"
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
self.enable_metrics = config.get("enable_metrics", False)
|
self.enable_metrics = config.get("enable_metrics", False)
|
||||||
self.report_stats = config.get("report_stats", None)
|
self.report_stats = config.get("report_stats", None)
|
||||||
|
|
|
@ -20,6 +20,8 @@ class PasswordConfig(Config):
|
||||||
"""Password login configuration
|
"""Password login configuration
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
section = "password"
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
password_config = config.get("password_config", {})
|
password_config = config.get("password_config", {})
|
||||||
if password_config is None:
|
if password_config is None:
|
||||||
|
|
|
@ -23,6 +23,8 @@ LDAP_PROVIDER = "ldap_auth_provider.LdapAuthProvider"
|
||||||
|
|
||||||
|
|
||||||
class PasswordAuthProviderConfig(Config):
|
class PasswordAuthProviderConfig(Config):
|
||||||
|
section = "authproviders"
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
self.password_providers = [] # type: List[Any]
|
self.password_providers = [] # type: List[Any]
|
||||||
providers = []
|
providers = []
|
||||||
|
|
|
@ -18,6 +18,8 @@ from ._base import Config
|
||||||
|
|
||||||
|
|
||||||
class PushConfig(Config):
|
class PushConfig(Config):
|
||||||
|
section = "push"
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
push_config = config.get("push", {})
|
push_config = config.get("push", {})
|
||||||
self.push_include_content = push_config.get("include_content", True)
|
self.push_include_content = push_config.get("include_content", True)
|
||||||
|
|
|
@ -36,6 +36,8 @@ class FederationRateLimitConfig(object):
|
||||||
|
|
||||||
|
|
||||||
class RatelimitConfig(Config):
|
class RatelimitConfig(Config):
|
||||||
|
section = "ratelimiting"
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
|
|
||||||
# Load the new-style messages config if it exists. Otherwise fall back
|
# Load the new-style messages config if it exists. Otherwise fall back
|
||||||
|
|
|
@ -24,6 +24,8 @@ from synapse.util.stringutils import random_string_with_symbols
|
||||||
|
|
||||||
|
|
||||||
class AccountValidityConfig(Config):
|
class AccountValidityConfig(Config):
|
||||||
|
section = "accountvalidity"
|
||||||
|
|
||||||
def __init__(self, config, synapse_config):
|
def __init__(self, config, synapse_config):
|
||||||
self.enabled = config.get("enabled", False)
|
self.enabled = config.get("enabled", False)
|
||||||
self.renew_by_email_enabled = "renew_at" in config
|
self.renew_by_email_enabled = "renew_at" in config
|
||||||
|
@ -77,6 +79,8 @@ class AccountValidityConfig(Config):
|
||||||
|
|
||||||
|
|
||||||
class RegistrationConfig(Config):
|
class RegistrationConfig(Config):
|
||||||
|
section = "registration"
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
self.enable_registration = bool(
|
self.enable_registration = bool(
|
||||||
strtobool(str(config.get("enable_registration", False)))
|
strtobool(str(config.get("enable_registration", False)))
|
||||||
|
|
|
@ -78,6 +78,8 @@ def parse_thumbnail_requirements(thumbnail_sizes):
|
||||||
|
|
||||||
|
|
||||||
class ContentRepositoryConfig(Config):
|
class ContentRepositoryConfig(Config):
|
||||||
|
section = "media"
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
|
|
||||||
# Only enable the media repo if either the media repo is enabled or the
|
# Only enable the media repo if either the media repo is enabled or the
|
||||||
|
|
|
@ -19,6 +19,8 @@ from ._base import Config, ConfigError
|
||||||
|
|
||||||
|
|
||||||
class RoomDirectoryConfig(Config):
|
class RoomDirectoryConfig(Config):
|
||||||
|
section = "roomdirectory"
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
self.enable_room_list_search = config.get("enable_room_list_search", True)
|
self.enable_room_list_search = config.get("enable_room_list_search", True)
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,8 @@ def _dict_merge(merge_dict, into_dict):
|
||||||
|
|
||||||
|
|
||||||
class SAML2Config(Config):
|
class SAML2Config(Config):
|
||||||
|
section = "saml2"
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
self.saml2_enabled = False
|
self.saml2_enabled = False
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,8 @@ on how to configure the new listener.
|
||||||
|
|
||||||
|
|
||||||
class ServerConfig(Config):
|
class ServerConfig(Config):
|
||||||
|
section = "server"
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
self.server_name = config["server_name"]
|
self.server_name = config["server_name"]
|
||||||
self.server_context = config.get("server_context", None)
|
self.server_context = config.get("server_context", None)
|
||||||
|
|
|
@ -59,6 +59,8 @@ class ServerNoticesConfig(Config):
|
||||||
None if server notices are not enabled.
|
None if server notices are not enabled.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
section = "servernotices"
|
||||||
|
|
||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
super(ServerNoticesConfig, self).__init__(*args)
|
super(ServerNoticesConfig, self).__init__(*args)
|
||||||
self.server_notices_mxid = None
|
self.server_notices_mxid = None
|
||||||
|
|
|
@ -19,6 +19,8 @@ from ._base import Config
|
||||||
|
|
||||||
|
|
||||||
class SpamCheckerConfig(Config):
|
class SpamCheckerConfig(Config):
|
||||||
|
section = "spamchecker"
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
self.spam_checker = None
|
self.spam_checker = None
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,8 @@ class StatsConfig(Config):
|
||||||
Configuration for the behaviour of synapse's stats engine
|
Configuration for the behaviour of synapse's stats engine
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
section = "stats"
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
self.stats_enabled = True
|
self.stats_enabled = True
|
||||||
self.stats_bucket_size = 86400 * 1000
|
self.stats_bucket_size = 86400 * 1000
|
||||||
|
|
|
@ -19,6 +19,8 @@ from ._base import Config
|
||||||
|
|
||||||
|
|
||||||
class ThirdPartyRulesConfig(Config):
|
class ThirdPartyRulesConfig(Config):
|
||||||
|
section = "thirdpartyrules"
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
self.third_party_event_rules = None
|
self.third_party_event_rules = None
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ import os
|
||||||
import warnings
|
import warnings
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
|
from typing import List
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
@ -33,7 +34,9 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class TlsConfig(Config):
|
class TlsConfig(Config):
|
||||||
def read_config(self, config, config_dir_path, **kwargs):
|
section = "tls"
|
||||||
|
|
||||||
|
def read_config(self, config: dict, config_dir_path: str, **kwargs):
|
||||||
|
|
||||||
acme_config = config.get("acme", None)
|
acme_config = config.get("acme", None)
|
||||||
if acme_config is None:
|
if acme_config is None:
|
||||||
|
@ -57,7 +60,7 @@ class TlsConfig(Config):
|
||||||
self.tls_certificate_file = self.abspath(config.get("tls_certificate_path"))
|
self.tls_certificate_file = self.abspath(config.get("tls_certificate_path"))
|
||||||
self.tls_private_key_file = self.abspath(config.get("tls_private_key_path"))
|
self.tls_private_key_file = self.abspath(config.get("tls_private_key_path"))
|
||||||
|
|
||||||
if self.has_tls_listener():
|
if self.root.server.has_tls_listener():
|
||||||
if not self.tls_certificate_file:
|
if not self.tls_certificate_file:
|
||||||
raise ConfigError(
|
raise ConfigError(
|
||||||
"tls_certificate_path must be specified if TLS-enabled listeners are "
|
"tls_certificate_path must be specified if TLS-enabled listeners are "
|
||||||
|
@ -108,7 +111,7 @@ class TlsConfig(Config):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Support globs (*) in whitelist values
|
# Support globs (*) in whitelist values
|
||||||
self.federation_certificate_verification_whitelist = []
|
self.federation_certificate_verification_whitelist = [] # type: List[str]
|
||||||
for entry in fed_whitelist_entries:
|
for entry in fed_whitelist_entries:
|
||||||
try:
|
try:
|
||||||
entry_regex = glob_to_regex(entry.encode("ascii").decode("ascii"))
|
entry_regex = glob_to_regex(entry.encode("ascii").decode("ascii"))
|
||||||
|
|
|
@ -19,6 +19,8 @@ from ._base import Config, ConfigError
|
||||||
|
|
||||||
|
|
||||||
class TracerConfig(Config):
|
class TracerConfig(Config):
|
||||||
|
section = "tracing"
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
opentracing_config = config.get("opentracing")
|
opentracing_config = config.get("opentracing")
|
||||||
if opentracing_config is None:
|
if opentracing_config is None:
|
||||||
|
|
|
@ -21,6 +21,8 @@ class UserDirectoryConfig(Config):
|
||||||
Configuration for the behaviour of the /user_directory API
|
Configuration for the behaviour of the /user_directory API
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
section = "userdirectory"
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
self.user_directory_search_enabled = True
|
self.user_directory_search_enabled = True
|
||||||
self.user_directory_search_all_users = False
|
self.user_directory_search_all_users = False
|
||||||
|
|
|
@ -16,6 +16,8 @@ from ._base import Config
|
||||||
|
|
||||||
|
|
||||||
class VoipConfig(Config):
|
class VoipConfig(Config):
|
||||||
|
section = "voip"
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
self.turn_uris = config.get("turn_uris", [])
|
self.turn_uris = config.get("turn_uris", [])
|
||||||
self.turn_shared_secret = config.get("turn_shared_secret")
|
self.turn_shared_secret = config.get("turn_shared_secret")
|
||||||
|
|
|
@ -21,6 +21,8 @@ class WorkerConfig(Config):
|
||||||
They have their own pid_file and listener configuration. They use the
|
They have their own pid_file and listener configuration. They use the
|
||||||
replication_url to talk to the main synapse process."""
|
replication_url to talk to the main synapse process."""
|
||||||
|
|
||||||
|
section = "worker"
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
self.worker_app = config.get("worker_app")
|
self.worker_app = config.get("worker_app")
|
||||||
|
|
||||||
|
|
|
@ -21,17 +21,24 @@ import yaml
|
||||||
|
|
||||||
from OpenSSL import SSL
|
from OpenSSL import SSL
|
||||||
|
|
||||||
|
from synapse.config._base import Config, RootConfig
|
||||||
from synapse.config.tls import ConfigError, TlsConfig
|
from synapse.config.tls import ConfigError, TlsConfig
|
||||||
from synapse.crypto.context_factory import ClientTLSOptionsFactory
|
from synapse.crypto.context_factory import ClientTLSOptionsFactory
|
||||||
|
|
||||||
from tests.unittest import TestCase
|
from tests.unittest import TestCase
|
||||||
|
|
||||||
|
|
||||||
class TestConfig(TlsConfig):
|
class FakeServer(Config):
|
||||||
|
section = "server"
|
||||||
|
|
||||||
def has_tls_listener(self):
|
def has_tls_listener(self):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class TestConfig(RootConfig):
|
||||||
|
config_classes = [FakeServer, TlsConfig]
|
||||||
|
|
||||||
|
|
||||||
class TLSConfigTests(TestCase):
|
class TLSConfigTests(TestCase):
|
||||||
def test_warn_self_signed(self):
|
def test_warn_self_signed(self):
|
||||||
"""
|
"""
|
||||||
|
@ -202,13 +209,13 @@ s4niecZKPBizL6aucT59CsunNmmb5Glq8rlAcU+1ZTZZzGYqVYhF6axB9Qg=
|
||||||
conf = TestConfig()
|
conf = TestConfig()
|
||||||
conf.read_config(
|
conf.read_config(
|
||||||
yaml.safe_load(
|
yaml.safe_load(
|
||||||
TestConfig().generate_config_section(
|
TestConfig().generate_config(
|
||||||
"/config_dir_path",
|
"/config_dir_path",
|
||||||
"my_super_secure_server",
|
"my_super_secure_server",
|
||||||
"/data_dir_path",
|
"/data_dir_path",
|
||||||
"/tls_cert_path",
|
tls_certificate_path="/tls_cert_path",
|
||||||
"tls_private_key",
|
tls_private_key_path="tls_private_key",
|
||||||
None, # This is the acme_domain
|
acme_domain=None, # This is the acme_domain
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
"/config_dir_path",
|
"/config_dir_path",
|
||||||
|
@ -223,13 +230,13 @@ s4niecZKPBizL6aucT59CsunNmmb5Glq8rlAcU+1ZTZZzGYqVYhF6axB9Qg=
|
||||||
conf = TestConfig()
|
conf = TestConfig()
|
||||||
conf.read_config(
|
conf.read_config(
|
||||||
yaml.safe_load(
|
yaml.safe_load(
|
||||||
TestConfig().generate_config_section(
|
TestConfig().generate_config(
|
||||||
"/config_dir_path",
|
"/config_dir_path",
|
||||||
"my_super_secure_server",
|
"my_super_secure_server",
|
||||||
"/data_dir_path",
|
"/data_dir_path",
|
||||||
"/tls_cert_path",
|
tls_certificate_path="/tls_cert_path",
|
||||||
"tls_private_key",
|
tls_private_key_path="tls_private_key",
|
||||||
"my_supe_secure_server", # This is the acme_domain
|
acme_domain="my_supe_secure_server", # This is the acme_domain
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
"/config_dir_path",
|
"/config_dir_path",
|
||||||
|
|
3
tox.ini
3
tox.ini
|
@ -163,10 +163,9 @@ deps =
|
||||||
{[base]deps}
|
{[base]deps}
|
||||||
mypy
|
mypy
|
||||||
mypy-zope
|
mypy-zope
|
||||||
typeshed
|
|
||||||
env =
|
env =
|
||||||
MYPYPATH = stubs/
|
MYPYPATH = stubs/
|
||||||
extras = all
|
extras = all
|
||||||
commands = mypy --show-traceback \
|
commands = mypy --show-traceback --check-untyped-defs --show-error-codes --follow-imports=normal \
|
||||||
synapse/logging/ \
|
synapse/logging/ \
|
||||||
synapse/config/
|
synapse/config/
|
||||||
|
|
Loading…
Reference in New Issue