fix(debyltech-cloud): external signers, proxy trust, light-only mail
- Enable LibreSign's email identify method (click-to-sign, no account creation), remove the stamp background and collect signer metadata. On Skudak these were only ever set in the admin UI. Without the email method a fresh instance answers "No signers." for any outside address. The verify script now asserts it. - Store add_footer=true explicitly. The code already defaults to it, but the 14.2 admin page shows unset as unchecked. - trusted_proxies = the container's own address, read per deploy. Behind rootless port forwarding every request arrives from it, so without this X-Forwarded-For was ignored and every client shared one IP for brute-force throttling. - maintenance_window_start and default_phone_region, which clears the setup warnings. - Mail declares color-scheme "light only" so Apple Mail's dark mode doesn't repaint the white ground and bury the black wordmark (asserted in verify). - Idempotency: redis image fully qualified (docker.io/library/...), the debyltechmail copy owned by the mapped www-data uid, and theming compared before setting. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
fa5bbf8e54
commit
9ce9610965
@@ -188,6 +188,21 @@ class DebyltechEMailTemplate extends EMailTemplate {
|
||||
'font-size:26px;font-weight:300;letter-spacing:-0.02em',
|
||||
$this->heading,
|
||||
);
|
||||
|
||||
// Opt out of mail-client dark mode. Without this Apple Mail repaints
|
||||
// the white ground charcoal on its own, and the black wordmark all but
|
||||
// disappears. Swapping to a white logo under prefers-color-scheme is
|
||||
// NOT a fix: with Apple Mail's "Use dark backgrounds for messages" off,
|
||||
// the query still matches while the ground stays white, leaving a
|
||||
// white-on-white logo. This mail is designed light; render it light.
|
||||
$this->head = str_replace(
|
||||
'</head>',
|
||||
'<meta name="color-scheme" content="light only">'
|
||||
. '<meta name="supported-color-schemes" content="light only">'
|
||||
. '<style type="text/css">:root{color-scheme:light only;supported-color-schemes:light only}</style>'
|
||||
. '</head>',
|
||||
$this->head,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -370,6 +370,58 @@
|
||||
register: debyltech_enum_fullmatch
|
||||
changed_when: "'is now set to' in debyltech_enum_fullmatch.stdout"
|
||||
|
||||
# Settings Skudak only ever had from clicks in the LibreSign admin page, never
|
||||
# in git -- a fresh instance without them cannot invite anyone by address:
|
||||
#
|
||||
# identify_methods The EMAIL identification method. Without it the signer
|
||||
# search only lists accounts, so an outside address
|
||||
# returns a bare "No signers." -- the whole point of this
|
||||
# instance. clickToSign (no emailed code) and
|
||||
# can_create_account=false, as on Skudak: the emailed link
|
||||
# is the identity check, and customers never get accounts.
|
||||
# signature_background_type=deleted
|
||||
# Drops the LibreSign logo watermark from behind the
|
||||
# stamp, so with GRAPHIC_ONLY the stamp is the drawn mark
|
||||
# and nothing else.
|
||||
# collect_metadata Records signer IP/user agent alongside each signature.
|
||||
- name: set libresign signer identification and stamp settings in debyltech-cloud
|
||||
become: true
|
||||
become_user: "{{ podman_user }}"
|
||||
ansible.builtin.command: >
|
||||
podman exec -u www-data debyltech-cloud
|
||||
php occ config:app:set libresign {{ item.k }} --value={{ item.v | quote }}
|
||||
register: debyltech_libresign_settings
|
||||
changed_when: "'is now set to' in debyltech_libresign_settings.stdout"
|
||||
loop:
|
||||
- k: identify_methods
|
||||
v: >-
|
||||
{{ [{'name': 'email', 'friendly_name': 'Email', 'enabled': true,
|
||||
'mandatory': true,
|
||||
'signatureMethods': {
|
||||
'clickToSign': {'name': 'clickToSign', 'enabled': true},
|
||||
'emailToken': {'name': 'emailToken', 'enabled': false}},
|
||||
'can_create_account': false,
|
||||
'test_url': '/index.php/settings/admin/mailtest',
|
||||
'signatureMethodEnabled': 'clickToSign'}] | to_json }}
|
||||
- {k: signature_background_type, v: deleted}
|
||||
- {k: collect_metadata, v: "1"}
|
||||
loop_control:
|
||||
label: "{{ item.k }}"
|
||||
|
||||
# The validation footer ("Digitally signed by ... Validate in <url>") is WANTED
|
||||
# -- only its QR code goes, below. FooterHandler defaults add_footer to true
|
||||
# when unset, but the 14.2 admin page renders unset as UNCHECKED, inviting
|
||||
# someone to "correct" it into actually turning the footer off. Stored
|
||||
# explicitly so the page shows what the code does.
|
||||
- name: keep libresign validation footer text in debyltech-cloud
|
||||
become: true
|
||||
become_user: "{{ podman_user }}"
|
||||
ansible.builtin.command: >
|
||||
podman exec -u www-data debyltech-cloud
|
||||
php occ config:app:set libresign add_footer --value=1 --type=boolean
|
||||
register: libresign_footer
|
||||
changed_when: "'is now set to' in libresign_footer.stdout"
|
||||
|
||||
- name: drop libresign validation QR code from signed-PDF footer in debyltech-cloud
|
||||
become: true
|
||||
become_user: "{{ podman_user }}"
|
||||
@@ -444,6 +496,23 @@
|
||||
register: debyltechmail_enable
|
||||
changed_when: "'already enabled' not in debyltechmail_enable.stdout"
|
||||
|
||||
# Behind rootless podman's port forwarder, every request -- Caddy's included --
|
||||
# reaches Apache FROM THE CONTAINER'S OWN ADDRESS on `shared`, not from the
|
||||
# host. Unless exactly that address is a trusted proxy, Nextcloud ignores the
|
||||
# X-Forwarded-For header Caddy sends, so every client looks like one IP:
|
||||
# brute-force throttling then penalises everyone at once. Read per deploy
|
||||
# because the address is assigned at container creation, and deploys are the
|
||||
# only thing that recreate it.
|
||||
- name: read debyltech-cloud container address
|
||||
become: true
|
||||
become_user: "{{ podman_user }}"
|
||||
ansible.builtin.command: >
|
||||
podman inspect debyltech-cloud
|
||||
--format "{{ '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' }}"
|
||||
register: debyltech_cloud_ip
|
||||
changed_when: false
|
||||
failed_when: debyltech_cloud_ip.stdout is not match('^[0-9.]+$')
|
||||
|
||||
# System config, set only when it differs so a clean re-deploy reports no
|
||||
# changes (Skudak's equivalents report changed on every run). Values are
|
||||
# single-quoted into the shell, so the backslashes in mail_template_class pass
|
||||
@@ -467,6 +536,10 @@
|
||||
loop:
|
||||
- {k: overwrite.cli.url, v: "https://{{ cloud_debyltech_server_name }}"}
|
||||
- {k: overwriteprotocol, v: https}
|
||||
- {k: trusted_proxies 0, v: "{{ debyltech_cloud_ip.stdout }}"}
|
||||
# Hour in UTC: 05:00 UTC is 01:00/00:00 Eastern, ahead of the 04:15 backup.
|
||||
- {k: maintenance_window_start, v: "5", t: integer}
|
||||
- {k: default_phone_region, v: US}
|
||||
- {k: loglevel, v: "2", t: integer}
|
||||
- {k: log_rotate_size, v: "10485760", t: integer}
|
||||
- {k: mail_template_class, v: "OCA\\Debyltechmail\\Mail\\DebyltechEMailTemplate"}
|
||||
|
||||
@@ -72,6 +72,10 @@ if (!str_contains($text, 'official document-signing request')) {
|
||||
if (!str_contains($html, 'debyltech.com/legal/privacy') || !str_contains($html, 'debyltech.com/legal/tos')) {
|
||||
$failures[] = 'privacy/terms links missing from footer';
|
||||
}
|
||||
if (!str_contains($html, 'content="light only"')) {
|
||||
$failures[] = 'color-scheme "light only" meta missing -- dark-mode mail clients will repaint '
|
||||
. 'the ground and bury the black wordmark (did upstream rename </head> in $head?)';
|
||||
}
|
||||
if (preg_match('/[»«]/u', $html)) {
|
||||
$failures[] = 'German guillemets survived into the body';
|
||||
}
|
||||
@@ -131,6 +135,20 @@ if ($appConfig->getValueString('core', 'shareapi_restrict_user_enumeration_full_
|
||||
. 'unset or "yes"; it is NOT the knob for the account-owned-email problem.';
|
||||
}
|
||||
|
||||
// Outside signers are invited by address; without an enabled email identify
|
||||
// method the signer search returns "No signers." for any non-account email.
|
||||
$methods = json_decode($appConfig->getValueString('libresign', 'identify_methods', '[]'), true) ?: [];
|
||||
$emailOn = false;
|
||||
foreach ($methods as $m) {
|
||||
if (($m['name'] ?? '') === 'email' && !empty($m['enabled'])) {
|
||||
$emailOn = true;
|
||||
}
|
||||
}
|
||||
if (!$emailOn) {
|
||||
$failures[] = 'libresign email identify method is not enabled -- outside addresses '
|
||||
. 'cannot be added as signers ("No signers.")';
|
||||
}
|
||||
|
||||
$identDocs = $appConfig->getValueString('libresign', 'identification_documents', '');
|
||||
if ($identDocs !== '0') {
|
||||
$failures[] = 'libresign identification_documents is "' . $identDocs
|
||||
|
||||
Reference in New Issue
Block a user