Compare commits

...
This repository has been archived on 2024-05-31. You can view files and clone it, but cannot push or open issues or pull requests.

5 Commits

Author SHA1 Message Date
Jens Langhammer 8543e140ef stages/consent: fix error when requests with identical empty permissions
closes #3280

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-08-01 20:58:25 +02:00
Jens Langhammer e2cf578afd root: use updated schema
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-08-01 10:11:06 +02:00
Jens Langhammer 0ce9fd9b2e web: fix updates
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-08-01 09:59:57 +02:00
Jens Langhammer d17ad65435 web: bump api client to match
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-07-31 23:54:29 +02:00
Jens L 01529d3894 flows/stages/consent: fix for post requests (#3339)
add unique token to consent stage to ensure it is shown

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-07-31 23:53:06 +02:00
11 changed files with 950 additions and 61 deletions

View File

@ -1,5 +1,6 @@
"""authentik consent stage"""
from typing import Optional
from uuid import uuid4
from django.http import HttpRequest, HttpResponse
from django.utils.timezone import now
@ -20,7 +21,8 @@ from authentik.stages.consent.models import ConsentMode, ConsentStage, UserConse
PLAN_CONTEXT_CONSENT_TITLE = "consent_title"
PLAN_CONTEXT_CONSENT_HEADER = "consent_header"
PLAN_CONTEXT_CONSENT_PERMISSIONS = "consent_permissions"
PLAN_CONTEXT_CONSNET_EXTRA_PERMISSIONS = "consent_additional_permissions"
PLAN_CONTEXT_CONSENT_EXTRA_PERMISSIONS = "consent_additional_permissions"
SESSION_KEY_CONSENT_TOKEN = "authentik/stages/consent/token" # nosec
class ConsentChallenge(WithUserInfoChallenge):
@ -30,12 +32,14 @@ class ConsentChallenge(WithUserInfoChallenge):
permissions = PermissionSerializer(many=True)
additional_permissions = PermissionSerializer(many=True)
component = CharField(default="ak-stage-consent")
token = CharField(required=True)
class ConsentChallengeResponse(ChallengeResponse):
"""Consent challenge response, any valid response request is valid"""
component = CharField(default="ak-stage-consent")
token = CharField(required=True)
class ConsentStageView(ChallengeStageView):
@ -44,12 +48,15 @@ class ConsentStageView(ChallengeStageView):
response_class = ConsentChallengeResponse
def get_challenge(self) -> Challenge:
token = str(uuid4())
self.request.session[SESSION_KEY_CONSENT_TOKEN] = token
data = {
"type": ChallengeTypes.NATIVE.value,
"permissions": self.executor.plan.context.get(PLAN_CONTEXT_CONSENT_PERMISSIONS, []),
"additional_permissions": self.executor.plan.context.get(
PLAN_CONTEXT_CONSNET_EXTRA_PERMISSIONS, []
PLAN_CONTEXT_CONSENT_EXTRA_PERMISSIONS, []
),
"token": token,
}
if PLAN_CONTEXT_CONSENT_TITLE in self.executor.plan.context:
data["title"] = self.executor.plan.context[PLAN_CONTEXT_CONSENT_TITLE]
@ -85,14 +92,14 @@ class ConsentStageView(ChallengeStageView):
if consent:
perms = self.executor.plan.context.get(PLAN_CONTEXT_CONSENT_PERMISSIONS, [])
allowed_perms = set(consent.permissions.split(" "))
allowed_perms = set(consent.permissions.split(" ") if consent.permissions != "" else [])
requested_perms = set(x["id"] for x in perms)
if allowed_perms != requested_perms:
self.executor.plan.context[PLAN_CONTEXT_CONSENT_PERMISSIONS] = [
x for x in perms if x["id"] in allowed_perms
]
self.executor.plan.context[PLAN_CONTEXT_CONSNET_EXTRA_PERMISSIONS] = [
self.executor.plan.context[PLAN_CONTEXT_CONSENT_EXTRA_PERMISSIONS] = [
x for x in perms if x["id"] in requested_perms.difference(allowed_perms)
]
return super().get(request, *args, **kwargs)
@ -102,13 +109,15 @@ class ConsentStageView(ChallengeStageView):
return super().get(request, *args, **kwargs)
def challenge_valid(self, response: ChallengeResponse) -> HttpResponse:
if response.data["token"] != self.request.session[SESSION_KEY_CONSENT_TOKEN]:
return self.get(self.request)
current_stage: ConsentStage = self.executor.current_stage
if PLAN_CONTEXT_APPLICATION not in self.executor.plan.context:
return self.executor.stage_ok()
application = self.executor.plan.context[PLAN_CONTEXT_APPLICATION]
permissions = self.executor.plan.context.get(
PLAN_CONTEXT_CONSENT_PERMISSIONS, []
) + self.executor.plan.context.get(PLAN_CONTEXT_CONSNET_EXTRA_PERMISSIONS, [])
) + self.executor.plan.context.get(PLAN_CONTEXT_CONSENT_EXTRA_PERMISSIONS, [])
permissions_string = " ".join(x["id"] for x in permissions)
# Make this StageView work when injected, in which case `current_stage` is an instance
# of the base class, and we don't save any consent, as it is assumed to be a one-time
@ -116,18 +125,14 @@ class ConsentStageView(ChallengeStageView):
if not isinstance(current_stage, ConsentStage):
return self.executor.stage_ok()
# Since we only get here when no consent exists, we can create it without update
consent = UserConsent(
user=self.request.user,
application=application,
permissions=permissions_string,
)
if current_stage.mode == ConsentMode.PERMANENT:
UserConsent.objects.create(
user=self.request.user,
application=application,
expiring=False,
permissions=permissions_string,
)
consent.expiring = False
if current_stage.mode == ConsentMode.EXPIRING:
UserConsent.objects.create(
user=self.request.user,
application=application,
expires=now() + timedelta_from_string(current_stage.consent_expire_in),
permissions=permissions_string,
)
consent.expires = now() + timedelta_from_string(current_stage.consent_expire_in)
consent.save()
return self.executor.stage_ok()

View File

@ -1,5 +1,6 @@
"""consent tests"""
from time import sleep
from uuid import uuid4
from django.urls import reverse
@ -14,7 +15,10 @@ from authentik.flows.tests import FlowTestCase
from authentik.flows.views.executor import SESSION_KEY_PLAN
from authentik.lib.generators import generate_id
from authentik.stages.consent.models import ConsentMode, ConsentStage, UserConsent
from authentik.stages.consent.stage import PLAN_CONTEXT_CONSENT_PERMISSIONS
from authentik.stages.consent.stage import (
PLAN_CONTEXT_CONSENT_PERMISSIONS,
SESSION_KEY_CONSENT_TOKEN,
)
class TestConsentStage(FlowTestCase):
@ -37,10 +41,13 @@ class TestConsentStage(FlowTestCase):
plan = FlowPlan(flow_pk=flow.pk.hex, bindings=[binding], markers=[StageMarker()])
session = self.client.session
session[SESSION_KEY_PLAN] = plan
session[SESSION_KEY_CONSENT_TOKEN] = str(uuid4())
session.save()
response = self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
{},
{
"token": session[SESSION_KEY_CONSENT_TOKEN],
},
)
# pylint: disable=no-member
self.assertEqual(response.status_code, 200)
@ -62,10 +69,13 @@ class TestConsentStage(FlowTestCase):
)
session = self.client.session
session[SESSION_KEY_PLAN] = plan
session[SESSION_KEY_CONSENT_TOKEN] = str(uuid4())
session.save()
response = self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
{},
{
"token": session[SESSION_KEY_CONSENT_TOKEN],
},
)
self.assertEqual(response.status_code, 200)
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
@ -96,7 +106,7 @@ class TestConsentStage(FlowTestCase):
{},
)
self.assertEqual(response.status_code, 200)
self.assertStageResponse(
raw_res = self.assertStageResponse(
response,
flow,
self.user,
@ -105,7 +115,9 @@ class TestConsentStage(FlowTestCase):
)
response = self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
{},
{
"token": raw_res["token"],
},
)
self.assertEqual(response.status_code, 200)
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
@ -144,7 +156,7 @@ class TestConsentStage(FlowTestCase):
{},
)
self.assertEqual(response.status_code, 200)
self.assertStageResponse(
raw_res = self.assertStageResponse(
response,
flow,
self.user,
@ -155,7 +167,9 @@ class TestConsentStage(FlowTestCase):
)
response = self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
{},
{
"token": raw_res["token"],
},
)
self.assertEqual(response.status_code, 200)
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
@ -187,7 +201,7 @@ class TestConsentStage(FlowTestCase):
{},
)
self.assertEqual(response.status_code, 200)
self.assertStageResponse(
raw_res = self.assertStageResponse(
response,
flow,
self.user,
@ -200,7 +214,9 @@ class TestConsentStage(FlowTestCase):
)
response = self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
{},
{
"token": raw_res["token"],
},
)
self.assertEqual(response.status_code, 200)
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
@ -209,3 +225,70 @@ class TestConsentStage(FlowTestCase):
user=self.user, application=self.application, permissions="foo bar"
).exists()
)
def test_permanent_same(self):
"""Test permanent consent from user"""
self.client.force_login(self.user)
flow = create_test_flow(FlowDesignation.AUTHENTICATION)
stage = ConsentStage.objects.create(name=generate_id(), mode=ConsentMode.PERMANENT)
binding = FlowStageBinding.objects.create(target=flow, stage=stage, order=2)
plan = FlowPlan(
flow_pk=flow.pk.hex,
bindings=[binding],
markers=[StageMarker()],
context={
PLAN_CONTEXT_APPLICATION: self.application,
},
)
session = self.client.session
session[SESSION_KEY_PLAN] = plan
session.save()
# First, consent with a single permission
response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
{},
)
self.assertEqual(response.status_code, 200)
raw_res = self.assertStageResponse(
response,
flow,
self.user,
permissions=[],
additional_permissions=[],
)
response = self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
{
"token": raw_res["token"],
},
)
self.assertEqual(response.status_code, 200)
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
self.assertTrue(
UserConsent.objects.filter(
user=self.user, application=self.application, permissions=""
).exists()
)
# Request again with the same perms
plan = FlowPlan(
flow_pk=flow.pk.hex,
bindings=[binding],
markers=[StageMarker()],
context={
PLAN_CONTEXT_APPLICATION: self.application,
PLAN_CONTEXT_CONSENT_PERMISSIONS: [],
},
)
session = self.client.session
session[SESSION_KEY_PLAN] = plan
session.save()
response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
{},
)
self.assertEqual(response.status_code, 200)
self.assertStageResponse(response, component="xak-flow-redirect")

View File

@ -179,6 +179,32 @@ paths:
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
/authenticators/admin/all/:
get:
operationId: authenticators_admin_all_list
description: Get all devices for current user
parameters:
- in: query
name: user
schema:
type: integer
tags:
- authenticators
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Device'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
/authenticators/admin/duo/:
get:
operationId: authenticators_admin_duo_list
@ -407,6 +433,30 @@ paths:
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
post:
operationId: authenticators_admin_sms_create
description: Viewset for sms authenticator devices (for admins)
tags:
- authenticators
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/SMSDeviceRequest'
required: true
security:
- authentik: []
responses:
'201':
content:
application/json:
schema:
$ref: '#/components/schemas/SMSDevice'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
/authenticators/admin/sms/{id}/:
get:
operationId: authenticators_admin_sms_retrieve
@ -433,6 +483,88 @@ paths:
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
put:
operationId: authenticators_admin_sms_update
description: Viewset for sms authenticator devices (for admins)
parameters:
- in: path
name: id
schema:
type: integer
description: A unique integer value identifying this SMS Device.
required: true
tags:
- authenticators
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/SMSDeviceRequest'
required: true
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/SMSDevice'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
patch:
operationId: authenticators_admin_sms_partial_update
description: Viewset for sms authenticator devices (for admins)
parameters:
- in: path
name: id
schema:
type: integer
description: A unique integer value identifying this SMS Device.
required: true
tags:
- authenticators
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/PatchedSMSDeviceRequest'
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/SMSDevice'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
delete:
operationId: authenticators_admin_sms_destroy
description: Viewset for sms authenticator devices (for admins)
parameters:
- in: path
name: id
schema:
type: integer
description: A unique integer value identifying this SMS Device.
required: true
tags:
- authenticators
security:
- authentik: []
responses:
'204':
description: No response body
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
/authenticators/admin/static/:
get:
operationId: authenticators_admin_static_list
@ -481,6 +613,30 @@ paths:
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
post:
operationId: authenticators_admin_static_create
description: Viewset for static authenticator devices (for admins)
tags:
- authenticators
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/StaticDeviceRequest'
required: true
security:
- authentik: []
responses:
'201':
content:
application/json:
schema:
$ref: '#/components/schemas/StaticDevice'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
/authenticators/admin/static/{id}/:
get:
operationId: authenticators_admin_static_retrieve
@ -507,6 +663,88 @@ paths:
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
put:
operationId: authenticators_admin_static_update
description: Viewset for static authenticator devices (for admins)
parameters:
- in: path
name: id
schema:
type: integer
description: A unique integer value identifying this static device.
required: true
tags:
- authenticators
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/StaticDeviceRequest'
required: true
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/StaticDevice'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
patch:
operationId: authenticators_admin_static_partial_update
description: Viewset for static authenticator devices (for admins)
parameters:
- in: path
name: id
schema:
type: integer
description: A unique integer value identifying this static device.
required: true
tags:
- authenticators
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/PatchedStaticDeviceRequest'
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/StaticDevice'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
delete:
operationId: authenticators_admin_static_destroy
description: Viewset for static authenticator devices (for admins)
parameters:
- in: path
name: id
schema:
type: integer
description: A unique integer value identifying this static device.
required: true
tags:
- authenticators
security:
- authentik: []
responses:
'204':
description: No response body
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
/authenticators/admin/totp/:
get:
operationId: authenticators_admin_totp_list
@ -555,6 +793,30 @@ paths:
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
post:
operationId: authenticators_admin_totp_create
description: Viewset for totp authenticator devices (for admins)
tags:
- authenticators
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/TOTPDeviceRequest'
required: true
security:
- authentik: []
responses:
'201':
content:
application/json:
schema:
$ref: '#/components/schemas/TOTPDevice'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
/authenticators/admin/totp/{id}/:
get:
operationId: authenticators_admin_totp_retrieve
@ -581,6 +843,88 @@ paths:
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
put:
operationId: authenticators_admin_totp_update
description: Viewset for totp authenticator devices (for admins)
parameters:
- in: path
name: id
schema:
type: integer
description: A unique integer value identifying this TOTP device.
required: true
tags:
- authenticators
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/TOTPDeviceRequest'
required: true
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/TOTPDevice'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
patch:
operationId: authenticators_admin_totp_partial_update
description: Viewset for totp authenticator devices (for admins)
parameters:
- in: path
name: id
schema:
type: integer
description: A unique integer value identifying this TOTP device.
required: true
tags:
- authenticators
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/PatchedTOTPDeviceRequest'
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/TOTPDevice'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
delete:
operationId: authenticators_admin_totp_destroy
description: Viewset for totp authenticator devices (for admins)
parameters:
- in: path
name: id
schema:
type: integer
description: A unique integer value identifying this TOTP device.
required: true
tags:
- authenticators
security:
- authentik: []
responses:
'204':
description: No response body
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
/authenticators/admin/webauthn/:
get:
operationId: authenticators_admin_webauthn_list
@ -629,6 +973,30 @@ paths:
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
post:
operationId: authenticators_admin_webauthn_create
description: Viewset for WebAuthn authenticator devices (for admins)
tags:
- authenticators
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/WebAuthnDeviceRequest'
required: true
security:
- authentik: []
responses:
'201':
content:
application/json:
schema:
$ref: '#/components/schemas/WebAuthnDevice'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
/authenticators/admin/webauthn/{id}/:
get:
operationId: authenticators_admin_webauthn_retrieve
@ -655,6 +1023,88 @@ paths:
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
put:
operationId: authenticators_admin_webauthn_update
description: Viewset for WebAuthn authenticator devices (for admins)
parameters:
- in: path
name: id
schema:
type: integer
description: A unique integer value identifying this WebAuthn Device.
required: true
tags:
- authenticators
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/WebAuthnDeviceRequest'
required: true
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/WebAuthnDevice'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
patch:
operationId: authenticators_admin_webauthn_partial_update
description: Viewset for WebAuthn authenticator devices (for admins)
parameters:
- in: path
name: id
schema:
type: integer
description: A unique integer value identifying this WebAuthn Device.
required: true
tags:
- authenticators
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/PatchedWebAuthnDeviceRequest'
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/WebAuthnDevice'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
delete:
operationId: authenticators_admin_webauthn_destroy
description: Viewset for WebAuthn authenticator devices (for admins)
parameters:
- in: path
name: id
schema:
type: integer
description: A unique integer value identifying this WebAuthn Device.
required: true
tags:
- authenticators
security:
- authentik: []
responses:
'204':
description: No response body
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
/authenticators/all/:
get:
operationId: authenticators_all_list
@ -1601,6 +2051,22 @@ paths:
operationId: core_applications_list
description: Custom list method that checks Policy based access instead of guardian
parameters:
- in: query
name: group
schema:
type: string
- in: query
name: meta_description
schema:
type: string
- in: query
name: meta_launch_url
schema:
type: string
- in: query
name: meta_publisher
schema:
type: string
- in: query
name: name
schema:
@ -5403,7 +5869,7 @@ paths:
/flows/instances/{slug}/export/:
get:
operationId: flows_instances_export_retrieve
description: Export flow to .akflow file
description: Export flow to .yaml file
parameters:
- in: path
name: slug
@ -5547,7 +6013,7 @@ paths:
/flows/instances/import_flow/:
post:
operationId: flows_instances_import_flow_create
description: Import flow from .akflow file
description: Import flow from .yaml file
tags:
- flows
requestBody:
@ -5564,6 +6030,215 @@ paths:
description: Bad request
'403':
$ref: '#/components/schemas/GenericError'
/managed/blueprints/:
get:
operationId: managed_blueprints_list
description: Blueprint instances
parameters:
- in: query
name: name
schema:
type: string
- name: ordering
required: false
in: query
description: Which field to use when ordering the results.
schema:
type: string
- name: page
required: false
in: query
description: A page number within the paginated result set.
schema:
type: integer
- name: page_size
required: false
in: query
description: Number of results to return per page.
schema:
type: integer
- in: query
name: path
schema:
type: string
- name: search
required: false
in: query
description: A search term.
schema:
type: string
tags:
- managed
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/PaginatedBlueprintInstanceList'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
post:
operationId: managed_blueprints_create
description: Blueprint instances
tags:
- managed
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/BlueprintInstanceRequest'
required: true
security:
- authentik: []
responses:
'201':
content:
application/json:
schema:
$ref: '#/components/schemas/BlueprintInstance'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
/managed/blueprints/{instance_uuid}/:
get:
operationId: managed_blueprints_retrieve
description: Blueprint instances
parameters:
- in: path
name: instance_uuid
schema:
type: string
format: uuid
description: A UUID string identifying this Blueprint Instance.
required: true
tags:
- managed
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/BlueprintInstance'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
put:
operationId: managed_blueprints_update
description: Blueprint instances
parameters:
- in: path
name: instance_uuid
schema:
type: string
format: uuid
description: A UUID string identifying this Blueprint Instance.
required: true
tags:
- managed
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/BlueprintInstanceRequest'
required: true
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/BlueprintInstance'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
patch:
operationId: managed_blueprints_partial_update
description: Blueprint instances
parameters:
- in: path
name: instance_uuid
schema:
type: string
format: uuid
description: A UUID string identifying this Blueprint Instance.
required: true
tags:
- managed
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/PatchedBlueprintInstanceRequest'
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/BlueprintInstance'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
delete:
operationId: managed_blueprints_destroy
description: Blueprint instances
parameters:
- in: path
name: instance_uuid
schema:
type: string
format: uuid
description: A UUID string identifying this Blueprint Instance.
required: true
tags:
- managed
security:
- authentik: []
responses:
'204':
description: No response body
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
/managed/blueprints/available/:
get:
operationId: managed_blueprints_available_list
description: Get blueprints
tags:
- managed
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
type: array
items:
type: string
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
'403':
$ref: '#/components/schemas/GenericError'
/oauth2/authorization_codes/:
get:
operationId: oauth2_authorization_codes_list
@ -7679,12 +8354,12 @@ paths:
enum:
- authentik.admin
- authentik.api
- authentik.blueprints
- authentik.core
- authentik.crypto
- authentik.events
- authentik.flows
- authentik.lib
- authentik.managed
- authentik.outposts
- authentik.policies
- authentik.policies.dummy
@ -19141,7 +19816,7 @@ components:
- authentik.stages.user_logout
- authentik.stages.user_write
- authentik.tenants
- authentik.managed
- authentik.blueprints
- authentik.core
type: string
AppleChallengeResponseRequest:
@ -20187,6 +20862,60 @@ components:
- POST
- POST_AUTO
type: string
BlueprintInstance:
type: object
description: Info about a single blueprint instance file
properties:
name:
type: string
path:
type: string
context:
type: object
additionalProperties: {}
last_applied:
type: string
format: date-time
readOnly: true
status:
$ref: '#/components/schemas/BlueprintInstanceStatusEnum'
enabled:
type: boolean
required:
- context
- last_applied
- name
- path
- status
BlueprintInstanceRequest:
type: object
description: Info about a single blueprint instance file
properties:
name:
type: string
minLength: 1
path:
type: string
minLength: 1
context:
type: object
additionalProperties: {}
status:
$ref: '#/components/schemas/BlueprintInstanceStatusEnum'
enabled:
type: boolean
required:
- context
- name
- path
- status
BlueprintInstanceStatusEnum:
enum:
- successful
- warning
- error
- unknown
type: string
Cache:
type: object
description: Generic cache stats for an object
@ -20523,11 +21252,14 @@ components:
type: array
items:
$ref: '#/components/schemas/Permission'
token:
type: string
required:
- additional_permissions
- pending_user
- pending_user_avatar
- permissions
- token
- type
ConsentChallengeResponseRequest:
type: object
@ -20537,6 +21269,11 @@ components:
type: string
minLength: 1
default: ak-stage-consent
token:
type: string
minLength: 1
required:
- token
ConsentStage:
type: object
description: ConsentStage Serializer
@ -20742,7 +21479,10 @@ components:
type:
type: string
readOnly: true
confirmed:
type: boolean
required:
- confirmed
- meta_model_name
- name
- pk
@ -24096,6 +24836,41 @@ components:
required:
- pagination
- results
PaginatedBlueprintInstanceList:
type: object
properties:
pagination:
type: object
properties:
next:
type: number
previous:
type: number
count:
type: number
current:
type: number
total_pages:
type: number
start_index:
type: number
end_index:
type: number
required:
- next
- previous
- count
- current
- total_pages
- start_index
- end_index
results:
type: array
items:
$ref: '#/components/schemas/BlueprintInstance'
required:
- pagination
- results
PaginatedCaptchaStageList:
type: object
properties:
@ -27003,6 +27778,23 @@ components:
minLength: 1
description: If any of the user's device has been used within this threshold,
this stage will be skipped
PatchedBlueprintInstanceRequest:
type: object
description: Info about a single blueprint instance file
properties:
name:
type: string
minLength: 1
path:
type: string
minLength: 1
context:
type: object
additionalProperties: {}
status:
$ref: '#/components/schemas/BlueprintInstanceStatusEnum'
enabled:
type: boolean
PatchedCaptchaStageRequest:
type: object
description: CaptchaStage Serializer
@ -30822,13 +31614,6 @@ components:
maxLength: 16
required:
- token
StatusEnum:
enum:
- SUCCESSFUL
- WARNING
- ERROR
- UNKNOWN
type: string
SubModeEnum:
enum:
- hashed_user_id
@ -30937,7 +31722,7 @@ components:
type: string
format: date-time
status:
$ref: '#/components/schemas/StatusEnum'
$ref: '#/components/schemas/TaskStatusEnum'
messages:
type: array
items: {}
@ -30947,6 +31732,13 @@ components:
- task_description
- task_finish_timestamp
- task_name
TaskStatusEnum:
enum:
- SUCCESSFUL
- WARNING
- ERROR
- UNKNOWN
type: string
Tenant:
type: object
description: Tenant Serializer

14
web/package-lock.json generated
View File

@ -21,7 +21,7 @@
"@codemirror/legacy-modes": "^6.1.0",
"@formatjs/intl-listformat": "^7.0.3",
"@fortawesome/fontawesome-free": "^6.1.1",
"@goauthentik/api": "^2022.7.2-1657137907",
"@goauthentik/api": "^2022.7.3-1659304078",
"@jackfranklin/rollup-plugin-markdown": "^0.4.0",
"@lingui/cli": "^3.14.0",
"@lingui/core": "^3.14.0",
@ -1922,9 +1922,9 @@
}
},
"node_modules/@goauthentik/api": {
"version": "2022.7.2-1657137907",
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2022.7.2-1657137907.tgz",
"integrity": "sha512-dNyoxWOhLpBMFY3u5v0DiWnqzbEApru87Dkl09i9tgAq5TEjENZbE5xsZO8IpjoZJ8rb8uYqFXyBgVLK9mBvgw=="
"version": "2022.7.3-1659304078",
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2022.7.3-1659304078.tgz",
"integrity": "sha512-dWvWB7mlhEZtbFJPVsUCK8Pyxb2lTJhOFmG6oAPVFI0k+TN9HGjY+0FSD+wto+Y9fWq6rPo4lZLaPVJluaA0WA=="
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.9.2",
@ -10384,9 +10384,9 @@
"integrity": "sha512-J/3yg2AIXc9wznaVqpHVX3Wa5jwKovVF0AMYSnbmcXTiL3PpRPfF58pzWucCwEiCJBp+hCNRLWClTomD8SseKg=="
},
"@goauthentik/api": {
"version": "2022.7.2-1657137907",
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2022.7.2-1657137907.tgz",
"integrity": "sha512-dNyoxWOhLpBMFY3u5v0DiWnqzbEApru87Dkl09i9tgAq5TEjENZbE5xsZO8IpjoZJ8rb8uYqFXyBgVLK9mBvgw=="
"version": "2022.7.3-1659304078",
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2022.7.3-1659304078.tgz",
"integrity": "sha512-dWvWB7mlhEZtbFJPVsUCK8Pyxb2lTJhOFmG6oAPVFI0k+TN9HGjY+0FSD+wto+Y9fWq6rPo4lZLaPVJluaA0WA=="
},
"@humanwhocodes/config-array": {
"version": "0.9.2",

View File

@ -64,7 +64,7 @@
"@codemirror/legacy-modes": "^6.1.0",
"@formatjs/intl-listformat": "^7.0.3",
"@fortawesome/fontawesome-free": "^6.1.1",
"@goauthentik/api": "^2022.7.2-1657137907",
"@goauthentik/api": "^2022.7.3-1659304078",
"@jackfranklin/rollup-plugin-markdown": "^0.4.0",
"@lingui/cli": "^3.14.0",
"@lingui/core": "^3.14.0",

View File

@ -32,6 +32,7 @@ export class UserConsentList extends Table<UserConsent> {
return [
new TableColumn(t`Application`, "application"),
new TableColumn(t`Expires`, "expires"),
new TableColumn(t`Permissions`, "permissions"),
];
}
@ -58,6 +59,10 @@ export class UserConsentList extends Table<UserConsent> {
}
row(item: UserConsent): TemplateResult[] {
return [html`${item.application.name}`, html`${item.expires?.toLocaleString()}`];
return [
html`${item.application.name}`,
html`${item.expires?.toLocaleString()}`,
html`${item.permissions || "-"}`,
];
}
}

View File

@ -23,17 +23,19 @@ export function readFileAsync(file: Blob) {
});
}
export type KeyUnknown = {
[key: string]: unknown;
};
export class BaseStage<Tin, Tout> extends LitElement {
host!: StageHost;
@property({ attribute: false })
challenge!: Tin;
async submitForm(e: Event): Promise<boolean> {
async submitForm(e: Event, defaults?: KeyUnknown): Promise<boolean> {
e.preventDefault();
const object: {
[key: string]: unknown;
} = {};
const object: KeyUnknown = defaults || {};
const form = new FormData(this.shadowRoot?.querySelector("form") || undefined);
for await (const [key, value] of form.entries()) {

View File

@ -107,7 +107,9 @@ export class ConsentStage extends BaseStage<ConsentChallenge, ConsentChallengeRe
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
this.submitForm(e, {
token: this.challenge.token,
});
}}
>
<ak-form-static

View File

@ -7,7 +7,7 @@ import { t } from "@lingui/macro";
import { customElement } from "lit/decorators.js";
import { SourcesApi, StatusEnum } from "@goauthentik/api";
import { SourcesApi, TaskStatusEnum } from "@goauthentik/api";
interface LDAPSyncStats {
healthy: number;
@ -50,7 +50,7 @@ export class LDAPSyncStatusChart extends AKChart<LDAPSyncStats> {
});
health.forEach((task) => {
if (task.status !== StatusEnum.Successful) {
if (task.status !== TaskStatusEnum.Successful) {
sourceKey = "failed";
}
const now = new Date().getTime();

View File

@ -24,7 +24,7 @@ import PFPage from "@patternfly/patternfly/components/Page/page.css";
import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import { LDAPSource, SourcesApi, StatusEnum } from "@goauthentik/api";
import { LDAPSource, SourcesApi, TaskStatusEnum } from "@goauthentik/api";
@customElement("ak-source-ldap-view")
export class LDAPSourceViewPage extends LitElement {
@ -145,9 +145,9 @@ export class LDAPSourceViewPage extends LitElement {
return html`<ul class="pf-c-list">
${tasks.map((task) => {
let header = "";
if (task.status === StatusEnum.Warning) {
if (task.status === TaskStatusEnum.Warning) {
header = t`Task finished with warnings`;
} else if (task.status === StatusEnum.Error) {
} else if (task.status === TaskStatusEnum.Error) {
header = t`Task finished with errors`;
} else {
header = t`Last sync: ${task.taskFinishTimestamp.toLocaleString()}`;

View File

@ -14,7 +14,7 @@ import { customElement, property } from "lit/decorators.js";
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
import { AdminApi, StatusEnum, Task } from "@goauthentik/api";
import { AdminApi, Task, TaskStatusEnum } from "@goauthentik/api";
@customElement("ak-system-task-list")
export class SystemTaskListPage extends TablePage<Task> {
@ -67,11 +67,11 @@ export class SystemTaskListPage extends TablePage<Task> {
taskStatus(task: Task): TemplateResult {
switch (task.status) {
case StatusEnum.Successful:
case TaskStatusEnum.Successful:
return html`<ak-label color=${PFColor.Green}>${t`Successful`}</ak-label>`;
case StatusEnum.Warning:
case TaskStatusEnum.Warning:
return html`<ak-label color=${PFColor.Orange}>${t`Warning`}</ak-label>`;
case StatusEnum.Error:
case TaskStatusEnum.Error:
return html`<ak-label color=${PFColor.Red}>${t`Error`}</ak-label>`;
default:
return html`<ak-label color=${PFColor.Grey}>${t`Unknown`}</ak-label>`;