mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-08-15 08:30:46 +02:00
Restructured server config
This commit is contained in:
parent
99c6c9ec92
commit
9228d51e86
@ -39,7 +39,7 @@ class FilterModule(object):
|
|||||||
# 1) Precompute canonical domains per app (fallback to default)
|
# 1) Precompute canonical domains per app (fallback to default)
|
||||||
canonical_map = {}
|
canonical_map = {}
|
||||||
for app_id, cfg in apps.items():
|
for app_id, cfg in apps.items():
|
||||||
domains_cfg = cfg.get('domains') or {}
|
domains_cfg = cfg.get('server',{}).get('domains',{})
|
||||||
entry = domains_cfg.get('canonical')
|
entry = domains_cfg.get('canonical')
|
||||||
if entry is None:
|
if entry is None:
|
||||||
canonical_map[app_id] = [default_domain(app_id, primary_domain)]
|
canonical_map[app_id] = [default_domain(app_id, primary_domain)]
|
||||||
@ -49,13 +49,13 @@ class FilterModule(object):
|
|||||||
canonical_map[app_id] = list(entry)
|
canonical_map[app_id] = list(entry)
|
||||||
else:
|
else:
|
||||||
raise AnsibleFilterError(
|
raise AnsibleFilterError(
|
||||||
f"Unexpected type for 'domains.canonical' in application '{app_id}': {type(entry).__name__}"
|
f"Unexpected type for 'server.domains.canonical' in application '{app_id}': {type(entry).__name__}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 2) Build alias list per app
|
# 2) Build alias list per app
|
||||||
result = {}
|
result = {}
|
||||||
for app_id, cfg in apps.items():
|
for app_id, cfg in apps.items():
|
||||||
domains_cfg = cfg.get('domains')
|
domains_cfg = cfg.get('server',{}).get('domains')
|
||||||
|
|
||||||
# no domains key → no aliases
|
# no domains key → no aliases
|
||||||
if domains_cfg is None:
|
if domains_cfg is None:
|
||||||
|
@ -28,7 +28,7 @@ class FilterModule(object):
|
|||||||
f"expected a dict, got {cfg!r}"
|
f"expected a dict, got {cfg!r}"
|
||||||
)
|
)
|
||||||
|
|
||||||
domains_cfg = cfg.get('domains')
|
domains_cfg = cfg.get('server',{}).get('domains',{})
|
||||||
if not domains_cfg or 'canonical' not in domains_cfg:
|
if not domains_cfg or 'canonical' not in domains_cfg:
|
||||||
self._add_default_domain(app_id, primary_domain, seen_domains, result)
|
self._add_default_domain(app_id, primary_domain, seen_domains, result)
|
||||||
continue
|
continue
|
||||||
@ -64,7 +64,7 @@ class FilterModule(object):
|
|||||||
self._process_canonical_domains_list(app_id, canonical_domains, seen_domains, result)
|
self._process_canonical_domains_list(app_id, canonical_domains, seen_domains, result)
|
||||||
else:
|
else:
|
||||||
raise AnsibleFilterError(
|
raise AnsibleFilterError(
|
||||||
f"Unexpected type for 'domains.canonical' in application '{app_id}': "
|
f"Unexpected type for 'server.domains.canonical' in application '{app_id}': "
|
||||||
f"{type(canonical_domains).__name__}"
|
f"{type(canonical_domains).__name__}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ class FilterModule(object):
|
|||||||
# 1) Compute canonical domains per app (always as a list)
|
# 1) Compute canonical domains per app (always as a list)
|
||||||
canonical_map = {}
|
canonical_map = {}
|
||||||
for app_id, cfg in apps.items():
|
for app_id, cfg in apps.items():
|
||||||
domains_cfg = cfg.get('domains') or {}
|
domains_cfg = cfg.get('server',{}).get('domains',{})
|
||||||
entry = domains_cfg.get('canonical')
|
entry = domains_cfg.get('canonical')
|
||||||
if entry is None:
|
if entry is None:
|
||||||
canonical_map[app_id] = [default_domain(app_id, primary_domain)]
|
canonical_map[app_id] = [default_domain(app_id, primary_domain)]
|
||||||
@ -46,13 +46,13 @@ class FilterModule(object):
|
|||||||
canonical_map[app_id] = list(entry)
|
canonical_map[app_id] = list(entry)
|
||||||
else:
|
else:
|
||||||
raise AnsibleFilterError(
|
raise AnsibleFilterError(
|
||||||
f"Unexpected type for 'domains.canonical' in application '{app_id}': {type(entry).__name__}"
|
f"Unexpected type for 'server.domains.canonical' in application '{app_id}': {type(entry).__name__}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 2) Compute alias domains per app
|
# 2) Compute alias domains per app
|
||||||
alias_map = {}
|
alias_map = {}
|
||||||
for app_id, cfg in apps.items():
|
for app_id, cfg in apps.items():
|
||||||
domains_cfg = cfg.get('domains')
|
domains_cfg = cfg.get('server',{}).get('domains',{})
|
||||||
if domains_cfg is None:
|
if domains_cfg is None:
|
||||||
alias_map[app_id] = []
|
alias_map[app_id] = []
|
||||||
continue
|
continue
|
||||||
|
@ -8,7 +8,8 @@ features:
|
|||||||
port-ui-desktop: true
|
port-ui-desktop: true
|
||||||
central_database: true
|
central_database: true
|
||||||
logout: true
|
logout: true
|
||||||
domains:
|
server:
|
||||||
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "accounting.{{ primary_domain }}"
|
- "accounting.{{ primary_domain }}"
|
||||||
docker:
|
docker:
|
||||||
|
@ -13,6 +13,7 @@ docker:
|
|||||||
enabled: true
|
enabled: true
|
||||||
database:
|
database:
|
||||||
enabled: true
|
enabled: true
|
||||||
domains:
|
server:
|
||||||
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "tickets.{{ primary_domain }}"
|
- "tickets.{{ primary_domain }}"
|
||||||
|
@ -18,7 +18,7 @@ docker:
|
|||||||
name: "baserow"
|
name: "baserow"
|
||||||
volumes:
|
volumes:
|
||||||
data: "baserow_data"
|
data: "baserow_data"
|
||||||
|
server:
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- baserow.{{ primary_domain }}
|
- baserow.{{ primary_domain }}
|
||||||
|
@ -12,13 +12,14 @@ features:
|
|||||||
oidc: true
|
oidc: true
|
||||||
central_database: false
|
central_database: false
|
||||||
logout: true
|
logout: true
|
||||||
domains:
|
server:
|
||||||
canonical:
|
csp:
|
||||||
- "meet.{{ primary_domain }}"
|
|
||||||
csp:
|
|
||||||
flags:
|
flags:
|
||||||
script-src-elem:
|
script-src-elem:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
style-src:
|
style-src:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
|
domains:
|
||||||
|
canonical:
|
||||||
|
- "meet.{{ primary_domain }}"
|
||||||
credentials: {}
|
credentials: {}
|
||||||
|
@ -8,7 +8,8 @@ features:
|
|||||||
port-ui-desktop: true
|
port-ui-desktop: true
|
||||||
central_database: true
|
central_database: true
|
||||||
logout: true
|
logout: true
|
||||||
domains:
|
server:
|
||||||
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
web: "bskyweb.{{ primary_domain }}"
|
web: "bskyweb.{{ primary_domain }}"
|
||||||
api: "bluesky.{{ primary_domain }}"
|
api: "bluesky.{{ primary_domain }}"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
domains:
|
server:
|
||||||
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "collabora.{{ primary_domain }}"
|
- "collabora.{{ primary_domain }}"
|
||||||
docker:
|
docker:
|
||||||
|
@ -7,7 +7,8 @@ features:
|
|||||||
central_database: true
|
central_database: true
|
||||||
ldap: false # @todo implement and activate
|
ldap: false # @todo implement and activate
|
||||||
logout: true
|
logout: true
|
||||||
csp:
|
server:
|
||||||
|
csp:
|
||||||
flags:
|
flags:
|
||||||
style-src:
|
style-src:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
@ -16,7 +17,7 @@ csp:
|
|||||||
whitelist:
|
whitelist:
|
||||||
font-src:
|
font-src:
|
||||||
- "http://*.{{primary_domain}}"
|
- "http://*.{{primary_domain}}"
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "forum.{{ primary_domain }}"
|
- "forum.{{ primary_domain }}"
|
||||||
docker:
|
docker:
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
features:
|
features:
|
||||||
logout: false # Just deactivated to oppress warnings, elk is anyhow not running
|
logout: false # Just deactivated to oppress warnings, elk is anyhow not running
|
||||||
|
server:
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- elk.{{ primary_domain }}
|
- elk.{{ primary_domain }}
|
||||||
|
@ -6,7 +6,8 @@ features:
|
|||||||
oidc: true
|
oidc: true
|
||||||
central_database: true
|
central_database: true
|
||||||
logout: true
|
logout: true
|
||||||
csp:
|
server:
|
||||||
|
csp:
|
||||||
flags:
|
flags:
|
||||||
script-src-elem:
|
script-src-elem:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
@ -21,7 +22,7 @@ csp:
|
|||||||
- "data:"
|
- "data:"
|
||||||
frame-src:
|
frame-src:
|
||||||
- https://s.espocrm.com/
|
- https://s.espocrm.com/
|
||||||
domains:
|
domains:
|
||||||
aliases:
|
aliases:
|
||||||
- "crm.{{ primary_domain }}"
|
- "crm.{{ primary_domain }}"
|
||||||
canonical:
|
canonical:
|
||||||
|
@ -9,10 +9,11 @@ features:
|
|||||||
ldap: true
|
ldap: true
|
||||||
oauth2: false # No special login side which could be protected, use 2FA of Friendica instead
|
oauth2: false # No special login side which could be protected, use 2FA of Friendica instead
|
||||||
logout: true
|
logout: true
|
||||||
domains:
|
server:
|
||||||
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "social.{{ primary_domain }}"
|
- "social.{{ primary_domain }}"
|
||||||
csp:
|
csp:
|
||||||
flags:
|
flags:
|
||||||
script-src-elem:
|
script-src-elem:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
|
@ -20,13 +20,14 @@ features:
|
|||||||
central_database: true
|
central_database: true
|
||||||
oauth2: false # Doesn't make sense to activate it atm, because login is possible on homepage
|
oauth2: false # Doesn't make sense to activate it atm, because login is possible on homepage
|
||||||
logout: true
|
logout: true
|
||||||
domains:
|
server:
|
||||||
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "audio.{{ primary_domain }}"
|
- "audio.{{ primary_domain }}"
|
||||||
aliases:
|
aliases:
|
||||||
- "music.{{ primary_domain }}"
|
- "music.{{ primary_domain }}"
|
||||||
- "sound.{{ primary_domain }}"
|
- "sound.{{ primary_domain }}"
|
||||||
csp:
|
csp:
|
||||||
flags:
|
flags:
|
||||||
style-src:
|
style-src:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
|
@ -19,7 +19,8 @@ oauth2_proxy:
|
|||||||
acl:
|
acl:
|
||||||
blacklist:
|
blacklist:
|
||||||
- "/user/login"
|
- "/user/login"
|
||||||
csp:
|
server:
|
||||||
|
csp:
|
||||||
flags:
|
flags:
|
||||||
script-src-elem:
|
script-src-elem:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
@ -33,7 +34,7 @@ csp:
|
|||||||
- "blob:"
|
- "blob:"
|
||||||
manifest-src:
|
manifest-src:
|
||||||
- "data:"
|
- "data:"
|
||||||
domains:
|
domains:
|
||||||
aliases:
|
aliases:
|
||||||
- "git.{{ primary_domain }}"
|
- "git.{{ primary_domain }}"
|
||||||
canonical:
|
canonical:
|
||||||
|
@ -15,7 +15,7 @@ docker:
|
|||||||
version: "latest"
|
version: "latest"
|
||||||
credentials:
|
credentials:
|
||||||
initial_root_password: "{{ users.administrator.password }}"
|
initial_root_password: "{{ users.administrator.password }}"
|
||||||
|
server:
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- gitlab.{{ primary_domain }}
|
- gitlab.{{ primary_domain }}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
features:
|
features:
|
||||||
logout: true # Same like with elk, anyhow not active atm
|
logout: true # Same like with elk, anyhow not active atm
|
||||||
|
server:
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- jenkins.{{ primary_domain }}
|
- jenkins.{{ primary_domain }}
|
||||||
|
@ -6,7 +6,8 @@ features:
|
|||||||
port-ui-desktop: true
|
port-ui-desktop: true
|
||||||
central_database: true
|
central_database: true
|
||||||
logout: true
|
logout: true
|
||||||
domains:
|
server:
|
||||||
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "cms.{{ primary_domain }}"
|
- "cms.{{ primary_domain }}"
|
||||||
docker:
|
docker:
|
||||||
|
@ -7,7 +7,8 @@ features:
|
|||||||
central_database: true
|
central_database: true
|
||||||
recaptcha: true
|
recaptcha: true
|
||||||
logout: true
|
logout: true
|
||||||
csp:
|
server:
|
||||||
|
csp:
|
||||||
flags:
|
flags:
|
||||||
script-src-elem:
|
script-src-elem:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
@ -18,7 +19,7 @@ csp:
|
|||||||
whitelist:
|
whitelist:
|
||||||
frame-src:
|
frame-src:
|
||||||
- "*" # For frontend channel logout it's necessary that iframes can be loaded
|
- "*" # For frontend channel logout it's necessary that iframes can be loaded
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "auth.{{ primary_domain }}"
|
- "auth.{{ primary_domain }}"
|
||||||
scopes:
|
scopes:
|
||||||
|
@ -13,7 +13,8 @@ features:
|
|||||||
central_database: false
|
central_database: false
|
||||||
oauth2: true
|
oauth2: true
|
||||||
logout: true
|
logout: true
|
||||||
csp:
|
server:
|
||||||
|
csp:
|
||||||
flags:
|
flags:
|
||||||
style-src:
|
style-src:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
@ -22,7 +23,7 @@ csp:
|
|||||||
unsafe-eval: true
|
unsafe-eval: true
|
||||||
script-src:
|
script-src:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
domains:
|
domains:
|
||||||
aliases:
|
aliases:
|
||||||
- "ldap.{{primary_domain}}"
|
- "ldap.{{primary_domain}}"
|
||||||
canonical:
|
canonical:
|
||||||
|
@ -18,10 +18,11 @@ features:
|
|||||||
oauth2: false # Enable the OAuth2-Proy
|
oauth2: false # Enable the OAuth2-Proy
|
||||||
javascript: false # Enables the custom JS in the javascript.js.j2 file
|
javascript: false # Enables the custom JS in the javascript.js.j2 file
|
||||||
logout: false # With this app I assume that it's a service, so should be renamed and logging is unneccessary
|
logout: false # With this app I assume that it's a service, so should be renamed and logging is unneccessary
|
||||||
csp:
|
server:
|
||||||
|
csp:
|
||||||
whitelist: {} # URL's which should be whitelisted
|
whitelist: {} # URL's which should be whitelisted
|
||||||
flags: {} # Flags which should be set
|
flags: {} # Flags which should be set
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "libretranslate.{{ primary_domain }}"
|
- "libretranslate.{{ primary_domain }}"
|
||||||
aliases: [] # Alias redirections to the first element of the canonical domains
|
aliases: [] # Alias redirections to the first element of the canonical domains
|
||||||
|
@ -6,7 +6,8 @@ features:
|
|||||||
central_database: true
|
central_database: true
|
||||||
oidc: true
|
oidc: true
|
||||||
logout: true
|
logout: true
|
||||||
domains:
|
server:
|
||||||
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "newsletter.{{ primary_domain }}"
|
- "newsletter.{{ primary_domain }}"
|
||||||
docker:
|
docker:
|
||||||
|
@ -9,10 +9,11 @@ features:
|
|||||||
oidc: true
|
oidc: true
|
||||||
central_database: false # Deactivate central database for mailu, I don't know why the database deactivation is necessary
|
central_database: false # Deactivate central database for mailu, I don't know why the database deactivation is necessary
|
||||||
logout: true
|
logout: true
|
||||||
domains:
|
server:
|
||||||
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "mail.{{ primary_domain }}"
|
- "mail.{{ primary_domain }}"
|
||||||
csp:
|
csp:
|
||||||
flags:
|
flags:
|
||||||
style-src:
|
style-src:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
|
@ -7,10 +7,11 @@ features:
|
|||||||
oidc: true
|
oidc: true
|
||||||
central_database: true
|
central_database: true
|
||||||
logout: true
|
logout: true
|
||||||
domains:
|
server:
|
||||||
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "microblog.{{ primary_domain }}"
|
- "microblog.{{ primary_domain }}"
|
||||||
csp:
|
csp:
|
||||||
whitelist:
|
whitelist:
|
||||||
frame-src:
|
frame-src:
|
||||||
- "*"
|
- "*"
|
||||||
|
@ -9,7 +9,8 @@ features:
|
|||||||
central_database: true
|
central_database: true
|
||||||
oauth2: false
|
oauth2: false
|
||||||
logout: true
|
logout: true
|
||||||
csp:
|
server:
|
||||||
|
csp:
|
||||||
whitelist:
|
whitelist:
|
||||||
script-src-elem:
|
script-src-elem:
|
||||||
- https://cdn.matomo.cloud
|
- https://cdn.matomo.cloud
|
||||||
@ -24,7 +25,7 @@ csp:
|
|||||||
style-src:
|
style-src:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
unsafe-eval: true
|
unsafe-eval: true
|
||||||
domains:
|
domains:
|
||||||
aliases:
|
aliases:
|
||||||
- "analytics.{{ primary_domain }}"
|
- "analytics.{{ primary_domain }}"
|
||||||
canonical:
|
canonical:
|
||||||
|
@ -24,7 +24,8 @@ features:
|
|||||||
oidc: true # Deactivated OIDC due to this issue https://github.com/matrix-org/synapse/issues/10492
|
oidc: true # Deactivated OIDC due to this issue https://github.com/matrix-org/synapse/issues/10492
|
||||||
central_database: true
|
central_database: true
|
||||||
logout: true
|
logout: true
|
||||||
csp:
|
server:
|
||||||
|
csp:
|
||||||
flags:
|
flags:
|
||||||
script-src:
|
script-src:
|
||||||
unsafe-eval: true
|
unsafe-eval: true
|
||||||
@ -39,6 +40,11 @@ csp:
|
|||||||
script-src-elem:
|
script-src-elem:
|
||||||
- "element.{{ primary_domain }}"
|
- "element.{{ primary_domain }}"
|
||||||
- "https://cdn.jsdelivr.net"
|
- "https://cdn.jsdelivr.net"
|
||||||
|
domains:
|
||||||
|
canonical:
|
||||||
|
synapse: "matrix.{{ primary_domain }}"
|
||||||
|
element: "element.{{ primary_domain }}"
|
||||||
|
client_max_body_size: "15M"
|
||||||
|
|
||||||
plugins:
|
plugins:
|
||||||
# You need to enable them in the inventory file
|
# You need to enable them in the inventory file
|
||||||
@ -50,10 +56,3 @@ plugins:
|
|||||||
slack: false
|
slack: false
|
||||||
telegram: false
|
telegram: false
|
||||||
whatsapp: false
|
whatsapp: false
|
||||||
|
|
||||||
client_max_body_size: "15M"
|
|
||||||
|
|
||||||
domains:
|
|
||||||
canonical:
|
|
||||||
synapse: "matrix.{{ primary_domain }}"
|
|
||||||
element: "element.{{ primary_domain }}"
|
|
||||||
|
@ -17,4 +17,4 @@ matrix_project: "{{ application_id | get_entity_name }}"
|
|||||||
# Webserver
|
# Webserver
|
||||||
well_known_directory: "{{nginx.directories.data.well_known}}/matrix/"
|
well_known_directory: "{{nginx.directories.data.well_known}}/matrix/"
|
||||||
location_upload: "~ ^/_matrix/media/v3/"
|
location_upload: "~ ^/_matrix/media/v3/"
|
||||||
client_max_body_size: "{{ applications | get_app_conf(application_id, 'client_max_body_size') }}"
|
client_max_body_size: "{{ applications | get_app_conf(application_id, 'server.client_max_body_size') }}"
|
@ -1,4 +1,5 @@
|
|||||||
domains:
|
server:
|
||||||
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "wiki.{{ primary_domain }}"
|
- "wiki.{{ primary_domain }}"
|
||||||
docker:
|
docker:
|
||||||
|
@ -9,7 +9,8 @@ features:
|
|||||||
css: true # use custom infinito stile
|
css: true # use custom infinito stile
|
||||||
port-ui-desktop: true # Enable in port-ui
|
port-ui-desktop: true # Enable in port-ui
|
||||||
logout: false
|
logout: false
|
||||||
csp:
|
server:
|
||||||
|
csp:
|
||||||
whitelist:
|
whitelist:
|
||||||
script-src-elem:
|
script-src-elem:
|
||||||
- https://cdn.jsdelivr.net
|
- https://cdn.jsdelivr.net
|
||||||
@ -30,7 +31,7 @@ csp:
|
|||||||
flags:
|
flags:
|
||||||
style-src:
|
style-src:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "mig.{{ primary_domain }}"
|
- "mig.{{ primary_domain }}"
|
||||||
aliases:
|
aliases:
|
||||||
|
@ -5,13 +5,14 @@ features:
|
|||||||
matomo: true
|
matomo: true
|
||||||
port-ui-desktop: true
|
port-ui-desktop: true
|
||||||
logout: true
|
logout: true
|
||||||
csp:
|
server:
|
||||||
|
csp:
|
||||||
flags:
|
flags:
|
||||||
script-src-elem:
|
script-src-elem:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
script-src:
|
script-src:
|
||||||
unsafe-eval: true
|
unsafe-eval: true
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "event.{{ primary_domain }}"
|
- "event.{{ primary_domain }}"
|
||||||
aliases:
|
aliases:
|
||||||
|
@ -6,7 +6,8 @@ features:
|
|||||||
central_database: true
|
central_database: true
|
||||||
oidc: true
|
oidc: true
|
||||||
logout: true
|
logout: true
|
||||||
csp:
|
server:
|
||||||
|
csp:
|
||||||
flags:
|
flags:
|
||||||
script-src-elem:
|
script-src-elem:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
@ -22,7 +23,7 @@ csp:
|
|||||||
- "blob:"
|
- "blob:"
|
||||||
script-src-elem:
|
script-src-elem:
|
||||||
- "https://cdn.jsdelivr.net"
|
- "https://cdn.jsdelivr.net"
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "academy.{{ primary_domain }}"
|
- "academy.{{ primary_domain }}"
|
||||||
docker:
|
docker:
|
||||||
|
@ -15,7 +15,7 @@ docker:
|
|||||||
name: "mybb"
|
name: "mybb"
|
||||||
volumes:
|
volumes:
|
||||||
data: "mybb_data"
|
data: "mybb_data"
|
||||||
|
server:
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- mybb.{{ primary_domain }}
|
- mybb.{{ primary_domain }}
|
||||||
|
@ -3,7 +3,8 @@ features:
|
|||||||
css: true
|
css: true
|
||||||
port-ui-desktop: true
|
port-ui-desktop: true
|
||||||
logout: false
|
logout: false
|
||||||
csp:
|
server:
|
||||||
|
csp:
|
||||||
whitelist:
|
whitelist:
|
||||||
script-src-elem:
|
script-src-elem:
|
||||||
- https://cdnjs.cloudflare.com
|
- https://cdnjs.cloudflare.com
|
||||||
@ -23,6 +24,6 @@ csp:
|
|||||||
unsafe-eval: true
|
unsafe-eval: true
|
||||||
script-src-elem:
|
script-src-elem:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "slides.{{ primary_domain }}"
|
- "slides.{{ primary_domain }}"
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
version: "production" # @see https://nextcloud.com/blog/nextcloud-release-channels-and-how-to-track-them/
|
version: "production" # @see https://nextcloud.com/blog/nextcloud-release-channels-and-how-to-track-them/
|
||||||
csp:
|
server:
|
||||||
|
csp:
|
||||||
flags:
|
flags:
|
||||||
style-src:
|
style-src:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
@ -8,7 +9,7 @@ csp:
|
|||||||
whitelist:
|
whitelist:
|
||||||
font-src:
|
font-src:
|
||||||
- "data:"
|
- "data:"
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "cloud.{{ primary_domain }}"
|
- "cloud.{{ primary_domain }}"
|
||||||
# nextcloud: "cloud.{{ primary_domain }}"
|
# nextcloud: "cloud.{{ primary_domain }}"
|
||||||
|
@ -6,7 +6,7 @@ features:
|
|||||||
css: true
|
css: true
|
||||||
port-ui-desktop: false
|
port-ui-desktop: false
|
||||||
logout: true
|
logout: true
|
||||||
|
server:
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- oauth2-proxy.{{ primary_domain }}
|
- oauth2-proxy.{{ primary_domain }}
|
||||||
|
@ -18,13 +18,14 @@ features:
|
|||||||
central_database: true
|
central_database: true
|
||||||
oauth2: true
|
oauth2: true
|
||||||
logout: true
|
logout: true
|
||||||
csp:
|
server:
|
||||||
|
csp:
|
||||||
flags:
|
flags:
|
||||||
script-src-elem:
|
script-src-elem:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
style-src:
|
style-src:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "project.{{ primary_domain }}"
|
- "project.{{ primary_domain }}"
|
||||||
|
|
||||||
|
@ -5,7 +5,8 @@ features:
|
|||||||
central_database: true
|
central_database: true
|
||||||
oidc: true
|
oidc: true
|
||||||
logout: true
|
logout: true
|
||||||
csp:
|
server:
|
||||||
|
csp:
|
||||||
flags:
|
flags:
|
||||||
script-src-elem:
|
script-src-elem:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
@ -20,7 +21,7 @@ csp:
|
|||||||
- "blob:"
|
- "blob:"
|
||||||
font-src:
|
font-src:
|
||||||
- "data:"
|
- "data:"
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "video.{{ primary_domain }}"
|
- "video.{{ primary_domain }}"
|
||||||
aliases:
|
aliases:
|
||||||
|
@ -13,7 +13,8 @@ features:
|
|||||||
central_database: true
|
central_database: true
|
||||||
oauth2: true
|
oauth2: true
|
||||||
logout: true
|
logout: true
|
||||||
csp:
|
server:
|
||||||
|
csp:
|
||||||
flags:
|
flags:
|
||||||
style-src:
|
style-src:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
@ -22,11 +23,10 @@ csp:
|
|||||||
whitelist:
|
whitelist:
|
||||||
font-src:
|
font-src:
|
||||||
- "data:"
|
- "data:"
|
||||||
|
domains:
|
||||||
|
canonical:
|
||||||
|
- pgadmin.{{ primary_domain }}
|
||||||
docker:
|
docker:
|
||||||
services:
|
services:
|
||||||
database:
|
database:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
||||||
domains:
|
|
||||||
canonical:
|
|
||||||
- pgadmin.{{ primary_domain }}
|
|
||||||
|
@ -11,7 +11,7 @@ features:
|
|||||||
ldap: true
|
ldap: true
|
||||||
oauth2: true
|
oauth2: true
|
||||||
logout: true
|
logout: true
|
||||||
|
server:
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- phpldapadmin.{{ primary_domain }}
|
- phpldapadmin.{{ primary_domain }}
|
||||||
|
@ -12,13 +12,14 @@ features:
|
|||||||
central_database: true
|
central_database: true
|
||||||
oauth2: true
|
oauth2: true
|
||||||
logout: true
|
logout: true
|
||||||
csp:
|
server:
|
||||||
|
csp:
|
||||||
flags:
|
flags:
|
||||||
style-src:
|
style-src:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
script-src-elem:
|
script-src-elem:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
domains:
|
domains:
|
||||||
aliases:
|
aliases:
|
||||||
- "mysql.{{ primary_domain }}"
|
- "mysql.{{ primary_domain }}"
|
||||||
- "mariadb.{{ primary_domain }}"
|
- "mariadb.{{ primary_domain }}"
|
||||||
|
@ -6,7 +6,8 @@ features:
|
|||||||
central_database: true
|
central_database: true
|
||||||
oidc: true
|
oidc: true
|
||||||
logout: true
|
logout: true
|
||||||
csp:
|
server:
|
||||||
|
csp:
|
||||||
flags:
|
flags:
|
||||||
script-src:
|
script-src:
|
||||||
unsafe-eval: true
|
unsafe-eval: true
|
||||||
@ -19,7 +20,7 @@ csp:
|
|||||||
whitelist:
|
whitelist:
|
||||||
frame-ancestors:
|
frame-ancestors:
|
||||||
- "*"
|
- "*"
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "picture.{{ primary_domain }}"
|
- "picture.{{ primary_domain }}"
|
||||||
aliases:
|
aliases:
|
||||||
|
@ -5,7 +5,8 @@ features:
|
|||||||
simpleicons: true # Activate Brand Icons for your groups
|
simpleicons: true # Activate Brand Icons for your groups
|
||||||
javascript: true # Necessary for URL sync
|
javascript: true # Necessary for URL sync
|
||||||
logout: false # Doesn't have own user data. Just a frame.
|
logout: false # Doesn't have own user data. Just a frame.
|
||||||
csp:
|
server:
|
||||||
|
csp:
|
||||||
whitelist:
|
whitelist:
|
||||||
script-src-elem:
|
script-src-elem:
|
||||||
- https://cdn.jsdelivr.net
|
- https://cdn.jsdelivr.net
|
||||||
@ -27,7 +28,7 @@ csp:
|
|||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
script-src-elem:
|
script-src-elem:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "{{ primary_domain }}"
|
- "{{ primary_domain }}"
|
||||||
|
|
||||||
|
@ -18,10 +18,11 @@ features:
|
|||||||
oauth2: false # Enable the OAuth2-Proy
|
oauth2: false # Enable the OAuth2-Proy
|
||||||
javascript: false # Enables the custom JS in the javascript.js.j2 file
|
javascript: false # Enables the custom JS in the javascript.js.j2 file
|
||||||
logout: true
|
logout: true
|
||||||
csp:
|
server:
|
||||||
|
csp:
|
||||||
whitelist: {} # URL's which should be whitelisted
|
whitelist: {} # URL's which should be whitelisted
|
||||||
flags: {} # Flags which should be set
|
flags: {} # Flags which should be set
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "pretix.{{ primary_domain }}"
|
- "pretix.{{ primary_domain }}"
|
||||||
aliases: [] # Alias redirections to the first element of the canonical domains
|
aliases: [] # Alias redirections to the first element of the canonical domains
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
features:
|
features:
|
||||||
logout: false
|
logout: false
|
||||||
domains:
|
server:
|
||||||
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "wheel.{{ primary_domain }}"
|
- "wheel.{{ primary_domain }}"
|
||||||
|
@ -6,10 +6,11 @@ features:
|
|||||||
ldap: true
|
ldap: true
|
||||||
oauth2: true
|
oauth2: true
|
||||||
logout: true
|
logout: true
|
||||||
domains:
|
server:
|
||||||
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "inventory.{{ primary_domain }}"
|
- "inventory.{{ primary_domain }}"
|
||||||
csp:
|
csp:
|
||||||
flags:
|
flags:
|
||||||
script-src:
|
script-src:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
|
@ -3,7 +3,8 @@ features:
|
|||||||
css: true
|
css: true
|
||||||
port-ui-desktop: true
|
port-ui-desktop: true
|
||||||
logout: false
|
logout: false
|
||||||
csp:
|
server:
|
||||||
|
csp:
|
||||||
flags:
|
flags:
|
||||||
script-src:
|
script-src:
|
||||||
unsafe-eval: true
|
unsafe-eval: true
|
||||||
@ -12,6 +13,6 @@ csp:
|
|||||||
unsafe-eval: true
|
unsafe-eval: true
|
||||||
style-src:
|
style-src:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "docs.{{ primary_domain }}"
|
- "docs.{{ primary_domain }}"
|
||||||
|
@ -13,7 +13,7 @@ features:
|
|||||||
# users:
|
# users:
|
||||||
# administrator:
|
# administrator:
|
||||||
# username: "{{ users.administrator.username }}"
|
# username: "{{ users.administrator.username }}"
|
||||||
|
server:
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- syncope.{{ primary_domain }}
|
- syncope.{{ primary_domain }}
|
||||||
|
@ -18,7 +18,8 @@ docker:
|
|||||||
enabled: true
|
enabled: true
|
||||||
taiga:
|
taiga:
|
||||||
version: "latest"
|
version: "latest"
|
||||||
csp:
|
server:
|
||||||
|
csp:
|
||||||
flags:
|
flags:
|
||||||
script-src-elem:
|
script-src-elem:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
@ -27,6 +28,6 @@ csp:
|
|||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
script-src:
|
script-src:
|
||||||
unsafe-eval: true
|
unsafe-eval: true
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "kanban.{{ primary_domain }}"
|
- "kanban.{{ primary_domain }}"
|
||||||
|
@ -14,7 +14,8 @@ features:
|
|||||||
oidc: true
|
oidc: true
|
||||||
central_database: true
|
central_database: true
|
||||||
logout: true
|
logout: true
|
||||||
csp:
|
server:
|
||||||
|
csp:
|
||||||
flags:
|
flags:
|
||||||
style-src:
|
style-src:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
@ -37,7 +38,7 @@ csp:
|
|||||||
frame-src:
|
frame-src:
|
||||||
- "blob:"
|
- "blob:"
|
||||||
- "*"
|
- "*"
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "blog.{{ primary_domain }}"
|
- "blog.{{ primary_domain }}"
|
||||||
docker:
|
docker:
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
- name: "Include role srv-proxy-6-6-domain for {{ application_id }}"
|
- name: "Include role srv-proxy-6-6-domain for {{ application_id }}"
|
||||||
include_role:
|
include_role:
|
||||||
name: srv-proxy-6-6-domain
|
name: srv-proxy-6-6-domain
|
||||||
loop: "{{ applications | get_app_conf(application_id, 'domains.canonical', True) }}"
|
loop: "{{ applications | get_app_conf(application_id, 'server.domains.canonical', True) }}"
|
||||||
loop_control:
|
loop_control:
|
||||||
loop_var: domain
|
loop_var: domain
|
||||||
vars:
|
vars:
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# xmpp is more a service then a app with ui interface. @todo Rename it
|
# xmpp is more a service then a app with ui interface. @todo Rename it
|
||||||
features:
|
features:
|
||||||
logout: false # Reactivated as soon as xmpp is fully implemented
|
logout: false # Reactivated as soon as xmpp is fully implemented
|
||||||
|
server:
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- xmpp.{{ primary_domain }}
|
- xmpp.{{ primary_domain }}
|
||||||
|
@ -13,11 +13,20 @@ features:
|
|||||||
central_database: true
|
central_database: true
|
||||||
oauth2: true
|
oauth2: true
|
||||||
logout: true
|
logout: true
|
||||||
domains:
|
server:
|
||||||
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "s.{{ primary_domain }}"
|
- "s.{{ primary_domain }}"
|
||||||
aliases:
|
aliases:
|
||||||
- "short.{{ primary_domain }}"
|
- "short.{{ primary_domain }}"
|
||||||
|
csp:
|
||||||
|
flags:
|
||||||
|
style-src:
|
||||||
|
unsafe-inline: true
|
||||||
|
script-src-elem:
|
||||||
|
unsafe-inline: true
|
||||||
|
script-src:
|
||||||
|
unsafe-inline: true
|
||||||
docker:
|
docker:
|
||||||
services:
|
services:
|
||||||
database:
|
database:
|
||||||
@ -26,11 +35,3 @@ docker:
|
|||||||
version: "latest"
|
version: "latest"
|
||||||
name: "yourls"
|
name: "yourls"
|
||||||
image: "yourls"
|
image: "yourls"
|
||||||
csp:
|
|
||||||
flags:
|
|
||||||
style-src:
|
|
||||||
unsafe-inline: true
|
|
||||||
script-src-elem:
|
|
||||||
unsafe-inline: true
|
|
||||||
script-src:
|
|
||||||
unsafe-inline: true
|
|
@ -1,6 +1,6 @@
|
|||||||
source_directory: "{{ playbook_dir }}/assets"
|
source_directory: "{{ playbook_dir }}/assets"
|
||||||
url: "{{ web_protocol }}://<< defaults_applications['web-svc-file']domains.canonical[0] >>/assets"
|
url: "{{ web_protocol }}://<< defaults_applications['web-svc-file']server.domains.canonical[0] >>/assets"
|
||||||
|
server:
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- asset.{{ primary_domain }}
|
- asset.{{ primary_domain }}
|
||||||
|
@ -2,6 +2,7 @@ features:
|
|||||||
matomo: true
|
matomo: true
|
||||||
css: true
|
css: true
|
||||||
port-ui-desktop: true
|
port-ui-desktop: true
|
||||||
domains:
|
server:
|
||||||
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "cdn.{{ primary_domain }}"
|
- "cdn.{{ primary_domain }}"
|
||||||
|
@ -2,7 +2,8 @@ features:
|
|||||||
matomo: true
|
matomo: true
|
||||||
css: true
|
css: true
|
||||||
port-ui-desktop: true
|
port-ui-desktop: true
|
||||||
domains:
|
server:
|
||||||
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "file.{{ primary_domain }}"
|
- "file.{{ primary_domain }}"
|
||||||
alias:
|
alias:
|
||||||
|
@ -2,6 +2,7 @@ features:
|
|||||||
matomo: true
|
matomo: true
|
||||||
css: true
|
css: true
|
||||||
port-ui-desktop: true
|
port-ui-desktop: true
|
||||||
domains:
|
server:
|
||||||
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "html.{{ primary_domain }}"
|
- "html.{{ primary_domain }}"
|
||||||
|
@ -4,10 +4,11 @@ features:
|
|||||||
port-ui-desktop: true
|
port-ui-desktop: true
|
||||||
javascript: false
|
javascript: false
|
||||||
logout: false
|
logout: false
|
||||||
domains:
|
server:
|
||||||
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "logout.{{ primary_domain }}"
|
- "logout.{{ primary_domain }}"
|
||||||
csp:
|
csp:
|
||||||
flags:
|
flags:
|
||||||
style-src:
|
style-src:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
@ -22,5 +23,5 @@ csp:
|
|||||||
style-src:
|
style-src:
|
||||||
- https://cdn.jsdelivr.net
|
- https://cdn.jsdelivr.net
|
||||||
frame-ancestors:
|
frame-ancestors:
|
||||||
- "{{ web_protocol }}://<< defaults_applications[web-app-keycloak].domains.canonical[0] >>"
|
- "{{ web_protocol }}://<< defaults_applications[web-app-keycloak].server.domains.canonical[0] >>"
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ class FilterModule(object):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# use canonical domains list if present
|
# use canonical domains list if present
|
||||||
domains_entry = config.get('domains', {}).get('canonical', [])
|
domains_entry = config.get('server', {}).get('domains', {}).get('canonical', [])
|
||||||
|
|
||||||
# normalize to a list of strings
|
# normalize to a list of strings
|
||||||
if isinstance(domains_entry, dict):
|
if isinstance(domains_entry, dict):
|
||||||
|
@ -16,8 +16,9 @@ features:
|
|||||||
central_database: false # Enable Central Database Network
|
central_database: false # Enable Central Database Network
|
||||||
recaptcha: false # Enable ReCaptcha
|
recaptcha: false # Enable ReCaptcha
|
||||||
oauth2: false # Enable the OAuth2-Proy
|
oauth2: false # Enable the OAuth2-Proy
|
||||||
csp: {}
|
server:
|
||||||
domains:
|
csp: {}
|
||||||
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "icons.{{ primary_domain }}"
|
- "icons.{{ primary_domain }}"
|
||||||
rbac:
|
rbac:
|
||||||
|
@ -25,7 +25,8 @@ features:
|
|||||||
oauth2: false # Enable the OAuth2-Proy
|
oauth2: false # Enable the OAuth2-Proy
|
||||||
javascript: false # Enable the custom JS in the javascript.js.j2 file
|
javascript: false # Enable the custom JS in the javascript.js.j2 file
|
||||||
logout: true # Enable the logout via the central logout mechanism (deleting all cookies)
|
logout: true # Enable the logout via the central logout mechanism (deleting all cookies)
|
||||||
csp:
|
server:
|
||||||
|
csp:
|
||||||
whitelist: # URL's which should be whitelisted
|
whitelist: # URL's which should be whitelisted
|
||||||
script-src-elem: []
|
script-src-elem: []
|
||||||
style-src: []
|
style-src: []
|
||||||
@ -39,8 +40,7 @@ csp:
|
|||||||
unsafe-inline: false
|
unsafe-inline: false
|
||||||
script-src-elem:
|
script-src-elem:
|
||||||
unsafe-inline: false
|
unsafe-inline: false
|
||||||
domains:
|
domains:
|
||||||
domains:
|
|
||||||
canonical: {} # Urls under which the domain should be directly accessible
|
canonical: {} # Urls under which the domain should be directly accessible
|
||||||
aliases: [] # Alias redirections to the first element of the canonical domains
|
aliases: [] # Alias redirections to the first element of the canonical domains
|
||||||
rbac:
|
rbac:
|
||||||
|
@ -25,7 +25,7 @@ class TestDomainUniqueness(unittest.TestCase):
|
|||||||
domain_to_apps = defaultdict(set)
|
domain_to_apps = defaultdict(set)
|
||||||
|
|
||||||
for app_name, app_cfg in apps.items():
|
for app_name, app_cfg in apps.items():
|
||||||
domains_cfg = app_cfg.get('domains', {})
|
domains_cfg = app_cfg.get('server',{}).get('domains',{})
|
||||||
|
|
||||||
# canonical entries may be a list or a mapping
|
# canonical entries may be a list or a mapping
|
||||||
canonical = domains_cfg.get('canonical', [])
|
canonical = domains_cfg.get('canonical', [])
|
||||||
|
@ -20,17 +20,17 @@ class TestWebRolesDomains(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertIsInstance(data, dict, f"YAML root is not a dict in {path}")
|
self.assertIsInstance(data, dict, f"YAML root is not a dict in {path}")
|
||||||
|
|
||||||
domains = data.get("domains")
|
domains = data.get('server',{}).get('domains',{})
|
||||||
self.assertIsNotNone(domains, f"'domains' section missing in {path}")
|
self.assertIsNotNone(domains, f"'domains' section missing in {path}")
|
||||||
self.assertIsInstance(domains, dict, f"'domains' must be a dict in {path}")
|
self.assertIsInstance(domains, dict, f"'domains' must be a dict in {path}")
|
||||||
|
|
||||||
canonical = domains.get("canonical")
|
canonical = domains.get("canonical")
|
||||||
self.assertIsNotNone(canonical, f"'domains.canonical' missing in {path}")
|
self.assertIsNotNone(canonical, f"'server.domains.canonical' missing in {path}")
|
||||||
|
|
||||||
# Check for emptiness
|
# Check for emptiness
|
||||||
empty_values = [{}, [], ""]
|
empty_values = [{}, [], ""]
|
||||||
self.assertNotIn(canonical, empty_values,
|
self.assertNotIn(canonical, empty_values,
|
||||||
f"'domains.canonical' in {path} must not be empty dict, list, or empty string")
|
f"'server.domains.canonical' in {path} must not be empty dict, list, or empty string")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -33,7 +33,7 @@ class TestDomainsStructure(unittest.TestCase):
|
|||||||
if 'domains' not in data:
|
if 'domains' not in data:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
domains = data['domains']
|
domains = data.get('server',{}).get('domains')
|
||||||
if not isinstance(domains, dict):
|
if not isinstance(domains, dict):
|
||||||
failed_roles.append((role_path.name, vars_file.name, "'domains' should be a dict"))
|
failed_roles.append((role_path.name, vars_file.name, "'domains' should be a dict"))
|
||||||
continue
|
continue
|
||||||
|
@ -33,9 +33,11 @@ class TestDomainFilters(unittest.TestCase):
|
|||||||
def test_alias_with_explicit_aliases(self):
|
def test_alias_with_explicit_aliases(self):
|
||||||
apps = {
|
apps = {
|
||||||
'app1': {
|
'app1': {
|
||||||
|
'server':{
|
||||||
'domains': {'aliases': ['alias.com']}
|
'domains': {'aliases': ['alias.com']}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
# canonical defaults to ['app1.example.com'], so alias should include alias.com and default
|
# canonical defaults to ['app1.example.com'], so alias should include alias.com and default
|
||||||
expected = {'app1': ['alias.com', 'app1.example.com']}
|
expected = {'app1': ['alias.com', 'app1.example.com']}
|
||||||
result = self.filter_module.alias_domains_map(apps, self.primary)
|
result = self.filter_module.alias_domains_map(apps, self.primary)
|
||||||
@ -44,7 +46,7 @@ class TestDomainFilters(unittest.TestCase):
|
|||||||
def test_alias_with_canonical_not_default(self):
|
def test_alias_with_canonical_not_default(self):
|
||||||
apps = {
|
apps = {
|
||||||
'app1': {
|
'app1': {
|
||||||
'domains': {'canonical': ['foo.com']}
|
'server':{'domains': {'canonical': ['foo.com']}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# foo.com is canonical, default not in canonical so added as alias
|
# foo.com is canonical, default not in canonical so added as alias
|
||||||
@ -55,12 +57,14 @@ class TestDomainFilters(unittest.TestCase):
|
|||||||
def test_alias_with_existing_default(self):
|
def test_alias_with_existing_default(self):
|
||||||
apps = {
|
apps = {
|
||||||
'app1': {
|
'app1': {
|
||||||
|
'server':{
|
||||||
'domains': {
|
'domains': {
|
||||||
'canonical': ['foo.com'],
|
'canonical': ['foo.com'],
|
||||||
'aliases': ['app1.example.com']
|
'aliases': ['app1.example.com']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
# default present in aliases, should not be duplicated
|
# default present in aliases, should not be duplicated
|
||||||
expected = {'app1': ['app1.example.com']}
|
expected = {'app1': ['app1.example.com']}
|
||||||
result = self.filter_module.alias_domains_map(apps, self.primary)
|
result = self.filter_module.alias_domains_map(apps, self.primary)
|
||||||
@ -68,7 +72,7 @@ class TestDomainFilters(unittest.TestCase):
|
|||||||
|
|
||||||
def test_invalid_aliases_type(self):
|
def test_invalid_aliases_type(self):
|
||||||
apps = {
|
apps = {
|
||||||
'app1': {'domains': {'aliases': 123}}
|
'app1': {'server':{'domains': {'aliases': 123}}}
|
||||||
}
|
}
|
||||||
with self.assertRaises(AnsibleFilterError):
|
with self.assertRaises(AnsibleFilterError):
|
||||||
self.filter_module.alias_domains_map(apps, self.primary)
|
self.filter_module.alias_domains_map(apps, self.primary)
|
||||||
@ -76,9 +80,11 @@ class TestDomainFilters(unittest.TestCase):
|
|||||||
def test_alias_with_empty_domains_cfg(self):
|
def test_alias_with_empty_domains_cfg(self):
|
||||||
apps = {
|
apps = {
|
||||||
'app1': {
|
'app1': {
|
||||||
|
'server':{
|
||||||
'domains': {}
|
'domains': {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
expected = apps
|
expected = apps
|
||||||
result = self.filter_module.alias_domains_map(apps, self.primary)
|
result = self.filter_module.alias_domains_map(apps, self.primary)
|
||||||
self.assertEqual(result, expected)
|
self.assertEqual(result, expected)
|
||||||
@ -86,6 +92,7 @@ class TestDomainFilters(unittest.TestCase):
|
|||||||
def test_alias_with_canonical_dict_not_default(self):
|
def test_alias_with_canonical_dict_not_default(self):
|
||||||
apps = {
|
apps = {
|
||||||
'app1': {
|
'app1': {
|
||||||
|
'server':{
|
||||||
'domains': {
|
'domains': {
|
||||||
'canonical': {
|
'canonical': {
|
||||||
'one': 'one.com',
|
'one': 'one.com',
|
||||||
@ -94,6 +101,7 @@ class TestDomainFilters(unittest.TestCase):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
expected = {'app1': ['app1.example.com']}
|
expected = {'app1': ['app1.example.com']}
|
||||||
result = self.filter_module.alias_domains_map(apps, self.primary)
|
result = self.filter_module.alias_domains_map(apps, self.primary)
|
||||||
self.assertEqual(result, expected)
|
self.assertEqual(result, expected)
|
||||||
|
@ -32,9 +32,11 @@ class TestDomainFilters(unittest.TestCase):
|
|||||||
def test_canonical_with_list(self):
|
def test_canonical_with_list(self):
|
||||||
apps = {
|
apps = {
|
||||||
'web-app-app1': {
|
'web-app-app1': {
|
||||||
|
'server':{
|
||||||
'domains': {'canonical': ['foo.com', 'bar.com']}
|
'domains': {'canonical': ['foo.com', 'bar.com']}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
result = self.filter_module.canonical_domains_map(apps, self.primary)
|
result = self.filter_module.canonical_domains_map(apps, self.primary)
|
||||||
self.assertCountEqual(
|
self.assertCountEqual(
|
||||||
result['web-app-app1'],
|
result['web-app-app1'],
|
||||||
@ -44,9 +46,11 @@ class TestDomainFilters(unittest.TestCase):
|
|||||||
def test_canonical_with_dict(self):
|
def test_canonical_with_dict(self):
|
||||||
apps = {
|
apps = {
|
||||||
'web-app-app1': {
|
'web-app-app1': {
|
||||||
|
'server':{
|
||||||
'domains': {'canonical': {'one': 'one.com', 'two': 'two.com'}}
|
'domains': {'canonical': {'one': 'one.com', 'two': 'two.com'}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
result = self.filter_module.canonical_domains_map(apps, self.primary)
|
result = self.filter_module.canonical_domains_map(apps, self.primary)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
result['web-app-app1'],
|
result['web-app-app1'],
|
||||||
@ -55,8 +59,14 @@ class TestDomainFilters(unittest.TestCase):
|
|||||||
|
|
||||||
def test_canonical_duplicate_raises(self):
|
def test_canonical_duplicate_raises(self):
|
||||||
apps = {
|
apps = {
|
||||||
'web-app-app1': {'domains': {'canonical': ['dup.com']}},
|
'web-app-app1':{
|
||||||
'web-app-app2': {'domains': {'canonical': ['dup.com']}},
|
'server':{'domains': {'canonical': ['dup.com']}},
|
||||||
|
},
|
||||||
|
'web-app-app2':{
|
||||||
|
'server':{
|
||||||
|
'domains': {'canonical': ['dup.com']}
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
with self.assertRaises(AnsibleFilterError) as cm:
|
with self.assertRaises(AnsibleFilterError) as cm:
|
||||||
self.filter_module.canonical_domains_map(apps, self.primary)
|
self.filter_module.canonical_domains_map(apps, self.primary)
|
||||||
@ -65,7 +75,7 @@ class TestDomainFilters(unittest.TestCase):
|
|||||||
|
|
||||||
def test_invalid_canonical_type(self):
|
def test_invalid_canonical_type(self):
|
||||||
apps = {
|
apps = {
|
||||||
'web-app-app1': {'domains': {'canonical': 123}}
|
'web-app-app1': {'server':{'domains': {'canonical': 123}}}
|
||||||
}
|
}
|
||||||
with self.assertRaises(AnsibleFilterError):
|
with self.assertRaises(AnsibleFilterError):
|
||||||
self.filter_module.canonical_domains_map(apps, self.primary)
|
self.filter_module.canonical_domains_map(apps, self.primary)
|
||||||
@ -76,7 +86,7 @@ class TestDomainFilters(unittest.TestCase):
|
|||||||
resulting in an empty mapping when only non-web apps are provided.
|
resulting in an empty mapping when only non-web apps are provided.
|
||||||
"""
|
"""
|
||||||
apps = {
|
apps = {
|
||||||
'db-app-app1': {'domains': {'canonical': ['db.example.com']}},
|
'db-app-app1': {'server':{'domains': {'canonical': ['db.example.com']}}},
|
||||||
'service-app-app2': {}
|
'service-app-app2': {}
|
||||||
}
|
}
|
||||||
result = self.filter_module.canonical_domains_map(apps, self.primary)
|
result = self.filter_module.canonical_domains_map(apps, self.primary)
|
||||||
@ -88,7 +98,7 @@ class TestDomainFilters(unittest.TestCase):
|
|||||||
non-web apps should be ignored alongside valid web apps.
|
non-web apps should be ignored alongside valid web apps.
|
||||||
"""
|
"""
|
||||||
apps = {
|
apps = {
|
||||||
'db-app-app1': {'domains': {'canonical': ['db.example.com']}},
|
'db-app-app1': {'server':{'domains': {'canonical': ['db.example.com']}}},
|
||||||
'web-app-app1': {}
|
'web-app-app1': {}
|
||||||
}
|
}
|
||||||
expected = {'web-app-app1': ['app1.example.com']}
|
expected = {'web-app-app1': ['app1.example.com']}
|
||||||
|
@ -37,9 +37,11 @@ class TestDomainMappings(unittest.TestCase):
|
|||||||
def test_explicit_aliases(self):
|
def test_explicit_aliases(self):
|
||||||
apps = {
|
apps = {
|
||||||
'app1': {
|
'app1': {
|
||||||
|
'server':{
|
||||||
'domains': {'aliases': ['alias.com']}
|
'domains': {'aliases': ['alias.com']}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
default = 'app1.example.com'
|
default = 'app1.example.com'
|
||||||
expected = [
|
expected = [
|
||||||
{'source': 'alias.com', 'target': default},
|
{'source': 'alias.com', 'target': default},
|
||||||
@ -51,9 +53,11 @@ class TestDomainMappings(unittest.TestCase):
|
|||||||
def test_canonical_not_default(self):
|
def test_canonical_not_default(self):
|
||||||
apps = {
|
apps = {
|
||||||
'app1': {
|
'app1': {
|
||||||
|
'server':{
|
||||||
'domains': {'canonical': ['foo.com']}
|
'domains': {'canonical': ['foo.com']}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
expected = [
|
expected = [
|
||||||
{'source': 'app1.example.com', 'target': 'foo.com'}
|
{'source': 'app1.example.com', 'target': 'foo.com'}
|
||||||
]
|
]
|
||||||
@ -63,11 +67,13 @@ class TestDomainMappings(unittest.TestCase):
|
|||||||
def test_canonical_dict(self):
|
def test_canonical_dict(self):
|
||||||
apps = {
|
apps = {
|
||||||
'app1': {
|
'app1': {
|
||||||
|
'server':{
|
||||||
'domains': {
|
'domains': {
|
||||||
'canonical': {'one': 'one.com', 'two': 'two.com'}
|
'canonical': {'one': 'one.com', 'two': 'two.com'}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
# first canonical key 'one' → one.com
|
# first canonical key 'one' → one.com
|
||||||
expected = [
|
expected = [
|
||||||
{'source': 'app1.example.com', 'target': 'one.com'}
|
{'source': 'app1.example.com', 'target': 'one.com'}
|
||||||
@ -77,8 +83,12 @@ class TestDomainMappings(unittest.TestCase):
|
|||||||
|
|
||||||
def test_multiple_apps(self):
|
def test_multiple_apps(self):
|
||||||
apps = {
|
apps = {
|
||||||
'app1': {'domains': {'aliases': ['a1.com']}},
|
'app1': {
|
||||||
'app2': {'domains': {'canonical': ['c2.com']}},
|
'server':{'domains': {'aliases': ['a1.com']}}
|
||||||
|
},
|
||||||
|
'app2': {
|
||||||
|
'server':{'domains': {'canonical': ['c2.com']}}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
expected = [
|
expected = [
|
||||||
{'source': 'a1.com', 'target': 'app1.example.com'},
|
{'source': 'a1.com', 'target': 'app1.example.com'},
|
||||||
@ -89,7 +99,10 @@ class TestDomainMappings(unittest.TestCase):
|
|||||||
|
|
||||||
def test_multiple_aliases(self):
|
def test_multiple_aliases(self):
|
||||||
apps = {
|
apps = {
|
||||||
'app1': {'domains': {'aliases': ['a1.com','a2.com']}}
|
'app1': {
|
||||||
|
'server':{'domains': {'aliases': ['a1.com','a2.com']}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
expected = [
|
expected = [
|
||||||
{'source': 'a1.com', 'target': 'app1.example.com'},
|
{'source': 'a1.com', 'target': 'app1.example.com'},
|
||||||
@ -100,7 +113,7 @@ class TestDomainMappings(unittest.TestCase):
|
|||||||
|
|
||||||
def test_invalid_aliases_type(self):
|
def test_invalid_aliases_type(self):
|
||||||
apps = {
|
apps = {
|
||||||
'app1': {'domains': {'aliases': 123}}
|
'app1': {'server':{'domains': {'aliases': 123}}}
|
||||||
}
|
}
|
||||||
with self.assertRaises(AnsibleFilterError):
|
with self.assertRaises(AnsibleFilterError):
|
||||||
self.filter.domain_mappings(apps, self.primary)
|
self.filter.domain_mappings(apps, self.primary)
|
||||||
|
@ -19,12 +19,14 @@ class TestLoadConfigurationFilter(unittest.TestCase):
|
|||||||
self.nested_cfg = {
|
self.nested_cfg = {
|
||||||
'html': {
|
'html': {
|
||||||
'features': {'matomo': True},
|
'features': {'matomo': True},
|
||||||
'domains': {'canonical': ['html.example.com']}
|
'server': {
|
||||||
|
'domains':{'canonical': ['html.example.com']}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.flat_cfg = {
|
self.flat_cfg = {
|
||||||
'features': {'matomo': False},
|
'features': {'matomo': False},
|
||||||
'domains': {'canonical': ['flat.example.com']}
|
'server': {'domains':{'canonical': ['flat.example.com']}}
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_invalid_key(self):
|
def test_invalid_key(self):
|
||||||
@ -69,7 +71,7 @@ class TestLoadConfigurationFilter(unittest.TestCase):
|
|||||||
self.assertIn(self.app, _cfg_cache)
|
self.assertIn(self.app, _cfg_cache)
|
||||||
mock_yaml.reset_mock()
|
mock_yaml.reset_mock()
|
||||||
# from cache
|
# from cache
|
||||||
self.assertEqual(self.f(self.app, 'domains.canonical'),
|
self.assertEqual(self.f(self.app, 'server.domains.canonical'),
|
||||||
['html.example.com'])
|
['html.example.com'])
|
||||||
mock_yaml.assert_not_called()
|
mock_yaml.assert_not_called()
|
||||||
|
|
||||||
@ -92,7 +94,7 @@ class TestLoadConfigurationFilter(unittest.TestCase):
|
|||||||
mock_yaml.return_value = self.nested_cfg
|
mock_yaml.return_value = self.nested_cfg
|
||||||
# nested fallback must work
|
# nested fallback must work
|
||||||
self.assertTrue(self.f(self.app, 'features.matomo'))
|
self.assertTrue(self.f(self.app, 'features.matomo'))
|
||||||
self.assertEqual(self.f(self.app, 'domains.canonical'),
|
self.assertEqual(self.f(self.app, 'server.domains.canonical'),
|
||||||
['html.example.com'])
|
['html.example.com'])
|
||||||
|
|
||||||
@patch('load_configuration.os.listdir', return_value=['r4'])
|
@patch('load_configuration.os.listdir', return_value=['r4'])
|
||||||
@ -105,13 +107,15 @@ class TestLoadConfigurationFilter(unittest.TestCase):
|
|||||||
mock_exists.side_effect = lambda p: p.endswith('config/main.yml')
|
mock_exists.side_effect = lambda p: p.endswith('config/main.yml')
|
||||||
mock_yaml.return_value = {
|
mock_yaml.return_value = {
|
||||||
'file': {
|
'file': {
|
||||||
'domains': {
|
'server': {
|
||||||
|
'domains':{
|
||||||
'canonical': ['files.example.com', 'extra.example.com']
|
'canonical': ['files.example.com', 'extra.example.com']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
# should get the first element of the canonical domains list
|
# should get the first element of the canonical domains list
|
||||||
self.assertEqual(self.f('file', 'domains.canonical[0]'),
|
self.assertEqual(self.f('file', 'server.domains.canonical[0]'),
|
||||||
'files.example.com')
|
'files.example.com')
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -33,23 +33,33 @@ class TestLogoutDomainsFilter(unittest.TestCase):
|
|||||||
def test_flatten_and_feature_flag(self):
|
def test_flatten_and_feature_flag(self):
|
||||||
applications = {
|
applications = {
|
||||||
"app1": {
|
"app1": {
|
||||||
"domains": {"canonical": "single.domain.com"},
|
'server':{
|
||||||
|
"domains": {"canonical": "single.domain.com"}
|
||||||
|
},
|
||||||
"features": {"logout": True},
|
"features": {"logout": True},
|
||||||
},
|
},
|
||||||
"app2": {
|
"app2": {
|
||||||
"domains": {"canonical": ["list1.com", "list2.com"]},
|
'server':{
|
||||||
|
"domains": {"canonical": ["list1.com", "list2.com"]}
|
||||||
|
},
|
||||||
"features": {"logout": True},
|
"features": {"logout": True},
|
||||||
},
|
},
|
||||||
"app3": {
|
"app3": {
|
||||||
"domains": {"canonical": {"k1": "dictA.com", "k2": "dictB.com"}},
|
'server':{
|
||||||
|
"domains": {"canonical": {"k1": "dictA.com", "k2": "dictB.com"}}
|
||||||
|
},
|
||||||
"features": {"logout": True},
|
"features": {"logout": True},
|
||||||
},
|
},
|
||||||
"app4": {
|
"app4": {
|
||||||
"domains": {"canonical": "no-logout.com"},
|
'server':{
|
||||||
|
"domains": {"canonical": "no-logout.com"}
|
||||||
|
},
|
||||||
"features": {"logout": False},
|
"features": {"logout": False},
|
||||||
},
|
},
|
||||||
"other": {
|
"other": {
|
||||||
"domains": {"canonical": "ignored.com"},
|
'server':{
|
||||||
|
"domains": {"canonical": "ignored.com"}
|
||||||
|
},
|
||||||
"features": {"logout": True},
|
"features": {"logout": True},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -67,7 +77,9 @@ class TestLogoutDomainsFilter(unittest.TestCase):
|
|||||||
def test_missing_canonical_defaults_empty(self):
|
def test_missing_canonical_defaults_empty(self):
|
||||||
applications = {
|
applications = {
|
||||||
"app1": {
|
"app1": {
|
||||||
"domains": {}, # no 'canonical' key
|
'server':{
|
||||||
|
"domains": {}
|
||||||
|
}, # no 'canonical' key
|
||||||
"features": {"logout": True},
|
"features": {"logout": True},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -77,7 +89,9 @@ class TestLogoutDomainsFilter(unittest.TestCase):
|
|||||||
def test_app_not_in_group(self):
|
def test_app_not_in_group(self):
|
||||||
applications = {
|
applications = {
|
||||||
"app1": {
|
"app1": {
|
||||||
"domains": {"canonical": "domain.com"},
|
'server':{
|
||||||
|
"domains": {"canonical": "domain.com"}
|
||||||
|
},
|
||||||
"features": {"logout": True},
|
"features": {"logout": True},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -87,7 +101,9 @@ class TestLogoutDomainsFilter(unittest.TestCase):
|
|||||||
def test_invalid_domain_type(self):
|
def test_invalid_domain_type(self):
|
||||||
applications = {
|
applications = {
|
||||||
"app1": {
|
"app1": {
|
||||||
"domains": {"canonical": 123},
|
'server':{
|
||||||
|
"domains": {"canonical": 123}
|
||||||
|
},
|
||||||
"features": {"logout": True},
|
"features": {"logout": True},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,11 +64,13 @@ class TestDictRenderer(unittest.TestCase):
|
|||||||
# Combine quoted key, dot access and numeric index
|
# Combine quoted key, dot access and numeric index
|
||||||
data = {
|
data = {
|
||||||
"web-svc-file": {
|
"web-svc-file": {
|
||||||
|
'server':{
|
||||||
"domains": {
|
"domains": {
|
||||||
"canonical": ["file.example.com"]
|
"canonical": ["file.example.com"]
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"url": '<<[\'web-svc-file\'].domains.canonical[0]>>'
|
"url": '<<[\'web-svc-file\'].server.domains.canonical[0]>>'
|
||||||
}
|
}
|
||||||
rendered = self.renderer.render(data)
|
rendered = self.renderer.render(data)
|
||||||
self.assertEqual(rendered["url"], "file.example.com")
|
self.assertEqual(rendered["url"], "file.example.com")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user