Files
mail-server/config/user-patches.sh
T

70 lines
3.4 KiB
Bash
Executable File

#!/bin/bash
# Dovecot shared mailboxes. info@produktor.io gets access to the mailboxes of
# two owner classes (issue #79, issue #251 / epic #250):
# - production owners (ano@, andriy.oblivantsev@, postmaster@): read-only
# (`lookup read`) — one login in Roundcube covers the whole account list;
# - incubator owners (wheregroup@produktor.io; future sources like
# gmail_lenovo@, tb-*/pst-*): read + delete (`lookup read delete expunge
# write-deleted`) — the mailbox owner never logs in, mail is imported via
# doveadm; deleting a message in Roundcube = filter/exclusion from the
# corpus (autosync, epic B).
# DMS runs this only on the FIRST start of each container instance (plain
# `docker compose restart` skips the setup step by design — /CONTAINER_START
# marker), so it must stay idempotent. ACLs, the shared dict and subscriptions
# persist in mail-state / maildirs across restarts.
set -euo pipefail
# 1. acl_shared_dict directory: must exist and be writable by the mail user.
SHARED_DB_DIR=/var/lib/dovecot/db
mkdir -p "${SHARED_DB_DIR}"
chown docker:docker "${SHARED_DB_DIR}"
chmod 0770 "${SHARED_DB_DIR}"
# 2. Grant info@ rights on every current mailbox of the shared owners.
# doveadm acl set is the only way Dovecot records the share in acl_shared_dict
# (manual dovecot-acl files do NOT populate the dictionary — Dovecot docs).
# NOTE: this Dovecot build accepts full right NAMES ("lookup read"), single
# letters ("lr") are rejected with "Invalid right". The incubator set below
# was verified on live (issue #251): `write-deleted` is enough for the
# \Deleted flag that Roundcube sets on Delete — the extra `write` right is
# NOT required; `expunge` is also what Dovecot MOVE needs on the source side
# when Roundcube moves a deleted message to Trash.
READER='info@produktor.io'
PRODUCTION_OWNERS='ano@produktor.io andriy.oblivantsev@produktor.io postmaster@produktor.io'
INCUBATOR_OWNERS='wheregroup@produktor.io'
grant_share() { # $1=owner, remaining=right names
local owner=$1
shift
# Skip owners not (yet) in postfix-accounts.cf — e.g. right after a fresh
# clone, before `setup email add` was run for the incubator source.
if ! doveadm mailbox list -u "${owner}" >/dev/null 2>&1; then
echo "user-patches: skip ${owner}: account does not exist yet (run 'setup email add ${owner}')"
return 0
fi
# The shared mailbox "shared/<owner>" maps to the owner's INBOX (Dovecot
# shared-storage semantics) — subscribe it explicitly so Roundcube's
# subscribed folder list shows it.
doveadm mailbox subscribe -u "${READER}" "shared/${owner}"
# A brand-new mailbox owner has no INBOX yet and `doveadm mailbox list`
# above would be empty, so no share would be recorded. Ensure INBOX exists
# first (issue #251); "Mailbox already exists" is fine.
doveadm mailbox create -u "${owner}" INBOX >/dev/null 2>&1 || true
for mb in $(doveadm mailbox list -u "${owner}"); do
doveadm acl set -u "${owner}" "${mb}" "user=${READER}" "$@"
if [ "${mb}" != "INBOX" ]; then
doveadm mailbox subscribe -u "${READER}" "shared/${owner}/${mb}"
fi
done
}
# Production owners stay read-only for info@ (regression guard for #79).
for owner in ${PRODUCTION_OWNERS}; do
grant_share "${owner}" lookup read
done
# Incubator owners: info@ can read AND delete (filter semantics, epic #250).
for owner in ${INCUBATOR_OWNERS}; do
grant_share "${owner}" lookup read delete expunge write-deleted
done