mirror of
https://github.com/element-hq/synapse.git
synced 2024-12-14 11:57:44 +00:00
Refactor HomeserverConfig so it can be typechecked (#6137)
This commit is contained in:
parent
def5413480
commit
f743108a94
37 changed files with 415 additions and 94 deletions
1
changelog.d/6137.misc
Normal file
1
changelog.d/6137.misc
Normal file
|
@ -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
|
||||
mypy_path=stubs
|
||||
|
||||
[mypy-synapse.config.homeserver]
|
||||
# this is a mess because of the metaclass shenanigans
|
||||
ignore_errors = True
|
||||
|
||||
[mypy-zope]
|
||||
ignore_missing_imports = True
|
||||
|
||||
|
@ -52,3 +48,15 @@ ignore_missing_imports = True
|
|||
|
||||
[mypy-signedjson.*]
|
||||
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 errno
|
||||
import os
|
||||
from collections import OrderedDict
|
||||
from textwrap import dedent
|
||||
from typing import Any, MutableMapping, Optional
|
||||
|
||||
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):
|
||||
"""
|
||||
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
|
||||
def parse_size(value):
|
||||
if isinstance(value, integer_types):
|
||||
|
@ -88,22 +139,7 @@ class Config(object):
|
|||
|
||||
@classmethod
|
||||
def path_exists(cls, 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
|
||||
return path_exists(file_path)
|
||||
|
||||
@classmethod
|
||||
def check_file(cls, file_path, config_name):
|
||||
|
@ -136,42 +172,106 @@ class Config(object):
|
|||
with open(file_path) as file_stream:
|
||||
return file_stream.read()
|
||||
|
||||
def invoke_all(self, name, *args, **kargs):
|
||||
"""Invoke all instance methods with the given name and arguments in the
|
||||
class's MRO.
|
||||
|
||||
class RootConfig(object):
|
||||
"""
|
||||
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:
|
||||
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
|
||||
**kwargs
|
||||
|
||||
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 cls in type(self).mro():
|
||||
if name in cls.__dict__:
|
||||
results.append(getattr(cls, name)(self, *args, **kargs))
|
||||
return results
|
||||
res = OrderedDict()
|
||||
|
||||
for name, config in self._configs.items():
|
||||
if hasattr(config, func_name):
|
||||
res[name] = getattr(config, func_name)(*args, **kwargs)
|
||||
|
||||
return res
|
||||
|
||||
@classmethod
|
||||
def invoke_all_static(cls, name, *args, **kargs):
|
||||
"""Invoke all static methods with the given name and arguments in the
|
||||
class's MRO.
|
||||
def invoke_all_static(cls, func_name: str, *args, **kwargs):
|
||||
"""
|
||||
Invoke a static function on config objects this RootConfig is
|
||||
configured to use.
|
||||
|
||||
Args:
|
||||
name (str): Name of function to invoke
|
||||
func_name: Name of function to invoke
|
||||
*args
|
||||
**kwargs
|
||||
|
||||
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 c in cls.mro():
|
||||
if name in c.__dict__:
|
||||
results.append(getattr(c, name)(*args, **kargs))
|
||||
return results
|
||||
for config in cls.config_classes:
|
||||
if hasattr(config, func_name):
|
||||
getattr(config, func_name)(*args, **kwargs)
|
||||
|
||||
def generate_config(
|
||||
self,
|
||||
|
@ -187,7 +287,8 @@ class Config(object):
|
|||
tls_private_key_path=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
|
||||
(eg with --generate_config).
|
||||
|
@ -242,6 +343,7 @@ class Config(object):
|
|||
Returns:
|
||||
str: the yaml config file
|
||||
"""
|
||||
|
||||
return "\n\n".join(
|
||||
dedent(conf)
|
||||
for conf in self.invoke_all(
|
||||
|
@ -257,7 +359,7 @@ class Config(object):
|
|||
tls_certificate_path=tls_certificate_path,
|
||||
tls_private_key_path=tls_private_key_path,
|
||||
acme_domain=acme_domain,
|
||||
)
|
||||
).values()
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
@ -444,7 +546,7 @@ class Config(object):
|
|||
)
|
||||
|
||||
(config_path,) = config_files
|
||||
if not cls.path_exists(config_path):
|
||||
if not path_exists(config_path):
|
||||
print("Generating config file %s" % (config_path,))
|
||||
|
||||
if config_args.data_directory:
|
||||
|
@ -469,7 +571,7 @@ class Config(object):
|
|||
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)
|
||||
with open(config_path, "w") as config_file:
|
||||
config_file.write("# vim:ft=yaml\n\n")
|
||||
|
@ -518,7 +620,7 @@ class Config(object):
|
|||
|
||||
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.
|
||||
|
||||
Args:
|
||||
|
@ -607,3 +709,6 @@ def find_config_files(search_paths):
|
|||
else:
|
||||
config_files.append(config_path)
|
||||
return config_files
|
||||
|
||||
|
||||
__all__ = ["Config", "RootConfig"]
|
||||
|
|
135
synapse/config/_base.pyi
Normal file
135
synapse/config/_base.pyi
Normal file
|
@ -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):
|
||||
section = "api"
|
||||
|
||||
def read_config(self, config, **kwargs):
|
||||
self.room_invite_state_types = config.get(
|
||||
"room_invite_state_types",
|
||||
|
|
|
@ -30,6 +30,8 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class AppServiceConfig(Config):
|
||||
section = "appservice"
|
||||
|
||||
def read_config(self, config, **kwargs):
|
||||
self.app_service_config_files = config.get("app_service_config_files", [])
|
||||
self.notify_appservices = config.get("notify_appservices", True)
|
||||
|
|
|
@ -16,6 +16,8 @@ from ._base import Config
|
|||
|
||||
|
||||
class CaptchaConfig(Config):
|
||||
section = "captcha"
|
||||
|
||||
def read_config(self, config, **kwargs):
|
||||
self.recaptcha_private_key = config.get("recaptcha_private_key")
|
||||
self.recaptcha_public_key = config.get("recaptcha_public_key")
|
||||
|
|
|
@ -22,6 +22,8 @@ class CasConfig(Config):
|
|||
cas_server_url: URL of CAS server
|
||||
"""
|
||||
|
||||
section = "cas"
|
||||
|
||||
def read_config(self, config, **kwargs):
|
||||
cas_config = config.get("cas_config", None)
|
||||
if cas_config:
|
||||
|
|
|
@ -73,6 +73,9 @@ DEFAULT_CONFIG = """\
|
|||
|
||||
|
||||
class ConsentConfig(Config):
|
||||
|
||||
section = "consent"
|
||||
|
||||
def __init__(self, *args):
|
||||
super(ConsentConfig, self).__init__(*args)
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@ from ._base import Config
|
|||
|
||||
|
||||
class DatabaseConfig(Config):
|
||||
section = "database"
|
||||
|
||||
def read_config(self, config, **kwargs):
|
||||
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):
|
||||
section = "email"
|
||||
|
||||
def read_config(self, config, **kwargs):
|
||||
# TODO: We should separate better the email configuration from the notification
|
||||
# and account validity config.
|
||||
|
|
|
@ -17,6 +17,8 @@ from ._base import Config
|
|||
|
||||
|
||||
class GroupsConfig(Config):
|
||||
section = "groups"
|
||||
|
||||
def read_config(self, config, **kwargs):
|
||||
self.enable_group_creation = config.get("enable_group_creation", False)
|
||||
self.group_creation_prefix = config.get("group_creation_prefix", "")
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from ._base import RootConfig
|
||||
from .api import ApiConfig
|
||||
from .appservice import AppServiceConfig
|
||||
from .captcha import CaptchaConfig
|
||||
|
@ -46,36 +47,37 @@ from .voip import VoipConfig
|
|||
from .workers import WorkerConfig
|
||||
|
||||
|
||||
class HomeServerConfig(
|
||||
ServerConfig,
|
||||
TlsConfig,
|
||||
DatabaseConfig,
|
||||
LoggingConfig,
|
||||
RatelimitConfig,
|
||||
ContentRepositoryConfig,
|
||||
CaptchaConfig,
|
||||
VoipConfig,
|
||||
RegistrationConfig,
|
||||
MetricsConfig,
|
||||
ApiConfig,
|
||||
AppServiceConfig,
|
||||
KeyConfig,
|
||||
SAML2Config,
|
||||
CasConfig,
|
||||
JWTConfig,
|
||||
PasswordConfig,
|
||||
EmailConfig,
|
||||
WorkerConfig,
|
||||
PasswordAuthProviderConfig,
|
||||
PushConfig,
|
||||
SpamCheckerConfig,
|
||||
GroupsConfig,
|
||||
UserDirectoryConfig,
|
||||
ConsentConfig,
|
||||
StatsConfig,
|
||||
ServerNoticesConfig,
|
||||
RoomDirectoryConfig,
|
||||
ThirdPartyRulesConfig,
|
||||
TracerConfig,
|
||||
):
|
||||
pass
|
||||
class HomeServerConfig(RootConfig):
|
||||
|
||||
config_classes = [
|
||||
ServerConfig,
|
||||
TlsConfig,
|
||||
DatabaseConfig,
|
||||
LoggingConfig,
|
||||
RatelimitConfig,
|
||||
ContentRepositoryConfig,
|
||||
CaptchaConfig,
|
||||
VoipConfig,
|
||||
RegistrationConfig,
|
||||
MetricsConfig,
|
||||
ApiConfig,
|
||||
AppServiceConfig,
|
||||
KeyConfig,
|
||||
SAML2Config,
|
||||
CasConfig,
|
||||
JWTConfig,
|
||||
PasswordConfig,
|
||||
EmailConfig,
|
||||
WorkerConfig,
|
||||
PasswordAuthProviderConfig,
|
||||
PushConfig,
|
||||
SpamCheckerConfig,
|
||||
GroupsConfig,
|
||||
UserDirectoryConfig,
|
||||
ConsentConfig,
|
||||
StatsConfig,
|
||||
ServerNoticesConfig,
|
||||
RoomDirectoryConfig,
|
||||
ThirdPartyRulesConfig,
|
||||
TracerConfig,
|
||||
]
|
||||
|
|
|
@ -23,6 +23,8 @@ MISSING_JWT = """Missing jwt library. This is required for jwt login.
|
|||
|
||||
|
||||
class JWTConfig(Config):
|
||||
section = "jwt"
|
||||
|
||||
def read_config(self, config, **kwargs):
|
||||
jwt_config = config.get("jwt_config", None)
|
||||
if jwt_config:
|
||||
|
|
|
@ -92,6 +92,8 @@ class TrustedKeyServer(object):
|
|||
|
||||
|
||||
class KeyConfig(Config):
|
||||
section = "key"
|
||||
|
||||
def read_config(self, config, config_dir_path, **kwargs):
|
||||
# the signing key can be specified inline or in a separate file
|
||||
if "signing_key" in config:
|
||||
|
|
|
@ -84,6 +84,8 @@ root:
|
|||
|
||||
|
||||
class LoggingConfig(Config):
|
||||
section = "logging"
|
||||
|
||||
def read_config(self, config, **kwargs):
|
||||
self.log_config = self.abspath(config.get("log_config"))
|
||||
self.no_redirect_stdio = config.get("no_redirect_stdio", False)
|
||||
|
|
|
@ -34,6 +34,8 @@ class MetricsFlags(object):
|
|||
|
||||
|
||||
class MetricsConfig(Config):
|
||||
section = "metrics"
|
||||
|
||||
def read_config(self, config, **kwargs):
|
||||
self.enable_metrics = config.get("enable_metrics", False)
|
||||
self.report_stats = config.get("report_stats", None)
|
||||
|
|
|
@ -20,6 +20,8 @@ class PasswordConfig(Config):
|
|||
"""Password login configuration
|
||||
"""
|
||||
|
||||
section = "password"
|
||||
|
||||
def read_config(self, config, **kwargs):
|
||||
password_config = config.get("password_config", {})
|
||||
if password_config is None:
|
||||
|
|
|
@ -23,6 +23,8 @@ LDAP_PROVIDER = "ldap_auth_provider.LdapAuthProvider"
|
|||
|
||||
|
||||
class PasswordAuthProviderConfig(Config):
|
||||
section = "authproviders"
|
||||
|
||||
def read_config(self, config, **kwargs):
|
||||
self.password_providers = [] # type: List[Any]
|
||||
providers = []
|
||||
|
|
|
@ -18,6 +18,8 @@ from ._base import Config
|
|||
|
||||
|
||||
class PushConfig(Config):
|
||||
section = "push"
|
||||
|
||||
def read_config(self, config, **kwargs):
|
||||
push_config = config.get("push", {})
|
||||
self.push_include_content = push_config.get("include_content", True)
|
||||
|
|
|
@ -36,6 +36,8 @@ class FederationRateLimitConfig(object):
|
|||
|
||||
|
||||
class RatelimitConfig(Config):
|
||||
section = "ratelimiting"
|
||||
|
||||
def read_config(self, config, **kwargs):
|
||||
|
||||
# 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):
|
||||
section = "accountvalidity"
|
||||
|
||||
def __init__(self, config, synapse_config):
|
||||
self.enabled = config.get("enabled", False)
|
||||
self.renew_by_email_enabled = "renew_at" in config
|
||||
|
@ -77,6 +79,8 @@ class AccountValidityConfig(Config):
|
|||
|
||||
|
||||
class RegistrationConfig(Config):
|
||||
section = "registration"
|
||||
|
||||
def read_config(self, config, **kwargs):
|
||||
self.enable_registration = bool(
|
||||
strtobool(str(config.get("enable_registration", False)))
|
||||
|
|
|
@ -78,6 +78,8 @@ def parse_thumbnail_requirements(thumbnail_sizes):
|
|||
|
||||
|
||||
class ContentRepositoryConfig(Config):
|
||||
section = "media"
|
||||
|
||||
def read_config(self, config, **kwargs):
|
||||
|
||||
# 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):
|
||||
section = "roomdirectory"
|
||||
|
||||
def read_config(self, config, **kwargs):
|
||||
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):
|
||||
section = "saml2"
|
||||
|
||||
def read_config(self, config, **kwargs):
|
||||
self.saml2_enabled = False
|
||||
|
||||
|
|
|
@ -58,6 +58,8 @@ on how to configure the new listener.
|
|||
|
||||
|
||||
class ServerConfig(Config):
|
||||
section = "server"
|
||||
|
||||
def read_config(self, config, **kwargs):
|
||||
self.server_name = config["server_name"]
|
||||
self.server_context = config.get("server_context", None)
|
||||
|
|
|
@ -59,6 +59,8 @@ class ServerNoticesConfig(Config):
|
|||
None if server notices are not enabled.
|
||||
"""
|
||||
|
||||
section = "servernotices"
|
||||
|
||||
def __init__(self, *args):
|
||||
super(ServerNoticesConfig, self).__init__(*args)
|
||||
self.server_notices_mxid = None
|
||||
|
|
|
@ -19,6 +19,8 @@ from ._base import Config
|
|||
|
||||
|
||||
class SpamCheckerConfig(Config):
|
||||
section = "spamchecker"
|
||||
|
||||
def read_config(self, config, **kwargs):
|
||||
self.spam_checker = None
|
||||
|
||||
|
|
|
@ -25,6 +25,8 @@ class StatsConfig(Config):
|
|||
Configuration for the behaviour of synapse's stats engine
|
||||
"""
|
||||
|
||||
section = "stats"
|
||||
|
||||
def read_config(self, config, **kwargs):
|
||||
self.stats_enabled = True
|
||||
self.stats_bucket_size = 86400 * 1000
|
||||
|
|
|
@ -19,6 +19,8 @@ from ._base import Config
|
|||
|
||||
|
||||
class ThirdPartyRulesConfig(Config):
|
||||
section = "thirdpartyrules"
|
||||
|
||||
def read_config(self, config, **kwargs):
|
||||
self.third_party_event_rules = None
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import os
|
|||
import warnings
|
||||
from datetime import datetime
|
||||
from hashlib import sha256
|
||||
from typing import List
|
||||
|
||||
import six
|
||||
|
||||
|
@ -33,7 +34,9 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
|
||||
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)
|
||||
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_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:
|
||||
raise ConfigError(
|
||||
"tls_certificate_path must be specified if TLS-enabled listeners are "
|
||||
|
@ -108,7 +111,7 @@ class TlsConfig(Config):
|
|||
)
|
||||
|
||||
# Support globs (*) in whitelist values
|
||||
self.federation_certificate_verification_whitelist = []
|
||||
self.federation_certificate_verification_whitelist = [] # type: List[str]
|
||||
for entry in fed_whitelist_entries:
|
||||
try:
|
||||
entry_regex = glob_to_regex(entry.encode("ascii").decode("ascii"))
|
||||
|
|
|
@ -19,6 +19,8 @@ from ._base import Config, ConfigError
|
|||
|
||||
|
||||
class TracerConfig(Config):
|
||||
section = "tracing"
|
||||
|
||||
def read_config(self, config, **kwargs):
|
||||
opentracing_config = config.get("opentracing")
|
||||
if opentracing_config is None:
|
||||
|
|
|
@ -21,6 +21,8 @@ class UserDirectoryConfig(Config):
|
|||
Configuration for the behaviour of the /user_directory API
|
||||
"""
|
||||
|
||||
section = "userdirectory"
|
||||
|
||||
def read_config(self, config, **kwargs):
|
||||
self.user_directory_search_enabled = True
|
||||
self.user_directory_search_all_users = False
|
||||
|
|
|
@ -16,6 +16,8 @@ from ._base import Config
|
|||
|
||||
|
||||
class VoipConfig(Config):
|
||||
section = "voip"
|
||||
|
||||
def read_config(self, config, **kwargs):
|
||||
self.turn_uris = config.get("turn_uris", [])
|
||||
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
|
||||
replication_url to talk to the main synapse process."""
|
||||
|
||||
section = "worker"
|
||||
|
||||
def read_config(self, config, **kwargs):
|
||||
self.worker_app = config.get("worker_app")
|
||||
|
||||
|
|
|
@ -21,17 +21,24 @@ import yaml
|
|||
|
||||
from OpenSSL import SSL
|
||||
|
||||
from synapse.config._base import Config, RootConfig
|
||||
from synapse.config.tls import ConfigError, TlsConfig
|
||||
from synapse.crypto.context_factory import ClientTLSOptionsFactory
|
||||
|
||||
from tests.unittest import TestCase
|
||||
|
||||
|
||||
class TestConfig(TlsConfig):
|
||||
class FakeServer(Config):
|
||||
section = "server"
|
||||
|
||||
def has_tls_listener(self):
|
||||
return False
|
||||
|
||||
|
||||
class TestConfig(RootConfig):
|
||||
config_classes = [FakeServer, TlsConfig]
|
||||
|
||||
|
||||
class TLSConfigTests(TestCase):
|
||||
def test_warn_self_signed(self):
|
||||
"""
|
||||
|
@ -202,13 +209,13 @@ s4niecZKPBizL6aucT59CsunNmmb5Glq8rlAcU+1ZTZZzGYqVYhF6axB9Qg=
|
|||
conf = TestConfig()
|
||||
conf.read_config(
|
||||
yaml.safe_load(
|
||||
TestConfig().generate_config_section(
|
||||
TestConfig().generate_config(
|
||||
"/config_dir_path",
|
||||
"my_super_secure_server",
|
||||
"/data_dir_path",
|
||||
"/tls_cert_path",
|
||||
"tls_private_key",
|
||||
None, # This is the acme_domain
|
||||
tls_certificate_path="/tls_cert_path",
|
||||
tls_private_key_path="tls_private_key",
|
||||
acme_domain=None, # This is the acme_domain
|
||||
)
|
||||
),
|
||||
"/config_dir_path",
|
||||
|
@ -223,13 +230,13 @@ s4niecZKPBizL6aucT59CsunNmmb5Glq8rlAcU+1ZTZZzGYqVYhF6axB9Qg=
|
|||
conf = TestConfig()
|
||||
conf.read_config(
|
||||
yaml.safe_load(
|
||||
TestConfig().generate_config_section(
|
||||
TestConfig().generate_config(
|
||||
"/config_dir_path",
|
||||
"my_super_secure_server",
|
||||
"/data_dir_path",
|
||||
"/tls_cert_path",
|
||||
"tls_private_key",
|
||||
"my_supe_secure_server", # This is the acme_domain
|
||||
tls_certificate_path="/tls_cert_path",
|
||||
tls_private_key_path="tls_private_key",
|
||||
acme_domain="my_supe_secure_server", # This is the acme_domain
|
||||
)
|
||||
),
|
||||
"/config_dir_path",
|
||||
|
|
3
tox.ini
3
tox.ini
|
@ -163,10 +163,9 @@ deps =
|
|||
{[base]deps}
|
||||
mypy
|
||||
mypy-zope
|
||||
typeshed
|
||||
env =
|
||||
MYPYPATH = stubs/
|
||||
extras = all
|
||||
commands = mypy --show-traceback \
|
||||
commands = mypy --show-traceback --check-untyped-defs --show-error-codes --follow-imports=normal \
|
||||
synapse/logging/ \
|
||||
synapse/config/
|
||||
|
|
Loading…
Reference in a new issue