Variablen strukturieren | Synapse #2

Closed
fenris wants to merge 1 commit from task-406 into main
5 changed files with 585 additions and 197 deletions
Showing only changes of commit 7a4b463a3b - Show all commits

View file

@ -0,0 +1,343 @@
{
"nullable": false,
"type": "object",
"properties": {
"scheme": {
"nullable": false,
"type": "string",
"default": "https"
},
"domain": {
"nullable": false,
"type": "string"
},
"database": {
"anyOf": [
{
"nullable": false,
"type": "object",
"properties": {
"kind": {
"nullable": false,
"type": "string",
"enum": ["sqlite"],
"default": "sqlite"
},
"data": {
"nullable": false,
"type": "object",
"properties": {
"path": {
"nullable": false,
"type": "string",
"default": "/var/synapse/data.sqlite"
}
},
"additionalProperties": false,
"required": [
"path"
]
}
},
"additionalProperties": false,
"required": [
"kind",
"data"
]
},
{
"nullable": false,
"type": "object",
"properties": {
"kind": {
"nullable": false,
"type": "string",
"enum": ["postgresql"],
"default": "postgresql"
},
"data": {
"nullable": false,
"type": "object",
"properties": {
"host": {
"nullable": false,
"type": "string",
"default": "localhost"
},
"port": {
"nullable": false,
"type": "integer",
"default": 5432
},
"username": {
"nullable": false,
"type": "string",
"default": "synapse_user"
},
"password": {
"nullable": false,
"type": "string"
},
"schema": {
"nullable": false,
"type": "string",
"default": "synapse"
}
},
"additionalProperties": false,
"required": [
"host",
"port",
"username",
"password",
"schema"
]
}
},
"additionalProperties": false,
"required": [
"kind",
"data"
]
}
]
},
"element_url": {
"nullable": true,
"type": "string",
"default": null
},
"title": {
"nullable": false,
"type": "string",
"default": "Example | Matrix"
},
"federation": {
"nullable": false,
"type": "object",
"properties": {
"enable": {
"nullable": false,
"type": "boolean",
"default": false
},
"whitelist": {
"nullable": false,
"type": "array",
"items": {
"type": "string"
},
"default": []
}
},
"additionalProperties": false,
"required": [
"enable",
"whitelist"
]
},
"password_strict_policy": {
"nullable": false,
"type": "boolean",
"default": true
},
"registration_shared_secret": {
"nullable": true,
"type": "string",
"default": null
},
"authentication": {
"anyOf": [
{
"nullable": false,
"type": "object",
"properties": {
"kind": {
"nullable": false,
"type": "string",
"enum": ["internal"],
"default": "internal"
},
"data": {
"nullable": false,
"type": "object",
"properties": {
},
"additionalProperties": false,
"required": [
]
}
},
"additionalProperties": false,
"required": [
"kind",
"data"
]
},
{
"nullable": false,
"type": "object",
"properties": {
"kind": {
"nullable": false,
"type": "string",
"enum": ["authelia"],
"default": "authelia"
},
"data": {
"nullable": false,
"type": "object",
"properties": {
"provider_id": {
"nullable": false,
"type": "string",
"default": "authelia"
},
"provider_name": {
"nullable": false,
"type": "string",
"default": "Authelia"
},
"client_id": {
"nullable": false,
"type": "string",
"default": "synapse"
},
"client_secret": {
"nullable": false,
"type": "string"
},
"url_base": {
"nullable": false,
"type": "string"
}
},
"additionalProperties": false,
"required": [
"provider_id",
"provider_name",
"client_id",
"client_secret",
"url_base"
]
}
},
"additionalProperties": false,
"required": [
"kind",
"data"
]
}
]
},
"smtp": {
"nullable": true,
"type": "object",
"properties": {
"host": {
"nullable": false,
"type": "string"
},
"port": {
"nullable": false,
"type": "integer",
"default": 587
},
"username": {
"nullable": false,
"type": "string",
"default": "synapse"
},
"password": {
"nullable": false,
"type": "string"
}
},
"additionalProperties": false,
"required": [
"host",
"port",
"username",
"password"
]
},
"notifications": {
"nullable": false,
"type": "object",
"properties": {
"source_address": {
"nullable": false,
"type": "string",
"default": "synapse@example.org"
},
"via_email": {
"nullable": false,
"type": "object",
"properties": {
"enabled_by_default": {
"nullable": false,
"type": "boolean",
"default": false
},
"delay": {
"nullable": false,
"type": "string",
"default": "1h"
}
},
"additionalProperties": false,
"required": [
"enabled_by_default",
"delay"
]
}
},
"additionalProperties": false,
"required": [
"source_address",
"via_email"
]
},
"admin_user": {
"nullable": false,
"type": "object",
"properties": {
"define": {
"nullable": false,
"type": "boolean",
"default": true
},
"name": {
"nullable": false,
"type": "string",
"default": "admin"
},
"password": {
"nullable": false,
"type": "string"
}
},
"additionalProperties": false,
"required": [
"define",
"name",
"password"
]
}
},
"additionalProperties": false,
"required": [
"scheme",
"domain",
"database",
"element_url",
"title",
"federation",
"password_strict_policy",
"registration_shared_secret",
"authentication",
"smtp",
"notification",
"admin_user"
]
}

View file

@ -1,34 +1,38 @@
{
"var_synapse_scheme": "https",
"var_synapse_domain": "synapse.example.org",
"var_synapse_database_kind": "sqlite",
"var_synapse_database_data_sqlite_path": "/var/synapse/data.sqlite",
"var_synapse_database_data_postgresql_host": "localhost",
"var_synapse_database_data_postgresql_port": 5432,
"var_synapse_database_data_postgresql_username": "synapse_user",
"var_synapse_database_data_postgresql_password": "REPLACE_ME",
"var_synapse_database_data_postgresql_schema": "synapse",
"var_synapse_element_url": "https://element.example.org",
"var_synapse_title": "Example | Matrix",
"var_synapse_federation_enable": true,
"var_synapse_federation_whitelist": [],
"var_synapse_password_strict_policy": true,
"var_synapse_registration_shared_secret": "REPLACE_ME",
"var_synapse_authentication_kind": "internal",
"var_synapse_authentication_data_authelia_provider_id": "authelia",
"var_synapse_authentication_data_authelia_provider_name": "Authelia",
"var_synapse_authentication_data_authelia_client_id": "synapse",
"var_synapse_authentication_data_authelia_client_secret": "REPLACE_ME",
"var_synapse_authentication_data_authelia_url_base": "https://authelia.example.org",
"var_synapse_smtp_host": "smtp.example.org",
"var_synapse_smtp_port": 587,
"var_synapse_smtp_username": "synapse@smtp.example.org",
"var_synapse_smtp_password": "REPLACE_ME",
"var_synapse_notifications_source_address": "synapse@example.org",
"var_synapse_notifications_via_email_enabled_by_default": false,
"var_synapse_notifications_via_email_delay": "1h",
"var_synapse_admin_user_define": true,
"var_synapse_admin_user_name": "admin",
"var_synapse_admin_user_password": "REPLACE_ME"
"cfg_synapse_defaults": {
"scheme": "https",
"database": {
"kind": "sqlite",
"data": {
"path": "/var/synapse/data.sqlite"
}
},
"element_url": null,
"title": "Example | Matrix",
"federation": {
"enable": false,
"whitelist": []
},
"password_strict_policy": true,
"registration_shared_secret": null,
"authentication": {
"kind": "internal",
"data": {}
},
"smtp": {
"port": 587,
"username": "synapse"
},
"notifications": {
"source_address": "synapse@example.org",
"via_email": {
"enabled_by_default": false,
"delay": "1h"
}
},
"admin_user": {
"define": true,
"name": "admin"
}
}
}

View file

@ -1,19 +1,19 @@
{% if var_synapse_database_kind == 'sqlite' %}
{% if cfg_synapse.database.kind == 'sqlite' %}
database:
name: sqlite3
args:
database: {{var_synapse_database_sqlite_path}}
database: {{var.synapse.database.data.path}}
{% endif %}
{% if var_synapse_database_kind == 'postgresql' %}
{% if cfg_synapse.database.kind == 'postgresql' %}
database:
name: psycopg2
args:
host: {{var_synapse_database_data_postgresql_host}}
port: {{var_synapse_database_data_postgresql_port | string}}
database: "{{var_synapse_database_data_postgresql_schema}}"
user: "{{var_synapse_database_data_postgresql_username}}"
password: "{{var_synapse_database_data_postgresql_password}}"
host: {{cfg_synapse.database.data.host}}
port: {{cfg_synapse.database.data.port | string}}
database: "{{cfg_synapse.database.data.schema}}"
user: "{{cfg_synapse.database.data.username}}"
password: "{{cfg_synapse.database.data.password}}"
cp_min: 5
cp_max: 10
{% endif %}
@ -26,9 +26,11 @@ pid_file: "/var/run/matrix-synapse.pid"
soft_file_limit: 0
web_client_location: {{var_synapse_element_url}}
{% if cfg_synapse.element_url != None %}
web_client_location: {{cfg_synapse.element_url}}
{% endif %}
public_baseurl: {{var_synapse_scheme}}://{{var_synapse_domain}}/
public_baseurl: {{var_synapse.scheme}}://{{var_synapse.domain}}/
listeners:
- port: 8008
@ -42,12 +44,12 @@ listeners:
resources:
- names: [client]
compress: true
{% if var_synapse_federation_enable %}
{% if cfg_synapse.federation.enable %}
- names: [federation]
compress: false
{% endif %}
federation_domain_whitelist: {{var_synapse_federation_whitelist | to_yaml}}
federation_domain_whitelist: {{cfg_synapse.federation.whitelist | to_yaml}}
serve_server_wellknown: true
@ -89,8 +91,8 @@ max_spider_size: "10M"
enable_registration_captcha: false
recaptcha_siteverify_api: "https://www.google.com/recaptcha/api/siteverify"
{% if var_synapse_registration_shared_secret != None %}
registration_shared_secret: "{{var_synapse_registration_shared_secret}}"
{% if cfg_synapse.registration.shared_secret != None %}
registration_shared_secret: "{{cfg_synapse.registration_shared_secret}}"
{% endif %}
oidc_config:
@ -99,23 +101,23 @@ oidc_config:
# NOT an Ansible variable
localpart_template: "{{"{{"}} user.preferred_username {{"}}"}}"
{% if var_synapse_authentication_kind == 'internal' %}
{% if var_synapse.authentication.kind == 'internal' %}
enable_registration: true
enable_registration_without_verification: true
{% endif %}
{% if var_synapse_authentication_kind == 'authelia' %}
{% if var_synapse.authentication.kind == 'authelia' %}
enable_registration: false
enable_registration_without_verification: false
oidc_providers:
- idp_id: "{{var_synapse_authentication_data_authelia_provider_id}}"
idp_name: "{{var_synapse_authentication_data_authelia_provider_name}}"
- idp_id: "{{cfg_synapse.authentication.data.provider_id}}"
idp_name: "{{cfg_synapse.authentication.data.provider_name}}"
idp_icon: "mxc://authelia.com/cKlrTPsGvlpKxAYeHWJsdVHI"
discover: true
issuer: "{{var_synapse_authentication_data_authelia_url_base}}"
client_id: "{{var_synapse_authentication_data_authelia_client_id}}"
client_secret: "{{var_synapse_authentication_data_authelia_client_secret}}"
issuer: "{{cfg_synapse.authentication.data.url_base}}"
client_id: "{{cfg_synapse.authentication.data.client_id}}"
client_secret: "{{cfg_synapse.authentication.data.client_secret}}"
scopes: ["openid", "profile", "email"]
allow_existing_users: true
user_mapping_provider:
@ -165,19 +167,21 @@ password_config:
policy:
enabled: {{var_synapse_password_strict_policy | to_yaml}}
{% if cfg_synapse.smtp != None %}
email:
smtp_host: "{{var_synapse_smtp_host}}"
smtp_port: {{var_synapse_smtp_port | to_yaml}}
smtp_user: "{{var_synapse_smtp_username}}"
smtp_pass: "{{var_synapse_smtp_password}}"
smtp_host: "{{cfg_synapse.smtp.host}}"
smtp_port: {{cfg_synapse.smtp.port | to_yaml}}
smtp_user: "{{cfg_synapse.smtp.username}}"
smtp_pass: "{{cfg_synapse.smtp.password}}"
require_transport_security: true
notif_from: "%(app)s | {{var_synapse_title}} <{{var_synapse_notifications_source_address}}>"
notif_from: "%(app)s | {{var_synapse_title}} <{{cfg_synapse.notifications.source_address}}>"
enable_notifs: true
notif_for_new_users: {{var_synapse_notifications_via_email_enabled_by_default | to_yaml}}
notif_delay_before_mail: {{var_synapse_notifications_via_email_delay}}
notif_for_new_users: {{cfg_synapse.notifications.via_email.enabled_by_default | to_yaml}}
notif_delay_before_mail: {{cfg_synapse.notifications.via_email.delay}}
subjects:
password_reset: "[%(server_name)s] Passwort zurücksetzen"
email_validation: "[%(server_name)s] Nutzer-Konto-Freischaltung"
{% endif %}
spam_checker:

View file

@ -1,137 +0,0 @@
{
"scheme": {
"type": "string",
"mandatory": false
},
"domain": {
"type": "string",
"mandatory": false
},
"database_kind": {
"type": "string",
"mandatory": false,
"options": [
"sqlite",
"postgresql"
]
},
"database_data_sqlite_path": {
"type": "string",
"mandatory": false
},
"database_data_postgresql_host": {
"type": "string",
"mandatory": false
},
"database_data_postgresql_port": {
"type": "integer",
"mandatory": false
},
"database_data_postgresql_username": {
"type": "string",
"mandatory": false
},
"database_data_postgresql_password": {
"type": "string",
"mandatory": true
},
"database_data_postgresql_schema": {
"type": "string",
"mandatory": false
},
"element_url": {
"type": "string",
"mandatory": false
},
"title": {
"type": "string",
"mandatory": false
},
"federation_enable": {
"type": "boolean",
"mandatory": false
},
"federation_whitelist": {
"type": "array",
"items": {
"type": "string"
},
"mandatory": false
},
"password_strict_policy": {
"type": "boolean",
"mandatory": false
},
"registration_shared_secret": {
"type": "string",
"mandatory": true
},
"authentication_kind": {
"type": "string",
"mandatory": false,
"options": [
"internal",
"authelia"
]
},
"authentication_data_authelia_provider_id": {
"type": "string",
"mandatory": false
},
"authentication_data_authelia_provider_name": {
"type": "string",
"mandatory": false
},
"authentication_data_authelia_client_id": {
"type": "string",
"mandatory": false
},
"authentication_data_authelia_client_secret": {
"type": "string",
"mandatory": false
},
"authentication_data_authelia_url_base": {
"type": "string",
"mandatory": false
},
"smtp_host": {
"type": "string",
"mandatory": false
},
"smtp_port": {
"type": "integer",
"mandatory": false
},
"smtp_username": {
"type": "string",
"mandatory": false
},
"smtp_password": {
"type": "string",
"mandatory": true
},
"notifications_source_address": {
"type": "string",
"mandatory": false
},
"notifications_via_email_enabled_by_default": {
"type": "boolean",
"mandatory": false
},
"notifications_via_email_delay": {
"type": "string",
"mandatory": false
},
"admin_user_define": {
"type": "boolean",
"mandatory": false
},
"admin_user_name": {
"type": "string",
"mandatory": false
},
"admin_user_password": {
"type": "string",
"mandatory": false
}
}

View file

@ -127,6 +127,172 @@ def generate_overrides(
)
def investigate(
schema,
value
):
flaws = []
if ("anyOf" in schema):
found = False
entries = []
for sub_schema in schema["anyOf"]:
sub_flaws = investigate(sub_schema, value)
if (len(sub_flaws) <= 0):
found = True
break
else:
entries.append({"schema": sub_schema, "flaws": sub_flaws})
if found:
pass
else:
flaws.append({"incident": "not_valid_against_any_sub_schema", "details": {"value": value, "results": entries}})
else:
if (value is None):
if (not schema.get("nullable", False)):
flaws.append({"incident": "not_nullable", "details": {"value": str(type(value))}})
else:
if (not ("type" in schema)):
raise ValueError("unhandled: %s" % _json.dumps(schema))
else:
if (schema["type"] == "boolean"):
if (not (type(value) == bool)):
flaws.append({"incident": "wrong_type", "details": {"shall": str(bool), "is": str(type(value))}})
else:
if (("enum" in schema) and not (value in schema["enum"])):
flaws.append({"incident": "not_in_enum", "details": {"enum": schema["enum"], "value": value}})
elif (schema["type"] == "integer"):
if (not (type(value) == int)):
flaws.append({"incident": "wrong_type", "details": {"shall": str(int), "is": str(type(value))}})
else:
if (("enum" in schema) and not (value in schema["enum"])):
flaws.append({"incident": "not_in_enum", "details": {"enum": schema["enum"], "value": value}})
# todo: min,max,step,etc.
elif (schema["type"] == "number"):
if (not (type(value) == int) and not (type(value) == float)):
flaws.append({"incident": "wrong_type", "details": {"shall": str(float), "is": str(type(value))}})
else:
if (("enum" in schema) and not (value in schema["enum"])):
flaws.append({"incident": "not_in_enum", "details": {"enum": schema["enum"], "value": value}})
# todo: min,max,step,etc.
elif (schema["type"] == "string"):
if (not (type(value) == str)):
flaws.append({"incident": "wrong_type", "details": {"shall": str(str), "is": str(type(value))}})
else:
if (("enum" in schema) and not (value in schema["enum"])):
flaws.append({"incident": "not_in_enum", "details": {"enum": schema["enum"], "value": value}})
# todo: min,max,pattern,etc.
elif (schema["type"] == "object"):
if (not (type(value) == dict)):
flaws.append({"incident": "wrong_type", "details": {"shall": str(dict), "is": str(type(value))}})
else:
entries = []
for (property_key, property_value, ) in schema["properties"].items():
if (not (property_key in value)):
if (not (property_key in schema.get("required", []))):
pass
else:
flaws.append({"incident": "mandatory_field_missing", "details": {"key": property_key}})
else:
sub_flaws = investigate(property_value, value[property_key])
if (len(sub_flaws) <= 0):
pass
else:
entries.append({"key": property_key, "result": sub_flaws})
if (len(entries) <= 0):
pass
else:
flaws.append({"incident": "field_flaws", "details": entries})
# todo: additionalProperties
# todo: required
else:
raise ValueError("unhandled: %s" % _json.dumps(schema))
return flaws
def validate(
schema,
value
):
investigation = investigate(schema, value)
if False:
_sys.stderr.write(
_json.dumps(
investigation,
indent = "\t"
)
+
"\n"
)
return (
len(investigation)
<=
0
)
def reduce(
schema,
value
):
if False:
_sys.stderr.write(
_json.dumps(
{
"schema": schema,
"value": value,
},
indent = "\t"
)
+
"\n"
)
if ("anyOf" in schema):
for sub_schema in schema["anyOf"]:
if validate(sub_schema, value):
return reduce(sub_schema, value)
else:
pass
raise ValueError("not valid against any sub schema")
else:
if (not ("default" in schema)):
return class_option_filled(value)
else:
if (
(schema["type"] == "boolean")
or
(schema["type"] == "integer")
or
(schema["type"] == "number")
or
(schema["type"] == "string")
):
if (value == schema["default"]):
return class_option_empty()
else:
return class_option_filled(value)
elif (schema["type"] == "array"):
if (value == schema["default"]):
return class_option_empty()
else:
return class_option_filled(value)
elif (schema["type"] == "object"):
if (not (type(value) == dict)):
raise ValueError("dict expected")
else:
value_out = {}
for (property_key, property_value, ) in schema["properties"].items():
sub_result = reduce(property_value, value[property_key])
if (not sub_result.is_filled()):
pass
else:
value_out[property_key] = sub_result.cull()
return class_option_filled(value_out)
# todo: additionalProperties
# toto: required
else:
raise ValueError("unhandled: %s" % _json.dumps(schema))
def role_name_derive(
role_name
):
@ -143,6 +309,7 @@ def main(
choices = [
"defaults",
"overrides",
"reduce",
],
metavar = "<action>",
)
@ -172,6 +339,13 @@ def main(
key = ("cfg_%s_overrides" % (role_name_derive(args.role)))
result = {key: (raw.cull() if raw.is_filled() else {})}
_sys.stdout.write(_json.dumps(result, indent = "\t") + "\n")
elif args.action == 'reduce':
cfg = _json.loads(_sys.stdin.read())
result = reduce(cfg_schema, cfg)
if (not result.is_filled()):
print("?")
else:
_sys.stdout.write(_json.dumps(result.cull(), indent = "\t") + "\n")
else:
raise ValueError("invalid action: %s" % args.action)