Files
computer-playbook/roles/web-app-akaunting/tasks/01_setup.yml
kevinveenbirkenbachandClaude Opus 5 962479ad71 fix(akaunting): realign the seeded administrator password on every pass
The install one-shot seeds the administrator from ADMIN_PASSWORD and runs only while
the first-run marker is absent, so after a credential rotation the account keeps the
secret the deployment no longer holds. The playwright trace of the run shows the
consequence: the native login never leaves /auth/login.

Akaunting ships no first-party command for this. user:seed only invokes a seeder and
sets no password, so the realignment goes through artisan tinker, which composer.json
lists under require rather than require-dev and is therefore present in the served
image. ADMIN_EMAIL and ADMIN_PASSWORD reach it through the environment, never through
an argument.

The password is assigned in clear text deliberately: App\Models\Auth\User defines
setPasswordAttribute, which bcrypts on save, so hashing before assignment would store
a hash of a hash. The fourth test case pins that down by asserting the raw value on
the model after save.

The role had no host/cid resolution at all -- setup/swarm.yml sidesteps it by scaling
to zero and running a one-shot service, which is too coarse for a per-run task -- so
it gains resolve_host_cid and addresses the container directly.

The snippet is not standalone PHP: it carries no opening tag because tinker evaluates
what it is given. The mirrored test therefore stubs App\Models\Auth\User and
Illuminate\Support\Facades\Hash and evaluates the file, covering the missing user, the
missing email, the already-matching password (no save) and the differing one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 15:11:56 +02:00

50 lines
2.0 KiB
YAML

---
- name: "🧭 Akaunting | Resolve the data volume's mountpoint on this node"
ansible.builtin.command: >-
container volume inspect --format '{{ "{{.Mountpoint}}" }}'
{{ AKAUNTING_VOLUME | quote }}
register: akaunting_volume_mountpoint
changed_when: false
- name: "🔎 Akaunting | Check if first run (marker exists?)"
ansible.builtin.stat:
path: "{{ AKAUNTING_SETUP_MARKER }}"
register: akaunting_marker_stat
- when: not akaunting_marker_stat.stat.exists
block:
- name: "▶️ Akaunting | First-run install one-shot ({{ DEPLOYMENT_MODE }})"
ansible.builtin.include_tasks:
file: "setup/{{ DEPLOYMENT_MODE }}.yml"
apply:
delegate_to: "{{ STACK_HOST }}"
- name: "📁 Akaunting | Create first-run marker to disable future setup"
ansible.builtin.file:
path: "{{ AKAUNTING_SETUP_MARKER }}"
state: touch
mode: "0644"
- name: "🔍 Akaunting | Resolve the hosting node and container id"
ansible.builtin.include_tasks: "{{ lookup('path_absolute', 'roles/sys-svc-compose/tasks/utils/swarm/resolve_host_cid.yml') }}"
vars:
resolve_service_key: "akaunting"
resolve_output_prefix: AKAUNTING
resolve_compose_cid: "{{ AKAUNTING_CONTAINER }}"
- name: "🔑 Akaunting | Realign the administrator password with the declared one"
ansible.builtin.shell: > # nocheck: container-exec-resolver AKAUNTING_LOCAL_CID resolved at delegate_to runtime
container exec
-e ADMIN_EMAIL={{ AKAUNTING_ADMIN_EMAIL | quote }}
-e ADMIN_PASSWORD={{ AKAUNTING_ADMIN_PASSWORD | quote }}
{{ AKAUNTING_LOCAL_CID | quote }}
php artisan tinker --execute={{ lookup('file', 'php/realign_admin_password.php') | quote }}
register: akaunting_admin_password
changed_when: "'CHANGED' in akaunting_admin_password.stdout"
failed_when: >-
akaunting_admin_password.rc != 0
or 'FAILED' in akaunting_admin_password.stdout
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
delegate_to: "{{ AKAUNTING_HOST_NODE }}"
run_once: true