Add notify_hibp cron job
This commit is contained in:
parent
5aef6cceb2
commit
fb4cb8727c
46
cron.py
46
cron.py
|
@ -7,6 +7,7 @@ from typing import List, Tuple
|
||||||
import arrow
|
import arrow
|
||||||
import requests
|
import requests
|
||||||
from sqlalchemy import func, desc, or_
|
from sqlalchemy import func, desc, or_
|
||||||
|
from sqlalchemy.orm import joinedload
|
||||||
|
|
||||||
from app import s3
|
from app import s3
|
||||||
from app.alias_utils import nb_email_log_for_mailbox
|
from app.alias_utils import nb_email_log_for_mailbox
|
||||||
|
@ -771,6 +772,47 @@ async def check_hibp():
|
||||||
LOG.d("Done checking HIBP API for aliases in breaches")
|
LOG.d("Done checking HIBP API for aliases in breaches")
|
||||||
|
|
||||||
|
|
||||||
|
def notify_hibp():
|
||||||
|
"""
|
||||||
|
Send aggregated email reports for HIBP breaches
|
||||||
|
"""
|
||||||
|
# to get a list of users that have at least a breached alias
|
||||||
|
alias_query = (
|
||||||
|
db.session.query(Alias)
|
||||||
|
.options(joinedload(Alias.hibp_breaches))
|
||||||
|
.filter(Alias.hibp_breaches.any())
|
||||||
|
.distinct(Alias.user_id)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
|
user_ids = [alias.user_id for alias in alias_query]
|
||||||
|
|
||||||
|
for user in User.query.filter(User.id.in_(user_ids)):
|
||||||
|
breached_aliases = (
|
||||||
|
db.session.query(Alias)
|
||||||
|
.options(joinedload(Alias.hibp_breaches))
|
||||||
|
.filter(Alias.hibp_breaches.any(), Alias.user_id == user.id)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
|
LOG.d(f"Send new breaches found email to user {user}")
|
||||||
|
|
||||||
|
send_email(
|
||||||
|
user.email,
|
||||||
|
f"You were in a data breach",
|
||||||
|
render(
|
||||||
|
"transactional/hibp-new-breaches.txt.jinja2",
|
||||||
|
user=user,
|
||||||
|
breached_aliases=breached_aliases,
|
||||||
|
),
|
||||||
|
render(
|
||||||
|
"transactional/hibp-new-breaches.html",
|
||||||
|
user=user,
|
||||||
|
breached_aliases=breached_aliases,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
LOG.d("Start running cronjob")
|
LOG.d("Start running cronjob")
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
|
@ -790,6 +832,7 @@ if __name__ == "__main__":
|
||||||
"delete_old_monitoring",
|
"delete_old_monitoring",
|
||||||
"check_custom_domain",
|
"check_custom_domain",
|
||||||
"check_hibp",
|
"check_hibp",
|
||||||
|
"notify_hibp",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
@ -827,3 +870,6 @@ if __name__ == "__main__":
|
||||||
elif args.job == "check_hibp":
|
elif args.job == "check_hibp":
|
||||||
LOG.d("Check HIBP")
|
LOG.d("Check HIBP")
|
||||||
asyncio.run(check_hibp())
|
asyncio.run(check_hibp())
|
||||||
|
elif args.job == "notify_hibp":
|
||||||
|
LOG.d("Notify users about HIBP breaches")
|
||||||
|
notify_hibp()
|
||||||
|
|
|
@ -58,4 +58,11 @@ jobs:
|
||||||
shell: /bin/bash
|
shell: /bin/bash
|
||||||
schedule: "0 18 * * *"
|
schedule: "0 18 * * *"
|
||||||
captureStderr: true
|
captureStderr: true
|
||||||
|
concurrencyPolicy: Forbid
|
||||||
|
|
||||||
|
- name: SimpleLogin Notify HIBP breaches
|
||||||
|
command: python /code/cron.py -j notify_hibp
|
||||||
|
shell: /bin/bash
|
||||||
|
schedule: "0 19 * * *"
|
||||||
|
captureStderr: true
|
||||||
concurrencyPolicy: Forbid
|
concurrencyPolicy: Forbid
|
17
server.py
17
server.py
|
@ -102,6 +102,8 @@ from app.models import (
|
||||||
Payout,
|
Payout,
|
||||||
Coupon,
|
Coupon,
|
||||||
SLDomain,
|
SLDomain,
|
||||||
|
Hibp,
|
||||||
|
AliasHibp,
|
||||||
)
|
)
|
||||||
from app.monitor.base import monitor_bp
|
from app.monitor.base import monitor_bp
|
||||||
from app.oauth.base import oauth_bp
|
from app.oauth.base import oauth_bp
|
||||||
|
@ -421,6 +423,21 @@ def fake_data():
|
||||||
|
|
||||||
SLDomain.create(domain="premium.com", premium_only=True, commit=True)
|
SLDomain.create(domain="premium.com", premium_only=True, commit=True)
|
||||||
|
|
||||||
|
hibp1 = Hibp.create(
|
||||||
|
name="first breach", description="breach description", commit=True
|
||||||
|
)
|
||||||
|
hibp2 = Hibp.create(
|
||||||
|
name="second breach", description="breach description", commit=True
|
||||||
|
)
|
||||||
|
breached_alias1 = Alias.create(
|
||||||
|
email="john@example.com", user_id=user.id, mailbox_id=m1.id, commit=True
|
||||||
|
)
|
||||||
|
breached_alias2 = Alias.create(
|
||||||
|
email="wick@example.com", user_id=user.id, mailbox_id=m1.id, commit=True
|
||||||
|
)
|
||||||
|
AliasHibp.create(hibp_id=hibp1.id, alias_id=breached_alias1.id)
|
||||||
|
AliasHibp.create(hibp_id=hibp2.id, alias_id=breached_alias2.id)
|
||||||
|
|
||||||
|
|
||||||
@login_manager.user_loader
|
@login_manager.user_loader
|
||||||
def load_user(user_id):
|
def load_user(user_id):
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% call text() %}
|
||||||
|
<h1>
|
||||||
|
{{ breached_aliases|count }} of your aliases are found in data breaches.
|
||||||
|
</h1>
|
||||||
|
{% endcall %}
|
||||||
|
|
||||||
|
<ol>
|
||||||
|
{%- for alias in breached_aliases[:10] %}
|
||||||
|
<li>{% call text() %}
|
||||||
|
<b>{{ alias.email }}</b> was found in {{ alias.hibp_breaches|count }} data breaches. <br>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
{% set breaches = alias.hibp_breaches|sort(attribute='date', reverse=True) %}
|
||||||
|
{%- for breach in breaches[:4] %}
|
||||||
|
<li>
|
||||||
|
<b>{{ breach.name }}</b> {% if breach.date %}({{ breach.date.format('YYYY-MM-DD') }}){% endif %}
|
||||||
|
{{ breach.description }}
|
||||||
|
</li>
|
||||||
|
{%- endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{% if breaches|length > 4 %}
|
||||||
|
And {{ breaches|length - 4 }} more data breaches...
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endcall %}</li>
|
||||||
|
{%- endfor %}
|
||||||
|
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
{% if breached_aliases|length > 10 %}
|
||||||
|
{% call text() %}
|
||||||
|
And {{ breached_aliases|length - 10 }} more aliases...
|
||||||
|
{% endcall %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% call text() %}
|
||||||
|
For more information, check <a href='https://haveibeenpwned.com/'>HaveIBeenPwned.com</a>.
|
||||||
|
{% endcall %}
|
||||||
|
|
||||||
|
|
||||||
|
{{ render_text('Best, <br />SimpleLogin Team.') }}
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,23 @@
|
||||||
|
{{ breached_aliases|count }} of your aliases are found in data breaches.
|
||||||
|
|
||||||
|
{% for alias in breached_aliases[:10] %}
|
||||||
|
{{ loop.index }} ) {{ alias.email }} was found in {{ alias.hibp_breaches|count }} data breaches.
|
||||||
|
|
||||||
|
{%- set breaches = alias.hibp_breaches|sort(attribute='date', reverse=True) %}
|
||||||
|
{% for breach in breaches[:4] %}
|
||||||
|
- {{ breach.name }} {% if breach.date %}({{ breach.date.format('YYYY-MM-DD') }}){% endif %}
|
||||||
|
{%- endfor %}
|
||||||
|
|
||||||
|
{%- if breaches|length > 4 %}
|
||||||
|
And {{ breaches|length - 4 }} more data breaches...
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{%- if breached_aliases|length > 10 %}
|
||||||
|
And {{ breached_aliases|length - 10 }} more aliases...
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
|
For more information, please check https://haveibeenpwned.com/.
|
||||||
|
|
||||||
|
Best,
|
||||||
|
SimpleLogin Team.
|
Loading…
Reference in New Issue