Files
computer-playbook/roles/web-app-suitecrm/tasks/01_manager_ops.yml
kevinveenbirkenbach d2abd332f0 fix(web-app-suitecrm): let the external-auth provisioning path create its user
SuiteCRM 8.10.2 tightened User::hasSaveAccess() from return true to is_admin($current_user) for a record without an id, and restrictAdminOnlyFields() now forces status Inactive on such a record. CreateUser::create() runs while the browser is still anonymous, because it is the LDAP and SAML auto-create path, so every FIRST login of a directory account ends in RuntimeException: Not authorized and HTTP 500 on POST /login. The pre-existing administrator is unaffected, which is why one persona passes and one fails in both auth modes. 451f63cbdd carried the role from 8.9.1 to 8.10.2; 8.9.1 exempted a new record from both guards.

The patch raises the admin context around that one save and restores it in a finally, rather than relaxing the guards, which stay closed for every request-driven save. create() has a single caller in its own file, createExternalAuthUser, which sets is_admin 0 and external_auth_only 1 before calling, so the raised context cannot mint an administrator. patch runs with -F0 so a hunk upstream has moved fails the build instead of landing somewhere else.

The patch path is derived once in vars/main.yml and reaches the Dockerfile as a build arg, following the role's existing SUITECRM_ENTRYPOINT_SCRIPT_* shape, so the file name is written once.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 495d23dbde)

# Conflicts:
#	roles/web-app-suitecrm/vars/main.yml
2026-10-16 13:29:07 +02:00

60 lines
1.8 KiB
YAML

---
- name: "🚀 Deploy PHP error configuration (env aware)"
stack_host_template:
src: "php.ini.j2"
dest: "{{ SUITECRM_PHP_CONFIG_HOST }}"
notify:
- compose-up
- name: "📄 Render SuiteCRM LDAP mapping"
stack_host_template:
src: "ldap.yaml.j2"
dest: "{{ SUITECRM_LDAP_CONFIG_HOST }}"
when: SUITECRM_LDAP_ENABLED | bool
notify:
- compose-up
- name: "📄 Deploy SuiteCRM session handler wiring"
stack_host_copy:
src: "session.yaml"
dest: "{{ SUITECRM_SESSION_CONFIG_HOST }}"
mode: "0444"
notify:
- compose-up
- name: "🚀 Deploy '{{ SUITECRM_ENTRYPOINT_SCRIPT_HOST_ABS }}'"
copy:
src: "{{ SUITECRM_ENTRYPOINT_SCRIPT_SRC }}"
dest: "{{ SUITECRM_ENTRYPOINT_SCRIPT_HOST_ABS }}"
notify:
- compose-up
- compose-build
- name: "🚀 Deploy '{{ SUITECRM_EXTERNAL_AUTH_PATCH_HOST_ABS }}'"
copy:
src: "{{ SUITECRM_EXTERNAL_AUTH_PATCH_SRC }}"
dest: "{{ SUITECRM_EXTERNAL_AUTH_PATCH_HOST_ABS }}"
notify:
- compose-up
- compose-build
- name: "🐳 Docker Compose Up for '{{ application_id }}'"
meta: flush_handlers
- name: "⏳ Wait until the SuiteCRM container is healthy"
shell: >
container inspect --type container --format '{% raw %}{{.State.Health.Status}}{% endraw %}' {{ SUITECRM_CONTAINER_ADDRESS }}
register: suitecrm_health
changed_when: false
until: suitecrm_health.stdout.strip() == "healthy"
retries: 300
delay: 5
- name: "🔎 List SuiteCRM log paths the web user cannot write"
shell: >
container exec --user {{ SUITECRM_WEB_USER }} {{ SUITECRM_CONTAINER_ADDRESS }}
find {{ SUITECRM_APP_DIR }}/logs ! -writable
register: suitecrm_unwritable_logs
changed_when: false
failed_when: suitecrm_unwritable_logs.rc != 0 or suitecrm_unwritable_logs.stdout | length > 0