Premium user can enable data breach monitoring (#2084)
* add User.enable_data_breach_check column * user can turn on/off the data breach check * only run data breach check for user who enables it * add tips to run tests using a local DB (without docker) * refactor True check * trim trailing space * fix test * Apply suggestions from code review Co-authored-by: Adrià Casajús <acasajus@users.noreply.github.com> * format --------- Co-authored-by: Son NK <son@simplelogin.io> Co-authored-by: Adrià Casajús <acasajus@users.noreply.github.com>
This commit is contained in:
parent
2eb5feaa8f
commit
d5df91aab6
|
@ -68,6 +68,12 @@ For most tests, you will need to have ``redis`` installed and started on your ma
|
|||
sh scripts/run-test.sh
|
||||
```
|
||||
|
||||
You can also run tests using a local Postgres DB to speed things up. This can be done by
|
||||
|
||||
- creating an empty test DB and running the database migration by `dropdb test && createdb test && DB_URI=postgresql://localhost:5432/test alembic upgrade head`
|
||||
|
||||
- replacing the `DB_URI` in `test.env` file by `DB_URI=postgresql://localhost:5432/test`
|
||||
|
||||
## Run the code locally
|
||||
|
||||
Install npm packages
|
||||
|
|
|
@ -227,6 +227,21 @@ def setting():
|
|||
Session.commit()
|
||||
flash("Your preference has been updated", "success")
|
||||
return redirect(url_for("dashboard.setting"))
|
||||
elif request.form.get("form-name") == "enable_data_breach_check":
|
||||
if not current_user.is_premium():
|
||||
flash("Only premium plan can enable data breach monitoring", "warning")
|
||||
return redirect(url_for("dashboard.setting"))
|
||||
choose = request.form.get("enable_data_breach_check")
|
||||
if choose == "on":
|
||||
LOG.i("User {current_user} has enabled data breach monitoring")
|
||||
current_user.enable_data_breach_check = True
|
||||
flash("Data breach monitoring is enabled", "success")
|
||||
else:
|
||||
LOG.i("User {current_user} has disabled data breach monitoring")
|
||||
current_user.enable_data_breach_check = False
|
||||
flash("Data breach monitoring is disabled", "info")
|
||||
Session.commit()
|
||||
return redirect(url_for("dashboard.setting"))
|
||||
elif request.form.get("form-name") == "sender-in-ra":
|
||||
choose = request.form.get("enable")
|
||||
if choose == "on":
|
||||
|
|
|
@ -525,6 +525,11 @@ class User(Base, ModelMixin, UserMixin, PasswordOracle):
|
|||
sa.Boolean, default=True, nullable=False, server_default="1"
|
||||
)
|
||||
|
||||
# user opted in for data breach check
|
||||
enable_data_breach_check = sa.Column(
|
||||
sa.Boolean, default=False, nullable=False, server_default="0"
|
||||
)
|
||||
|
||||
# bitwise flags. Allow for future expansion
|
||||
flags = sa.Column(
|
||||
sa.BigInteger,
|
||||
|
|
1
cron.py
1
cron.py
|
@ -1070,6 +1070,7 @@ def get_alias_to_check_hibp(
|
|||
Alias.id >= min_alias_id,
|
||||
Alias.id < max_alias_id,
|
||||
User.disabled == False, # noqa: E712
|
||||
User.enable_data_breach_check,
|
||||
or_(
|
||||
User.lifetime,
|
||||
ManualSubscription.end_at > now,
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: fa2f19bb4e5a
|
||||
Revises: 52510a633d6f
|
||||
Create Date: 2024-04-09 13:12:26.305340
|
||||
|
||||
"""
|
||||
import sqlalchemy_utils
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'fa2f19bb4e5a'
|
||||
down_revision = '52510a633d6f'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('users', sa.Column('enable_data_breach_check', sa.Boolean(), server_default='0', nullable=False))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('users', 'enable_data_breach_check')
|
||||
# ### end Alembic commands ###
|
|
@ -249,6 +249,42 @@
|
|||
</div>
|
||||
</div>
|
||||
<!-- END Random alias -->
|
||||
<!-- Data breach check -->
|
||||
<div class="card" id="data-breach">
|
||||
<div class="card-body">
|
||||
<div class="card-title">Data breach monitoring</div>
|
||||
<div class="mt-1 mb-3">
|
||||
{% if not current_user.is_premium() %}
|
||||
|
||||
<div class="alert alert-info" role="alert">
|
||||
This feature is only available on Premium plan.
|
||||
<a href="{{ url_for('dashboard.pricing') }}"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">
|
||||
Upgrade<i class="fe fe-external-link"></i>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
If enabled, we will inform you via email if one of your aliases appears in a data breach.
|
||||
<br>
|
||||
SimpleLogin uses <a href="https://haveibeenpwned.com/">HaveIBeenPwned</a> API for checking for data breaches.
|
||||
</div>
|
||||
<form method="post" action="#data-breach">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="enable_data_breach_check">
|
||||
<div class="form-check">
|
||||
<input type="checkbox"
|
||||
id="enable_data_breach_check"
|
||||
name="enable_data_breach_check"
|
||||
{% if current_user.enable_data_breach_check %} checked{% endif %}
|
||||
class="form-check-input">
|
||||
<label for="enable_data_breach_check">Enable data breach monitoring</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-outline-primary">Update</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END Data breach check -->
|
||||
<!-- Sender Format -->
|
||||
<div class="card" id="sender-format">
|
||||
<div class="card-body">
|
||||
|
@ -285,7 +321,9 @@
|
|||
No Name (i.e. only reverse-alias)
|
||||
</option>
|
||||
</select>
|
||||
<button class="btn btn-outline-primary mt-3">Update</button>
|
||||
<button class="btn btn-outline-primary mt-3">
|
||||
Update
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -295,7 +333,9 @@
|
|||
<div class="card-body">
|
||||
<div class="card-title">
|
||||
Reverse Alias Replacement
|
||||
<div class="badge badge-warning">Experimental</div>
|
||||
<div class="badge badge-warning">
|
||||
Experimental
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
When replying to a forwarded email, the <b>reverse-alias</b> can be automatically included
|
||||
|
@ -312,9 +352,13 @@
|
|||
name="replace-ra"
|
||||
{% if current_user.replace_reverse_alias %} checked{% endif %}
|
||||
class="form-check-input">
|
||||
<label for="replace-ra">Enable replacing reverse alias</label>
|
||||
<label for="replace-ra">
|
||||
Enable replacing reverse alias
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-outline-primary">Update</button>
|
||||
<button type="submit" class="btn btn-outline-primary">
|
||||
Update
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -31,6 +31,7 @@ def test_get_alias_for_free_user_has_no_alias():
|
|||
def test_get_alias_for_lifetime_with_null_hibp_date():
|
||||
user = create_new_user()
|
||||
user.lifetime = True
|
||||
user.enable_data_breach_check = True
|
||||
alias_id = Alias.create_new_random(user).id
|
||||
Session.commit()
|
||||
aliases = list(
|
||||
|
@ -42,6 +43,7 @@ def test_get_alias_for_lifetime_with_null_hibp_date():
|
|||
def test_get_alias_for_lifetime_with_old_hibp_date():
|
||||
user = create_new_user()
|
||||
user.lifetime = True
|
||||
user.enable_data_breach_check = True
|
||||
alias = Alias.create_new_random(user)
|
||||
alias.hibp_last_check = arrow.now().shift(days=-1)
|
||||
alias_id = alias.id
|
||||
|
@ -97,6 +99,7 @@ sub_generator_list = [
|
|||
@pytest.mark.parametrize("sub_generator", sub_generator_list)
|
||||
def test_get_alias_for_sub(sub_generator):
|
||||
user = create_new_user()
|
||||
user.enable_data_breach_check = True
|
||||
sub_generator(user)
|
||||
alias_id = Alias.create_new_random(user).id
|
||||
Session.commit()
|
||||
|
@ -140,3 +143,26 @@ def test_already_checked_is_not_checked():
|
|||
cron.get_alias_to_check_hibp(arrow.now(), [user.id], alias_id, alias_id + 1)
|
||||
)
|
||||
assert len(aliases) == 0
|
||||
|
||||
|
||||
def test_outed_in_user_is_checked():
|
||||
user = create_new_user()
|
||||
user.lifetime = True
|
||||
user.enable_data_breach_check = True
|
||||
alias_id = Alias.create_new_random(user).id
|
||||
Session.commit()
|
||||
aliases = list(
|
||||
cron.get_alias_to_check_hibp(arrow.now(), [], alias_id, alias_id + 1)
|
||||
)
|
||||
assert len(aliases) == 1
|
||||
|
||||
|
||||
def test_outed_out_user_is_not_checked():
|
||||
user = create_new_user()
|
||||
user.lifetime = True
|
||||
alias_id = Alias.create_new_random(user).id
|
||||
Session.commit()
|
||||
aliases = list(
|
||||
cron.get_alias_to_check_hibp(arrow.now(), [], alias_id, alias_id + 1)
|
||||
)
|
||||
assert len(aliases) == 0
|
||||
|
|
Loading…
Reference in New Issue