mirror of
				https://github.com/kevinveenbirkenbach/computer-playbook.git
				synced 2025-11-04 04:08:15 +00: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,
 | 
			
		||||
          'oidc_login'
 | 
			
		||||
          if applications
 | 
			
		||||
            | get_app_conf('web-app-nextcloud','features.ldap',False)
 | 
			
		||||
            | get_app_conf('web-app-nextcloud','features.ldap',False, True)
 | 
			
		||||
          else 'sociallogin'
 | 
			
		||||
        )
 | 
			
		||||
  }}
 | 
			
		||||
@@ -5,7 +5,7 @@ services:
 | 
			
		||||
{% include 'roles/cmp-rdbms/templates/services/main.yml.j2' %}
 | 
			
		||||
{% endif %}
 | 
			
		||||
{# 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' %}
 | 
			
		||||
{% endif %}
 | 
			
		||||
{# Load OAuth2 Proxy #}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,11 @@
 | 
			
		||||
{# This template needs to be included in docker-compose.yml which contain a database and additional 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:
 | 
			
		||||
    name: {{ database_volume }}
 | 
			
		||||
{% 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" }}
 | 
			
		||||
@@ -26,6 +26,10 @@ server {
 | 
			
		||||
 | 
			
		||||
  {% 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 %}
 | 
			
		||||
    location {{ ws_path }} {
 | 
			
		||||
    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"
 | 
			
		||||
  failed_when: ldapadd_result.rc not in [0, 20, 68, 65]
 | 
			
		||||
  listen:
 | 
			
		||||
    - "Import data LDIF files"
 | 
			
		||||
    - "Import groups LDIF files"
 | 
			
		||||
    - "Import all LDIF files"
 | 
			
		||||
  loop: "{{ query('fileglob', role_path ~ '/templates/ldif/groups/*.j2') | sort }}"
 | 
			
		||||
@@ -24,6 +24,5 @@
 | 
			
		||||
      - .:/var/www
 | 
			
		||||
 | 
			
		||||
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
 | 
			
		||||
  redis:
 | 
			
		||||
 | 
			
		||||
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
 | 
			
		||||
@@ -18,3 +18,7 @@ docker:
 | 
			
		||||
      name:               "baserow"
 | 
			
		||||
    volumes:
 | 
			
		||||
      data:               "baserow_data"
 | 
			
		||||
 | 
			
		||||
domains:
 | 
			
		||||
  canonical:
 | 
			
		||||
    - baserow.{{ primary_domain }}
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,5 @@
 | 
			
		||||
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
 | 
			
		||||
  data:
 | 
			
		||||
    name: {{ baserow_volume }}
 | 
			
		||||
  redis:
 | 
			
		||||
 | 
			
		||||
{% 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/volumes.yml.j2' %}
 | 
			
		||||
  redis:
 | 
			
		||||
 | 
			
		||||
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
 | 
			
		||||
  {{ discourse_network }}:
 | 
			
		||||
 
 | 
			
		||||
@@ -1,2 +1,6 @@
 | 
			
		||||
features:
 | 
			
		||||
  universal_logout: false # Just deactivated to oppress warnings, elk is anyhow not running
 | 
			
		||||
 | 
			
		||||
domains:
 | 
			
		||||
  canonical:
 | 
			
		||||
    - elk.{{ primary_domain }}
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,8 @@ csp:
 | 
			
		||||
domains:
 | 
			
		||||
  aliases:
 | 
			
		||||
    - "crm.{{ primary_domain }}"
 | 
			
		||||
  canonical:
 | 
			
		||||
    - espocrm.{{ primary_domain }}
 | 
			
		||||
email:
 | 
			
		||||
  from_name: "Customer Relationship Management ({{ primary_domain }})"
 | 
			
		||||
docker:
 | 
			
		||||
 
 | 
			
		||||
@@ -63,7 +63,6 @@
 | 
			
		||||
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
 | 
			
		||||
  data:
 | 
			
		||||
  funkwhale_static_root:
 | 
			
		||||
  redis:
 | 
			
		||||
  music:
 | 
			
		||||
 | 
			
		||||
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
 | 
			
		||||
@@ -36,6 +36,8 @@ csp:
 | 
			
		||||
domains:
 | 
			
		||||
  aliases:
 | 
			
		||||
    - "git.{{ primary_domain }}"
 | 
			
		||||
  canonical:
 | 
			
		||||
    - gitea.{{ primary_domain }}
 | 
			
		||||
docker:
 | 
			
		||||
  services:
 | 
			
		||||
    database:
 | 
			
		||||
 
 | 
			
		||||
@@ -15,3 +15,7 @@ docker:
 | 
			
		||||
      version:  "latest"
 | 
			
		||||
credentials:
 | 
			
		||||
  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-compose/templates/volumes.yml.j2' %}
 | 
			
		||||
  redis:
 | 
			
		||||
  config:
 | 
			
		||||
  logs:
 | 
			
		||||
  data:
 | 
			
		||||
 
 | 
			
		||||
@@ -1,2 +1,6 @@
 | 
			
		||||
features:
 | 
			
		||||
  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
 | 
			
		||||
  ldap:                         true
 | 
			
		||||
  central_database:             false
 | 
			
		||||
  oauth2:                       false
 | 
			
		||||
  oauth2:                       true
 | 
			
		||||
  universal_logout:             true
 | 
			
		||||
csp:
 | 
			
		||||
  flags:
 | 
			
		||||
@@ -25,4 +25,6 @@ csp:
 | 
			
		||||
domains:
 | 
			
		||||
  aliases:
 | 
			
		||||
    - "ldap.{{primary_domain}}"
 | 
			
		||||
  canonical:
 | 
			
		||||
    - lam.{{ primary_domain }}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,8 @@ csp:
 | 
			
		||||
  whitelist:        {}      # URL's which should be whitelisted
 | 
			
		||||
  flags:            {}      # Flags which should be set
 | 
			
		||||
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
 | 
			
		||||
rbac:
 | 
			
		||||
  roles: {}
 | 
			
		||||
 
 | 
			
		||||
@@ -100,7 +100,7 @@
 | 
			
		||||
      - "{{docker_compose.directories.volumes}}overrides/rspamd:/overrides:ro"
 | 
			
		||||
    depends_on:
 | 
			
		||||
      - front
 | 
			
		||||
      - redis 
 | 
			
		||||
      - redis
 | 
			
		||||
      - antivirus
 | 
			
		||||
      - resolver
 | 
			
		||||
    dns:
 | 
			
		||||
@@ -181,8 +181,6 @@
 | 
			
		||||
    name: {{ mailu_dkim }}
 | 
			
		||||
  dovecot_mail:
 | 
			
		||||
    name: {{ mailu_dovecot_mail }}
 | 
			
		||||
  redis:
 | 
			
		||||
    name: {{ mailu_redis }}
 | 
			
		||||
 | 
			
		||||
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
 | 
			
		||||
  radicale:
 | 
			
		||||
 
 | 
			
		||||
@@ -44,7 +44,6 @@
 | 
			
		||||
{% include 'roles/docker-container/templates/networks.yml.j2' %}
 | 
			
		||||
 | 
			
		||||
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
 | 
			
		||||
  redis:
 | 
			
		||||
  data:
 | 
			
		||||
    name: "{{ mastodon_volume }}"
 | 
			
		||||
{% 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-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"
 | 
			
		||||
  volumes:
 | 
			
		||||
    data:       "mybb_data"
 | 
			
		||||
 | 
			
		||||
domains:
 | 
			
		||||
  canonical:
 | 
			
		||||
    - mybb.{{ primary_domain }}
 | 
			
		||||
 
 | 
			
		||||
@@ -71,6 +71,5 @@
 | 
			
		||||
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
 | 
			
		||||
  data:
 | 
			
		||||
    name: {{ nextcloud_volume }}
 | 
			
		||||
  redis:
 | 
			
		||||
 | 
			
		||||
{% 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' %}
 | 
			
		||||
 | 
			
		||||
  {% 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 {
 | 
			
		||||
    rewrite ^/\.well-known/host-meta\.json  /public.php?service=host-meta-json  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_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_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_domain:                          "{{ domains[application_id].talk }}"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,3 +6,7 @@ features:
 | 
			
		||||
  css: true
 | 
			
		||||
  port-ui-desktop: false
 | 
			
		||||
  universal_logout: true
 | 
			
		||||
 | 
			
		||||
domains:
 | 
			
		||||
  canonical:
 | 
			
		||||
    - oauth2-proxy.{{ primary_domain }}
 | 
			
		||||
 
 | 
			
		||||
@@ -21,4 +21,7 @@ allowed_groups          =   {{ applications | get_app_conf(oauth2_proxy_applicat
 | 
			
		||||
email_domains           =   ["*"]
 | 
			
		||||
{% else %}
 | 
			
		||||
email_domains           =   "{{ primary_domain }}"
 | 
			
		||||
{% endif %}
 | 
			
		||||
{% endif %}
 | 
			
		||||
 | 
			
		||||
session_store_type       = "redis"
 | 
			
		||||
redis_connection_url     = "redis://redis:6379"
 | 
			
		||||
@@ -19,7 +19,6 @@
 | 
			
		||||
  assets:
 | 
			
		||||
  data:
 | 
			
		||||
    name: {{ peertube_volume }}
 | 
			
		||||
  redis:
 | 
			
		||||
  config:
 | 
			
		||||
 | 
			
		||||
{% 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]}};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  {% if applications | get_app_conf(application_id, 'features.universal_logout', False) %}
 | 
			
		||||
    {% include 'roles/web-svc-logout/templates/logout-proxy.conf.j2' %}
 | 
			
		||||
  {% endif %}
 | 
			
		||||
 | 
			
		||||
  location / {
 | 
			
		||||
    try_files /dev/null @api;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -26,3 +26,7 @@ docker:
 | 
			
		||||
  services:
 | 
			
		||||
    database:
 | 
			
		||||
      enabled: true
 | 
			
		||||
 | 
			
		||||
domains:
 | 
			
		||||
  canonical:
 | 
			
		||||
    - pgadmin.{{ primary_domain }}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,3 +11,7 @@ features:
 | 
			
		||||
  ldap:               true
 | 
			
		||||
  oauth2:             true
 | 
			
		||||
  universal_logout:   true
 | 
			
		||||
 | 
			
		||||
domains:
 | 
			
		||||
  canonical:
 | 
			
		||||
    - phpldapadmin.{{ primary_domain }}
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,8 @@ domains:
 | 
			
		||||
  aliases:
 | 
			
		||||
    - "mysql.{{ primary_domain }}"
 | 
			
		||||
    - "mariadb.{{ primary_domain }}"
 | 
			
		||||
  canonical:
 | 
			
		||||
    - phpmyadmin.{{ primary_domain }}
 | 
			
		||||
docker:
 | 
			
		||||
  services:
 | 
			
		||||
    database:
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,6 @@
 | 
			
		||||
{% include 'roles/docker-container/templates/networks.yml.j2' %}
 | 
			
		||||
 | 
			
		||||
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
 | 
			
		||||
  redis:
 | 
			
		||||
  data:
 | 
			
		||||
    name: {{ pixelfed_volume }}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -18,11 +18,12 @@ features:
 | 
			
		||||
  oauth2:           false   # Enable the OAuth2-Proy
 | 
			
		||||
  javascript:       false   # Enables the custom JS in the javascript.js.j2 file   
 | 
			
		||||
  universal_logout: true
 | 
			
		||||
csp:                        
 | 
			
		||||
  whitelist:        {}      # URL's which should be whitelisted           
 | 
			
		||||
  flags:            {}      # Flags which should be set 
 | 
			
		||||
csp:
 | 
			
		||||
  whitelist:        {}      # URL's which should be whitelisted
 | 
			
		||||
  flags:            {}      # Flags which should be set
 | 
			
		||||
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
 | 
			
		||||
rbac:
 | 
			
		||||
  roles: {}
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,6 @@
 | 
			
		||||
# Compose Configuration
 | 
			
		||||
 | 
			
		||||
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
 | 
			
		||||
  redis:
 | 
			
		||||
  data:
 | 
			
		||||
    name: "{{ snipe_it_volume }}"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -13,3 +13,7 @@ features:
 | 
			
		||||
#     users:
 | 
			
		||||
#       administrator:
 | 
			
		||||
#         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 
 | 
			
		||||
features:
 | 
			
		||||
  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"
 | 
			
		||||
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 subprocess
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from collections import Counter
 | 
			
		||||
from collections import Counter, defaultdict
 | 
			
		||||
 | 
			
		||||
class TestDomainUniqueness(unittest.TestCase):
 | 
			
		||||
    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 {}
 | 
			
		||||
        apps = cfg.get('defaults_applications', {})
 | 
			
		||||
 | 
			
		||||
        all_domains = []
 | 
			
		||||
        domain_to_apps = defaultdict(set)
 | 
			
		||||
 | 
			
		||||
        for app_name, app_cfg in apps.items():
 | 
			
		||||
            domains_cfg = app_cfg.get('domains', {})
 | 
			
		||||
 | 
			
		||||
@@ -32,7 +33,10 @@ class TestDomainUniqueness(unittest.TestCase):
 | 
			
		||||
                values = list(canonical.values())
 | 
			
		||||
            else:
 | 
			
		||||
                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 = domains_cfg.get('aliases', [])
 | 
			
		||||
@@ -40,16 +44,16 @@ class TestDomainUniqueness(unittest.TestCase):
 | 
			
		||||
                values = list(aliases.values())
 | 
			
		||||
            else:
 | 
			
		||||
                values = aliases or []
 | 
			
		||||
            all_domains.extend(values)
 | 
			
		||||
 | 
			
		||||
        # Filter out any empty or non-string entries
 | 
			
		||||
        domain_list = [d for d in all_domains if isinstance(d, str) and d.strip()]
 | 
			
		||||
        counts = Counter(domain_list)
 | 
			
		||||
            for d in values:
 | 
			
		||||
                if isinstance(d, str) and d.strip():
 | 
			
		||||
                    domain_to_apps[d].add(app_name)
 | 
			
		||||
 | 
			
		||||
        # Find duplicates
 | 
			
		||||
        duplicates = [domain for domain, count in counts.items() if count > 1]
 | 
			
		||||
        # Find duplicates: domains that appear in more than one app
 | 
			
		||||
        duplicates = {domain: list(apps) for domain, apps in domain_to_apps.items() if len(apps) > 1}
 | 
			
		||||
        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__":
 | 
			
		||||
    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