nextcloud: per-user mail sync + portal RBAC

This commit is contained in:
Brad Stein 2026-01-03 12:18:29 -03:00
parent 51b0a88a62
commit 91106ee298
2 changed files with 106 additions and 5 deletions

View File

@ -6,6 +6,7 @@ KC_REALM="${KC_REALM:?}"
KC_ADMIN_USER="${KC_ADMIN_USER:?}"
KC_ADMIN_PASS="${KC_ADMIN_PASS:?}"
MAILU_DOMAIN="${MAILU_DOMAIN:?}"
ONLY_USERNAME="${ONLY_USERNAME:-}"
if ! command -v jq >/dev/null 2>&1; then
apt-get update && apt-get install -y jq curl >/dev/null
@ -57,11 +58,52 @@ fi
cd /var/www/html
users=$(curl -s -H "Authorization: Bearer ${token}" \
"${KC_BASE}/admin/realms/${KC_REALM}/users?max=2000")
kc_users_url="${KC_BASE}/admin/realms/${KC_REALM}/users?max=2000"
if [[ -n "${ONLY_USERNAME}" ]]; then
username_q=$(jq -nr --arg v "${ONLY_USERNAME}" '$v|@uri')
kc_users_url="${KC_BASE}/admin/realms/${KC_REALM}/users?username=${username_q}&exact=true&max=1"
fi
echo "${users}" | jq -c '.[]' | while read -r user; do
username=$(echo "${user}" | jq -r '.username')
users=$(curl -s -H "Authorization: Bearer ${token}" "${kc_users_url}")
kc_set_user_mail_meta() {
local user_id="${1}"
local primary_email="${2}"
local mailu_account_count="${3}"
local synced_at="${4}"
# Fetch the full user representation so we don't accidentally clobber attributes.
local user_json updated_json
if ! user_json=$(curl -fsS -H "Authorization: Bearer ${token}" \
"${KC_BASE}/admin/realms/${KC_REALM}/users/${user_id}"); then
echo "WARN: unable to fetch Keycloak user ${user_id} for metadata writeback" >&2
return 1
fi
updated_json=$(
jq -c \
--arg primary_email "${primary_email}" \
--arg mailu_account_count "${mailu_account_count}" \
--arg synced_at "${synced_at}" \
'
.attributes = (.attributes // {}) |
.attributes.nextcloud_mail_primary_email = [$primary_email] |
.attributes.nextcloud_mail_account_count = [$mailu_account_count] |
.attributes.nextcloud_mail_synced_at = [$synced_at] |
del(.access)
' <<<"${user_json}"
)
curl -fsS -X PUT \
-H "Authorization: Bearer ${token}" \
-H "Content-Type: application/json" \
-d "${updated_json}" \
"${KC_BASE}/admin/realms/${KC_REALM}/users/${user_id}" >/dev/null
}
while read -r user; do
user_id=$(jq -r '.id' <<<"${user}")
username=$(jq -r '.username' <<<"${user}")
keycloak_email=$(echo "${user}" | jq -r '.email // empty')
mailu_email=$(echo "${user}" | jq -r '(.attributes.mailu_email[0] // .attributes.mailu_email // empty)')
app_pw=$(echo "${user}" | jq -r '(.attributes.mailu_app_password[0] // .attributes.mailu_app_password // empty)')
@ -131,4 +173,32 @@ echo "${users}" | jq -c '.[]' | while read -r user; do
mail.bstein.dev 993 ssl "${desired_email}" "${app_pw}" \
mail.bstein.dev 587 tls "${desired_email}" "${app_pw}" password >/dev/null 2>&1 || true
fi
done
# Write non-secret metadata back to Keycloak for UI introspection and onboarding gating.
synced_at=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
if accounts_after=$(list_mail_accounts "${username}"); then
mailu_accounts_after=$(awk -v d="${MAILU_DOMAIN,,}" 'tolower($2) ~ ("@" d "$") {print}' <<<"${accounts_after}" || true)
if [[ -n "${mailu_accounts_after}" ]]; then
mailu_account_count=$(printf '%s\n' "${mailu_accounts_after}" | wc -l | tr -d ' ')
else
mailu_account_count="0"
fi
primary_email_after=""
if [[ -n "${mailu_accounts_after}" ]]; then
while IFS=$'\t' read -r _account_id account_email; do
if [[ "${account_email,,}" == "${desired_email,,}" ]]; then
primary_email_after="${account_email}"
break
fi
if [[ -z "${primary_email_after}" ]]; then
primary_email_after="${account_email}"
fi
done <<<"${mailu_accounts_after}"
fi
else
mailu_account_count="0"
primary_email_after=""
fi
kc_set_user_mail_meta "${user_id}" "${primary_email_after}" "${mailu_account_count}" "${synced_at}" || true
done < <(jq -c '.[]' <<<"${users}")

View File

@ -75,3 +75,34 @@ subjects:
- kind: ServiceAccount
name: bstein-dev-home
namespace: bstein-dev-home
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: bstein-dev-home-nextcloud-mail-sync
namespace: nextcloud
rules:
- apiGroups: ["batch"]
resources: ["cronjobs"]
verbs: ["get"]
resourceNames: ["nextcloud-mail-sync"]
- apiGroups: ["batch"]
resources: ["jobs"]
verbs: ["create", "get", "list", "watch"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: bstein-dev-home-nextcloud-mail-sync
namespace: nextcloud
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: bstein-dev-home-nextcloud-mail-sync
subjects:
- kind: ServiceAccount
name: bstein-dev-home
namespace: bstein-dev-home