From d33f632203b98c09c8dcf3c2cd0ca86339878515 Mon Sep 17 00:00:00 2001
From: Jens Langhammer
Date: Tue, 30 Jun 2020 00:11:01 +0200
Subject: [PATCH 1/8] flows: add CancelView to cancel current flow execution
---
.../core/templates/login/form_with_user.html | 2 +-
passbook/flows/urls.py | 2 +
passbook/flows/views.py | 37 ++++++++++++-------
.../templates/oidc_provider/authorize.html | 2 +-
.../templates/stages/password/backend.html | 2 +-
5 files changed, 29 insertions(+), 16 deletions(-)
diff --git a/passbook/core/templates/login/form_with_user.html b/passbook/core/templates/login/form_with_user.html
index a7c2461fe..bed72f78c 100644
--- a/passbook/core/templates/login/form_with_user.html
+++ b/passbook/core/templates/login/form_with_user.html
@@ -14,7 +14,7 @@
{{ user.username }}
diff --git a/passbook/flows/urls.py b/passbook/flows/urls.py
index ca83335f7..26b0ebb59 100644
--- a/passbook/flows/urls.py
+++ b/passbook/flows/urls.py
@@ -3,6 +3,7 @@ from django.urls import path
from passbook.flows.models import FlowDesignation
from passbook.flows.views import (
+ CancelView,
FlowExecutorShellView,
FlowExecutorView,
FlowPermissionDeniedView,
@@ -36,6 +37,7 @@ urlpatterns = [
ToDefaultFlow.as_view(designation=FlowDesignation.UNRENOLLMENT),
name="default-unenrollment",
),
+ path("-/cancel/", CancelView.as_view(), name="cancel"),
path("b//", FlowExecutorView.as_view(), name="flow-executor"),
path(
"/", FlowExecutorShellView.as_view(), name="flow-executor-shell"
diff --git a/passbook/flows/views.py b/passbook/flows/views.py
index c78f1aa38..bbe010392 100644
--- a/passbook/flows/views.py
+++ b/passbook/flows/views.py
@@ -183,6 +183,30 @@ class FlowPermissionDeniedView(PermissionDeniedView):
"""User could not be authenticated"""
+class FlowExecutorShellView(TemplateView):
+ """Executor Shell view, loads a dummy card with a spinner
+ that loads the next stage in the background."""
+
+ template_name = "flows/shell.html"
+
+ def get_context_data(self, **kwargs) -> Dict[str, Any]:
+ kwargs["exec_url"] = reverse("passbook_flows:flow-executor", kwargs=self.kwargs)
+ kwargs["msg_url"] = reverse("passbook_api:messages-list")
+ self.request.session[SESSION_KEY_GET] = self.request.GET
+ return kwargs
+
+
+class CancelView(View):
+ """View which canels the currently active plan"""
+
+ def get(self, request: HttpRequest) -> HttpResponse:
+ """View which canels the currently active plan"""
+ if SESSION_KEY_PLAN in request.session:
+ del request.session[SESSION_KEY_PLAN]
+ LOGGER.debug("Canceled current plan")
+ return redirect("passbook_core:overview")
+
+
class ToDefaultFlow(View):
"""Redirect to default flow matching by designation"""
@@ -206,19 +230,6 @@ class ToDefaultFlow(View):
)
-class FlowExecutorShellView(TemplateView):
- """Executor Shell view, loads a dummy card with a spinner
- that loads the next stage in the background."""
-
- template_name = "flows/shell.html"
-
- def get_context_data(self, **kwargs) -> Dict[str, Any]:
- kwargs["exec_url"] = reverse("passbook_flows:flow-executor", kwargs=self.kwargs)
- kwargs["msg_url"] = reverse("passbook_api:messages-list")
- self.request.session[SESSION_KEY_GET] = self.request.GET
- return kwargs
-
-
def to_stage_response(request: HttpRequest, source: HttpResponse) -> HttpResponse:
"""Convert normal HttpResponse into JSON Response"""
if isinstance(source, HttpResponseRedirect) or source.status_code == 302:
diff --git a/passbook/providers/oidc/templates/oidc_provider/authorize.html b/passbook/providers/oidc/templates/oidc_provider/authorize.html
index 9326ee625..aabbb262e 100644
--- a/passbook/providers/oidc/templates/oidc_provider/authorize.html
+++ b/passbook/providers/oidc/templates/oidc_provider/authorize.html
@@ -38,7 +38,7 @@
{% blocktrans with user=user %}
You are logged in as {{ user }}. Not you?
{% endblocktrans %}
- {% trans 'Logout' %}
+ {% trans 'Logout' %}
diff --git a/passbook/stages/password/templates/stages/password/backend.html b/passbook/stages/password/templates/stages/password/backend.html
index a2b3db483..74547608d 100644
--- a/passbook/stages/password/templates/stages/password/backend.html
+++ b/passbook/stages/password/templates/stages/password/backend.html
@@ -21,7 +21,7 @@
{{ user.username }}
From dbee714dac786d4dafa59b5274f33cd66262a3fc Mon Sep 17 00:00:00 2001
From: Jens Langhammer
Date: Tue, 30 Jun 2020 00:19:06 +0200
Subject: [PATCH 2/8] api: fix consent stage missing from API
---
passbook/api/v2/urls.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/passbook/api/v2/urls.py b/passbook/api/v2/urls.py
index 8b2ee36f3..0ff9668b7 100644
--- a/passbook/api/v2/urls.py
+++ b/passbook/api/v2/urls.py
@@ -33,6 +33,7 @@ from passbook.sources.oauth.api import OAuthSourceViewSet
from passbook.sources.saml.api import SAMLSourceViewSet
from passbook.stages.captcha.api import CaptchaStageViewSet
from passbook.stages.dummy.api import DummyStageViewSet
+from passbook.stages.consent.api import ConsentStageViewSet
from passbook.stages.email.api import EmailStageViewSet
from passbook.stages.identification.api import IdentificationStageViewSet
from passbook.stages.invitation.api import InvitationStageViewSet, InvitationViewSet
@@ -85,6 +86,7 @@ router.register("propertymappings/saml", SAMLPropertyMappingViewSet)
router.register("stages/all", StageViewSet)
router.register("stages/captcha", CaptchaStageViewSet)
+router.register("stages/consent", ConsentStageViewSet)
router.register("stages/email", EmailStageViewSet)
router.register("stages/identification", IdentificationStageViewSet)
router.register("stages/invitation", InvitationStageViewSet)
From 9743ad33d60a18c69cd75b327833f7438f67f102 Mon Sep 17 00:00:00 2001
From: Jens Langhammer
Date: Tue, 30 Jun 2020 00:45:04 +0200
Subject: [PATCH 3/8] ci: add snyk
---
.github/workflows/ci.yml | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 883142150..481221f50 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -52,6 +52,14 @@ jobs:
run: sudo pip install -U wheel pipenv && pipenv install --dev
- name: Lint with bandit
run: pipenv run bandit -r passbook
+ snyk:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@master
+ - name: Run Snyk to check for vulnerabilities
+ uses: snyk/actions/python@master
+ env:
+ SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
pyright:
runs-on: ubuntu-latest
steps:
From 8e38bc87bcc0a2daba57955ac09a762406de969c Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
<27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Tue, 30 Jun 2020 05:16:31 +0000
Subject: [PATCH 4/8] build(deps): bump boto3 from 1.14.12 to 1.14.13
Bumps [boto3](https://github.com/boto/boto3) from 1.14.12 to 1.14.13.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.14.12...1.14.13)
Signed-off-by: dependabot-preview[bot]
---
Pipfile.lock | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/Pipfile.lock b/Pipfile.lock
index 20b2277dc..332684f95 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -46,18 +46,18 @@
},
"boto3": {
"hashes": [
- "sha256:2616351c98eec18d20a1d64b33355c86cd855ac96219d1b8428c9bfc590bde53",
- "sha256:7daad26a008c91dd7b82fde17d246d1fe6e4b3813426689ef8bac9017a277cfb"
+ "sha256:77d926c16ab2ab2bfe68811f3bc987e7d79c97f3e2adebf9265c587cdb1fc47b",
+ "sha256:7f558165eaa608a5d0e05227ee820f4b3cc74533a52c9dde7eb488eba091d50d"
],
"index": "pypi",
- "version": "==1.14.12"
+ "version": "==1.14.13"
},
"botocore": {
"hashes": [
- "sha256:45934d880378777cefeca727f369d1f5aebf6b254e9be58e7c77dd0b059338bb",
- "sha256:a94e0e2307f1b9fe3a84660842909cd2680b57a9fc9fb0c3a03b0afb2eadbe21"
+ "sha256:37b65cc48c99b7dd4d5606e56f76ecd88eb7be392ea8a166df734a6b3035301c",
+ "sha256:5ac9b53a75852fe4282be8741c8136e6948714fef6eff1bd9babb861f8647ba3"
],
- "version": "==1.17.12"
+ "version": "==1.17.13"
},
"celery": {
"hashes": [
From bf297b85933e2a975953078ac59c88bc5d2ccf04 Mon Sep 17 00:00:00 2001
From: Jens Langhammer
Date: Tue, 30 Jun 2020 10:23:39 +0200
Subject: [PATCH 5/8] admin: add info about latest version
---
.../templates/administration/overview.html | 12 +-
passbook/admin/views/overview.py | 20 ++-
passbook/api/v2/urls.py | 2 +-
swagger.yaml | 141 ++++++++++++++++++
4 files changed, 172 insertions(+), 3 deletions(-)
diff --git a/passbook/admin/templates/administration/overview.html b/passbook/admin/templates/administration/overview.html
index bc586ce7c..1c4cd1a4f 100644
--- a/passbook/admin/templates/administration/overview.html
+++ b/passbook/admin/templates/administration/overview.html
@@ -120,7 +120,17 @@
- {{ version }}
+ {% if version >= version_latest %}
+
+ {% blocktrans with version=version %}
+ {{ version }} (Up-to-date!)
+ {% endblocktrans %}
+ {% else %}
+
+ {% blocktrans with version=version latest=version_latest %}
+ {{ version }} ({{ latest }} is available!)
+ {% endblocktrans %}
+ {% endif %}
diff --git a/passbook/admin/views/overview.py b/passbook/admin/views/overview.py
index 83d0ea009..5c049cf7d 100644
--- a/passbook/admin/views/overview.py
+++ b/passbook/admin/views/overview.py
@@ -1,7 +1,11 @@
"""passbook administration overview"""
+from functools import lru_cache
+
from django.core.cache import cache
from django.shortcuts import redirect, reverse
from django.views.generic import TemplateView
+from packaging.version import Version, parse
+from requests import RequestException, get
from passbook import __version__
from passbook.admin.mixins import AdminRequiredMixin
@@ -12,6 +16,19 @@ from passbook.root.celery import CELERY_APP
from passbook.stages.invitation.models import Invitation
+@lru_cache
+def latest_version() -> Version:
+ """Get latest release from GitHub, cached"""
+ try:
+ data = get(
+ "https://api.github.com/repos/beryju/passbook/releases/latest"
+ ).json()
+ tag_name = data.get("tag_name")
+ return parse(tag_name.split("/")[1])
+ except RequestException:
+ return parse("0.0.0")
+
+
class AdministrationOverviewView(AdminRequiredMixin, TemplateView):
"""Overview View"""
@@ -33,7 +50,8 @@ class AdministrationOverviewView(AdminRequiredMixin, TemplateView):
kwargs["stage_count"] = len(Stage.objects.all())
kwargs["flow_count"] = len(Flow.objects.all())
kwargs["invitation_count"] = len(Invitation.objects.all())
- kwargs["version"] = __version__
+ kwargs["version"] = parse(__version__)
+ kwargs["version_latest"] = latest_version()
kwargs["worker_count"] = len(CELERY_APP.control.ping(timeout=0.5))
kwargs["providers_without_application"] = Provider.objects.filter(
application=None
diff --git a/passbook/api/v2/urls.py b/passbook/api/v2/urls.py
index 0ff9668b7..256cdb589 100644
--- a/passbook/api/v2/urls.py
+++ b/passbook/api/v2/urls.py
@@ -32,8 +32,8 @@ from passbook.sources.ldap.api import LDAPPropertyMappingViewSet, LDAPSourceView
from passbook.sources.oauth.api import OAuthSourceViewSet
from passbook.sources.saml.api import SAMLSourceViewSet
from passbook.stages.captcha.api import CaptchaStageViewSet
-from passbook.stages.dummy.api import DummyStageViewSet
from passbook.stages.consent.api import ConsentStageViewSet
+from passbook.stages.dummy.api import DummyStageViewSet
from passbook.stages.email.api import EmailStageViewSet
from passbook.stages.identification.api import IdentificationStageViewSet
from passbook.stages.invitation.api import InvitationStageViewSet, InvitationViewSet
diff --git a/swagger.yaml b/swagger.yaml
index 8a11bd93f..c5833bff9 100755
--- a/swagger.yaml
+++ b/swagger.yaml
@@ -3275,6 +3275,133 @@ paths:
required: true
type: string
format: uuid
+ /stages/consent/:
+ get:
+ operationId: stages_consent_list
+ description: ConsentStage Viewset
+ parameters:
+ - name: ordering
+ in: query
+ description: Which field to use when ordering the results.
+ required: false
+ type: string
+ - name: search
+ in: query
+ description: A search term.
+ required: false
+ type: string
+ - name: limit
+ in: query
+ description: Number of results to return per page.
+ required: false
+ type: integer
+ - name: offset
+ in: query
+ description: The initial index from which to return the results.
+ required: false
+ type: integer
+ responses:
+ '200':
+ description: ''
+ schema:
+ required:
+ - count
+ - results
+ type: object
+ properties:
+ count:
+ type: integer
+ next:
+ type: string
+ format: uri
+ x-nullable: true
+ previous:
+ type: string
+ format: uri
+ x-nullable: true
+ results:
+ type: array
+ items:
+ $ref: '#/definitions/ConsentStage'
+ tags:
+ - stages
+ post:
+ operationId: stages_consent_create
+ description: ConsentStage Viewset
+ parameters:
+ - name: data
+ in: body
+ required: true
+ schema:
+ $ref: '#/definitions/ConsentStage'
+ responses:
+ '201':
+ description: ''
+ schema:
+ $ref: '#/definitions/ConsentStage'
+ tags:
+ - stages
+ parameters: []
+ /stages/consent/{stage_uuid}/:
+ get:
+ operationId: stages_consent_read
+ description: ConsentStage Viewset
+ parameters: []
+ responses:
+ '200':
+ description: ''
+ schema:
+ $ref: '#/definitions/ConsentStage'
+ tags:
+ - stages
+ put:
+ operationId: stages_consent_update
+ description: ConsentStage Viewset
+ parameters:
+ - name: data
+ in: body
+ required: true
+ schema:
+ $ref: '#/definitions/ConsentStage'
+ responses:
+ '200':
+ description: ''
+ schema:
+ $ref: '#/definitions/ConsentStage'
+ tags:
+ - stages
+ patch:
+ operationId: stages_consent_partial_update
+ description: ConsentStage Viewset
+ parameters:
+ - name: data
+ in: body
+ required: true
+ schema:
+ $ref: '#/definitions/ConsentStage'
+ responses:
+ '200':
+ description: ''
+ schema:
+ $ref: '#/definitions/ConsentStage'
+ tags:
+ - stages
+ delete:
+ operationId: stages_consent_delete
+ description: ConsentStage Viewset
+ parameters: []
+ responses:
+ '204':
+ description: ''
+ tags:
+ - stages
+ parameters:
+ - name: stage_uuid
+ in: path
+ description: A UUID string identifying this Consent Stage.
+ required: true
+ type: string
+ format: uuid
/stages/dummy/:
get:
operationId: stages_dummy_list
@@ -6052,6 +6179,20 @@ definitions:
description: Private key, acquired from https://www.google.com/recaptcha/intro/v3.html
type: string
minLength: 1
+ ConsentStage:
+ required:
+ - name
+ type: object
+ properties:
+ pk:
+ title: Stage uuid
+ type: string
+ format: uuid
+ readOnly: true
+ name:
+ title: Name
+ type: string
+ minLength: 1
DummyStage:
required:
- name
From f0402236469fb4c9ab98c153eb400200f869fd0c Mon Sep 17 00:00:00 2001
From: Jens Langhammer
Date: Tue, 30 Jun 2020 10:23:54 +0200
Subject: [PATCH 6/8] audit: move events list from admin to audit app
---
passbook/admin/urls.py | 3 ---
.../administration => audit/templates}/audit/list.html | 0
passbook/audit/urls.py | 9 ++++++++-
passbook/{admin/views/audit.py => audit/views.py} | 2 +-
passbook/core/templates/base/page.html | 4 ++--
5 files changed, 11 insertions(+), 7 deletions(-)
rename passbook/{admin/templates/administration => audit/templates}/audit/list.html (100%)
rename passbook/{admin/views/audit.py => audit/views.py} (87%)
diff --git a/passbook/admin/urls.py b/passbook/admin/urls.py
index c1a7f824a..56a7603a6 100644
--- a/passbook/admin/urls.py
+++ b/passbook/admin/urls.py
@@ -3,7 +3,6 @@ from django.urls import path
from passbook.admin.views import (
applications,
- audit,
certificate_key_pair,
debug,
flows,
@@ -252,8 +251,6 @@ urlpatterns = [
certificate_key_pair.CertificateKeyPairDeleteView.as_view(),
name="certificatekeypair-delete",
),
- # Audit Log
- path("audit/", audit.EventListView.as_view(), name="audit-log"),
# Groups
path("groups/", groups.GroupListView.as_view(), name="groups"),
# Debug
diff --git a/passbook/admin/templates/administration/audit/list.html b/passbook/audit/templates/audit/list.html
similarity index 100%
rename from passbook/admin/templates/administration/audit/list.html
rename to passbook/audit/templates/audit/list.html
diff --git a/passbook/audit/urls.py b/passbook/audit/urls.py
index f7327b0ac..83be4fe29 100644
--- a/passbook/audit/urls.py
+++ b/passbook/audit/urls.py
@@ -1,2 +1,9 @@
"""passbook audit urls"""
-urlpatterns = []
+from django.urls import path
+
+from passbook.audit.views import EventListView
+
+urlpatterns = [
+ # Audit Log
+ path("audit/", EventListView.as_view(), name="log"),
+]
diff --git a/passbook/admin/views/audit.py b/passbook/audit/views.py
similarity index 87%
rename from passbook/admin/views/audit.py
rename to passbook/audit/views.py
index 56b274c8f..90599db49 100644
--- a/passbook/admin/views/audit.py
+++ b/passbook/audit/views.py
@@ -9,7 +9,7 @@ class EventListView(PermissionListMixin, ListView):
"""Show list of all invitations"""
model = Event
- template_name = "administration/audit/list.html"
+ template_name = "audit/list.html"
permission_required = "passbook_audit.view_event"
ordering = "-created"
paginate_by = 20
diff --git a/passbook/core/templates/base/page.html b/passbook/core/templates/base/page.html
index f3fea0fd7..9b918451f 100644
--- a/passbook/core/templates/base/page.html
+++ b/passbook/core/templates/base/page.html
@@ -32,8 +32,8 @@
{% if user.is_superuser %}
{% trans 'Administrate' %}
- {% trans 'Monitor' %}
+ {% trans 'Monitor' %}
{% endif %}
From 34be1dd9f445223925a1848348c63f8b7a408ca6 Mon Sep 17 00:00:00 2001
From: Jens Langhammer
Date: Tue, 30 Jun 2020 10:55:38 +0200
Subject: [PATCH 7/8] admin: add execute button to flow which executes flow
without cache
---
.../templates/administration/flow/list.html | 1 +
passbook/admin/urls.py | 5 ++++
passbook/admin/views/flows.py | 25 ++++++++++++++++++-
3 files changed, 30 insertions(+), 1 deletion(-)
diff --git a/passbook/admin/templates/administration/flow/list.html b/passbook/admin/templates/administration/flow/list.html
index c013e9348..0956ead3f 100644
--- a/passbook/admin/templates/administration/flow/list.html
+++ b/passbook/admin/templates/administration/flow/list.html
@@ -61,6 +61,7 @@
{% trans 'Edit' %}
{% trans 'Delete' %}
+ {% trans 'Execute' %}
|
{% endfor %}
diff --git a/passbook/admin/urls.py b/passbook/admin/urls.py
index 56a7603a6..af7e41ec9 100644
--- a/passbook/admin/urls.py
+++ b/passbook/admin/urls.py
@@ -187,6 +187,11 @@ urlpatterns = [
path(
"flows//update/", flows.FlowUpdateView.as_view(), name="flow-update",
),
+ path(
+ "flows//execute/",
+ flows.FlowDebugExecuteView.as_view(),
+ name="flow-execute",
+ ),
path(
"flows//delete/", flows.FlowDeleteView.as_view(), name="flow-delete",
),
diff --git a/passbook/admin/views/flows.py b/passbook/admin/views/flows.py
index 377cf93f4..2ec591c15 100644
--- a/passbook/admin/views/flows.py
+++ b/passbook/admin/views/flows.py
@@ -5,13 +5,17 @@ from django.contrib.auth.mixins import (
PermissionRequiredMixin as DjangoPermissionRequiredMixin,
)
from django.contrib.messages.views import SuccessMessageMixin
+from django.http import HttpRequest, HttpResponse
from django.urls import reverse_lazy
from django.utils.translation import ugettext as _
-from django.views.generic import DeleteView, ListView, UpdateView
+from django.views.generic import DeleteView, DetailView, ListView, UpdateView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
from passbook.flows.forms import FlowForm
from passbook.flows.models import Flow
+from passbook.flows.planner import PLAN_CONTEXT_PENDING_USER
+from passbook.flows.views import SESSION_KEY_PLAN, FlowPlanner
+from passbook.lib.utils.urls import redirect_with_qs
from passbook.lib.views import CreateAssignPermView
@@ -46,6 +50,25 @@ class FlowCreateView(
return super().get_context_data(**kwargs)
+class FlowDebugExecuteView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
+ """Debug exectue flow, setting the current user as pending user"""
+
+ model = Flow
+ permission_required = "passbook_flows.view_flow"
+
+ # pylint: disable=unused-argument
+ def get(self, request: HttpRequest, pk: str) -> HttpResponse:
+ """Debug exectue flow, setting the current user as pending user"""
+ flow: Flow = self.get_object()
+ planner = FlowPlanner(flow)
+ planner.use_cache = False
+ plan = planner.plan(self.request, {PLAN_CONTEXT_PENDING_USER: request.user,},)
+ self.request.session[SESSION_KEY_PLAN] = plan
+ return redirect_with_qs(
+ "passbook_flows:flow-executor-shell", self.request.GET, flow_slug=flow.slug,
+ )
+
+
class FlowUpdateView(
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, UpdateView
):
From de3b753a263c724b904607ae5dd49dd30ecbd3d9 Mon Sep 17 00:00:00 2001
From: Jens Langhammer
Date: Tue, 30 Jun 2020 11:18:39 +0200
Subject: [PATCH 8/8] flows: show error message in flow when stage raises
---
passbook/flows/templates/flows/error.html | 22 ++++++++++++++++
passbook/flows/views.py | 31 +++++++++++++++++++----
2 files changed, 48 insertions(+), 5 deletions(-)
create mode 100644 passbook/flows/templates/flows/error.html
diff --git a/passbook/flows/templates/flows/error.html b/passbook/flows/templates/flows/error.html
new file mode 100644
index 000000000..9f549077c
--- /dev/null
+++ b/passbook/flows/templates/flows/error.html
@@ -0,0 +1,22 @@
+{% load i18n %}
+
+
+
+
+
+ {% trans 'Whoops!' %}
+
+
+
+
+ {% trans 'Something went wrong! Please try again later.' %}
+
+ {% if debug %}
+
{{ tb }}
+ {% endif %}
+
diff --git a/passbook/flows/views.py b/passbook/flows/views.py
index bbe010392..0e6d0fef5 100644
--- a/passbook/flows/views.py
+++ b/passbook/flows/views.py
@@ -1,4 +1,5 @@
"""passbook multi-stage authentication engine"""
+from traceback import format_tb
from typing import Any, Dict, Optional
from django.http import (
@@ -8,7 +9,7 @@ from django.http import (
HttpResponseRedirect,
JsonResponse,
)
-from django.shortcuts import get_object_or_404, redirect, reverse
+from django.shortcuts import get_object_or_404, redirect, render, reverse
from django.template.response import TemplateResponse
from django.utils.decorators import method_decorator
from django.views.decorators.clickjacking import xframe_options_sameorigin
@@ -106,8 +107,18 @@ class FlowExecutorView(View):
stage=self.current_stage,
flow_slug=self.flow.slug,
)
- stage_response = self.current_stage_view.get(request, *args, **kwargs)
- return to_stage_response(request, stage_response)
+ try:
+ stage_response = self.current_stage_view.get(request, *args, **kwargs)
+ return to_stage_response(request, stage_response)
+ except Exception as exc: # pylint: disable=broad-except
+ return to_stage_response(
+ request,
+ render(
+ request,
+ "flows/error.html",
+ {"error": exc, "tb": "".join(format_tb(exc.__traceback__)),},
+ ),
+ )
def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
"""pass post request to current stage"""
@@ -117,8 +128,18 @@ class FlowExecutorView(View):
stage=self.current_stage,
flow_slug=self.flow.slug,
)
- stage_response = self.current_stage_view.post(request, *args, **kwargs)
- return to_stage_response(request, stage_response)
+ try:
+ stage_response = self.current_stage_view.post(request, *args, **kwargs)
+ return to_stage_response(request, stage_response)
+ except Exception as exc: # pylint: disable=broad-except
+ return to_stage_response(
+ request,
+ render(
+ request,
+ "flows/error.html",
+ {"error": exc, "tb": "".join(format_tb(exc.__traceback__)),},
+ ),
+ )
def _initiate_plan(self) -> FlowPlan:
planner = FlowPlanner(self.flow)