Merge branch 'main' into dev
* main: (38 commits) crypto: fix race conditions when creating self-signed certificates on startup (#7344) blueprints: fix entries with state: absent not being deleted if their serializer has errors (#7345) web/admin: fix @change handler for ak-radio elements (#7348) rbac: handle lookup error (#7341) website/docs: add warning about upgrading to 2023.10 (#7340) web/admin: fix role form reacting to enter (#7330) core: bump github.com/google/uuid from 1.3.1 to 1.4.0 (#7333) core: bump goauthentik.io/api/v3 from 3.2023083.10 to 3.2023101.1 (#7334) core: bump ruff from 0.1.2 to 0.1.3 (#7335) core: bump pydantic-scim from 0.0.7 to 0.0.8 (#7336) website/blogs: Blog dockers (#7328) providers/proxy: attempt to fix duplicate cookie (#7324) stages/email: fix sending emails from task (#7325) web: bump API Client version (#7321) website/docs: update release notes for 2023.10.1 (#7316) release: 2023.10.1 lifecycle: fix otp merge migration (#7315) root: fix pylint errors (#7312) web: bump API Client version (#7311) release: 2023.10.0 ...
This commit is contained in:
commit
639a8ceb5a
|
@ -1,5 +1,5 @@
|
||||||
[bumpversion]
|
[bumpversion]
|
||||||
current_version = 2023.8.3
|
current_version = 2023.10.1
|
||||||
tag = True
|
tag = True
|
||||||
commit = True
|
commit = True
|
||||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)
|
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)
|
||||||
|
|
|
@ -27,8 +27,10 @@ jobs:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: make empty ts client
|
- name: make empty clients
|
||||||
run: mkdir -p ./gen-ts-client
|
run: |
|
||||||
|
mkdir -p ./gen-ts-api
|
||||||
|
mkdir -p ./gen-go-api
|
||||||
- name: Build Docker Image
|
- name: Build Docker Image
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
|
@ -69,6 +71,10 @@ jobs:
|
||||||
- name: prepare variables
|
- name: prepare variables
|
||||||
uses: ./.github/actions/docker-push-variables
|
uses: ./.github/actions/docker-push-variables
|
||||||
id: ev
|
id: ev
|
||||||
|
- name: make empty clients
|
||||||
|
run: |
|
||||||
|
mkdir -p ./gen-ts-api
|
||||||
|
mkdir -p ./gen-go-api
|
||||||
- name: Docker Login Registry
|
- name: Docker Login Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
|
@ -93,6 +99,7 @@ jobs:
|
||||||
ghcr.io/goauthentik/${{ matrix.type }}:latest
|
ghcr.io/goauthentik/${{ matrix.type }}:latest
|
||||||
file: ${{ matrix.type }}.Dockerfile
|
file: ${{ matrix.type }}.Dockerfile
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
|
context: .
|
||||||
build-args: |
|
build-args: |
|
||||||
VERSION=${{ steps.ev.outputs.version }}
|
VERSION=${{ steps.ev.outputs.version }}
|
||||||
VERSION_FAMILY=${{ steps.ev.outputs.versionFamily }}
|
VERSION_FAMILY=${{ steps.ev.outputs.versionFamily }}
|
||||||
|
|
|
@ -16,6 +16,7 @@ jobs:
|
||||||
echo "PG_PASS=$(openssl rand -base64 32)" >> .env
|
echo "PG_PASS=$(openssl rand -base64 32)" >> .env
|
||||||
echo "AUTHENTIK_SECRET_KEY=$(openssl rand -base64 32)" >> .env
|
echo "AUTHENTIK_SECRET_KEY=$(openssl rand -base64 32)" >> .env
|
||||||
docker buildx install
|
docker buildx install
|
||||||
|
mkdir -p ./gen-ts-api
|
||||||
docker build -t testing:latest .
|
docker build -t testing:latest .
|
||||||
echo "AUTHENTIK_IMAGE=testing" >> .env
|
echo "AUTHENTIK_IMAGE=testing" >> .env
|
||||||
echo "AUTHENTIK_TAG=latest" >> .env
|
echo "AUTHENTIK_TAG=latest" >> .env
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
from os import environ
|
from os import environ
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
__version__ = "2023.8.3"
|
__version__ = "2023.10.1"
|
||||||
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"
|
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -584,12 +584,17 @@ class EntryInvalidError(SentryIgnoredException):
|
||||||
entry_model: Optional[str]
|
entry_model: Optional[str]
|
||||||
entry_id: Optional[str]
|
entry_id: Optional[str]
|
||||||
validation_error: Optional[ValidationError]
|
validation_error: Optional[ValidationError]
|
||||||
|
serializer: Optional[Serializer] = None
|
||||||
|
|
||||||
def __init__(self, *args: object, validation_error: Optional[ValidationError] = None) -> None:
|
def __init__(
|
||||||
|
self, *args: object, validation_error: Optional[ValidationError] = None, **kwargs
|
||||||
|
) -> None:
|
||||||
super().__init__(*args)
|
super().__init__(*args)
|
||||||
self.entry_model = None
|
self.entry_model = None
|
||||||
self.entry_id = None
|
self.entry_id = None
|
||||||
self.validation_error = validation_error
|
self.validation_error = validation_error
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
setattr(self, key, value)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_entry(
|
def from_entry(
|
||||||
|
|
|
@ -255,7 +255,10 @@ class Importer:
|
||||||
try:
|
try:
|
||||||
full_data = self.__update_pks_for_attrs(entry.get_attrs(self._import))
|
full_data = self.__update_pks_for_attrs(entry.get_attrs(self._import))
|
||||||
except ValueError as exc:
|
except ValueError as exc:
|
||||||
raise EntryInvalidError.from_entry(exc, entry) from exc
|
raise EntryInvalidError.from_entry(
|
||||||
|
exc,
|
||||||
|
entry,
|
||||||
|
) from exc
|
||||||
always_merger.merge(full_data, updated_identifiers)
|
always_merger.merge(full_data, updated_identifiers)
|
||||||
serializer_kwargs["data"] = full_data
|
serializer_kwargs["data"] = full_data
|
||||||
|
|
||||||
|
@ -272,6 +275,7 @@ class Importer:
|
||||||
f"Serializer errors {serializer.errors}",
|
f"Serializer errors {serializer.errors}",
|
||||||
validation_error=exc,
|
validation_error=exc,
|
||||||
entry=entry,
|
entry=entry,
|
||||||
|
serializer=serializer,
|
||||||
) from exc
|
) from exc
|
||||||
return serializer
|
return serializer
|
||||||
|
|
||||||
|
@ -300,12 +304,14 @@ class Importer:
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
# Validate each single entry
|
# Validate each single entry
|
||||||
|
serializer = None
|
||||||
try:
|
try:
|
||||||
serializer = self._validate_single(entry)
|
serializer = self._validate_single(entry)
|
||||||
except EntryInvalidError as exc:
|
except EntryInvalidError as exc:
|
||||||
# For deleting objects we don't need the serializer to be valid
|
# For deleting objects we don't need the serializer to be valid
|
||||||
if entry.get_state(self._import) == BlueprintEntryDesiredState.ABSENT:
|
if entry.get_state(self._import) == BlueprintEntryDesiredState.ABSENT:
|
||||||
continue
|
serializer = exc.serializer
|
||||||
|
else:
|
||||||
self.logger.warning(f"entry invalid: {exc}", entry=entry, error=exc)
|
self.logger.warning(f"entry invalid: {exc}", entry=entry, error=exc)
|
||||||
if raise_errors:
|
if raise_errors:
|
||||||
raise exc
|
raise exc
|
||||||
|
|
|
@ -82,7 +82,7 @@ class BlueprintEventHandler(FileSystemEventHandler):
|
||||||
path = Path(event.src_path)
|
path = Path(event.src_path)
|
||||||
root = Path(CONFIG.get("blueprints_dir")).absolute()
|
root = Path(CONFIG.get("blueprints_dir")).absolute()
|
||||||
rel_path = str(path.relative_to(root))
|
rel_path = str(path.relative_to(root))
|
||||||
for instance in BlueprintInstance.objects.filter(path=rel_path):
|
for instance in BlueprintInstance.objects.filter(path=rel_path, enabled=True):
|
||||||
LOGGER.debug("modified blueprint file, starting apply", instance=instance)
|
LOGGER.debug("modified blueprint file, starting apply", instance=instance)
|
||||||
apply_blueprint.delay(instance.pk.hex)
|
apply_blueprint.delay(instance.pk.hex)
|
||||||
|
|
||||||
|
|
|
@ -98,6 +98,7 @@ class ApplicationSerializer(ModelSerializer):
|
||||||
class ApplicationViewSet(UsedByMixin, ModelViewSet):
|
class ApplicationViewSet(UsedByMixin, ModelViewSet):
|
||||||
"""Application Viewset"""
|
"""Application Viewset"""
|
||||||
|
|
||||||
|
# pylint: disable=no-member
|
||||||
queryset = Application.objects.all().prefetch_related("provider")
|
queryset = Application.objects.all().prefetch_related("provider")
|
||||||
serializer_class = ApplicationSerializer
|
serializer_class = ApplicationSerializer
|
||||||
search_fields = [
|
search_fields = [
|
||||||
|
|
|
@ -139,6 +139,7 @@ class UserAccountSerializer(PassiveSerializer):
|
||||||
class GroupViewSet(UsedByMixin, ModelViewSet):
|
class GroupViewSet(UsedByMixin, ModelViewSet):
|
||||||
"""Group Viewset"""
|
"""Group Viewset"""
|
||||||
|
|
||||||
|
# pylint: disable=no-member
|
||||||
queryset = Group.objects.all().select_related("parent").prefetch_related("users")
|
queryset = Group.objects.all().select_related("parent").prefetch_related("users")
|
||||||
serializer_class = GroupSerializer
|
serializer_class = GroupSerializer
|
||||||
search_fields = ["name", "is_superuser"]
|
search_fields = ["name", "is_superuser"]
|
||||||
|
|
|
@ -97,6 +97,7 @@ class SourceFlowManager:
|
||||||
if self.request.user.is_authenticated:
|
if self.request.user.is_authenticated:
|
||||||
new_connection.user = self.request.user
|
new_connection.user = self.request.user
|
||||||
new_connection = self.update_connection(new_connection, **kwargs)
|
new_connection = self.update_connection(new_connection, **kwargs)
|
||||||
|
# pylint: disable=no-member
|
||||||
new_connection.save()
|
new_connection.save()
|
||||||
return Action.LINK, new_connection
|
return Action.LINK, new_connection
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
"""authentik crypto app config"""
|
"""authentik crypto app config"""
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import TYPE_CHECKING, Optional
|
from typing import Optional
|
||||||
|
|
||||||
from authentik.blueprints.apps import ManagedAppConfig
|
from authentik.blueprints.apps import ManagedAppConfig
|
||||||
from authentik.lib.generators import generate_id
|
from authentik.lib.generators import generate_id
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from authentik.crypto.models import CertificateKeyPair
|
|
||||||
|
|
||||||
MANAGED_KEY = "goauthentik.io/crypto/jwt-managed"
|
MANAGED_KEY = "goauthentik.io/crypto/jwt-managed"
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,33 +20,37 @@ class AuthentikCryptoConfig(ManagedAppConfig):
|
||||||
"""Load crypto tasks"""
|
"""Load crypto tasks"""
|
||||||
self.import_module("authentik.crypto.tasks")
|
self.import_module("authentik.crypto.tasks")
|
||||||
|
|
||||||
def _create_update_cert(self, cert: Optional["CertificateKeyPair"] = None):
|
def _create_update_cert(self):
|
||||||
from authentik.crypto.builder import CertificateBuilder
|
from authentik.crypto.builder import CertificateBuilder
|
||||||
from authentik.crypto.models import CertificateKeyPair
|
from authentik.crypto.models import CertificateKeyPair
|
||||||
|
|
||||||
builder = CertificateBuilder("authentik Internal JWT Certificate")
|
common_name = "authentik Internal JWT Certificate"
|
||||||
|
builder = CertificateBuilder(common_name)
|
||||||
builder.build(
|
builder.build(
|
||||||
subject_alt_names=["goauthentik.io"],
|
subject_alt_names=["goauthentik.io"],
|
||||||
validity_days=360,
|
validity_days=360,
|
||||||
)
|
)
|
||||||
if not cert:
|
CertificateKeyPair.objects.update_or_create(
|
||||||
cert = CertificateKeyPair()
|
managed=MANAGED_KEY,
|
||||||
builder.cert = cert
|
defaults={
|
||||||
builder.cert.managed = MANAGED_KEY
|
"name": common_name,
|
||||||
builder.save()
|
"certificate_data": builder.certificate,
|
||||||
|
"key_data": builder.private_key,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
def reconcile_managed_jwt_cert(self):
|
def reconcile_managed_jwt_cert(self):
|
||||||
"""Ensure managed JWT certificate"""
|
"""Ensure managed JWT certificate"""
|
||||||
from authentik.crypto.models import CertificateKeyPair
|
from authentik.crypto.models import CertificateKeyPair
|
||||||
|
|
||||||
certs = CertificateKeyPair.objects.filter(managed=MANAGED_KEY)
|
cert: Optional[CertificateKeyPair] = CertificateKeyPair.objects.filter(
|
||||||
if not certs.exists():
|
managed=MANAGED_KEY
|
||||||
self._create_update_cert()
|
).first()
|
||||||
return
|
|
||||||
cert: CertificateKeyPair = certs.first()
|
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
if now < cert.certificate.not_valid_before or now > cert.certificate.not_valid_after:
|
if not cert or (
|
||||||
self._create_update_cert(cert)
|
now < cert.certificate.not_valid_before or now > cert.certificate.not_valid_after
|
||||||
|
):
|
||||||
|
self._create_update_cert()
|
||||||
|
|
||||||
def reconcile_self_signed(self):
|
def reconcile_self_signed(self):
|
||||||
"""Create self-signed keypair"""
|
"""Create self-signed keypair"""
|
||||||
|
@ -61,4 +62,10 @@ class AuthentikCryptoConfig(ManagedAppConfig):
|
||||||
return
|
return
|
||||||
builder = CertificateBuilder(name)
|
builder = CertificateBuilder(name)
|
||||||
builder.build(subject_alt_names=[f"{generate_id()}.self-signed.goauthentik.io"])
|
builder.build(subject_alt_names=[f"{generate_id()}.self-signed.goauthentik.io"])
|
||||||
builder.save()
|
CertificateKeyPair.objects.get_or_create(
|
||||||
|
name=name,
|
||||||
|
defaults={
|
||||||
|
"certificate_data": builder.certificate,
|
||||||
|
"key_data": builder.private_key,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
|
@ -32,13 +32,19 @@ class PermissionSerializer(ModelSerializer):
|
||||||
|
|
||||||
def get_app_label_verbose(self, instance: Permission) -> str:
|
def get_app_label_verbose(self, instance: Permission) -> str:
|
||||||
"""Human-readable app label"""
|
"""Human-readable app label"""
|
||||||
|
try:
|
||||||
return apps.get_app_config(instance.content_type.app_label).verbose_name
|
return apps.get_app_config(instance.content_type.app_label).verbose_name
|
||||||
|
except LookupError:
|
||||||
|
return f"{instance.content_type.app_label}.{instance.content_type.model}"
|
||||||
|
|
||||||
def get_model_verbose(self, instance: Permission) -> str:
|
def get_model_verbose(self, instance: Permission) -> str:
|
||||||
"""Human-readable model name"""
|
"""Human-readable model name"""
|
||||||
|
try:
|
||||||
return apps.get_model(
|
return apps.get_model(
|
||||||
instance.content_type.app_label, instance.content_type.model
|
instance.content_type.app_label, instance.content_type.model
|
||||||
)._meta.verbose_name
|
)._meta.verbose_name
|
||||||
|
except LookupError:
|
||||||
|
return f"{instance.content_type.app_label}.{instance.content_type.model}"
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Permission
|
model = Permission
|
||||||
|
|
|
@ -28,9 +28,12 @@ class ExtraRoleObjectPermissionSerializer(RoleObjectPermissionSerializer):
|
||||||
|
|
||||||
def get_model_verbose(self, instance: GroupObjectPermission) -> str:
|
def get_model_verbose(self, instance: GroupObjectPermission) -> str:
|
||||||
"""Get model label from permission's model"""
|
"""Get model label from permission's model"""
|
||||||
|
try:
|
||||||
return apps.get_model(
|
return apps.get_model(
|
||||||
instance.content_type.app_label, instance.content_type.model
|
instance.content_type.app_label, instance.content_type.model
|
||||||
)._meta.verbose_name
|
)._meta.verbose_name
|
||||||
|
except LookupError:
|
||||||
|
return f"{instance.content_type.app_label}.{instance.content_type.model}"
|
||||||
|
|
||||||
def get_object_description(self, instance: GroupObjectPermission) -> Optional[str]:
|
def get_object_description(self, instance: GroupObjectPermission) -> Optional[str]:
|
||||||
"""Get model description from attached model. This operation takes at least
|
"""Get model description from attached model. This operation takes at least
|
||||||
|
@ -38,7 +41,10 @@ class ExtraRoleObjectPermissionSerializer(RoleObjectPermissionSerializer):
|
||||||
view_ permission on the object"""
|
view_ permission on the object"""
|
||||||
app_label = instance.content_type.app_label
|
app_label = instance.content_type.app_label
|
||||||
model = instance.content_type.model
|
model = instance.content_type.model
|
||||||
|
try:
|
||||||
model_class = apps.get_model(app_label, model)
|
model_class = apps.get_model(app_label, model)
|
||||||
|
except LookupError:
|
||||||
|
return None
|
||||||
objects = get_objects_for_group(instance.group, f"{app_label}.view_{model}", model_class)
|
objects = get_objects_for_group(instance.group, f"{app_label}.view_{model}", model_class)
|
||||||
obj = objects.first()
|
obj = objects.first()
|
||||||
if not obj:
|
if not obj:
|
||||||
|
|
|
@ -28,9 +28,12 @@ class ExtraUserObjectPermissionSerializer(UserObjectPermissionSerializer):
|
||||||
|
|
||||||
def get_model_verbose(self, instance: UserObjectPermission) -> str:
|
def get_model_verbose(self, instance: UserObjectPermission) -> str:
|
||||||
"""Get model label from permission's model"""
|
"""Get model label from permission's model"""
|
||||||
|
try:
|
||||||
return apps.get_model(
|
return apps.get_model(
|
||||||
instance.content_type.app_label, instance.content_type.model
|
instance.content_type.app_label, instance.content_type.model
|
||||||
)._meta.verbose_name
|
)._meta.verbose_name
|
||||||
|
except LookupError:
|
||||||
|
return f"{instance.content_type.app_label}.{instance.content_type.model}"
|
||||||
|
|
||||||
def get_object_description(self, instance: UserObjectPermission) -> Optional[str]:
|
def get_object_description(self, instance: UserObjectPermission) -> Optional[str]:
|
||||||
"""Get model description from attached model. This operation takes at least
|
"""Get model description from attached model. This operation takes at least
|
||||||
|
@ -38,7 +41,10 @@ class ExtraUserObjectPermissionSerializer(UserObjectPermissionSerializer):
|
||||||
view_ permission on the object"""
|
view_ permission on the object"""
|
||||||
app_label = instance.content_type.app_label
|
app_label = instance.content_type.app_label
|
||||||
model = instance.content_type.model
|
model = instance.content_type.model
|
||||||
|
try:
|
||||||
model_class = apps.get_model(app_label, model)
|
model_class = apps.get_model(app_label, model)
|
||||||
|
except LookupError:
|
||||||
|
return None
|
||||||
objects = get_objects_for_user(instance.user, f"{app_label}.view_{model}", model_class)
|
objects = get_objects_for_user(instance.user, f"{app_label}.view_{model}", model_class)
|
||||||
obj = objects.first()
|
obj = objects.first()
|
||||||
if not obj:
|
if not obj:
|
||||||
|
|
|
@ -13,6 +13,7 @@ from authentik.events.models import Event, EventAction
|
||||||
from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus
|
from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus
|
||||||
from authentik.root.celery import CELERY_APP
|
from authentik.root.celery import CELERY_APP
|
||||||
from authentik.stages.email.models import EmailStage
|
from authentik.stages.email.models import EmailStage
|
||||||
|
from authentik.stages.email.utils import logo_data
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
|
|
||||||
|
@ -81,6 +82,10 @@ def send_mail(self: MonitoredTask, message: dict[Any, Any], email_stage_pk: Opti
|
||||||
# Because we use the Message-ID as UID for the task, manually assign it
|
# Because we use the Message-ID as UID for the task, manually assign it
|
||||||
message_object.extra_headers["Message-ID"] = message_id
|
message_object.extra_headers["Message-ID"] = message_id
|
||||||
|
|
||||||
|
# Add the logo (we can't add it in the previous message since MIMEImage
|
||||||
|
# can't be converted to json)
|
||||||
|
message_object.attach(logo_data())
|
||||||
|
|
||||||
LOGGER.debug("Sending mail", to=message_object.to)
|
LOGGER.debug("Sending mail", to=message_object.to)
|
||||||
backend.send_messages([message_object])
|
backend.send_messages([message_object])
|
||||||
Event.new(
|
Event.new(
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
"""email utils"""
|
"""email utils"""
|
||||||
from email.mime.image import MIMEImage
|
from email.mime.image import MIMEImage
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from django.core.mail import EmailMultiAlternatives
|
from django.core.mail import EmailMultiAlternatives
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
|
@ -8,9 +9,12 @@ from django.utils import translation
|
||||||
|
|
||||||
|
|
||||||
@lru_cache()
|
@lru_cache()
|
||||||
def logo_data():
|
def logo_data() -> MIMEImage:
|
||||||
"""Get logo as MIME Image for emails"""
|
"""Get logo as MIME Image for emails"""
|
||||||
with open("web/icons/icon_left_brand.png", "rb") as _logo_file:
|
path = Path("web/icons/icon_left_brand.png")
|
||||||
|
if not path.exists():
|
||||||
|
path = Path("web/dist/assets/icons/icon_left_brand.png")
|
||||||
|
with open(path, "rb") as _logo_file:
|
||||||
logo = MIMEImage(_logo_file.read())
|
logo = MIMEImage(_logo_file.read())
|
||||||
logo.add_header("Content-ID", "logo.png")
|
logo.add_header("Content-ID", "logo.png")
|
||||||
return logo
|
return logo
|
||||||
|
@ -25,5 +29,4 @@ class TemplateEmailMessage(EmailMultiAlternatives):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.content_subtype = "html"
|
self.content_subtype = "html"
|
||||||
self.mixed_subtype = "related"
|
self.mixed_subtype = "related"
|
||||||
self.attach(logo_data())
|
|
||||||
self.attach_alternative(html_content, "text/html")
|
self.attach_alternative(html_content, "text/html")
|
||||||
|
|
|
@ -15,6 +15,7 @@ class UserWriteStageSerializer(StageSerializer):
|
||||||
"user_creation_mode",
|
"user_creation_mode",
|
||||||
"create_users_as_inactive",
|
"create_users_as_inactive",
|
||||||
"create_users_group",
|
"create_users_group",
|
||||||
|
"user_type",
|
||||||
"user_path_template",
|
"user_path_template",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Generated by Django 4.2.6 on 2023-10-25 15:19
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("authentik_stages_user_write", "0007_remove_userwritestage_can_create_users_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="userwritestage",
|
||||||
|
name="user_type",
|
||||||
|
field=models.TextField(
|
||||||
|
choices=[
|
||||||
|
("internal", "Internal"),
|
||||||
|
("external", "External"),
|
||||||
|
("service_account", "Service Account"),
|
||||||
|
("internal_service_account", "Internal Service Account"),
|
||||||
|
],
|
||||||
|
default="external",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -5,7 +5,7 @@ from django.utils.translation import gettext_lazy as _
|
||||||
from django.views import View
|
from django.views import View
|
||||||
from rest_framework.serializers import BaseSerializer
|
from rest_framework.serializers import BaseSerializer
|
||||||
|
|
||||||
from authentik.core.models import Group
|
from authentik.core.models import Group, UserTypes
|
||||||
from authentik.flows.models import Stage
|
from authentik.flows.models import Stage
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,6 +39,10 @@ class UserWriteStage(Stage):
|
||||||
help_text=_("Optionally add newly created users to this group."),
|
help_text=_("Optionally add newly created users to this group."),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
user_type = models.TextField(
|
||||||
|
choices=UserTypes.choices,
|
||||||
|
default=UserTypes.EXTERNAL,
|
||||||
|
)
|
||||||
user_path_template = models.TextField(
|
user_path_template = models.TextField(
|
||||||
default="",
|
default="",
|
||||||
blank=True,
|
blank=True,
|
||||||
|
|
|
@ -9,7 +9,7 @@ from django.utils.translation import gettext as _
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
|
|
||||||
from authentik.core.middleware import SESSION_KEY_IMPERSONATE_USER
|
from authentik.core.middleware import SESSION_KEY_IMPERSONATE_USER
|
||||||
from authentik.core.models import USER_ATTRIBUTE_SOURCES, User, UserSourceConnection
|
from authentik.core.models import USER_ATTRIBUTE_SOURCES, User, UserSourceConnection, UserTypes
|
||||||
from authentik.core.sources.stage import PLAN_CONTEXT_SOURCES_CONNECTION
|
from authentik.core.sources.stage import PLAN_CONTEXT_SOURCES_CONNECTION
|
||||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
|
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
|
||||||
from authentik.flows.stage import StageView
|
from authentik.flows.stage import StageView
|
||||||
|
@ -22,6 +22,7 @@ from authentik.stages.user_write.models import UserCreationMode
|
||||||
from authentik.stages.user_write.signals import user_write
|
from authentik.stages.user_write.signals import user_write
|
||||||
|
|
||||||
PLAN_CONTEXT_GROUPS = "groups"
|
PLAN_CONTEXT_GROUPS = "groups"
|
||||||
|
PLAN_CONTEXT_USER_TYPE = "user_type"
|
||||||
PLAN_CONTEXT_USER_PATH = "user_path"
|
PLAN_CONTEXT_USER_PATH = "user_path"
|
||||||
|
|
||||||
|
|
||||||
|
@ -55,6 +56,19 @@ class UserWriteStageView(StageView):
|
||||||
)
|
)
|
||||||
if path == "":
|
if path == "":
|
||||||
path = User.default_path()
|
path = User.default_path()
|
||||||
|
|
||||||
|
try:
|
||||||
|
user_type = UserTypes(
|
||||||
|
self.executor.plan.context.get(
|
||||||
|
PLAN_CONTEXT_USER_TYPE,
|
||||||
|
self.executor.current_stage.user_type,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except ValueError:
|
||||||
|
user_type = self.executor.current_stage.user_type
|
||||||
|
if user_type == UserTypes.INTERNAL_SERVICE_ACCOUNT:
|
||||||
|
user_type = UserTypes.SERVICE_ACCOUNT
|
||||||
|
|
||||||
if not self.request.user.is_anonymous:
|
if not self.request.user.is_anonymous:
|
||||||
self.executor.plan.context.setdefault(PLAN_CONTEXT_PENDING_USER, self.request.user)
|
self.executor.plan.context.setdefault(PLAN_CONTEXT_PENDING_USER, self.request.user)
|
||||||
if (
|
if (
|
||||||
|
@ -66,6 +80,7 @@ class UserWriteStageView(StageView):
|
||||||
self.executor.plan.context[PLAN_CONTEXT_PENDING_USER] = User(
|
self.executor.plan.context[PLAN_CONTEXT_PENDING_USER] = User(
|
||||||
is_active=not self.executor.current_stage.create_users_as_inactive,
|
is_active=not self.executor.current_stage.create_users_as_inactive,
|
||||||
path=path,
|
path=path,
|
||||||
|
type=user_type,
|
||||||
)
|
)
|
||||||
self.executor.plan.context[PLAN_CONTEXT_AUTHENTICATION_BACKEND] = BACKEND_INBUILT
|
self.executor.plan.context[PLAN_CONTEXT_AUTHENTICATION_BACKEND] = BACKEND_INBUILT
|
||||||
self.logger.debug(
|
self.logger.debug(
|
||||||
|
|
|
@ -8368,6 +8368,16 @@
|
||||||
"title": "Create users group",
|
"title": "Create users group",
|
||||||
"description": "Optionally add newly created users to this group."
|
"description": "Optionally add newly created users to this group."
|
||||||
},
|
},
|
||||||
|
"user_type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"internal",
|
||||||
|
"external",
|
||||||
|
"service_account",
|
||||||
|
"internal_service_account"
|
||||||
|
],
|
||||||
|
"title": "User type"
|
||||||
|
},
|
||||||
"user_path_template": {
|
"user_path_template": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"title": "User path template"
|
"title": "User path template"
|
||||||
|
|
|
@ -32,7 +32,7 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- redis:/data
|
- redis:/data
|
||||||
server:
|
server:
|
||||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.8.3}
|
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.10.1}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command: server
|
command: server
|
||||||
environment:
|
environment:
|
||||||
|
@ -53,7 +53,7 @@ services:
|
||||||
- postgresql
|
- postgresql
|
||||||
- redis
|
- redis
|
||||||
worker:
|
worker:
|
||||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.8.3}
|
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.10.1}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command: worker
|
command: worker
|
||||||
environment:
|
environment:
|
||||||
|
|
4
go.mod
4
go.mod
|
@ -12,7 +12,7 @@ require (
|
||||||
github.com/go-openapi/runtime v0.26.0
|
github.com/go-openapi/runtime v0.26.0
|
||||||
github.com/go-openapi/strfmt v0.21.7
|
github.com/go-openapi/strfmt v0.21.7
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||||
github.com/google/uuid v1.3.1
|
github.com/google/uuid v1.4.0
|
||||||
github.com/gorilla/handlers v1.5.1
|
github.com/gorilla/handlers v1.5.1
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/gorilla/securecookie v1.1.1
|
github.com/gorilla/securecookie v1.1.1
|
||||||
|
@ -27,7 +27,7 @@ require (
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/spf13/cobra v1.7.0
|
github.com/spf13/cobra v1.7.0
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
goauthentik.io/api/v3 v3.2023083.10
|
goauthentik.io/api/v3 v3.2023101.1
|
||||||
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
|
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
|
||||||
golang.org/x/oauth2 v0.13.0
|
golang.org/x/oauth2 v0.13.0
|
||||||
golang.org/x/sync v0.4.0
|
golang.org/x/sync v0.4.0
|
||||||
|
|
7
go.sum
7
go.sum
|
@ -211,8 +211,9 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf
|
||||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
|
||||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
||||||
|
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
|
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
|
||||||
|
@ -355,8 +356,8 @@ go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyK
|
||||||
go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8=
|
go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8=
|
||||||
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
||||||
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
|
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
|
||||||
goauthentik.io/api/v3 v3.2023083.10 h1:mMCOfsqjouSSxedSkCK4k0Cwtt68CWzQgR7Um6ooOQs=
|
goauthentik.io/api/v3 v3.2023101.1 h1:KIQ4wmxjE+geAVB0wBfmxW9Uzo/tA0dbd2hSUJ7YJ3M=
|
||||||
goauthentik.io/api/v3 v3.2023083.10/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
goauthentik.io/api/v3 v3.2023101.1/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||||
|
|
|
@ -29,4 +29,4 @@ func UserAgent() string {
|
||||||
return fmt.Sprintf("authentik@%s", FullVersion())
|
return fmt.Sprintf("authentik@%s", FullVersion())
|
||||||
}
|
}
|
||||||
|
|
||||||
const VERSION = "2023.8.3"
|
const VERSION = "2023.10.1"
|
||||||
|
|
|
@ -50,7 +50,7 @@ func (a *Application) getStore(p api.ProxyOutpostConfig, externalHost *url.URL)
|
||||||
Domain: *p.CookieDomain,
|
Domain: *p.CookieDomain,
|
||||||
SameSite: http.SameSiteLaxMode,
|
SameSite: http.SameSiteLaxMode,
|
||||||
MaxAge: maxAge,
|
MaxAge: maxAge,
|
||||||
Path: externalHost.Path,
|
Path: "/",
|
||||||
})
|
})
|
||||||
|
|
||||||
a.log.Trace("using redis session backend")
|
a.log.Trace("using redis session backend")
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
from lifecycle.migrate import BaseMigration
|
from lifecycle.migrate import BaseMigration
|
||||||
|
|
||||||
SQL_STATEMENT = """
|
SQL_STATEMENT = """
|
||||||
|
BEGIN TRANSACTION;
|
||||||
DELETE FROM django_migrations WHERE app = 'otp_static';
|
DELETE FROM django_migrations WHERE app = 'otp_static';
|
||||||
DELETE FROM django_migrations WHERE app = 'otp_totp';
|
DELETE FROM django_migrations WHERE app = 'otp_totp';
|
||||||
-- Rename tables (static)
|
-- Rename tables (static)
|
||||||
|
@ -12,6 +13,7 @@ ALTER SEQUENCE otp_static_staticdevice_id_seq RENAME TO authentik_stages_authent
|
||||||
-- Rename tables (totp)
|
-- Rename tables (totp)
|
||||||
ALTER TABLE otp_totp_totpdevice RENAME TO authentik_stages_authenticator_totp_totpdevice;
|
ALTER TABLE otp_totp_totpdevice RENAME TO authentik_stages_authenticator_totp_totpdevice;
|
||||||
ALTER SEQUENCE otp_totp_totpdevice_id_seq RENAME TO authentik_stages_authenticator_totp_totpdevice_id_seq;
|
ALTER SEQUENCE otp_totp_totpdevice_id_seq RENAME TO authentik_stages_authenticator_totp_totpdevice_id_seq;
|
||||||
|
COMMIT;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1359,13 +1359,13 @@ files = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "duo-client"
|
name = "duo-client"
|
||||||
version = "5.1.0"
|
version = "5.2.0"
|
||||||
description = "Reference client for Duo Security APIs"
|
description = "Reference client for Duo Security APIs"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
files = [
|
files = [
|
||||||
{file = "duo_client-5.1.0-py2.py3-none-any.whl", hash = "sha256:5dd6e7a526ea79952c078e5a5be93a1d70d36e685fad9478188156587e85b571"},
|
{file = "duo_client-5.2.0-py3-none-any.whl", hash = "sha256:da3237e34300665c40ba5215f1e6656fec1a0136295917541aa973e7fcbf027e"},
|
||||||
{file = "duo_client-5.1.0.tar.gz", hash = "sha256:0dd8b7223a105beca4fdbfa71d400e813d9f33250c3da5fd63e437fb571b55f2"},
|
{file = "duo_client-5.2.0.tar.gz", hash = "sha256:f82361740792b06303f9721e7ba593916080461769396b4f73c0502c0bfcee44"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
@ -2780,13 +2780,13 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pydantic-scim"
|
name = "pydantic-scim"
|
||||||
version = "0.0.7"
|
version = "0.0.8"
|
||||||
description = "Pydantic types for SCIM"
|
description = "Pydantic types for SCIM"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8.0"
|
python-versions = ">=3.8.0"
|
||||||
files = [
|
files = [
|
||||||
{file = "pydantic-scim-0.0.7.tar.gz", hash = "sha256:bc043da51c346051dfd372f12d1837c0846b815236340156d663a8514cba5761"},
|
{file = "pydantic-scim-0.0.8.tar.gz", hash = "sha256:b6c62031126e8c54f0fc7df837678e63934a5b068533fc52e5dfb6cfc24d59e9"},
|
||||||
{file = "pydantic_scim-0.0.7-py3-none-any.whl", hash = "sha256:058eb195f75ef32d04eaf6369c125d5fb7052891694686f8e55e04d184ab1360"},
|
{file = "pydantic_scim-0.0.8-py3-none-any.whl", hash = "sha256:407b3bf55240947155c77a6dd839881d63368c61d64076d6b167ef124ceac79a"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
@ -3374,28 +3374,28 @@ pyasn1 = ">=0.1.3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruff"
|
name = "ruff"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
description = "An extremely fast Python linter, written in Rust."
|
description = "An extremely fast Python linter, written in Rust."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
{file = "ruff-0.1.2-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:0d3ee66b825b713611f89aa35d16de984f76f26c50982a25d52cd0910dff3923"},
|
{file = "ruff-0.1.3-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:b46d43d51f7061652eeadb426a9e3caa1e0002470229ab2fc19de8a7b0766901"},
|
||||||
{file = "ruff-0.1.2-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:f85f850a320ff532b8f93e8d1da6a36ef03698c446357c8c43b46ef90bb321eb"},
|
{file = "ruff-0.1.3-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:b8afeb9abd26b4029c72adc9921b8363374f4e7edb78385ffaa80278313a15f9"},
|
||||||
{file = "ruff-0.1.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:809c6d4e45683696d19ca79e4c6bd3b2e9204fe9546923f2eb3b126ec314b0dc"},
|
{file = "ruff-0.1.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca3cf365bf32e9ba7e6db3f48a4d3e2c446cd19ebee04f05338bc3910114528b"},
|
||||||
{file = "ruff-0.1.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46005e4abb268e93cad065244e17e2ea16b6fcb55a5c473f34fbc1fd01ae34cb"},
|
{file = "ruff-0.1.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4874c165f96c14a00590dcc727a04dca0cfd110334c24b039458c06cf78a672e"},
|
||||||
{file = "ruff-0.1.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10cdb302f519664d5e2cf954562ac86c9d20ca05855e5b5c2f9d542228f45da4"},
|
{file = "ruff-0.1.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eec2dd31eed114e48ea42dbffc443e9b7221976554a504767ceaee3dd38edeb8"},
|
||||||
{file = "ruff-0.1.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f89ebcbe57a1eab7d7b4ceb57ddf0af9ed13eae24e443a7c1dc078000bd8cc6b"},
|
{file = "ruff-0.1.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:dc3ec4edb3b73f21b4aa51337e16674c752f1d76a4a543af56d7d04e97769613"},
|
||||||
{file = "ruff-0.1.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7344eaca057d4c32373c9c3a7afb7274f56040c225b6193dd495fcf69453b436"},
|
{file = "ruff-0.1.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e3de9ed2e39160800281848ff4670e1698037ca039bda7b9274f849258d26ce"},
|
||||||
{file = "ruff-0.1.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dffa25f6e03c4950b6ac6f216bc0f98a4be9719cb0c5260c8e88d1bac36f1683"},
|
{file = "ruff-0.1.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c595193881922cc0556a90f3af99b1c5681f0c552e7a2a189956141d8666fe8"},
|
||||||
{file = "ruff-0.1.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42ddaea52cb7ba7c785e8593a7532866c193bc774fe570f0e4b1ccedd95b83c5"},
|
{file = "ruff-0.1.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f75e670d529aa2288cd00fc0e9b9287603d95e1536d7a7e0cafe00f75e0dd9d"},
|
||||||
{file = "ruff-0.1.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a8533efda625bbec0bf27da2886bd641dae0c209104f6c39abc4be5b7b22de2a"},
|
{file = "ruff-0.1.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:76dd49f6cd945d82d9d4a9a6622c54a994689d8d7b22fa1322983389b4892e20"},
|
||||||
{file = "ruff-0.1.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b0b1b82221ba7c50e03b7a86b983157b5d3f4d8d4f16728132bdf02c6d651f77"},
|
{file = "ruff-0.1.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:918b454bc4f8874a616f0d725590277c42949431ceb303950e87fef7a7d94cb3"},
|
||||||
{file = "ruff-0.1.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6c1362eb9288f8cc95535294cb03bd4665c8cef86ec32745476a4e5c6817034c"},
|
{file = "ruff-0.1.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d8859605e729cd5e53aa38275568dbbdb4fe882d2ea2714c5453b678dca83784"},
|
||||||
{file = "ruff-0.1.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ffa7ef5ded0563329a35bd5a1cfdae40f05a75c0cc2dd30f00b1320b1fb461fc"},
|
{file = "ruff-0.1.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0b6c55f5ef8d9dd05b230bb6ab80bc4381ecb60ae56db0330f660ea240cb0d4a"},
|
||||||
{file = "ruff-0.1.2-py3-none-win32.whl", hash = "sha256:6e8073f85e47072256e2e1909f1ae515cf61ff5a4d24730a63b8b4ac24b6704a"},
|
{file = "ruff-0.1.3-py3-none-win32.whl", hash = "sha256:3e7afcbdcfbe3399c34e0f6370c30f6e529193c731b885316c5a09c9e4317eef"},
|
||||||
{file = "ruff-0.1.2-py3-none-win_amd64.whl", hash = "sha256:b836ddff662a45385948ee0878b0a04c3a260949905ad861a37b931d6ee1c210"},
|
{file = "ruff-0.1.3-py3-none-win_amd64.whl", hash = "sha256:7a18df6638cec4a5bd75350639b2bb2a2366e01222825562c7346674bdceb7ea"},
|
||||||
{file = "ruff-0.1.2-py3-none-win_arm64.whl", hash = "sha256:b0c42d00db5639dbd5f7f9923c63648682dd197bf5de1151b595160c96172691"},
|
{file = "ruff-0.1.3-py3-none-win_arm64.whl", hash = "sha256:12fd53696c83a194a2db7f9a46337ce06445fb9aa7d25ea6f293cf75b21aca9f"},
|
||||||
{file = "ruff-0.1.2.tar.gz", hash = "sha256:afd4785ae060ce6edcd52436d0c197628a918d6d09e3107a892a1bad6a4c6608"},
|
{file = "ruff-0.1.3.tar.gz", hash = "sha256:3ba6145369a151401d5db79f0a47d50e470384d0d89d0d6f7fab0b589ad07c34"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -4332,4 +4332,4 @@ files = [
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.11"
|
python-versions = "^3.11"
|
||||||
content-hash = "e6b1df989cb5c50609540c1229d05d8458ef1cc343fb5868402db8b7679ad73c"
|
content-hash = "2fc746976187f4674f04575cffd6a367744723bf78c356b6951c2370bc47ceae"
|
||||||
|
|
|
@ -113,7 +113,7 @@ filterwarnings = [
|
||||||
|
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "authentik"
|
name = "authentik"
|
||||||
version = "2023.8.3"
|
version = "2023.10.1"
|
||||||
description = ""
|
description = ""
|
||||||
authors = ["authentik Team <hello@goauthentik.io>"]
|
authors = ["authentik Team <hello@goauthentik.io>"]
|
||||||
|
|
||||||
|
@ -152,7 +152,7 @@ paramiko = "*"
|
||||||
psycopg = { extras = ["c"], version = "*" }
|
psycopg = { extras = ["c"], version = "*" }
|
||||||
pycryptodome = "*"
|
pycryptodome = "*"
|
||||||
pydantic = "<3.0.0"
|
pydantic = "<3.0.0"
|
||||||
pydantic-scim = "^0.0.7"
|
pydantic-scim = "^0.0.8"
|
||||||
pyjwt = "*"
|
pyjwt = "*"
|
||||||
python = "^3.11"
|
python = "^3.11"
|
||||||
pyyaml = "*"
|
pyyaml = "*"
|
||||||
|
|
22
schema.yml
22
schema.yml
|
@ -1,7 +1,7 @@
|
||||||
openapi: 3.0.3
|
openapi: 3.0.3
|
||||||
info:
|
info:
|
||||||
title: authentik
|
title: authentik
|
||||||
version: 2023.8.3
|
version: 2023.10.1
|
||||||
description: Making authentication simple.
|
description: Making authentication simple.
|
||||||
contact:
|
contact:
|
||||||
email: hello@goauthentik.io
|
email: hello@goauthentik.io
|
||||||
|
@ -27494,6 +27494,20 @@ paths:
|
||||||
name: user_path_template
|
name: user_path_template
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: user_type
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- external
|
||||||
|
- internal
|
||||||
|
- internal_service_account
|
||||||
|
- service_account
|
||||||
|
description: |-
|
||||||
|
* `internal` - Internal
|
||||||
|
* `external` - External
|
||||||
|
* `service_account` - Service Account
|
||||||
|
* `internal_service_account` - Internal Service Account
|
||||||
tags:
|
tags:
|
||||||
- stages
|
- stages
|
||||||
security:
|
security:
|
||||||
|
@ -38052,6 +38066,8 @@ components:
|
||||||
format: uuid
|
format: uuid
|
||||||
nullable: true
|
nullable: true
|
||||||
description: Optionally add newly created users to this group.
|
description: Optionally add newly created users to this group.
|
||||||
|
user_type:
|
||||||
|
$ref: '#/components/schemas/UserTypeEnum'
|
||||||
user_path_template:
|
user_path_template:
|
||||||
type: string
|
type: string
|
||||||
PatchedWebAuthnDeviceRequest:
|
PatchedWebAuthnDeviceRequest:
|
||||||
|
@ -42422,6 +42438,8 @@ components:
|
||||||
format: uuid
|
format: uuid
|
||||||
nullable: true
|
nullable: true
|
||||||
description: Optionally add newly created users to this group.
|
description: Optionally add newly created users to this group.
|
||||||
|
user_type:
|
||||||
|
$ref: '#/components/schemas/UserTypeEnum'
|
||||||
user_path_template:
|
user_path_template:
|
||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
|
@ -42452,6 +42470,8 @@ components:
|
||||||
format: uuid
|
format: uuid
|
||||||
nullable: true
|
nullable: true
|
||||||
description: Optionally add newly created users to this group.
|
description: Optionally add newly created users to this group.
|
||||||
|
user_type:
|
||||||
|
$ref: '#/components/schemas/UserTypeEnum'
|
||||||
user_path_template:
|
user_path_template:
|
||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -38,15 +38,15 @@
|
||||||
"@codemirror/theme-one-dark": "^6.1.2",
|
"@codemirror/theme-one-dark": "^6.1.2",
|
||||||
"@formatjs/intl-listformat": "^7.5.0",
|
"@formatjs/intl-listformat": "^7.5.0",
|
||||||
"@fortawesome/fontawesome-free": "^6.4.2",
|
"@fortawesome/fontawesome-free": "^6.4.2",
|
||||||
"@goauthentik/api": "^2023.8.3-1697813667",
|
"@goauthentik/api": "^2023.10.1-1698348102",
|
||||||
"@lit-labs/context": "^0.4.1",
|
"@lit-labs/context": "^0.4.1",
|
||||||
"@lit-labs/task": "^3.1.0",
|
"@lit-labs/task": "^3.1.0",
|
||||||
"@lit/localize": "^0.11.4",
|
"@lit/localize": "^0.11.4",
|
||||||
"@open-wc/lit-helpers": "^0.6.0",
|
"@open-wc/lit-helpers": "^0.6.0",
|
||||||
"@patternfly/elements": "^2.4.0",
|
"@patternfly/elements": "^2.4.0",
|
||||||
"@patternfly/patternfly": "^4.224.2",
|
"@patternfly/patternfly": "^4.224.2",
|
||||||
"@sentry/browser": "^7.75.0",
|
"@sentry/browser": "^7.75.1",
|
||||||
"@sentry/tracing": "^7.75.0",
|
"@sentry/tracing": "^7.75.1",
|
||||||
"@webcomponents/webcomponentsjs": "^2.8.0",
|
"@webcomponents/webcomponentsjs": "^2.8.0",
|
||||||
"base64-js": "^1.5.1",
|
"base64-js": "^1.5.1",
|
||||||
"chart.js": "^4.4.0",
|
"chart.js": "^4.4.0",
|
||||||
|
@ -55,9 +55,9 @@
|
||||||
"construct-style-sheets-polyfill": "^3.1.0",
|
"construct-style-sheets-polyfill": "^3.1.0",
|
||||||
"core-js": "^3.33.1",
|
"core-js": "^3.33.1",
|
||||||
"country-flag-icons": "^1.5.7",
|
"country-flag-icons": "^1.5.7",
|
||||||
"fuse.js": "^6.6.2",
|
"fuse.js": "^7.0.0",
|
||||||
"lit": "^2.8.0",
|
"lit": "^2.8.0",
|
||||||
"mermaid": "^10.5.1",
|
"mermaid": "^10.6.0",
|
||||||
"rapidoc": "^9.3.4",
|
"rapidoc": "^9.3.4",
|
||||||
"style-mod": "^4.1.0",
|
"style-mod": "^4.1.0",
|
||||||
"webcomponent-qr-code": "^1.2.0",
|
"webcomponent-qr-code": "^1.2.0",
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
"eslint-plugin-lit": "^1.10.1",
|
"eslint-plugin-lit": "^1.10.1",
|
||||||
"eslint-plugin-sonarjs": "^0.21.0",
|
"eslint-plugin-sonarjs": "^0.21.0",
|
||||||
"eslint-plugin-storybook": "^0.6.15",
|
"eslint-plugin-storybook": "^0.6.15",
|
||||||
"lit-analyzer": "^1.2.1",
|
"lit-analyzer": "^2.0.1",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"prettier": "^3.0.3",
|
"prettier": "^3.0.3",
|
||||||
"pseudolocale": "^2.0.0",
|
"pseudolocale": "^2.0.0",
|
||||||
|
@ -115,7 +115,7 @@
|
||||||
"rollup-plugin-postcss-lit": "^2.1.0",
|
"rollup-plugin-postcss-lit": "^2.1.0",
|
||||||
"storybook": "^7.5.1",
|
"storybook": "^7.5.1",
|
||||||
"storybook-addon-mock": "^4.3.0",
|
"storybook-addon-mock": "^4.3.0",
|
||||||
"ts-lit-plugin": "^1.2.1",
|
"ts-lit-plugin": "^2.0.0",
|
||||||
"tslib": "^2.6.2",
|
"tslib": "^2.6.2",
|
||||||
"turnstile-types": "^1.1.3",
|
"turnstile-types": "^1.1.3",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.2.2",
|
||||||
|
|
|
@ -114,8 +114,8 @@ export class ApplicationWizardAuthenticationByOauth extends BaseProviderPanel {
|
||||||
label=${msg("Client type")}
|
label=${msg("Client type")}
|
||||||
.value=${provider?.clientType}
|
.value=${provider?.clientType}
|
||||||
required
|
required
|
||||||
@change=${(ev: CustomEvent<ClientTypeEnum>) => {
|
@change=${(ev: CustomEvent<{ value: ClientTypeEnum }>) => {
|
||||||
this.showClientSecret = ev.detail !== ClientTypeEnum.Public;
|
this.showClientSecret = ev.detail.value !== ClientTypeEnum.Public;
|
||||||
}}
|
}}
|
||||||
.options=${clientTypeOptions}
|
.options=${clientTypeOptions}
|
||||||
>
|
>
|
||||||
|
|
|
@ -78,8 +78,8 @@ export class TransportForm extends ModelForm<NotificationTransport, string> {
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
<ak-form-element-horizontal label=${msg("Mode")} ?required=${true} name="mode">
|
<ak-form-element-horizontal label=${msg("Mode")} ?required=${true} name="mode">
|
||||||
<ak-radio
|
<ak-radio
|
||||||
@change=${(ev: CustomEvent<NotificationTransportModeEnum>) => {
|
@change=${(ev: CustomEvent<{ value: NotificationTransportModeEnum }>) => {
|
||||||
this.onModeChange(ev.detail);
|
this.onModeChange(ev.detail.value);
|
||||||
}}
|
}}
|
||||||
.options=${[
|
.options=${[
|
||||||
{
|
{
|
||||||
|
|
|
@ -210,8 +210,8 @@ export class OAuth2ProviderFormPage extends ModelForm<OAuth2Provider, number> {
|
||||||
label=${msg("Client type")}
|
label=${msg("Client type")}
|
||||||
.value=${provider?.clientType}
|
.value=${provider?.clientType}
|
||||||
required
|
required
|
||||||
@change=${(ev: CustomEvent<ClientTypeEnum>) => {
|
@change=${(ev: CustomEvent<{ value: ClientTypeEnum }>) => {
|
||||||
this.showClientSecret = ev.detail !== ClientTypeEnum.Public;
|
this.showClientSecret = ev.detail.value !== ClientTypeEnum.Public;
|
||||||
}}
|
}}
|
||||||
.options=${clientTypeOptions}
|
.options=${clientTypeOptions}
|
||||||
>
|
>
|
||||||
|
|
|
@ -42,15 +42,13 @@ export class RoleForm extends ModelForm<Role, string> {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderForm(): TemplateResult {
|
renderForm(): TemplateResult {
|
||||||
return html`<form class="pf-c-form pf-m-horizontal">
|
return html`<ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||||
<ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value="${ifDefined(this.instance?.name)}"
|
value="${ifDefined(this.instance?.name)}"
|
||||||
class="pf-c-form-control"
|
class="pf-c-form-control"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>`;
|
||||||
</form>`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,11 @@ import { TablePage } from "@goauthentik/elements/table/TablePage";
|
||||||
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
|
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
|
||||||
|
|
||||||
import { msg } from "@lit/localize";
|
import { msg } from "@lit/localize";
|
||||||
import { TemplateResult, html } from "lit";
|
import { CSSResult, TemplateResult, html } from "lit";
|
||||||
import { customElement, property } from "lit/decorators.js";
|
import { customElement, property } from "lit/decorators.js";
|
||||||
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
|
|
||||||
|
import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
|
||||||
|
|
||||||
import { RbacApi, Role } from "@goauthentik/api";
|
import { RbacApi, Role } from "@goauthentik/api";
|
||||||
|
|
||||||
|
@ -34,6 +37,10 @@ export class RoleListPage extends TablePage<Role> {
|
||||||
@property()
|
@property()
|
||||||
order = "name";
|
order = "name";
|
||||||
|
|
||||||
|
static get styles(): CSSResult[] {
|
||||||
|
return [...super.styles, PFBanner];
|
||||||
|
}
|
||||||
|
|
||||||
async apiEndpoint(page: number): Promise<PaginatedResponse<Role>> {
|
async apiEndpoint(page: number): Promise<PaginatedResponse<Role>> {
|
||||||
return new RbacApi(DEFAULT_CONFIG).rbacRolesList({
|
return new RbacApi(DEFAULT_CONFIG).rbacRolesList({
|
||||||
ordering: this.order,
|
ordering: this.order,
|
||||||
|
@ -69,6 +76,22 @@ export class RoleListPage extends TablePage<Role> {
|
||||||
</ak-forms-delete-bulk>`;
|
</ak-forms-delete-bulk>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
render(): TemplateResult {
|
||||||
|
return html`<ak-page-header
|
||||||
|
icon=${this.pageIcon()}
|
||||||
|
header=${this.pageTitle()}
|
||||||
|
description=${ifDefined(this.pageDescription())}
|
||||||
|
>
|
||||||
|
</ak-page-header>
|
||||||
|
<div class="pf-c-banner pf-m-info">
|
||||||
|
${msg("RBAC is in preview.")}
|
||||||
|
<a href="mailto:hello@goauthentik.io">${msg("Send us feedback!")}</a>
|
||||||
|
</div>
|
||||||
|
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||||
|
<div class="pf-c-card">${this.renderTable()}</div>
|
||||||
|
</section>`;
|
||||||
|
}
|
||||||
|
|
||||||
row(item: Role): TemplateResult[] {
|
row(item: Role): TemplateResult[] {
|
||||||
return [
|
return [
|
||||||
html`<a href="#/identity/roles/${item.pk}">${item.name}</a>`,
|
html`<a href="#/identity/roles/${item.pk}">${item.name}</a>`,
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { msg, str } from "@lit/localize";
|
||||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators.js";
|
import { customElement, property, state } from "lit/decorators.js";
|
||||||
|
|
||||||
|
import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
|
||||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
||||||
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
||||||
|
@ -52,6 +53,7 @@ export class RoleViewPage extends AKElement {
|
||||||
PFContent,
|
PFContent,
|
||||||
PFCard,
|
PFCard,
|
||||||
PFDescriptionList,
|
PFDescriptionList,
|
||||||
|
PFBanner,
|
||||||
css`
|
css`
|
||||||
.pf-c-description-list__description ak-action-button {
|
.pf-c-description-list__description ak-action-button {
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
|
@ -85,7 +87,11 @@ export class RoleViewPage extends AKElement {
|
||||||
if (!this._role) {
|
if (!this._role) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
return html`<ak-tabs>
|
return html`<div class="pf-c-banner pf-m-info">
|
||||||
|
${msg("RBAC is in preview.")}
|
||||||
|
<a href="mailto:hello@goauthentik.io">${msg("Send us feedback!")}</a>
|
||||||
|
</div>
|
||||||
|
<ak-tabs>
|
||||||
<section
|
<section
|
||||||
slot="page-overview"
|
slot="page-overview"
|
||||||
data-tab-title="${msg("Overview")}"
|
data-tab-title="${msg("Overview")}"
|
||||||
|
@ -116,7 +122,9 @@ export class RoleViewPage extends AKElement {
|
||||||
<div
|
<div
|
||||||
class="pf-c-card pf-l-grid__item pf-m-12-col pf-m-9-col-on-xl pf-m-9-col-on-2xl"
|
class="pf-c-card pf-l-grid__item pf-m-12-col pf-m-9-col-on-xl pf-m-9-col-on-2xl"
|
||||||
>
|
>
|
||||||
<div class="pf-c-card__title">${msg("Assigned global permissions")}</div>
|
<div class="pf-c-card__title">
|
||||||
|
${msg("Assigned global permissions")}
|
||||||
|
</div>
|
||||||
<div class="pf-c-card__body">
|
<div class="pf-c-card__body">
|
||||||
<ak-role-permissions-global-table
|
<ak-role-permissions-global-table
|
||||||
roleUuid=${this._role.pk}
|
roleUuid=${this._role.pk}
|
||||||
|
@ -124,7 +132,9 @@ export class RoleViewPage extends AKElement {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pf-c-card pf-l-grid__item pf-m-12-col">
|
<div class="pf-c-card pf-l-grid__item pf-m-12-col">
|
||||||
<div class="pf-c-card__title">${msg("Assigned object permissions")}</div>
|
<div class="pf-c-card__title">
|
||||||
|
${msg("Assigned object permissions")}
|
||||||
|
</div>
|
||||||
<div class="pf-c-card__body">
|
<div class="pf-c-card__body">
|
||||||
<ak-role-permissions-object-table
|
<ak-role-permissions-object-table
|
||||||
roleUuid=${this._role.pk}
|
roleUuid=${this._role.pk}
|
||||||
|
@ -138,6 +148,7 @@ export class RoleViewPage extends AKElement {
|
||||||
data-tab-title="${msg("Permissions")}"
|
data-tab-title="${msg("Permissions")}"
|
||||||
model=${RbacPermissionsAssignedByUsersListModelEnum.RbacRole}
|
model=${RbacPermissionsAssignedByUsersListModelEnum.RbacRole}
|
||||||
objectPk=${this._role.pk}
|
objectPk=${this._role.pk}
|
||||||
|
.showBanner=${false}
|
||||||
></ak-rbac-object-permission-page>
|
></ak-rbac-object-permission-page>
|
||||||
</ak-tabs>`;
|
</ak-tabs>`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,14 @@ import { TemplateResult, html } from "lit";
|
||||||
import { customElement } from "lit/decorators.js";
|
import { customElement } from "lit/decorators.js";
|
||||||
import { ifDefined } from "lit/directives/if-defined.js";
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
|
|
||||||
import { CoreApi, CoreGroupsListRequest, Group, StagesApi, UserWriteStage } from "@goauthentik/api";
|
import {
|
||||||
|
CoreApi,
|
||||||
|
CoreGroupsListRequest,
|
||||||
|
Group,
|
||||||
|
StagesApi,
|
||||||
|
UserTypeEnum,
|
||||||
|
UserWriteStage,
|
||||||
|
} from "@goauthentik/api";
|
||||||
|
|
||||||
@customElement("ak-stage-user-write-form")
|
@customElement("ak-stage-user-write-form")
|
||||||
export class UserWriteStageForm extends ModelForm<UserWriteStage, string> {
|
export class UserWriteStageForm extends ModelForm<UserWriteStage, string> {
|
||||||
|
@ -111,6 +118,42 @@ export class UserWriteStageForm extends ModelForm<UserWriteStage, string> {
|
||||||
${msg("Mark newly created users as inactive.")}
|
${msg("Mark newly created users as inactive.")}
|
||||||
</p>
|
</p>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
|
<ak-form-element-horizontal
|
||||||
|
label=${msg("User path template")}
|
||||||
|
name="userPathTemplate"
|
||||||
|
>
|
||||||
|
<ak-radio
|
||||||
|
.options=${[
|
||||||
|
{
|
||||||
|
label: "Internal",
|
||||||
|
value: UserTypeEnum.Internal,
|
||||||
|
default: true,
|
||||||
|
description: html`${msg(
|
||||||
|
"Internal users might be users such as company employees, which will get access to the full Enterprise feature set.",
|
||||||
|
)}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "External",
|
||||||
|
value: UserTypeEnum.External,
|
||||||
|
description: html`${msg(
|
||||||
|
"External users might be external consultants or B2C customers. These users don't get access to enterprise features.",
|
||||||
|
)}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Service account",
|
||||||
|
value: UserTypeEnum.ServiceAccount,
|
||||||
|
description: html`${msg(
|
||||||
|
"Service accounts should be used for machine-to-machine authentication or other automations.",
|
||||||
|
)}`,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
.value=${this.instance?.userType}
|
||||||
|
>
|
||||||
|
</ak-radio>
|
||||||
|
<p class="pf-c-form__helper-text">
|
||||||
|
${msg("User type used for newly created users.")}
|
||||||
|
</p>
|
||||||
|
</ak-form-element-horizontal>
|
||||||
<ak-form-element-horizontal
|
<ak-form-element-horizontal
|
||||||
label=${msg("User path template")}
|
label=${msg("User path template")}
|
||||||
name="userPathTemplate"
|
name="userPathTemplate"
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import "@goauthentik/admin/users/GroupSelectModal";
|
import "@goauthentik/admin/users/GroupSelectModal";
|
||||||
import { UserTypeEnum } from "@goauthentik/api/dist/models/UserTypeEnum";
|
|
||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||||
import { first } from "@goauthentik/common/utils";
|
import { first } from "@goauthentik/common/utils";
|
||||||
import "@goauthentik/elements/CodeMirror";
|
import "@goauthentik/elements/CodeMirror";
|
||||||
|
@ -14,7 +13,7 @@ import { CSSResult, TemplateResult, css, html } from "lit";
|
||||||
import { customElement } from "lit/decorators.js";
|
import { customElement } from "lit/decorators.js";
|
||||||
import { ifDefined } from "lit/directives/if-defined.js";
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
|
|
||||||
import { CoreApi, User } from "@goauthentik/api";
|
import { CoreApi, User, UserTypeEnum } from "@goauthentik/api";
|
||||||
|
|
||||||
@customElement("ak-user-form")
|
@customElement("ak-user-form")
|
||||||
export class UserForm extends ModelForm<User, number> {
|
export class UserForm extends ModelForm<User, number> {
|
||||||
|
|
|
@ -33,6 +33,7 @@ import { msg, str } from "@lit/localize";
|
||||||
import { css, html, nothing } from "lit";
|
import { css, html, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators.js";
|
import { customElement, property, state } from "lit/decorators.js";
|
||||||
|
|
||||||
|
import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
|
||||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
||||||
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
||||||
|
@ -86,6 +87,7 @@ export class UserViewPage extends AKElement {
|
||||||
PFCard,
|
PFCard,
|
||||||
PFDescriptionList,
|
PFDescriptionList,
|
||||||
PFSizing,
|
PFSizing,
|
||||||
|
PFBanner,
|
||||||
css`
|
css`
|
||||||
.ak-button-collection {
|
.ak-button-collection {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -465,21 +467,30 @@ export class UserViewPage extends AKElement {
|
||||||
model=${RbacPermissionsAssignedByUsersListModelEnum.CoreUser}
|
model=${RbacPermissionsAssignedByUsersListModelEnum.CoreUser}
|
||||||
objectPk=${this.user.pk}
|
objectPk=${this.user.pk}
|
||||||
></ak-rbac-object-permission-page>
|
></ak-rbac-object-permission-page>
|
||||||
<section
|
<div
|
||||||
slot="page-mfa-assigned-permissions"
|
slot="page-mfa-assigned-permissions"
|
||||||
data-tab-title="${msg("Assigned permissions")}"
|
data-tab-title="${msg("Assigned permissions")}"
|
||||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
class=""
|
||||||
>
|
>
|
||||||
|
<div class="pf-c-banner pf-m-info">
|
||||||
|
${msg("RBAC is in preview.")}
|
||||||
|
<a href="mailto:hello@goauthentik.io">${msg("Send us feedback!")}</a>
|
||||||
|
</div>
|
||||||
|
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||||
<div class="pf-l-grid pf-m-gutter">
|
<div class="pf-l-grid pf-m-gutter">
|
||||||
<div class="pf-c-card pf-l-grid__item pf-m-12-col">
|
<div class="pf-c-card pf-l-grid__item pf-m-12-col">
|
||||||
<div class="pf-c-card__title">${msg("Assigned global permissions")}</div>
|
<div class="pf-c-card__title">
|
||||||
|
${msg("Assigned global permissions")}
|
||||||
|
</div>
|
||||||
<div class="pf-c-card__body">
|
<div class="pf-c-card__body">
|
||||||
<ak-user-assigned-global-permissions-table userId=${this.user.pk}>
|
<ak-user-assigned-global-permissions-table userId=${this.user.pk}>
|
||||||
</ak-user-assigned-global-permissions-table>
|
</ak-user-assigned-global-permissions-table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pf-c-card pf-l-grid__item pf-m-12-col">
|
<div class="pf-c-card pf-l-grid__item pf-m-12-col">
|
||||||
<div class="pf-c-card__title">${msg("Assigned object permissions")}</div>
|
<div class="pf-c-card__title">
|
||||||
|
${msg("Assigned object permissions")}
|
||||||
|
</div>
|
||||||
<div class="pf-c-card__body">
|
<div class="pf-c-card__body">
|
||||||
<ak-user-assigned-object-permissions-table userId=${this.user.pk}>
|
<ak-user-assigned-object-permissions-table userId=${this.user.pk}>
|
||||||
</ak-user-assigned-object-permissions-table>
|
</ak-user-assigned-object-permissions-table>
|
||||||
|
@ -487,6 +498,7 @@ export class UserViewPage extends AKElement {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
</div>
|
||||||
</ak-tabs>`;
|
</ak-tabs>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ export const SUCCESS_CLASS = "pf-m-success";
|
||||||
export const ERROR_CLASS = "pf-m-danger";
|
export const ERROR_CLASS = "pf-m-danger";
|
||||||
export const PROGRESS_CLASS = "pf-m-in-progress";
|
export const PROGRESS_CLASS = "pf-m-in-progress";
|
||||||
export const CURRENT_CLASS = "pf-m-current";
|
export const CURRENT_CLASS = "pf-m-current";
|
||||||
export const VERSION = "2023.8.3";
|
export const VERSION = "2023.10.1";
|
||||||
export const TITLE_DEFAULT = "authentik";
|
export const TITLE_DEFAULT = "authentik";
|
||||||
export const ROUTE_SEPARATOR = ";";
|
export const ROUTE_SEPARATOR = ";";
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,93 @@ export interface KeyUnknown {
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively assign `value` into `json` while interpreting the dot-path of `element.name`
|
||||||
|
*/
|
||||||
|
function assignValue(element: HTMLInputElement, value: unknown, json: KeyUnknown): void {
|
||||||
|
let parent = json;
|
||||||
|
if (!element.name?.includes(".")) {
|
||||||
|
parent[element.name] = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const nameElements = element.name.split(".");
|
||||||
|
for (let index = 0; index < nameElements.length - 1; index++) {
|
||||||
|
const nameEl = nameElements[index];
|
||||||
|
// Ensure all nested structures exist
|
||||||
|
if (!(nameEl in parent)) parent[nameEl] = {};
|
||||||
|
parent = parent[nameEl] as { [key: string]: unknown };
|
||||||
|
}
|
||||||
|
parent[nameElements[nameElements.length - 1]] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert the elements of the form to JSON.[4]
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export function serializeForm<T extends KeyUnknown>(
|
||||||
|
elements: NodeListOf<HorizontalFormElement>,
|
||||||
|
): T | undefined {
|
||||||
|
const json: { [key: string]: unknown } = {};
|
||||||
|
elements.forEach((element) => {
|
||||||
|
element.requestUpdate();
|
||||||
|
const inputElement = element.querySelector<HTMLInputElement>("[name]");
|
||||||
|
if (element.hidden || !inputElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Skip elements that are writeOnly where the user hasn't clicked on the value
|
||||||
|
if (element.writeOnly && !element.writeOnlyActivated) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
inputElement.tagName.toLowerCase() === "select" &&
|
||||||
|
"multiple" in inputElement.attributes
|
||||||
|
) {
|
||||||
|
const selectElement = inputElement as unknown as HTMLSelectElement;
|
||||||
|
assignValue(
|
||||||
|
inputElement,
|
||||||
|
Array.from(selectElement.selectedOptions).map((v) => v.value),
|
||||||
|
json,
|
||||||
|
);
|
||||||
|
} else if (inputElement.tagName.toLowerCase() === "input" && inputElement.type === "date") {
|
||||||
|
assignValue(inputElement, inputElement.valueAsDate, json);
|
||||||
|
} else if (
|
||||||
|
inputElement.tagName.toLowerCase() === "input" &&
|
||||||
|
inputElement.type === "datetime-local"
|
||||||
|
) {
|
||||||
|
assignValue(inputElement, new Date(inputElement.valueAsNumber), json);
|
||||||
|
} else if (
|
||||||
|
inputElement.tagName.toLowerCase() === "input" &&
|
||||||
|
"type" in inputElement.dataset &&
|
||||||
|
inputElement.dataset["type"] === "datetime-local"
|
||||||
|
) {
|
||||||
|
// Workaround for Firefox <93, since 92 and older don't support
|
||||||
|
// datetime-local fields
|
||||||
|
assignValue(inputElement, new Date(inputElement.value), json);
|
||||||
|
} else if (
|
||||||
|
inputElement.tagName.toLowerCase() === "input" &&
|
||||||
|
inputElement.type === "checkbox"
|
||||||
|
) {
|
||||||
|
assignValue(inputElement, inputElement.checked, json);
|
||||||
|
} else if ("selectedFlow" in inputElement) {
|
||||||
|
assignValue(inputElement, inputElement.value, json);
|
||||||
|
} else if (inputElement.tagName.toLowerCase() === "ak-search-select") {
|
||||||
|
const select = inputElement as unknown as SearchSelect<unknown>;
|
||||||
|
try {
|
||||||
|
const value = select.toForm();
|
||||||
|
assignValue(inputElement, value, json);
|
||||||
|
} catch (exc) {
|
||||||
|
if (exc instanceof PreventFormSubmit) {
|
||||||
|
throw new PreventFormSubmit(exc.message, element);
|
||||||
|
}
|
||||||
|
throw exc;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assignValue(inputElement, inputElement.value, json);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return json as unknown as T;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Form
|
* Form
|
||||||
*
|
*
|
||||||
|
@ -177,95 +264,13 @@ export abstract class Form<T> extends AKElement {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
serializeForm(): T | undefined {
|
serializeForm(): T | undefined {
|
||||||
const elements =
|
const elements = this.shadowRoot?.querySelectorAll<HorizontalFormElement>(
|
||||||
this.shadowRoot?.querySelectorAll<HorizontalFormElement>(
|
|
||||||
"ak-form-element-horizontal",
|
"ak-form-element-horizontal",
|
||||||
) || [];
|
|
||||||
const json: { [key: string]: unknown } = {};
|
|
||||||
elements.forEach((element) => {
|
|
||||||
element.requestUpdate();
|
|
||||||
const inputElement = element.querySelector<HTMLInputElement>("[name]");
|
|
||||||
if (element.hidden || !inputElement) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Skip elements that are writeOnly where the user hasn't clicked on the value
|
|
||||||
if (element.writeOnly && !element.writeOnlyActivated) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
inputElement.tagName.toLowerCase() === "select" &&
|
|
||||||
"multiple" in inputElement.attributes
|
|
||||||
) {
|
|
||||||
const selectElement = inputElement as unknown as HTMLSelectElement;
|
|
||||||
this.assignValue(
|
|
||||||
inputElement,
|
|
||||||
Array.from(selectElement.selectedOptions).map((v) => v.value),
|
|
||||||
json,
|
|
||||||
);
|
);
|
||||||
} else if (
|
if (!elements) {
|
||||||
inputElement.tagName.toLowerCase() === "input" &&
|
return {} as T;
|
||||||
inputElement.type === "date"
|
|
||||||
) {
|
|
||||||
this.assignValue(inputElement, inputElement.valueAsDate, json);
|
|
||||||
} else if (
|
|
||||||
inputElement.tagName.toLowerCase() === "input" &&
|
|
||||||
inputElement.type === "datetime-local"
|
|
||||||
) {
|
|
||||||
this.assignValue(inputElement, new Date(inputElement.valueAsNumber), json);
|
|
||||||
} else if (
|
|
||||||
inputElement.tagName.toLowerCase() === "input" &&
|
|
||||||
"type" in inputElement.dataset &&
|
|
||||||
inputElement.dataset["type"] === "datetime-local"
|
|
||||||
) {
|
|
||||||
// Workaround for Firefox <93, since 92 and older don't support
|
|
||||||
// datetime-local fields
|
|
||||||
this.assignValue(inputElement, new Date(inputElement.value), json);
|
|
||||||
} else if (
|
|
||||||
inputElement.tagName.toLowerCase() === "input" &&
|
|
||||||
inputElement.type === "checkbox"
|
|
||||||
) {
|
|
||||||
this.assignValue(inputElement, inputElement.checked, json);
|
|
||||||
} else if ("selectedFlow" in inputElement) {
|
|
||||||
this.assignValue(inputElement, inputElement.value, json);
|
|
||||||
} else if (inputElement.tagName.toLowerCase() === "ak-search-select") {
|
|
||||||
const select = inputElement as unknown as SearchSelect<unknown>;
|
|
||||||
try {
|
|
||||||
const value = select.toForm();
|
|
||||||
this.assignValue(inputElement, value, json);
|
|
||||||
} catch (exc) {
|
|
||||||
if (exc instanceof PreventFormSubmit) {
|
|
||||||
throw new PreventFormSubmit(exc.message, element);
|
|
||||||
}
|
}
|
||||||
throw exc;
|
return serializeForm(elements) as T;
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.assignValue(inputElement, inputElement.value, json);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return json as unknown as T;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursively assign `value` into `json` while interpreting the dot-path of `element.name`
|
|
||||||
*/
|
|
||||||
private assignValue(
|
|
||||||
element: HTMLInputElement,
|
|
||||||
value: unknown,
|
|
||||||
json: { [key: string]: unknown },
|
|
||||||
): void {
|
|
||||||
let parent = json;
|
|
||||||
if (!element.name?.includes(".")) {
|
|
||||||
parent[element.name] = value;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const nameElements = element.name.split(".");
|
|
||||||
for (let index = 0; index < nameElements.length - 1; index++) {
|
|
||||||
const nameEl = nameElements[index];
|
|
||||||
// Ensure all nested structures exist
|
|
||||||
if (!(nameEl in parent)) parent[nameEl] = {};
|
|
||||||
parent = parent[nameEl] as { [key: string]: unknown };
|
|
||||||
}
|
|
||||||
parent[nameElements[nameElements.length - 1]] = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { msg } from "@lit/localize";
|
||||||
import { CSSResult, TemplateResult, html } from "lit";
|
import { CSSResult, TemplateResult, html } from "lit";
|
||||||
import { customElement, property } from "lit/decorators.js";
|
import { customElement, property } from "lit/decorators.js";
|
||||||
|
|
||||||
|
import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
|
||||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||||
|
|
||||||
|
@ -51,13 +52,17 @@ export class ObjectPermissionModal extends AKElement {
|
||||||
objectPk?: string | number;
|
objectPk?: string | number;
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult[] {
|
||||||
return [PFBase, PFButton];
|
return [PFBase, PFButton, PFBanner];
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): TemplateResult {
|
render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<ak-forms-modal .showSubmitButton=${false} cancelText=${msg("Close")}>
|
<ak-forms-modal .showSubmitButton=${false} cancelText=${msg("Close")}>
|
||||||
<span slot="header"> ${msg("Update Permissions")} </span>
|
<span slot="header"> ${msg("Update Permissions")} </span>
|
||||||
|
<div class="pf-c-banner pf-m-info" slot="above-form">
|
||||||
|
${msg("RBAC is in preview.")}
|
||||||
|
<a href="mailto:hello@goauthentik.io">${msg("Send us feedback!")}</a>
|
||||||
|
</div>
|
||||||
<ak-rbac-object-permission-modal-form
|
<ak-rbac-object-permission-modal-form
|
||||||
slot="form"
|
slot="form"
|
||||||
.model=${this.model}
|
.model=${this.model}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { msg } from "@lit/localize";
|
||||||
import { CSSResult, TemplateResult, html } from "lit";
|
import { CSSResult, TemplateResult, html } from "lit";
|
||||||
import { customElement, property } from "lit/decorators.js";
|
import { customElement, property } from "lit/decorators.js";
|
||||||
|
|
||||||
|
import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
|
||||||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
||||||
import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
||||||
import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
|
import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
|
||||||
|
@ -22,12 +23,21 @@ export class ObjectPermissionPage extends AKElement {
|
||||||
@property()
|
@property()
|
||||||
objectPk?: string | number;
|
objectPk?: string | number;
|
||||||
|
|
||||||
|
@property({ type: Boolean })
|
||||||
|
showBanner = true;
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult[] {
|
||||||
return [PFBase, PFGrid, PFPage, PFCard];
|
return [PFBase, PFGrid, PFPage, PFCard, PFBanner];
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): TemplateResult {
|
render(): TemplateResult {
|
||||||
return html`<ak-tabs pageIdentifier="permissionPage">
|
return html`${this.showBanner
|
||||||
|
? html`<div class="pf-c-banner pf-m-info">
|
||||||
|
${msg("RBAC is in preview.")}
|
||||||
|
<a href="mailto:hello@goauthentik.io">${msg("Send us feedback!")}</a>
|
||||||
|
</div>`
|
||||||
|
: html``}
|
||||||
|
<ak-tabs pageIdentifier="permissionPage">
|
||||||
<section
|
<section
|
||||||
slot="page-object-user"
|
slot="page-object-user"
|
||||||
data-tab-title="${msg("User Object Permissions")}"
|
data-tab-title="${msg("User Object Permissions")}"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { AKElement } from "@goauthentik/elements/Base";
|
||||||
import { getURLParam, updateURLParams } from "@goauthentik/elements/router/RouteMatch";
|
import { getURLParam, updateURLParams } from "@goauthentik/elements/router/RouteMatch";
|
||||||
import Fuse from "fuse.js";
|
import Fuse from "fuse.js";
|
||||||
|
import { FuseResult } from "fuse.js";
|
||||||
|
|
||||||
import { msg } from "@lit/localize";
|
import { msg } from "@lit/localize";
|
||||||
import { css, html } from "lit";
|
import { css, html } from "lit";
|
||||||
|
@ -66,7 +67,7 @@ export class LibraryPageApplicationList extends AKElement {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onSelected(apps: Fuse.FuseResult<Application>[]) {
|
onSelected(apps: FuseResult<Application>[]) {
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
customEvent(SEARCH_UPDATED, {
|
customEvent(SEARCH_UPDATED, {
|
||||||
apps: apps.map((app) => app.item),
|
apps: apps.map((app) => app.item),
|
||||||
|
|
|
@ -5798,12 +5798,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
<trans-unit id="s945a6b94361ee45b">
|
<trans-unit id="s945a6b94361ee45b">
|
||||||
<source>For transparent reverse proxies with required authentication</source>
|
<source>For transparent reverse proxies with required authentication</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sadf073913458acbd">
|
|
||||||
<source>For nginx's auth_request or traefik's forwardAuth</source>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="se770e9498b3bacf6">
|
|
||||||
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s40830ec037f34626">
|
<trans-unit id="s40830ec037f34626">
|
||||||
<source>Configure SAML provider manually</source>
|
<source>Configure SAML provider manually</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
@ -6031,6 +6025,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s0924f51b028233a3">
|
<trans-unit id="s0924f51b028233a3">
|
||||||
<source><No name set></source>
|
<source><No name set></source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sdc9a6ad1af30572c">
|
||||||
|
<source>For nginx's auth_request or traefik's forwardAuth</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sfc31264ef7ff86ef">
|
||||||
|
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sc615309d10a9228c">
|
||||||
|
<source>RBAC is in preview.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s32babfed740fd3c1">
|
||||||
|
<source>User type used for newly created users.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
|
|
@ -6079,12 +6079,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
<trans-unit id="s945a6b94361ee45b">
|
<trans-unit id="s945a6b94361ee45b">
|
||||||
<source>For transparent reverse proxies with required authentication</source>
|
<source>For transparent reverse proxies with required authentication</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sadf073913458acbd">
|
|
||||||
<source>For nginx's auth_request or traefik's forwardAuth</source>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="se770e9498b3bacf6">
|
|
||||||
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s40830ec037f34626">
|
<trans-unit id="s40830ec037f34626">
|
||||||
<source>Configure SAML provider manually</source>
|
<source>Configure SAML provider manually</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
@ -6312,6 +6306,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s0924f51b028233a3">
|
<trans-unit id="s0924f51b028233a3">
|
||||||
<source><No name set></source>
|
<source><No name set></source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sdc9a6ad1af30572c">
|
||||||
|
<source>For nginx's auth_request or traefik's forwardAuth</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sfc31264ef7ff86ef">
|
||||||
|
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sc615309d10a9228c">
|
||||||
|
<source>RBAC is in preview.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s32babfed740fd3c1">
|
||||||
|
<source>User type used for newly created users.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
|
|
@ -5713,12 +5713,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
<trans-unit id="s945a6b94361ee45b">
|
<trans-unit id="s945a6b94361ee45b">
|
||||||
<source>For transparent reverse proxies with required authentication</source>
|
<source>For transparent reverse proxies with required authentication</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sadf073913458acbd">
|
|
||||||
<source>For nginx's auth_request or traefik's forwardAuth</source>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="se770e9498b3bacf6">
|
|
||||||
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s40830ec037f34626">
|
<trans-unit id="s40830ec037f34626">
|
||||||
<source>Configure SAML provider manually</source>
|
<source>Configure SAML provider manually</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
@ -5946,6 +5940,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s0924f51b028233a3">
|
<trans-unit id="s0924f51b028233a3">
|
||||||
<source><No name set></source>
|
<source><No name set></source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sdc9a6ad1af30572c">
|
||||||
|
<source>For nginx's auth_request or traefik's forwardAuth</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sfc31264ef7ff86ef">
|
||||||
|
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sc615309d10a9228c">
|
||||||
|
<source>RBAC is in preview.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s32babfed740fd3c1">
|
||||||
|
<source>User type used for newly created users.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
|
|
@ -3850,7 +3850,7 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s773aa6621d7e37b7">
|
<trans-unit id="s773aa6621d7e37b7">
|
||||||
<source>Create Tenant</source>
|
<source>Create Tenant</source>
|
||||||
<target>Créer un locataire</target>
|
<target>Créer un tenant</target>
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s8cb7bb82e96d5d77">
|
<trans-unit id="s8cb7bb82e96d5d77">
|
||||||
|
@ -7597,60 +7597,71 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
|
||||||
<source>Stage used to configure a WebAuthn authenticator (i.e. Yubikey, FaceID/Windows Hello).</source>
|
<source>Stage used to configure a WebAuthn authenticator (i.e. Yubikey, FaceID/Windows Hello).</source>
|
||||||
<target>Étape de configuration d'un authentificateur WebAuthn (Yubikey, FaceID/Windows Hello).</target>
|
<target>Étape de configuration d'un authentificateur WebAuthn (Yubikey, FaceID/Windows Hello).</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<<<<<<< HEAD
|
|
||||||
<trans-unit id="s1cffe58249b04669">
|
<trans-unit id="s1cffe58249b04669">
|
||||||
<source>Internal application name used in URLs.</source>
|
<source>Internal application name used in URLs.</source>
|
||||||
|
<target>Nom de l'application interne utilisé dans les URLs.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sb3d4f79d9d8b71e5">
|
<trans-unit id="sb3d4f79d9d8b71e5">
|
||||||
<source>Submit</source>
|
<source>Submit</source>
|
||||||
|
<target>Soumettre</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="se2b29e6cfe59414c">
|
<trans-unit id="se2b29e6cfe59414c">
|
||||||
<source>UI Settings</source>
|
<source>UI Settings</source>
|
||||||
|
<target>Paramètres d'UI</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s836148f721d8913b">
|
<trans-unit id="s836148f721d8913b">
|
||||||
<source>Transparent Reverse Proxy</source>
|
<source>Transparent Reverse Proxy</source>
|
||||||
|
<target>Reverse Proxy Transparent</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s945a6b94361ee45b">
|
<trans-unit id="s945a6b94361ee45b">
|
||||||
<source>For transparent reverse proxies with required authentication</source>
|
<source>For transparent reverse proxies with required authentication</source>
|
||||||
</trans-unit>
|
<target>Pour les reverses proxy transparents avec authentification requise</target>
|
||||||
<trans-unit id="sadf073913458acbd">
|
|
||||||
<source>For nginx's auth_request or traefik's forwardAuth</source>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="se770e9498b3bacf6">
|
|
||||||
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s40830ec037f34626">
|
<trans-unit id="s40830ec037f34626">
|
||||||
<source>Configure SAML provider manually</source>
|
<source>Configure SAML provider manually</source>
|
||||||
|
<target>Configurer le fournisseur SAML manuellement</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sea9fc40dfd1d18b1">
|
<trans-unit id="sea9fc40dfd1d18b1">
|
||||||
<source>Configure RADIUS provider manually</source>
|
<source>Configure RADIUS provider manually</source>
|
||||||
|
<target>Configurer le fournisseur RADIUS manuellement</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sa1b0052ae095b9b3">
|
<trans-unit id="sa1b0052ae095b9b3">
|
||||||
<source>Configure SCIM provider manually</source>
|
<source>Configure SCIM provider manually</source>
|
||||||
|
<target>Configurer le fournisseur SCIM manuellement</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s15831fa50a116545">
|
<trans-unit id="s15831fa50a116545">
|
||||||
<source>Saving Application...</source>
|
<source>Saving Application...</source>
|
||||||
|
<target>Enregistrement de l'application...</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s823abdb61543a826">
|
<trans-unit id="s823abdb61543a826">
|
||||||
<source>Authentik was unable to save this application:</source>
|
<source>Authentik was unable to save this application:</source>
|
||||||
|
<target>authentik n'a pas pu sauvegarder cette application :</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s848288f8c2265aad">
|
<trans-unit id="s848288f8c2265aad">
|
||||||
<source>Your application has been saved</source>
|
<source>Your application has been saved</source>
|
||||||
|
<target>L'application a été sauvegardée</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sf60f1e5b76897c93">
|
<trans-unit id="sf60f1e5b76897c93">
|
||||||
<source>In the Application:</source>
|
<source>In the Application:</source>
|
||||||
|
<target>Dans l'application :</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s7ce65cf482b7bff0">
|
<trans-unit id="s7ce65cf482b7bff0">
|
||||||
<source>In the Provider:</source>
|
<source>In the Provider:</source>
|
||||||
|
<target>Dans le fournisseur :</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s67d858051b34c38b">
|
<trans-unit id="s67d858051b34c38b">
|
||||||
<source>Method's display Name.</source>
|
<source>Method's display Name.</source>
|
||||||
|
<target>Nom d'affichage de la méthode.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="h10ef80d434185070">
|
<trans-unit id="h10ef80d434185070">
|
||||||
<source>Use this provider with nginx's <x id="0" equiv-text="<code>"/>auth_request<x id="1" equiv-text="</code>"/> or traefik's
|
<source>Use this provider with nginx's <x id="0" equiv-text="<code>"/>auth_request<x id="1" equiv-text="</code>"/> or traefik's
|
||||||
<x id="2" equiv-text="<code>"/>forwardAuth<x id="3" equiv-text="</code>"/>. Each application/domain needs its own provider.
|
<x id="2" equiv-text="<code>"/>forwardAuth<x id="3" equiv-text="</code>"/>. Each application/domain needs its own provider.
|
||||||
Additionally, on each domain, <x id="4" equiv-text="<code>"/>/outpost.goauthentik.io<x id="5" equiv-text="</code>"/> must be
|
Additionally, on each domain, <x id="4" equiv-text="<code>"/>/outpost.goauthentik.io<x id="5" equiv-text="</code>"/> must be
|
||||||
routed to the outpost (when using a managed outpost, this is done for you).</source>
|
routed to the outpost (when using a managed outpost, this is done for you).</source>
|
||||||
|
<target>Utiliser ce fournisseur avec nginx <x id="0" equiv-text="<code>"/>auth_request<x id="1" equiv-text="</code>"/> ou traefik
|
||||||
|
<x id="2" equiv-text="<code>"/>forwardAuth<x id="3" equiv-text="</code>"/>. Chaque application/domaine a besoin de son fournisseur.
|
||||||
|
De plus, sur chaque domaine, <x id="4" equiv-text="<code>"/>/outpost.goauthentik.io<x id="5" equiv-text="</code>"/> doit être
|
||||||
|
routé vers l'avant-post (lors de l'utilisation d'un avant-poste managé, cela est fait automatiquement).</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sd18b18f91b804c3f">
|
<trans-unit id="sd18b18f91b804c3f">
|
||||||
<source>Custom attributes</source>
|
<source>Custom attributes</source>
|
||||||
|
@ -7802,87 +7813,127 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s2da4aa7a9abeb653">
|
<trans-unit id="s2da4aa7a9abeb653">
|
||||||
<source>Pseudolocale (for testing)</source>
|
<source>Pseudolocale (for testing)</source>
|
||||||
|
<target>Pseudolocale (pour tests)</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s4bd386db7302bb22">
|
<trans-unit id="s4bd386db7302bb22">
|
||||||
<source>Create With Wizard</source>
|
<source>Create With Wizard</source>
|
||||||
|
<target>Créer avec l'assistant</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s070fdfb03034ca9b">
|
<trans-unit id="s070fdfb03034ca9b">
|
||||||
<source>One hint, 'New Application Wizard', is currently hidden</source>
|
<source>One hint, 'New Application Wizard', is currently hidden</source>
|
||||||
|
<target>Un indice, l'assistant nouvelle application est actuellement caché</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s61bd841e66966325">
|
<trans-unit id="s61bd841e66966325">
|
||||||
<source>External applications that use authentik as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
<source>External applications that use authentik as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||||
|
<target>Applications externes qui utilisent authentik comme fournisseur d'identité, en utilisant des protocoles comme OAuth2 et SAML. Toutes les applications sont affichées ici, même celles auxquelles vous n'avez pas accès.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s1cc306d8e28c4464">
|
<trans-unit id="s1cc306d8e28c4464">
|
||||||
<source>Deny message</source>
|
<source>Deny message</source>
|
||||||
|
<target>Message de refus</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s6985c401e1100122">
|
<trans-unit id="s6985c401e1100122">
|
||||||
<source>Message shown when this stage is run.</source>
|
<source>Message shown when this stage is run.</source>
|
||||||
|
<target>Message affiché lorsque cette étape est exécutée.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s09f0c100d0ad2fec">
|
<trans-unit id="s09f0c100d0ad2fec">
|
||||||
<source>Open Wizard</source>
|
<source>Open Wizard</source>
|
||||||
|
<target>Lancer l'assistant</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sf2ef885f7d0a101d">
|
<trans-unit id="sf2ef885f7d0a101d">
|
||||||
<source>Demo Wizard</source>
|
<source>Demo Wizard</source>
|
||||||
|
<target>Assistant de démo</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s77505ee5d2e45e53">
|
<trans-unit id="s77505ee5d2e45e53">
|
||||||
<source>Run the demo wizard</source>
|
<source>Run the demo wizard</source>
|
||||||
|
<target>Lancer l'assistant de démo</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s4498e890d47a8066">
|
<trans-unit id="s4498e890d47a8066">
|
||||||
<source>OAuth2/OIDC (Open Authorization/OpenID Connect)</source>
|
<source>OAuth2/OIDC (Open Authorization/OpenID Connect)</source>
|
||||||
|
<target>OAuth2/OIDC (Open Authorization/OpenID Connect)</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s4f2e195d09e2868c">
|
<trans-unit id="s4f2e195d09e2868c">
|
||||||
<source>LDAP (Lightweight Directory Access Protocol)</source>
|
<source>LDAP (Lightweight Directory Access Protocol)</source>
|
||||||
|
<target>LDAP (Lightweight Directory Access Protocol)</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s7f5bb0c9923315ed">
|
<trans-unit id="s7f5bb0c9923315ed">
|
||||||
<source>Forward Auth (Single Application)</source>
|
<source>Forward Auth (Single Application)</source>
|
||||||
|
<target>Transférer l'authentification (application unique)</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sf8008d2d6b064b95">
|
<trans-unit id="sf8008d2d6b064b95">
|
||||||
<source>Forward Auth (Domain Level)</source>
|
<source>Forward Auth (Domain Level)</source>
|
||||||
|
<target>Transférer l'authentification (niveau domaine)</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sfa8a1ffa9fee07d3">
|
<trans-unit id="sfa8a1ffa9fee07d3">
|
||||||
<source>SAML (Security Assertion Markup Language)</source>
|
<source>SAML (Security Assertion Markup Language)</source>
|
||||||
|
<target>SAML (Security Assertion Markup Language)</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s848a23972e388662">
|
<trans-unit id="s848a23972e388662">
|
||||||
<source>RADIUS (Remote Authentication Dial-In User Service)</source>
|
<source>RADIUS (Remote Authentication Dial-In User Service)</source>
|
||||||
|
<target>RADIUS (Remote Authentication Dial-In User Service)</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s3e902999ddf7b50e">
|
<trans-unit id="s3e902999ddf7b50e">
|
||||||
<source>SCIM (System for Cross-domain Identity Management)</source>
|
<source>SCIM (System for Cross-domain Identity Management)</source>
|
||||||
|
<target>SCIM (System for Cross-domain Identity Management)</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sdc5690be4a342985">
|
<trans-unit id="sdc5690be4a342985">
|
||||||
<source>The token has been copied to your clipboard</source>
|
<source>The token has been copied to your clipboard</source>
|
||||||
|
<target>Le jeton a été copié dans le presse-paper</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s7f3edfee24690c9f">
|
<trans-unit id="s7f3edfee24690c9f">
|
||||||
<source>The token was displayed because authentik does not have permission to write to the clipboard</source>
|
<source>The token was displayed because authentik does not have permission to write to the clipboard</source>
|
||||||
|
<target>Le jeton a été affiché car authentik n'a pas la permission d'écrire dans le presse-papier</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="saf6097bfa25205b8">
|
<trans-unit id="saf6097bfa25205b8">
|
||||||
<source>A copy of this recovery link has been placed in your clipboard</source>
|
<source>A copy of this recovery link has been placed in your clipboard</source>
|
||||||
|
<target>Une copie de ce lien de récupération a été placée dans le presse-papier</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s5b8ee296ed258568">
|
<trans-unit id="s5b8ee296ed258568">
|
||||||
<source>The current tenant must have a recovery flow configured to use a recovery link</source>
|
<source>The current tenant must have a recovery flow configured to use a recovery link</source>
|
||||||
|
<target>Le tenant actuel doit avoir un flux de récupération configuré pour utiliser un lien de récupération</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s895514dda9cb9c94">
|
<trans-unit id="s895514dda9cb9c94">
|
||||||
<source>Create recovery link</source>
|
<source>Create recovery link</source>
|
||||||
|
<target>Créer un lien de récupération</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="se5c795faf2c07514">
|
<trans-unit id="se5c795faf2c07514">
|
||||||
<source>Create Recovery Link</source>
|
<source>Create Recovery Link</source>
|
||||||
|
<target>Créer un lien de récupération</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s84fcddede27b8e2a">
|
<trans-unit id="s84fcddede27b8e2a">
|
||||||
<source>External</source>
|
<source>External</source>
|
||||||
|
<target>Externe</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s1a635369edaf4dc3">
|
<trans-unit id="s1a635369edaf4dc3">
|
||||||
<source>Service account</source>
|
<source>Service account</source>
|
||||||
|
<target>Compte de service</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sff930bf2834e2201">
|
<trans-unit id="sff930bf2834e2201">
|
||||||
<source>Service account (internal)</source>
|
<source>Service account (internal)</source>
|
||||||
|
<target>Compte de service (interne)</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s66313b45b69cfc88">
|
<trans-unit id="s66313b45b69cfc88">
|
||||||
<source>Check the release notes</source>
|
<source>Check the release notes</source>
|
||||||
|
<target>Voir les notes de version</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sb4d7bae2440d9781">
|
<trans-unit id="sb4d7bae2440d9781">
|
||||||
<source>User Statistics</source>
|
<source>User Statistics</source>
|
||||||
|
<target>Statistiques Utilisateur</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s0924f51b028233a3">
|
<trans-unit id="s0924f51b028233a3">
|
||||||
<source><No name set></source>
|
<source><No name set></source>
|
||||||
|
<target><No name set></target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s32babfed740fd3c1">
|
||||||
|
<source>User type used for newly created users.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sdc9a6ad1af30572c">
|
||||||
|
<source>For nginx's auth_request or traefik's forwardAuth</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sfc31264ef7ff86ef">
|
||||||
|
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sc615309d10a9228c">
|
||||||
|
<source>RBAC is in preview.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
|
|
@ -5921,12 +5921,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
<trans-unit id="s945a6b94361ee45b">
|
<trans-unit id="s945a6b94361ee45b">
|
||||||
<source>For transparent reverse proxies with required authentication</source>
|
<source>For transparent reverse proxies with required authentication</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sadf073913458acbd">
|
|
||||||
<source>For nginx's auth_request or traefik's forwardAuth</source>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="se770e9498b3bacf6">
|
|
||||||
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s40830ec037f34626">
|
<trans-unit id="s40830ec037f34626">
|
||||||
<source>Configure SAML provider manually</source>
|
<source>Configure SAML provider manually</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
@ -6154,6 +6148,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s0924f51b028233a3">
|
<trans-unit id="s0924f51b028233a3">
|
||||||
<source><No name set></source>
|
<source><No name set></source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sdc9a6ad1af30572c">
|
||||||
|
<source>For nginx's auth_request or traefik's forwardAuth</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sfc31264ef7ff86ef">
|
||||||
|
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sc615309d10a9228c">
|
||||||
|
<source>RBAC is in preview.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s32babfed740fd3c1">
|
||||||
|
<source>User type used for newly created users.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
|
|
@ -7557,14 +7557,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
<source>For transparent reverse proxies with required authentication</source>
|
<source>For transparent reverse proxies with required authentication</source>
|
||||||
<target>Ƒōŕ ţŕàńśƥàŕēńţ ŕēvēŕśē ƥŕōxĩēś ŵĩţĥ ŕēǫũĩŕēď àũţĥēńţĩćàţĩōń</target>
|
<target>Ƒōŕ ţŕàńśƥàŕēńţ ŕēvēŕśē ƥŕōxĩēś ŵĩţĥ ŕēǫũĩŕēď àũţĥēńţĩćàţĩōń</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sadf073913458acbd">
|
|
||||||
<source>For nginx's auth_request or traefik's forwardAuth</source>
|
|
||||||
<target>Ƒōŕ ńĝĩńx'ś àũţĥ_ŕēǫũēśţ ōŕ ţŕàēƒĩx'ś ƒōŕŵàŕďÀũţĥ</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="se770e9498b3bacf6">
|
|
||||||
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
|
||||||
<target>Ƒōŕ ńĝĩńx'ś àũţĥ_ŕēǫũēśţ ōŕ ţŕàēƒĩx'ś ƒōŕŵàŕďÀũţĥ ƥēŕ ŕōōţ ďōmàĩń</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s40830ec037f34626">
|
<trans-unit id="s40830ec037f34626">
|
||||||
<source>Configure SAML provider manually</source>
|
<source>Configure SAML provider manually</source>
|
||||||
<target>Ćōńƒĩĝũŕē ŚÀMĹ ƥŕōvĩďēŕ màńũàĺĺŷ</target>
|
<target>Ćōńƒĩĝũŕē ŚÀMĹ ƥŕōvĩďēŕ màńũàĺĺŷ</target>
|
||||||
|
@ -7844,4 +7836,16 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
<trans-unit id="s0924f51b028233a3">
|
<trans-unit id="s0924f51b028233a3">
|
||||||
<source><No name set></source>
|
<source><No name set></source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="sdc9a6ad1af30572c">
|
||||||
|
<source>For nginx's auth_request or traefik's forwardAuth</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sfc31264ef7ff86ef">
|
||||||
|
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sc615309d10a9228c">
|
||||||
|
<source>RBAC is in preview.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s32babfed740fd3c1">
|
||||||
|
<source>User type used for newly created users.</source>
|
||||||
|
</trans-unit>
|
||||||
</body></file></xliff>
|
</body></file></xliff>
|
||||||
|
|
|
@ -5706,12 +5706,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
<trans-unit id="s945a6b94361ee45b">
|
<trans-unit id="s945a6b94361ee45b">
|
||||||
<source>For transparent reverse proxies with required authentication</source>
|
<source>For transparent reverse proxies with required authentication</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sadf073913458acbd">
|
|
||||||
<source>For nginx's auth_request or traefik's forwardAuth</source>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="se770e9498b3bacf6">
|
|
||||||
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s40830ec037f34626">
|
<trans-unit id="s40830ec037f34626">
|
||||||
<source>Configure SAML provider manually</source>
|
<source>Configure SAML provider manually</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
@ -5939,6 +5933,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s0924f51b028233a3">
|
<trans-unit id="s0924f51b028233a3">
|
||||||
<source><No name set></source>
|
<source><No name set></source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sdc9a6ad1af30572c">
|
||||||
|
<source>For nginx's auth_request or traefik's forwardAuth</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sfc31264ef7ff86ef">
|
||||||
|
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sc615309d10a9228c">
|
||||||
|
<source>RBAC is in preview.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s32babfed740fd3c1">
|
||||||
|
<source>User type used for newly created users.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
|
|
@ -7620,14 +7620,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
<source>For transparent reverse proxies with required authentication</source>
|
<source>For transparent reverse proxies with required authentication</source>
|
||||||
<target>适用于需要验证身份的透明反向代理</target>
|
<target>适用于需要验证身份的透明反向代理</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sadf073913458acbd">
|
|
||||||
<source>For nginx's auth_request or traefik's forwardAuth</source>
|
|
||||||
<target>适用于 nginx 的 auth_request 或 traefik 的 forwardAuth</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="se770e9498b3bacf6">
|
|
||||||
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
|
||||||
<target>适用于按根域名配置的 nginx 的 auth_request 或 traefik 的 forwardAuth</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s40830ec037f34626">
|
<trans-unit id="s40830ec037f34626">
|
||||||
<source>Configure SAML provider manually</source>
|
<source>Configure SAML provider manually</source>
|
||||||
<target>手动配置 SAML 提供程序</target>
|
<target>手动配置 SAML 提供程序</target>
|
||||||
|
@ -7912,21 +7904,39 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s84fcddede27b8e2a">
|
<trans-unit id="s84fcddede27b8e2a">
|
||||||
<source>External</source>
|
<source>External</source>
|
||||||
|
<target>外部</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s1a635369edaf4dc3">
|
<trans-unit id="s1a635369edaf4dc3">
|
||||||
<source>Service account</source>
|
<source>Service account</source>
|
||||||
|
<target>服务账户</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sff930bf2834e2201">
|
<trans-unit id="sff930bf2834e2201">
|
||||||
<source>Service account (internal)</source>
|
<source>Service account (internal)</source>
|
||||||
|
<target>服务账户(内部)</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s66313b45b69cfc88">
|
<trans-unit id="s66313b45b69cfc88">
|
||||||
<source>Check the release notes</source>
|
<source>Check the release notes</source>
|
||||||
|
<target>查看发行日志</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sb4d7bae2440d9781">
|
<trans-unit id="sb4d7bae2440d9781">
|
||||||
<source>User Statistics</source>
|
<source>User Statistics</source>
|
||||||
|
<target>用户统计</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s0924f51b028233a3">
|
<trans-unit id="s0924f51b028233a3">
|
||||||
<source><No name set></source>
|
<source><No name set></source>
|
||||||
|
<target><未设置名称></target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sdc9a6ad1af30572c">
|
||||||
|
<source>For nginx's auth_request or traefik's forwardAuth</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sfc31264ef7ff86ef">
|
||||||
|
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sc615309d10a9228c">
|
||||||
|
<source>RBAC is in preview.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s32babfed740fd3c1">
|
||||||
|
<source>User type used for newly created users.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
|
|
@ -5754,12 +5754,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
<trans-unit id="s945a6b94361ee45b">
|
<trans-unit id="s945a6b94361ee45b">
|
||||||
<source>For transparent reverse proxies with required authentication</source>
|
<source>For transparent reverse proxies with required authentication</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sadf073913458acbd">
|
|
||||||
<source>For nginx's auth_request or traefik's forwardAuth</source>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="se770e9498b3bacf6">
|
|
||||||
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s40830ec037f34626">
|
<trans-unit id="s40830ec037f34626">
|
||||||
<source>Configure SAML provider manually</source>
|
<source>Configure SAML provider manually</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
@ -5987,6 +5981,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s0924f51b028233a3">
|
<trans-unit id="s0924f51b028233a3">
|
||||||
<source><No name set></source>
|
<source><No name set></source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sdc9a6ad1af30572c">
|
||||||
|
<source>For nginx's auth_request or traefik's forwardAuth</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sfc31264ef7ff86ef">
|
||||||
|
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sc615309d10a9228c">
|
||||||
|
<source>RBAC is in preview.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s32babfed740fd3c1">
|
||||||
|
<source>User type used for newly created users.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
|
|
@ -658,11 +658,6 @@
|
||||||
<source>Manage users</source>
|
<source>Manage users</source>
|
||||||
<target>管理用户</target>
|
<target>管理用户</target>
|
||||||
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s51cda7b1ebcd970f">
|
|
||||||
<source>Check release notes</source>
|
|
||||||
<target>查看发行日志</target>
|
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s8763a33c3d46aaf5">
|
<trans-unit id="s8763a33c3d46aaf5">
|
||||||
<source>Outpost status</source>
|
<source>Outpost status</source>
|
||||||
|
@ -694,11 +689,6 @@
|
||||||
<source>Objects created</source>
|
<source>Objects created</source>
|
||||||
<target>已创建对象</target>
|
<target>已创建对象</target>
|
||||||
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s770baa8560fd8aa1">
|
|
||||||
<source>User statistics</source>
|
|
||||||
<target>用户统计</target>
|
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sfbadb77fbc61efb8">
|
<trans-unit id="sfbadb77fbc61efb8">
|
||||||
<source>Users created per day in the last month</source>
|
<source>Users created per day in the last month</source>
|
||||||
|
@ -7919,6 +7909,30 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
<trans-unit id="se5c795faf2c07514">
|
<trans-unit id="se5c795faf2c07514">
|
||||||
<source>Create Recovery Link</source>
|
<source>Create Recovery Link</source>
|
||||||
<target>创建恢复链接</target>
|
<target>创建恢复链接</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s84fcddede27b8e2a">
|
||||||
|
<source>External</source>
|
||||||
|
<target>外部</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s1a635369edaf4dc3">
|
||||||
|
<source>Service account</source>
|
||||||
|
<target>服务账户</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sff930bf2834e2201">
|
||||||
|
<source>Service account (internal)</source>
|
||||||
|
<target>服务账户(内部)</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s66313b45b69cfc88">
|
||||||
|
<source>Check the release notes</source>
|
||||||
|
<target>查看发行日志</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sb4d7bae2440d9781">
|
||||||
|
<source>User Statistics</source>
|
||||||
|
<target>用户统计</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s0924f51b028233a3">
|
||||||
|
<source><No name set></source>
|
||||||
|
<target><未设置名称></target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
|
|
@ -5753,12 +5753,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
<trans-unit id="s945a6b94361ee45b">
|
<trans-unit id="s945a6b94361ee45b">
|
||||||
<source>For transparent reverse proxies with required authentication</source>
|
<source>For transparent reverse proxies with required authentication</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sadf073913458acbd">
|
|
||||||
<source>For nginx's auth_request or traefik's forwardAuth</source>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="se770e9498b3bacf6">
|
|
||||||
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s40830ec037f34626">
|
<trans-unit id="s40830ec037f34626">
|
||||||
<source>Configure SAML provider manually</source>
|
<source>Configure SAML provider manually</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
@ -5986,6 +5980,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s0924f51b028233a3">
|
<trans-unit id="s0924f51b028233a3">
|
||||||
<source><No name set></source>
|
<source><No name set></source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sdc9a6ad1af30572c">
|
||||||
|
<source>For nginx's auth_request or traefik's forwardAuth</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sfc31264ef7ff86ef">
|
||||||
|
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sc615309d10a9228c">
|
||||||
|
<source>RBAC is in preview.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s32babfed740fd3c1">
|
||||||
|
<source>User type used for newly created users.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
---
|
||||||
|
title: 3 ways you (might be) doing containers wrong
|
||||||
|
description: “Using containers is not a best practice in itself. Here are some mistakes beginners make with containers, and how we set them up correctly at authentik.”
|
||||||
|
authors:
|
||||||
|
- name: Jens Langhammer
|
||||||
|
title: CTO at Authentik Security Inc
|
||||||
|
url: https://github.com/BeryJu
|
||||||
|
image_url: https://github.com/BeryJu.png
|
||||||
|
tags:
|
||||||
|
- application
|
||||||
|
- runtime
|
||||||
|
- SSO
|
||||||
|
- Docker
|
||||||
|
- containers
|
||||||
|
- :latest
|
||||||
|
- identity provider
|
||||||
|
- security
|
||||||
|
- authentication
|
||||||
|
hide_table_of_contents: false
|
||||||
|
---
|
||||||
|
|
||||||
|
_authentik is an open source Identity Provider that unifies your identity needs into a single platform, replacing Okta, Active Directory, and Auth0. Authentik Security is a [public benefit company](https://github.com/OpenCoreVentures/ocv-public-benefit-company/blob/main/ocv-public-benefit-company-charter.md) building on top of the open source project._
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
There are two ways to judge an application:
|
||||||
|
|
||||||
|
1. Does it do what it’s supposed to do?
|
||||||
|
2. Is it easy to run?
|
||||||
|
|
||||||
|
This post is about the second.
|
||||||
|
|
||||||
|
Using containers is not a best practice in itself. As an infrastructure engineer by background, I’m pretty opinionated about how to set up containers properly. Doing things the “right” way makes things easier not just for you, but for your users as well.
|
||||||
|
|
||||||
|
Below are some common mistakes that I see beginners make with containers:
|
||||||
|
|
||||||
|
1. Using one container per application
|
||||||
|
2. Installing things at runtime
|
||||||
|
3. Writing logs to files instead of stdout
|
||||||
|
|
||||||
|
## Mistake #1: One container per application
|
||||||
|
|
||||||
|
There tend to be two mindsets when approaching setting up containers:
|
||||||
|
|
||||||
|
- The inexperienced usually think 1 container = 1 application
|
||||||
|
- The other option is 1 container = 1 service
|
||||||
|
|
||||||
|
Your application usually consists of multiple services, and to my mind these should always be separated into their own containers (in keeping with the [Single Responsibility Principle](https://en.wikipedia.org/wiki/Single-responsibility_principle)).
|
||||||
|
|
||||||
|
For example, authentik consists of four components (services):
|
||||||
|
|
||||||
|
- Server
|
||||||
|
- Worker
|
||||||
|
- Database
|
||||||
|
- Cache
|
||||||
|
|
||||||
|
With our deployment, that means you get four different containers because they each run one of those four services.
|
||||||
|
|
||||||
|
### Why you should use one container per _service_
|
||||||
|
|
||||||
|
At the point where you need to scale, or need High Availability, having different processes in separate containers enables horizontal scaling. Because of how authentik deploys, if we need to handle more traffic we can scale up to 50 servers, rather than having to scale up _everything_. This wouldn’t work if all those components were all bundled together.
|
||||||
|
|
||||||
|
Additionally, if you’re using a container orchestrator (whether that’s Kubernetes or something simpler like [Docker Compose](https://goauthentik.io/docs/installation/docker-compose)), if it’s all bundled together, the orchestrator can’t distinguish between components because they’re all in the black box of your container.
|
||||||
|
|
||||||
|
Say you want to start up processes in a specific order. This isn’t possible if they’re in a single container (unless you rebuild the entire image). If those processes are separate, you can just tell Docker Compose to start them up in the order you want, or you can run specific components on specific servers.
|
||||||
|
|
||||||
|
Of course, your application architecture and deployment model need to support this setup, which is why it’s critical to think about these things when you’re starting out. If you’re reading this and thinking, I have a small-scale, hobby project, this doesn’t apply to me—let me put it this way: you will never regret setting things up the “right” way. It’s not going to come back to bite you if your situation changes later. It also gives users who install the application a lot more freedom and flexibility in how _they_ want to run it.
|
||||||
|
|
||||||
|
## Mistake #2: Installing things at runtime
|
||||||
|
|
||||||
|
Your container image should be complete in itself: it should contain all code and dependencies—everything it needs to run. This is the point of a container—it’s self contained.
|
||||||
|
|
||||||
|
I’ve seen people set up their container to download an application from the vendor and install it into the container on startup. While this does work, what happens if you don’t have internet access? What if the vendor shut down and that URL now points to a malicious bit of code?
|
||||||
|
|
||||||
|
If you have 100 instances downloading files at startup (or end up scaling to that point), this can lead to rate limiting, failed downloads, or your internet connection getting saturated—it’s just inefficient and causes problems that can be avoided.
|
||||||
|
|
||||||
|
### Also, don’t use :latest
|
||||||
|
|
||||||
|
This leads me to a different but related bad practice: using the `:latest` tag. It’s a common pitfall for folks who use containers but don’t necessarily build them themselves.
|
||||||
|
|
||||||
|
It’s easy to get started with the `:latest` tag and it’s understandable to want the latest version without having to go into files and manually edit everything. But what can happen is that you update and suddenly it’s pointing to a new version and breaking things.
|
||||||
|
|
||||||
|
I’ve seen this happen where you’re just running something on a local server and your disk is full, so you empty out your Docker images. The next time you pull, it’s with a new version which now no longer works and you’re stuck trying to figure out what version you were on before.
|
||||||
|
|
||||||
|
### Instead: Pin your dependencies
|
||||||
|
|
||||||
|
You should be pinning your dependencies to a specific version, and updating to newer versions intentionally rather than by default.
|
||||||
|
|
||||||
|
The most reliable way to do this is with a process called GitOps:
|
||||||
|
|
||||||
|
- In the context of Kubernetes, all the YAML files you deploy with Kubernetes are stored in the central Git repository.
|
||||||
|
- You have software in your Kubernetes cluster that automatically pulls the files from your Git repo and installs them into the cluster.
|
||||||
|
- Then you can use a tool like [Dependabot](https://github.com/dependabot) or [Renovate](https://github.com/renovatebot/renovate) to automatically create PRs with a new version (if there is one) so you can test and approve it, and it’s all captured in your Git history.
|
||||||
|
|
||||||
|
GitOps might be a bit excessive if you’re only running a small hobby project on a single server, but in any case you should still pin a version.
|
||||||
|
|
||||||
|
For a long time, authentik purposefully didn’t have a `:latest` tag, because people would use it inadvertently (sometimes not realizing they had an auto-updater running). Suddenly something wouldn’t work and there wasn’t really a way to downgrade.
|
||||||
|
|
||||||
|
We have since added it due to popular request. This is how authentik’s version tags work:
|
||||||
|
|
||||||
|
- Our version number is 3 digits reflecting the date of the release, so the latest currently is [2023.10.1](https://goauthentik.io/docs/releases/2023.10).
|
||||||
|
- You can either use 2023.10.1 as the tag, pinning to that specific version
|
||||||
|
- You can pin to 2023.10, which you means that you always get the latest patch version, or
|
||||||
|
- You can use 2023, which means you always get the latest version within that year.
|
||||||
|
|
||||||
|
The principle is roughly the same with any project using [SemVer](https://semver.org/): you could just lock to v1, which means you get the latest v1 with all minor patches and fixes, without breaking updates. Then you switch to v2 when you’re ready.
|
||||||
|
|
||||||
|
With this approach you are putting some trust in the developer not to publish any breaking changes with the wrong version number (but you’re technically always putting trust in some developer when using someone else’s software!).
|
||||||
|
|
||||||
|
## Mistake #3: Writing logs to files instead of stdout
|
||||||
|
|
||||||
|
This is another issue on the infrastructure side that mainly happens when you put legacy applications into containers. It used to be standard that applications put their log output into a file, and you’d probably have a system daemon set up to rotate those files and archive the old ones. This was great when everything ran on the same server without containers.
|
||||||
|
|
||||||
|
A lot of software still logs to files by default, but this makes collecting and aggregating your services logs much harder. Docker (and containers in general) expect that you log to standard output so your orchestration platform can route the logs to your monitoring tool of choice.
|
||||||
|
|
||||||
|
Docker puts the logs into a JSON file that it can read itself and see the timestamps and which container the log refers to. You can set up log forwarding with both Docker and Kubernetes. If you have a central logging server, the plugin gets the standard output of a container and sends it to that server.
|
||||||
|
|
||||||
|
Not logging to `stdout` just makes it harder for everyone, including making it harder to debug: Instead of just running `docker logs` + the name of the container, you need to `exec` into the container, go to find the files, then look at the files to start debugging.
|
||||||
|
|
||||||
|
### This bad practice is arguably the easiest one to work around
|
||||||
|
|
||||||
|
As an engineer you can easily redirect the logs back from a file into the standard output, but there’s no real reason not to do it the “correct” way.
|
||||||
|
|
||||||
|
There aren’t many use cases where there’s an advantage to writing your logs directly to a file instead of stdout—in fact the main one is for when you’re making the first mistake (having your whole application in one container)! If you’re running multiple services in one container, then you’ll have logs from multiple different processes in one place, which _could_ be easier to work with in a file vs stdout.
|
||||||
|
|
||||||
|
Even if you specifically want your logs to exist in a file, by default if you run `docker logs` it just reads a JSON file that it adds the logs to, so you’re not losing anything by logging to stdout. You can configure Docker to just put the logs into a plain text file wherever you want to.
|
||||||
|
|
||||||
|
It’s a little simplistic, but I’d encourage you to check out [The Twelve-Factor App](https://12factor.net/) which outlines good practices for making software that’s easy to run.
|
||||||
|
|
||||||
|
Are you doing containers differently and is it working for you? Let us know in the comments, or send us an email at hello@goauthentik.io!
|
|
@ -66,7 +66,7 @@ return ak_is_group_member(request.user, name="test_group")
|
||||||
|
|
||||||
Fetch a user matching `**filters`.
|
Fetch a user matching `**filters`.
|
||||||
|
|
||||||
Returns "None" if no user was found, otherwise returns the [User](/docs/user-group/user) object.
|
Returns "None" if no user was found, otherwise returns the [User](/docs/user-group-role/user) object.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
- `user`: The current user. This may be `None` if there is no contextual user. See [User](../user-group/user/user_ref.md#object-properties).
|
- `user`: The current user. This may be `None` if there is no contextual user. See [User](../user-group-role/user/user_ref.md#object-properties).
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ Keys prefixed with `goauthentik.io` are used internally by authentik and are sub
|
||||||
|
|
||||||
### Common keys
|
### Common keys
|
||||||
|
|
||||||
#### `pending_user` ([User object](../../user-group/user/user_ref.md#object-properties))
|
#### `pending_user` ([User object](../../user-group-role/user/user_ref.md#object-properties))
|
||||||
|
|
||||||
`pending_user` is used by multiple stages. In the context of most flow executions, it represents the data of the user that is executing the flow. This value is not set automatically, it is set via the [Identification stage](../stages/identification/).
|
`pending_user` is used by multiple stages. In the context of most flow executions, it represents the data of the user that is executing the flow. This value is not set automatically, it is set via the [Identification stage](../stages/identification/).
|
||||||
|
|
||||||
|
@ -110,9 +110,9 @@ Optionally overwrite the deny message shown, has a higher priority than the mess
|
||||||
|
|
||||||
#### User write stage
|
#### User write stage
|
||||||
|
|
||||||
##### `groups` (List of [Group objects](../../user-group/group.md))
|
##### `groups` (List of [Group objects](../../user-group-role/groups/index.mdx))
|
||||||
|
|
||||||
See [Group](../../user-group/group.md). If set in the flow context, the `pending_user` will be added to all the groups in this list.
|
See [Group](../../user-group-role/groups/index.mdx). If set in the flow context, the `pending_user` will be added to all the groups in this list.
|
||||||
|
|
||||||
If set, this must be a list of group objects and not group names.
|
If set, this must be a list of group objects and not group names.
|
||||||
|
|
||||||
|
@ -120,6 +120,14 @@ If set, this must be a list of group objects and not group names.
|
||||||
|
|
||||||
Path the `pending_user` will be written to. If not set in the flow, falls back to the value set in the user_write stage, and otherwise to the `users` path.
|
Path the `pending_user` will be written to. If not set in the flow, falls back to the value set in the user_write stage, and otherwise to the `users` path.
|
||||||
|
|
||||||
|
##### `user_type` (string)
|
||||||
|
|
||||||
|
:::info
|
||||||
|
Requires authentik 2023.10
|
||||||
|
:::
|
||||||
|
|
||||||
|
Type the `pending_user` will be created as. Must be one of `internal`, `external` or `service_account`.
|
||||||
|
|
||||||
#### Password stage
|
#### Password stage
|
||||||
|
|
||||||
##### `user_backend` (string)
|
##### `user_backend` (string)
|
||||||
|
|
|
@ -41,7 +41,7 @@ import Objects from "../expressions/_objects.md";
|
||||||
|
|
||||||
- `request`: A PolicyRequest object, which has the following properties:
|
- `request`: A PolicyRequest object, which has the following properties:
|
||||||
|
|
||||||
- `request.user`: The current user, against which the policy is applied. See [User](../user-group/user/user_ref.md#object-properties)
|
- `request.user`: The current user, against which the policy is applied. See [User](../user-group-role/user/user_ref.md#object-properties)
|
||||||
|
|
||||||
:::caution
|
:::caution
|
||||||
When a policy is executed in the context of a flow, this will be set to the user initiaing request, and will only be changed by a `user_login` stage. For that reason, using this value in authentication flow policies may not return the expected user. Use `context['pending_user']` instead; User Identification and other stages update this value during flow execution.
|
When a policy is executed in the context of a flow, this will be set to the user initiaing request, and will only be changed by a `user_login` stage. For that reason, using this value in authentication flow policies may not return the expected user. Use `context['pending_user']` instead; User Identification and other stages update this value during flow execution.
|
||||||
|
@ -77,7 +77,7 @@ This includes the following:
|
||||||
- `context['prompt_data']`: Data which has been saved from a prompt stage or an external source. (Optional)
|
- `context['prompt_data']`: Data which has been saved from a prompt stage or an external source. (Optional)
|
||||||
- `context['application']`: The application the user is in the process of authorizing. (Optional)
|
- `context['application']`: The application the user is in the process of authorizing. (Optional)
|
||||||
- `context['source']`: The source the user is authenticating/enrolling with. (Optional)
|
- `context['source']`: The source the user is authenticating/enrolling with. (Optional)
|
||||||
- `context['pending_user']`: The currently pending user, see [User](../user-group/user/user_ref.md#object-properties)
|
- `context['pending_user']`: The currently pending user, see [User](../user-group-role/user/user_ref.md#object-properties)
|
||||||
- `context['is_restored']`: Contains the flow token when the flow plan was restored from a link, for example the user clicked a link to a flow which was sent by an email stage. (Optional)
|
- `context['is_restored']`: Contains the flow token when the flow plan was restored from a link, for example the user clicked a link to a flow which was sent by an email stage. (Optional)
|
||||||
- `context['auth_method']`: Authentication method (this value is set by password stages) (Optional)
|
- `context['auth_method']`: Authentication method (this value is set by password stages) (Optional)
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
After Width: | Height: | Size: 116 KiB |
|
@ -0,0 +1,16 @@
|
||||||
|
---
|
||||||
|
title: About access control
|
||||||
|
---
|
||||||
|
|
||||||
|
import DocCardList from "@theme/DocCardList";
|
||||||
|
import { useCurrentSidebarCategory } from "@docusaurus/theme-common";
|
||||||
|
|
||||||
|
To comply with important regulations such as PCI-DSS, HIPAA, SOC 2, and GDPR, it's necessary to have the ability to control which users have access to specific areas of the system, what [permissions](./permissions.md) they have globally and on certain objects, and a way to monitor [events](../../events) related to user activity.
|
||||||
|
|
||||||
|
In authentik, we provide role-based access control (RBAC), an industry standard for managing access control. By carefully designing roles with appropriate permissions, and then assigning those roles to groups, RBAC provides a fine-tuned approach to controlling user access.
|
||||||
|
|
||||||
|
RBAC is a way of ensuring the well-known [principal of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege) whereby "every module (such as a process, a user, or a program, depending on the subject) must be able to access only the information and resources that are necessary for its legitimate purpose."
|
||||||
|
|
||||||
|
To learn more about access control with authentik, refer to these topics:
|
||||||
|
|
||||||
|
<DocCardList items={useCurrentSidebarCategory().items} />
|
|
@ -0,0 +1,118 @@
|
||||||
|
---
|
||||||
|
title: "Manage permissions"
|
||||||
|
description: "Learn how to use global and object permissions in authentik."
|
||||||
|
---
|
||||||
|
|
||||||
|
Refer to the following topics for instructions to view and manage permissions.
|
||||||
|
|
||||||
|
## View permissions
|
||||||
|
|
||||||
|
You can view all permissions that are assigned to a user, group, role, flow, or stage.
|
||||||
|
|
||||||
|
### View user, group, and role permissions
|
||||||
|
|
||||||
|
To view _object_ permissions for a specific user, role, or group:
|
||||||
|
|
||||||
|
1. Go to the Admin interface and navigate to **Directory**.
|
||||||
|
2. Select either **Users**, **Groups**, or **Roles**
|
||||||
|
3. Select a specific user/group/role by clicking on the name (this opens the details page).
|
||||||
|
4. Click the **Assigned Permissions** tab at the top of the page (to the right of the **Permissions** tab).
|
||||||
|
5. Scroll down to see both the global and object-level permissions.
|
||||||
|
|
||||||
|
:::info
|
||||||
|
Note that groups do not have global permissions.
|
||||||
|
:::
|
||||||
|
|
||||||
|
### View flow permissions
|
||||||
|
|
||||||
|
1. Go to the Admin interface and navigate to **Flows and Stages -> Flows**.
|
||||||
|
2. Click the name of the flow (this opens the details page).
|
||||||
|
3. Click the **Permissions** tab at the top of the page.
|
||||||
|
4. View the assigned permissions using the **User Object Permissions** and the **Role Object Permissions** tabs.
|
||||||
|
|
||||||
|
### View stage permissions
|
||||||
|
|
||||||
|
1. Go to the Admin interface and navigate to **Flows and Stages -> Stagess**.
|
||||||
|
2. On the row for the specific stage whose permissions you want to view, click the lock icon.
|
||||||
|
3. On the **Update Permissions** tab, you can view the assigned permissions using the **User Object Permissions** and the **Role Object Permissions** tabs.
|
||||||
|
|
||||||
|
## Manage permissions
|
||||||
|
|
||||||
|
You can assign or remove permissions to a user, role, group, flow, or stage.
|
||||||
|
|
||||||
|
### Assign, modify, or remove permissions for a user
|
||||||
|
|
||||||
|
To assign or remove _object_ permissions for a specific user:
|
||||||
|
|
||||||
|
1. Go to the Admin interface and navigate to **Directory -> Users**.
|
||||||
|
2. Select a specific user by clicking on the user's name.
|
||||||
|
3. Click the **Permissions** tab at the top of the page.
|
||||||
|
4. To assign or remove permissions that another _user_ has on this specific user:
|
||||||
|
1. Click the **User Object Permissions** tab, click **Assign to new user**.
|
||||||
|
2. In the **User** drop-down, select the user object.
|
||||||
|
3. Use the toggles to set which permissions on that selected user object you want to grant to (or remove from) the specific user.
|
||||||
|
4. Click **Assign** to save your settings and close the modal.
|
||||||
|
5. To assign or remove permissions that another _role_ has on this specific user:
|
||||||
|
Click the **Role Object Permissions** tab, click **Assign to new role**. 2. In the **User** drop-down, select the user object. 3. Use the toggles to set which permissions you want to grant to (or remove from) the selected role. 4. Click **Assign** to save your settings and close the modal.
|
||||||
|
|
||||||
|
To assign or remove _global_ permissions for a user:
|
||||||
|
|
||||||
|
1. Go to the Admin interface and navigate to **Directory -> Users**.
|
||||||
|
2. Select a specific user the clicking on the user's name.
|
||||||
|
3. Click the **Assigned Permissions** tab at the top of the page (to the right of the **Permissions** tab).
|
||||||
|
4. In the **Assigned Global Permissions** area, click **Assign Permission**.
|
||||||
|
5. In the **Assign permissions to user** modal, click the plus sign (**+**) and then click the checkbox beside each permission that you want to assign to the user. To remove permissions, deselect the checkbox.
|
||||||
|
6. Click **Add**, and then click **Assign** to save your changes and close the modal.
|
||||||
|
|
||||||
|
### Assign or remove permissions on a specific group
|
||||||
|
|
||||||
|
:::info
|
||||||
|
Note that groups themselves do not have permissions. Rather, users and roles have permissions assigned that allow them to create, modify, delete, etc., a group.
|
||||||
|
Also there are no global permissions for groups.
|
||||||
|
:::
|
||||||
|
|
||||||
|
To assign or remove _object_ permissions on a specific group by users and roles:
|
||||||
|
|
||||||
|
1. Go to the Admin interface and navigate to **Directory -> Groups**.
|
||||||
|
2. Select a specific group by clicking the the group's name.
|
||||||
|
3. Click the **Permissions** tab at the top of the page.
|
||||||
|
To assign or remove permissions that another _user_ has on this specific group:
|
||||||
|
1. Click the **User Object Permissions** tab, click **Assign to new user**.
|
||||||
|
2. In the **User** drop-down, select the user object.
|
||||||
|
3. Use the toggles to set which permissions on that selected group you want to grant to (or remove from) the specific user.
|
||||||
|
4. Click **Assign** to save your settings and close the modal.
|
||||||
|
4. To assign or remove permissions that another _role_ has on this specific group:
|
||||||
|
Click the **Role Object Permissions** tab, click **Assign to new role**. 2. In the **Role** drop-down, select the role. 3. Use the toggles to set which permissions you want to grant to (or remove from ) the selected role. 4. Click **Assign** to save your settings and close the modal.
|
||||||
|
|
||||||
|
### Assign or remove permissions for a specific role
|
||||||
|
|
||||||
|
To assign or remove _object_ permissions for a specific role:
|
||||||
|
|
||||||
|
1. Go to the Admin interface and navigate to **Directory -> Roles**.
|
||||||
|
2. Select a specific role the clicking on the role's name.
|
||||||
|
3. Click the **Permissions** tab at the top of the page.
|
||||||
|
To assign or remove permissions that another _user_ has on this specific role: 1. Click the **User Object Permissions** tab, click **Assign to new user**. 2. In the **User** drop-down, select the user object. 3. Use the toggles to set which permissions on that role you want to grant to (or remove from) the selected user. 4. Click **Assign** to save your settings and close the modal.
|
||||||
|
4. To assign or remove permissions that another _role_ has on this specific group:
|
||||||
|
Click the **Role Object Permissions** tab, click **Assign to new role**. 2. In the **Role** drop-down, select the role. 3. Use the toggles to set which permissions you want to grant to (or remove from) the selected role. 4. Click **Assign** to save your settings and close the modal.
|
||||||
|
|
||||||
|
To assign or remove _global_ permissions for a role:
|
||||||
|
|
||||||
|
1. Go to the Admin interface and navigate to **Directory -> Roles**.
|
||||||
|
2. Select a specific role by clicking on the role's name.
|
||||||
|
3. The **Overview** tab at the top of the page displays all assigned global permissions for the role.
|
||||||
|
4. In the **Assigned Global Permissions** area, click **Assign Permission**.
|
||||||
|
5. In the **Assign permissions to role** modal, click the plus sign (**+**) and then click the checkbox beside each permission that you want to assign to the role. To remove permissions, deselect the checkbox.
|
||||||
|
6. Click **Assign** to save your changes and close the modal.
|
||||||
|
|
||||||
|
### Assign or remove flow permissions
|
||||||
|
|
||||||
|
1. Go to the Admin interface and navigate to **Flows and Stages -> Flows**.
|
||||||
|
2. Click the name of the flow (this opens the details page).
|
||||||
|
3. Click the **Permissions** tab at the top of the page.
|
||||||
|
4. Add or remove permissions using the **User Object Permissions** and the **Role Object Permissions** tabs.
|
||||||
|
|
||||||
|
### Assign or remove stage permissions
|
||||||
|
|
||||||
|
1. Go to the Admin interface and navigate to **Flows and Stages -> Stagess**.
|
||||||
|
2. On the row for the specific stage that you want to manage permissions, click the lock icon.
|
||||||
|
3. On the **Update Permissions** tab, you can add or remove the assigned permissions using the **User Object Permissions** and the **Role Object Permissions** tabs.
|
|
@ -0,0 +1,44 @@
|
||||||
|
---
|
||||||
|
title: "About permissions"
|
||||||
|
description: "Learn about global and object permissions in authentik."
|
||||||
|
---
|
||||||
|
|
||||||
|
Permissions are the central components in all access control systems, the lowest-level components, the controlling pieces of access data. Permissions are assigned to (or removed from!) to define exactly WHO can do WHAT to WHICH part of the overall software system.
|
||||||
|
|
||||||
|
:::info
|
||||||
|
Note that global and object permissions only apply to objects within authentik, and not to who can access certain applications (which are access-controlled using [policies](../../policies/index.md).
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Fundamentals of authentik permissions
|
||||||
|
|
||||||
|
There are two main types of permissions in authentik:
|
||||||
|
|
||||||
|
- [**Global permissions**](#global-permissions)
|
||||||
|
- [**Object permissions**](#object-permissions)
|
||||||
|
|
||||||
|
### Global permissions
|
||||||
|
|
||||||
|
Global permissions define who can do what on a global level across the entire system. Some examples in authentik are the ability to add new [flows](../../flow/index.md) or to create a URL for users to recover their login credentials.
|
||||||
|
|
||||||
|
You can assign _global permissions_ to individual [users](../user/index.mdx) or to [roles](../roles/index.mdx). The most common and best practice is to assign permissions to roles.
|
||||||
|
|
||||||
|
### Object permissions
|
||||||
|
|
||||||
|
Object permissions have two categories:
|
||||||
|
|
||||||
|
- **_User_ object permissions**: defines WHO (which user) can change the **_object_**
|
||||||
|
- **_Role_ object permissions**: defines which ROLE can change the **_object_**
|
||||||
|
|
||||||
|
Object permissions are assigned, as the name indicates, to an object (users, [groups](../groups/index.mdx), roles, flows, and stages), and the assigned permissions state exactly what a user or role can do TO the object (i.e. what permissions does the user or role have on that object).
|
||||||
|
|
||||||
|
When working with object permissions, it is important to understand that when you are viewing the page for an object the permissions table shows which users or roles have permissions ON that object. Those permissions describe what those users or roles can do TO the object detailed on the page.
|
||||||
|
|
||||||
|
For example, the UI below shows a user page for the user named Peter.
|
||||||
|
|
||||||
|
![](./user-page.png)
|
||||||
|
|
||||||
|
You can see in the **User Object Permissions** table that another user, roberto, has permissions on Peter (that is, on the user object Peter).
|
||||||
|
|
||||||
|
Looking at another example, with a flow object called `default-recovery-flow` you can see that the Admin user (akadmin) has all object permissions on the flow, but roberto only has a few permissions on that flow.
|
||||||
|
|
||||||
|
![](./flow-page.png)
|
Binary file not shown.
After Width: | Height: | Size: 119 KiB |
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Group
|
title: About groups
|
||||||
|
description: Learn about groups in authentik
|
||||||
---
|
---
|
||||||
|
|
||||||
## Hierarchy
|
## Hierarchy
|
|
@ -0,0 +1,45 @@
|
||||||
|
---
|
||||||
|
title: Manage groups
|
||||||
|
description: "Learn how to work with groups in authentik."
|
||||||
|
---
|
||||||
|
|
||||||
|
A group is a collection of users. Refer to the following sections to learn how to create and manage groups, assign users and roles to groups, and how [permissions](../access-control/manage_permissions.md) work on a group level.
|
||||||
|
|
||||||
|
## Create a group
|
||||||
|
|
||||||
|
To create a new group, follow these steps:
|
||||||
|
|
||||||
|
1. In the Admin interface, navigate to **Directory > Groups**.
|
||||||
|
2. Click **Create** at the top of the Groups page.
|
||||||
|
3. In the Create modal, define the following:
|
||||||
|
- name of the group
|
||||||
|
- whether or not users in that group will all be superusers (means anyone in that group has all permissions on everything)
|
||||||
|
- the parent group
|
||||||
|
- any custom attributes
|
||||||
|
4. Click **Create**.
|
||||||
|
|
||||||
|
## Modify a group
|
||||||
|
|
||||||
|
To edit the group's name, parent group, whether or not the group is for superusers, associated roles, and any custom attributes, click the Edit icon beside the role's name. Make the changes, and then click **Update**.
|
||||||
|
|
||||||
|
To [add or remove users](../user/user_basic_operations.md#add-a-user-to-a-group) from the group, or to manage permissions assigned to the group, click on the name of the group to go to the group's detail page.
|
||||||
|
|
||||||
|
For more information about permissions, refer to ["Assign or remove permissions for a specific group"](../access-control/manage_permissions.md#assign-or-remove-permissions-for-a-specific-group).
|
||||||
|
|
||||||
|
## Delete a group
|
||||||
|
|
||||||
|
To delete a group, follow these steps:
|
||||||
|
|
||||||
|
1. In the Admin interface, navigate to **Directory > Groups**.
|
||||||
|
2. Select the checkbox beside the name of the group that you want to delete.
|
||||||
|
3. Click **Delete**.
|
||||||
|
|
||||||
|
## Assign, modify, or remove permissions for a group
|
||||||
|
|
||||||
|
You can grant a group specific global or object-level permissions. Any user who is a member of a group inherits all of the group's permissions.
|
||||||
|
|
||||||
|
For more information, review ["Permissions"](../access-control/permissions.md).
|
||||||
|
|
||||||
|
## Assign a role to a group
|
||||||
|
|
||||||
|
You can assign a role to a group, and then all users in the group inherit the permissions assigned to that role. For instructions and more information, see ["Assign a role to a group"](../roles/manage_roles.md#assign-a-role-to-a-group).
|
|
@ -0,0 +1,20 @@
|
||||||
|
---
|
||||||
|
title: About roles
|
||||||
|
---
|
||||||
|
|
||||||
|
import DocCardList from "@theme/DocCardList";
|
||||||
|
import { useCurrentSidebarCategory } from "@docusaurus/theme-common";
|
||||||
|
|
||||||
|
Roles are a way to simplify the assignment of permissions. Roles are also the backbone of role-based access control (RBAC), an industry standard for managing [access control](../access-control). In authentik, RBAC is how you manage access to system components and specific objects such as flows, stages, users, etc.
|
||||||
|
|
||||||
|
Think of roles as a collection of permissions. A role, along with its "bucket" of assigned permissions, can then be assigned to a group, which means that every user who is a part of that group will inherit all of the permissions in that role's "bucket".
|
||||||
|
|
||||||
|
For example, let's take a look at the following scenario:
|
||||||
|
|
||||||
|
> You need to add 5 new users, all new hires, to authentik, your identity management system. These users will be the first team members on the brand new Security team, so they will need some high-level permissions, with object permissions to create and remove other users, revoke permissions, and send recovery emails. They will also need [global permissions](../access-control/permissions#fundamentals-of-authentik-permissions) to control access to flows and stages.
|
||||||
|
|
||||||
|
The easiest workflow for setting up these new users involves [creating a role](./manage_roles.md#create-a-role) specifically for their type of work, and then [assigning that role to a group](./manage_roles.md#assign-a-role-to-a-group) to which all of the users belong.
|
||||||
|
|
||||||
|
To learn more about working with roles in authentik, refer to the following topics:
|
||||||
|
|
||||||
|
<DocCardList items={useCurrentSidebarCategory().items} />
|
|
@ -0,0 +1,48 @@
|
||||||
|
---
|
||||||
|
title: "Manage roles"
|
||||||
|
description: "Learn how to work with roles and permissions in authentik."
|
||||||
|
---
|
||||||
|
|
||||||
|
Roles are a collection of permissions, which can then be assigned, en masse, to a group. Using roles is a way to quickly grant permissions; by adding a user to the group with the appropriate assigned roles, any user in that group then inherits all of those permissions that are assigned to the role.
|
||||||
|
|
||||||
|
:::info
|
||||||
|
In authentik, we assign roles to groups, not to individual users.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Create a role
|
||||||
|
|
||||||
|
To create a new role, follow these steps:
|
||||||
|
|
||||||
|
1. In the Admin interface, navigate to **Directory > Roles**.
|
||||||
|
2. Click **Create**, enter the name of the role, and then click **Create** in the modal.
|
||||||
|
3. Next, [assign permissions to the role](../access-control/permissions.md#assign-or-remove-permissions-for-a-specific-role).
|
||||||
|
|
||||||
|
## Modify a role
|
||||||
|
|
||||||
|
To modify a role, follow these steps:
|
||||||
|
|
||||||
|
- To edit the name of the role, click the Edit icon beside the role's name.
|
||||||
|
|
||||||
|
- To modify the permissions that are assigned to the role click on the role's name to go to the role's detail page. There you can add, modify, or remove permissions. For more information, refer to ["Assign or remove permissions for a specific role"](../access-control/permissions.md#assign-or-remove-permissions-for-a-specific-role).
|
||||||
|
|
||||||
|
## Delete a role
|
||||||
|
|
||||||
|
To delete a role, follow these steps:
|
||||||
|
|
||||||
|
1. In the Admin interface, navigate to **Directory > Roles**.
|
||||||
|
2. Select the checkbox beside the name of the role that you want to delete.
|
||||||
|
3. Click **Delete**.
|
||||||
|
|
||||||
|
## Assign a role to a group
|
||||||
|
|
||||||
|
In authentik, roles are assigned to [groups](../groups/index.mdx), not to individual users.
|
||||||
|
|
||||||
|
1. To assign the role to a group, navigate to **Directory -> Groups**.
|
||||||
|
2. Click the name of the group to which you want to add a role.
|
||||||
|
3. On the group's detail page, on the Overview tab, click **Edit** in the **Group Info** area.
|
||||||
|
4. On the **Update Group** modal, in the **Roles** field, scroll through the list of existent roles, and click to select the one you want to add to the group. (You can select multiple roles at once by holding the Control and Command keys while selecting the roles.)
|
||||||
|
5. Click **Update** to add the role(s) and close the modal.
|
||||||
|
|
||||||
|
:::info
|
||||||
|
To remove a role from a group, hold the Command key and click the name of the role that you want to remove from the group. This desepcts the role. Then click **Update**.
|
||||||
|
:::
|
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 171 KiB |
|
@ -9,11 +9,8 @@ The following topics are for the basic management of users: how to create, modif
|
||||||
> If you want to automate user creation, you can do that either by [invitations](./invitations.md), [`user_write` stage](../../flow/stages/user_write), or [using the API](/developer-docs/api/browser).
|
> If you want to automate user creation, you can do that either by [invitations](./invitations.md), [`user_write` stage](../../flow/stages/user_write), or [using the API](/developer-docs/api/browser).
|
||||||
|
|
||||||
1. In the Admin interface of your authentik instance, select **Directory > Users** in the left side menu.
|
1. In the Admin interface of your authentik instance, select **Directory > Users** in the left side menu.
|
||||||
|
|
||||||
2. Select the folder where you want to create a user.
|
2. Select the folder where you want to create a user.
|
||||||
|
|
||||||
3. Click **Create** (for a default user).
|
3. Click **Create** (for a default user).
|
||||||
|
|
||||||
4. Fill in the required fields:
|
4. Fill in the required fields:
|
||||||
|
|
||||||
- **Username**: This value must be unique across your user folders.
|
- **Username**: This value must be unique across your user folders.
|
||||||
|
@ -22,8 +19,8 @@ The following topics are for the basic management of users: how to create, modif
|
||||||
5. Fill the **_optional_** fields if needed:
|
5. Fill the **_optional_** fields if needed:
|
||||||
|
|
||||||
- **Name**: The display name of the user.
|
- **Name**: The display name of the user.
|
||||||
- **Email**: The email address of the user. That will be used if there is a [notification rule](../../events/notifications) triggered or for [email stages](../../flow/stages/email).
|
- **Email**: The email address of the user. Email addresses are used in [email stages](../../flow/stages/email) and to receive [notifications](../../events/notifications), if configured.
|
||||||
- **Is active**: Define is the newly created user account is active. Selected by default.
|
- **Is active**: Define if the newly created user account is active. Selected by default.
|
||||||
- **Attributes**: Custom attributes definition for the user, in YAML or JSON format. These attributes can be used to enforce additional prompts on authentication stages or define conditions to enforce specific policies if the current implementation does not fit your use case. The value is an empty dictionary by default.
|
- **Attributes**: Custom attributes definition for the user, in YAML or JSON format. These attributes can be used to enforce additional prompts on authentication stages or define conditions to enforce specific policies if the current implementation does not fit your use case. The value is an empty dictionary by default.
|
||||||
|
|
||||||
6. Click **Create**
|
6. Click **Create**
|
||||||
|
@ -43,7 +40,7 @@ To view details about a specific user:
|
||||||
2. To see further details, click any of the other tabs:
|
2. To see further details, click any of the other tabs:
|
||||||
|
|
||||||
- **Session** shows the active sessions established by the user. If there is any need, you can clean up the connected devices for a user by selecting the device(s) and then clicking **Delete**. This forces the user to authenticate again on the deleted devices.
|
- **Session** shows the active sessions established by the user. If there is any need, you can clean up the connected devices for a user by selecting the device(s) and then clicking **Delete**. This forces the user to authenticate again on the deleted devices.
|
||||||
- **Groups** allows you to manage the group membership of the user. You can find more details on [groups](../group).
|
- **Groups** allows you to manage the group membership of the user. You can find more details on [groups](../groups/index.mdx).
|
||||||
- **User events** displays all the events generated by the user during a session, such as login, logout, application authorisation, password reset, user info update, etc.
|
- **User events** displays all the events generated by the user during a session, such as login, logout, application authorisation, password reset, user info update, etc.
|
||||||
- **Explicit consent** lists all the permissions the user has given explicitly to an application. Entries will only appear if the user is validating an [explicit consent flow in an OAuth2 provider](../../providers/oauth2/). If you want to delete the explicit consent (because the application is requiring new permissions, or the user has explicitly asked to reset his consent on third-party apps), select the applications and click **Delete**. The user will be asked to again give explicit consent to share information with the application.
|
- **Explicit consent** lists all the permissions the user has given explicitly to an application. Entries will only appear if the user is validating an [explicit consent flow in an OAuth2 provider](../../providers/oauth2/). If you want to delete the explicit consent (because the application is requiring new permissions, or the user has explicitly asked to reset his consent on third-party apps), select the applications and click **Delete**. The user will be asked to again give explicit consent to share information with the application.
|
||||||
- **OAuth Refresh Tokens** lists all the OAuth tokens currently distributed. You can remove the tokens by selecting the applications and then clicking **Delete**.
|
- **OAuth Refresh Tokens** lists all the OAuth tokens currently distributed. You can remove the tokens by selecting the applications and then clicking **Delete**.
|
||||||
|
@ -53,32 +50,38 @@ To view details about a specific user:
|
||||||
|
|
||||||
After the creation of the user, you can edit any parameter defined during the creation.
|
After the creation of the user, you can edit any parameter defined during the creation.
|
||||||
|
|
||||||
To modify a user object, go to **Directory > Users**, and click the edit icon beside the name.
|
To modify a user object, go to **Directory > Users**, and click the edit icon beside the name. You can also go into [user details](#view-user-details), and click **Edit**.
|
||||||
|
|
||||||
You can also go into [user details](#view-user-details), and click **Edit**.
|
### Assign, modify, or remove permissions for a user
|
||||||
|
|
||||||
## User recovery
|
You can grant a user specific global or object-level permissions. Alternatively, you can add a user to a group that has the appropriate permissions, and the user inherits all of the group's permissions.
|
||||||
|
|
||||||
|
For more information, review ["Permissions"](../access-control/permissions.md).
|
||||||
|
|
||||||
|
## Add a user to a group
|
||||||
|
|
||||||
|
1. To add a user to a group, navigate to **Directory > Users** to display all users.
|
||||||
|
2. Click the name of the user to display the full user details page.
|
||||||
|
3. Click the **Groups** tab, and then click either **Add to existing group** or **Add to new group**.
|
||||||
|
|
||||||
|
## User credentials recovery
|
||||||
|
|
||||||
If a user has lost their credentials, there are several options.
|
If a user has lost their credentials, there are several options.
|
||||||
|
|
||||||
### Email them a recovery link
|
### Email them a recovery link
|
||||||
|
|
||||||
1. In the Admin interface, navigate to **Directory > Users** to display all users.
|
1. In the Admin interface, navigate to **Directory > Users** to display all users.
|
||||||
|
2. Either click the name of the user to display the full User details page, or click the chevron (the › symbol) beside their name to expand the options.
|
||||||
2. Either click the name of the user to display the full User details page, or click the chevron (the › symbol) beside their name to expand the toptions.
|
|
||||||
|
|
||||||
3. To generate a recovery link, which you can then copy and paste into an email, click **View recovery link**.
|
3. To generate a recovery link, which you can then copy and paste into an email, click **View recovery link**.
|
||||||
|
|
||||||
A pop-up will appear on your browser with the link for you to copy and to send to the user.
|
A pop-up will appear on your browser with the link for you to copy and to send to the user.
|
||||||
|
|
||||||
### Automate email to a user
|
### Automate email to a user
|
||||||
|
|
||||||
You can use our automated email to send a link with the URL for the user to reset their password. This option will only work if you have properly [configured a SMTP server during the installation](../../installation/docker-compose#email-configuration-optional-but-recommended) and set an email address for the user.
|
You can use our automated email to send a link with the URL for the user to reset their password. This option will only work if you have properly [configured a SMTP server during the installation](../../installation/docker-compose#email-configuration-optional-but-recommended) and set an email address for the user.
|
||||||
|
|
||||||
1. In the Admin interface, navigate to **Directory > Users** to display all users.
|
1. In the Admin interface, navigate to **Directory > Users** to display all users.
|
||||||
|
|
||||||
2. Either click the name of the user to display the full User details page, or click the chevron beside their name to expand the toptions.
|
2. Either click the name of the user to display the full User details page, or click the chevron beside their name to expand the toptions.
|
||||||
|
|
||||||
3. To send the automated email to the user, click **Email recovery link**.
|
3. To send the automated email to the user, click **Email recovery link**.
|
||||||
|
|
||||||
If the user does not receive the email, check if the mail server parameters [are properly configured](../../troubleshooting/emails).
|
If the user does not receive the email, check if the mail server parameters [are properly configured](../../troubleshooting/emails).
|
||||||
|
@ -88,9 +91,7 @@ If the user does not receive the email, check if the mail server parameters [are
|
||||||
As an Admin, you can simply reset the password for the user.
|
As an Admin, you can simply reset the password for the user.
|
||||||
|
|
||||||
1. In the Admin interface, navigate to **Directory > Users** to display all users.
|
1. In the Admin interface, navigate to **Directory > Users** to display all users.
|
||||||
|
|
||||||
2. Either click the name of the user to display the full User details page, or click the chevron beside their name to expand the toptions.
|
2. Either click the name of the user to display the full User details page, or click the chevron beside their name to expand the toptions.
|
||||||
|
|
||||||
3. To reset the user's password, click **Reset password**, and then define the new value.
|
3. To reset the user's password, click **Reset password**, and then define the new value.
|
||||||
|
|
||||||
## Deactivate or Delete user
|
## Deactivate or Delete user
|
||||||
|
@ -98,7 +99,6 @@ As an Admin, you can simply reset the password for the user.
|
||||||
#### To deactivate a user:
|
#### To deactivate a user:
|
||||||
|
|
||||||
1. Go into the user list or detail, and click **Deactivate**.
|
1. Go into the user list or detail, and click **Deactivate**.
|
||||||
|
|
||||||
2. Review the changes and click **Update**.
|
2. Review the changes and click **Update**.
|
||||||
|
|
||||||
The active sessions are revoked and the authentication of the user blocked. You can reactivate the account by following the same procedure.
|
The active sessions are revoked and the authentication of the user blocked. You can reactivate the account by following the same procedure.
|
||||||
|
@ -111,7 +111,6 @@ You may instead deactivate the account to preserve identity data.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
1. Go into the user list and select one (or multiple users) to delete and click **Delete** on the top-right of the page.
|
1. Go into the user list and select one (or multiple users) to delete and click **Delete** on the top-right of the page.
|
||||||
|
|
||||||
2. Review the changes and click **Delete**.
|
2. Review the changes and click **Delete**.
|
||||||
|
|
||||||
The user list refreshes and no longer displays the removed users.
|
The user list refreshes and no longer displays the removed users.
|
|
@ -43,7 +43,7 @@ elif ak_is_group_member(request.user, name="Minio users"):
|
||||||
return None
|
return None
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that you can assign multiple policies to a user by returning a list, and returning `None` will map no policies to the user, resulting in no access to the MinIO instance. For more information on writing expressions, see [Expressions](../../../docs/property-mappings/expression) and [User](../../../docs/user-group/user#object-attributes) docs.
|
Note that you can assign multiple policies to a user by returning a list, and returning `None` will map no policies to the user, resulting in no access to the MinIO instance. For more information on writing expressions, see [Expressions](../../../docs/property-mappings/expression) and [User](../../../docs/user-group-role/user/user_ref#object-properties) docs.
|
||||||
|
|
||||||
### Creating application and provider
|
### Creating application and provider
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ const docsSidebar = {
|
||||||
{
|
{
|
||||||
type: "category",
|
type: "category",
|
||||||
label: "Installation",
|
label: "Installation",
|
||||||
collapsed: false,
|
collapsed: true,
|
||||||
link: {
|
link: {
|
||||||
type: "doc",
|
type: "doc",
|
||||||
id: "installation/index",
|
id: "installation/index",
|
||||||
|
@ -259,22 +259,51 @@ const docsSidebar = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "category",
|
type: "category",
|
||||||
label: "Users & Groups",
|
label: "Users, Groups, & Roles",
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
type: "category",
|
type: "category",
|
||||||
label: "Users",
|
label: "Users",
|
||||||
link: {
|
link: {
|
||||||
type: "doc",
|
type: "doc",
|
||||||
id: "user-group/user/index",
|
id: "user-group-role/user/index",
|
||||||
},
|
},
|
||||||
items: [
|
items: [
|
||||||
"user-group/user/user_basic_operations",
|
"user-group-role/user/user_basic_operations",
|
||||||
"user-group/user/user_ref",
|
"user-group-role/user/user_ref",
|
||||||
"user-group/user/invitations",
|
"user-group-role/user/invitations",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
label: "Groups",
|
||||||
|
link: {
|
||||||
|
type: "doc",
|
||||||
|
id: "user-group-role/groups/index",
|
||||||
|
},
|
||||||
|
items: ["user-group-role/groups/manage_groups"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
label: "Roles",
|
||||||
|
link: {
|
||||||
|
type: "doc",
|
||||||
|
id: "user-group-role/roles/index",
|
||||||
|
},
|
||||||
|
items: ["user-group-role/roles/manage_roles"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
label: "Access control",
|
||||||
|
link: {
|
||||||
|
type: "doc",
|
||||||
|
id: "user-group-role/access-control/index",
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
"user-group-role/access-control/permissions",
|
||||||
|
"user-group-role/access-control/manage_permissions",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
"user-group/group",
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -287,13 +316,14 @@ const docsSidebar = {
|
||||||
description: "Release notes for recent authentik versions",
|
description: "Release notes for recent authentik versions",
|
||||||
},
|
},
|
||||||
items: [
|
items: [
|
||||||
|
"releases/2023/v2023.10",
|
||||||
"releases/2023/v2023.8",
|
"releases/2023/v2023.8",
|
||||||
"releases/2023/v2023.6",
|
"releases/2023/v2023.6",
|
||||||
"releases/2023/v2023.5",
|
|
||||||
{
|
{
|
||||||
type: "category",
|
type: "category",
|
||||||
label: "Previous versions",
|
label: "Previous versions",
|
||||||
items: [
|
items: [
|
||||||
|
"releases/2023/v2023.5",
|
||||||
"releases/2023/v2023.4",
|
"releases/2023/v2023.4",
|
||||||
"releases/2023/v2023.3",
|
"releases/2023/v2023.3",
|
||||||
"releases/2023/v2023.2",
|
"releases/2023/v2023.2",
|
||||||
|
|
Reference in New Issue