Skip to content

[Feature] Introduce sender-blacklists (on both global/admin and user level) - #2694

Open
chrisblech wants to merge 25 commits into
simple-login:masterfrom
chrisblech:feature/user-blacklists
Open

[Feature] Introduce sender-blacklists (on both global/admin and user level)#2694
chrisblech wants to merge 25 commits into
simple-login:masterfrom
chrisblech:feature/user-blacklists

Conversation

@chrisblech

Copy link
Copy Markdown
Contributor

While using a self-hosted instance für some months now with a throughput of more than 5000 mails/month and consequently manually disabling spam-sender (on contact level), simplelogin blocks about 600 SPAM mails per month.
When looking at the remaining 350 spam mails that are still forwarded to my inbox (and get caught by GMails SPAM sensor), I realize that a significant amount of them belongs to "spammer-domains" or at least follow the same pattern.

This PR adds two levels of sender blacklists to SimpleLogin: Admin and Users can use this to block sender addresses (or whole domains / TLDs) by regex pattern.

  • New DB table global_sender_blacklist (pattern regex + enabled + comment)
  • Admin UI (Flask-Admin) and User UI (Settings) to manage patterns
  • Inbound SMTP handling: unknown senders matching the blacklist create a Contact that is disabled. The following (untouched) mail handling logic will using the pre-existing behavior as a blocked Contact (2xx vs 5xx depending on user.block_behaviour)
  • Users can override the global logic, by (re-)enabling a disabled Contact.

Migration and tests are included.

[Bonus/Example] My personal blacklist patterns:

  • @novastek\.homes$
  • @devalser\.hair$
  • @tabot\.com\.tr$
  • @casang\.vip$
  • ^[a-z]+[0-9]{5}@[a-z]+\.[a-z]+\.shop$
  • @juntadeandalucia\.es$
  • @resend\.dev$

Comment thread app/models.py Outdated
Comment thread app/admin/global_sender_blacklist.py Outdated
Comment thread app/dashboard/views/setting.py Outdated
Comment thread app/dashboard/views/setting.py Outdated
Comment thread app/dashboard/views/setting.py
Comment thread app/dashboard/views/setting.py
Comment thread app/models.py Outdated
Comment thread app/regex_utils.py
@acasajus

Copy link
Copy Markdown
Collaborator

Please move the management of the ForbiddenEnvelopeSender into a separate file that can be tested and called from other places instead of adding more code to the settings page.

@acasajus

Copy link
Copy Markdown
Collaborator

Also there are no tests for per-user entry, regex validation on entry or deletion.

Comment thread app/sender_blacklist.py Outdated
Comment thread app/sender_blacklist.py
Comment thread app/sender_blacklist.py Outdated
Comment thread app/sender_blacklist.py Outdated
Comment thread email_handler.py
Comment thread email_handler.py Outdated
Comment thread app/sender_blacklist.py
@chrisblech

Copy link
Copy Markdown
Contributor Author

@acasajus thank you for your valuable feedback. I tried to address all your suggestions, hoping there is nothing left and this PR can find its way into this gerat product :-)

@chrisblech
chrisblech requested a review from acasajus July 8, 2026 17:56
@chrisblech

Copy link
Copy Markdown
Contributor Author

Hi @acasajus @cquintana92 ! Just a friendly follow-up on this PR.

I have addressed all review comments and updated the implementation accordingly. Since then, the PR has been waiting for a while, so I wanted to ask whether there are still any concerns or if anything else is needed before this can be considered for merging.

I've been running this change in my own self-hosted instance for some time now, and it has significantly reduced the amount of spam reaching my mailbox. As mentioned in the PR description, the feature is completely optional and does not change the default behaviour for existing users.

If there are any additional changes, refactoring, tests or documentation updates that would make this PR easier to merge, I'd be happy to work on them.

Thanks again for taking the time to review it.

This reverts commit bfb269a.
Replaces the earlier 'reorder migrations' approach, which reused the
existing revision IDs b7c1d6a4f2e1/9c2a7f3c1b21 with a different
down_revision. That silently no-ops on any deployment already stamped
at 9c2a7f3c1b21 (i.e. prod), skipping 4a9f8c2e1b3d's schema changes.
This adds a dedicated merge revision instead, per Alembic's documented
mechanism for divergent heads.
Unrelated pre-existing drift: pyproject.toml gained the cachetools
dependency on 2026-03-25 (28554a8) but uv.lock was never
re-locked afterwards (last touched 2026-02-12), so 'uv sync --locked'
in the Dockerfile build failed outright. Needed to get a buildable
image for the migration-graph fix; no dependency version constraints
in pyproject.toml were changed.
…t pin

The uv.lock regeneration exposed a real, pre-existing conflict: the ancient
'google-api-python-client ~= 1.12.3' pin (only used for
googleapiclient.discovery.build('oauth2','v2',...) in app/api/views/auth.py)
forced its google-api-core/protobuf transitive chain down to protobuf 3.20.3.
That is incompatible with the checked-in generated app/events/generated/event_pb2.py,
which requires google.protobuf.runtime_version (protobuf >= 4.25). This is why
the Feb-12 uv.lock (recorded protobuf 5.27.1) silently drifted from what
pyproject.toml's constraints actually resolve to today - PyPI's currently
available versions changed underneath it.

Confirmed merging upstream release v4.81.7 would NOT have fixed this: the 13
commits it has beyond this branch touch no dependency files at all, and its
own uv.lock still pins the same old google-api-python-client==1.12.3
(it just happens to still resolve to protobuf 5.27.1 there because it lacks
our added cachetools dependency's ripple effect through the resolver, and
because it was locked against an older PyPI package-version snapshot).

Fix: bump google-api-python-client to >=2.100,<3 and google-auth-httplib2 to
>=0.2.0,<1.0 (the only caller, app/api/views/auth.py, uses only the generic
discovery.build() API, stable across this major bump - verified by import
test), plus an explicit protobuf>=4.25,<6 constraint so the resolver can no
longer silently pick a protobuf incompatible with event_pb2.py. Verified:
uv lock resolves google-api-core to a stable 2.30.3 (no dev/pre-release),
protobuf to 5.29.6, and app/email_handler.py/job_runner.py/app.api.views.auth
all import cleanly against a real image build.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants