diff --git a/Makefile b/Makefile index c504d228d7..d89e01b199 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ pg_name := $(shell python -m authentik.lib.config postgresql.name 2>/dev/null) CODESPELL_ARGS = -D - -D .github/codespell-dictionary.txt \ -I .github/codespell-words.txt \ -S 'web/src/locales/**' \ - -S 'website/developer-docs/api/reference/**' \ + -S 'website/docs/developer-docs/api/reference/**' \ -S '**/node_modules/**' \ -S '**/dist/**' \ $(PY_SOURCES) \ diff --git a/authentik/blueprints/v1/importer.py b/authentik/blueprints/v1/importer.py index a86e62c4a8..700a32ad73 100644 --- a/authentik/blueprints/v1/importer.py +++ b/authentik/blueprints/v1/importer.py @@ -50,7 +50,6 @@ from authentik.enterprise.providers.microsoft_entra.models import ( MicrosoftEntraProviderGroup, MicrosoftEntraProviderUser, ) -from authentik.enterprise.providers.rac.models import ConnectionToken from authentik.enterprise.providers.ssf.models import StreamEvent from authentik.enterprise.stages.authenticator_endpoint_gdtc.models import ( EndpointDevice, @@ -72,6 +71,7 @@ from authentik.providers.oauth2.models import ( DeviceToken, RefreshToken, ) +from authentik.providers.rac.models import ConnectionToken from authentik.providers.scim.models import SCIMProviderGroup, SCIMProviderUser from authentik.rbac.models import Role from authentik.sources.scim.models import SCIMSourceGroup, SCIMSourceUser diff --git a/authentik/core/tasks.py b/authentik/core/tasks.py index 1a565d8f7f..c2e6929a20 100644 --- a/authentik/core/tasks.py +++ b/authentik/core/tasks.py @@ -67,6 +67,8 @@ def clean_expired_models(self: SystemTask): raise ImproperlyConfigured( "Invalid session_storage setting, allowed values are db and cache" ) + if CONFIG.get("session_storage", "cache") == "db": + DBSessionStore.clear_expired() LOGGER.debug("Expired sessions", model=AuthenticatedSession, amount=amount) messages.append(f"Expired {amount} {AuthenticatedSession._meta.verbose_name_plural}") diff --git a/authentik/enterprise/providers/rac/apps.py b/authentik/enterprise/providers/rac/apps.py deleted file mode 100644 index 6359c5594b..0000000000 --- a/authentik/enterprise/providers/rac/apps.py +++ /dev/null @@ -1,14 +0,0 @@ -"""RAC app config""" - -from authentik.enterprise.apps import EnterpriseConfig - - -class AuthentikEnterpriseProviderRAC(EnterpriseConfig): - """authentik enterprise rac app config""" - - name = "authentik.enterprise.providers.rac" - label = "authentik_providers_rac" - verbose_name = "authentik Enterprise.Providers.RAC" - default = True - mountpoint = "" - ws_mountpoint = "authentik.enterprise.providers.rac.urls" diff --git a/authentik/enterprise/settings.py b/authentik/enterprise/settings.py index a032fbd016..7f735eb312 100644 --- a/authentik/enterprise/settings.py +++ b/authentik/enterprise/settings.py @@ -16,7 +16,6 @@ TENANT_APPS = [ "authentik.enterprise.audit", "authentik.enterprise.providers.google_workspace", "authentik.enterprise.providers.microsoft_entra", - "authentik.enterprise.providers.rac", "authentik.enterprise.providers.ssf", "authentik.enterprise.stages.authenticator_endpoint_gdtc", "authentik.enterprise.stages.source", diff --git a/authentik/outposts/api/outposts.py b/authentik/outposts/api/outposts.py index ba84cf42e3..b4723ce3a6 100644 --- a/authentik/outposts/api/outposts.py +++ b/authentik/outposts/api/outposts.py @@ -19,7 +19,6 @@ from authentik.core.api.used_by import UsedByMixin from authentik.core.api.utils import JSONDictField, ModelSerializer, PassiveSerializer from authentik.core.models import Provider from authentik.enterprise.license import LicenseKey -from authentik.enterprise.providers.rac.models import RACProvider from authentik.lib.utils.time import timedelta_from_string, timedelta_string_validator from authentik.outposts.api.service_connections import ServiceConnectionSerializer from authentik.outposts.apps import MANAGED_OUTPOST, MANAGED_OUTPOST_NAME @@ -31,6 +30,7 @@ from authentik.outposts.models import ( ) from authentik.providers.ldap.models import LDAPProvider from authentik.providers.proxy.models import ProxyProvider +from authentik.providers.rac.models import RACProvider from authentik.providers.radius.models import RadiusProvider diff --git a/authentik/outposts/tasks.py b/authentik/outposts/tasks.py index 7a80ce9be4..e09dcf769f 100644 --- a/authentik/outposts/tasks.py +++ b/authentik/outposts/tasks.py @@ -18,8 +18,6 @@ from kubernetes.config.kube_config import KUBE_CONFIG_DEFAULT_LOCATION from structlog.stdlib import get_logger from yaml import safe_load -from authentik.enterprise.providers.rac.controllers.docker import RACDockerController -from authentik.enterprise.providers.rac.controllers.kubernetes import RACKubernetesController from authentik.events.models import TaskStatus from authentik.events.system_tasks import SystemTask, prefill_task from authentik.lib.config import CONFIG @@ -41,6 +39,8 @@ from authentik.providers.ldap.controllers.docker import LDAPDockerController from authentik.providers.ldap.controllers.kubernetes import LDAPKubernetesController from authentik.providers.proxy.controllers.docker import ProxyDockerController from authentik.providers.proxy.controllers.kubernetes import ProxyKubernetesController +from authentik.providers.rac.controllers.docker import RACDockerController +from authentik.providers.rac.controllers.kubernetes import RACKubernetesController from authentik.providers.radius.controllers.docker import RadiusDockerController from authentik.providers.radius.controllers.kubernetes import RadiusKubernetesController from authentik.root.celery import CELERY_APP diff --git a/authentik/policies/geoip/api.py b/authentik/policies/geoip/api.py index 5ea0b1e4aa..6d171c1378 100644 --- a/authentik/policies/geoip/api.py +++ b/authentik/policies/geoip/api.py @@ -42,6 +42,12 @@ class GeoIPPolicySerializer(CountryFieldMixin, PolicySerializer): "asns", "countries", "countries_obj", + "check_history_distance", + "history_max_distance_km", + "distance_tolerance_km", + "history_login_count", + "check_impossible_travel", + "impossible_tolerance_km", ] diff --git a/authentik/policies/geoip/migrations/0002_geoippolicy_check_history_distance_and_more.py b/authentik/policies/geoip/migrations/0002_geoippolicy_check_history_distance_and_more.py new file mode 100644 index 0000000000..9e44800c94 --- /dev/null +++ b/authentik/policies/geoip/migrations/0002_geoippolicy_check_history_distance_and_more.py @@ -0,0 +1,43 @@ +# Generated by Django 5.0.10 on 2025-01-02 20:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("authentik_policies_geoip", "0001_initial"), + ] + + operations = [ + migrations.AddField( + model_name="geoippolicy", + name="check_history_distance", + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name="geoippolicy", + name="check_impossible_travel", + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name="geoippolicy", + name="distance_tolerance_km", + field=models.PositiveIntegerField(default=50), + ), + migrations.AddField( + model_name="geoippolicy", + name="history_login_count", + field=models.PositiveIntegerField(default=5), + ), + migrations.AddField( + model_name="geoippolicy", + name="history_max_distance_km", + field=models.PositiveBigIntegerField(default=100), + ), + migrations.AddField( + model_name="geoippolicy", + name="impossible_tolerance_km", + field=models.PositiveIntegerField(default=100), + ), + ] diff --git a/authentik/policies/geoip/models.py b/authentik/policies/geoip/models.py index bd83ad12f6..f94341afd9 100644 --- a/authentik/policies/geoip/models.py +++ b/authentik/policies/geoip/models.py @@ -4,15 +4,21 @@ from itertools import chain from django.contrib.postgres.fields import ArrayField from django.db import models +from django.utils.timezone import now from django.utils.translation import gettext as _ from django_countries.fields import CountryField +from geopy import distance from rest_framework.serializers import BaseSerializer +from authentik.events.context_processors.geoip import GeoIPDict +from authentik.events.models import Event, EventAction from authentik.policies.exceptions import PolicyException from authentik.policies.geoip.exceptions import GeoIPNotFoundException from authentik.policies.models import Policy from authentik.policies.types import PolicyRequest, PolicyResult +MAX_DISTANCE_HOUR_KM = 1000 + class GeoIPPolicy(Policy): """Ensure the user satisfies requirements of geography or network topology, based on IP @@ -21,6 +27,15 @@ class GeoIPPolicy(Policy): asns = ArrayField(models.IntegerField(), blank=True, default=list) countries = CountryField(multiple=True, blank=True) + distance_tolerance_km = models.PositiveIntegerField(default=50) + + check_history_distance = models.BooleanField(default=False) + history_max_distance_km = models.PositiveBigIntegerField(default=100) + history_login_count = models.PositiveIntegerField(default=5) + + check_impossible_travel = models.BooleanField(default=False) + impossible_tolerance_km = models.PositiveIntegerField(default=100) + @property def serializer(self) -> type[BaseSerializer]: from authentik.policies.geoip.api import GeoIPPolicySerializer @@ -37,21 +52,27 @@ class GeoIPPolicy(Policy): - the client IP is advertised by an autonomous system with ASN in the `asns` - the client IP is geolocated in a country of `countries` """ - results: list[PolicyResult] = [] + static_results: list[PolicyResult] = [] + dynamic_results: list[PolicyResult] = [] if self.asns: - results.append(self.passes_asn(request)) + static_results.append(self.passes_asn(request)) if self.countries: - results.append(self.passes_country(request)) + static_results.append(self.passes_country(request)) - if not results: + if self.check_history_distance or self.check_impossible_travel: + dynamic_results.append(self.passes_distance(request)) + + if not static_results and not dynamic_results: return PolicyResult(True) - passing = any(r.passing for r in results) - messages = chain(*[r.messages for r in results]) + passing = any(r.passing for r in static_results) and all(r.passing for r in dynamic_results) + messages = chain( + *[r.messages for r in static_results], *[r.messages for r in dynamic_results] + ) result = PolicyResult(passing, *messages) - result.source_results = results + result.source_results = list(chain(static_results, dynamic_results)) return result @@ -73,7 +94,7 @@ class GeoIPPolicy(Policy): def passes_country(self, request: PolicyRequest) -> PolicyResult: # This is not a single get chain because `request.context` can contain `{ "geoip": None }`. - geoip_data = request.context.get("geoip") + geoip_data: GeoIPDict | None = request.context.get("geoip") country = geoip_data.get("country") if geoip_data else None if not country: @@ -87,6 +108,42 @@ class GeoIPPolicy(Policy): return PolicyResult(True) + def passes_distance(self, request: PolicyRequest) -> PolicyResult: + """Check if current policy execution is out of distance range compared + to previous authentication requests""" + # Get previous login event and GeoIP data + previous_logins = Event.objects.filter( + action=EventAction.LOGIN, user__pk=request.user.pk, context__geo__isnull=False + ).order_by("-created")[: self.history_login_count] + _now = now() + geoip_data: GeoIPDict | None = request.context.get("geoip") + if not geoip_data: + return PolicyResult(False) + for previous_login in previous_logins: + previous_login_geoip: GeoIPDict = previous_login.context["geo"] + + # Figure out distance + dist = distance.geodesic( + (previous_login_geoip["lat"], previous_login_geoip["long"]), + (geoip_data["lat"], geoip_data["long"]), + ) + if self.check_history_distance and dist.km >= ( + self.history_max_distance_km - self.distance_tolerance_km + ): + return PolicyResult( + False, _("Distance from previous authentication is larger than threshold.") + ) + # Check if distance between `previous_login` and now is more + # than max distance per hour times the amount of hours since the previous login + # (round down to the lowest closest time of hours) + # clamped to be at least 1 hour + rel_time_hours = max(int((_now - previous_login.created).total_seconds() / 3600), 1) + if self.check_impossible_travel and dist.km >= ( + (MAX_DISTANCE_HOUR_KM * rel_time_hours) - self.distance_tolerance_km + ): + return PolicyResult(False, _("Distance is further than possible.")) + return PolicyResult(True) + class Meta(Policy.PolicyMeta): verbose_name = _("GeoIP Policy") verbose_name_plural = _("GeoIP Policies") diff --git a/authentik/policies/geoip/tests.py b/authentik/policies/geoip/tests.py index f84727d1df..a1178f35c4 100644 --- a/authentik/policies/geoip/tests.py +++ b/authentik/policies/geoip/tests.py @@ -1,8 +1,10 @@ """geoip policy tests""" from django.test import TestCase -from guardian.shortcuts import get_anonymous_user +from authentik.core.tests.utils import create_test_user +from authentik.events.models import Event, EventAction +from authentik.events.utils import get_user from authentik.policies.engine import PolicyRequest, PolicyResult from authentik.policies.exceptions import PolicyException from authentik.policies.geoip.exceptions import GeoIPNotFoundException @@ -14,8 +16,8 @@ class TestGeoIPPolicy(TestCase): def setUp(self): super().setUp() - - self.request = PolicyRequest(get_anonymous_user()) + self.user = create_test_user() + self.request = PolicyRequest(self.user) self.context_disabled_geoip = {} self.context_unknown_ip = {"asn": None, "geoip": None} @@ -126,3 +128,70 @@ class TestGeoIPPolicy(TestCase): result: PolicyResult = policy.passes(self.request) self.assertTrue(result.passing) + + def test_history(self): + """Test history checks""" + Event.objects.create( + action=EventAction.LOGIN, + user=get_user(self.user), + context={ + # Random location in Canada + "geo": {"lat": 55.868351, "long": -104.441011}, + }, + ) + # Random location in Poland + self.request.context["geoip"] = {"lat": 50.950613, "long": 20.363679} + + policy = GeoIPPolicy.objects.create(check_history_distance=True) + + result: PolicyResult = policy.passes(self.request) + self.assertFalse(result.passing) + + def test_history_no_data(self): + """Test history checks (with no geoip data in context)""" + Event.objects.create( + action=EventAction.LOGIN, + user=get_user(self.user), + context={ + # Random location in Canada + "geo": {"lat": 55.868351, "long": -104.441011}, + }, + ) + + policy = GeoIPPolicy.objects.create(check_history_distance=True) + + result: PolicyResult = policy.passes(self.request) + self.assertFalse(result.passing) + + def test_history_impossible_travel(self): + """Test history checks""" + Event.objects.create( + action=EventAction.LOGIN, + user=get_user(self.user), + context={ + # Random location in Canada + "geo": {"lat": 55.868351, "long": -104.441011}, + }, + ) + # Random location in Poland + self.request.context["geoip"] = {"lat": 50.950613, "long": 20.363679} + + policy = GeoIPPolicy.objects.create(check_impossible_travel=True) + + result: PolicyResult = policy.passes(self.request) + self.assertFalse(result.passing) + + def test_history_no_geoip(self): + """Test history checks (previous login with no geoip data)""" + Event.objects.create( + action=EventAction.LOGIN, + user=get_user(self.user), + context={}, + ) + # Random location in Poland + self.request.context["geoip"] = {"lat": 50.950613, "long": 20.363679} + + policy = GeoIPPolicy.objects.create(check_history_distance=True) + + result: PolicyResult = policy.passes(self.request) + self.assertFalse(result.passing) diff --git a/authentik/enterprise/providers/rac/__init__.py b/authentik/providers/rac/__init__.py similarity index 100% rename from authentik/enterprise/providers/rac/__init__.py rename to authentik/providers/rac/__init__.py diff --git a/authentik/enterprise/providers/rac/api/__init__.py b/authentik/providers/rac/api/__init__.py similarity index 100% rename from authentik/enterprise/providers/rac/api/__init__.py rename to authentik/providers/rac/api/__init__.py diff --git a/authentik/enterprise/providers/rac/api/connection_tokens.py b/authentik/providers/rac/api/connection_tokens.py similarity index 78% rename from authentik/enterprise/providers/rac/api/connection_tokens.py rename to authentik/providers/rac/api/connection_tokens.py index 18c1485d12..c6de12b1c2 100644 --- a/authentik/enterprise/providers/rac/api/connection_tokens.py +++ b/authentik/providers/rac/api/connection_tokens.py @@ -6,13 +6,12 @@ from rest_framework.viewsets import GenericViewSet from authentik.core.api.groups import GroupMemberSerializer from authentik.core.api.used_by import UsedByMixin from authentik.core.api.utils import ModelSerializer -from authentik.enterprise.api import EnterpriseRequiredMixin -from authentik.enterprise.providers.rac.api.endpoints import EndpointSerializer -from authentik.enterprise.providers.rac.api.providers import RACProviderSerializer -from authentik.enterprise.providers.rac.models import ConnectionToken +from authentik.providers.rac.api.endpoints import EndpointSerializer +from authentik.providers.rac.api.providers import RACProviderSerializer +from authentik.providers.rac.models import ConnectionToken -class ConnectionTokenSerializer(EnterpriseRequiredMixin, ModelSerializer): +class ConnectionTokenSerializer(ModelSerializer): """ConnectionToken Serializer""" provider_obj = RACProviderSerializer(source="provider", read_only=True) diff --git a/authentik/enterprise/providers/rac/api/endpoints.py b/authentik/providers/rac/api/endpoints.py similarity index 94% rename from authentik/enterprise/providers/rac/api/endpoints.py rename to authentik/providers/rac/api/endpoints.py index 6cb4aea8fa..4f63976158 100644 --- a/authentik/enterprise/providers/rac/api/endpoints.py +++ b/authentik/providers/rac/api/endpoints.py @@ -14,10 +14,9 @@ from structlog.stdlib import get_logger from authentik.core.api.used_by import UsedByMixin from authentik.core.api.utils import ModelSerializer from authentik.core.models import Provider -from authentik.enterprise.api import EnterpriseRequiredMixin -from authentik.enterprise.providers.rac.api.providers import RACProviderSerializer -from authentik.enterprise.providers.rac.models import Endpoint from authentik.policies.engine import PolicyEngine +from authentik.providers.rac.api.providers import RACProviderSerializer +from authentik.providers.rac.models import Endpoint from authentik.rbac.filters import ObjectFilter LOGGER = get_logger() @@ -28,7 +27,7 @@ def user_endpoint_cache_key(user_pk: str) -> str: return f"goauthentik.io/providers/rac/endpoint_access/{user_pk}" -class EndpointSerializer(EnterpriseRequiredMixin, ModelSerializer): +class EndpointSerializer(ModelSerializer): """Endpoint Serializer""" provider_obj = RACProviderSerializer(source="provider", read_only=True) diff --git a/authentik/enterprise/providers/rac/api/property_mappings.py b/authentik/providers/rac/api/property_mappings.py similarity index 95% rename from authentik/enterprise/providers/rac/api/property_mappings.py rename to authentik/providers/rac/api/property_mappings.py index d41a4eb16c..4a5beafdb3 100644 --- a/authentik/enterprise/providers/rac/api/property_mappings.py +++ b/authentik/providers/rac/api/property_mappings.py @@ -10,7 +10,7 @@ from rest_framework.viewsets import ModelViewSet from authentik.core.api.property_mappings import PropertyMappingSerializer from authentik.core.api.used_by import UsedByMixin from authentik.core.api.utils import JSONDictField -from authentik.enterprise.providers.rac.models import RACPropertyMapping +from authentik.providers.rac.models import RACPropertyMapping class RACPropertyMappingSerializer(PropertyMappingSerializer): diff --git a/authentik/enterprise/providers/rac/api/providers.py b/authentik/providers/rac/api/providers.py similarity index 87% rename from authentik/enterprise/providers/rac/api/providers.py rename to authentik/providers/rac/api/providers.py index 9d0439ee7e..35ae2b2410 100644 --- a/authentik/enterprise/providers/rac/api/providers.py +++ b/authentik/providers/rac/api/providers.py @@ -5,11 +5,10 @@ from rest_framework.viewsets import ModelViewSet from authentik.core.api.providers import ProviderSerializer from authentik.core.api.used_by import UsedByMixin -from authentik.enterprise.api import EnterpriseRequiredMixin -from authentik.enterprise.providers.rac.models import RACProvider +from authentik.providers.rac.models import RACProvider -class RACProviderSerializer(EnterpriseRequiredMixin, ProviderSerializer): +class RACProviderSerializer(ProviderSerializer): """RACProvider Serializer""" outpost_set = ListField(child=CharField(), read_only=True, source="outpost_set.all") diff --git a/authentik/providers/rac/apps.py b/authentik/providers/rac/apps.py new file mode 100644 index 0000000000..7b11c1e540 --- /dev/null +++ b/authentik/providers/rac/apps.py @@ -0,0 +1,14 @@ +"""RAC app config""" + +from django.apps import AppConfig + + +class AuthentikProviderRAC(AppConfig): + """authentik rac app config""" + + name = "authentik.providers.rac" + label = "authentik_providers_rac" + verbose_name = "authentik Providers.RAC" + default = True + mountpoint = "" + ws_mountpoint = "authentik.providers.rac.urls" diff --git a/authentik/enterprise/providers/rac/consumer_client.py b/authentik/providers/rac/consumer_client.py similarity index 96% rename from authentik/enterprise/providers/rac/consumer_client.py rename to authentik/providers/rac/consumer_client.py index b6331ca563..b55059ec20 100644 --- a/authentik/enterprise/providers/rac/consumer_client.py +++ b/authentik/providers/rac/consumer_client.py @@ -7,22 +7,22 @@ from channels.generic.websocket import AsyncWebsocketConsumer from django.http.request import QueryDict from structlog.stdlib import BoundLogger, get_logger -from authentik.enterprise.providers.rac.models import ConnectionToken, RACProvider from authentik.outposts.consumer import OUTPOST_GROUP_INSTANCE from authentik.outposts.models import Outpost, OutpostState, OutpostType +from authentik.providers.rac.models import ConnectionToken, RACProvider # Global broadcast group, which messages are sent to when the outpost connects back # to authentik for a specific connection # The `RACClientConsumer` consumer adds itself to this group on connection, # and removes itself once it has been assigned a specific outpost channel -RAC_CLIENT_GROUP = "group_enterprise_rac_client" +RAC_CLIENT_GROUP = "group_rac_client" # A group for all connections in a given authentik session ID # A disconnect message is sent to this group when the session expires/is deleted -RAC_CLIENT_GROUP_SESSION = "group_enterprise_rac_client_%(session)s" +RAC_CLIENT_GROUP_SESSION = "group_rac_client_%(session)s" # A group for all connections with a specific token, which in almost all cases # is just one connection, however this is used to disconnect the connection # when the token is deleted -RAC_CLIENT_GROUP_TOKEN = "group_enterprise_rac_token_%(token)s" # nosec +RAC_CLIENT_GROUP_TOKEN = "group_rac_token_%(token)s" # nosec # Step 1: Client connects to this websocket endpoint # Step 2: We prepare all the connection args for Guac diff --git a/authentik/enterprise/providers/rac/consumer_outpost.py b/authentik/providers/rac/consumer_outpost.py similarity index 95% rename from authentik/enterprise/providers/rac/consumer_outpost.py rename to authentik/providers/rac/consumer_outpost.py index a1119d85a8..a6f8aea07f 100644 --- a/authentik/enterprise/providers/rac/consumer_outpost.py +++ b/authentik/providers/rac/consumer_outpost.py @@ -3,7 +3,7 @@ from channels.exceptions import ChannelFull from channels.generic.websocket import AsyncWebsocketConsumer -from authentik.enterprise.providers.rac.consumer_client import RAC_CLIENT_GROUP +from authentik.providers.rac.consumer_client import RAC_CLIENT_GROUP class RACOutpostConsumer(AsyncWebsocketConsumer): diff --git a/authentik/enterprise/providers/rac/controllers/__init__.py b/authentik/providers/rac/controllers/__init__.py similarity index 100% rename from authentik/enterprise/providers/rac/controllers/__init__.py rename to authentik/providers/rac/controllers/__init__.py diff --git a/authentik/enterprise/providers/rac/controllers/docker.py b/authentik/providers/rac/controllers/docker.py similarity index 100% rename from authentik/enterprise/providers/rac/controllers/docker.py rename to authentik/providers/rac/controllers/docker.py diff --git a/authentik/enterprise/providers/rac/controllers/kubernetes.py b/authentik/providers/rac/controllers/kubernetes.py similarity index 100% rename from authentik/enterprise/providers/rac/controllers/kubernetes.py rename to authentik/providers/rac/controllers/kubernetes.py diff --git a/authentik/enterprise/providers/rac/migrations/0001_initial.py b/authentik/providers/rac/migrations/0001_initial.py similarity index 100% rename from authentik/enterprise/providers/rac/migrations/0001_initial.py rename to authentik/providers/rac/migrations/0001_initial.py diff --git a/authentik/enterprise/providers/rac/migrations/0001_squashed_0003_alter_connectiontoken_options_and_more.py b/authentik/providers/rac/migrations/0001_squashed_0003_alter_connectiontoken_options_and_more.py similarity index 100% rename from authentik/enterprise/providers/rac/migrations/0001_squashed_0003_alter_connectiontoken_options_and_more.py rename to authentik/providers/rac/migrations/0001_squashed_0003_alter_connectiontoken_options_and_more.py diff --git a/authentik/enterprise/providers/rac/migrations/0002_endpoint_maximum_connections.py b/authentik/providers/rac/migrations/0002_endpoint_maximum_connections.py similarity index 100% rename from authentik/enterprise/providers/rac/migrations/0002_endpoint_maximum_connections.py rename to authentik/providers/rac/migrations/0002_endpoint_maximum_connections.py diff --git a/authentik/enterprise/providers/rac/migrations/0003_alter_connectiontoken_options_and_more.py b/authentik/providers/rac/migrations/0003_alter_connectiontoken_options_and_more.py similarity index 100% rename from authentik/enterprise/providers/rac/migrations/0003_alter_connectiontoken_options_and_more.py rename to authentik/providers/rac/migrations/0003_alter_connectiontoken_options_and_more.py diff --git a/authentik/enterprise/providers/rac/migrations/0004_alter_connectiontoken_expires.py b/authentik/providers/rac/migrations/0004_alter_connectiontoken_expires.py similarity index 100% rename from authentik/enterprise/providers/rac/migrations/0004_alter_connectiontoken_expires.py rename to authentik/providers/rac/migrations/0004_alter_connectiontoken_expires.py diff --git a/authentik/enterprise/providers/rac/migrations/0005_alter_racpropertymapping_options.py b/authentik/providers/rac/migrations/0005_alter_racpropertymapping_options.py similarity index 100% rename from authentik/enterprise/providers/rac/migrations/0005_alter_racpropertymapping_options.py rename to authentik/providers/rac/migrations/0005_alter_racpropertymapping_options.py diff --git a/authentik/enterprise/providers/rac/migrations/0006_connectiontoken_authentik_p_expires_91f148_idx_and_more.py b/authentik/providers/rac/migrations/0006_connectiontoken_authentik_p_expires_91f148_idx_and_more.py similarity index 100% rename from authentik/enterprise/providers/rac/migrations/0006_connectiontoken_authentik_p_expires_91f148_idx_and_more.py rename to authentik/providers/rac/migrations/0006_connectiontoken_authentik_p_expires_91f148_idx_and_more.py diff --git a/authentik/enterprise/providers/rac/migrations/__init__.py b/authentik/providers/rac/migrations/__init__.py similarity index 100% rename from authentik/enterprise/providers/rac/migrations/__init__.py rename to authentik/providers/rac/migrations/__init__.py diff --git a/authentik/enterprise/providers/rac/models.py b/authentik/providers/rac/models.py similarity index 96% rename from authentik/enterprise/providers/rac/models.py rename to authentik/providers/rac/models.py index 39b59553ce..26d7b60734 100644 --- a/authentik/enterprise/providers/rac/models.py +++ b/authentik/providers/rac/models.py @@ -74,7 +74,7 @@ class RACProvider(Provider): @property def serializer(self) -> type[Serializer]: - from authentik.enterprise.providers.rac.api.providers import RACProviderSerializer + from authentik.providers.rac.api.providers import RACProviderSerializer return RACProviderSerializer @@ -100,7 +100,7 @@ class Endpoint(SerializerModel, PolicyBindingModel): @property def serializer(self) -> type[Serializer]: - from authentik.enterprise.providers.rac.api.endpoints import EndpointSerializer + from authentik.providers.rac.api.endpoints import EndpointSerializer return EndpointSerializer @@ -129,7 +129,7 @@ class RACPropertyMapping(PropertyMapping): @property def serializer(self) -> type[Serializer]: - from authentik.enterprise.providers.rac.api.property_mappings import ( + from authentik.providers.rac.api.property_mappings import ( RACPropertyMappingSerializer, ) diff --git a/authentik/enterprise/providers/rac/signals.py b/authentik/providers/rac/signals.py similarity index 88% rename from authentik/enterprise/providers/rac/signals.py rename to authentik/providers/rac/signals.py index 2cf7b00bf9..f36cb19898 100644 --- a/authentik/enterprise/providers/rac/signals.py +++ b/authentik/providers/rac/signals.py @@ -10,12 +10,12 @@ from django.dispatch import receiver from django.http import HttpRequest from authentik.core.models import User -from authentik.enterprise.providers.rac.api.endpoints import user_endpoint_cache_key -from authentik.enterprise.providers.rac.consumer_client import ( +from authentik.providers.rac.api.endpoints import user_endpoint_cache_key +from authentik.providers.rac.consumer_client import ( RAC_CLIENT_GROUP_SESSION, RAC_CLIENT_GROUP_TOKEN, ) -from authentik.enterprise.providers.rac.models import ConnectionToken, Endpoint +from authentik.providers.rac.models import ConnectionToken, Endpoint @receiver(user_logged_out) diff --git a/authentik/enterprise/providers/rac/templates/if/rac.html b/authentik/providers/rac/templates/if/rac.html similarity index 85% rename from authentik/enterprise/providers/rac/templates/if/rac.html rename to authentik/providers/rac/templates/if/rac.html index 156d96085e..4f26fba880 100644 --- a/authentik/enterprise/providers/rac/templates/if/rac.html +++ b/authentik/providers/rac/templates/if/rac.html @@ -3,7 +3,7 @@ {% load authentik_core %} {% block head %} - + diff --git a/authentik/enterprise/providers/rac/tests/__init__.py b/authentik/providers/rac/tests/__init__.py similarity index 100% rename from authentik/enterprise/providers/rac/tests/__init__.py rename to authentik/providers/rac/tests/__init__.py diff --git a/authentik/enterprise/providers/rac/tests/test_api.py b/authentik/providers/rac/tests/test_api.py similarity index 53% rename from authentik/enterprise/providers/rac/tests/test_api.py rename to authentik/providers/rac/tests/test_api.py index da71133e80..c66bb11853 100644 --- a/authentik/enterprise/providers/rac/tests/test_api.py +++ b/authentik/providers/rac/tests/test_api.py @@ -1,16 +1,9 @@ """Test RAC Provider""" -from datetime import timedelta -from time import mktime -from unittest.mock import MagicMock, patch - from django.urls import reverse -from django.utils.timezone import now from rest_framework.test import APITestCase from authentik.core.tests.utils import create_test_admin_user, create_test_flow -from authentik.enterprise.license import LicenseKey -from authentik.enterprise.models import License from authentik.lib.generators import generate_id @@ -20,21 +13,8 @@ class TestAPI(APITestCase): def setUp(self) -> None: self.user = create_test_admin_user() - @patch( - "authentik.enterprise.license.LicenseKey.validate", - MagicMock( - return_value=LicenseKey( - aud="", - exp=int(mktime((now() + timedelta(days=3000)).timetuple())), - name=generate_id(), - internal_users=100, - external_users=100, - ) - ), - ) def test_create(self): """Test creation of RAC Provider""" - License.objects.create(key=generate_id()) self.client.force_login(self.user) response = self.client.post( reverse("authentik_api:racprovider-list"), diff --git a/authentik/enterprise/providers/rac/tests/test_endpoints_api.py b/authentik/providers/rac/tests/test_endpoints_api.py similarity index 98% rename from authentik/enterprise/providers/rac/tests/test_endpoints_api.py rename to authentik/providers/rac/tests/test_endpoints_api.py index 1ad9b70daf..9a2469bbba 100644 --- a/authentik/enterprise/providers/rac/tests/test_endpoints_api.py +++ b/authentik/providers/rac/tests/test_endpoints_api.py @@ -5,10 +5,10 @@ from rest_framework.test import APITestCase from authentik.core.models import Application from authentik.core.tests.utils import create_test_admin_user -from authentik.enterprise.providers.rac.models import Endpoint, Protocols, RACProvider from authentik.lib.generators import generate_id from authentik.policies.dummy.models import DummyPolicy from authentik.policies.models import PolicyBinding +from authentik.providers.rac.models import Endpoint, Protocols, RACProvider class TestEndpointsAPI(APITestCase): diff --git a/authentik/enterprise/providers/rac/tests/test_models.py b/authentik/providers/rac/tests/test_models.py similarity index 98% rename from authentik/enterprise/providers/rac/tests/test_models.py rename to authentik/providers/rac/tests/test_models.py index b6e7258d83..4a9fad4e67 100644 --- a/authentik/enterprise/providers/rac/tests/test_models.py +++ b/authentik/providers/rac/tests/test_models.py @@ -4,14 +4,14 @@ from django.test import TransactionTestCase from authentik.core.models import Application, AuthenticatedSession from authentik.core.tests.utils import create_test_admin_user -from authentik.enterprise.providers.rac.models import ( +from authentik.lib.generators import generate_id +from authentik.providers.rac.models import ( ConnectionToken, Endpoint, Protocols, RACPropertyMapping, RACProvider, ) -from authentik.lib.generators import generate_id class TestModels(TransactionTestCase): diff --git a/authentik/enterprise/providers/rac/tests/test_views.py b/authentik/providers/rac/tests/test_views.py similarity index 67% rename from authentik/enterprise/providers/rac/tests/test_views.py rename to authentik/providers/rac/tests/test_views.py index a63f27fba0..80778d2ebc 100644 --- a/authentik/enterprise/providers/rac/tests/test_views.py +++ b/authentik/providers/rac/tests/test_views.py @@ -1,23 +1,17 @@ """RAC Views tests""" -from datetime import timedelta from json import loads -from time import mktime -from unittest.mock import MagicMock, patch from django.urls import reverse -from django.utils.timezone import now from rest_framework.test import APITestCase from authentik.core.models import Application from authentik.core.tests.utils import create_test_admin_user, create_test_flow -from authentik.enterprise.license import LicenseKey -from authentik.enterprise.models import License -from authentik.enterprise.providers.rac.models import Endpoint, Protocols, RACProvider from authentik.lib.generators import generate_id from authentik.policies.denied import AccessDeniedResponse from authentik.policies.dummy.models import DummyPolicy from authentik.policies.models import PolicyBinding +from authentik.providers.rac.models import Endpoint, Protocols, RACProvider class TestRACViews(APITestCase): @@ -39,21 +33,8 @@ class TestRACViews(APITestCase): provider=self.provider, ) - @patch( - "authentik.enterprise.license.LicenseKey.validate", - MagicMock( - return_value=LicenseKey( - aud="", - exp=int(mktime((now() + timedelta(days=3000)).timetuple())), - name=generate_id(), - internal_users=100, - external_users=100, - ) - ), - ) def test_no_policy(self): """Test request""" - License.objects.create(key=generate_id()) self.client.force_login(self.user) response = self.client.get( reverse( @@ -70,18 +51,6 @@ class TestRACViews(APITestCase): final_response = self.client.get(next_url) self.assertEqual(final_response.status_code, 200) - @patch( - "authentik.enterprise.license.LicenseKey.validate", - MagicMock( - return_value=LicenseKey( - aud="", - exp=int(mktime((now() + timedelta(days=3000)).timetuple())), - name=generate_id(), - internal_users=100, - external_users=100, - ) - ), - ) def test_app_deny(self): """Test request (deny on app level)""" PolicyBinding.objects.create( @@ -89,7 +58,6 @@ class TestRACViews(APITestCase): policy=DummyPolicy.objects.create(name="deny", result=False, wait_min=1, wait_max=2), order=0, ) - License.objects.create(key=generate_id()) self.client.force_login(self.user) response = self.client.get( reverse( @@ -99,18 +67,6 @@ class TestRACViews(APITestCase): ) self.assertIsInstance(response, AccessDeniedResponse) - @patch( - "authentik.enterprise.license.LicenseKey.validate", - MagicMock( - return_value=LicenseKey( - aud="", - exp=int(mktime((now() + timedelta(days=3000)).timetuple())), - name=generate_id(), - internal_users=100, - external_users=100, - ) - ), - ) def test_endpoint_deny(self): """Test request (deny on endpoint level)""" PolicyBinding.objects.create( @@ -118,7 +74,6 @@ class TestRACViews(APITestCase): policy=DummyPolicy.objects.create(name="deny", result=False, wait_min=1, wait_max=2), order=0, ) - License.objects.create(key=generate_id()) self.client.force_login(self.user) response = self.client.get( reverse( diff --git a/authentik/enterprise/providers/rac/urls.py b/authentik/providers/rac/urls.py similarity index 66% rename from authentik/enterprise/providers/rac/urls.py rename to authentik/providers/rac/urls.py index 88b3e2e828..07e6f661cc 100644 --- a/authentik/enterprise/providers/rac/urls.py +++ b/authentik/providers/rac/urls.py @@ -4,14 +4,14 @@ from channels.auth import AuthMiddleware from channels.sessions import CookieMiddleware from django.urls import path -from authentik.enterprise.providers.rac.api.connection_tokens import ConnectionTokenViewSet -from authentik.enterprise.providers.rac.api.endpoints import EndpointViewSet -from authentik.enterprise.providers.rac.api.property_mappings import RACPropertyMappingViewSet -from authentik.enterprise.providers.rac.api.providers import RACProviderViewSet -from authentik.enterprise.providers.rac.consumer_client import RACClientConsumer -from authentik.enterprise.providers.rac.consumer_outpost import RACOutpostConsumer -from authentik.enterprise.providers.rac.views import RACInterface, RACStartView from authentik.outposts.channels import TokenOutpostMiddleware +from authentik.providers.rac.api.connection_tokens import ConnectionTokenViewSet +from authentik.providers.rac.api.endpoints import EndpointViewSet +from authentik.providers.rac.api.property_mappings import RACPropertyMappingViewSet +from authentik.providers.rac.api.providers import RACProviderViewSet +from authentik.providers.rac.consumer_client import RACClientConsumer +from authentik.providers.rac.consumer_outpost import RACOutpostConsumer +from authentik.providers.rac.views import RACInterface, RACStartView from authentik.root.asgi_middleware import SessionMiddleware from authentik.root.middleware import ChannelsLoggingMiddleware diff --git a/authentik/enterprise/providers/rac/views.py b/authentik/providers/rac/views.py similarity index 96% rename from authentik/enterprise/providers/rac/views.py rename to authentik/providers/rac/views.py index 36785766a3..bac8e21b90 100644 --- a/authentik/enterprise/providers/rac/views.py +++ b/authentik/providers/rac/views.py @@ -10,8 +10,6 @@ from django.utils.translation import gettext as _ from authentik.core.models import Application, AuthenticatedSession from authentik.core.views.interface import InterfaceView -from authentik.enterprise.policy import EnterprisePolicyAccessView -from authentik.enterprise.providers.rac.models import ConnectionToken, Endpoint, RACProvider from authentik.events.models import Event, EventAction from authentik.flows.challenge import RedirectChallenge from authentik.flows.exceptions import FlowNonApplicableException @@ -20,9 +18,11 @@ from authentik.flows.planner import PLAN_CONTEXT_APPLICATION, FlowPlanner from authentik.flows.stage import RedirectStage from authentik.lib.utils.time import timedelta_from_string from authentik.policies.engine import PolicyEngine +from authentik.policies.views import PolicyAccessView +from authentik.providers.rac.models import ConnectionToken, Endpoint, RACProvider -class RACStartView(EnterprisePolicyAccessView): +class RACStartView(PolicyAccessView): """Start a RAC connection by checking access and creating a connection token""" endpoint: Endpoint diff --git a/authentik/root/settings.py b/authentik/root/settings.py index 6eddf6c98d..a9568c64ba 100644 --- a/authentik/root/settings.py +++ b/authentik/root/settings.py @@ -87,6 +87,7 @@ TENANT_APPS = [ "authentik.providers.ldap", "authentik.providers.oauth2", "authentik.providers.proxy", + "authentik.providers.rac", "authentik.providers.radius", "authentik.providers.saml", "authentik.providers.scim", diff --git a/blueprints/schema.json b/blueprints/schema.json index d43da273e2..bca4395e32 100644 --- a/blueprints/schema.json +++ b/blueprints/schema.json @@ -801,6 +801,126 @@ } } }, + { + "type": "object", + "required": [ + "model", + "identifiers" + ], + "properties": { + "model": { + "const": "authentik_providers_rac.racprovider" + }, + "id": { + "type": "string" + }, + "state": { + "type": "string", + "enum": [ + "absent", + "present", + "created", + "must_created" + ], + "default": "present" + }, + "conditions": { + "type": "array", + "items": { + "type": "boolean" + } + }, + "permissions": { + "$ref": "#/$defs/model_authentik_providers_rac.racprovider_permissions" + }, + "attrs": { + "$ref": "#/$defs/model_authentik_providers_rac.racprovider" + }, + "identifiers": { + "$ref": "#/$defs/model_authentik_providers_rac.racprovider" + } + } + }, + { + "type": "object", + "required": [ + "model", + "identifiers" + ], + "properties": { + "model": { + "const": "authentik_providers_rac.endpoint" + }, + "id": { + "type": "string" + }, + "state": { + "type": "string", + "enum": [ + "absent", + "present", + "created", + "must_created" + ], + "default": "present" + }, + "conditions": { + "type": "array", + "items": { + "type": "boolean" + } + }, + "permissions": { + "$ref": "#/$defs/model_authentik_providers_rac.endpoint_permissions" + }, + "attrs": { + "$ref": "#/$defs/model_authentik_providers_rac.endpoint" + }, + "identifiers": { + "$ref": "#/$defs/model_authentik_providers_rac.endpoint" + } + } + }, + { + "type": "object", + "required": [ + "model", + "identifiers" + ], + "properties": { + "model": { + "const": "authentik_providers_rac.racpropertymapping" + }, + "id": { + "type": "string" + }, + "state": { + "type": "string", + "enum": [ + "absent", + "present", + "created", + "must_created" + ], + "default": "present" + }, + "conditions": { + "type": "array", + "items": { + "type": "boolean" + } + }, + "permissions": { + "$ref": "#/$defs/model_authentik_providers_rac.racpropertymapping_permissions" + }, + "attrs": { + "$ref": "#/$defs/model_authentik_providers_rac.racpropertymapping" + }, + "identifiers": { + "$ref": "#/$defs/model_authentik_providers_rac.racpropertymapping" + } + } + }, { "type": "object", "required": [ @@ -3561,126 +3681,6 @@ } } }, - { - "type": "object", - "required": [ - "model", - "identifiers" - ], - "properties": { - "model": { - "const": "authentik_providers_rac.racprovider" - }, - "id": { - "type": "string" - }, - "state": { - "type": "string", - "enum": [ - "absent", - "present", - "created", - "must_created" - ], - "default": "present" - }, - "conditions": { - "type": "array", - "items": { - "type": "boolean" - } - }, - "permissions": { - "$ref": "#/$defs/model_authentik_providers_rac.racprovider_permissions" - }, - "attrs": { - "$ref": "#/$defs/model_authentik_providers_rac.racprovider" - }, - "identifiers": { - "$ref": "#/$defs/model_authentik_providers_rac.racprovider" - } - } - }, - { - "type": "object", - "required": [ - "model", - "identifiers" - ], - "properties": { - "model": { - "const": "authentik_providers_rac.endpoint" - }, - "id": { - "type": "string" - }, - "state": { - "type": "string", - "enum": [ - "absent", - "present", - "created", - "must_created" - ], - "default": "present" - }, - "conditions": { - "type": "array", - "items": { - "type": "boolean" - } - }, - "permissions": { - "$ref": "#/$defs/model_authentik_providers_rac.endpoint_permissions" - }, - "attrs": { - "$ref": "#/$defs/model_authentik_providers_rac.endpoint" - }, - "identifiers": { - "$ref": "#/$defs/model_authentik_providers_rac.endpoint" - } - } - }, - { - "type": "object", - "required": [ - "model", - "identifiers" - ], - "properties": { - "model": { - "const": "authentik_providers_rac.racpropertymapping" - }, - "id": { - "type": "string" - }, - "state": { - "type": "string", - "enum": [ - "absent", - "present", - "created", - "must_created" - ], - "default": "present" - }, - "conditions": { - "type": "array", - "items": { - "type": "boolean" - } - }, - "permissions": { - "$ref": "#/$defs/model_authentik_providers_rac.racpropertymapping_permissions" - }, - "attrs": { - "$ref": "#/$defs/model_authentik_providers_rac.racpropertymapping" - }, - "identifiers": { - "$ref": "#/$defs/model_authentik_providers_rac.racpropertymapping" - } - } - }, { "type": "object", "required": [ @@ -4663,6 +4663,7 @@ "authentik.providers.ldap", "authentik.providers.oauth2", "authentik.providers.proxy", + "authentik.providers.rac", "authentik.providers.radius", "authentik.providers.saml", "authentik.providers.scim", @@ -4703,7 +4704,6 @@ "authentik.enterprise.audit", "authentik.enterprise.providers.google_workspace", "authentik.enterprise.providers.microsoft_entra", - "authentik.enterprise.providers.rac", "authentik.enterprise.providers.ssf", "authentik.enterprise.stages.authenticator_endpoint_gdtc", "authentik.enterprise.stages.source", @@ -4738,6 +4738,9 @@ "authentik_providers_oauth2.scopemapping", "authentik_providers_oauth2.oauth2provider", "authentik_providers_proxy.proxyprovider", + "authentik_providers_rac.racprovider", + "authentik_providers_rac.endpoint", + "authentik_providers_rac.racpropertymapping", "authentik_providers_radius.radiusprovider", "authentik_providers_radius.radiusproviderpropertymapping", "authentik_providers_saml.samlprovider", @@ -4807,9 +4810,6 @@ "authentik_providers_google_workspace.googleworkspaceprovidermapping", "authentik_providers_microsoft_entra.microsoftentraprovider", "authentik_providers_microsoft_entra.microsoftentraprovidermapping", - "authentik_providers_rac.racprovider", - "authentik_providers_rac.endpoint", - "authentik_providers_rac.racpropertymapping", "authentik_providers_ssf.ssfprovider", "authentik_stages_authenticator_endpoint_gdtc.authenticatorendpointgdtcstage", "authentik_stages_source.sourcestage", @@ -5232,6 +5232,38 @@ }, "maxItems": 249, "title": "Countries" + }, + "check_history_distance": { + "type": "boolean", + "title": "Check history distance" + }, + "history_max_distance_km": { + "type": "integer", + "minimum": 0, + "maximum": 9223372036854775807, + "title": "History max distance km" + }, + "distance_tolerance_km": { + "type": "integer", + "minimum": 0, + "maximum": 2147483647, + "title": "Distance tolerance km" + }, + "history_login_count": { + "type": "integer", + "minimum": 0, + "maximum": 2147483647, + "title": "History login count" + }, + "check_impossible_travel": { + "type": "boolean", + "title": "Check impossible travel" + }, + "impossible_tolerance_km": { + "type": "integer", + "minimum": 0, + "maximum": 2147483647, + "title": "Impossible tolerance km" } }, "required": [] @@ -6014,6 +6046,216 @@ } } }, + "model_authentik_providers_rac.racprovider": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "title": "Name" + }, + "authentication_flow": { + "type": "string", + "format": "uuid", + "title": "Authentication flow", + "description": "Flow used for authentication when the associated application is accessed by an un-authenticated user." + }, + "authorization_flow": { + "type": "string", + "format": "uuid", + "title": "Authorization flow", + "description": "Flow used when authorizing this provider." + }, + "property_mappings": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + }, + "title": "Property mappings" + }, + "settings": { + "type": "object", + "additionalProperties": true, + "title": "Settings" + }, + "connection_expiry": { + "type": "string", + "minLength": 1, + "title": "Connection expiry", + "description": "Determines how long a session lasts. Default of 0 means that the sessions lasts until the browser is closed. (Format: hours=-1;minutes=-2;seconds=-3)" + }, + "delete_token_on_disconnect": { + "type": "boolean", + "title": "Delete token on disconnect", + "description": "When set to true, connection tokens will be deleted upon disconnect." + } + }, + "required": [] + }, + "model_authentik_providers_rac.racprovider_permissions": { + "type": "array", + "items": { + "type": "object", + "required": [ + "permission" + ], + "properties": { + "permission": { + "type": "string", + "enum": [ + "add_racprovider", + "change_racprovider", + "delete_racprovider", + "view_racprovider" + ] + }, + "user": { + "type": "integer" + }, + "role": { + "type": "string" + } + } + } + }, + "model_authentik_providers_rac.endpoint": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "title": "Name" + }, + "provider": { + "type": "integer", + "title": "Provider" + }, + "protocol": { + "type": "string", + "enum": [ + "rdp", + "vnc", + "ssh" + ], + "title": "Protocol" + }, + "host": { + "type": "string", + "minLength": 1, + "title": "Host" + }, + "settings": { + "type": "object", + "additionalProperties": true, + "title": "Settings" + }, + "property_mappings": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + }, + "title": "Property mappings" + }, + "auth_mode": { + "type": "string", + "enum": [ + "static", + "prompt" + ], + "title": "Auth mode" + }, + "maximum_connections": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "title": "Maximum connections" + } + }, + "required": [] + }, + "model_authentik_providers_rac.endpoint_permissions": { + "type": "array", + "items": { + "type": "object", + "required": [ + "permission" + ], + "properties": { + "permission": { + "type": "string", + "enum": [ + "add_endpoint", + "change_endpoint", + "delete_endpoint", + "view_endpoint" + ] + }, + "user": { + "type": "integer" + }, + "role": { + "type": "string" + } + } + } + }, + "model_authentik_providers_rac.racpropertymapping": { + "type": "object", + "properties": { + "managed": { + "type": [ + "string", + "null" + ], + "minLength": 1, + "title": "Managed by authentik", + "description": "Objects that are managed by authentik. These objects are created and updated automatically. This flag only indicates that an object can be overwritten by migrations. You can still modify the objects via the API, but expect changes to be overwritten in a later update." + }, + "name": { + "type": "string", + "minLength": 1, + "title": "Name" + }, + "expression": { + "type": "string", + "title": "Expression" + }, + "static_settings": { + "type": "object", + "additionalProperties": true, + "title": "Static settings" + } + }, + "required": [] + }, + "model_authentik_providers_rac.racpropertymapping_permissions": { + "type": "array", + "items": { + "type": "object", + "required": [ + "permission" + ], + "properties": { + "permission": { + "type": "string", + "enum": [ + "add_racpropertymapping", + "change_racpropertymapping", + "delete_racpropertymapping", + "view_racpropertymapping" + ] + }, + "user": { + "type": "integer" + }, + "role": { + "type": "string" + } + } + } + }, "model_authentik_providers_radius.radiusprovider": { "type": "object", "properties": { @@ -14183,216 +14425,6 @@ } } }, - "model_authentik_providers_rac.racprovider": { - "type": "object", - "properties": { - "name": { - "type": "string", - "minLength": 1, - "title": "Name" - }, - "authentication_flow": { - "type": "string", - "format": "uuid", - "title": "Authentication flow", - "description": "Flow used for authentication when the associated application is accessed by an un-authenticated user." - }, - "authorization_flow": { - "type": "string", - "format": "uuid", - "title": "Authorization flow", - "description": "Flow used when authorizing this provider." - }, - "property_mappings": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - }, - "title": "Property mappings" - }, - "settings": { - "type": "object", - "additionalProperties": true, - "title": "Settings" - }, - "connection_expiry": { - "type": "string", - "minLength": 1, - "title": "Connection expiry", - "description": "Determines how long a session lasts. Default of 0 means that the sessions lasts until the browser is closed. (Format: hours=-1;minutes=-2;seconds=-3)" - }, - "delete_token_on_disconnect": { - "type": "boolean", - "title": "Delete token on disconnect", - "description": "When set to true, connection tokens will be deleted upon disconnect." - } - }, - "required": [] - }, - "model_authentik_providers_rac.racprovider_permissions": { - "type": "array", - "items": { - "type": "object", - "required": [ - "permission" - ], - "properties": { - "permission": { - "type": "string", - "enum": [ - "add_racprovider", - "change_racprovider", - "delete_racprovider", - "view_racprovider" - ] - }, - "user": { - "type": "integer" - }, - "role": { - "type": "string" - } - } - } - }, - "model_authentik_providers_rac.endpoint": { - "type": "object", - "properties": { - "name": { - "type": "string", - "minLength": 1, - "title": "Name" - }, - "provider": { - "type": "integer", - "title": "Provider" - }, - "protocol": { - "type": "string", - "enum": [ - "rdp", - "vnc", - "ssh" - ], - "title": "Protocol" - }, - "host": { - "type": "string", - "minLength": 1, - "title": "Host" - }, - "settings": { - "type": "object", - "additionalProperties": true, - "title": "Settings" - }, - "property_mappings": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - }, - "title": "Property mappings" - }, - "auth_mode": { - "type": "string", - "enum": [ - "static", - "prompt" - ], - "title": "Auth mode" - }, - "maximum_connections": { - "type": "integer", - "minimum": -2147483648, - "maximum": 2147483647, - "title": "Maximum connections" - } - }, - "required": [] - }, - "model_authentik_providers_rac.endpoint_permissions": { - "type": "array", - "items": { - "type": "object", - "required": [ - "permission" - ], - "properties": { - "permission": { - "type": "string", - "enum": [ - "add_endpoint", - "change_endpoint", - "delete_endpoint", - "view_endpoint" - ] - }, - "user": { - "type": "integer" - }, - "role": { - "type": "string" - } - } - } - }, - "model_authentik_providers_rac.racpropertymapping": { - "type": "object", - "properties": { - "managed": { - "type": [ - "string", - "null" - ], - "minLength": 1, - "title": "Managed by authentik", - "description": "Objects that are managed by authentik. These objects are created and updated automatically. This flag only indicates that an object can be overwritten by migrations. You can still modify the objects via the API, but expect changes to be overwritten in a later update." - }, - "name": { - "type": "string", - "minLength": 1, - "title": "Name" - }, - "expression": { - "type": "string", - "title": "Expression" - }, - "static_settings": { - "type": "object", - "additionalProperties": true, - "title": "Static settings" - } - }, - "required": [] - }, - "model_authentik_providers_rac.racpropertymapping_permissions": { - "type": "array", - "items": { - "type": "object", - "required": [ - "permission" - ], - "properties": { - "permission": { - "type": "string", - "enum": [ - "add_racpropertymapping", - "change_racpropertymapping", - "delete_racpropertymapping", - "view_racpropertymapping" - ] - }, - "user": { - "type": "integer" - }, - "role": { - "type": "string" - } - } - } - }, "model_authentik_providers_ssf.ssfprovider": { "type": "object", "properties": { diff --git a/go.mod b/go.mod index 274837c657..3966daef7a 100644 --- a/go.mod +++ b/go.mod @@ -29,7 +29,7 @@ require ( github.com/spf13/cobra v1.9.1 github.com/stretchr/testify v1.10.0 github.com/wwt/guac v1.3.2 - goauthentik.io/api/v3 v3.2024123.4 + goauthentik.io/api/v3 v3.2024123.6 golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab golang.org/x/oauth2 v0.26.0 golang.org/x/sync v0.11.0 diff --git a/go.sum b/go.sum index 949be28e57..bf3f192e33 100644 --- a/go.sum +++ b/go.sum @@ -299,8 +299,8 @@ go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -goauthentik.io/api/v3 v3.2024123.4 h1:JYLsUjkJ7kT+jHO72DyFTXFwKEGAcOOlLh36SRG9BDw= -goauthentik.io/api/v3 v3.2024123.4/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw= +goauthentik.io/api/v3 v3.2024123.6 h1:AGOCa7Fc/9eONCPEW4sEhTiyEBvxN57Lfqz1zm6Gy98= +goauthentik.io/api/v3 v3.2024123.6/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= diff --git a/lifecycle/aws/package-lock.json b/lifecycle/aws/package-lock.json index 7f7058b327..98dd6b7c86 100644 --- a/lifecycle/aws/package-lock.json +++ b/lifecycle/aws/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0", "license": "MIT", "devDependencies": { - "aws-cdk": "^2.178.2", + "aws-cdk": "^2.179.0", "cross-env": "^7.0.3" }, "engines": { @@ -17,9 +17,9 @@ } }, "node_modules/aws-cdk": { - "version": "2.178.2", - "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.178.2.tgz", - "integrity": "sha512-ojMCMnBGinvDUD6+BOOlUOB9pjsYXoQdFVbf4bvi3dy3nwn557r0j6qDUcJMeikzPJ6YWzfAdL0fYxBZg4xcOg==", + "version": "2.179.0", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.179.0.tgz", + "integrity": "sha512-aA2+8S2g4UBQHkUEt0mYd16VLt/ucR+QfyUJi34LDKRAhOCNDjPCZ4z9z/JEDyuni0BdzsYA55pnpDN9tMULpA==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/lifecycle/aws/package.json b/lifecycle/aws/package.json index 8d091ee737..4521357683 100644 --- a/lifecycle/aws/package.json +++ b/lifecycle/aws/package.json @@ -10,7 +10,7 @@ "node": ">=20" }, "devDependencies": { - "aws-cdk": "^2.178.2", + "aws-cdk": "^2.179.0", "cross-env": "^7.0.3" } } diff --git a/poetry.lock b/poetry.lock index a772a7c1c2..81c451ddf3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -358,22 +358,6 @@ jsii = ">=1.105.0,<2.0.0" publication = ">=0.0.3" typeguard = ">=2.13.3,<4.3.0" -[[package]] -name = "aws-cdk-asset-kubectl-v20" -version = "2.1.3" -description = "A Lambda Layer that contains kubectl v1.20" -optional = false -python-versions = "~=3.8" -files = [ - {file = "aws_cdk.asset_kubectl_v20-2.1.3-py3-none-any.whl", hash = "sha256:d5612e5bd03c215a28ce53193b1144ecf4e93b3b6779563c046a8a74d83a3979"}, - {file = "aws_cdk_asset_kubectl_v20-2.1.3.tar.gz", hash = "sha256:237cd8530d9e8be0bbc7159af927dbb6b7f91bf3f4099c8ef4d9a213b34264be"}, -] - -[package.dependencies] -jsii = ">=1.103.1,<2.0.0" -publication = ">=0.0.3" -typeguard = ">=2.13.3,<5.0.0" - [[package]] name = "aws-cdk-asset-node-proxy-agent-v6" version = "2.1.0" @@ -408,18 +392,17 @@ typeguard = ">=2.13.3,<4.3.0" [[package]] name = "aws-cdk-lib" -version = "2.178.2" +version = "2.179.0" description = "Version 2 of the AWS Cloud Development Kit library" optional = false python-versions = "~=3.8" files = [ - {file = "aws_cdk_lib-2.178.2-py3-none-any.whl", hash = "sha256:624383e57fe2b32f7d0fc098b78b4cd21d19ae3af3f24b01f32ec4795baaee25"}, - {file = "aws_cdk_lib-2.178.2.tar.gz", hash = "sha256:c00757885b74023350bb34f388f6447155e802ecf827e595bda917098a4925fe"}, + {file = "aws_cdk_lib-2.179.0-py3-none-any.whl", hash = "sha256:1d7b88ee69067b8d58dac9eeb6697bbaf5d5c032a3070898389c41e7c4f3e3d7"}, + {file = "aws_cdk_lib-2.179.0.tar.gz", hash = "sha256:b653a55754f4020a4b36e4ae183d213e76e27b18b842cbf9e430e9eccb700550"}, ] [package.dependencies] "aws-cdk.asset-awscli-v1" = ">=2.2.208,<3.0.0" -"aws-cdk.asset-kubectl-v20" = ">=2.1.3,<3.0.0" "aws-cdk.asset-node-proxy-agent-v6" = ">=2.1.0,<3.0.0" "aws-cdk.cloud-assembly-schema" = ">=39.2.0,<40.0.0" constructs = ">=10.0.0,<11.0.0" @@ -466,13 +449,13 @@ typing-extensions = ">=4.0.0" [[package]] name = "bandit" -version = "1.8.2" +version = "1.8.3" description = "Security oriented static analyser for python code." optional = false python-versions = ">=3.9" files = [ - {file = "bandit-1.8.2-py3-none-any.whl", hash = "sha256:df6146ad73dd30e8cbda4e29689ddda48364e36ff655dbfc86998401fcf1721f"}, - {file = "bandit-1.8.2.tar.gz", hash = "sha256:e00ad5a6bc676c0954669fe13818024d66b70e42cf5adb971480cf3b671e835f"}, + {file = "bandit-1.8.3-py3-none-any.whl", hash = "sha256:28f04dc0d258e1dd0f99dee8eefa13d1cb5e3fde1a5ab0c523971f97b289bcd8"}, + {file = "bandit-1.8.3.tar.gz", hash = "sha256:f5847beb654d309422985c36644649924e0ea4425c76dec2e89110b87506193a"}, ] [package.dependencies] @@ -1884,6 +1867,17 @@ files = [ {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, ] +[[package]] +name = "geographiclib" +version = "2.0" +description = "The geodesic routines from GeographicLib" +optional = false +python-versions = ">=3.7" +files = [ + {file = "geographiclib-2.0-py3-none-any.whl", hash = "sha256:6b7225248e45ff7edcee32becc4e0a1504c606ac5ee163a5656d482e0cd38734"}, + {file = "geographiclib-2.0.tar.gz", hash = "sha256:f7f41c85dc3e1c2d3d935ec86660dc3b2c848c83e17f9a9e51ba9d5146a15859"}, +] + [[package]] name = "geoip2" version = "5.0.1" @@ -1903,6 +1897,29 @@ requests = ">=2.24.0,<3.0.0" [package.extras] test = ["pytest-httpserver (>=1.0.10)"] +[[package]] +name = "geopy" +version = "2.4.1" +description = "Python Geocoding Toolbox" +optional = false +python-versions = ">=3.7" +files = [ + {file = "geopy-2.4.1-py3-none-any.whl", hash = "sha256:ae8b4bc5c1131820f4d75fce9d4aaaca0c85189b3aa5d64c3dcaf5e3b7b882a7"}, + {file = "geopy-2.4.1.tar.gz", hash = "sha256:50283d8e7ad07d89be5cb027338c6365a32044df3ae2556ad3f52f4840b3d0d1"}, +] + +[package.dependencies] +geographiclib = ">=1.52,<3" + +[package.extras] +aiohttp = ["aiohttp"] +dev = ["coverage", "flake8 (>=5.0,<5.1)", "isort (>=5.10.0,<5.11.0)", "pytest (>=3.10)", "pytest-asyncio (>=0.17)", "readme-renderer", "sphinx (<=4.3.2)", "sphinx-issues", "sphinx-rtd-theme (>=0.5.0)"] +dev-docs = ["readme-renderer", "sphinx (<=4.3.2)", "sphinx-issues", "sphinx-rtd-theme (>=0.5.0)"] +dev-lint = ["flake8 (>=5.0,<5.1)", "isort (>=5.10.0,<5.11.0)"] +dev-test = ["coverage", "pytest (>=3.10)", "pytest-asyncio (>=0.17)", "sphinx (<=4.3.2)"] +requests = ["requests (>=2.16.2)", "urllib3 (>=1.24.2)"] +timezone = ["pytz"] + [[package]] name = "google-api-core" version = "2.19.1" @@ -4611,13 +4628,13 @@ websocket-client = ">=1.8,<2.0" [[package]] name = "sentry-sdk" -version = "2.21.0" +version = "2.22.0" description = "Python client for Sentry (https://sentry.io)" optional = false python-versions = ">=3.6" files = [ - {file = "sentry_sdk-2.21.0-py2.py3-none-any.whl", hash = "sha256:7623cfa9e2c8150948a81ca253b8e2bfe4ce0b96ab12f8cd78e3ac9c490fd92f"}, - {file = "sentry_sdk-2.21.0.tar.gz", hash = "sha256:a6d38e0fb35edda191acf80b188ec713c863aaa5ad8d5798decb8671d02077b6"}, + {file = "sentry_sdk-2.22.0-py2.py3-none-any.whl", hash = "sha256:3d791d631a6c97aad4da7074081a57073126c69487560c6f8bffcf586461de66"}, + {file = "sentry_sdk-2.22.0.tar.gz", hash = "sha256:b4bf43bb38f547c84b2eadcefbe389b36ef75f3f38253d7a74d6b928c07ae944"}, ] [package.dependencies] @@ -4661,6 +4678,7 @@ sanic = ["sanic (>=0.8)"] sqlalchemy = ["sqlalchemy (>=1.2)"] starlette = ["starlette (>=0.19.1)"] starlite = ["starlite (>=1.48)"] +statsig = ["statsig (>=0.55.3)"] tornado = ["tornado (>=6)"] unleash = ["UnleashClient (>=6.0.1)"] @@ -5847,4 +5865,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "~3.12" -content-hash = "a3915ac2ef2bb53f7cd67070912cdaf717c3bf73ed972fa337a9b07fce162451" +content-hash = "8a6bfd4833e415a9f4f613ab4f33e60c8332b9f5743583222cdb7190f6286216" diff --git a/pyproject.toml b/pyproject.toml index 2fb4649d4b..e4312479f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -113,6 +113,7 @@ duo-client = "*" fido2 = "*" flower = "*" geoip2 = "*" +geopy = "*" google-api-python-client = "*" gunicorn = "*" gssapi = "*" diff --git a/schema.yml b/schema.yml index ebb6732b40..9896329197 100644 --- a/schema.yml +++ b/schema.yml @@ -39482,6 +39482,7 @@ components: - authentik.providers.ldap - authentik.providers.oauth2 - authentik.providers.proxy + - authentik.providers.rac - authentik.providers.radius - authentik.providers.saml - authentik.providers.scim @@ -39522,7 +39523,6 @@ components: - authentik.enterprise.audit - authentik.enterprise.providers.google_workspace - authentik.enterprise.providers.microsoft_entra - - authentik.enterprise.providers.rac - authentik.enterprise.providers.ssf - authentik.enterprise.stages.authenticator_endpoint_gdtc - authentik.enterprise.stages.source @@ -44006,6 +44006,27 @@ components: items: $ref: '#/components/schemas/DetailedCountryField' readOnly: true + check_history_distance: + type: boolean + history_max_distance_km: + type: integer + maximum: 9223372036854775807 + minimum: 0 + format: int64 + distance_tolerance_km: + type: integer + maximum: 2147483647 + minimum: 0 + history_login_count: + type: integer + maximum: 2147483647 + minimum: 0 + check_impossible_travel: + type: boolean + impossible_tolerance_km: + type: integer + maximum: 2147483647 + minimum: 0 required: - bound_to - component @@ -44038,6 +44059,27 @@ components: items: $ref: '#/components/schemas/CountryCodeEnum' maxItems: 249 + check_history_distance: + type: boolean + history_max_distance_km: + type: integer + maximum: 9223372036854775807 + minimum: 0 + format: int64 + distance_tolerance_km: + type: integer + maximum: 2147483647 + minimum: 0 + history_login_count: + type: integer + maximum: 2147483647 + minimum: 0 + check_impossible_travel: + type: boolean + impossible_tolerance_km: + type: integer + maximum: 2147483647 + minimum: 0 required: - countries - name @@ -46583,6 +46625,9 @@ components: - authentik_providers_oauth2.scopemapping - authentik_providers_oauth2.oauth2provider - authentik_providers_proxy.proxyprovider + - authentik_providers_rac.racprovider + - authentik_providers_rac.endpoint + - authentik_providers_rac.racpropertymapping - authentik_providers_radius.radiusprovider - authentik_providers_radius.radiusproviderpropertymapping - authentik_providers_saml.samlprovider @@ -46652,9 +46697,6 @@ components: - authentik_providers_google_workspace.googleworkspaceprovidermapping - authentik_providers_microsoft_entra.microsoftentraprovider - authentik_providers_microsoft_entra.microsoftentraprovidermapping - - authentik_providers_rac.racprovider - - authentik_providers_rac.endpoint - - authentik_providers_rac.racpropertymapping - authentik_providers_ssf.ssfprovider - authentik_stages_authenticator_endpoint_gdtc.authenticatorendpointgdtcstage - authentik_stages_source.sourcestage @@ -50557,6 +50599,27 @@ components: items: $ref: '#/components/schemas/CountryCodeEnum' maxItems: 249 + check_history_distance: + type: boolean + history_max_distance_km: + type: integer + maximum: 9223372036854775807 + minimum: 0 + format: int64 + distance_tolerance_km: + type: integer + maximum: 2147483647 + minimum: 0 + history_login_count: + type: integer + maximum: 2147483647 + minimum: 0 + check_impossible_travel: + type: boolean + impossible_tolerance_km: + type: integer + maximum: 2147483647 + minimum: 0 PatchedGoogleWorkspaceProviderMappingRequest: type: object description: GoogleWorkspaceProviderMapping Serializer diff --git a/web/build.mjs b/web/build.mjs index 65d36a7d80..f9ca7b346c 100644 --- a/web/build.mjs +++ b/web/build.mjs @@ -74,7 +74,7 @@ const interfaces = [ ["user/UserInterface.ts", "user"], ["flow/FlowInterface.ts", "flow"], ["standalone/api-browser/index.ts", "standalone/api-browser"], - ["enterprise/rac/index.ts", "enterprise/rac"], + ["rac/index.ts", "rac"], ["standalone/loading/index.ts", "standalone/loading"], ["polyfill/poly.ts", "."], ]; diff --git a/web/package-lock.json b/web/package-lock.json index 5f0c10b660..967432ec77 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -23,7 +23,7 @@ "@floating-ui/dom": "^1.6.11", "@formatjs/intl-listformat": "^7.5.7", "@fortawesome/fontawesome-free": "^6.6.0", - "@goauthentik/api": "^2024.12.3-1739801838", + "@goauthentik/api": "^2024.12.3-1739965710", "@lit-labs/ssr": "^3.2.2", "@lit/context": "^1.1.2", "@lit/localize": "^0.12.2", @@ -1814,9 +1814,9 @@ } }, "node_modules/@goauthentik/api": { - "version": "2024.12.3-1739801838", - "resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2024.12.3-1739801838.tgz", - "integrity": "sha512-gK+chjueX2MbHyjeYczx+plapSRmzaTkZDH6PFwulcvxIFb3awfMkw4cWDjwHZunfDgu8EoP50cPlF3AA/PNoQ==" + "version": "2024.12.3-1739965710", + "resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2024.12.3-1739965710.tgz", + "integrity": "sha512-16zoQWeJhAFSwttvqLRoXoQA43tMW1ZXDEihW6r8rtWtlxqPh7n36RtcWYraYiLcjmJskI90zdgz6k1kmY5AXw==" }, "node_modules/@goauthentik/web": { "resolved": "", diff --git a/web/package.json b/web/package.json index 801479e3ec..864a8bfaa2 100644 --- a/web/package.json +++ b/web/package.json @@ -11,7 +11,7 @@ "@floating-ui/dom": "^1.6.11", "@formatjs/intl-listformat": "^7.5.7", "@fortawesome/fontawesome-free": "^6.6.0", - "@goauthentik/api": "^2024.12.3-1739801838", + "@goauthentik/api": "^2024.12.3-1739965710", "@lit-labs/ssr": "^3.2.2", "@lit/context": "^1.1.2", "@lit/localize": "^0.12.2", diff --git a/web/scripts/knip.config.ts b/web/scripts/knip.config.ts index a923bd63ae..b2a1ac3f11 100644 --- a/web/scripts/knip.config.ts +++ b/web/scripts/knip.config.ts @@ -6,7 +6,7 @@ const config: KnipConfig = { "./src/user/UserInterface.ts", "./src/flow/FlowInterface.ts", "./src/standalone/api-browser/index.ts", - "./src/enterprise/rac/index.ts", + "./src/rac/index.ts", "./src/standalone/loading/index.ts", "./src/polyfill/poly.ts", ], diff --git a/web/src/admin/policies/geoip/GeoIPPolicyForm.ts b/web/src/admin/policies/geoip/GeoIPPolicyForm.ts index 440323a430..1b5119ae2b 100644 --- a/web/src/admin/policies/geoip/GeoIPPolicyForm.ts +++ b/web/src/admin/policies/geoip/GeoIPPolicyForm.ts @@ -1,5 +1,6 @@ import { BasePolicyForm } from "@goauthentik/admin/policies/BasePolicyForm"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; +import { first } from "@goauthentik/common/utils"; import "@goauthentik/elements/ak-dual-select"; import { DataProvision, DualSelectPair } from "@goauthentik/elements/ak-dual-select/types"; import "@goauthentik/elements/forms/FormGroup"; @@ -46,7 +47,7 @@ export class GeoIPPolicyForm extends BasePolicyForm { } renderForm(): TemplateResult { - return html` + return html` ${msg( "Ensure the user satisfies requirements of geography or network topology, based on IP address. If any of the configured values match, the policy passes.", )} @@ -79,13 +80,125 @@ export class GeoIPPolicyForm extends BasePolicyForm { )}

- - ${msg("Policy-specific settings")} + + ${msg("Distance settings")} +
+ + +

+ ${msg( + "When this option enabled, the GeoIP data of the policy request is compared to the specified number of historical logins.", + )} +

+
+ + +

+ ${msg("Tolerance in checking for distances in kilometers.")} +

+
+ + +

+ ${msg("Amount of previous login events to check against.")} +

+
+ + +

+ ${msg( + "Maximum distance a login attempt is allowed from in kilometers.", + )} +

+
+
+
+ + ${msg("Distance settings (Impossible travel)")} +
+ + +

+ ${msg( + "When this option enabled, the GeoIP data of the policy request is compared to the specified number of historical logins and if the travel would have been possible in the amount of time since the previous event.", + )} +

+
+ + +

+ ${msg("Tolerance in checking for distances in kilometers.")} +

+
+
+
+ + ${msg("Static rule settings")}
Enterprise Preview** Add support for Shared Signals Framework + TODO: Add preview banner to UI +- **RAC moved open source** Remote access is now available to everyone! +- **GeoIP distance and impossible travel checks** Add the ability to check for the distance a user has moved compared to a previous login, and if the user could have travelled the distance +- **Email OTP Stage** Allow users to use their email accounts as a one-time-password during authentication +- **Fine-grained permission for superuser toggle on groups** Setting the **Is superuser** toggle on a group now requires a separate permission. + +## Breaking changes + +- **Deprecated and frozen `:latest` container image tag after 2025.2** + + Using the `:latest` tag with container images is not recommended as it can lead to unintentional updates and potentially broken setups. + + The tag will not be removed, however it will also not be updated past 2025.2. + + We strongly recommended the use of a specific version tag for authentik instances' container images like `:2025.2`. + +## New features + +- SSF Provider Enterprise Preview + + [Shared Signals Framework](#todo) allows applications to register a stream with authentik within which they can received events from authentik such as when a session was revoked or a credential was add/changed/deleted and execute actions based on these events. + + This allows admins to integrate authentik with Apple Business/School Manager for federated Apple IDs. See the integration docs [here](#todo) + +- RAC to open source + + Remote access (RDP, VNC and SSH) has moved from enterprise to our free, open source code. We try our best to limit enterprise-specific functionality to features that would be non-essential to homelab users and far more valuable to enterprise use cases. We've had a variety of homelab users reach out with excellent use cases for RAC functionality, so while this will mean giving up some potential revenue, we think that opening up RAC to the community is the right thing to do! + +- GeoIP distance and impossible travel checks + + Add the ability to check for the distance a user has moved compared to a previous login, and add the option to check impossible travel distances based on client IP. + + These options can be used to detect and prevent access from potentially stolen authentik sessions or stolen devices. + +- Email OTP Stage + + Admins now have the ability to configure the option for users to use their email as an authenticator. Users that already have an email address set on their account will be able to use that address to receive one-time-passwords. It is also possible to configure authentik to allow users to add additional email addresses as authenticators. + + See [Email OTP Stage](#todo) + +- Application Wizard is the default way to create applications + + The default way of creating an application now allows admins to configure the provider and any kind of bindings without having to jump through different sections of the UI. The previous way of creating an application is and will stay available alongside the new and streamlined method. + +- Fine-grained permission for superuser toggle on groups + + Setting the **Is superuser** toggle on a group now requires a separate permission, making it much easier to allow for delegated management of groups without risking the ability for users to self-elevate permissions. + +- Improved debugging experience + + For people developing authentik or building very complex, custom integrations, configuring debugging in authentik is now documented [here](#todo) + +## TODO + +temp + +## Upgrading + +This release does not introduce any new requirements. You can follow the upgrade instructions below; for more detailed information about upgrading authentik, refer to our [Upgrade documentation](../../install-config/upgrade.mdx). + +:::warning +When you upgrade, be aware that the version of the authentik instance and of any outposts must be the same. We recommended that you always upgrade any outposts at the same time you upgrade your authentik instance. +::: + +### Docker Compose + +To upgrade, download the new docker-compose file and update the Docker stack with the new version, using these commands: + +```shell +wget -O docker-compose.yml https://goauthentik.io/version/2025.2/docker-compose.yml +docker compose up -d +``` + +The `-O` flag retains the downloaded file's name, overwriting any existing local file with the same name. + +### Kubernetes + +Upgrade the Helm Chart to the new version, using the following commands: + +```shell +helm repo update +helm upgrade authentik authentik/authentik -f values.yaml --version ^2025.2 +``` + +## Minor changes/fixes + +- admin: monitor worker version (#12463) +- api: cleanup owner permissions (#12598) +- blueprints: add REPL for blueprint YAML tags (#9223) +- blueprints: fix schema for meta models (#12421) +- core: add indexes on ExpiringModel (#12658) +- core: fix application entitlements not creatable with blueprints (#12673) +- core: fix error when creating new user with default path (#12609) +- core: fix generic sources not being fetchable by pk (#12896) +- core: fix permissions for admin device listing (#12787) +- core: search users' attributes (#12740) +- core: show last password change date (#12958) +- enterprise/providers: SSF (#12327) +- enterprise/providers/SSF: fix a couple of bugs after real world testing (#12987) +- enterprise/rac: Improve client connection status & bugfixes (#12684) +- events: make sure password set event has the correct IP (#12585) +- events: notification_cleanup: avoid unnecessary loop (#12417) +- flows: clear flow state before redirecting to final URL (#12788) +- flows: fix history containing other plans (#12655) +- flows: fix inspector permission check (#12907) +- flows: more tests (#11587) +- flows: show policy messages in reevaluate marker (#12855) +- flows/inspector: add button to open flow inspector (#12656) +- internal: fix missing trailing slash in outpost websocket (#12470) +- internal: fix URL generation for websocket connection (#12439) +- lifecycle: update python to 3.12.8 (#12783) +- lifecycle/migrate: don't migrate tenants if not enabled (#12850) +- outposts: fix version label (#12486) +- providers/oauth2: include scope in token response (#12921) +- providers/oauth2: support token revocation for public clients (#12704) +- providers/saml: fix handle Accept: application/xml for SAML Metadata endpoint (#12483) (#12518) +- providers/saml: fix invalid SAML Response when assertion and response are signed (#12611) +- providers/saml: provide generic metadata url when possible (#12413) +- rbac: exclude permissions for internal models (#12803) +- rbac: permissions endpoint: allow authenticated users (#12608) +- root: backport version bump (#12426) +- root: docker: ensure apt packages are up-to-date (#12683) +- root: expose CONN_MAX_AGE, CONN_HEALTH_CHECKS and DISABLE_SERVER_SIDE_CURSORS for PostgreSQL config (#10159) +- root: fix dev build version being invalid semver (#12472) +- root: redis, make sure tlscacert isn't an empty string (#12407) +- sources: allow uuid or slug to be used for retrieving a source (#12780) +- sources: allow uuid or slug to be used for retrieving a source (2024.12 fix) (#12772) +- sources/kerberos: authenticate with the user's username instead of the first username in authentik (#12497) +- sources/kerberos: handle principal expire time (#12748) +- sources/oauth: fix authentication only being sent in form body (#12713) +- sources/scim: fix user creation (duplicate userName) (#12547) +- stages/authenticator: add user field to devices (#12636) +- stages/prompt: always show policy messages (#12765) +- stages/redirect: fix query parameter when redirecting to flow (#12750) +- web, core: fix grammatical issue in stage bindings (#10799) +- web: fix build dev build (#12473) +- web: fix error handling bug in ApplicationWizard.RACProviderForm (#12640) +- web: Fix issue where Codemirror partially applies OneDark theme. (#12811) +- web: fix mobile scrolling bug (#12601) +- web: fix source selection and outpost integration health (#12530) +- web: fix source selection and outpost integration health (#12530) +- web: fixes broken docLinks - url missing s (#12789) +- web: housekeeping, optimizations and small fixes (#12450) +- web: improve notification and API drawers (#12659) +- web: misc fixes for admin and flow inspector (#12461) +- web: only load version context when authenticated (#12482) +- web: update gen-client-ts to OpenAPI 7.11.0 (#12756) +- web/admin: fix role changelog missing primary key filter (#12671) +- web/admin: improve user display view (#12988) +- web/admin: more cleanup and consistency (#12657) +- web/admin: Refine navigation (#12441) +- web/components: ak-number-input: add support for min (#12703) +- web/flows: fix `login` / `log in` inconsistency (#12526) + +## API Changes + + diff --git a/website/docs/users-sources/sources/social-logins/discord/index.md b/website/docs/users-sources/sources/social-logins/discord/index.md index 8f93765297..dab8fdd60d 100644 --- a/website/docs/users-sources/sources/social-logins/discord/index.md +++ b/website/docs/users-sources/sources/social-logins/discord/index.md @@ -22,7 +22,7 @@ The following placeholders are used in this guide: ![Name App](./discord2.png) -3. Select **OAuth2** from the left Menu +3. Select **OAuth2** from the left menu 4. Copy the **Client ID** and _save it for later_ @@ -38,8 +38,8 @@ Here is an example of a completed OAuth2 screen for Discord. 8. Under _Directory -> Federation & Social login_ Click **Create Discord OAuth Source** -9. **Name:** Choose a name (For the example I used Discord) -10. **Slug:** discord (You can choose a different slug, if you do you will need to update the Discord redirect URLand point it to the correct slug.) +9. **Name:** Choose a name (For the example I used `Discord`) +10. **Slug:** discord (You can choose a different slug, if you do you will need to update the Discord redirect URL and point it to the correct slug.) 11. **Consumer Key:** Client ID from step 4 12. **Consumer Secret:** Client Secret from step 5 diff --git a/website/integrations/services/adventurelog/index.md b/website/integrations/services/adventurelog/index.md new file mode 100644 index 0000000000..50cfa5193c --- /dev/null +++ b/website/integrations/services/adventurelog/index.md @@ -0,0 +1,100 @@ +--- +title: Integrate with AdventureLog +sidebar_label: AdventureLog +--- + +# Integrate with AdventureLog + +Support level: Community + +## What is AdventureLog + +> AdventureLog is a self-hosted travel tracker and trip planner. AdventureLog is the ultimate travel companion for the modern-day explorer. +> +> -- https://adventurelog.app/ + +## Preparation + +The following placeholders are used in this guide: + +- `https://adventurelog.company` is the FQDN of the AdventureLog server installation. +- `https://authentik.company` is the FQDN of the authentik installation. + +:::note +This documentation lists only the settings that you need to change from their default values. Be aware that any changes other than those explicitly mentioned in this guide could cause issues accessing your application. +::: + +## authentik configuration + +1. Create a new OAuth2/OpenID Provider under **Applications** > **Providers** using the following settings: + - **Name**: AdventureLog + - **Authentication flow**: default-authentication-flow + - **Authorization flow**: default-provider-authorization-explicit-consent + - **Client type**: Confidential + - **Client ID**: Either create your own Client ID or use the auto-populated ID + - **Client Secret**: Either create your own Client Secret or use the auto-populated secret + :::note + Take note of the `Client ID` and `Client Secret` as they are required when configuring AdventureLog. + ::: + - **Redirect URIs/Origins (RegEx)**: + :::note + Make sure type is set to `RegEx` and the following RegEx is used. + ::: + - `^https://adventurelog.company/accounts/oidc/.*$` + - **Signing Key**: authentik Self-signed Certificate + - Leave everything else as default +2. Open the new provider you've just created. +3. Make a note of the **OpenID Configuration Issuer**. +4. Navigate to **Applications -> Applications** and create a new application that uses the provider you just created. + +## AdventureLog configuration + +AdventureLog documentation can be found here: https://adventurelog.app/docs/configuration/social_auth/authentik.html + +This configuration is done in the Admin Panel. Launch the panel by clicking your user avatar in the navbar, selecting **Settings**, and then clicking **Launch Admin Panel**. Make sure you are logged in as an administrator for this to work. + +Alternatively, navigate to `/admin` on your AdventureLog server. + +1. In the admin panel, scroll down to the **Social Accounts** section and click **Add** next to **Social applications**. Fill in the following fields: + + - Provider: OpenID Connect + - Provider ID: authentik Client ID + - Name: authentik + - Client ID: authentik Client ID + - Secret Key: authentik Client Secret + - Key: _should be left blank_ + - Settings: (make sure http/https is set correctly) + + ```json + { + "server_url": "https://authentik.company/application/o/[YOUR_SLUG]/" + } + ``` + + - Sites: move over the sites you want to enable authentik on, usually `example.com` and `www.example.com` unless you renamed your sites. + +:::warning +`localhost` is most likely not a valid `server_url` for authentik in this instance because `localhost` is the server running AdventureLog, not authentik. You should use the IP address of the server running authentik or the domain name if you have one. +::: + +2. Save the configuration. + +Ensure that the authentik server is running and accessible by AdventureLog. Users should now be able to log in to AdventureLog using their authentik account. + +## Configuration validation + +To validate the configuration, either link to an existing account as described below or naviage to the AdventureLog login page and click the **authentik** button to log in. You should be redirected to the authentik login page. After logging in, you should be redirected back to AdventureLog. + +### Linking to Existing Account + +If a user has an existing AdventureLog account and wants to link it to their authentik account, they can do so by logging in to their AdventureLog account and navigating to the **Settings** page. There is a button that says **Launch Account Connections**, click that and then choose the provider to link to the existing account. + +## Troubleshooting + +### 404 error when logging in. + +Ensure the `https://adventurelog.company/accounts` path is routed to the backend, as it shouldn't hit the frontend when it's properly configured. For information on how to configure this, refer to the AdventureLog documentation on reverse proxy configuration [here](https://adventurelog.app/docs/install/getting_started.html). + +### authentik - No Permission + +Launch your authentik dashboard as an admin and find the AdventureLog app. Click **More details** then **Edit**. In the admin interface, click **Test** under **Check Access**. If you get a 403 error, you need to grant the user the correct permissions. This can be done by going to the user's profile and adding the correct permissions. diff --git a/website/integrations/services/engomo/index.md b/website/integrations/services/engomo/index.md deleted file mode 100644 index 2bd066da3d..0000000000 --- a/website/integrations/services/engomo/index.md +++ /dev/null @@ -1,95 +0,0 @@ ---- -title: Integrate with engomo -sidebar_label: engomo ---- - -# Integrate with engomo - -Support level: Community - -## What is engomo - -> engomo is an low-code app development platform to create enterprise apps for smartphones and tablets based on Android, iOS, or iPadOS. -> -- https://engomo.com/ -> -> This guide explains how to set up engomo to use authentik as the OAuth provider for the application login on the smartphone/tablet and login to the admin WebGUI (composer). - -## Preparation - -The following placeholders are used in this guide: - -- `engomo.company` is the FQDN of the engomo installation. -- `authentik.company` is the FQDN of the authentik installation. -- `engomo.mapping` is the name of the Scope Mapping. -- `ak.cert` is the self-signed certificate that will be used for the service provider. - -:::note -This documentation lists only the settings that you need to change from their default values. Be aware that any changes other than those explicitly mentioned in this guide could cause issues accessing your application. -::: - -## authentik configuration - -In authentik, create a new scope mapping. To do so, log in and navigate to the Admin interface, then go to **Customization --> Property Mapping** and click **Create**. - -- `engomo.mapping` is the value of the Mapping's name. -- `profile` is the value for the Scope name. -- `return {"preferred_username": request.user.email}` is the value for the Expression. - -Create an application and an OAuth2/OpenID provider in authentik. Use the following parameters for the OAuth2/OpenID provider: - -**Provider:** - -- Name: `SP-engomo` -- Client type: `Public` -- Redirect URIs/Origins (RegEx): `https://engomo.company/auth` and `com.engomo.engomo://callback/` -- Signing Key: `ak.cert` -- Scopes: `authentik default OAuth Mapping: OpenID 'email', 'offline_access', OpenID 'openid'` and `engomo.mapping` - -> [!IMPORTANT] -> Redirect URIs => write the values line by line. - -Leave the rest as default values. The durations can be changed as needed. - -**Application:** - -- Name: `engomo` -- Slug: `engomo` -- Launch URL: `https://engomo.company/` - -## engomo configuration - -Navigate to `https://engomo.company/composer` and log in with your admin credentials. - -- Select `Server`. -- Select `Authentication`. -- Add a new authentication method by clicking on the plus icon on the right. -- Name: `authentik` -- Type: `OpenID Connect` -- Click **Create**. -- Set the `Issuer` to the authentik FQDN `https://authentik.company/application/o/engomo`. -- Set the `Client ID` to the Client ID from the SP-engomo provider that you created in authentik. -- Set the `Client Secret` to the Client Secret from the SP-engomo provider that you created in authentik. - -Leave the rest as default. - -## engomo user creation - -engomo doesn't create users automatically when signing in. So you have to do it manually right now. -Navigate to `https://engomo.company/composer` and log in with your admin credentials. - -- Select `Users & Devices`. -- Click the plus button next in the Users section. -- Select `authentik` as the Authenticator in the dropdown. -- Create your user by typing in the email as the Username used in authentik. - -At this point you are done. - -## Test the login - -- Open a browser of your choice and open the URL `https://engomo.company`. -- Enter the created user's email address and click the small arrow icon to log in. -- You should be redirected to authentik (with the login flows you created) and then authentik should redirect you back to `https://engomo.company/composer` URL. -- If you are redirected back to the `https://engomo.company/composer` URL you did everything correct. - -> [!IMPORTANT] -> The created user will only have access to the app or composer page if you granted the permission to the user of course. diff --git a/website/integrations/services/engomo/index.mdx b/website/integrations/services/engomo/index.mdx new file mode 100644 index 0000000000..f7327c4c11 --- /dev/null +++ b/website/integrations/services/engomo/index.mdx @@ -0,0 +1,87 @@ +--- +title: Integrate with engomo +sidebar_label: engomo +--- + +# Integrate with engomo + +Support level: Community + +## What is engomo + +> engomo is an low-code app development platform to create enterprise apps for smartphones and tablets based on Android, iOS, or iPadOS. +> +> -- https://engomo.com/ +> +> This guide explains how to set up engomo to use authentik as the OAuth provider for the application login on the smartphone/tablet and login to the admin WebGUI (composer). + +## Preparation + +The following placeholders are used in this guide: + +- `engomo.company` is the FQDN of the engomo installation. +- `authentik.company` is the FQDN of the authentik installation. +- `engomo.mapping` is the name of the Scope Mapping. + +:::note +This documentation lists only the settings that you need to change from their default values. Be aware that any changes other than those explicitly mentioned in this guide could cause issues accessing your application. +::: + +## authentik configuration + +In authentik, create a new scope mapping. To do so, log in and navigate to the Admin interface, then go to **Customization --> Property Mapping** and click **Create**. + +- `engomo.mapping` is the value of the Mapping's name. +- `profile` is the value for the Scope name. +- `return {"preferred_username": request.user.email}` is the value for the Expression. + +[Create](https://docs.goauthentik.io/docs/add-secure-apps/applications/manage_apps#add-new-applications) an OAuth2/OpenID provider and an application in authentik. Use the following parameters for the OAuth2/OpenID provider: + +1. In the authentik Admin interface, navigate to **Applications** -> **Applications**. +2. Use the wizard to create a new application and provider. During this process: + - Note the **Client ID**, **Client Secret**, and **slug** values for later use. + - Select implicit or explicit authorization flow as desired. + - Set Client type to `Public`. + - Set the redirect URI to https://engomo.company/auth and com.engomo.engomo://callback/. + - Select any available signing key. + - Add the `engomo.mapping` scope in addition to the default values. + +:::note +Redirect URIs => write the values line by line. +::: + +## engomo configuration + +Navigate to https://engomo.company/composer and log in with your admin credentials. + +1. Select **Server**. +2. Select **Authentication**. +3. Add a new authentication method by clicking on the plus icon on the right. +4. Name: `authentik` +5. Type: **OpenID Connect** +6. Click **Create**. +7. Configure the following values using information from the authentik provider: + - Set **Issuer** to https://authentik.company/application/o/engomo. + - Set **Client ID** to the Client ID copied from authentik. + - Set **Client secret** to the Client Secret copied from authentik. + +## engomo user creation + +engomo doesn't create users automatically when signing in. So you have to do it manually right now. +Navigate to https://engomo.company/composer and log in with your admin credentials. + +- Select **Users & Devices**. +- Click the plus button in the Users section. +- Choose `authentik` from the Authenticator dropdown. +- Create your user by entering the email address as the username. This email must match the one used for the user in authentik. + +## Test the login + +- Open a browser of your choice and open the URL https://engomo.company. +- Enter the created user's email address and click the small arrow icon to log in. +- You should be redirected to authentik (with the login flows you created) and then authentik should redirect you back to https://engomo.company/composer URL. +- If you are redirected back to the https://engomo.company/composer URL you did everything correct. + +:::note +The created user will only have access to the app or composer page if they have been granted the necessary permissions. +::: diff --git a/website/integrations/services/rustdesk-pro/index.mdx b/website/integrations/services/rustdesk-pro/index.mdx index 83c4a6b5f9..80c8306a7e 100644 --- a/website/integrations/services/rustdesk-pro/index.mdx +++ b/website/integrations/services/rustdesk-pro/index.mdx @@ -14,6 +14,8 @@ sidebar_label: RustDesk Server Pro > Ideal for businesses, it provides full control over data while ensuring scalable and reliable remote access. > > -- https://rustdesk.com/ +> +> This guide explains how to configure Rustdesk Server Pro to use authentik as the OAuth provider for logging in to the Web GUI. ## Preparation @@ -28,31 +30,30 @@ This documentation lists only the settings that you need to change from their de ## authentik configuration +[Create](https://docs.goauthentik.io/docs/add-secure-apps/applications/manage_apps#add-new-applications) an OAuth2/OpenID provider and an application in authentik. Use the following parameters for the OAuth2/OpenID provider: + 1. In the authentik Admin interface, navigate to **Applications** -> **Applications**. 2. Use the wizard to create a new application and provider. During this process: - Note the **Client ID**, **Client Secret**, and **slug** values for later use. - - Set the redirect URI to https://_rustdesk.company_/api/oidc/callback. + - Select implicit or explicit authorization flow as desired. + - Set the redirect URI to https://rustdesk.company/api/oidc/callback. - Select any available signing key. ## RustDesk Server Pro configuration 1. Sign in to RustDesk Server Pro using a browser. - 2. In the left menu, select **Settings** and then **OIDC**. - 3. Click **+ New Auth Provider**. - 4. In the popup window, select **custom** as the **Auth Type** and click **OK**. - 5. Configure the following values using information from the authentik provider: - - **Name**: _SSO-Login_ - - **Client ID**: _client-id_ - - **Client Secret**: _client-secret_ - - **Issuer**: https://_authentik.company_/application/o/_slug_/ - - **Authorization Endpoint**: https://_authentik.company_/application/o/authorize/ - - **Token Endpoint**: https://_authentik.company_/application/o/token/ - - **Userinfo Endpoint**: https://_authentik.company_/application/o/userinfo/ - - **JWKS Endpoint**: https://_authentik.company_/application/o/_slug_/jwks/ + - Set **Name** to `authentik` + - Set **Client ID** to the Client ID copied from authentik. + - Set **Client secret** to the Client Secret copied from authentik. + - Set **Issuer** to https://authentik.company/application/o/slug/ + - Set **Authorization Endpoint** to https://authentik.company/application/o/authorize/ + - Set **Token Endpoint** to https://authentik.company/application/o/token/ + - Set **Userinfo Endpoint** to https://authentik.company/application/o/userinfo/ + - Set **JWKS Endpoint** to https://authentik.company/application/o/slug/jwks/ :::info Users are created automatically on login. Permissions must be assigned by an administrator after user creation. @@ -60,7 +61,7 @@ Users are created automatically on login. Permissions must be assigned by an adm ## Test the Login -- Open a browser and navigate to https://_rustdesk.company_. -- Click **Continue with SSO-Login**. -- You should be redirected to authentik (with the login flows you configured). After logging in, authentik will redirect you back to https://_rustdesk.company_. -- If you are redirected back to https://_rustdesk.company_ and can read the username in the top right corner, the setup was successful. +- Open a browser and navigate to https://rustdesk.company. +- Click **Continue with authentik**. +- You should be redirected to authentik (with the login flows you configured). After logging in, authentik will redirect you back to https://rustdesk.company. +- If you are redirected back to https://rustdesk.company and can read the username in the top right corner, the setup was successful. diff --git a/website/integrations/services/semaphore/index.mdx b/website/integrations/services/semaphore/index.mdx index c43a21634f..37ab8df3b2 100644 --- a/website/integrations/services/semaphore/index.mdx +++ b/website/integrations/services/semaphore/index.mdx @@ -28,27 +28,14 @@ This documentation lists only the settings that you need to change from their de ## authentik configuration -Start the wizard for adding a new application. +[Create](https://docs.goauthentik.io/docs/add-secure-apps/applications/manage_apps#add-new-applications) an OAuth2/OpenID provider and an application in authentik. Use the following parameters for the OAuth2/OpenID provider: -**1. Application:** - -- Name: `Semaphore UI` -- Slug: `semaphore` - -**2. Choose a Provider** - -Select `OAuth2/OpenID Provider` - -**3. Configure Provider** - -Select implicit or explicit authorization flow as desired. - -Take note of the Client ID and Client Secret, you'll need to give them to Semaphore UI later. - -- Redirect URIs/Origins (RegEx): `https://semaphore.company/api/auth/oidc/authentik/redirect/` -- Signing Key: `authentik Self-signed Certificate` - -Leave the rest as default values. +1. In the authentik Admin interface, navigate to **Applications** -> **Applications**. +2. Use the wizard to create a new application and provider. During this process: + - Note the **Client ID**, **Client Secret**, and **slug** values for later use. + - Select implicit or explicit authorization flow as desired. + - Set the redirect URI to https://semaphore.company/api/auth/oidc/authentik/redirect/. + - Select any available signing key. ## Semaphore UI configuration @@ -60,7 +47,7 @@ Add the `oidc_providers` configuration: { "oidc_providers": { "authentik": { - "display_name": "Sign in with Authentik", + "display_name": "Sign in with authentik", "provider_url": "https://authentik.company/application/o//", "client_id": "", "client_secret": "", @@ -89,14 +76,12 @@ SEMAPHORE_WEB_ROOT: / More information on this can be found in the Semaphore documentation https://docs.semaphoreui.com/administration-guide/openid/authentik/. -Leave the rest as default. - ## Test the login -- Open a browser of your choice and open the URL `https://semaphore.company`. +- Open a browser of your choice and open the URL https://semaphore.company. - Click on the SSO-Login button. -- You should be redirected to authentik (with the login flows you created) and then authentik should redirect you back to `https://semaphore.company` URL. -- If you are redirected back to the `https://semaphore.company` URL you did everything correct. +- You should be redirected to authentik (with the login flows you created) and then authentik should redirect you back to https://semaphore.company URL. +- If you are redirected back to the https://semaphore.company URL you did everything correct. :::info Users are created upon logging in with authentik. They will not have the rights to create anything initially. These permissions must be assigned later by the local admin created during the first login to the Semaphore UI. diff --git a/website/sidebarsIntegrations.js b/website/sidebarsIntegrations.js index 16df957bfb..7c6b9ed4c5 100644 --- a/website/sidebarsIntegrations.js +++ b/website/sidebarsIntegrations.js @@ -123,6 +123,7 @@ module.exports = { label: "Miscellaneous", items: [ "services/actual-budget/index", + "services/adventurelog/index", "services/engomo/index", "services/frappe/index", "services/freshrss/index",