diff --git a/passbook/stages/otp_static/migrations/0002_otpstaticstage_configure_flow.py b/passbook/stages/otp_static/migrations/0002_otpstaticstage_configure_flow.py new file mode 100644 index 000000000..bcd664b5e --- /dev/null +++ b/passbook/stages/otp_static/migrations/0002_otpstaticstage_configure_flow.py @@ -0,0 +1,52 @@ +# Generated by Django 3.1.1 on 2020-09-24 20:51 + +import django.db.models.deletion +from django.apps.registry import Apps +from django.db import migrations, models +from django.db.backends.base.schema import BaseDatabaseSchemaEditor + + +def create_default_setup_flow(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): + Flow = apps.get_model("passbook_flows", "Flow") + FlowStageBinding = apps.get_model("passbook_flows", "FlowStageBinding") + + OTPStaticStage = apps.get_model("passbook_stages_otp_static", "OTPStaticStage") + + db_alias = schema_editor.connection.alias + + flow, _ = Flow.objects.using(db_alias).update_or_create( + slug="default-otp-static-configure", + designation=FlowDesignation.STAGE_SETUP, + defaults={"name": "Setup Static OTP Tokens"}, + ) + + stage = OTPStaticStage.objects.using(db_alias).update_or_create( + name="default-otp-static-configure" + ) + + FlowStageBinding.objects.using(db_alias).update_or_create( + target=flow, stage=stage, defaults={"order": 0} + ) + + +class Migration(migrations.Migration): + + dependencies = [ + ("passbook_flows", "0013_auto_20200924_1605"), + ("passbook_stages_otp_static", "0001_initial"), + ] + + operations = [ + migrations.AddField( + model_name="otpstaticstage", + name="configure_flow", + field=models.ForeignKey( + blank=True, + help_text="Flow used by an authenticated user to configure this Stage. If empty, user will not be able to configure this stage.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="passbook_flows.flow", + ), + ), + migrations.RunPython(create_default_setup_flow), + ] diff --git a/passbook/stages/otp_static/models.py b/passbook/stages/otp_static/models.py index 2b65324b0..06fddbed2 100644 --- a/passbook/stages/otp_static/models.py +++ b/passbook/stages/otp_static/models.py @@ -9,10 +9,10 @@ from django.views import View from rest_framework.serializers import BaseSerializer from passbook.core.types import UIUserSettings -from passbook.flows.models import Stage +from passbook.flows.models import ConfigurableStage, Stage -class OTPStaticStage(Stage): +class OTPStaticStage(ConfigurableStage, Stage): """Generate static tokens for the user as a backup.""" token_count = models.IntegerField(default=6) @@ -36,7 +36,11 @@ class OTPStaticStage(Stage): @property def ui_user_settings(self) -> Optional[UIUserSettings]: return UIUserSettings( - name="Static OTP", url=reverse("passbook_stages_otp_static:user-settings"), + name="Static OTP", + url=reverse( + "passbook_stages_otp_static:user-settings", + kwargs={"stage_uuid": self.stage_uuid}, + ), ) def __str__(self) -> str: diff --git a/passbook/stages/otp_static/templates/stages/otp_static/user_settings.html b/passbook/stages/otp_static/templates/stages/otp_static/user_settings.html index ab443368a..a54270c35 100644 --- a/passbook/stages/otp_static/templates/stages/otp_static/user_settings.html +++ b/passbook/stages/otp_static/templates/stages/otp_static/user_settings.html @@ -9,12 +9,28 @@ {% trans "Static One-Time Passwords" %}
+

+ {% blocktrans with state=state|yesno:"Enabled,Disabled" %} + Status: {{ state }} + {% endblocktrans %} + {% if state %} + + {% else %} + + {% endif %} +

- {% trans "Disable Static Tokens" %} + {% if not state %} + {% if stage.configure_flow %} + {% trans "Enable Static Tokens" %} + {% endif %} + {% else %} + {% trans "Disable Static Tokens" %} + {% endif %}
{% endblock %} diff --git a/passbook/stages/otp_static/urls.py b/passbook/stages/otp_static/urls.py index 7eb2ce2c8..38db39c12 100644 --- a/passbook/stages/otp_static/urls.py +++ b/passbook/stages/otp_static/urls.py @@ -4,6 +4,8 @@ from django.urls import path from passbook.stages.otp_static.views import DisableView, UserSettingsView urlpatterns = [ - path("settings", UserSettingsView.as_view(), name="user-settings"), - path("disable", DisableView.as_view(), name="disable"), + path( + "/settings/", UserSettingsView.as_view(), name="user-settings" + ), + path("/disable/", DisableView.as_view(), name="disable"), ] diff --git a/passbook/stages/otp_static/views.py b/passbook/stages/otp_static/views.py index f766d6158..b8b1436eb 100644 --- a/passbook/stages/otp_static/views.py +++ b/passbook/stages/otp_static/views.py @@ -21,6 +21,7 @@ class UserSettingsView(LoginRequiredMixin, TemplateView): static_devices = StaticDevice.objects.filter( user=self.request.user, confirmed=True ) + kwargs["state"] = static_devices.exists() if static_devices.exists(): kwargs["tokens"] = StaticToken.objects.filter(device=static_devices.first()) return kwargs