Compare commits

..

15 Commits

Author SHA1 Message Date
c255ec7e71 root: remove /if/help/
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-01-18 14:14:29 +01:00
b23a6d5359 website/integrations: Fix OIDC attribute mapping for "groups" is specified in Nextcloud (#8214)
Fix typo in OIDC attribute mapping for groups

Signed-off-by: Josh Q <12601483+DonQuinleone@users.noreply.github.com>
2024-01-18 13:57:38 +01:00
212904537b web: bump the sentry group in /web with 1 update (#8219)
Bumps the sentry group in /web with 1 update: @spotlightjs/spotlight.


Updates `@spotlightjs/spotlight` from 1.2.6 to 1.2.7

---
updated-dependencies:
- dependency-name: "@spotlightjs/spotlight"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: sentry
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-18 12:18:13 +01:00
c46cd5e7e5 website/docs: embed install video directly (#8215)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-01-18 12:17:23 +01:00
480a765f5f core: bump pdoc from 14.3.0 to 14.4.0 (#8216)
Bumps [pdoc](https://github.com/mitmproxy/pdoc) from 14.3.0 to 14.4.0.
- [Changelog](https://github.com/mitmproxy/pdoc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mitmproxy/pdoc/compare/v14.3.0...v14.4.0)

---
updated-dependencies:
- dependency-name: pdoc
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-18 12:01:24 +01:00
65b6ea416c website: bump prettier from 3.2.3 to 3.2.4 in /website (#8218)
Bumps [prettier](https://github.com/prettier/prettier) from 3.2.3 to 3.2.4.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.2.3...3.2.4)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-18 12:01:13 +01:00
2f56c3cecf web: bump the storybook group in /web with 7 updates (#8220)
Bumps the storybook group in /web with 7 updates:

| Package | From | To |
| --- | --- | --- |
| [@storybook/addon-essentials](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/essentials) | `7.6.8` | `7.6.9` |
| [@storybook/addon-links](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/links) | `7.6.8` | `7.6.9` |
| [@storybook/api](https://github.com/storybookjs/storybook/tree/HEAD/code/deprecated/manager-api-shim) | `7.6.8` | `7.6.9` |
| [@storybook/manager-api](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/manager-api) | `7.6.8` | `7.6.9` |
| [@storybook/web-components](https://github.com/storybookjs/storybook/tree/HEAD/code/renderers/web-components) | `7.6.8` | `7.6.9` |
| [@storybook/web-components-vite](https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/web-components-vite) | `7.6.8` | `7.6.9` |
| [storybook](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/cli) | `7.6.8` | `7.6.9` |


Updates `@storybook/addon-essentials` from 7.6.8 to 7.6.9
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v7.6.9/code/addons/essentials)

Updates `@storybook/addon-links` from 7.6.8 to 7.6.9
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v7.6.9/code/addons/links)

Updates `@storybook/api` from 7.6.8 to 7.6.9
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/v7.6.9/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v7.6.9/code/deprecated/manager-api-shim)

Updates `@storybook/manager-api` from 7.6.8 to 7.6.9
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v7.6.9/code/lib/manager-api)

Updates `@storybook/web-components` from 7.6.8 to 7.6.9
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v7.6.9/code/renderers/web-components)

Updates `@storybook/web-components-vite` from 7.6.8 to 7.6.9
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v7.6.9/code/frameworks/web-components-vite)

Updates `storybook` from 7.6.8 to 7.6.9
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v7.6.9/code/lib/cli)

---
updated-dependencies:
- dependency-name: "@storybook/addon-essentials"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-links"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/api"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/manager-api"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/web-components"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/web-components-vite"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: storybook
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-18 12:00:53 +01:00
5c1432c670 web: bump prettier from 3.2.3 to 3.2.4 in /tests/wdio (#8222)
Bumps [prettier](https://github.com/prettier/prettier) from 3.2.3 to 3.2.4.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.2.3...3.2.4)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-18 12:00:38 +01:00
c49e83a926 web: bump the wdio group in /tests/wdio with 4 updates (#8221)
Bumps the wdio group in /tests/wdio with 4 updates: [@wdio/cli](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-cli), [@wdio/local-runner](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-local-runner), [@wdio/mocha-framework](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-mocha-framework) and [@wdio/spec-reporter](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-spec-reporter).


Updates `@wdio/cli` from 8.28.0 to 8.28.6
- [Release notes](https://github.com/webdriverio/webdriverio/releases)
- [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webdriverio/webdriverio/commits/v8.28.6/packages/wdio-cli)

Updates `@wdio/local-runner` from 8.28.0 to 8.28.7
- [Release notes](https://github.com/webdriverio/webdriverio/releases)
- [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webdriverio/webdriverio/commits/v8.28.7/packages/wdio-local-runner)

Updates `@wdio/mocha-framework` from 8.28.0 to 8.28.6
- [Release notes](https://github.com/webdriverio/webdriverio/releases)
- [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webdriverio/webdriverio/commits/v8.28.6/packages/wdio-mocha-framework)

Updates `@wdio/spec-reporter` from 8.28.0 to 8.28.6
- [Release notes](https://github.com/webdriverio/webdriverio/releases)
- [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webdriverio/webdriverio/commits/v8.28.6/packages/wdio-spec-reporter)

---
updated-dependencies:
- dependency-name: "@wdio/cli"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: wdio
- dependency-name: "@wdio/local-runner"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: wdio
- dependency-name: "@wdio/mocha-framework"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: wdio
- dependency-name: "@wdio/spec-reporter"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: wdio
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-18 12:00:29 +01:00
0dc2c46d49 ci: bump actions/cache from 3 to 4
Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-18 10:21:29 +01:00
245d8b7b5c translate: Updates for file web/xliff/en.xlf in fr (#8212)
Translate web/xliff/en.xlf in fr

100% translated source file: 'web/xliff/en.xlf'
on 'fr'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-01-17 17:56:32 +00:00
5884af8af1 translate: Updates for file web/xliff/en.xlf in zh-Hans (#8211)
Translate web/xliff/en.xlf in zh-Hans

100% translated source file: 'web/xliff/en.xlf'
on 'zh-Hans'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-01-17 17:42:20 +00:00
08d52be20d translate: Updates for file web/xliff/en.xlf in zh_CN (#8210)
Translate web/xliff/en.xlf in zh_CN

100% translated source file: 'web/xliff/en.xlf'
on 'zh_CN'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-01-17 17:41:58 +00:00
941f05e7fa web/flows: update flow background (#8209)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-01-17 14:25:12 +01:00
493cefaa6e web: clear "blanked" placeholder when present (#15) (#5948)
* web: clear "blanked" placeholder when present (#15)

- Renames "SearchSelect.ts" to "ak-search-select.ts", the better to reflect that it is a web
  component.
- Moves it into an independent folder named "SearchSelect" so that all existing folders that use it
  don't need any renaming or manipulation.
- Refactors SearchSelect.ts in the following ways:
  - Re-arranges the properties declaration so the seven properties actually used by callers are at
    the top; comments and documents every property.
  - Separates out the `renderItem` and `renderEmptyItem` HTML blocks into their own templates.
  - Separates `renderItem` further into `renderItemWithDescription` and
    `RenderItemWithoutDescription`; prior to this, there were multiple conditionals handling the
    description issue
  - Separates `renderItems` into `renderItemsAsGroups` and `renderItems`; this documents what each
    function does and removes multiple conditionals
  - Isolates the `groupedItems()` logic into a single method, moving the *how* away from the *what*.
  - Replaces the manual styling of `renderMenu()` into a lit-element `styleMap()`.  This makes the
    actual render a lot more readable!
  - Refactors the `value` logic into its own method, as a _getter_.
  - Refactors the ad-hoc handlers for `focus`, `input`, and `blur` into functions on the `render()`
    method itself.
    - Alternatively, I could have put the handlers as methods on the ak-search-select Node itself;
      Lit would automatically bind `this` correctly if referenced through the `@event` syntax.
      Moving them *out* of the `render()` method would require significantly more testing, however,
      as that would change the code flow enough it might have risked the original behavior.  By
      leaving them in the `render()` scope, this guarantees their original behavior -- whether that
      behavior is correct or not.
- FIXES #15
  - Having isolated as much functionality as was possible, it was easy to change the `onFocus()`
    event so that when the user focuses on the `<input>` object, if it's currently populated with
    the empty option and the user specified `isBlankable`, clear it.
  - **Notice**: This creates a new, possibly undesirable behavior; since it's not possible to know
    *why* the input object is currently empty, in the event that it is currently empty as a result
    of this clearing there is no way to know when the "empty option" marker needs to be put back.

This is an incredibly complex bit of code, the sort that really shouldn't be written by application
teams. The behavior is undefined in a number of cases, and although none of those cases are fatal,
some of them are quite annoying. I recommend that we seriously consider adopting a third-party
solution.

Selects (and DataLists) are notoriously difficult to get right on the desktop; they are almost
impossible to get right on mobile. Every responsible implementation of Selects has a
"default-to-native" experience on mobile because, for the most part, the mobile native experience is
excellent -- delta wanting two-line `<option>` blocks and `<optiongroup>`s, both of which we do
want.

This component implements:

- Rendering the `<input>` element and handling its behavior
- Rendering the `<select>` element and handling its behavior
- Mediating between these two components
- Fetching the data for the `<select>` component from the back-end
- Filtering the data via a partial-match search through the `<input>` element
- Distinguishing between hard-affirm and soft-affirm "No choice" options
- Dispatching the `<select>` element via a portal, the better to control rendering.

That's a *lot* of responsibilities! And it makes Storybooking this component non-viable. I recommend
breaking this up further, but I've already spent a lot of time just doing the refactoring and
getting the new behavior as right as possible, so for now I'm just going to submit the clean-up and
come back to this later.

* web: refactor search-select and fix placeholder

* web: refactor search-select and fix placeholder; fix misleading comment

* backport changes

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

* Fix display issue when using "grouped" select lists

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2024-01-17 13:44:12 +01:00
37 changed files with 6683 additions and 1277 deletions

View File

@ -172,7 +172,7 @@ jobs:
run: |
docker-compose -f tests/e2e/docker-compose.yml up -d
- id: cache-web
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: web/dist
key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'web/src/**') }}

View File

@ -1,24 +1,6 @@
# syntax=docker/dockerfile:1
# Stage 1: Build website
FROM --platform=${BUILDPLATFORM} docker.io/node:21 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 ./SECURITY.md /work/
RUN npm run build-docs-only
# Stage 2: Build webui
# Stage 1: Build webui
FROM --platform=${BUILDPLATFORM} docker.io/node:21 as web-builder
ENV NODE_ENV=production
@ -36,7 +18,7 @@ COPY ./gen-ts-api /work/web/node_modules/@goauthentik/api
RUN npm run build
# Stage 3: Build go proxy
# Stage 2: Build go proxy
FROM --platform=${BUILDPLATFORM} docker.io/golang:1.21.6-bookworm AS go-builder
ARG TARGETOS
@ -68,7 +50,7 @@ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
--mount=type=cache,id=go-build-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/root/.cache/go-build \
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:v6.1 as geoip
ENV GEOIPUPDATE_EDITION_IDS="GeoLite2-City GeoLite2-ASN"
@ -82,7 +64,7 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
mkdir -p /usr/share/GeoIP && \
/bin/sh -c "/usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
# Stage 5: Python dependencies
# Stage 4: Python dependencies
FROM docker.io/python:3.12.1-slim-bookworm AS python-deps
WORKDIR /ak-root/poetry
@ -107,7 +89,7 @@ RUN --mount=type=bind,target=./pyproject.toml,src=./pyproject.toml \
pip3 install poetry && \
poetry install --only=main --no-ansi --no-interaction
# Stage 6: Run
# Stage 5: Run
FROM docker.io/python:3.12.1-slim-bookworm AS final-image
ARG GIT_BUILD_HASH
@ -149,7 +131,6 @@ 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/help/ /website/help/
COPY --from=geoip /usr/share/GeoIP /geoip
USER 1000

View File

@ -22,7 +22,6 @@ func (ws *WebServer) configureStatic() {
distFs := http.FileServer(http.Dir("./web/dist"))
distHandler := http.StripPrefix("/static/dist/", distFs)
authentikHandler := http.StripPrefix("/static/authentik/", http.FileServer(http.Dir("./web/authentik")))
helpHandler := http.FileServer(http.Dir("./website/help/"))
indexLessRouter.PathPrefix("/static/dist/").Handler(distHandler)
indexLessRouter.PathPrefix("/static/authentik/").Handler(authentikHandler)
@ -42,9 +41,6 @@ func (ws *WebServer) configureStatic() {
indexLessRouter.PathPrefix("/media/").Handler(http.StripPrefix("/media", fs))
statRouter.PathPrefix("/if/help/").Handler(http.StripPrefix("/if/help/", helpHandler))
statRouter.PathPrefix("/help").Handler(http.RedirectHandler("/if/help/", http.StatusMovedPermanently))
ws.lh.Path("/robots.txt").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
rw.Header()["Content-Type"] = []string{"text/plain"}
rw.WriteHeader(200)

6
poetry.lock generated
View File

@ -2543,13 +2543,13 @@ files = [
[[package]]
name = "pdoc"
version = "14.3.0"
version = "14.4.0"
description = "API Documentation for Python Projects"
optional = false
python-versions = ">=3.8"
files = [
{file = "pdoc-14.3.0-py3-none-any.whl", hash = "sha256:9a8f9a48bda5a99c249367c2b99779dbdd9f4a56f905068c9c2d6868dbae6882"},
{file = "pdoc-14.3.0.tar.gz", hash = "sha256:40bf8f092fcd91560d5e6cebb7c21b65df699f90a468c8ea316235c3368d5449"},
{file = "pdoc-14.4.0-py3-none-any.whl", hash = "sha256:6ea4fe07620b1f7601e2708a307a257636ec206e20b5611640b30f2e3cab47d6"},
{file = "pdoc-14.4.0.tar.gz", hash = "sha256:c92edc425429ccbe287ace2a027953c24f13de53eab484c1a6d31ca72dd2fda9"},
]
[package.dependencies]

View File

@ -9,15 +9,15 @@
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@typescript-eslint/eslint-plugin": "^6.19.0",
"@typescript-eslint/parser": "^6.19.0",
"@wdio/cli": "^8.28.0",
"@wdio/local-runner": "^8.28.0",
"@wdio/mocha-framework": "^8.28.0",
"@wdio/spec-reporter": "^8.28.0",
"@wdio/cli": "^8.28.6",
"@wdio/local-runner": "^8.28.7",
"@wdio/mocha-framework": "^8.28.6",
"@wdio/spec-reporter": "^8.28.6",
"eslint": "^8.56.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-sonarjs": "^0.23.0",
"npm-run-all": "^4.1.5",
"prettier": "^3.2.3",
"prettier": "^3.2.4",
"ts-node": "^10.9.2",
"typescript": "^5.3.3",
"wdio-wait-for": "^3.0.10"
@ -1166,18 +1166,18 @@
"dev": true
},
"node_modules/@wdio/cli": {
"version": "8.28.0",
"resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.28.0.tgz",
"integrity": "sha512-pC15cIh1N14R6qidiymSmKtXEvvJBGrrWqKi8vlVVfby81TQEpfD2VqymzqDqv+YsLIp2J4XD8rwwcSvqtA9UA==",
"version": "8.28.6",
"resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.28.6.tgz",
"integrity": "sha512-cBgm/RA12tlKGqIywsqAJaACST2tbcBtbkNl16io88iiBKAWOMlK5tX75+5dNEeyKs7JlQqBJ+3toXcypf68tA==",
"dev": true,
"dependencies": {
"@types/node": "^20.1.1",
"@wdio/config": "8.28.0",
"@wdio/globals": "8.28.0",
"@wdio/config": "8.28.6",
"@wdio/globals": "8.28.6",
"@wdio/logger": "8.28.0",
"@wdio/protocols": "8.24.12",
"@wdio/types": "8.28.0",
"@wdio/utils": "8.28.0",
"@wdio/types": "8.28.6",
"@wdio/utils": "8.28.6",
"async-exit-hook": "^2.0.1",
"chalk": "^5.2.0",
"chokidar": "^3.5.3",
@ -1192,7 +1192,7 @@
"lodash.union": "^4.6.0",
"read-pkg-up": "^10.0.0",
"recursive-readdir": "^2.2.3",
"webdriverio": "8.28.0",
"webdriverio": "8.28.6",
"yargs": "^17.7.2"
},
"bin": {
@ -1215,14 +1215,14 @@
}
},
"node_modules/@wdio/config": {
"version": "8.28.0",
"resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.28.0.tgz",
"integrity": "sha512-uXav11uUZSqbYyXGLzyggO8togdm6Bjdjkg8f0zZe4nQpqKpLAkcH7jRiekhuj7oIV5hZai6w5YFhFy5nsw/QA==",
"version": "8.28.6",
"resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.28.6.tgz",
"integrity": "sha512-rJ7GFnzg55MvG/CmN3rX79fFFBMcpoFZpILPTNaWJg43lBxidHue5pm7kXJT06D41sSJJPbtgoh6w4VPThIJrg==",
"dev": true,
"dependencies": {
"@wdio/logger": "8.28.0",
"@wdio/types": "8.28.0",
"@wdio/utils": "8.28.0",
"@wdio/types": "8.28.6",
"@wdio/utils": "8.28.6",
"decamelize": "^6.0.0",
"deepmerge-ts": "^5.0.0",
"glob": "^10.2.2",
@ -1233,29 +1233,29 @@
}
},
"node_modules/@wdio/globals": {
"version": "8.28.0",
"resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.28.0.tgz",
"integrity": "sha512-wVgkHOsKskZYu6FPaJpT19tYul3hi7nkB/TayYIk1rQTRuf3hoP2vHVjibGsU9W3JdhrZA/MUNTm5yrID70KZA==",
"version": "8.28.6",
"resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.28.6.tgz",
"integrity": "sha512-6Wjk7iWnpK1ft/caTAhDXxlmwU+pARouzCssqUtdO/vlvP1VhUo3xArCA2dzzJgE+uD16KLR63DRpgkTdwWPkQ==",
"dev": true,
"engines": {
"node": "^16.13 || >=18"
},
"optionalDependencies": {
"expect-webdriverio": "^4.8.0",
"webdriverio": "8.28.0"
"webdriverio": "8.28.6"
}
},
"node_modules/@wdio/local-runner": {
"version": "8.28.0",
"resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.28.0.tgz",
"integrity": "sha512-zPev8IfItJtIWArTRyr9XjPu4Kp4kO0B/NAJmGQgDauLMBBzciwf35tPp1CFmggGtaO4czryAclyk2CsCkkAoA==",
"version": "8.28.7",
"resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.28.7.tgz",
"integrity": "sha512-QOeJluWEV3My+41f4kHe7Bo08UcSd3TvH4TbPMflOkl0VvGqxJuKfXlllkQhJ8w5e53fBRsf368vzHscbX86/A==",
"dev": true,
"dependencies": {
"@types/node": "^20.1.0",
"@wdio/logger": "8.28.0",
"@wdio/repl": "8.24.12",
"@wdio/runner": "8.28.0",
"@wdio/types": "8.28.0",
"@wdio/runner": "8.28.7",
"@wdio/types": "8.28.6",
"async-exit-hook": "^2.0.1",
"split2": "^4.1.0",
"stream-buffers": "^3.0.2"
@ -1292,16 +1292,16 @@
}
},
"node_modules/@wdio/mocha-framework": {
"version": "8.28.0",
"resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.28.0.tgz",
"integrity": "sha512-ykXbJXu2sb7agTnLP6Tv2Ak+ee3ZG3Tag2bT30j30yiulnNpIIuHMuSgrxDs2NKzlF2Q4qla/SrjiMPSoZYhsw==",
"version": "8.28.6",
"resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.28.6.tgz",
"integrity": "sha512-3jTI1GilFGZROJi+UCywEt/ac0b8ETQkG9YlkTAVcxMqG+wUKY2Ks7ow/UDGAaYMHkrEPdopvUFGZmJmpNMKUg==",
"dev": true,
"dependencies": {
"@types/mocha": "^10.0.0",
"@types/node": "^20.1.0",
"@wdio/logger": "8.28.0",
"@wdio/types": "8.28.0",
"@wdio/utils": "8.28.0",
"@wdio/types": "8.28.6",
"@wdio/utils": "8.28.6",
"mocha": "^10.0.0"
},
"engines": {
@ -1327,14 +1327,14 @@
}
},
"node_modules/@wdio/reporter": {
"version": "8.28.0",
"resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-8.28.0.tgz",
"integrity": "sha512-O2MfFv1xIm95cBnTgZINpiejVlG8LpiXrvlmj9FPPiGzcWIsERtbTHDb6+8UagQZv9nBLmISLARUyR7XRYfwLQ==",
"version": "8.28.6",
"resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-8.28.6.tgz",
"integrity": "sha512-2KUSytk75fjT4XGEL43u1XLzZSvu1SUg3a7h8fRm2fU2q1dCC16TJk5eod4eSxHzjm2fL1vwFQIAZkwO3WyQZQ==",
"dev": true,
"dependencies": {
"@types/node": "^20.1.0",
"@wdio/logger": "8.28.0",
"@wdio/types": "8.28.0",
"@wdio/types": "8.28.6",
"diff": "^5.0.0",
"object-inspect": "^1.12.0"
},
@ -1343,35 +1343,35 @@
}
},
"node_modules/@wdio/runner": {
"version": "8.28.0",
"resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.28.0.tgz",
"integrity": "sha512-IPaYSSjN6DDn75gDfzGQtFAu5oE1ee90L2xzXCYXK7xR4xr4O0IgNtFTq5cuLZsPRzJsGoq3z+1GxGSogC3u1A==",
"version": "8.28.7",
"resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.28.7.tgz",
"integrity": "sha512-qk/3cMccCLXrIMN4a/vqmL0UeKaGXDclL/8buWC61IeZZ3SpDUgOoCJH47ULrkIeuoXXrnttZvo12fGjb8zmOg==",
"dev": true,
"dependencies": {
"@types/node": "^20.1.0",
"@wdio/config": "8.28.0",
"@wdio/globals": "8.28.0",
"@wdio/config": "8.28.6",
"@wdio/globals": "8.28.6",
"@wdio/logger": "8.28.0",
"@wdio/types": "8.28.0",
"@wdio/utils": "8.28.0",
"@wdio/types": "8.28.6",
"@wdio/utils": "8.28.6",
"deepmerge-ts": "^5.0.0",
"expect-webdriverio": "^4.8.0",
"gaze": "^1.1.2",
"webdriver": "8.28.0",
"webdriverio": "8.28.0"
"webdriver": "8.28.6",
"webdriverio": "8.28.6"
},
"engines": {
"node": "^16.13 || >=18"
}
},
"node_modules/@wdio/spec-reporter": {
"version": "8.28.0",
"resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-8.28.0.tgz",
"integrity": "sha512-ukyViS7KbeL7Q0+8gHqx1C4YZJT5ne1/6fi0dbgnGNPe+R7c76vSmf0lIGGiqEcDV1W27E1OoL17NwRKBIt0Pw==",
"version": "8.28.6",
"resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-8.28.6.tgz",
"integrity": "sha512-ZMwbM7hxcV0DumGaK0Y9jzPgTjwYHN539FFcEgbYZV1eNKUyIi9cdEP2xIWZiYQHmq2eKBMlUEPA87mwVZ1V6w==",
"dev": true,
"dependencies": {
"@wdio/reporter": "8.28.0",
"@wdio/types": "8.28.0",
"@wdio/reporter": "8.28.6",
"@wdio/types": "8.28.6",
"chalk": "^5.1.2",
"easy-table": "^1.2.0",
"pretty-ms": "^7.0.0"
@ -1393,9 +1393,9 @@
}
},
"node_modules/@wdio/types": {
"version": "8.28.0",
"resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.28.0.tgz",
"integrity": "sha512-4/mUn3IGNa1GTiV0PMOtl1sRqStpbHOQldxz4Vheh0lYNc15W12jXRm84CwGsV6UW93GO9W2K9EprFJsUjc9sg==",
"version": "8.28.6",
"resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.28.6.tgz",
"integrity": "sha512-FU3mMRqULpc2XYh6DrSo/KgNoaS6EO9GFJQX5q7+EiOAqVeo1TCLggvAWIPayKyjfD1/ctd9q+uW9vmNicaOjw==",
"dev": true,
"dependencies": {
"@types/node": "^20.1.0"
@ -1405,14 +1405,14 @@
}
},
"node_modules/@wdio/utils": {
"version": "8.28.0",
"resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.28.0.tgz",
"integrity": "sha512-v3xDJuQShLSfHW/Ee0y3z9ZtiV/UrILlucgKBCwCpLwHnO5HhfAH4Ehirt0yzQvYz+Pn9BuOXJImD/wsSbJtLw==",
"version": "8.28.6",
"resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.28.6.tgz",
"integrity": "sha512-EYQmGvejMiTMB18lW3CDc6cR+jXXjxDebP6ci53oK20QPx9VBuMQZdCbuoftKrKVLA+e9Fk0XfXq8xWYbjAvBQ==",
"dev": true,
"dependencies": {
"@puppeteer/browsers": "^1.6.0",
"@wdio/logger": "8.28.0",
"@wdio/types": "8.28.0",
"@wdio/types": "8.28.6",
"decamelize": "^6.0.0",
"deepmerge-ts": "^5.1.0",
"edgedriver": "^5.3.5",
@ -6508,9 +6508,9 @@
}
},
"node_modules/prettier": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.3.tgz",
"integrity": "sha512-QNhUTBq+mqt1oH1dTfY3phOKNhcDdJkfttHI6u0kj7M2+c+7fmNKlgh2GhnHiqMcbxJ+a0j2igz/2jfl9QKLuw==",
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.4.tgz",
"integrity": "sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==",
"dev": true,
"bin": {
"prettier": "bin/prettier.cjs"
@ -8520,18 +8520,18 @@
}
},
"node_modules/webdriver": {
"version": "8.28.0",
"resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.28.0.tgz",
"integrity": "sha512-1ASMK+sNfVh5rdaRRk+eFLIfae93ViXHJBpuJemeORwZkfOJNF2CNSZl5uK2e6+nzbkY2cjM6QsZwfhL3lCiRg==",
"version": "8.28.6",
"resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.28.6.tgz",
"integrity": "sha512-qKZuG2uqGhq2xjk14vSAvE3C6TTTYQyOqDHQOSWNzPNhdBI99g2h4EUbmO3bc/5YaWRsVWWp+RB7jfQZUcE/MA==",
"dev": true,
"dependencies": {
"@types/node": "^20.1.0",
"@types/ws": "^8.5.3",
"@wdio/config": "8.28.0",
"@wdio/config": "8.28.6",
"@wdio/logger": "8.28.0",
"@wdio/protocols": "8.24.12",
"@wdio/types": "8.28.0",
"@wdio/utils": "8.28.0",
"@wdio/types": "8.28.6",
"@wdio/utils": "8.28.6",
"deepmerge-ts": "^5.1.0",
"got": "^12.6.1",
"ky": "^0.33.0",
@ -8542,18 +8542,18 @@
}
},
"node_modules/webdriverio": {
"version": "8.28.0",
"resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.28.0.tgz",
"integrity": "sha512-rRVE8pvcxAEqnhhC70oMFkUZ82YWbpXYyzKgfl2LKBue13AHaiN5qWncsJv29rqREIim0dNj6q2JuuUTDFm1gg==",
"version": "8.28.6",
"resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.28.6.tgz",
"integrity": "sha512-vWo3Qx0bWubzUBBLaLYgeQkSN63KTfW5P3IbayhJ8Qx+NVVnclG0mu4mQj00gvj8+3a8opef6T54gdzitVSHSw==",
"dev": true,
"dependencies": {
"@types/node": "^20.1.0",
"@wdio/config": "8.28.0",
"@wdio/config": "8.28.6",
"@wdio/logger": "8.28.0",
"@wdio/protocols": "8.24.12",
"@wdio/repl": "8.24.12",
"@wdio/types": "8.28.0",
"@wdio/utils": "8.28.0",
"@wdio/types": "8.28.6",
"@wdio/utils": "8.28.6",
"archiver": "^6.0.0",
"aria-query": "^5.0.0",
"css-shorthand-properties": "^1.1.1",
@ -8570,7 +8570,7 @@
"resq": "^1.9.1",
"rgb2hex": "0.2.5",
"serialize-error": "^11.0.1",
"webdriver": "8.28.0"
"webdriver": "8.28.6"
},
"engines": {
"node": "^16.13 || >=18"

View File

@ -6,15 +6,15 @@
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@typescript-eslint/eslint-plugin": "^6.19.0",
"@typescript-eslint/parser": "^6.19.0",
"@wdio/cli": "^8.28.0",
"@wdio/local-runner": "^8.28.0",
"@wdio/mocha-framework": "^8.28.0",
"@wdio/spec-reporter": "^8.28.0",
"@wdio/cli": "^8.28.6",
"@wdio/local-runner": "^8.28.7",
"@wdio/mocha-framework": "^8.28.6",
"@wdio/spec-reporter": "^8.28.6",
"eslint": "^8.56.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-sonarjs": "^0.23.0",
"npm-run-all": "^4.1.5",
"prettier": "^3.2.3",
"prettier": "^3.2.4",
"ts-node": "^10.9.2",
"typescript": "^5.3.3",
"wdio-wait-for": "^3.0.10"

668
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -87,14 +87,14 @@
"@rollup/plugin-replace": "^5.0.5",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^11.1.6",
"@spotlightjs/spotlight": "^1.2.6",
"@storybook/addon-essentials": "^7.6.8",
"@storybook/addon-links": "^7.6.8",
"@storybook/api": "^7.6.8",
"@spotlightjs/spotlight": "^1.2.7",
"@storybook/addon-essentials": "^7.6.9",
"@storybook/addon-links": "^7.6.9",
"@storybook/api": "^7.6.9",
"@storybook/blocks": "^7.6.4",
"@storybook/manager-api": "^7.6.8",
"@storybook/web-components": "^7.6.8",
"@storybook/web-components-vite": "^7.6.8",
"@storybook/manager-api": "^7.6.9",
"@storybook/web-components": "^7.6.9",
"@storybook/web-components-vite": "^7.6.9",
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/chart.js": "^2.9.41",
"@types/codemirror": "5.60.15",
@ -123,7 +123,7 @@
"rollup-plugin-cssimport": "^1.0.3",
"rollup-plugin-modify": "^3.0.0",
"rollup-plugin-postcss-lit": "^2.1.0",
"storybook": "^7.6.8",
"storybook": "^7.6.9",
"storybook-addon-mock": "^4.3.0",
"ts-lit-plugin": "^2.0.2",
"tslib": "^2.6.2",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 717 KiB

After

Width:  |  Height:  |  Size: 699 KiB

View File

@ -134,9 +134,11 @@ html > form > input {
);
max-height: 9rem;
}
.ak-brand {
display: flex;
justify-content: center;
width: 100%;
}
.ak-brand img {
padding: 0 2rem;

View File

@ -1,324 +0,0 @@
import { PreventFormSubmit } from "@goauthentik/app/elements/forms/helpers";
import { EVENT_REFRESH } from "@goauthentik/common/constants";
import { ascii_letters, digits, groupBy, randomString } from "@goauthentik/common/utils";
import { adaptCSS } from "@goauthentik/common/utils";
import { AKElement } from "@goauthentik/elements/Base";
import { CustomEmitterElement } from "@goauthentik/elements/utils/eventEmitter";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, html, render } from "lit";
import { customElement, property } from "lit/decorators.js";
import PFDropdown from "@patternfly/patternfly/components/Dropdown/dropdown.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
import PFSelect from "@patternfly/patternfly/components/Select/select.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
@customElement("ak-search-select")
export class SearchSelect<T> extends CustomEmitterElement(AKElement) {
@property()
query?: string;
@property({ attribute: false })
objects?: T[];
@property({ attribute: false })
selectedObject?: T;
@property()
name?: string;
@property({ type: Boolean })
open = false;
@property({ type: Boolean })
blankable = false;
@property()
placeholder: string = msg("Select an object.");
static get styles(): CSSResult[] {
return [PFBase, PFForm, PFFormControl, PFSelect];
}
@property({ attribute: false })
fetchObjects!: (query?: string) => Promise<T[]>;
@property({ attribute: false })
renderElement!: (element: T) => string;
@property({ attribute: false })
renderDescription?: (element: T) => TemplateResult;
@property({ attribute: false })
value!: (element: T | undefined) => unknown;
@property({ attribute: false })
selected?: (element: T, elements: T[]) => boolean;
@property()
emptyOption = "---------";
@property({ attribute: false })
groupBy: (items: T[]) => [string, T[]][] = (items: T[]): [string, T[]][] => {
return groupBy(items, () => {
return "";
});
};
scrollHandler?: () => void;
observer: IntersectionObserver;
dropdownUID: string;
dropdownContainer: HTMLDivElement;
isFetchingData = false;
constructor() {
super();
if (!document.adoptedStyleSheets.includes(PFDropdown)) {
document.adoptedStyleSheets = adaptCSS([...document.adoptedStyleSheets, PFDropdown]);
}
this.dropdownContainer = document.createElement("div");
this.observer = new IntersectionObserver(() => {
this.open = false;
this.shadowRoot
?.querySelectorAll<HTMLInputElement>(
".pf-c-form-control.pf-c-select__toggle-typeahead",
)
.forEach((input) => {
input.blur();
});
});
this.observer.observe(this);
this.dropdownUID = `dropdown-${randomString(10, ascii_letters + digits)}`;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
shouldUpdate(changedProperties: Map<string, any>) {
if (changedProperties.has("selectedObject")) {
this.dispatchCustomEvent("ak-change", {
value: this.selectedObject,
});
}
return true;
}
toForm(): unknown {
if (!this.objects) {
throw new PreventFormSubmit(msg("Loading options..."));
}
return this.value(this.selectedObject) || "";
}
firstUpdated(): void {
this.updateData();
}
updateData(): void {
if (this.isFetchingData) {
return;
}
this.isFetchingData = true;
this.fetchObjects(this.query).then((objects) => {
objects.forEach((obj) => {
if (this.selected && this.selected(obj, objects || [])) {
this.selectedObject = obj;
}
});
this.objects = objects;
this.isFetchingData = false;
});
}
connectedCallback(): void {
super.connectedCallback();
this.dropdownContainer = document.createElement("div");
this.dropdownContainer.dataset["managedBy"] = "ak-search-select";
if (this.name) {
this.dropdownContainer.dataset["managedFor"] = this.name;
}
document.body.append(this.dropdownContainer);
this.updateData();
this.addEventListener(EVENT_REFRESH, this.updateData);
this.scrollHandler = () => {
this.requestUpdate();
};
window.addEventListener("scroll", this.scrollHandler);
}
disconnectedCallback(): void {
super.disconnectedCallback();
this.removeEventListener(EVENT_REFRESH, this.updateData);
if (this.scrollHandler) {
window.removeEventListener("scroll", this.scrollHandler);
}
this.dropdownContainer.remove();
this.observer.disconnect();
}
/*
* This is a little bit hacky. Because we mainly want to use this field in modal-based forms,
* rendering this menu inline makes the menu not overlay over top of the modal, and cause
* the modal to scroll.
* Hence, we render the menu into the document root, hide it when this menu isn't open
* and remove it on disconnect
* Also to move it to the correct position we're getting this elements's position and use that
* to position the menu
* The other downside this has is that, since we're rendering outside of a shadow root,
* the pf-c-dropdown CSS needs to be loaded on the body.
*/
renderMenu(): void {
if (!this.objects) {
return;
}
const pos = this.getBoundingClientRect();
let groupedItems = this.groupBy(this.objects);
let shouldRenderGroups = true;
if (groupedItems.length === 1) {
if (groupedItems[0].length < 1 || groupedItems[0][0] === "") {
shouldRenderGroups = false;
}
}
if (groupedItems.length === 0) {
shouldRenderGroups = false;
groupedItems = [["", []]];
}
const renderGroup = (items: T[], tabIndexStart: number): TemplateResult => {
return html`${items.map((obj, index) => {
let desc = undefined;
if (this.renderDescription) {
desc = this.renderDescription(obj);
}
return html`
<li>
<button
class="pf-c-dropdown__menu-item ${desc === undefined
? ""
: "pf-m-description"}"
role="option"
@click=${() => {
this.selectedObject = obj;
this.open = false;
}}
tabindex=${index + tabIndexStart}
>
${desc === undefined
? this.renderElement(obj)
: html`
<div class="pf-c-dropdown__menu-item-main">
${this.renderElement(obj)}
</div>
<div class="pf-c-dropdown__menu-item-description">
${desc}
</div>
`}
</button>
</li>
`;
})}`;
};
render(
html`<div
class="pf-c-dropdown pf-m-expanded"
style="position: fixed; inset: 0px auto auto 0px; z-index: 9999; transform: translate(${pos.x}px, ${pos.y +
this.offsetHeight}px); width: ${pos.width}px; ${this.open
? ""
: "visibility: hidden;"}"
>
<ul
class="pf-c-dropdown__menu pf-m-static"
role="listbox"
style="max-height:50vh;overflow-y:auto;"
id=${this.dropdownUID}
tabindex="0"
>
${this.blankable
? html`
<li>
<button
class="pf-c-dropdown__menu-item"
role="option"
@click=${() => {
this.selectedObject = undefined;
this.open = false;
}}
tabindex="0"
>
${this.emptyOption}
</button>
</li>
`
: html``}
${shouldRenderGroups
? html`${groupedItems.map(([group, items], idx) => {
return html`
<section class="pf-c-dropdown__group">
<h1 class="pf-c-dropdown__group-title">${group}</h1>
<ul>
${renderGroup(items, idx)}
</ul>
</section>
`;
})}`
: html`${renderGroup(groupedItems[0][1], 0)}`}
</ul>
</div>`,
this.dropdownContainer,
{ host: this },
);
}
render(): TemplateResult {
this.renderMenu();
let value = "";
if (!this.objects) {
value = msg("Loading...");
} else if (this.selectedObject) {
value = this.renderElement(this.selectedObject);
} else if (this.blankable) {
value = this.emptyOption;
}
return html`<div class="pf-c-select">
<div class="pf-c-select__toggle pf-m-typeahead">
<div class="pf-c-select__toggle-wrapper">
<input
class="pf-c-form-control pf-c-select__toggle-typeahead"
type="text"
placeholder=${this.placeholder}
spellcheck="false"
@input=${(ev: InputEvent) => {
this.query = (ev.target as HTMLInputElement).value;
this.updateData();
}}
@focus=${() => {
this.open = true;
this.renderMenu();
}}
@blur=${(ev: FocusEvent) => {
// For Safari, we get the <ul> element itself here when clicking on one of
// it's buttons, as the container has tabindex set
if (
ev.relatedTarget &&
(ev.relatedTarget as HTMLElement).id === this.dropdownUID
) {
return;
}
// Check if we're losing focus to one of our dropdown items, and if such don't blur
if (ev.relatedTarget instanceof HTMLButtonElement) {
const parentMenu = ev.relatedTarget.closest(
"ul.pf-c-dropdown__menu.pf-m-static",
);
if (parentMenu && parentMenu.id === this.dropdownUID) {
return;
}
}
this.open = false;
this.renderMenu();
}}
.value=${value}
/>
</div>
</div>
</div>`;
}
}

View File

@ -0,0 +1,370 @@
import { PreventFormSubmit } from "@goauthentik/app/elements/forms/helpers";
import { EVENT_REFRESH } from "@goauthentik/common/constants";
import { ascii_letters, digits, groupBy, randomString } from "@goauthentik/common/utils";
import { AKElement } from "@goauthentik/elements/Base";
import { CustomEmitterElement } from "@goauthentik/elements/utils/eventEmitter";
import { msg } from "@lit/localize";
import { TemplateResult, html, render } from "lit";
import { customElement, property } from "lit/decorators.js";
import { styleMap } from "lit/directives/style-map.js";
import PFDropdown from "@patternfly/patternfly/components/Dropdown/dropdown.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
import PFSelect from "@patternfly/patternfly/components/Select/select.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
type Group<T> = [string, T[]];
@customElement("ak-search-select")
export class SearchSelect<T> extends CustomEmitterElement(AKElement) {
// A function which takes the query state object (accepting that it may be empty) and returns a
// new collection of objects.
@property({ attribute: false })
fetchObjects!: (query?: string) => Promise<T[]>;
// A function passed to this object that extracts a string representation of items of the
// collection under search.
@property({ attribute: false })
renderElement!: (element: T) => string;
// A function passed to this object that extracts an HTML representation of additional
// information for items of the collection under search.
@property({ attribute: false })
renderDescription?: (element: T) => TemplateResult;
// A function which returns the currently selected object's primary key, used for serialization
// into forms.
@property({ attribute: false })
value!: (element: T | undefined) => unknown;
// A function passed to this object that determines an object in the collection under search
// should be automatically selected. Only used when the search itself is responsible for
// fetching the data; sets an initial default value.
@property({ attribute: false })
selected?: (element: T, elements: T[]) => boolean;
// A function passed to this object (or using the default below) that groups objects in the
// collection under search into categories.
@property({ attribute: false })
groupBy: (items: T[]) => [string, T[]][] = (items: T[]): [string, T[]][] => {
return groupBy(items, () => {
return "";
});
};
// Whether or not the dropdown component can be left blank
@property({ type: Boolean })
blankable = false;
// An initial string to filter the search contents, and the value of the input which further
// serves to restrict the search
@property()
query?: string;
// The objects currently available under search
@property({ attribute: false })
objects?: T[];
// The currently selected object
@property({ attribute: false })
selectedObject?: T;
// Not used in this object. No known purpose.
@property()
name?: string;
// Whether or not the dropdown component is visible.
@property({ type: Boolean })
open = false;
// The textual placeholder for the search's <input> object, if currently empty. Used as the
// native <input> object's `placeholder` field.
@property()
placeholder: string = msg("Select an object.");
// A textual string representing "The user has affirmed they want to leave the selection blank."
// Only used if `blankable` above is true.
@property()
emptyOption = "---------";
// Handle the behavior of the drop-down when the :host scrolls off the page.
scrollHandler?: () => void;
observer: IntersectionObserver;
// Handle communication between the :host and the portal
dropdownUID: string;
dropdownContainer: HTMLDivElement;
isFetchingData = false;
static styles = [PFBase, PFForm, PFFormControl, PFSelect];
constructor() {
super();
if (!document.adoptedStyleSheets.includes(PFDropdown)) {
document.adoptedStyleSheets = [...document.adoptedStyleSheets, PFDropdown];
}
this.dropdownContainer = document.createElement("div");
this.observer = new IntersectionObserver(() => {
this.open = false;
this.shadowRoot
?.querySelectorAll<HTMLInputElement>(
".pf-c-form-control.pf-c-select__toggle-typeahead",
)
.forEach((input) => {
input.blur();
});
});
this.observer.observe(this);
this.dropdownUID = `dropdown-${randomString(10, ascii_letters + digits)}`;
this.onMenuItemClick = this.onMenuItemClick.bind(this);
this.renderWithMenuGroupTitle = this.renderWithMenuGroupTitle.bind(this);
}
toForm(): unknown {
if (!this.objects) {
throw new PreventFormSubmit(msg("Loading options..."));
}
return this.value(this.selectedObject) || "";
}
firstUpdated(): void {
this.updateData();
}
updateData(): void {
if (this.isFetchingData) {
return;
}
this.isFetchingData = true;
this.fetchObjects(this.query).then((objects) => {
objects.forEach((obj) => {
if (this.selected && this.selected(obj, objects || [])) {
this.selectedObject = obj;
}
});
this.objects = objects;
this.isFetchingData = false;
});
}
connectedCallback(): void {
super.connectedCallback();
this.dropdownContainer = document.createElement("div");
this.dropdownContainer.dataset["managedBy"] = "ak-search-select";
document.body.append(this.dropdownContainer);
this.updateData();
this.addEventListener(EVENT_REFRESH, this.updateData);
this.scrollHandler = () => {
this.requestUpdate();
};
window.addEventListener("scroll", this.scrollHandler);
}
disconnectedCallback(): void {
super.disconnectedCallback();
this.removeEventListener(EVENT_REFRESH, this.updateData);
if (this.scrollHandler) {
window.removeEventListener("scroll", this.scrollHandler);
}
this.dropdownContainer.remove();
this.observer.disconnect();
}
renderMenuItemWithDescription(obj: T, desc: TemplateResult, index: number) {
return html`
<li>
<button
class="pf-c-dropdown__menu-item pf-m-description"
role="option"
@click=${this.onMenuItemClick(obj)}
tabindex=${index}
>
<div class="pf-c-dropdown__menu-item-main">${this.renderElement(obj)}</div>
<div class="pf-c-dropdown__menu-item-description">${desc}</div>
</button>
</li>
`;
}
renderMenuItemWithoutDescription(obj: T, index: number) {
return html`
<li>
<button
class="pf-c-dropdown__menu-item"
role="option"
@click=${this.onMenuItemClick(obj)}
tabindex=${index}
>
${this.renderElement(obj)}
</button>
</li>
`;
}
renderEmptyMenuItem() {
return html`<li>
<button
class="pf-c-dropdown__menu-item"
role="option"
@click=${this.onMenuItemClick(undefined)}
tabindex="0"
>
${this.emptyOption}
</button>
</li>`;
}
onMenuItemClick(obj: T | undefined) {
return () => {
this.selectedObject = obj;
this.open = false;
};
}
renderMenuGroup(items: T[], tabIndexStart: number) {
const renderedItems = items.map((obj, index) => {
const desc = this.renderDescription ? this.renderDescription(obj) : null;
const tabIndex = index + tabIndexStart;
return desc
? this.renderMenuItemWithDescription(obj, desc, tabIndex)
: this.renderMenuItemWithoutDescription(obj, tabIndex);
});
return html`${renderedItems}`;
}
renderWithMenuGroupTitle([group, items]: Group<T>, idx: number) {
return html`
<section class="pf-c-dropdown__group">
<h1 class="pf-c-dropdown__group-title">${group}</h1>
<ul>
${this.renderMenuGroup(items, idx)}
</ul>
</section>
`;
}
get groupedItems(): [boolean, Group<T>[]] {
const items = this.groupBy(this.objects || []);
if (items.length === 0) {
return [false, [["", []]]];
}
if (items.length === 1 && (items[0].length < 1 || items[0][0] === "")) {
return [false, items];
}
return [true, items];
}
/*
* This is a little bit hacky. Because we mainly want to use this field in modal-based forms,
* rendering this menu inline makes the menu not overlay over top of the modal, and cause
* the modal to scroll.
* Hence, we render the menu into the document root, hide it when this menu isn't open
* and remove it on disconnect
* Also to move it to the correct position we're getting this elements's position and use that
* to position the menu
* The other downside this has is that, since we're rendering outside of a shadow root,
* the pf-c-dropdown CSS needs to be loaded on the body.
*/
renderMenu(): void {
if (!this.objects) {
return;
}
const [shouldRenderGroups, groupedItems] = this.groupedItems;
const pos = this.getBoundingClientRect();
const position = {
"position": "fixed",
"inset": "0px auto auto 0px",
"z-index": "9999",
"transform": `translate(${pos.x}px, ${pos.y + this.offsetHeight}px)`,
"width": `${pos.width}px`,
...(this.open ? {} : { visibility: "hidden" }),
};
render(
html`<div style=${styleMap(position)} class="pf-c-dropdown pf-m-expanded">
<ul
class="pf-c-dropdown__menu pf-m-static"
role="listbox"
style="max-height:50vh;overflow-y:auto;"
id=${this.dropdownUID}
tabindex="0"
>
${this.blankable ? this.renderEmptyMenuItem() : html``}
${shouldRenderGroups
? html`${groupedItems.map(this.renderWithMenuGroupTitle)}`
: html`${this.renderMenuGroup(groupedItems[0][1], 0)}`}
</ul>
</div>`,
this.dropdownContainer,
{ host: this },
);
}
get renderedValue() {
// prettier-ignore
return (!this.objects) ? msg("Loading...")
: (this.selectedObject) ? this.renderElement(this.selectedObject)
: (this.blankable) ? this.emptyOption
: "";
}
render(): TemplateResult {
this.renderMenu();
const onFocus = (ev: FocusEvent) => {
this.open = true;
this.renderMenu();
if (this.blankable && this.renderedValue === this.emptyOption) {
if (ev.target && ev.target instanceof HTMLInputElement) {
ev.target.value = "";
}
}
};
const onInput = (ev: InputEvent) => {
this.query = (ev.target as HTMLInputElement).value;
this.updateData();
};
const onBlur = (ev: FocusEvent) => {
// For Safari, we get the <ul> element itself here when clicking on one of
// it's buttons, as the container has tabindex set
if (ev.relatedTarget && (ev.relatedTarget as HTMLElement).id === this.dropdownUID) {
return;
}
// Check if we're losing focus to one of our dropdown items, and if such don't blur
if (ev.relatedTarget instanceof HTMLButtonElement) {
const parentMenu = ev.relatedTarget.closest("ul.pf-c-dropdown__menu.pf-m-static");
if (parentMenu && parentMenu.id === this.dropdownUID) {
return;
}
}
this.open = false;
this.renderMenu();
};
return html`<div class="pf-c-select">
<div class="pf-c-select__toggle pf-m-typeahead">
<div class="pf-c-select__toggle-wrapper">
<input
class="pf-c-form-control pf-c-select__toggle-typeahead"
type="text"
placeholder=${this.placeholder}
spellcheck="false"
@input=${onInput}
@focus=${onFocus}
@blur=${onBlur}
.value=${this.renderedValue}
/>
</div>
</div>
</div>`;
}
}
export default SearchSelect;

View File

@ -0,0 +1,4 @@
import { SearchSelect } from "./ak-search-select";
export { SearchSelect };
export default SearchSelect;

View File

@ -43,7 +43,6 @@ export class SidebarBrand extends WithTenantConfig(AKElement) {
min-height: 114px;
}
.pf-c-brand img {
width: 100%;
padding: 0 0.5rem;
height: 42px;
}

View File

@ -505,7 +505,7 @@ export class FlowExecutor extends Interface implements StageHost {
? html`
<li>
<a
href="https://unsplash.com/@federize"
href="https://unsplash.com/@theforestbirds"
>${msg("Background image")}</a
>
</li>

View File

@ -6249,6 +6249,12 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s62418cbcd2a25498">
<source>Maximum concurrent allowed connections to this endpoint. Can be set to -1 to disable the limit.</source>
</trans-unit>
<trans-unit id="s94d61907ee22a8c1">
<source>Korean</source>
</trans-unit>
<trans-unit id="s95d56e58f816d211">
<source>Dutch</source>
</trans-unit>
</body>
</file>

View File

@ -6525,6 +6525,12 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s62418cbcd2a25498">
<source>Maximum concurrent allowed connections to this endpoint. Can be set to -1 to disable the limit.</source>
</trans-unit>
<trans-unit id="s94d61907ee22a8c1">
<source>Korean</source>
</trans-unit>
<trans-unit id="s95d56e58f816d211">
<source>Dutch</source>
</trans-unit>
</body>
</file>

View File

@ -6165,6 +6165,12 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s62418cbcd2a25498">
<source>Maximum concurrent allowed connections to this endpoint. Can be set to -1 to disable the limit.</source>
</trans-unit>
<trans-unit id="s94d61907ee22a8c1">
<source>Korean</source>
</trans-unit>
<trans-unit id="s95d56e58f816d211">
<source>Dutch</source>
</trans-unit>
</body>
</file>

View File

@ -8217,6 +8217,14 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
<trans-unit id="s62418cbcd2a25498">
<source>Maximum concurrent allowed connections to this endpoint. Can be set to -1 to disable the limit.</source>
<target>Nombre maximum de connections concurrentes à ce point de terminaison. Peut être défini à -1 pour désactiver la limite.</target>
</trans-unit>
<trans-unit id="s94d61907ee22a8c1">
<source>Korean</source>
<target>Coréen</target>
</trans-unit>
<trans-unit id="s95d56e58f816d211">
<source>Dutch</source>
<target>Néerlandais</target>
</trans-unit>
</body>
</file>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" ?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<?xml version="1.0"?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file target-language="ko" source-language="en" original="lit-localize-inputs" datatype="plaintext">
<body>
<trans-unit id="s4caed5b7a7e5d89b">
@ -612,8 +612,8 @@
</trans-unit>
<trans-unit id="saa0e2675da69651b">
<source>The URL &quot;<x id="0" equiv-text="${this.url}"/>&quot; was not found.</source>
<target>URL &quot;<x id="0" equiv-text="${this.url}"/>&quot; 을 찾을 수 없습니다.</target>
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
<target>URL "<x id="0" equiv-text="${this.url}"/>" 을 찾을 수 없습니다.</target>
</trans-unit>
<trans-unit id="s58cd9c2fe836d9c6">
@ -1054,8 +1054,8 @@
</trans-unit>
<trans-unit id="sa8384c9c26731f83">
<source>To allow any redirect URI, set this value to &quot;.*&quot;. Be aware of the possible security implications this can have.</source>
<target>리디렉션 URI를 허용하려면 이 값을 &quot;.*&quot;로 설정합니다. 이로 인해 발생할 수 있는 보안상의 영향에 유의하세요.</target>
<source>To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have.</source>
<target>리디렉션 URI를 허용하려면 이 값을 ".*"로 설정합니다. 이로 인해 발생할 수 있는 보안상의 영향에 유의하세요.</target>
</trans-unit>
<trans-unit id="s55787f4dfcdce52b">
@ -1792,8 +1792,8 @@
</trans-unit>
<trans-unit id="sa90b7809586c35ce">
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon &quot;fa-test&quot;.</source>
<target>전체 URL, 상대 경로를 입력하거나, 또는 'fa://fa-test'를 사용하여 Font Awesome 아이콘 &quot;fa-test&quot;를 사용합니다.</target>
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
<target>전체 URL, 상대 경로를 입력하거나, 또는 'fa://fa-test'를 사용하여 Font Awesome 아이콘 "fa-test"를 사용합니다.</target>
</trans-unit>
<trans-unit id="s0410779cb47de312">
@ -2972,7 +2972,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s76768bebabb7d543">
<source>Field which contains members of a group. Note that if using the &quot;memberUid&quot; field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source>
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source>
<target>그룹 구성원이 포함된 필드입니다. 'memberUid' 필드를 사용하는 경우 값에 상대적인 고유 이름이 포함된 것으로 가정합니다 (예:'memberUid=some-user' 대신 'memberUid=cn=some-user,ou=groups,...').</target>
</trans-unit>
@ -3764,8 +3764,8 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s7b1fba26d245cb1c">
<source>When using an external logging solution for archiving, this can be set to &quot;minutes=5&quot;.</source>
<target>아카이브에 외부 로깅 솔루션을 사용하는 경우, 이 값을 &quot;minutes=5&quot;로 설정할 수 있습니다.</target>
<source>When using an external logging solution for archiving, this can be set to "minutes=5".</source>
<target>아카이브에 외부 로깅 솔루션을 사용하는 경우, 이 값을 "minutes=5"로 설정할 수 있습니다.</target>
</trans-unit>
<trans-unit id="s44536d20bb5c8257">
@ -3774,8 +3774,8 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s3bb51cabb02b997e">
<source>Format: &quot;weeks=3;days=2;hours=3,seconds=2&quot;.</source>
<target>서식: &quot;weeks=3;days=2;hours=3,seconds=2&quot;.</target>
<source>Format: "weeks=3;days=2;hours=3,seconds=2".</source>
<target>서식: "weeks=3;days=2;hours=3,seconds=2".</target>
</trans-unit>
<trans-unit id="s04bfd02201db5ab8">
@ -3967,8 +3967,8 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="sa95a538bfbb86111">
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> &quot;<x id="1" equiv-text="${this.obj?.name}"/>&quot;?</source>
<target>정말 <x id="0" equiv-text="${this.objectLabel}"/> &quot;<x id="1" equiv-text="${this.obj?.name}"/>&quot; 을(를) 업데이트 하시겠습니까?</target>
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
<target>정말 <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>" 을(를) 업데이트 하시겠습니까?</target>
</trans-unit>
<trans-unit id="sc92d7cfb6ee1fec6">
@ -5052,8 +5052,8 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="sdf1d8edef27236f0">
<source>A &quot;roaming&quot; authenticator, like a YubiKey</source>
<target>YubiKey 같은 &quot;로밍&quot; 인증기</target>
<source>A "roaming" authenticator, like a YubiKey</source>
<target>YubiKey 같은 "로밍" 인증기</target>
</trans-unit>
<trans-unit id="sfffba7b23d8fb40c">
@ -5387,8 +5387,8 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${prompt.name}"/> (&quot;<x id="1" equiv-text="${prompt.fieldKey}"/>&quot;, of type <x id="2" equiv-text="${prompt.type}"/>)</source>
<target><x id="0" equiv-text="${prompt.name}"/> (&quot;<x id="1" equiv-text="${prompt.fieldKey}"/>&quot;, of type <x id="2" equiv-text="${prompt.type}"/>)</target>
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
<target><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</target>
</trans-unit>
<trans-unit id="s3b7b519444181264">
@ -5436,7 +5436,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s1608b2f94fa0dbd4">
<source>If set to a duration above 0, the user will have the option to choose to &quot;stay signed in&quot;, which will extend their session by the time specified here.</source>
<source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
<target>기간을 0 이상으로 설정하면, 사용자에게 '로그인 상태 유지'를 선택할 수 있는 옵션이 제공되며, 이 경우 세션이 여기에 지정된 시간만큼 연장됩니다.</target>
</trans-unit>
@ -7926,8 +7926,8 @@ Bindings to groups/users are checked against the user of the event.</source>
<target>사용자 생성과 <x id="0" equiv-text="${this.group.name}"/> 그룹 추가에 성공했습니다.</target>
</trans-unit>
<trans-unit id="s824e0943a7104668">
<source>This user will be added to the group &quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&quot;.</source>
<target>이 사용자는 &quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&quot; 그룹에 추가됩니다.</target>
<source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
<target>이 사용자는 "<x id="0" equiv-text="${this.targetGroup.name}"/>" 그룹에 추가됩니다.</target>
</trans-unit>
<trans-unit id="s62e7f6ed7d9cb3ca">
<source>Pretend user exists</source>
@ -8004,7 +8004,145 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s047a5f0211fedc72">
<source>Require Outpost (flow can only be executed from an outpost).</source>
<target>Outpost필요 (플로우는 Outpost에서만 실행할 수 있음).</target>
</trans-unit>
<trans-unit id="scc7f34824150bfb8">
<source>Provider require enterprise.</source>
</trans-unit>
<trans-unit id="s31f1afc1bfe1cb3a">
<source>Learn more</source>
</trans-unit>
<trans-unit id="sa2ea0fcd3ffa80e0">
<source>Connection expiry</source>
</trans-unit>
<trans-unit id="s6dd297c217729828">
<source>Determines how long a session lasts before being disconnected and requiring re-authorization.</source>
</trans-unit>
<trans-unit id="s3271da6c18c25b18">
<source>Connection settings.</source>
</trans-unit>
<trans-unit id="s2f4ca2148183d692">
<source>Successfully updated endpoint.</source>
</trans-unit>
<trans-unit id="s5adee855dbe191d9">
<source>Successfully created endpoint.</source>
</trans-unit>
<trans-unit id="s61e136c0658e27d5">
<source>Protocol</source>
</trans-unit>
<trans-unit id="sa062b019ff0c8809">
<source>RDP</source>
</trans-unit>
<trans-unit id="s97f9bf19fa5b57d1">
<source>SSH</source>
</trans-unit>
<trans-unit id="s7c100119e9ffcc32">
<source>VNC</source>
</trans-unit>
<trans-unit id="s6b05f9d8801fc14f">
<source>Host</source>
</trans-unit>
<trans-unit id="sb474f652a2c2fc76">
<source>Hostname/IP to connect to.</source>
</trans-unit>
<trans-unit id="sc39f6abf0daedb0f">
<source>Maximum concurrent connections</source>
</trans-unit>
<trans-unit id="s62418cbcd2a25498">
<source>Maximum concurrent allowed connections to this endpoint. Can be set to -1 to disable the limit.</source>
</trans-unit>
<trans-unit id="s8276649077e8715c">
<source>Endpoint(s)</source>
</trans-unit>
<trans-unit id="sf1dabfe0fe8a75ad">
<source>Update Endpoint</source>
</trans-unit>
<trans-unit id="s008496c7716b9812">
<source>These bindings control which users will have access to this endpoint. Users must also have access to the application.</source>
</trans-unit>
<trans-unit id="s38e7cd1a24e70faa">
<source>Create Endpoint</source>
</trans-unit>
<trans-unit id="s4770c10e5b1c028c">
<source>RAC is in preview.</source>
</trans-unit>
<trans-unit id="s168565f5ac74a89f">
<source>Update RAC Provider</source>
</trans-unit>
<trans-unit id="s8465a2caa2d9ea5d">
<source>Endpoints</source>
</trans-unit>
<trans-unit id="s9857d883d8eb98fc">
<source>General settings</source>
</trans-unit>
<trans-unit id="sd2066881798a1b96">
<source>RDP settings</source>
</trans-unit>
<trans-unit id="sb864dc36a463a155">
<source>Ignore server certificate</source>
</trans-unit>
<trans-unit id="s20366a8d1eaaca54">
<source>Enable wallpaper</source>
</trans-unit>
<trans-unit id="s1e44c5350ef7598c">
<source>Enable font-smoothing</source>
</trans-unit>
<trans-unit id="s04ff5d6ae711e6d6">
<source>Enable full window dragging</source>
</trans-unit>
<trans-unit id="s94d61907ee22a8c1">
<source>Korean</source>
</trans-unit>
<trans-unit id="s95d56e58f816d211">
<source>Dutch</source>
</trans-unit>
<trans-unit id="s663ccbfdf27e8dd0">
<source>Network binding</source>
</trans-unit>
<trans-unit id="sb108a06693c67753">
<source>No binding</source>
</trans-unit>
<trans-unit id="s5aab90c74f1233b8">
<source>Bind ASN</source>
</trans-unit>
<trans-unit id="s488303b048afe83b">
<source>Bind ASN and Network</source>
</trans-unit>
<trans-unit id="s3268dcfe0c8234dc">
<source>Bind ASN, Network and IP</source>
</trans-unit>
<trans-unit id="s226381aca231644f">
<source>Configure if sessions created by this stage should be bound to the Networks they were created in.</source>
</trans-unit>
<trans-unit id="s2555a1f20f3fd93e">
<source>GeoIP binding</source>
</trans-unit>
<trans-unit id="s3d63c78f93c9a92e">
<source>Bind Continent</source>
</trans-unit>
<trans-unit id="s395d5863b3a259b5">
<source>Bind Continent and Country</source>
</trans-unit>
<trans-unit id="s625ea0c32b4b136c">
<source>Bind Continent, Country and City</source>
</trans-unit>
<trans-unit id="s4bc7a1a88961be90">
<source>Configure if sessions created by this stage should be bound to their GeoIP-based location</source>
</trans-unit>
<trans-unit id="sa06cd519ff151b6d">
<source>RAC</source>
</trans-unit>
<trans-unit id="s28b99b59541f54ca">
<source>Connection failed after <x id="0" equiv-text="${this.connectionAttempt}"/> attempts.</source>
</trans-unit>
<trans-unit id="s7c7d956418e1c8c8">
<source>Re-connecting in <x id="0" equiv-text="${Math.max(1, delay / 1000)}"/> second(s).</source>
</trans-unit>
<trans-unit id="sfc003381f593d943">
<source>Connecting...</source>
</trans-unit>
<trans-unit id="s31aa94a0b3c7edb2">
<source>Select endpoint to connect to</source>
</trans-unit>
</body>
</file>
</xliff>
</xliff>

File diff suppressed because it is too large Load Diff

View File

@ -6373,6 +6373,12 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s62418cbcd2a25498">
<source>Maximum concurrent allowed connections to this endpoint. Can be set to -1 to disable the limit.</source>
</trans-unit>
<trans-unit id="s94d61907ee22a8c1">
<source>Korean</source>
</trans-unit>
<trans-unit id="s95d56e58f816d211">
<source>Dutch</source>
</trans-unit>
</body>
</file>

View File

@ -8111,4 +8111,10 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s62418cbcd2a25498">
<source>Maximum concurrent allowed connections to this endpoint. Can be set to -1 to disable the limit.</source>
</trans-unit>
<trans-unit id="s94d61907ee22a8c1">
<source>Korean</source>
</trans-unit>
<trans-unit id="s95d56e58f816d211">
<source>Dutch</source>
</trans-unit>
</body></file></xliff>

View File

@ -6158,6 +6158,12 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s62418cbcd2a25498">
<source>Maximum concurrent allowed connections to this endpoint. Can be set to -1 to disable the limit.</source>
</trans-unit>
<trans-unit id="s94d61907ee22a8c1">
<source>Korean</source>
</trans-unit>
<trans-unit id="s95d56e58f816d211">
<source>Dutch</source>
</trans-unit>
</body>
</file>

5068
web/xliff/zh-CN.xlf Normal file

File diff suppressed because it is too large Load Diff

View File

@ -8219,6 +8219,14 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s62418cbcd2a25498">
<source>Maximum concurrent allowed connections to this endpoint. Can be set to -1 to disable the limit.</source>
<target>允许到此端点的最大并发连接数。可以设置为 -1 以禁用限制。</target>
</trans-unit>
<trans-unit id="s94d61907ee22a8c1">
<source>Korean</source>
<target>韩语</target>
</trans-unit>
<trans-unit id="s95d56e58f816d211">
<source>Dutch</source>
<target>荷兰语</target>
</trans-unit>
</body>
</file>

View File

@ -6206,6 +6206,12 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s62418cbcd2a25498">
<source>Maximum concurrent allowed connections to this endpoint. Can be set to -1 to disable the limit.</source>
</trans-unit>
<trans-unit id="s94d61907ee22a8c1">
<source>Korean</source>
</trans-unit>
<trans-unit id="s95d56e58f816d211">
<source>Dutch</source>
</trans-unit>
</body>
</file>

View File

@ -8219,6 +8219,14 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s62418cbcd2a25498">
<source>Maximum concurrent allowed connections to this endpoint. Can be set to -1 to disable the limit.</source>
<target>允许到此端点的最大并发连接数。可以设置为 -1 以禁用限制。</target>
</trans-unit>
<trans-unit id="s94d61907ee22a8c1">
<source>Korean</source>
<target>韩语</target>
</trans-unit>
<trans-unit id="s95d56e58f816d211">
<source>Dutch</source>
<target>荷兰语</target>
</trans-unit>
</body>
</file>

View File

@ -8095,6 +8095,12 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s62418cbcd2a25498">
<source>Maximum concurrent allowed connections to this endpoint. Can be set to -1 to disable the limit.</source>
</trans-unit>
<trans-unit id="s94d61907ee22a8c1">
<source>Korean</source>
</trans-unit>
<trans-unit id="s95d56e58f816d211">
<source>Dutch</source>
</trans-unit>
</body>
</file>

View File

@ -4,16 +4,16 @@ title: Docker Compose installation
This installation method is for test-setups and small-scale production setups.
:::info
You can also [view a video walk-through](https://www.youtube.com/watch?v=O1qUbrk4Yc8) of the installation process on Docker (with bonus details about email configuration and other important options).
:::
## Requirements
- A host with at least 2 CPU cores and 2 GB of RAM
- Docker
- Docker Compose
## Video
<iframe width="560" height="315" src="https://www.youtube.com/embed/O1qUbrk4Yc8?si=HiSBjmJYhE_oJhB1&amp;start=22" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
## Preparation
To download the latest `docker-compose.yml` open your terminal and navigate to the directory of your choice.

View File

@ -4,6 +4,6 @@ title: Installation
Everything you need to get authentik up and running! For information about upgrading to a new version, refer to the <b>Upgrade</b> section in the relevant [Release Notes](../releases).
import DocCardList from '@theme/DocCardList';
import DocCardList from "@theme/DocCardList";
<DocCardList />

View File

@ -13,6 +13,10 @@ You can also [view a video walk-through](https://www.youtube.com/watch?v=O1qUbrk
- Kubernetes
- Helm
## Video
<iframe width="560" height="315" src="https://www.youtube.com/embed/O1qUbrk4Yc8?si=hs-ZhbVk4Y-TW_Vw&amp;start=562" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
### Generate Passwords
Start by generating passwords for the database and cache. You can use either of the following commands:

View File

@ -1,129 +0,0 @@
const config = require("./docusaurus.config");
import type { Config } from "@docusaurus/types";
module.exports = async function (): Promise<Config> {
const remarkGithub = (await import("remark-github")).default;
const defaultBuildUrl = (await import("remark-github")).defaultBuildUrl;
const mainConfig = await config();
return {
title: "authentik",
tagline: "Making authentication simple.",
url: "https://goauthentik.io",
baseUrl: "/if/help/",
onBrokenLinks: "throw",
favicon: "img/icon.png",
organizationName: "BeryJu",
projectName: "authentik",
themeConfig: {
navbar: {
logo: {
alt: "authentik logo",
src: "img/icon_left_brand.svg",
},
items: [
{
to: "docs/",
activeBasePath: "docs",
label: "Docs",
position: "left",
},
{
to: "integrations/",
activeBasePath: "integrations",
label: "Integrations",
position: "left",
},
{
to: "developer-docs/",
activeBasePath: "developer-docs",
label: "Developer Docs",
position: "left",
},
{
href: "https://github.com/goauthentik/authentik",
label: "GitHub",
position: "right",
},
{
href: "https://goauthentik.io/discord",
label: "Discord",
position: "right",
},
],
},
footer: {
links: [],
copyright: mainConfig.themeConfig.footer.copyright,
},
colorMode: mainConfig.themeConfig.colorMode,
tableOfContents: mainConfig.themeConfig.tableOfContents,
prims: mainConfig.themeConfig.prism,
},
presets: [
[
"@docusaurus/preset-classic",
{
docs: {
id: "docs",
sidebarPath: require.resolve("./sidebars.js"),
editUrl:
"https://github.com/goauthentik/authentik/edit/main/website/",
remarkPlugins: [
[
remarkGithub,
{
repository: "goauthentik/authentik",
// Only replace issues and PR links
buildUrl: function (values) {
return values.type === "issue"
? defaultBuildUrl(values)
: false;
},
},
],
],
},
pages: false,
theme: {
customCss: require.resolve("./src/css/custom.css"),
},
},
],
],
plugins: [
[
"@docusaurus/plugin-content-docs",
{
id: "docsIntegrations",
path: "integrations",
routeBasePath: "integrations",
sidebarPath: require.resolve("./sidebarsIntegrations.js"),
editUrl:
"https://github.com/goauthentik/authentik/edit/main/website/",
},
],
[
"@docusaurus/plugin-content-docs",
{
id: "docsDevelopers",
path: "developer-docs",
routeBasePath: "developer-docs",
sidebarPath: require.resolve("./sidebarsDev.js"),
editUrl:
"https://github.com/goauthentik/authentik/edit/main/website/",
},
],
[
"@docusaurus/plugin-client-redirects",
{
redirects: [
{
to: "/docs/",
from: ["/"],
},
],
},
],
],
};
};

View File

View File

@ -133,7 +133,7 @@ Add a new provider using the `+` button and set the following values:
- Display name mapping: name
- Email mapping: email
- Quota mapping: quota (leave empty if you have skipped the [custom profile scope](#custom-profile-scope) section)
- Groups mapping: group (leave empty if you have skipped the [custom profile scope](#custom-profile-scope) section)
- Groups mapping: groups (leave empty if you have skipped the [custom profile scope](#custom-profile-scope) section)
:::tip
You need to enable the "Use group provisioning" checkmark to be able to write to this field
:::

View File

@ -34,7 +34,7 @@
"@docusaurus/tsconfig": "3.0.1",
"@docusaurus/types": "3.0.1",
"@types/react": "^18.2.48",
"prettier": "3.2.3",
"prettier": "3.2.4",
"typescript": "~5.3.3"
},
"engines": {
@ -13754,9 +13754,9 @@
}
},
"node_modules/prettier": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.3.tgz",
"integrity": "sha512-QNhUTBq+mqt1oH1dTfY3phOKNhcDdJkfttHI6u0kj7M2+c+7fmNKlgh2GhnHiqMcbxJ+a0j2igz/2jfl9QKLuw==",
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.4.tgz",
"integrity": "sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==",
"dev": true,
"bin": {
"prettier": "bin/prettier.cjs"

View File

@ -53,7 +53,7 @@
"@docusaurus/tsconfig": "3.0.1",
"@docusaurus/types": "3.0.1",
"@types/react": "^18.2.48",
"prettier": "3.2.3",
"prettier": "3.2.4",
"typescript": "~5.3.3"
},
"engines": {