mirror of
				https://github.com/kevinveenbirkenbach/computer-playbook.git
				synced 2025-11-04 04:08:15 +00:00 
			
		
		
		
	Optimized code and solved bugs
This commit is contained in:
		@@ -1,8 +1,8 @@
 | 
			
		||||
from ansible.errors import AnsibleFilterError
 | 
			
		||||
import os
 | 
			
		||||
import sys
 | 
			
		||||
import yaml
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FilterModule(object):
 | 
			
		||||
    def filters(self):
 | 
			
		||||
        return {
 | 
			
		||||
@@ -15,48 +15,67 @@ class FilterModule(object):
 | 
			
		||||
          1) directly in group_names, or
 | 
			
		||||
          2) the application_id of any role reachable (recursively)
 | 
			
		||||
             from any group in group_names via meta/dependencies.
 | 
			
		||||
        Expects:
 | 
			
		||||
          - applications: dict mapping application_id → config
 | 
			
		||||
          - group_names: list of active role names
 | 
			
		||||
        """
 | 
			
		||||
        # validate inputs
 | 
			
		||||
        self._validate_inputs(applications, group_names)
 | 
			
		||||
 | 
			
		||||
        roles_dir = self._get_roles_directory()
 | 
			
		||||
 | 
			
		||||
        included_roles = self._collect_reachable_roles(group_names, roles_dir)
 | 
			
		||||
        included_app_ids = self._gather_application_ids(included_roles, roles_dir)
 | 
			
		||||
 | 
			
		||||
        return self._filter_applications(applications, group_names, included_app_ids)
 | 
			
		||||
 | 
			
		||||
    def _validate_inputs(self, applications, group_names):
 | 
			
		||||
        """Validate the inputs for correct types."""
 | 
			
		||||
        if not isinstance(applications, dict):
 | 
			
		||||
            raise AnsibleFilterError(f"Expected applications as dict, got {type(applications).__name__}")
 | 
			
		||||
        if not isinstance(group_names, (list, tuple)):
 | 
			
		||||
            raise AnsibleFilterError(f"Expected group_names as list/tuple, got {type(group_names).__name__}")
 | 
			
		||||
 | 
			
		||||
        # locate roles directory (assume plugin sits in filter_plugins/)
 | 
			
		||||
    def _get_roles_directory(self):
 | 
			
		||||
        """Locate and return the roles directory."""
 | 
			
		||||
        plugin_dir = os.path.dirname(__file__)
 | 
			
		||||
        project_root = os.path.abspath(os.path.join(plugin_dir, '..'))
 | 
			
		||||
        roles_dir = os.path.join(project_root, 'roles')
 | 
			
		||||
 | 
			
		||||
        # recursively collect all roles reachable from the given groups
 | 
			
		||||
        def collect_roles(role, seen):
 | 
			
		||||
            if role in seen:
 | 
			
		||||
                return
 | 
			
		||||
            seen.add(role)
 | 
			
		||||
            meta_file = os.path.join(roles_dir, role, 'meta', 'main.yml')
 | 
			
		||||
            if not os.path.isfile(meta_file):
 | 
			
		||||
                return
 | 
			
		||||
            try:
 | 
			
		||||
                with open(meta_file) as f:
 | 
			
		||||
                    meta = yaml.safe_load(f) or {}
 | 
			
		||||
            except Exception:
 | 
			
		||||
                return
 | 
			
		||||
            for dep in meta.get('dependencies', []):
 | 
			
		||||
                if isinstance(dep, str):
 | 
			
		||||
                    dep_name = dep
 | 
			
		||||
                elif isinstance(dep, dict):
 | 
			
		||||
                    dep_name = dep.get('role') or dep.get('name')
 | 
			
		||||
                else:
 | 
			
		||||
                    continue
 | 
			
		||||
                collect_roles(dep_name, seen)
 | 
			
		||||
        return os.path.join(project_root, 'roles')
 | 
			
		||||
 | 
			
		||||
    def _collect_reachable_roles(self, group_names, roles_dir):
 | 
			
		||||
        """Recursively collect all roles reachable from the given groups via meta/dependencies."""
 | 
			
		||||
        included_roles = set()
 | 
			
		||||
        for grp in group_names:
 | 
			
		||||
            collect_roles(grp, included_roles)
 | 
			
		||||
        for group in group_names:
 | 
			
		||||
            self._collect_roles_from_group(group, included_roles, roles_dir)
 | 
			
		||||
        return included_roles
 | 
			
		||||
 | 
			
		||||
        # gather application_ids from those roles
 | 
			
		||||
    def _collect_roles_from_group(self, group, seen, roles_dir):
 | 
			
		||||
        """Recursively collect roles from a specific group."""
 | 
			
		||||
        if group in seen:
 | 
			
		||||
            return
 | 
			
		||||
        seen.add(group)
 | 
			
		||||
 | 
			
		||||
        meta_file = os.path.join(roles_dir, group, 'meta', 'main.yml')
 | 
			
		||||
        if not os.path.isfile(meta_file):
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            with open(meta_file) as f:
 | 
			
		||||
                meta = yaml.safe_load(f) or {}
 | 
			
		||||
        except Exception:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        for dep in meta.get('dependencies', []):
 | 
			
		||||
            dep_name = self._get_dependency_name(dep)
 | 
			
		||||
            if dep_name:
 | 
			
		||||
                self._collect_roles_from_group(dep_name, seen, roles_dir)
 | 
			
		||||
 | 
			
		||||
    def _get_dependency_name(self, dependency):
 | 
			
		||||
        """Extract the dependency role name from the meta data."""
 | 
			
		||||
        if isinstance(dependency, str):
 | 
			
		||||
            return dependency
 | 
			
		||||
        elif isinstance(dependency, dict):
 | 
			
		||||
            return dependency.get('role') or dependency.get('name')
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    def _gather_application_ids(self, included_roles, roles_dir):
 | 
			
		||||
        """Gather application_ids from the roles."""
 | 
			
		||||
        included_app_ids = set()
 | 
			
		||||
        for role in included_roles:
 | 
			
		||||
            vars_file = os.path.join(roles_dir, role, 'vars', 'main.yml')
 | 
			
		||||
@@ -67,14 +86,17 @@ class FilterModule(object):
 | 
			
		||||
                    vars_data = yaml.safe_load(f) or {}
 | 
			
		||||
            except Exception:
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            app_id = vars_data.get('application_id')
 | 
			
		||||
            if isinstance(app_id, str) and app_id:
 | 
			
		||||
                included_app_ids.add(app_id)
 | 
			
		||||
 | 
			
		||||
        # build filtered result: include any application whose key is in group_names or in included_app_ids
 | 
			
		||||
        return included_app_ids
 | 
			
		||||
 | 
			
		||||
    def _filter_applications(self, applications, group_names, included_app_ids):
 | 
			
		||||
        """Filter and return the applications that match the conditions."""
 | 
			
		||||
        result = {}
 | 
			
		||||
        for app_key, cfg in applications.items():
 | 
			
		||||
            if app_key in group_names or app_key in included_app_ids:
 | 
			
		||||
                result[app_key] = cfg
 | 
			
		||||
 | 
			
		||||
        return result
 | 
			
		||||
        return result
 | 
			
		||||
 
 | 
			
		||||
@@ -5,71 +5,79 @@ class FilterModule(object):
 | 
			
		||||
        return {'canonical_domains_map': self.canonical_domains_map}
 | 
			
		||||
 | 
			
		||||
    def canonical_domains_map(self, apps, primary_domain):
 | 
			
		||||
        def parse_entry(domains_cfg, key, app_id):
 | 
			
		||||
            if key not in domains_cfg:
 | 
			
		||||
                return None
 | 
			
		||||
            entry = domains_cfg[key]
 | 
			
		||||
            if isinstance(entry, dict):
 | 
			
		||||
                values = list(entry.values())
 | 
			
		||||
            elif isinstance(entry, list):
 | 
			
		||||
                values = entry
 | 
			
		||||
            else:
 | 
			
		||||
                raise AnsibleFilterError(
 | 
			
		||||
                    f"Unexpected type for 'domains.{key}' in application '{app_id}': {type(entry).__name__}"
 | 
			
		||||
                )
 | 
			
		||||
            for d in values:
 | 
			
		||||
                if not isinstance(d, str) or not d.strip():
 | 
			
		||||
                    raise AnsibleFilterError(
 | 
			
		||||
                        f"Invalid domain entry in '{key}' for application '{app_id}': {d!r}"
 | 
			
		||||
                    )
 | 
			
		||||
            return values
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        Maps applications to their canonical domains, checking for conflicts 
 | 
			
		||||
        and ensuring all domains are valid and unique across applications.
 | 
			
		||||
        """
 | 
			
		||||
        result = {}
 | 
			
		||||
        seen = {}
 | 
			
		||||
        seen_domains = {}
 | 
			
		||||
 | 
			
		||||
        for app_id, cfg in apps.items():
 | 
			
		||||
            domains_cfg = cfg.get('domains')
 | 
			
		||||
            if not domains_cfg or 'canonical' not in domains_cfg:
 | 
			
		||||
                default = f"{app_id}.{primary_domain}"
 | 
			
		||||
                if default in seen:
 | 
			
		||||
                    raise AnsibleFilterError(
 | 
			
		||||
                        f"Domain '{default}' is already configured for '{seen[default]}' and '{app_id}'"
 | 
			
		||||
                    )
 | 
			
		||||
                seen[default] = app_id
 | 
			
		||||
                result[app_id] = [default]
 | 
			
		||||
                self._add_default_domain(app_id, primary_domain, seen_domains, result)
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            entry = domains_cfg['canonical']
 | 
			
		||||
 | 
			
		||||
            if isinstance(entry, dict):
 | 
			
		||||
                for name, domain in entry.items():
 | 
			
		||||
                    if not isinstance(domain, str) or not domain.strip():
 | 
			
		||||
                        raise AnsibleFilterError(
 | 
			
		||||
                            f"Invalid domain entry in 'canonical' for application '{app_id}': {domain!r}"
 | 
			
		||||
                        )
 | 
			
		||||
                    if domain in seen:
 | 
			
		||||
                        raise AnsibleFilterError(
 | 
			
		||||
                            f"Domain '{domain}' is already configured for '{seen[domain]}' and '{app_id}'"
 | 
			
		||||
                        )
 | 
			
		||||
                    seen[domain] = app_id
 | 
			
		||||
                result[app_id] = entry.copy()
 | 
			
		||||
 | 
			
		||||
            elif isinstance(entry, list):
 | 
			
		||||
                for domain in entry:
 | 
			
		||||
                    if not isinstance(domain, str) or not domain.strip():
 | 
			
		||||
                        raise AnsibleFilterError(
 | 
			
		||||
                            f"Invalid domain entry in 'canonical' for application '{app_id}': {domain!r}"
 | 
			
		||||
                        )
 | 
			
		||||
                    if domain in seen:
 | 
			
		||||
                        raise AnsibleFilterError(
 | 
			
		||||
                            f"Domain '{domain}' is already configured for '{seen[domain]}' and '{app_id}'"
 | 
			
		||||
                        )
 | 
			
		||||
                    seen[domain] = app_id
 | 
			
		||||
                result[app_id] = list(entry)
 | 
			
		||||
 | 
			
		||||
            else:
 | 
			
		||||
                raise AnsibleFilterError(
 | 
			
		||||
                    f"Unexpected type for 'domains.canonical' in application '{app_id}': {type(entry).__name__}"
 | 
			
		||||
                )
 | 
			
		||||
            canonical_domains = domains_cfg['canonical']
 | 
			
		||||
            self._process_canonical_domains(app_id, canonical_domains, seen_domains, result)
 | 
			
		||||
 | 
			
		||||
        return result
 | 
			
		||||
 | 
			
		||||
    def _add_default_domain(self, app_id, primary_domain, seen_domains, result):
 | 
			
		||||
        """
 | 
			
		||||
        Add the default domain for an application if no canonical domains are defined.
 | 
			
		||||
        Ensures the domain is unique across applications.
 | 
			
		||||
        """
 | 
			
		||||
        default_domain = f"{app_id}.{primary_domain}"
 | 
			
		||||
        if default_domain in seen_domains:
 | 
			
		||||
            raise AnsibleFilterError(
 | 
			
		||||
                f"Domain '{default_domain}' is already configured for "
 | 
			
		||||
                f"'{seen_domains[default_domain]}' and '{app_id}'"
 | 
			
		||||
            )
 | 
			
		||||
        seen_domains[default_domain] = app_id
 | 
			
		||||
        result[app_id] = [default_domain]
 | 
			
		||||
 | 
			
		||||
    def _process_canonical_domains(self, app_id, canonical_domains, seen_domains, result):
 | 
			
		||||
        """
 | 
			
		||||
        Process the canonical domains for an application, handling both lists and dicts,
 | 
			
		||||
        and ensuring each domain is unique.
 | 
			
		||||
        """
 | 
			
		||||
        if isinstance(canonical_domains, dict):
 | 
			
		||||
            self._process_canonical_domains_dict(app_id, canonical_domains, seen_domains, result)
 | 
			
		||||
        elif isinstance(canonical_domains, list):
 | 
			
		||||
            self._process_canonical_domains_list(app_id, canonical_domains, seen_domains, result)
 | 
			
		||||
        else:
 | 
			
		||||
            raise AnsibleFilterError(
 | 
			
		||||
                f"Unexpected type for 'domains.canonical' in application '{app_id}': "
 | 
			
		||||
                f"{type(canonical_domains).__name__}"
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    def _process_canonical_domains_dict(self, app_id, domains_dict, seen_domains, result):
 | 
			
		||||
        """
 | 
			
		||||
        Process a dictionary of canonical domains for an application.
 | 
			
		||||
        """
 | 
			
		||||
        for name, domain in domains_dict.items():
 | 
			
		||||
            self._validate_and_check_domain(app_id, domain, seen_domains)
 | 
			
		||||
        result[app_id] = domains_dict.copy()
 | 
			
		||||
 | 
			
		||||
    def _process_canonical_domains_list(self, app_id, domains_list, seen_domains, result):
 | 
			
		||||
        """
 | 
			
		||||
        Process a list of canonical domains for an application.
 | 
			
		||||
        """
 | 
			
		||||
        for domain in domains_list:
 | 
			
		||||
            self._validate_and_check_domain(app_id, domain, seen_domains)
 | 
			
		||||
        result[app_id] = list(domains_list)
 | 
			
		||||
 | 
			
		||||
    def _validate_and_check_domain(self, app_id, domain, seen_domains):
 | 
			
		||||
        """
 | 
			
		||||
        Validate the domain and check if it has already been assigned to another application.
 | 
			
		||||
        """
 | 
			
		||||
        if not isinstance(domain, str) or not domain.strip():
 | 
			
		||||
            raise AnsibleFilterError(
 | 
			
		||||
                f"Invalid domain entry in 'canonical' for application '{app_id}': {domain!r}"
 | 
			
		||||
            )
 | 
			
		||||
        if domain in seen_domains:
 | 
			
		||||
            raise AnsibleFilterError(
 | 
			
		||||
                f"Domain '{domain}' is already configured for '{seen_domains[domain]}' and '{app_id}'"
 | 
			
		||||
            )
 | 
			
		||||
        seen_domains[domain] = app_id
 | 
			
		||||
 
 | 
			
		||||
@@ -30,10 +30,4 @@ defaults_service_provider:
 | 
			
		||||
  legal:
 | 
			
		||||
    editorial_responsible:  "Johannes Gutenberg"
 | 
			
		||||
    source_code:            "https://github.com/kevinveenbirkenbach/cymais"
 | 
			
		||||
    imprint: >-
 | 
			
		||||
      {{ "{protocol}://{domain}/imprint.html"
 | 
			
		||||
        | safe_placeholders({
 | 
			
		||||
            'protocol': web_protocol,
 | 
			
		||||
            'domain':   domains.html_server
 | 
			
		||||
          })
 | 
			
		||||
      }}
 | 
			
		||||
    imprint: "{{web_protocol}}://{{domains['html-server']}}/imprint.html"
 | 
			
		||||
@@ -1,8 +1,3 @@
 | 
			
		||||
- name: "Debug: cloudflare_domains"
 | 
			
		||||
  debug:
 | 
			
		||||
    var: cloudflare_domains
 | 
			
		||||
  when: enable_debug
 | 
			
		||||
 | 
			
		||||
- name: Create or update Cloudflare A-record for {{ item }}
 | 
			
		||||
  community.general.cloudflare_dns:
 | 
			
		||||
    api_token: "{{ cloudflare_api_token }}"
 | 
			
		||||
 
 | 
			
		||||
@@ -283,7 +283,7 @@ HELP_URL=https://docs.bigbluebutton.org/greenlight/gl-overview.html
 | 
			
		||||
#   approval - For approve/decline registration
 | 
			
		||||
DEFAULT_REGISTRATION=invite
 | 
			
		||||
 | 
			
		||||
{% if applications[application_id].features.oidc | bool %}
 | 
			
		||||
{% if applications | is_feature_enabled('oidc',application_id) %}
 | 
			
		||||
### EXTERNAL AUTHENTICATION METHODS
 | 
			
		||||
# @See https://docs.bigbluebutton.org/greenlight/v3/external-authentication/
 | 
			
		||||
#
 | 
			
		||||
 
 | 
			
		||||
@@ -118,7 +118,7 @@ run:
 | 
			
		||||
  ## If you want to set the 'From' email address for your first registration, uncomment and change:
 | 
			
		||||
  ## After getting the first signup email, re-comment the line. It only needs to run once.
 | 
			
		||||
  #- exec: rails r "SiteSetting.notification_email='info@unconfigured.discourse.org'"
 | 
			
		||||
{% if applications[application_id].features.oidc | bool %}
 | 
			
		||||
{% if applications | is_feature_enabled('oidc',application_id) %}
 | 
			
		||||
  # Deactivate Default Login
 | 
			
		||||
  - exec: rails r "SiteSetting.enable_local_logins = false"
 | 
			
		||||
  - exec: rails r "SiteSetting.enable_passkeys = false" # https://meta.discourse.org/t/passwordless-login-using-passkeys/285589
 | 
			
		||||
 
 | 
			
		||||
@@ -77,7 +77,7 @@ ESPOCRM_CONFIG_LDAP_USER_LOGIN_FILTER=(sAMAccountName=%USERNAME%)
 | 
			
		||||
# OpenID Connect settings (optional)
 | 
			
		||||
# Applied only if the feature flag is true
 | 
			
		||||
# ------------------------------------------------
 | 
			
		||||
{% if applications[application_id].features.oidc | bool %}
 | 
			
		||||
{% if applications | is_feature_enabled('oidc',application_id) %}
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------
 | 
			
		||||
# OpenID Connect settings
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@ listmonk_settings:
 | 
			
		||||
         "provider_url": oidc.client.issuer_url,
 | 
			
		||||
         "client_secret": oidc.client.secret
 | 
			
		||||
      } | to_json }}
 | 
			
		||||
    when: applications[application_id].features.oidc | bool
 | 
			
		||||
    when: applications | is_feature_enabled('oidc',application_id)
 | 
			
		||||
 | 
			
		||||
  # hCaptcha toggles and credentials
 | 
			
		||||
  - key: "security.enable_captcha"
 | 
			
		||||
 
 | 
			
		||||
@@ -158,7 +158,7 @@ API_TOKEN={{applications.mailu.credentials.api_token}}
 | 
			
		||||
AUTH_REQUIRE_TOKENS=True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
{% if applications[application_id].features.oidc | bool %}
 | 
			
		||||
{% if applications | is_feature_enabled('oidc',application_id) %}
 | 
			
		||||
################################### 
 | 
			
		||||
# OpenID Connect settings
 | 
			
		||||
###################################
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@ cert_mount_directory:     "{{docker_compose.directories.volumes}}certs/"
 | 
			
		||||
 | 
			
		||||
# Use dedicated source for oidc if activated  
 | 
			
		||||
# @see https://github.com/heviat/Mailu-OIDC/tree/2024.06
 | 
			
		||||
docker_source:            "{{ 'ghcr.io/heviat' if applications[application_id].features.oidc | bool else 'ghcr.io/mailu' }}"
 | 
			
		||||
docker_source:            "{{ 'ghcr.io/heviat' if applications | is_feature_enabled('oidc',application_id) else 'ghcr.io/mailu' }}"
 | 
			
		||||
 | 
			
		||||
domain:                   "{{ domains | get_domain(application_id) }}"
 | 
			
		||||
http_port:                "{{ ports.localhost.http[application_id] }}"
 | 
			
		||||
@@ -52,7 +52,7 @@ SMTP_OPENSSL_VERIFY_MODE=none
 | 
			
		||||
SMTP_ENABLE_STARTTLS=auto
 | 
			
		||||
SMTP_FROM_ADDRESS=Mastodon <{{ users['no-reply'].email }}>
 | 
			
		||||
 | 
			
		||||
{% if applications[application_id].features.oidc | bool %}
 | 
			
		||||
{% if applications | is_feature_enabled('oidc',application_id) %}
 | 
			
		||||
################################### 
 | 
			
		||||
# OpenID Connect settings
 | 
			
		||||
###################################
 | 
			
		||||
 
 | 
			
		||||
@@ -20,8 +20,6 @@ oidc:
 | 
			
		||||
  # @see https://apps.nextcloud.com/apps/sociallogin
 | 
			
		||||
  flavor:                     "oidc_login"                                      # Keeping on sociallogin because the other option is not implemented yet                                             
 | 
			
		||||
credentials:
 | 
			
		||||
#  database_password:          Null       # Needs to be set in inventory file
 | 
			
		||||
#  administrator_password:     None       # Keep in mind to change the password fast after creation and activate 2FA
 | 
			
		||||
features:
 | 
			
		||||
  matomo:                       true
 | 
			
		||||
  css:                          true
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,15 @@
 | 
			
		||||
http_address            =   "0.0.0.0:4180"
 | 
			
		||||
cookie_secret           =   "{{ applications[oauth2_proxy_application_id].credentials.oauth2_proxy_cookie_secret }}"
 | 
			
		||||
email_domains           =   "{{ primary_domain }}"
 | 
			
		||||
cookie_secure           =   "true"                                                  # True is necessary to force the cookie set via https
 | 
			
		||||
cookie_secure           =   "true"                                                                                                                                                  # True is necessary to force the cookie set via https
 | 
			
		||||
upstreams               =   "http://{{ applications[oauth2_proxy_application_id].oauth2_proxy.application }}:{{ applications[oauth2_proxy_application_id].oauth2_proxy.port }}"
 | 
			
		||||
cookie_domains          =   ["{{ domains[oauth2_proxy_application_id] }}", "{{ domains | get_domain('keycloak') }}"]                  # Required so cookie can be read on all subdomains.
 | 
			
		||||
whitelist_domains       =   [".{{ primary_domain }}"]                                 # Required to allow redirection back to original requested target.
 | 
			
		||||
cookie_domains          =   ["{{ domains | get_domain(oauth2_proxy_application_id) }}", "{{ domains | get_domain('keycloak') }}"]                                                   # Required so cookie can be read on all subdomains.
 | 
			
		||||
whitelist_domains       =   [".{{ primary_domain }}"]                                                                                                                               # Required to allow redirection back to original requested target.
 | 
			
		||||
 | 
			
		||||
# keycloak provider
 | 
			
		||||
client_secret           =   "{{ oidc.client.secret }}"
 | 
			
		||||
client_id               =   "{{ oidc.client.id }}"
 | 
			
		||||
redirect_url            =   "{{ web_protocol }}://{{domains[oauth2_proxy_application_id]}}/oauth2/callback"
 | 
			
		||||
redirect_url            =   "{{ web_protocol }}://{{ domains | get_domain(oauth2_proxy_application_id) }}/oauth2/callback"
 | 
			
		||||
oidc_issuer_url         =   "{{ oidc.client.issuer_url }}"
 | 
			
		||||
provider                =   "oidc"
 | 
			
		||||
provider_display_name   =   "Keycloak"
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,8 @@ csp:
 | 
			
		||||
  flags:
 | 
			
		||||
    script-src:
 | 
			
		||||
      unsafe-inline: true
 | 
			
		||||
    style-src:
 | 
			
		||||
      unsafe-inline: true  
 | 
			
		||||
domains:
 | 
			
		||||
  canonical:
 | 
			
		||||
    - "project.{{ primary_domain }}"
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
application_id:               "pgadmin"
 | 
			
		||||
database_type:                "postgres"
 | 
			
		||||
database_host:                "{{ 'central-' + database_type if applications | is_feature_enabled('central_database',application_id)"
 | 
			
		||||
database_host:                "{{ 'central-' + database_type if applications | is_feature_enabled('central_database',application_id) }}"
 | 
			
		||||
pgadmin_user:                 5050
 | 
			
		||||
pgadmin_group:                "{{pgadmin_user}}"
 | 
			
		||||
@@ -1,3 +1,3 @@
 | 
			
		||||
application_id:       "phpmyadmin"
 | 
			
		||||
database_type:        "mariadb"
 | 
			
		||||
database_host:        "{{ 'central-' + database_type if applications | is_feature_enabled('central_database',application_id)"
 | 
			
		||||
database_host:        "{{ 'central-' + database_type if applications | is_feature_enabled('central_database',application_id) }}"
 | 
			
		||||
@@ -47,7 +47,7 @@ for filename in os.listdir(config_path):
 | 
			
		||||
        # Prepare the URL and expected status codes
 | 
			
		||||
        url = f"{{ web_protocol }}://{domain}"
 | 
			
		||||
        
 | 
			
		||||
        redirected_domains = [domain['source'] for domain in {{current_play_redirect_domain_mappings}}]
 | 
			
		||||
        redirected_domains = [domain['source'] for domain in {{ current_play_domain_mappings_redirect}}]
 | 
			
		||||
        {%- if domains.mailu | safe_var | bool %}
 | 
			
		||||
        redirected_domains.append("{{domains | get_domain('mailu')}}")
 | 
			
		||||
        {%- endif %}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,2 +1,2 @@
 | 
			
		||||
application_id: "html_server"
 | 
			
		||||
application_id: "html-server"
 | 
			
		||||
domain:         "{{domains | get_domain(application_id)}}"
 | 
			
		||||
@@ -9,14 +9,17 @@
 | 
			
		||||
    set_fact:
 | 
			
		||||
      system_email: "{{ default_system_email | combine(system_email | default({}, true), recursive=True) }}"
 | 
			
		||||
 | 
			
		||||
  - name: Merge application definitions
 | 
			
		||||
    set_fact:
 | 
			
		||||
      applications: "{{ defaults_applications | combine(applications | default({}, true), recursive=True) }}"
 | 
			
		||||
 | 
			
		||||
  - name: Merge current play applications
 | 
			
		||||
    set_fact:
 | 
			
		||||
      current_play_applications: >-
 | 
			
		||||
        {{ 
 | 
			
		||||
          defaults_applications | 
 | 
			
		||||
          combine(applications | default({}, true), recursive=True) |
 | 
			
		||||
          applications |
 | 
			
		||||
          applications_if_group_and_deps(group_names)
 | 
			
		||||
          }}
 | 
			
		||||
        }}
 | 
			
		||||
 | 
			
		||||
  - name: Merge current play domain definitions
 | 
			
		||||
    set_fact:
 | 
			
		||||
@@ -26,28 +29,29 @@
 | 
			
		||||
            combine(domains | default({}, true), recursive=True) 
 | 
			
		||||
        }}
 | 
			
		||||
 | 
			
		||||
  - name: Set current play all domains incl. www redirect if enabled
 | 
			
		||||
    set_fact:
 | 
			
		||||
      current_play_domains_all: >-
 | 
			
		||||
        {{ 
 | 
			
		||||
          current_play_domains  | 
 | 
			
		||||
          generate_all_domains(
 | 
			
		||||
              ('www_redirect' in group_names)
 | 
			
		||||
          )
 | 
			
		||||
        }}
 | 
			
		||||
  
 | 
			
		||||
  - name: Set current play redirect domain mappings 
 | 
			
		||||
    set_fact:
 | 
			
		||||
      current_play_redirect_domain_mappings: >-
 | 
			
		||||
      current_play_domain_mappings_redirect: >-
 | 
			
		||||
        {{ 
 | 
			
		||||
          current_play_applications |
 | 
			
		||||
          domain_mappings(primary_domain) | 
 | 
			
		||||
          merge_mapping(redirect_domain_mappings, 'source') 
 | 
			
		||||
        }}
 | 
			
		||||
 | 
			
		||||
  - name: Merge application definitions
 | 
			
		||||
  - name: Set current play all domains incl. www redirect if enabled
 | 
			
		||||
    set_fact:
 | 
			
		||||
      applications: "{{ defaults_applications | combine(applications | default({}, true), recursive=True) }}"
 | 
			
		||||
      current_play_domains_all: >-
 | 
			
		||||
        {{
 | 
			
		||||
          (current_play_domains |
 | 
			
		||||
          combine(
 | 
			
		||||
            current_play_domain_mappings_redirect | 
 | 
			
		||||
            items2dict(key_name='target', value_name='source'),
 | 
			
		||||
            recursive=True
 | 
			
		||||
          )) |
 | 
			
		||||
          generate_all_domains(
 | 
			
		||||
            ('www_redirect' in group_names)
 | 
			
		||||
          )
 | 
			
		||||
        }}
 | 
			
		||||
 | 
			
		||||
  - name: Merge domain definitions for all domains
 | 
			
		||||
    set_fact:
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,7 @@
 | 
			
		||||
  include_role:
 | 
			
		||||
    name: nginx-redirect-domains
 | 
			
		||||
  vars:
 | 
			
		||||
    domain_mappings: "{{current_play_redirect_domain_mappings}}"
 | 
			
		||||
    domain_mappings: "{{ current_play_domain_mappings_redirect}}"
 | 
			
		||||
 | 
			
		||||
- name: setup www redirect 
 | 
			
		||||
  when: ("www_redirect" in group_names)
 | 
			
		||||
 
 | 
			
		||||
@@ -15,9 +15,9 @@ class TestLoadConfigurationFilter(unittest.TestCase):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        _cfg_cache.clear()
 | 
			
		||||
        self.f = FilterModule().filters()['load_configuration']
 | 
			
		||||
        self.app = 'html_server'
 | 
			
		||||
        self.app = 'html-server'
 | 
			
		||||
        self.nested_cfg = {
 | 
			
		||||
            'html_server': {
 | 
			
		||||
            'html-server': {
 | 
			
		||||
                'features': {'matomo': True},
 | 
			
		||||
                'domains': {'canonical': ['html.example.com']}
 | 
			
		||||
            }
 | 
			
		||||
@@ -76,8 +76,8 @@ class TestLoadConfigurationFilter(unittest.TestCase):
 | 
			
		||||
    @patch('load_configuration.os.listdir', return_value=['r1'])
 | 
			
		||||
    @patch('load_configuration.os.path.isdir', return_value=True)
 | 
			
		||||
    @patch('load_configuration.os.path.exists', return_value=True)
 | 
			
		||||
    @patch('load_configuration.open', mock_open(read_data="html_server: {}"))
 | 
			
		||||
    @patch('load_configuration.yaml.safe_load', return_value={'html_server': {}})
 | 
			
		||||
    @patch('load_configuration.open', mock_open(read_data="html-server: {}"))
 | 
			
		||||
    @patch('load_configuration.yaml.safe_load', return_value={'html-server': {}})
 | 
			
		||||
    def test_key_not_found_after_load(self, *_):
 | 
			
		||||
        with self.assertRaises(AnsibleFilterError):
 | 
			
		||||
            self.f(self.app, 'does.not.exist')
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user