Compare commits
21 Commits
better-ver
...
policies/o
Author | SHA1 | Date | |
---|---|---|---|
6e1cf8a23c | |||
fde6120e67 | |||
dabd812071 | |||
db92da4cb8 | |||
81c23fff98 | |||
54b5774a15 | |||
ba4650a088 | |||
b7d2c5188b | |||
dd8e71df7d | |||
baa4deda99 | |||
b7417e77c7 | |||
a01bb551d0 | |||
9a03bdeaf1 | |||
6b530ff764 | |||
e77a3241f0 | |||
a718ff2617 | |||
969fa82b7f | |||
bb47a70310 | |||
a306cecb73 | |||
760879c3db | |||
ef5d3580e8 |
@ -5,6 +5,7 @@ dist/**
|
||||
build/**
|
||||
build_docs/**
|
||||
*Dockerfile
|
||||
**/*Dockerfile
|
||||
blueprints/local
|
||||
.git
|
||||
!gen-ts-api/node_modules
|
||||
|
62
.github/workflows/ci-website.yml
vendored
62
.github/workflows/ci-website.yml
vendored
@ -41,32 +41,60 @@ jobs:
|
||||
- name: test
|
||||
working-directory: website/
|
||||
run: npm test
|
||||
build:
|
||||
build-container:
|
||||
runs-on: ubuntu-latest
|
||||
name: ${{ matrix.job }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
job:
|
||||
- build
|
||||
permissions:
|
||||
# Needed to upload container images to ghcr.io
|
||||
packages: write
|
||||
# Needed for attestation
|
||||
id-token: write
|
||||
attestations: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: website/package.json
|
||||
cache: "npm"
|
||||
cache-dependency-path: website/package-lock.json
|
||||
- working-directory: website/
|
||||
run: npm ci
|
||||
- name: build
|
||||
working-directory: website/
|
||||
run: npm run ${{ matrix.job }}
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3.6.0
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: prepare variables
|
||||
uses: ./.github/actions/docker-push-variables
|
||||
id: ev
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
with:
|
||||
image-name: ghcr.io/goauthentik/dev-docs
|
||||
- name: Login to Container Registry
|
||||
if: ${{ steps.ev.outputs.shouldPush == 'true' }}
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build Docker Image
|
||||
id: push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
tags: ${{ steps.ev.outputs.imageTags }}
|
||||
file: website/Dockerfile
|
||||
push: ${{ steps.ev.outputs.shouldPush == 'true' }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
context: .
|
||||
cache-from: type=registry,ref=ghcr.io/goauthentik/dev-docs:buildcache
|
||||
cache-to: ${{ steps.ev.outputs.shouldPush == 'true' && 'type=registry,ref=ghcr.io/goauthentik/dev-docs:buildcache,mode=max' || '' }}
|
||||
- uses: actions/attest-build-provenance@v2
|
||||
id: attest
|
||||
if: ${{ steps.ev.outputs.shouldPush == 'true' }}
|
||||
with:
|
||||
subject-name: ${{ steps.ev.outputs.attestImageNames }}
|
||||
subject-digest: ${{ steps.push.outputs.digest }}
|
||||
push-to-registry: true
|
||||
ci-website-mark:
|
||||
if: always()
|
||||
needs:
|
||||
- lint
|
||||
- test
|
||||
- build
|
||||
- build-container
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: re-actors/alls-green@release/v1
|
||||
|
45
.github/workflows/release-publish.yml
vendored
45
.github/workflows/release-publish.yml
vendored
@ -20,6 +20,49 @@ jobs:
|
||||
release: true
|
||||
registry_dockerhub: true
|
||||
registry_ghcr: true
|
||||
build-docs:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# Needed to upload container images to ghcr.io
|
||||
packages: write
|
||||
# Needed for attestation
|
||||
id-token: write
|
||||
attestations: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3.6.0
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: prepare variables
|
||||
uses: ./.github/actions/docker-push-variables
|
||||
id: ev
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
with:
|
||||
image-name: ghcr.io/goauthentik/docs
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build Docker Image
|
||||
id: push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
tags: ${{ steps.ev.outputs.imageTags }}
|
||||
file: website/Dockerfile
|
||||
push: true
|
||||
platforms: linux/amd64,linux/arm64
|
||||
context: .
|
||||
- uses: actions/attest-build-provenance@v2
|
||||
id: attest
|
||||
if: true
|
||||
with:
|
||||
subject-name: ${{ steps.ev.outputs.attestImageNames }}
|
||||
subject-digest: ${{ steps.push.outputs.digest }}
|
||||
push-to-registry: true
|
||||
build-outpost:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
@ -193,6 +236,6 @@ jobs:
|
||||
SENTRY_ORG: authentik-security-inc
|
||||
SENTRY_PROJECT: authentik
|
||||
with:
|
||||
version: authentik@${{ steps.ev.outputs.version }}
|
||||
release: authentik@${{ steps.ev.outputs.version }}
|
||||
sourcemaps: "./web/dist"
|
||||
url_prefix: "~/static/dist"
|
||||
|
46
Dockerfile
46
Dockerfile
@ -1,26 +1,7 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Stage 1: Build website
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/node:24 AS website-builder
|
||||
|
||||
ENV NODE_ENV=production
|
||||
|
||||
WORKDIR /work/website
|
||||
|
||||
RUN --mount=type=bind,target=/work/website/package.json,src=./website/package.json \
|
||||
--mount=type=bind,target=/work/website/package-lock.json,src=./website/package-lock.json \
|
||||
--mount=type=cache,id=npm-website,sharing=shared,target=/root/.npm \
|
||||
npm ci --include=dev
|
||||
|
||||
COPY ./website /work/website/
|
||||
COPY ./blueprints /work/blueprints/
|
||||
COPY ./schema.yml /work/
|
||||
COPY ./SECURITY.md /work/
|
||||
|
||||
RUN npm run build-bundled
|
||||
|
||||
# Stage 2: Build webui
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/node:24 AS web-builder
|
||||
# Stage 1: Build webui
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/node:24-slim AS node-builder
|
||||
|
||||
ARG GIT_BUILD_HASH
|
||||
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
|
||||
@ -32,7 +13,7 @@ RUN --mount=type=bind,target=/work/web/package.json,src=./web/package.json \
|
||||
--mount=type=bind,target=/work/web/package-lock.json,src=./web/package-lock.json \
|
||||
--mount=type=bind,target=/work/web/packages/sfe/package.json,src=./web/packages/sfe/package.json \
|
||||
--mount=type=bind,target=/work/web/scripts,src=./web/scripts \
|
||||
--mount=type=cache,id=npm-web,sharing=shared,target=/root/.npm \
|
||||
--mount=type=cache,id=npm-ak,sharing=shared,target=/root/.npm \
|
||||
npm ci --include=dev
|
||||
|
||||
COPY ./package.json /work
|
||||
@ -43,7 +24,7 @@ COPY ./gen-ts-api /work/web/node_modules/@goauthentik/api
|
||||
RUN npm run build && \
|
||||
npm run build:sfe
|
||||
|
||||
# Stage 3: Build go proxy
|
||||
# Stage 2: Build go proxy
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS go-builder
|
||||
|
||||
ARG TARGETOS
|
||||
@ -68,8 +49,8 @@ RUN --mount=type=bind,target=/go/src/goauthentik.io/go.mod,src=./go.mod \
|
||||
COPY ./cmd /go/src/goauthentik.io/cmd
|
||||
COPY ./authentik/lib /go/src/goauthentik.io/authentik/lib
|
||||
COPY ./web/static.go /go/src/goauthentik.io/web/static.go
|
||||
COPY --from=web-builder /work/web/robots.txt /go/src/goauthentik.io/web/robots.txt
|
||||
COPY --from=web-builder /work/web/security.txt /go/src/goauthentik.io/web/security.txt
|
||||
COPY --from=node-builder /work/web/robots.txt /go/src/goauthentik.io/web/robots.txt
|
||||
COPY --from=node-builder /work/web/security.txt /go/src/goauthentik.io/web/security.txt
|
||||
COPY ./internal /go/src/goauthentik.io/internal
|
||||
COPY ./go.mod /go/src/goauthentik.io/go.mod
|
||||
COPY ./go.sum /go/src/goauthentik.io/go.sum
|
||||
@ -80,7 +61,7 @@ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
|
||||
CGO_ENABLED=1 GOFIPS140=latest GOARM="${TARGETVARIANT#v}" \
|
||||
go build -o /go/authentik ./cmd/server
|
||||
|
||||
# Stage 4: MaxMind GeoIP
|
||||
# Stage 3: MaxMind GeoIP
|
||||
FROM --platform=${BUILDPLATFORM} ghcr.io/maxmind/geoipupdate:v7.1.0 AS geoip
|
||||
|
||||
ENV GEOIPUPDATE_EDITION_IDS="GeoLite2-City GeoLite2-ASN"
|
||||
@ -93,9 +74,9 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
|
||||
mkdir -p /usr/share/GeoIP && \
|
||||
/bin/sh -c "GEOIPUPDATE_LICENSE_KEY_FILE=/run/secrets/GEOIPUPDATE_LICENSE_KEY /usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
|
||||
|
||||
# Stage 5: Download uv
|
||||
# Stage 4: Download uv
|
||||
FROM ghcr.io/astral-sh/uv:0.7.11 AS uv
|
||||
# Stage 6: Base python image
|
||||
# Stage 5: Base python image
|
||||
FROM ghcr.io/goauthentik/fips-python:3.13.3-slim-bookworm-fips AS python-base
|
||||
|
||||
ENV VENV_PATH="/ak-root/.venv" \
|
||||
@ -109,7 +90,7 @@ WORKDIR /ak-root/
|
||||
|
||||
COPY --from=uv /uv /uvx /bin/
|
||||
|
||||
# Stage 7: Python dependencies
|
||||
# Stage 6: Python dependencies
|
||||
FROM python-base AS python-deps
|
||||
|
||||
ARG TARGETARCH
|
||||
@ -144,7 +125,7 @@ RUN --mount=type=bind,target=pyproject.toml,src=pyproject.toml \
|
||||
--mount=type=cache,target=/root/.cache/uv \
|
||||
uv sync --frozen --no-install-project --no-dev
|
||||
|
||||
# Stage 8: Run
|
||||
# Stage 7: Run
|
||||
FROM python-base AS final-image
|
||||
|
||||
ARG VERSION
|
||||
@ -187,9 +168,8 @@ COPY ./lifecycle/ /lifecycle
|
||||
COPY ./authentik/sources/kerberos/krb5.conf /etc/krb5.conf
|
||||
COPY --from=go-builder /go/authentik /bin/authentik
|
||||
COPY --from=python-deps /ak-root/.venv /ak-root/.venv
|
||||
COPY --from=web-builder /work/web/dist/ /web/dist/
|
||||
COPY --from=web-builder /work/web/authentik/ /web/authentik/
|
||||
COPY --from=website-builder /work/website/build/ /website/help/
|
||||
COPY --from=node-builder /work/web/dist/ /web/dist/
|
||||
COPY --from=node-builder /work/web/authentik/ /web/authentik/
|
||||
COPY --from=geoip /usr/share/GeoIP /geoip
|
||||
|
||||
USER 1000
|
||||
|
@ -153,10 +153,10 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet):
|
||||
return applications
|
||||
|
||||
def _filter_applications_with_launch_url(
|
||||
self, pagined_apps: Iterator[Application]
|
||||
self, paginated_apps: Iterator[Application]
|
||||
) -> list[Application]:
|
||||
applications = []
|
||||
for app in pagined_apps:
|
||||
for app in paginated_apps:
|
||||
if app.get_launch_url():
|
||||
applications.append(app)
|
||||
return applications
|
||||
|
@ -10,7 +10,7 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<ak-message-container></ak-message-container>
|
||||
<ak-message-container alignment="bottom"></ak-message-container>
|
||||
<ak-interface-admin>
|
||||
<ak-loading></ak-loading>
|
||||
</ak-interface-admin>
|
||||
|
@ -1,11 +1,11 @@
|
||||
"""authentik policy engine"""
|
||||
|
||||
from collections.abc import Iterator
|
||||
from collections.abc import Iterable
|
||||
from multiprocessing import Pipe, current_process
|
||||
from multiprocessing.connection import Connection
|
||||
from time import perf_counter
|
||||
|
||||
from django.core.cache import cache
|
||||
from django.db.models import Count, Q, QuerySet
|
||||
from django.http import HttpRequest
|
||||
from sentry_sdk import start_span
|
||||
from sentry_sdk.tracing import Span
|
||||
@ -67,14 +67,11 @@ class PolicyEngine:
|
||||
self.__processes: list[PolicyProcessInfo] = []
|
||||
self.use_cache = True
|
||||
self.__expected_result_count = 0
|
||||
self.__static_result: PolicyResult | None = None
|
||||
|
||||
def iterate_bindings(self) -> Iterator[PolicyBinding]:
|
||||
def bindings(self) -> QuerySet[PolicyBinding] | Iterable[PolicyBinding]:
|
||||
"""Make sure all Policies are their respective classes"""
|
||||
return (
|
||||
PolicyBinding.objects.filter(target=self.__pbm, enabled=True)
|
||||
.order_by("order")
|
||||
.iterator()
|
||||
)
|
||||
return PolicyBinding.objects.filter(target=self.__pbm, enabled=True).order_by("order")
|
||||
|
||||
def _check_policy_type(self, binding: PolicyBinding):
|
||||
"""Check policy type, make sure it's not the root class as that has no logic implemented"""
|
||||
@ -84,30 +81,66 @@ class PolicyEngine:
|
||||
def _check_cache(self, binding: PolicyBinding):
|
||||
if not self.use_cache:
|
||||
return False
|
||||
before = perf_counter()
|
||||
key = cache_key(binding, self.request)
|
||||
cached_policy = cache.get(key, None)
|
||||
duration = max(perf_counter() - before, 0)
|
||||
if not cached_policy:
|
||||
return False
|
||||
self.logger.debug(
|
||||
"P_ENG: Taking result from cache",
|
||||
binding=binding,
|
||||
cache_key=key,
|
||||
request=self.request,
|
||||
)
|
||||
HIST_POLICIES_EXECUTION_TIME.labels(
|
||||
# It's a bit silly to time this, but
|
||||
with HIST_POLICIES_EXECUTION_TIME.labels(
|
||||
binding_order=binding.order,
|
||||
binding_target_type=binding.target_type,
|
||||
binding_target_name=binding.target_name,
|
||||
object_pk=str(self.request.obj.pk),
|
||||
object_type=class_to_path(self.request.obj.__class__),
|
||||
mode="cache_retrieve",
|
||||
).observe(duration)
|
||||
# It's a bit silly to time this, but
|
||||
).time():
|
||||
key = cache_key(binding, self.request)
|
||||
cached_policy = cache.get(key, None)
|
||||
if not cached_policy:
|
||||
return False
|
||||
self.logger.debug(
|
||||
"P_ENG: Taking result from cache",
|
||||
binding=binding,
|
||||
cache_key=key,
|
||||
request=self.request,
|
||||
)
|
||||
self.__cached_policies.append(cached_policy)
|
||||
return True
|
||||
|
||||
def compute_static_bindings(self, bindings: QuerySet[PolicyBinding]):
|
||||
"""Check static bindings if possible"""
|
||||
aggrs = {
|
||||
"total": Count(
|
||||
"pk", filter=Q(Q(group__isnull=False) | Q(user__isnull=False), policy=None)
|
||||
),
|
||||
}
|
||||
if self.request.user.pk:
|
||||
all_groups = self.request.user.all_groups()
|
||||
aggrs["passing"] = Count(
|
||||
"pk",
|
||||
filter=Q(
|
||||
Q(
|
||||
Q(user=self.request.user) | Q(group__in=all_groups),
|
||||
negate=False,
|
||||
)
|
||||
| Q(
|
||||
Q(~Q(user=self.request.user), user__isnull=False)
|
||||
| Q(~Q(group__in=all_groups), group__isnull=False),
|
||||
negate=True,
|
||||
),
|
||||
enabled=True,
|
||||
),
|
||||
)
|
||||
matched_bindings = bindings.aggregate(**aggrs)
|
||||
passing = False
|
||||
if matched_bindings["total"] == 0 and matched_bindings.get("passing", 0) == 0:
|
||||
# If we didn't find any static bindings, do nothing
|
||||
return
|
||||
self.logger.debug("P_ENG: Found static bindings", **matched_bindings)
|
||||
if matched_bindings.get("passing", 0) > 0:
|
||||
# Any passing static binding -> passing
|
||||
passing = True
|
||||
elif matched_bindings["total"] > 0 and matched_bindings.get("passing", 0) < 1:
|
||||
# No matching static bindings but at least one is configured -> not passing
|
||||
passing = False
|
||||
self.__static_result = PolicyResult(passing)
|
||||
|
||||
def build(self) -> "PolicyEngine":
|
||||
"""Build wrapper which monitors performance"""
|
||||
with (
|
||||
@ -123,7 +156,12 @@ class PolicyEngine:
|
||||
span: Span
|
||||
span.set_data("pbm", self.__pbm)
|
||||
span.set_data("request", self.request)
|
||||
for binding in self.iterate_bindings():
|
||||
bindings = self.bindings()
|
||||
policy_bindings = bindings
|
||||
if isinstance(bindings, QuerySet):
|
||||
self.compute_static_bindings(bindings)
|
||||
policy_bindings = [x for x in bindings if x.policy]
|
||||
for binding in policy_bindings:
|
||||
self.__expected_result_count += 1
|
||||
|
||||
self._check_policy_type(binding)
|
||||
@ -153,10 +191,13 @@ class PolicyEngine:
|
||||
@property
|
||||
def result(self) -> PolicyResult:
|
||||
"""Get policy-checking result"""
|
||||
self.__processes.sort(key=lambda x: x.binding.order)
|
||||
process_results: list[PolicyResult] = [x.result for x in self.__processes if x.result]
|
||||
all_results = list(process_results + self.__cached_policies)
|
||||
if len(all_results) < self.__expected_result_count: # pragma: no cover
|
||||
raise AssertionError("Got less results than polices")
|
||||
if self.__static_result:
|
||||
all_results.append(self.__static_result)
|
||||
# No results, no policies attached -> passing
|
||||
if len(all_results) == 0:
|
||||
return PolicyResult(self.empty_result)
|
||||
|
@ -1,9 +1,12 @@
|
||||
"""policy engine tests"""
|
||||
|
||||
from django.core.cache import cache
|
||||
from django.db import connections
|
||||
from django.test import TestCase
|
||||
from django.test.utils import CaptureQueriesContext
|
||||
|
||||
from authentik.core.tests.utils import create_test_admin_user
|
||||
from authentik.core.models import Group
|
||||
from authentik.core.tests.utils import create_test_user
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.policies.dummy.models import DummyPolicy
|
||||
from authentik.policies.engine import PolicyEngine
|
||||
@ -19,7 +22,7 @@ class TestPolicyEngine(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
clear_policy_cache()
|
||||
self.user = create_test_admin_user()
|
||||
self.user = create_test_user()
|
||||
self.policy_false = DummyPolicy.objects.create(
|
||||
name=generate_id(), result=False, wait_min=0, wait_max=1
|
||||
)
|
||||
@ -127,3 +130,43 @@ class TestPolicyEngine(TestCase):
|
||||
self.assertEqual(len(cache.keys(f"{CACHE_PREFIX}{binding.policy_binding_uuid.hex}*")), 1)
|
||||
self.assertEqual(engine.build().passing, False)
|
||||
self.assertEqual(len(cache.keys(f"{CACHE_PREFIX}{binding.policy_binding_uuid.hex}*")), 1)
|
||||
|
||||
def test_engine_static_bindings(self):
|
||||
"""Test static bindings"""
|
||||
group_a = Group.objects.create(name=generate_id())
|
||||
group_b = Group.objects.create(name=generate_id())
|
||||
group_b.users.add(self.user)
|
||||
user = create_test_user()
|
||||
|
||||
for case in [
|
||||
{
|
||||
"message": "Group, not member",
|
||||
"binding_args": {"group": group_a},
|
||||
"passing": False,
|
||||
},
|
||||
{
|
||||
"message": "Group, member",
|
||||
"binding_args": {"group": group_b},
|
||||
"passing": True,
|
||||
},
|
||||
{
|
||||
"message": "User, other",
|
||||
"binding_args": {"user": user},
|
||||
"passing": False,
|
||||
},
|
||||
{
|
||||
"message": "User, same",
|
||||
"binding_args": {"user": self.user},
|
||||
"passing": True,
|
||||
},
|
||||
]:
|
||||
with self.subTest():
|
||||
pbm = PolicyBindingModel.objects.create()
|
||||
for x in range(1000):
|
||||
PolicyBinding.objects.create(target=pbm, order=x, **case["binding_args"])
|
||||
engine = PolicyEngine(pbm, self.user)
|
||||
engine.use_cache = False
|
||||
with CaptureQueriesContext(connections["default"]) as ctx:
|
||||
engine.build()
|
||||
self.assertLess(ctx.final_queries, 1000)
|
||||
self.assertEqual(engine.result.passing, case["passing"])
|
||||
|
@ -29,13 +29,12 @@ class TestPolicyProcess(TestCase):
|
||||
def setUp(self):
|
||||
clear_policy_cache()
|
||||
self.factory = RequestFactory()
|
||||
self.user = User.objects.create_user(username="policyuser")
|
||||
self.user = User.objects.create_user(username=generate_id())
|
||||
|
||||
def test_group_passing(self):
|
||||
"""Test binding to group"""
|
||||
group = Group.objects.create(name="test-group")
|
||||
group = Group.objects.create(name=generate_id())
|
||||
group.users.add(self.user)
|
||||
group.save()
|
||||
binding = PolicyBinding(group=group)
|
||||
|
||||
request = PolicyRequest(self.user)
|
||||
@ -44,8 +43,7 @@ class TestPolicyProcess(TestCase):
|
||||
|
||||
def test_group_negative(self):
|
||||
"""Test binding to group"""
|
||||
group = Group.objects.create(name="test-group")
|
||||
group.save()
|
||||
group = Group.objects.create(name=generate_id())
|
||||
binding = PolicyBinding(group=group)
|
||||
|
||||
request = PolicyRequest(self.user)
|
||||
@ -115,8 +113,10 @@ class TestPolicyProcess(TestCase):
|
||||
|
||||
def test_exception(self):
|
||||
"""Test policy execution"""
|
||||
policy = Policy.objects.create(name="test-execution")
|
||||
binding = PolicyBinding(policy=policy, target=Application.objects.create(name="test"))
|
||||
policy = Policy.objects.create(name=generate_id())
|
||||
binding = PolicyBinding(
|
||||
policy=policy, target=Application.objects.create(name=generate_id())
|
||||
)
|
||||
|
||||
request = PolicyRequest(self.user)
|
||||
response = PolicyProcess(binding, request, None).execute()
|
||||
@ -125,13 +125,15 @@ class TestPolicyProcess(TestCase):
|
||||
def test_execution_logging(self):
|
||||
"""Test policy execution creates event"""
|
||||
policy = DummyPolicy.objects.create(
|
||||
name="test-execution-logging",
|
||||
name=generate_id(),
|
||||
result=False,
|
||||
wait_min=0,
|
||||
wait_max=1,
|
||||
execution_logging=True,
|
||||
)
|
||||
binding = PolicyBinding(policy=policy, target=Application.objects.create(name="test"))
|
||||
binding = PolicyBinding(
|
||||
policy=policy, target=Application.objects.create(name=generate_id())
|
||||
)
|
||||
|
||||
http_request = self.factory.get(reverse("authentik_api:user-impersonate-end"))
|
||||
http_request.user = self.user
|
||||
@ -186,13 +188,15 @@ class TestPolicyProcess(TestCase):
|
||||
def test_execution_logging_anonymous(self):
|
||||
"""Test policy execution creates event with anonymous user"""
|
||||
policy = DummyPolicy.objects.create(
|
||||
name="test-execution-logging-anon",
|
||||
name=generate_id(),
|
||||
result=False,
|
||||
wait_min=0,
|
||||
wait_max=1,
|
||||
execution_logging=True,
|
||||
)
|
||||
binding = PolicyBinding(policy=policy, target=Application.objects.create(name="test"))
|
||||
binding = PolicyBinding(
|
||||
policy=policy, target=Application.objects.create(name=generate_id())
|
||||
)
|
||||
|
||||
user = AnonymousUser()
|
||||
|
||||
@ -219,9 +223,9 @@ class TestPolicyProcess(TestCase):
|
||||
|
||||
def test_raises(self):
|
||||
"""Test policy that raises error"""
|
||||
policy_raises = ExpressionPolicy.objects.create(name="raises", expression="{{ 0/0 }}")
|
||||
policy_raises = ExpressionPolicy.objects.create(name=generate_id(), expression="{{ 0/0 }}")
|
||||
binding = PolicyBinding(
|
||||
policy=policy_raises, target=Application.objects.create(name="test")
|
||||
policy=policy_raises, target=Application.objects.create(name=generate_id())
|
||||
)
|
||||
|
||||
request = PolicyRequest(self.user)
|
||||
|
@ -1,6 +1,6 @@
|
||||
"""Prompt Stage Logic"""
|
||||
|
||||
from collections.abc import Callable, Iterator
|
||||
from collections.abc import Callable
|
||||
from email.policy import Policy
|
||||
from types import MethodType
|
||||
from typing import Any
|
||||
@ -190,7 +190,7 @@ class ListPolicyEngine(PolicyEngine):
|
||||
self.__list = policies
|
||||
self.use_cache = False
|
||||
|
||||
def iterate_bindings(self) -> Iterator[PolicyBinding]:
|
||||
def bindings(self):
|
||||
for policy in self.__list:
|
||||
yield PolicyBinding(
|
||||
policy=policy,
|
||||
|
@ -2,17 +2,13 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"goauthentik.io/internal/common"
|
||||
"goauthentik.io/internal/config"
|
||||
"goauthentik.io/internal/constants"
|
||||
"goauthentik.io/internal/debug"
|
||||
"goauthentik.io/internal/outpost/ak"
|
||||
"goauthentik.io/internal/outpost/ak/entrypoint"
|
||||
"goauthentik.io/internal/outpost/ak/healthcheck"
|
||||
"goauthentik.io/internal/outpost/ldap"
|
||||
)
|
||||
@ -25,65 +21,15 @@ Required environment variables:
|
||||
- AUTHENTIK_INSECURE: Skip SSL Certificate verification`
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Long: helpMessage,
|
||||
Version: constants.FullVersion(),
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
log.SetFormatter(&log.JSONFormatter{
|
||||
FieldMap: log.FieldMap{
|
||||
log.FieldKeyMsg: "event",
|
||||
log.FieldKeyTime: "timestamp",
|
||||
},
|
||||
DisableHTMLEscape: true,
|
||||
})
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
debug.EnableDebugServer()
|
||||
akURL := config.Get().AuthentikHost
|
||||
if akURL == "" {
|
||||
fmt.Println("env AUTHENTIK_HOST not set!")
|
||||
fmt.Println(helpMessage)
|
||||
os.Exit(1)
|
||||
}
|
||||
akToken := config.Get().AuthentikToken
|
||||
if akToken == "" {
|
||||
fmt.Println("env AUTHENTIK_TOKEN not set!")
|
||||
fmt.Println(helpMessage)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
akURLActual, err := url.Parse(akURL)
|
||||
Long: helpMessage,
|
||||
Version: constants.FullVersion(),
|
||||
PersistentPreRun: common.PreRun,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := entrypoint.OutpostMain("authentik.outpost.ldap", ldap.NewServer)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Println(helpMessage)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
ex := common.Init()
|
||||
defer common.Defer()
|
||||
go func() {
|
||||
for {
|
||||
<-ex
|
||||
os.Exit(0)
|
||||
}
|
||||
}()
|
||||
|
||||
ac := ak.NewAPIController(*akURLActual, akToken)
|
||||
if ac == nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
defer ac.Shutdown()
|
||||
|
||||
ac.Server = ldap.NewServer(ac)
|
||||
|
||||
err = ac.Start()
|
||||
if err != nil {
|
||||
log.WithError(err).Panic("Failed to run server")
|
||||
}
|
||||
|
||||
for {
|
||||
<-ex
|
||||
}
|
||||
return err
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -2,17 +2,13 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"goauthentik.io/internal/common"
|
||||
"goauthentik.io/internal/config"
|
||||
"goauthentik.io/internal/constants"
|
||||
"goauthentik.io/internal/debug"
|
||||
"goauthentik.io/internal/outpost/ak"
|
||||
"goauthentik.io/internal/outpost/ak/entrypoint"
|
||||
"goauthentik.io/internal/outpost/ak/healthcheck"
|
||||
"goauthentik.io/internal/outpost/proxyv2"
|
||||
)
|
||||
@ -28,65 +24,15 @@ Optionally, you can set these:
|
||||
- AUTHENTIK_HOST_BROWSER: URL to use in the browser, when it differs from AUTHENTIK_HOST`
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Long: helpMessage,
|
||||
Version: constants.FullVersion(),
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
log.SetFormatter(&log.JSONFormatter{
|
||||
FieldMap: log.FieldMap{
|
||||
log.FieldKeyMsg: "event",
|
||||
log.FieldKeyTime: "timestamp",
|
||||
},
|
||||
DisableHTMLEscape: true,
|
||||
})
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
debug.EnableDebugServer()
|
||||
akURL := config.Get().AuthentikHost
|
||||
if akURL == "" {
|
||||
fmt.Println("env AUTHENTIK_HOST not set!")
|
||||
fmt.Println(helpMessage)
|
||||
os.Exit(1)
|
||||
}
|
||||
akToken := config.Get().AuthentikToken
|
||||
if akToken == "" {
|
||||
fmt.Println("env AUTHENTIK_TOKEN not set!")
|
||||
fmt.Println(helpMessage)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
akURLActual, err := url.Parse(akURL)
|
||||
Long: helpMessage,
|
||||
Version: constants.FullVersion(),
|
||||
PersistentPreRun: common.PreRun,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := entrypoint.OutpostMain("authentik.outpost.proxy", proxyv2.NewProxyServer)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Println(helpMessage)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
ex := common.Init()
|
||||
defer common.Defer()
|
||||
go func() {
|
||||
for {
|
||||
<-ex
|
||||
os.Exit(0)
|
||||
}
|
||||
}()
|
||||
|
||||
ac := ak.NewAPIController(*akURLActual, akToken)
|
||||
if ac == nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
defer ac.Shutdown()
|
||||
|
||||
ac.Server = proxyv2.NewProxyServer(ac)
|
||||
|
||||
err = ac.Start()
|
||||
if err != nil {
|
||||
log.WithError(err).Panic("Failed to run server")
|
||||
}
|
||||
|
||||
for {
|
||||
<-ex
|
||||
}
|
||||
return err
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -2,16 +2,13 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"goauthentik.io/internal/common"
|
||||
"goauthentik.io/internal/constants"
|
||||
"goauthentik.io/internal/debug"
|
||||
"goauthentik.io/internal/outpost/ak"
|
||||
"goauthentik.io/internal/outpost/ak/entrypoint"
|
||||
"goauthentik.io/internal/outpost/ak/healthcheck"
|
||||
"goauthentik.io/internal/outpost/rac"
|
||||
)
|
||||
@ -24,65 +21,15 @@ Required environment variables:
|
||||
- AUTHENTIK_INSECURE: Skip SSL Certificate verification`
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Long: helpMessage,
|
||||
Version: constants.FullVersion(),
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
log.SetFormatter(&log.JSONFormatter{
|
||||
FieldMap: log.FieldMap{
|
||||
log.FieldKeyMsg: "event",
|
||||
log.FieldKeyTime: "timestamp",
|
||||
},
|
||||
DisableHTMLEscape: true,
|
||||
})
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
debug.EnableDebugServer()
|
||||
akURL, found := os.LookupEnv("AUTHENTIK_HOST")
|
||||
if !found {
|
||||
fmt.Println("env AUTHENTIK_HOST not set!")
|
||||
fmt.Println(helpMessage)
|
||||
os.Exit(1)
|
||||
}
|
||||
akToken, found := os.LookupEnv("AUTHENTIK_TOKEN")
|
||||
if !found {
|
||||
fmt.Println("env AUTHENTIK_TOKEN not set!")
|
||||
fmt.Println(helpMessage)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
akURLActual, err := url.Parse(akURL)
|
||||
Long: helpMessage,
|
||||
Version: constants.FullVersion(),
|
||||
PersistentPreRun: common.PreRun,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := entrypoint.OutpostMain("authentik.outpost.rac", rac.NewServer)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Println(helpMessage)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
ex := common.Init()
|
||||
defer common.Defer()
|
||||
go func() {
|
||||
for {
|
||||
<-ex
|
||||
os.Exit(0)
|
||||
}
|
||||
}()
|
||||
|
||||
ac := ak.NewAPIController(*akURLActual, akToken)
|
||||
if ac == nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
defer ac.Shutdown()
|
||||
|
||||
ac.Server = rac.NewServer(ac)
|
||||
|
||||
err = ac.Start()
|
||||
if err != nil {
|
||||
log.WithError(err).Panic("Failed to run server")
|
||||
}
|
||||
|
||||
for {
|
||||
<-ex
|
||||
}
|
||||
return err
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -2,16 +2,13 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"goauthentik.io/internal/common"
|
||||
"goauthentik.io/internal/constants"
|
||||
"goauthentik.io/internal/debug"
|
||||
"goauthentik.io/internal/outpost/ak"
|
||||
"goauthentik.io/internal/outpost/ak/entrypoint"
|
||||
"goauthentik.io/internal/outpost/ak/healthcheck"
|
||||
"goauthentik.io/internal/outpost/radius"
|
||||
)
|
||||
@ -24,65 +21,15 @@ Required environment variables:
|
||||
- AUTHENTIK_INSECURE: Skip SSL Certificate verification`
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Long: helpMessage,
|
||||
Version: constants.FullVersion(),
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
log.SetFormatter(&log.JSONFormatter{
|
||||
FieldMap: log.FieldMap{
|
||||
log.FieldKeyMsg: "event",
|
||||
log.FieldKeyTime: "timestamp",
|
||||
},
|
||||
DisableHTMLEscape: true,
|
||||
})
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
debug.EnableDebugServer()
|
||||
akURL, found := os.LookupEnv("AUTHENTIK_HOST")
|
||||
if !found {
|
||||
fmt.Println("env AUTHENTIK_HOST not set!")
|
||||
fmt.Println(helpMessage)
|
||||
os.Exit(1)
|
||||
}
|
||||
akToken, found := os.LookupEnv("AUTHENTIK_TOKEN")
|
||||
if !found {
|
||||
fmt.Println("env AUTHENTIK_TOKEN not set!")
|
||||
fmt.Println(helpMessage)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
akURLActual, err := url.Parse(akURL)
|
||||
Long: helpMessage,
|
||||
Version: constants.FullVersion(),
|
||||
PersistentPreRun: common.PreRun,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := entrypoint.OutpostMain("authentik.outpost.radius", radius.NewServer)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Println(helpMessage)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
ex := common.Init()
|
||||
defer common.Defer()
|
||||
go func() {
|
||||
for {
|
||||
<-ex
|
||||
os.Exit(0)
|
||||
}
|
||||
}()
|
||||
|
||||
ac := ak.NewAPIController(*akURLActual, akToken)
|
||||
if ac == nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
defer ac.Shutdown()
|
||||
|
||||
ac.Server = radius.NewServer(ac)
|
||||
|
||||
err = ac.Start()
|
||||
if err != nil {
|
||||
log.WithError(err).Panic("Failed to run server")
|
||||
}
|
||||
|
||||
for {
|
||||
<-ex
|
||||
}
|
||||
return err
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -22,21 +22,12 @@ import (
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "authentik",
|
||||
Short: "Start authentik instance",
|
||||
Version: constants.FullVersion(),
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
log.SetFormatter(&log.JSONFormatter{
|
||||
FieldMap: log.FieldMap{
|
||||
log.FieldKeyMsg: "event",
|
||||
log.FieldKeyTime: "timestamp",
|
||||
},
|
||||
DisableHTMLEscape: true,
|
||||
})
|
||||
},
|
||||
Use: "authentik",
|
||||
Short: "Start authentik instance",
|
||||
Version: constants.FullVersion(),
|
||||
PersistentPreRun: common.PreRun,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
debug.EnableDebugServer()
|
||||
debug.EnableDebugServer("authentik.core")
|
||||
l := log.WithField("logger", "authentik.root")
|
||||
|
||||
if config.Get().ErrorReporting.Enabled {
|
||||
@ -99,7 +90,7 @@ func attemptProxyStart(ws *web.WebServer, u *url.URL) {
|
||||
})
|
||||
|
||||
srv := proxyv2.NewProxyServer(ac)
|
||||
ws.ProxyServer = srv
|
||||
ws.ProxyServer = srv.(*proxyv2.ProxyServer)
|
||||
ac.Server = srv
|
||||
l.Debug("attempting to start outpost")
|
||||
err := ac.StartBackgroundTasks()
|
||||
|
3
go.mod
3
go.mod
@ -16,6 +16,7 @@ require (
|
||||
github.com/gorilla/securecookie v1.1.2
|
||||
github.com/gorilla/sessions v1.4.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/grafana/pyroscope-go v1.2.2
|
||||
github.com/jellydator/ttlcache/v3 v3.3.0
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
|
||||
@ -58,8 +59,10 @@ require (
|
||||
github.com/go-openapi/strfmt v0.23.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-openapi/validate v0.24.0 // indirect
|
||||
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/oklog/ulid v1.3.1 // indirect
|
||||
|
6
go.sum
6
go.sum
@ -178,6 +178,10 @@ github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2e
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grafana/pyroscope-go v1.2.2 h1:uvKCyZMD724RkaCEMrSTC38Yn7AnFe8S2wiAIYdDPCE=
|
||||
github.com/grafana/pyroscope-go v1.2.2/go.mod h1:zzT9QXQAp2Iz2ZdS216UiV8y9uXJYQiGE1q8v1FyhqU=
|
||||
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg=
|
||||
github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
|
||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
@ -262,6 +266,8 @@ github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
|
17
internal/common/prerun.go
Normal file
17
internal/common/prerun.go
Normal file
@ -0,0 +1,17 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func PreRun(cmd *cobra.Command, args []string) {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
log.SetFormatter(&log.JSONFormatter{
|
||||
FieldMap: log.FieldMap{
|
||||
log.FieldKeyMsg: "event",
|
||||
log.FieldKeyTime: "timestamp",
|
||||
},
|
||||
DisableHTMLEscape: true,
|
||||
})
|
||||
}
|
@ -5,19 +5,24 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/pprof"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/grafana/pyroscope-go"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"goauthentik.io/internal/config"
|
||||
"goauthentik.io/internal/utils/web"
|
||||
)
|
||||
|
||||
func EnableDebugServer() {
|
||||
l := log.WithField("logger", "authentik.go_debugger")
|
||||
var l = log.WithField("logger", "authentik.debugger.go")
|
||||
|
||||
func EnableDebugServer(appName string) {
|
||||
if !config.Get().Debug {
|
||||
return
|
||||
}
|
||||
h := mux.NewRouter()
|
||||
enablePyroscope(appName)
|
||||
h.HandleFunc("/debug/pprof/", pprof.Index)
|
||||
h.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
|
||||
h.HandleFunc("/debug/pprof/profile", pprof.Profile)
|
||||
@ -54,3 +59,38 @@ func EnableDebugServer() {
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func enablePyroscope(appName string) {
|
||||
p, pok := os.LookupEnv("AUTHENTIK_PYROSCOPE_HOST")
|
||||
if !pok {
|
||||
return
|
||||
}
|
||||
l.Debug("Enabling pyroscope")
|
||||
runtime.SetMutexProfileFraction(5)
|
||||
runtime.SetBlockProfileRate(5)
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = pyroscope.Start(pyroscope.Config{
|
||||
ApplicationName: appName,
|
||||
ServerAddress: p,
|
||||
Logger: pyroscope.StandardLogger,
|
||||
Tags: map[string]string{"hostname": hostname},
|
||||
ProfileTypes: []pyroscope.ProfileType{
|
||||
pyroscope.ProfileCPU,
|
||||
pyroscope.ProfileAllocObjects,
|
||||
pyroscope.ProfileAllocSpace,
|
||||
pyroscope.ProfileInuseObjects,
|
||||
pyroscope.ProfileInuseSpace,
|
||||
pyroscope.ProfileGoroutines,
|
||||
pyroscope.ProfileMutexCount,
|
||||
pyroscope.ProfileMutexDuration,
|
||||
pyroscope.ProfileBlockCount,
|
||||
pyroscope.ProfileBlockDuration,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
@ -135,6 +135,10 @@ func NewAPIController(akURL url.URL, token string) *APIController {
|
||||
return ac
|
||||
}
|
||||
|
||||
func (a *APIController) Log() *log.Entry {
|
||||
return a.logger
|
||||
}
|
||||
|
||||
// Start Starts all handlers, non-blocking
|
||||
func (a *APIController) Start() error {
|
||||
err := a.Server.Refresh()
|
||||
|
51
internal/outpost/ak/entrypoint/entrypoint.go
Normal file
51
internal/outpost/ak/entrypoint/entrypoint.go
Normal file
@ -0,0 +1,51 @@
|
||||
package entrypoint
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"goauthentik.io/internal/common"
|
||||
"goauthentik.io/internal/config"
|
||||
"goauthentik.io/internal/debug"
|
||||
"goauthentik.io/internal/outpost/ak"
|
||||
)
|
||||
|
||||
func OutpostMain(appName string, server func(ac *ak.APIController) ak.Outpost) error {
|
||||
debug.EnableDebugServer(appName)
|
||||
akURL := config.Get().AuthentikHost
|
||||
if akURL == "" {
|
||||
return errors.New("environment variable `AUTHENTIK_HOST` not set")
|
||||
}
|
||||
akToken := config.Get().AuthentikToken
|
||||
if akToken == "" {
|
||||
return errors.New("environment variable `AUTHENTIK_TOKEN` not set")
|
||||
}
|
||||
|
||||
akURLActual, err := url.Parse(akURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ex := common.Init()
|
||||
defer common.Defer()
|
||||
|
||||
ac := ak.NewAPIController(*akURLActual, akToken)
|
||||
if ac == nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
defer ac.Shutdown()
|
||||
|
||||
ac.Server = server(ac)
|
||||
|
||||
err = ac.Start()
|
||||
if err != nil {
|
||||
ac.Log().WithError(err).Panic("Failed to run server")
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
<-ex
|
||||
return nil
|
||||
}
|
||||
}
|
@ -48,20 +48,20 @@ func doGlobalSetup(outpost api.Outpost, globalConfig *api.Config) {
|
||||
if globalConfig.ErrorReporting.Enabled {
|
||||
if !initialSetup {
|
||||
l.WithField("env", globalConfig.ErrorReporting.Environment).Debug("Error reporting enabled")
|
||||
}
|
||||
err := sentry.Init(sentry.ClientOptions{
|
||||
Dsn: globalConfig.ErrorReporting.SentryDsn,
|
||||
Environment: globalConfig.ErrorReporting.Environment,
|
||||
EnableTracing: true,
|
||||
TracesSampler: sentryutils.SamplerFunc(float64(globalConfig.ErrorReporting.TracesSampleRate)),
|
||||
Release: fmt.Sprintf("authentik@%s", constants.VERSION),
|
||||
HTTPTransport: webutils.NewUserAgentTransport(constants.UserAgentOutpost(), http.DefaultTransport),
|
||||
IgnoreErrors: []string{
|
||||
http.ErrAbortHandler.Error(),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
l.WithField("env", globalConfig.ErrorReporting.Environment).WithError(err).Warning("Failed to initialise sentry")
|
||||
err := sentry.Init(sentry.ClientOptions{
|
||||
Dsn: globalConfig.ErrorReporting.SentryDsn,
|
||||
Environment: globalConfig.ErrorReporting.Environment,
|
||||
EnableTracing: true,
|
||||
TracesSampler: sentryutils.SamplerFunc(float64(globalConfig.ErrorReporting.TracesSampleRate)),
|
||||
Release: fmt.Sprintf("authentik@%s", constants.VERSION),
|
||||
HTTPTransport: webutils.NewUserAgentTransport(constants.UserAgentOutpost(), http.DefaultTransport),
|
||||
IgnoreErrors: []string{
|
||||
http.ErrAbortHandler.Error(),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
l.WithField("env", globalConfig.ErrorReporting.Environment).WithError(err).Warning("Failed to initialise sentry")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ type LDAPServer struct {
|
||||
providers []*ProviderInstance
|
||||
}
|
||||
|
||||
func NewServer(ac *ak.APIController) *LDAPServer {
|
||||
func NewServer(ac *ak.APIController) ak.Outpost {
|
||||
ls := &LDAPServer{
|
||||
log: log.WithField("logger", "authentik.outpost.ldap"),
|
||||
ac: ac,
|
||||
|
@ -3,6 +3,7 @@ package application
|
||||
type ProxyClaims struct {
|
||||
UserAttributes map[string]interface{} `json:"user_attributes"`
|
||||
BackendOverride string `json:"backend_override"`
|
||||
HostHeader string `json:"host_header"`
|
||||
IsSuperuser bool `json:"is_superuser"`
|
||||
}
|
||||
|
||||
|
@ -74,13 +74,18 @@ func (a *Application) proxyModifyRequest(ou *url.URL) func(req *http.Request) {
|
||||
r.URL.Scheme = ou.Scheme
|
||||
r.URL.Host = ou.Host
|
||||
claims := a.getClaimsFromSession(r)
|
||||
if claims != nil && claims.Proxy != nil && claims.Proxy.BackendOverride != "" {
|
||||
u, err := url.Parse(claims.Proxy.BackendOverride)
|
||||
if err != nil {
|
||||
a.log.WithField("backend_override", claims.Proxy.BackendOverride).WithError(err).Warning("failed parse user backend override")
|
||||
} else {
|
||||
r.URL.Scheme = u.Scheme
|
||||
r.URL.Host = u.Host
|
||||
if claims != nil && claims.Proxy != nil {
|
||||
if claims.Proxy.BackendOverride != "" {
|
||||
u, err := url.Parse(claims.Proxy.BackendOverride)
|
||||
if err != nil {
|
||||
a.log.WithField("backend_override", claims.Proxy.BackendOverride).WithError(err).Warning("failed parse user backend override")
|
||||
} else {
|
||||
r.URL.Scheme = u.Scheme
|
||||
r.URL.Host = u.Host
|
||||
}
|
||||
}
|
||||
if claims.Proxy.HostHeader != "" {
|
||||
r.Host = claims.Proxy.HostHeader
|
||||
}
|
||||
}
|
||||
a.log.WithField("upstream_url", r.URL.String()).Trace("final upstream url")
|
||||
|
@ -35,7 +35,7 @@ type ProxyServer struct {
|
||||
akAPI *ak.APIController
|
||||
}
|
||||
|
||||
func NewProxyServer(ac *ak.APIController) *ProxyServer {
|
||||
func NewProxyServer(ac *ak.APIController) ak.Outpost {
|
||||
l := log.WithField("logger", "authentik.outpost.proxyv2")
|
||||
defaultCert, err := crypto.GenerateSelfSignedCert()
|
||||
if err != nil {
|
||||
|
@ -23,7 +23,7 @@ type RACServer struct {
|
||||
conns map[string]connection.Connection
|
||||
}
|
||||
|
||||
func NewServer(ac *ak.APIController) *RACServer {
|
||||
func NewServer(ac *ak.APIController) ak.Outpost {
|
||||
rs := &RACServer{
|
||||
log: log.WithField("logger", "authentik.outpost.rac"),
|
||||
ac: ac,
|
||||
|
@ -34,7 +34,7 @@ type RadiusServer struct {
|
||||
providers []*ProviderInstance
|
||||
}
|
||||
|
||||
func NewServer(ac *ak.APIController) *RadiusServer {
|
||||
func NewServer(ac *ak.APIController) ak.Outpost {
|
||||
rs := &RadiusServer{
|
||||
log: log.WithField("logger", "authentik.outpost.radius"),
|
||||
ac: ac,
|
||||
|
@ -2,7 +2,6 @@ package web
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
@ -20,7 +19,7 @@ func NewTracingTransport(ctx context.Context, inner http.RoundTripper) *tracingT
|
||||
func (tt *tracingTransport) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
span := sentry.StartSpan(tt.ctx, "authentik.go.http_request")
|
||||
r.Header.Set("sentry-trace", span.ToSentryTrace())
|
||||
span.Description = fmt.Sprintf("%s %s", r.Method, r.URL.String())
|
||||
span.Description = r.Method + " " + r.URL.String()
|
||||
span.SetTag("url", r.URL.String())
|
||||
span.SetTag("method", r.Method)
|
||||
defer span.Finish()
|
||||
|
@ -31,8 +31,6 @@ func (ws *WebServer) configureStatic() {
|
||||
return h
|
||||
}
|
||||
|
||||
helpHandler := http.FileServer(http.Dir("./website/help/"))
|
||||
|
||||
indexLessRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/static/dist/").Handler(pathStripper(
|
||||
distFs,
|
||||
"static/dist/",
|
||||
@ -78,13 +76,6 @@ func (ws *WebServer) configureStatic() {
|
||||
))
|
||||
}
|
||||
|
||||
staticRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/if/help/").Handler(pathStripper(
|
||||
helpHandler,
|
||||
config.Get().Web.Path,
|
||||
"/if/help/",
|
||||
))
|
||||
staticRouter.PathPrefix(config.Get().Web.Path).PathPrefix("/help").Handler(http.RedirectHandler(fmt.Sprintf("%sif/help/", config.Get().Web.Path), http.StatusMovedPermanently))
|
||||
|
||||
staticRouter.PathPrefix(config.Get().Web.Path).Path("/robots.txt").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
rw.Header()["Content-Type"] = []string{"text/plain"}
|
||||
rw.WriteHeader(200)
|
||||
|
Binary file not shown.
Binary file not shown.
@ -13,7 +13,7 @@ dependencies = [
|
||||
"dacite==1.9.2",
|
||||
"deepmerge==2.0",
|
||||
"defusedxml==0.7.1",
|
||||
"django==5.1.9",
|
||||
"django==5.1.10",
|
||||
"django-countries==7.6.1",
|
||||
"django-cte==1.3.3",
|
||||
"django-filter==25.1",
|
||||
|
@ -1,4 +1,4 @@
|
||||
# yaml-language-server: $schema=https://json.schemastore.org/traefik-v2.json
|
||||
# yaml-language-server: $schema=https://json.schemastore.org/traefik-v3.json
|
||||
api:
|
||||
insecure: true
|
||||
debug: true
|
||||
|
97
tests/e2e/test_flows_authenticators_webauthn.py
Normal file
97
tests/e2e/test_flows_authenticators_webauthn.py
Normal file
@ -0,0 +1,97 @@
|
||||
"""test flow with WebAuthn Stage"""
|
||||
|
||||
from selenium.webdriver.common.virtual_authenticator import (
|
||||
Protocol,
|
||||
Transport,
|
||||
VirtualAuthenticatorOptions,
|
||||
)
|
||||
|
||||
from authentik.blueprints.tests import apply_blueprint
|
||||
from authentik.stages.authenticator_webauthn.models import (
|
||||
AuthenticatorWebAuthnStage,
|
||||
WebAuthnDevice,
|
||||
)
|
||||
from tests.e2e.test_flows_login_sfe import login_sfe
|
||||
from tests.e2e.utils import SeleniumTestCase, retry
|
||||
|
||||
|
||||
class TestFlowsAuthenticatorWebAuthn(SeleniumTestCase):
|
||||
"""test flow with WebAuthn Stage"""
|
||||
|
||||
host = "localhost"
|
||||
|
||||
def register(self):
|
||||
options = VirtualAuthenticatorOptions(
|
||||
protocol=Protocol.CTAP2,
|
||||
transport=Transport.INTERNAL,
|
||||
has_resident_key=True,
|
||||
has_user_verification=True,
|
||||
is_user_verified=True,
|
||||
)
|
||||
self.driver.add_virtual_authenticator(options)
|
||||
|
||||
self.driver.get(self.url("authentik_core:if-flow", flow_slug="default-authentication-flow"))
|
||||
self.login()
|
||||
|
||||
self.wait_for_url(self.if_user_url("/library"))
|
||||
self.assert_user(self.user)
|
||||
|
||||
self.driver.get(
|
||||
self.url(
|
||||
"authentik_flows:configure",
|
||||
stage_uuid=AuthenticatorWebAuthnStage.objects.first().stage_uuid,
|
||||
)
|
||||
)
|
||||
|
||||
self.wait_for_url(self.if_user_url("/library"))
|
||||
self.assertTrue(WebAuthnDevice.objects.filter(user=self.user, confirmed=True).exists())
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint("default/flow-default-authenticator-webauthn-setup.yaml")
|
||||
def test_webauthn_setup(self):
|
||||
"""Test WebAuthn setup"""
|
||||
self.register()
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint("default/flow-default-authenticator-webauthn-setup.yaml")
|
||||
def test_webauthn_authenticate(self):
|
||||
"""Test WebAuthn authentication"""
|
||||
self.register()
|
||||
self.driver.delete_all_cookies()
|
||||
|
||||
self.driver.get(self.url("authentik_core:if-flow", flow_slug="default-authentication-flow"))
|
||||
self.login()
|
||||
|
||||
self.wait_for_url(self.if_user_url("/library"))
|
||||
|
||||
self.assert_user(self.user)
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint("default/flow-default-authenticator-webauthn-setup.yaml")
|
||||
def test_webauthn_authenticate_sfe(self):
|
||||
"""Test WebAuthn authentication (SFE)"""
|
||||
self.register()
|
||||
self.driver.delete_all_cookies()
|
||||
|
||||
self.driver.get(
|
||||
self.url(
|
||||
"authentik_core:if-flow",
|
||||
flow_slug="default-authentication-flow",
|
||||
query={"sfe": True},
|
||||
)
|
||||
)
|
||||
login_sfe(self.driver, self.user)
|
||||
self.wait_for_url(self.if_user_url("/library"))
|
||||
self.assert_user(self.user)
|
@ -4,34 +4,35 @@ from time import sleep
|
||||
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
from selenium.webdriver.remote.webdriver import WebDriver
|
||||
|
||||
from authentik.blueprints.tests import apply_blueprint
|
||||
from authentik.core.models import User
|
||||
from tests.e2e.utils import SeleniumTestCase, retry
|
||||
|
||||
|
||||
def login_sfe(driver: WebDriver, user: User):
|
||||
"""Do entire login flow adjusted for SFE"""
|
||||
flow_executor = driver.find_element(By.ID, "flow-sfe-container")
|
||||
identification_stage = flow_executor.find_element(By.ID, "ident-form")
|
||||
|
||||
identification_stage.find_element(By.CSS_SELECTOR, "input[name=uid_field]").click()
|
||||
identification_stage.find_element(By.CSS_SELECTOR, "input[name=uid_field]").send_keys(
|
||||
user.username
|
||||
)
|
||||
identification_stage.find_element(By.CSS_SELECTOR, "input[name=uid_field]").send_keys(
|
||||
Keys.ENTER
|
||||
)
|
||||
|
||||
password_stage = flow_executor.find_element(By.ID, "password-form")
|
||||
password_stage.find_element(By.CSS_SELECTOR, "input[name=password]").send_keys(user.username)
|
||||
password_stage.find_element(By.CSS_SELECTOR, "input[name=password]").send_keys(Keys.ENTER)
|
||||
sleep(1)
|
||||
|
||||
|
||||
class TestFlowsLoginSFE(SeleniumTestCase):
|
||||
"""test default login flow"""
|
||||
|
||||
def login(self):
|
||||
"""Do entire login flow adjusted for SFE"""
|
||||
flow_executor = self.driver.find_element(By.ID, "flow-sfe-container")
|
||||
identification_stage = flow_executor.find_element(By.ID, "ident-form")
|
||||
|
||||
identification_stage.find_element(By.CSS_SELECTOR, "input[name=uid_field]").click()
|
||||
identification_stage.find_element(By.CSS_SELECTOR, "input[name=uid_field]").send_keys(
|
||||
self.user.username
|
||||
)
|
||||
identification_stage.find_element(By.CSS_SELECTOR, "input[name=uid_field]").send_keys(
|
||||
Keys.ENTER
|
||||
)
|
||||
|
||||
password_stage = flow_executor.find_element(By.ID, "password-form")
|
||||
password_stage.find_element(By.CSS_SELECTOR, "input[name=password]").send_keys(
|
||||
self.user.username
|
||||
)
|
||||
password_stage.find_element(By.CSS_SELECTOR, "input[name=password]").send_keys(Keys.ENTER)
|
||||
sleep(1)
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
@ -46,6 +47,6 @@ class TestFlowsLoginSFE(SeleniumTestCase):
|
||||
query={"sfe": True},
|
||||
)
|
||||
)
|
||||
self.login()
|
||||
login_sfe(self.driver, self.user)
|
||||
self.wait_for_url(self.if_user_url("/library"))
|
||||
self.assert_user(self.user)
|
||||
|
8
uv.lock
generated
8
uv.lock
generated
@ -274,7 +274,7 @@ requires-dist = [
|
||||
{ name = "dacite", specifier = "==1.9.2" },
|
||||
{ name = "deepmerge", specifier = "==2.0" },
|
||||
{ name = "defusedxml", specifier = "==0.7.1" },
|
||||
{ name = "django", specifier = "==5.1.9" },
|
||||
{ name = "django", specifier = "==5.1.10" },
|
||||
{ name = "django-countries", specifier = "==7.6.1" },
|
||||
{ name = "django-cte", specifier = "==1.3.3" },
|
||||
{ name = "django-filter", specifier = "==25.1" },
|
||||
@ -968,16 +968,16 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "django"
|
||||
version = "5.1.9"
|
||||
version = "5.1.10"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "asgiref" },
|
||||
{ name = "sqlparse" },
|
||||
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/10/08/2e6f05494b3fc0a3c53736846034f882b82ee6351791a7815bbb45715d79/django-5.1.9.tar.gz", hash = "sha256:565881bdd0eb67da36442e9ac788bda90275386b549070d70aee86327781a4fc", size = 10710887, upload-time = "2025-05-07T14:06:45.257Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/73/ca/1c724be89e603eb8b5587ea24c63a8c30094c8ff4d990780b5033ee15c40/django-5.1.10.tar.gz", hash = "sha256:73e5d191421d177803dbd5495d94bc7d06d156df9561f4eea9e11b4994c07137", size = 10714538, upload-time = "2025-06-04T13:53:18.805Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e1/d1/d8b6b8250b84380d5a123e099ad3298a49407d81598faa13b43a2c6d96d7/django-5.1.9-py3-none-any.whl", hash = "sha256:2fd1d4a0a66a5ba702699eb692e75b0d828b73cc2f4e1fc4b6a854a918967411", size = 8277363, upload-time = "2025-05-07T14:06:37.426Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/fc/80dc741ba0acb3241aac1213d7272c573d52d8a62ec2c69e9b3bef1547f2/django-5.1.10-py3-none-any.whl", hash = "sha256:19c9b771e9cf4de91101861aadd2daaa159bcf10698ca909c5755c88e70ccb84", size = 8277457, upload-time = "2025-06-04T13:53:07.676Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -403,6 +403,9 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.challenge.deviceChallenges.length === 1) {
|
||||
this.deviceChallenge = this.challenge.deviceChallenges[0];
|
||||
}
|
||||
if (!this.deviceChallenge) {
|
||||
return this.renderChallengePicker();
|
||||
}
|
||||
@ -431,9 +434,7 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
|
||||
${
|
||||
challenges.length > 0
|
||||
? "<p>Select an authentication method.</p>"
|
||||
: `
|
||||
<p>No compatible authentication method available</p>
|
||||
`
|
||||
: `<p>No compatible authentication method available</p>`
|
||||
}
|
||||
${challenges
|
||||
.map((challenge) => {
|
||||
|
@ -56,18 +56,14 @@ export class AdminInterface extends WithCapabilitiesConfig(AuthenticatedInterfac
|
||||
|
||||
protected readonly ws: WebsocketClient;
|
||||
|
||||
@property({
|
||||
type: Object,
|
||||
attribute: false,
|
||||
reflect: false,
|
||||
})
|
||||
@property({ type: Object, attribute: false })
|
||||
public user?: SessionUser;
|
||||
|
||||
@query("ak-about-modal")
|
||||
public aboutModal?: AboutModal;
|
||||
|
||||
@property({ type: Boolean, reflect: true })
|
||||
public sidebarOpen: boolean;
|
||||
public sidebarOpen = false;
|
||||
|
||||
@eventOptions({ passive: true })
|
||||
protected sidebarListener(event: CustomEvent<SidebarToggleEventDetail>) {
|
||||
|
@ -67,6 +67,17 @@ export class DebugPage extends AKElement {
|
||||
>
|
||||
POST System
|
||||
</button>
|
||||
<button
|
||||
class="pf-c-button pf-m-primary"
|
||||
@click=${() => {
|
||||
showMessage({
|
||||
level: MessageLevel.info,
|
||||
message: `lorem ipsum ${Date.now()}`,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Message
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -88,7 +88,6 @@ export class AdminOverviewPage extends AdminOverviewBase {
|
||||
return html`<ak-page-header
|
||||
header=${this.user ? msg(str`Welcome, ${username || ""}.`) : msg("Welcome.")}
|
||||
description=${msg("General system status")}
|
||||
?hasIcon=${false}
|
||||
>
|
||||
</ak-page-header>
|
||||
<section class="pf-c-page__main-section">
|
||||
|
@ -72,7 +72,7 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
|
||||
name="avatars"
|
||||
label=${msg("Avatars")}
|
||||
value="${ifDefined(this._settings?.avatars)}"
|
||||
inputHint="code"
|
||||
input-hint="code"
|
||||
.bighelp=${html`
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
@ -159,7 +159,7 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
|
||||
<ak-text-input
|
||||
name="eventRetention"
|
||||
label=${msg("Event retention")}
|
||||
inputHint="code"
|
||||
input-hint="code"
|
||||
required
|
||||
value="${ifDefined(this._settings?.eventRetention)}"
|
||||
.bighelp=${html`<p class="pf-c-form__helper-text">
|
||||
@ -237,7 +237,7 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
|
||||
<ak-text-input
|
||||
name="defaultTokenDuration"
|
||||
label=${msg("Default token duration")}
|
||||
inputHint="code"
|
||||
input-hint="code"
|
||||
required
|
||||
value="${ifDefined(this._settings?.defaultTokenDuration)}"
|
||||
.bighelp=${html`<p class="pf-c-form__helper-text">
|
||||
|
@ -93,11 +93,7 @@ export class ApplicationCheckAccessForm extends Form<{ forUser: number }> {
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<ak-form-element-horizontal
|
||||
label=${msg("User")}
|
||||
?required=${true}
|
||||
name="forUser"
|
||||
>
|
||||
return html`<ak-form-element-horizontal label=${msg("User")} required name="forUser">
|
||||
<ak-search-select
|
||||
.fetchObjects=${async (query?: string): Promise<User[]> => {
|
||||
const args: CoreUsersListRequest = {
|
||||
|
@ -136,7 +136,7 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio
|
||||
label=${msg("Slug")}
|
||||
required
|
||||
help=${msg("Internal application name used in URLs.")}
|
||||
inputHint="code"
|
||||
input-hint="code"
|
||||
></ak-text-input>
|
||||
<ak-text-input
|
||||
name="group"
|
||||
@ -145,7 +145,7 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio
|
||||
help=${msg(
|
||||
"Optionally enter a group name. Applications with identical groups are shown grouped together.",
|
||||
)}
|
||||
inputHint="code"
|
||||
input-hint="code"
|
||||
></ak-text-input>
|
||||
<ak-provider-search-input
|
||||
name="provider"
|
||||
@ -186,7 +186,7 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio
|
||||
help=${msg(
|
||||
"If left empty, authentik will try to extract the launch URL based on the selected provider.",
|
||||
)}
|
||||
inputHint="code"
|
||||
input-hint="code"
|
||||
></ak-text-input>
|
||||
<ak-switch-input
|
||||
name="openInNewTab"
|
||||
|
@ -99,7 +99,6 @@ export class ApplicationViewPage extends AKElement {
|
||||
return html`<ak-page-header
|
||||
header=${this.application?.name || msg("Loading")}
|
||||
description=${ifDefined(this.application?.metaPublisher)}
|
||||
.iconImage=${true}
|
||||
>
|
||||
<ak-app-icon
|
||||
size=${PFSize.Medium}
|
||||
|
@ -20,7 +20,7 @@ export class ProviderSelectModal extends TableModal<Provider> {
|
||||
}
|
||||
|
||||
@property({ type: Boolean })
|
||||
backchannel?: boolean;
|
||||
backchannel = false;
|
||||
|
||||
@property()
|
||||
confirm!: (selectedItems: Provider[]) => Promise<unknown>;
|
||||
|
@ -57,7 +57,7 @@ export class AkBackchannelProvidersInput extends AKElement {
|
||||
render() {
|
||||
const renderOneChip = (provider: Provider) =>
|
||||
html`<ak-chip
|
||||
.removable=${true}
|
||||
removable
|
||||
value=${ifDefined(provider.pk)}
|
||||
@remove=${this.remover(provider)}
|
||||
>${provider.name}</ak-chip
|
||||
@ -66,7 +66,7 @@ export class AkBackchannelProvidersInput extends AKElement {
|
||||
return html`
|
||||
<ak-form-element-horizontal label=${this.label} name=${this.name}>
|
||||
<div class="pf-c-input-group">
|
||||
<ak-provider-select-table ?backchannel=${true} .confirm=${this.confirm}>
|
||||
<ak-provider-select-table backchannel .confirm=${this.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>
|
||||
|
@ -54,7 +54,7 @@ export class ApplicationEntitlementForm extends ModelForm<ApplicationEntitlement
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${this.instance?.name ?? ""}"
|
||||
@ -62,11 +62,7 @@ export class ApplicationEntitlementForm extends ModelForm<ApplicationEntitlement
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Attributes")}
|
||||
?required=${false}
|
||||
name="attributes"
|
||||
>
|
||||
<ak-form-element-horizontal label=${msg("Attributes")} name="attributes">
|
||||
<ak-codemirror
|
||||
mode=${CodeMirrorMode.YAML}
|
||||
value="${YAML.stringify(this.instance?.attributes ?? {})}"
|
||||
|
@ -128,7 +128,7 @@ export class ApplicationWizardApplicationStep extends ApplicationWizardStep {
|
||||
?invalid=${errors.slug ?? this.errors.has("slug")}
|
||||
.errorMessages=${this.errorMessages("slug")}
|
||||
help=${msg("Internal application name used in URLs.")}
|
||||
inputHint="code"
|
||||
input-hint="code"
|
||||
></ak-slug-input>
|
||||
<ak-text-input
|
||||
name="group"
|
||||
@ -138,7 +138,7 @@ export class ApplicationWizardApplicationStep extends ApplicationWizardStep {
|
||||
help=${msg(
|
||||
"Optionally enter a group name. Applications with identical groups are shown grouped together.",
|
||||
)}
|
||||
inputHint="code"
|
||||
input-hint="code"
|
||||
></ak-text-input>
|
||||
<ak-radio-input
|
||||
label=${msg("Policy engine mode")}
|
||||
@ -161,7 +161,7 @@ export class ApplicationWizardApplicationStep extends ApplicationWizardStep {
|
||||
help=${msg(
|
||||
"If left empty, authentik will try to extract the launch URL based on the selected provider.",
|
||||
)}
|
||||
inputHint="code"
|
||||
input-hint="code"
|
||||
></ak-text-input>
|
||||
<ak-switch-input
|
||||
name="openInNewTab"
|
||||
|
@ -37,7 +37,7 @@ export class ApplicationWizardRACProviderForm extends ApplicationWizardProviderF
|
||||
<ak-form-element-horizontal
|
||||
name="authorizationFlow"
|
||||
label=${msg("Authorization flow")}
|
||||
?required=${true}
|
||||
required
|
||||
>
|
||||
<ak-flow-search
|
||||
flowType=${FlowsInstancesListDesignationEnum.Authorization}
|
||||
@ -57,10 +57,10 @@ export class ApplicationWizardRACProviderForm extends ApplicationWizardProviderF
|
||||
help=${msg(
|
||||
"Determines how long a session lasts before being disconnected and requiring re-authorization.",
|
||||
)}
|
||||
inputHint="code"
|
||||
input-hint="code"
|
||||
></ak-text-input>
|
||||
|
||||
<ak-form-group .expanded=${true}>
|
||||
<ak-form-group expanded>
|
||||
<span slot="header"> ${msg("Protocol settings")} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
|
@ -65,7 +65,7 @@ export class BlueprintForm extends ModelForm<BlueprintInstance, string> {
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
@ -133,7 +133,7 @@ export class BlueprintForm extends ModelForm<BlueprintInstance, string> {
|
||||
.selected=${(item: BlueprintFile): boolean => {
|
||||
return this.instance?.path === item.path;
|
||||
}}
|
||||
?blankable=${true}
|
||||
blankable
|
||||
>
|
||||
</ak-search-select>
|
||||
</ak-form-element-horizontal>`
|
||||
|
@ -134,7 +134,7 @@ export class BrandForm extends ModelForm<Brand, string> {
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Default flow background")}
|
||||
?required=${true}
|
||||
required
|
||||
name="brandingDefaultFlowBackground"
|
||||
>
|
||||
<input
|
||||
|
@ -95,7 +95,7 @@ export class CoreGroupSearch extends CustomListenerElement(AKElement) {
|
||||
.value=${renderValue}
|
||||
.selected=${this.selected}
|
||||
@ak-change=${this.handleSearchUpdate}
|
||||
?blankable=${true}
|
||||
blankable
|
||||
>
|
||||
</ak-search-select>
|
||||
`;
|
||||
|
@ -120,7 +120,7 @@ export class AkCryptoCertificateSearch extends CustomListenerElement(AKElement)
|
||||
.value=${renderValue}
|
||||
.selected=${this.selected}
|
||||
@ak-change=${this.handleSearchUpdate}
|
||||
?blankable=${true}
|
||||
blankable
|
||||
>
|
||||
</ak-search-select>
|
||||
`;
|
||||
|
@ -110,7 +110,7 @@ export const Default = () =>
|
||||
container(
|
||||
html` <ak-form-element-horizontal
|
||||
label=${msg("Authorization flow")}
|
||||
?required=${true}
|
||||
required
|
||||
name="authorizationFlow"
|
||||
>
|
||||
<ak-flow-search
|
||||
@ -124,7 +124,7 @@ export const WithInitialValue = () =>
|
||||
container(
|
||||
html` <ak-form-element-horizontal
|
||||
label=${msg("Authorization flow")}
|
||||
?required=${true}
|
||||
required
|
||||
name="authorizationFlow"
|
||||
>
|
||||
<ak-flow-search
|
||||
|
@ -29,7 +29,7 @@ export class CertificateKeyPairForm extends Form<CertificateGenerationRequest> {
|
||||
return html`<ak-form-element-horizontal
|
||||
label=${msg("Common Name")}
|
||||
name="commonName"
|
||||
?required=${true}
|
||||
required
|
||||
>
|
||||
<input type="text" class="pf-c-form-control" required />
|
||||
</ak-form-element-horizontal>
|
||||
@ -39,18 +39,10 @@ export class CertificateKeyPairForm extends Form<CertificateGenerationRequest> {
|
||||
${msg("Optional, comma-separated SubjectAlt Names.")}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Validity days")}
|
||||
name="validityDays"
|
||||
?required=${true}
|
||||
>
|
||||
<ak-form-element-horizontal label=${msg("Validity days")} name="validityDays" required>
|
||||
<input class="pf-c-form-control" type="number" value="365" />
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Private key Algorithm")}
|
||||
?required=${true}
|
||||
name="alg"
|
||||
>
|
||||
<ak-form-element-horizontal label=${msg("Private key Algorithm")} required name="alg">
|
||||
<ak-radio
|
||||
.options=${[
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import "@goauthentik/components/ak-private-textarea-input.js";
|
||||
import "@goauthentik/elements/CodeMirror";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||
@ -37,7 +38,7 @@ export class CertificateKeyPairForm extends ModelForm<CertificateKeyPair, string
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} name="name" ?required=${true}>
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} name="name" required>
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
@ -45,37 +46,24 @@ export class CertificateKeyPairForm extends ModelForm<CertificateKeyPair, string
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
<ak-private-textarea-input
|
||||
label=${msg("Certificate")}
|
||||
name="certificateData"
|
||||
?writeOnly=${this.instance !== undefined}
|
||||
?required=${true}
|
||||
>
|
||||
<textarea
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
class="pf-c-form-control pf-m-monospace"
|
||||
placeholder="-----BEGIN CERTIFICATE-----"
|
||||
required
|
||||
></textarea>
|
||||
<p class="pf-c-form__helper-text">${msg("PEM-encoded Certificate data.")}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
name="keyData"
|
||||
?writeOnly=${this.instance !== undefined}
|
||||
input-hint="code"
|
||||
placeholder="-----BEGIN CERTIFICATE-----"
|
||||
required
|
||||
?revealed=${this.instance === undefined}
|
||||
help=${msg("PEM-encoded Certificate data.")}
|
||||
></ak-private-textarea-input>
|
||||
<ak-private-textarea-input
|
||||
label=${msg("Private Key")}
|
||||
>
|
||||
<textarea
|
||||
autocomplete="off"
|
||||
class="pf-c-form-control pf-m-monospace"
|
||||
spellcheck="false"
|
||||
></textarea>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
"Optional Private Key. If this is set, you can use this keypair for encryption.",
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>`;
|
||||
name="keyData"
|
||||
input-hint="code"
|
||||
?revealed=${this.instance === undefined}
|
||||
help=${msg(
|
||||
"Optional Private Key. If this is set, you can use this keypair for encryption.",
|
||||
)}
|
||||
></ak-private-textarea-input>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EVENT_REFRESH_ENTERPRISE } from "@goauthentik/common/constants";
|
||||
import "@goauthentik/components/ak-private-textarea-input.js";
|
||||
import "@goauthentik/elements/CodeMirror";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||
@ -61,17 +62,13 @@ export class EnterpriseLicenseForm extends ModelForm<License, string> {
|
||||
value="${ifDefined(this.installID)}"
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
<ak-private-textarea-input
|
||||
name="key"
|
||||
?writeOnly=${this.instance !== undefined}
|
||||
?revealed=${this.instance === undefined}
|
||||
label=${msg("License key")}
|
||||
input-hint="code"
|
||||
>
|
||||
<textarea
|
||||
class="pf-c-form-control pf-m-monospace"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
></textarea>
|
||||
</ak-form-element-horizontal>`;
|
||||
</ak-private-textarea-input>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,7 @@ export class RuleForm extends ModelForm<NotificationRule, string> {
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
@ -88,7 +88,7 @@ export class RuleForm extends ModelForm<NotificationRule, string> {
|
||||
.selected=${(group: Group): boolean => {
|
||||
return group.pk === this.instance?.group;
|
||||
}}
|
||||
?blankable=${true}
|
||||
blankable
|
||||
>
|
||||
</ak-search-select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
@ -97,11 +97,7 @@ export class RuleForm extends ModelForm<NotificationRule, string> {
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Transports")}
|
||||
?required=${true}
|
||||
name="transports"
|
||||
>
|
||||
<ak-form-element-horizontal label=${msg("Transports")} required name="transports">
|
||||
<ak-dual-select-dynamic-selected
|
||||
.provider=${eventTransportsProvider}
|
||||
.selector=${eventTransportsSelector(this.instance?.transports)}
|
||||
@ -114,7 +110,7 @@ export class RuleForm extends ModelForm<NotificationRule, string> {
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Severity")} ?required=${true} name="severity">
|
||||
<ak-form-element-horizontal label=${msg("Severity")} required name="severity">
|
||||
<ak-radio
|
||||
.options=${[
|
||||
{
|
||||
|
@ -137,7 +137,7 @@ export class BoundStagesList extends Table<FlowStageBinding> {
|
||||
<div slot="primary">
|
||||
<ak-stage-wizard
|
||||
createText=${msg("Create and bind Stage")}
|
||||
?showBindingPage=${true}
|
||||
showBindingPage
|
||||
bindingTarget=${ifDefined(this.target)}
|
||||
></ak-stage-wizard>
|
||||
<ak-forms-modal>
|
||||
@ -158,7 +158,7 @@ export class BoundStagesList extends Table<FlowStageBinding> {
|
||||
return html`
|
||||
<ak-stage-wizard
|
||||
createText=${msg("Create and bind Stage")}
|
||||
?showBindingPage=${true}
|
||||
showBindingPage
|
||||
bindingTarget=${ifDefined(this.target)}
|
||||
></ak-stage-wizard>
|
||||
<ak-forms-modal>
|
||||
|
@ -74,7 +74,7 @@ export class FlowForm extends WithCapabilitiesConfig(ModelForm<Flow, string>) {
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
@ -82,7 +82,7 @@ export class FlowForm extends WithCapabilitiesConfig(ModelForm<Flow, string>) {
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Title")} ?required=${true} name="title">
|
||||
<ak-form-element-horizontal label=${msg("Title")} required name="title">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.title)}"
|
||||
@ -91,7 +91,7 @@ export class FlowForm extends WithCapabilitiesConfig(ModelForm<Flow, string>) {
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">${msg("Shown as the Title in Flow pages.")}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Slug")} ?required=${true} name="slug">
|
||||
<ak-form-element-horizontal label=${msg("Slug")} required name="slug">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.slug)}"
|
||||
@ -102,11 +102,7 @@ export class FlowForm extends WithCapabilitiesConfig(ModelForm<Flow, string>) {
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">${msg("Visible in the URL.")}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Designation")}
|
||||
?required=${true}
|
||||
name="designation"
|
||||
>
|
||||
<ak-form-element-horizontal label=${msg("Designation")} required name="designation">
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.designation === undefined}>
|
||||
---------
|
||||
@ -165,7 +161,7 @@ export class FlowForm extends WithCapabilitiesConfig(ModelForm<Flow, string>) {
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Authentication")}
|
||||
?required=${true}
|
||||
required
|
||||
name="authentication"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
@ -240,7 +236,7 @@ export class FlowForm extends WithCapabilitiesConfig(ModelForm<Flow, string>) {
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Denied action")}
|
||||
?required=${true}
|
||||
required
|
||||
name="deniedAction"
|
||||
>
|
||||
<ak-radio
|
||||
@ -279,7 +275,7 @@ export class FlowForm extends WithCapabilitiesConfig(ModelForm<Flow, string>) {
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Policy engine mode")}
|
||||
?required=${true}
|
||||
required
|
||||
name="policyEngineMode"
|
||||
>
|
||||
<ak-radio
|
||||
@ -309,11 +305,7 @@ export class FlowForm extends WithCapabilitiesConfig(ModelForm<Flow, string>) {
|
||||
<ak-form-group>
|
||||
<span slot="header"> ${msg("Appearance settings")} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Layout")}
|
||||
?required=${true}
|
||||
name="layout"
|
||||
>
|
||||
<ak-form-element-horizontal label=${msg("Layout")} required name="layout">
|
||||
<select class="pf-c-form-control">
|
||||
<option
|
||||
value=${FlowLayoutEnum.Stacked}
|
||||
|
@ -76,11 +76,7 @@ export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
|
||||
if (this.instance?.target || this.targetPk) {
|
||||
return html``;
|
||||
}
|
||||
return html`<ak-form-element-horizontal
|
||||
label=${msg("Target")}
|
||||
?required=${true}
|
||||
name="target"
|
||||
>
|
||||
return html`<ak-form-element-horizontal label=${msg("Target")} required name="target">
|
||||
<ak-flow-search
|
||||
flowType=${FlowsInstancesListDesignationEnum.Authorization}
|
||||
.currentFlow=${this.instance?.target}
|
||||
@ -91,7 +87,7 @@ export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html` ${this.renderTarget()}
|
||||
<ak-form-element-horizontal label=${msg("Stage")} ?required=${true} name="stage">
|
||||
<ak-form-element-horizontal label=${msg("Stage")} required name="stage">
|
||||
<ak-search-select
|
||||
.fetchObjects=${async (query?: string): Promise<Stage[]> => {
|
||||
const args: StagesAllListRequest = {
|
||||
@ -118,7 +114,7 @@ export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
|
||||
>
|
||||
</ak-search-select>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Order")} ?required=${true} name="order">
|
||||
<ak-form-element-horizontal label=${msg("Order")} required name="order">
|
||||
<input
|
||||
type="number"
|
||||
value="${this.instance?.order ?? this.defaultOrder}"
|
||||
@ -164,7 +160,7 @@ export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Invalid response behavior")}
|
||||
?required=${true}
|
||||
required
|
||||
name="invalidResponseAction"
|
||||
>
|
||||
<ak-radio
|
||||
@ -201,7 +197,7 @@ export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Policy engine mode")}
|
||||
?required=${true}
|
||||
required
|
||||
name="policyEngineMode"
|
||||
>
|
||||
<ak-radio
|
||||
|
@ -62,7 +62,7 @@ export class GroupForm extends ModelForm<Group, string> {
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
@ -112,7 +112,7 @@ export class GroupForm extends ModelForm<Group, string> {
|
||||
.selected=${(group: Group): boolean => {
|
||||
return group.pk === this.instance?.parent;
|
||||
}}
|
||||
?blankable=${true}
|
||||
blankable
|
||||
>
|
||||
</ak-search-select>
|
||||
</ak-form-element-horizontal>
|
||||
@ -141,11 +141,7 @@ export class GroupForm extends ModelForm<Group, string> {
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Attributes")}
|
||||
?required=${true}
|
||||
name="attributes"
|
||||
>
|
||||
<ak-form-element-horizontal label=${msg("Attributes")} required name="attributes">
|
||||
<ak-codemirror
|
||||
mode=${CodeMirrorMode.YAML}
|
||||
value="${YAML.stringify(this.instance?.attributes ?? {})}"
|
||||
|
@ -65,7 +65,7 @@ export class RelatedGroupAdd extends Form<{ groups: string[] }> {
|
||||
<ak-chip-group>
|
||||
${this.groupsToAdd.map((group) => {
|
||||
return html`<ak-chip
|
||||
.removable=${true}
|
||||
removable
|
||||
value=${ifDefined(group.pk)}
|
||||
@remove=${() => {
|
||||
const idx = this.groupsToAdd.indexOf(group);
|
||||
|
@ -83,7 +83,7 @@ export class RelatedUserAdd extends Form<{ users: number[] }> {
|
||||
<ak-chip-group>
|
||||
${this.usersToAdd.map((user) => {
|
||||
return html`<ak-chip
|
||||
.removable=${true}
|
||||
removable
|
||||
value=${ifDefined(user.pk)}
|
||||
@remove=${() => {
|
||||
const idx = this.usersToAdd.indexOf(user);
|
||||
|
@ -143,7 +143,7 @@ export class OutpostForm extends ModelForm<Outpost, string> {
|
||||
[OutpostTypeEnum.Rac, msg("RAC")],
|
||||
];
|
||||
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
@ -151,7 +151,7 @@ export class OutpostForm extends ModelForm<Outpost, string> {
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Type")} ?required=${true} name="type">
|
||||
<ak-form-element-horizontal label=${msg("Type")} required name="type">
|
||||
<select
|
||||
class="pf-c-form-control"
|
||||
@change=${(ev: Event) => {
|
||||
@ -202,7 +202,7 @@ export class OutpostForm extends ModelForm<Outpost, string> {
|
||||
}
|
||||
return selected;
|
||||
}}
|
||||
?blankable=${true}
|
||||
blankable
|
||||
>
|
||||
</ak-search-select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
|
@ -49,7 +49,7 @@ export class OutpostHealthElement extends AKElement {
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<ak-label color=${PFColor.Green} ?compact=${true}>
|
||||
<ak-label color=${PFColor.Green} compact>
|
||||
${msg(
|
||||
str`${formatElapsedTime(this.outpostHealth.lastSeen)} (${this.outpostHealth.lastSeen?.toLocaleTimeString()})`,
|
||||
)}
|
||||
@ -64,12 +64,12 @@ export class OutpostHealthElement extends AKElement {
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.outpostHealth.versionOutdated
|
||||
? html`<ak-label color=${PFColor.Red} ?compact=${true}
|
||||
? html`<ak-label color=${PFColor.Red} compact
|
||||
>${msg(
|
||||
str`${this.outpostHealth.version}, should be ${this.outpostHealth.versionShould}`,
|
||||
)}
|
||||
</ak-label>`
|
||||
: html`<ak-label color=${PFColor.Green} ?compact=${true}
|
||||
: html`<ak-label color=${PFColor.Green} compact
|
||||
>${versionString}
|
||||
</ak-label>`}
|
||||
</div>
|
||||
|
@ -112,12 +112,12 @@ export class OutpostListPage extends TablePage<Outpost> {
|
||||
return [
|
||||
html`<div>${item.name}</div>
|
||||
${item.config.authentik_host === ""
|
||||
? html`<ak-label color=${PFColor.Orange} ?compact=${true}>
|
||||
? html`<ak-label color=${PFColor.Orange} compact>
|
||||
${msg(
|
||||
"Warning: authentik Domain is not configured, authentication will not work.",
|
||||
)}
|
||||
</ak-label>`
|
||||
: html`<ak-label color=${PFColor.Green} ?compact=${true}>
|
||||
: html`<ak-label color=${PFColor.Green} compact>
|
||||
${msg(str`Logging in via ${item.config.authentik_host}.`)}
|
||||
</ak-label>`}`,
|
||||
html`${TypeToLabel(item.type)}`,
|
||||
|
@ -38,7 +38,7 @@ export class ServiceConnectionDockerForm extends ModelForm<DockerServiceConnecti
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
@ -66,7 +66,7 @@ export class ServiceConnectionDockerForm extends ModelForm<DockerServiceConnecti
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Docker URL")} ?required=${true} name="url">
|
||||
<ak-form-element-horizontal label=${msg("Docker URL")} required name="url">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.url)}"
|
||||
|
@ -42,7 +42,7 @@ export class ServiceConnectionKubernetesForm extends ModelForm<
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
|
@ -199,7 +199,7 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
|
||||
<div slot="primary">
|
||||
<ak-policy-wizard
|
||||
createText=${msg("Create and bind Policy")}
|
||||
?showBindingPage=${true}
|
||||
showBindingPage
|
||||
bindingTarget=${ifDefined(this.target)}
|
||||
></ak-policy-wizard>
|
||||
<ak-forms-modal size=${PFSize.Medium}>
|
||||
@ -225,7 +225,7 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
|
||||
return html`${this.allowedTypes.includes(PolicyBindingCheckTarget.policy)
|
||||
? html`<ak-policy-wizard
|
||||
createText=${msg("Create and bind Policy")}
|
||||
?showBindingPage=${true}
|
||||
showBindingPage
|
||||
bindingTarget=${ifDefined(this.target)}
|
||||
></ak-policy-wizard>`
|
||||
: nothing}
|
||||
|
@ -182,7 +182,7 @@ export class PolicyBindingForm extends ModelForm<PolicyBinding, string> {
|
||||
.selected=${(policy: Policy): boolean => {
|
||||
return policy.pk === this.instance?.policy;
|
||||
}}
|
||||
?blankable=${true}
|
||||
blankable
|
||||
>
|
||||
</ak-search-select>
|
||||
${this.typeNotices
|
||||
@ -219,7 +219,7 @@ export class PolicyBindingForm extends ModelForm<PolicyBinding, string> {
|
||||
.selected=${(group: Group): boolean => {
|
||||
return group.pk === this.instance?.group;
|
||||
}}
|
||||
?blankable=${true}
|
||||
blankable
|
||||
>
|
||||
</ak-search-select>
|
||||
${this.typeNotices
|
||||
@ -256,7 +256,7 @@ export class PolicyBindingForm extends ModelForm<PolicyBinding, string> {
|
||||
.selected=${(user: User): boolean => {
|
||||
return user.pk === this.instance?.user;
|
||||
}}
|
||||
?blankable=${true}
|
||||
blankable
|
||||
>
|
||||
</ak-search-select>
|
||||
${this.typeNotices
|
||||
@ -300,7 +300,7 @@ export class PolicyBindingForm extends ModelForm<PolicyBinding, string> {
|
||||
${msg("Negates the outcome of the binding. Messages are unaffected.")}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Order")} ?required=${true} name="order">
|
||||
<ak-form-element-horizontal label=${msg("Order")} required name="order">
|
||||
<input
|
||||
type="number"
|
||||
value="${this.instance?.order ?? this.defaultOrder}"
|
||||
@ -308,7 +308,7 @@ export class PolicyBindingForm extends ModelForm<PolicyBinding, string> {
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Timeout")} ?required=${true} name="timeout">
|
||||
<ak-form-element-horizontal label=${msg("Timeout")} required name="timeout">
|
||||
<input
|
||||
type="number"
|
||||
value="${this.instance?.timeout ?? 30}"
|
||||
|
@ -65,10 +65,10 @@ export class PolicyListPage extends TablePage<Policy> {
|
||||
return [
|
||||
html`<div>${item.name}</div>
|
||||
${(item.boundTo || 0) > 0
|
||||
? html`<ak-label color=${PFColor.Green} ?compact=${true}>
|
||||
? html`<ak-label color=${PFColor.Green} compact>
|
||||
${msg(str`Assigned to ${item.boundTo} object(s).`)}
|
||||
</ak-label>`
|
||||
: html`<ak-label color=${PFColor.Orange} ?compact=${true}>
|
||||
: html`<ak-label color=${PFColor.Orange} compact>
|
||||
${msg("Warning: Policy is not assigned.")}
|
||||
</ak-label>`}`,
|
||||
html`${item.verboseName}`,
|
||||
|
@ -94,7 +94,7 @@ export class PolicyTestForm extends Form<PolicyTestRequest> {
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<ak-form-element-horizontal label=${msg("User")} ?required=${true} name="user">
|
||||
return html`<ak-form-element-horizontal label=${msg("User")} required name="user">
|
||||
<ak-search-select
|
||||
.fetchObjects=${async (query?: string): Promise<User[]> => {
|
||||
const args: CoreUsersListRequest = {
|
||||
|
@ -36,7 +36,7 @@ export class DummyPolicyForm extends BasePolicyForm<DummyPolicy> {
|
||||
"A policy used for testing. Always returns the same result as specified below after waiting a random duration.",
|
||||
)}
|
||||
</span>
|
||||
<ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
<ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name || "")}"
|
||||
@ -64,7 +64,7 @@ export class DummyPolicyForm extends BasePolicyForm<DummyPolicy> {
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-group .expanded=${true}>
|
||||
<ak-form-group expanded>
|
||||
<span slot="header"> ${msg("Policy-specific settings")} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal name="result">
|
||||
@ -82,11 +82,7 @@ export class DummyPolicyForm extends BasePolicyForm<DummyPolicy> {
|
||||
<span class="pf-c-switch__label">${msg("Pass policy?")}</span>
|
||||
</label>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Wait (min)")}
|
||||
?required=${true}
|
||||
name="waitMin"
|
||||
>
|
||||
<ak-form-element-horizontal label=${msg("Wait (min)")} required name="waitMin">
|
||||
<input
|
||||
type="number"
|
||||
value="${this.instance?.waitMin ?? 1}"
|
||||
@ -99,11 +95,7 @@ export class DummyPolicyForm extends BasePolicyForm<DummyPolicy> {
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Wait (max)")}
|
||||
?required=${true}
|
||||
name="waitMax"
|
||||
>
|
||||
<ak-form-element-horizontal label=${msg("Wait (max)")} required name="waitMax">
|
||||
<input
|
||||
type="number"
|
||||
value="${this.instance?.waitMax ?? 5}"
|
||||
|
@ -48,7 +48,7 @@ export class EventMatcherPolicyForm extends BasePolicyForm<EventMatcherPolicy> {
|
||||
"Matches an event against a set of criteria. If any of the configured values match, the policy passes.",
|
||||
)}
|
||||
</span>
|
||||
<ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
<ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name || "")}"
|
||||
@ -76,7 +76,7 @@ export class EventMatcherPolicyForm extends BasePolicyForm<EventMatcherPolicy> {
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-group .expanded=${true}>
|
||||
<ak-form-group expanded>
|
||||
<span slot="header"> ${msg("Policy-specific settings")} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal label=${msg("Action")} name="action">
|
||||
@ -98,7 +98,7 @@ export class EventMatcherPolicyForm extends BasePolicyForm<EventMatcherPolicy> {
|
||||
.selected=${(item: TypeCreate): boolean => {
|
||||
return this.instance?.action === item.component;
|
||||
}}
|
||||
?blankable=${true}
|
||||
blankable
|
||||
>
|
||||
</ak-search-select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
@ -138,7 +138,7 @@ export class EventMatcherPolicyForm extends BasePolicyForm<EventMatcherPolicy> {
|
||||
.selected=${(item: App): boolean => {
|
||||
return this.instance?.app === item.name;
|
||||
}}
|
||||
?blankable=${true}
|
||||
blankable
|
||||
>
|
||||
</ak-search-select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
@ -168,7 +168,7 @@ export class EventMatcherPolicyForm extends BasePolicyForm<EventMatcherPolicy> {
|
||||
.selected=${(item: App): boolean => {
|
||||
return this.instance?.model === item.name;
|
||||
}}
|
||||
?blankable=${true}
|
||||
blankable
|
||||
>
|
||||
</ak-search-select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
|
@ -36,7 +36,7 @@ export class PasswordExpiryPolicyForm extends BasePolicyForm<PasswordExpiryPolic
|
||||
"Checks if the request's user's password has been changed in the last x days, and denys based on settings.",
|
||||
)}
|
||||
</span>
|
||||
<ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
<ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name || "")}"
|
||||
@ -64,12 +64,12 @@ export class PasswordExpiryPolicyForm extends BasePolicyForm<PasswordExpiryPolic
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-group .expanded=${true}>
|
||||
<ak-form-group expanded>
|
||||
<span slot="header"> ${msg("Policy-specific settings")} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Maximum age (in days)")}
|
||||
?required=${true}
|
||||
required
|
||||
name="days"
|
||||
>
|
||||
<input
|
||||
|
@ -39,7 +39,7 @@ export class ExpressionPolicyForm extends BasePolicyForm<ExpressionPolicy> {
|
||||
"Executes the python snippet to determine whether to allow or deny a request.",
|
||||
)}
|
||||
</span>
|
||||
<ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
<ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name || "")}"
|
||||
@ -67,12 +67,12 @@ export class ExpressionPolicyForm extends BasePolicyForm<ExpressionPolicy> {
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-group .expanded=${true}>
|
||||
<ak-form-group expanded>
|
||||
<span slot="header"> ${msg("Policy-specific settings")} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Expression")}
|
||||
?required=${true}
|
||||
required
|
||||
name="expression"
|
||||
>
|
||||
<ak-codemirror
|
||||
|
@ -49,7 +49,7 @@ export class PasswordPolicyForm extends BasePolicyForm<PasswordPolicy> {
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Minimum length")}
|
||||
?required=${true}
|
||||
required
|
||||
name="lengthMin"
|
||||
>
|
||||
<input
|
||||
@ -61,7 +61,7 @@ export class PasswordPolicyForm extends BasePolicyForm<PasswordPolicy> {
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Minimum amount of Uppercase Characters")}
|
||||
?required=${true}
|
||||
required
|
||||
name="amountUppercase"
|
||||
>
|
||||
<input
|
||||
@ -73,7 +73,7 @@ export class PasswordPolicyForm extends BasePolicyForm<PasswordPolicy> {
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Minimum amount of Lowercase Characters")}
|
||||
?required=${true}
|
||||
required
|
||||
name="amountLowercase"
|
||||
>
|
||||
<input
|
||||
@ -85,7 +85,7 @@ export class PasswordPolicyForm extends BasePolicyForm<PasswordPolicy> {
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Minimum amount of Digits")}
|
||||
?required=${true}
|
||||
required
|
||||
name="amountDigits"
|
||||
>
|
||||
<input
|
||||
@ -97,7 +97,7 @@ export class PasswordPolicyForm extends BasePolicyForm<PasswordPolicy> {
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Minimum amount of Symbols Characters")}
|
||||
?required=${true}
|
||||
required
|
||||
name="amountSymbols"
|
||||
>
|
||||
<input
|
||||
@ -109,7 +109,7 @@ export class PasswordPolicyForm extends BasePolicyForm<PasswordPolicy> {
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Error message")}
|
||||
?required=${true}
|
||||
required
|
||||
name="errorMessage"
|
||||
>
|
||||
<input
|
||||
@ -121,7 +121,7 @@ export class PasswordPolicyForm extends BasePolicyForm<PasswordPolicy> {
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Symbol charset")}
|
||||
?required=${true}
|
||||
required
|
||||
name="symbolCharset"
|
||||
>
|
||||
<input
|
||||
@ -142,12 +142,12 @@ export class PasswordPolicyForm extends BasePolicyForm<PasswordPolicy> {
|
||||
|
||||
renderHIBP(): TemplateResult {
|
||||
return html`
|
||||
<ak-form-group .expanded=${true}>
|
||||
<ak-form-group expanded>
|
||||
<span slot="header"> ${msg("HaveIBeenPwned settings")} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Allowed count")}
|
||||
?required=${true}
|
||||
required
|
||||
name="hibpAllowedCount"
|
||||
>
|
||||
<input
|
||||
@ -167,12 +167,12 @@ export class PasswordPolicyForm extends BasePolicyForm<PasswordPolicy> {
|
||||
|
||||
renderZxcvbn(): TemplateResult {
|
||||
return html`
|
||||
<ak-form-group .expanded=${true}>
|
||||
<ak-form-group expanded>
|
||||
<span slot="header"> ${msg("zxcvbn settings")} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Score threshold")}
|
||||
?required=${true}
|
||||
required
|
||||
name="zxcvbnScoreThreshold"
|
||||
>
|
||||
<input
|
||||
@ -221,7 +221,7 @@ export class PasswordPolicyForm extends BasePolicyForm<PasswordPolicy> {
|
||||
"Checks the value from the policy request against several rules, mostly used to ensure password strength.",
|
||||
)}
|
||||
</span>
|
||||
<ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
<ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name || "")}"
|
||||
@ -251,7 +251,7 @@ export class PasswordPolicyForm extends BasePolicyForm<PasswordPolicy> {
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Password field")}
|
||||
?required=${true}
|
||||
required
|
||||
name="passwordField"
|
||||
>
|
||||
<input
|
||||
|
@ -46,7 +46,7 @@ username they are attempting to login as, by one.`,
|
||||
doesn't pass when either or both of the selected options are equal or above the threshold.`,
|
||||
)}
|
||||
</span>
|
||||
<ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
<ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name || "")}"
|
||||
@ -74,7 +74,7 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-group .expanded=${true}>
|
||||
<ak-form-group expanded>
|
||||
<span slot="header"> ${msg("Policy-specific settings")} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal name="checkIp">
|
||||
@ -107,11 +107,7 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<span class="pf-c-switch__label">${msg("Check Username")}</span>
|
||||
</label>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Threshold")}
|
||||
?required=${true}
|
||||
name="threshold"
|
||||
>
|
||||
<ak-form-element-horizontal label=${msg("Threshold")} required name="threshold">
|
||||
<input
|
||||
type="number"
|
||||
value="${ifDefined(this.instance?.threshold || -5)}"
|
||||
|
@ -36,7 +36,7 @@ export class UniquePasswordPolicyForm extends BasePolicyForm<UniquePasswordPolic
|
||||
"Ensure that the user's new password is different from their previous passwords. The number of past passwords to check is configurable.",
|
||||
)}
|
||||
</span>
|
||||
<ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
<ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name || "")}"
|
||||
@ -66,7 +66,7 @@ export class UniquePasswordPolicyForm extends BasePolicyForm<UniquePasswordPolic
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Password field")}
|
||||
?required=${true}
|
||||
required
|
||||
name="passwordField"
|
||||
>
|
||||
<input
|
||||
@ -81,7 +81,7 @@ export class UniquePasswordPolicyForm extends BasePolicyForm<UniquePasswordPolic
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Number of previous passwords to check")}
|
||||
?required=${true}
|
||||
required
|
||||
name="numHistoricalPasswords"
|
||||
>
|
||||
<input
|
||||
|
@ -62,7 +62,7 @@ export class PropertyMappingProviderRACForm extends BasePropertyMappingForm<RACP
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-group .expanded=${true}>
|
||||
<ak-form-group expanded>
|
||||
<span slot="header"> ${msg("General settings")} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
|
@ -54,7 +54,7 @@ export class PolicyTestForm extends Form<PropertyMappingTestRequest> {
|
||||
${this.result?.successful
|
||||
? html`<ak-codemirror
|
||||
mode=${CodeMirrorMode.JavaScript}
|
||||
?readOnly=${true}
|
||||
readOnly
|
||||
value="${ifDefined(this.result?.result)}"
|
||||
>
|
||||
</ak-codemirror>`
|
||||
|
@ -42,7 +42,7 @@ export class ProviderViewPage extends AKElement {
|
||||
|
||||
renderProvider(): TemplateResult {
|
||||
if (!this.provider) {
|
||||
return html`<ak-empty-state loading ?fullHeight=${true}></ak-empty-state>`;
|
||||
return html`<ak-empty-state loading fullHeight></ak-empty-state>`;
|
||||
}
|
||||
switch (this.provider?.component) {
|
||||
case "ak-provider-saml-form":
|
||||
|
@ -48,7 +48,7 @@ export class GoogleWorkspaceProviderFormPage extends BaseProviderForm<GoogleWork
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
@ -56,12 +56,12 @@ export class GoogleWorkspaceProviderFormPage extends BaseProviderForm<GoogleWork
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-group .expanded=${true}>
|
||||
<ak-form-group expanded>
|
||||
<span slot="header"> ${msg("Protocol settings")} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Credentials")}
|
||||
?required=${true}
|
||||
required
|
||||
name="credentials"
|
||||
>
|
||||
<ak-codemirror
|
||||
@ -74,7 +74,7 @@ export class GoogleWorkspaceProviderFormPage extends BaseProviderForm<GoogleWork
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Delegated Subject")}
|
||||
?required=${true}
|
||||
required
|
||||
name="delegatedSubject"
|
||||
>
|
||||
<input
|
||||
@ -91,7 +91,7 @@ export class GoogleWorkspaceProviderFormPage extends BaseProviderForm<GoogleWork
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Default group email domain")}
|
||||
?required=${true}
|
||||
required
|
||||
name="defaultGroupEmailDomain"
|
||||
>
|
||||
<input
|
||||
@ -181,7 +181,7 @@ export class GoogleWorkspaceProviderFormPage extends BaseProviderForm<GoogleWork
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
<ak-form-group ?expanded=${true}>
|
||||
<ak-form-group expanded>
|
||||
<span slot="header">${msg("User filtering")}</span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal name="excludeUsersServiceAccount">
|
||||
@ -225,7 +225,7 @@ export class GoogleWorkspaceProviderFormPage extends BaseProviderForm<GoogleWork
|
||||
.selected=${(group: Group): boolean => {
|
||||
return group.pk === this.instance?.filterGroup;
|
||||
}}
|
||||
?blankable=${true}
|
||||
blankable
|
||||
>
|
||||
</ak-search-select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
@ -234,7 +234,7 @@ export class GoogleWorkspaceProviderFormPage extends BaseProviderForm<GoogleWork
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
<ak-form-group ?expanded=${true}>
|
||||
<ak-form-group expanded>
|
||||
<span slot="header"> ${msg("Attribute mapping")} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
|
@ -86,7 +86,7 @@ export function renderForm(
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Bind flow")}
|
||||
?required=${true}
|
||||
required
|
||||
name="authorizationFlow"
|
||||
.errorMessages=${errors?.authorizationFlow ?? []}
|
||||
>
|
||||
@ -127,7 +127,7 @@ export function renderForm(
|
||||
label=${msg("Base DN")}
|
||||
required
|
||||
value="${provider?.baseDn ?? "DC=ldap,DC=goauthentik,DC=io"}"
|
||||
inputHint="code"
|
||||
input-hint="code"
|
||||
.errorMessages=${errors?.baseDn ?? []}
|
||||
help=${msg(
|
||||
"LDAP DN under which bind requests and search requests can be made.",
|
||||
@ -154,7 +154,7 @@ export function renderForm(
|
||||
value="${provider?.tlsServerName ?? ""}"
|
||||
.errorMessages=${errors?.tlsServerName ?? []}
|
||||
help=${tlsServerNameHelp}
|
||||
inputHint="code"
|
||||
input-hint="code"
|
||||
></ak-text-input>
|
||||
|
||||
<ak-number-input
|
||||
|
@ -46,7 +46,7 @@ export class MicrosoftEntraProviderFormPage extends BaseProviderForm<MicrosoftEn
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
@ -54,14 +54,10 @@ export class MicrosoftEntraProviderFormPage extends BaseProviderForm<MicrosoftEn
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-group .expanded=${true}>
|
||||
<ak-form-group expanded>
|
||||
<span slot="header"> ${msg("Protocol settings")} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Client ID")}
|
||||
?required=${true}
|
||||
name="clientId"
|
||||
>
|
||||
<ak-form-element-horizontal label=${msg("Client ID")} required name="clientId">
|
||||
<input
|
||||
type="text"
|
||||
value="${this.instance?.clientId ?? ""}"
|
||||
@ -74,7 +70,7 @@ export class MicrosoftEntraProviderFormPage extends BaseProviderForm<MicrosoftEn
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Client Secret")}
|
||||
?required=${true}
|
||||
required
|
||||
name="clientSecret"
|
||||
>
|
||||
<input
|
||||
@ -87,11 +83,7 @@ export class MicrosoftEntraProviderFormPage extends BaseProviderForm<MicrosoftEn
|
||||
${msg("Client secret for the app registration.")}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Tenant ID")}
|
||||
?required=${true}
|
||||
name="tenantId"
|
||||
>
|
||||
<ak-form-element-horizontal label=${msg("Tenant ID")} required name="tenantId">
|
||||
<input
|
||||
type="text"
|
||||
value="${this.instance?.tenantId ?? ""}"
|
||||
@ -170,7 +162,7 @@ export class MicrosoftEntraProviderFormPage extends BaseProviderForm<MicrosoftEn
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
<ak-form-group ?expanded=${true}>
|
||||
<ak-form-group expanded>
|
||||
<span slot="header">${msg("User filtering")}</span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal name="excludeUsersServiceAccount">
|
||||
@ -214,7 +206,7 @@ export class MicrosoftEntraProviderFormPage extends BaseProviderForm<MicrosoftEn
|
||||
.selected=${(group: Group): boolean => {
|
||||
return group.pk === this.instance?.filterGroup;
|
||||
}}
|
||||
?blankable=${true}
|
||||
blankable
|
||||
>
|
||||
</ak-search-select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
@ -223,7 +215,7 @@ export class MicrosoftEntraProviderFormPage extends BaseProviderForm<MicrosoftEn
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
<ak-form-group ?expanded=${true}>
|
||||
<ak-form-group expanded>
|
||||
<span slot="header"> ${msg("Attribute mapping")} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
|
@ -133,7 +133,7 @@ export function renderForm(
|
||||
<ak-form-element-horizontal
|
||||
name="authorizationFlow"
|
||||
label=${msg("Authorization flow")}
|
||||
?required=${true}
|
||||
required
|
||||
>
|
||||
<ak-flow-search
|
||||
flowType=${FlowsInstancesListDesignationEnum.Authorization}
|
||||
@ -163,14 +163,14 @@ export function renderForm(
|
||||
label=${msg("Client ID")}
|
||||
value="${provider?.clientId ?? randomString(40, ascii_letters + digits)}"
|
||||
required
|
||||
inputHint="code"
|
||||
input-hint="code"
|
||||
>
|
||||
</ak-text-input>
|
||||
<ak-text-input
|
||||
name="clientSecret"
|
||||
label=${msg("Client Secret")}
|
||||
value="${provider?.clientSecret ?? randomString(128, ascii_letters + digits)}"
|
||||
inputHint="code"
|
||||
input-hint="code"
|
||||
?hidden=${!showClientSecret}
|
||||
>
|
||||
</ak-text-input>
|
||||
@ -252,7 +252,7 @@ export function renderForm(
|
||||
<ak-text-input
|
||||
name="accessCodeValidity"
|
||||
label=${msg("Access code validity")}
|
||||
inputHint="code"
|
||||
input-hint="code"
|
||||
required
|
||||
value="${provider?.accessCodeValidity ?? "minutes=1"}"
|
||||
.bighelp=${html`<p class="pf-c-form__helper-text">
|
||||
@ -265,7 +265,7 @@ export function renderForm(
|
||||
name="accessTokenValidity"
|
||||
label=${msg("Access Token validity")}
|
||||
value="${provider?.accessTokenValidity ?? "minutes=5"}"
|
||||
inputHint="code"
|
||||
input-hint="code"
|
||||
required
|
||||
.bighelp=${html` <p class="pf-c-form__helper-text">
|
||||
${msg("Configure how long access tokens are valid for.")}
|
||||
@ -278,8 +278,8 @@ export function renderForm(
|
||||
name="refreshTokenValidity"
|
||||
label=${msg("Refresh Token validity")}
|
||||
value="${provider?.refreshTokenValidity ?? "days=30"}"
|
||||
inputHint="code"
|
||||
?required=${true}
|
||||
input-hint="code"
|
||||
required
|
||||
.bighelp=${html` <p class="pf-c-form__helper-text">
|
||||
${msg("Configure how long refresh tokens are valid for.")}
|
||||
</p>
|
||||
|
@ -416,7 +416,7 @@ export class OAuth2ProviderViewPage extends AKElement {
|
||||
.selected=${(user: User): boolean => {
|
||||
return user.pk === this.previewUser?.pk;
|
||||
}}
|
||||
?blankable=${true}
|
||||
blankable
|
||||
@ak-change=${(ev: CustomEvent) => {
|
||||
this.previewUser = ev.detail.value;
|
||||
this.fetchPreview();
|
||||
|
@ -48,7 +48,7 @@ function renderHttpBasic(provider: Partial<ProxyProvider>) {
|
||||
help=${msg(
|
||||
"User/Group Attribute used for the user part of the HTTP-Basic Header. If not set, the user's Email address is used.",
|
||||
)}
|
||||
inputHint="code"
|
||||
input-hint="code"
|
||||
>
|
||||
</ak-text-input>
|
||||
|
||||
@ -57,7 +57,7 @@ function renderHttpBasic(provider: Partial<ProxyProvider>) {
|
||||
label=${msg("HTTP-Basic Password Key")}
|
||||
value="${ifDefined(provider?.basicAuthPasswordAttribute)}"
|
||||
help=${msg("User/Group Attribute used for the password part of the HTTP-Basic Header.")}
|
||||
inputHint="code"
|
||||
input-hint="code"
|
||||
>
|
||||
</ak-text-input>`;
|
||||
}
|
||||
@ -90,7 +90,7 @@ function renderProxySettings(provider: Partial<ProxyProvider>, errors?: Validati
|
||||
help=${msg(
|
||||
"The external URL you'll access the application at. Include any non-standard port.",
|
||||
)}
|
||||
inputHint="code"
|
||||
input-hint="code"
|
||||
></ak-text-input>
|
||||
<ak-text-input
|
||||
name="internalHost"
|
||||
@ -99,7 +99,7 @@ function renderProxySettings(provider: Partial<ProxyProvider>, errors?: Validati
|
||||
required
|
||||
.errorMessages=${errors?.internalHost ?? []}
|
||||
help=${msg("Upstream host that the requests are forwarded to.")}
|
||||
inputHint="code"
|
||||
input-hint="code"
|
||||
></ak-text-input>
|
||||
|
||||
<ak-switch-input
|
||||
@ -126,7 +126,7 @@ function renderForwardSingleSettings(provider: Partial<ProxyProvider>, errors?:
|
||||
help=${msg(
|
||||
"The external URL you'll access the application at. Include any non-standard port.",
|
||||
)}
|
||||
inputHint="code"
|
||||
input-hint="code"
|
||||
></ak-text-input>`;
|
||||
}
|
||||
|
||||
@ -225,7 +225,7 @@ export function renderForm(
|
||||
.errorMessages=${errors?.accessTokenValidity ?? []}
|
||||
required
|
||||
.help=${msg("Configure how long tokens are valid for.")}
|
||||
inputHint="code"
|
||||
input-hint="code"
|
||||
></ak-text-input>
|
||||
|
||||
<ak-form-group>
|
||||
|
@ -53,7 +53,7 @@ export class EndpointForm extends ModelForm<Endpoint, string> {
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`
|
||||
<ak-form-element-horizontal label=${msg("Name")} name="name" ?required=${true}>
|
||||
<ak-form-element-horizontal label=${msg("Name")} name="name" required>
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
@ -61,7 +61,7 @@ export class EndpointForm extends ModelForm<Endpoint, string> {
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Protocol")} ?required=${true} name="protocol">
|
||||
<ak-form-element-horizontal label=${msg("Protocol")} required name="protocol">
|
||||
<ak-radio
|
||||
.options=${[
|
||||
{
|
||||
@ -81,7 +81,7 @@ export class EndpointForm extends ModelForm<Endpoint, string> {
|
||||
>
|
||||
</ak-radio>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Host")} name="host" ?required=${true}>
|
||||
<ak-form-element-horizontal label=${msg("Host")} name="host" required>
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.host)}"
|
||||
@ -93,7 +93,7 @@ export class EndpointForm extends ModelForm<Endpoint, string> {
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Maximum concurrent connections")}
|
||||
name="maximumConnections"
|
||||
?required=${true}
|
||||
required
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
|
@ -49,7 +49,7 @@ export class RACProviderFormPage extends ModelForm<RACProvider, number> {
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`
|
||||
<ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
<ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
@ -61,7 +61,7 @@ export class RACProviderFormPage extends ModelForm<RACProvider, number> {
|
||||
<ak-form-element-horizontal
|
||||
name="authorizationFlow"
|
||||
label=${msg("Authorization flow")}
|
||||
?required=${true}
|
||||
required
|
||||
>
|
||||
<ak-flow-search
|
||||
flowType=${FlowsInstancesListDesignationEnum.Authorization}
|
||||
@ -74,7 +74,7 @@ export class RACProviderFormPage extends ModelForm<RACProvider, number> {
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Connection expiry")}
|
||||
?required=${true}
|
||||
required
|
||||
name="connectionExpiry"
|
||||
>
|
||||
<input
|
||||
@ -115,7 +115,7 @@ export class RACProviderFormPage extends ModelForm<RACProvider, number> {
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-form-group .expanded=${true}>
|
||||
<ak-form-group expanded>
|
||||
<span slot="header"> ${msg("Protocol settings")} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
|
@ -80,7 +80,7 @@ export function renderForm(
|
||||
.errorMessages=${errors?.sharedSecret ?? []}
|
||||
value=${provider?.sharedSecret ?? randomString(128, ascii_letters + digits)}
|
||||
required
|
||||
inputHint="code"
|
||||
input-hint="code"
|
||||
></ak-text-input>
|
||||
<ak-text-input
|
||||
name="clientNetworks"
|
||||
@ -89,7 +89,7 @@ export function renderForm(
|
||||
.errorMessages=${errors?.clientNetworks ?? []}
|
||||
required
|
||||
help=${clientNetworksHelp}
|
||||
inputHint="code"
|
||||
input-hint="code"
|
||||
></ak-text-input>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Property mappings")}
|
||||
|
@ -84,7 +84,7 @@ export function renderForm(
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-form-group .expanded=${true}>
|
||||
<ak-form-group expanded>
|
||||
<span slot="header"> ${msg("Protocol settings")} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-text-input
|
||||
@ -127,7 +127,6 @@ export function renderForm(
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Authentication flow")}
|
||||
?required=${false}
|
||||
name="authenticationFlow"
|
||||
>
|
||||
<ak-flow-search
|
||||
@ -236,7 +235,7 @@ export function renderForm(
|
||||
.selected=${(item: SAMLPropertyMapping): boolean => {
|
||||
return provider?.nameIdMapping === item.pk;
|
||||
}}
|
||||
?blankable=${true}
|
||||
blankable
|
||||
>
|
||||
</ak-search-select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
@ -271,7 +270,7 @@ export function renderForm(
|
||||
.selected=${(item: SAMLPropertyMapping): boolean => {
|
||||
return provider?.authnContextClassRefMapping === item.pk;
|
||||
}}
|
||||
?blankable=${true}
|
||||
blankable
|
||||
>
|
||||
</ak-search-select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
|
@ -31,12 +31,12 @@ export class SAMLProviderImportForm extends Form<SAMLProvider> {
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
return html`<ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input type="text" class="pf-c-form-control" required />
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Authorization flow")}
|
||||
?required=${true}
|
||||
required
|
||||
name="authorizationFlow"
|
||||
>
|
||||
<ak-flow-search-no-default
|
||||
@ -49,7 +49,7 @@ export class SAMLProviderImportForm extends Form<SAMLProvider> {
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Invalidation flow")}
|
||||
?required=${true}
|
||||
required
|
||||
name="invalidationFlow"
|
||||
>
|
||||
<ak-flow-search-no-default
|
||||
|
@ -489,7 +489,7 @@ export class SAMLProviderViewPage extends AKElement {
|
||||
<div class="pf-c-card__footer">
|
||||
<ak-codemirror
|
||||
mode=${CodeMirrorMode.XML}
|
||||
?readOnly=${true}
|
||||
readOnly
|
||||
value="${ifDefined(this.metadata?.metadata)}"
|
||||
></ak-codemirror>
|
||||
</div>
|
||||
@ -539,7 +539,7 @@ export class SAMLProviderViewPage extends AKElement {
|
||||
.selected=${(user: User): boolean => {
|
||||
return user.pk === this.previewUser?.pk;
|
||||
}}
|
||||
?blankable=${true}
|
||||
blankable
|
||||
@ak-change=${(ev: CustomEvent) => {
|
||||
this.previewUser = ev.detail.value;
|
||||
this.fetchPreview();
|
||||
|
@ -40,7 +40,7 @@ export function renderForm(provider?: Partial<SCIMProvider>, errors: ValidationE
|
||||
.errorMessages=${errors?.url ?? []}
|
||||
required
|
||||
help=${msg("SCIM base url, usually ends in /v2.")}
|
||||
inputHint="code"
|
||||
input-hint="code"
|
||||
></ak-text-input>
|
||||
|
||||
<ak-switch-input
|
||||
@ -59,7 +59,7 @@ export function renderForm(provider?: Partial<SCIMProvider>, errors: ValidationE
|
||||
help=${msg(
|
||||
"Token to authenticate with. Currently only bearer authentication is supported.",
|
||||
)}
|
||||
inputHint="code"
|
||||
input-hint="code"
|
||||
></ak-text-input>
|
||||
<ak-radio-input
|
||||
name="compatibilityMode"
|
||||
|
@ -37,7 +37,6 @@ export class ObjectPermissionsPageForm extends ModelForm<unknown, string> {
|
||||
.model=${this.model}
|
||||
.objectPk=${this.objectPk}
|
||||
slot="form"
|
||||
.embedded=${true}
|
||||
>
|
||||
</ak-rbac-object-permission-page>`;
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ export class RoleForm extends ModelForm<Role, string> {
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
return html`<ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
|
@ -67,7 +67,7 @@ export class RolePermissionForm extends ModelForm<RolePermissionAssign, number>
|
||||
<ak-chip-group>
|
||||
${this.permissionsToAdd.map((permission) => {
|
||||
return html`<ak-chip
|
||||
.removable=${true}
|
||||
removable
|
||||
value=${`${permission.appLabel}.${permission.codename}`}
|
||||
@remove=${() => {
|
||||
const idx = this.permissionsToAdd.indexOf(permission);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user