mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-09-08 19:27:18 +02:00
Compare commits
8 Commits
ff18c7cd73
...
c8be88e3b1
Author | SHA1 | Date | |
---|---|---|---|
c8be88e3b1 | |||
5e315f9603 | |||
bab1035a24 | |||
30930c4136 | |||
bba663f95d | |||
c2f83abb60 | |||
3bc64023af | |||
d94254effb |
@@ -67,7 +67,7 @@ _applications_nextcloud_oidc_flavor: >-
|
|||||||
False,
|
False,
|
||||||
'oidc_login'
|
'oidc_login'
|
||||||
if applications
|
if applications
|
||||||
| get_app_conf('web-app-nextcloud','features.ldap',False)
|
| get_app_conf('web-app-nextcloud','features.ldap',False, True)
|
||||||
else 'sociallogin'
|
else 'sociallogin'
|
||||||
)
|
)
|
||||||
}}
|
}}
|
@@ -5,7 +5,7 @@ services:
|
|||||||
{% include 'roles/cmp-rdbms/templates/services/main.yml.j2' %}
|
{% include 'roles/cmp-rdbms/templates/services/main.yml.j2' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{# Load Redis #}
|
{# Load Redis #}
|
||||||
{% if applications | is_docker_service_enabled(application_id, 'redis') %}
|
{% if applications | is_docker_service_enabled(application_id, 'redis') or applications | get_app_conf(application_id, 'features.oauth2', False) %}
|
||||||
{% include 'roles/svc-db-redis/templates/service.yml.j2' %}
|
{% include 'roles/svc-db-redis/templates/service.yml.j2' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{# Load OAuth2 Proxy #}
|
{# Load OAuth2 Proxy #}
|
||||||
|
@@ -1,7 +1,11 @@
|
|||||||
{# This template needs to be included in docker-compose.yml which contain a database and additional volumes #}
|
{# This template needs to be included in docker-compose.yml which contain a database and additional volumes #}
|
||||||
volumes:
|
volumes:
|
||||||
{% if not applications | get_app_conf(application_id, 'features.central_database', False) and applications | get_app_conf(application_id, 'docker.services.database.enabled', False) %}
|
{% if applications | is_docker_service_enabled(application_id, 'database') and not applications | get_app_conf(application_id, 'features.central_database', False) %}
|
||||||
database:
|
database:
|
||||||
name: {{ database_volume }}
|
name: {{ database_volume }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if applications | is_docker_service_enabled(application_id, 'redis') or applications | get_app_conf(application_id, 'features.oauth2', False) %}
|
||||||
|
redis:
|
||||||
|
name: {{ application_id | get_entity_name }}
|
||||||
|
{% endif %}
|
||||||
{{ "\n" }}
|
{{ "\n" }}
|
@@ -26,6 +26,10 @@ server {
|
|||||||
|
|
||||||
{% include 'roles/srv-proxy-7-4-core/templates/location/proxy_basic.conf.j2' %}
|
{% include 'roles/srv-proxy-7-4-core/templates/location/proxy_basic.conf.j2' %}
|
||||||
|
|
||||||
|
{% if applications | get_app_conf(application_id, 'features.universal_logout', False) or domain == primary_domain %}
|
||||||
|
{% include 'roles/web-svc-logout/templates/logout-proxy.conf.j2' %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if ws_path is defined %}
|
{% if ws_path is defined %}
|
||||||
location {{ ws_path }} {
|
location {{ ws_path }} {
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
|
28
roles/srv-web-7-7-inj-logout/meta/main.yml
Normal file
28
roles/srv-web-7-7-inj-logout/meta/main.yml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
galaxy_info:
|
||||||
|
author: "Kevin Veen-Birkenbach"
|
||||||
|
description: "Injects a catcher, which catches the actions of all logout elements and redirects them to the central logout."
|
||||||
|
company: |
|
||||||
|
Kevin Veen-Birkenbach
|
||||||
|
Consulting & Coaching Solutions
|
||||||
|
https://www.veen.world
|
||||||
|
license: "CyMaIS NonCommercial License (CNCL)"
|
||||||
|
license_url: "https://s.veen.world/cncl"
|
||||||
|
min_ansible_version: "2.9"
|
||||||
|
platforms:
|
||||||
|
- name: Archlinux
|
||||||
|
versions:
|
||||||
|
- rolling
|
||||||
|
galaxy_tags:
|
||||||
|
- nginx
|
||||||
|
- javascript
|
||||||
|
- csp
|
||||||
|
- sub_filter
|
||||||
|
- injection
|
||||||
|
- global
|
||||||
|
repository: "https://s.veen.world/cymais"
|
||||||
|
documentation: "https://s.veen.world/cymais"
|
||||||
|
issue_tracker_url: "https://s.veen.world/cymaisissues"
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
- srv-web-7-4-core
|
13
roles/srv-web-7-7-inj-logout/tasks/main.yml
Normal file
13
roles/srv-web-7-7-inj-logout/tasks/main.yml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# run_once_srv_web_7_7_inj_javascript: deactivated
|
||||||
|
- name: "Load JavaScript code for '{{ application_id }}'"
|
||||||
|
set_fact:
|
||||||
|
javascript_code: "{{ lookup('template', modifier_javascript_template_file) }}"
|
||||||
|
|
||||||
|
- name: "Collapse Javascript code into one-liner for '{{application_id}}'"
|
||||||
|
set_fact:
|
||||||
|
javascript_code_one_liner: "{{ javascript_code | to_one_liner }}"
|
||||||
|
|
||||||
|
- name: "Append Javascript CSP hash for '{{application_id}}'"
|
||||||
|
set_fact:
|
||||||
|
applications: "{{ applications | append_csp_hash(application_id, javascript_code_one_liner) }}"
|
||||||
|
changed_when: false
|
1
roles/srv-web-7-7-inj-logout/templates/head_sub.j2
Normal file
1
roles/srv-web-7-7-inj-logout/templates/head_sub.j2
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<script>{{ javascript_code_one_liner | replace("'", "\\'") }}</script>
|
38
roles/srv-web-7-7-inj-logout/templates/logout.js.j2
Normal file
38
roles/srv-web-7-7-inj-logout/templates/logout.js.j2
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
(function() {
|
||||||
|
const logoutUrlBase = 'https://auth.cymais.cloud/realms/cymais.cloud/protocol/openid-connect/logout';
|
||||||
|
const redirectUri = encodeURIComponent('https://cymais.cloud');
|
||||||
|
const logoutUrl = `${logoutUrlBase}?redirect_uri=${redirectUri}`;
|
||||||
|
|
||||||
|
// Check if a string matches logout keywords
|
||||||
|
function matchesLogout(str) {
|
||||||
|
return str && /logout|log\s*out|abmelden/i.test(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if any attribute name contains "logout" (case-insensitive)
|
||||||
|
function hasLogoutAttribute(el) {
|
||||||
|
for (let attr of el.attributes) {
|
||||||
|
if (/logout/i.test(attr.name)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all elements
|
||||||
|
const allElements = document.querySelectorAll('*');
|
||||||
|
allElements.forEach(el => {
|
||||||
|
if (
|
||||||
|
matchesLogout(el.getAttribute('name')) ||
|
||||||
|
matchesLogout(el.id) ||
|
||||||
|
matchesLogout(el.className) ||
|
||||||
|
matchesLogout(el.innerText) ||
|
||||||
|
hasLogoutAttribute(el)
|
||||||
|
) {
|
||||||
|
el.style.cursor = 'pointer';
|
||||||
|
el.addEventListener('click', function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
window.location.href = logoutUrl;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
1
roles/srv-web-7-7-inj-logout/vars/main.yml
Normal file
1
roles/srv-web-7-7-inj-logout/vars/main.yml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
modifier_javascript_template_file: "{{ application_id | abs_role_path_by_application_id }}/templates/javascript.js.j2"
|
@@ -50,6 +50,6 @@
|
|||||||
changed_when: "'adding new entry' in ldapadd_result.stdout"
|
changed_when: "'adding new entry' in ldapadd_result.stdout"
|
||||||
failed_when: ldapadd_result.rc not in [0, 20, 68, 65]
|
failed_when: ldapadd_result.rc not in [0, 20, 68, 65]
|
||||||
listen:
|
listen:
|
||||||
- "Import data LDIF files"
|
- "Import groups LDIF files"
|
||||||
- "Import all LDIF files"
|
- "Import all LDIF files"
|
||||||
loop: "{{ query('fileglob', role_path ~ '/templates/ldif/groups/*.j2') | sort }}"
|
loop: "{{ query('fileglob', role_path ~ '/templates/ldif/groups/*.j2') | sort }}"
|
@@ -24,6 +24,5 @@
|
|||||||
- .:/var/www
|
- .:/var/www
|
||||||
|
|
||||||
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
|
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
|
||||||
redis:
|
|
||||||
|
|
||||||
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
|
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
|
@@ -18,3 +18,7 @@ docker:
|
|||||||
name: "baserow"
|
name: "baserow"
|
||||||
volumes:
|
volumes:
|
||||||
data: "baserow_data"
|
data: "baserow_data"
|
||||||
|
|
||||||
|
domains:
|
||||||
|
canonical:
|
||||||
|
- baserow.{{ primary_domain }}
|
||||||
|
@@ -14,6 +14,5 @@
|
|||||||
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
|
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
|
||||||
data:
|
data:
|
||||||
name: {{ baserow_volume }}
|
name: {{ baserow_volume }}
|
||||||
redis:
|
|
||||||
|
|
||||||
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
|
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
|
@@ -1,7 +1,6 @@
|
|||||||
{% include 'roles/docker-compose/templates/base.yml.j2' %}
|
{% include 'roles/docker-compose/templates/base.yml.j2' %}
|
||||||
|
|
||||||
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
|
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
|
||||||
redis:
|
|
||||||
|
|
||||||
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
|
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
|
||||||
{{ discourse_network }}:
|
{{ discourse_network }}:
|
||||||
|
@@ -1,2 +1,6 @@
|
|||||||
features:
|
features:
|
||||||
universal_logout: false # Just deactivated to oppress warnings, elk is anyhow not running
|
universal_logout: false # Just deactivated to oppress warnings, elk is anyhow not running
|
||||||
|
|
||||||
|
domains:
|
||||||
|
canonical:
|
||||||
|
- elk.{{ primary_domain }}
|
||||||
|
@@ -24,6 +24,8 @@ csp:
|
|||||||
domains:
|
domains:
|
||||||
aliases:
|
aliases:
|
||||||
- "crm.{{ primary_domain }}"
|
- "crm.{{ primary_domain }}"
|
||||||
|
canonical:
|
||||||
|
- espocrm.{{ primary_domain }}
|
||||||
email:
|
email:
|
||||||
from_name: "Customer Relationship Management ({{ primary_domain }})"
|
from_name: "Customer Relationship Management ({{ primary_domain }})"
|
||||||
docker:
|
docker:
|
||||||
|
@@ -63,7 +63,6 @@
|
|||||||
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
|
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
|
||||||
data:
|
data:
|
||||||
funkwhale_static_root:
|
funkwhale_static_root:
|
||||||
redis:
|
|
||||||
music:
|
music:
|
||||||
|
|
||||||
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
|
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
|
@@ -36,6 +36,8 @@ csp:
|
|||||||
domains:
|
domains:
|
||||||
aliases:
|
aliases:
|
||||||
- "git.{{ primary_domain }}"
|
- "git.{{ primary_domain }}"
|
||||||
|
canonical:
|
||||||
|
- gitea.{{ primary_domain }}
|
||||||
docker:
|
docker:
|
||||||
services:
|
services:
|
||||||
database:
|
database:
|
||||||
|
@@ -15,3 +15,7 @@ docker:
|
|||||||
version: "latest"
|
version: "latest"
|
||||||
credentials:
|
credentials:
|
||||||
initial_root_password: "{{ users.administrator.password }}"
|
initial_root_password: "{{ users.administrator.password }}"
|
||||||
|
|
||||||
|
domains:
|
||||||
|
canonical:
|
||||||
|
- gitlab.{{ primary_domain }}
|
||||||
|
@@ -16,7 +16,6 @@
|
|||||||
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
||||||
|
|
||||||
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
|
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
|
||||||
redis:
|
|
||||||
config:
|
config:
|
||||||
logs:
|
logs:
|
||||||
data:
|
data:
|
||||||
|
@@ -1,2 +1,6 @@
|
|||||||
features:
|
features:
|
||||||
universal_logout: true # Same like with elk, anyhow not active atm
|
universal_logout: true # Same like with elk, anyhow not active atm
|
||||||
|
|
||||||
|
domains:
|
||||||
|
canonical:
|
||||||
|
- jenkins.{{ primary_domain }}
|
||||||
|
@@ -11,7 +11,7 @@ features:
|
|||||||
port-ui-desktop: true
|
port-ui-desktop: true
|
||||||
ldap: true
|
ldap: true
|
||||||
central_database: false
|
central_database: false
|
||||||
oauth2: false
|
oauth2: true
|
||||||
universal_logout: true
|
universal_logout: true
|
||||||
csp:
|
csp:
|
||||||
flags:
|
flags:
|
||||||
@@ -25,4 +25,6 @@ csp:
|
|||||||
domains:
|
domains:
|
||||||
aliases:
|
aliases:
|
||||||
- "ldap.{{primary_domain}}"
|
- "ldap.{{primary_domain}}"
|
||||||
|
canonical:
|
||||||
|
- lam.{{ primary_domain }}
|
||||||
|
|
||||||
|
@@ -22,7 +22,8 @@ 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: {} # Urls under which the domain should be directly accessible
|
canonical:
|
||||||
|
- "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
|
||||||
rbac:
|
rbac:
|
||||||
roles: {}
|
roles: {}
|
||||||
|
@@ -100,7 +100,7 @@
|
|||||||
- "{{docker_compose.directories.volumes}}overrides/rspamd:/overrides:ro"
|
- "{{docker_compose.directories.volumes}}overrides/rspamd:/overrides:ro"
|
||||||
depends_on:
|
depends_on:
|
||||||
- front
|
- front
|
||||||
- redis
|
- redis
|
||||||
- antivirus
|
- antivirus
|
||||||
- resolver
|
- resolver
|
||||||
dns:
|
dns:
|
||||||
@@ -181,8 +181,6 @@
|
|||||||
name: {{ mailu_dkim }}
|
name: {{ mailu_dkim }}
|
||||||
dovecot_mail:
|
dovecot_mail:
|
||||||
name: {{ mailu_dovecot_mail }}
|
name: {{ mailu_dovecot_mail }}
|
||||||
redis:
|
|
||||||
name: {{ mailu_redis }}
|
|
||||||
|
|
||||||
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
|
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
|
||||||
radicale:
|
radicale:
|
||||||
|
@@ -44,7 +44,6 @@
|
|||||||
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
||||||
|
|
||||||
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
|
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
|
||||||
redis:
|
|
||||||
data:
|
data:
|
||||||
name: "{{ mastodon_volume }}"
|
name: "{{ mastodon_volume }}"
|
||||||
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
|
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
|
@@ -13,4 +13,9 @@ server {
|
|||||||
|
|
||||||
{% include 'roles/srv-web-7-7-inj-compose/templates/global.includes.conf.j2'%}
|
{% include 'roles/srv-web-7-7-inj-compose/templates/global.includes.conf.j2'%}
|
||||||
{% include 'roles/srv-proxy-7-4-core/templates/location/proxy_basic.conf.j2' %}
|
{% include 'roles/srv-proxy-7-4-core/templates/location/proxy_basic.conf.j2' %}
|
||||||
|
|
||||||
|
{% if applications | get_app_conf(application_id, 'features.universal_logout', False) %}
|
||||||
|
{% include 'roles/web-svc-logout/templates/logout-proxy.conf.j2' %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
}
|
}
|
@@ -15,3 +15,7 @@ docker:
|
|||||||
name: "mybb"
|
name: "mybb"
|
||||||
volumes:
|
volumes:
|
||||||
data: "mybb_data"
|
data: "mybb_data"
|
||||||
|
|
||||||
|
domains:
|
||||||
|
canonical:
|
||||||
|
- mybb.{{ primary_domain }}
|
||||||
|
@@ -71,6 +71,5 @@
|
|||||||
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
|
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
|
||||||
data:
|
data:
|
||||||
name: {{ nextcloud_volume }}
|
name: {{ nextcloud_volume }}
|
||||||
redis:
|
|
||||||
|
|
||||||
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
|
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
|
||||||
|
@@ -20,6 +20,10 @@ server
|
|||||||
|
|
||||||
{% include 'roles/srv-proxy-7-4-core/templates/location/proxy_basic.conf.j2' %}
|
{% include 'roles/srv-proxy-7-4-core/templates/location/proxy_basic.conf.j2' %}
|
||||||
|
|
||||||
|
{% if applications | get_app_conf(application_id, 'features.universal_logout', False) %}
|
||||||
|
{% include 'roles/web-svc-logout/templates/logout-proxy.conf.j2' %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
location ^~ /.well-known {
|
location ^~ /.well-known {
|
||||||
rewrite ^/\.well-known/host-meta\.json /public.php?service=host-meta-json last;
|
rewrite ^/\.well-known/host-meta\.json /public.php?service=host-meta-json last;
|
||||||
rewrite ^/\.well-known/host-meta /public.php?service=host-meta last;
|
rewrite ^/\.well-known/host-meta /public.php?service=host-meta last;
|
||||||
|
@@ -42,7 +42,7 @@ nextcloud_cron_name: "{{ applications | get_app_conf(
|
|||||||
nextcloud_talk_name: "{{ applications | get_app_conf(application_id, 'docker.services.talk.name', True) }}"
|
nextcloud_talk_name: "{{ applications | get_app_conf(application_id, 'docker.services.talk.name', True) }}"
|
||||||
nextcloud_talk_image: "{{ applications | get_app_conf(application_id, 'docker.services.talk.image', True) }}"
|
nextcloud_talk_image: "{{ applications | get_app_conf(application_id, 'docker.services.talk.image', True) }}"
|
||||||
nextcloud_talk_version: "{{ applications | get_app_conf(application_id, 'docker.services.talk.version', True) }}"
|
nextcloud_talk_version: "{{ applications | get_app_conf(application_id, 'docker.services.talk.version', True) }}"
|
||||||
nextcloud_talk_enabled: "{{ applications | get_app_conf(application_id, 'docker.services.talk.enabled', True) }}"
|
nextcloud_talk_enabled: "{{ applications | is_docker_service_enabled(application_id, 'talk') }}"
|
||||||
nextcloud_talk_stun_port: "{{ ports.public.stun[application_id] }}"
|
nextcloud_talk_stun_port: "{{ ports.public.stun[application_id] }}"
|
||||||
# nextcloud_talk_domain: "{{ domains[application_id].talk }}"
|
# nextcloud_talk_domain: "{{ domains[application_id].talk }}"
|
||||||
|
|
||||||
|
@@ -6,3 +6,7 @@ features:
|
|||||||
css: true
|
css: true
|
||||||
port-ui-desktop: false
|
port-ui-desktop: false
|
||||||
universal_logout: true
|
universal_logout: true
|
||||||
|
|
||||||
|
domains:
|
||||||
|
canonical:
|
||||||
|
- oauth2-proxy.{{ primary_domain }}
|
||||||
|
@@ -21,4 +21,7 @@ allowed_groups = {{ applications | get_app_conf(oauth2_proxy_applicat
|
|||||||
email_domains = ["*"]
|
email_domains = ["*"]
|
||||||
{% else %}
|
{% else %}
|
||||||
email_domains = "{{ primary_domain }}"
|
email_domains = "{{ primary_domain }}"
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
session_store_type = "redis"
|
||||||
|
redis_connection_url = "redis://redis:6379"
|
@@ -19,7 +19,6 @@
|
|||||||
assets:
|
assets:
|
||||||
data:
|
data:
|
||||||
name: {{ peertube_volume }}
|
name: {{ peertube_volume }}
|
||||||
redis:
|
|
||||||
config:
|
config:
|
||||||
|
|
||||||
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
|
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
|
@@ -27,6 +27,10 @@ server {
|
|||||||
proxy_pass http://127.0.0.1:{{ports.localhost.http[application_id]}};
|
proxy_pass http://127.0.0.1:{{ports.localhost.http[application_id]}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{% if applications | get_app_conf(application_id, 'features.universal_logout', False) %}
|
||||||
|
{% include 'roles/web-svc-logout/templates/logout-proxy.conf.j2' %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
try_files /dev/null @api;
|
try_files /dev/null @api;
|
||||||
}
|
}
|
||||||
|
@@ -26,3 +26,7 @@ docker:
|
|||||||
services:
|
services:
|
||||||
database:
|
database:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
||||||
|
domains:
|
||||||
|
canonical:
|
||||||
|
- pgadmin.{{ primary_domain }}
|
||||||
|
@@ -11,3 +11,7 @@ features:
|
|||||||
ldap: true
|
ldap: true
|
||||||
oauth2: true
|
oauth2: true
|
||||||
universal_logout: true
|
universal_logout: true
|
||||||
|
|
||||||
|
domains:
|
||||||
|
canonical:
|
||||||
|
- phpldapadmin.{{ primary_domain }}
|
||||||
|
@@ -22,6 +22,8 @@ domains:
|
|||||||
aliases:
|
aliases:
|
||||||
- "mysql.{{ primary_domain }}"
|
- "mysql.{{ primary_domain }}"
|
||||||
- "mariadb.{{ primary_domain }}"
|
- "mariadb.{{ primary_domain }}"
|
||||||
|
canonical:
|
||||||
|
- phpmyadmin.{{ primary_domain }}
|
||||||
docker:
|
docker:
|
||||||
services:
|
services:
|
||||||
database:
|
database:
|
||||||
|
@@ -30,7 +30,6 @@
|
|||||||
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
||||||
|
|
||||||
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
|
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
|
||||||
redis:
|
|
||||||
data:
|
data:
|
||||||
name: {{ pixelfed_volume }}
|
name: {{ pixelfed_volume }}
|
||||||
|
|
||||||
|
@@ -18,11 +18,12 @@ 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
|
||||||
universal_logout: true
|
universal_logout: true
|
||||||
csp:
|
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: {} # Urls under which the domain should be directly accessible
|
canonical:
|
||||||
|
- "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
|
||||||
rbac:
|
rbac:
|
||||||
roles: {}
|
roles: {}
|
||||||
|
@@ -18,7 +18,6 @@
|
|||||||
# Compose Configuration
|
# Compose Configuration
|
||||||
|
|
||||||
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
|
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
|
||||||
redis:
|
|
||||||
data:
|
data:
|
||||||
name: "{{ snipe_it_volume }}"
|
name: "{{ snipe_it_volume }}"
|
||||||
|
|
||||||
|
@@ -13,3 +13,7 @@ features:
|
|||||||
# users:
|
# users:
|
||||||
# administrator:
|
# administrator:
|
||||||
# username: "{{ users.administrator.username }}"
|
# username: "{{ users.administrator.username }}"
|
||||||
|
|
||||||
|
domains:
|
||||||
|
canonical:
|
||||||
|
- syncope.{{ primary_domain }}
|
||||||
|
@@ -1,3 +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:
|
||||||
universal_logout: false # Reactivated as soon as xmpp is fully implemented
|
universal_logout: false # Reactivated as soon as xmpp is fully implemented
|
||||||
|
|
||||||
|
domains:
|
||||||
|
canonical:
|
||||||
|
- xmpp.{{ primary_domain }}
|
||||||
|
@@ -1,2 +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']domains.canonical[0] >>/assets"
|
||||||
|
|
||||||
|
domains:
|
||||||
|
canonical:
|
||||||
|
- asset.{{ primary_domain }}
|
||||||
|
@@ -2,7 +2,7 @@ import unittest
|
|||||||
import yaml
|
import yaml
|
||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from collections import Counter
|
from collections import Counter, defaultdict
|
||||||
|
|
||||||
class TestDomainUniqueness(unittest.TestCase):
|
class TestDomainUniqueness(unittest.TestCase):
|
||||||
def test_no_duplicate_domains(self):
|
def test_no_duplicate_domains(self):
|
||||||
@@ -22,7 +22,8 @@ class TestDomainUniqueness(unittest.TestCase):
|
|||||||
cfg = yaml.safe_load(yaml_file.read_text(encoding='utf-8')) or {}
|
cfg = yaml.safe_load(yaml_file.read_text(encoding='utf-8')) or {}
|
||||||
apps = cfg.get('defaults_applications', {})
|
apps = cfg.get('defaults_applications', {})
|
||||||
|
|
||||||
all_domains = []
|
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('domains', {})
|
||||||
|
|
||||||
@@ -32,7 +33,10 @@ class TestDomainUniqueness(unittest.TestCase):
|
|||||||
values = list(canonical.values())
|
values = list(canonical.values())
|
||||||
else:
|
else:
|
||||||
values = canonical or []
|
values = canonical or []
|
||||||
all_domains.extend(values)
|
|
||||||
|
for d in values:
|
||||||
|
if isinstance(d, str) and d.strip():
|
||||||
|
domain_to_apps[d].add(app_name)
|
||||||
|
|
||||||
# aliases entries may be a list or a mapping
|
# aliases entries may be a list or a mapping
|
||||||
aliases = domains_cfg.get('aliases', [])
|
aliases = domains_cfg.get('aliases', [])
|
||||||
@@ -40,16 +44,16 @@ class TestDomainUniqueness(unittest.TestCase):
|
|||||||
values = list(aliases.values())
|
values = list(aliases.values())
|
||||||
else:
|
else:
|
||||||
values = aliases or []
|
values = aliases or []
|
||||||
all_domains.extend(values)
|
|
||||||
|
|
||||||
# Filter out any empty or non-string entries
|
for d in values:
|
||||||
domain_list = [d for d in all_domains if isinstance(d, str) and d.strip()]
|
if isinstance(d, str) and d.strip():
|
||||||
counts = Counter(domain_list)
|
domain_to_apps[d].add(app_name)
|
||||||
|
|
||||||
# Find duplicates
|
# Find duplicates: domains that appear in more than one app
|
||||||
duplicates = [domain for domain, count in counts.items() if count > 1]
|
duplicates = {domain: list(apps) for domain, apps in domain_to_apps.items() if len(apps) > 1}
|
||||||
if duplicates:
|
if duplicates:
|
||||||
self.fail(f"Duplicate domain entries found: {duplicates}\n (May 'make build' solves this issue.)")
|
details = "\n".join(f"Domain '{domain}' is used in applications: {apps}" for domain, apps in duplicates.items())
|
||||||
|
self.fail(f"Duplicate domain entries found:\n{details}\n(Maybe 'make build' solves this issue.)")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
36
tests/integration/test_domains_canonical.py
Normal file
36
tests/integration/test_domains_canonical.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import unittest
|
||||||
|
import yaml
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
|
||||||
|
class TestWebRolesDomains(unittest.TestCase):
|
||||||
|
def test_canonical_domains_present_and_not_empty(self):
|
||||||
|
"""
|
||||||
|
Check all roles/web-*/config/main.yml files:
|
||||||
|
- must have domains.canonical defined
|
||||||
|
- domains.canonical must not be empty dict, empty list, or empty string
|
||||||
|
"""
|
||||||
|
role_config_paths = glob.glob("roles/web-*/config/main.yml")
|
||||||
|
self.assertTrue(role_config_paths, "No roles/web-*/config/main.yml files found.")
|
||||||
|
|
||||||
|
for path in role_config_paths:
|
||||||
|
with self.subTest(role_config=path):
|
||||||
|
with open(path, "r") as f:
|
||||||
|
data = yaml.safe_load(f)
|
||||||
|
|
||||||
|
self.assertIsInstance(data, dict, f"YAML root is not a dict in {path}")
|
||||||
|
|
||||||
|
domains = data.get("domains")
|
||||||
|
self.assertIsNotNone(domains, f"'domains' section missing in {path}")
|
||||||
|
self.assertIsInstance(domains, dict, f"'domains' must be a dict in {path}")
|
||||||
|
|
||||||
|
canonical = domains.get("canonical")
|
||||||
|
self.assertIsNotNone(canonical, f"'domains.canonical' missing in {path}")
|
||||||
|
|
||||||
|
# Check for emptiness
|
||||||
|
empty_values = [{}, [], ""]
|
||||||
|
self.assertNotIn(canonical, empty_values,
|
||||||
|
f"'domains.canonical' in {path} must not be empty dict, list, or empty string")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
Reference in New Issue
Block a user