From e893272d3bc7209f90314d04fe8b0086a0de92a0 Mon Sep 17 00:00:00 2001 From: Son NK Date: Tue, 9 Apr 2024 13:33:41 +0200 Subject: [PATCH 1/9] add User.enable_data_breach_check column --- app/models.py | 5 ++++ .../versions/2024_040913_fa2f19bb4e5a_.py | 29 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 migrations/versions/2024_040913_fa2f19bb4e5a_.py diff --git a/app/models.py b/app/models.py index 472b98a85..25d77bf39 100644 --- a/app/models.py +++ b/app/models.py @@ -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, diff --git a/migrations/versions/2024_040913_fa2f19bb4e5a_.py b/migrations/versions/2024_040913_fa2f19bb4e5a_.py new file mode 100644 index 000000000..509198880 --- /dev/null +++ b/migrations/versions/2024_040913_fa2f19bb4e5a_.py @@ -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 ### From 67df0e95c45fe142cc9db6997bf73a39a7bfbe06 Mon Sep 17 00:00:00 2001 From: Son NK Date: Tue, 9 Apr 2024 13:34:57 +0200 Subject: [PATCH 2/9] user can turn on/off the data breach check --- app/dashboard/views/setting.py | 15 +++++++++ templates/dashboard/setting.html | 52 +++++++++++++++++++++++++++++--- 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/app/dashboard/views/setting.py b/app/dashboard/views/setting.py index cefbaf58a..857905ab8 100644 --- a/app/dashboard/views/setting.py +++ b/app/dashboard/views/setting.py @@ -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 add enable data breach monitoring", "warning" + ) + return redirect(url_for("dashboard.setting")) + choose = request.form.get("enable_data_breach_check") + if choose == "on": + current_user.enable_data_breach_check = True + flash("Data breach monitoring is enabled", "success") + else: + 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": diff --git a/templates/dashboard/setting.html b/templates/dashboard/setting.html index 03403766f..ab6d865cb 100644 --- a/templates/dashboard/setting.html +++ b/templates/dashboard/setting.html @@ -249,6 +249,42 @@ + +
+
+
Data breach monitoring
+
+ {% if not current_user.is_premium() %} + + + {% endif %} + If enabled, we will inform you via email if one of your aliases appears in a data breach. +
+ SimpleLogin uses HaveIBeenPwned API for checking for data breaches. +
+
+ {{ csrf_form.csrf_token }} + +
+ + +
+ +
+
+
+
@@ -285,7 +321,9 @@ No Name (i.e. only reverse-alias) - +
@@ -295,7 +333,9 @@
Reverse Alias Replacement -
Experimental
+
+ Experimental +
When replying to a forwarded email, the reverse-alias can be automatically included @@ -312,9 +352,13 @@ name="replace-ra" {% if current_user.replace_reverse_alias %} checked{% endif %} class="form-check-input"> - +
- +
From b22ca08f0e869be9b3140f5b4a5368b250ff3bd1 Mon Sep 17 00:00:00 2001 From: Son NK Date: Tue, 9 Apr 2024 13:36:09 +0200 Subject: [PATCH 3/9] only run data breach check for user who enables it --- cron.py | 1 + tests/cron/test_get_alias_for_hibp.py | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/cron.py b/cron.py index 7466af780..5dce4fc5e 100644 --- a/cron.py +++ b/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 == True, or_( User.lifetime, ManualSubscription.end_at > now, diff --git a/tests/cron/test_get_alias_for_hibp.py b/tests/cron/test_get_alias_for_hibp.py index ba41666c2..241261cb5 100644 --- a/tests/cron/test_get_alias_for_hibp.py +++ b/tests/cron/test_get_alias_for_hibp.py @@ -140,3 +140,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 From 1c6dab3c86c2f013962dc00f21c708527d61d121 Mon Sep 17 00:00:00 2001 From: Son NK Date: Tue, 9 Apr 2024 13:36:24 +0200 Subject: [PATCH 4/9] add tips to run tests using a local DB (without docker) --- CONTRIBUTING.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b6b1019fa..e88d2a1a6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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 From 57cd1452b0bddd20b31c81e13c9f9add5840e802 Mon Sep 17 00:00:00 2001 From: Son NK Date: Tue, 9 Apr 2024 13:54:06 +0200 Subject: [PATCH 5/9] refactor True check --- cron.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cron.py b/cron.py index 5dce4fc5e..2f1e6c886 100644 --- a/cron.py +++ b/cron.py @@ -1070,7 +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 == True, + User.enable_data_breach_check, or_( User.lifetime, ManualSubscription.end_at > now, From 0e0e0271e91ef738b344166338dc3f017ee100bc Mon Sep 17 00:00:00 2001 From: Son NK Date: Tue, 9 Apr 2024 14:34:13 +0200 Subject: [PATCH 6/9] trim trailing space --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e88d2a1a6..6dc29eae6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -68,7 +68,7 @@ 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 +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` From c21c3421b574f542d046a6ce202516986fdf5265 Mon Sep 17 00:00:00 2001 From: Son NK Date: Tue, 9 Apr 2024 21:42:05 +0200 Subject: [PATCH 7/9] fix test --- tests/cron/test_get_alias_for_hibp.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/cron/test_get_alias_for_hibp.py b/tests/cron/test_get_alias_for_hibp.py index 241261cb5..370c6ff8d 100644 --- a/tests/cron/test_get_alias_for_hibp.py +++ b/tests/cron/test_get_alias_for_hibp.py @@ -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 @@ def create_partner_sub(user: User): @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() From 6ce07f456f8918ab25980488d5cd61adf50ff420 Mon Sep 17 00:00:00 2001 From: Son Nguyen Kim Date: Thu, 11 Apr 2024 21:52:58 +0200 Subject: [PATCH 8/9] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Adrià Casajús --- app/dashboard/views/setting.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/dashboard/views/setting.py b/app/dashboard/views/setting.py index 857905ab8..07f05235a 100644 --- a/app/dashboard/views/setting.py +++ b/app/dashboard/views/setting.py @@ -230,14 +230,16 @@ def setting(): elif request.form.get("form-name") == "enable_data_breach_check": if not current_user.is_premium(): flash( - "Only premium plan can add enable data breach monitoring", "warning" + "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() From 1dd74ac6e7c3acf436497121e7b433ff405ebe42 Mon Sep 17 00:00:00 2001 From: Son NK Date: Thu, 11 Apr 2024 22:47:01 +0200 Subject: [PATCH 9/9] format --- app/dashboard/views/setting.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/dashboard/views/setting.py b/app/dashboard/views/setting.py index 07f05235a..f07ebb17a 100644 --- a/app/dashboard/views/setting.py +++ b/app/dashboard/views/setting.py @@ -229,9 +229,7 @@ def setting(): 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" - ) + 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":