Compare commits

...

68 Commits

Author SHA1 Message Date
87bf75e51c add Registration closed note 2023-07-28 12:36:15 -05:00
1ba1a1def5 web/user: fix app icon size for user interface
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-28 17:11:20 +02:00
782d95b4a3 web: app icons v2 (#6410)
* fix more icons stuff

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

* refactor app icon into separate component

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

* update locale

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

* make app icon work correctly in admin list and app view page

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-28 14:25:56 +02:00
5803c39e91 web: fix app icon rendering, style refinements (#6409)
* add very slight drop shadow to icons so dark colours are better visible, fix expand text

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

* web/admin: fix rendering of icons for admin interface

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-28 11:09:11 +02:00
e5322a6dd3 core: bump goauthentik.io/api/v3 from 3.2023061.5 to 3.2023061.6 (#6407)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2023061.5 to 3.2023061.6.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2023061.5...v3.2023061.6)

---
updated-dependencies:
- dependency-name: goauthentik.io/api/v3
  dependency-type: direct:production
  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>
2023-07-28 11:08:57 +02:00
364edfb4a8 web: bump core-js from 3.31.1 to 3.32.0 in /web (#6406)
Bumps [core-js](https://github.com/zloirock/core-js/tree/HEAD/packages/core-js) from 3.31.1 to 3.32.0.
- [Release notes](https://github.com/zloirock/core-js/releases)
- [Changelog](https://github.com/zloirock/core-js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/zloirock/core-js/commits/v3.32.0/packages/core-js)

---
updated-dependencies:
- dependency-name: core-js
  dependency-type: direct:production
  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>
2023-07-28 11:08:47 +02:00
de16988cac web/user: experiment with some slightly different styles (#6405)
* web/user: experiment with some slightly different styles

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

* rework application card

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

* fix color and expand

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

* fix expansion

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-28 00:23:06 +02:00
a2714ab1f1 outposts: make metrics compliant with Prometheus best-practices (#6398)
web/outpost: make metrics compliant with Prometheus best-practices

Today, all NewHistogramVec store values in nanoseconds without changing
the default histogram bucket, which are made for seconds, making them
a bit useless. In addition, some metrics names are not self-explanatoryand
and do not comply with Prometheus best practices.

This commit tries to fix all of this "issues".

NOTE: I kept old metrics in order to avoid breaking changes with
existing dashboards and metrics.

Signed-off-by: Alexandre NICOLAIE <xunleii@users.noreply.github.com>
2023-07-27 18:51:08 +02:00
5347dd7022 website: add tooltips to comparison table (#6402)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-27 17:08:11 +02:00
aaddb76962 web: bump API Client version (#6401) 2023-07-27 13:14:02 +02:00
b08f8d8e0c api: re-fix url import logging (#6400)
* fix logging

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

* remove lib from apps

lib doesn't declare any models, so it really doesn't need to be in there anyways?

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

* remove lib from schema too

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-27 12:56:51 +02:00
664bc19bba website: revamp (#6375)
* make things work better

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

* fix styling

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

* move comparison css to its own file

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

* more cleanup

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

* more cleanup

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

* make release bar work, more cleanup

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

* fix a bunch of styling issues

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

* fix table

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

* move text slider into component

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

* fix mobile and more cleanup

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

* more fixes

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

* test out gradient?

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

* update meta?

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

* fix lint

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

* remove underline on news links

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

* adjust gradient

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

* remove override

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

* start changing screenshots

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

* use smaller screenshots for landing page

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

* website: fix the font scaling issue on the text slider

- Change the text slider and hero__title to use em/rem calculations

This patch changes the way the text slider and hero__title font sizes
are calculated so that the font scales with the device; devices with
viewports larger than 379 pixels will now render the slider without
line breaks or having the phrase "active directory" disappear from
the page.

The 379pixel break is just the best we could come up with on the fly.
This does mean that if you own an iPhone 5 or an old WIFI-capable
iPod, it still looks awful.  :-)

* fix some more react issues

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

* a bit less padding on the bottom CTA

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

* use some old copy for now

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Ken Sternberg <ken@goauthentik.io>
2023-07-27 11:44:50 +02:00
f315360be1 web: bump @sentry/browser from 7.60.0 to 7.60.1 in /web (#6392)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 7.60.0 to 7.60.1.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.60.0...7.60.1)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  dependency-type: direct:production
  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>
2023-07-27 11:32:57 +02:00
4ac255d579 web: bump @sentry/tracing from 7.60.0 to 7.60.1 in /web (#6393)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 7.60.0 to 7.60.1.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.60.0...7.60.1)

---
updated-dependencies:
- dependency-name: "@sentry/tracing"
  dependency-type: direct:production
  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>
2023-07-27 11:30:15 +02:00
3f9f57f0fd web: bump chart.js from 4.3.1 to 4.3.2 in /web (#6395)
Bumps [chart.js](https://github.com/chartjs/Chart.js) from 4.3.1 to 4.3.2.
- [Release notes](https://github.com/chartjs/Chart.js/releases)
- [Commits](https://github.com/chartjs/Chart.js/compare/v4.3.1...v4.3.2)

---
updated-dependencies:
- dependency-name: chart.js
  dependency-type: direct:production
  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>
2023-07-27 11:30:04 +02:00
3569eb15b1 core: bump pylint from 2.17.4 to 2.17.5 (#6396)
Bumps [pylint](https://github.com/pylint-dev/pylint) from 2.17.4 to 2.17.5.
- [Release notes](https://github.com/pylint-dev/pylint/releases)
- [Commits](https://github.com/pylint-dev/pylint/compare/v2.17.4...v2.17.5)

---
updated-dependencies:
- dependency-name: pylint
  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>
2023-07-27 11:29:52 +02:00
94836a3ce7 api: log errors if app URLs import fail (#6397)
* api: log errors if app URLs import fail

Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>

* bump level to warning

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

---------

Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2023-07-27 11:29:20 +02:00
f272d14fcf events: fix monitored task not removing state (#6386)
when `save_on_success` is set, a task failure saves state. when it succeeds afterwards, that state should be removed

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-26 16:00:50 +02:00
17fe595528 sources/ldap: fix syncing large LDAP directories (#6384)
* sources/ldap: fix syncing large LDAP directories

* add test

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2023-07-26 12:25:40 +02:00
3cce6d79eb web/user: fix background alignment (#6383)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-26 11:03:58 +02:00
7ac5c8eaa6 translate: Updates for file locale/en/LC_MESSAGES/django.po in fr on branch main (#6376)
* Translate locale/en/LC_MESSAGES/django.po in fr

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'fr'.

* remove debug

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2023-07-26 11:03:37 +02:00
7316f126de ci: test rename action more
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-26 11:02:02 +02:00
d645965a33 web: bump mermaid from 10.2.4 to 10.3.0 in /web (#6382)
Bumps [mermaid](https://github.com/mermaid-js/mermaid) from 10.2.4 to 10.3.0.
- [Release notes](https://github.com/mermaid-js/mermaid/releases)
- [Changelog](https://github.com/mermaid-js/mermaid/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/mermaid-js/mermaid/compare/v10.2.4...v10.3.0)

---
updated-dependencies:
- dependency-name: mermaid
  dependency-type: direct:production
  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>
2023-07-26 11:00:16 +02:00
47abbcf8b8 ci: test rename
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-26 10:59:59 +02:00
e86a41b83d ci: automatically rename transifex PRs to match the naming scheme (#6352)
* ci: automatically rename transifex PRs to match the naming scheme

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

* add name

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-26 10:57:08 +02:00
f2293c0f5b website/docs: Update syntax in traefik standalone example (#6303)
* Update syntax in traefik standalone example

Signed-off-by: Thomas Moschny <thomas.moschny@gmx.de>

* One more syntax update

Signed-off-by: Thomas Moschny <thomas.moschny@gmx.de>

---------

Signed-off-by: Thomas Moschny <thomas.moschny@gmx.de>
2023-07-26 10:56:31 +02:00
da3393abb4 web: bump @esbuild/linux-arm64 from 0.18.16 to 0.18.17 in /web (#6380)
Bumps [@esbuild/linux-arm64](https://github.com/evanw/esbuild) from 0.18.16 to 0.18.17.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.18.16...v0.18.17)

---
updated-dependencies:
- dependency-name: "@esbuild/linux-arm64"
  dependency-type: direct:production
  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>
2023-07-26 10:54:40 +02:00
211da35a93 web: bump pyright from 1.1.318 to 1.1.319 in /web (#6378)
Bumps [pyright](https://github.com/Microsoft/pyright/tree/HEAD/packages/pyright) from 1.1.318 to 1.1.319.
- [Release notes](https://github.com/Microsoft/pyright/releases)
- [Commits](https://github.com/Microsoft/pyright/commits/1.1.319/packages/pyright)

---
updated-dependencies:
- dependency-name: pyright
  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>
2023-07-26 10:47:26 +02:00
0b8c501326 web: bump @esbuild/darwin-arm64 from 0.18.16 to 0.18.17 in /web (#6379)
Bumps [@esbuild/darwin-arm64](https://github.com/evanw/esbuild) from 0.18.16 to 0.18.17.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.18.16...v0.18.17)

---
updated-dependencies:
- dependency-name: "@esbuild/darwin-arm64"
  dependency-type: direct:production
  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>
2023-07-26 10:47:09 +02:00
18472c231a enterprise: fix license check not using the proper JWT algorithm
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2023-07-25 12:10:15 +02:00
e51bef218a web: bump @typescript-eslint/parser from 6.1.0 to 6.2.0 in /web (#6372)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 6.1.0 to 6.2.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.2.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  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>
2023-07-25 11:43:08 +02:00
486e17920e core: bump goauthentik.io/api/v3 from 3.2023061.4 to 3.2023061.5 (#6362)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2023061.4 to 3.2023061.5.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2023061.4...v3.2023061.5)

---
updated-dependencies:
- dependency-name: goauthentik.io/api/v3
  dependency-type: direct:production
  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>
2023-07-25 11:34:24 +02:00
505bad0895 web: bump @typescript-eslint/eslint-plugin from 6.1.0 to 6.2.0 in /web (#6370)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 6.1.0 to 6.2.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.2.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  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>
2023-07-25 11:33:04 +02:00
e4b7691181 web: bump tslib from 2.6.0 to 2.6.1 in /web (#6366)
Bumps [tslib](https://github.com/Microsoft/tslib) from 2.6.0 to 2.6.1.
- [Release notes](https://github.com/Microsoft/tslib/releases)
- [Commits](https://github.com/Microsoft/tslib/compare/2.6.0...v2.6.1)

---
updated-dependencies:
- dependency-name: tslib
  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>
2023-07-25 11:32:48 +02:00
ba5adad53d web: bump @storybook/addon-links from 7.1.0 to 7.1.1 in /web (#6363)
Bumps [@storybook/addon-links](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/links) from 7.1.0 to 7.1.1.
- [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.1.1/code/addons/links)

---
updated-dependencies:
- dependency-name: "@storybook/addon-links"
  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>
2023-07-25 11:31:48 +02:00
2b1dee6aed web: bump storybook from 7.1.0 to 7.1.1 in /web (#6364)
Bumps [storybook](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/cli) from 7.1.0 to 7.1.1.
- [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.1.1/code/lib/cli)

---
updated-dependencies:
- dependency-name: storybook
  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>
2023-07-25 11:28:38 +02:00
b976acff42 web: bump chart.js from 4.3.0 to 4.3.1 in /web (#6368)
Bumps [chart.js](https://github.com/chartjs/Chart.js) from 4.3.0 to 4.3.1.
- [Release notes](https://github.com/chartjs/Chart.js/releases)
- [Commits](https://github.com/chartjs/Chart.js/compare/v4.3.0...v4.3.1)

---
updated-dependencies:
- dependency-name: chart.js
  dependency-type: direct:production
  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>
2023-07-25 11:28:20 +02:00
78092ddfea web: bump @storybook/addon-essentials from 7.1.0 to 7.1.1 in /web (#6365)
Bumps [@storybook/addon-essentials](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/essentials) from 7.1.0 to 7.1.1.
- [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.1.1/code/addons/essentials)

---
updated-dependencies:
- dependency-name: "@storybook/addon-essentials"
  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>
2023-07-25 11:27:50 +02:00
22d013817f web: bump @storybook/web-components-vite from 7.1.0 to 7.1.1 in /web (#6367)
Bumps [@storybook/web-components-vite](https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/web-components-vite) from 7.1.0 to 7.1.1.
- [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.1.1/code/frameworks/web-components-vite)

---
updated-dependencies:
- dependency-name: "@storybook/web-components-vite"
  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>
2023-07-25 11:12:14 +02:00
56224fc712 web: bump @storybook/blocks from 7.1.0 to 7.1.1 in /web (#6371)
Bumps [@storybook/blocks](https://github.com/storybookjs/storybook/tree/HEAD/code/ui/blocks) from 7.1.0 to 7.1.1.
- [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.1.1/code/ui/blocks)

---
updated-dependencies:
- dependency-name: "@storybook/blocks"
  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>
2023-07-25 11:11:01 +02:00
86d64b2234 web/admin: hide pagination when no data is loaded yet (#6353)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-24 13:59:43 +02:00
a320aec9d0 web/admin: adjust style of page header (#6355)
light theme now matches dark theme

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-24 13:59:09 +02:00
7be94df00c root: set csrf cookie's secure flag same as session (#6350)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-24 13:57:30 +02:00
346c6e6a85 outposts: Fix infinite self-recursion in traefik reconciler. (#6336)
Fix infinite self-recursion in traefik reconciler.
2023-07-24 10:25:29 +00:00
8d4b7ce8d3 outposts: fix patch processing (#6338)
* outposts: fix patch processing for custom object types

* outposts: correct parsing patch type

* small change

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2023-07-24 10:25:14 +00:00
56cf14e5ef web: bump API Client version (#6351) 2023-07-24 12:23:20 +02:00
69543c14d3 Updates for file web/xliff/en.xlf in zh_CN on branch main (#6340)
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>
2023-07-24 12:13:31 +02:00
f3f07f2c98 Updates for file web/xliff/en.xlf in zh-Hans on branch main (#6341)
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>
2023-07-24 12:13:21 +02:00
4647fbacb0 enterprise: fix license check not using DER as spec specifies (#6348)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-24 12:11:47 +02:00
4359fab560 core: bump goauthentik.io/api/v3 from 3.2023061.3 to 3.2023061.4 (#6342)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2023061.3 to 3.2023061.4.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2023061.3...v3.2023061.4)

---
updated-dependencies:
- dependency-name: goauthentik.io/api/v3
  dependency-type: direct:production
  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>
2023-07-24 11:51:05 +02:00
f8b36e1737 core: bump drf-spectacular from 0.26.3 to 0.26.4 (#6343)
Bumps [drf-spectacular](https://github.com/tfranzel/drf-spectacular) from 0.26.3 to 0.26.4.
- [Release notes](https://github.com/tfranzel/drf-spectacular/releases)
- [Changelog](https://github.com/tfranzel/drf-spectacular/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/tfranzel/drf-spectacular/compare/0.26.3...0.26.4)

---
updated-dependencies:
- dependency-name: drf-spectacular
  dependency-type: direct:production
  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>
2023-07-24 11:50:52 +02:00
c50148072e core: bump ruff from 0.0.278 to 0.0.280 (#6344)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.0.278 to 0.0.280.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/BREAKING_CHANGES.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.0.278...v0.0.280)

---
updated-dependencies:
- dependency-name: ruff
  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>
2023-07-24 11:50:40 +02:00
deda3a57ee website: bump postcss from 8.4.26 to 8.4.27 in /website (#6345)
Bumps [postcss](https://github.com/postcss/postcss) from 8.4.26 to 8.4.27.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.26...8.4.27)

---
updated-dependencies:
- dependency-name: postcss
  dependency-type: direct:production
  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>
2023-07-24 11:50:24 +02:00
8f0c0fae62 web: bump @esbuild/linux-arm64 from 0.18.15 to 0.18.16 in /web (#6347)
Bumps [@esbuild/linux-arm64](https://github.com/evanw/esbuild) from 0.18.15 to 0.18.16.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.18.15...v0.18.16)

---
updated-dependencies:
- dependency-name: "@esbuild/linux-arm64"
  dependency-type: direct:production
  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>
2023-07-24 11:50:12 +02:00
2015463fe0 web: bump @esbuild/darwin-arm64 from 0.18.15 to 0.18.16 in /web (#6346)
Bumps [@esbuild/darwin-arm64](https://github.com/evanw/esbuild) from 0.18.15 to 0.18.16.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.18.15...v0.18.16)

---
updated-dependencies:
- dependency-name: "@esbuild/darwin-arm64"
  dependency-type: direct:production
  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>
2023-07-24 10:30:10 +02:00
d435a65cfd outposts: support json patch for Kubernetes (#6319) 2023-07-22 02:29:28 +02:00
a728dad166 providers/oauth2: fix grant_type password raising an exception (#6333) 2023-07-22 01:36:55 +02:00
e0564b3770 web: bump API Client version (#6331) 2023-07-21 18:27:14 +02:00
d50f92d8b4 enterprise: cleanup v2 (#6330)
* cleanup minor stuff

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

* change default user type to internal to be more consistent

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-21 18:23:51 +02:00
03f3ad89df web: bump prettier from 2.8.8 to 3.0.0 in /web (#6329)
* web: bump prettier from 2.8.8 to 3.0.0 in /web

Bumps [prettier](https://github.com/prettier/prettier) from 2.8.8 to 3.0.0.
- [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/2.8.8...3.0.0)

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

Signed-off-by: dependabot[bot] <support@github.com>

* update formatting and config

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

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2023-07-21 18:19:19 +02:00
e604e70395 web: bump @sentry/browser from 7.59.3 to 7.60.0 in /web (#6328)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 7.59.3 to 7.60.0.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.59.3...7.60.0)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  dependency-type: direct:production
  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>
2023-07-21 13:48:20 +02:00
1db048bdaf web: bump @trivago/prettier-plugin-sort-imports from 4.1.1 to 4.2.0 in /web (#6326)
web: bump @trivago/prettier-plugin-sort-imports in /web

Bumps [@trivago/prettier-plugin-sort-imports](https://github.com/trivago/prettier-plugin-sort-imports) from 4.1.1 to 4.2.0.
- [Release notes](https://github.com/trivago/prettier-plugin-sort-imports/releases)
- [Changelog](https://github.com/trivago/prettier-plugin-sort-imports/blob/main/CHANGELOG.md)
- [Commits](https://github.com/trivago/prettier-plugin-sort-imports/compare/v4.1.1...v4.2.0)

---
updated-dependencies:
- dependency-name: "@trivago/prettier-plugin-sort-imports"
  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>
2023-07-21 13:43:50 +02:00
3d973e7ce3 web: bump @sentry/tracing from 7.59.3 to 7.60.0 in /web (#6327)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 7.59.3 to 7.60.0.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.59.3...7.60.0)

---
updated-dependencies:
- dependency-name: "@sentry/tracing"
  dependency-type: direct:production
  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>
2023-07-21 13:43:30 +02:00
9bc3327f03 web: bump @esbuild/darwin-arm64 from 0.18.14 to 0.18.15 in /web (#6322)
Bumps [@esbuild/darwin-arm64](https://github.com/evanw/esbuild) from 0.18.14 to 0.18.15.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.18.14...v0.18.15)

---
updated-dependencies:
- dependency-name: "@esbuild/darwin-arm64"
  dependency-type: direct:production
  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>
2023-07-21 13:39:45 +02:00
f1979e12cc web: bump @esbuild/linux-arm64 from 0.18.14 to 0.18.15 in /web (#6323)
Bumps [@esbuild/linux-arm64](https://github.com/evanw/esbuild) from 0.18.14 to 0.18.15.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.18.14...v0.18.15)

---
updated-dependencies:
- dependency-name: "@esbuild/linux-arm64"
  dependency-type: direct:production
  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>
2023-07-21 13:04:15 +02:00
121cc6ac98 web: bump @codemirror/legacy-modes from 6.3.2 to 6.3.3 in /web (#6324)
Bumps [@codemirror/legacy-modes](https://github.com/codemirror/legacy-modes) from 6.3.2 to 6.3.3.
- [Changelog](https://github.com/codemirror/legacy-modes/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codemirror/legacy-modes/compare/6.3.2...6.3.3)

---
updated-dependencies:
- dependency-name: "@codemirror/legacy-modes"
  dependency-type: direct:production
  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>
2023-07-21 13:04:04 +02:00
9b7c30d44c sources/ldap: fix ldap_sync cli command not running in foreground (#6325)
closes #6317

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-21 13:03:06 +02:00
82935ddf11 website/devdocs: updated hackathon page with exact times, other info (#6321)
updated hackathon page with exact times, other info

Co-authored-by: Tana Berry <tana@goauthentik.io>
2023-07-21 12:08:37 +02:00
136 changed files with 5491 additions and 2973 deletions

View File

@ -0,0 +1,39 @@
# Rename transifex pull requests to have a correct naming
name: authentik-translation-transifex-rename
on:
pull_request:
types: [opened, reopened]
jobs:
rename_pr:
runs-on: ubuntu-latest
if: ${{ github.event.pull_request.user.login == 'transifex-integration[bot]'}}
steps:
- id: generate_token
uses: tibdex/github-app-token@v1
with:
app_id: ${{ secrets.GH_APP_ID }}
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- name: Get current title
id: title
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
run: |
title=$(curl -q -L \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${GH_TOKEN}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/${GITHUB_REPOSITORY}/pulls/${{ github.event.pull_request.number }} | jq -r .title)
echo "title=${title}" >> "$GITHUB_OUTPUT"
- name: Rename
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
run: |
curl -L \
-X PATCH \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${GH_TOKEN}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/${GITHUB_REPOSITORY}/pulls/${{ github.event.pull_request.number }} \
-d "{\"title\":\"translate: ${{ steps.title.outputs.title }}\"}"

View File

@ -21,9 +21,14 @@ _other_urls = []
for _authentik_app in get_apps():
try:
api_urls = import_module(f"{_authentik_app.name}.urls")
except (ModuleNotFoundError, ImportError):
except (ModuleNotFoundError, ImportError) as exc:
LOGGER.warning("Could not import app's URLs", app_name=_authentik_app.name, exc=exc)
continue
if not hasattr(api_urls, "api_urlpatterns"):
LOGGER.debug(
"App does not define API URLs",
app_name=_authentik_app.name,
)
continue
urls: list = getattr(api_urls, "api_urlpatterns")
for url in urls:

View File

@ -12,7 +12,7 @@ def migrate_user_type(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
from authentik.core.models import UserTypes
for user in User.objects.using(db_alias).all():
user.type = UserTypes.DEFAULT
user.type = UserTypes.INTERNAL
if "goauthentik.io/user/service-account" in user.attributes:
user.type = UserTypes.SERVICE_ACCOUNT
if "goauthentik.io/user/override-ips" in user.attributes:

View File

@ -0,0 +1,41 @@
# Generated by Django 4.1.10 on 2023-07-21 12:54
from django.apps.registry import Apps
from django.db import migrations, models
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
def migrate_user_type_v2(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
db_alias = schema_editor.connection.alias
User = apps.get_model("authentik_core", "User")
from authentik.core.models import UserTypes
for user in User.objects.using(db_alias).all():
if user.type != "default":
continue
user.type = UserTypes.INTERNAL
user.save()
class Migration(migrations.Migration):
dependencies = [
("authentik_core", "0030_user_type"),
]
operations = [
migrations.AlterField(
model_name="user",
name="type",
field=models.TextField(
choices=[
("internal", "Internal"),
("external", "External"),
("service_account", "Service Account"),
("internal_service_account", "Internal Service Account"),
],
default="internal",
),
),
migrations.RunPython(migrate_user_type_v2),
]

View File

@ -67,7 +67,7 @@ class UserTypes(models.TextChoices):
"""User types, both for grouping, licensing and permissions in the case
of the internal_service_account"""
DEFAULT = "default"
INTERNAL = "internal"
EXTERNAL = "external"
# User-created service accounts
@ -161,7 +161,7 @@ class User(SerializerModel, GuardianUserMixin, AbstractUser):
uuid = models.UUIDField(default=uuid4, editable=False, unique=True)
name = models.TextField(help_text=_("User's display name."))
path = models.TextField(default="users")
type = models.TextField(choices=UserTypes.choices, default=UserTypes.DEFAULT)
type = models.TextField(choices=UserTypes.choices, default=UserTypes.INTERNAL)
sources = models.ManyToManyField("Source", through="UserSourceConnection")
ak_groups = models.ManyToManyField("Group", related_name="users")

View File

@ -137,7 +137,7 @@ class LicenseViewSet(UsedByMixin, ModelViewSet):
last_month = now() - timedelta(days=30)
# Forecast for default users
users_in_last_month = User.objects.filter(
type=UserTypes.DEFAULT, date_joined__gte=last_month
type=UserTypes.INTERNAL, date_joined__gte=last_month
).count()
# Forecast for external users
external_in_last_month = LicenseKey.get_external_user_count()

View File

@ -9,7 +9,7 @@ from time import mktime
from uuid import uuid4
from cryptography.exceptions import InvalidSignature
from cryptography.x509 import Certificate, load_pem_x509_certificate
from cryptography.x509 import Certificate, load_der_x509_certificate, load_pem_x509_certificate
from dacite import from_dict
from django.db import models
from django.db.models.query import QuerySet
@ -61,8 +61,8 @@ class LicenseKey:
if len(x5c) < 1:
raise ValidationError("Unable to verify license")
try:
our_cert = load_pem_x509_certificate(b64decode(x5c[0]))
intermediate = load_pem_x509_certificate(b64decode(x5c[1]))
our_cert = load_der_x509_certificate(b64decode(x5c[0]))
intermediate = load_der_x509_certificate(b64decode(x5c[1]))
our_cert.verify_directly_issued_by(intermediate)
intermediate.verify_directly_issued_by(get_licensing_key())
except (InvalidSignature, TypeError, ValueError, Error):
@ -73,7 +73,7 @@ class LicenseKey:
decode(
jwt,
our_cert.public_key(),
algorithms=["ES521"],
algorithms=["ES512"],
audience=get_license_aud(),
),
)
@ -105,7 +105,7 @@ class LicenseKey:
@staticmethod
def get_default_user_count():
"""Get current default user count"""
return LicenseKey.base_user_qs().filter(type=UserTypes.DEFAULT).count()
return LicenseKey.base_user_qs().filter(type=UserTypes.INTERNAL).count()
@staticmethod
def get_external_user_count():

View File

@ -24,7 +24,7 @@ class EnterprisePolicy(Policy):
def passes(self, request: PolicyRequest) -> PolicyResult:
if not LicenseKey.get_total().is_valid():
return PolicyResult(False)
if request.user.type != UserTypes.DEFAULT:
if request.user.type != UserTypes.INTERNAL:
return PolicyResult(False)
return PolicyResult(True)

View File

@ -76,9 +76,20 @@ class TaskInfo:
return cache.get_many(cache.keys(CACHE_KEY_PREFIX + name)).values()
return cache.get(CACHE_KEY_PREFIX + name, None)
@property
def full_name(self) -> str:
"""Get the full cache key with task name and UID"""
key = CACHE_KEY_PREFIX + self.task_name
if self.result.uid:
uid_suffix = f":{self.result.uid}"
key += uid_suffix
if not self.task_name.endswith(uid_suffix):
self.task_name += uid_suffix
return key
def delete(self):
"""Delete task info from cache"""
return cache.delete(CACHE_KEY_PREFIX + self.task_name)
return cache.delete(self.full_name)
def update_metrics(self):
"""Update prometheus metrics"""
@ -97,12 +108,8 @@ class TaskInfo:
def save(self, timeout_hours=6):
"""Save task into cache"""
key = CACHE_KEY_PREFIX + self.task_name
if self.result.uid:
key += f":{self.result.uid}"
self.task_name += f":{self.result.uid}"
self.update_metrics()
cache.set(key, self, timeout=timeout_hours * 60 * 60)
cache.set(self.full_name, self, timeout=timeout_hours * 60 * 60)
class MonitoredTask(Task):

View File

@ -0,0 +1,43 @@
"""Test Monitored tasks"""
from django.test import TestCase
from authentik.events.monitored_tasks import MonitoredTask, TaskInfo, TaskResult, TaskResultStatus
from authentik.lib.generators import generate_id
from authentik.root.celery import CELERY_APP
class TestMonitoredTasks(TestCase):
"""Test Monitored tasks"""
def test_failed_successful_remove_state(self):
"""Test that a task with `save_on_success` set to `False` that failed saves
a state, and upon successful completion will delete the state"""
should_fail = True
uid = generate_id()
@CELERY_APP.task(
bind=True,
base=MonitoredTask,
)
def test_task(self: MonitoredTask):
self.save_on_success = False
self.set_uid(uid)
self.set_status(
TaskResult(TaskResultStatus.ERROR if should_fail else TaskResultStatus.SUCCESSFUL)
)
# First test successful run
should_fail = False
test_task.delay().get()
self.assertIsNone(TaskInfo.by_name(f"test_task:{uid}"))
# Then test failed
should_fail = True
test_task.delay().get()
info = TaskInfo.by_name(f"test_task:{uid}")
self.assertEqual(info.result.status, TaskResultStatus.ERROR)
# Then after that, the state should be removed
should_fail = False
test_task.delay().get()
self.assertIsNone(TaskInfo.by_name(f"test_task:{uid}"))

View File

@ -1,10 +0,0 @@
"""authentik lib app config"""
from django.apps import AppConfig
class AuthentikLibConfig(AppConfig):
"""authentik lib app config"""
name = "authentik.lib"
label = "authentik_lib"
verbose_name = "authentik lib"

View File

@ -1,16 +1,22 @@
"""Base Kubernetes Reconciler"""
from dataclasses import asdict
from json import dumps
from typing import TYPE_CHECKING, Generic, Optional, TypeVar
from dacite.core import from_dict
from django.utils.text import slugify
from kubernetes.client import V1ObjectMeta
from jsonpatch import JsonPatchConflict, JsonPatchException, JsonPatchTestFailed, apply_patch
from kubernetes.client import ApiClient, V1ObjectMeta
from kubernetes.client.exceptions import ApiException, OpenApiException
from kubernetes.client.models.v1_deployment import V1Deployment
from kubernetes.client.models.v1_pod import V1Pod
from requests import Response
from structlog.stdlib import get_logger
from urllib3.exceptions import HTTPError
from authentik import __version__
from authentik.outposts.apps import MANAGED_OUTPOST
from authentik.outposts.controllers.base import ControllerException
from authentik.outposts.controllers.k8s.triggers import NeedsRecreate, NeedsUpdate
if TYPE_CHECKING:
@ -34,11 +40,23 @@ class KubernetesObjectReconciler(Generic[T]):
self.namespace = controller.outpost.config.kubernetes_namespace
self.logger = get_logger().bind(type=self.__class__.__name__)
def get_patch(self):
"""Get any patches that apply to this CRD"""
patches = self.controller.outpost.config.kubernetes_json_patches
if not patches:
return None
return patches.get(self.reconciler_name(), None)
@property
def is_embedded(self) -> bool:
"""Return true if the current outpost is embedded"""
return self.controller.outpost.managed == MANAGED_OUTPOST
@staticmethod
def reconciler_name() -> str:
"""A name this reconciler is identified by in the configuration"""
raise NotImplementedError
@property
def noop(self) -> bool:
"""Return true if this object should not be created/updated/deleted in this cluster"""
@ -55,6 +73,32 @@ class KubernetesObjectReconciler(Generic[T]):
}
).lower()
def get_patched_reference_object(self) -> T:
"""Get patched reference object"""
reference = self.get_reference_object()
patch = self.get_patch()
try:
json = ApiClient().sanitize_for_serialization(reference)
# Custom objects will not be known to the clients openapi types
except AttributeError:
json = asdict(reference)
try:
ref = json
if patch is not None:
ref = apply_patch(json, patch)
except (JsonPatchException, JsonPatchConflict, JsonPatchTestFailed) as exc:
raise ControllerException(f"JSON Patch failed: {exc}") from exc
mock_response = Response()
mock_response.data = dumps(ref)
try:
result = ApiClient().deserialize(mock_response, reference.__class__.__name__)
# Custom objects will not be known to the clients openapi types
except AttributeError:
result = from_dict(reference.__class__, data=ref)
return result
# pylint: disable=invalid-name
def up(self):
"""Create object if it doesn't exist, update if needed or recreate if needed."""
@ -62,7 +106,7 @@ class KubernetesObjectReconciler(Generic[T]):
if self.noop:
self.logger.debug("Object is noop")
return
reference = self.get_reference_object()
reference = self.get_patched_reference_object()
try:
try:
current = self.retrieve()
@ -129,6 +173,16 @@ class KubernetesObjectReconciler(Generic[T]):
if current.metadata.labels != reference.metadata.labels:
raise NeedsUpdate()
patch = self.get_patch()
if patch is not None:
current_json = ApiClient().sanitize_for_serialization(current)
try:
if apply_patch(current_json, patch) != current_json:
raise NeedsUpdate()
except (JsonPatchException, JsonPatchConflict, JsonPatchTestFailed) as exc:
raise ControllerException(f"JSON Patch failed: {exc}") from exc
def create(self, reference: T):
"""API Wrapper to create object"""
raise NotImplementedError

View File

@ -43,6 +43,10 @@ class DeploymentReconciler(KubernetesObjectReconciler[V1Deployment]):
self.api = AppsV1Api(controller.client)
self.outpost = self.controller.outpost
@staticmethod
def reconciler_name() -> str:
return "deployment"
def reconcile(self, current: V1Deployment, reference: V1Deployment):
compare_ports(
current.spec.template.spec.containers[0].ports,

View File

@ -24,6 +24,10 @@ class SecretReconciler(KubernetesObjectReconciler[V1Secret]):
super().__init__(controller)
self.api = CoreV1Api(controller.client)
@staticmethod
def reconciler_name() -> str:
return "secret"
def reconcile(self, current: V1Secret, reference: V1Secret):
super().reconcile(current, reference)
for key in reference.data.keys():

View File

@ -20,6 +20,10 @@ class ServiceReconciler(KubernetesObjectReconciler[V1Service]):
super().__init__(controller)
self.api = CoreV1Api(controller.client)
@staticmethod
def reconciler_name() -> str:
return "service"
def reconcile(self, current: V1Service, reference: V1Service):
compare_ports(current.spec.ports, reference.spec.ports)
# run the base reconcile last, as that will probably raise NeedsUpdate

View File

@ -71,6 +71,10 @@ class PrometheusServiceMonitorReconciler(KubernetesObjectReconciler[PrometheusSe
self.api_ex = ApiextensionsV1Api(controller.client)
self.api = CustomObjectsApi(controller.client)
@staticmethod
def reconciler_name() -> str:
return "prometheus servicemonitor"
@property
def noop(self) -> bool:
return (not self._crd_exists()) or (self.is_embedded)

View File

@ -64,12 +64,19 @@ class KubernetesController(BaseController):
super().__init__(outpost, connection)
self.client = KubernetesClient(connection)
self.reconcilers = {
"secret": SecretReconciler,
"deployment": DeploymentReconciler,
"service": ServiceReconciler,
"prometheus servicemonitor": PrometheusServiceMonitorReconciler,
SecretReconciler.reconciler_name(): SecretReconciler,
DeploymentReconciler.reconciler_name(): DeploymentReconciler,
ServiceReconciler.reconciler_name(): ServiceReconciler,
PrometheusServiceMonitorReconciler.reconciler_name(): (
PrometheusServiceMonitorReconciler
),
}
self.reconcile_order = ["secret", "deployment", "service", "prometheus servicemonitor"]
self.reconcile_order = [
SecretReconciler.reconciler_name(),
DeploymentReconciler.reconciler_name(),
ServiceReconciler.reconciler_name(),
PrometheusServiceMonitorReconciler.reconciler_name(),
]
def up(self):
try:

View File

@ -1,7 +1,7 @@
"""Outpost models"""
from dataclasses import asdict, dataclass, field
from datetime import datetime
from typing import Iterable, Optional
from typing import Any, Iterable, Optional
from uuid import uuid4
from dacite.core import from_dict
@ -75,6 +75,7 @@ class OutpostConfig:
kubernetes_service_type: str = field(default="ClusterIP")
kubernetes_disabled_components: list[str] = field(default_factory=list)
kubernetes_image_pull_secrets: list[str] = field(default_factory=list)
kubernetes_json_patches: Optional[dict[str, list[dict[str, Any]]]] = field(default=None)
class OutpostModel(Model):

View File

@ -11,6 +11,7 @@ from authentik.core.tests.utils import create_test_admin_user, create_test_cert,
from authentik.policies.models import PolicyBinding
from authentik.providers.oauth2.constants import (
GRANT_TYPE_CLIENT_CREDENTIALS,
GRANT_TYPE_PASSWORD,
SCOPE_OPENID,
SCOPE_OPENID_EMAIL,
SCOPE_OPENID_PROFILE,
@ -150,3 +151,28 @@ class TestTokenClientCredentials(OAuthTestCase):
)
self.assertEqual(jwt["given_name"], self.user.name)
self.assertEqual(jwt["preferred_username"], self.user.username)
def test_successful_password(self):
"""test successful (password grant)"""
response = self.client.post(
reverse("authentik_providers_oauth2:token"),
{
"grant_type": GRANT_TYPE_PASSWORD,
"scope": f"{SCOPE_OPENID} {SCOPE_OPENID_EMAIL} {SCOPE_OPENID_PROFILE}",
"client_id": self.provider.client_id,
"username": "sa",
"password": self.token.key,
},
)
self.assertEqual(response.status_code, 200)
body = loads(response.content.decode())
self.assertEqual(body["token_type"], TOKEN_TYPE)
_, alg = self.provider.jwt_key
jwt = decode(
body["access_token"],
key=self.provider.signing_key.public_key,
algorithms=[alg],
audience=self.provider.client_id,
)
self.assertEqual(jwt["given_name"], self.user.name)
self.assertEqual(jwt["preferred_username"], self.user.username)

View File

@ -459,13 +459,13 @@ class TokenView(View):
if self.params.grant_type == GRANT_TYPE_REFRESH_TOKEN:
LOGGER.debug("Refreshing refresh token")
return TokenResponse(self.create_refresh_response())
if self.params.grant_type == GRANT_TYPE_CLIENT_CREDENTIALS:
LOGGER.debug("Client credentials grant")
if self.params.grant_type in [GRANT_TYPE_CLIENT_CREDENTIALS, GRANT_TYPE_PASSWORD]:
LOGGER.debug("Client credentials/password grant")
return TokenResponse(self.create_client_credentials_response())
if self.params.grant_type == GRANT_TYPE_DEVICE_CODE:
LOGGER.debug("Device code grant")
return TokenResponse(self.create_device_code_response())
raise ValueError(f"Invalid grant_type: {self.params.grant_type}")
raise TokenError("unsupported_grant_type")
except (TokenError, DeviceCodeError) as error:
return TokenResponse(error.create_dict(), status=400)
except UserAuthError as error:

View File

@ -31,6 +31,10 @@ class IngressReconciler(KubernetesObjectReconciler[V1Ingress]):
super().__init__(controller)
self.api = NetworkingV1Api(controller.client)
@staticmethod
def reconciler_name() -> str:
return "ingress"
def _check_annotations(self, reference: V1Ingress):
"""Check that all annotations *we* set are correct"""
for key, value in self.get_ingress_annotations().items():

View File

@ -17,24 +17,28 @@ class TraefikMiddlewareReconciler(KubernetesObjectReconciler):
if not self.reconciler.crd_exists():
self.reconciler = Traefik2MiddlewareReconciler(controller)
@staticmethod
def reconciler_name() -> str:
return "traefik middleware"
@property
def noop(self) -> bool:
return self.reconciler.noop
def reconcile(self, current: TraefikMiddleware, reference: TraefikMiddleware):
return self.reconcile(current, reference)
return self.reconciler.reconcile(current, reference)
def get_reference_object(self) -> TraefikMiddleware:
return self.get_reference_object()
return self.reconciler.get_reference_object()
def create(self, reference: TraefikMiddleware):
return self.create(reference)
return self.reconciler.create(reference)
def delete(self, reference: TraefikMiddleware):
return self.delete(reference)
return self.reconciler.delete(reference)
def retrieve(self) -> TraefikMiddleware:
return self.retrieve()
return self.reconciler.retrieve()
def update(self, current: TraefikMiddleware, reference: TraefikMiddleware):
return self.update(current, reference)
return self.reconciler.update(current, reference)

View File

@ -67,6 +67,10 @@ class Traefik3MiddlewareReconciler(KubernetesObjectReconciler[TraefikMiddleware]
self.crd_version = "v1alpha1"
self.crd_plural = "middlewares"
@staticmethod
def reconciler_name() -> str:
return "traefik middleware"
@property
def noop(self) -> bool:
if not ProxyProvider.objects.filter(

View File

@ -16,7 +16,9 @@ class ProxyKubernetesController(KubernetesController):
DeploymentPort(9300, "http-metrics", "tcp"),
DeploymentPort(9443, "https", "tcp"),
]
self.reconcilers["ingress"] = IngressReconciler
self.reconcilers["traefik middleware"] = TraefikMiddlewareReconciler
self.reconcile_order.append("ingress")
self.reconcile_order.append("traefik middleware")
self.reconcilers[IngressReconciler.reconciler_name()] = IngressReconciler
self.reconcilers[
TraefikMiddlewareReconciler.reconciler_name()
] = TraefikMiddlewareReconciler
self.reconcile_order.append(IngressReconciler.reconciler_name())
self.reconcile_order.append(TraefikMiddlewareReconciler.reconciler_name())

View File

@ -10,6 +10,8 @@ from django.contrib.sessions.exceptions import SessionInterrupted
from django.contrib.sessions.middleware import SessionMiddleware as UpstreamSessionMiddleware
from django.http.request import HttpRequest
from django.http.response import HttpResponse
from django.middleware.csrf import CSRF_SESSION_KEY
from django.middleware.csrf import CsrfViewMiddleware as UpstreamCsrfViewMiddleware
from django.utils.cache import patch_vary_headers
from django.utils.http import http_date
from jwt import PyJWTError, decode, encode
@ -131,6 +133,29 @@ class SessionMiddleware(UpstreamSessionMiddleware):
return response
class CsrfViewMiddleware(UpstreamCsrfViewMiddleware):
"""Dynamically set secure depending if the upstream connection is TLS or not"""
def _set_csrf_cookie(self, request: HttpRequest, response: HttpResponse):
if settings.CSRF_USE_SESSIONS:
if request.session.get(CSRF_SESSION_KEY) != request.META["CSRF_COOKIE"]:
request.session[CSRF_SESSION_KEY] = request.META["CSRF_COOKIE"]
else:
secure = SessionMiddleware.is_secure(request)
response.set_cookie(
settings.CSRF_COOKIE_NAME,
request.META["CSRF_COOKIE"],
max_age=settings.CSRF_COOKIE_AGE,
domain=settings.CSRF_COOKIE_DOMAIN,
path=settings.CSRF_COOKIE_PATH,
secure=secure,
httponly=settings.CSRF_COOKIE_HTTPONLY,
samesite=settings.CSRF_COOKIE_SAMESITE,
)
# Set the Vary header since content varies with the CSRF cookie.
patch_vary_headers(response, ("Cookie",))
class ChannelsLoggingMiddleware:
"""Logging middleware for channels"""

View File

@ -66,7 +66,6 @@ INSTALLED_APPS = [
"authentik.crypto",
"authentik.events",
"authentik.flows",
"authentik.lib",
"authentik.outposts",
"authentik.policies.dummy",
"authentik.policies.event_matcher",
@ -226,7 +225,7 @@ MIDDLEWARE = [
"authentik.events.middleware.AuditMiddleware",
"django.middleware.security.SecurityMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"authentik.root.middleware.CsrfViewMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"authentik.core.middleware.ImpersonateMiddleware",

View File

@ -3,7 +3,10 @@ from django.core.management.base import BaseCommand
from structlog.stdlib import get_logger
from authentik.sources.ldap.models import LDAPSource
from authentik.sources.ldap.tasks import ldap_sync_single
from authentik.sources.ldap.sync.groups import GroupLDAPSynchronizer
from authentik.sources.ldap.sync.membership import MembershipLDAPSynchronizer
from authentik.sources.ldap.sync.users import UserLDAPSynchronizer
from authentik.sources.ldap.tasks import ldap_sync_paginator
LOGGER = get_logger()
@ -20,4 +23,10 @@ class Command(BaseCommand):
if not source:
LOGGER.warning("Source does not exist", slug=source_slug)
continue
ldap_sync_single(source)
tasks = (
ldap_sync_paginator(source, UserLDAPSynchronizer)
+ ldap_sync_paginator(source, GroupLDAPSynchronizer)
+ ldap_sync_paginator(source, MembershipLDAPSynchronizer)
)
for task in tasks:
task()

View File

@ -49,7 +49,7 @@ class UserLDAPSynchronizer(BaseLDAPSynchronizer):
uniq = self._flatten(attributes[self._source.object_uniqueness_field])
try:
defaults = self.build_user_properties(user_dn, **attributes)
self._logger.debug("Creating user with attributes", **defaults)
self._logger.debug("Writing user with attributes", **defaults)
if "username" not in defaults:
raise IntegrityError("Username was not set by propertymappings")
ak_user, created = self.update_or_create_attributes(

View File

@ -59,7 +59,7 @@ def ldap_sync_paginator(source: LDAPSource, sync: type[BaseLDAPSynchronizer]) ->
signatures = []
for page in sync_inst.get_objects():
page_cache_key = CACHE_KEY_PREFIX + str(uuid4())
cache.set(page_cache_key, page)
cache.set(page_cache_key, page, 60 * 60 * int(CONFIG.get("ldap.task_timeout_hours")))
page_sync = ldap_sync.si(source.pk, class_to_path(sync), page_cache_key)
signatures.append(page_sync)
return signatures
@ -86,6 +86,12 @@ def ldap_sync(self: MonitoredTask, source_pk: str, sync_class: str, page_cache_k
sync_inst: BaseLDAPSynchronizer = sync(source)
page = cache.get(page_cache_key)
if not page:
error_message = (
f"Could not find page in cache: {page_cache_key}. "
+ "Try increasing ldap.task_timeout_hours"
)
LOGGER.warning(error_message)
self.set_status(TaskResult(TaskResultStatus.ERROR, [error_message]))
return
cache.touch(page_cache_key)
count = sync_inst.sync(page)

View File

@ -8,12 +8,14 @@ from authentik.blueprints.tests import apply_blueprint
from authentik.core.models import Group, User
from authentik.core.tests.utils import create_test_admin_user
from authentik.events.models import Event, EventAction
from authentik.events.monitored_tasks import TaskInfo, TaskResultStatus
from authentik.lib.generators import generate_key
from authentik.lib.utils.reflection import class_to_path
from authentik.sources.ldap.models import LDAPPropertyMapping, LDAPSource
from authentik.sources.ldap.sync.groups import GroupLDAPSynchronizer
from authentik.sources.ldap.sync.membership import MembershipLDAPSynchronizer
from authentik.sources.ldap.sync.users import UserLDAPSynchronizer
from authentik.sources.ldap.tasks import ldap_sync_all
from authentik.sources.ldap.tasks import ldap_sync, ldap_sync_all
from authentik.sources.ldap.tests.mock_ad import mock_ad_connection
from authentik.sources.ldap.tests.mock_slapd import mock_slapd_connection
@ -33,6 +35,14 @@ class LDAPSyncTests(TestCase):
additional_group_dn="ou=groups",
)
def test_sync_missing_page(self):
"""Test sync with missing page"""
connection = MagicMock(return_value=mock_ad_connection(LDAP_PASSWORD))
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
ldap_sync.delay(self.source.pk, class_to_path(UserLDAPSynchronizer), "foo").get()
status = TaskInfo.by_name("ldap_sync:ldap:users:foo")
self.assertEqual(status.result.status, TaskResultStatus.ERROR)
def test_sync_error(self):
"""Test user sync"""
self.source.property_mappings.set(

View File

@ -3213,7 +3213,6 @@
"authentik.crypto",
"authentik.events",
"authentik.flows",
"authentik.lib",
"authentik.outposts",
"authentik.policies.dummy",
"authentik.policies.event_matcher",
@ -3984,7 +3983,7 @@
"type": {
"type": "string",
"enum": [
"default",
"internal",
"external",
"service_account",
"internal_service_account"
@ -4185,7 +4184,7 @@
"type": {
"type": "string",
"enum": [
"default",
"internal",
"external",
"service_account",
"internal_service_account"
@ -4390,7 +4389,7 @@
"type": {
"type": "string",
"enum": [
"default",
"internal",
"external",
"service_account",
"internal_service_account"
@ -6556,7 +6555,7 @@
"type": {
"type": "string",
"enum": [
"default",
"internal",
"external",
"service_account",
"internal_service_account"
@ -7301,7 +7300,7 @@
"type": {
"type": "string",
"enum": [
"default",
"internal",
"external",
"service_account",
"internal_service_account"
@ -8387,7 +8386,7 @@
"type": {
"type": "string",
"enum": [
"default",
"internal",
"external",
"service_account",
"internal_service_account"

View File

@ -1,36 +0,0 @@
# This file is used for development and debugging, and should not be used for production instances
version: '3.5'
services:
flower:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.5.4}
restart: unless-stopped
command: worker-status
environment:
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_POSTGRESQL__HOST: postgresql
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
env_file:
- .env
ports:
- "9001:9000"
depends_on:
- postgresql
- redis
server:
environment:
AUTHENTIK_REMOTE_DEBUG: "true"
PYDEVD_THREAD_DUMP_ON_WARN_EVALUATION_TIMEOUT: "true"
ports:
- 6800:6800
worker:
environment:
CELERY_RDB_HOST: "0.0.0.0"
CELERY_RDBSIG: "1"
AUTHENTIK_REMOTE_DEBUG: "true"
PYDEVD_THREAD_DUMP_ON_WARN_EVALUATION_TIMEOUT: "true"
ports:
- 6900:6900

2
go.mod
View File

@ -26,7 +26,7 @@ require (
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.7.0
github.com/stretchr/testify v1.8.4
goauthentik.io/api/v3 v3.2023061.3
goauthentik.io/api/v3 v3.2023061.6
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
golang.org/x/oauth2 v0.10.0
golang.org/x/sync v0.3.0

4
go.sum
View File

@ -1070,8 +1070,8 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe
go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
goauthentik.io/api/v3 v3.2023061.3 h1:uD+M52JTba2w8n5rWQKoPQ3gD0BDQuLNaDx/G0rKwKE=
goauthentik.io/api/v3 v3.2023061.3/go.mod h1:tC7qK9VSP0zJah5p5xHFnjZt/4dAkXVwcrWyZNGYhwQ=
goauthentik.io/api/v3 v3.2023061.6 h1:4zbo0Dtx42HLYObizIlTWAk7iBvCv9kmCvzBxMElkIk=
goauthentik.io/api/v3 v3.2023061.6/go.mod h1:tC7qK9VSP0zJah5p5xHFnjZt/4dAkXVwcrWyZNGYhwQ=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=

View File

@ -8,6 +8,7 @@ import (
"net/http/cookiejar"
"net/url"
"strings"
"time"
"github.com/getsentry/sentry-go"
"github.com/prometheus/client_golang/prometheus"
@ -21,10 +22,20 @@ import (
var (
FlowTimingGet = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "authentik_outpost_flow_timing_get_seconds",
Help: "Duration it took to get a challenge in seconds",
}, []string{"stage", "flow"})
FlowTimingPost = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "authentik_outpost_flow_timing_post_seconds",
Help: "Duration it took to send a challenge in seconds",
}, []string{"stage", "flow"})
// NOTE: the following metrics are kept for compatibility purpose
FlowTimingGetLegacy = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "authentik_outpost_flow_timing_get",
Help: "Duration it took to get a challenge",
}, []string{"stage", "flow"})
FlowTimingPost = promauto.NewHistogramVec(prometheus.HistogramOpts{
FlowTimingPostLegacy = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "authentik_outpost_flow_timing_post",
Help: "Duration it took to send a challenge",
}, []string{"stage", "flow"})
@ -186,6 +197,10 @@ func (fe *FlowExecutor) getInitialChallenge() (*api.ChallengeTypes, error) {
FlowTimingGet.With(prometheus.Labels{
"stage": ch.GetComponent(),
"flow": fe.flowSlug,
}).Observe(float64(gcsp.EndTime.Sub(gcsp.StartTime)) / float64(time.Second))
FlowTimingGetLegacy.With(prometheus.Labels{
"stage": ch.GetComponent(),
"flow": fe.flowSlug,
}).Observe(float64(gcsp.EndTime.Sub(gcsp.StartTime)))
return challenge, nil
}
@ -243,6 +258,10 @@ func (fe *FlowExecutor) solveFlowChallenge(challenge *api.ChallengeTypes, depth
FlowTimingPost.With(prometheus.Labels{
"stage": ch.GetComponent(),
"flow": fe.flowSlug,
}).Observe(float64(scsp.EndTime.Sub(scsp.StartTime)) / float64(time.Second))
FlowTimingPostLegacy.With(prometheus.Labels{
"stage": ch.GetComponent(),
"flow": fe.flowSlug,
}).Observe(float64(scsp.EndTime.Sub(scsp.StartTime)))
if depth >= 10 {

View File

@ -2,6 +2,7 @@ package ldap
import (
"net"
"time"
"beryju.io/ldap"
"github.com/getsentry/sentry-go"
@ -20,6 +21,11 @@ func (ls *LDAPServer) Bind(bindDN string, bindPW string, conn net.Conn) (ldap.LD
"outpost_name": ls.ac.Outpost.Name,
"type": "bind",
"app": selectedApp,
}).Observe(float64(span.EndTime.Sub(span.StartTime)) / float64(time.Second))
metrics.RequestsLegacy.With(prometheus.Labels{
"outpost_name": ls.ac.Outpost.Name,
"type": "bind",
"app": selectedApp,
}).Observe(float64(span.EndTime.Sub(span.StartTime)))
req.Log().WithField("took-ms", span.EndTime.Sub(span.StartTime).Milliseconds()).Info("Bind request")
}()
@ -49,6 +55,12 @@ func (ls *LDAPServer) Bind(bindDN string, bindPW string, conn net.Conn) (ldap.LD
"reason": "no_provider",
"app": "",
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": ls.ac.Outpost.Name,
"type": "bind",
"reason": "no_provider",
"app": "",
}).Inc()
return ldap.LDAPResultInsufficientAccessRights, nil
}

View File

@ -52,6 +52,12 @@ func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResul
"reason": "flow_error",
"app": db.si.GetAppSlug(),
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": db.si.GetOutpostName(),
"type": "bind",
"reason": "flow_error",
"app": db.si.GetAppSlug(),
}).Inc()
req.Log().WithError(err).Warning("failed to execute flow")
return ldap.LDAPResultInvalidCredentials, nil
}
@ -62,6 +68,12 @@ func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResul
"reason": "invalid_credentials",
"app": db.si.GetAppSlug(),
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": db.si.GetOutpostName(),
"type": "bind",
"reason": "invalid_credentials",
"app": db.si.GetAppSlug(),
}).Inc()
req.Log().Info("Invalid credentials")
return ldap.LDAPResultInvalidCredentials, nil
}
@ -75,6 +87,12 @@ func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResul
"reason": "access_denied",
"app": db.si.GetAppSlug(),
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": db.si.GetOutpostName(),
"type": "bind",
"reason": "access_denied",
"app": db.si.GetAppSlug(),
}).Inc()
return ldap.LDAPResultInsufficientAccessRights, nil
}
if err != nil {
@ -84,6 +102,12 @@ func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResul
"reason": "access_check_fail",
"app": db.si.GetAppSlug(),
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": db.si.GetOutpostName(),
"type": "bind",
"reason": "access_check_fail",
"app": db.si.GetAppSlug(),
}).Inc()
req.Log().WithError(err).Warning("failed to check access")
return ldap.LDAPResultOperationsError, nil
}
@ -98,6 +122,12 @@ func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResul
"reason": "user_info_fail",
"app": db.si.GetAppSlug(),
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": db.si.GetOutpostName(),
"type": "bind",
"reason": "user_info_fail",
"app": db.si.GetAppSlug(),
}).Inc()
req.Log().WithError(err).Warning("failed to get user info")
return ldap.LDAPResultOperationsError, nil
}

View File

@ -15,10 +15,20 @@ import (
var (
Requests = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "authentik_outpost_ldap_request_duration_seconds",
Help: "LDAP request latencies in seconds",
}, []string{"outpost_name", "type", "app"})
RequestsRejected = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "authentik_outpost_ldap_requests_rejected_total",
Help: "Total number of rejected requests",
}, []string{"outpost_name", "type", "reason", "app"})
// NOTE: the following metrics are kept for compatibility purpose
RequestsLegacy = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "authentik_outpost_ldap_requests",
Help: "The total number of configured providers",
}, []string{"outpost_name", "type", "app"})
RequestsRejected = promauto.NewCounterVec(prometheus.CounterOpts{
RequestsRejectedLegacy = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "authentik_outpost_ldap_requests_rejected",
Help: "Total number of rejected requests",
}, []string{"outpost_name", "type", "reason", "app"})

View File

@ -2,6 +2,7 @@ package ldap
import (
"net"
"time"
"beryju.io/ldap"
"github.com/getsentry/sentry-go"
@ -21,6 +22,11 @@ func (ls *LDAPServer) Search(bindDN string, searchReq ldap.SearchRequest, conn n
"outpost_name": ls.ac.Outpost.Name,
"type": "search",
"app": selectedApp,
}).Observe(float64(span.EndTime.Sub(span.StartTime)) / float64(time.Second))
metrics.RequestsLegacy.With(prometheus.Labels{
"outpost_name": ls.ac.Outpost.Name,
"type": "search",
"app": selectedApp,
}).Observe(float64(span.EndTime.Sub(span.StartTime)))
req.Log().WithField("attributes", searchReq.Attributes).WithField("took-ms", span.EndTime.Sub(span.StartTime).Milliseconds()).Info("Search request")
}()

View File

@ -45,6 +45,12 @@ func (ds *DirectSearcher) Search(req *search.Request) (ldap.ServerSearchResult,
"reason": "empty_bind_dn",
"app": ds.si.GetAppSlug(),
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": ds.si.GetOutpostName(),
"type": "search",
"reason": "empty_bind_dn",
"app": ds.si.GetAppSlug(),
}).Inc()
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Search Error: Anonymous BindDN not allowed %s", req.BindDN)
}
if !utils.HasSuffixNoCase(req.BindDN, ","+baseDN) {
@ -54,6 +60,12 @@ func (ds *DirectSearcher) Search(req *search.Request) (ldap.ServerSearchResult,
"reason": "invalid_bind_dn",
"app": ds.si.GetAppSlug(),
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": ds.si.GetOutpostName(),
"type": "search",
"reason": "invalid_bind_dn",
"app": ds.si.GetAppSlug(),
}).Inc()
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Search Error: BindDN %s not in our BaseDN %s", req.BindDN, ds.si.GetBaseDN())
}
@ -66,6 +78,12 @@ func (ds *DirectSearcher) Search(req *search.Request) (ldap.ServerSearchResult,
"reason": "user_info_not_cached",
"app": ds.si.GetAppSlug(),
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": ds.si.GetOutpostName(),
"type": "search",
"reason": "user_info_not_cached",
"app": ds.si.GetAppSlug(),
}).Inc()
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, errors.New("access denied")
}
accsp.Finish()
@ -78,6 +96,12 @@ func (ds *DirectSearcher) Search(req *search.Request) (ldap.ServerSearchResult,
"reason": "filter_parse_fail",
"app": ds.si.GetAppSlug(),
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": ds.si.GetOutpostName(),
"type": "search",
"reason": "filter_parse_fail",
"app": ds.si.GetAppSlug(),
}).Inc()
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("Search Error: error parsing filter: %s", req.Filter)
}

View File

@ -62,6 +62,12 @@ func (ms *MemorySearcher) Search(req *search.Request) (ldap.ServerSearchResult,
"reason": "empty_bind_dn",
"app": ms.si.GetAppSlug(),
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": ms.si.GetOutpostName(),
"type": "search",
"reason": "empty_bind_dn",
"app": ms.si.GetAppSlug(),
}).Inc()
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Search Error: Anonymous BindDN not allowed %s", req.BindDN)
}
if !utils.HasSuffixNoCase(req.BindDN, ","+baseDN) {
@ -71,6 +77,12 @@ func (ms *MemorySearcher) Search(req *search.Request) (ldap.ServerSearchResult,
"reason": "invalid_bind_dn",
"app": ms.si.GetAppSlug(),
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": ms.si.GetOutpostName(),
"type": "search",
"reason": "invalid_bind_dn",
"app": ms.si.GetAppSlug(),
}).Inc()
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Search Error: BindDN %s not in our BaseDN %s", req.BindDN, ms.si.GetBaseDN())
}
@ -83,6 +95,12 @@ func (ms *MemorySearcher) Search(req *search.Request) (ldap.ServerSearchResult,
"reason": "user_info_not_cached",
"app": ms.si.GetAppSlug(),
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": ms.si.GetOutpostName(),
"type": "search",
"reason": "user_info_not_cached",
"app": ms.si.GetAppSlug(),
}).Inc()
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, errors.New("access denied")
}
accsp.Finish()

View File

@ -2,9 +2,10 @@ package ldap
import (
"net"
"time"
"github.com/getsentry/sentry-go"
"beryju.io/ldap"
"github.com/getsentry/sentry-go"
"github.com/prometheus/client_golang/prometheus"
log "github.com/sirupsen/logrus"
"goauthentik.io/internal/outpost/ldap/bind"
@ -20,6 +21,11 @@ func (ls *LDAPServer) Unbind(boundDN string, conn net.Conn) (ldap.LDAPResultCode
"outpost_name": ls.ac.Outpost.Name,
"type": "unbind",
"app": selectedApp,
}).Observe(float64(span.EndTime.Sub(span.StartTime)) / float64(time.Second))
metrics.RequestsLegacy.With(prometheus.Labels{
"outpost_name": ls.ac.Outpost.Name,
"type": "unbind",
"app": selectedApp,
}).Observe(float64(span.EndTime.Sub(span.StartTime)))
req.Log().WithField("took-ms", span.EndTime.Sub(span.StartTime).Milliseconds()).Info("Unbind request")
}()
@ -49,5 +55,11 @@ func (ls *LDAPServer) Unbind(boundDN string, conn net.Conn) (ldap.LDAPResultCode
"reason": "no_provider",
"app": "",
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": ls.ac.Outpost.Name,
"type": "unbind",
"reason": "no_provider",
"app": "",
}).Inc()
return ldap.LDAPResultOperationsError, nil
}

View File

@ -163,13 +163,19 @@ func NewApplication(p api.ProxyOutpostConfig, c *http.Client, server Server) (*A
}
before := time.Now()
inner.ServeHTTP(rw, r)
after := time.Since(before)
elapsed := time.Since(before)
metrics.Requests.With(prometheus.Labels{
"outpost_name": a.outpostName,
"type": "app",
"method": r.Method,
"host": web.GetHost(r),
}).Observe(float64(after))
}).Observe(float64(elapsed) / float64(time.Second))
metrics.RequestsLegacy.With(prometheus.Labels{
"outpost_name": a.outpostName,
"type": "app",
"method": r.Method,
"host": web.GetHost(r),
}).Observe(float64(elapsed))
})
})
if server.API().GlobalConfig.ErrorReporting.Enabled {

View File

@ -55,7 +55,7 @@ func (a *Application) configureProxy() error {
}
before := time.Now()
rp.ServeHTTP(rw, r)
after := time.Since(before)
elapsed := time.Since(before)
metrics.UpstreamTiming.With(prometheus.Labels{
"outpost_name": a.outpostName,
@ -63,7 +63,14 @@ func (a *Application) configureProxy() error {
"method": r.Method,
"scheme": r.URL.Scheme,
"host": web.GetHost(r),
}).Observe(float64(after))
}).Observe(float64(elapsed) / float64(time.Second))
metrics.UpstreamTimingLegacy.With(prometheus.Labels{
"outpost_name": a.outpostName,
"upstream_host": r.URL.Host,
"method": r.Method,
"scheme": r.URL.Scheme,
"host": web.GetHost(r),
}).Observe(float64(elapsed))
})
return nil
}

View File

@ -19,25 +19,37 @@ import (
func (ps *ProxyServer) HandlePing(rw http.ResponseWriter, r *http.Request) {
before := time.Now()
rw.WriteHeader(204)
after := time.Since(before)
elapsed := time.Since(before)
metrics.Requests.With(prometheus.Labels{
"outpost_name": ps.akAPI.Outpost.Name,
"method": r.Method,
"host": web.GetHost(r),
"type": "ping",
}).Observe(float64(after))
}).Observe(float64(elapsed) / float64(time.Second))
metrics.RequestsLegacy.With(prometheus.Labels{
"outpost_name": ps.akAPI.Outpost.Name,
"method": r.Method,
"host": web.GetHost(r),
"type": "ping",
}).Observe(float64(elapsed))
}
func (ps *ProxyServer) HandleStatic(rw http.ResponseWriter, r *http.Request) {
before := time.Now()
web.DisableIndex(http.StripPrefix("/outpost.goauthentik.io/static/dist", staticWeb.StaticHandler)).ServeHTTP(rw, r)
after := time.Since(before)
elapsed := time.Since(before)
metrics.Requests.With(prometheus.Labels{
"outpost_name": ps.akAPI.Outpost.Name,
"method": r.Method,
"host": web.GetHost(r),
"type": "static",
}).Observe(float64(after))
}).Observe(float64(elapsed) / float64(time.Second))
metrics.RequestsLegacy.With(prometheus.Labels{
"outpost_name": ps.akAPI.Outpost.Name,
"method": r.Method,
"host": web.GetHost(r),
"type": "static",
}).Observe(float64(elapsed))
}
func (ps *ProxyServer) lookupApp(r *http.Request) (*application.Application, string) {

View File

@ -15,10 +15,20 @@ import (
var (
Requests = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "authentik_outpost_proxy_request_duration_seconds",
Help: "Proxy request latencies in seconds",
}, []string{"outpost_name", "method", "host", "type"})
UpstreamTiming = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "authentik_outpost_proxy_upstream_response_duration_seconds",
Help: "Proxy upstream response latencies in seconds",
}, []string{"outpost_name", "method", "scheme", "host", "upstream_host"})
// NOTE: the following metric is kept for compatibility purpose
RequestsLegacy = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "authentik_outpost_proxy_requests",
Help: "The total number of configured providers",
}, []string{"outpost_name", "method", "host", "type"})
UpstreamTiming = promauto.NewHistogramVec(prometheus.HistogramOpts{
UpstreamTimingLegacy = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "authentik_outpost_proxy_upstream_time",
Help: "A summary of the duration we wait for the upstream reply",
}, []string{"outpost_name", "method", "scheme", "host", "upstream_host"})

View File

@ -32,6 +32,11 @@ func (rs *RadiusServer) Handle_AccessRequest(w radius.ResponseWriter, r *RadiusR
"reason": "flow_error",
"app": r.pi.appSlug,
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": rs.ac.Outpost.Name,
"reason": "flow_error",
"app": r.pi.appSlug,
}).Inc()
_ = w.Write(r.Response(radius.CodeAccessReject))
return
}
@ -41,6 +46,11 @@ func (rs *RadiusServer) Handle_AccessRequest(w radius.ResponseWriter, r *RadiusR
"reason": "invalid_credentials",
"app": r.pi.appSlug,
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": rs.ac.Outpost.Name,
"reason": "invalid_credentials",
"app": r.pi.appSlug,
}).Inc()
_ = w.Write(r.Response(radius.CodeAccessReject))
return
}
@ -53,6 +63,11 @@ func (rs *RadiusServer) Handle_AccessRequest(w radius.ResponseWriter, r *RadiusR
"reason": "access_check_fail",
"app": r.pi.appSlug,
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": rs.ac.Outpost.Name,
"reason": "access_check_fail",
"app": r.pi.appSlug,
}).Inc()
return
}
if !access {
@ -63,6 +78,11 @@ func (rs *RadiusServer) Handle_AccessRequest(w radius.ResponseWriter, r *RadiusR
"reason": "access_denied",
"app": r.pi.appSlug,
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": rs.ac.Outpost.Name,
"reason": "access_denied",
"app": r.pi.appSlug,
}).Inc()
return
}
_ = w.Write(r.Response(radius.CodeAccessAccept))

View File

@ -2,6 +2,7 @@ package radius
import (
"crypto/sha512"
"time"
"github.com/getsentry/sentry-go"
"github.com/google/uuid"
@ -45,6 +46,10 @@ func (rs *RadiusServer) ServeRADIUS(w radius.ResponseWriter, r *radius.Request)
metrics.Requests.With(prometheus.Labels{
"outpost_name": rs.ac.Outpost.Name,
"app": selectedApp,
}).Observe(float64(span.EndTime.Sub(span.StartTime)) / float64(time.Second))
metrics.RequestsLegacy.With(prometheus.Labels{
"outpost_name": rs.ac.Outpost.Name,
"app": selectedApp,
}).Observe(float64(span.EndTime.Sub(span.StartTime)))
}()

View File

@ -15,10 +15,20 @@ import (
var (
Requests = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "authentik_outpost_radius_request_duration_seconds",
Help: "RADIUS request latencies in seconds",
}, []string{"outpost_name", "app"})
RequestsRejected = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "authentik_outpost_radius_requests_rejected_total",
Help: "Total number of rejected requests",
}, []string{"outpost_name", "reason", "app"})
// NOTE: the following metric is kept for compatibility purpose
RequestsLegacy = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "authentik_outpost_radius_requests",
Help: "The total number of successful requests",
}, []string{"outpost_name", "app"})
RequestsRejected = promauto.NewCounterVec(prometheus.CounterOpts{
RequestsRejectedLegacy = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "authentik_outpost_radius_requests_rejected",
Help: "Total number of rejected requests",
}, []string{"outpost_name", "reason", "app"})

View File

@ -15,6 +15,12 @@ import (
var (
Requests = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "authentik_main_request_duration_seconds",
Help: "API request latencies in seconds",
}, []string{"dest"})
// NOTE: the following metric is kept for compatibility purpose
RequestsLegacy = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "authentik_main_requests",
Help: "The total number of configured providers",
}, []string{"dest"})

View File

@ -34,9 +34,13 @@ func (ws *WebServer) configureProxy() {
if ws.ProxyServer != nil {
before := time.Now()
ws.ProxyServer.Handle(rw, r)
elapsed := time.Since(before)
Requests.With(prometheus.Labels{
"dest": "embedded_outpost",
}).Observe(float64(time.Since(before)))
}).Observe(float64(elapsed) / float64(time.Second))
RequestsLegacy.With(prometheus.Labels{
"dest": "embedded_outpost",
}).Observe(float64(elapsed))
return
}
ws.proxyErrorHandler(rw, r, fmt.Errorf("proxy not running"))
@ -52,15 +56,23 @@ func (ws *WebServer) configureProxy() {
before := time.Now()
if ws.ProxyServer != nil {
if ws.ProxyServer.HandleHost(rw, r) {
elapsed := time.Since(before)
Requests.With(prometheus.Labels{
"dest": "embedded_outpost",
}).Observe(float64(time.Since(before)))
}).Observe(float64(elapsed) / float64(time.Second))
RequestsLegacy.With(prometheus.Labels{
"dest": "embedded_outpost",
}).Observe(float64(elapsed))
return
}
}
elapsed := time.Since(before)
Requests.With(prometheus.Labels{
"dest": "core",
}).Observe(float64(time.Since(before)))
}).Observe(float64(elapsed) / float64(time.Second))
RequestsLegacy.With(prometheus.Labels{
"dest": "core",
}).Observe(float64(elapsed))
r.Body = http.MaxBytesReader(rw, r.Body, 32*1024*1024)
rp.ServeHTTP(rw, r)
}))

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-07-16 13:59+0000\n"
"POT-Creation-Date: 2023-07-28 11:50+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -427,7 +427,7 @@ msgstr ""
msgid "Webhook Mappings"
msgstr ""
#: authentik/events/monitored_tasks.py:198
#: authentik/events/monitored_tasks.py:205
msgid "Task has not been run yet."
msgstr ""
@ -585,65 +585,65 @@ msgstr ""
msgid "Invalid kubeconfig"
msgstr ""
#: authentik/outposts/models.py:121
#: authentik/outposts/models.py:122
msgid ""
"If enabled, use the local connection. Required Docker socket/Kubernetes "
"Integration"
msgstr ""
#: authentik/outposts/models.py:151
#: authentik/outposts/models.py:152
msgid "Outpost Service-Connection"
msgstr ""
#: authentik/outposts/models.py:152
#: authentik/outposts/models.py:153
msgid "Outpost Service-Connections"
msgstr ""
#: authentik/outposts/models.py:160
#: authentik/outposts/models.py:161
msgid ""
"Can be in the format of 'unix://<path>' when connecting to a local docker "
"daemon, or 'https://<hostname>:2376' when connecting to a remote system."
msgstr ""
#: authentik/outposts/models.py:172
#: authentik/outposts/models.py:173
msgid ""
"CA which the endpoint's Certificate is verified against. Can be left empty "
"for no validation."
msgstr ""
#: authentik/outposts/models.py:184
#: authentik/outposts/models.py:185
msgid ""
"Certificate/Key used for authentication. Can be left empty for no "
"authentication."
msgstr ""
#: authentik/outposts/models.py:202
#: authentik/outposts/models.py:203
msgid "Docker Service-Connection"
msgstr ""
#: authentik/outposts/models.py:203
#: authentik/outposts/models.py:204
msgid "Docker Service-Connections"
msgstr ""
#: authentik/outposts/models.py:211
#: authentik/outposts/models.py:212
msgid ""
"Paste your kubeconfig here. authentik will automatically use the currently "
"selected context."
msgstr ""
#: authentik/outposts/models.py:217
#: authentik/outposts/models.py:218
msgid "Verify SSL Certificates of the Kubernetes API endpoint"
msgstr ""
#: authentik/outposts/models.py:234
#: authentik/outposts/models.py:235
msgid "Kubernetes Service-Connection"
msgstr ""
#: authentik/outposts/models.py:235
#: authentik/outposts/models.py:236
msgid "Kubernetes Service-Connections"
msgstr ""
#: authentik/outposts/models.py:251
#: authentik/outposts/models.py:252
msgid ""
"Select Service-Connection authentik should use to manage this outpost. Leave "
"empty if authentik should not handle the deployment."
@ -2017,33 +2017,33 @@ msgstr ""
msgid "Dummy Stages"
msgstr ""
#: authentik/stages/email/models.py:25
#: authentik/stages/email/models.py:26
msgid "Password Reset"
msgstr ""
#: authentik/stages/email/models.py:29
#: authentik/stages/email/models.py:30
msgid "Account Confirmation"
msgstr ""
#: authentik/stages/email/models.py:58
#: authentik/stages/email/models.py:59
msgid ""
"When enabled, global Email connection settings will be used and connection "
"settings below will be ignored."
msgstr ""
#: authentik/stages/email/models.py:73
#: authentik/stages/email/models.py:74
msgid "Activate users upon completion of stage."
msgstr ""
#: authentik/stages/email/models.py:77
#: authentik/stages/email/models.py:78
msgid "Time in minutes the token sent is valid."
msgstr ""
#: authentik/stages/email/models.py:122
#: authentik/stages/email/models.py:132
msgid "Email Stage"
msgstr ""
#: authentik/stages/email/models.py:123
#: authentik/stages/email/models.py:133
msgid "Email Stages"
msgstr ""

File diff suppressed because it is too large Load Diff

83
poetry.lock generated
View File

@ -253,13 +253,13 @@ files = [
[[package]]
name = "astroid"
version = "2.15.5"
version = "2.15.6"
description = "An abstract syntax tree for Python with inference support."
optional = false
python-versions = ">=3.7.2"
files = [
{file = "astroid-2.15.5-py3-none-any.whl", hash = "sha256:078e5212f9885fa85fbb0cf0101978a336190aadea6e13305409d099f71b2324"},
{file = "astroid-2.15.5.tar.gz", hash = "sha256:1039262575027b441137ab4a62a793a9b43defb42c32d5670f38686207cd780f"},
{file = "astroid-2.15.6-py3-none-any.whl", hash = "sha256:389656ca57b6108f939cf5d2f9a2a825a3be50ba9d589670f393236e0a03b91c"},
{file = "astroid-2.15.6.tar.gz", hash = "sha256:903f024859b7c7687d7a7f3a3f73b17301f8e42dfd9cc9df9d4418172d3e2dbd"},
]
[package.dependencies]
@ -1340,13 +1340,13 @@ tests = ["black", "django-stubs[compatible-mypy]", "djangorestframework-stubs[co
[[package]]
name = "drf-spectacular"
version = "0.26.3"
version = "0.26.4"
description = "Sane and flexible OpenAPI 3 schema generation for Django REST framework"
optional = false
python-versions = ">=3.6"
files = [
{file = "drf-spectacular-0.26.3.tar.gz", hash = "sha256:b907a72a0244e5dcfeca625e9632cd8ebccdbe2cb528b7c1de1191708be6f31e"},
{file = "drf_spectacular-0.26.3-py3-none-any.whl", hash = "sha256:1d84ac70522baaadd6d84a25ce5fe5ea50cfcba0387856689f98ac536f14aa32"},
{file = "drf-spectacular-0.26.4.tar.gz", hash = "sha256:8f5a8f87353d1bb8dcb3f3909b7109b2dcbe1d91f3e069409cf322963e140bd6"},
{file = "drf_spectacular-0.26.4-py3-none-any.whl", hash = "sha256:afeccc6533dcdb4e78afbfcc49f3c5e9c369aeb62f965e4d1a43b165449c147a"},
]
[package.dependencies]
@ -1809,6 +1809,31 @@ pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"
plugins = ["setuptools"]
requirements-deprecated-finder = ["pip-api", "pipreqs"]
[[package]]
name = "jsonpatch"
version = "1.33"
description = "Apply JSON-Patches (RFC 6902)"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*"
files = [
{file = "jsonpatch-1.33-py2.py3-none-any.whl", hash = "sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade"},
{file = "jsonpatch-1.33.tar.gz", hash = "sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c"},
]
[package.dependencies]
jsonpointer = ">=1.9"
[[package]]
name = "jsonpointer"
version = "2.4"
description = "Identify specific nodes in a JSON document (RFC 6901)"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*"
files = [
{file = "jsonpointer-2.4-py2.py3-none-any.whl", hash = "sha256:15d51bba20eea3165644553647711d150376234112651b4f1811022aecad7d7a"},
{file = "jsonpointer-2.4.tar.gz", hash = "sha256:585cee82b70211fa9e6043b7bb89db6e1aa49524340dde8ad6b63206ea689d88"},
]
[[package]]
name = "jsonschema"
version = "4.17.3"
@ -2699,17 +2724,17 @@ tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
[[package]]
name = "pylint"
version = "2.17.4"
version = "2.17.5"
description = "python code static checker"
optional = false
python-versions = ">=3.7.2"
files = [
{file = "pylint-2.17.4-py3-none-any.whl", hash = "sha256:7a1145fb08c251bdb5cca11739722ce64a63db479283d10ce718b2460e54123c"},
{file = "pylint-2.17.4.tar.gz", hash = "sha256:5dcf1d9e19f41f38e4e85d10f511e5b9c35e1aa74251bf95cdd8cb23584e2db1"},
{file = "pylint-2.17.5-py3-none-any.whl", hash = "sha256:73995fb8216d3bed149c8d51bba25b2c52a8251a2c8ac846ec668ce38fab5413"},
{file = "pylint-2.17.5.tar.gz", hash = "sha256:f7b601cbc06fef7e62a754e2b41294c2aa31f1cb659624b9a85bcba29eaf8252"},
]
[package.dependencies]
astroid = ">=2.15.4,<=2.17.0-dev0"
astroid = ">=2.15.6,<=2.17.0-dev0"
colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
dill = {version = ">=0.3.6", markers = "python_version >= \"3.11\""}
isort = ">=4.2.5,<6"
@ -3148,28 +3173,28 @@ pyasn1 = ">=0.1.3"
[[package]]
name = "ruff"
version = "0.0.278"
version = "0.0.280"
description = "An extremely fast Python linter, written in Rust."
optional = false
python-versions = ">=3.7"
files = [
{file = "ruff-0.0.278-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:1a90ebd8f2a554db1ee8d12b2f3aa575acbd310a02cd1a9295b3511a4874cf98"},
{file = "ruff-0.0.278-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:38ca1c0c8c1221fe64c0a66784c91501d09a8ed02a4dbfdc117c0ce32a81eefc"},
{file = "ruff-0.0.278-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c62a0bde4d20d087cabce2fa8b012d74c2e985da86d00fb3359880469b90e31"},
{file = "ruff-0.0.278-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7545bb037823cd63dca19280f75a523a68bd3e78e003de74609320d6822b5a52"},
{file = "ruff-0.0.278-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb380d2d6fdb60656a0b5fa78305535db513fc72ce11f4532cc1641204ef380"},
{file = "ruff-0.0.278-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d11149c7b186f224f2055e437a030cd83b164a43cc0211314c33ad1553ed9c4c"},
{file = "ruff-0.0.278-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:666e739fb2685277b879d493848afe6933e3be30d40f41fe0e571ad479d57d77"},
{file = "ruff-0.0.278-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ec8b0469b54315803aaf1fbf9a37162a3849424cab6182496f972ad56e0ea702"},
{file = "ruff-0.0.278-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c25b96602695a147d62a572865b753ef56aff1524abab13b9436724df30f9bd7"},
{file = "ruff-0.0.278-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a48621f5f372d5019662db5b3dbfc5f1450f927683d75f1153fe0ebf20eb9698"},
{file = "ruff-0.0.278-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1078125123a3c68e92463afacedb7e41b15ccafc09e510c6c755a23087afc8de"},
{file = "ruff-0.0.278-py3-none-musllinux_1_2_i686.whl", hash = "sha256:3ce0d620e257b4cad16e2f0c103b2f43a07981668a3763380542e8a131d11537"},
{file = "ruff-0.0.278-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1cae4c07d334eb588f171f1363fa89a8911047eb93184276be11a24dbbc996c7"},
{file = "ruff-0.0.278-py3-none-win32.whl", hash = "sha256:70d39f5599d8449082ab8ce542fa98e16413145eb411dd1dc16575b44565d52d"},
{file = "ruff-0.0.278-py3-none-win_amd64.whl", hash = "sha256:e131595ab7f4ce61a1650463bd2fe304b49e7d0deb0dfa664b92817c97cdba5f"},
{file = "ruff-0.0.278-py3-none-win_arm64.whl", hash = "sha256:737a0cfb6c36aaa92d97a46957dfd5e55329299074ad06ed12663b98e0c6fc82"},
{file = "ruff-0.0.278.tar.gz", hash = "sha256:1a9f1d925204cfba81b18368b7ac943befcfccc3a41e170c91353b674c6b7a66"},
{file = "ruff-0.0.280-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:48ed5aca381050a4e2f6d232db912d2e4e98e61648b513c350990c351125aaec"},
{file = "ruff-0.0.280-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:ef6ee3e429fd29d6a5ceed295809e376e6ece5b0f13c7e703efaf3d3bcb30b96"},
{file = "ruff-0.0.280-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d878370f7e9463ac40c253724229314ff6ebe4508cdb96cb536e1af4d5a9cd4f"},
{file = "ruff-0.0.280-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:83e8f372fa5627eeda5b83b5a9632d2f9c88fc6d78cead7e2a1f6fb05728d137"},
{file = "ruff-0.0.280-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7008fc6ca1df18b21fa98bdcfc711dad5f94d0fc3c11791f65e460c48ef27c82"},
{file = "ruff-0.0.280-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:fe7118c1eae3fda17ceb409629c7f3b5a22dffa7caf1f6796776936dca1fe653"},
{file = "ruff-0.0.280-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:37359cd67d2af8e09110a546507c302cbea11c66a52d2a9b6d841d465f9962d4"},
{file = "ruff-0.0.280-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd58af46b0221efb95966f1f0f7576df711cb53e50d2fdb0e83c2f33360116a4"},
{file = "ruff-0.0.280-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e7c15828d09f90e97bea8feefcd2907e8c8ce3a1f959c99f9b4b3469679f33c"},
{file = "ruff-0.0.280-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2dae8f2d9c44c5c49af01733c2f7956f808db682a4193180dedb29dd718d7bbe"},
{file = "ruff-0.0.280-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5f972567163a20fb8c2d6afc60c2ea5ef8b68d69505760a8bd0377de8984b4f6"},
{file = "ruff-0.0.280-py3-none-musllinux_1_2_i686.whl", hash = "sha256:8ffa7347ad11643f29de100977c055e47c988cd6d9f5f5ff83027600b11b9189"},
{file = "ruff-0.0.280-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7a37dab70114671d273f203268f6c3366c035fe0c8056614069e90a65e614bfc"},
{file = "ruff-0.0.280-py3-none-win32.whl", hash = "sha256:7784e3606352fcfb193f3cd22b2e2117c444cb879ef6609ec69deabd662b0763"},
{file = "ruff-0.0.280-py3-none-win_amd64.whl", hash = "sha256:4a7d52457b5dfcd3ab24b0b38eefaead8e2dca62b4fbf10de4cd0938cf20ce30"},
{file = "ruff-0.0.280-py3-none-win_arm64.whl", hash = "sha256:b7de5b8689575918e130e4384ed9f539ce91d067c0a332aedef6ca7188adac2d"},
{file = "ruff-0.0.280.tar.gz", hash = "sha256:581c43e4ac5e5a7117ad7da2120d960a4a99e68ec4021ec3cd47fe1cf78f8380"},
]
[[package]]
@ -4186,4 +4211,4 @@ files = [
[metadata]
lock-version = "2.0"
python-versions = "^3.11"
content-hash = "06466753c4ce0063905809123b1e2bb444034d84acdd108dcb20a9f92ce12fa6"
content-hash = "ab00edcd235c1c92dad9a91ace11d50df4564297193683cca7aa2b207ca27be6"

View File

@ -172,6 +172,7 @@ webauthn = "*"
wsproto = "*"
xmlsec = "*"
zxcvbn = "*"
jsonpatch = "*"
[tool.poetry.dev-dependencies]
bandit = "*"

View File

@ -3633,78 +3633,60 @@ paths:
operationId: core_tenants_list
description: Tenant Viewset
parameters:
- name: branding_favicon
required: false
in: query
description: branding_favicon
- in: query
name: branding_favicon
schema:
type: string
- name: branding_logo
required: false
in: query
description: branding_logo
- in: query
name: branding_logo
schema:
type: string
- name: branding_title
required: false
in: query
description: branding_title
- in: query
name: branding_title
schema:
type: string
- name: default
required: false
in: query
description: default
- in: query
name: default
schema:
type: boolean
- in: query
name: domain
schema:
type: string
- name: domain
required: false
in: query
description: domain
- in: query
name: event_retention
schema:
type: string
- name: event_retention
required: false
in: query
description: event_retention
- in: query
name: flow_authentication
schema:
type: string
- name: flow_authentication
required: false
in: query
description: flow_authentication
format: uuid
- in: query
name: flow_device_code
schema:
type: string
- name: flow_device_code
required: false
in: query
description: flow_device_code
format: uuid
- in: query
name: flow_invalidation
schema:
type: string
- name: flow_invalidation
required: false
in: query
description: flow_invalidation
format: uuid
- in: query
name: flow_recovery
schema:
type: string
- name: flow_recovery
required: false
in: query
description: flow_recovery
format: uuid
- in: query
name: flow_unenrollment
schema:
type: string
- name: flow_unenrollment
required: false
in: query
description: flow_unenrollment
schema:
type: string
- name: flow_user_settings
required: false
in: query
description: flow_user_settings
format: uuid
- in: query
name: flow_user_settings
schema:
type: string
format: uuid
- name: ordering
required: false
in: query
@ -3729,18 +3711,16 @@ paths:
description: A search term.
schema:
type: string
- name: tenant_uuid
required: false
in: query
description: tenant_uuid
- in: query
name: tenant_uuid
schema:
type: string
- name: web_certificate
required: false
in: query
description: web_certificate
format: uuid
- in: query
name: web_certificate
schema:
type: string
format: uuid
tags:
- core
security:
@ -4609,12 +4589,12 @@ paths:
schema:
type: string
enum:
- default
- external
- internal
- internal_service_account
- service_account
description: |-
* `default` - Default
* `internal` - Internal
* `external` - External
* `service_account` - Service Account
* `internal_service_account` - Internal Service Account
@ -5163,16 +5143,12 @@ paths:
schema:
type: boolean
default: true
- name: managed
required: false
in: query
description: managed
- in: query
name: managed
schema:
type: string
- name: name
required: false
in: query
description: name
- in: query
name: name
schema:
type: string
- name: ordering
@ -26689,7 +26665,6 @@ components:
- authentik.crypto
- authentik.events
- authentik.flows
- authentik.lib
- authentik.outposts
- authentik.policies.dummy
- authentik.policies.event_matcher
@ -26739,7 +26714,6 @@ components:
* `authentik.crypto` - authentik Crypto
* `authentik.events` - authentik Events
* `authentik.flows` - authentik Flows
* `authentik.lib` - authentik lib
* `authentik.outposts` - authentik Outpost
* `authentik.policies.dummy` - authentik Policies.Dummy
* `authentik.policies.event_matcher` - authentik Policies.Event Matcher
@ -29466,7 +29440,6 @@ components:
* `authentik.crypto` - authentik Crypto
* `authentik.events` - authentik Events
* `authentik.flows` - authentik Flows
* `authentik.lib` - authentik lib
* `authentik.outposts` - authentik Outpost
* `authentik.policies.dummy` - authentik Policies.Dummy
* `authentik.policies.event_matcher` - authentik Policies.Event Matcher
@ -29657,7 +29630,6 @@ components:
* `authentik.crypto` - authentik Crypto
* `authentik.events` - authentik Events
* `authentik.flows` - authentik Flows
* `authentik.lib` - authentik lib
* `authentik.outposts` - authentik Outpost
* `authentik.policies.dummy` - authentik Policies.Dummy
* `authentik.policies.event_matcher` - authentik Policies.Event Matcher
@ -36754,7 +36726,6 @@ components:
* `authentik.crypto` - authentik Crypto
* `authentik.events` - authentik Events
* `authentik.flows` - authentik Flows
* `authentik.lib` - authentik lib
* `authentik.outposts` - authentik Outpost
* `authentik.policies.dummy` - authentik Policies.Dummy
* `authentik.policies.event_matcher` - authentik Policies.Event Matcher
@ -42581,13 +42552,13 @@ components:
- user
UserTypeEnum:
enum:
- default
- internal
- external
- service_account
- internal_service_account
type: string
description: |-
* `default` - Default
* `internal` - Internal
* `external` - External
* `service_account` - Service Account
* `internal_service_account` - Internal Service Account

View File

@ -35,6 +35,19 @@ class OutpostKubernetesTests(TestCase):
service_connection=self.service_connection,
)
self.outpost.providers.add(self.provider)
self.outpost.config.kubernetes_json_patches = {
"deployment": [
{
"op": "add",
"path": "/spec/template/spec/containers/0/resources",
"value": {
"requests": {"cpu": "2000m", "memory": "2000Mi"},
"limits": {"cpu": "4000m", "memory": "8000Mi"},
},
}
]
}
self.outpost.providers.add(self.provider)
self.outpost.save()
def test_deployment_reconciler(self):
@ -46,6 +59,18 @@ class OutpostKubernetesTests(TestCase):
config = self.outpost.config
config.kubernetes_replicas = 3
config.kubernetes_json_patches = {
"deployment": [
{
"op": "add",
"path": "/spec/template/spec/containers/0/resources",
"value": {
"requests": {"cpu": "1000m", "memory": "2000Mi"},
"limits": {"cpu": "2000m", "memory": "4000Mi"},
},
}
]
}
self.outpost.config = config
with self.assertRaises(NeedsUpdate):

View File

@ -15,6 +15,7 @@
"trailingComma": "all",
"useTabs": false,
"vueIndentScriptAndStyle": false,
"plugins": ["@trivago/prettier-plugin-sort-imports"],
"importOrder": ["^(@?)lit(.*)$", "\\.css$", "^@goauthentik/api$", "^[./]"],
"importOrderSeparation": true,
"importOrderSortSpecifiers": true,

4230
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -30,28 +30,28 @@
"@codemirror/lang-javascript": "^6.1.9",
"@codemirror/lang-python": "^6.1.3",
"@codemirror/lang-xml": "^6.0.2",
"@codemirror/legacy-modes": "^6.3.2",
"@codemirror/legacy-modes": "^6.3.3",
"@codemirror/theme-one-dark": "^6.1.2",
"@formatjs/intl-listformat": "^7.4.0",
"@fortawesome/fontawesome-free": "^6.4.0",
"@goauthentik/api": "^2023.6.1-1689715510",
"@goauthentik/api": "^2023.6.1-1690455444",
"@lit-labs/context": "^0.3.3",
"@lit-labs/task": "^2.1.2",
"@lit/localize": "^0.11.4",
"@patternfly/patternfly": "^4.224.2",
"@sentry/browser": "^7.59.3",
"@sentry/tracing": "^7.59.3",
"@sentry/browser": "^7.60.1",
"@sentry/tracing": "^7.60.1",
"@webcomponents/webcomponentsjs": "^2.8.0",
"base64-js": "^1.5.1",
"chart.js": "^4.3.0",
"chart.js": "^4.3.2",
"chartjs-adapter-moment": "^1.0.1",
"codemirror": "^6.0.1",
"construct-style-sheets-polyfill": "^3.1.0",
"core-js": "^3.31.1",
"core-js": "^3.32.0",
"country-flag-icons": "^1.5.7",
"fuse.js": "^6.6.2",
"lit": "^2.7.6",
"mermaid": "^10.2.4",
"mermaid": "^10.3.0",
"rapidoc": "^9.3.4",
"style-mod": "^4.0.3",
"webcomponent-qr-code": "^1.2.0",
@ -75,17 +75,17 @@
"@rollup/plugin-node-resolve": "^15.0.2",
"@rollup/plugin-replace": "^5.0.2",
"@rollup/plugin-typescript": "^11.1.2",
"@storybook/addon-essentials": "^7.1.0",
"@storybook/addon-links": "^7.1.0",
"@storybook/blocks": "^7.1.0",
"@storybook/addon-essentials": "^7.1.1",
"@storybook/addon-links": "^7.1.1",
"@storybook/blocks": "^7.1.1",
"@storybook/web-components": "^7.1.0",
"@storybook/web-components-vite": "^7.1.0",
"@trivago/prettier-plugin-sort-imports": "^4.1.1",
"@storybook/web-components-vite": "^7.1.1",
"@trivago/prettier-plugin-sort-imports": "^4.2.0",
"@types/chart.js": "^2.9.37",
"@types/codemirror": "5.60.8",
"@types/grecaptcha": "^3.0.4",
"@typescript-eslint/eslint-plugin": "^6.1.0",
"@typescript-eslint/parser": "^6.1.0",
"@typescript-eslint/eslint-plugin": "^6.2.0",
"@typescript-eslint/parser": "^6.2.0",
"babel-plugin-macros": "^3.1.0",
"babel-plugin-tsconfig-paths": "^1.0.3",
"eslint": "^8.45.0",
@ -95,8 +95,8 @@
"eslint-plugin-storybook": "^0.6.13",
"lit-analyzer": "^1.2.1",
"npm-run-all": "^4.1.5",
"prettier": "^2.8.8",
"pyright": "^1.1.318",
"prettier": "^3.0.0",
"pyright": "^1.1.319",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"rollup": "^2.79.1",
@ -106,17 +106,17 @@
"rollup-plugin-postcss-lit": "^2.1.0",
"rollup-plugin-terser": "^7.0.2",
"sharp-cli": "^4.1.1",
"storybook": "^7.1.0",
"storybook": "^7.1.1",
"storybook-addon-mock": "^4.1.0",
"ts-lit-plugin": "^1.2.1",
"tslib": "^2.6.0",
"tslib": "^2.6.1",
"turnstile-types": "^1.1.2",
"typescript": "^5.1.6",
"vite-tsconfig-paths": "^4.2.0"
},
"optionalDependencies": {
"@esbuild/darwin-arm64": "^0.18.13",
"@esbuild/darwin-arm64": "^0.18.17",
"@esbuild/linux-amd64": "^0.18.11",
"@esbuild/linux-arm64": "^0.18.14"
"@esbuild/linux-arm64": "^0.18.17"
}
}

View File

@ -92,8 +92,10 @@ export class RecentEventsCard extends Table<Event> {
}
renderEmpty(): TemplateResult {
return super.renderEmpty(html`<ak-empty-state header=${msg("No Events found.")}>
<div slot="body">${msg("No matching events could be found.")}</div>
</ak-empty-state>`);
return super.renderEmpty(
html`<ak-empty-state header=${msg("No Events found.")}>
<div slot="body">${msg("No matching events could be found.")}</div>
</ak-empty-state>`,
);
}
}

View File

@ -1,5 +1,6 @@
import "@goauthentik/admin/applications/ApplicationForm";
import "@goauthentik/admin/applications/wizard/ApplicationWizard";
import { PFSize } from "@goauthentik/app/elements/Spinner";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { uiConfig } from "@goauthentik/common/ui/config";
import MDApplication from "@goauthentik/docs/core/applications.md";
@ -11,13 +12,12 @@ import { getURLParam } from "@goauthentik/elements/router/RouteMatch";
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { TableColumn } from "@goauthentik/elements/table/Table";
import { TablePage } from "@goauthentik/elements/table/TablePage";
import "@goauthentik/user/LibraryApplication/AppIcon";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, css, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";
import PFCard from "@patternfly/patternfly/components/Card/card.css";
import { Application, CoreApi } from "@goauthentik/api";
@ -56,7 +56,6 @@ export class ApplicationListPage extends TablePage<Application> {
static get styles(): CSSResult[] {
return super.styles.concat(
PFAvatar,
PFCard,
css`
/* Fix alignment issues with images in tables */
@ -125,24 +124,9 @@ export class ApplicationListPage extends TablePage<Application> {
</ak-forms-delete-bulk>`;
}
renderIcon(item: Application): TemplateResult {
if (item?.metaIcon) {
if (item.metaIcon.startsWith("fa://")) {
const icon = item.metaIcon.replaceAll("fa://", "");
return html`<i class="fas ${icon}"></i>`;
}
return html`<img
class="app-icon pf-c-avatar"
src="${ifDefined(item.metaIcon)}"
alt="${msg("Application Icon")}"
/>`;
}
return html`<i class="fas fa-share-square"></i>`;
}
row(item: Application): TemplateResult[] {
return [
this.renderIcon(item),
html`<ak-app-icon size=${PFSize.Medium} .app=${item}></ak-app-icon>`,
html`<a href="#/core/applications/${item.slug}">
<div>${item.name}</div>
${item.metaPublisher ? html`<small>${item.metaPublisher}</small>` : html``}

View File

@ -2,6 +2,7 @@ import "@goauthentik/admin/applications/ApplicationAuthorizeChart";
import "@goauthentik/admin/applications/ApplicationCheckAccessForm";
import "@goauthentik/admin/applications/ApplicationForm";
import "@goauthentik/admin/policies/BoundPoliciesList";
import { PFSize } from "@goauthentik/app/elements/Spinner";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/EmptyState";
@ -9,6 +10,7 @@ import "@goauthentik/elements/PageHeader";
import "@goauthentik/elements/Tabs";
import "@goauthentik/elements/buttons/SpinnerButton";
import "@goauthentik/elements/events/ObjectChangelog";
import "@goauthentik/user/LibraryApplication/AppIcon";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, html } from "lit";
@ -80,11 +82,15 @@ export class ApplicationViewPage extends AKElement {
render(): TemplateResult {
return html`<ak-page-header
icon=${this.application?.metaIcon || ""}
header=${this.application?.name || msg("Loading")}
description=${ifDefined(this.application?.metaPublisher)}
.iconImage=${true}
>
<ak-app-icon
size=${PFSize.Small}
slot="icon"
.app=${this.application}
></ak-app-icon>
</ak-page-header>
${this.renderApp()}`;
}

View File

@ -92,6 +92,24 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
];
}
// TODO: Make this more generic, maybe automatically get the plural name
// of the object to use in the renderEmpty
renderEmpty(inner?: TemplateResult): TemplateResult {
return super.renderEmpty(html`
${inner
? inner
: html`<ak-empty-state
icon=${this.pageIcon()}
header="${msg("No licenses found.")}"
>
<div slot="body">
${this.searchEnabled() ? this.renderEmptyClearSearch() : html``}
</div>
<div slot="primary">${this.renderObjectCreate()}</div>
</ak-empty-state>`}
`);
}
renderToolbarSelected(): TemplateResult {
const disabled = this.selectedElements.length < 1;
return html`<ak-forms-delete-bulk
@ -149,9 +167,9 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
<ak-aggregate-card
class="pf-l-grid__item"
icon="pf-icon pf-icon-user"
header=${msg("Forecast default users")}
header=${msg("Forecast internal users")}
subtext=${msg(
str`Estimated user count one year from now based on ${this.forecast?.users} current users and ${this.forecast?.forecastedUsers} forecasted users.`,
str`Estimated user count one year from now based on ${this.forecast?.users} current internal users and ${this.forecast?.forecastedUsers} forecasted internal users.`,
)}
>
~&nbsp;${(this.forecast?.users || 0) +

View File

@ -121,23 +121,22 @@ export class BoundStagesList extends Table<FlowStageBinding> {
}
renderEmpty(): TemplateResult {
return super.renderEmpty(html`<ak-empty-state
header=${msg("No Stages bound")}
icon="pf-icon-module"
>
<div slot="body">${msg("No stages are currently bound to this flow.")}</div>
<div slot="primary">
<ak-forms-modal>
<span slot="submit"> ${msg("Create")} </span>
<span slot="header"> ${msg("Create Stage binding")} </span>
<ak-stage-binding-form slot="form" targetPk=${ifDefined(this.target)}>
</ak-stage-binding-form>
<button slot="trigger" class="pf-c-button pf-m-primary">
${msg("Bind stage")}
</button>
</ak-forms-modal>
</div>
</ak-empty-state>`);
return super.renderEmpty(
html`<ak-empty-state header=${msg("No Stages bound")} icon="pf-icon-module">
<div slot="body">${msg("No stages are currently bound to this flow.")}</div>
<div slot="primary">
<ak-forms-modal>
<span slot="submit"> ${msg("Create")} </span>
<span slot="header"> ${msg("Create Stage binding")} </span>
<ak-stage-binding-form slot="form" targetPk=${ifDefined(this.target)}>
</ak-stage-binding-form>
<button slot="trigger" class="pf-c-button pf-m-primary">
${msg("Bind stage")}
</button>
</ak-forms-modal>
</div>
</ak-empty-state>`,
);
}
renderToolbar(): TemplateResult {

View File

@ -40,16 +40,14 @@ export class FlowViewPage extends AKElement {
flow!: Flow;
static get styles(): CSSResult[] {
return [PFBase, PFPage, PFDescriptionList, PFButton, PFCard, PFContent, PFGrid].concat(
css`
img.pf-icon {
max-height: 24px;
}
ak-tabs {
height: 100%;
}
`,
);
return [PFBase, PFPage, PFDescriptionList, PFButton, PFCard, PFContent, PFGrid].concat(css`
img.pf-icon {
max-height: 24px;
}
ak-tabs {
height: 100%;
}
`);
}
render(): TemplateResult {

View File

@ -170,27 +170,26 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
}
renderEmpty(): TemplateResult {
return super.renderEmpty(html`<ak-empty-state
header=${msg("No Policies bound.")}
icon="pf-icon-module"
>
<div slot="body">${msg("No policies are currently bound to this object.")}</div>
<div slot="primary">
<ak-forms-modal size=${PFSize.Medium}>
<span slot="submit"> ${msg("Create")} </span>
<span slot="header"> ${msg("Create Binding")} </span>
<ak-policy-binding-form
slot="form"
targetPk=${ifDefined(this.target)}
?policyOnly=${this.policyOnly}
>
</ak-policy-binding-form>
<button slot="trigger" class="pf-c-button pf-m-primary">
${msg("Create Binding")}
</button>
</ak-forms-modal>
</div>
</ak-empty-state>`);
return super.renderEmpty(
html`<ak-empty-state header=${msg("No Policies bound.")} icon="pf-icon-module">
<div slot="body">${msg("No policies are currently bound to this object.")}</div>
<div slot="primary">
<ak-forms-modal size=${PFSize.Medium}>
<span slot="submit"> ${msg("Create")} </span>
<span slot="header"> ${msg("Create Binding")} </span>
<ak-policy-binding-form
slot="form"
targetPk=${ifDefined(this.target)}
?policyOnly=${this.policyOnly}
>
</ak-policy-binding-form>
<button slot="trigger" class="pf-c-button pf-m-primary">
${msg("Create Binding")}
</button>
</ak-forms-modal>
</div>
</ak-empty-state>`,
);
}
renderToolbar(): TemplateResult {

View File

@ -191,9 +191,8 @@ export class LDAPProviderViewPage extends AKElement {
class="pf-c-form-control"
readonly
type="text"
value=${`cn=${
this.me?.user.username
},ou=users,${this.provider?.baseDn?.toLowerCase()}`}
value=${`cn=${this.me?.user
.username},ou=users,${this.provider?.baseDn?.toLowerCase()}`}
/>
</div>
<div class="pf-c-form__group">

View File

@ -120,58 +120,62 @@ export class SAMLProviderViewPage extends AKElement {
renderRelatedObjects(): TemplateResult {
const relatedObjects = [];
if (this.provider?.assignedApplicationName) {
relatedObjects.push(html`<div class="pf-c-description-list__group">
<dt class="pf-c-description-list__term">
<span class="pf-c-description-list__text">${msg("Metadata")}</span>
</dt>
<dd class="pf-c-description-list__description">
<div class="pf-c-description-list__text">
<a
class="pf-c-button pf-m-primary"
target="_blank"
href=${ifDefined(this.provider?.urlDownloadMetadata)}
>
${msg("Download")}
</a>
<ak-action-button
class="pf-m-secondary"
.apiRequest=${() => {
if (!navigator.clipboard) {
return Promise.resolve(
showMessage({
level: MessageLevel.info,
message: this.provider?.urlDownloadMetadata || "",
}),
relatedObjects.push(
html`<div class="pf-c-description-list__group">
<dt class="pf-c-description-list__term">
<span class="pf-c-description-list__text">${msg("Metadata")}</span>
</dt>
<dd class="pf-c-description-list__description">
<div class="pf-c-description-list__text">
<a
class="pf-c-button pf-m-primary"
target="_blank"
href=${ifDefined(this.provider?.urlDownloadMetadata)}
>
${msg("Download")}
</a>
<ak-action-button
class="pf-m-secondary"
.apiRequest=${() => {
if (!navigator.clipboard) {
return Promise.resolve(
showMessage({
level: MessageLevel.info,
message: this.provider?.urlDownloadMetadata || "",
}),
);
}
return navigator.clipboard.writeText(
this.provider?.urlDownloadMetadata || "",
);
}
return navigator.clipboard.writeText(
this.provider?.urlDownloadMetadata || "",
);
}}
>
${msg("Copy download URL")}
</ak-action-button>
</div>
</dd>
</div>`);
}}
>
${msg("Copy download URL")}
</ak-action-button>
</div>
</dd>
</div>`,
);
}
if (this.signer) {
relatedObjects.push(html`<div class="pf-c-description-list__group">
<dt class="pf-c-description-list__term">
<span class="pf-c-description-list__text"
>${msg("Download signing certificate")}</span
>
</dt>
<dd class="pf-c-description-list__description">
<div class="pf-c-description-list__text">
<a
class="pf-c-button pf-m-primary"
href=${this.signer.certificateDownloadUrl}
>${msg("Download")}</a
relatedObjects.push(
html`<div class="pf-c-description-list__group">
<dt class="pf-c-description-list__term">
<span class="pf-c-description-list__text"
>${msg("Download signing certificate")}</span
>
</div>
</dd>
</div>`);
</dt>
<dd class="pf-c-description-list__description">
<div class="pf-c-description-list__text">
<a
class="pf-c-button pf-m-primary"
href=${this.signer.certificateDownloadUrl}
>${msg("Download")}</a
>
</div>
</dd>
</div>`,
);
}
return html` <div class="pf-c-card pf-l-grid__item pf-m-12-col">
<div class="pf-c-card__title">${msg("Related objects")}</div>

View File

@ -80,22 +80,27 @@ export class UserForm extends ModelForm<User, number> {
<ak-form-element-horizontal label=${msg("User type")} ?required=${true} name="type">
<ak-radio
.options=${[
// TODO: Add better copy
{
label: "Default",
value: UserTypeEnum.Default,
label: "Internal",
value: UserTypeEnum.Internal,
default: true,
description: html`${msg("Default user")}`,
description: html`${msg(
"Internal users might be users such as company employees, which will get access to the full Enterprise feature set.",
)}`,
},
{
label: "External",
value: UserTypeEnum.External,
description: html`${msg("External user")}`,
description: html`${msg(
"External users might be external consultants or B2C customers. These users don't get access to enterprise features.",
)}`,
},
{
label: "Service account",
value: UserTypeEnum.ServiceAccount,
description: html`${msg("Service account")}`,
description: html`${msg(
"Service accounts should be used for machine-to-machine authentication or other automations.",
)}`,
},
]}
.value=${this.instance?.type}

View File

@ -9,6 +9,9 @@
--ak-dark-background-light: #1c1e21;
--ak-dark-background-light-ish: #212427;
--ak-dark-background-lighter: #2b2e33;
/* PatternFly likes to override global variables for some reason */
--ak-global--Color--100: var(--pf-global--Color--100);
}
::-webkit-scrollbar {

View File

@ -1,11 +1,12 @@
body {
background-color: var(--ak-dark-background) !important;
}
:root {
--pf-global--Color--100: var(--ak-dark-foreground) !important;
--ak-global--Color--100: var(--ak-dark-foreground) !important;
--pf-c-page__main-section--m-light--BackgroundColor: var(--ak-dark-background-darker);
--pf-global--link--Color: var(--ak-dark-foreground-link) !important;
}
body {
background-color: var(--ak-dark-background) !important;
}
.pf-c-radio {
--pf-c-radio__label--Color: var(--ak-dark-foreground);
}

View File

@ -1,7 +1,7 @@
import { AKElement } from "@goauthentik/elements/Base";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, html } from "lit";
import { CSSResult, TemplateResult, css, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import PFExpandableSection from "@patternfly/patternfly/components/ExpandableSection/expandable-section.css";
@ -19,7 +19,15 @@ export class Expand extends AKElement {
textClosed = msg("Show more");
static get styles(): CSSResult[] {
return [PFBase, PFExpandableSection];
return [
PFBase,
PFExpandableSection,
css`
.pf-c-expandable-section.pf-m-display-lg {
background-color: var(--pf-global--BackgroundColor--100);
}
`,
];
}
render(): TemplateResult {

View File

@ -69,9 +69,12 @@ export class PageHeader extends AKElement {
min-height: 114px;
}
.pf-c-button.pf-m-plain {
background-color: var(--pf-c-page__main-section--m-light--BackgroundColor);
background-color: transparent;
border-radius: 0px;
}
.pf-c-page__main-section.pf-m-light {
background-color: transparent;
}
.pf-c-page__main-section {
flex-grow: 1;
display: flex;
@ -88,6 +91,11 @@ export class PageHeader extends AKElement {
.notification-trigger.has-notifications {
color: var(--pf-global--active-color--100);
}
h1 {
display: flex;
flex-direction: row;
align-items: center !important;
}
`,
];
}
@ -117,10 +125,10 @@ export class PageHeader extends AKElement {
renderIcon(): TemplateResult {
if (this.icon) {
if (this.iconImage && !this.icon.startsWith("fa://")) {
return html`<img class="pf-icon" src="${this.icon}" alt="page icon" />&nbsp;`;
return html`<img class="pf-icon" src="${this.icon}" alt="page icon" />`;
}
const icon = this.icon.replaceAll("fa://", "fa ");
return html`<i class=${icon}></i>&nbsp;`;
return html`<i class=${icon}></i>`;
}
return html``;
}
@ -144,8 +152,8 @@ export class PageHeader extends AKElement {
<section class="pf-c-page__main-section pf-m-light">
<div class="pf-c-content">
<h1>
${this.renderIcon()}
<slot name="header"> ${this.header} </slot>
<slot name="icon">${this.renderIcon()}</slot>&nbsp;
<slot name="header">${this.header}</slot>
</h1>
${this.description ? html`<p>${this.description}</p>` : html``}
</div>

View File

@ -27,20 +27,21 @@ const metadata: Meta<AKActionButton> = {
export default metadata;
const container = (testItem: TemplateResult) => html` <div style="background: #fff; padding: 2em">
<style>
li {
display: block;
}
p {
margin-top: 1em;
}
</style>
<ak-message-container></ak-message-container>
${testItem}
<p>Messages received from the button:</p>
<ul id="action-button-message-pad" style="margin-top: 1em"></ul>
</div>`;
const container = (testItem: TemplateResult) =>
html` <div style="background: #fff; padding: 2em">
<style>
li {
display: block;
}
p {
margin-top: 1em;
}
</style>
<ak-message-container></ak-message-container>
${testItem}
<p>Messages received from the button:</p>
<ul id="action-button-message-pad" style="margin-top: 1em"></ul>
</div>`;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const displayMessage = (result: any) => {
@ -81,7 +82,9 @@ export const ButtonWithError = () => {
}, 3000);
});
return container(html` <ak-action-button class="pf-m-secondary" .apiRequest=${run}
>3 Seconds</ak-action-button
>`);
return container(
html` <ak-action-button class="pf-m-secondary" .apiRequest=${run}
>3 Seconds</ak-action-button
>`,
);
};

View File

@ -21,25 +21,26 @@ function makeid(length: number) {
// We want the display to be rich and comprehensive. The next two functions provide
// a styled wrapper for the return messages, and a styled wrapper for each message.
const container = (testItem: TemplateResult) => html` <div style="background: #fff; padding: 2em">
<style>
li {
display: block;
}
p {
display: block;
margin-top: 1em;
}
p + p {
margin-top: 0.2em;
padding-left: 2.5rem;
}
</style>
<ak-message-container></ak-message-container>
${testItem}
<p>Messages received from the button:</p>
<ul id="action-button-message-pad" style="margin-top: 1em"></ul>
</div>`;
const container = (testItem: TemplateResult) =>
html` <div style="background: #fff; padding: 2em">
<style>
li {
display: block;
}
p {
display: block;
margin-top: 1em;
}
p + p {
margin-top: 0.2em;
padding-left: 2.5rem;
}
</style>
<ak-message-container></ak-message-container>
${testItem}
<p>Messages received from the button:</p>
<ul id="action-button-message-pad" style="margin-top: 1em"></ul>
</div>`;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const displayMessage = (result: any) => {

View File

@ -33,7 +33,12 @@ export const FONT_COLOUR_DARK_MODE = "#fafafa";
export const FONT_COLOUR_LIGHT_MODE = "#151515";
export class RGBAColor {
constructor(public r: number, public g: number, public b: number, public a: number = 1) {}
constructor(
public r: number,
public g: number,
public b: number,
public a: number = 1,
) {}
toString(): string {
return `rgba(${this.r}, ${this.g}, ${this.b}, ${this.a})`;
}

View File

@ -97,8 +97,10 @@ export class ObjectChangelog extends Table<Event> {
}
renderEmpty(): TemplateResult {
return super.renderEmpty(html`<ak-empty-state header=${msg("No Events found.")}>
<div slot="body">${msg("No matching events could be found.")}</div>
</ak-empty-state>`);
return super.renderEmpty(
html`<ak-empty-state header=${msg("No Events found.")}>
<div slot="body">${msg("No matching events could be found.")}</div>
</ak-empty-state>`,
);
}
}

View File

@ -71,8 +71,10 @@ export class UserEvents extends Table<Event> {
}
renderEmpty(): TemplateResult {
return super.renderEmpty(html`<ak-empty-state header=${msg("No Events found.")}>
<div slot="body">${msg("No matching events could be found.")}</div>
</ak-empty-state>`);
return super.renderEmpty(
html`<ak-empty-state header=${msg("No Events found.")}>
<div slot="body">${msg("No matching events could be found.")}</div>
</ak-empty-state>`,
);
}
}

View File

@ -22,7 +22,10 @@ import { ResponseError, ValidationError, ValidationErrorFromJSON } from "@goauth
export class PreventFormSubmit {
// Stub class which can be returned by form elements to prevent the form from submitting
constructor(public message: string, public element?: HorizontalFormElement) {}
constructor(
public message: string,
public element?: HorizontalFormElement,
) {}
}
export class APIError extends Error {

View File

@ -28,31 +28,29 @@ export class NotificationDrawer extends AKElement {
unread = 0;
static get styles(): CSSResult[] {
return [PFBase, PFButton, PFNotificationDrawer, PFContent, PFDropdown].concat(
css`
.pf-c-drawer__body {
height: 100%;
}
.pf-c-notification-drawer__body {
flex-grow: 1;
}
.pf-c-notification-drawer__header {
height: 114px;
align-items: center;
}
.pf-c-notification-drawer__header-action,
.pf-c-notification-drawer__header-action-close,
.pf-c-notification-drawer__header-action-close > .pf-c-button.pf-m-plain {
height: 100%;
}
.pf-c-notification-drawer__list-item-description {
white-space: pre-wrap;
}
.pf-c-notification-drawer__footer {
margin: 1rem;
}
`,
);
return [PFBase, PFButton, PFNotificationDrawer, PFContent, PFDropdown].concat(css`
.pf-c-drawer__body {
height: 100%;
}
.pf-c-notification-drawer__body {
flex-grow: 1;
}
.pf-c-notification-drawer__header {
height: 114px;
align-items: center;
}
.pf-c-notification-drawer__header-action,
.pf-c-notification-drawer__header-action-close,
.pf-c-notification-drawer__header-action-close > .pf-c-button.pf-m-plain {
height: 100%;
}
.pf-c-notification-drawer__list-item-description {
white-space: pre-wrap;
}
.pf-c-notification-drawer__footer {
margin: 1rem;
}
`);
}
firstUpdated(): void {

View File

@ -48,6 +48,9 @@ export class TablePagination extends AKElement {
}
render(): TemplateResult {
if (!this.pages) {
return html``;
}
return html` <div class="pf-c-pagination pf-m-compact pf-m-hidden pf-m-visible-on-md">
<div class="pf-c-pagination pf-m-compact pf-m-compact pf-m-hidden pf-m-visible-on-md">
<div class="pf-c-options-menu">

View File

@ -44,24 +44,22 @@ export class IdentificationStage extends BaseStage<
form?: HTMLFormElement;
static get styles(): CSSResult[] {
return [PFBase, PFAlert, PFLogin, PFForm, PFFormControl, PFTitle, PFButton].concat(
css`
/* login page's icons */
.pf-c-login__main-footer-links-item button {
background-color: transparent;
border: 0;
display: flex;
align-items: stretch;
}
.pf-c-login__main-footer-links-item img {
fill: var(--pf-c-login__main-footer-links-item-link-svg--Fill);
width: 100px;
max-width: var(--pf-c-login__main-footer-links-item-link-svg--Width);
height: 100%;
max-height: var(--pf-c-login__main-footer-links-item-link-svg--Height);
}
`,
);
return [PFBase, PFAlert, PFLogin, PFForm, PFFormControl, PFTitle, PFButton].concat(css`
/* login page's icons */
.pf-c-login__main-footer-links-item button {
background-color: transparent;
border: 0;
display: flex;
align-items: stretch;
}
.pf-c-login__main-footer-links-item img {
fill: var(--pf-c-login__main-footer-links-item-link-svg--Fill);
width: 100px;
max-width: var(--pf-c-login__main-footer-links-item-link-svg--Width);
height: 100%;
max-height: var(--pf-c-login__main-footer-links-item-link-svg--Height);
}
`);
}
firstUpdated(): void {

View File

@ -200,12 +200,13 @@ ${prompt.initialValue}</textarea
? LOCALES
: LOCALES.filter((locale) => locale.code !== "debug");
const options = locales.map(
(locale) => html`<option
value=${locale.code}
?selected=${locale.code === prompt.initialValue}
>
${locale.code.toUpperCase()} - ${locale.label()}
</option> `,
(locale) =>
html`<option
value=${locale.code}
?selected=${locale.code === prompt.initialValue}
>
${locale.code.toUpperCase()} - ${locale.label()}
</option> `,
);
return html`<select class="pf-c-form-control" name="${prompt.fieldKey}">

View File

@ -0,0 +1,80 @@
import { AKElement } from "@goauthentik/app/elements/Base";
import { PFSize } from "@goauthentik/app/elements/Spinner";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, css, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import PFFAIcons from "@patternfly/patternfly/base/patternfly-fa-icons.css";
import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";
import { Application } from "@goauthentik/api";
@customElement("ak-app-icon")
export class AppIcon extends AKElement {
@property({ attribute: false })
app?: Application;
@property()
size?: PFSize;
static get styles(): CSSResult[] {
return [
PFFAIcons,
PFAvatar,
css`
:host([size="pf-m-lg"]) {
--icon-height: 4rem;
--icon-border: 0.25rem;
}
:host([size="pf-m-md"]) {
--icon-height: 2rem;
--icon-border: 0.125rem;
}
:host([size="pf-m-sm"]) {
--icon-height: 1rem;
--icon-border: 0.125rem;
}
.pf-c-avatar {
--pf-c-avatar--BorderRadius: 0;
--pf-c-avatar--Height: calc(
var(--icon-height) + var(--icon-border) + var(--icon-border)
);
--pf-c-avatar--Width: calc(
var(--icon-height) + var(--icon-border) + var(--icon-border)
);
}
.icon {
font-size: var(--icon-height);
color: var(--ak-global--Color--100);
padding: var(--icon-border);
max-height: calc(var(--icon-height) + var(--icon-border) + var(--icon-border));
line-height: calc(var(--icon-height) + var(--icon-border) + var(--icon-border));
filter: drop-shadow(5px 5px 5px rgba(128, 128, 128, 0.25));
}
div {
height: calc(var(--icon-height) + var(--icon-border) + var(--icon-border));
}
`,
];
}
render(): TemplateResult {
if (!this.app) {
return html`<div><i class="icon fas fa-question-circle"></i></div>`;
}
if (this.app?.metaIcon) {
if (this.app.metaIcon.startsWith("fa://")) {
const icon = this.app.metaIcon.replaceAll("fa://", "");
return html`<div><i class="icon fas ${icon}"></i></div>`;
}
return html`<img
class="icon pf-c-avatar"
src="${ifDefined(this.app.metaIcon)}"
alt="${msg("Application Icon")}"
/>`;
}
return html`<span class="icon">${this.app?.name.charAt(0).toUpperCase()}</span>`;
}
}

View File

@ -1,5 +1,8 @@
import { PFSize } from "@goauthentik/app/elements/Spinner";
import { truncateWords } from "@goauthentik/common/utils";
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
import "@goauthentik/elements/Expand";
import "@goauthentik/user/LibraryApplication/AppIcon";
import { UserInterface } from "@goauthentik/user/UserInterface";
import { msg } from "@lit/localize";
@ -7,7 +10,6 @@ import { CSSResult, TemplateResult, css, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFCard from "@patternfly/patternfly/components/Card/card.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
@ -30,49 +32,36 @@ export class LibraryApplication extends AKElement {
PFBase,
PFCard,
PFButton,
PFAvatar,
css`
.pf-c-card {
height: 100%;
}
i.pf-icon {
height: 36px;
display: flex;
flex-direction: column;
justify-content: center;
}
.pf-c-avatar {
--pf-c-avatar--BorderRadius: 0;
--pf-c-card--BoxShadow: var(--pf-global--BoxShadow--md);
}
.pf-c-card__header {
min-height: 60px;
justify-content: space-between;
flex-direction: column;
}
.pf-c-card__header a {
display: flex;
flex-direction: column;
justify-content: center;
margin-right: 0.25em;
}
a:hover {
text-decoration: none;
}
.expander {
flex-grow: 1;
}
.pf-c-card__title {
text-align: center;
/* This is not ideal as it hard limits us to 2 lines of text for the title
of the application. In theory that should be fine for most cases, but ideally
we don't do this */
height: 48px;
}
`,
];
}
renderIcon(): TemplateResult {
if (this.application?.metaIcon) {
if (this.application.metaIcon.startsWith("fa://")) {
const icon = this.application.metaIcon.replaceAll("fa://", "");
return html`<i class="fas ${icon}"></i>`;
}
return html`<img
class="app-icon pf-c-avatar"
src="${ifDefined(this.application.metaIcon)}"
alt="${msg("Application Icon")}"
/>`;
}
return html`<i class="fas fa-share-square"></i>`;
}
render(): TemplateResult {
if (!this.application) {
return html`<ak-spinner></ak-spinner>`;
@ -82,41 +71,40 @@ export class LibraryApplication extends AKElement {
class="pf-c-card pf-m-hoverable pf-m-compact ${this.selected
? "pf-m-selectable pf-m-selected"
: ""}"
style="background: ${this.background} !important"
style=${this.background !== "" ? `background: ${this.background} !important` : ""}
>
<div class="pf-c-card__header">
<a
href="${ifDefined(this.application.launchUrl ?? "")}"
target="${ifDefined(this.application.openInNewTab ? "_blank" : undefined)}"
>
${this.renderIcon()}
<ak-app-icon size=${PFSize.Large} .app=${this.application}></ak-app-icon>
</a>
${rootInterface()?.uiConfig?.enabledFeatures.applicationEdit && me?.user.isSuperuser
? html`
<a
class="pf-c-button pf-m-control pf-m-small"
href="/if/admin/#/core/applications/${this.application?.slug}"
>
<i class="fas fa-pencil-alt"></i>
</a>
`
: html``}
</div>
<div class="pf-c-card__title">
<p>
<a
href="${ifDefined(this.application.launchUrl ?? "")}"
target="${ifDefined(this.application.openInNewTab ? "_blank" : undefined)}"
>${this.application.name}</a
>
</p>
<a
href="${ifDefined(this.application.launchUrl ?? "")}"
target="${ifDefined(this.application.openInNewTab ? "_blank" : undefined)}"
>${this.application.name}</a
>
</div>
<div class="expander"></div>
<ak-expand textOpen=${msg("Less details")} textClosed=${msg("More details")}>
<div class="pf-c-content">
<small>${this.application.metaPublisher}</small>
</div>
</div>
<div class="pf-c-card__body">
${truncateWords(this.application.metaDescription || "", 35)}
</div>
${truncateWords(this.application.metaDescription || "", 10)}
${rootInterface()?.uiConfig?.enabledFeatures.applicationEdit && me?.user.isSuperuser
? html`
<a
class="pf-c-button pf-m-control pf-m-small pf-m-block"
href="/if/admin/#/core/applications/${this.application?.slug}"
>
<i class="fas fa-pencil-alt"></i>&nbsp;${msg("Edit")}
</a>
`
: html``}
</ak-expand>
</div>`;
}
}

View File

@ -73,12 +73,22 @@ export class UserInterface extends Interface {
z-index: auto !important;
background-color: transparent !important;
}
.pf-c-page__header {
background-color: transparent !important;
box-shadow: none !important;
color: black !important;
}
:host([theme="dark"]) .pf-c-page__header {
color: var(--ak-dark-foreground) !important;
}
.pf-c-page__header-tools-item .fas,
.pf-c-notification-badge__count,
.pf-c-page__header-tools-group .pf-c-button {
color: var(--ak-global--Color--100) !important;
}
.pf-c-page {
background-color: transparent;
}
.background-wrapper {
background-color: var(--pf-c-page--BackgroundColor) !important;
}
.display-none {
display: none;
}
@ -92,10 +102,19 @@ export class UserInterface extends Interface {
.background-wrapper {
height: 100vh;
width: 100%;
position: absolute;
position: fixed;
z-index: -1;
top: 0;
left: 0;
background-color: var(--pf-c-page--BackgroundColor) !important;
}
.background-default-slant {
background-color: white; /*var(--ak-accent);*/
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 calc(100% - 5vw));
height: 50vh;
}
:host([theme="dark"]) .background-default-slant {
background-color: black;
}
ak-locale-context {
display: flex;
@ -158,7 +177,11 @@ export class UserInterface extends Interface {
return html` <ak-locale-context>
<ak-enterprise-status interface="user"></ak-enterprise-status>
<div class="pf-c-page">
<div class="background-wrapper" style="${this.uiConfig.theme.background}"></div>
<div class="background-wrapper" style="${this.uiConfig.theme.background}">
${this.uiConfig.theme.background === ""
? html`<div class="background-default-slant"></div>`
: html``}
</div>
<header class="pf-c-page__header">
<div class="pf-c-page__header-brand">
<a href="#/" class="pf-c-page__header-brand-link">
@ -215,7 +238,7 @@ export class UserInterface extends Interface {
? "pf-m-unread"
: ""}"
>
<i class="pf-icon-bell" aria-hidden="true"></i>
<i class="fas fa-bell" aria-hidden="true"></i>
<span class="pf-c-notification-badge__count"
>${this.notificationsCount}</span
>
@ -244,7 +267,7 @@ export class UserInterface extends Interface {
</div>
${this.me.user.isSuperuser
? html`<a
class="pf-c-button pf-m-primary pf-m-small pf-u-display-none pf-u-display-block-on-md"
class="pf-c-button pf-m-secondary pf-m-small pf-u-display-none pf-u-display-block-on-md"
href="/if/admin"
>
${msg("Admin interface")}

View File

@ -5751,15 +5751,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s9f9492d30a96b9c6">
<source>User type</source>
</trans-unit>
<trans-unit id="s0b9a40b7b2853c7d">
<source>Default user</source>
</trans-unit>
<trans-unit id="s35b9fa270f45b391">
<source>External user</source>
</trans-unit>
<trans-unit id="s1a635369edaf4dc3">
<source>Service account</source>
</trans-unit>
<trans-unit id="s0e427111d750cc02">
<source>Successfully updated license.</source>
</trans-unit>
@ -5775,45 +5766,18 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s2e109263b73c12d5">
<source>Licenses</source>
</trans-unit>
<trans-unit id="sf8f9f3032e891e16">
<source>TODO Copy</source>
</trans-unit>
<trans-unit id="sd49099e9522635f4">
<source>License(s)</source>
</trans-unit>
<trans-unit id="s3be1d90ffa46b7f1">
<source>Enterprise is in preview.</source>
</trans-unit>
<trans-unit id="s34dca481f039c226">
<source>How to get a license</source>
</trans-unit>
<trans-unit id="s948364901c166232">
<source>Copy the installation ID</source>
</trans-unit>
<trans-unit id="s75c167446b237e0f">
<source>Then open the customer portal</source>
</trans-unit>
<trans-unit id="s9748dd3bd53d27a4">
<source>Forecasted default users</source>
</trans-unit>
<trans-unit id="s6b18f594d94c2374">
<source>Estimated user count one year from now</source>
</trans-unit>
<trans-unit id="s69f246d164be88d0">
<source>Forecasted external users</source>
</trans-unit>
<trans-unit id="s878fc2eaf94642db">
<source>Estimated external user count one year from now</source>
</trans-unit>
<trans-unit id="sd22bd01bdf28c548">
<source>Cumulative license expiry</source>
</trans-unit>
<trans-unit id="sdeb6cee42435dd07">
<source>Update License</source>
</trans-unit>
<trans-unit id="s99afa741c259d70e">
<source>Create License</source>
</trans-unit>
<trans-unit id="s7df5b92a3f93544f">
<source>Warning: The current user count has exceeded the configured licenses.</source>
</trans-unit>
@ -5825,6 +5789,51 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s9ce7cc01fb9b5b53">
<source>Manage enterprise licenses</source>
</trans-unit>
<trans-unit id="sf9ebf11ac2645820">
<source>No licenses found.</source>
</trans-unit>
<trans-unit id="sa1db89262360550b">
<source>Send us feedback!</source>
</trans-unit>
<trans-unit id="s4015746f55a8d89f">
<source>Get a license</source>
</trans-unit>
<trans-unit id="sb2cbd06f8e25b47e">
<source>Go to Customer Portal</source>
</trans-unit>
<trans-unit id="sf58825457d61c429">
<source>Forecast internal users</source>
</trans-unit>
<trans-unit id="sde9a3f41977ec1f8">
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.users}"/> current internal users and <x id="1" equiv-text="${this.forecast?.forecastedUsers}"/> forecasted internal users.</source>
</trans-unit>
<trans-unit id="s4557b6b9da258643">
<source>Forecast external users</source>
</trans-unit>
<trans-unit id="sf52479d6daa0a4a8">
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.externalUsers}"/> current external users and <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> forecasted external users.</source>
</trans-unit>
<trans-unit id="s6196153c4b0c1ea0">
<source>Install</source>
</trans-unit>
<trans-unit id="s0285b4bd69130fa3">
<source>Install License</source>
</trans-unit>
<trans-unit id="scef2eb6a2bfe3110">
<source>Internal users might be users such as company employees, which will get access to the full Enterprise feature set.</source>
</trans-unit>
<trans-unit id="sf66389b04fcc219c">
<source>External users might be external consultants or B2C customers. These users don't get access to enterprise features.</source>
</trans-unit>
<trans-unit id="s77e8668a27dbc402">
<source>Service accounts should be used for machine-to-machine authentication or other automations.</source>
</trans-unit>
<trans-unit id="s28cbd874ba450b4e">
<source>Less details</source>
</trans-unit>
<trans-unit id="s8fa26f65aed77c96">
<source>More details</source>
</trans-unit>
</body>
</file>

View File

@ -6067,15 +6067,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s9f9492d30a96b9c6">
<source>User type</source>
</trans-unit>
<trans-unit id="s0b9a40b7b2853c7d">
<source>Default user</source>
</trans-unit>
<trans-unit id="s35b9fa270f45b391">
<source>External user</source>
</trans-unit>
<trans-unit id="s1a635369edaf4dc3">
<source>Service account</source>
</trans-unit>
<trans-unit id="s0e427111d750cc02">
<source>Successfully updated license.</source>
</trans-unit>
@ -6091,45 +6082,18 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s2e109263b73c12d5">
<source>Licenses</source>
</trans-unit>
<trans-unit id="sf8f9f3032e891e16">
<source>TODO Copy</source>
</trans-unit>
<trans-unit id="sd49099e9522635f4">
<source>License(s)</source>
</trans-unit>
<trans-unit id="s3be1d90ffa46b7f1">
<source>Enterprise is in preview.</source>
</trans-unit>
<trans-unit id="s34dca481f039c226">
<source>How to get a license</source>
</trans-unit>
<trans-unit id="s948364901c166232">
<source>Copy the installation ID</source>
</trans-unit>
<trans-unit id="s75c167446b237e0f">
<source>Then open the customer portal</source>
</trans-unit>
<trans-unit id="s9748dd3bd53d27a4">
<source>Forecasted default users</source>
</trans-unit>
<trans-unit id="s6b18f594d94c2374">
<source>Estimated user count one year from now</source>
</trans-unit>
<trans-unit id="s69f246d164be88d0">
<source>Forecasted external users</source>
</trans-unit>
<trans-unit id="s878fc2eaf94642db">
<source>Estimated external user count one year from now</source>
</trans-unit>
<trans-unit id="sd22bd01bdf28c548">
<source>Cumulative license expiry</source>
</trans-unit>
<trans-unit id="sdeb6cee42435dd07">
<source>Update License</source>
</trans-unit>
<trans-unit id="s99afa741c259d70e">
<source>Create License</source>
</trans-unit>
<trans-unit id="s7df5b92a3f93544f">
<source>Warning: The current user count has exceeded the configured licenses.</source>
</trans-unit>
@ -6141,6 +6105,51 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s9ce7cc01fb9b5b53">
<source>Manage enterprise licenses</source>
</trans-unit>
<trans-unit id="sf9ebf11ac2645820">
<source>No licenses found.</source>
</trans-unit>
<trans-unit id="sa1db89262360550b">
<source>Send us feedback!</source>
</trans-unit>
<trans-unit id="s4015746f55a8d89f">
<source>Get a license</source>
</trans-unit>
<trans-unit id="sb2cbd06f8e25b47e">
<source>Go to Customer Portal</source>
</trans-unit>
<trans-unit id="sf58825457d61c429">
<source>Forecast internal users</source>
</trans-unit>
<trans-unit id="sde9a3f41977ec1f8">
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.users}"/> current internal users and <x id="1" equiv-text="${this.forecast?.forecastedUsers}"/> forecasted internal users.</source>
</trans-unit>
<trans-unit id="s4557b6b9da258643">
<source>Forecast external users</source>
</trans-unit>
<trans-unit id="sf52479d6daa0a4a8">
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.externalUsers}"/> current external users and <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> forecasted external users.</source>
</trans-unit>
<trans-unit id="s6196153c4b0c1ea0">
<source>Install</source>
</trans-unit>
<trans-unit id="s0285b4bd69130fa3">
<source>Install License</source>
</trans-unit>
<trans-unit id="scef2eb6a2bfe3110">
<source>Internal users might be users such as company employees, which will get access to the full Enterprise feature set.</source>
</trans-unit>
<trans-unit id="sf66389b04fcc219c">
<source>External users might be external consultants or B2C customers. These users don't get access to enterprise features.</source>
</trans-unit>
<trans-unit id="s77e8668a27dbc402">
<source>Service accounts should be used for machine-to-machine authentication or other automations.</source>
</trans-unit>
<trans-unit id="s28cbd874ba450b4e">
<source>Less details</source>
</trans-unit>
<trans-unit id="s8fa26f65aed77c96">
<source>More details</source>
</trans-unit>
</body>
</file>

View File

@ -5659,15 +5659,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s9f9492d30a96b9c6">
<source>User type</source>
</trans-unit>
<trans-unit id="s0b9a40b7b2853c7d">
<source>Default user</source>
</trans-unit>
<trans-unit id="s35b9fa270f45b391">
<source>External user</source>
</trans-unit>
<trans-unit id="s1a635369edaf4dc3">
<source>Service account</source>
</trans-unit>
<trans-unit id="s0e427111d750cc02">
<source>Successfully updated license.</source>
</trans-unit>
@ -5683,45 +5674,18 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s2e109263b73c12d5">
<source>Licenses</source>
</trans-unit>
<trans-unit id="sf8f9f3032e891e16">
<source>TODO Copy</source>
</trans-unit>
<trans-unit id="sd49099e9522635f4">
<source>License(s)</source>
</trans-unit>
<trans-unit id="s3be1d90ffa46b7f1">
<source>Enterprise is in preview.</source>
</trans-unit>
<trans-unit id="s34dca481f039c226">
<source>How to get a license</source>
</trans-unit>
<trans-unit id="s948364901c166232">
<source>Copy the installation ID</source>
</trans-unit>
<trans-unit id="s75c167446b237e0f">
<source>Then open the customer portal</source>
</trans-unit>
<trans-unit id="s9748dd3bd53d27a4">
<source>Forecasted default users</source>
</trans-unit>
<trans-unit id="s6b18f594d94c2374">
<source>Estimated user count one year from now</source>
</trans-unit>
<trans-unit id="s69f246d164be88d0">
<source>Forecasted external users</source>
</trans-unit>
<trans-unit id="s878fc2eaf94642db">
<source>Estimated external user count one year from now</source>
</trans-unit>
<trans-unit id="sd22bd01bdf28c548">
<source>Cumulative license expiry</source>
</trans-unit>
<trans-unit id="sdeb6cee42435dd07">
<source>Update License</source>
</trans-unit>
<trans-unit id="s99afa741c259d70e">
<source>Create License</source>
</trans-unit>
<trans-unit id="s7df5b92a3f93544f">
<source>Warning: The current user count has exceeded the configured licenses.</source>
</trans-unit>
@ -5733,6 +5697,51 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s9ce7cc01fb9b5b53">
<source>Manage enterprise licenses</source>
</trans-unit>
<trans-unit id="sf9ebf11ac2645820">
<source>No licenses found.</source>
</trans-unit>
<trans-unit id="sa1db89262360550b">
<source>Send us feedback!</source>
</trans-unit>
<trans-unit id="s4015746f55a8d89f">
<source>Get a license</source>
</trans-unit>
<trans-unit id="sb2cbd06f8e25b47e">
<source>Go to Customer Portal</source>
</trans-unit>
<trans-unit id="sf58825457d61c429">
<source>Forecast internal users</source>
</trans-unit>
<trans-unit id="sde9a3f41977ec1f8">
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.users}"/> current internal users and <x id="1" equiv-text="${this.forecast?.forecastedUsers}"/> forecasted internal users.</source>
</trans-unit>
<trans-unit id="s4557b6b9da258643">
<source>Forecast external users</source>
</trans-unit>
<trans-unit id="sf52479d6daa0a4a8">
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.externalUsers}"/> current external users and <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> forecasted external users.</source>
</trans-unit>
<trans-unit id="s6196153c4b0c1ea0">
<source>Install</source>
</trans-unit>
<trans-unit id="s0285b4bd69130fa3">
<source>Install License</source>
</trans-unit>
<trans-unit id="scef2eb6a2bfe3110">
<source>Internal users might be users such as company employees, which will get access to the full Enterprise feature set.</source>
</trans-unit>
<trans-unit id="sf66389b04fcc219c">
<source>External users might be external consultants or B2C customers. These users don't get access to enterprise features.</source>
</trans-unit>
<trans-unit id="s77e8668a27dbc402">
<source>Service accounts should be used for machine-to-machine authentication or other automations.</source>
</trans-unit>
<trans-unit id="s28cbd874ba450b4e">
<source>Less details</source>
</trans-unit>
<trans-unit id="s8fa26f65aed77c96">
<source>More details</source>
</trans-unit>
</body>
</file>

View File

@ -5766,15 +5766,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s9f9492d30a96b9c6">
<source>User type</source>
</trans-unit>
<trans-unit id="s0b9a40b7b2853c7d">
<source>Default user</source>
</trans-unit>
<trans-unit id="s35b9fa270f45b391">
<source>External user</source>
</trans-unit>
<trans-unit id="s1a635369edaf4dc3">
<source>Service account</source>
</trans-unit>
<trans-unit id="s0e427111d750cc02">
<source>Successfully updated license.</source>
</trans-unit>
@ -5790,45 +5781,18 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s2e109263b73c12d5">
<source>Licenses</source>
</trans-unit>
<trans-unit id="sf8f9f3032e891e16">
<source>TODO Copy</source>
</trans-unit>
<trans-unit id="sd49099e9522635f4">
<source>License(s)</source>
</trans-unit>
<trans-unit id="s3be1d90ffa46b7f1">
<source>Enterprise is in preview.</source>
</trans-unit>
<trans-unit id="s34dca481f039c226">
<source>How to get a license</source>
</trans-unit>
<trans-unit id="s948364901c166232">
<source>Copy the installation ID</source>
</trans-unit>
<trans-unit id="s75c167446b237e0f">
<source>Then open the customer portal</source>
</trans-unit>
<trans-unit id="s9748dd3bd53d27a4">
<source>Forecasted default users</source>
</trans-unit>
<trans-unit id="s6b18f594d94c2374">
<source>Estimated user count one year from now</source>
</trans-unit>
<trans-unit id="s69f246d164be88d0">
<source>Forecasted external users</source>
</trans-unit>
<trans-unit id="s878fc2eaf94642db">
<source>Estimated external user count one year from now</source>
</trans-unit>
<trans-unit id="sd22bd01bdf28c548">
<source>Cumulative license expiry</source>
</trans-unit>
<trans-unit id="sdeb6cee42435dd07">
<source>Update License</source>
</trans-unit>
<trans-unit id="s99afa741c259d70e">
<source>Create License</source>
</trans-unit>
<trans-unit id="s7df5b92a3f93544f">
<source>Warning: The current user count has exceeded the configured licenses.</source>
</trans-unit>
@ -5840,6 +5804,51 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s9ce7cc01fb9b5b53">
<source>Manage enterprise licenses</source>
</trans-unit>
<trans-unit id="sf9ebf11ac2645820">
<source>No licenses found.</source>
</trans-unit>
<trans-unit id="sa1db89262360550b">
<source>Send us feedback!</source>
</trans-unit>
<trans-unit id="s4015746f55a8d89f">
<source>Get a license</source>
</trans-unit>
<trans-unit id="sb2cbd06f8e25b47e">
<source>Go to Customer Portal</source>
</trans-unit>
<trans-unit id="sf58825457d61c429">
<source>Forecast internal users</source>
</trans-unit>
<trans-unit id="sde9a3f41977ec1f8">
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.users}"/> current internal users and <x id="1" equiv-text="${this.forecast?.forecastedUsers}"/> forecasted internal users.</source>
</trans-unit>
<trans-unit id="s4557b6b9da258643">
<source>Forecast external users</source>
</trans-unit>
<trans-unit id="sf52479d6daa0a4a8">
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.externalUsers}"/> current external users and <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> forecasted external users.</source>
</trans-unit>
<trans-unit id="s6196153c4b0c1ea0">
<source>Install</source>
</trans-unit>
<trans-unit id="s0285b4bd69130fa3">
<source>Install License</source>
</trans-unit>
<trans-unit id="scef2eb6a2bfe3110">
<source>Internal users might be users such as company employees, which will get access to the full Enterprise feature set.</source>
</trans-unit>
<trans-unit id="sf66389b04fcc219c">
<source>External users might be external consultants or B2C customers. These users don't get access to enterprise features.</source>
</trans-unit>
<trans-unit id="s77e8668a27dbc402">
<source>Service accounts should be used for machine-to-machine authentication or other automations.</source>
</trans-unit>
<trans-unit id="s28cbd874ba450b4e">
<source>Less details</source>
</trans-unit>
<trans-unit id="s8fa26f65aed77c96">
<source>More details</source>
</trans-unit>
</body>
</file>

View File

@ -5898,15 +5898,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s9f9492d30a96b9c6">
<source>User type</source>
</trans-unit>
<trans-unit id="s0b9a40b7b2853c7d">
<source>Default user</source>
</trans-unit>
<trans-unit id="s35b9fa270f45b391">
<source>External user</source>
</trans-unit>
<trans-unit id="s1a635369edaf4dc3">
<source>Service account</source>
</trans-unit>
<trans-unit id="s0e427111d750cc02">
<source>Successfully updated license.</source>
</trans-unit>
@ -5922,45 +5913,18 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s2e109263b73c12d5">
<source>Licenses</source>
</trans-unit>
<trans-unit id="sf8f9f3032e891e16">
<source>TODO Copy</source>
</trans-unit>
<trans-unit id="sd49099e9522635f4">
<source>License(s)</source>
</trans-unit>
<trans-unit id="s3be1d90ffa46b7f1">
<source>Enterprise is in preview.</source>
</trans-unit>
<trans-unit id="s34dca481f039c226">
<source>How to get a license</source>
</trans-unit>
<trans-unit id="s948364901c166232">
<source>Copy the installation ID</source>
</trans-unit>
<trans-unit id="s75c167446b237e0f">
<source>Then open the customer portal</source>
</trans-unit>
<trans-unit id="s9748dd3bd53d27a4">
<source>Forecasted default users</source>
</trans-unit>
<trans-unit id="s6b18f594d94c2374">
<source>Estimated user count one year from now</source>
</trans-unit>
<trans-unit id="s69f246d164be88d0">
<source>Forecasted external users</source>
</trans-unit>
<trans-unit id="s878fc2eaf94642db">
<source>Estimated external user count one year from now</source>
</trans-unit>
<trans-unit id="sd22bd01bdf28c548">
<source>Cumulative license expiry</source>
</trans-unit>
<trans-unit id="sdeb6cee42435dd07">
<source>Update License</source>
</trans-unit>
<trans-unit id="s99afa741c259d70e">
<source>Create License</source>
</trans-unit>
<trans-unit id="s7df5b92a3f93544f">
<source>Warning: The current user count has exceeded the configured licenses.</source>
</trans-unit>
@ -5972,6 +5936,51 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s9ce7cc01fb9b5b53">
<source>Manage enterprise licenses</source>
</trans-unit>
<trans-unit id="sf9ebf11ac2645820">
<source>No licenses found.</source>
</trans-unit>
<trans-unit id="sa1db89262360550b">
<source>Send us feedback!</source>
</trans-unit>
<trans-unit id="s4015746f55a8d89f">
<source>Get a license</source>
</trans-unit>
<trans-unit id="sb2cbd06f8e25b47e">
<source>Go to Customer Portal</source>
</trans-unit>
<trans-unit id="sf58825457d61c429">
<source>Forecast internal users</source>
</trans-unit>
<trans-unit id="sde9a3f41977ec1f8">
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.users}"/> current internal users and <x id="1" equiv-text="${this.forecast?.forecastedUsers}"/> forecasted internal users.</source>
</trans-unit>
<trans-unit id="s4557b6b9da258643">
<source>Forecast external users</source>
</trans-unit>
<trans-unit id="sf52479d6daa0a4a8">
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.externalUsers}"/> current external users and <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> forecasted external users.</source>
</trans-unit>
<trans-unit id="s6196153c4b0c1ea0">
<source>Install</source>
</trans-unit>
<trans-unit id="s0285b4bd69130fa3">
<source>Install License</source>
</trans-unit>
<trans-unit id="scef2eb6a2bfe3110">
<source>Internal users might be users such as company employees, which will get access to the full Enterprise feature set.</source>
</trans-unit>
<trans-unit id="sf66389b04fcc219c">
<source>External users might be external consultants or B2C customers. These users don't get access to enterprise features.</source>
</trans-unit>
<trans-unit id="s77e8668a27dbc402">
<source>Service accounts should be used for machine-to-machine authentication or other automations.</source>
</trans-unit>
<trans-unit id="s28cbd874ba450b4e">
<source>Less details</source>
</trans-unit>
<trans-unit id="s8fa26f65aed77c96">
<source>More details</source>
</trans-unit>
</body>
</file>

View File

@ -6002,15 +6002,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s9f9492d30a96b9c6">
<source>User type</source>
</trans-unit>
<trans-unit id="s0b9a40b7b2853c7d">
<source>Default user</source>
</trans-unit>
<trans-unit id="s35b9fa270f45b391">
<source>External user</source>
</trans-unit>
<trans-unit id="s1a635369edaf4dc3">
<source>Service account</source>
</trans-unit>
<trans-unit id="s0e427111d750cc02">
<source>Successfully updated license.</source>
</trans-unit>
@ -6026,45 +6017,18 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s2e109263b73c12d5">
<source>Licenses</source>
</trans-unit>
<trans-unit id="sf8f9f3032e891e16">
<source>TODO Copy</source>
</trans-unit>
<trans-unit id="sd49099e9522635f4">
<source>License(s)</source>
</trans-unit>
<trans-unit id="s3be1d90ffa46b7f1">
<source>Enterprise is in preview.</source>
</trans-unit>
<trans-unit id="s34dca481f039c226">
<source>How to get a license</source>
</trans-unit>
<trans-unit id="s948364901c166232">
<source>Copy the installation ID</source>
</trans-unit>
<trans-unit id="s75c167446b237e0f">
<source>Then open the customer portal</source>
</trans-unit>
<trans-unit id="s9748dd3bd53d27a4">
<source>Forecasted default users</source>
</trans-unit>
<trans-unit id="s6b18f594d94c2374">
<source>Estimated user count one year from now</source>
</trans-unit>
<trans-unit id="s69f246d164be88d0">
<source>Forecasted external users</source>
</trans-unit>
<trans-unit id="s878fc2eaf94642db">
<source>Estimated external user count one year from now</source>
</trans-unit>
<trans-unit id="sd22bd01bdf28c548">
<source>Cumulative license expiry</source>
</trans-unit>
<trans-unit id="sdeb6cee42435dd07">
<source>Update License</source>
</trans-unit>
<trans-unit id="s99afa741c259d70e">
<source>Create License</source>
</trans-unit>
<trans-unit id="s7df5b92a3f93544f">
<source>Warning: The current user count has exceeded the configured licenses.</source>
</trans-unit>
@ -6076,6 +6040,51 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s9ce7cc01fb9b5b53">
<source>Manage enterprise licenses</source>
</trans-unit>
<trans-unit id="sf9ebf11ac2645820">
<source>No licenses found.</source>
</trans-unit>
<trans-unit id="sa1db89262360550b">
<source>Send us feedback!</source>
</trans-unit>
<trans-unit id="s4015746f55a8d89f">
<source>Get a license</source>
</trans-unit>
<trans-unit id="sb2cbd06f8e25b47e">
<source>Go to Customer Portal</source>
</trans-unit>
<trans-unit id="sf58825457d61c429">
<source>Forecast internal users</source>
</trans-unit>
<trans-unit id="sde9a3f41977ec1f8">
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.users}"/> current internal users and <x id="1" equiv-text="${this.forecast?.forecastedUsers}"/> forecasted internal users.</source>
</trans-unit>
<trans-unit id="s4557b6b9da258643">
<source>Forecast external users</source>
</trans-unit>
<trans-unit id="sf52479d6daa0a4a8">
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.externalUsers}"/> current external users and <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> forecasted external users.</source>
</trans-unit>
<trans-unit id="s6196153c4b0c1ea0">
<source>Install</source>
</trans-unit>
<trans-unit id="s0285b4bd69130fa3">
<source>Install License</source>
</trans-unit>
<trans-unit id="scef2eb6a2bfe3110">
<source>Internal users might be users such as company employees, which will get access to the full Enterprise feature set.</source>
</trans-unit>
<trans-unit id="sf66389b04fcc219c">
<source>External users might be external consultants or B2C customers. These users don't get access to enterprise features.</source>
</trans-unit>
<trans-unit id="s77e8668a27dbc402">
<source>Service accounts should be used for machine-to-machine authentication or other automations.</source>
</trans-unit>
<trans-unit id="s28cbd874ba450b4e">
<source>Less details</source>
</trans-unit>
<trans-unit id="s8fa26f65aed77c96">
<source>More details</source>
</trans-unit>
</body>
</file>

View File

@ -5649,15 +5649,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s9f9492d30a96b9c6">
<source>User type</source>
</trans-unit>
<trans-unit id="s0b9a40b7b2853c7d">
<source>Default user</source>
</trans-unit>
<trans-unit id="s35b9fa270f45b391">
<source>External user</source>
</trans-unit>
<trans-unit id="s1a635369edaf4dc3">
<source>Service account</source>
</trans-unit>
<trans-unit id="s0e427111d750cc02">
<source>Successfully updated license.</source>
</trans-unit>
@ -5673,45 +5664,18 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s2e109263b73c12d5">
<source>Licenses</source>
</trans-unit>
<trans-unit id="sf8f9f3032e891e16">
<source>TODO Copy</source>
</trans-unit>
<trans-unit id="sd49099e9522635f4">
<source>License(s)</source>
</trans-unit>
<trans-unit id="s3be1d90ffa46b7f1">
<source>Enterprise is in preview.</source>
</trans-unit>
<trans-unit id="s34dca481f039c226">
<source>How to get a license</source>
</trans-unit>
<trans-unit id="s948364901c166232">
<source>Copy the installation ID</source>
</trans-unit>
<trans-unit id="s75c167446b237e0f">
<source>Then open the customer portal</source>
</trans-unit>
<trans-unit id="s9748dd3bd53d27a4">
<source>Forecasted default users</source>
</trans-unit>
<trans-unit id="s6b18f594d94c2374">
<source>Estimated user count one year from now</source>
</trans-unit>
<trans-unit id="s69f246d164be88d0">
<source>Forecasted external users</source>
</trans-unit>
<trans-unit id="s878fc2eaf94642db">
<source>Estimated external user count one year from now</source>
</trans-unit>
<trans-unit id="sd22bd01bdf28c548">
<source>Cumulative license expiry</source>
</trans-unit>
<trans-unit id="sdeb6cee42435dd07">
<source>Update License</source>
</trans-unit>
<trans-unit id="s99afa741c259d70e">
<source>Create License</source>
</trans-unit>
<trans-unit id="s7df5b92a3f93544f">
<source>Warning: The current user count has exceeded the configured licenses.</source>
</trans-unit>
@ -5723,6 +5687,51 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s9ce7cc01fb9b5b53">
<source>Manage enterprise licenses</source>
</trans-unit>
<trans-unit id="sf9ebf11ac2645820">
<source>No licenses found.</source>
</trans-unit>
<trans-unit id="sa1db89262360550b">
<source>Send us feedback!</source>
</trans-unit>
<trans-unit id="s4015746f55a8d89f">
<source>Get a license</source>
</trans-unit>
<trans-unit id="sb2cbd06f8e25b47e">
<source>Go to Customer Portal</source>
</trans-unit>
<trans-unit id="sf58825457d61c429">
<source>Forecast internal users</source>
</trans-unit>
<trans-unit id="sde9a3f41977ec1f8">
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.users}"/> current internal users and <x id="1" equiv-text="${this.forecast?.forecastedUsers}"/> forecasted internal users.</source>
</trans-unit>
<trans-unit id="s4557b6b9da258643">
<source>Forecast external users</source>
</trans-unit>
<trans-unit id="sf52479d6daa0a4a8">
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.externalUsers}"/> current external users and <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> forecasted external users.</source>
</trans-unit>
<trans-unit id="s6196153c4b0c1ea0">
<source>Install</source>
</trans-unit>
<trans-unit id="s0285b4bd69130fa3">
<source>Install License</source>
</trans-unit>
<trans-unit id="scef2eb6a2bfe3110">
<source>Internal users might be users such as company employees, which will get access to the full Enterprise feature set.</source>
</trans-unit>
<trans-unit id="sf66389b04fcc219c">
<source>External users might be external consultants or B2C customers. These users don't get access to enterprise features.</source>
</trans-unit>
<trans-unit id="s77e8668a27dbc402">
<source>Service accounts should be used for machine-to-machine authentication or other automations.</source>
</trans-unit>
<trans-unit id="s28cbd874ba450b4e">
<source>Less details</source>
</trans-unit>
<trans-unit id="s8fa26f65aed77c96">
<source>More details</source>
</trans-unit>
</body>
</file>

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="zh-Hans" source-language="en" original="lit-localize-inputs" datatype="plaintext">
<body>
<trans-unit id="s4caed5b7a7e5d89b">
@ -618,9 +618,9 @@
</trans-unit>
<trans-unit id="saa0e2675da69651b">
<source>The URL &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">
@ -1072,8 +1072,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">
@ -1819,8 +1819,8 @@
</trans-unit>
<trans-unit id="sa90b7809586c35ce">
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon &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">
@ -3248,8 +3248,8 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s76768bebabb7d543">
<source>Field which contains members of a group. Note that if using the &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>
<target>包含组成员的字段。请注意,如果使用 &quot;memberUid&quot; 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...'</target>
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source>
<target>包含组成员的字段。请注意,如果使用 "memberUid" 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...'</target>
</trans-unit>
<trans-unit id="s026555347e589f0e">
@ -4046,8 +4046,8 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s7b1fba26d245cb1c">
<source>When using an external logging solution for archiving, this can be set to &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">
@ -4056,8 +4056,8 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s3bb51cabb02b997e">
<source>Format: &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">
@ -4253,10 +4253,10 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="sa95a538bfbb86111">
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> &quot;<x id="1" equiv-text="${this.obj?.name}"/>&quot;?</source>
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
<target>您确定要更新
<x id="0" equiv-text="${this.objectLabel}"/>&quot;
<x id="1" equiv-text="${this.obj?.name}"/>&quot; 吗?</target>
<x id="0" equiv-text="${this.objectLabel}"/>"
<x id="1" equiv-text="${this.obj?.name}"/>" 吗?</target>
</trans-unit>
<trans-unit id="sc92d7cfb6ee1fec6">
@ -5372,7 +5372,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="sdf1d8edef27236f0">
<source>A &quot;roaming&quot; authenticator, like a YubiKey</source>
<source>A "roaming" authenticator, like a YubiKey</source>
<target>像 YubiKey 这样的“漫游”身份验证器</target>
</trans-unit>
@ -5712,10 +5712,10 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${prompt.name}"/> (&quot;<x id="1" equiv-text="${prompt.fieldKey}"/>&quot;, of type <x id="2" equiv-text="${prompt.type}"/>)</source>
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
<target>
<x id="0" equiv-text="${prompt.name}"/>&quot;
<x id="1" equiv-text="${prompt.fieldKey}"/>&quot;,类型为
<x id="0" equiv-text="${prompt.name}"/>"
<x id="1" equiv-text="${prompt.fieldKey}"/>",类型为
<x id="2" equiv-text="${prompt.type}"/></target>
</trans-unit>
@ -5764,7 +5764,7 @@ doesn't pass when either or both of the selected options are equal or above the
</trans-unit>
<trans-unit id="s1608b2f94fa0dbd4">
<source>If set to a duration above 0, the user will have the option to choose to &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>
@ -7585,18 +7585,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>User type</source>
<target>用户类型</target>
</trans-unit>
<trans-unit id="s0b9a40b7b2853c7d">
<source>Default user</source>
<target>默认用户</target>
</trans-unit>
<trans-unit id="s35b9fa270f45b391">
<source>External user</source>
<target>外部用户</target>
</trans-unit>
<trans-unit id="s1a635369edaf4dc3">
<source>Service account</source>
<target>服务账户</target>
</trans-unit>
<trans-unit id="s0e427111d750cc02">
<source>Successfully updated license.</source>
<target>已成功更新许可证。</target>
@ -7617,10 +7605,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Licenses</source>
<target>许可证</target>
</trans-unit>
<trans-unit id="sf8f9f3032e891e16">
<source>TODO Copy</source>
<target>未定副本</target>
</trans-unit>
<trans-unit id="sd49099e9522635f4">
<source>License(s)</source>
<target>许可证</target>
@ -7629,34 +7613,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Enterprise is in preview.</source>
<target>企业版目前处于预览状态。</target>
</trans-unit>
<trans-unit id="s34dca481f039c226">
<source>How to get a license</source>
<target>如何获取许可证</target>
</trans-unit>
<trans-unit id="s948364901c166232">
<source>Copy the installation ID</source>
<target>复制安装 ID</target>
</trans-unit>
<trans-unit id="s75c167446b237e0f">
<source>Then open the customer portal</source>
<target>然后打开消费者中心</target>
</trans-unit>
<trans-unit id="s9748dd3bd53d27a4">
<source>Forecasted default users</source>
<target>预测默认用户</target>
</trans-unit>
<trans-unit id="s6b18f594d94c2374">
<source>Estimated user count one year from now</source>
<target>预计从此时开始一年内的用户数</target>
</trans-unit>
<trans-unit id="s69f246d164be88d0">
<source>Forecasted external users</source>
<target>预测外部用户</target>
</trans-unit>
<trans-unit id="s878fc2eaf94642db">
<source>Estimated external user count one year from now</source>
<target>预计从此时开始一年内的外部用户数</target>
</trans-unit>
<trans-unit id="sd22bd01bdf28c548">
<source>Cumulative license expiry</source>
<target>累计许可证过期时间</target>
@ -7665,10 +7621,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Update License</source>
<target>更新许可证</target>
</trans-unit>
<trans-unit id="s99afa741c259d70e">
<source>Create License</source>
<target>创建许可证</target>
</trans-unit>
<trans-unit id="s7df5b92a3f93544f">
<source>Warning: The current user count has exceeded the configured licenses.</source>
<target>警告:当前用户数超过了配置的许可证限制</target>
@ -7684,7 +7636,62 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s9ce7cc01fb9b5b53">
<source>Manage enterprise licenses</source>
<target>管理企业版许可证</target>
</trans-unit>
<trans-unit id="sf9ebf11ac2645820">
<source>No licenses found.</source>
<target>未找到许可证。</target>
</trans-unit>
<trans-unit id="sa1db89262360550b">
<source>Send us feedback!</source>
<target>给我们发送反馈!</target>
</trans-unit>
<trans-unit id="s4015746f55a8d89f">
<source>Get a license</source>
<target>获取许可证</target>
</trans-unit>
<trans-unit id="sb2cbd06f8e25b47e">
<source>Go to Customer Portal</source>
<target>前往客户中心</target>
</trans-unit>
<trans-unit id="sf58825457d61c429">
<source>Forecast internal users</source>
<target>预测内部用户</target>
</trans-unit>
<trans-unit id="sde9a3f41977ec1f8">
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.users}"/> current internal users and <x id="1" equiv-text="${this.forecast?.forecastedUsers}"/> forecasted internal users.</source>
<target>根据当前 <x id="0" equiv-text="${this.forecast?.users}"/> 名内部用户和 <x id="1" equiv-text="${this.forecast?.forecastedUsers}"/> 名预测的内部用户,估算从此时起一年后的用户数。</target>
</trans-unit>
<trans-unit id="s4557b6b9da258643">
<source>Forecast external users</source>
<target>预测外部用户</target>
</trans-unit>
<trans-unit id="sf52479d6daa0a4a8">
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.externalUsers}"/> current external users and <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> forecasted external users.</source>
<target>根据当前 <x id="0" equiv-text="${this.forecast?.externalUsers}"/> 名外部用户和 <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> 名预测的外部用户,估算从此时起一年后的用户数。</target>
</trans-unit>
<trans-unit id="s6196153c4b0c1ea0">
<source>Install</source>
<target>安装</target>
</trans-unit>
<trans-unit id="s0285b4bd69130fa3">
<source>Install License</source>
<target>安装许可证</target>
</trans-unit>
<trans-unit id="scef2eb6a2bfe3110">
<source>Internal users might be users such as company employees, which will get access to the full Enterprise feature set.</source>
</trans-unit>
<trans-unit id="sf66389b04fcc219c">
<source>External users might be external consultants or B2C customers. These users don't get access to enterprise features.</source>
</trans-unit>
<trans-unit id="s77e8668a27dbc402">
<source>Service accounts should be used for machine-to-machine authentication or other automations.</source>
</trans-unit>
<trans-unit id="s28cbd874ba450b4e">
<source>Less details</source>
</trans-unit>
<trans-unit id="s8fa26f65aed77c96">
<source>More details</source>
</trans-unit>
</body>
</file>
</xliff>
</xliff>

View File

@ -5704,15 +5704,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s9f9492d30a96b9c6">
<source>User type</source>
</trans-unit>
<trans-unit id="s0b9a40b7b2853c7d">
<source>Default user</source>
</trans-unit>
<trans-unit id="s35b9fa270f45b391">
<source>External user</source>
</trans-unit>
<trans-unit id="s1a635369edaf4dc3">
<source>Service account</source>
</trans-unit>
<trans-unit id="s0e427111d750cc02">
<source>Successfully updated license.</source>
</trans-unit>
@ -5728,45 +5719,18 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s2e109263b73c12d5">
<source>Licenses</source>
</trans-unit>
<trans-unit id="sf8f9f3032e891e16">
<source>TODO Copy</source>
</trans-unit>
<trans-unit id="sd49099e9522635f4">
<source>License(s)</source>
</trans-unit>
<trans-unit id="s3be1d90ffa46b7f1">
<source>Enterprise is in preview.</source>
</trans-unit>
<trans-unit id="s34dca481f039c226">
<source>How to get a license</source>
</trans-unit>
<trans-unit id="s948364901c166232">
<source>Copy the installation ID</source>
</trans-unit>
<trans-unit id="s75c167446b237e0f">
<source>Then open the customer portal</source>
</trans-unit>
<trans-unit id="s9748dd3bd53d27a4">
<source>Forecasted default users</source>
</trans-unit>
<trans-unit id="s6b18f594d94c2374">
<source>Estimated user count one year from now</source>
</trans-unit>
<trans-unit id="s69f246d164be88d0">
<source>Forecasted external users</source>
</trans-unit>
<trans-unit id="s878fc2eaf94642db">
<source>Estimated external user count one year from now</source>
</trans-unit>
<trans-unit id="sd22bd01bdf28c548">
<source>Cumulative license expiry</source>
</trans-unit>
<trans-unit id="sdeb6cee42435dd07">
<source>Update License</source>
</trans-unit>
<trans-unit id="s99afa741c259d70e">
<source>Create License</source>
</trans-unit>
<trans-unit id="s7df5b92a3f93544f">
<source>Warning: The current user count has exceeded the configured licenses.</source>
</trans-unit>
@ -5778,6 +5742,51 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s9ce7cc01fb9b5b53">
<source>Manage enterprise licenses</source>
</trans-unit>
<trans-unit id="sf9ebf11ac2645820">
<source>No licenses found.</source>
</trans-unit>
<trans-unit id="sa1db89262360550b">
<source>Send us feedback!</source>
</trans-unit>
<trans-unit id="s4015746f55a8d89f">
<source>Get a license</source>
</trans-unit>
<trans-unit id="sb2cbd06f8e25b47e">
<source>Go to Customer Portal</source>
</trans-unit>
<trans-unit id="sf58825457d61c429">
<source>Forecast internal users</source>
</trans-unit>
<trans-unit id="sde9a3f41977ec1f8">
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.users}"/> current internal users and <x id="1" equiv-text="${this.forecast?.forecastedUsers}"/> forecasted internal users.</source>
</trans-unit>
<trans-unit id="s4557b6b9da258643">
<source>Forecast external users</source>
</trans-unit>
<trans-unit id="sf52479d6daa0a4a8">
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.externalUsers}"/> current external users and <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> forecasted external users.</source>
</trans-unit>
<trans-unit id="s6196153c4b0c1ea0">
<source>Install</source>
</trans-unit>
<trans-unit id="s0285b4bd69130fa3">
<source>Install License</source>
</trans-unit>
<trans-unit id="scef2eb6a2bfe3110">
<source>Internal users might be users such as company employees, which will get access to the full Enterprise feature set.</source>
</trans-unit>
<trans-unit id="sf66389b04fcc219c">
<source>External users might be external consultants or B2C customers. These users don't get access to enterprise features.</source>
</trans-unit>
<trans-unit id="s77e8668a27dbc402">
<source>Service accounts should be used for machine-to-machine authentication or other automations.</source>
</trans-unit>
<trans-unit id="s28cbd874ba450b4e">
<source>Less details</source>
</trans-unit>
<trans-unit id="s8fa26f65aed77c96">
<source>More details</source>
</trans-unit>
</body>
</file>

View File

@ -7617,10 +7617,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Licenses</source>
<target>许可证</target>
</trans-unit>
<trans-unit id="sf8f9f3032e891e16">
<source>TODO Copy</source>
<target>未定副本</target>
</trans-unit>
<trans-unit id="sd49099e9522635f4">
<source>License(s)</source>
<target>许可证</target>
@ -7629,34 +7625,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Enterprise is in preview.</source>
<target>企业版目前处于预览状态。</target>
</trans-unit>
<trans-unit id="s34dca481f039c226">
<source>How to get a license</source>
<target>如何获取许可证</target>
</trans-unit>
<trans-unit id="s948364901c166232">
<source>Copy the installation ID</source>
<target>复制安装 ID</target>
</trans-unit>
<trans-unit id="s75c167446b237e0f">
<source>Then open the customer portal</source>
<target>然后打开消费者中心</target>
</trans-unit>
<trans-unit id="s9748dd3bd53d27a4">
<source>Forecasted default users</source>
<target>预测默认用户</target>
</trans-unit>
<trans-unit id="s6b18f594d94c2374">
<source>Estimated user count one year from now</source>
<target>预计从此时开始一年内的用户数</target>
</trans-unit>
<trans-unit id="s69f246d164be88d0">
<source>Forecasted external users</source>
<target>预测外部用户</target>
</trans-unit>
<trans-unit id="s878fc2eaf94642db">
<source>Estimated external user count one year from now</source>
<target>预计从此时开始一年内的外部用户数</target>
</trans-unit>
<trans-unit id="sd22bd01bdf28c548">
<source>Cumulative license expiry</source>
<target>累计许可证过期时间</target>
@ -7665,10 +7633,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Update License</source>
<target>更新许可证</target>
</trans-unit>
<trans-unit id="s99afa741c259d70e">
<source>Create License</source>
<target>创建许可证</target>
</trans-unit>
<trans-unit id="s7df5b92a3f93544f">
<source>Warning: The current user count has exceeded the configured licenses.</source>
<target>警告:当前用户数超过了配置的许可证限制</target>
@ -7684,6 +7648,46 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s9ce7cc01fb9b5b53">
<source>Manage enterprise licenses</source>
<target>管理企业版许可证</target>
</trans-unit>
<trans-unit id="sf9ebf11ac2645820">
<source>No licenses found.</source>
<target>未找到许可证。</target>
</trans-unit>
<trans-unit id="sa1db89262360550b">
<source>Send us feedback!</source>
<target>给我们发送反馈!</target>
</trans-unit>
<trans-unit id="s4015746f55a8d89f">
<source>Get a license</source>
<target>获取许可证</target>
</trans-unit>
<trans-unit id="sb2cbd06f8e25b47e">
<source>Go to Customer Portal</source>
<target>前往客户中心</target>
</trans-unit>
<trans-unit id="sf58825457d61c429">
<source>Forecast internal users</source>
<target>预测内部用户</target>
</trans-unit>
<trans-unit id="sde9a3f41977ec1f8">
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.users}"/> current internal users and <x id="1" equiv-text="${this.forecast?.forecastedUsers}"/> forecasted internal users.</source>
<target>根据当前 <x id="0" equiv-text="${this.forecast?.users}"/> 名内部用户和 <x id="1" equiv-text="${this.forecast?.forecastedUsers}"/> 名预测的内部用户,估算从此时起一年后的用户数。</target>
</trans-unit>
<trans-unit id="s4557b6b9da258643">
<source>Forecast external users</source>
<target>预测外部用户</target>
</trans-unit>
<trans-unit id="sf52479d6daa0a4a8">
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.externalUsers}"/> current external users and <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> forecasted external users.</source>
<target>根据当前 <x id="0" equiv-text="${this.forecast?.externalUsers}"/> 名外部用户和 <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> 名预测的外部用户,估算从此时起一年后的用户数。</target>
</trans-unit>
<trans-unit id="s6196153c4b0c1ea0">
<source>Install</source>
<target>安装</target>
</trans-unit>
<trans-unit id="s0285b4bd69130fa3">
<source>Install License</source>
<target>安装许可证</target>
</trans-unit>
</body>
</file>

View File

@ -5703,15 +5703,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s9f9492d30a96b9c6">
<source>User type</source>
</trans-unit>
<trans-unit id="s0b9a40b7b2853c7d">
<source>Default user</source>
</trans-unit>
<trans-unit id="s35b9fa270f45b391">
<source>External user</source>
</trans-unit>
<trans-unit id="s1a635369edaf4dc3">
<source>Service account</source>
</trans-unit>
<trans-unit id="s0e427111d750cc02">
<source>Successfully updated license.</source>
</trans-unit>
@ -5727,45 +5718,18 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s2e109263b73c12d5">
<source>Licenses</source>
</trans-unit>
<trans-unit id="sf8f9f3032e891e16">
<source>TODO Copy</source>
</trans-unit>
<trans-unit id="sd49099e9522635f4">
<source>License(s)</source>
</trans-unit>
<trans-unit id="s3be1d90ffa46b7f1">
<source>Enterprise is in preview.</source>
</trans-unit>
<trans-unit id="s34dca481f039c226">
<source>How to get a license</source>
</trans-unit>
<trans-unit id="s948364901c166232">
<source>Copy the installation ID</source>
</trans-unit>
<trans-unit id="s75c167446b237e0f">
<source>Then open the customer portal</source>
</trans-unit>
<trans-unit id="s9748dd3bd53d27a4">
<source>Forecasted default users</source>
</trans-unit>
<trans-unit id="s6b18f594d94c2374">
<source>Estimated user count one year from now</source>
</trans-unit>
<trans-unit id="s69f246d164be88d0">
<source>Forecasted external users</source>
</trans-unit>
<trans-unit id="s878fc2eaf94642db">
<source>Estimated external user count one year from now</source>
</trans-unit>
<trans-unit id="sd22bd01bdf28c548">
<source>Cumulative license expiry</source>
</trans-unit>
<trans-unit id="sdeb6cee42435dd07">
<source>Update License</source>
</trans-unit>
<trans-unit id="s99afa741c259d70e">
<source>Create License</source>
</trans-unit>
<trans-unit id="s7df5b92a3f93544f">
<source>Warning: The current user count has exceeded the configured licenses.</source>
</trans-unit>
@ -5777,6 +5741,51 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s9ce7cc01fb9b5b53">
<source>Manage enterprise licenses</source>
</trans-unit>
<trans-unit id="sf9ebf11ac2645820">
<source>No licenses found.</source>
</trans-unit>
<trans-unit id="sa1db89262360550b">
<source>Send us feedback!</source>
</trans-unit>
<trans-unit id="s4015746f55a8d89f">
<source>Get a license</source>
</trans-unit>
<trans-unit id="sb2cbd06f8e25b47e">
<source>Go to Customer Portal</source>
</trans-unit>
<trans-unit id="sf58825457d61c429">
<source>Forecast internal users</source>
</trans-unit>
<trans-unit id="sde9a3f41977ec1f8">
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.users}"/> current internal users and <x id="1" equiv-text="${this.forecast?.forecastedUsers}"/> forecasted internal users.</source>
</trans-unit>
<trans-unit id="s4557b6b9da258643">
<source>Forecast external users</source>
</trans-unit>
<trans-unit id="sf52479d6daa0a4a8">
<source>Estimated user count one year from now based on <x id="0" equiv-text="${this.forecast?.externalUsers}"/> current external users and <x id="1" equiv-text="${this.forecast?.forecastedExternalUsers}"/> forecasted external users.</source>
</trans-unit>
<trans-unit id="s6196153c4b0c1ea0">
<source>Install</source>
</trans-unit>
<trans-unit id="s0285b4bd69130fa3">
<source>Install License</source>
</trans-unit>
<trans-unit id="scef2eb6a2bfe3110">
<source>Internal users might be users such as company employees, which will get access to the full Enterprise feature set.</source>
</trans-unit>
<trans-unit id="sf66389b04fcc219c">
<source>External users might be external consultants or B2C customers. These users don't get access to enterprise features.</source>
</trans-unit>
<trans-unit id="s77e8668a27dbc402">
<source>Service accounts should be used for machine-to-machine authentication or other automations.</source>
</trans-unit>
<trans-unit id="s28cbd874ba450b4e">
<source>Less details</source>
</trans-unit>
<trans-unit id="s8fa26f65aed77c96">
<source>More details</source>
</trans-unit>
</body>
</file>

View File

@ -0,0 +1,37 @@
---
title: "Procedural topic"
---
Use a title that focuses on the task you are writing about... for example, "Add a new Group" or "Edit user profiles". For procedural docs, there should be a verb in the tilte, and usually the noun (the thing you are working on). For the title (and all headings) use the infinitive form of the verb (i.e. "add") not the gerund form (i.e. "adding").
In this first section write one or two sentences about the task. Keep it brief; if it goes on too long, then create a separate conceptual topic, in a separate `.md` file. We don't want readers to have to scroll through paragraphs of conceptual info before they get to Step 1.
## Prerequisites (optional section)
In this section, inform the reader of anything they need to do, or have configured or installed, before they start following the procedural instructions below.
## Overview of steps/workflow (optional section)
If the task is quite long or complex, it might be good to add a bullet list of the main steps, or even a diagram of the workflow, just so that the reader can first familairize themselves with the 50,000 meter view before they dive into the detailed steps.
## first several group steps
If the task involves a lot of steps, try to group them into simalr steps and have a Head3 or Hedad4 title for each group.
In this section, help the reader get oriented... where do they need to be (i.e. in the GUI, on a CLI, etc).
Have a separate paragraph for each step.
Start instructions with the desired outcome, followed by the instructions.
EXAMPLE: To define a new port number, navigate to the Admin interface, and then to the **Settings** tab.
## next step of grouped steps
Continue with the steps...
Use screenshots sparingly, only for complex UIs where it is difficult to describe a UI element with words.
## verify the steps
Whenever possible, it is useful to add verification steps at the end of a procedural topic. For example, if the procedural was about installing a product, use this section to tell them how they can verify that the install was successful

View File

@ -10,35 +10,49 @@ Everyone welcome; we will work on code, docs, and anything else that looks inter
Moderators will be available for most US and European hours, so if during the multi-day event, participants have questions or a PR needs a technical review, we are here for you.
> More details will be added soon about the exact daily timeframes, for both European-based and US-based participants.
Prizes? Why, Yes! We've got a total prize pool of $5000 and a bunch of cool authentik-branded socks and, indubitably, GitHub fame.
Prizes? Why, Yes! We've got a total prize pool of $5000 and a bunch of cool authentik-branded socks, food certificates, and indubitably GitHub fame.
## Date
## When
July 26-30, 2023
- Kickoff meeting is on Wednesday, July 26th, at 8:00am Pacific USA (UTC -7), 5:00pm in Central Europe (UTC +2), and 8:30pm in Mumbai (UTC +5.30)
- Check-in calls on Thursday and Friday, for one hour, at the same times as above.
- Wrap-up and first demos on Saturday, starting at same times as above.
- Final demos, voting, and awards on Sunday! Yep, same times as above.
## Where
Online, in our [GitHub repo](https://github.com/goauthentik/authentik), using [Discord](https://discord.com/channels/809154715984199690/1110948434552299673) for conversations, questions, and video calls.
Online, in our [GitHub repo](https://github.com/goauthentik/authentik), and on Discord in our [#hackathon23 channel](https://discord.com/channels/809154715984199690/1110948434552299673) for our Kickoff call, checkins, and the wrap-up and awards events. We will also use the #hackathon23 channel throughout the entire five days, for questions and general chatting. Be sure to first visit our [welcome-info-rules channel](https://discord.com/channels/809154715984199690/813452440660606986), to review our code of conduct and see the latest posts about the hackathon.
## Registration form
Use the [Hackathon 2023 Registration Form](https://docs.google.com/forms/d/e/1FAIpQLSc10MiK4li-SIY8SidhwFEwFbDcna-fio1xFY2nmimqXPPvLA/viewform) to let us know that you'll join us!
> If you know what you and/or your team want to work on, you can open an [Issue](https://github.com/goauthentik/authentik/issues) using our template for all hackathon Issues at any time (why not now?) and add the `hackathon` label. Then, when you register, enter the Issue number that you opened on your registration form. This way, on Kickoff Day we can easily match particpants with their Issue of interest.
## Take a look on GitHub
If you already know what you and/or your team want to work on, you can open an [Issue](https://github.com/goauthentik/authentik/issues) using our template for all hackathon Issues at any time (why not now?) and add the `hackathon` label. Then, when you register, enter the Issue number that you opened on your registration form. This way, on Kickoff Day we can easily match particpants with their Issue of interest.
During the Kickoff call, there will be time to peruse existing Issues and add emotes to indicate your interest in working on it (or having it worked on!)
- 🚀 I want to work on this
- ❤️ I want to see this worked on
## Agenda
- **Wednesday, July 26th**: Kickoff, voting for topics to work on, teams formed, participants select the Issue/team they are going to work on, and get their environment set up. After the online kickoff, you can start your work at any time.
- **Thursday July 27th**: HackDay #1: participants working on their PRs
- **Thursday July 27th**: HackDay #1: participants working on their PRs, a one-hour Check-in call
- **Friday, July 28th**: HackDay #2: participants working on their PRs
- **Friday, July 28th**: HackDay #2: participants working on their PRs, a one-hour Check-in call
- **Saturday, July 29th**: an online “meeting” to do wrap-up, participants sign-up for demo slots (Saturday and Sunday slots available), then afternoon demos
- **Saturday, July 29th**: an online “meeting” to do wrap-up, participants sign-up for demo slots (Saturday and Sunday slots available), then some demos
- **Sunday, July 30th**: demos, votes, awards, and wrap up
- **Sunday, July 30th**: rest of the demos, votes, and awards
## About that money...
Be aware that all prize money distributions will follow local/state/country laws regarding taxation, not providing funds to citizens of countries prohibited by US law, and all other legal requirements.
## Questions?

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