e2e: add OIDC Provider test against grafana, more formatting, minor bug fixes

This commit is contained in:
Jens Langhammer 2020-06-19 19:34:27 +02:00
parent 8c6a4a4968
commit 73e7158178
11 changed files with 512 additions and 14 deletions

300
e2e/passbook.side Normal file
View File

@ -0,0 +1,300 @@
{
"id": "7d9b2407-1520-4c04-b040-68e8ada9aecc",
"version": "2.0",
"name": "passbook",
"url": "http://localhost:8000",
"tests": [{
"id": "94b39863-74ec-4b7d-98c5-2b380b6d2c55",
"name": "passbook login simple",
"commands": [{
"id": "e60e4382-4f96-44c3-ba06-5e18609c9c2b",
"comment": "",
"command": "open",
"target": "/flows/default-authentication-flow/?next=%2F",
"targets": [],
"value": ""
}, {
"id": "b2652f24-931e-45b0-b01d-2f0ac0f74db8",
"comment": "",
"command": "click",
"target": "id=id_uid_field",
"targets": [
["id=id_uid_field", "id"],
["name=uid_field", "name"],
["css=#id_uid_field", "css:finder"],
["xpath=//input[@id='id_uid_field']", "xpath:attributes"],
["xpath=//main[@id='flow-body']/div/form/div/input", "xpath:idRelative"],
["xpath=//div/input", "xpath:position"]
],
"value": ""
}, {
"id": "f1930f8a-984a-4076-a925-20937bb2f8d3",
"comment": "",
"command": "type",
"target": "id=id_uid_field",
"targets": [
["id=id_uid_field", "id"],
["name=uid_field", "name"],
["css=#id_uid_field", "css:finder"],
["xpath=//input[@id='id_uid_field']", "xpath:attributes"],
["xpath=//main[@id='flow-body']/div/form/div/input", "xpath:idRelative"],
["xpath=//div/input", "xpath:position"]
],
"value": "admin@example.tld"
}, {
"id": "0b568ee3-1bed-4821-a3bc-f6b960dbed9d",
"comment": "",
"command": "sendKeys",
"target": "id=id_uid_field",
"targets": [
["id=id_uid_field", "id"],
["name=uid_field", "name"],
["css=#id_uid_field", "css:finder"],
["xpath=//input[@id='id_uid_field']", "xpath:attributes"],
["xpath=//main[@id='flow-body']/div/form/div/input", "xpath:idRelative"],
["xpath=//div/input", "xpath:position"]
],
"value": "${KEY_ENTER}"
}, {
"id": "6d98e479-2825-484d-996a-ccf350d2761f",
"comment": "",
"command": "type",
"target": "id=id_password",
"targets": [
["id=id_password", "id"],
["name=password", "name"],
["css=#id_password", "css:finder"],
["xpath=//input[@id='id_password']", "xpath:attributes"],
["xpath=//main[@id='flow-body']/div/form/div[2]/input", "xpath:idRelative"],
["xpath=//div[2]/input", "xpath:position"]
],
"value": "pbadmin"
}, {
"id": "6f7abec6-ff44-4eb5-ae23-520c1c29a706",
"comment": "",
"command": "sendKeys",
"target": "id=id_password",
"targets": [
["id=id_password", "id"],
["name=password", "name"],
["css=#id_password", "css:finder"],
["xpath=//input[@id='id_password']", "xpath:attributes"],
["xpath=//main[@id='flow-body']/div/form/div[2]/input", "xpath:idRelative"],
["xpath=//div[2]/input", "xpath:position"]
],
"value": "${KEY_ENTER}"
}, {
"id": "04c5876f-1405-4077-a98b-e911f09113d7",
"comment": "",
"command": "assertText",
"target": "xpath=//a[contains(@href, '/-/user/')]",
"targets": [
["linkText=pbadmin", "linkText"],
["css=.pf-c-page__header-tools-group:nth-child(2) > .pf-c-button", "css:finder"],
["xpath=//a[contains(text(),'pbadmin')]", "xpath:link"],
["xpath=//div[@id='page-default-nav-example']/header/div[3]/div[2]/a", "xpath:idRelative"],
["xpath=//a[contains(@href, '/-/user/')]", "xpath:href"],
["xpath=//div[2]/a", "xpath:position"],
["xpath=//a[contains(.,'pbadmin')]", "xpath:innerText"]
],
"value": "pbadmin"
}]
}, {
"id": "61948b3c-3012-4f97-aa52-bc8f34fec333",
"name": "passbook enroll simple",
"commands": [{
"id": "0f4884b3-4891-41bc-956d-1fa433e892e9",
"comment": "",
"command": "open",
"target": "/flows/default-authentication-flow/?next=%2F",
"targets": [],
"value": ""
}, {
"id": "84d3861f-a60c-4650-8689-535f82b39577",
"comment": "",
"command": "click",
"target": "linkText=Sign up.",
"targets": [
["linkText=Sign up.", "linkText"],
["css=.pf-c-login__main-footer-band-item > a", "css:finder"],
["xpath=//a[contains(text(),'Sign up.')]", "xpath:link"],
["xpath=//main[@id='flow-body']/footer/div/p/a", "xpath:idRelative"],
["xpath=//a[contains(@href, '/flows/default-enrollment-flow/')]", "xpath:href"],
["xpath=//a", "xpath:position"],
["xpath=//a[contains(.,'Sign up.')]", "xpath:innerText"]
],
"value": ""
}, {
"id": "a32435ca-d84a-41e7-a915-fcbbc5f88341",
"comment": "",
"command": "type",
"target": "id=id_username",
"targets": [
["id=id_username", "id"],
["name=username", "name"],
["css=#id_username", "css:finder"],
["xpath=//input[@id='id_username']", "xpath:attributes"],
["xpath=//main[@id='flow-body']/div/form/div/input", "xpath:idRelative"],
["xpath=//div/input", "xpath:position"]
],
"value": "foo"
}, {
"id": "3b5dcf53-8297-46c5-88b7-11c2eb25f34f",
"comment": "",
"command": "type",
"target": "id=id_password",
"targets": [
["id=id_password", "id"],
["name=password", "name"],
["css=#id_password", "css:finder"],
["xpath=//input[@id='id_password']", "xpath:attributes"],
["xpath=//main[@id='flow-body']/div/form/div[2]/input", "xpath:idRelative"],
["xpath=//div[2]/input", "xpath:position"]
],
"value": "pbadmin"
}, {
"id": "e948d61c-dae6-4994-b56f-ff130892b342",
"comment": "",
"command": "type",
"target": "id=id_password_repeat",
"targets": [
["id=id_password_repeat", "id"],
["name=password_repeat", "name"],
["css=#id_password_repeat", "css:finder"],
["xpath=//input[@id='id_password_repeat']", "xpath:attributes"],
["xpath=//main[@id='flow-body']/div/form/div[3]/input", "xpath:idRelative"],
["xpath=//div[3]/input", "xpath:position"]
],
"value": "pbadmin"
}, {
"id": "e7527bfc-ec74-4d96-86f0-5a3a55a59025",
"comment": "",
"command": "click",
"target": "css=.pf-c-button",
"targets": [
["css=.pf-c-button", "css:finder"],
["xpath=//button[@type='submit']", "xpath:attributes"],
["xpath=//main[@id='flow-body']/div/form/div[4]/button", "xpath:idRelative"],
["xpath=//button", "xpath:position"],
["xpath=//button[contains(.,'Continue')]", "xpath:innerText"]
],
"value": ""
}, {
"id": "434b842c-a659-4ff5-aca8-06a6a3489597",
"comment": "",
"command": "type",
"target": "id=id_name",
"targets": [
["id=id_name", "id"],
["name=name", "name"],
["css=#id_name", "css:finder"],
["xpath=//input[@id='id_name']", "xpath:attributes"],
["xpath=//main[@id='flow-body']/div/form/div/input", "xpath:idRelative"],
["xpath=//div/input", "xpath:position"]
],
"value": "some name"
}, {
"id": "cbc43a1b-2cfe-46e2-85bc-476fb32c6cb1",
"comment": "",
"command": "type",
"target": "id=id_email",
"targets": [
["id=id_email", "id"],
["name=email", "name"],
["css=#id_email", "css:finder"],
["xpath=//input[@id='id_email']", "xpath:attributes"],
["xpath=//main[@id='flow-body']/div/form/div[2]/input", "xpath:idRelative"],
["xpath=//div[2]/input", "xpath:position"]
],
"value": "foo@bar.baz"
}, {
"id": "e74389a0-228b-4312-9677-e9add6358de3",
"comment": "",
"command": "click",
"target": "css=.pf-c-button",
"targets": [
["css=.pf-c-button", "css:finder"],
["xpath=//button[@type='submit']", "xpath:attributes"],
["xpath=//main[@id='flow-body']/div/form/div[3]/button", "xpath:idRelative"],
["xpath=//button", "xpath:position"],
["xpath=//button[contains(.,'Continue')]", "xpath:innerText"]
],
"value": ""
}, {
"id": "3e22f9c2-5ebd-49c2-81b1-340fa0435bbc",
"comment": "",
"command": "click",
"target": "linkText=foo",
"targets": [
["linkText=foo", "linkText"],
["css=.pf-c-page__header-tools-group:nth-child(2) > .pf-c-button", "css:finder"],
["xpath=//a[contains(text(),'foo')]", "xpath:link"],
["xpath=//div[@id='page-default-nav-example']/header/div[3]/div[2]/a", "xpath:idRelative"],
["xpath=//a[contains(@href, '/-/user/')]", "xpath:href"],
["xpath=//div[2]/a", "xpath:position"],
["xpath=//a[contains(.,'foo')]", "xpath:innerText"]
],
"value": ""
}, {
"id": "60124cfd-f11c-4d7f-8b01-bef54c8cbd73",
"comment": "",
"command": "assertText",
"target": "xpath=//a[contains(@href, '/-/user/')]",
"targets": [
["linkText=foo", "linkText"],
["css=.pf-c-page__header-tools-group:nth-child(2) > .pf-c-button", "css:finder"],
["xpath=//a[contains(text(),'foo')]", "xpath:link"],
["xpath=//div[@id='page-default-nav-example']/header/div[3]/div[2]/a", "xpath:idRelative"],
["xpath=//a[contains(@href, '/-/user/')]", "xpath:href"],
["xpath=//div[2]/a", "xpath:position"],
["xpath=//a[contains(.,'foo')]", "xpath:innerText"]
],
"value": "foo"
}, {
"id": "429ee61b-9991-4919-8131-55f8e1bd9a0d",
"comment": "",
"command": "assertValue",
"target": "id=id_username",
"targets": [],
"value": "foo"
}, {
"id": "f6c50760-52ed-4c1d-b232-30f8afe144eb",
"comment": "",
"command": "assertText",
"target": "id=id_name",
"targets": [
["id=id_name", "id"],
["name=name", "name"],
["css=#id_name", "css:finder"],
["xpath=//input[@id='id_name']", "xpath:attributes"],
["xpath=//main[@id='main-content']/section/div/div/div/div[2]/form/div[2]/div/input", "xpath:idRelative"],
["xpath=//div[2]/div/input", "xpath:position"]
],
"value": "some name"
}, {
"id": "b26905b5-89b5-4b41-abf5-a9f848f08622",
"comment": "",
"command": "assertText",
"target": "id=id_email",
"targets": [
["id=id_email", "id"],
["name=email", "name"],
["css=#id_email", "css:finder"],
["xpath=//input[@id='id_email']", "xpath:attributes"],
["xpath=//main[@id='main-content']/section/div/div/div/div[2]/form/div[3]/div/input", "xpath:idRelative"],
["xpath=//div[3]/div/input", "xpath:position"]
],
"value": "foo@bar.baz"
}]
}],
"suites": [{
"id": "495657fb-3f5e-4431-877c-4d0b248c0841",
"name": "Default Suite",
"persistSession": false,
"parallel": false,
"timeout": 300,
"tests": ["94b39863-74ec-4b7d-98c5-2b380b6d2c55"]
}],
"urls": ["http://localhost:8000/"],
"plugins": []
}

View File

@ -4,14 +4,14 @@ from selenium import webdriver
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from e2e.utils import apply_default_data
from passbook.flows.models import Flow, FlowDesignation, FlowStageBinding from passbook.flows.models import Flow, FlowDesignation, FlowStageBinding
from passbook.policies.expression.models import ExpressionPolicy from passbook.policies.expression.models import ExpressionPolicy
from passbook.policies.models import PolicyBinding from passbook.policies.models import PolicyBinding
from passbook.stages.identification.models import IdentificationStage
from passbook.stages.prompt.models import FieldTypes, Prompt, PromptStage from passbook.stages.prompt.models import FieldTypes, Prompt, PromptStage
from passbook.stages.user_login.models import UserLoginStage from passbook.stages.user_login.models import UserLoginStage
from passbook.stages.user_write.models import UserWriteStage from passbook.stages.user_write.models import UserWriteStage
from passbook.stages.identification.models import IdentificationStage
from e2e.utils import apply_default_data
class TestEnroll2Step(StaticLiveServerTestCase): class TestEnroll2Step(StaticLiveServerTestCase):

View File

@ -4,6 +4,7 @@ from selenium import webdriver
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.keys import Keys
from e2e.utils import apply_default_data from e2e.utils import apply_default_data
@ -24,9 +25,7 @@ class TestLogin(StaticLiveServerTestCase):
def test_login(self): def test_login(self):
"""test default login flow""" """test default login flow"""
self.driver.get( self.driver.get(f"{self.live_server_url}/flows/default-authentication-flow/")
f"{self.live_server_url}/flows/default-authentication-flow/"
)
self.driver.find_element(By.ID, "id_uid_field").click() self.driver.find_element(By.ID, "id_uid_field").click()
self.driver.find_element(By.ID, "id_uid_field").send_keys("pbadmin") self.driver.find_element(By.ID, "id_uid_field").send_keys("pbadmin")
self.driver.find_element(By.ID, "id_uid_field").send_keys(Keys.ENTER) self.driver.find_element(By.ID, "id_uid_field").send_keys(Keys.ENTER)

176
e2e/test_provider_oidc.py Normal file
View File

@ -0,0 +1,176 @@
"""test OpenID Provider flow"""
from time import sleep
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from oauth2_provider.generators import generate_client_id, generate_client_secret
from oidc_provider.models import Client, ResponseType
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.webdriver.common.keys import Keys
from docker import DockerClient, from_env
from docker.models.containers import Container
from docker.types import Healthcheck
from e2e.utils import apply_default_data, ensure_rsa_key
from passbook.core.models import Application
from passbook.flows.models import Flow
from passbook.providers.oidc.models import OpenIDProvider
class TestProviderOIDC(StaticLiveServerTestCase):
"""test OpenID Provider flow"""
def setUp(self):
self.driver = webdriver.Remote(
command_executor="http://localhost:4444/wd/hub",
desired_capabilities=DesiredCapabilities.CHROME,
)
self.driver.implicitly_wait(30)
apply_default_data()
self.client_id = generate_client_id()
self.client_secret = generate_client_secret()
self.container = self.setup_client()
def setup_client(self) -> Container:
"""Setup client grafana container which we test OIDC against"""
client: DockerClient = from_env()
container = client.containers.run(
image="grafana/grafana:latest",
detach=True,
name="passbook-e2e-grafana-client",
network_mode="host",
auto_remove=True,
healthcheck=Healthcheck(
test=["CMD", "wget", "--spider", "http://localhost:3000"],
interval=5 * 100 * 1000000,
start_period=1 * 100 * 1000000,
),
environment={
"GF_AUTH_GENERIC_OAUTH_ENABLED": "true",
"GF_AUTH_GENERIC_OAUTH_CLIENT_ID": self.client_id,
"GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET": self.client_secret,
"GF_AUTH_GENERIC_OAUTH_SCOPES": "openid email profile",
"GF_AUTH_GENERIC_OAUTH_AUTH_URL": (
f"{self.live_server_url}/application/oidc/authorize"
),
"GF_AUTH_GENERIC_OAUTH_TOKEN_URL": (
f"{self.live_server_url}/application/oidc/token"
),
"GF_AUTH_GENERIC_OAUTH_API_URL": (
f"{self.live_server_url}/application/oidc/userinfo"
),
"GF_LOG_LEVEL": "debug",
},
)
while True:
container.reload()
status = container.attrs.get("State", {}).get("Health", {}).get("Status")
if status == "healthy":
return container
sleep(1)
def tearDown(self):
super().tearDown()
self.driver.quit()
self.container.kill()
def test_redirect_uri_error(self):
"""test OpenID Provider flow (invalid redirect URI, check error message)"""
# Bootstrap all needed objects
authorization_flow = Flow.objects.get(slug="default-provider-authorization")
client = Client.objects.create(
name="grafana",
client_type="confidential",
client_id=self.client_id,
client_secret=self.client_secret,
_redirect_uris="http://localhost:3000/",
_scope="openid userinfo",
)
# At least one of these objects must exist
ensure_rsa_key()
# This response_code object might exist or not, depending on the order the tests are run
rp_type, _ = ResponseType.objects.get_or_create(value="code")
client.response_types.set([rp_type])
client.save()
provider = OpenIDProvider.objects.create(
oidc_client=client, authorization_flow=authorization_flow,
)
Application.objects.create(
name="Grafana", slug="grafana", provider=provider,
)
self.driver.get(f"http://localhost:3000")
self.driver.find_element(By.CLASS_NAME, "btn-service--oauth").click()
self.driver.find_element(By.ID, "id_uid_field").click()
self.driver.find_element(By.ID, "id_uid_field").send_keys("pbadmin")
self.driver.find_element(By.ID, "id_uid_field").send_keys(Keys.ENTER)
self.driver.find_element(By.ID, "id_password").send_keys("pbadmin")
self.driver.find_element(By.ID, "id_password").send_keys(Keys.ENTER)
sleep(2)
self.assertEqual(
self.driver.find_element(By.CLASS_NAME, "pf-c-title").text,
"Redirect URI Error",
)
def test_authorization_no_consent(self):
"""test OpenID Provider flow (default authorization flow without consent)"""
# Bootstrap all needed objects
authorization_flow = Flow.objects.get(slug="default-provider-authorization")
client = Client.objects.create(
name="grafana",
client_type="confidential",
client_id=self.client_id,
client_secret=self.client_secret,
_redirect_uris="http://localhost:3000/login/generic_oauth",
_scope="openid profile email",
reuse_consent=False,
require_consent=False,
)
# At least one of these objects must exist
ensure_rsa_key()
# This response_code object might exist or not, depending on the order the tests are run
rp_type, _ = ResponseType.objects.get_or_create(value="code")
client.response_types.set([rp_type])
client.save()
provider = OpenIDProvider.objects.create(
oidc_client=client, authorization_flow=authorization_flow,
)
Application.objects.create(
name="Grafana", slug="grafana", provider=provider,
)
self.driver.get("http://localhost:3000")
self.driver.find_element(By.CLASS_NAME, "btn-service--oauth").click()
self.driver.find_element(By.ID, "id_uid_field").click()
self.driver.find_element(By.ID, "id_uid_field").send_keys("pbadmin")
self.driver.find_element(By.ID, "id_uid_field").send_keys(Keys.ENTER)
self.driver.find_element(By.ID, "id_password").send_keys("pbadmin")
self.driver.find_element(By.ID, "id_password").send_keys(Keys.ENTER)
self.driver.find_element(By.XPATH, "//a[contains(@href, '/profile')]").click()
# sleep()
self.assertEqual(
self.driver.find_element(By.CLASS_NAME, "page-header__title").text,
"passbook Default Admin",
)
self.assertEqual(
self.driver.find_element(
By.XPATH,
"/html/body/grafana-app/div/div/div/react-profile-wrapper/form[1]/div[1]/div/input",
).get_attribute("value"),
"passbook Default Admin",
)
self.assertEqual(
self.driver.find_element(
By.XPATH,
"/html/body/grafana-app/div/div/div/react-profile-wrapper/form[1]/div[2]/div/input",
).get_attribute("value"),
"root@localhost",
)
self.assertEqual(
self.driver.find_element(
By.XPATH,
"/html/body/grafana-app/div/div/div/react-profile-wrapper/form[1]/div[3]/div/input",
).get_attribute("value"),
"root@localhost",
)

View File

@ -1,8 +1,10 @@
"""passbook e2e testing utilities""" """passbook e2e testing utilities"""
from glob import glob from glob import glob
from importlib.util import module_from_spec, spec_from_file_location
from inspect import getmembers, isfunction from inspect import getmembers, isfunction
from importlib.util import spec_from_file_location, module_from_spec
from Cryptodome.PublicKey import RSA
from django.apps import apps from django.apps import apps
from django.db import connection, transaction from django.db import connection, transaction
from django.db.utils import IntegrityError from django.db.utils import IntegrityError
@ -15,7 +17,7 @@ def apply_default_data():
migration_files = glob("**/migrations/*.py", recursive=True) migration_files = glob("**/migrations/*.py", recursive=True)
matches = [] matches = []
for migration in migration_files: for migration in migration_files:
with open(migration, 'r+') as migration_file: with open(migration, "r+") as migration_file:
# Check if they have a `RunPython` # Check if they have a `RunPython`
if "RunPython" in migration_file.read(): if "RunPython" in migration_file.read():
matches.append(migration) matches.append(migration)
@ -34,3 +36,13 @@ def apply_default_data():
func(apps, schema_editor) func(apps, schema_editor)
except IntegrityError: except IntegrityError:
pass pass
def ensure_rsa_key():
"""Ensure that at least one RSAKey Object exists, create one if none exist"""
from oidc_provider.models import RSAKey
if not RSAKey.objects.exists():
key = RSA.generate(2048)
rsakey = RSAKey(key=key.exportKey("PEM").decode("utf8"))
rsakey.save()

View File

@ -2,6 +2,7 @@
"""Django manage.py""" """Django manage.py"""
import os import os
import sys import sys
from defusedxml import defuse_stdlib from defusedxml import defuse_stdlib
defuse_stdlib() defuse_stdlib()

View File

@ -39,6 +39,11 @@ class FlowPlan:
context: Dict[str, Any] = field(default_factory=dict) context: Dict[str, Any] = field(default_factory=dict)
markers: List[StageMarker] = field(default_factory=list) markers: List[StageMarker] = field(default_factory=list)
def append(self, stage: Stage, marker: Optional[StageMarker] = None):
"""Append `stage` to all stages, optionall with stage marker"""
self.stages.append(stage)
self.markers.append(marker or StageMarker())
def next(self) -> Optional[Stage]: def next(self) -> Optional[Stage]:
"""Return next pending stage from the bottom of the list""" """Return next pending stage from the bottom of the list"""
if not self.has_stages: if not self.has_stages:
@ -54,7 +59,6 @@ class FlowPlan:
self.markers.remove(marker) self.markers.remove(marker)
if not self.has_stages: if not self.has_stages:
return None return None
# pylint: disable=not-callable
return self.next() return self.next()
return marked_stage return marked_stage

View File

@ -27,6 +27,7 @@ LOGGER = get_logger()
# Argument used to redirect user after login # Argument used to redirect user after login
NEXT_ARG_NAME = "next" NEXT_ARG_NAME = "next"
SESSION_KEY_PLAN = "passbook_flows_plan" SESSION_KEY_PLAN = "passbook_flows_plan"
SESSION_KEY_NEXT = "passbook_flows_shell_next"
@method_decorator(xframe_options_sameorigin, name="dispatch") @method_decorator(xframe_options_sameorigin, name="dispatch")
@ -127,7 +128,10 @@ class FlowExecutorView(View):
def _flow_done(self) -> HttpResponse: def _flow_done(self) -> HttpResponse:
"""User Successfully passed all stages""" """User Successfully passed all stages"""
self.cancel() self.cancel()
next_param = self.request.GET.get(NEXT_ARG_NAME, "passbook_core:overview") # Since this is wrapped by the ExecutorShell, the next argument is saved in the session
next_param = self.request.session.get(
SESSION_KEY_NEXT, "passbook_core:overview"
)
return redirect_with_qs(next_param) return redirect_with_qs(next_param)
def stage_ok(self) -> HttpResponse: def stage_ok(self) -> HttpResponse:
@ -210,6 +214,8 @@ class FlowExecutorShellView(TemplateView):
def get_context_data(self, **kwargs) -> Dict[str, Any]: def get_context_data(self, **kwargs) -> Dict[str, Any]:
kwargs["exec_url"] = reverse("passbook_flows:flow-executor", kwargs=self.kwargs) kwargs["exec_url"] = reverse("passbook_flows:flow-executor", kwargs=self.kwargs)
kwargs["msg_url"] = reverse("passbook_api:messages-list") kwargs["msg_url"] = reverse("passbook_api:messages-list")
if NEXT_ARG_NAME in self.request.GET:
self.request.session[SESSION_KEY_NEXT] = self.request.GET[NEXT_ARG_NAME]
return kwargs return kwargs

View File

@ -10,5 +10,5 @@ def userinfo(claims: Dict[str, Any], user: User) -> Dict[str, Any]:
claims["given_name"] = user.name claims["given_name"] = user.name
claims["family_name"] = user.name claims["family_name"] = user.name
claims["email"] = user.email claims["email"] = user.email
claims["preferred_username"] = user.username
return claims return claims

View File

@ -61,7 +61,7 @@ class AuthorizationFlowInitView(AccessMixin, LoginRequiredMixin, View):
PLAN_CONTEXT_PARAMS: endpoint.params, PLAN_CONTEXT_PARAMS: endpoint.params,
}, },
) )
plan.stages.append(in_memory_stage(OIDCStage)) plan.append(in_memory_stage(OIDCStage))
self.request.session[SESSION_KEY_PLAN] = plan self.request.session[SESSION_KEY_PLAN] = plan
return redirect_with_qs( return redirect_with_qs(
"passbook_flows:flow-executor-shell", "passbook_flows:flow-executor-shell",

View File

@ -1,9 +1,9 @@
#!/bin/bash -xe #!/bin/bash -xe
isort -rc passbook isort -rc .
pyright pyright
black passbook black .
./manage.py generate_swagger -o swagger.yaml -f yaml ./manage.py generate_swagger -o swagger.yaml -f yaml
scripts/coverage.sh scripts/coverage.sh
bandit -r passbook bandit -r .
pylint passbook pylint passbook
prospector prospector