Sanitize Helpdesk mail settings for all projects

This commit is contained in:
Jason Thistlethwaite
2026-04-25 01:26:24 +00:00
parent f109fdcb91
commit 3c1d03bd7a
5 changed files with 36 additions and 15 deletions
+2 -1
View File
@@ -242,7 +242,8 @@ The worker processing policy is documented in:
### 7. Test Helpdesk Mail Reset
After importing a production database into the LAN test instance, reset all
Helpdesk-enabled projects to use the local Mailpit test mailbox with:
active projects to use the local Mailpit test mailbox for Helpdesk settings
with:
- [reset_helpdesk_mail_settings.py](/home/iadnah/redmine/reset_helpdesk_mail_settings.py:1)
+21
View File
@@ -32,6 +32,27 @@ environment. Before risky edits, archive the current plugin directories in
- Run `validate_helpdesk_outbox_worker.py` after outbox or worker changes,
then choose the external index target.
## 2026-04-25 - Test Helpdesk Credential Sanitization
- Touched areas:
- Test instance post-import tooling
- Post-import validation
- Purpose:
- Ensure production database imports cannot leave real Helpdesk POP3/SMTP
credentials on projects where Helpdesk is disabled but settings rows still
exist.
- Behavior changed:
- `reset_helpdesk_mail_settings.py` now rewrites Helpdesk mail settings for
every active project, not only projects with `contacts_helpdesk` enabled.
- `validate_test_instance.py` checks every active project for Mailpit
Helpdesk settings.
- LAN test result:
- `./reset_helpdesk_mail_settings.py --dry-run` matched 52 active projects.
- `./reset_helpdesk_mail_settings.py` updated 1,092 setting rows across 52
active projects.
- `./validate_test_instance.py` passed and reported 52 active projects with
matching Mailpit Helpdesk settings.
## 2026-04-25 - Outbox Worker Processing Policy
- Touched areas:
+4 -2
View File
@@ -69,8 +69,10 @@ Apply:
./reset_helpdesk_mail_settings.py
```
This rewrites all active `contacts_helpdesk` projects to use Mailpit for POP3
and SMTP. Passwords are written but not printed.
This rewrites all active projects to use Mailpit for Helpdesk POP3 and SMTP,
even if the Helpdesk module is currently disabled for a project. That prevents
imported real mail credentials from being used accidentally in the test
instance. Passwords are written but not printed.
## 4. Restart Passenger
+8 -10
View File
@@ -2,8 +2,9 @@
"""Reset RedmineUP Helpdesk mail settings on the LAN test Redmine instance.
This is intended to be run after importing a production database into the test
instance. It finds projects with the Helpdesk module enabled and rewrites only
the incoming/outgoing mail settings so test mail flows through Mailpit.
instance. It rewrites every active project's incoming/outgoing Helpdesk mail
settings so test mail flows through Mailpit and imported real credentials cannot
be used accidentally.
"""
from __future__ import annotations
@@ -113,7 +114,7 @@ class RemoteRedmine:
def main() -> int:
parser = argparse.ArgumentParser(
description="Reset Helpdesk mail settings for projects with the contacts_helpdesk module enabled."
description="Reset Helpdesk mail settings for all active projects."
)
parser.add_argument("--ssh-host", default=os.getenv("REDMINE_SSH_HOST", DEFAULT_SSH_HOST))
parser.add_argument("--ssh-key", type=Path, default=Path(os.getenv("REDMINE_SSH_KEY", str(DEFAULT_SSH_KEY))))
@@ -141,12 +142,12 @@ def main() -> int:
remote = RemoteRedmine(args.ssh_host, args.ssh_key, args.remote_redmine)
try:
projects = find_helpdesk_projects(remote, args.project)
projects = find_active_projects(remote, args.project)
if not projects:
print("No active projects with contacts_helpdesk enabled matched the requested filters.")
print("No active projects matched the requested filters.")
return 0
print(f"Matched {len(projects)} Helpdesk-enabled project(s):")
print(f"Matched {len(projects)} active project(s):")
for project in projects:
print(f" - #{project['id']} {project['identifier']} ({project['name']})")
@@ -165,7 +166,7 @@ def main() -> int:
return 1
def find_helpdesk_projects(remote: RemoteRedmine, filters: list[str]) -> list[dict[str, Any]]:
def find_active_projects(remote: RemoteRedmine, filters: list[str]) -> list[dict[str, Any]]:
where = ["p.status = 1"]
if filters:
clauses = []
@@ -183,9 +184,6 @@ SELECT HEX(CAST(JSON_OBJECT(
'name', p.name
) AS CHAR)) AS document
FROM projects p
JOIN enabled_modules em
ON em.project_id = p.id
AND em.name = 'contacts_helpdesk'
WHERE {' AND '.join(where)}
ORDER BY p.identifier;
"""
+1 -2
View File
@@ -231,7 +231,7 @@ ORDER BY identifier;
if failures:
results.append(CheckResult("FAIL", "Helpdesk Mailpit settings", "; ".join(failures[:8])))
else:
results.append(CheckResult("OK", "Helpdesk Mailpit settings", f"{len(settings_rows)} project(s) match"))
results.append(CheckResult("OK", "Helpdesk Mailpit settings", f"{len(settings_rows)} active project(s) match"))
except Exception as exc:
results.append(CheckResult("FAIL", "Database checks", f"{exc.__class__.__name__}: {exc}"))
return results
@@ -257,7 +257,6 @@ SELECT HEX(CAST(JSON_OBJECT(
'smtp_tls', MAX(CASE WHEN cs.name = 'helpdesk_smtp_tls' THEN cs.value END)
) AS CHAR)) AS document
FROM projects p
JOIN enabled_modules em ON em.project_id = p.id AND em.name = 'contacts_helpdesk'
LEFT JOIN contacts_settings cs ON cs.project_id = p.id
WHERE p.status = 1
GROUP BY p.id, p.identifier