Merge branch 'main' into application-wizard-2

* main: (36 commits)
  website/blog: add github user name links (#6468)
  website/developer-docs: add new template for procedures (#6390)
  website/blogs: blog to celebrate hackathon (#6457)
  web/flows: add more stories (#6444)
  web: bump prettier from 3.0.0 to 3.0.1 in /web (#6465)
  core: bump debugpy from 1.6.7 to 1.6.8 (#6458)
  ci: bump peter-evans/create-pull-request from 4 to 5 (#6459)
  web: bump lit from 2.7.6 to 2.8.0 in /web (#6460)
  web: bump @fortawesome/fontawesome-free from 6.4.0 to 6.4.2 in /web (#6461)
  web: bump chart.js from 4.3.2 to 4.3.3 in /web (#6462)
  web: bump @lit-labs/task from 2.1.2 to 3.0.0 in /web (#6463)
  web, website: compress images (#6121)
  core: bump cryptography from 41.0.2 to 41.0.3 (#6456)
  root: replace builtin psycopg libpq binary implementation with distro… (#6448)
  website: fix broken links in NewsBar
  core: bump github.com/getsentry/sentry-go from 0.22.0 to 0.23.0 (#6449)
  core: bump goauthentik.io/api/v3 from 3.2023061.6 to 3.2023061.7 (#6450)
  web: bump pyright from 1.1.319 to 1.1.320 in /web (#6451)
  core: bump ruff from 0.0.281 to 0.0.282 (#6453)
  core: bump golang from 1.20.6-bullseye to 1.20.7-bullseye (#6454)
  ...
This commit is contained in:
Ken Sternberg 2023-08-03 13:51:25 -07:00
commit 79e0a32fa8
291 changed files with 2265 additions and 1704 deletions

View File

@ -14,7 +14,7 @@ runs:
run: | run: |
pipx install poetry || true pipx install poetry || true
sudo apt update sudo apt update
sudo apt install -y libxmlsec1-dev pkg-config gettext sudo apt install -y libpq-dev openssl libxmlsec1-dev pkg-config gettext
- name: Setup python and restore poetry - name: Setup python and restore poetry
uses: actions/setup-python@v3 uses: actions/setup-python@v3
with: with:

View File

@ -38,6 +38,11 @@ updates:
patterns: patterns:
- "@babel/*" - "@babel/*"
- "babel-*" - "babel-*"
eslint:
patterns:
- "@typescript-eslint/eslint-*"
- "eslint"
- "eslint-*"
storybook: storybook:
patterns: patterns:
- "@storybook/*" - "@storybook/*"

View File

@ -88,8 +88,8 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
psql: psql:
- 11-alpine
- 12-alpine - 12-alpine
- 15-alpine
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Setup authentik env - name: Setup authentik env

61
.github/workflows/image-compress.yml vendored Normal file
View File

@ -0,0 +1,61 @@
---
name: authentik-compress-images
on:
push:
branches:
- main
paths:
- "**.jpg"
- "**.jpeg"
- "**.png"
- "**.webp"
pull_request:
paths:
- "**.jpg"
- "**.jpeg"
- "**.png"
- "**.webp"
workflow_dispatch:
jobs:
compress:
name: compress
runs-on: ubuntu-latest
# Don't run on forks. Token will not be available. Will run on main and open a PR anyway
if: |
github.repository == 'goauthentik/authentik' &&
(github.event_name != 'pull_request' ||
github.event.pull_request.head.repo.full_name == github.repository)
steps:
- id: generate_token
uses: tibdex/github-app-token@v1
with:
app_id: ${{ secrets.GH_APP_ID }}
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/checkout@v3
with:
token: ${{ steps.generate_token.outputs.token }}
- name: Compress images
id: compress
uses: calibreapp/image-actions@main
with:
githubToken: ${{ steps.generate_token.outputs.token }}
compressOnly: ${{ github.event_name != 'pull_request' }}
- uses: peter-evans/create-pull-request@v5
if: "${{ github.event_name != 'pull_request' && steps.compress.outputs.markdown != '' }}"
id: cpr
with:
token: ${{ steps.generate_token.outputs.token }}
title: "*: Auto compress images"
branch-suffix: timestamp
commit-messsage: "*: compress images"
body: ${{ steps.compress.outputs.markdown }}
delete-branch: true
signoff: true
- uses: peter-evans/enable-pull-request-automerge@v3
if: "${{ github.event_name != 'pull_request' && steps.compress.outputs.markdown != '' }}"
with:
token: ${{ steps.generate_token.outputs.token }}
pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}
merge-method: squash

View File

@ -31,7 +31,8 @@
"!Format sequence", "!Format sequence",
"!Condition sequence", "!Condition sequence",
"!Env sequence", "!Env sequence",
"!Env scalar" "!Env scalar",
"!If sequence"
], ],
"typescript.preferences.importModuleSpecifier": "non-relative", "typescript.preferences.importModuleSpecifier": "non-relative",
"typescript.preferences.importModuleSpecifierEnding": "index", "typescript.preferences.importModuleSpecifierEnding": "index",

View File

@ -31,7 +31,7 @@ RUN pip install --no-cache-dir poetry && \
poetry export -f requirements.txt --dev --output requirements-dev.txt poetry export -f requirements.txt --dev --output requirements-dev.txt
# Stage 4: Build go proxy # Stage 4: Build go proxy
FROM docker.io/golang:1.20.6-bullseye AS go-builder FROM docker.io/golang:1.20.7-bullseye AS go-builder
WORKDIR /work WORKDIR /work
@ -81,13 +81,13 @@ COPY --from=geoip /usr/share/GeoIP /geoip
RUN apt-get update && \ RUN apt-get update && \
# Required for installing pip packages # Required for installing pip packages
apt-get install -y --no-install-recommends build-essential pkg-config libxmlsec1-dev zlib1g-dev && \ apt-get install -y --no-install-recommends build-essential pkg-config libxmlsec1-dev zlib1g-dev libpq-dev python3-dev && \
# Required for runtime # Required for runtime
apt-get install -y --no-install-recommends libxmlsec1-openssl libmaxminddb0 && \ apt-get install -y --no-install-recommends libpq5 openssl libxmlsec1-openssl libmaxminddb0 && \
# Required for bootstrap & healtcheck # Required for bootstrap & healtcheck
apt-get install -y --no-install-recommends runit && \ apt-get install -y --no-install-recommends runit && \
pip install --no-cache-dir -r /requirements.txt && \ pip install --no-cache-dir -r /requirements.txt && \
apt-get remove --purge -y build-essential pkg-config libxmlsec1-dev && \ apt-get remove --purge -y build-essential pkg-config libxmlsec1-dev libpq-dev python3-dev && \
apt-get autoremove --purge -y && \ apt-get autoremove --purge -y && \
apt-get clean && \ apt-get clean && \
rm -rf /tmp/* /var/lib/apt/lists/* /var/tmp/ && \ rm -rf /tmp/* /var/lib/apt/lists/* /var/tmp/ && \

View File

@ -140,6 +140,9 @@ web-watch:
touch web/dist/.gitkeep touch web/dist/.gitkeep
cd web && npm run watch cd web && npm run watch
web-storybook-watch:
cd web && npm run storybook
web-lint-fix: web-lint-fix:
cd web && npm run prettier cd web && npm run prettier

View File

@ -93,10 +93,10 @@ class ConfigView(APIView):
"traces_sample_rate": float(CONFIG.get("error_reporting.sample_rate", 0.4)), "traces_sample_rate": float(CONFIG.get("error_reporting.sample_rate", 0.4)),
}, },
"capabilities": self.get_capabilities(), "capabilities": self.get_capabilities(),
"cache_timeout": int(CONFIG.get("redis.cache_timeout")), "cache_timeout": CONFIG.get_int("redis.cache_timeout"),
"cache_timeout_flows": int(CONFIG.get("redis.cache_timeout_flows")), "cache_timeout_flows": CONFIG.get_int("redis.cache_timeout_flows"),
"cache_timeout_policies": int(CONFIG.get("redis.cache_timeout_policies")), "cache_timeout_policies": CONFIG.get_int("redis.cache_timeout_policies"),
"cache_timeout_reputation": int(CONFIG.get("redis.cache_timeout_reputation")), "cache_timeout_reputation": CONFIG.get_int("redis.cache_timeout_reputation"),
} }
) )

View File

@ -7,7 +7,5 @@ entries:
state: absent state: absent
- identifiers: - identifiers:
name: "%(id)s" name: "%(id)s"
expression: |
return True
model: authentik_policies_expression.expressionpolicy model: authentik_policies_expression.expressionpolicy
state: absent state: absent

View File

@ -9,6 +9,8 @@ context:
mapping: mapping:
key1: value key1: value
key2: 2 key2: 2
context1: context-nested-value
context2: !Context context1
entries: entries:
- model: !Format ["%s", authentik_sources_oauth.oauthsource] - model: !Format ["%s", authentik_sources_oauth.oauthsource]
state: !Format ["%s", present] state: !Format ["%s", present]
@ -97,6 +99,7 @@ entries:
[list, with, items, !Format ["foo-%s", !Context foo]], [list, with, items, !Format ["foo-%s", !Context foo]],
] ]
if_true_simple: !If [!Context foo, true, text] if_true_simple: !If [!Context foo, true, text]
if_short: !If [!Context foo]
if_false_simple: !If [null, false, 2] if_false_simple: !If [null, false, 2]
enumerate_mapping_to_mapping: !Enumerate [ enumerate_mapping_to_mapping: !Enumerate [
!Context mapping, !Context mapping,
@ -141,6 +144,7 @@ entries:
] ]
] ]
] ]
nested_context: !Context context2
identifiers: identifiers:
name: test name: test
conditions: conditions:

View File

@ -155,6 +155,7 @@ class TestBlueprintsV1(TransactionTestCase):
}, },
"if_false_complex": ["list", "with", "items", "foo-bar"], "if_false_complex": ["list", "with", "items", "foo-bar"],
"if_true_simple": True, "if_true_simple": True,
"if_short": True,
"if_false_simple": 2, "if_false_simple": 2,
"enumerate_mapping_to_mapping": { "enumerate_mapping_to_mapping": {
"prefix-key1": "other-prefix-value", "prefix-key1": "other-prefix-value",
@ -211,6 +212,7 @@ class TestBlueprintsV1(TransactionTestCase):
], ],
}, },
}, },
"nested_context": "context-nested-value",
} }
) )
) )

View File

@ -249,6 +249,8 @@ class Context(YAMLTag):
value = self.default value = self.default
if self.key in blueprint.context: if self.key in blueprint.context:
value = blueprint.context[self.key] value = blueprint.context[self.key]
if isinstance(value, YAMLTag):
return value.resolve(entry, blueprint)
return value return value
@ -372,6 +374,10 @@ class If(YAMLTag):
def __init__(self, loader: "BlueprintLoader", node: SequenceNode) -> None: def __init__(self, loader: "BlueprintLoader", node: SequenceNode) -> None:
super().__init__() super().__init__()
self.condition = loader.construct_object(node.value[0]) self.condition = loader.construct_object(node.value[0])
if len(node.value) == 1:
self.when_true = True
self.when_false = False
else:
self.when_true = loader.construct_object(node.value[1]) self.when_true = loader.construct_object(node.value[1])
self.when_false = loader.construct_object(node.value[2]) self.when_false = loader.construct_object(node.value[2])

View File

@ -199,9 +199,6 @@ class Importer:
serializer_kwargs = {} serializer_kwargs = {}
model_instance = existing_models.first() model_instance = existing_models.first()
if not isinstance(model(), BaseMetaModel) and model_instance: if not isinstance(model(), BaseMetaModel) and model_instance:
if entry.get_state(self.__import) == BlueprintEntryDesiredState.CREATED:
self.logger.debug("instance exists, skipping")
return None
self.logger.debug( self.logger.debug(
"initialise serializer with instance", "initialise serializer with instance",
model=model, model=model,
@ -268,21 +265,34 @@ class Importer:
try: try:
serializer = self._validate_single(entry) serializer = self._validate_single(entry)
except EntryInvalidError as exc: except EntryInvalidError as exc:
# For deleting objects we don't need the serializer to be valid
if entry.get_state(self.__import) == BlueprintEntryDesiredState.ABSENT:
continue
self.logger.warning(f"entry invalid: {exc}", entry=entry, error=exc) self.logger.warning(f"entry invalid: {exc}", entry=entry, error=exc)
return False return False
if not serializer: if not serializer:
continue continue
state = entry.get_state(self.__import) state = entry.get_state(self.__import)
if state in [ if state in [BlueprintEntryDesiredState.PRESENT, BlueprintEntryDesiredState.CREATED]:
BlueprintEntryDesiredState.PRESENT, instance = serializer.instance
BlueprintEntryDesiredState.CREATED, if (
]: instance
model = serializer.save() and not instance._state.adding
and state == BlueprintEntryDesiredState.CREATED
):
self.logger.debug(
"instance exists, skipping",
model=model,
instance=instance,
pk=instance.pk,
)
else:
instance = serializer.save()
self.logger.debug("updated model", model=instance)
if "pk" in entry.identifiers: if "pk" in entry.identifiers:
self.__pk_map[entry.identifiers["pk"]] = model.pk self.__pk_map[entry.identifiers["pk"]] = instance.pk
entry._state = BlueprintEntryState(model) entry._state = BlueprintEntryState(instance)
self.logger.debug("updated model", model=model)
elif state == BlueprintEntryDesiredState.ABSENT: elif state == BlueprintEntryDesiredState.ABSENT:
instance: Optional[Model] = serializer.instance instance: Optional[Model] = serializer.instance
if instance.pk: if instance.pk:
@ -309,5 +319,6 @@ class Importer:
self.logger.debug("Blueprint validation failed") self.logger.debug("Blueprint validation failed")
for log in logs: for log in logs:
getattr(self.logger, log.get("log_level"))(**log) getattr(self.logger, log.get("log_level"))(**log)
self.logger.debug("Finished blueprint import validation")
self.__import = orig_import self.__import = orig_import
return successful, logs return successful, logs

View File

@ -1,55 +1,11 @@
# Generated by Django 3.2.8 on 2021-10-10 16:16 # Generated by Django 3.2.8 on 2021-10-10 16:16
from os import environ
import django.db.models.deletion import django.db.models.deletion
from django.apps.registry import Apps
from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
import authentik.core.models import authentik.core.models
def create_default_user(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
from django.contrib.auth.hashers import make_password
User = apps.get_model("authentik_core", "User")
db_alias = schema_editor.connection.alias
akadmin, _ = User.objects.using(db_alias).get_or_create(
username="akadmin",
email=environ.get("AUTHENTIK_BOOTSTRAP_EMAIL", "root@localhost"),
name="authentik Default Admin",
)
password = None
if "TF_BUILD" in environ or settings.TEST:
password = "akadmin" # noqa # nosec
if "AUTHENTIK_BOOTSTRAP_PASSWORD" in environ:
password = environ["AUTHENTIK_BOOTSTRAP_PASSWORD"]
if password:
akadmin.password = make_password(password)
else:
akadmin.password = make_password(None)
akadmin.save()
def create_default_admin_group(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
db_alias = schema_editor.connection.alias
Group = apps.get_model("authentik_core", "Group")
User = apps.get_model("authentik_core", "User")
# Creates a default admin group
group, _ = Group.objects.using(db_alias).get_or_create(
is_superuser=True,
defaults={
"name": "authentik Admins",
},
)
group.users.set(User.objects.filter(username="akadmin"))
group.save()
class Migration(migrations.Migration): class Migration(migrations.Migration):
replaces = [ replaces = [
("authentik_core", "0002_auto_20200523_1133"), ("authentik_core", "0002_auto_20200523_1133"),
@ -119,9 +75,6 @@ class Migration(migrations.Migration):
model_name="user", model_name="user",
name="is_staff", name="is_staff",
), ),
migrations.RunPython(
code=create_default_user,
),
migrations.AddField( migrations.AddField(
model_name="user", model_name="user",
name="is_superuser", name="is_superuser",
@ -201,9 +154,6 @@ class Migration(migrations.Migration):
default=False, help_text="Users added to this group will be superusers." default=False, help_text="Users added to this group will be superusers."
), ),
), ),
migrations.RunPython(
code=create_default_admin_group,
),
migrations.AlterModelManagers( migrations.AlterModelManagers(
name="user", name="user",
managers=[ managers=[

View File

@ -1,7 +1,6 @@
# Generated by Django 3.2.8 on 2021-10-10 16:12 # Generated by Django 3.2.8 on 2021-10-10 16:12
import uuid import uuid
from os import environ
import django.db.models.deletion import django.db.models.deletion
from django.apps.registry import Apps from django.apps.registry import Apps
@ -35,29 +34,6 @@ def fix_duplicates(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
Token.objects.using(db_alias).filter(identifier=ident["identifier"]).delete() Token.objects.using(db_alias).filter(identifier=ident["identifier"]).delete()
def create_default_user_token(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
from authentik.core.models import TokenIntents
User = apps.get_model("authentik_core", "User")
Token = apps.get_model("authentik_core", "Token")
db_alias = schema_editor.connection.alias
akadmin = User.objects.using(db_alias).filter(username="akadmin")
if not akadmin.exists():
return
if "AUTHENTIK_BOOTSTRAP_TOKEN" not in environ:
return
key = environ["AUTHENTIK_BOOTSTRAP_TOKEN"]
Token.objects.using(db_alias).create(
identifier="authentik-bootstrap-token",
user=akadmin.first(),
intent=TokenIntents.INTENT_API,
expiring=False,
key=key,
)
class Migration(migrations.Migration): class Migration(migrations.Migration):
replaces = [ replaces = [
("authentik_core", "0018_auto_20210330_1345"), ("authentik_core", "0018_auto_20210330_1345"),
@ -214,9 +190,6 @@ class Migration(migrations.Migration):
"verbose_name_plural": "Authenticated Sessions", "verbose_name_plural": "Authenticated Sessions",
}, },
), ),
migrations.RunPython(
code=create_default_user_token,
),
migrations.AlterField( migrations.AlterField(
model_name="token", model_name="token",
name="intent", name="intent",

View File

@ -60,7 +60,7 @@ def default_token_key():
"""Default token key""" """Default token key"""
# We use generate_id since the chars in the key should be easy # We use generate_id since the chars in the key should be easy
# to use in Emails (for verification) and URLs (for recovery) # to use in Emails (for verification) and URLs (for recovery)
return generate_id(int(CONFIG.get("default_token_length"))) return generate_id(CONFIG.get_int("default_token_length"))
class UserTypes(models.TextChoices): class UserTypes(models.TextChoices):

View File

@ -33,7 +33,7 @@ PLAN_CONTEXT_SOURCE = "source"
# Is set by the Flow Planner when a FlowToken was used, and the currently active flow plan # Is set by the Flow Planner when a FlowToken was used, and the currently active flow plan
# was restored. # was restored.
PLAN_CONTEXT_IS_RESTORED = "is_restored" PLAN_CONTEXT_IS_RESTORED = "is_restored"
CACHE_TIMEOUT = int(CONFIG.get("redis.cache_timeout_flows")) CACHE_TIMEOUT = CONFIG.get_int("redis.cache_timeout_flows")
CACHE_PREFIX = "goauthentik.io/flows/planner/" CACHE_PREFIX = "goauthentik.io/flows/planner/"

View File

@ -213,6 +213,14 @@ class ConfigLoader:
attr: Attr = get_path_from_dict(root, path, sep=sep, default=Attr(default)) attr: Attr = get_path_from_dict(root, path, sep=sep, default=Attr(default))
return attr.value return attr.value
def get_int(self, path: str, default=0) -> int:
"""Wrapper for get that converts value into int"""
try:
return int(self.get(path, default))
except ValueError as exc:
self.log("warning", "Failed to parse config as int", path=path, exc=str(exc))
return default
def get_bool(self, path: str, default=False) -> bool: def get_bool(self, path: str, default=False) -> bool:
"""Wrapper for get that converts value into boolean""" """Wrapper for get that converts value into boolean"""
return str(self.get(path, default)).lower() == "true" return str(self.get(path, default)).lower() == "true"

View File

@ -98,7 +98,7 @@ def traces_sampler(sampling_context: dict) -> float:
def before_send(event: dict, hint: dict) -> Optional[dict]: def before_send(event: dict, hint: dict) -> Optional[dict]:
"""Check if error is database error, and ignore if so""" """Check if error is database error, and ignore if so"""
# pylint: disable=no-name-in-module # pylint: disable=no-name-in-module
from psycopg2.errors import Error from psycopg.errors import Error
ignored_classes = ( ignored_classes = (
# Inbuilt types # Inbuilt types

View File

@ -79,3 +79,15 @@ class TestConfig(TestCase):
config.update_from_file(file2_name) config.update_from_file(file2_name)
unlink(file_name) unlink(file_name)
unlink(file2_name) unlink(file2_name)
def test_get_int(self):
"""Test get_int"""
config = ConfigLoader()
config.set("foo", 1234)
self.assertEqual(config.get_int("foo"), 1234)
def test_get_int_invalid(self):
"""Test get_int"""
config = ConfigLoader()
config.set("foo", "bar")
self.assertEqual(config.get_int("foo", 1234), 1234)

View File

@ -19,7 +19,7 @@ from authentik.policies.types import CACHE_PREFIX, PolicyRequest, PolicyResult
LOGGER = get_logger() LOGGER = get_logger()
FORK_CTX = get_context("fork") FORK_CTX = get_context("fork")
CACHE_TIMEOUT = int(CONFIG.get("redis.cache_timeout_policies")) CACHE_TIMEOUT = CONFIG.get_int("redis.cache_timeout_policies")
PROCESS_CLASS = FORK_CTX.Process PROCESS_CLASS = FORK_CTX.Process

View File

@ -13,7 +13,7 @@ from authentik.policies.reputation.tasks import save_reputation
from authentik.stages.identification.signals import identification_failed from authentik.stages.identification.signals import identification_failed
LOGGER = get_logger() LOGGER = get_logger()
CACHE_TIMEOUT = int(CONFIG.get("redis.cache_timeout_reputation")) CACHE_TIMEOUT = CONFIG.get_int("redis.cache_timeout_reputation")
def update_score(request: HttpRequest, identifier: str, amount: int): def update_score(request: HttpRequest, identifier: str, amount: int):

View File

@ -1,6 +1,6 @@
"""id_token utils""" """id_token utils"""
from dataclasses import asdict, dataclass, field from dataclasses import asdict, dataclass, field
from typing import TYPE_CHECKING, Any, Optional from typing import TYPE_CHECKING, Any, Optional, Union
from django.db import models from django.db import models
from django.http import HttpRequest from django.http import HttpRequest
@ -57,7 +57,7 @@ class IDToken:
# Subject, https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.2 # Subject, https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.2
sub: Optional[str] = None sub: Optional[str] = None
# Audience, https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.3 # Audience, https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.3
aud: Optional[str] = None aud: Optional[Union[str, list[str]]] = None
# Expiration time, https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.4 # Expiration time, https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.4
exp: Optional[int] = None exp: Optional[int] = None
# Issued at, https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.6 # Issued at, https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.6

View File

@ -44,7 +44,11 @@ def config_loggers(*args, **kwargs):
def after_task_publish_hook(sender=None, headers=None, body=None, **kwargs): def after_task_publish_hook(sender=None, headers=None, body=None, **kwargs):
"""Log task_id after it was published""" """Log task_id after it was published"""
info = headers if "task" in headers else body info = headers if "task" in headers else body
LOGGER.info("Task published", task_id=info.get("id", ""), task_name=info.get("task", "")) LOGGER.info(
"Task published",
task_id=info.get("id", "").replace("-", ""),
task_name=info.get("task", ""),
)
@task_prerun.connect @task_prerun.connect
@ -59,7 +63,9 @@ def task_prerun_hook(task_id: str, task, *args, **kwargs):
def task_postrun_hook(task_id, task, *args, retval=None, state=None, **kwargs): def task_postrun_hook(task_id, task, *args, retval=None, state=None, **kwargs):
"""Log task_id on worker""" """Log task_id on worker"""
CTX_TASK_ID.set(...) CTX_TASK_ID.set(...)
LOGGER.info("Task finished", task_id=task_id, task_name=task.__name__, state=state) LOGGER.info(
"Task finished", task_id=task_id.replace("-", ""), task_name=task.__name__, state=state
)
@task_failure.connect @task_failure.connect

View File

@ -2,7 +2,7 @@
from functools import lru_cache from functools import lru_cache
from uuid import uuid4 from uuid import uuid4
from psycopg2 import connect from psycopg import connect
from authentik.lib.config import CONFIG from authentik.lib.config import CONFIG
@ -30,7 +30,7 @@ def get_install_id_raw():
user=CONFIG.get("postgresql.user"), user=CONFIG.get("postgresql.user"),
password=CONFIG.get("postgresql.password"), password=CONFIG.get("postgresql.password"),
host=CONFIG.get("postgresql.host"), host=CONFIG.get("postgresql.host"),
port=int(CONFIG.get("postgresql.port")), port=CONFIG.get_int("postgresql.port"),
sslmode=CONFIG.get("postgresql.sslmode"), sslmode=CONFIG.get("postgresql.sslmode"),
sslrootcert=CONFIG.get("postgresql.sslrootcert"), sslrootcert=CONFIG.get("postgresql.sslrootcert"),
sslcert=CONFIG.get("postgresql.sslcert"), sslcert=CONFIG.get("postgresql.sslcert"),

View File

@ -190,14 +190,14 @@ if CONFIG.get_bool("redis.tls", False):
_redis_url = ( _redis_url = (
f"{_redis_protocol_prefix}:" f"{_redis_protocol_prefix}:"
f"{quote_plus(CONFIG.get('redis.password'))}@{quote_plus(CONFIG.get('redis.host'))}:" f"{quote_plus(CONFIG.get('redis.password'))}@{quote_plus(CONFIG.get('redis.host'))}:"
f"{int(CONFIG.get('redis.port'))}" f"{CONFIG.get_int('redis.port')}"
) )
CACHES = { CACHES = {
"default": { "default": {
"BACKEND": "django_redis.cache.RedisCache", "BACKEND": "django_redis.cache.RedisCache",
"LOCATION": f"{_redis_url}/{CONFIG.get('redis.db')}", "LOCATION": f"{_redis_url}/{CONFIG.get('redis.db')}",
"TIMEOUT": int(CONFIG.get("redis.cache_timeout", 300)), "TIMEOUT": CONFIG.get_int("redis.cache_timeout", 300),
"OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient"}, "OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient"},
"KEY_PREFIX": "authentik_cache", "KEY_PREFIX": "authentik_cache",
} }
@ -274,7 +274,7 @@ DATABASES = {
"NAME": CONFIG.get("postgresql.name"), "NAME": CONFIG.get("postgresql.name"),
"USER": CONFIG.get("postgresql.user"), "USER": CONFIG.get("postgresql.user"),
"PASSWORD": CONFIG.get("postgresql.password"), "PASSWORD": CONFIG.get("postgresql.password"),
"PORT": int(CONFIG.get("postgresql.port")), "PORT": CONFIG.get_int("postgresql.port"),
"SSLMODE": CONFIG.get("postgresql.sslmode"), "SSLMODE": CONFIG.get("postgresql.sslmode"),
"SSLROOTCERT": CONFIG.get("postgresql.sslrootcert"), "SSLROOTCERT": CONFIG.get("postgresql.sslrootcert"),
"SSLCERT": CONFIG.get("postgresql.sslcert"), "SSLCERT": CONFIG.get("postgresql.sslcert"),
@ -293,12 +293,12 @@ if CONFIG.get_bool("postgresql.use_pgbouncer", False):
# loads the config directly from CONFIG # loads the config directly from CONFIG
# See authentik/stages/email/models.py, line 105 # See authentik/stages/email/models.py, line 105
EMAIL_HOST = CONFIG.get("email.host") EMAIL_HOST = CONFIG.get("email.host")
EMAIL_PORT = int(CONFIG.get("email.port")) EMAIL_PORT = CONFIG.get_int("email.port")
EMAIL_HOST_USER = CONFIG.get("email.username") EMAIL_HOST_USER = CONFIG.get("email.username")
EMAIL_HOST_PASSWORD = CONFIG.get("email.password") EMAIL_HOST_PASSWORD = CONFIG.get("email.password")
EMAIL_USE_TLS = CONFIG.get_bool("email.use_tls", False) EMAIL_USE_TLS = CONFIG.get_bool("email.use_tls", False)
EMAIL_USE_SSL = CONFIG.get_bool("email.use_ssl", False) EMAIL_USE_SSL = CONFIG.get_bool("email.use_ssl", False)
EMAIL_TIMEOUT = int(CONFIG.get("email.timeout")) EMAIL_TIMEOUT = CONFIG.get_int("email.timeout")
DEFAULT_FROM_EMAIL = CONFIG.get("email.from") DEFAULT_FROM_EMAIL = CONFIG.get("email.from")
SERVER_EMAIL = DEFAULT_FROM_EMAIL SERVER_EMAIL = DEFAULT_FROM_EMAIL
EMAIL_SUBJECT_PREFIX = "[authentik] " EMAIL_SUBJECT_PREFIX = "[authentik] "

View File

@ -93,7 +93,7 @@ class BaseLDAPSynchronizer:
types_only=False, types_only=False,
get_operational_attributes=False, get_operational_attributes=False,
controls=None, controls=None,
paged_size=int(CONFIG.get("ldap.page_size", 50)), paged_size=CONFIG.get_int("ldap.page_size", 50),
paged_criticality=False, paged_criticality=False,
): ):
"""Search in pages, returns each page""" """Search in pages, returns each page"""

View File

@ -59,7 +59,7 @@ def ldap_sync_paginator(source: LDAPSource, sync: type[BaseLDAPSynchronizer]) ->
signatures = [] signatures = []
for page in sync_inst.get_objects(): for page in sync_inst.get_objects():
page_cache_key = CACHE_KEY_PREFIX + str(uuid4()) 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, 60 * 60 * CONFIG.get_int("ldap.task_timeout_hours"))
page_sync = ldap_sync.si(source.pk, class_to_path(sync), page_cache_key) page_sync = ldap_sync.si(source.pk, class_to_path(sync), page_cache_key)
signatures.append(page_sync) signatures.append(page_sync)
return signatures return signatures
@ -68,12 +68,12 @@ def ldap_sync_paginator(source: LDAPSource, sync: type[BaseLDAPSynchronizer]) ->
@CELERY_APP.task( @CELERY_APP.task(
bind=True, bind=True,
base=MonitoredTask, base=MonitoredTask,
soft_time_limit=60 * 60 * int(CONFIG.get("ldap.task_timeout_hours")), soft_time_limit=60 * 60 * CONFIG.get_int("ldap.task_timeout_hours"),
task_time_limit=60 * 60 * int(CONFIG.get("ldap.task_timeout_hours")), task_time_limit=60 * 60 * CONFIG.get_int("ldap.task_timeout_hours"),
) )
def ldap_sync(self: MonitoredTask, source_pk: str, sync_class: str, page_cache_key: str): def ldap_sync(self: MonitoredTask, source_pk: str, sync_class: str, page_cache_key: str):
"""Synchronization of an LDAP Source""" """Synchronization of an LDAP Source"""
self.result_timeout_hours = int(CONFIG.get("ldap.task_timeout_hours")) self.result_timeout_hours = CONFIG.get_int("ldap.task_timeout_hours")
source: LDAPSource = LDAPSource.objects.filter(pk=source_pk).first() source: LDAPSource = LDAPSource.objects.filter(pk=source_pk).first()
if not source: if not source:
# Because the source couldn't be found, we don't have a UID # Because the source couldn't be found, we don't have a UID

View File

@ -108,12 +108,12 @@ class EmailStage(Stage):
CONFIG.refresh("email.password") CONFIG.refresh("email.password")
return self.backend_class( return self.backend_class(
host=CONFIG.get("email.host"), host=CONFIG.get("email.host"),
port=int(CONFIG.get("email.port")), port=CONFIG.get_int("email.port"),
username=CONFIG.get("email.username"), username=CONFIG.get("email.username"),
password=CONFIG.get("email.password"), password=CONFIG.get("email.password"),
use_tls=CONFIG.get_bool("email.use_tls", False), use_tls=CONFIG.get_bool("email.use_tls", False),
use_ssl=CONFIG.get_bool("email.use_ssl", False), use_ssl=CONFIG.get_bool("email.use_ssl", False),
timeout=int(CONFIG.get("email.timeout")), timeout=CONFIG.get_int("email.timeout"),
) )
return self.backend_class( return self.backend_class(
host=self.host, host=self.host,

View File

@ -2,6 +2,12 @@ version: 1
metadata: metadata:
name: Default - Events Transport & Rules name: Default - Events Transport & Rules
entries: entries:
# Run bootstrap blueprint first to ensure we have the group created
- model: authentik_blueprints.metaapplyblueprint
attrs:
identifiers:
path: system/bootstrap.yaml
required: false
- model: authentik_events.notificationtransport - model: authentik_events.notificationtransport
id: default-email-transport id: default-email-transport
attrs: attrs:
@ -16,6 +22,7 @@ entries:
name: default-local-transport name: default-local-transport
- model: authentik_core.group - model: authentik_core.group
id: group id: group
state: created
identifiers: identifiers:
name: authentik Admins name: authentik Admins

View File

@ -1,8 +1,8 @@
version: 1 version: 1
metadata: metadata:
name: Migration - Remove old prompt fields
labels: labels:
blueprints.goauthentik.io/description: Migrate to 2023.2, remove unused prompt fields blueprints.goauthentik.io/description: Migrate to 2023.2, remove unused prompt fields
name: Migration - Remove old prompt fields
entries: entries:
- model: authentik_stages_prompt.prompt - model: authentik_stages_prompt.prompt
identifiers: identifiers:

View File

@ -0,0 +1,49 @@
version: 1
metadata:
name: authentik Bootstrap
labels:
blueprints.goauthentik.io/system-bootstrap: "true"
blueprints.goauthentik.io/system: "true"
blueprints.goauthentik.io/description: |
This blueprint configures the default admin user and group, and configures them for the [Automated install](https://goauthentik.io/docs/installation/automated-install).
context:
username: akadmin
group_name: authentik Admins
email: !Env [AUTHENTIK_BOOTSTRAP_EMAIL, "root@example.com"]
password: !Env [AUTHENTIK_BOOTSTRAP_PASSWORD, null]
token: !Env [AUTHENTIK_BOOTSTRAP_TOKEN, null]
entries:
- model: authentik_core.group
state: created
identifiers:
name: !Context group_name
attrs:
is_superuser: true
id: admin-group
- model: authentik_core.user
state: created
id: admin-user
identifiers:
username: !Context username
attrs:
name: authentik Default Admin
email: !Context email
groups:
- !KeyOf admin-group
password: !Context password
- model: authentik_core.token
state: created
conditions:
- !If [!Context token]
identifiers:
identifier: authentik-bootstrap-token
intent: api
expiring: false
key: !Context token
user: !KeyOf admin-user
- model: authentik_blueprints.blueprintinstance
identifiers:
metadata:
labels:
blueprints.goauthentik.io/system-bootstrap: "true"
state: absent

6
go.mod
View File

@ -7,7 +7,7 @@ require (
github.com/Netflix/go-env v0.0.0-20210215222557-e437a7e7f9fb github.com/Netflix/go-env v0.0.0-20210215222557-e437a7e7f9fb
github.com/coreos/go-oidc v2.2.1+incompatible github.com/coreos/go-oidc v2.2.1+incompatible
github.com/garyburd/redigo v1.6.4 github.com/garyburd/redigo v1.6.4
github.com/getsentry/sentry-go v0.22.0 github.com/getsentry/sentry-go v0.23.0
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1 github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
github.com/go-ldap/ldap/v3 v3.4.5 github.com/go-ldap/ldap/v3 v3.4.5
github.com/go-openapi/runtime v0.26.0 github.com/go-openapi/runtime v0.26.0
@ -26,7 +26,7 @@ require (
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.7.0 github.com/spf13/cobra v1.7.0
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.8.4
goauthentik.io/api/v3 v3.2023061.6 goauthentik.io/api/v3 v3.2023061.7
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
golang.org/x/oauth2 v0.10.0 golang.org/x/oauth2 v0.10.0
golang.org/x/sync v0.3.0 golang.org/x/sync v0.3.0
@ -74,7 +74,7 @@ require (
go.opentelemetry.io/otel v1.14.0 // indirect go.opentelemetry.io/otel v1.14.0 // indirect
go.opentelemetry.io/otel/trace v1.14.0 // indirect go.opentelemetry.io/otel/trace v1.14.0 // indirect
golang.org/x/crypto v0.11.0 // indirect golang.org/x/crypto v0.11.0 // indirect
golang.org/x/net v0.12.0 // indirect golang.org/x/net v0.13.0 // indirect
golang.org/x/sys v0.10.0 // indirect golang.org/x/sys v0.10.0 // indirect
golang.org/x/text v0.11.0 // indirect golang.org/x/text v0.11.0 // indirect
google.golang.org/appengine v1.6.7 // indirect google.golang.org/appengine v1.6.7 // indirect

11
go.sum
View File

@ -681,8 +681,8 @@ github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/garyburd/redigo v1.6.4 h1:LFu2R3+ZOPgSMWMOL+saa/zXRjw0ID2G8FepO53BGlg= github.com/garyburd/redigo v1.6.4 h1:LFu2R3+ZOPgSMWMOL+saa/zXRjw0ID2G8FepO53BGlg=
github.com/garyburd/redigo v1.6.4/go.mod h1:rTb6epsqigu3kYKBnaF028A7Tf/Aw5s0cqA47doKKqw= github.com/garyburd/redigo v1.6.4/go.mod h1:rTb6epsqigu3kYKBnaF028A7Tf/Aw5s0cqA47doKKqw=
github.com/getsentry/sentry-go v0.22.0 h1:XNX9zKbv7baSEI65l+H1GEJgSeIC1c7EN5kluWaP6dM= github.com/getsentry/sentry-go v0.23.0 h1:dn+QRCeJv4pPt9OjVXiMcGIBIefaTJPw/h0bZWO05nE=
github.com/getsentry/sentry-go v0.22.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/getsentry/sentry-go v0.23.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A= github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
@ -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.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
go.opentelemetry.io/proto/otlp v0.19.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= 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.7 h1:uxN57usnWEyBjlbp+0npSv1HNlwrCUhVMu8Gtoabgqk=
goauthentik.io/api/v3 v3.2023061.6/go.mod h1:tC7qK9VSP0zJah5p5xHFnjZt/4dAkXVwcrWyZNGYhwQ= goauthentik.io/api/v3 v3.2023061.7/go.mod h1:JWRqmfaXtI9vMljPXbzzIF70rjn55v0iEZkFVNDXvwk=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
@ -1213,8 +1213,9 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY=
golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=

View File

@ -1,5 +1,5 @@
# Stage 1: Build # Stage 1: Build
FROM docker.io/golang:1.20.6-bullseye AS builder FROM docker.io/golang:1.20.7-bullseye AS builder
WORKDIR /go/src/goauthentik.io WORKDIR /go/src/goauthentik.io

View File

@ -80,8 +80,8 @@ if SERVICE_HOST_ENV_NAME in os.environ:
else: else:
default_workers = max(cpu_count() * 0.25, 1) + 1 # Minimum of 2 workers default_workers = max(cpu_count() * 0.25, 1) + 1 # Minimum of 2 workers
workers = int(CONFIG.get("web.workers", default_workers)) workers = CONFIG.get_int("web.workers", default_workers)
threads = int(CONFIG.get("web.threads", 4)) threads = CONFIG.get_int("web.threads", 4)
def post_fork(server: "Arbiter", worker: DjangoUvicornWorker): def post_fork(server: "Arbiter", worker: DjangoUvicornWorker):

View File

@ -6,7 +6,7 @@ from inspect import getmembers, isclass
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
from psycopg2 import connect from psycopg import connect
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik.lib.config import CONFIG from authentik.lib.config import CONFIG
@ -56,7 +56,7 @@ if __name__ == "__main__":
user=CONFIG.get("postgresql.user"), user=CONFIG.get("postgresql.user"),
password=CONFIG.get("postgresql.password"), password=CONFIG.get("postgresql.password"),
host=CONFIG.get("postgresql.host"), host=CONFIG.get("postgresql.host"),
port=int(CONFIG.get("postgresql.port")), port=CONFIG.get_int("postgresql.port"),
sslmode=CONFIG.get("postgresql.sslmode"), sslmode=CONFIG.get("postgresql.sslmode"),
sslrootcert=CONFIG.get("postgresql.sslrootcert"), sslrootcert=CONFIG.get("postgresql.sslrootcert"),
sslcert=CONFIG.get("postgresql.sslcert"), sslcert=CONFIG.get("postgresql.sslcert"),

View File

@ -5,7 +5,7 @@ from sys import exit as sysexit
from time import sleep from time import sleep
from urllib.parse import quote_plus from urllib.parse import quote_plus
from psycopg2 import OperationalError, connect from psycopg import OperationalError, connect
from redis import Redis from redis import Redis
from redis.exceptions import RedisError from redis.exceptions import RedisError
@ -28,7 +28,7 @@ while True:
user=CONFIG.get("postgresql.user"), user=CONFIG.get("postgresql.user"),
password=CONFIG.get("postgresql.password"), password=CONFIG.get("postgresql.password"),
host=CONFIG.get("postgresql.host"), host=CONFIG.get("postgresql.host"),
port=int(CONFIG.get("postgresql.port")), port=CONFIG.get_int("postgresql.port"),
sslmode=CONFIG.get("postgresql.sslmode"), sslmode=CONFIG.get("postgresql.sslmode"),
sslrootcert=CONFIG.get("postgresql.sslrootcert"), sslrootcert=CONFIG.get("postgresql.sslrootcert"),
sslcert=CONFIG.get("postgresql.sslcert"), sslcert=CONFIG.get("postgresql.sslcert"),
@ -47,7 +47,7 @@ if CONFIG.get_bool("redis.tls", False):
REDIS_URL = ( REDIS_URL = (
f"{REDIS_PROTOCOL_PREFIX}:" f"{REDIS_PROTOCOL_PREFIX}:"
f"{quote_plus(CONFIG.get('redis.password'))}@{quote_plus(CONFIG.get('redis.host'))}:" f"{quote_plus(CONFIG.get('redis.password'))}@{quote_plus(CONFIG.get('redis.host'))}:"
f"{int(CONFIG.get('redis.port'))}/{CONFIG.get('redis.db')}" f"{CONFIG.get_int('redis.port')}/{CONFIG.get('redis.db')}"
) )
while True: while True:
try: try:

1166
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@ WORKDIR /static
RUN npm ci --include=dev && npm run build-proxy RUN npm ci --include=dev && npm run build-proxy
# Stage 2: Build # Stage 2: Build
FROM docker.io/golang:1.20.6-bullseye AS builder FROM docker.io/golang:1.20.7-bullseye AS builder
WORKDIR /go/src/goauthentik.io WORKDIR /go/src/goauthentik.io

View File

@ -127,7 +127,7 @@ colorama = "*"
dacite = "*" dacite = "*"
deepmerge = "*" deepmerge = "*"
defusedxml = "*" defusedxml = "*"
django = "<4.2.0" django = "*"
django-filter = "*" django-filter = "*"
django-guardian = "*" django-guardian = "*"
django-model-utils = "*" django-model-utils = "*"
@ -150,7 +150,7 @@ lxml = "*"
opencontainers = { extras = ["reggie"], version = "*" } opencontainers = { extras = ["reggie"], version = "*" }
packaging = "*" packaging = "*"
paramiko = "*" paramiko = "*"
psycopg2-binary = "*" psycopg = { extras = ["c"], version = "*" }
pycryptodome = "*" pycryptodome = "*"
pydantic = "<2.0.0" pydantic = "<2.0.0"
pydantic-scim = "^0.0.7" pydantic-scim = "^0.0.7"

View File

@ -1,5 +1,5 @@
# Stage 1: Build # Stage 1: Build
FROM docker.io/golang:1.20.6-bullseye AS builder FROM docker.io/golang:1.20.7-bullseye AS builder
WORKDIR /go/src/goauthentik.io WORKDIR /go/src/goauthentik.io

View File

@ -1,6 +1,5 @@
import replace from "@rollup/plugin-replace"; import replace from "@rollup/plugin-replace";
import type { StorybookConfig } from "@storybook/web-components-vite"; import type { StorybookConfig } from "@storybook/web-components-vite";
import path from "path";
import { cwd } from "process"; import { cwd } from "process";
import postcssLit from "rollup-plugin-postcss-lit"; import postcssLit from "rollup-plugin-postcss-lit";
import tsconfigPaths from "vite-tsconfig-paths"; import tsconfigPaths from "vite-tsconfig-paths";

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 12 KiB

278
web/package-lock.json generated
View File

@ -16,24 +16,25 @@
"@codemirror/legacy-modes": "^6.3.3", "@codemirror/legacy-modes": "^6.3.3",
"@codemirror/theme-one-dark": "^6.1.2", "@codemirror/theme-one-dark": "^6.1.2",
"@formatjs/intl-listformat": "^7.4.0", "@formatjs/intl-listformat": "^7.4.0",
"@fortawesome/fontawesome-free": "^6.4.0", "@fortawesome/fontawesome-free": "^6.4.2",
"@goauthentik/api": "^2023.6.1-1690455444", "@goauthentik/api": "^2023.6.1-1690455444",
"@lit-labs/context": "^0.3.3", "@lit-labs/context": "^0.3.3",
"@lit-labs/task": "^2.1.2", "@lit-labs/task": "^3.0.0",
"@lit/localize": "^0.11.4", "@lit/localize": "^0.11.4",
"@patternfly/elements": "^2.3.2",
"@patternfly/patternfly": "^4.224.2", "@patternfly/patternfly": "^4.224.2",
"@sentry/browser": "^7.60.1", "@sentry/browser": "^7.61.0",
"@sentry/tracing": "^7.60.1", "@sentry/tracing": "^7.61.0",
"@webcomponents/webcomponentsjs": "^2.8.0", "@webcomponents/webcomponentsjs": "^2.8.0",
"base64-js": "^1.5.1", "base64-js": "^1.5.1",
"chart.js": "^4.3.2", "chart.js": "^4.3.3",
"chartjs-adapter-moment": "^1.0.1", "chartjs-adapter-moment": "^1.0.1",
"codemirror": "^6.0.1", "codemirror": "^6.0.1",
"construct-style-sheets-polyfill": "^3.1.0", "construct-style-sheets-polyfill": "^3.1.0",
"core-js": "^3.32.0", "core-js": "^3.32.0",
"country-flag-icons": "^1.5.7", "country-flag-icons": "^1.5.7",
"fuse.js": "^6.6.2", "fuse.js": "^6.6.2",
"lit": "^2.7.6", "lit": "^2.8.0",
"mermaid": "^10.3.0", "mermaid": "^10.3.0",
"rapidoc": "^9.3.4", "rapidoc": "^9.3.4",
"style-mod": "^4.0.3", "style-mod": "^4.0.3",
@ -67,8 +68,8 @@
"@types/chart.js": "^2.9.37", "@types/chart.js": "^2.9.37",
"@types/codemirror": "5.60.8", "@types/codemirror": "5.60.8",
"@types/grecaptcha": "^3.0.4", "@types/grecaptcha": "^3.0.4",
"@typescript-eslint/eslint-plugin": "^6.2.0", "@typescript-eslint/eslint-plugin": "^6.2.1",
"@typescript-eslint/parser": "^6.2.0", "@typescript-eslint/parser": "^6.2.1",
"babel-plugin-macros": "^3.1.0", "babel-plugin-macros": "^3.1.0",
"babel-plugin-tsconfig-paths": "^1.0.3", "babel-plugin-tsconfig-paths": "^1.0.3",
"eslint": "^8.46.0", "eslint": "^8.46.0",
@ -78,8 +79,8 @@
"eslint-plugin-storybook": "^0.6.13", "eslint-plugin-storybook": "^0.6.13",
"lit-analyzer": "^1.2.1", "lit-analyzer": "^1.2.1",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"prettier": "^3.0.0", "prettier": "^3.0.1",
"pyright": "^1.1.319", "pyright": "^1.1.320",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"rollup": "^2.79.1", "rollup": "^2.79.1",
@ -2853,6 +2854,28 @@
"integrity": "sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ==", "integrity": "sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ==",
"dev": true "dev": true
}, },
"node_modules/@floating-ui/core": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.4.1.tgz",
"integrity": "sha512-jk3WqquEJRlcyu7997NtR5PibI+y5bi+LS3hPmguVClypenMsCY3CBa3LAQnozRCtCrYWSEtAdiskpamuJRFOQ==",
"dependencies": {
"@floating-ui/utils": "^0.1.1"
}
},
"node_modules/@floating-ui/dom": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.1.tgz",
"integrity": "sha512-KwvVcPSXg6mQygvA1TjbN/gh///36kKtllIF8SUm0qpFj8+rvYrpvlYdL1JoA71SHpDqgSSdGOSoQ0Mp3uY5aw==",
"dependencies": {
"@floating-ui/core": "^1.4.1",
"@floating-ui/utils": "^0.1.1"
}
},
"node_modules/@floating-ui/utils": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.1.tgz",
"integrity": "sha512-m0G6wlnhm/AX0H12IOWtK8gASEMffnX08RtKkCgTdHb9JpHKGloI7icFfLg9ZmQeavcvR0PKmzxClyuFPSjKWw=="
},
"node_modules/@formatjs/ecma402-abstract": { "node_modules/@formatjs/ecma402-abstract": {
"version": "1.17.0", "version": "1.17.0",
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.17.0.tgz", "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.17.0.tgz",
@ -2881,9 +2904,9 @@
} }
}, },
"node_modules/@fortawesome/fontawesome-free": { "node_modules/@fortawesome/fontawesome-free": {
"version": "6.4.0", "version": "6.4.2",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.0.tgz", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.2.tgz",
"integrity": "sha512-0NyytTlPJwB/BF5LtRV8rrABDbe3TdTXqNB3PdZ+UUUZAEIrdOJdmABqKjt4AXwIoJNaRVVZEXxpNrqvE1GAYQ==", "integrity": "sha512-m5cPn3e2+FDCOgi1mz0RexTUvvQibBebOUlUlW0+YrMjDTPkiJ6VTKukA1GRsvRw+12KyJndNjj0O4AgTxm2Pg==",
"hasInstallScript": true, "hasInstallScript": true,
"engines": { "engines": {
"node": ">=6" "node": ">=6"
@ -3463,9 +3486,9 @@
"integrity": "sha512-kXOeFbfCm4fFf2A3WwVEeQj55tMZa8c8/f9AKHMobQMkzNUfUj+antR3fRPaZJawsa1aZiP/Da3ndpZrwEe4rQ==" "integrity": "sha512-kXOeFbfCm4fFf2A3WwVEeQj55tMZa8c8/f9AKHMobQMkzNUfUj+antR3fRPaZJawsa1aZiP/Da3ndpZrwEe4rQ=="
}, },
"node_modules/@lit-labs/task": { "node_modules/@lit-labs/task": {
"version": "2.1.2", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/@lit-labs/task/-/task-2.1.2.tgz", "resolved": "https://registry.npmjs.org/@lit-labs/task/-/task-3.0.0.tgz",
"integrity": "sha512-pDjPYojmCXnOezT/4BgqxHA2kahmSsCE0y6uPCYyVqigIx9DJav05s0KAtD9IImTY3LvWu5isSlq6nkWagAybA==", "integrity": "sha512-h5Jh7PnJrQ10QABddGGmnCEdZyCveCCz/FDg4wLX7/tZQc281zJaxaTFnozUUqpessPjSzOOvQjF19p2QdThXg==",
"dependencies": { "dependencies": {
"@lit/reactive-element": "^1.1.0" "@lit/reactive-element": "^1.1.0"
} }
@ -3612,11 +3635,46 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/@patternfly/elements": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/@patternfly/elements/-/elements-2.3.2.tgz",
"integrity": "sha512-cndt+xxlUKbWU8GxFlOXMNqNeJ792kXUnvcYZbUIA+TUbWD71yw0kdC+EOWql94B4jtzOmDyG7Eq33il3G6ZLQ==",
"dependencies": {
"@patternfly/icons": "^1.0.2",
"@patternfly/pfe-core": "^2.4.0",
"lit": "2.6.1",
"tslib": "^2.4.1"
}
},
"node_modules/@patternfly/elements/node_modules/lit": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/lit/-/lit-2.6.1.tgz",
"integrity": "sha512-DT87LD64f8acR7uVp7kZfhLRrHkfC/N4BVzAtnw9Yg8087mbBJ//qedwdwX0kzDbxgPccWRW6mFwGbRQIxy0pw==",
"dependencies": {
"@lit/reactive-element": "^1.6.0",
"lit-element": "^3.2.0",
"lit-html": "^2.6.0"
}
},
"node_modules/@patternfly/icons": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@patternfly/icons/-/icons-1.0.2.tgz",
"integrity": "sha512-/faOTZsKkTPxuCDcWZbunknjUhJIjjN0h+OicNiFWxTq/saLp366cLhdgNf8A46oeGR/21aQTYVXTqcQA1yvOg=="
},
"node_modules/@patternfly/patternfly": { "node_modules/@patternfly/patternfly": {
"version": "4.224.2", "version": "4.224.2",
"resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-4.224.2.tgz", "resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-4.224.2.tgz",
"integrity": "sha512-HGNV26uyHSIECuhjPg/WGn0mXbAotcs6ODfhAOkfYjIgGylddgiwElxUe1rpEHV5mQJJ2rMn4OdeJIIpzRX61g==" "integrity": "sha512-HGNV26uyHSIECuhjPg/WGn0mXbAotcs6ODfhAOkfYjIgGylddgiwElxUe1rpEHV5mQJJ2rMn4OdeJIIpzRX61g=="
}, },
"node_modules/@patternfly/pfe-core": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@patternfly/pfe-core/-/pfe-core-2.4.1.tgz",
"integrity": "sha512-ZqN4Zk2ysrnHp84hKvUr54xeSwtzqM3qRPpMzdd2fa58htYnPfhow4TQ8LH9vArLxPzEAB3Hrfql0VW1bk5PXw==",
"dependencies": {
"@floating-ui/dom": "^1.2.6",
"lit": "^2.7.2"
}
},
"node_modules/@pkgjs/parseargs": { "node_modules/@pkgjs/parseargs": {
"version": "0.11.0", "version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@ -3773,13 +3831,13 @@
} }
}, },
"node_modules/@sentry-internal/tracing": { "node_modules/@sentry-internal/tracing": {
"version": "7.60.1", "version": "7.61.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.60.1.tgz", "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.61.0.tgz",
"integrity": "sha512-2vM+3/ddzmoBfi92OOD9FFTHXf0HdQhKtNM26+/RsmkKnTid+/inbvA7nKi+Qa7ExcnlC6eclEHQEg+0X3yDkQ==", "integrity": "sha512-zTr+MXEG4SxNxif42LIgm2RQn+JRXL2NuGhRaKSD2i4lXKFqHVGlVdoWqY5UfqnnJPokiTWIj9ejR8I5HV8Ogw==",
"dependencies": { "dependencies": {
"@sentry/core": "7.60.1", "@sentry/core": "7.61.0",
"@sentry/types": "7.60.1", "@sentry/types": "7.61.0",
"@sentry/utils": "7.60.1", "@sentry/utils": "7.61.0",
"tslib": "^2.4.1 || ^1.9.3" "tslib": "^2.4.1 || ^1.9.3"
}, },
"engines": { "engines": {
@ -3787,15 +3845,15 @@
} }
}, },
"node_modules/@sentry/browser": { "node_modules/@sentry/browser": {
"version": "7.60.1", "version": "7.61.0",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.60.1.tgz", "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.61.0.tgz",
"integrity": "sha512-opZQee3S0c459LXt8YGpwOM/qiTlzluHEEnfW2q+D2yVCWh8iegsDX3kbRiv4i/mtQu9yPhM9M761KDnc/0eZw==", "integrity": "sha512-IGEkJZRP16Oe5CkXkmhU3QdV5RugW6Vds16yJFFYsgp87NprWtRZgqzldFDYkINStfBHVdctj/Rh/ZrLf8QlkQ==",
"dependencies": { "dependencies": {
"@sentry-internal/tracing": "7.60.1", "@sentry-internal/tracing": "7.61.0",
"@sentry/core": "7.60.1", "@sentry/core": "7.61.0",
"@sentry/replay": "7.60.1", "@sentry/replay": "7.61.0",
"@sentry/types": "7.60.1", "@sentry/types": "7.61.0",
"@sentry/utils": "7.60.1", "@sentry/utils": "7.61.0",
"tslib": "^2.4.1 || ^1.9.3" "tslib": "^2.4.1 || ^1.9.3"
}, },
"engines": { "engines": {
@ -3803,12 +3861,12 @@
} }
}, },
"node_modules/@sentry/core": { "node_modules/@sentry/core": {
"version": "7.60.1", "version": "7.61.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.60.1.tgz", "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.61.0.tgz",
"integrity": "sha512-yr/0VFYWOJyXj+F2nifkRYxXskotsNnDggUnFOZZN2ZgTG94IzRFsOZQ6RslHJ8nrYPTBNO74reU0C0GB++xRw==", "integrity": "sha512-zl0ZKRjIoYJQWYTd3K/U6zZfS4GDY9yGd2EH4vuYO4kfYtEp/nJ8A+tfAeDo0c9FGxZ0Q+5t5F4/SfwbgyyQzg==",
"dependencies": { "dependencies": {
"@sentry/types": "7.60.1", "@sentry/types": "7.61.0",
"@sentry/utils": "7.60.1", "@sentry/utils": "7.61.0",
"tslib": "^2.4.1 || ^1.9.3" "tslib": "^2.4.1 || ^1.9.3"
}, },
"engines": { "engines": {
@ -3816,43 +3874,43 @@
} }
}, },
"node_modules/@sentry/replay": { "node_modules/@sentry/replay": {
"version": "7.60.1", "version": "7.61.0",
"resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.60.1.tgz", "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.61.0.tgz",
"integrity": "sha512-WHQxEpJbHICs12L17LGgS/ql91yn9wJDH/hgb+1H90HaasjoR54ofWCKul29OvYV0snTWuHd6xauwtzyv9tzvg==", "integrity": "sha512-1ugk0yZssOPkSg6uTVcysjxlBydycXiOgV0PCU7DsXCFOV1ua5YpyPZFReTz9iFTtwD0LwGFM1LW9wJeQ67Fzg==",
"dependencies": { "dependencies": {
"@sentry/core": "7.60.1", "@sentry/core": "7.61.0",
"@sentry/types": "7.60.1", "@sentry/types": "7.61.0",
"@sentry/utils": "7.60.1" "@sentry/utils": "7.61.0"
}, },
"engines": { "engines": {
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/@sentry/tracing": { "node_modules/@sentry/tracing": {
"version": "7.60.1", "version": "7.61.0",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.60.1.tgz", "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.61.0.tgz",
"integrity": "sha512-yzjbFaaOPeMERD5GPaBdKQRihznluYO7O24y0hznROPGOVNozwPX8JZgX0plOfSmCttjYjDwRrIo9nFFpzFhtw==", "integrity": "sha512-asGbw3n04qkM5zUCPlGg9i52GEHq9Nmore38SJFN0L4N/YH6EPJLEUfNm2jFvXWCo96kq/dPKnyjOEuPccwhIA==",
"dependencies": { "dependencies": {
"@sentry-internal/tracing": "7.60.1" "@sentry-internal/tracing": "7.61.0"
}, },
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/@sentry/types": { "node_modules/@sentry/types": {
"version": "7.60.1", "version": "7.61.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.60.1.tgz", "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.61.0.tgz",
"integrity": "sha512-8lKKSCOhZ953cWxwnfZwoR3ZFFlZG4P3PQFTaFt/u4LxLh/0zYbdtgvtUqXRURjMCi5P6ddeE9Uw9FGnTJCsTw==", "integrity": "sha512-/GLlIBNR35NKPE/SfWi9W10dK9hE8qTShzsuPVn5wAJxpT3Lb4+dkwmKCTLUYxdkmvRDEudkfOxgalsfQGTAWA==",
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/@sentry/utils": { "node_modules/@sentry/utils": {
"version": "7.60.1", "version": "7.61.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.60.1.tgz", "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.61.0.tgz",
"integrity": "sha512-ik+5sKGBx4DWuvf6UUKPSafaDiASxP+Xvjg3C9ppop2I/JWxP1FfZ5g22n5ZmPmNahD6clTSoTWly8qyDUlUOw==", "integrity": "sha512-jfj14d0XBFiCU0G6dZZ12SizATiF5Mt4stBGzkM5iS9nXFj8rh1oTT7/p+aZoYzP2JTF+sDzkNjWxyKZkcTo0Q==",
"dependencies": { "dependencies": {
"@sentry/types": "7.60.1", "@sentry/types": "7.61.0",
"tslib": "^2.4.1 || ^1.9.3" "tslib": "^2.4.1 || ^1.9.3"
}, },
"engines": { "engines": {
@ -10737,16 +10795,16 @@
"dev": true "dev": true
}, },
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "6.2.0", "version": "6.2.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.2.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.2.1.tgz",
"integrity": "sha512-rClGrMuyS/3j0ETa1Ui7s6GkLhfZGKZL3ZrChLeAiACBE/tRc1wq8SNZESUuluxhLj9FkUefRs2l6bCIArWBiQ==", "integrity": "sha512-iZVM/ALid9kO0+I81pnp1xmYiFyqibAHzrqX4q5YvvVEyJqY+e6rfTXSCsc2jUxGNqJqTfFSSij/NFkZBiBzLw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@eslint-community/regexpp": "^4.5.1", "@eslint-community/regexpp": "^4.5.1",
"@typescript-eslint/scope-manager": "6.2.0", "@typescript-eslint/scope-manager": "6.2.1",
"@typescript-eslint/type-utils": "6.2.0", "@typescript-eslint/type-utils": "6.2.1",
"@typescript-eslint/utils": "6.2.0", "@typescript-eslint/utils": "6.2.1",
"@typescript-eslint/visitor-keys": "6.2.0", "@typescript-eslint/visitor-keys": "6.2.1",
"debug": "^4.3.4", "debug": "^4.3.4",
"graphemer": "^1.4.0", "graphemer": "^1.4.0",
"ignore": "^5.2.4", "ignore": "^5.2.4",
@ -10806,15 +10864,15 @@
"dev": true "dev": true
}, },
"node_modules/@typescript-eslint/parser": { "node_modules/@typescript-eslint/parser": {
"version": "6.2.0", "version": "6.2.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.2.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.2.1.tgz",
"integrity": "sha512-igVYOqtiK/UsvKAmmloQAruAdUHihsOCvplJpplPZ+3h4aDkC/UKZZNKgB6h93ayuYLuEymU3h8nF1xMRbh37g==", "integrity": "sha512-Ld+uL1kYFU8e6btqBFpsHkwQ35rw30IWpdQxgOqOh4NfxSDH6uCkah1ks8R/RgQqI5hHPXMaLy9fbFseIe+dIg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "6.2.0", "@typescript-eslint/scope-manager": "6.2.1",
"@typescript-eslint/types": "6.2.0", "@typescript-eslint/types": "6.2.1",
"@typescript-eslint/typescript-estree": "6.2.0", "@typescript-eslint/typescript-estree": "6.2.1",
"@typescript-eslint/visitor-keys": "6.2.0", "@typescript-eslint/visitor-keys": "6.2.1",
"debug": "^4.3.4" "debug": "^4.3.4"
}, },
"engines": { "engines": {
@ -10834,13 +10892,13 @@
} }
}, },
"node_modules/@typescript-eslint/scope-manager": { "node_modules/@typescript-eslint/scope-manager": {
"version": "6.2.0", "version": "6.2.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.2.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.2.1.tgz",
"integrity": "sha512-1ZMNVgm5nnHURU8ZSJ3snsHzpFeNK84rdZjluEVBGNu7jDymfqceB3kdIZ6A4xCfEFFhRIB6rF8q/JIqJd2R0Q==", "integrity": "sha512-UCqBF9WFqv64xNsIEPfBtenbfodPXsJ3nPAr55mGPkQIkiQvgoWNo+astj9ZUfJfVKiYgAZDMnM6dIpsxUMp3Q==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@typescript-eslint/types": "6.2.0", "@typescript-eslint/types": "6.2.1",
"@typescript-eslint/visitor-keys": "6.2.0" "@typescript-eslint/visitor-keys": "6.2.1"
}, },
"engines": { "engines": {
"node": "^16.0.0 || >=18.0.0" "node": "^16.0.0 || >=18.0.0"
@ -10851,13 +10909,13 @@
} }
}, },
"node_modules/@typescript-eslint/type-utils": { "node_modules/@typescript-eslint/type-utils": {
"version": "6.2.0", "version": "6.2.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.2.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.2.1.tgz",
"integrity": "sha512-DnGZuNU2JN3AYwddYIqrVkYW0uUQdv0AY+kz2M25euVNlujcN2u+rJgfJsBFlUEzBB6OQkUqSZPyuTLf2bP5mw==", "integrity": "sha512-fTfCgomBMIgu2Dh2Or3gMYgoNAnQm3RLtRp+jP7A8fY+LJ2+9PNpi5p6QB5C4RSP+U3cjI0vDlI3mspAkpPVbQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@typescript-eslint/typescript-estree": "6.2.0", "@typescript-eslint/typescript-estree": "6.2.1",
"@typescript-eslint/utils": "6.2.0", "@typescript-eslint/utils": "6.2.1",
"debug": "^4.3.4", "debug": "^4.3.4",
"ts-api-utils": "^1.0.1" "ts-api-utils": "^1.0.1"
}, },
@ -10878,9 +10936,9 @@
} }
}, },
"node_modules/@typescript-eslint/types": { "node_modules/@typescript-eslint/types": {
"version": "6.2.0", "version": "6.2.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.2.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.2.1.tgz",
"integrity": "sha512-1nRRaDlp/XYJQLvkQJG5F3uBTno5SHPT7XVcJ5n1/k2WfNI28nJsvLakxwZRNY5spuatEKO7d5nZWsQpkqXwBA==", "integrity": "sha512-528bGcoelrpw+sETlyM91k51Arl2ajbNT9L4JwoXE2dvRe1yd8Q64E4OL7vHYw31mlnVsf+BeeLyAZUEQtqahQ==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": "^16.0.0 || >=18.0.0" "node": "^16.0.0 || >=18.0.0"
@ -10891,13 +10949,13 @@
} }
}, },
"node_modules/@typescript-eslint/typescript-estree": { "node_modules/@typescript-eslint/typescript-estree": {
"version": "6.2.0", "version": "6.2.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.2.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.2.1.tgz",
"integrity": "sha512-Mts6+3HQMSM+LZCglsc2yMIny37IhUgp1Qe8yJUYVyO6rHP7/vN0vajKu3JvHCBIy8TSiKddJ/Zwu80jhnGj1w==", "integrity": "sha512-G+UJeQx9AKBHRQBpmvr8T/3K5bJa485eu+4tQBxFq0KoT22+jJyzo1B50JDT9QdC1DEmWQfdKsa8ybiNWYsi0Q==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@typescript-eslint/types": "6.2.0", "@typescript-eslint/types": "6.2.1",
"@typescript-eslint/visitor-keys": "6.2.0", "@typescript-eslint/visitor-keys": "6.2.1",
"debug": "^4.3.4", "debug": "^4.3.4",
"globby": "^11.1.0", "globby": "^11.1.0",
"is-glob": "^4.0.3", "is-glob": "^4.0.3",
@ -10951,17 +11009,17 @@
"dev": true "dev": true
}, },
"node_modules/@typescript-eslint/utils": { "node_modules/@typescript-eslint/utils": {
"version": "6.2.0", "version": "6.2.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.2.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.2.1.tgz",
"integrity": "sha512-RCFrC1lXiX1qEZN8LmLrxYRhOkElEsPKTVSNout8DMzf8PeWoQG7Rxz2SadpJa3VSh5oYKGwt7j7X/VRg+Y3OQ==", "integrity": "sha512-eBIXQeupYmxVB6S7x+B9SdBeB6qIdXKjgQBge2J+Ouv8h9Cxm5dHf/gfAZA6dkMaag+03HdbVInuXMmqFB/lKQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.4.0", "@eslint-community/eslint-utils": "^4.4.0",
"@types/json-schema": "^7.0.12", "@types/json-schema": "^7.0.12",
"@types/semver": "^7.5.0", "@types/semver": "^7.5.0",
"@typescript-eslint/scope-manager": "6.2.0", "@typescript-eslint/scope-manager": "6.2.1",
"@typescript-eslint/types": "6.2.0", "@typescript-eslint/types": "6.2.1",
"@typescript-eslint/typescript-estree": "6.2.0", "@typescript-eslint/typescript-estree": "6.2.1",
"semver": "^7.5.4" "semver": "^7.5.4"
}, },
"engines": { "engines": {
@ -11009,12 +11067,12 @@
"dev": true "dev": true
}, },
"node_modules/@typescript-eslint/visitor-keys": { "node_modules/@typescript-eslint/visitor-keys": {
"version": "6.2.0", "version": "6.2.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.2.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.2.1.tgz",
"integrity": "sha512-QbaYUQVKKo9bgCzpjz45llCfwakyoxHetIy8CAvYCtd16Zu1KrpzNHofwF8kGkpPOxZB2o6kz+0nqH8ZkIzuoQ==", "integrity": "sha512-iTN6w3k2JEZ7cyVdZJTVJx2Lv7t6zFA8DCrJEHD2mwfc16AEvvBWVhbFh34XyG2NORCd0viIgQY1+u7kPI0WpA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@typescript-eslint/types": "6.2.0", "@typescript-eslint/types": "6.2.1",
"eslint-visitor-keys": "^3.4.1" "eslint-visitor-keys": "^3.4.1"
}, },
"engines": { "engines": {
@ -12009,9 +12067,9 @@
} }
}, },
"node_modules/chart.js": { "node_modules/chart.js": {
"version": "4.3.2", "version": "4.3.3",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.2.tgz", "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.3.tgz",
"integrity": "sha512-pvQNyFOY1QmbmIr8oDORL16/FFivfxj8V26VFpFilMo4cNvkV5WXLJetDio365pd9gKUHGdirUTbqJfw8tr+Dg==", "integrity": "sha512-aTk7pBw+x6sQYhon/NR3ikfUJuym/LdgpTlgZRe2PaEhjUMKBKyNaFCMVRAyTEWYFNO7qRu7iQVqOw/OqzxZxQ==",
"dependencies": { "dependencies": {
"@kurkle/color": "^0.3.0" "@kurkle/color": "^0.3.0"
}, },
@ -17039,13 +17097,13 @@
"dev": true "dev": true
}, },
"node_modules/lit": { "node_modules/lit": {
"version": "2.7.6", "version": "2.8.0",
"resolved": "https://registry.npmjs.org/lit/-/lit-2.7.6.tgz", "resolved": "https://registry.npmjs.org/lit/-/lit-2.8.0.tgz",
"integrity": "sha512-1amFHA7t4VaaDe+vdQejSVBklwtH9svGoG6/dZi9JhxtJBBlqY5D1RV7iLUYY0trCqQc4NfhYYZilZiVHt7Hxg==", "integrity": "sha512-4Sc3OFX9QHOJaHbmTMk28SYgVxLN3ePDjg7hofEft2zWlehFL3LiAuapWc4U/kYwMYJSh2hTCPZ6/LIC7ii0MA==",
"dependencies": { "dependencies": {
"@lit/reactive-element": "^1.6.0", "@lit/reactive-element": "^1.6.0",
"lit-element": "^3.3.0", "lit-element": "^3.3.0",
"lit-html": "^2.7.0" "lit-html": "^2.8.0"
} }
}, },
"node_modules/lit-analyzer": { "node_modules/lit-analyzer": {
@ -17272,9 +17330,9 @@
} }
}, },
"node_modules/lit-html": { "node_modules/lit-html": {
"version": "2.7.5", "version": "2.8.0",
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.7.5.tgz", "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.8.0.tgz",
"integrity": "sha512-YqUzpisJodwKIlbMFCtyrp58oLloKGnnPLMJ1t23cbfIJjg/H9pvLWK4XS69YeubK5HUs1UE4ys9w5dP1zg6IA==", "integrity": "sha512-o9t+MQM3P4y7M7yNzqAyjp7z+mQGa4NS4CxiyLqFPyFWyc4O+nodLrkrxSaCTrla6M5YOLaT3RpbbqjszB5g3Q==",
"dependencies": { "dependencies": {
"@types/trusted-types": "^2.0.2" "@types/trusted-types": "^2.0.2"
} }
@ -19552,9 +19610,9 @@
} }
}, },
"node_modules/prettier": { "node_modules/prettier": {
"version": "3.0.0", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.0.tgz", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.1.tgz",
"integrity": "sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==", "integrity": "sha512-fcOWSnnpCrovBsmFZIGIy9UqK2FaI7Hqax+DIO0A9UxeVoY4iweyaFjS5TavZN97Hfehph0nhsZnjlVKzEQSrQ==",
"dev": true, "dev": true,
"bin": { "bin": {
"prettier": "bin/prettier.cjs" "prettier": "bin/prettier.cjs"
@ -19785,9 +19843,9 @@
} }
}, },
"node_modules/pyright": { "node_modules/pyright": {
"version": "1.1.319", "version": "1.1.320",
"resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.319.tgz", "resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.320.tgz",
"integrity": "sha512-6AC0r2r5rT0BpcPH7S27JS0CpFNKvvfdTRLinWwzeMdJCma9ceF8zUgnvMahHfLUcXn4fyypfth9Dito9tey8g==", "integrity": "sha512-YO5GuZTJuSeQBxYPOD+Ih3vfmNcnvc/VafxTG8cObuY5Zu5y+Dq4E0gcdhyGVeg3w29tFL9OvlEO23C6+CjqNw==",
"dev": true, "dev": true,
"bin": { "bin": {
"pyright": "index.js", "pyright": "index.js",

View File

@ -34,24 +34,25 @@
"@codemirror/legacy-modes": "^6.3.3", "@codemirror/legacy-modes": "^6.3.3",
"@codemirror/theme-one-dark": "^6.1.2", "@codemirror/theme-one-dark": "^6.1.2",
"@formatjs/intl-listformat": "^7.4.0", "@formatjs/intl-listformat": "^7.4.0",
"@fortawesome/fontawesome-free": "^6.4.0", "@fortawesome/fontawesome-free": "^6.4.2",
"@goauthentik/api": "^2023.6.1-1690455444", "@goauthentik/api": "^2023.6.1-1690455444",
"@lit-labs/context": "^0.3.3", "@lit-labs/context": "^0.3.3",
"@lit-labs/task": "^2.1.2", "@lit-labs/task": "^3.0.0",
"@lit/localize": "^0.11.4", "@lit/localize": "^0.11.4",
"@patternfly/elements": "^2.3.2",
"@patternfly/patternfly": "^4.224.2", "@patternfly/patternfly": "^4.224.2",
"@sentry/browser": "^7.60.1", "@sentry/browser": "^7.61.0",
"@sentry/tracing": "^7.60.1", "@sentry/tracing": "^7.61.0",
"@webcomponents/webcomponentsjs": "^2.8.0", "@webcomponents/webcomponentsjs": "^2.8.0",
"base64-js": "^1.5.1", "base64-js": "^1.5.1",
"chart.js": "^4.3.2", "chart.js": "^4.3.3",
"chartjs-adapter-moment": "^1.0.1", "chartjs-adapter-moment": "^1.0.1",
"codemirror": "^6.0.1", "codemirror": "^6.0.1",
"construct-style-sheets-polyfill": "^3.1.0", "construct-style-sheets-polyfill": "^3.1.0",
"core-js": "^3.32.0", "core-js": "^3.32.0",
"country-flag-icons": "^1.5.7", "country-flag-icons": "^1.5.7",
"fuse.js": "^6.6.2", "fuse.js": "^6.6.2",
"lit": "^2.7.6", "lit": "^2.8.0",
"mermaid": "^10.3.0", "mermaid": "^10.3.0",
"rapidoc": "^9.3.4", "rapidoc": "^9.3.4",
"style-mod": "^4.0.3", "style-mod": "^4.0.3",
@ -85,8 +86,8 @@
"@types/chart.js": "^2.9.37", "@types/chart.js": "^2.9.37",
"@types/codemirror": "5.60.8", "@types/codemirror": "5.60.8",
"@types/grecaptcha": "^3.0.4", "@types/grecaptcha": "^3.0.4",
"@typescript-eslint/eslint-plugin": "^6.2.0", "@typescript-eslint/eslint-plugin": "^6.2.1",
"@typescript-eslint/parser": "^6.2.0", "@typescript-eslint/parser": "^6.2.1",
"babel-plugin-macros": "^3.1.0", "babel-plugin-macros": "^3.1.0",
"babel-plugin-tsconfig-paths": "^1.0.3", "babel-plugin-tsconfig-paths": "^1.0.3",
"eslint": "^8.46.0", "eslint": "^8.46.0",
@ -96,8 +97,8 @@
"eslint-plugin-storybook": "^0.6.13", "eslint-plugin-storybook": "^0.6.13",
"lit-analyzer": "^1.2.1", "lit-analyzer": "^1.2.1",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"prettier": "^3.0.0", "prettier": "^3.0.1",
"pyright": "^1.1.319", "pyright": "^1.1.320",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"rollup": "^2.79.1", "rollup": "^2.79.1",

View File

@ -15,6 +15,7 @@ import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import "@goauthentik/elements/forms/ProxyForm"; import "@goauthentik/elements/forms/ProxyForm";
import "@goauthentik/elements/forms/Radio"; import "@goauthentik/elements/forms/Radio";
import "@goauthentik/elements/forms/SearchSelect"; import "@goauthentik/elements/forms/SearchSelect";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg } from "@lit/localize"; import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
@ -158,7 +159,7 @@ export class ApplicationForm extends ModelForm<Application, string> {
value=${this.instance?.group} value=${this.instance?.group}
label=${msg("Group")} label=${msg("Group")}
help=${msg( help=${msg(
"Optionally enter a group name. Applications with identical groups are shown grouped together.", "Optionally enter a group name. Applications with identical groups are shown grouped together."
)} )}
></ak-text-input> ></ak-text-input>
<ak-provider-search-input <ak-provider-search-input
@ -172,11 +173,15 @@ export class ApplicationForm extends ModelForm<Application, string> {
name="backchannelProviders" name="backchannelProviders"
label=${msg("Backchannel Providers")} label=${msg("Backchannel Providers")}
help=${msg( help=${msg(
"Select backchannel providers which augment the functionality of the main provider.", "Select backchannel providers which augment the functionality of the main provider."
)} )}
.providers=${this.backchannelProviders} .providers=${this.backchannelProviders}
.confirm=${this.handleConfirmBackchannelProviders} .confirm=${this.handleConfirmBackchannelProviders}
.remover=${this.makeRemoveBackchannelProviderHandler} .remover=${this.makeRemoveBackchannelProviderHandler}
.tooltip=${html`<pf-tooltip
position="top"
content=${msg("Add provider")}
></pf-tooltip>`}
> >
</ak-backchannel-providers-input> </ak-backchannel-providers-input>
<ak-radio-input <ak-radio-input
@ -194,7 +199,7 @@ export class ApplicationForm extends ModelForm<Application, string> {
label=${msg("Launch URL")} label=${msg("Launch URL")}
value=${ifDefined(this.instance?.metaLaunchUrl)} value=${ifDefined(this.instance?.metaLaunchUrl)}
help=${msg( help=${msg(
"If left empty, authentik will try to extract the launch URL based on the selected provider.", "If left empty, authentik will try to extract the launch URL based on the selected provider."
)} )}
></ak-text-input> ></ak-text-input>
<ak-switch-input <ak-switch-input
@ -202,7 +207,7 @@ export class ApplicationForm extends ModelForm<Application, string> {
?checked=${first(this.instance?.openInNewTab, false)} ?checked=${first(this.instance?.openInNewTab, false)}
label=${msg("Open in new tab")} label=${msg("Open in new tab")}
help=${msg( help=${msg(
"If checked, the launch URL will open in a new browser tab or window from the user's application library.", "If checked, the launch URL will open in a new browser tab or window from the user's application library."
)} )}
> >
</ak-switch-input> </ak-switch-input>

View File

@ -12,6 +12,7 @@ import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { TableColumn } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table";
import { TablePage } from "@goauthentik/elements/table/TablePage"; import { TablePage } from "@goauthentik/elements/table/TablePage";
import "@goauthentik/user/LibraryApplication/AppIcon"; import "@goauthentik/user/LibraryApplication/AppIcon";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg } from "@lit/localize"; import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, css, html } from "lit"; import { CSSResult, TemplateResult, css, html } from "lit";
@ -146,12 +147,16 @@ export class ApplicationListPage extends TablePage<Application> {
<ak-application-form slot="form" .instancePk=${item.slug}> <ak-application-form slot="form" .instancePk=${item.slug}>
</ak-application-form> </ak-application-form>
<button slot="trigger" class="pf-c-button pf-m-plain"> <button slot="trigger" class="pf-c-button pf-m-plain">
<pf-tooltip position="top" content=${msg("Edit")}>
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</pf-tooltip>
</button> </button>
</ak-forms-modal> </ak-forms-modal>
${item.launchUrl ${item.launchUrl
? html`<a href=${item.launchUrl} target="_blank" class="pf-c-button pf-m-plain"> ? html`<a href=${item.launchUrl} target="_blank" class="pf-c-button pf-m-plain">
<pf-tooltip position="top" content=${msg("Open")}>
<i class="fas fa-share-square"></i> <i class="fas fa-share-square"></i>
</pf-tooltip>
</a>` </a>`
: html``}`, : html``}`,
]; ];

View File

@ -1,60 +1,13 @@
import "@goauthentik/admin/applications/ProviderSelectModal"; import "@goauthentik/admin/applications/ProviderSelectModal";
import { AKElement } from "@goauthentik/elements/Base"; import { AKElement } from "@goauthentik/elements/Base";
import { html, nothing } from "lit"; import { TemplateResult, html, nothing } from "lit";
import { customElement, property } from "lit/decorators.js"; import { customElement, property } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js"; import { ifDefined } from "lit/directives/if-defined.js";
import { map } from "lit/directives/map.js"; import { map } from "lit/directives/map.js";
import { Provider } from "@goauthentik/api"; import { Provider } from "@goauthentik/api";
type AkBackchannelProvidersArgs = {
// The name of the field, snake-to-camel'd if necessary.
name: string;
// The label of the field.
label: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
value?: any;
// The help message, shown at the bottom.
help?: string;
providers: Provider[];
confirm: ({ items }: { items: Provider[] }) => Promise<void>;
remove: (provider: Provider) => () => void;
};
const akBackchannelProvidersDefaults = {
required: false,
};
export function akBackchannelProvidersInput(args: AkBackchannelProvidersArgs) {
const { name, label, help, providers, confirm, remove } = {
...akBackchannelProvidersDefaults,
...args,
};
const renderOneChip = (provider: Provider) =>
html`<ak-chip .removable=${true} value=${ifDefined(provider.pk)} @remove=${remove(provider)}
>${provider.name}</ak-chip
>`;
return html`
<ak-form-element-horizontal label=${label} name=${name}>
<div class="pf-c-input-group">
<ak-provider-select-table ?backchannelOnly=${true} .confirm=${confirm}>
<button slot="trigger" class="pf-c-button pf-m-control" type="button">
<i class="fas fa-plus" aria-hidden="true"></i>
</button>
</ak-provider-select-table>
<div class="pf-c-form-control">
<ak-chip-group> ${map(providers, renderOneChip)} </ak-chip-group>
</div>
</div>
${help ? html`<p class="pf-c-form__helper-radio">${help}</p>` : nothing}
</ak-form-element-horizontal>
`;
}
@customElement("ak-backchannel-providers-input") @customElement("ak-backchannel-providers-input")
export class AkBackchannelProvidersInput extends AKElement { export class AkBackchannelProvidersInput extends AKElement {
// Render into the lightDOM. This effectively erases the shadowDOM nature of this component, but // Render into the lightDOM. This effectively erases the shadowDOM nature of this component, but
@ -80,6 +33,9 @@ export class AkBackchannelProvidersInput extends AKElement {
@property({ type: Array }) @property({ type: Array })
providers: Provider[] = []; providers: Provider[] = [];
@property({ type: Object })
tooltip?: TemplateResult;
@property({ attribute: false, type: Object }) @property({ attribute: false, type: Object })
confirm!: ({ items }: { items: Provider[] }) => Promise<void>; confirm!: ({ items }: { items: Provider[] }) => Promise<void>;
@ -96,13 +52,29 @@ export class AkBackchannelProvidersInput extends AKElement {
help = ""; help = "";
render() { render() {
return akBackchannelProvidersInput({ const renderOneChip = (provider: Provider) =>
name: this.name, html`<ak-chip
label: this.label, .removable=${true}
help: this.help.trim() !== "" ? this.help : undefined, value=${ifDefined(provider.pk)}
providers: this.providers, @remove=${remove(provider)}
confirm: this.confirm, >${provider.name}</ak-chip
remove: this.remover, >`;
});
return html`
<ak-form-element-horizontal label=${label} name=${name}>
<div class="pf-c-input-group">
<ak-provider-select-table ?backchannelOnly=${true} .confirm=${confirm}>
<button slot="trigger" class="pf-c-button pf-m-control" type="button">
$ {this.tooltip ? this.tooltip : nothing }
<i class="fas fa-plus" aria-hidden="true"></i>
</button>
</ak-provider-select-table>
<div class="pf-c-form-control">
<ak-chip-group> ${map(providers, renderOneChip)} </ak-chip-group>
</div>
</div>
${help ? html`<p class="pf-c-form__helper-radio">${help}</p>` : nothing}
</ak-form-element-horizontal>
`;
} }
} }

View File

@ -10,6 +10,7 @@ import "@goauthentik/elements/forms/ModalForm";
import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { TableColumn } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table";
import { TablePage } from "@goauthentik/elements/table/TablePage"; import { TablePage } from "@goauthentik/elements/table/TablePage";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg } from "@lit/localize"; import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, html } from "lit"; import { CSSResult, TemplateResult, html } from "lit";
@ -140,14 +141,17 @@ export class BlueprintListPage extends TablePage<BlueprintInstance> {
html`<ak-label color=${item.enabled ? PFColor.Green : PFColor.Red}> html`<ak-label color=${item.enabled ? PFColor.Green : PFColor.Red}>
${item.enabled ? msg("Yes") : msg("No")} ${item.enabled ? msg("Yes") : msg("No")}
</ak-label>`, </ak-label>`,
html` <ak-forms-modal> html`<ak-forms-modal>
<span slot="submit"> ${msg("Update")} </span> <span slot="submit"> ${msg("Update")} </span>
<span slot="header"> ${msg("Update Blueprint")} </span> <span slot="header"> ${msg("Update Blueprint")} </span>
<ak-blueprint-form slot="form" .instancePk=${item.pk}> </ak-blueprint-form> <ak-blueprint-form slot="form" .instancePk=${item.pk}> </ak-blueprint-form>
<button slot="trigger" class="pf-c-button pf-m-plain"> <button slot="trigger" class="pf-c-button pf-m-plain">
<pf-tooltip position="top" content=${msg("Edit")}>
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</button> </ak-forms-modal </pf-tooltip>
><ak-action-button </button>
</ak-forms-modal>
<ak-action-button
class="pf-m-plain" class="pf-m-plain"
.apiRequest=${() => { .apiRequest=${() => {
return new ManagedApi(DEFAULT_CONFIG) return new ManagedApi(DEFAULT_CONFIG)
@ -164,7 +168,9 @@ export class BlueprintListPage extends TablePage<BlueprintInstance> {
}); });
}} }}
> >
<pf-tooltip position="top" content=${msg("Apply")}>
<i class="fas fa-play" aria-hidden="true"></i> <i class="fas fa-play" aria-hidden="true"></i>
</pf-tooltip>
</ak-action-button>`, </ak-action-button>`,
]; ];
} }

View File

@ -9,6 +9,7 @@ import "@goauthentik/elements/forms/ModalForm";
import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { TableColumn } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table";
import { TablePage } from "@goauthentik/elements/table/TablePage"; import { TablePage } from "@goauthentik/elements/table/TablePage";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg, str } from "@lit/localize"; import { msg, str } from "@lit/localize";
import { CSSResult, TemplateResult, html } from "lit"; import { CSSResult, TemplateResult, html } from "lit";
@ -123,7 +124,9 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
<ak-crypto-certificate-form slot="form" .instancePk=${item.pk}> <ak-crypto-certificate-form slot="form" .instancePk=${item.pk}>
</ak-crypto-certificate-form> </ak-crypto-certificate-form>
<button slot="trigger" class="pf-c-button pf-m-plain"> <button slot="trigger" class="pf-c-button pf-m-plain">
<pf-tooltip position="top" content=${msg("Edit")}>
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</pf-tooltip>
</button> </button>
</ak-forms-modal>`, </ak-forms-modal>`,
]; ];

View File

@ -10,6 +10,7 @@ import "@goauthentik/elements/forms/ModalForm";
import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { TableColumn } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table";
import { TablePage } from "@goauthentik/elements/table/TablePage"; import { TablePage } from "@goauthentik/elements/table/TablePage";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg, str } from "@lit/localize"; import { msg, str } from "@lit/localize";
import { CSSResult, TemplateResult, css, html } from "lit"; import { CSSResult, TemplateResult, css, html } from "lit";
@ -227,7 +228,9 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
<ak-enterprise-license-form slot="form" .instancePk=${item.licenseUuid}> <ak-enterprise-license-form slot="form" .instancePk=${item.licenseUuid}>
</ak-enterprise-license-form> </ak-enterprise-license-form>
<button slot="trigger" class="pf-c-button pf-m-plain"> <button slot="trigger" class="pf-c-button pf-m-plain">
<pf-tooltip position="top" content=${msg("Edit")}>
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</pf-tooltip>
</button> </button>
</ak-forms-modal>`, </ak-forms-modal>`,
]; ];

View File

@ -6,6 +6,7 @@ import { uiConfig } from "@goauthentik/common/ui/config";
import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { TableColumn } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table";
import { TablePage } from "@goauthentik/elements/table/TablePage"; import { TablePage } from "@goauthentik/elements/table/TablePage";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg, str } from "@lit/localize"; import { msg, str } from "@lit/localize";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
@ -75,7 +76,9 @@ export class EventListPage extends TablePage<Event> {
<small>${EventGeo(item)}</small>`, <small>${EventGeo(item)}</small>`,
html`<span>${item.tenant?.name || msg("-")}</span>`, html`<span>${item.tenant?.name || msg("-")}</span>`,
html`<a href="#/events/log/${item.pk}"> html`<a href="#/events/log/${item.pk}">
<pf-tooltip position="top" content=${msg("Show details")}>
<i class="fas fa-share-square"></i> <i class="fas fa-share-square"></i>
</pf-tooltip>
</a>`, </a>`,
]; ];
} }

View File

@ -9,6 +9,7 @@ import "@goauthentik/elements/forms/ModalForm";
import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { TableColumn } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table";
import { TablePage } from "@goauthentik/elements/table/TablePage"; import { TablePage } from "@goauthentik/elements/table/TablePage";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg } from "@lit/localize"; import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
@ -91,7 +92,9 @@ export class RuleListPage extends TablePage<NotificationRule> {
<span slot="header"> ${msg("Update Notification Rule")} </span> <span slot="header"> ${msg("Update Notification Rule")} </span>
<ak-event-rule-form slot="form" .instancePk=${item.pk}> </ak-event-rule-form> <ak-event-rule-form slot="form" .instancePk=${item.pk}> </ak-event-rule-form>
<button slot="trigger" class="pf-c-button pf-m-plain"> <button slot="trigger" class="pf-c-button pf-m-plain">
<pf-tooltip position="top" content=${msg("Edit")}>
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</pf-tooltip>
</button> </button>
</ak-forms-modal>`, </ak-forms-modal>`,
]; ];

View File

@ -8,6 +8,7 @@ import "@goauthentik/elements/forms/ModalForm";
import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { TableColumn } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table";
import { TablePage } from "@goauthentik/elements/table/TablePage"; import { TablePage } from "@goauthentik/elements/table/TablePage";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg } from "@lit/localize"; import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
@ -84,7 +85,9 @@ export class TransportListPage extends TablePage<NotificationTransport> {
<ak-event-transport-form slot="form" .instancePk=${item.pk}> <ak-event-transport-form slot="form" .instancePk=${item.pk}>
</ak-event-transport-form> </ak-event-transport-form>
<button slot="trigger" class="pf-c-button pf-m-plain"> <button slot="trigger" class="pf-c-button pf-m-plain">
<pf-tooltip position="top" content=${msg("Edit")}>
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</pf-tooltip>
</button> </button>
</ak-forms-modal> </ak-forms-modal>
<ak-action-button <ak-action-button
@ -95,7 +98,9 @@ export class TransportListPage extends TablePage<NotificationTransport> {
}); });
}} }}
> >
<pf-tooltip position="top" content=${msg("Test")}>
<i class="fas fa-vial" aria-hidden="true"></i> <i class="fas fa-vial" aria-hidden="true"></i>
</pf-tooltip>
</ak-action-button>`, </ak-action-button>`,
]; ];
} }

View File

@ -11,6 +11,7 @@ import "@goauthentik/elements/forms/ModalForm";
import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { TableColumn } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table";
import { TablePage } from "@goauthentik/elements/table/TablePage"; import { TablePage } from "@goauthentik/elements/table/TablePage";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg } from "@lit/localize"; import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
@ -103,7 +104,9 @@ export class FlowListPage extends TablePage<Flow> {
<span slot="header"> ${msg("Update Flow")} </span> <span slot="header"> ${msg("Update Flow")} </span>
<ak-flow-form slot="form" .instancePk=${item.slug}> </ak-flow-form> <ak-flow-form slot="form" .instancePk=${item.slug}> </ak-flow-form>
<button slot="trigger" class="pf-c-button pf-m-plain"> <button slot="trigger" class="pf-c-button pf-m-plain">
<pf-tooltip position="top" content=${msg("Edit")}>
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</pf-tooltip>
</button> </button>
</ak-forms-modal> </ak-forms-modal>
<button <button
@ -115,10 +118,14 @@ export class FlowListPage extends TablePage<Flow> {
window.open(finalURL, "_blank"); window.open(finalURL, "_blank");
}} }}
> >
<i class="fas fa-play"></i> <pf-tooltip position="top" content=${msg("Execute")}>
<i class="fas fa-play" aria-hidden="true"></i>
</pf-tooltip>
</button> </button>
<a class="pf-c-button pf-m-plain" href=${item.exportUrl}> <a class="pf-c-button pf-m-plain" href=${item.exportUrl}>
<pf-tooltip position="top" content=${msg("Export")}>
<i class="fas fa-download"></i> <i class="fas fa-download"></i>
</pf-tooltip>
</a>`, </a>`,
]; ];
} }

View File

@ -8,6 +8,7 @@ import "@goauthentik/elements/forms/ModalForm";
import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { TableColumn } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table";
import { TablePage } from "@goauthentik/elements/table/TablePage"; import { TablePage } from "@goauthentik/elements/table/TablePage";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg } from "@lit/localize"; import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
@ -88,7 +89,9 @@ export class GroupListPage extends TablePage<Group> {
<span slot="header"> ${msg("Update Group")} </span> <span slot="header"> ${msg("Update Group")} </span>
<ak-group-form slot="form" .instancePk=${item.pk}> </ak-group-form> <ak-group-form slot="form" .instancePk=${item.pk}> </ak-group-form>
<button slot="trigger" class="pf-c-button pf-m-plain"> <button slot="trigger" class="pf-c-button pf-m-plain">
<pf-tooltip position="top" content=${msg("Edit")}>
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</pf-tooltip>
</button> </button>
</ak-forms-modal>`, </ak-forms-modal>`,
]; ];

View File

@ -11,6 +11,7 @@ import "@goauthentik/elements/forms/HorizontalFormElement";
import "@goauthentik/elements/forms/ModalForm"; import "@goauthentik/elements/forms/ModalForm";
import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { Table, TableColumn } from "@goauthentik/elements/table/Table"; import { Table, TableColumn } from "@goauthentik/elements/table/Table";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg, str } from "@lit/localize"; import { msg, str } from "@lit/localize";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
@ -56,7 +57,9 @@ export class RelatedGroupAdd extends Form<{ groups: string[] }> {
}} }}
> >
<button slot="trigger" class="pf-c-button pf-m-control" type="button"> <button slot="trigger" class="pf-c-button pf-m-control" type="button">
<pf-tooltip position="top" content=${msg("Add group")}>
<i class="fas fa-plus" aria-hidden="true"></i> <i class="fas fa-plus" aria-hidden="true"></i>
</pf-tooltip>
</button> </button>
</ak-user-group-select-table> </ak-user-group-select-table>
<div class="pf-c-form-control"> <div class="pf-c-form-control">
@ -150,7 +153,9 @@ export class RelatedGroupList extends Table<Group> {
<span slot="header"> ${msg("Update Group")} </span> <span slot="header"> ${msg("Update Group")} </span>
<ak-group-form slot="form" .instancePk=${item.pk}> </ak-group-form> <ak-group-form slot="form" .instancePk=${item.pk}> </ak-group-form>
<button slot="trigger" class="pf-c-button pf-m-plain"> <button slot="trigger" class="pf-c-button pf-m-plain">
<pf-tooltip position="top" content=${msg("Edit")}>
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</pf-tooltip>
</button> </button>
</ak-forms-modal>`, </ak-forms-modal>`,
]; ];

View File

@ -13,6 +13,7 @@ import "@goauthentik/elements/forms/ModalForm";
import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { TableColumn } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table";
import { TablePage } from "@goauthentik/elements/table/TablePage"; import { TablePage } from "@goauthentik/elements/table/TablePage";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg, str } from "@lit/localize"; import { msg, str } from "@lit/localize";
import { CSSResult } from "lit"; import { CSSResult } from "lit";
@ -135,7 +136,9 @@ export class OutpostListPage extends TablePage<Outpost> {
> >
</ak-outpost-form> </ak-outpost-form>
<button slot="trigger" class="pf-c-button pf-m-plain"> <button slot="trigger" class="pf-c-button pf-m-plain">
<pf-tooltip position="top" content=${msg("Edit")}>
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</pf-tooltip>
</button> </button>
</ak-forms-modal> </ak-forms-modal>
${item.managed !== "goauthentik.io/outposts/embedded" ${item.managed !== "goauthentik.io/outposts/embedded"

View File

@ -12,6 +12,7 @@ import "@goauthentik/elements/forms/ProxyForm";
import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { TableColumn } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table";
import { TablePage } from "@goauthentik/elements/table/TablePage"; import { TablePage } from "@goauthentik/elements/table/TablePage";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg, str } from "@lit/localize"; import { msg, str } from "@lit/localize";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
@ -99,7 +100,9 @@ export class OutpostServiceConnectionListPage extends TablePage<ServiceConnectio
> >
</ak-proxy-form> </ak-proxy-form>
<button slot="trigger" class="pf-c-button pf-m-plain"> <button slot="trigger" class="pf-c-button pf-m-plain">
<pf-tooltip position="top" content=${msg("Edit")}>
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</pf-tooltip>
</button> </button>
</ak-forms-modal>`, </ak-forms-modal>`,
]; ];

View File

@ -16,6 +16,7 @@ import "@goauthentik/elements/forms/ProxyForm";
import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { TableColumn } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table";
import { TablePage } from "@goauthentik/elements/table/TablePage"; import { TablePage } from "@goauthentik/elements/table/TablePage";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg, str } from "@lit/localize"; import { msg, str } from "@lit/localize";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
@ -86,7 +87,9 @@ export class PolicyListPage extends TablePage<Policy> {
> >
</ak-proxy-form> </ak-proxy-form>
<button slot="trigger" class="pf-c-button pf-m-plain"> <button slot="trigger" class="pf-c-button pf-m-plain">
<i class="fas fa-pencil-alt" aria-hidden="true"></i> <pf-tooltip position="top" content=${msg("Edit")}>
<i class="fas fa-edit"></i>
</pf-tooltip>
</button> </button>
</ak-forms-modal> </ak-forms-modal>
<ak-forms-modal .closeAfterSuccessfulSubmit=${false}> <ak-forms-modal .closeAfterSuccessfulSubmit=${false}>
@ -94,7 +97,9 @@ export class PolicyListPage extends TablePage<Policy> {
<span slot="header"> ${msg("Test Policy")} </span> <span slot="header"> ${msg("Test Policy")} </span>
<ak-policy-test-form slot="form" .policy=${item}> </ak-policy-test-form> <ak-policy-test-form slot="form" .policy=${item}> </ak-policy-test-form>
<button slot="trigger" class="pf-c-button pf-m-plain"> <button slot="trigger" class="pf-c-button pf-m-plain">
<pf-tooltip position="top" content=${msg("Test")}>
<i class="fas fa-vial" aria-hidden="true"></i> <i class="fas fa-vial" aria-hidden="true"></i>
</pf-tooltip>
</button> </button>
</ak-forms-modal>`, </ak-forms-modal>`,
]; ];

View File

@ -14,6 +14,7 @@ import { getURLParam, updateURLParams } from "@goauthentik/elements/router/Route
import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { TableColumn } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table";
import { TablePage } from "@goauthentik/elements/table/TablePage"; import { TablePage } from "@goauthentik/elements/table/TablePage";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg, str } from "@lit/localize"; import { msg, str } from "@lit/localize";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
@ -101,7 +102,9 @@ export class PropertyMappingListPage extends TablePage<PropertyMapping> {
> >
</ak-proxy-form> </ak-proxy-form>
<button slot="trigger" class="pf-c-button pf-m-plain"> <button slot="trigger" class="pf-c-button pf-m-plain">
<pf-tooltip position="top" content=${msg("Edit")}>
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</pf-tooltip>
</button> </button>
</ak-forms-modal> </ak-forms-modal>
<ak-forms-modal .closeAfterSuccessfulSubmit=${false}> <ak-forms-modal .closeAfterSuccessfulSubmit=${false}>
@ -110,7 +113,9 @@ export class PropertyMappingListPage extends TablePage<PropertyMapping> {
<ak-property-mapping-test-form slot="form" .mapping=${item}> <ak-property-mapping-test-form slot="form" .mapping=${item}>
</ak-property-mapping-test-form> </ak-property-mapping-test-form>
<button slot="trigger" class="pf-c-button pf-m-plain"> <button slot="trigger" class="pf-c-button pf-m-plain">
<pf-tooltip position="top" content=${msg("Test")}>
<i class="fas fa-vial" aria-hidden="true"></i> <i class="fas fa-vial" aria-hidden="true"></i>
</pf-tooltip>
</button> </button>
</ak-forms-modal>`, </ak-forms-modal>`,
]; ];

View File

@ -14,6 +14,7 @@ import "@goauthentik/elements/forms/ProxyForm";
import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { TableColumn } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table";
import { TablePage } from "@goauthentik/elements/table/TablePage"; import { TablePage } from "@goauthentik/elements/table/TablePage";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg, str } from "@lit/localize"; import { msg, str } from "@lit/localize";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
@ -118,7 +119,9 @@ export class ProviderListPage extends TablePage<Provider> {
> >
</ak-proxy-form> </ak-proxy-form>
<button slot="trigger" class="pf-c-button pf-m-plain"> <button slot="trigger" class="pf-c-button pf-m-plain">
<pf-tooltip position="top" content=${msg("Edit")}>
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</pf-tooltip>
</button> </button>
</ak-forms-modal>`, </ak-forms-modal>`,
]; ];

View File

@ -12,6 +12,7 @@ import "@goauthentik/elements/forms/ProxyForm";
import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { TableColumn } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table";
import { TablePage } from "@goauthentik/elements/table/TablePage"; import { TablePage } from "@goauthentik/elements/table/TablePage";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg, str } from "@lit/localize"; import { msg, str } from "@lit/localize";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
@ -107,7 +108,9 @@ export class SourceListPage extends TablePage<Source> {
> >
</ak-proxy-form> </ak-proxy-form>
<button slot="trigger" class="pf-c-button pf-m-plain"> <button slot="trigger" class="pf-c-button pf-m-plain">
<pf-tooltip position="top" content=${msg("Edit")}>
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</pf-tooltip>
</button> </button>
</ak-forms-modal>`, </ak-forms-modal>`,
]; ];

View File

@ -27,6 +27,7 @@ import "@goauthentik/elements/forms/ProxyForm";
import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { TableColumn } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table";
import { TablePage } from "@goauthentik/elements/table/TablePage"; import { TablePage } from "@goauthentik/elements/table/TablePage";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg, str } from "@lit/localize"; import { msg, str } from "@lit/localize";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
@ -108,7 +109,9 @@ export class StageListPage extends TablePage<Stage> {
> >
</ak-stage-authenticator-duo-device-import-form> </ak-stage-authenticator-duo-device-import-form>
<button slot="trigger" class="pf-c-button pf-m-plain"> <button slot="trigger" class="pf-c-button pf-m-plain">
<i class="fas fa-file-import"></i> <pf-tooltip position="top" content=${msg("Import devices")}>
<i class="fas fa-file-import" aria-hidden="true"></i>
</pf-tooltip>
</button> </button>
</ak-forms-modal>`; </ak-forms-modal>`;
default: default:
@ -141,7 +144,9 @@ export class StageListPage extends TablePage<Stage> {
> >
</ak-proxy-form> </ak-proxy-form>
<button slot="trigger" class="pf-c-button pf-m-plain"> <button slot="trigger" class="pf-c-button pf-m-plain">
<pf-tooltip position="top" content=${msg("Edit")}>
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</pf-tooltip>
</button> </button>
</ak-forms-modal> </ak-forms-modal>
${this.renderStageActions(item)}`, ${this.renderStageActions(item)}`,

View File

@ -10,6 +10,7 @@ import "@goauthentik/elements/forms/ModalForm";
import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { TableColumn } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table";
import { TablePage } from "@goauthentik/elements/table/TablePage"; import { TablePage } from "@goauthentik/elements/table/TablePage";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg } from "@lit/localize"; import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, html } from "lit"; import { CSSResult, TemplateResult, html } from "lit";
@ -127,7 +128,9 @@ export class InvitationListPage extends TablePage<Invitation> {
<span slot="header"> ${msg("Update Invitation")} </span> <span slot="header"> ${msg("Update Invitation")} </span>
<ak-invitation-form slot="form" .instancePk=${item.pk}> </ak-invitation-form> <ak-invitation-form slot="form" .instancePk=${item.pk}> </ak-invitation-form>
<button slot="trigger" class="pf-c-button pf-m-plain"> <button slot="trigger" class="pf-c-button pf-m-plain">
<pf-tooltip position="top" content=${msg("Edit")}>
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</pf-tooltip>
</button> </button>
</ak-forms-modal>`, </ak-forms-modal>`,
]; ];

View File

@ -8,6 +8,7 @@ import "@goauthentik/elements/forms/ModalForm";
import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { TableColumn } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table";
import { TablePage } from "@goauthentik/elements/table/TablePage"; import { TablePage } from "@goauthentik/elements/table/TablePage";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg } from "@lit/localize"; import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
@ -91,7 +92,9 @@ export class PromptListPage extends TablePage<Prompt> {
<span slot="header"> ${msg("Update Prompt")} </span> <span slot="header"> ${msg("Update Prompt")} </span>
<ak-prompt-form slot="form" .instancePk=${item.pk}> </ak-prompt-form> <ak-prompt-form slot="form" .instancePk=${item.pk}> </ak-prompt-form>
<button slot="trigger" class="pf-c-button pf-m-plain"> <button slot="trigger" class="pf-c-button pf-m-plain">
<pf-tooltip position="top" content=${msg("Edit")}>
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</pf-tooltip>
</button> </button>
</ak-forms-modal>`, </ak-forms-modal>`,
]; ];

View File

@ -6,6 +6,7 @@ import "@goauthentik/elements/buttons/SpinnerButton";
import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { TableColumn } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table";
import { TablePage } from "@goauthentik/elements/table/TablePage"; import { TablePage } from "@goauthentik/elements/table/TablePage";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg, str } from "@lit/localize"; import { msg, str } from "@lit/localize";
import { CSSResult, TemplateResult, html } from "lit"; import { CSSResult, TemplateResult, html } from "lit";
@ -133,7 +134,9 @@ export class SystemTaskListPage extends TablePage<Task> {
}); });
}} }}
> >
<i class="fas fa-play" aria-hidden="true"></i> <pf-tooltip position="top" content=${msg("Restart task")}>
<i class="fas fa-redo" aria-hidden="true"></i>
</pf-tooltip>
</ak-action-button>`, </ak-action-button>`,
]; ];
} }

View File

@ -8,6 +8,7 @@ import "@goauthentik/elements/forms/ModalForm";
import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { TableColumn } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table";
import { TablePage } from "@goauthentik/elements/table/TablePage"; import { TablePage } from "@goauthentik/elements/table/TablePage";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg } from "@lit/localize"; import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
@ -88,7 +89,9 @@ export class TenantListPage extends TablePage<Tenant> {
<span slot="header"> ${msg("Update Tenant")} </span> <span slot="header"> ${msg("Update Tenant")} </span>
<ak-tenant-form slot="form" .instancePk=${item.tenantUuid}> </ak-tenant-form> <ak-tenant-form slot="form" .instancePk=${item.tenantUuid}> </ak-tenant-form>
<button slot="trigger" class="pf-c-button pf-m-plain"> <button slot="trigger" class="pf-c-button pf-m-plain">
<pf-tooltip position="top" content=${msg("Edit")}>
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</pf-tooltip>
</button> </button>
</ak-forms-modal>`, </ak-forms-modal>`,
]; ];

View File

@ -9,6 +9,7 @@ import "@goauthentik/elements/forms/ModalForm";
import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { TableColumn } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table";
import { TablePage } from "@goauthentik/elements/table/TablePage"; import { TablePage } from "@goauthentik/elements/table/TablePage";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg } from "@lit/localize"; import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
@ -128,7 +129,9 @@ export class TokenListPage extends TablePage<Token> {
<span slot="header"> ${msg("Update Token")} </span> <span slot="header"> ${msg("Update Token")} </span>
<ak-token-form slot="form" .instancePk=${item.identifier}></ak-token-form> <ak-token-form slot="form" .instancePk=${item.identifier}></ak-token-form>
<button slot="trigger" class="pf-c-button pf-m-plain"> <button slot="trigger" class="pf-c-button pf-m-plain">
<pf-tooltip position="top" content=${msg("Edit")}>
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</pf-tooltip>
</button> </button>
</ak-forms-modal>` </ak-forms-modal>`
: html``} : html``}
@ -136,7 +139,9 @@ export class TokenListPage extends TablePage<Token> {
class="pf-c-button pf-m-plain" class="pf-c-button pf-m-plain"
identifier="${item.identifier}" identifier="${item.identifier}"
> >
<pf-tooltip position="top" content=${msg("Copy token")}>
<i class="fas fa-copy"></i> <i class="fas fa-copy"></i>
</pf-tooltip>
</ak-token-copy-button> </ak-token-copy-button>
`, `,
]; ];

View File

@ -20,6 +20,7 @@ import { getURLParam, updateURLParams } from "@goauthentik/elements/router/Route
import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { Table, TableColumn } from "@goauthentik/elements/table/Table"; import { Table, TableColumn } from "@goauthentik/elements/table/Table";
import { UserOption } from "@goauthentik/elements/user/utils"; import { UserOption } from "@goauthentik/elements/user/utils";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg, str } from "@lit/localize"; import { msg, str } from "@lit/localize";
import { CSSResult, TemplateResult, html } from "lit"; import { CSSResult, TemplateResult, html } from "lit";
@ -70,7 +71,9 @@ export class RelatedUserAdd extends Form<{ users: number[] }> {
}} }}
> >
<button slot="trigger" class="pf-c-button pf-m-control" type="button"> <button slot="trigger" class="pf-c-button pf-m-control" type="button">
<pf-tooltip position="top" content=${msg("Add users")}>
<i class="fas fa-plus" aria-hidden="true"></i> <i class="fas fa-plus" aria-hidden="true"></i>
</pf-tooltip>
</button> </button>
</ak-group-member-select-table> </ak-group-member-select-table>
<div class="pf-c-form-control"> <div class="pf-c-form-control">
@ -187,7 +190,9 @@ export class RelatedUserList extends Table<User> {
<span slot="header"> ${msg("Update User")} </span> <span slot="header"> ${msg("Update User")} </span>
<ak-user-form slot="form" .instancePk=${item.pk}> </ak-user-form> <ak-user-form slot="form" .instancePk=${item.pk}> </ak-user-form>
<button slot="trigger" class="pf-c-button pf-m-plain"> <button slot="trigger" class="pf-c-button pf-m-plain">
<pf-tooltip position="top" content=${msg("Edit")}>
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</pf-tooltip>
</button> </button>
</ak-forms-modal> </ak-forms-modal>
${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanImpersonate) ${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanImpersonate)

View File

@ -20,6 +20,7 @@ import { getURLParam, updateURLParams } from "@goauthentik/elements/router/Route
import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { TableColumn } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table";
import { TablePage } from "@goauthentik/elements/table/TablePage"; import { TablePage } from "@goauthentik/elements/table/TablePage";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg, str } from "@lit/localize"; import { msg, str } from "@lit/localize";
import { CSSResult, TemplateResult, html } from "lit"; import { CSSResult, TemplateResult, html } from "lit";
@ -192,7 +193,9 @@ export class UserListPage extends TablePage<User> {
<span slot="header"> ${msg("Update User")} </span> <span slot="header"> ${msg("Update User")} </span>
<ak-user-form slot="form" .instancePk=${item.pk}> </ak-user-form> <ak-user-form slot="form" .instancePk=${item.pk}> </ak-user-form>
<button slot="trigger" class="pf-c-button pf-m-plain"> <button slot="trigger" class="pf-c-button pf-m-plain">
<pf-tooltip position="top" content=${msg("Edit")}>
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</pf-tooltip>
</button> </button>
</ak-forms-modal> </ak-forms-modal>
${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanImpersonate) ${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanImpersonate)

View File

@ -4,7 +4,7 @@ import { UIConfig, uiConfig } from "@goauthentik/common/ui/config";
import { adaptCSS } from "@goauthentik/common/utils"; import { adaptCSS } from "@goauthentik/common/utils";
import { localized } from "@lit/localize"; import { localized } from "@lit/localize";
import { LitElement } from "lit"; import { CSSResult, LitElement } from "lit";
import { state } from "lit/decorators.js"; import { state } from "lit/decorators.js";
import AKGlobal from "@goauthentik/common/styles/authentik.css"; import AKGlobal from "@goauthentik/common/styles/authentik.css";
@ -20,6 +20,13 @@ export function rootInterface<T extends Interface>(): T | undefined {
return el[0] as T; return el[0] as T;
} }
export function ensureCSSStyleSheet(css: CSSStyleSheet | CSSResult): CSSStyleSheet {
if (css instanceof CSSResult) {
return css.styleSheet!;
}
return css;
}
let css: Promise<string[]> | undefined; let css: Promise<string[]> | undefined;
function fetchCustomCSS(): Promise<string[]> { function fetchCustomCSS(): Promise<string[]> {
if (!css) { if (!css) {
@ -66,7 +73,10 @@ export class AKElement extends LitElement {
if ("ShadyDOM" in window) { if ("ShadyDOM" in window) {
styleRoot = document; styleRoot = document;
} }
styleRoot.adoptedStyleSheets = adaptCSS([...styleRoot.adoptedStyleSheets, AKGlobal]); styleRoot.adoptedStyleSheets = adaptCSS([
...styleRoot.adoptedStyleSheets,
ensureCSSStyleSheet(AKGlobal),
]);
this._initTheme(styleRoot); this._initTheme(styleRoot);
this._initCustomCSS(styleRoot); this._initCustomCSS(styleRoot);
return root; return root;
@ -151,7 +161,7 @@ export class AKElement extends LitElement {
const stylesheet = AKElement.themeToStylesheet(theme); const stylesheet = AKElement.themeToStylesheet(theme);
const oldStylesheet = AKElement.themeToStylesheet(this._activeTheme); const oldStylesheet = AKElement.themeToStylesheet(this._activeTheme);
if (stylesheet) { if (stylesheet) {
root.adoptedStyleSheets = [...root.adoptedStyleSheets, stylesheet]; root.adoptedStyleSheets = [...root.adoptedStyleSheets, ensureCSSStyleSheet(stylesheet)];
} }
if (oldStylesheet) { if (oldStylesheet) {
root.adoptedStyleSheets = root.adoptedStyleSheets.filter((v) => v !== oldStylesheet); root.adoptedStyleSheets = root.adoptedStyleSheets.filter((v) => v !== oldStylesheet);
@ -173,7 +183,7 @@ export class Interface extends AKElement {
constructor() { constructor() {
super(); super();
document.adoptedStyleSheets = [...document.adoptedStyleSheets, PFBase]; document.adoptedStyleSheets = [...document.adoptedStyleSheets, ensureCSSStyleSheet(PFBase)];
tenant().then((tenant) => (this.tenant = tenant)); tenant().then((tenant) => (this.tenant = tenant));
config().then((config) => (this.config = config)); config().then((config) => (this.config = config));
} }

View File

@ -9,6 +9,7 @@ import {
import { currentInterface } from "@goauthentik/common/sentry"; import { currentInterface } from "@goauthentik/common/sentry";
import { me } from "@goauthentik/common/users"; import { me } from "@goauthentik/common/users";
import { AKElement, rootInterface } from "@goauthentik/elements/Base"; import { AKElement, rootInterface } from "@goauthentik/elements/Base";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg } from "@lit/localize"; import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, css, html } from "lit"; import { CSSResult, TemplateResult, css, html } from "lit";
@ -169,7 +170,9 @@ export class PageHeader extends AKElement {
); );
}} }}
> >
<pf-tooltip position="top" content=${msg("Open API drawer")}>
<i class="fas fa-code"></i> <i class="fas fa-code"></i>
</pf-tooltip>
</button> </button>
<button <button
class="notification-trigger pf-c-button pf-m-plain ${this.hasNotifications class="notification-trigger pf-c-button pf-m-plain ${this.hasNotifications
@ -184,7 +187,9 @@ export class PageHeader extends AKElement {
); );
}} }}
> >
<pf-tooltip position="top" content=${msg("Open Notification drawer")}>
<i class="fas fa-bell"></i> <i class="fas fa-bell"></i>
</pf-tooltip>
</button> </button>
</div>`; </div>`;
} }

View File

@ -1,55 +0,0 @@
import { AKElement } from "@goauthentik/elements/Base";
import { CSSResult, TemplateResult, css, html } from "lit";
import { customElement, state } from "lit/decorators.js";
import PFTooltip from "@patternfly/patternfly/components/Tooltip/tooltip.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
@customElement("ak-tooltip")
export class Tooltip extends AKElement {
@state()
open = false;
static get styles(): CSSResult[] {
return [
PFBase,
PFTooltip,
css`
.pf-c-tooltip__content {
text-align: inherit;
}
.outer {
position: relative;
}
.pf-c-tooltip {
position: absolute;
z-index: 999;
}
`,
];
}
render(): TemplateResult {
return html`<slot
@mouseenter=${() => {
this.open = true;
}}
@mouseleave=${() => {
this.open = false;
}}
name="trigger"
></slot>
${this.open
? html`<div class="outer">
<div class="pf-c-tooltip" role="tooltip">
<div class="pf-c-tooltip__arrow"></div>
<div class="pf-c-tooltip__content">
<slot name="tooltip"></slot>
</div>
</div>
</div>`
: html``}`;
}
}

View File

@ -1,5 +1,7 @@
import { AKElement } from "@goauthentik/elements/Base"; import { AKElement } from "@goauthentik/elements/Base";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, html } from "lit"; import { CSSResult, TemplateResult, html } from "lit";
import { customElement, property } from "lit/decorators.js"; import { customElement, property } from "lit/decorators.js";
@ -38,7 +40,9 @@ export class Chip extends AKElement {
); );
}} }}
> >
<pf-tooltip position="top" content=${msg("Remove item")}>
<i class="fas fa-times" aria-hidden="true"></i> <i class="fas fa-times" aria-hidden="true"></i>
</pf-tooltip>
</button>` </button>`
: html``} : html``}
</div> </div>

View File

@ -99,7 +99,9 @@ export class NotificationDrawer extends AKElement {
class="pf-c-dropdown__toggle pf-m-plain" class="pf-c-dropdown__toggle pf-m-plain"
href="/if/admin/#/events/log/${item.event?.pk}" href="/if/admin/#/events/log/${item.event?.pk}"
> >
<pf-tooltip position="top" content=${msg("Show details")}>
<i class="fas fa-share-square"></i> <i class="fas fa-share-square"></i>
</pf-tooltip>
</a> </a>
`} `}
<button <button

View File

@ -1,5 +1,5 @@
import { AKElement } from "@goauthentik/elements/Base"; import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/Tooltip"; import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg } from "@lit/localize"; import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, html } from "lit"; import { CSSResult, TemplateResult, html } from "lit";
@ -19,15 +19,15 @@ export class TimeDeltaHelp extends AKElement {
} }
render(): TemplateResult { render(): TemplateResult {
return html` <ak-tooltip> return html`<div class="pf-c-form__helper-text">
<p class="pf-c-form__helper-text" slot="trigger"> <span>
${this.negative ${this.negative
? msg("(Format: hours=-1;minutes=-2;seconds=-3).") ? msg("(Format: hours=-1;minutes=-2;seconds=-3).")
: msg("(Format: hours=1;minutes=2;seconds=3).")} : msg("(Format: hours=1;minutes=2;seconds=3).")}
</span>
<pf-tooltip position="top">
<i class="pf-icon fa fa-question-circle" aria-hidden="true"></i> <i class="pf-icon fa fa-question-circle" aria-hidden="true"></i>
</p> <div slot="content">
<div slot="tooltip">
${msg("The following keywords are supported:")} ${msg("The following keywords are supported:")}
<ul class="pf-c-list"> <ul class="pf-c-list">
<li><pre>microseconds</pre></li> <li><pre>microseconds</pre></li>
@ -39,6 +39,7 @@ export class TimeDeltaHelp extends AKElement {
<li><pre>weeks</pre></li> <li><pre>weeks</pre></li>
</ul> </ul>
</div> </div>
</ak-tooltip>`; </pf-tooltip>
</div>`;
} }
} }

View File

@ -27,8 +27,9 @@ export class FlowErrorStage extends BaseStage<FlowErrorChallenge, FlowChallengeR
pre { pre {
overflow-x: scroll; overflow-x: scroll;
max-width: calc( max-width: calc(
35rem - var(--pf-c-login__main-body--PaddingRight) - 35rem - var(--pf-c-login__main-body--PaddingRight) - var(
var(--pf-c-login__main-body--PaddingRight) --pf-c-login__main-body--PaddingRight
)
); );
} }
`, `,

View File

@ -0,0 +1,56 @@
import type { StoryObj } from "@storybook/web-components";
import { html } from "lit";
import "@patternfly/patternfly/components/Login/login.css";
import { AccessDeniedChallenge, ChallengeChoices, UiThemeEnum } from "@goauthentik/api";
import "../../../stories/flow-interface";
import "./AccessDeniedStage";
export default {
title: "Flow / Stages / AccessDeniedStage",
};
export const LoadingNoChallenge = () => {
return html`<ak-storybook-interface theme=${UiThemeEnum.Dark}>
<div class="pf-c-login">
<div class="pf-c-login__container">
<div class="pf-c-login__main">
<ak-stage-access-denied></ak-stage-access-denied>
</div>
</div>
</div>
</ak-storybook-interface>`;
};
export const Challenge: StoryObj = {
render: ({ theme, challenge }) => {
return html`<ak-storybook-interface theme=${theme}>
<div class="pf-c-login">
<div class="pf-c-login__container">
<div class="pf-c-login__main">
<ak-stage-access-denied .challenge=${challenge}></ak-stage-access-denied>
</div>
</div></div
></ak-storybook-interface>`;
},
args: {
theme: "automatic",
challenge: {
type: ChallengeChoices.Native,
pendingUser: "foo",
pendingUserAvatar: "https://picsum.photos/64",
errorMessage: "This is an error message",
} as AccessDeniedChallenge,
},
argTypes: {
theme: {
options: [UiThemeEnum.Automatic, UiThemeEnum.Light, UiThemeEnum.Dark],
control: {
type: "select",
},
},
},
};

View File

@ -1,50 +0,0 @@
import type { Meta, StoryObj } from "@storybook/web-components";
import type { ButtonProps } from "./Button";
import { Button } from "./Button";
// More on how to set up stories at: https://storybook.js.org/docs/web-components/writing-stories/introduction
const meta = {
title: "Example/Button",
tags: ["autodocs"],
render: (args) => Button(args),
argTypes: {
backgroundColor: { control: "color" },
onClick: { action: "onClick" },
size: {
control: { type: "select" },
options: ["small", "medium", "large"],
},
},
} satisfies Meta<ButtonProps>;
export default meta;
type Story = StoryObj<ButtonProps>;
// More on writing stories with args: https://storybook.js.org/docs/web-components/writing-stories/args
export const Primary: Story = {
args: {
primary: true,
label: "Button",
},
};
export const Secondary: Story = {
args: {
label: "Button",
},
};
export const Large: Story = {
args: {
size: "large",
label: "Button",
},
};
export const Small: Story = {
args: {
size: "small",
label: "Button",
},
};

View File

@ -1,44 +0,0 @@
import { html } from "lit";
import { styleMap } from "lit/directives/style-map.js";
import "./button.css";
export interface ButtonProps {
/**
* Is this the principal call to action on the page?
*/
primary?: boolean;
/**
* What background color to use
*/
backgroundColor?: string;
/**
* How large should the button be?
*/
size?: "small" | "medium" | "large";
/**
* Button contents
*/
label: string;
/**
* Optional click handler
*/
onClick?: () => void;
}
/**
* Primary UI component for user interaction
*/
export const Button = ({ primary, backgroundColor, size, label, onClick }: ButtonProps) => {
const mode = primary ? "storybook-button--primary" : "storybook-button--secondary";
return html`
<button
type="button"
class=${["storybook-button", `storybook-button--${size || "medium"}`, mode].join(" ")}
style=${styleMap({ backgroundColor })}
@click=${onClick}
>
${label}
</button>
`;
};

View File

@ -1,24 +0,0 @@
import type { Meta, StoryObj } from "@storybook/web-components";
import type { HeaderProps } from "./Header";
import { Header } from "./Header";
const meta = {
title: "Example/Header",
// This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/web-components/writing-docs/autodocs
tags: ["autodocs"],
render: (args: HeaderProps) => Header(args),
} satisfies Meta<HeaderProps>;
export default meta;
type Story = StoryObj<HeaderProps>;
export const LoggedIn: Story = {
args: {
user: {
name: "Jane Doe",
},
},
};
export const LoggedOut: Story = {};

View File

@ -1,57 +0,0 @@
import { html } from "lit";
import "./header.css";
import { Button } from "./Button";
type User = {
name: string;
};
export interface HeaderProps {
user?: User;
onLogin: () => void;
onLogout: () => void;
onCreateAccount: () => void;
}
export const Header = ({ user, onLogin, onLogout, onCreateAccount }: HeaderProps) => html`
<header>
<div class="storybook-header">
<div>
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fillRule="evenodd">
<path
d="M10 0h12a10 10 0 0110 10v12a10 10 0 01-10 10H10A10 10 0 010 22V10A10 10 0 0110 0z"
fill="#FFF"
/>
<path
d="M5.3 10.6l10.4 6v11.1l-10.4-6v-11zm11.4-6.2l9.7 5.5-9.7 5.6V4.4z"
fill="#555AB9"
/>
<path
d="M27.2 10.6v11.2l-10.5 6V16.5l10.5-6zM15.7 4.4v11L6 10l9.7-5.5z"
fill="#91BAF8"
/>
</g>
</svg>
<h1>Acme</h1>
</div>
<div>
${user
? Button({ size: "small", onClick: onLogout, label: "Log out" })
: html`${Button({
size: "small",
onClick: onLogin,
label: "Log in",
})}
${Button({
primary: true,
size: "small",
onClick: onCreateAccount,
label: "Sign up",
})}`}
</div>
</div>
</header>
`;

View File

@ -1,214 +0,0 @@
import { Meta } from "@storybook/blocks";
import Code from "./assets/code-brackets.svg";
import Colors from "./assets/colors.svg";
import Comments from "./assets/comments.svg";
import Direction from "./assets/direction.svg";
import Flow from "./assets/flow.svg";
import Plugin from "./assets/plugin.svg";
import Repo from "./assets/repo.svg";
import StackAlt from "./assets/stackalt.svg";
<Meta title="Example/Introduction" />
<style>
{`
.subheading {
--mediumdark: '#999999';
font-weight: 700;
font-size: 13px;
color: #999;
letter-spacing: 6px;
line-height: 24px;
text-transform: uppercase;
margin-bottom: 12px;
margin-top: 40px;
}
.link-list {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: 1fr 1fr;
row-gap: 10px;
}
@media (min-width: 620px) {
.link-list {
row-gap: 20px;
column-gap: 20px;
grid-template-columns: 1fr 1fr;
}
}
@media all and (-ms-high-contrast:none) {
.link-list {
display: -ms-grid;
-ms-grid-columns: 1fr 1fr;
-ms-grid-rows: 1fr 1fr;
}
}
.link-item {
display: block;
padding: 20px;
border: 1px solid #00000010;
border-radius: 5px;
transition: background 150ms ease-out, border 150ms ease-out, transform 150ms ease-out;
color: #333333;
display: flex;
align-items: flex-start;
}
.link-item:hover {
border-color: #1EA7FD50;
transform: translate3d(0, -3px, 0);
box-shadow: rgba(0, 0, 0, 0.08) 0 3px 10px 0;
}
.link-item:active {
border-color: #1EA7FD;
transform: translate3d(0, 0, 0);
}
.link-item strong {
font-weight: 700;
display: block;
margin-bottom: 2px;
}
.link-item img {
height: 40px;
width: 40px;
margin-right: 15px;
flex: none;
}
.link-item span,
.link-item p {
margin: 0;
font-size: 14px;
line-height: 20px;
}
.tip {
display: inline-block;
border-radius: 1em;
font-size: 11px;
line-height: 12px;
font-weight: 700;
background: #E7FDD8;
color: #66BF3C;
padding: 4px 12px;
margin-right: 10px;
vertical-align: top;
}
.tip-wrapper {
font-size: 13px;
line-height: 20px;
margin-top: 40px;
margin-bottom: 40px;
}
.tip-wrapper code {
font-size: 12px;
display: inline-block;
}
`}
</style>
# Welcome to Storybook
Storybook helps you build UI components in isolation from your app's business logic, data, and context.
That makes it easy to develop hard-to-reach states. Save these UI states as **stories** to revisit during development, testing, or QA.
Browse example stories now by navigating to them in the sidebar.
View their code in the `stories` directory to learn how they work.
We recommend building UIs with a [**component-driven**](https://componentdriven.org) process starting with atomic components and ending with pages.
<div className="subheading">Configure</div>
<div className="link-list">
<a
className="link-item"
href="https://storybook.js.org/docs/react/addons/addon-types"
target="_blank"
>
<img src={Plugin} alt="plugin" />
<span>
<strong>Presets for popular tools</strong>
Easy setup for TypeScript, SCSS and more.
</span>
</a>
<a
className="link-item"
href="https://storybook.js.org/docs/react/configure/webpack"
target="_blank"
>
<img src={StackAlt} alt="Build" />
<span>
<strong>Build configuration</strong>
How to customize webpack and Babel
</span>
</a>
<a
className="link-item"
href="https://storybook.js.org/docs/react/configure/styling-and-css"
target="_blank"
>
<img src={Colors} alt="colors" />
<span>
<strong>Styling</strong>
How to load and configure CSS libraries
</span>
</a>
<a
className="link-item"
href="https://storybook.js.org/docs/react/get-started/setup#configure-storybook-for-your-stack"
target="_blank"
>
<img src={Flow} alt="flow" />
<span>
<strong>Data</strong>
Providers and mocking for data libraries
</span>
</a>
</div>
<div className="subheading">Learn</div>
<div className="link-list">
<a className="link-item" href="https://storybook.js.org/docs" target="_blank">
<img src={Repo} alt="repo" />
<span>
<strong>Storybook documentation</strong>
Configure, customize, and extend
</span>
</a>
<a className="link-item" href="https://storybook.js.org/tutorials/" target="_blank">
<img src={Direction} alt="direction" />
<span>
<strong>In-depth guides</strong>
Best practices from leading teams
</span>
</a>
<a className="link-item" href="https://github.com/storybookjs/storybook" target="_blank">
<img src={Code} alt="code" />
<span>
<strong>GitHub project</strong>
View the source and add issues
</span>
</a>
<a className="link-item" href="https://discord.gg/storybook" target="_blank">
<img src={Comments} alt="comments" />
<span>
<strong>Discord chat</strong>
Chat with maintainers and the community
</span>
</a>
</div>
<div className="tip-wrapper">
<span className="tip">Tip</span>Edit the Markdown in{" "}
<code>stories/Introduction.stories.mdx</code>
</div>

View File

@ -1,26 +0,0 @@
import type { Meta, StoryObj } from "@storybook/web-components";
import * as HeaderStories from "./Header.stories";
import type { PageProps } from "./Page";
import { Page } from "./Page";
const meta = {
title: "Example/Page",
render: (args: PageProps) => Page(args),
} satisfies Meta<PageProps>;
export default meta;
type Story = StoryObj<PageProps>;
export const LoggedIn: Story = {
args: {
// More on composing args: https://storybook.js.org/docs/web-components/writing-stories/args#args-composition
...HeaderStories.LoggedIn.args,
},
};
export const LoggedOut: Story = {
args: {
...HeaderStories.LoggedOut.args,
},
};

View File

@ -1,80 +0,0 @@
import { html } from "lit";
import "./page.css";
import { Header } from "./Header";
type User = {
name: string;
};
export interface PageProps {
user?: User;
onLogin: () => void;
onLogout: () => void;
onCreateAccount: () => void;
}
export const Page = ({ user, onLogin, onLogout, onCreateAccount }: PageProps) => html`
<article>
${Header({
user,
onLogin,
onLogout,
onCreateAccount,
})}
<section class="storybook-page">
<h2>Pages in Storybook</h2>
<p>
We recommend building UIs with a
<a href="https://componentdriven.org" target="_blank" rel="noopener noreferrer">
<strong>component-driven</strong> </a
>process starting with atomic components and ending with pages.
</p>
<p>
Render pages with mock data. This makes it easy to build and review page states
without needing to navigate to them in your app. Here are some handy patterns for
managing page data in Storybook:
</p>
<ul>
<li>
Use a higher-level connected component. Storybook helps you compose such data
from the "args" of child component stories
</li>
<li>
Assemble data in the page component from your services. You can mock these
services out using Storybook.
</li>
</ul>
<p>
Get a guided tutorial on component-driven development at
<a
href="https://storybook.js.org/tutorials/"
target="_blank"
rel="noopener noreferrer"
>
Storybook tutorials
</a>
. Read more in the
<a href="https://storybook.js.org/docs" target="_blank" rel="noopener noreferrer">
docs
</a>
.
</p>
<div class="tip-wrapper">
<span class="tip">Tip</span> Adjust the width of the canvas with the
<svg width="10" height="10" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fillRule="evenodd">
<path
d="M1.5 5.2h4.8c.3 0 .5.2.5.4v5.1c-.1.2-.3.3-.4.3H1.4a.5.5 0 01-.5-.4V5.7c0-.3.2-.5.5-.5zm0-2.1h6.9c.3 0 .5.2.5.4v7a.5.5 0 01-1 0V4H1.5a.5.5 0 010-1zm0-2.1h9c.3 0 .5.2.5.4v9.1a.5.5 0 01-1 0V2H1.5a.5.5 0 010-1zm4.3 5.2H2V10h3.8V6.2z"
id="a"
fill="#999"
/>
</g>
</svg>
Viewports addon in the toolbar
</div>
</section>
</article>
`;

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48" version="1.1" viewBox="0 0 48 48"><title>illustration/code-brackets</title><g id="illustration/code-brackets" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><path id="Combined-Shape" fill="#87E6E5" d="M11.4139325,12 C11.7605938,12 12,12.5059743 12,13.3779712 L12,17.4951758 L6.43502246,23.3839989 C5.85499251,23.9978337 5.85499251,25.0021663 6.43502246,25.6160011 L12,31.5048242 L12,35.6220288 C12,36.4939606 11.7605228,37 11.4139325,37 C11.2725831,37 11.1134406,36.9158987 10.9453839,36.7379973 L0.435022463,25.6160011 C-0.145007488,25.0021663 -0.145007488,23.9978337 0.435022463,23.3839989 L10.9453839,12.2620027 C11.1134051,12.0841663 11.2725831,12 11.4139325,12 Z M36.5860675,12 C36.7274169,12 36.8865594,12.0841013 37.0546161,12.2620027 L47.5649775,23.3839989 C48.1450075,23.9978337 48.1450075,25.0021663 47.5649775,25.6160011 L37.0546161,36.7379973 C36.8865949,36.9158337 36.7274169,37 36.5860675,37 C36.2394062,37 36,36.4940257 36,35.6220288 L36,31.5048242 L41.5649775,25.6160011 C42.1450075,25.0021663 42.1450075,23.9978337 41.5649775,23.3839989 L36,17.4951758 L36,13.3779712 C36,12.5060394 36.2394772,12 36.5860675,12 Z"/><rect id="Rectangle-7-Copy-5" width="35.57" height="4" x="5.009" y="22.662" fill="#A0DB77" rx="2" transform="translate(22.793959, 24.662305) rotate(-75.000000) translate(-22.793959, -24.662305)"/></g></svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.3 KiB

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48" version="1.1" viewBox="0 0 48 48"><title>illustration/comments</title><g id="illustration/comments" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><path id="Path" fill="#96D07C" d="M2.52730803,17.9196415 C2.44329744,17.9745167 2.36370847,18.000488 2.29303375,18.000488 C2.1197031,18.000488 2,17.8443588 2,17.5752855 L2,4 C2,1.790861 3.790861,3.23296945e-13 6,3.23296945e-13 L33.9995117,3.23296945e-13 C36.2086507,3.23296945e-13 37.9995117,1.790861 37.9995117,4 L37.9995117,9.999512 C37.9995117,12.208651 36.2086507,13.999512 33.9995117,13.999512 L8,13.999512 C7.83499225,13.999512 7.6723181,13.9895206 7.51254954,13.9701099 L2.52730803,17.9196415 Z"/><path id="Path" fill="#73E1E0" d="M7.51066,44.9703679 L2.52730803,47.9186655 C2.44329744,47.9735407 2.36370847,47.999512 2.29303375,47.999512 C2.1197031,47.999512 2,47.8433828 2,47.5743095 L2,35 C2,32.790861 3.790861,31 6,31 L26,31 C28.209139,31 30,32.790861 30,35 L30,41 C30,43.209139 28.209139,45 26,45 L8,45 C7.8343417,45 7.67103544,44.9899297 7.51066,44.9703679 Z"/><path id="Path" fill="#FFD476" d="M46,19.5 L46,33.0747975 C46,33.3438708 45.8802969,33.5 45.7069663,33.5 C45.6362915,33.5 45.5567026,33.4740287 45.472692,33.4191535 L40.4887103,29.4704446 C40.3285371,29.489956 40.1654415,29.5 40,29.5 L18,29.5 C15.790861,29.5 14,27.709139 14,25.5 L14,19.5 C14,17.290861 15.790861,15.5 18,15.5 L42,15.5 C44.209139,15.5 46,17.290861 46,19.5 Z"/></g></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48" version="1.1" viewBox="0 0 48 48"><title>illustration/direction</title><g id="illustration/direction" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><path id="Combined-Shape" fill="#FFD476" d="M23.4917015,33.6030641 L2.93840258,31.4321033 C2.38917316,31.3740904 1.99096346,30.8818233 2.04897631,30.3325939 C2.0747515,30.0885705 2.18934861,29.8625419 2.37095722,29.6975265 L34.2609105,0.721285325 C34.6696614,0.349881049 35.3021022,0.38015648 35.6735064,0.788907393 C35.9232621,1.06377731 36.0001133,1.45442096 35.8730901,1.80341447 L24.5364357,32.9506164 C24.3793473,33.3822133 23.9484565,33.6513092 23.4917015,33.6030641 L23.4917015,33.6030641 Z"/><path id="Combined-Shape-Copy" fill="#FFC445" d="M24.3163597,33.2881029 C24.0306575,33.0138462 23.9337246,32.5968232 24.069176,32.2246735 L35.091923,1.9399251 C35.2266075,1.56988243 35.5659249,1.31333613 35.9586669,1.28460955 C36.5094802,1.24432106 36.9886628,1.65818318 37.0289513,2.20899647 L40.2437557,46.1609256 C40.2644355,46.4436546 40.1641446,46.7218752 39.9678293,46.9263833 C39.5853672,47.3248067 38.9523344,47.3377458 38.5539111,46.9552837 L24.3163597,33.2881029 L24.3163597,33.2881029 Z"/></g></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48" version="1.1" viewBox="0 0 48 48"><title>illustration/flow</title><g id="illustration/flow" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><path id="Combined-Shape" fill="#79C9FC" fill-rule="nonzero" d="M30,29 C32.7614237,29 35,26.7614237 35,24 C35,14.6111593 27.3888407,7 18,7 C8.61115925,7 1,14.6111593 1,24 C1,33.3888407 8.61115925,41 18,41 C19.3333404,41 20.6447683,40.8466238 21.9154603,40.5471706 C19.5096374,39.3319645 17.5510566,37.8612875 16.0456579,36.1314815 C14.1063138,33.9030427 12.769443,31.0725999 12.0293806,27.6556449 C11.360469,26.565281 11,25.3082308 11,24 C11,20.1340068 14.1340068,17 18,17 C21.8659932,17 25,20.1340068 25,24 C25,26.125 27.7040312,29 30,29 Z"/><path id="Combined-Shape-Copy" fill="#FFC445" fill-rule="nonzero" d="M42,29 C44.7614237,29 47,26.7614237 47,24 C47,14.6111593 39.3888407,7 30,7 C20.6111593,7 13,14.6111593 13,24 C13,33.3888407 20.6111593,41 30,41 C31.3333404,41 32.6447683,40.8466238 33.9154603,40.5471706 C31.5096374,39.3319645 29.4051056,37.9781963 28.0456579,36.1314815 C26.0625,33.4375 23,27.1875 23,24 C23,20.1340068 26.1340068,17 30,17 C33.8659932,17 37,20.1340068 37,24 C37.02301,26.3435241 39.7040312,29 42,29 Z" transform="translate(30.000000, 24.000000) scale(-1, -1) translate(-30.000000, -24.000000)"/></g></svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48" version="1.1" viewBox="0 0 48 48"><title>illustration/plugin</title><g id="illustration/plugin" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><path id="Combined-Shape" fill="#79C9FC" d="M26,15.3994248 C26,15.4091303 26,15.4188459 26,15.4285714 L26,21.4694881 C25.8463595,21.4969567 25.6941676,21.51275 25.5873784,21.51275 C25.4974117,21.51275 25.4230979,21.4768034 25.377756,21.4206259 L25.2660784,21.2822603 L25.1317423,21.1657666 C24.2436317,20.3956144 23.100098,19.9633214 21.895551,19.9633214 C19.2039137,19.9633214 17,22.1075558 17,24.7804643 C17,27.4533728 19.2039137,29.5976071 21.895551,29.5976071 C23.1972122,29.5976071 24.3149423,29.2878193 25.1231445,28.3613697 C25.4542273,27.9818463 25.568273,27.9073214 25.5873784,27.9073214 C25.681532,27.9073214 25.8352452,27.9239643 26,27.9524591 L26,32.5714286 C26,32.5811541 26,32.5908697 26,32.6005752 L26,33 C26,35.209139 24.209139,37 22,37 L4,37 C1.790861,37 0,35.209139 0,33 L0,15 C0,12.790861 1.790861,11 4,11 L22,11 C24.209139,11 26,12.790861 26,15 L26,15.3994248 Z"/><path id="Path" fill="#87E6E5" d="M27.9998779,32.5714286 C27.9998779,33.3604068 28.6572726,34 29.4682101,34 L46.5315458,34 C47.3424832,34 47.9998779,33.3604068 47.9998779,32.5714286 L47.9998779,15.4285714 C47.9998779,14.6395932 47.3424832,14 46.5315458,14 L29.4682101,14 C28.6572726,14 27.9998779,14.6395932 27.9998779,15.4285714 L27.9998779,21.8355216 C27.9334367,22.2650514 27.8567585,22.6454496 27.746391,22.8084643 C27.4245309,23.2838571 26.2402709,23.51275 25.5873784,23.51275 C24.8705773,23.51275 24.2322714,23.1857725 23.8214379,22.6767605 C23.3096996,22.2329909 22.6349941,21.9633214 21.895551,21.9633214 C20.2963823,21.9633214 19,23.2245992 19,24.7804643 C19,26.3363293 20.2963823,27.5976071 21.895551,27.5976071 C22.5398535,27.5976071 23.2399343,27.477727 23.6160247,27.0466112 C24.1396029,26.4464286 24.7367044,25.9073214 25.5873784,25.9073214 C26.2402709,25.9073214 27.5912951,26.1766031 27.8226692,26.6116071 C27.8819199,26.7230038 27.9403239,26.921677 27.9998779,27.1556219 L27.9998779,32.5714286 Z"/></g></svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

Some files were not shown because too many files have changed in this diff Show More