Files
homelab/ansible/roles/obsidian_headless/tasks/verify.yml
T
JARVISandClaude Opus 5.5 fc2ad85e19 obsidian_headless: grant the service user on shared folders too (WDWL-22)
Content multica creates in inbox/ is owned multica:multica, so obsync fell
into "other" (---) and Sync could neither read nor upload Wong's
submissions. Found after the real Sync sign-in on spaceship-earth; the
earlier verify only tested the obsync -> multica direction.

Shared folders now carry user:obsync:rwx as an access and a default entry
alongside the shared user's, and verify tests the multica -> obsync
direction (read + rewrite, as merge needs). Second run changed=0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 21:28:52 -05:00

152 lines
5.2 KiB
YAML

---
# Each check runs AS the user it is about, because file modes and ACLs only
# mean something when exercised. Probes are cleaned up; nothing is left behind.
- name: Verify | Shared users can write, read and delete in their folders
ansible.builtin.command:
argv:
- /bin/sh
- -c
- 'f="$1/.obh-verify.$$"; echo ok > "$f" && grep -q ok "$f" && rm -f "$f"'
- sh
- "{{ obsidian_headless_vault_path }}/{{ item.path }}"
become: true
become_user: "{{ item.user }}"
loop: "{{ obsidian_headless_shared_folders }}"
loop_control:
label: "{{ item.user }} -> {{ item.path }}"
changed_when: false
check_mode: false
# The reverse direction: what the shared user writes, Sync must be able to
# read (to upload it) and rewrite (to merge remote edits into it).
- name: Verify | Content a shared user creates stays usable by the service (create)
ansible.builtin.command:
argv:
- /bin/sh
- -c
- 'd="$1/.obh-verify-rev"; mkdir -p "$d" && echo ok > "$d/note.md"'
- sh
- "{{ obsidian_headless_vault_path }}/{{ item.path }}"
become: true
become_user: "{{ item.user }}"
loop: "{{ obsidian_headless_shared_folders }}"
loop_control:
label: "{{ item.user }} -> {{ item.path }}"
changed_when: false
check_mode: false
- name: Verify | Content a shared user creates stays usable by the service (use)
ansible.builtin.command:
argv:
- /bin/sh
- -c
- 'd="$1/.obh-verify-rev"; grep -q ok "$d/note.md" && echo more >> "$d/note.md" && echo ok > "$d/reply.md"'
- sh
- "{{ obsidian_headless_vault_path }}/{{ item.path }}"
become: true
become_user: "{{ obsidian_headless_user }}"
loop: "{{ obsidian_headless_shared_folders }}"
loop_control:
label: "{{ item.path }}"
register: _obh_reverse
changed_when: false
failed_when: false
check_mode: false
- name: Verify | Remove the reverse probes
ansible.builtin.file:
path: "{{ obsidian_headless_vault_path }}/{{ item.path }}/.obh-verify-rev"
state: absent
loop: "{{ obsidian_headless_shared_folders }}"
loop_control:
label: "{{ item.path }}"
changed_when: false
- name: Verify | The service can read and rewrite shared users' content
ansible.builtin.assert:
that:
- _obh_reverse.results | rejectattr('rc', 'equalto', 0) | list | length == 0
fail_msg: >-
{{ obsidian_headless_user }} cannot read or write content a shared user
created; Sync would never upload it. Check the user:{{ obsidian_headless_user }}
default ACL entry on the shared folder.
quiet: true
# Simulates the service: obsync, umask 0077, creating a folder and a file the
# way Sync does. The shared user must still reach both, via the default ACL.
- name: Verify | Content the service creates in a shared folder inherits the grant (create)
ansible.builtin.command:
argv:
- /bin/sh
- -c
- 'umask 0077; d="$1/.obh-verify-dir"; mkdir -p "$d" && echo ok > "$d/note.md"'
- sh
- "{{ obsidian_headless_vault_path }}/{{ item.path }}"
become: true
become_user: "{{ obsidian_headless_user }}"
loop: "{{ obsidian_headless_shared_folders }}"
loop_control:
label: "{{ item.path }}"
changed_when: false
check_mode: false
- name: Verify | Content the service creates in a shared folder inherits the grant (use)
ansible.builtin.command:
argv:
- /bin/sh
- -c
- 'd="$1/.obh-verify-dir"; grep -q ok "$d/note.md" && echo ok > "$d/reply.md"'
- sh
- "{{ obsidian_headless_vault_path }}/{{ item.path }}"
become: true
become_user: "{{ item.user }}"
loop: "{{ obsidian_headless_shared_folders }}"
loop_control:
label: "{{ item.user }} -> {{ item.path }}"
register: _obh_inherit
changed_when: false
failed_when: false
check_mode: false
- name: Verify | Remove the inheritance probes
ansible.builtin.file:
path: "{{ obsidian_headless_vault_path }}/{{ item.path }}/.obh-verify-dir"
state: absent
loop: "{{ obsidian_headless_shared_folders }}"
loop_control:
label: "{{ item.path }}"
changed_when: false
- name: Verify | Inheritance held
ansible.builtin.assert:
that:
- _obh_inherit.results | rejectattr('rc', 'equalto', 0) | list | length == 0
fail_msg: >-
A shared user could not read or write content created by
{{ obsidian_headless_user }} under umask 0077; the default ACL is not
being inherited.
quiet: true
# Every shared user must be refused: listing the vault root, and listing,
# reading into or writing into each private folder.
- name: Verify | Shared users are denied the vault root listing and every private folder
ansible.builtin.command:
argv: >-
{{ ['/bin/sh', '-c', _obh_deny_script, 'sh', obsidian_headless_vault_path]
+ obsidian_headless_private_folders }}
vars:
_obh_deny_script: |
v="$1"; shift
ls "$v" >/dev/null 2>&1 && { echo "can list $v"; exit 1; }
for p in "$@"; do
ls "$v/$p" >/dev/null 2>&1 && { echo "can list $p"; exit 1; }
touch "$v/$p/.obh-verify" 2>/dev/null && { rm -f "$v/$p/.obh-verify"; echo "can write $p"; exit 1; }
done
exit 0
become: true
become_user: "{{ item }}"
loop: "{{ obsidian_headless_shared_folders | map(attribute='user') | unique | list }}"
changed_when: false
check_mode: false