enterprise/providers/ssf: fixes v2 (#13003)
* enterprise/providers/ssf: check providers's application's policies to determine if an ssf event should be sent Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add preview banner to ssf provider Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix and test Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
@ -5,6 +5,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
from requests.exceptions import RequestException
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.core.models import User
|
||||
from authentik.enterprise.providers.ssf.models import (
|
||||
DeliveryMethods,
|
||||
EventTypes,
|
||||
@ -17,6 +18,7 @@ from authentik.events.models import TaskStatus
|
||||
from authentik.events.system_tasks import SystemTask
|
||||
from authentik.lib.utils.http import get_http_session
|
||||
from authentik.lib.utils.time import timedelta_from_string
|
||||
from authentik.policies.engine import PolicyEngine
|
||||
from authentik.root.celery import CELERY_APP
|
||||
|
||||
session = get_http_session()
|
||||
@ -43,10 +45,32 @@ def send_ssf_event(
|
||||
return _send_ssf_event.delay(payload)
|
||||
|
||||
|
||||
def _check_app_access(stream_uuid: str, event_data: dict) -> bool:
|
||||
"""Check if event is related to user and if so, check
|
||||
if the user has access to the application"""
|
||||
stream = Stream.objects.filter(pk=stream_uuid).first()
|
||||
if not stream:
|
||||
return False
|
||||
# `event_data` is a dict version of a StreamEvent
|
||||
sub_id = event_data.get("payload", {}).get("sub_id", {})
|
||||
email = sub_id.get("user", {}).get("email", None)
|
||||
if not email:
|
||||
return True
|
||||
user = User.objects.filter(email=email).first()
|
||||
if not user:
|
||||
return True
|
||||
engine = PolicyEngine(stream.provider.backchannel_application, user)
|
||||
engine.use_cache = False
|
||||
engine.build()
|
||||
return engine.passing
|
||||
|
||||
|
||||
@CELERY_APP.task()
|
||||
def _send_ssf_event(event_data: list[tuple[str, dict]]):
|
||||
tasks = []
|
||||
for stream, data in event_data:
|
||||
if not _check_app_access(stream, data):
|
||||
continue
|
||||
event = StreamEvent.objects.create(**data)
|
||||
tasks.extend(send_single_ssf_event(stream, str(event.uuid)))
|
||||
main_task = group(*tasks)
|
||||
|
@ -3,18 +3,20 @@ from uuid import uuid4
|
||||
from django.urls import reverse
|
||||
from rest_framework.test import APITestCase
|
||||
|
||||
from authentik.core.models import Application
|
||||
from authentik.core.models import Application, Group
|
||||
from authentik.core.tests.utils import (
|
||||
create_test_cert,
|
||||
create_test_user,
|
||||
)
|
||||
from authentik.enterprise.providers.ssf.models import (
|
||||
EventTypes,
|
||||
SSFEventStatus,
|
||||
SSFProvider,
|
||||
Stream,
|
||||
StreamEvent,
|
||||
)
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.policies.models import PolicyBinding
|
||||
from authentik.stages.authenticator_webauthn.models import WebAuthnDevice
|
||||
|
||||
|
||||
@ -147,3 +149,20 @@ class TestSignals(APITestCase):
|
||||
self.assertEqual(event.payload["sub_id"]["format"], "complex")
|
||||
self.assertEqual(event.payload["sub_id"]["user"]["format"], "email")
|
||||
self.assertEqual(event.payload["sub_id"]["user"]["email"], user.email)
|
||||
|
||||
def test_signal_policy_ignore(self):
|
||||
"""Test event not being created for user that doesn't have access to the application"""
|
||||
PolicyBinding.objects.create(
|
||||
target=self.application, group=Group.objects.create(name=generate_id()), order=0
|
||||
)
|
||||
user = create_test_user()
|
||||
self.client.force_login(user)
|
||||
user.set_password(generate_id())
|
||||
user.save()
|
||||
|
||||
stream = Stream.objects.filter(provider=self.provider).first()
|
||||
self.assertIsNotNone(stream)
|
||||
event = StreamEvent.objects.filter(
|
||||
stream=stream, type=EventTypes.CAEP_CREDENTIAL_CHANGE
|
||||
).first()
|
||||
self.assertIsNone(event)
|
||||
|
@ -110,10 +110,14 @@ export class SSFProviderViewPage extends AKElement {
|
||||
if (!this.provider) {
|
||||
return html``;
|
||||
}
|
||||
return html`<div
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile pf-l-grid pf-m-gutter"
|
||||
return html`<div slot="header" class="pf-c-banner pf-m-info">
|
||||
${msg("SSF Provider is in preview.")}
|
||||
<a href="mailto:hello+feature/ssf@goauthentik.io">${msg("Send us feedback!")}</a>
|
||||
</div>
|
||||
<div class="pf-c-page__main-section pf-m-no-padding-mobile pf-l-grid pf-m-gutter">
|
||||
<div
|
||||
class="pf-c-card pf-l-grid__item pf-m-12-col pf-m-4-col-on-xl pf-m-4-col-on-2xl"
|
||||
>
|
||||
<div class="pf-c-card pf-l-grid__item pf-m-12-col pf-m-4-col-on-xl pf-m-4-col-on-2xl">
|
||||
<div class="pf-c-card__body">
|
||||
<dl class="pf-c-description-list">
|
||||
<div class="pf-c-description-list__group">
|
||||
@ -121,7 +125,9 @@ export class SSFProviderViewPage extends AKElement {
|
||||
<span class="pf-c-description-list__text">${msg("Name")}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">${this.provider.name}</div>
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.provider.name}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
|
Reference in New Issue
Block a user