No Description

Josef Straßl 094d5adde4 changing move order to move, then release, to have the messages already in the target folder, when release might fail. 5 days ago
certs d383deaeb6 expanding 3 weeks ago
deploy dad8a929bb unify service name to ppsq 2 weeks ago
docs 094d5adde4 changing move order to move, then release, to have the messages already in the target folder, when release might fail. 5 days ago
static 094d5adde4 changing move order to move, then release, to have the messages already in the target folder, when release might fail. 5 days ago
templates 094d5adde4 changing move order to move, then release, to have the messages already in the target folder, when release might fail. 5 days ago
tests 094d5adde4 changing move order to move, then release, to have the messages already in the target folder, when release might fail. 5 days ago
.gitignore 239f422b9b more dev 2 weeks ago
AGENTS.md 094d5adde4 changing move order to move, then release, to have the messages already in the target folder, when release might fail. 5 days ago
README.md 094d5adde4 changing move order to move, then release, to have the messages already in the target folder, when release might fail. 5 days ago
admin.py 239f422b9b more dev 2 weeks ago
app.py 239f422b9b more dev 2 weeks ago
auth.py 239f422b9b more dev 2 weeks ago
config.example.toml 094d5adde4 changing move order to move, then release, to have the messages already in the target folder, when release might fail. 5 days ago
config_store.py 094d5adde4 changing move order to move, then release, to have the messages already in the target folder, when release might fail. 5 days ago
logs.py 239f422b9b more dev 2 weeks ago
pipeline.py 094d5adde4 changing move order to move, then release, to have the messages already in the target folder, when release might fail. 5 days ago
pps_client.py 95c2a431ba improvement 3 weeks ago
prefs.py 239f422b9b more dev 2 weeks ago
pytest.ini 239f422b9b more dev 2 weeks ago
requirements-dev.txt 239f422b9b more dev 2 weeks ago
requirements.lock 239f422b9b more dev 2 weeks ago
requirements.txt 239f422b9b more dev 2 weeks ago
worker.py 094d5adde4 changing move order to move, then release, to have the messages already in the target folder, when release might fail. 5 days ago
wsgi.py 239f422b9b more dev 2 weeks ago

README.md

PPS Quarantine Manager

A web app for operators to manage a Proofpoint Protection Server (PPS) email quarantine: list quarantined mail by folder, read messages (headers, body, attachments, raw headers), and run bulk actions — Release, Report & Release, Delete, Move — as asynchronous background jobs, so slow PPS calls never block the operator.

  • Okta OIDC login with an allowed-users list and roles (admin/user).
  • Admin panel (/admin, admin-only) to manage users, the PPS connection, folders, defaults, the Report & Release pipeline, background jobs, and logs — editing config.toml live (comment-preserving), with write-only credentials.
  • Per-user preferences (show limit, default folder, sort) layered over admin defaults.
  • Audit log of every message acted on, attributed to the acting user.

Quick start

python3 -m venv venv
venv/bin/pip install -r requirements.txt
cp config.example.toml config.toml     # edit: [okta], [pps] creds, [[auth.users]], secret_key
venv/bin/python wsgi.py                 # http://127.0.0.1:8080

Generate a session key: python -c "import secrets; print(secrets.token_urlsafe(48))".

Local dev without Okta: set [auth] mode = "static", use [auth.static] credentials, and run with PPSQ_CONFIG=/tmp/dev.toml PPSQ_ALLOW_INSECURE=1 venv/bin/python wsgi.py. See docs/development.md.

How it works

wsgi.py is the single-process entrypoint: it builds the config store, the SQLite-backed job queue, and the per-user prefs store, starts one background worker thread, and serves via waitress. The browser fires an action and gets an immediate 202; the worker drains the queue and calls PPS. Jobs survive a restart. Run as a single process only.

Report & Release is a configurable pipeline of steps (move → release, pick either or both) — move files the mail into a report folder, release delivers it from there. It never deletes; Delete is its own action. Configured with checkboxes in the admin panel. Between steps the job waits step_delay_seconds for PPS's backend to settle and then re-reads where the messages actually are; while it waits it is parked back on the queue, so other jobs keep running.

Documentation

| Doc | | |-----|--| | AGENTS.md | Start here. Module map + the invariants you must not break. | | docs/architecture.md | System design, request/job lifecycle, hot-reload. | | docs/configuration.md | Every config key, zones, live-vs-restart. | | docs/api.md | HTTP API reference. | | docs/okta-setup.md | Okta OIDC app setup + access model. | | docs/operations.md | Deploy, logs, backup, credential rotation, troubleshooting. | | docs/development.md | Dev setup, running without Okta/PPS, tests. | | docs/security.md | Auth, sessions/CSRF, secrets, known limitations. |

Tests

venv/bin/pip install -r requirements-dev.txt
venv/bin/python -m pytest        # ~57 tests, no Okta tenant or real config needed

Requirements

Python 3.11+ (uses tomllib/tomlkit; developed on 3.12). Flask, Authlib, tomlkit, requests, waitress — see requirements.txt (ranges) / requirements.lock (pinned).

Status

Internal production release, evolved from the original PoC. Single-process; secrets are plaintext-on-disk (0600); roles are app-managed, not Okta groups. See docs/security.md for the accepted limitations.