sources: prevent deletion of built-in source (#12914)

* web: sources: disable "delete" button for built-in source

* poetry doesn't like that I use python 3.13 / implement check on backend too

* fix ruff i think

Signed-off-by: Dominic R <git@sdko.org>

* nvm

Signed-off-by: Dominic R <git@sdko.org>

* reformat

* check by managed attribute

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* like this?

---------

Signed-off-by: Dominic R <git@sdko.org>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Dominic R <git@sdko.org>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Dominic R
2025-03-14 13:39:09 -04:00
committed by GitHub
parent 75eb025ef4
commit a3f86115e1
4 changed files with 20 additions and 3 deletions

View File

@ -5,6 +5,7 @@ from collections.abc import Iterable
from drf_spectacular.utils import OpenApiResponse, extend_schema
from rest_framework import mixins
from rest_framework.decorators import action
from rest_framework.exceptions import ValidationError
from rest_framework.fields import CharField, ReadOnlyField, SerializerMethodField
from rest_framework.parsers import MultiPartParser
from rest_framework.request import Request
@ -154,6 +155,17 @@ class SourceViewSet(
matching_sources.append(source_settings.validated_data)
return Response(matching_sources)
def destroy(self, request: Request, *args, **kwargs):
"""Prevent deletion of built-in sources"""
instance: Source = self.get_object()
if instance.managed == Source.MANAGED_INBUILT:
raise ValidationError(
{"detail": "Built-in sources cannot be deleted"}, code="protected"
)
return super().destroy(request, *args, **kwargs)
class UserSourceConnectionSerializer(SourceSerializer):
"""User source connection"""

View File

@ -32,5 +32,5 @@ class AuthentikCoreConfig(ManagedAppConfig):
"name": "authentik Built-in",
"slug": "authentik-built-in",
},
managed="goauthentik.io/sources/inbuilt",
managed=Source.MANAGED_INBUILT,
)

View File

@ -678,6 +678,8 @@ class SourceGroupMatchingModes(models.TextChoices):
class Source(ManagedModel, SerializerModel, PolicyBindingModel):
"""Base Authentication source, i.e. an OAuth Provider, SAML Remote or LDAP Server"""
MANAGED_INBUILT = "goauthentik.io/sources/inbuilt"
name = models.TextField(help_text=_("Source's display Name."))
slug = models.SlugField(help_text=_("Internal source name, used in URLs."), unique=True)

View File

@ -57,10 +57,13 @@ export class SourceListPage extends TablePage<Source> {
}
renderToolbarSelected(): TemplateResult {
const disabled = this.selectedElements.length < 1;
const disabled =
this.selectedElements.length < 1 ||
this.selectedElements.some((item) => item.component === "");
const nonBuiltInSources = this.selectedElements.filter((item) => item.component !== "");
return html`<ak-forms-delete-bulk
objectLabel=${msg("Source(s)")}
.objects=${this.selectedElements}
.objects=${nonBuiltInSources}
.usedBy=${(item: Source) => {
return new SourcesApi(DEFAULT_CONFIG).sourcesAllUsedByList({
slug: item.slug,