Compare commits
1 Commits
hack-close
...
tanberry-p
| Author | SHA1 | Date | |
|---|---|---|---|
| 115973376f |
39
.github/workflows/translation-rename.yml
vendored
39
.github/workflows/translation-rename.yml
vendored
@ -1,39 +0,0 @@
|
||||
# Rename transifex pull requests to have a correct naming
|
||||
name: authentik-translation-transifex-rename
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened]
|
||||
|
||||
jobs:
|
||||
rename_pr:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.pull_request.user.login == 'transifex-integration[bot]'}}
|
||||
steps:
|
||||
- id: generate_token
|
||||
uses: tibdex/github-app-token@v1
|
||||
with:
|
||||
app_id: ${{ secrets.GH_APP_ID }}
|
||||
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
- name: Get current title
|
||||
id: title
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
|
||||
run: |
|
||||
title=$(curl -q -L \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${GH_TOKEN}" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/repos/${GITHUB_REPOSITORY}/pulls/${{ github.event.pull_request.number }} | jq -r .title)
|
||||
echo "title=${title}" >> "$GITHUB_OUTPUT"
|
||||
- name: Rename
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
|
||||
run: |
|
||||
curl -L \
|
||||
-X PATCH \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${GH_TOKEN}" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/repos/${GITHUB_REPOSITORY}/pulls/${{ github.event.pull_request.number }} \
|
||||
-d "{\"title\":\"translate: ${{ steps.title.outputs.title }}\"}"
|
||||
@ -21,14 +21,9 @@ _other_urls = []
|
||||
for _authentik_app in get_apps():
|
||||
try:
|
||||
api_urls = import_module(f"{_authentik_app.name}.urls")
|
||||
except (ModuleNotFoundError, ImportError) as exc:
|
||||
LOGGER.warning("Could not import app's URLs", app_name=_authentik_app.name, exc=exc)
|
||||
except (ModuleNotFoundError, ImportError):
|
||||
continue
|
||||
if not hasattr(api_urls, "api_urlpatterns"):
|
||||
LOGGER.debug(
|
||||
"App does not define API URLs",
|
||||
app_name=_authentik_app.name,
|
||||
)
|
||||
continue
|
||||
urls: list = getattr(api_urls, "api_urlpatterns")
|
||||
for url in urls:
|
||||
|
||||
@ -12,7 +12,7 @@ def migrate_user_type(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
|
||||
from authentik.core.models import UserTypes
|
||||
|
||||
for user in User.objects.using(db_alias).all():
|
||||
user.type = UserTypes.INTERNAL
|
||||
user.type = UserTypes.DEFAULT
|
||||
if "goauthentik.io/user/service-account" in user.attributes:
|
||||
user.type = UserTypes.SERVICE_ACCOUNT
|
||||
if "goauthentik.io/user/override-ips" in user.attributes:
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
# Generated by Django 4.1.10 on 2023-07-21 12:54
|
||||
|
||||
from django.apps.registry import Apps
|
||||
from django.db import migrations, models
|
||||
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
||||
|
||||
|
||||
def migrate_user_type_v2(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
|
||||
db_alias = schema_editor.connection.alias
|
||||
User = apps.get_model("authentik_core", "User")
|
||||
|
||||
from authentik.core.models import UserTypes
|
||||
|
||||
for user in User.objects.using(db_alias).all():
|
||||
if user.type != "default":
|
||||
continue
|
||||
user.type = UserTypes.INTERNAL
|
||||
user.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("authentik_core", "0030_user_type"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="user",
|
||||
name="type",
|
||||
field=models.TextField(
|
||||
choices=[
|
||||
("internal", "Internal"),
|
||||
("external", "External"),
|
||||
("service_account", "Service Account"),
|
||||
("internal_service_account", "Internal Service Account"),
|
||||
],
|
||||
default="internal",
|
||||
),
|
||||
),
|
||||
migrations.RunPython(migrate_user_type_v2),
|
||||
]
|
||||
@ -67,7 +67,7 @@ class UserTypes(models.TextChoices):
|
||||
"""User types, both for grouping, licensing and permissions in the case
|
||||
of the internal_service_account"""
|
||||
|
||||
INTERNAL = "internal"
|
||||
DEFAULT = "default"
|
||||
EXTERNAL = "external"
|
||||
|
||||
# User-created service accounts
|
||||
@ -161,7 +161,7 @@ class User(SerializerModel, GuardianUserMixin, AbstractUser):
|
||||
uuid = models.UUIDField(default=uuid4, editable=False, unique=True)
|
||||
name = models.TextField(help_text=_("User's display name."))
|
||||
path = models.TextField(default="users")
|
||||
type = models.TextField(choices=UserTypes.choices, default=UserTypes.INTERNAL)
|
||||
type = models.TextField(choices=UserTypes.choices, default=UserTypes.DEFAULT)
|
||||
|
||||
sources = models.ManyToManyField("Source", through="UserSourceConnection")
|
||||
ak_groups = models.ManyToManyField("Group", related_name="users")
|
||||
|
||||
@ -137,7 +137,7 @@ class LicenseViewSet(UsedByMixin, ModelViewSet):
|
||||
last_month = now() - timedelta(days=30)
|
||||
# Forecast for default users
|
||||
users_in_last_month = User.objects.filter(
|
||||
type=UserTypes.INTERNAL, date_joined__gte=last_month
|
||||
type=UserTypes.DEFAULT, date_joined__gte=last_month
|
||||
).count()
|
||||
# Forecast for external users
|
||||
external_in_last_month = LicenseKey.get_external_user_count()
|
||||
|
||||
@ -9,7 +9,7 @@ from time import mktime
|
||||
from uuid import uuid4
|
||||
|
||||
from cryptography.exceptions import InvalidSignature
|
||||
from cryptography.x509 import Certificate, load_der_x509_certificate, load_pem_x509_certificate
|
||||
from cryptography.x509 import Certificate, load_pem_x509_certificate
|
||||
from dacite import from_dict
|
||||
from django.db import models
|
||||
from django.db.models.query import QuerySet
|
||||
@ -61,8 +61,8 @@ class LicenseKey:
|
||||
if len(x5c) < 1:
|
||||
raise ValidationError("Unable to verify license")
|
||||
try:
|
||||
our_cert = load_der_x509_certificate(b64decode(x5c[0]))
|
||||
intermediate = load_der_x509_certificate(b64decode(x5c[1]))
|
||||
our_cert = load_pem_x509_certificate(b64decode(x5c[0]))
|
||||
intermediate = load_pem_x509_certificate(b64decode(x5c[1]))
|
||||
our_cert.verify_directly_issued_by(intermediate)
|
||||
intermediate.verify_directly_issued_by(get_licensing_key())
|
||||
except (InvalidSignature, TypeError, ValueError, Error):
|
||||
@ -73,7 +73,7 @@ class LicenseKey:
|
||||
decode(
|
||||
jwt,
|
||||
our_cert.public_key(),
|
||||
algorithms=["ES512"],
|
||||
algorithms=["ES521"],
|
||||
audience=get_license_aud(),
|
||||
),
|
||||
)
|
||||
@ -105,7 +105,7 @@ class LicenseKey:
|
||||
@staticmethod
|
||||
def get_default_user_count():
|
||||
"""Get current default user count"""
|
||||
return LicenseKey.base_user_qs().filter(type=UserTypes.INTERNAL).count()
|
||||
return LicenseKey.base_user_qs().filter(type=UserTypes.DEFAULT).count()
|
||||
|
||||
@staticmethod
|
||||
def get_external_user_count():
|
||||
|
||||
@ -24,7 +24,7 @@ class EnterprisePolicy(Policy):
|
||||
def passes(self, request: PolicyRequest) -> PolicyResult:
|
||||
if not LicenseKey.get_total().is_valid():
|
||||
return PolicyResult(False)
|
||||
if request.user.type != UserTypes.INTERNAL:
|
||||
if request.user.type != UserTypes.DEFAULT:
|
||||
return PolicyResult(False)
|
||||
return PolicyResult(True)
|
||||
|
||||
|
||||
@ -76,20 +76,9 @@ class TaskInfo:
|
||||
return cache.get_many(cache.keys(CACHE_KEY_PREFIX + name)).values()
|
||||
return cache.get(CACHE_KEY_PREFIX + name, None)
|
||||
|
||||
@property
|
||||
def full_name(self) -> str:
|
||||
"""Get the full cache key with task name and UID"""
|
||||
key = CACHE_KEY_PREFIX + self.task_name
|
||||
if self.result.uid:
|
||||
uid_suffix = f":{self.result.uid}"
|
||||
key += uid_suffix
|
||||
if not self.task_name.endswith(uid_suffix):
|
||||
self.task_name += uid_suffix
|
||||
return key
|
||||
|
||||
def delete(self):
|
||||
"""Delete task info from cache"""
|
||||
return cache.delete(self.full_name)
|
||||
return cache.delete(CACHE_KEY_PREFIX + self.task_name)
|
||||
|
||||
def update_metrics(self):
|
||||
"""Update prometheus metrics"""
|
||||
@ -108,8 +97,12 @@ class TaskInfo:
|
||||
|
||||
def save(self, timeout_hours=6):
|
||||
"""Save task into cache"""
|
||||
key = CACHE_KEY_PREFIX + self.task_name
|
||||
if self.result.uid:
|
||||
key += f":{self.result.uid}"
|
||||
self.task_name += f":{self.result.uid}"
|
||||
self.update_metrics()
|
||||
cache.set(self.full_name, self, timeout=timeout_hours * 60 * 60)
|
||||
cache.set(key, self, timeout=timeout_hours * 60 * 60)
|
||||
|
||||
|
||||
class MonitoredTask(Task):
|
||||
|
||||
@ -1,43 +0,0 @@
|
||||
"""Test Monitored tasks"""
|
||||
from django.test import TestCase
|
||||
|
||||
from authentik.events.monitored_tasks import MonitoredTask, TaskInfo, TaskResult, TaskResultStatus
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.root.celery import CELERY_APP
|
||||
|
||||
|
||||
class TestMonitoredTasks(TestCase):
|
||||
"""Test Monitored tasks"""
|
||||
|
||||
def test_failed_successful_remove_state(self):
|
||||
"""Test that a task with `save_on_success` set to `False` that failed saves
|
||||
a state, and upon successful completion will delete the state"""
|
||||
should_fail = True
|
||||
uid = generate_id()
|
||||
|
||||
@CELERY_APP.task(
|
||||
bind=True,
|
||||
base=MonitoredTask,
|
||||
)
|
||||
def test_task(self: MonitoredTask):
|
||||
self.save_on_success = False
|
||||
self.set_uid(uid)
|
||||
self.set_status(
|
||||
TaskResult(TaskResultStatus.ERROR if should_fail else TaskResultStatus.SUCCESSFUL)
|
||||
)
|
||||
|
||||
# First test successful run
|
||||
should_fail = False
|
||||
test_task.delay().get()
|
||||
self.assertIsNone(TaskInfo.by_name(f"test_task:{uid}"))
|
||||
|
||||
# Then test failed
|
||||
should_fail = True
|
||||
test_task.delay().get()
|
||||
info = TaskInfo.by_name(f"test_task:{uid}")
|
||||
self.assertEqual(info.result.status, TaskResultStatus.ERROR)
|
||||
|
||||
# Then after that, the state should be removed
|
||||
should_fail = False
|
||||
test_task.delay().get()
|
||||
self.assertIsNone(TaskInfo.by_name(f"test_task:{uid}"))
|
||||
10
authentik/lib/apps.py
Normal file
10
authentik/lib/apps.py
Normal file
@ -0,0 +1,10 @@
|
||||
"""authentik lib app config"""
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AuthentikLibConfig(AppConfig):
|
||||
"""authentik lib app config"""
|
||||
|
||||
name = "authentik.lib"
|
||||
label = "authentik_lib"
|
||||
verbose_name = "authentik lib"
|
||||
@ -1,22 +1,16 @@
|
||||
"""Base Kubernetes Reconciler"""
|
||||
from dataclasses import asdict
|
||||
from json import dumps
|
||||
from typing import TYPE_CHECKING, Generic, Optional, TypeVar
|
||||
|
||||
from dacite.core import from_dict
|
||||
from django.utils.text import slugify
|
||||
from jsonpatch import JsonPatchConflict, JsonPatchException, JsonPatchTestFailed, apply_patch
|
||||
from kubernetes.client import ApiClient, V1ObjectMeta
|
||||
from kubernetes.client import V1ObjectMeta
|
||||
from kubernetes.client.exceptions import ApiException, OpenApiException
|
||||
from kubernetes.client.models.v1_deployment import V1Deployment
|
||||
from kubernetes.client.models.v1_pod import V1Pod
|
||||
from requests import Response
|
||||
from structlog.stdlib import get_logger
|
||||
from urllib3.exceptions import HTTPError
|
||||
|
||||
from authentik import __version__
|
||||
from authentik.outposts.apps import MANAGED_OUTPOST
|
||||
from authentik.outposts.controllers.base import ControllerException
|
||||
from authentik.outposts.controllers.k8s.triggers import NeedsRecreate, NeedsUpdate
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -40,23 +34,11 @@ class KubernetesObjectReconciler(Generic[T]):
|
||||
self.namespace = controller.outpost.config.kubernetes_namespace
|
||||
self.logger = get_logger().bind(type=self.__class__.__name__)
|
||||
|
||||
def get_patch(self):
|
||||
"""Get any patches that apply to this CRD"""
|
||||
patches = self.controller.outpost.config.kubernetes_json_patches
|
||||
if not patches:
|
||||
return None
|
||||
return patches.get(self.reconciler_name(), None)
|
||||
|
||||
@property
|
||||
def is_embedded(self) -> bool:
|
||||
"""Return true if the current outpost is embedded"""
|
||||
return self.controller.outpost.managed == MANAGED_OUTPOST
|
||||
|
||||
@staticmethod
|
||||
def reconciler_name() -> str:
|
||||
"""A name this reconciler is identified by in the configuration"""
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def noop(self) -> bool:
|
||||
"""Return true if this object should not be created/updated/deleted in this cluster"""
|
||||
@ -73,32 +55,6 @@ class KubernetesObjectReconciler(Generic[T]):
|
||||
}
|
||||
).lower()
|
||||
|
||||
def get_patched_reference_object(self) -> T:
|
||||
"""Get patched reference object"""
|
||||
reference = self.get_reference_object()
|
||||
patch = self.get_patch()
|
||||
try:
|
||||
json = ApiClient().sanitize_for_serialization(reference)
|
||||
# Custom objects will not be known to the clients openapi types
|
||||
except AttributeError:
|
||||
json = asdict(reference)
|
||||
try:
|
||||
ref = json
|
||||
if patch is not None:
|
||||
ref = apply_patch(json, patch)
|
||||
except (JsonPatchException, JsonPatchConflict, JsonPatchTestFailed) as exc:
|
||||
raise ControllerException(f"JSON Patch failed: {exc}") from exc
|
||||
mock_response = Response()
|
||||
mock_response.data = dumps(ref)
|
||||
|
||||
try:
|
||||
result = ApiClient().deserialize(mock_response, reference.__class__.__name__)
|
||||
# Custom objects will not be known to the clients openapi types
|
||||
except AttributeError:
|
||||
result = from_dict(reference.__class__, data=ref)
|
||||
|
||||
return result
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
def up(self):
|
||||
"""Create object if it doesn't exist, update if needed or recreate if needed."""
|
||||
@ -106,7 +62,7 @@ class KubernetesObjectReconciler(Generic[T]):
|
||||
if self.noop:
|
||||
self.logger.debug("Object is noop")
|
||||
return
|
||||
reference = self.get_patched_reference_object()
|
||||
reference = self.get_reference_object()
|
||||
try:
|
||||
try:
|
||||
current = self.retrieve()
|
||||
@ -173,16 +129,6 @@ class KubernetesObjectReconciler(Generic[T]):
|
||||
if current.metadata.labels != reference.metadata.labels:
|
||||
raise NeedsUpdate()
|
||||
|
||||
patch = self.get_patch()
|
||||
if patch is not None:
|
||||
current_json = ApiClient().sanitize_for_serialization(current)
|
||||
|
||||
try:
|
||||
if apply_patch(current_json, patch) != current_json:
|
||||
raise NeedsUpdate()
|
||||
except (JsonPatchException, JsonPatchConflict, JsonPatchTestFailed) as exc:
|
||||
raise ControllerException(f"JSON Patch failed: {exc}") from exc
|
||||
|
||||
def create(self, reference: T):
|
||||
"""API Wrapper to create object"""
|
||||
raise NotImplementedError
|
||||
|
||||
@ -43,10 +43,6 @@ class DeploymentReconciler(KubernetesObjectReconciler[V1Deployment]):
|
||||
self.api = AppsV1Api(controller.client)
|
||||
self.outpost = self.controller.outpost
|
||||
|
||||
@staticmethod
|
||||
def reconciler_name() -> str:
|
||||
return "deployment"
|
||||
|
||||
def reconcile(self, current: V1Deployment, reference: V1Deployment):
|
||||
compare_ports(
|
||||
current.spec.template.spec.containers[0].ports,
|
||||
|
||||
@ -24,10 +24,6 @@ class SecretReconciler(KubernetesObjectReconciler[V1Secret]):
|
||||
super().__init__(controller)
|
||||
self.api = CoreV1Api(controller.client)
|
||||
|
||||
@staticmethod
|
||||
def reconciler_name() -> str:
|
||||
return "secret"
|
||||
|
||||
def reconcile(self, current: V1Secret, reference: V1Secret):
|
||||
super().reconcile(current, reference)
|
||||
for key in reference.data.keys():
|
||||
|
||||
@ -20,10 +20,6 @@ class ServiceReconciler(KubernetesObjectReconciler[V1Service]):
|
||||
super().__init__(controller)
|
||||
self.api = CoreV1Api(controller.client)
|
||||
|
||||
@staticmethod
|
||||
def reconciler_name() -> str:
|
||||
return "service"
|
||||
|
||||
def reconcile(self, current: V1Service, reference: V1Service):
|
||||
compare_ports(current.spec.ports, reference.spec.ports)
|
||||
# run the base reconcile last, as that will probably raise NeedsUpdate
|
||||
|
||||
@ -71,10 +71,6 @@ class PrometheusServiceMonitorReconciler(KubernetesObjectReconciler[PrometheusSe
|
||||
self.api_ex = ApiextensionsV1Api(controller.client)
|
||||
self.api = CustomObjectsApi(controller.client)
|
||||
|
||||
@staticmethod
|
||||
def reconciler_name() -> str:
|
||||
return "prometheus servicemonitor"
|
||||
|
||||
@property
|
||||
def noop(self) -> bool:
|
||||
return (not self._crd_exists()) or (self.is_embedded)
|
||||
|
||||
@ -64,19 +64,12 @@ class KubernetesController(BaseController):
|
||||
super().__init__(outpost, connection)
|
||||
self.client = KubernetesClient(connection)
|
||||
self.reconcilers = {
|
||||
SecretReconciler.reconciler_name(): SecretReconciler,
|
||||
DeploymentReconciler.reconciler_name(): DeploymentReconciler,
|
||||
ServiceReconciler.reconciler_name(): ServiceReconciler,
|
||||
PrometheusServiceMonitorReconciler.reconciler_name(): (
|
||||
PrometheusServiceMonitorReconciler
|
||||
),
|
||||
"secret": SecretReconciler,
|
||||
"deployment": DeploymentReconciler,
|
||||
"service": ServiceReconciler,
|
||||
"prometheus servicemonitor": PrometheusServiceMonitorReconciler,
|
||||
}
|
||||
self.reconcile_order = [
|
||||
SecretReconciler.reconciler_name(),
|
||||
DeploymentReconciler.reconciler_name(),
|
||||
ServiceReconciler.reconciler_name(),
|
||||
PrometheusServiceMonitorReconciler.reconciler_name(),
|
||||
]
|
||||
self.reconcile_order = ["secret", "deployment", "service", "prometheus servicemonitor"]
|
||||
|
||||
def up(self):
|
||||
try:
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
"""Outpost models"""
|
||||
from dataclasses import asdict, dataclass, field
|
||||
from datetime import datetime
|
||||
from typing import Any, Iterable, Optional
|
||||
from typing import Iterable, Optional
|
||||
from uuid import uuid4
|
||||
|
||||
from dacite.core import from_dict
|
||||
@ -75,7 +75,6 @@ class OutpostConfig:
|
||||
kubernetes_service_type: str = field(default="ClusterIP")
|
||||
kubernetes_disabled_components: list[str] = field(default_factory=list)
|
||||
kubernetes_image_pull_secrets: list[str] = field(default_factory=list)
|
||||
kubernetes_json_patches: Optional[dict[str, list[dict[str, Any]]]] = field(default=None)
|
||||
|
||||
|
||||
class OutpostModel(Model):
|
||||
|
||||
@ -11,7 +11,6 @@ from authentik.core.tests.utils import create_test_admin_user, create_test_cert,
|
||||
from authentik.policies.models import PolicyBinding
|
||||
from authentik.providers.oauth2.constants import (
|
||||
GRANT_TYPE_CLIENT_CREDENTIALS,
|
||||
GRANT_TYPE_PASSWORD,
|
||||
SCOPE_OPENID,
|
||||
SCOPE_OPENID_EMAIL,
|
||||
SCOPE_OPENID_PROFILE,
|
||||
@ -151,28 +150,3 @@ class TestTokenClientCredentials(OAuthTestCase):
|
||||
)
|
||||
self.assertEqual(jwt["given_name"], self.user.name)
|
||||
self.assertEqual(jwt["preferred_username"], self.user.username)
|
||||
|
||||
def test_successful_password(self):
|
||||
"""test successful (password grant)"""
|
||||
response = self.client.post(
|
||||
reverse("authentik_providers_oauth2:token"),
|
||||
{
|
||||
"grant_type": GRANT_TYPE_PASSWORD,
|
||||
"scope": f"{SCOPE_OPENID} {SCOPE_OPENID_EMAIL} {SCOPE_OPENID_PROFILE}",
|
||||
"client_id": self.provider.client_id,
|
||||
"username": "sa",
|
||||
"password": self.token.key,
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
body = loads(response.content.decode())
|
||||
self.assertEqual(body["token_type"], TOKEN_TYPE)
|
||||
_, alg = self.provider.jwt_key
|
||||
jwt = decode(
|
||||
body["access_token"],
|
||||
key=self.provider.signing_key.public_key,
|
||||
algorithms=[alg],
|
||||
audience=self.provider.client_id,
|
||||
)
|
||||
self.assertEqual(jwt["given_name"], self.user.name)
|
||||
self.assertEqual(jwt["preferred_username"], self.user.username)
|
||||
|
||||
@ -459,13 +459,13 @@ class TokenView(View):
|
||||
if self.params.grant_type == GRANT_TYPE_REFRESH_TOKEN:
|
||||
LOGGER.debug("Refreshing refresh token")
|
||||
return TokenResponse(self.create_refresh_response())
|
||||
if self.params.grant_type in [GRANT_TYPE_CLIENT_CREDENTIALS, GRANT_TYPE_PASSWORD]:
|
||||
LOGGER.debug("Client credentials/password grant")
|
||||
if self.params.grant_type == GRANT_TYPE_CLIENT_CREDENTIALS:
|
||||
LOGGER.debug("Client credentials grant")
|
||||
return TokenResponse(self.create_client_credentials_response())
|
||||
if self.params.grant_type == GRANT_TYPE_DEVICE_CODE:
|
||||
LOGGER.debug("Device code grant")
|
||||
return TokenResponse(self.create_device_code_response())
|
||||
raise TokenError("unsupported_grant_type")
|
||||
raise ValueError(f"Invalid grant_type: {self.params.grant_type}")
|
||||
except (TokenError, DeviceCodeError) as error:
|
||||
return TokenResponse(error.create_dict(), status=400)
|
||||
except UserAuthError as error:
|
||||
|
||||
@ -31,10 +31,6 @@ class IngressReconciler(KubernetesObjectReconciler[V1Ingress]):
|
||||
super().__init__(controller)
|
||||
self.api = NetworkingV1Api(controller.client)
|
||||
|
||||
@staticmethod
|
||||
def reconciler_name() -> str:
|
||||
return "ingress"
|
||||
|
||||
def _check_annotations(self, reference: V1Ingress):
|
||||
"""Check that all annotations *we* set are correct"""
|
||||
for key, value in self.get_ingress_annotations().items():
|
||||
|
||||
@ -17,28 +17,24 @@ class TraefikMiddlewareReconciler(KubernetesObjectReconciler):
|
||||
if not self.reconciler.crd_exists():
|
||||
self.reconciler = Traefik2MiddlewareReconciler(controller)
|
||||
|
||||
@staticmethod
|
||||
def reconciler_name() -> str:
|
||||
return "traefik middleware"
|
||||
|
||||
@property
|
||||
def noop(self) -> bool:
|
||||
return self.reconciler.noop
|
||||
|
||||
def reconcile(self, current: TraefikMiddleware, reference: TraefikMiddleware):
|
||||
return self.reconciler.reconcile(current, reference)
|
||||
return self.reconcile(current, reference)
|
||||
|
||||
def get_reference_object(self) -> TraefikMiddleware:
|
||||
return self.reconciler.get_reference_object()
|
||||
return self.get_reference_object()
|
||||
|
||||
def create(self, reference: TraefikMiddleware):
|
||||
return self.reconciler.create(reference)
|
||||
return self.create(reference)
|
||||
|
||||
def delete(self, reference: TraefikMiddleware):
|
||||
return self.reconciler.delete(reference)
|
||||
return self.delete(reference)
|
||||
|
||||
def retrieve(self) -> TraefikMiddleware:
|
||||
return self.reconciler.retrieve()
|
||||
return self.retrieve()
|
||||
|
||||
def update(self, current: TraefikMiddleware, reference: TraefikMiddleware):
|
||||
return self.reconciler.update(current, reference)
|
||||
return self.update(current, reference)
|
||||
|
||||
@ -67,10 +67,6 @@ class Traefik3MiddlewareReconciler(KubernetesObjectReconciler[TraefikMiddleware]
|
||||
self.crd_version = "v1alpha1"
|
||||
self.crd_plural = "middlewares"
|
||||
|
||||
@staticmethod
|
||||
def reconciler_name() -> str:
|
||||
return "traefik middleware"
|
||||
|
||||
@property
|
||||
def noop(self) -> bool:
|
||||
if not ProxyProvider.objects.filter(
|
||||
|
||||
@ -16,9 +16,7 @@ class ProxyKubernetesController(KubernetesController):
|
||||
DeploymentPort(9300, "http-metrics", "tcp"),
|
||||
DeploymentPort(9443, "https", "tcp"),
|
||||
]
|
||||
self.reconcilers[IngressReconciler.reconciler_name()] = IngressReconciler
|
||||
self.reconcilers[
|
||||
TraefikMiddlewareReconciler.reconciler_name()
|
||||
] = TraefikMiddlewareReconciler
|
||||
self.reconcile_order.append(IngressReconciler.reconciler_name())
|
||||
self.reconcile_order.append(TraefikMiddlewareReconciler.reconciler_name())
|
||||
self.reconcilers["ingress"] = IngressReconciler
|
||||
self.reconcilers["traefik middleware"] = TraefikMiddlewareReconciler
|
||||
self.reconcile_order.append("ingress")
|
||||
self.reconcile_order.append("traefik middleware")
|
||||
|
||||
@ -10,8 +10,6 @@ from django.contrib.sessions.exceptions import SessionInterrupted
|
||||
from django.contrib.sessions.middleware import SessionMiddleware as UpstreamSessionMiddleware
|
||||
from django.http.request import HttpRequest
|
||||
from django.http.response import HttpResponse
|
||||
from django.middleware.csrf import CSRF_SESSION_KEY
|
||||
from django.middleware.csrf import CsrfViewMiddleware as UpstreamCsrfViewMiddleware
|
||||
from django.utils.cache import patch_vary_headers
|
||||
from django.utils.http import http_date
|
||||
from jwt import PyJWTError, decode, encode
|
||||
@ -133,29 +131,6 @@ class SessionMiddleware(UpstreamSessionMiddleware):
|
||||
return response
|
||||
|
||||
|
||||
class CsrfViewMiddleware(UpstreamCsrfViewMiddleware):
|
||||
"""Dynamically set secure depending if the upstream connection is TLS or not"""
|
||||
|
||||
def _set_csrf_cookie(self, request: HttpRequest, response: HttpResponse):
|
||||
if settings.CSRF_USE_SESSIONS:
|
||||
if request.session.get(CSRF_SESSION_KEY) != request.META["CSRF_COOKIE"]:
|
||||
request.session[CSRF_SESSION_KEY] = request.META["CSRF_COOKIE"]
|
||||
else:
|
||||
secure = SessionMiddleware.is_secure(request)
|
||||
response.set_cookie(
|
||||
settings.CSRF_COOKIE_NAME,
|
||||
request.META["CSRF_COOKIE"],
|
||||
max_age=settings.CSRF_COOKIE_AGE,
|
||||
domain=settings.CSRF_COOKIE_DOMAIN,
|
||||
path=settings.CSRF_COOKIE_PATH,
|
||||
secure=secure,
|
||||
httponly=settings.CSRF_COOKIE_HTTPONLY,
|
||||
samesite=settings.CSRF_COOKIE_SAMESITE,
|
||||
)
|
||||
# Set the Vary header since content varies with the CSRF cookie.
|
||||
patch_vary_headers(response, ("Cookie",))
|
||||
|
||||
|
||||
class ChannelsLoggingMiddleware:
|
||||
"""Logging middleware for channels"""
|
||||
|
||||
|
||||
@ -66,6 +66,7 @@ INSTALLED_APPS = [
|
||||
"authentik.crypto",
|
||||
"authentik.events",
|
||||
"authentik.flows",
|
||||
"authentik.lib",
|
||||
"authentik.outposts",
|
||||
"authentik.policies.dummy",
|
||||
"authentik.policies.event_matcher",
|
||||
@ -225,7 +226,7 @@ MIDDLEWARE = [
|
||||
"authentik.events.middleware.AuditMiddleware",
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
"authentik.root.middleware.CsrfViewMiddleware",
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
"authentik.core.middleware.ImpersonateMiddleware",
|
||||
|
||||
@ -3,10 +3,7 @@ from django.core.management.base import BaseCommand
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.sources.ldap.models import LDAPSource
|
||||
from authentik.sources.ldap.sync.groups import GroupLDAPSynchronizer
|
||||
from authentik.sources.ldap.sync.membership import MembershipLDAPSynchronizer
|
||||
from authentik.sources.ldap.sync.users import UserLDAPSynchronizer
|
||||
from authentik.sources.ldap.tasks import ldap_sync_paginator
|
||||
from authentik.sources.ldap.tasks import ldap_sync_single
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
@ -23,10 +20,4 @@ class Command(BaseCommand):
|
||||
if not source:
|
||||
LOGGER.warning("Source does not exist", slug=source_slug)
|
||||
continue
|
||||
tasks = (
|
||||
ldap_sync_paginator(source, UserLDAPSynchronizer)
|
||||
+ ldap_sync_paginator(source, GroupLDAPSynchronizer)
|
||||
+ ldap_sync_paginator(source, MembershipLDAPSynchronizer)
|
||||
)
|
||||
for task in tasks:
|
||||
task()
|
||||
ldap_sync_single(source)
|
||||
|
||||
@ -49,7 +49,7 @@ class UserLDAPSynchronizer(BaseLDAPSynchronizer):
|
||||
uniq = self._flatten(attributes[self._source.object_uniqueness_field])
|
||||
try:
|
||||
defaults = self.build_user_properties(user_dn, **attributes)
|
||||
self._logger.debug("Writing user with attributes", **defaults)
|
||||
self._logger.debug("Creating user with attributes", **defaults)
|
||||
if "username" not in defaults:
|
||||
raise IntegrityError("Username was not set by propertymappings")
|
||||
ak_user, created = self.update_or_create_attributes(
|
||||
|
||||
@ -59,7 +59,7 @@ def ldap_sync_paginator(source: LDAPSource, sync: type[BaseLDAPSynchronizer]) ->
|
||||
signatures = []
|
||||
for page in sync_inst.get_objects():
|
||||
page_cache_key = CACHE_KEY_PREFIX + str(uuid4())
|
||||
cache.set(page_cache_key, page, 60 * 60 * int(CONFIG.get("ldap.task_timeout_hours")))
|
||||
cache.set(page_cache_key, page)
|
||||
page_sync = ldap_sync.si(source.pk, class_to_path(sync), page_cache_key)
|
||||
signatures.append(page_sync)
|
||||
return signatures
|
||||
@ -86,12 +86,6 @@ def ldap_sync(self: MonitoredTask, source_pk: str, sync_class: str, page_cache_k
|
||||
sync_inst: BaseLDAPSynchronizer = sync(source)
|
||||
page = cache.get(page_cache_key)
|
||||
if not page:
|
||||
error_message = (
|
||||
f"Could not find page in cache: {page_cache_key}. "
|
||||
+ "Try increasing ldap.task_timeout_hours"
|
||||
)
|
||||
LOGGER.warning(error_message)
|
||||
self.set_status(TaskResult(TaskResultStatus.ERROR, [error_message]))
|
||||
return
|
||||
cache.touch(page_cache_key)
|
||||
count = sync_inst.sync(page)
|
||||
|
||||
@ -8,14 +8,12 @@ from authentik.blueprints.tests import apply_blueprint
|
||||
from authentik.core.models import Group, User
|
||||
from authentik.core.tests.utils import create_test_admin_user
|
||||
from authentik.events.models import Event, EventAction
|
||||
from authentik.events.monitored_tasks import TaskInfo, TaskResultStatus
|
||||
from authentik.lib.generators import generate_key
|
||||
from authentik.lib.utils.reflection import class_to_path
|
||||
from authentik.sources.ldap.models import LDAPPropertyMapping, LDAPSource
|
||||
from authentik.sources.ldap.sync.groups import GroupLDAPSynchronizer
|
||||
from authentik.sources.ldap.sync.membership import MembershipLDAPSynchronizer
|
||||
from authentik.sources.ldap.sync.users import UserLDAPSynchronizer
|
||||
from authentik.sources.ldap.tasks import ldap_sync, ldap_sync_all
|
||||
from authentik.sources.ldap.tasks import ldap_sync_all
|
||||
from authentik.sources.ldap.tests.mock_ad import mock_ad_connection
|
||||
from authentik.sources.ldap.tests.mock_slapd import mock_slapd_connection
|
||||
|
||||
@ -35,14 +33,6 @@ class LDAPSyncTests(TestCase):
|
||||
additional_group_dn="ou=groups",
|
||||
)
|
||||
|
||||
def test_sync_missing_page(self):
|
||||
"""Test sync with missing page"""
|
||||
connection = MagicMock(return_value=mock_ad_connection(LDAP_PASSWORD))
|
||||
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||
ldap_sync.delay(self.source.pk, class_to_path(UserLDAPSynchronizer), "foo").get()
|
||||
status = TaskInfo.by_name("ldap_sync:ldap:users:foo")
|
||||
self.assertEqual(status.result.status, TaskResultStatus.ERROR)
|
||||
|
||||
def test_sync_error(self):
|
||||
"""Test user sync"""
|
||||
self.source.property_mappings.set(
|
||||
|
||||
@ -3213,6 +3213,7 @@
|
||||
"authentik.crypto",
|
||||
"authentik.events",
|
||||
"authentik.flows",
|
||||
"authentik.lib",
|
||||
"authentik.outposts",
|
||||
"authentik.policies.dummy",
|
||||
"authentik.policies.event_matcher",
|
||||
@ -3983,7 +3984,7 @@
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"internal",
|
||||
"default",
|
||||
"external",
|
||||
"service_account",
|
||||
"internal_service_account"
|
||||
@ -4184,7 +4185,7 @@
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"internal",
|
||||
"default",
|
||||
"external",
|
||||
"service_account",
|
||||
"internal_service_account"
|
||||
@ -4389,7 +4390,7 @@
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"internal",
|
||||
"default",
|
||||
"external",
|
||||
"service_account",
|
||||
"internal_service_account"
|
||||
@ -6555,7 +6556,7 @@
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"internal",
|
||||
"default",
|
||||
"external",
|
||||
"service_account",
|
||||
"internal_service_account"
|
||||
@ -7300,7 +7301,7 @@
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"internal",
|
||||
"default",
|
||||
"external",
|
||||
"service_account",
|
||||
"internal_service_account"
|
||||
@ -8386,7 +8387,7 @@
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"internal",
|
||||
"default",
|
||||
"external",
|
||||
"service_account",
|
||||
"internal_service_account"
|
||||
|
||||
36
docker-compose.override.yml
Normal file
36
docker-compose.override.yml
Normal file
@ -0,0 +1,36 @@
|
||||
# This file is used for development and debugging, and should not be used for production instances
|
||||
|
||||
version: '3.5'
|
||||
|
||||
services:
|
||||
flower:
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.5.4}
|
||||
restart: unless-stopped
|
||||
command: worker-status
|
||||
environment:
|
||||
AUTHENTIK_REDIS__HOST: redis
|
||||
AUTHENTIK_POSTGRESQL__HOST: postgresql
|
||||
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
|
||||
AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
|
||||
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
|
||||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
- "9001:9000"
|
||||
depends_on:
|
||||
- postgresql
|
||||
- redis
|
||||
server:
|
||||
environment:
|
||||
AUTHENTIK_REMOTE_DEBUG: "true"
|
||||
PYDEVD_THREAD_DUMP_ON_WARN_EVALUATION_TIMEOUT: "true"
|
||||
ports:
|
||||
- 6800:6800
|
||||
worker:
|
||||
environment:
|
||||
CELERY_RDB_HOST: "0.0.0.0"
|
||||
CELERY_RDBSIG: "1"
|
||||
AUTHENTIK_REMOTE_DEBUG: "true"
|
||||
PYDEVD_THREAD_DUMP_ON_WARN_EVALUATION_TIMEOUT: "true"
|
||||
ports:
|
||||
- 6900:6900
|
||||
2
go.mod
2
go.mod
@ -26,7 +26,7 @@ require (
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
goauthentik.io/api/v3 v3.2023061.6
|
||||
goauthentik.io/api/v3 v3.2023061.3
|
||||
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
|
||||
golang.org/x/oauth2 v0.10.0
|
||||
golang.org/x/sync v0.3.0
|
||||
|
||||
4
go.sum
4
go.sum
@ -1070,8 +1070,8 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe
|
||||
go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
|
||||
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
|
||||
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
|
||||
goauthentik.io/api/v3 v3.2023061.6 h1:4zbo0Dtx42HLYObizIlTWAk7iBvCv9kmCvzBxMElkIk=
|
||||
goauthentik.io/api/v3 v3.2023061.6/go.mod h1:tC7qK9VSP0zJah5p5xHFnjZt/4dAkXVwcrWyZNGYhwQ=
|
||||
goauthentik.io/api/v3 v3.2023061.3 h1:uD+M52JTba2w8n5rWQKoPQ3gD0BDQuLNaDx/G0rKwKE=
|
||||
goauthentik.io/api/v3 v3.2023061.3/go.mod h1:tC7qK9VSP0zJah5p5xHFnjZt/4dAkXVwcrWyZNGYhwQ=
|
||||
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-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
|
||||
@ -8,7 +8,6 @@ import (
|
||||
"net/http/cookiejar"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@ -22,20 +21,10 @@ import (
|
||||
|
||||
var (
|
||||
FlowTimingGet = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Name: "authentik_outpost_flow_timing_get_seconds",
|
||||
Help: "Duration it took to get a challenge in seconds",
|
||||
}, []string{"stage", "flow"})
|
||||
FlowTimingPost = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Name: "authentik_outpost_flow_timing_post_seconds",
|
||||
Help: "Duration it took to send a challenge in seconds",
|
||||
}, []string{"stage", "flow"})
|
||||
|
||||
// NOTE: the following metrics are kept for compatibility purpose
|
||||
FlowTimingGetLegacy = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Name: "authentik_outpost_flow_timing_get",
|
||||
Help: "Duration it took to get a challenge",
|
||||
}, []string{"stage", "flow"})
|
||||
FlowTimingPostLegacy = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
FlowTimingPost = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Name: "authentik_outpost_flow_timing_post",
|
||||
Help: "Duration it took to send a challenge",
|
||||
}, []string{"stage", "flow"})
|
||||
@ -197,10 +186,6 @@ func (fe *FlowExecutor) getInitialChallenge() (*api.ChallengeTypes, error) {
|
||||
FlowTimingGet.With(prometheus.Labels{
|
||||
"stage": ch.GetComponent(),
|
||||
"flow": fe.flowSlug,
|
||||
}).Observe(float64(gcsp.EndTime.Sub(gcsp.StartTime)) / float64(time.Second))
|
||||
FlowTimingGetLegacy.With(prometheus.Labels{
|
||||
"stage": ch.GetComponent(),
|
||||
"flow": fe.flowSlug,
|
||||
}).Observe(float64(gcsp.EndTime.Sub(gcsp.StartTime)))
|
||||
return challenge, nil
|
||||
}
|
||||
@ -258,10 +243,6 @@ func (fe *FlowExecutor) solveFlowChallenge(challenge *api.ChallengeTypes, depth
|
||||
FlowTimingPost.With(prometheus.Labels{
|
||||
"stage": ch.GetComponent(),
|
||||
"flow": fe.flowSlug,
|
||||
}).Observe(float64(scsp.EndTime.Sub(scsp.StartTime)) / float64(time.Second))
|
||||
FlowTimingPostLegacy.With(prometheus.Labels{
|
||||
"stage": ch.GetComponent(),
|
||||
"flow": fe.flowSlug,
|
||||
}).Observe(float64(scsp.EndTime.Sub(scsp.StartTime)))
|
||||
|
||||
if depth >= 10 {
|
||||
|
||||
@ -2,7 +2,6 @@ package ldap
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"beryju.io/ldap"
|
||||
"github.com/getsentry/sentry-go"
|
||||
@ -21,11 +20,6 @@ func (ls *LDAPServer) Bind(bindDN string, bindPW string, conn net.Conn) (ldap.LD
|
||||
"outpost_name": ls.ac.Outpost.Name,
|
||||
"type": "bind",
|
||||
"app": selectedApp,
|
||||
}).Observe(float64(span.EndTime.Sub(span.StartTime)) / float64(time.Second))
|
||||
metrics.RequestsLegacy.With(prometheus.Labels{
|
||||
"outpost_name": ls.ac.Outpost.Name,
|
||||
"type": "bind",
|
||||
"app": selectedApp,
|
||||
}).Observe(float64(span.EndTime.Sub(span.StartTime)))
|
||||
req.Log().WithField("took-ms", span.EndTime.Sub(span.StartTime).Milliseconds()).Info("Bind request")
|
||||
}()
|
||||
@ -55,12 +49,6 @@ func (ls *LDAPServer) Bind(bindDN string, bindPW string, conn net.Conn) (ldap.LD
|
||||
"reason": "no_provider",
|
||||
"app": "",
|
||||
}).Inc()
|
||||
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
|
||||
"outpost_name": ls.ac.Outpost.Name,
|
||||
"type": "bind",
|
||||
"reason": "no_provider",
|
||||
"app": "",
|
||||
}).Inc()
|
||||
|
||||
return ldap.LDAPResultInsufficientAccessRights, nil
|
||||
}
|
||||
|
||||
@ -52,12 +52,6 @@ func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResul
|
||||
"reason": "flow_error",
|
||||
"app": db.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
|
||||
"outpost_name": db.si.GetOutpostName(),
|
||||
"type": "bind",
|
||||
"reason": "flow_error",
|
||||
"app": db.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
req.Log().WithError(err).Warning("failed to execute flow")
|
||||
return ldap.LDAPResultInvalidCredentials, nil
|
||||
}
|
||||
@ -68,12 +62,6 @@ func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResul
|
||||
"reason": "invalid_credentials",
|
||||
"app": db.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
|
||||
"outpost_name": db.si.GetOutpostName(),
|
||||
"type": "bind",
|
||||
"reason": "invalid_credentials",
|
||||
"app": db.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
req.Log().Info("Invalid credentials")
|
||||
return ldap.LDAPResultInvalidCredentials, nil
|
||||
}
|
||||
@ -87,12 +75,6 @@ func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResul
|
||||
"reason": "access_denied",
|
||||
"app": db.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
|
||||
"outpost_name": db.si.GetOutpostName(),
|
||||
"type": "bind",
|
||||
"reason": "access_denied",
|
||||
"app": db.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
return ldap.LDAPResultInsufficientAccessRights, nil
|
||||
}
|
||||
if err != nil {
|
||||
@ -102,12 +84,6 @@ func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResul
|
||||
"reason": "access_check_fail",
|
||||
"app": db.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
|
||||
"outpost_name": db.si.GetOutpostName(),
|
||||
"type": "bind",
|
||||
"reason": "access_check_fail",
|
||||
"app": db.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
req.Log().WithError(err).Warning("failed to check access")
|
||||
return ldap.LDAPResultOperationsError, nil
|
||||
}
|
||||
@ -122,12 +98,6 @@ func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResul
|
||||
"reason": "user_info_fail",
|
||||
"app": db.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
|
||||
"outpost_name": db.si.GetOutpostName(),
|
||||
"type": "bind",
|
||||
"reason": "user_info_fail",
|
||||
"app": db.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
req.Log().WithError(err).Warning("failed to get user info")
|
||||
return ldap.LDAPResultOperationsError, nil
|
||||
}
|
||||
|
||||
@ -15,20 +15,10 @@ import (
|
||||
|
||||
var (
|
||||
Requests = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Name: "authentik_outpost_ldap_request_duration_seconds",
|
||||
Help: "LDAP request latencies in seconds",
|
||||
}, []string{"outpost_name", "type", "app"})
|
||||
RequestsRejected = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: "authentik_outpost_ldap_requests_rejected_total",
|
||||
Help: "Total number of rejected requests",
|
||||
}, []string{"outpost_name", "type", "reason", "app"})
|
||||
|
||||
// NOTE: the following metrics are kept for compatibility purpose
|
||||
RequestsLegacy = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Name: "authentik_outpost_ldap_requests",
|
||||
Help: "The total number of configured providers",
|
||||
}, []string{"outpost_name", "type", "app"})
|
||||
RequestsRejectedLegacy = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
RequestsRejected = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: "authentik_outpost_ldap_requests_rejected",
|
||||
Help: "Total number of rejected requests",
|
||||
}, []string{"outpost_name", "type", "reason", "app"})
|
||||
|
||||
@ -2,7 +2,6 @@ package ldap
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"beryju.io/ldap"
|
||||
"github.com/getsentry/sentry-go"
|
||||
@ -22,11 +21,6 @@ func (ls *LDAPServer) Search(bindDN string, searchReq ldap.SearchRequest, conn n
|
||||
"outpost_name": ls.ac.Outpost.Name,
|
||||
"type": "search",
|
||||
"app": selectedApp,
|
||||
}).Observe(float64(span.EndTime.Sub(span.StartTime)) / float64(time.Second))
|
||||
metrics.RequestsLegacy.With(prometheus.Labels{
|
||||
"outpost_name": ls.ac.Outpost.Name,
|
||||
"type": "search",
|
||||
"app": selectedApp,
|
||||
}).Observe(float64(span.EndTime.Sub(span.StartTime)))
|
||||
req.Log().WithField("attributes", searchReq.Attributes).WithField("took-ms", span.EndTime.Sub(span.StartTime).Milliseconds()).Info("Search request")
|
||||
}()
|
||||
|
||||
@ -45,12 +45,6 @@ func (ds *DirectSearcher) Search(req *search.Request) (ldap.ServerSearchResult,
|
||||
"reason": "empty_bind_dn",
|
||||
"app": ds.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
|
||||
"outpost_name": ds.si.GetOutpostName(),
|
||||
"type": "search",
|
||||
"reason": "empty_bind_dn",
|
||||
"app": ds.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Search Error: Anonymous BindDN not allowed %s", req.BindDN)
|
||||
}
|
||||
if !utils.HasSuffixNoCase(req.BindDN, ","+baseDN) {
|
||||
@ -60,12 +54,6 @@ func (ds *DirectSearcher) Search(req *search.Request) (ldap.ServerSearchResult,
|
||||
"reason": "invalid_bind_dn",
|
||||
"app": ds.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
|
||||
"outpost_name": ds.si.GetOutpostName(),
|
||||
"type": "search",
|
||||
"reason": "invalid_bind_dn",
|
||||
"app": ds.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Search Error: BindDN %s not in our BaseDN %s", req.BindDN, ds.si.GetBaseDN())
|
||||
}
|
||||
|
||||
@ -78,12 +66,6 @@ func (ds *DirectSearcher) Search(req *search.Request) (ldap.ServerSearchResult,
|
||||
"reason": "user_info_not_cached",
|
||||
"app": ds.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
|
||||
"outpost_name": ds.si.GetOutpostName(),
|
||||
"type": "search",
|
||||
"reason": "user_info_not_cached",
|
||||
"app": ds.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, errors.New("access denied")
|
||||
}
|
||||
accsp.Finish()
|
||||
@ -96,12 +78,6 @@ func (ds *DirectSearcher) Search(req *search.Request) (ldap.ServerSearchResult,
|
||||
"reason": "filter_parse_fail",
|
||||
"app": ds.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
|
||||
"outpost_name": ds.si.GetOutpostName(),
|
||||
"type": "search",
|
||||
"reason": "filter_parse_fail",
|
||||
"app": ds.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("Search Error: error parsing filter: %s", req.Filter)
|
||||
}
|
||||
|
||||
|
||||
@ -62,12 +62,6 @@ func (ms *MemorySearcher) Search(req *search.Request) (ldap.ServerSearchResult,
|
||||
"reason": "empty_bind_dn",
|
||||
"app": ms.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
|
||||
"outpost_name": ms.si.GetOutpostName(),
|
||||
"type": "search",
|
||||
"reason": "empty_bind_dn",
|
||||
"app": ms.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Search Error: Anonymous BindDN not allowed %s", req.BindDN)
|
||||
}
|
||||
if !utils.HasSuffixNoCase(req.BindDN, ","+baseDN) {
|
||||
@ -77,12 +71,6 @@ func (ms *MemorySearcher) Search(req *search.Request) (ldap.ServerSearchResult,
|
||||
"reason": "invalid_bind_dn",
|
||||
"app": ms.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
|
||||
"outpost_name": ms.si.GetOutpostName(),
|
||||
"type": "search",
|
||||
"reason": "invalid_bind_dn",
|
||||
"app": ms.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Search Error: BindDN %s not in our BaseDN %s", req.BindDN, ms.si.GetBaseDN())
|
||||
}
|
||||
|
||||
@ -95,12 +83,6 @@ func (ms *MemorySearcher) Search(req *search.Request) (ldap.ServerSearchResult,
|
||||
"reason": "user_info_not_cached",
|
||||
"app": ms.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
|
||||
"outpost_name": ms.si.GetOutpostName(),
|
||||
"type": "search",
|
||||
"reason": "user_info_not_cached",
|
||||
"app": ms.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, errors.New("access denied")
|
||||
}
|
||||
accsp.Finish()
|
||||
|
||||
@ -2,10 +2,9 @@ package ldap
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"beryju.io/ldap"
|
||||
"github.com/getsentry/sentry-go"
|
||||
"beryju.io/ldap"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"goauthentik.io/internal/outpost/ldap/bind"
|
||||
@ -21,11 +20,6 @@ func (ls *LDAPServer) Unbind(boundDN string, conn net.Conn) (ldap.LDAPResultCode
|
||||
"outpost_name": ls.ac.Outpost.Name,
|
||||
"type": "unbind",
|
||||
"app": selectedApp,
|
||||
}).Observe(float64(span.EndTime.Sub(span.StartTime)) / float64(time.Second))
|
||||
metrics.RequestsLegacy.With(prometheus.Labels{
|
||||
"outpost_name": ls.ac.Outpost.Name,
|
||||
"type": "unbind",
|
||||
"app": selectedApp,
|
||||
}).Observe(float64(span.EndTime.Sub(span.StartTime)))
|
||||
req.Log().WithField("took-ms", span.EndTime.Sub(span.StartTime).Milliseconds()).Info("Unbind request")
|
||||
}()
|
||||
@ -55,11 +49,5 @@ func (ls *LDAPServer) Unbind(boundDN string, conn net.Conn) (ldap.LDAPResultCode
|
||||
"reason": "no_provider",
|
||||
"app": "",
|
||||
}).Inc()
|
||||
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
|
||||
"outpost_name": ls.ac.Outpost.Name,
|
||||
"type": "unbind",
|
||||
"reason": "no_provider",
|
||||
"app": "",
|
||||
}).Inc()
|
||||
return ldap.LDAPResultOperationsError, nil
|
||||
}
|
||||
|
||||
@ -163,19 +163,13 @@ func NewApplication(p api.ProxyOutpostConfig, c *http.Client, server Server) (*A
|
||||
}
|
||||
before := time.Now()
|
||||
inner.ServeHTTP(rw, r)
|
||||
elapsed := time.Since(before)
|
||||
after := time.Since(before)
|
||||
metrics.Requests.With(prometheus.Labels{
|
||||
"outpost_name": a.outpostName,
|
||||
"type": "app",
|
||||
"method": r.Method,
|
||||
"host": web.GetHost(r),
|
||||
}).Observe(float64(elapsed) / float64(time.Second))
|
||||
metrics.RequestsLegacy.With(prometheus.Labels{
|
||||
"outpost_name": a.outpostName,
|
||||
"type": "app",
|
||||
"method": r.Method,
|
||||
"host": web.GetHost(r),
|
||||
}).Observe(float64(elapsed))
|
||||
}).Observe(float64(after))
|
||||
})
|
||||
})
|
||||
if server.API().GlobalConfig.ErrorReporting.Enabled {
|
||||
|
||||
@ -55,7 +55,7 @@ func (a *Application) configureProxy() error {
|
||||
}
|
||||
before := time.Now()
|
||||
rp.ServeHTTP(rw, r)
|
||||
elapsed := time.Since(before)
|
||||
after := time.Since(before)
|
||||
|
||||
metrics.UpstreamTiming.With(prometheus.Labels{
|
||||
"outpost_name": a.outpostName,
|
||||
@ -63,14 +63,7 @@ func (a *Application) configureProxy() error {
|
||||
"method": r.Method,
|
||||
"scheme": r.URL.Scheme,
|
||||
"host": web.GetHost(r),
|
||||
}).Observe(float64(elapsed) / float64(time.Second))
|
||||
metrics.UpstreamTimingLegacy.With(prometheus.Labels{
|
||||
"outpost_name": a.outpostName,
|
||||
"upstream_host": r.URL.Host,
|
||||
"method": r.Method,
|
||||
"scheme": r.URL.Scheme,
|
||||
"host": web.GetHost(r),
|
||||
}).Observe(float64(elapsed))
|
||||
}).Observe(float64(after))
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -19,37 +19,25 @@ import (
|
||||
func (ps *ProxyServer) HandlePing(rw http.ResponseWriter, r *http.Request) {
|
||||
before := time.Now()
|
||||
rw.WriteHeader(204)
|
||||
elapsed := time.Since(before)
|
||||
after := time.Since(before)
|
||||
metrics.Requests.With(prometheus.Labels{
|
||||
"outpost_name": ps.akAPI.Outpost.Name,
|
||||
"method": r.Method,
|
||||
"host": web.GetHost(r),
|
||||
"type": "ping",
|
||||
}).Observe(float64(elapsed) / float64(time.Second))
|
||||
metrics.RequestsLegacy.With(prometheus.Labels{
|
||||
"outpost_name": ps.akAPI.Outpost.Name,
|
||||
"method": r.Method,
|
||||
"host": web.GetHost(r),
|
||||
"type": "ping",
|
||||
}).Observe(float64(elapsed))
|
||||
}).Observe(float64(after))
|
||||
}
|
||||
|
||||
func (ps *ProxyServer) HandleStatic(rw http.ResponseWriter, r *http.Request) {
|
||||
before := time.Now()
|
||||
web.DisableIndex(http.StripPrefix("/outpost.goauthentik.io/static/dist", staticWeb.StaticHandler)).ServeHTTP(rw, r)
|
||||
elapsed := time.Since(before)
|
||||
after := time.Since(before)
|
||||
metrics.Requests.With(prometheus.Labels{
|
||||
"outpost_name": ps.akAPI.Outpost.Name,
|
||||
"method": r.Method,
|
||||
"host": web.GetHost(r),
|
||||
"type": "static",
|
||||
}).Observe(float64(elapsed) / float64(time.Second))
|
||||
metrics.RequestsLegacy.With(prometheus.Labels{
|
||||
"outpost_name": ps.akAPI.Outpost.Name,
|
||||
"method": r.Method,
|
||||
"host": web.GetHost(r),
|
||||
"type": "static",
|
||||
}).Observe(float64(elapsed))
|
||||
}).Observe(float64(after))
|
||||
}
|
||||
|
||||
func (ps *ProxyServer) lookupApp(r *http.Request) (*application.Application, string) {
|
||||
|
||||
@ -15,20 +15,10 @@ import (
|
||||
|
||||
var (
|
||||
Requests = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Name: "authentik_outpost_proxy_request_duration_seconds",
|
||||
Help: "Proxy request latencies in seconds",
|
||||
}, []string{"outpost_name", "method", "host", "type"})
|
||||
UpstreamTiming = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Name: "authentik_outpost_proxy_upstream_response_duration_seconds",
|
||||
Help: "Proxy upstream response latencies in seconds",
|
||||
}, []string{"outpost_name", "method", "scheme", "host", "upstream_host"})
|
||||
|
||||
// NOTE: the following metric is kept for compatibility purpose
|
||||
RequestsLegacy = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Name: "authentik_outpost_proxy_requests",
|
||||
Help: "The total number of configured providers",
|
||||
}, []string{"outpost_name", "method", "host", "type"})
|
||||
UpstreamTimingLegacy = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
UpstreamTiming = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Name: "authentik_outpost_proxy_upstream_time",
|
||||
Help: "A summary of the duration we wait for the upstream reply",
|
||||
}, []string{"outpost_name", "method", "scheme", "host", "upstream_host"})
|
||||
|
||||
@ -32,11 +32,6 @@ func (rs *RadiusServer) Handle_AccessRequest(w radius.ResponseWriter, r *RadiusR
|
||||
"reason": "flow_error",
|
||||
"app": r.pi.appSlug,
|
||||
}).Inc()
|
||||
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
|
||||
"outpost_name": rs.ac.Outpost.Name,
|
||||
"reason": "flow_error",
|
||||
"app": r.pi.appSlug,
|
||||
}).Inc()
|
||||
_ = w.Write(r.Response(radius.CodeAccessReject))
|
||||
return
|
||||
}
|
||||
@ -46,11 +41,6 @@ func (rs *RadiusServer) Handle_AccessRequest(w radius.ResponseWriter, r *RadiusR
|
||||
"reason": "invalid_credentials",
|
||||
"app": r.pi.appSlug,
|
||||
}).Inc()
|
||||
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
|
||||
"outpost_name": rs.ac.Outpost.Name,
|
||||
"reason": "invalid_credentials",
|
||||
"app": r.pi.appSlug,
|
||||
}).Inc()
|
||||
_ = w.Write(r.Response(radius.CodeAccessReject))
|
||||
return
|
||||
}
|
||||
@ -63,11 +53,6 @@ func (rs *RadiusServer) Handle_AccessRequest(w radius.ResponseWriter, r *RadiusR
|
||||
"reason": "access_check_fail",
|
||||
"app": r.pi.appSlug,
|
||||
}).Inc()
|
||||
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
|
||||
"outpost_name": rs.ac.Outpost.Name,
|
||||
"reason": "access_check_fail",
|
||||
"app": r.pi.appSlug,
|
||||
}).Inc()
|
||||
return
|
||||
}
|
||||
if !access {
|
||||
@ -78,11 +63,6 @@ func (rs *RadiusServer) Handle_AccessRequest(w radius.ResponseWriter, r *RadiusR
|
||||
"reason": "access_denied",
|
||||
"app": r.pi.appSlug,
|
||||
}).Inc()
|
||||
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
|
||||
"outpost_name": rs.ac.Outpost.Name,
|
||||
"reason": "access_denied",
|
||||
"app": r.pi.appSlug,
|
||||
}).Inc()
|
||||
return
|
||||
}
|
||||
_ = w.Write(r.Response(radius.CodeAccessAccept))
|
||||
|
||||
@ -2,7 +2,6 @@ package radius
|
||||
|
||||
import (
|
||||
"crypto/sha512"
|
||||
"time"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/google/uuid"
|
||||
@ -46,10 +45,6 @@ func (rs *RadiusServer) ServeRADIUS(w radius.ResponseWriter, r *radius.Request)
|
||||
metrics.Requests.With(prometheus.Labels{
|
||||
"outpost_name": rs.ac.Outpost.Name,
|
||||
"app": selectedApp,
|
||||
}).Observe(float64(span.EndTime.Sub(span.StartTime)) / float64(time.Second))
|
||||
metrics.RequestsLegacy.With(prometheus.Labels{
|
||||
"outpost_name": rs.ac.Outpost.Name,
|
||||
"app": selectedApp,
|
||||
}).Observe(float64(span.EndTime.Sub(span.StartTime)))
|
||||
}()
|
||||
|
||||
|
||||
@ -15,20 +15,10 @@ import (
|
||||
|
||||
var (
|
||||
Requests = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Name: "authentik_outpost_radius_request_duration_seconds",
|
||||
Help: "RADIUS request latencies in seconds",
|
||||
}, []string{"outpost_name", "app"})
|
||||
RequestsRejected = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: "authentik_outpost_radius_requests_rejected_total",
|
||||
Help: "Total number of rejected requests",
|
||||
}, []string{"outpost_name", "reason", "app"})
|
||||
|
||||
// NOTE: the following metric is kept for compatibility purpose
|
||||
RequestsLegacy = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Name: "authentik_outpost_radius_requests",
|
||||
Help: "The total number of successful requests",
|
||||
}, []string{"outpost_name", "app"})
|
||||
RequestsRejectedLegacy = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
RequestsRejected = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: "authentik_outpost_radius_requests_rejected",
|
||||
Help: "Total number of rejected requests",
|
||||
}, []string{"outpost_name", "reason", "app"})
|
||||
|
||||
@ -15,12 +15,6 @@ import (
|
||||
|
||||
var (
|
||||
Requests = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Name: "authentik_main_request_duration_seconds",
|
||||
Help: "API request latencies in seconds",
|
||||
}, []string{"dest"})
|
||||
|
||||
// NOTE: the following metric is kept for compatibility purpose
|
||||
RequestsLegacy = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Name: "authentik_main_requests",
|
||||
Help: "The total number of configured providers",
|
||||
}, []string{"dest"})
|
||||
|
||||
@ -34,13 +34,9 @@ func (ws *WebServer) configureProxy() {
|
||||
if ws.ProxyServer != nil {
|
||||
before := time.Now()
|
||||
ws.ProxyServer.Handle(rw, r)
|
||||
elapsed := time.Since(before)
|
||||
Requests.With(prometheus.Labels{
|
||||
"dest": "embedded_outpost",
|
||||
}).Observe(float64(elapsed) / float64(time.Second))
|
||||
RequestsLegacy.With(prometheus.Labels{
|
||||
"dest": "embedded_outpost",
|
||||
}).Observe(float64(elapsed))
|
||||
}).Observe(float64(time.Since(before)))
|
||||
return
|
||||
}
|
||||
ws.proxyErrorHandler(rw, r, fmt.Errorf("proxy not running"))
|
||||
@ -56,23 +52,15 @@ func (ws *WebServer) configureProxy() {
|
||||
before := time.Now()
|
||||
if ws.ProxyServer != nil {
|
||||
if ws.ProxyServer.HandleHost(rw, r) {
|
||||
elapsed := time.Since(before)
|
||||
Requests.With(prometheus.Labels{
|
||||
"dest": "embedded_outpost",
|
||||
}).Observe(float64(elapsed) / float64(time.Second))
|
||||
RequestsLegacy.With(prometheus.Labels{
|
||||
"dest": "embedded_outpost",
|
||||
}).Observe(float64(elapsed))
|
||||
}).Observe(float64(time.Since(before)))
|
||||
return
|
||||
}
|
||||
}
|
||||
elapsed := time.Since(before)
|
||||
Requests.With(prometheus.Labels{
|
||||
"dest": "core",
|
||||
}).Observe(float64(elapsed) / float64(time.Second))
|
||||
RequestsLegacy.With(prometheus.Labels{
|
||||
"dest": "core",
|
||||
}).Observe(float64(elapsed))
|
||||
}).Observe(float64(time.Since(before)))
|
||||
r.Body = http.MaxBytesReader(rw, r.Body, 32*1024*1024)
|
||||
rp.ServeHTTP(rw, r)
|
||||
}))
|
||||
|
||||
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-07-28 11:50+0000\n"
|
||||
"POT-Creation-Date: 2023-07-16 13:59+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -427,7 +427,7 @@ msgstr ""
|
||||
msgid "Webhook Mappings"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/monitored_tasks.py:205
|
||||
#: authentik/events/monitored_tasks.py:198
|
||||
msgid "Task has not been run yet."
|
||||
msgstr ""
|
||||
|
||||
@ -585,65 +585,65 @@ msgstr ""
|
||||
msgid "Invalid kubeconfig"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/outposts/models.py:122
|
||||
#: authentik/outposts/models.py:121
|
||||
msgid ""
|
||||
"If enabled, use the local connection. Required Docker socket/Kubernetes "
|
||||
"Integration"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/outposts/models.py:152
|
||||
#: authentik/outposts/models.py:151
|
||||
msgid "Outpost Service-Connection"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/outposts/models.py:153
|
||||
#: authentik/outposts/models.py:152
|
||||
msgid "Outpost Service-Connections"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/outposts/models.py:161
|
||||
#: authentik/outposts/models.py:160
|
||||
msgid ""
|
||||
"Can be in the format of 'unix://<path>' when connecting to a local docker "
|
||||
"daemon, or 'https://<hostname>:2376' when connecting to a remote system."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/outposts/models.py:173
|
||||
#: authentik/outposts/models.py:172
|
||||
msgid ""
|
||||
"CA which the endpoint's Certificate is verified against. Can be left empty "
|
||||
"for no validation."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/outposts/models.py:185
|
||||
#: authentik/outposts/models.py:184
|
||||
msgid ""
|
||||
"Certificate/Key used for authentication. Can be left empty for no "
|
||||
"authentication."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/outposts/models.py:203
|
||||
#: authentik/outposts/models.py:202
|
||||
msgid "Docker Service-Connection"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/outposts/models.py:204
|
||||
#: authentik/outposts/models.py:203
|
||||
msgid "Docker Service-Connections"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/outposts/models.py:212
|
||||
#: authentik/outposts/models.py:211
|
||||
msgid ""
|
||||
"Paste your kubeconfig here. authentik will automatically use the currently "
|
||||
"selected context."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/outposts/models.py:218
|
||||
#: authentik/outposts/models.py:217
|
||||
msgid "Verify SSL Certificates of the Kubernetes API endpoint"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/outposts/models.py:235
|
||||
#: authentik/outposts/models.py:234
|
||||
msgid "Kubernetes Service-Connection"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/outposts/models.py:236
|
||||
#: authentik/outposts/models.py:235
|
||||
msgid "Kubernetes Service-Connections"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/outposts/models.py:252
|
||||
#: authentik/outposts/models.py:251
|
||||
msgid ""
|
||||
"Select Service-Connection authentik should use to manage this outpost. Leave "
|
||||
"empty if authentik should not handle the deployment."
|
||||
@ -2017,33 +2017,33 @@ msgstr ""
|
||||
msgid "Dummy Stages"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/models.py:26
|
||||
#: authentik/stages/email/models.py:25
|
||||
msgid "Password Reset"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/models.py:30
|
||||
#: authentik/stages/email/models.py:29
|
||||
msgid "Account Confirmation"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/models.py:59
|
||||
#: authentik/stages/email/models.py:58
|
||||
msgid ""
|
||||
"When enabled, global Email connection settings will be used and connection "
|
||||
"settings below will be ignored."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/models.py:74
|
||||
#: authentik/stages/email/models.py:73
|
||||
msgid "Activate users upon completion of stage."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/models.py:78
|
||||
#: authentik/stages/email/models.py:77
|
||||
msgid "Time in minutes the token sent is valid."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/models.py:132
|
||||
#: authentik/stages/email/models.py:122
|
||||
msgid "Email Stage"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/models.py:133
|
||||
#: authentik/stages/email/models.py:123
|
||||
msgid "Email Stages"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
83
poetry.lock
generated
83
poetry.lock
generated
@ -253,13 +253,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "astroid"
|
||||
version = "2.15.6"
|
||||
version = "2.15.5"
|
||||
description = "An abstract syntax tree for Python with inference support."
|
||||
optional = false
|
||||
python-versions = ">=3.7.2"
|
||||
files = [
|
||||
{file = "astroid-2.15.6-py3-none-any.whl", hash = "sha256:389656ca57b6108f939cf5d2f9a2a825a3be50ba9d589670f393236e0a03b91c"},
|
||||
{file = "astroid-2.15.6.tar.gz", hash = "sha256:903f024859b7c7687d7a7f3a3f73b17301f8e42dfd9cc9df9d4418172d3e2dbd"},
|
||||
{file = "astroid-2.15.5-py3-none-any.whl", hash = "sha256:078e5212f9885fa85fbb0cf0101978a336190aadea6e13305409d099f71b2324"},
|
||||
{file = "astroid-2.15.5.tar.gz", hash = "sha256:1039262575027b441137ab4a62a793a9b43defb42c32d5670f38686207cd780f"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -1340,13 +1340,13 @@ tests = ["black", "django-stubs[compatible-mypy]", "djangorestframework-stubs[co
|
||||
|
||||
[[package]]
|
||||
name = "drf-spectacular"
|
||||
version = "0.26.4"
|
||||
version = "0.26.3"
|
||||
description = "Sane and flexible OpenAPI 3 schema generation for Django REST framework"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "drf-spectacular-0.26.4.tar.gz", hash = "sha256:8f5a8f87353d1bb8dcb3f3909b7109b2dcbe1d91f3e069409cf322963e140bd6"},
|
||||
{file = "drf_spectacular-0.26.4-py3-none-any.whl", hash = "sha256:afeccc6533dcdb4e78afbfcc49f3c5e9c369aeb62f965e4d1a43b165449c147a"},
|
||||
{file = "drf-spectacular-0.26.3.tar.gz", hash = "sha256:b907a72a0244e5dcfeca625e9632cd8ebccdbe2cb528b7c1de1191708be6f31e"},
|
||||
{file = "drf_spectacular-0.26.3-py3-none-any.whl", hash = "sha256:1d84ac70522baaadd6d84a25ce5fe5ea50cfcba0387856689f98ac536f14aa32"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -1809,31 +1809,6 @@ pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"
|
||||
plugins = ["setuptools"]
|
||||
requirements-deprecated-finder = ["pip-api", "pipreqs"]
|
||||
|
||||
[[package]]
|
||||
name = "jsonpatch"
|
||||
version = "1.33"
|
||||
description = "Apply JSON-Patches (RFC 6902)"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*"
|
||||
files = [
|
||||
{file = "jsonpatch-1.33-py2.py3-none-any.whl", hash = "sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade"},
|
||||
{file = "jsonpatch-1.33.tar.gz", hash = "sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
jsonpointer = ">=1.9"
|
||||
|
||||
[[package]]
|
||||
name = "jsonpointer"
|
||||
version = "2.4"
|
||||
description = "Identify specific nodes in a JSON document (RFC 6901)"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*"
|
||||
files = [
|
||||
{file = "jsonpointer-2.4-py2.py3-none-any.whl", hash = "sha256:15d51bba20eea3165644553647711d150376234112651b4f1811022aecad7d7a"},
|
||||
{file = "jsonpointer-2.4.tar.gz", hash = "sha256:585cee82b70211fa9e6043b7bb89db6e1aa49524340dde8ad6b63206ea689d88"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonschema"
|
||||
version = "4.17.3"
|
||||
@ -2724,17 +2699,17 @@ tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "pylint"
|
||||
version = "2.17.5"
|
||||
version = "2.17.4"
|
||||
description = "python code static checker"
|
||||
optional = false
|
||||
python-versions = ">=3.7.2"
|
||||
files = [
|
||||
{file = "pylint-2.17.5-py3-none-any.whl", hash = "sha256:73995fb8216d3bed149c8d51bba25b2c52a8251a2c8ac846ec668ce38fab5413"},
|
||||
{file = "pylint-2.17.5.tar.gz", hash = "sha256:f7b601cbc06fef7e62a754e2b41294c2aa31f1cb659624b9a85bcba29eaf8252"},
|
||||
{file = "pylint-2.17.4-py3-none-any.whl", hash = "sha256:7a1145fb08c251bdb5cca11739722ce64a63db479283d10ce718b2460e54123c"},
|
||||
{file = "pylint-2.17.4.tar.gz", hash = "sha256:5dcf1d9e19f41f38e4e85d10f511e5b9c35e1aa74251bf95cdd8cb23584e2db1"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
astroid = ">=2.15.6,<=2.17.0-dev0"
|
||||
astroid = ">=2.15.4,<=2.17.0-dev0"
|
||||
colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
|
||||
dill = {version = ">=0.3.6", markers = "python_version >= \"3.11\""}
|
||||
isort = ">=4.2.5,<6"
|
||||
@ -3173,28 +3148,28 @@ pyasn1 = ">=0.1.3"
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.280"
|
||||
version = "0.0.278"
|
||||
description = "An extremely fast Python linter, written in Rust."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "ruff-0.0.280-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:48ed5aca381050a4e2f6d232db912d2e4e98e61648b513c350990c351125aaec"},
|
||||
{file = "ruff-0.0.280-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:ef6ee3e429fd29d6a5ceed295809e376e6ece5b0f13c7e703efaf3d3bcb30b96"},
|
||||
{file = "ruff-0.0.280-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d878370f7e9463ac40c253724229314ff6ebe4508cdb96cb536e1af4d5a9cd4f"},
|
||||
{file = "ruff-0.0.280-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:83e8f372fa5627eeda5b83b5a9632d2f9c88fc6d78cead7e2a1f6fb05728d137"},
|
||||
{file = "ruff-0.0.280-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7008fc6ca1df18b21fa98bdcfc711dad5f94d0fc3c11791f65e460c48ef27c82"},
|
||||
{file = "ruff-0.0.280-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:fe7118c1eae3fda17ceb409629c7f3b5a22dffa7caf1f6796776936dca1fe653"},
|
||||
{file = "ruff-0.0.280-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:37359cd67d2af8e09110a546507c302cbea11c66a52d2a9b6d841d465f9962d4"},
|
||||
{file = "ruff-0.0.280-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd58af46b0221efb95966f1f0f7576df711cb53e50d2fdb0e83c2f33360116a4"},
|
||||
{file = "ruff-0.0.280-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e7c15828d09f90e97bea8feefcd2907e8c8ce3a1f959c99f9b4b3469679f33c"},
|
||||
{file = "ruff-0.0.280-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2dae8f2d9c44c5c49af01733c2f7956f808db682a4193180dedb29dd718d7bbe"},
|
||||
{file = "ruff-0.0.280-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5f972567163a20fb8c2d6afc60c2ea5ef8b68d69505760a8bd0377de8984b4f6"},
|
||||
{file = "ruff-0.0.280-py3-none-musllinux_1_2_i686.whl", hash = "sha256:8ffa7347ad11643f29de100977c055e47c988cd6d9f5f5ff83027600b11b9189"},
|
||||
{file = "ruff-0.0.280-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7a37dab70114671d273f203268f6c3366c035fe0c8056614069e90a65e614bfc"},
|
||||
{file = "ruff-0.0.280-py3-none-win32.whl", hash = "sha256:7784e3606352fcfb193f3cd22b2e2117c444cb879ef6609ec69deabd662b0763"},
|
||||
{file = "ruff-0.0.280-py3-none-win_amd64.whl", hash = "sha256:4a7d52457b5dfcd3ab24b0b38eefaead8e2dca62b4fbf10de4cd0938cf20ce30"},
|
||||
{file = "ruff-0.0.280-py3-none-win_arm64.whl", hash = "sha256:b7de5b8689575918e130e4384ed9f539ce91d067c0a332aedef6ca7188adac2d"},
|
||||
{file = "ruff-0.0.280.tar.gz", hash = "sha256:581c43e4ac5e5a7117ad7da2120d960a4a99e68ec4021ec3cd47fe1cf78f8380"},
|
||||
{file = "ruff-0.0.278-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:1a90ebd8f2a554db1ee8d12b2f3aa575acbd310a02cd1a9295b3511a4874cf98"},
|
||||
{file = "ruff-0.0.278-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:38ca1c0c8c1221fe64c0a66784c91501d09a8ed02a4dbfdc117c0ce32a81eefc"},
|
||||
{file = "ruff-0.0.278-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c62a0bde4d20d087cabce2fa8b012d74c2e985da86d00fb3359880469b90e31"},
|
||||
{file = "ruff-0.0.278-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7545bb037823cd63dca19280f75a523a68bd3e78e003de74609320d6822b5a52"},
|
||||
{file = "ruff-0.0.278-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb380d2d6fdb60656a0b5fa78305535db513fc72ce11f4532cc1641204ef380"},
|
||||
{file = "ruff-0.0.278-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d11149c7b186f224f2055e437a030cd83b164a43cc0211314c33ad1553ed9c4c"},
|
||||
{file = "ruff-0.0.278-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:666e739fb2685277b879d493848afe6933e3be30d40f41fe0e571ad479d57d77"},
|
||||
{file = "ruff-0.0.278-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ec8b0469b54315803aaf1fbf9a37162a3849424cab6182496f972ad56e0ea702"},
|
||||
{file = "ruff-0.0.278-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c25b96602695a147d62a572865b753ef56aff1524abab13b9436724df30f9bd7"},
|
||||
{file = "ruff-0.0.278-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a48621f5f372d5019662db5b3dbfc5f1450f927683d75f1153fe0ebf20eb9698"},
|
||||
{file = "ruff-0.0.278-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1078125123a3c68e92463afacedb7e41b15ccafc09e510c6c755a23087afc8de"},
|
||||
{file = "ruff-0.0.278-py3-none-musllinux_1_2_i686.whl", hash = "sha256:3ce0d620e257b4cad16e2f0c103b2f43a07981668a3763380542e8a131d11537"},
|
||||
{file = "ruff-0.0.278-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1cae4c07d334eb588f171f1363fa89a8911047eb93184276be11a24dbbc996c7"},
|
||||
{file = "ruff-0.0.278-py3-none-win32.whl", hash = "sha256:70d39f5599d8449082ab8ce542fa98e16413145eb411dd1dc16575b44565d52d"},
|
||||
{file = "ruff-0.0.278-py3-none-win_amd64.whl", hash = "sha256:e131595ab7f4ce61a1650463bd2fe304b49e7d0deb0dfa664b92817c97cdba5f"},
|
||||
{file = "ruff-0.0.278-py3-none-win_arm64.whl", hash = "sha256:737a0cfb6c36aaa92d97a46957dfd5e55329299074ad06ed12663b98e0c6fc82"},
|
||||
{file = "ruff-0.0.278.tar.gz", hash = "sha256:1a9f1d925204cfba81b18368b7ac943befcfccc3a41e170c91353b674c6b7a66"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4211,4 +4186,4 @@ files = [
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.11"
|
||||
content-hash = "ab00edcd235c1c92dad9a91ace11d50df4564297193683cca7aa2b207ca27be6"
|
||||
content-hash = "06466753c4ce0063905809123b1e2bb444034d84acdd108dcb20a9f92ce12fa6"
|
||||
|
||||
@ -172,7 +172,6 @@ webauthn = "*"
|
||||
wsproto = "*"
|
||||
xmlsec = "*"
|
||||
zxcvbn = "*"
|
||||
jsonpatch = "*"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
bandit = "*"
|
||||
|
||||
121
schema.yml
121
schema.yml
@ -3633,60 +3633,78 @@ paths:
|
||||
operationId: core_tenants_list
|
||||
description: Tenant Viewset
|
||||
parameters:
|
||||
- in: query
|
||||
name: branding_favicon
|
||||
- name: branding_favicon
|
||||
required: false
|
||||
in: query
|
||||
description: branding_favicon
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: branding_logo
|
||||
- name: branding_logo
|
||||
required: false
|
||||
in: query
|
||||
description: branding_logo
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: branding_title
|
||||
- name: branding_title
|
||||
required: false
|
||||
in: query
|
||||
description: branding_title
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: default
|
||||
schema:
|
||||
type: boolean
|
||||
- in: query
|
||||
name: domain
|
||||
- name: default
|
||||
required: false
|
||||
in: query
|
||||
description: default
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: event_retention
|
||||
- name: domain
|
||||
required: false
|
||||
in: query
|
||||
description: domain
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: flow_authentication
|
||||
- name: event_retention
|
||||
required: false
|
||||
in: query
|
||||
description: event_retention
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- in: query
|
||||
name: flow_device_code
|
||||
- name: flow_authentication
|
||||
required: false
|
||||
in: query
|
||||
description: flow_authentication
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- in: query
|
||||
name: flow_invalidation
|
||||
- name: flow_device_code
|
||||
required: false
|
||||
in: query
|
||||
description: flow_device_code
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- in: query
|
||||
name: flow_recovery
|
||||
- name: flow_invalidation
|
||||
required: false
|
||||
in: query
|
||||
description: flow_invalidation
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- in: query
|
||||
name: flow_unenrollment
|
||||
- name: flow_recovery
|
||||
required: false
|
||||
in: query
|
||||
description: flow_recovery
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- in: query
|
||||
name: flow_user_settings
|
||||
- name: flow_unenrollment
|
||||
required: false
|
||||
in: query
|
||||
description: flow_unenrollment
|
||||
schema:
|
||||
type: string
|
||||
- name: flow_user_settings
|
||||
required: false
|
||||
in: query
|
||||
description: flow_user_settings
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- name: ordering
|
||||
required: false
|
||||
in: query
|
||||
@ -3711,16 +3729,18 @@ paths:
|
||||
description: A search term.
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: tenant_uuid
|
||||
- name: tenant_uuid
|
||||
required: false
|
||||
in: query
|
||||
description: tenant_uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- in: query
|
||||
name: web_certificate
|
||||
- name: web_certificate
|
||||
required: false
|
||||
in: query
|
||||
description: web_certificate
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
tags:
|
||||
- core
|
||||
security:
|
||||
@ -4589,12 +4609,12 @@ paths:
|
||||
schema:
|
||||
type: string
|
||||
enum:
|
||||
- default
|
||||
- external
|
||||
- internal
|
||||
- internal_service_account
|
||||
- service_account
|
||||
description: |-
|
||||
* `internal` - Internal
|
||||
* `default` - Default
|
||||
* `external` - External
|
||||
* `service_account` - Service Account
|
||||
* `internal_service_account` - Internal Service Account
|
||||
@ -5143,12 +5163,16 @@ paths:
|
||||
schema:
|
||||
type: boolean
|
||||
default: true
|
||||
- in: query
|
||||
name: managed
|
||||
- name: managed
|
||||
required: false
|
||||
in: query
|
||||
description: managed
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: name
|
||||
- name: name
|
||||
required: false
|
||||
in: query
|
||||
description: name
|
||||
schema:
|
||||
type: string
|
||||
- name: ordering
|
||||
@ -26665,6 +26689,7 @@ components:
|
||||
- authentik.crypto
|
||||
- authentik.events
|
||||
- authentik.flows
|
||||
- authentik.lib
|
||||
- authentik.outposts
|
||||
- authentik.policies.dummy
|
||||
- authentik.policies.event_matcher
|
||||
@ -26714,6 +26739,7 @@ components:
|
||||
* `authentik.crypto` - authentik Crypto
|
||||
* `authentik.events` - authentik Events
|
||||
* `authentik.flows` - authentik Flows
|
||||
* `authentik.lib` - authentik lib
|
||||
* `authentik.outposts` - authentik Outpost
|
||||
* `authentik.policies.dummy` - authentik Policies.Dummy
|
||||
* `authentik.policies.event_matcher` - authentik Policies.Event Matcher
|
||||
@ -29440,6 +29466,7 @@ components:
|
||||
* `authentik.crypto` - authentik Crypto
|
||||
* `authentik.events` - authentik Events
|
||||
* `authentik.flows` - authentik Flows
|
||||
* `authentik.lib` - authentik lib
|
||||
* `authentik.outposts` - authentik Outpost
|
||||
* `authentik.policies.dummy` - authentik Policies.Dummy
|
||||
* `authentik.policies.event_matcher` - authentik Policies.Event Matcher
|
||||
@ -29630,6 +29657,7 @@ components:
|
||||
* `authentik.crypto` - authentik Crypto
|
||||
* `authentik.events` - authentik Events
|
||||
* `authentik.flows` - authentik Flows
|
||||
* `authentik.lib` - authentik lib
|
||||
* `authentik.outposts` - authentik Outpost
|
||||
* `authentik.policies.dummy` - authentik Policies.Dummy
|
||||
* `authentik.policies.event_matcher` - authentik Policies.Event Matcher
|
||||
@ -36726,6 +36754,7 @@ components:
|
||||
* `authentik.crypto` - authentik Crypto
|
||||
* `authentik.events` - authentik Events
|
||||
* `authentik.flows` - authentik Flows
|
||||
* `authentik.lib` - authentik lib
|
||||
* `authentik.outposts` - authentik Outpost
|
||||
* `authentik.policies.dummy` - authentik Policies.Dummy
|
||||
* `authentik.policies.event_matcher` - authentik Policies.Event Matcher
|
||||
@ -42552,13 +42581,13 @@ components:
|
||||
- user
|
||||
UserTypeEnum:
|
||||
enum:
|
||||
- internal
|
||||
- default
|
||||
- external
|
||||
- service_account
|
||||
- internal_service_account
|
||||
type: string
|
||||
description: |-
|
||||
* `internal` - Internal
|
||||
* `default` - Default
|
||||
* `external` - External
|
||||
* `service_account` - Service Account
|
||||
* `internal_service_account` - Internal Service Account
|
||||
|
||||
@ -35,19 +35,6 @@ class OutpostKubernetesTests(TestCase):
|
||||
service_connection=self.service_connection,
|
||||
)
|
||||
self.outpost.providers.add(self.provider)
|
||||
self.outpost.config.kubernetes_json_patches = {
|
||||
"deployment": [
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/spec/template/spec/containers/0/resources",
|
||||
"value": {
|
||||
"requests": {"cpu": "2000m", "memory": "2000Mi"},
|
||||
"limits": {"cpu": "4000m", "memory": "8000Mi"},
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
self.outpost.providers.add(self.provider)
|
||||
self.outpost.save()
|
||||
|
||||
def test_deployment_reconciler(self):
|
||||
@ -59,18 +46,6 @@ class OutpostKubernetesTests(TestCase):
|
||||
|
||||
config = self.outpost.config
|
||||
config.kubernetes_replicas = 3
|
||||
config.kubernetes_json_patches = {
|
||||
"deployment": [
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/spec/template/spec/containers/0/resources",
|
||||
"value": {
|
||||
"requests": {"cpu": "1000m", "memory": "2000Mi"},
|
||||
"limits": {"cpu": "2000m", "memory": "4000Mi"},
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
self.outpost.config = config
|
||||
|
||||
with self.assertRaises(NeedsUpdate):
|
||||
|
||||
@ -15,7 +15,6 @@
|
||||
"trailingComma": "all",
|
||||
"useTabs": false,
|
||||
"vueIndentScriptAndStyle": false,
|
||||
"plugins": ["@trivago/prettier-plugin-sort-imports"],
|
||||
"importOrder": ["^(@?)lit(.*)$", "\\.css$", "^@goauthentik/api$", "^[./]"],
|
||||
"importOrderSeparation": true,
|
||||
"importOrderSortSpecifiers": true,
|
||||
|
||||
4222
web/package-lock.json
generated
4222
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -30,28 +30,28 @@
|
||||
"@codemirror/lang-javascript": "^6.1.9",
|
||||
"@codemirror/lang-python": "^6.1.3",
|
||||
"@codemirror/lang-xml": "^6.0.2",
|
||||
"@codemirror/legacy-modes": "^6.3.3",
|
||||
"@codemirror/legacy-modes": "^6.3.2",
|
||||
"@codemirror/theme-one-dark": "^6.1.2",
|
||||
"@formatjs/intl-listformat": "^7.4.0",
|
||||
"@fortawesome/fontawesome-free": "^6.4.0",
|
||||
"@goauthentik/api": "^2023.6.1-1690455444",
|
||||
"@goauthentik/api": "^2023.6.1-1689715510",
|
||||
"@lit-labs/context": "^0.3.3",
|
||||
"@lit-labs/task": "^2.1.2",
|
||||
"@lit/localize": "^0.11.4",
|
||||
"@patternfly/patternfly": "^4.224.2",
|
||||
"@sentry/browser": "^7.60.1",
|
||||
"@sentry/tracing": "^7.60.1",
|
||||
"@sentry/browser": "^7.59.3",
|
||||
"@sentry/tracing": "^7.59.3",
|
||||
"@webcomponents/webcomponentsjs": "^2.8.0",
|
||||
"base64-js": "^1.5.1",
|
||||
"chart.js": "^4.3.2",
|
||||
"chart.js": "^4.3.0",
|
||||
"chartjs-adapter-moment": "^1.0.1",
|
||||
"codemirror": "^6.0.1",
|
||||
"construct-style-sheets-polyfill": "^3.1.0",
|
||||
"core-js": "^3.32.0",
|
||||
"core-js": "^3.31.1",
|
||||
"country-flag-icons": "^1.5.7",
|
||||
"fuse.js": "^6.6.2",
|
||||
"lit": "^2.7.6",
|
||||
"mermaid": "^10.3.0",
|
||||
"mermaid": "^10.2.4",
|
||||
"rapidoc": "^9.3.4",
|
||||
"style-mod": "^4.0.3",
|
||||
"webcomponent-qr-code": "^1.2.0",
|
||||
@ -75,17 +75,17 @@
|
||||
"@rollup/plugin-node-resolve": "^15.0.2",
|
||||
"@rollup/plugin-replace": "^5.0.2",
|
||||
"@rollup/plugin-typescript": "^11.1.2",
|
||||
"@storybook/addon-essentials": "^7.1.1",
|
||||
"@storybook/addon-links": "^7.1.1",
|
||||
"@storybook/blocks": "^7.1.1",
|
||||
"@storybook/addon-essentials": "^7.1.0",
|
||||
"@storybook/addon-links": "^7.1.0",
|
||||
"@storybook/blocks": "^7.1.0",
|
||||
"@storybook/web-components": "^7.1.0",
|
||||
"@storybook/web-components-vite": "^7.1.1",
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.2.0",
|
||||
"@storybook/web-components-vite": "^7.1.0",
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.1.1",
|
||||
"@types/chart.js": "^2.9.37",
|
||||
"@types/codemirror": "5.60.8",
|
||||
"@types/grecaptcha": "^3.0.4",
|
||||
"@typescript-eslint/eslint-plugin": "^6.2.0",
|
||||
"@typescript-eslint/parser": "^6.2.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.1.0",
|
||||
"@typescript-eslint/parser": "^6.1.0",
|
||||
"babel-plugin-macros": "^3.1.0",
|
||||
"babel-plugin-tsconfig-paths": "^1.0.3",
|
||||
"eslint": "^8.45.0",
|
||||
@ -95,8 +95,8 @@
|
||||
"eslint-plugin-storybook": "^0.6.13",
|
||||
"lit-analyzer": "^1.2.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.0.0",
|
||||
"pyright": "^1.1.319",
|
||||
"prettier": "^2.8.8",
|
||||
"pyright": "^1.1.318",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"rollup": "^2.79.1",
|
||||
@ -106,17 +106,17 @@
|
||||
"rollup-plugin-postcss-lit": "^2.1.0",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"sharp-cli": "^4.1.1",
|
||||
"storybook": "^7.1.1",
|
||||
"storybook": "^7.1.0",
|
||||
"storybook-addon-mock": "^4.1.0",
|
||||
"ts-lit-plugin": "^1.2.1",
|
||||
"tslib": "^2.6.1",
|
||||
"tslib": "^2.6.0",
|
||||
"turnstile-types": "^1.1.2",
|
||||
"typescript": "^5.1.6",
|
||||
"vite-tsconfig-paths": "^4.2.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/darwin-arm64": "^0.18.17",
|
||||
"@esbuild/darwin-arm64": "^0.18.13",
|
||||
"@esbuild/linux-amd64": "^0.18.11",
|
||||
"@esbuild/linux-arm64": "^0.18.17"
|
||||
"@esbuild/linux-arm64": "^0.18.14"
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,10 +92,8 @@ export class RecentEventsCard extends Table<Event> {
|
||||
}
|
||||
|
||||
renderEmpty(): TemplateResult {
|
||||
return super.renderEmpty(
|
||||
html`<ak-empty-state header=${msg("No Events found.")}>
|
||||
<div slot="body">${msg("No matching events could be found.")}</div>
|
||||
</ak-empty-state>`,
|
||||
);
|
||||
return super.renderEmpty(html`<ak-empty-state header=${msg("No Events found.")}>
|
||||
<div slot="body">${msg("No matching events could be found.")}</div>
|
||||
</ak-empty-state>`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import "@goauthentik/admin/applications/ApplicationForm";
|
||||
import "@goauthentik/admin/applications/wizard/ApplicationWizard";
|
||||
import { PFSize } from "@goauthentik/app/elements/Spinner";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { uiConfig } from "@goauthentik/common/ui/config";
|
||||
import MDApplication from "@goauthentik/docs/core/applications.md";
|
||||
@ -12,12 +11,13 @@ import { getURLParam } from "@goauthentik/elements/router/RouteMatch";
|
||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
|
||||
import { TableColumn } from "@goauthentik/elements/table/Table";
|
||||
import { TablePage } from "@goauthentik/elements/table/TablePage";
|
||||
import "@goauthentik/user/LibraryApplication/AppIcon";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";
|
||||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
||||
|
||||
import { Application, CoreApi } from "@goauthentik/api";
|
||||
@ -56,6 +56,7 @@ export class ApplicationListPage extends TablePage<Application> {
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return super.styles.concat(
|
||||
PFAvatar,
|
||||
PFCard,
|
||||
css`
|
||||
/* Fix alignment issues with images in tables */
|
||||
@ -124,9 +125,24 @@ export class ApplicationListPage extends TablePage<Application> {
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
renderIcon(item: Application): TemplateResult {
|
||||
if (item?.metaIcon) {
|
||||
if (item.metaIcon.startsWith("fa://")) {
|
||||
const icon = item.metaIcon.replaceAll("fa://", "");
|
||||
return html`<i class="fas ${icon}"></i>`;
|
||||
}
|
||||
return html`<img
|
||||
class="app-icon pf-c-avatar"
|
||||
src="${ifDefined(item.metaIcon)}"
|
||||
alt="${msg("Application Icon")}"
|
||||
/>`;
|
||||
}
|
||||
return html`<i class="fas fa-share-square"></i>`;
|
||||
}
|
||||
|
||||
row(item: Application): TemplateResult[] {
|
||||
return [
|
||||
html`<ak-app-icon size=${PFSize.Medium} .app=${item}></ak-app-icon>`,
|
||||
this.renderIcon(item),
|
||||
html`<a href="#/core/applications/${item.slug}">
|
||||
<div>${item.name}</div>
|
||||
${item.metaPublisher ? html`<small>${item.metaPublisher}</small>` : html``}
|
||||
|
||||
@ -2,7 +2,6 @@ import "@goauthentik/admin/applications/ApplicationAuthorizeChart";
|
||||
import "@goauthentik/admin/applications/ApplicationCheckAccessForm";
|
||||
import "@goauthentik/admin/applications/ApplicationForm";
|
||||
import "@goauthentik/admin/policies/BoundPoliciesList";
|
||||
import { PFSize } from "@goauthentik/app/elements/Spinner";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import "@goauthentik/elements/EmptyState";
|
||||
@ -10,7 +9,6 @@ import "@goauthentik/elements/PageHeader";
|
||||
import "@goauthentik/elements/Tabs";
|
||||
import "@goauthentik/elements/buttons/SpinnerButton";
|
||||
import "@goauthentik/elements/events/ObjectChangelog";
|
||||
import "@goauthentik/user/LibraryApplication/AppIcon";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, html } from "lit";
|
||||
@ -82,15 +80,11 @@ export class ApplicationViewPage extends AKElement {
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<ak-page-header
|
||||
icon=${this.application?.metaIcon || ""}
|
||||
header=${this.application?.name || msg("Loading")}
|
||||
description=${ifDefined(this.application?.metaPublisher)}
|
||||
.iconImage=${true}
|
||||
>
|
||||
<ak-app-icon
|
||||
size=${PFSize.Small}
|
||||
slot="icon"
|
||||
.app=${this.application}
|
||||
></ak-app-icon>
|
||||
</ak-page-header>
|
||||
${this.renderApp()}`;
|
||||
}
|
||||
|
||||
@ -92,24 +92,6 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
|
||||
];
|
||||
}
|
||||
|
||||
// TODO: Make this more generic, maybe automatically get the plural name
|
||||
// of the object to use in the renderEmpty
|
||||
renderEmpty(inner?: TemplateResult): TemplateResult {
|
||||
return super.renderEmpty(html`
|
||||
${inner
|
||||
? inner
|
||||
: html`<ak-empty-state
|
||||
icon=${this.pageIcon()}
|
||||
header="${msg("No licenses found.")}"
|
||||
>
|
||||
<div slot="body">
|
||||
${this.searchEnabled() ? this.renderEmptyClearSearch() : html``}
|
||||
</div>
|
||||
<div slot="primary">${this.renderObjectCreate()}</div>
|
||||
</ak-empty-state>`}
|
||||
`);
|
||||
}
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
@ -167,9 +149,9 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
|
||||
<ak-aggregate-card
|
||||
class="pf-l-grid__item"
|
||||
icon="pf-icon pf-icon-user"
|
||||
header=${msg("Forecast internal users")}
|
||||
header=${msg("Forecast default users")}
|
||||
subtext=${msg(
|
||||
str`Estimated user count one year from now based on ${this.forecast?.users} current internal users and ${this.forecast?.forecastedUsers} forecasted internal users.`,
|
||||
str`Estimated user count one year from now based on ${this.forecast?.users} current users and ${this.forecast?.forecastedUsers} forecasted users.`,
|
||||
)}
|
||||
>
|
||||
~ ${(this.forecast?.users || 0) +
|
||||
|
||||
@ -121,22 +121,23 @@ export class BoundStagesList extends Table<FlowStageBinding> {
|
||||
}
|
||||
|
||||
renderEmpty(): TemplateResult {
|
||||
return super.renderEmpty(
|
||||
html`<ak-empty-state header=${msg("No Stages bound")} icon="pf-icon-module">
|
||||
<div slot="body">${msg("No stages are currently bound to this flow.")}</div>
|
||||
<div slot="primary">
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${msg("Create")} </span>
|
||||
<span slot="header"> ${msg("Create Stage binding")} </span>
|
||||
<ak-stage-binding-form slot="form" targetPk=${ifDefined(this.target)}>
|
||||
</ak-stage-binding-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${msg("Bind stage")}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</div>
|
||||
</ak-empty-state>`,
|
||||
);
|
||||
return super.renderEmpty(html`<ak-empty-state
|
||||
header=${msg("No Stages bound")}
|
||||
icon="pf-icon-module"
|
||||
>
|
||||
<div slot="body">${msg("No stages are currently bound to this flow.")}</div>
|
||||
<div slot="primary">
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${msg("Create")} </span>
|
||||
<span slot="header"> ${msg("Create Stage binding")} </span>
|
||||
<ak-stage-binding-form slot="form" targetPk=${ifDefined(this.target)}>
|
||||
</ak-stage-binding-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${msg("Bind stage")}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</div>
|
||||
</ak-empty-state>`);
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
|
||||
@ -40,14 +40,16 @@ export class FlowViewPage extends AKElement {
|
||||
flow!: Flow;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFPage, PFDescriptionList, PFButton, PFCard, PFContent, PFGrid].concat(css`
|
||||
img.pf-icon {
|
||||
max-height: 24px;
|
||||
}
|
||||
ak-tabs {
|
||||
height: 100%;
|
||||
}
|
||||
`);
|
||||
return [PFBase, PFPage, PFDescriptionList, PFButton, PFCard, PFContent, PFGrid].concat(
|
||||
css`
|
||||
img.pf-icon {
|
||||
max-height: 24px;
|
||||
}
|
||||
ak-tabs {
|
||||
height: 100%;
|
||||
}
|
||||
`,
|
||||
);
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
|
||||
@ -170,26 +170,27 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
|
||||
}
|
||||
|
||||
renderEmpty(): TemplateResult {
|
||||
return super.renderEmpty(
|
||||
html`<ak-empty-state header=${msg("No Policies bound.")} icon="pf-icon-module">
|
||||
<div slot="body">${msg("No policies are currently bound to this object.")}</div>
|
||||
<div slot="primary">
|
||||
<ak-forms-modal size=${PFSize.Medium}>
|
||||
<span slot="submit"> ${msg("Create")} </span>
|
||||
<span slot="header"> ${msg("Create Binding")} </span>
|
||||
<ak-policy-binding-form
|
||||
slot="form"
|
||||
targetPk=${ifDefined(this.target)}
|
||||
?policyOnly=${this.policyOnly}
|
||||
>
|
||||
</ak-policy-binding-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${msg("Create Binding")}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</div>
|
||||
</ak-empty-state>`,
|
||||
);
|
||||
return super.renderEmpty(html`<ak-empty-state
|
||||
header=${msg("No Policies bound.")}
|
||||
icon="pf-icon-module"
|
||||
>
|
||||
<div slot="body">${msg("No policies are currently bound to this object.")}</div>
|
||||
<div slot="primary">
|
||||
<ak-forms-modal size=${PFSize.Medium}>
|
||||
<span slot="submit"> ${msg("Create")} </span>
|
||||
<span slot="header"> ${msg("Create Binding")} </span>
|
||||
<ak-policy-binding-form
|
||||
slot="form"
|
||||
targetPk=${ifDefined(this.target)}
|
||||
?policyOnly=${this.policyOnly}
|
||||
>
|
||||
</ak-policy-binding-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${msg("Create Binding")}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
</div>
|
||||
</ak-empty-state>`);
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
|
||||
@ -191,8 +191,9 @@ export class LDAPProviderViewPage extends AKElement {
|
||||
class="pf-c-form-control"
|
||||
readonly
|
||||
type="text"
|
||||
value=${`cn=${this.me?.user
|
||||
.username},ou=users,${this.provider?.baseDn?.toLowerCase()}`}
|
||||
value=${`cn=${
|
||||
this.me?.user.username
|
||||
},ou=users,${this.provider?.baseDn?.toLowerCase()}`}
|
||||
/>
|
||||
</div>
|
||||
<div class="pf-c-form__group">
|
||||
|
||||
@ -120,62 +120,58 @@ export class SAMLProviderViewPage extends AKElement {
|
||||
renderRelatedObjects(): TemplateResult {
|
||||
const relatedObjects = [];
|
||||
if (this.provider?.assignedApplicationName) {
|
||||
relatedObjects.push(
|
||||
html`<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${msg("Metadata")}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<a
|
||||
class="pf-c-button pf-m-primary"
|
||||
target="_blank"
|
||||
href=${ifDefined(this.provider?.urlDownloadMetadata)}
|
||||
>
|
||||
${msg("Download")}
|
||||
</a>
|
||||
<ak-action-button
|
||||
class="pf-m-secondary"
|
||||
.apiRequest=${() => {
|
||||
if (!navigator.clipboard) {
|
||||
return Promise.resolve(
|
||||
showMessage({
|
||||
level: MessageLevel.info,
|
||||
message: this.provider?.urlDownloadMetadata || "",
|
||||
}),
|
||||
);
|
||||
}
|
||||
return navigator.clipboard.writeText(
|
||||
this.provider?.urlDownloadMetadata || "",
|
||||
relatedObjects.push(html`<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${msg("Metadata")}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<a
|
||||
class="pf-c-button pf-m-primary"
|
||||
target="_blank"
|
||||
href=${ifDefined(this.provider?.urlDownloadMetadata)}
|
||||
>
|
||||
${msg("Download")}
|
||||
</a>
|
||||
<ak-action-button
|
||||
class="pf-m-secondary"
|
||||
.apiRequest=${() => {
|
||||
if (!navigator.clipboard) {
|
||||
return Promise.resolve(
|
||||
showMessage({
|
||||
level: MessageLevel.info,
|
||||
message: this.provider?.urlDownloadMetadata || "",
|
||||
}),
|
||||
);
|
||||
}}
|
||||
>
|
||||
${msg("Copy download URL")}
|
||||
</ak-action-button>
|
||||
</div>
|
||||
</dd>
|
||||
</div>`,
|
||||
);
|
||||
}
|
||||
return navigator.clipboard.writeText(
|
||||
this.provider?.urlDownloadMetadata || "",
|
||||
);
|
||||
}}
|
||||
>
|
||||
${msg("Copy download URL")}
|
||||
</ak-action-button>
|
||||
</div>
|
||||
</dd>
|
||||
</div>`);
|
||||
}
|
||||
if (this.signer) {
|
||||
relatedObjects.push(
|
||||
html`<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${msg("Download signing certificate")}</span
|
||||
relatedObjects.push(html`<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${msg("Download signing certificate")}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<a
|
||||
class="pf-c-button pf-m-primary"
|
||||
href=${this.signer.certificateDownloadUrl}
|
||||
>${msg("Download")}</a
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<a
|
||||
class="pf-c-button pf-m-primary"
|
||||
href=${this.signer.certificateDownloadUrl}
|
||||
>${msg("Download")}</a
|
||||
>
|
||||
</div>
|
||||
</dd>
|
||||
</div>`,
|
||||
);
|
||||
</div>
|
||||
</dd>
|
||||
</div>`);
|
||||
}
|
||||
return html` <div class="pf-c-card pf-l-grid__item pf-m-12-col">
|
||||
<div class="pf-c-card__title">${msg("Related objects")}</div>
|
||||
|
||||
@ -80,27 +80,22 @@ export class UserForm extends ModelForm<User, number> {
|
||||
<ak-form-element-horizontal label=${msg("User type")} ?required=${true} name="type">
|
||||
<ak-radio
|
||||
.options=${[
|
||||
// TODO: Add better copy
|
||||
{
|
||||
label: "Internal",
|
||||
value: UserTypeEnum.Internal,
|
||||
label: "Default",
|
||||
value: UserTypeEnum.Default,
|
||||
default: true,
|
||||
description: html`${msg(
|
||||
"Internal users might be users such as company employees, which will get access to the full Enterprise feature set.",
|
||||
)}`,
|
||||
description: html`${msg("Default user")}`,
|
||||
},
|
||||
{
|
||||
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.",
|
||||
)}`,
|
||||
description: html`${msg("External user")}`,
|
||||
},
|
||||
{
|
||||
label: "Service account",
|
||||
value: UserTypeEnum.ServiceAccount,
|
||||
description: html`${msg(
|
||||
"Service accounts should be used for machine-to-machine authentication or other automations.",
|
||||
)}`,
|
||||
description: html`${msg("Service account")}`,
|
||||
},
|
||||
]}
|
||||
.value=${this.instance?.type}
|
||||
|
||||
@ -9,9 +9,6 @@
|
||||
--ak-dark-background-light: #1c1e21;
|
||||
--ak-dark-background-light-ish: #212427;
|
||||
--ak-dark-background-lighter: #2b2e33;
|
||||
|
||||
/* PatternFly likes to override global variables for some reason */
|
||||
--ak-global--Color--100: var(--pf-global--Color--100);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
:root {
|
||||
--pf-global--Color--100: var(--ak-dark-foreground) !important;
|
||||
--ak-global--Color--100: var(--ak-dark-foreground) !important;
|
||||
--pf-c-page__main-section--m-light--BackgroundColor: var(--ak-dark-background-darker);
|
||||
--pf-global--link--Color: var(--ak-dark-foreground-link) !important;
|
||||
}
|
||||
body {
|
||||
background-color: var(--ak-dark-background) !important;
|
||||
}
|
||||
:root {
|
||||
--pf-global--Color--100: var(--ak-dark-foreground) !important;
|
||||
--pf-c-page__main-section--m-light--BackgroundColor: var(--ak-dark-background-darker);
|
||||
--pf-global--link--Color: var(--ak-dark-foreground-link) !important;
|
||||
}
|
||||
.pf-c-radio {
|
||||
--pf-c-radio__label--Color: var(--ak-dark-foreground);
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||
import { CSSResult, TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import PFExpandableSection from "@patternfly/patternfly/components/ExpandableSection/expandable-section.css";
|
||||
@ -19,15 +19,7 @@ export class Expand extends AKElement {
|
||||
textClosed = msg("Show more");
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
PFBase,
|
||||
PFExpandableSection,
|
||||
css`
|
||||
.pf-c-expandable-section.pf-m-display-lg {
|
||||
background-color: var(--pf-global--BackgroundColor--100);
|
||||
}
|
||||
`,
|
||||
];
|
||||
return [PFBase, PFExpandableSection];
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
|
||||
@ -69,12 +69,9 @@ export class PageHeader extends AKElement {
|
||||
min-height: 114px;
|
||||
}
|
||||
.pf-c-button.pf-m-plain {
|
||||
background-color: transparent;
|
||||
background-color: var(--pf-c-page__main-section--m-light--BackgroundColor);
|
||||
border-radius: 0px;
|
||||
}
|
||||
.pf-c-page__main-section.pf-m-light {
|
||||
background-color: transparent;
|
||||
}
|
||||
.pf-c-page__main-section {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
@ -91,11 +88,6 @@ export class PageHeader extends AKElement {
|
||||
.notification-trigger.has-notifications {
|
||||
color: var(--pf-global--active-color--100);
|
||||
}
|
||||
h1 {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center !important;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
@ -125,10 +117,10 @@ export class PageHeader extends AKElement {
|
||||
renderIcon(): TemplateResult {
|
||||
if (this.icon) {
|
||||
if (this.iconImage && !this.icon.startsWith("fa://")) {
|
||||
return html`<img class="pf-icon" src="${this.icon}" alt="page icon" />`;
|
||||
return html`<img class="pf-icon" src="${this.icon}" alt="page icon" /> `;
|
||||
}
|
||||
const icon = this.icon.replaceAll("fa://", "fa ");
|
||||
return html`<i class=${icon}></i>`;
|
||||
return html`<i class=${icon}></i> `;
|
||||
}
|
||||
return html``;
|
||||
}
|
||||
@ -152,8 +144,8 @@ export class PageHeader extends AKElement {
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>
|
||||
<slot name="icon">${this.renderIcon()}</slot>
|
||||
<slot name="header">${this.header}</slot>
|
||||
${this.renderIcon()}
|
||||
<slot name="header"> ${this.header} </slot>
|
||||
</h1>
|
||||
${this.description ? html`<p>${this.description}</p>` : html``}
|
||||
</div>
|
||||
|
||||
@ -27,21 +27,20 @@ const metadata: Meta<AKActionButton> = {
|
||||
|
||||
export default metadata;
|
||||
|
||||
const container = (testItem: TemplateResult) =>
|
||||
html` <div style="background: #fff; padding: 2em">
|
||||
<style>
|
||||
li {
|
||||
display: block;
|
||||
}
|
||||
p {
|
||||
margin-top: 1em;
|
||||
}
|
||||
</style>
|
||||
<ak-message-container></ak-message-container>
|
||||
${testItem}
|
||||
<p>Messages received from the button:</p>
|
||||
<ul id="action-button-message-pad" style="margin-top: 1em"></ul>
|
||||
</div>`;
|
||||
const container = (testItem: TemplateResult) => html` <div style="background: #fff; padding: 2em">
|
||||
<style>
|
||||
li {
|
||||
display: block;
|
||||
}
|
||||
p {
|
||||
margin-top: 1em;
|
||||
}
|
||||
</style>
|
||||
<ak-message-container></ak-message-container>
|
||||
${testItem}
|
||||
<p>Messages received from the button:</p>
|
||||
<ul id="action-button-message-pad" style="margin-top: 1em"></ul>
|
||||
</div>`;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const displayMessage = (result: any) => {
|
||||
@ -82,9 +81,7 @@ export const ButtonWithError = () => {
|
||||
}, 3000);
|
||||
});
|
||||
|
||||
return container(
|
||||
html` <ak-action-button class="pf-m-secondary" .apiRequest=${run}
|
||||
>3 Seconds</ak-action-button
|
||||
>`,
|
||||
);
|
||||
return container(html` <ak-action-button class="pf-m-secondary" .apiRequest=${run}
|
||||
>3 Seconds</ak-action-button
|
||||
>`);
|
||||
};
|
||||
|
||||
@ -21,26 +21,25 @@ function makeid(length: number) {
|
||||
// We want the display to be rich and comprehensive. The next two functions provide
|
||||
// a styled wrapper for the return messages, and a styled wrapper for each message.
|
||||
|
||||
const container = (testItem: TemplateResult) =>
|
||||
html` <div style="background: #fff; padding: 2em">
|
||||
<style>
|
||||
li {
|
||||
display: block;
|
||||
}
|
||||
p {
|
||||
display: block;
|
||||
margin-top: 1em;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 0.2em;
|
||||
padding-left: 2.5rem;
|
||||
}
|
||||
</style>
|
||||
<ak-message-container></ak-message-container>
|
||||
${testItem}
|
||||
<p>Messages received from the button:</p>
|
||||
<ul id="action-button-message-pad" style="margin-top: 1em"></ul>
|
||||
</div>`;
|
||||
const container = (testItem: TemplateResult) => html` <div style="background: #fff; padding: 2em">
|
||||
<style>
|
||||
li {
|
||||
display: block;
|
||||
}
|
||||
p {
|
||||
display: block;
|
||||
margin-top: 1em;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 0.2em;
|
||||
padding-left: 2.5rem;
|
||||
}
|
||||
</style>
|
||||
<ak-message-container></ak-message-container>
|
||||
${testItem}
|
||||
<p>Messages received from the button:</p>
|
||||
<ul id="action-button-message-pad" style="margin-top: 1em"></ul>
|
||||
</div>`;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const displayMessage = (result: any) => {
|
||||
|
||||
@ -33,12 +33,7 @@ export const FONT_COLOUR_DARK_MODE = "#fafafa";
|
||||
export const FONT_COLOUR_LIGHT_MODE = "#151515";
|
||||
|
||||
export class RGBAColor {
|
||||
constructor(
|
||||
public r: number,
|
||||
public g: number,
|
||||
public b: number,
|
||||
public a: number = 1,
|
||||
) {}
|
||||
constructor(public r: number, public g: number, public b: number, public a: number = 1) {}
|
||||
toString(): string {
|
||||
return `rgba(${this.r}, ${this.g}, ${this.b}, ${this.a})`;
|
||||
}
|
||||
|
||||
@ -97,10 +97,8 @@ export class ObjectChangelog extends Table<Event> {
|
||||
}
|
||||
|
||||
renderEmpty(): TemplateResult {
|
||||
return super.renderEmpty(
|
||||
html`<ak-empty-state header=${msg("No Events found.")}>
|
||||
<div slot="body">${msg("No matching events could be found.")}</div>
|
||||
</ak-empty-state>`,
|
||||
);
|
||||
return super.renderEmpty(html`<ak-empty-state header=${msg("No Events found.")}>
|
||||
<div slot="body">${msg("No matching events could be found.")}</div>
|
||||
</ak-empty-state>`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,10 +71,8 @@ export class UserEvents extends Table<Event> {
|
||||
}
|
||||
|
||||
renderEmpty(): TemplateResult {
|
||||
return super.renderEmpty(
|
||||
html`<ak-empty-state header=${msg("No Events found.")}>
|
||||
<div slot="body">${msg("No matching events could be found.")}</div>
|
||||
</ak-empty-state>`,
|
||||
);
|
||||
return super.renderEmpty(html`<ak-empty-state header=${msg("No Events found.")}>
|
||||
<div slot="body">${msg("No matching events could be found.")}</div>
|
||||
</ak-empty-state>`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,10 +22,7 @@ import { ResponseError, ValidationError, ValidationErrorFromJSON } from "@goauth
|
||||
|
||||
export class PreventFormSubmit {
|
||||
// Stub class which can be returned by form elements to prevent the form from submitting
|
||||
constructor(
|
||||
public message: string,
|
||||
public element?: HorizontalFormElement,
|
||||
) {}
|
||||
constructor(public message: string, public element?: HorizontalFormElement) {}
|
||||
}
|
||||
|
||||
export class APIError extends Error {
|
||||
|
||||
@ -28,29 +28,31 @@ export class NotificationDrawer extends AKElement {
|
||||
unread = 0;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFButton, PFNotificationDrawer, PFContent, PFDropdown].concat(css`
|
||||
.pf-c-drawer__body {
|
||||
height: 100%;
|
||||
}
|
||||
.pf-c-notification-drawer__body {
|
||||
flex-grow: 1;
|
||||
}
|
||||
.pf-c-notification-drawer__header {
|
||||
height: 114px;
|
||||
align-items: center;
|
||||
}
|
||||
.pf-c-notification-drawer__header-action,
|
||||
.pf-c-notification-drawer__header-action-close,
|
||||
.pf-c-notification-drawer__header-action-close > .pf-c-button.pf-m-plain {
|
||||
height: 100%;
|
||||
}
|
||||
.pf-c-notification-drawer__list-item-description {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.pf-c-notification-drawer__footer {
|
||||
margin: 1rem;
|
||||
}
|
||||
`);
|
||||
return [PFBase, PFButton, PFNotificationDrawer, PFContent, PFDropdown].concat(
|
||||
css`
|
||||
.pf-c-drawer__body {
|
||||
height: 100%;
|
||||
}
|
||||
.pf-c-notification-drawer__body {
|
||||
flex-grow: 1;
|
||||
}
|
||||
.pf-c-notification-drawer__header {
|
||||
height: 114px;
|
||||
align-items: center;
|
||||
}
|
||||
.pf-c-notification-drawer__header-action,
|
||||
.pf-c-notification-drawer__header-action-close,
|
||||
.pf-c-notification-drawer__header-action-close > .pf-c-button.pf-m-plain {
|
||||
height: 100%;
|
||||
}
|
||||
.pf-c-notification-drawer__list-item-description {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.pf-c-notification-drawer__footer {
|
||||
margin: 1rem;
|
||||
}
|
||||
`,
|
||||
);
|
||||
}
|
||||
|
||||
firstUpdated(): void {
|
||||
|
||||
@ -48,9 +48,6 @@ export class TablePagination extends AKElement {
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
if (!this.pages) {
|
||||
return html``;
|
||||
}
|
||||
return html` <div class="pf-c-pagination pf-m-compact pf-m-hidden pf-m-visible-on-md">
|
||||
<div class="pf-c-pagination pf-m-compact pf-m-compact pf-m-hidden pf-m-visible-on-md">
|
||||
<div class="pf-c-options-menu">
|
||||
|
||||
@ -44,22 +44,24 @@ export class IdentificationStage extends BaseStage<
|
||||
form?: HTMLFormElement;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFAlert, PFLogin, PFForm, PFFormControl, PFTitle, PFButton].concat(css`
|
||||
/* login page's icons */
|
||||
.pf-c-login__main-footer-links-item button {
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
}
|
||||
.pf-c-login__main-footer-links-item img {
|
||||
fill: var(--pf-c-login__main-footer-links-item-link-svg--Fill);
|
||||
width: 100px;
|
||||
max-width: var(--pf-c-login__main-footer-links-item-link-svg--Width);
|
||||
height: 100%;
|
||||
max-height: var(--pf-c-login__main-footer-links-item-link-svg--Height);
|
||||
}
|
||||
`);
|
||||
return [PFBase, PFAlert, PFLogin, PFForm, PFFormControl, PFTitle, PFButton].concat(
|
||||
css`
|
||||
/* login page's icons */
|
||||
.pf-c-login__main-footer-links-item button {
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
}
|
||||
.pf-c-login__main-footer-links-item img {
|
||||
fill: var(--pf-c-login__main-footer-links-item-link-svg--Fill);
|
||||
width: 100px;
|
||||
max-width: var(--pf-c-login__main-footer-links-item-link-svg--Width);
|
||||
height: 100%;
|
||||
max-height: var(--pf-c-login__main-footer-links-item-link-svg--Height);
|
||||
}
|
||||
`,
|
||||
);
|
||||
}
|
||||
|
||||
firstUpdated(): void {
|
||||
|
||||
@ -200,13 +200,12 @@ ${prompt.initialValue}</textarea
|
||||
? LOCALES
|
||||
: LOCALES.filter((locale) => locale.code !== "debug");
|
||||
const options = locales.map(
|
||||
(locale) =>
|
||||
html`<option
|
||||
value=${locale.code}
|
||||
?selected=${locale.code === prompt.initialValue}
|
||||
>
|
||||
${locale.code.toUpperCase()} - ${locale.label()}
|
||||
</option> `,
|
||||
(locale) => html`<option
|
||||
value=${locale.code}
|
||||
?selected=${locale.code === prompt.initialValue}
|
||||
>
|
||||
${locale.code.toUpperCase()} - ${locale.label()}
|
||||
</option> `,
|
||||
);
|
||||
|
||||
return html`<select class="pf-c-form-control" name="${prompt.fieldKey}">
|
||||
|
||||
@ -1,8 +1,5 @@
|
||||
import { PFSize } from "@goauthentik/app/elements/Spinner";
|
||||
import { truncateWords } from "@goauthentik/common/utils";
|
||||
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
|
||||
import "@goauthentik/elements/Expand";
|
||||
import "@goauthentik/user/LibraryApplication/AppIcon";
|
||||
import { UserInterface } from "@goauthentik/user/UserInterface";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
@ -10,6 +7,7 @@ import { CSSResult, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
@ -32,36 +30,49 @@ export class LibraryApplication extends AKElement {
|
||||
PFBase,
|
||||
PFCard,
|
||||
PFButton,
|
||||
PFAvatar,
|
||||
css`
|
||||
.pf-c-card {
|
||||
--pf-c-card--BoxShadow: var(--pf-global--BoxShadow--md);
|
||||
height: 100%;
|
||||
}
|
||||
i.pf-icon {
|
||||
height: 36px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
.pf-c-avatar {
|
||||
--pf-c-avatar--BorderRadius: 0;
|
||||
}
|
||||
.pf-c-card__header {
|
||||
min-height: 60px;
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
}
|
||||
.pf-c-card__header a {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.expander {
|
||||
flex-grow: 1;
|
||||
}
|
||||
.pf-c-card__title {
|
||||
text-align: center;
|
||||
/* This is not ideal as it hard limits us to 2 lines of text for the title
|
||||
of the application. In theory that should be fine for most cases, but ideally
|
||||
we don't do this */
|
||||
height: 48px;
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
renderIcon(): TemplateResult {
|
||||
if (this.application?.metaIcon) {
|
||||
if (this.application.metaIcon.startsWith("fa://")) {
|
||||
const icon = this.application.metaIcon.replaceAll("fa://", "");
|
||||
return html`<i class="fas ${icon}"></i>`;
|
||||
}
|
||||
return html`<img
|
||||
class="app-icon pf-c-avatar"
|
||||
src="${ifDefined(this.application.metaIcon)}"
|
||||
alt="${msg("Application Icon")}"
|
||||
/>`;
|
||||
}
|
||||
return html`<i class="fas fa-share-square"></i>`;
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
if (!this.application) {
|
||||
return html`<ak-spinner></ak-spinner>`;
|
||||
@ -71,40 +82,41 @@ export class LibraryApplication extends AKElement {
|
||||
class="pf-c-card pf-m-hoverable pf-m-compact ${this.selected
|
||||
? "pf-m-selectable pf-m-selected"
|
||||
: ""}"
|
||||
style=${this.background !== "" ? `background: ${this.background} !important` : ""}
|
||||
style="background: ${this.background} !important"
|
||||
>
|
||||
<div class="pf-c-card__header">
|
||||
<a
|
||||
href="${ifDefined(this.application.launchUrl ?? "")}"
|
||||
target="${ifDefined(this.application.openInNewTab ? "_blank" : undefined)}"
|
||||
>
|
||||
<ak-app-icon size=${PFSize.Large} .app=${this.application}></ak-app-icon>
|
||||
${this.renderIcon()}
|
||||
</a>
|
||||
</div>
|
||||
<div class="pf-c-card__title">
|
||||
<a
|
||||
href="${ifDefined(this.application.launchUrl ?? "")}"
|
||||
target="${ifDefined(this.application.openInNewTab ? "_blank" : undefined)}"
|
||||
>${this.application.name}</a
|
||||
>
|
||||
</div>
|
||||
<div class="expander"></div>
|
||||
<ak-expand textOpen=${msg("Less details")} textClosed=${msg("More details")}>
|
||||
<div class="pf-c-content">
|
||||
<small>${this.application.metaPublisher}</small>
|
||||
</div>
|
||||
${truncateWords(this.application.metaDescription || "", 10)}
|
||||
${rootInterface()?.uiConfig?.enabledFeatures.applicationEdit && me?.user.isSuperuser
|
||||
? html`
|
||||
<a
|
||||
class="pf-c-button pf-m-control pf-m-small pf-m-block"
|
||||
class="pf-c-button pf-m-control pf-m-small"
|
||||
href="/if/admin/#/core/applications/${this.application?.slug}"
|
||||
>
|
||||
<i class="fas fa-pencil-alt"></i> ${msg("Edit")}
|
||||
<i class="fas fa-pencil-alt"></i>
|
||||
</a>
|
||||
`
|
||||
: html``}
|
||||
</ak-expand>
|
||||
</div>
|
||||
<div class="pf-c-card__title">
|
||||
<p>
|
||||
<a
|
||||
href="${ifDefined(this.application.launchUrl ?? "")}"
|
||||
target="${ifDefined(this.application.openInNewTab ? "_blank" : undefined)}"
|
||||
>${this.application.name}</a
|
||||
>
|
||||
</p>
|
||||
<div class="pf-c-content">
|
||||
<small>${this.application.metaPublisher}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
${truncateWords(this.application.metaDescription || "", 35)}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
@ -1,80 +0,0 @@
|
||||
import { AKElement } from "@goauthentik/app/elements/Base";
|
||||
import { PFSize } from "@goauthentik/app/elements/Spinner";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import PFFAIcons from "@patternfly/patternfly/base/patternfly-fa-icons.css";
|
||||
import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";
|
||||
|
||||
import { Application } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-app-icon")
|
||||
export class AppIcon extends AKElement {
|
||||
@property({ attribute: false })
|
||||
app?: Application;
|
||||
|
||||
@property()
|
||||
size?: PFSize;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
PFFAIcons,
|
||||
PFAvatar,
|
||||
css`
|
||||
:host([size="pf-m-lg"]) {
|
||||
--icon-height: 4rem;
|
||||
--icon-border: 0.25rem;
|
||||
}
|
||||
:host([size="pf-m-md"]) {
|
||||
--icon-height: 2rem;
|
||||
--icon-border: 0.125rem;
|
||||
}
|
||||
:host([size="pf-m-sm"]) {
|
||||
--icon-height: 1rem;
|
||||
--icon-border: 0.125rem;
|
||||
}
|
||||
.pf-c-avatar {
|
||||
--pf-c-avatar--BorderRadius: 0;
|
||||
--pf-c-avatar--Height: calc(
|
||||
var(--icon-height) + var(--icon-border) + var(--icon-border)
|
||||
);
|
||||
--pf-c-avatar--Width: calc(
|
||||
var(--icon-height) + var(--icon-border) + var(--icon-border)
|
||||
);
|
||||
}
|
||||
.icon {
|
||||
font-size: var(--icon-height);
|
||||
color: var(--ak-global--Color--100);
|
||||
padding: var(--icon-border);
|
||||
max-height: calc(var(--icon-height) + var(--icon-border) + var(--icon-border));
|
||||
line-height: calc(var(--icon-height) + var(--icon-border) + var(--icon-border));
|
||||
filter: drop-shadow(5px 5px 5px rgba(128, 128, 128, 0.25));
|
||||
}
|
||||
div {
|
||||
height: calc(var(--icon-height) + var(--icon-border) + var(--icon-border));
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
if (!this.app) {
|
||||
return html`<div><i class="icon fas fa-question-circle"></i></div>`;
|
||||
}
|
||||
if (this.app?.metaIcon) {
|
||||
if (this.app.metaIcon.startsWith("fa://")) {
|
||||
const icon = this.app.metaIcon.replaceAll("fa://", "");
|
||||
return html`<div><i class="icon fas ${icon}"></i></div>`;
|
||||
}
|
||||
return html`<img
|
||||
class="icon pf-c-avatar"
|
||||
src="${ifDefined(this.app.metaIcon)}"
|
||||
alt="${msg("Application Icon")}"
|
||||
/>`;
|
||||
}
|
||||
return html`<span class="icon">${this.app?.name.charAt(0).toUpperCase()}</span>`;
|
||||
}
|
||||
}
|
||||
@ -73,22 +73,12 @@ export class UserInterface extends Interface {
|
||||
z-index: auto !important;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
.pf-c-page__header {
|
||||
background-color: transparent !important;
|
||||
box-shadow: none !important;
|
||||
color: black !important;
|
||||
}
|
||||
:host([theme="dark"]) .pf-c-page__header {
|
||||
color: var(--ak-dark-foreground) !important;
|
||||
}
|
||||
.pf-c-page__header-tools-item .fas,
|
||||
.pf-c-notification-badge__count,
|
||||
.pf-c-page__header-tools-group .pf-c-button {
|
||||
color: var(--ak-global--Color--100) !important;
|
||||
}
|
||||
.pf-c-page {
|
||||
background-color: transparent;
|
||||
}
|
||||
.background-wrapper {
|
||||
background-color: var(--pf-c-page--BackgroundColor) !important;
|
||||
}
|
||||
.display-none {
|
||||
display: none;
|
||||
}
|
||||
@ -102,19 +92,10 @@ export class UserInterface extends Interface {
|
||||
.background-wrapper {
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: var(--pf-c-page--BackgroundColor) !important;
|
||||
}
|
||||
.background-default-slant {
|
||||
background-color: white; /*var(--ak-accent);*/
|
||||
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 calc(100% - 5vw));
|
||||
height: 50vh;
|
||||
}
|
||||
:host([theme="dark"]) .background-default-slant {
|
||||
background-color: black;
|
||||
}
|
||||
ak-locale-context {
|
||||
display: flex;
|
||||
@ -177,11 +158,7 @@ export class UserInterface extends Interface {
|
||||
return html` <ak-locale-context>
|
||||
<ak-enterprise-status interface="user"></ak-enterprise-status>
|
||||
<div class="pf-c-page">
|
||||
<div class="background-wrapper" style="${this.uiConfig.theme.background}">
|
||||
${this.uiConfig.theme.background === ""
|
||||
? html`<div class="background-default-slant"></div>`
|
||||
: html``}
|
||||
</div>
|
||||
<div class="background-wrapper" style="${this.uiConfig.theme.background}"></div>
|
||||
<header class="pf-c-page__header">
|
||||
<div class="pf-c-page__header-brand">
|
||||
<a href="#/" class="pf-c-page__header-brand-link">
|
||||
@ -238,7 +215,7 @@ export class UserInterface extends Interface {
|
||||
? "pf-m-unread"
|
||||
: ""}"
|
||||
>
|
||||
<i class="fas fa-bell" aria-hidden="true"></i>
|
||||
<i class="pf-icon-bell" aria-hidden="true"></i>
|
||||
<span class="pf-c-notification-badge__count"
|
||||
>${this.notificationsCount}</span
|
||||
>
|
||||
@ -267,7 +244,7 @@ export class UserInterface extends Interface {
|
||||
</div>
|
||||
${this.me.user.isSuperuser
|
||||
? html`<a
|
||||
class="pf-c-button pf-m-secondary pf-m-small pf-u-display-none pf-u-display-block-on-md"
|
||||
class="pf-c-button pf-m-primary pf-m-small pf-u-display-none pf-u-display-block-on-md"
|
||||
href="/if/admin"
|
||||
>
|
||||
${msg("Admin interface")}
|
||||
|
||||
@ -5751,6 +5751,15 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s9f9492d30a96b9c6">
|
||||
<source>User type</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0b9a40b7b2853c7d">
|
||||
<source>Default user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s35b9fa270f45b391">
|
||||
<source>External user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1a635369edaf4dc3">
|
||||
<source>Service account</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0e427111d750cc02">
|
||||
<source>Successfully updated license.</source>
|
||||
</trans-unit>
|
||||
@ -5766,18 +5775,45 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s2e109263b73c12d5">
|
||||
<source>Licenses</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf8f9f3032e891e16">
|
||||
<source>TODO Copy</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd49099e9522635f4">
|
||||
<source>License(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3be1d90ffa46b7f1">
|
||||
<source>Enterprise is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s34dca481f039c226">
|
||||
<source>How to get a license</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s948364901c166232">
|
||||
<source>Copy the installation ID</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s75c167446b237e0f">
|
||||
<source>Then open the customer portal</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9748dd3bd53d27a4">
|
||||
<source>Forecasted default users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6b18f594d94c2374">
|
||||
<source>Estimated user count one year from now</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s69f246d164be88d0">
|
||||
<source>Forecasted external users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s878fc2eaf94642db">
|
||||
<source>Estimated external user count one year from now</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd22bd01bdf28c548">
|
||||
<source>Cumulative license expiry</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sdeb6cee42435dd07">
|
||||
<source>Update License</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s99afa741c259d70e">
|
||||
<source>Create License</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7df5b92a3f93544f">
|
||||
<source>Warning: The current user count has exceeded the configured licenses.</source>
|
||||
</trans-unit>
|
||||
@ -5789,51 +5825,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9ce7cc01fb9b5b53">
|
||||
<source>Manage enterprise licenses</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf9ebf11ac2645820">
|
||||
<source>No licenses found.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa1db89262360550b">
|
||||
<source>Send us feedback!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4015746f55a8d89f">
|
||||
<source>Get a license</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb2cbd06f8e25b47e">
|
||||
<source>Go to Customer Portal</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf58825457d61c429">
|
||||
<source>Forecast internal users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sde9a3f41977ec1f8">
|
||||
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.users}"/> current internal users and <x id="1" equiv-text="${this.forecast?.forecastedUsers}"/> forecasted internal users.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4557b6b9da258643">
|
||||
<source>Forecast external users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf52479d6daa0a4a8">
|
||||
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.externalUsers}"/> current external users and <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> forecasted external users.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6196153c4b0c1ea0">
|
||||
<source>Install</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0285b4bd69130fa3">
|
||||
<source>Install License</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scef2eb6a2bfe3110">
|
||||
<source>Internal users might be users such as company employees, which will get access to the full Enterprise feature set.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf66389b04fcc219c">
|
||||
<source>External users might be external consultants or B2C customers. These users don't get access to enterprise features.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s77e8668a27dbc402">
|
||||
<source>Service accounts should be used for machine-to-machine authentication or other automations.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s28cbd874ba450b4e">
|
||||
<source>Less details</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8fa26f65aed77c96">
|
||||
<source>More details</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@ -6067,6 +6067,15 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s9f9492d30a96b9c6">
|
||||
<source>User type</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0b9a40b7b2853c7d">
|
||||
<source>Default user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s35b9fa270f45b391">
|
||||
<source>External user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1a635369edaf4dc3">
|
||||
<source>Service account</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0e427111d750cc02">
|
||||
<source>Successfully updated license.</source>
|
||||
</trans-unit>
|
||||
@ -6082,18 +6091,45 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s2e109263b73c12d5">
|
||||
<source>Licenses</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf8f9f3032e891e16">
|
||||
<source>TODO Copy</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd49099e9522635f4">
|
||||
<source>License(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3be1d90ffa46b7f1">
|
||||
<source>Enterprise is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s34dca481f039c226">
|
||||
<source>How to get a license</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s948364901c166232">
|
||||
<source>Copy the installation ID</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s75c167446b237e0f">
|
||||
<source>Then open the customer portal</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9748dd3bd53d27a4">
|
||||
<source>Forecasted default users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6b18f594d94c2374">
|
||||
<source>Estimated user count one year from now</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s69f246d164be88d0">
|
||||
<source>Forecasted external users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s878fc2eaf94642db">
|
||||
<source>Estimated external user count one year from now</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd22bd01bdf28c548">
|
||||
<source>Cumulative license expiry</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sdeb6cee42435dd07">
|
||||
<source>Update License</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s99afa741c259d70e">
|
||||
<source>Create License</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7df5b92a3f93544f">
|
||||
<source>Warning: The current user count has exceeded the configured licenses.</source>
|
||||
</trans-unit>
|
||||
@ -6105,51 +6141,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9ce7cc01fb9b5b53">
|
||||
<source>Manage enterprise licenses</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf9ebf11ac2645820">
|
||||
<source>No licenses found.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa1db89262360550b">
|
||||
<source>Send us feedback!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4015746f55a8d89f">
|
||||
<source>Get a license</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb2cbd06f8e25b47e">
|
||||
<source>Go to Customer Portal</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf58825457d61c429">
|
||||
<source>Forecast internal users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sde9a3f41977ec1f8">
|
||||
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.users}"/> current internal users and <x id="1" equiv-text="${this.forecast?.forecastedUsers}"/> forecasted internal users.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4557b6b9da258643">
|
||||
<source>Forecast external users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf52479d6daa0a4a8">
|
||||
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.externalUsers}"/> current external users and <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> forecasted external users.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6196153c4b0c1ea0">
|
||||
<source>Install</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0285b4bd69130fa3">
|
||||
<source>Install License</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scef2eb6a2bfe3110">
|
||||
<source>Internal users might be users such as company employees, which will get access to the full Enterprise feature set.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf66389b04fcc219c">
|
||||
<source>External users might be external consultants or B2C customers. These users don't get access to enterprise features.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s77e8668a27dbc402">
|
||||
<source>Service accounts should be used for machine-to-machine authentication or other automations.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s28cbd874ba450b4e">
|
||||
<source>Less details</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8fa26f65aed77c96">
|
||||
<source>More details</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@ -5659,6 +5659,15 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s9f9492d30a96b9c6">
|
||||
<source>User type</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0b9a40b7b2853c7d">
|
||||
<source>Default user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s35b9fa270f45b391">
|
||||
<source>External user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1a635369edaf4dc3">
|
||||
<source>Service account</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0e427111d750cc02">
|
||||
<source>Successfully updated license.</source>
|
||||
</trans-unit>
|
||||
@ -5674,18 +5683,45 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s2e109263b73c12d5">
|
||||
<source>Licenses</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf8f9f3032e891e16">
|
||||
<source>TODO Copy</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd49099e9522635f4">
|
||||
<source>License(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3be1d90ffa46b7f1">
|
||||
<source>Enterprise is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s34dca481f039c226">
|
||||
<source>How to get a license</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s948364901c166232">
|
||||
<source>Copy the installation ID</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s75c167446b237e0f">
|
||||
<source>Then open the customer portal</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9748dd3bd53d27a4">
|
||||
<source>Forecasted default users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6b18f594d94c2374">
|
||||
<source>Estimated user count one year from now</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s69f246d164be88d0">
|
||||
<source>Forecasted external users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s878fc2eaf94642db">
|
||||
<source>Estimated external user count one year from now</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd22bd01bdf28c548">
|
||||
<source>Cumulative license expiry</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sdeb6cee42435dd07">
|
||||
<source>Update License</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s99afa741c259d70e">
|
||||
<source>Create License</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7df5b92a3f93544f">
|
||||
<source>Warning: The current user count has exceeded the configured licenses.</source>
|
||||
</trans-unit>
|
||||
@ -5697,51 +5733,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9ce7cc01fb9b5b53">
|
||||
<source>Manage enterprise licenses</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf9ebf11ac2645820">
|
||||
<source>No licenses found.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa1db89262360550b">
|
||||
<source>Send us feedback!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4015746f55a8d89f">
|
||||
<source>Get a license</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb2cbd06f8e25b47e">
|
||||
<source>Go to Customer Portal</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf58825457d61c429">
|
||||
<source>Forecast internal users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sde9a3f41977ec1f8">
|
||||
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.users}"/> current internal users and <x id="1" equiv-text="${this.forecast?.forecastedUsers}"/> forecasted internal users.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4557b6b9da258643">
|
||||
<source>Forecast external users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf52479d6daa0a4a8">
|
||||
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.externalUsers}"/> current external users and <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> forecasted external users.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6196153c4b0c1ea0">
|
||||
<source>Install</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0285b4bd69130fa3">
|
||||
<source>Install License</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scef2eb6a2bfe3110">
|
||||
<source>Internal users might be users such as company employees, which will get access to the full Enterprise feature set.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf66389b04fcc219c">
|
||||
<source>External users might be external consultants or B2C customers. These users don't get access to enterprise features.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s77e8668a27dbc402">
|
||||
<source>Service accounts should be used for machine-to-machine authentication or other automations.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s28cbd874ba450b4e">
|
||||
<source>Less details</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8fa26f65aed77c96">
|
||||
<source>More details</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@ -5766,6 +5766,15 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s9f9492d30a96b9c6">
|
||||
<source>User type</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0b9a40b7b2853c7d">
|
||||
<source>Default user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s35b9fa270f45b391">
|
||||
<source>External user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1a635369edaf4dc3">
|
||||
<source>Service account</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0e427111d750cc02">
|
||||
<source>Successfully updated license.</source>
|
||||
</trans-unit>
|
||||
@ -5781,18 +5790,45 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s2e109263b73c12d5">
|
||||
<source>Licenses</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf8f9f3032e891e16">
|
||||
<source>TODO Copy</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd49099e9522635f4">
|
||||
<source>License(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3be1d90ffa46b7f1">
|
||||
<source>Enterprise is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s34dca481f039c226">
|
||||
<source>How to get a license</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s948364901c166232">
|
||||
<source>Copy the installation ID</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s75c167446b237e0f">
|
||||
<source>Then open the customer portal</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9748dd3bd53d27a4">
|
||||
<source>Forecasted default users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6b18f594d94c2374">
|
||||
<source>Estimated user count one year from now</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s69f246d164be88d0">
|
||||
<source>Forecasted external users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s878fc2eaf94642db">
|
||||
<source>Estimated external user count one year from now</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd22bd01bdf28c548">
|
||||
<source>Cumulative license expiry</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sdeb6cee42435dd07">
|
||||
<source>Update License</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s99afa741c259d70e">
|
||||
<source>Create License</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7df5b92a3f93544f">
|
||||
<source>Warning: The current user count has exceeded the configured licenses.</source>
|
||||
</trans-unit>
|
||||
@ -5804,51 +5840,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9ce7cc01fb9b5b53">
|
||||
<source>Manage enterprise licenses</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf9ebf11ac2645820">
|
||||
<source>No licenses found.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa1db89262360550b">
|
||||
<source>Send us feedback!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4015746f55a8d89f">
|
||||
<source>Get a license</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb2cbd06f8e25b47e">
|
||||
<source>Go to Customer Portal</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf58825457d61c429">
|
||||
<source>Forecast internal users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sde9a3f41977ec1f8">
|
||||
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.users}"/> current internal users and <x id="1" equiv-text="${this.forecast?.forecastedUsers}"/> forecasted internal users.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4557b6b9da258643">
|
||||
<source>Forecast external users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf52479d6daa0a4a8">
|
||||
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.externalUsers}"/> current external users and <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> forecasted external users.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6196153c4b0c1ea0">
|
||||
<source>Install</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0285b4bd69130fa3">
|
||||
<source>Install License</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scef2eb6a2bfe3110">
|
||||
<source>Internal users might be users such as company employees, which will get access to the full Enterprise feature set.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf66389b04fcc219c">
|
||||
<source>External users might be external consultants or B2C customers. These users don't get access to enterprise features.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s77e8668a27dbc402">
|
||||
<source>Service accounts should be used for machine-to-machine authentication or other automations.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s28cbd874ba450b4e">
|
||||
<source>Less details</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8fa26f65aed77c96">
|
||||
<source>More details</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@ -5898,6 +5898,15 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s9f9492d30a96b9c6">
|
||||
<source>User type</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0b9a40b7b2853c7d">
|
||||
<source>Default user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s35b9fa270f45b391">
|
||||
<source>External user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1a635369edaf4dc3">
|
||||
<source>Service account</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0e427111d750cc02">
|
||||
<source>Successfully updated license.</source>
|
||||
</trans-unit>
|
||||
@ -5913,18 +5922,45 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s2e109263b73c12d5">
|
||||
<source>Licenses</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf8f9f3032e891e16">
|
||||
<source>TODO Copy</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd49099e9522635f4">
|
||||
<source>License(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3be1d90ffa46b7f1">
|
||||
<source>Enterprise is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s34dca481f039c226">
|
||||
<source>How to get a license</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s948364901c166232">
|
||||
<source>Copy the installation ID</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s75c167446b237e0f">
|
||||
<source>Then open the customer portal</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9748dd3bd53d27a4">
|
||||
<source>Forecasted default users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6b18f594d94c2374">
|
||||
<source>Estimated user count one year from now</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s69f246d164be88d0">
|
||||
<source>Forecasted external users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s878fc2eaf94642db">
|
||||
<source>Estimated external user count one year from now</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd22bd01bdf28c548">
|
||||
<source>Cumulative license expiry</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sdeb6cee42435dd07">
|
||||
<source>Update License</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s99afa741c259d70e">
|
||||
<source>Create License</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7df5b92a3f93544f">
|
||||
<source>Warning: The current user count has exceeded the configured licenses.</source>
|
||||
</trans-unit>
|
||||
@ -5936,51 +5972,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9ce7cc01fb9b5b53">
|
||||
<source>Manage enterprise licenses</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf9ebf11ac2645820">
|
||||
<source>No licenses found.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa1db89262360550b">
|
||||
<source>Send us feedback!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4015746f55a8d89f">
|
||||
<source>Get a license</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb2cbd06f8e25b47e">
|
||||
<source>Go to Customer Portal</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf58825457d61c429">
|
||||
<source>Forecast internal users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sde9a3f41977ec1f8">
|
||||
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.users}"/> current internal users and <x id="1" equiv-text="${this.forecast?.forecastedUsers}"/> forecasted internal users.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4557b6b9da258643">
|
||||
<source>Forecast external users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf52479d6daa0a4a8">
|
||||
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.externalUsers}"/> current external users and <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> forecasted external users.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6196153c4b0c1ea0">
|
||||
<source>Install</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0285b4bd69130fa3">
|
||||
<source>Install License</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scef2eb6a2bfe3110">
|
||||
<source>Internal users might be users such as company employees, which will get access to the full Enterprise feature set.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf66389b04fcc219c">
|
||||
<source>External users might be external consultants or B2C customers. These users don't get access to enterprise features.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s77e8668a27dbc402">
|
||||
<source>Service accounts should be used for machine-to-machine authentication or other automations.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s28cbd874ba450b4e">
|
||||
<source>Less details</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8fa26f65aed77c96">
|
||||
<source>More details</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@ -6002,6 +6002,15 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s9f9492d30a96b9c6">
|
||||
<source>User type</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0b9a40b7b2853c7d">
|
||||
<source>Default user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s35b9fa270f45b391">
|
||||
<source>External user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1a635369edaf4dc3">
|
||||
<source>Service account</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0e427111d750cc02">
|
||||
<source>Successfully updated license.</source>
|
||||
</trans-unit>
|
||||
@ -6017,18 +6026,45 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s2e109263b73c12d5">
|
||||
<source>Licenses</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf8f9f3032e891e16">
|
||||
<source>TODO Copy</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd49099e9522635f4">
|
||||
<source>License(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3be1d90ffa46b7f1">
|
||||
<source>Enterprise is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s34dca481f039c226">
|
||||
<source>How to get a license</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s948364901c166232">
|
||||
<source>Copy the installation ID</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s75c167446b237e0f">
|
||||
<source>Then open the customer portal</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9748dd3bd53d27a4">
|
||||
<source>Forecasted default users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6b18f594d94c2374">
|
||||
<source>Estimated user count one year from now</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s69f246d164be88d0">
|
||||
<source>Forecasted external users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s878fc2eaf94642db">
|
||||
<source>Estimated external user count one year from now</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd22bd01bdf28c548">
|
||||
<source>Cumulative license expiry</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sdeb6cee42435dd07">
|
||||
<source>Update License</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s99afa741c259d70e">
|
||||
<source>Create License</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7df5b92a3f93544f">
|
||||
<source>Warning: The current user count has exceeded the configured licenses.</source>
|
||||
</trans-unit>
|
||||
@ -6040,51 +6076,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9ce7cc01fb9b5b53">
|
||||
<source>Manage enterprise licenses</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf9ebf11ac2645820">
|
||||
<source>No licenses found.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa1db89262360550b">
|
||||
<source>Send us feedback!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4015746f55a8d89f">
|
||||
<source>Get a license</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb2cbd06f8e25b47e">
|
||||
<source>Go to Customer Portal</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf58825457d61c429">
|
||||
<source>Forecast internal users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sde9a3f41977ec1f8">
|
||||
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.users}"/> current internal users and <x id="1" equiv-text="${this.forecast?.forecastedUsers}"/> forecasted internal users.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4557b6b9da258643">
|
||||
<source>Forecast external users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf52479d6daa0a4a8">
|
||||
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.externalUsers}"/> current external users and <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> forecasted external users.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6196153c4b0c1ea0">
|
||||
<source>Install</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0285b4bd69130fa3">
|
||||
<source>Install License</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scef2eb6a2bfe3110">
|
||||
<source>Internal users might be users such as company employees, which will get access to the full Enterprise feature set.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf66389b04fcc219c">
|
||||
<source>External users might be external consultants or B2C customers. These users don't get access to enterprise features.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s77e8668a27dbc402">
|
||||
<source>Service accounts should be used for machine-to-machine authentication or other automations.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s28cbd874ba450b4e">
|
||||
<source>Less details</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8fa26f65aed77c96">
|
||||
<source>More details</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@ -5649,6 +5649,15 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s9f9492d30a96b9c6">
|
||||
<source>User type</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0b9a40b7b2853c7d">
|
||||
<source>Default user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s35b9fa270f45b391">
|
||||
<source>External user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1a635369edaf4dc3">
|
||||
<source>Service account</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0e427111d750cc02">
|
||||
<source>Successfully updated license.</source>
|
||||
</trans-unit>
|
||||
@ -5664,18 +5673,45 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s2e109263b73c12d5">
|
||||
<source>Licenses</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf8f9f3032e891e16">
|
||||
<source>TODO Copy</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd49099e9522635f4">
|
||||
<source>License(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3be1d90ffa46b7f1">
|
||||
<source>Enterprise is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s34dca481f039c226">
|
||||
<source>How to get a license</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s948364901c166232">
|
||||
<source>Copy the installation ID</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s75c167446b237e0f">
|
||||
<source>Then open the customer portal</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9748dd3bd53d27a4">
|
||||
<source>Forecasted default users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6b18f594d94c2374">
|
||||
<source>Estimated user count one year from now</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s69f246d164be88d0">
|
||||
<source>Forecasted external users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s878fc2eaf94642db">
|
||||
<source>Estimated external user count one year from now</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd22bd01bdf28c548">
|
||||
<source>Cumulative license expiry</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sdeb6cee42435dd07">
|
||||
<source>Update License</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s99afa741c259d70e">
|
||||
<source>Create License</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7df5b92a3f93544f">
|
||||
<source>Warning: The current user count has exceeded the configured licenses.</source>
|
||||
</trans-unit>
|
||||
@ -5687,51 +5723,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9ce7cc01fb9b5b53">
|
||||
<source>Manage enterprise licenses</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf9ebf11ac2645820">
|
||||
<source>No licenses found.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa1db89262360550b">
|
||||
<source>Send us feedback!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4015746f55a8d89f">
|
||||
<source>Get a license</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb2cbd06f8e25b47e">
|
||||
<source>Go to Customer Portal</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf58825457d61c429">
|
||||
<source>Forecast internal users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sde9a3f41977ec1f8">
|
||||
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.users}"/> current internal users and <x id="1" equiv-text="${this.forecast?.forecastedUsers}"/> forecasted internal users.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4557b6b9da258643">
|
||||
<source>Forecast external users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf52479d6daa0a4a8">
|
||||
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.externalUsers}"/> current external users and <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> forecasted external users.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6196153c4b0c1ea0">
|
||||
<source>Install</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0285b4bd69130fa3">
|
||||
<source>Install License</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scef2eb6a2bfe3110">
|
||||
<source>Internal users might be users such as company employees, which will get access to the full Enterprise feature set.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf66389b04fcc219c">
|
||||
<source>External users might be external consultants or B2C customers. These users don't get access to enterprise features.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s77e8668a27dbc402">
|
||||
<source>Service accounts should be used for machine-to-machine authentication or other automations.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s28cbd874ba450b4e">
|
||||
<source>Less details</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8fa26f65aed77c96">
|
||||
<source>More details</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0"?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
|
||||
<?xml version="1.0" ?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
|
||||
<file target-language="zh-Hans" source-language="en" original="lit-localize-inputs" datatype="plaintext">
|
||||
<body>
|
||||
<trans-unit id="s4caed5b7a7e5d89b">
|
||||
@ -618,9 +618,9 @@
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="saa0e2675da69651b">
|
||||
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
|
||||
<target>未找到 URL "
|
||||
<x id="0" equiv-text="${this.url}"/>"。</target>
|
||||
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
|
||||
<target>未找到 URL "
|
||||
<x id="0" equiv-text="${this.url}"/>"。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s58cd9c2fe836d9c6">
|
||||
@ -1072,8 +1072,8 @@
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sa8384c9c26731f83">
|
||||
<source>To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have.</source>
|
||||
<target>要允许任何重定向 URI,请将此值设置为 ".*"。请注意这可能带来的安全影响。</target>
|
||||
<source>To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have.</source>
|
||||
<target>要允许任何重定向 URI,请将此值设置为 ".*"。请注意这可能带来的安全影响。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s55787f4dfcdce52b">
|
||||
@ -1819,8 +1819,8 @@
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sa90b7809586c35ce">
|
||||
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
|
||||
<target>输入完整 URL、相对路径,或者使用 'fa://fa-test' 来使用 Font Awesome 图标 "fa-test"。</target>
|
||||
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
|
||||
<target>输入完整 URL、相对路径,或者使用 'fa://fa-test' 来使用 Font Awesome 图标 "fa-test"。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s0410779cb47de312">
|
||||
@ -3248,8 +3248,8 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s76768bebabb7d543">
|
||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source>
|
||||
<target>包含组成员的字段。请注意,如果使用 "memberUid" 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...'</target>
|
||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source>
|
||||
<target>包含组成员的字段。请注意,如果使用 "memberUid" 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...'</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s026555347e589f0e">
|
||||
@ -4046,8 +4046,8 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s7b1fba26d245cb1c">
|
||||
<source>When using an external logging solution for archiving, this can be set to "minutes=5".</source>
|
||||
<target>使用外部日志记录解决方案进行存档时,可以将其设置为 "minutes=5"。</target>
|
||||
<source>When using an external logging solution for archiving, this can be set to "minutes=5".</source>
|
||||
<target>使用外部日志记录解决方案进行存档时,可以将其设置为 "minutes=5"。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s44536d20bb5c8257">
|
||||
@ -4056,8 +4056,8 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s3bb51cabb02b997e">
|
||||
<source>Format: "weeks=3;days=2;hours=3,seconds=2".</source>
|
||||
<target>格式:"weeks=3;days=2;hours=3,seconds=2"。</target>
|
||||
<source>Format: "weeks=3;days=2;hours=3,seconds=2".</source>
|
||||
<target>格式:"weeks=3;days=2;hours=3,seconds=2"。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s04bfd02201db5ab8">
|
||||
@ -4253,10 +4253,10 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sa95a538bfbb86111">
|
||||
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
|
||||
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
|
||||
<target>您确定要更新
|
||||
<x id="0" equiv-text="${this.objectLabel}"/>"
|
||||
<x id="1" equiv-text="${this.obj?.name}"/>" 吗?</target>
|
||||
<x id="0" equiv-text="${this.objectLabel}"/>"
|
||||
<x id="1" equiv-text="${this.obj?.name}"/>" 吗?</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sc92d7cfb6ee1fec6">
|
||||
@ -5372,7 +5372,7 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sdf1d8edef27236f0">
|
||||
<source>A "roaming" authenticator, like a YubiKey</source>
|
||||
<source>A "roaming" authenticator, like a YubiKey</source>
|
||||
<target>像 YubiKey 这样的“漫游”身份验证器</target>
|
||||
|
||||
</trans-unit>
|
||||
@ -5712,10 +5712,10 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s2d5f69929bb7221d">
|
||||
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
|
||||
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
|
||||
<target>
|
||||
<x id="0" equiv-text="${prompt.name}"/>("
|
||||
<x id="1" equiv-text="${prompt.fieldKey}"/>",类型为
|
||||
<x id="0" equiv-text="${prompt.name}"/>("
|
||||
<x id="1" equiv-text="${prompt.fieldKey}"/>",类型为
|
||||
<x id="2" equiv-text="${prompt.type}"/>)</target>
|
||||
|
||||
</trans-unit>
|
||||
@ -5764,7 +5764,7 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s1608b2f94fa0dbd4">
|
||||
<source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
|
||||
<source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
|
||||
<target>如果设置时长大于 0,用户可以选择“保持登录”选项,这将使用户的会话延长此处设置的时间。</target>
|
||||
|
||||
</trans-unit>
|
||||
@ -7585,6 +7585,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>User type</source>
|
||||
<target>用户类型</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0b9a40b7b2853c7d">
|
||||
<source>Default user</source>
|
||||
<target>默认用户</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s35b9fa270f45b391">
|
||||
<source>External user</source>
|
||||
<target>外部用户</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1a635369edaf4dc3">
|
||||
<source>Service account</source>
|
||||
<target>服务账户</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0e427111d750cc02">
|
||||
<source>Successfully updated license.</source>
|
||||
<target>已成功更新许可证。</target>
|
||||
@ -7605,6 +7617,10 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>Licenses</source>
|
||||
<target>许可证</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf8f9f3032e891e16">
|
||||
<source>TODO Copy</source>
|
||||
<target>未定副本</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd49099e9522635f4">
|
||||
<source>License(s)</source>
|
||||
<target>许可证</target>
|
||||
@ -7613,6 +7629,34 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>Enterprise is in preview.</source>
|
||||
<target>企业版目前处于预览状态。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s34dca481f039c226">
|
||||
<source>How to get a license</source>
|
||||
<target>如何获取许可证</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s948364901c166232">
|
||||
<source>Copy the installation ID</source>
|
||||
<target>复制安装 ID</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s75c167446b237e0f">
|
||||
<source>Then open the customer portal</source>
|
||||
<target>然后打开消费者中心</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9748dd3bd53d27a4">
|
||||
<source>Forecasted default users</source>
|
||||
<target>预测默认用户</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6b18f594d94c2374">
|
||||
<source>Estimated user count one year from now</source>
|
||||
<target>预计从此时开始一年内的用户数</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s69f246d164be88d0">
|
||||
<source>Forecasted external users</source>
|
||||
<target>预测外部用户</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s878fc2eaf94642db">
|
||||
<source>Estimated external user count one year from now</source>
|
||||
<target>预计从此时开始一年内的外部用户数</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd22bd01bdf28c548">
|
||||
<source>Cumulative license expiry</source>
|
||||
<target>累计许可证过期时间</target>
|
||||
@ -7621,6 +7665,10 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>Update License</source>
|
||||
<target>更新许可证</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s99afa741c259d70e">
|
||||
<source>Create License</source>
|
||||
<target>创建许可证</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7df5b92a3f93544f">
|
||||
<source>Warning: The current user count has exceeded the configured licenses.</source>
|
||||
<target>警告:当前用户数超过了配置的许可证限制</target>
|
||||
@ -7636,62 +7684,7 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s9ce7cc01fb9b5b53">
|
||||
<source>Manage enterprise licenses</source>
|
||||
<target>管理企业版许可证</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf9ebf11ac2645820">
|
||||
<source>No licenses found.</source>
|
||||
<target>未找到许可证。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa1db89262360550b">
|
||||
<source>Send us feedback!</source>
|
||||
<target>给我们发送反馈!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4015746f55a8d89f">
|
||||
<source>Get a license</source>
|
||||
<target>获取许可证</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb2cbd06f8e25b47e">
|
||||
<source>Go to Customer Portal</source>
|
||||
<target>前往客户中心</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf58825457d61c429">
|
||||
<source>Forecast internal users</source>
|
||||
<target>预测内部用户</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sde9a3f41977ec1f8">
|
||||
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.users}"/> current internal users and <x id="1" equiv-text="${this.forecast?.forecastedUsers}"/> forecasted internal users.</source>
|
||||
<target>根据当前 <x id="0" equiv-text="${this.forecast?.users}"/> 名内部用户和 <x id="1" equiv-text="${this.forecast?.forecastedUsers}"/> 名预测的内部用户,估算从此时起一年后的用户数。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4557b6b9da258643">
|
||||
<source>Forecast external users</source>
|
||||
<target>预测外部用户</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf52479d6daa0a4a8">
|
||||
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.externalUsers}"/> current external users and <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> forecasted external users.</source>
|
||||
<target>根据当前 <x id="0" equiv-text="${this.forecast?.externalUsers}"/> 名外部用户和 <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> 名预测的外部用户,估算从此时起一年后的用户数。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6196153c4b0c1ea0">
|
||||
<source>Install</source>
|
||||
<target>安装</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0285b4bd69130fa3">
|
||||
<source>Install License</source>
|
||||
<target>安装许可证</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="scef2eb6a2bfe3110">
|
||||
<source>Internal users might be users such as company employees, which will get access to the full Enterprise feature set.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf66389b04fcc219c">
|
||||
<source>External users might be external consultants or B2C customers. These users don't get access to enterprise features.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s77e8668a27dbc402">
|
||||
<source>Service accounts should be used for machine-to-machine authentication or other automations.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s28cbd874ba450b4e">
|
||||
<source>Less details</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8fa26f65aed77c96">
|
||||
<source>More details</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
</xliff>
|
||||
@ -5704,6 +5704,15 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s9f9492d30a96b9c6">
|
||||
<source>User type</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0b9a40b7b2853c7d">
|
||||
<source>Default user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s35b9fa270f45b391">
|
||||
<source>External user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1a635369edaf4dc3">
|
||||
<source>Service account</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0e427111d750cc02">
|
||||
<source>Successfully updated license.</source>
|
||||
</trans-unit>
|
||||
@ -5719,18 +5728,45 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s2e109263b73c12d5">
|
||||
<source>Licenses</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf8f9f3032e891e16">
|
||||
<source>TODO Copy</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd49099e9522635f4">
|
||||
<source>License(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3be1d90ffa46b7f1">
|
||||
<source>Enterprise is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s34dca481f039c226">
|
||||
<source>How to get a license</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s948364901c166232">
|
||||
<source>Copy the installation ID</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s75c167446b237e0f">
|
||||
<source>Then open the customer portal</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9748dd3bd53d27a4">
|
||||
<source>Forecasted default users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6b18f594d94c2374">
|
||||
<source>Estimated user count one year from now</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s69f246d164be88d0">
|
||||
<source>Forecasted external users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s878fc2eaf94642db">
|
||||
<source>Estimated external user count one year from now</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd22bd01bdf28c548">
|
||||
<source>Cumulative license expiry</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sdeb6cee42435dd07">
|
||||
<source>Update License</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s99afa741c259d70e">
|
||||
<source>Create License</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7df5b92a3f93544f">
|
||||
<source>Warning: The current user count has exceeded the configured licenses.</source>
|
||||
</trans-unit>
|
||||
@ -5742,51 +5778,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9ce7cc01fb9b5b53">
|
||||
<source>Manage enterprise licenses</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf9ebf11ac2645820">
|
||||
<source>No licenses found.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa1db89262360550b">
|
||||
<source>Send us feedback!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4015746f55a8d89f">
|
||||
<source>Get a license</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb2cbd06f8e25b47e">
|
||||
<source>Go to Customer Portal</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf58825457d61c429">
|
||||
<source>Forecast internal users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sde9a3f41977ec1f8">
|
||||
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.users}"/> current internal users and <x id="1" equiv-text="${this.forecast?.forecastedUsers}"/> forecasted internal users.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4557b6b9da258643">
|
||||
<source>Forecast external users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf52479d6daa0a4a8">
|
||||
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.externalUsers}"/> current external users and <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> forecasted external users.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6196153c4b0c1ea0">
|
||||
<source>Install</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0285b4bd69130fa3">
|
||||
<source>Install License</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scef2eb6a2bfe3110">
|
||||
<source>Internal users might be users such as company employees, which will get access to the full Enterprise feature set.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf66389b04fcc219c">
|
||||
<source>External users might be external consultants or B2C customers. These users don't get access to enterprise features.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s77e8668a27dbc402">
|
||||
<source>Service accounts should be used for machine-to-machine authentication or other automations.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s28cbd874ba450b4e">
|
||||
<source>Less details</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8fa26f65aed77c96">
|
||||
<source>More details</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@ -7617,6 +7617,10 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>Licenses</source>
|
||||
<target>许可证</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf8f9f3032e891e16">
|
||||
<source>TODO Copy</source>
|
||||
<target>未定副本</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd49099e9522635f4">
|
||||
<source>License(s)</source>
|
||||
<target>许可证</target>
|
||||
@ -7625,6 +7629,34 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>Enterprise is in preview.</source>
|
||||
<target>企业版目前处于预览状态。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s34dca481f039c226">
|
||||
<source>How to get a license</source>
|
||||
<target>如何获取许可证</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s948364901c166232">
|
||||
<source>Copy the installation ID</source>
|
||||
<target>复制安装 ID</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s75c167446b237e0f">
|
||||
<source>Then open the customer portal</source>
|
||||
<target>然后打开消费者中心</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9748dd3bd53d27a4">
|
||||
<source>Forecasted default users</source>
|
||||
<target>预测默认用户</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6b18f594d94c2374">
|
||||
<source>Estimated user count one year from now</source>
|
||||
<target>预计从此时开始一年内的用户数</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s69f246d164be88d0">
|
||||
<source>Forecasted external users</source>
|
||||
<target>预测外部用户</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s878fc2eaf94642db">
|
||||
<source>Estimated external user count one year from now</source>
|
||||
<target>预计从此时开始一年内的外部用户数</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd22bd01bdf28c548">
|
||||
<source>Cumulative license expiry</source>
|
||||
<target>累计许可证过期时间</target>
|
||||
@ -7633,6 +7665,10 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>Update License</source>
|
||||
<target>更新许可证</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s99afa741c259d70e">
|
||||
<source>Create License</source>
|
||||
<target>创建许可证</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7df5b92a3f93544f">
|
||||
<source>Warning: The current user count has exceeded the configured licenses.</source>
|
||||
<target>警告:当前用户数超过了配置的许可证限制</target>
|
||||
@ -7648,46 +7684,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s9ce7cc01fb9b5b53">
|
||||
<source>Manage enterprise licenses</source>
|
||||
<target>管理企业版许可证</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf9ebf11ac2645820">
|
||||
<source>No licenses found.</source>
|
||||
<target>未找到许可证。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa1db89262360550b">
|
||||
<source>Send us feedback!</source>
|
||||
<target>给我们发送反馈!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4015746f55a8d89f">
|
||||
<source>Get a license</source>
|
||||
<target>获取许可证</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb2cbd06f8e25b47e">
|
||||
<source>Go to Customer Portal</source>
|
||||
<target>前往客户中心</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf58825457d61c429">
|
||||
<source>Forecast internal users</source>
|
||||
<target>预测内部用户</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sde9a3f41977ec1f8">
|
||||
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.users}"/> current internal users and <x id="1" equiv-text="${this.forecast?.forecastedUsers}"/> forecasted internal users.</source>
|
||||
<target>根据当前 <x id="0" equiv-text="${this.forecast?.users}"/> 名内部用户和 <x id="1" equiv-text="${this.forecast?.forecastedUsers}"/> 名预测的内部用户,估算从此时起一年后的用户数。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4557b6b9da258643">
|
||||
<source>Forecast external users</source>
|
||||
<target>预测外部用户</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf52479d6daa0a4a8">
|
||||
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.externalUsers}"/> current external users and <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> forecasted external users.</source>
|
||||
<target>根据当前 <x id="0" equiv-text="${this.forecast?.externalUsers}"/> 名外部用户和 <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> 名预测的外部用户,估算从此时起一年后的用户数。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6196153c4b0c1ea0">
|
||||
<source>Install</source>
|
||||
<target>安装</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0285b4bd69130fa3">
|
||||
<source>Install License</source>
|
||||
<target>安装许可证</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@ -5703,6 +5703,15 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s9f9492d30a96b9c6">
|
||||
<source>User type</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0b9a40b7b2853c7d">
|
||||
<source>Default user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s35b9fa270f45b391">
|
||||
<source>External user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1a635369edaf4dc3">
|
||||
<source>Service account</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0e427111d750cc02">
|
||||
<source>Successfully updated license.</source>
|
||||
</trans-unit>
|
||||
@ -5718,18 +5727,45 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s2e109263b73c12d5">
|
||||
<source>Licenses</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf8f9f3032e891e16">
|
||||
<source>TODO Copy</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd49099e9522635f4">
|
||||
<source>License(s)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3be1d90ffa46b7f1">
|
||||
<source>Enterprise is in preview.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s34dca481f039c226">
|
||||
<source>How to get a license</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s948364901c166232">
|
||||
<source>Copy the installation ID</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s75c167446b237e0f">
|
||||
<source>Then open the customer portal</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9748dd3bd53d27a4">
|
||||
<source>Forecasted default users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6b18f594d94c2374">
|
||||
<source>Estimated user count one year from now</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s69f246d164be88d0">
|
||||
<source>Forecasted external users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s878fc2eaf94642db">
|
||||
<source>Estimated external user count one year from now</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd22bd01bdf28c548">
|
||||
<source>Cumulative license expiry</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sdeb6cee42435dd07">
|
||||
<source>Update License</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s99afa741c259d70e">
|
||||
<source>Create License</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7df5b92a3f93544f">
|
||||
<source>Warning: The current user count has exceeded the configured licenses.</source>
|
||||
</trans-unit>
|
||||
@ -5741,51 +5777,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9ce7cc01fb9b5b53">
|
||||
<source>Manage enterprise licenses</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf9ebf11ac2645820">
|
||||
<source>No licenses found.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa1db89262360550b">
|
||||
<source>Send us feedback!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4015746f55a8d89f">
|
||||
<source>Get a license</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb2cbd06f8e25b47e">
|
||||
<source>Go to Customer Portal</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf58825457d61c429">
|
||||
<source>Forecast internal users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sde9a3f41977ec1f8">
|
||||
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.users}"/> current internal users and <x id="1" equiv-text="${this.forecast?.forecastedUsers}"/> forecasted internal users.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4557b6b9da258643">
|
||||
<source>Forecast external users</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf52479d6daa0a4a8">
|
||||
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.externalUsers}"/> current external users and <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> forecasted external users.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6196153c4b0c1ea0">
|
||||
<source>Install</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0285b4bd69130fa3">
|
||||
<source>Install License</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="scef2eb6a2bfe3110">
|
||||
<source>Internal users might be users such as company employees, which will get access to the full Enterprise feature set.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf66389b04fcc219c">
|
||||
<source>External users might be external consultants or B2C customers. These users don't get access to enterprise features.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s77e8668a27dbc402">
|
||||
<source>Service accounts should be used for machine-to-machine authentication or other automations.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s28cbd874ba450b4e">
|
||||
<source>Less details</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8fa26f65aed77c96">
|
||||
<source>More details</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@ -1,37 +0,0 @@
|
||||
---
|
||||
title: "Procedural topic"
|
||||
---
|
||||
|
||||
Use a title that focuses on the task you are writing about... for example, "Add a new Group" or "Edit user profiles". For procedural docs, there should be a verb in the tilte, and usually the noun (the thing you are working on). For the title (and all headings) use the infinitive form of the verb (i.e. "add") not the gerund form (i.e. "adding").
|
||||
|
||||
In this first section write one or two sentences about the task. Keep it brief; if it goes on too long, then create a separate conceptual topic, in a separate `.md` file. We don't want readers to have to scroll through paragraphs of conceptual info before they get to Step 1.
|
||||
|
||||
## Prerequisites (optional section)
|
||||
|
||||
In this section, inform the reader of anything they need to do, or have configured or installed, before they start following the procedural instructions below.
|
||||
|
||||
## Overview of steps/workflow (optional section)
|
||||
|
||||
If the task is quite long or complex, it might be good to add a bullet list of the main steps, or even a diagram of the workflow, just so that the reader can first familairize themselves with the 50,000 meter view before they dive into the detailed steps.
|
||||
|
||||
## first several group steps
|
||||
|
||||
If the task involves a lot of steps, try to group them into simalr steps and have a Head3 or Hedad4 title for each group.
|
||||
|
||||
In this section, help the reader get oriented... where do they need to be (i.e. in the GUI, on a CLI, etc).
|
||||
|
||||
Have a separate paragraph for each step.
|
||||
|
||||
Start instructions with the desired outcome, followed by the instructions.
|
||||
|
||||
EXAMPLE: To define a new port number, navigate to the Admin interface, and then to the **Settings** tab.
|
||||
|
||||
## next step of grouped steps
|
||||
|
||||
Continue with the steps...
|
||||
|
||||
Use screenshots sparingly, only for complex UIs where it is difficult to describe a UI element with words.
|
||||
|
||||
## verify the steps
|
||||
|
||||
Whenever possible, it is useful to add verification steps at the end of a procedural topic. For example, if the procedural was about installing a product, use this section to tell them how they can verify that the install was successful
|
||||
@ -10,16 +10,16 @@ Everyone welcome; we will work on code, docs, and anything else that looks inter
|
||||
|
||||
Moderators will be available for most US and European hours, so if during the multi-day event, participants have questions or a PR needs a technical review, we are here for you.
|
||||
|
||||
Prizes? Why, Yes! We've got a total prize pool of $5000 and a bunch of cool authentik-branded socks and, indubitably, GitHub fame.
|
||||
Prizes? Why, Yes! We've got a total prize pool of $5000 and a bunch of cool authentik-branded socks, food certificates, and indubitably GitHub fame.
|
||||
|
||||
## When
|
||||
|
||||
July 26-30, 2023
|
||||
|
||||
- Kickoff meeting is on Wednesday, July 26th, at 8:00am Pacific USA (UTC -7), 5:00pm in Central Europe (UTC +2), and 8:30pm in Mumbai (UTC +5.30)
|
||||
- Check-in calls on Thursday and Friday, for one hour, at the same times as above.
|
||||
- Wrap-up and first demos on Saturday, starting at same times as above.
|
||||
- Final demos, voting, and awards on Sunday! Yep, same times as above.
|
||||
* Kickoff meeting is on Wednesday, July 26th, at 8:00am Pacific USA (UTC -7), 5:00pm in Central Europe (UTC +2), and 8:30pm in Mumbai (UTC +5.30)
|
||||
* Check-in calls on Thursday and Friday, for one hour, at the same times as above.
|
||||
* Wrap-up and first demos on Saturday, starting at same times as above.
|
||||
* Final demos, voting, and awards on Sunday! Yep, same times as above.
|
||||
|
||||
## Where
|
||||
|
||||
@ -34,9 +34,8 @@ Use the [Hackathon 2023 Registration Form](https://docs.google.com/forms/d/e/1FA
|
||||
If you already know what you and/or your team want to work on, you can open an [Issue](https://github.com/goauthentik/authentik/issues) using our template for all hackathon Issues at any time (why not now?) and add the `hackathon` label. Then, when you register, enter the Issue number that you opened on your registration form. This way, on Kickoff Day we can easily match particpants with their Issue of interest.
|
||||
|
||||
During the Kickoff call, there will be time to peruse existing Issues and add emotes to indicate your interest in working on it (or having it worked on!)
|
||||
|
||||
- 🚀 I want to work on this
|
||||
- ❤️ I want to see this worked on
|
||||
- 🚀 I want to work on this
|
||||
- ❤️ I want to see this worked on
|
||||
|
||||
## Agenda
|
||||
|
||||
@ -52,7 +51,7 @@ During the Kickoff call, there will be time to peruse existing Issues and add em
|
||||
|
||||
## About that money...
|
||||
|
||||
Be aware that all prize money distributions will follow local/state/country laws regarding taxation, not providing funds to citizens of countries prohibited by US law, and all other legal requirements.
|
||||
Be aware that all prize money distributions will follow local/state/country laws regarding taxation, providing funds to countries prohibited by US law, and all other legal requirements.
|
||||
|
||||
## Questions?
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user