Merge branch 'main' into web/update-provider-forms-for-invalidation
* main: (44 commits) web/admin: add strict dompurify config for diagram (#11783) core: bump cryptography from 43.0.1 to 43.0.3 (#11750) web: bump API Client version (#11781) sources: add Kerberos (#10815) root: rework CSRF middleware to set secure flag (#11753) web/admin: improve invalidation flow default & field grouping (#11769) providers/scim: add comparison with existing group on update and delta update users (#11414) website: bump mermaid from 10.6.0 to 10.9.3 in /website (#11766) web/flows: use dompurify for footer links (#11773) core, web: update translations (#11775) core: bump goauthentik.io/api/v3 from 3.2024083.10 to 3.2024083.11 (#11776) website: bump @types/react from 18.3.11 to 18.3.12 in /website (#11777) website: bump http-proxy-middleware from 2.0.6 to 2.0.7 in /website (#11771) web: bump API Client version (#11770) stages: authenticator_endpoint_gdtc (#10477) core: add prompt_data to auth flow (#11702) tests/e2e: fix dex tests failing (#11761) web/rac: disable DPI scaling (#11757) web/admin: update flow background (#11758) website/docs: fix some broken links (#11742) ...
This commit is contained in:
		
							
								
								
									
										
											BIN
										
									
								
								web/authentik/sources/kerberos.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								web/authentik/sources/kerberos.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 74 KiB  | 
							
								
								
									
										83
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										83
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							@ -23,7 +23,8 @@
 | 
			
		||||
                "@floating-ui/dom": "^1.6.11",
 | 
			
		||||
                "@formatjs/intl-listformat": "^7.5.7",
 | 
			
		||||
                "@fortawesome/fontawesome-free": "^6.6.0",
 | 
			
		||||
                "@goauthentik/api": "^2024.8.3-1729166675",
 | 
			
		||||
                "@goauthentik/api": "^2024.8.3-1729699127",
 | 
			
		||||
                "@lit-labs/ssr": "^3.2.2",
 | 
			
		||||
                "@lit/context": "^1.1.2",
 | 
			
		||||
                "@lit/localize": "^0.12.2",
 | 
			
		||||
                "@lit/reactive-element": "^2.0.4",
 | 
			
		||||
@ -41,6 +42,7 @@
 | 
			
		||||
                "construct-style-sheets-polyfill": "^3.1.0",
 | 
			
		||||
                "core-js": "^3.38.1",
 | 
			
		||||
                "country-flag-icons": "^1.5.13",
 | 
			
		||||
                "dompurify": "^3.1.7",
 | 
			
		||||
                "fuse.js": "^7.0.0",
 | 
			
		||||
                "guacamole-common-js": "^1.5.0",
 | 
			
		||||
                "lit": "^3.2.0",
 | 
			
		||||
@ -69,6 +71,7 @@
 | 
			
		||||
                "@trivago/prettier-plugin-sort-imports": "^4.3.0",
 | 
			
		||||
                "@types/chart.js": "^2.9.41",
 | 
			
		||||
                "@types/codemirror": "^5.60.15",
 | 
			
		||||
                "@types/dompurify": "^3.0.5",
 | 
			
		||||
                "@types/eslint__js": "^8.42.3",
 | 
			
		||||
                "@types/grecaptcha": "^3.0.9",
 | 
			
		||||
                "@types/guacamole-common-js": "^1.5.2",
 | 
			
		||||
@ -1772,9 +1775,9 @@
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@goauthentik/api": {
 | 
			
		||||
            "version": "2024.8.3-1729166675",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2024.8.3-1729166675.tgz",
 | 
			
		||||
            "integrity": "sha512-mAcVaMHB2KGGGigUgu3aurSo05yBLarbfvEnvhUs2pvxaTSuX5Zezl34OlFh/gGWQEt0Z7St7GGaY256F3E74g=="
 | 
			
		||||
            "version": "2024.8.3-1729699127",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2024.8.3-1729699127.tgz",
 | 
			
		||||
            "integrity": "sha512-luo0SAASR6BTTtLszDgfdwofBejv4F3hCHgPxeSoTSFgE8/A2+zJD8EtWPZaa1udDkwPa9lbIeJSSmbgFke3jA=="
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@goauthentik/web": {
 | 
			
		||||
            "resolved": "",
 | 
			
		||||
@ -2459,11 +2462,50 @@
 | 
			
		||||
                "@lezer/lr": "^1.0.0"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@lit-labs/ssr": {
 | 
			
		||||
            "version": "3.2.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@lit-labs/ssr/-/ssr-3.2.2.tgz",
 | 
			
		||||
            "integrity": "sha512-He5TzeNPM9ECmVpgXRYmVlz0UA5YnzHlT43kyLi2Lu6mUidskqJVonk9W5K699+2DKhoXp8Ra4EJmHR6KrcW1Q==",
 | 
			
		||||
            "license": "BSD-3-Clause",
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "@lit-labs/ssr-client": "^1.1.7",
 | 
			
		||||
                "@lit-labs/ssr-dom-shim": "^1.2.0",
 | 
			
		||||
                "@lit/reactive-element": "^2.0.4",
 | 
			
		||||
                "@parse5/tools": "^0.3.0",
 | 
			
		||||
                "@types/node": "^16.0.0",
 | 
			
		||||
                "enhanced-resolve": "^5.10.0",
 | 
			
		||||
                "lit": "^3.1.2",
 | 
			
		||||
                "lit-element": "^4.0.4",
 | 
			
		||||
                "lit-html": "^3.1.2",
 | 
			
		||||
                "node-fetch": "^3.2.8",
 | 
			
		||||
                "parse5": "^7.1.1"
 | 
			
		||||
            },
 | 
			
		||||
            "engines": {
 | 
			
		||||
                "node": ">=13.9.0"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@lit-labs/ssr-client": {
 | 
			
		||||
            "version": "1.1.7",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@lit-labs/ssr-client/-/ssr-client-1.1.7.tgz",
 | 
			
		||||
            "integrity": "sha512-VvqhY/iif3FHrlhkzEPsuX/7h/NqnfxLwVf0p8ghNIlKegRyRqgeaJevZ57s/u/LiFyKgqksRP5n+LmNvpxN+A==",
 | 
			
		||||
            "license": "BSD-3-Clause",
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "@lit/reactive-element": "^2.0.4",
 | 
			
		||||
                "lit": "^3.1.2",
 | 
			
		||||
                "lit-html": "^3.1.2"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@lit-labs/ssr-dom-shim": {
 | 
			
		||||
            "version": "1.2.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.1.tgz",
 | 
			
		||||
            "integrity": "sha512-wx4aBmgeGvFmOKucFKY+8VFJSYZxs9poN3SDNQFF6lT6NrQUnHiPB2PWz2sc4ieEcAaYYzN+1uWahEeTq2aRIQ=="
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@lit-labs/ssr/node_modules/@types/node": {
 | 
			
		||||
            "version": "16.18.114",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.114.tgz",
 | 
			
		||||
            "integrity": "sha512-7oAtnxrgkMNzyzT443UDWwzkmYew81F1ZSPm3/lsITJfW/WludaSOpegTvUG+UdapcbrtWOtY/E4LyTkhPGJ5Q==",
 | 
			
		||||
            "license": "MIT"
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@lit/context": {
 | 
			
		||||
            "version": "1.1.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@lit/context/-/context-1.1.2.tgz",
 | 
			
		||||
@ -3027,7 +3069,6 @@
 | 
			
		||||
            "version": "0.3.0",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@parse5/tools/-/tools-0.3.0.tgz",
 | 
			
		||||
            "integrity": "sha512-zxRyTHkqb7WQMV8kTNBKWb1BeOFUKXBXTBWuxg9H9hfvQB3IwP6Iw2U75Ia5eyRxPNltmY7E8YAlz6zWwUnjKg==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "parse5": "^7.0.0"
 | 
			
		||||
            }
 | 
			
		||||
@ -5629,6 +5670,16 @@
 | 
			
		||||
                "@types/node": "*"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@types/dompurify": {
 | 
			
		||||
            "version": "3.0.5",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz",
 | 
			
		||||
            "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "license": "MIT",
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "@types/trusted-types": "*"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@types/eslint": {
 | 
			
		||||
            "version": "9.6.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
 | 
			
		||||
@ -9676,7 +9727,6 @@
 | 
			
		||||
            "version": "4.0.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
 | 
			
		||||
            "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "engines": {
 | 
			
		||||
                "node": ">= 12"
 | 
			
		||||
            }
 | 
			
		||||
@ -10053,9 +10103,10 @@
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/dompurify": {
 | 
			
		||||
            "version": "3.1.6",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.6.tgz",
 | 
			
		||||
            "integrity": "sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ=="
 | 
			
		||||
            "version": "3.1.7",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.7.tgz",
 | 
			
		||||
            "integrity": "sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ==",
 | 
			
		||||
            "license": "(MPL-2.0 OR Apache-2.0)"
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/domutils": {
 | 
			
		||||
            "version": "3.1.0",
 | 
			
		||||
@ -10278,7 +10329,6 @@
 | 
			
		||||
            "version": "5.17.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
 | 
			
		||||
            "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "graceful-fs": "^4.2.4",
 | 
			
		||||
                "tapable": "^2.2.0"
 | 
			
		||||
@ -10316,7 +10366,6 @@
 | 
			
		||||
            "version": "4.5.0",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
 | 
			
		||||
            "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "engines": {
 | 
			
		||||
                "node": ">=0.12"
 | 
			
		||||
            },
 | 
			
		||||
@ -12518,8 +12567,7 @@
 | 
			
		||||
        "node_modules/graceful-fs": {
 | 
			
		||||
            "version": "4.2.11",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
 | 
			
		||||
            "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
 | 
			
		||||
            "dev": true
 | 
			
		||||
            "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/grapheme-splitter": {
 | 
			
		||||
            "version": "1.0.4",
 | 
			
		||||
@ -14969,6 +15017,12 @@
 | 
			
		||||
                "uuid": "^9.0.1"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/mermaid/node_modules/dompurify": {
 | 
			
		||||
            "version": "3.1.6",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.6.tgz",
 | 
			
		||||
            "integrity": "sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ==",
 | 
			
		||||
            "license": "(MPL-2.0 OR Apache-2.0)"
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/methods": {
 | 
			
		||||
            "version": "1.1.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
 | 
			
		||||
@ -15649,7 +15703,6 @@
 | 
			
		||||
            "version": "3.3.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
 | 
			
		||||
            "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "data-uri-to-buffer": "^4.0.0",
 | 
			
		||||
                "fetch-blob": "^3.1.4",
 | 
			
		||||
@ -16588,7 +16641,6 @@
 | 
			
		||||
            "version": "7.1.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
 | 
			
		||||
            "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "entities": "^4.4.0"
 | 
			
		||||
            },
 | 
			
		||||
@ -19574,7 +19626,6 @@
 | 
			
		||||
            "version": "2.2.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
 | 
			
		||||
            "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "engines": {
 | 
			
		||||
                "node": ">=6"
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,8 @@
 | 
			
		||||
        "@floating-ui/dom": "^1.6.11",
 | 
			
		||||
        "@formatjs/intl-listformat": "^7.5.7",
 | 
			
		||||
        "@fortawesome/fontawesome-free": "^6.6.0",
 | 
			
		||||
        "@goauthentik/api": "^2024.8.3-1729166675",
 | 
			
		||||
        "@goauthentik/api": "^2024.8.3-1729699127",
 | 
			
		||||
        "@lit-labs/ssr": "^3.2.2",
 | 
			
		||||
        "@lit/context": "^1.1.2",
 | 
			
		||||
        "@lit/localize": "^0.12.2",
 | 
			
		||||
        "@lit/reactive-element": "^2.0.4",
 | 
			
		||||
@ -29,6 +30,7 @@
 | 
			
		||||
        "construct-style-sheets-polyfill": "^3.1.0",
 | 
			
		||||
        "core-js": "^3.38.1",
 | 
			
		||||
        "country-flag-icons": "^1.5.13",
 | 
			
		||||
        "dompurify": "^3.1.7",
 | 
			
		||||
        "fuse.js": "^7.0.0",
 | 
			
		||||
        "guacamole-common-js": "^1.5.0",
 | 
			
		||||
        "lit": "^3.2.0",
 | 
			
		||||
@ -57,6 +59,7 @@
 | 
			
		||||
        "@trivago/prettier-plugin-sort-imports": "^4.3.0",
 | 
			
		||||
        "@types/chart.js": "^2.9.41",
 | 
			
		||||
        "@types/codemirror": "^5.60.15",
 | 
			
		||||
        "@types/dompurify": "^3.0.5",
 | 
			
		||||
        "@types/eslint__js": "^8.42.3",
 | 
			
		||||
        "@types/grecaptcha": "^3.0.9",
 | 
			
		||||
        "@types/guacamole-common-js": "^1.5.2",
 | 
			
		||||
 | 
			
		||||
@ -1,33 +1,14 @@
 | 
			
		||||
import "@goauthentik/admin/applications/wizard/ak-wizard-title";
 | 
			
		||||
import "@goauthentik/admin/common/ak-crypto-certificate-search";
 | 
			
		||||
import "@goauthentik/admin/common/ak-flow-search/ak-branded-flow-search";
 | 
			
		||||
import { first } from "@goauthentik/common/utils";
 | 
			
		||||
import "@goauthentik/components/ak-number-input";
 | 
			
		||||
import "@goauthentik/components/ak-radio-input";
 | 
			
		||||
import "@goauthentik/components/ak-switch-input";
 | 
			
		||||
import "@goauthentik/components/ak-text-input";
 | 
			
		||||
import { renderForm } from "@goauthentik/admin/providers/ldap/LDAPProviderFormForm.js";
 | 
			
		||||
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
 | 
			
		||||
import "@goauthentik/elements/forms/FormGroup";
 | 
			
		||||
import "@goauthentik/elements/forms/HorizontalFormElement";
 | 
			
		||||
 | 
			
		||||
import { msg } from "@lit/localize";
 | 
			
		||||
import { customElement } from "@lit/reactive-element/decorators/custom-element.js";
 | 
			
		||||
import { html, nothing } from "lit";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined.js";
 | 
			
		||||
import { html } from "lit";
 | 
			
		||||
 | 
			
		||||
import { FlowsInstancesListDesignationEnum } from "@goauthentik/api";
 | 
			
		||||
import type { LDAPProvider } from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
import BaseProviderPanel from "../BaseProviderPanel";
 | 
			
		||||
import {
 | 
			
		||||
    bindModeOptions,
 | 
			
		||||
    cryptoCertificateHelp,
 | 
			
		||||
    gidStartNumberHelp,
 | 
			
		||||
    mfaSupportHelp,
 | 
			
		||||
    searchModeOptions,
 | 
			
		||||
    tlsServerNameHelp,
 | 
			
		||||
    uidStartNumberHelp,
 | 
			
		||||
} from "./LDAPOptionsAndHelp";
 | 
			
		||||
 | 
			
		||||
@customElement("ak-application-wizard-authentication-by-ldap")
 | 
			
		||||
export class ApplicationWizardApplicationDetails extends WithBrandConfig(BaseProviderPanel) {
 | 
			
		||||
@ -37,115 +18,7 @@ export class ApplicationWizardApplicationDetails extends WithBrandConfig(BasePro
 | 
			
		||||
 | 
			
		||||
        return html` <ak-wizard-title>${msg("Configure LDAP Provider")}</ak-wizard-title>
 | 
			
		||||
            <form class="pf-c-form pf-m-horizontal" @input=${this.handleChange}>
 | 
			
		||||
                <ak-text-input
 | 
			
		||||
                    name="name"
 | 
			
		||||
                    value=${ifDefined(provider?.name)}
 | 
			
		||||
                    label=${msg("Name")}
 | 
			
		||||
                    .errorMessages=${errors?.name ?? []}
 | 
			
		||||
                    required
 | 
			
		||||
                    help=${msg("Method's display Name.")}
 | 
			
		||||
                ></ak-text-input>
 | 
			
		||||
 | 
			
		||||
                <ak-form-element-horizontal
 | 
			
		||||
                    label=${msg("Bind flow")}
 | 
			
		||||
                    ?required=${true}
 | 
			
		||||
                    name="authorizationFlow"
 | 
			
		||||
                    .errorMessages=${errors?.authorizationFlow ?? []}
 | 
			
		||||
                >
 | 
			
		||||
                    <ak-branded-flow-search
 | 
			
		||||
                        flowType=${FlowsInstancesListDesignationEnum.Authentication}
 | 
			
		||||
                        .currentFlow=${provider?.authorizationFlow}
 | 
			
		||||
                        .brandFlow=${this.brand.flowAuthentication}
 | 
			
		||||
                        required
 | 
			
		||||
                    ></ak-branded-flow-search>
 | 
			
		||||
                    <p class="pf-c-form__helper-text">
 | 
			
		||||
                        ${msg("Flow used for users to authenticate.")}
 | 
			
		||||
                    </p>
 | 
			
		||||
                </ak-form-element-horizontal>
 | 
			
		||||
 | 
			
		||||
                <ak-radio-input
 | 
			
		||||
                    label=${msg("Bind mode")}
 | 
			
		||||
                    name="bindMode"
 | 
			
		||||
                    .options=${bindModeOptions}
 | 
			
		||||
                    .value=${provider?.bindMode}
 | 
			
		||||
                    help=${msg("Configure how the outpost authenticates requests.")}
 | 
			
		||||
                >
 | 
			
		||||
                </ak-radio-input>
 | 
			
		||||
 | 
			
		||||
                <ak-radio-input
 | 
			
		||||
                    label=${msg("Search mode")}
 | 
			
		||||
                    name="searchMode"
 | 
			
		||||
                    .options=${searchModeOptions}
 | 
			
		||||
                    .value=${provider?.searchMode}
 | 
			
		||||
                    help=${msg(
 | 
			
		||||
                        "Configure how the outpost queries the core authentik server's users.",
 | 
			
		||||
                    )}
 | 
			
		||||
                >
 | 
			
		||||
                </ak-radio-input>
 | 
			
		||||
 | 
			
		||||
                <ak-switch-input
 | 
			
		||||
                    name="openInNewTab"
 | 
			
		||||
                    label=${msg("Code-based MFA Support")}
 | 
			
		||||
                    ?checked=${provider?.mfaSupport ?? true}
 | 
			
		||||
                    help=${mfaSupportHelp}
 | 
			
		||||
                >
 | 
			
		||||
                </ak-switch-input>
 | 
			
		||||
 | 
			
		||||
                <ak-form-group .expanded=${true}>
 | 
			
		||||
                    <span slot="header"> ${msg("Protocol settings")} </span>
 | 
			
		||||
                    <div slot="body" class="pf-c-form">
 | 
			
		||||
                        <ak-text-input
 | 
			
		||||
                            name="baseDn"
 | 
			
		||||
                            label=${msg("Base DN")}
 | 
			
		||||
                            required
 | 
			
		||||
                            value="${first(provider?.baseDn, "DC=ldap,DC=goauthentik,DC=io")}"
 | 
			
		||||
                            .errorMessages=${errors?.baseDn ?? []}
 | 
			
		||||
                            help=${msg(
 | 
			
		||||
                                "LDAP DN under which bind requests and search requests can be made.",
 | 
			
		||||
                            )}
 | 
			
		||||
                        >
 | 
			
		||||
                        </ak-text-input>
 | 
			
		||||
 | 
			
		||||
                        <ak-form-element-horizontal
 | 
			
		||||
                            label=${msg("Certificate")}
 | 
			
		||||
                            name="certificate"
 | 
			
		||||
                            .errorMessages=${errors?.certificate ?? []}
 | 
			
		||||
                        >
 | 
			
		||||
                            <ak-crypto-certificate-search
 | 
			
		||||
                                certificate=${ifDefined(provider?.certificate ?? nothing)}
 | 
			
		||||
                                name="certificate"
 | 
			
		||||
                            >
 | 
			
		||||
                            </ak-crypto-certificate-search>
 | 
			
		||||
                            <p class="pf-c-form__helper-text">${cryptoCertificateHelp}</p>
 | 
			
		||||
                        </ak-form-element-horizontal>
 | 
			
		||||
 | 
			
		||||
                        <ak-text-input
 | 
			
		||||
                            label=${msg("TLS Server name")}
 | 
			
		||||
                            name="tlsServerName"
 | 
			
		||||
                            value="${first(provider?.tlsServerName, "")}"
 | 
			
		||||
                            .errorMessages=${errors?.tlsServerName ?? []}
 | 
			
		||||
                            help=${tlsServerNameHelp}
 | 
			
		||||
                        ></ak-text-input>
 | 
			
		||||
 | 
			
		||||
                        <ak-number-input
 | 
			
		||||
                            label=${msg("UID start number")}
 | 
			
		||||
                            required
 | 
			
		||||
                            name="uidStartNumber"
 | 
			
		||||
                            value="${first(provider?.uidStartNumber, 2000)}"
 | 
			
		||||
                            .errorMessages=${errors?.uidStartNumber ?? []}
 | 
			
		||||
                            help=${uidStartNumberHelp}
 | 
			
		||||
                        ></ak-number-input>
 | 
			
		||||
 | 
			
		||||
                        <ak-number-input
 | 
			
		||||
                            label=${msg("GID start number")}
 | 
			
		||||
                            required
 | 
			
		||||
                            name="gidStartNumber"
 | 
			
		||||
                            value="${first(provider?.gidStartNumber, 4000)}"
 | 
			
		||||
                            .errorMessages=${errors?.gidStartNumber ?? []}
 | 
			
		||||
                            help=${gidStartNumberHelp}
 | 
			
		||||
                        ></ak-number-input>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </ak-form-group>
 | 
			
		||||
                ${renderForm(provider, errors, this.brand)}
 | 
			
		||||
            </form>`;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -119,21 +119,6 @@ export class AkTypeProxyApplicationWizardPage extends BaseProviderPanel {
 | 
			
		||||
                        ${msg("Flow used when authorizing this provider.")}
 | 
			
		||||
                    </p>
 | 
			
		||||
                </ak-form-element-horizontal>
 | 
			
		||||
                <ak-form-element-horizontal
 | 
			
		||||
                    name="invalidationFlow"
 | 
			
		||||
                    label=${msg("Invalidation flow")}
 | 
			
		||||
                    .errorMessages=${errors?.invalidationFlow ?? []}
 | 
			
		||||
                    ?required=${true}
 | 
			
		||||
                >
 | 
			
		||||
                    <ak-flow-search
 | 
			
		||||
                        flowType=${FlowsInstancesListDesignationEnum.Invalidation}
 | 
			
		||||
                        .currentFlow=${this.instance?.invalidationFlow}
 | 
			
		||||
                        required
 | 
			
		||||
                    ></ak-flow-search>
 | 
			
		||||
                    <p class="pf-c-form__helper-text">
 | 
			
		||||
                        ${msg("Flow used when logging out of this provider.")}
 | 
			
		||||
                    </p>
 | 
			
		||||
                </ak-form-element-horizontal>
 | 
			
		||||
 | 
			
		||||
                ${this.renderProxyMode()}
 | 
			
		||||
 | 
			
		||||
@ -176,9 +161,11 @@ export class AkTypeProxyApplicationWizardPage extends BaseProviderPanel {
 | 
			
		||||
 | 
			
		||||
                        <ak-textarea-input
 | 
			
		||||
                            name="skipPathRegex"
 | 
			
		||||
                            label=${this.mode === ProxyMode.ForwardDomain
 | 
			
		||||
                                ? msg("Unauthenticated URLs")
 | 
			
		||||
                                : msg("Unauthenticated Paths")}
 | 
			
		||||
                            label=${
 | 
			
		||||
                                this.mode === ProxyMode.ForwardDomain
 | 
			
		||||
                                    ? msg("Unauthenticated URLs")
 | 
			
		||||
                                    : msg("Unauthenticated Paths")
 | 
			
		||||
                            }
 | 
			
		||||
                            value=${ifDefined(this.instance?.skipPathRegex)}
 | 
			
		||||
                            .errorMessages=${errors?.skipPathRegex ?? []}
 | 
			
		||||
                            .bighelp=${html` <p class="pf-c-form__helper-text">
 | 
			
		||||
@ -195,6 +182,39 @@ export class AkTypeProxyApplicationWizardPage extends BaseProviderPanel {
 | 
			
		||||
                        </ak-textarea-input>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </ak-form-group>
 | 
			
		||||
                <ak-form-group>
 | 
			
		||||
                    <span slot="header"> ${msg("Advanced flow settings")} </span>
 | 
			
		||||
                    <ak-form-element-horizontal
 | 
			
		||||
                        name="authenticationFlow"
 | 
			
		||||
                        label=${msg("Authentication flow")}
 | 
			
		||||
                    >
 | 
			
		||||
                        <ak-flow-search
 | 
			
		||||
                            flowType=${FlowsInstancesListDesignationEnum.Authentication}
 | 
			
		||||
                            .currentFlow=${this.instance?.authenticationFlow}
 | 
			
		||||
                        ></ak-flow-search>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${msg(
 | 
			
		||||
                                "Flow used when a user access this provider and is not authenticated.",
 | 
			
		||||
                            )}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                    <ak-form-element-horizontal
 | 
			
		||||
                        label=${msg("Invalidation flow")}
 | 
			
		||||
                        name="invalidationFlow"
 | 
			
		||||
                        required
 | 
			
		||||
                    >
 | 
			
		||||
                        <ak-flow-search
 | 
			
		||||
                            flowType=${FlowsInstancesListDesignationEnum.Invalidation}
 | 
			
		||||
                            .currentFlow=${this.instance?.invalidationFlow}
 | 
			
		||||
                            defaultFlowSlug="default-provider-invalidation-flow"
 | 
			
		||||
                            required
 | 
			
		||||
                        ></ak-flow-search>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${msg("Flow used when logging out of this provider.")}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </ak-form-group>
 | 
			
		||||
                <ak-form-group>
 | 
			
		||||
                    <span slot="header">${msg("Authentication settings")}</span>
 | 
			
		||||
                    <div slot="body" class="pf-c-form">
 | 
			
		||||
 | 
			
		||||
@ -103,21 +103,6 @@ export class ApplicationWizardProviderSamlConfiguration extends BaseProviderPane
 | 
			
		||||
                        ${msg("Flow used when authorizing this provider.")}
 | 
			
		||||
                    </p>
 | 
			
		||||
                </ak-form-element-horizontal>
 | 
			
		||||
                <ak-form-element-horizontal
 | 
			
		||||
                    name="invalidationFlow"
 | 
			
		||||
                    label=${msg("Invalidation flow")}
 | 
			
		||||
                    .errorMessages=${errors?.invalidationFlow ?? []}
 | 
			
		||||
                    ?required=${true}
 | 
			
		||||
                >
 | 
			
		||||
                    <ak-flow-search
 | 
			
		||||
                        flowType=${FlowsInstancesListDesignationEnum.Invalidation}
 | 
			
		||||
                        .currentFlow=${provider?.invalidationFlow}
 | 
			
		||||
                        required
 | 
			
		||||
                    ></ak-flow-search>
 | 
			
		||||
                    <p class="pf-c-form__helper-text">
 | 
			
		||||
                        ${msg("Flow used when logging out of this provider.")}
 | 
			
		||||
                    </p>
 | 
			
		||||
                </ak-form-element-horizontal>
 | 
			
		||||
 | 
			
		||||
                <ak-form-group .expanded=${true}>
 | 
			
		||||
                    <span slot="header"> ${msg("Protocol settings")} </span>
 | 
			
		||||
@ -160,6 +145,39 @@ export class ApplicationWizardProviderSamlConfiguration extends BaseProviderPane
 | 
			
		||||
                    </div>
 | 
			
		||||
                </ak-form-group>
 | 
			
		||||
 | 
			
		||||
                <ak-form-group>
 | 
			
		||||
                    <span slot="header"> ${msg("Advanced flow settings")} </span>
 | 
			
		||||
                    <ak-form-element-horizontal
 | 
			
		||||
                        name="authenticationFlow"
 | 
			
		||||
                        label=${msg("Authentication flow")}
 | 
			
		||||
                    >
 | 
			
		||||
                        <ak-flow-search
 | 
			
		||||
                            flowType=${FlowsInstancesListDesignationEnum.Authentication}
 | 
			
		||||
                            .currentFlow=${provider?.authenticationFlow}
 | 
			
		||||
                        ></ak-flow-search>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${msg(
 | 
			
		||||
                                "Flow used when a user access this provider and is not authenticated.",
 | 
			
		||||
                            )}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                    <ak-form-element-horizontal
 | 
			
		||||
                        label=${msg("Invalidation flow")}
 | 
			
		||||
                        name="invalidationFlow"
 | 
			
		||||
                        required
 | 
			
		||||
                    >
 | 
			
		||||
                        <ak-flow-search
 | 
			
		||||
                            flowType=${FlowsInstancesListDesignationEnum.Invalidation}
 | 
			
		||||
                            .currentFlow=${provider?.invalidationFlow}
 | 
			
		||||
                            defaultFlowSlug="default-provider-invalidation-flow"
 | 
			
		||||
                            required
 | 
			
		||||
                        ></ak-flow-search>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${msg("Flow used when logging out of this provider.")}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </ak-form-group>
 | 
			
		||||
                <ak-form-group>
 | 
			
		||||
                    <span slot="header"> ${msg("Advanced protocol settings")} </span>
 | 
			
		||||
                    <div slot="body" class="pf-c-form">
 | 
			
		||||
@ -181,52 +199,60 @@ export class ApplicationWizardProviderSamlConfiguration extends BaseProviderPane
 | 
			
		||||
                                )}
 | 
			
		||||
                            </p>
 | 
			
		||||
                        </ak-form-element-horizontal>
 | 
			
		||||
                        ${this.hasSigningKp
 | 
			
		||||
                            ? html` <ak-form-element-horizontal name="signAssertion">
 | 
			
		||||
                                      <label class="pf-c-switch">
 | 
			
		||||
                                          <input
 | 
			
		||||
                                              class="pf-c-switch__input"
 | 
			
		||||
                                              type="checkbox"
 | 
			
		||||
                                              ?checked=${first(provider?.signAssertion, true)}
 | 
			
		||||
                                          />
 | 
			
		||||
                                          <span class="pf-c-switch__toggle">
 | 
			
		||||
                                              <span class="pf-c-switch__toggle-icon">
 | 
			
		||||
                                                  <i class="fas fa-check" aria-hidden="true"></i>
 | 
			
		||||
                        ${
 | 
			
		||||
                            this.hasSigningKp
 | 
			
		||||
                                ? html` <ak-form-element-horizontal name="signAssertion">
 | 
			
		||||
                                          <label class="pf-c-switch">
 | 
			
		||||
                                              <input
 | 
			
		||||
                                                  class="pf-c-switch__input"
 | 
			
		||||
                                                  type="checkbox"
 | 
			
		||||
                                                  ?checked=${first(provider?.signAssertion, true)}
 | 
			
		||||
                                              />
 | 
			
		||||
                                              <span class="pf-c-switch__toggle">
 | 
			
		||||
                                                  <span class="pf-c-switch__toggle-icon">
 | 
			
		||||
                                                      <i
 | 
			
		||||
                                                          class="fas fa-check"
 | 
			
		||||
                                                          aria-hidden="true"
 | 
			
		||||
                                                      ></i>
 | 
			
		||||
                                                  </span>
 | 
			
		||||
                                              </span>
 | 
			
		||||
                                          </span>
 | 
			
		||||
                                          <span class="pf-c-switch__label"
 | 
			
		||||
                                              >${msg("Sign assertions")}</span
 | 
			
		||||
                                          >
 | 
			
		||||
                                      </label>
 | 
			
		||||
                                      <p class="pf-c-form__helper-text">
 | 
			
		||||
                                          ${msg(
 | 
			
		||||
                                              "When enabled, the assertion element of the SAML response will be signed.",
 | 
			
		||||
                                          )}
 | 
			
		||||
                                      </p>
 | 
			
		||||
                                  </ak-form-element-horizontal>
 | 
			
		||||
                                  <ak-form-element-horizontal name="signResponse">
 | 
			
		||||
                                      <label class="pf-c-switch">
 | 
			
		||||
                                          <input
 | 
			
		||||
                                              class="pf-c-switch__input"
 | 
			
		||||
                                              type="checkbox"
 | 
			
		||||
                                              ?checked=${first(provider?.signResponse, false)}
 | 
			
		||||
                                          />
 | 
			
		||||
                                          <span class="pf-c-switch__toggle">
 | 
			
		||||
                                              <span class="pf-c-switch__toggle-icon">
 | 
			
		||||
                                                  <i class="fas fa-check" aria-hidden="true"></i>
 | 
			
		||||
                                              <span class="pf-c-switch__label"
 | 
			
		||||
                                                  >${msg("Sign assertions")}</span
 | 
			
		||||
                                              >
 | 
			
		||||
                                          </label>
 | 
			
		||||
                                          <p class="pf-c-form__helper-text">
 | 
			
		||||
                                              ${msg(
 | 
			
		||||
                                                  "When enabled, the assertion element of the SAML response will be signed.",
 | 
			
		||||
                                              )}
 | 
			
		||||
                                          </p>
 | 
			
		||||
                                      </ak-form-element-horizontal>
 | 
			
		||||
                                      <ak-form-element-horizontal name="signResponse">
 | 
			
		||||
                                          <label class="pf-c-switch">
 | 
			
		||||
                                              <input
 | 
			
		||||
                                                  class="pf-c-switch__input"
 | 
			
		||||
                                                  type="checkbox"
 | 
			
		||||
                                                  ?checked=${first(provider?.signResponse, false)}
 | 
			
		||||
                                              />
 | 
			
		||||
                                              <span class="pf-c-switch__toggle">
 | 
			
		||||
                                                  <span class="pf-c-switch__toggle-icon">
 | 
			
		||||
                                                      <i
 | 
			
		||||
                                                          class="fas fa-check"
 | 
			
		||||
                                                          aria-hidden="true"
 | 
			
		||||
                                                      ></i>
 | 
			
		||||
                                                  </span>
 | 
			
		||||
                                              </span>
 | 
			
		||||
                                          </span>
 | 
			
		||||
                                          <span class="pf-c-switch__label"
 | 
			
		||||
                                              >${msg("Sign responses")}</span
 | 
			
		||||
                                          >
 | 
			
		||||
                                      </label>
 | 
			
		||||
                                      <p class="pf-c-form__helper-text">
 | 
			
		||||
                                          ${msg(
 | 
			
		||||
                                              "When enabled, the assertion element of the SAML response will be signed.",
 | 
			
		||||
                                          )}
 | 
			
		||||
                                      </p>
 | 
			
		||||
                                  </ak-form-element-horizontal>`
 | 
			
		||||
                            : nothing}
 | 
			
		||||
                                              <span class="pf-c-switch__label"
 | 
			
		||||
                                                  >${msg("Sign responses")}</span
 | 
			
		||||
                                              >
 | 
			
		||||
                                          </label>
 | 
			
		||||
                                          <p class="pf-c-form__helper-text">
 | 
			
		||||
                                              ${msg(
 | 
			
		||||
                                                  "When enabled, the assertion element of the SAML response will be signed.",
 | 
			
		||||
                                              )}
 | 
			
		||||
                                          </p>
 | 
			
		||||
                                      </ak-form-element-horizontal>`
 | 
			
		||||
                                : nothing
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        <ak-form-element-horizontal
 | 
			
		||||
                            label=${msg("Verification Certificate")}
 | 
			
		||||
 | 
			
		||||
@ -61,6 +61,14 @@ export class FlowSearch<T extends Flow> extends CustomListenerElement(AKElement)
 | 
			
		||||
    @query("ak-search-select")
 | 
			
		||||
    search!: SearchSelect<T>;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * When specified and the object instance does not have a flow selected, auto-select the flow with the given slug.
 | 
			
		||||
     *
 | 
			
		||||
     * @attr
 | 
			
		||||
     */
 | 
			
		||||
    @property()
 | 
			
		||||
    defaultFlowSlug?: string;
 | 
			
		||||
 | 
			
		||||
    @property({ type: String })
 | 
			
		||||
    name: string | null | undefined;
 | 
			
		||||
 | 
			
		||||
@ -97,9 +105,12 @@ export class FlowSearch<T extends Flow> extends CustomListenerElement(AKElement)
 | 
			
		||||
     * use this method, but several have more complex needs, such as relating to the brand, or just
 | 
			
		||||
     * returning false.
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    selected(flow: Flow): boolean {
 | 
			
		||||
        return this.currentFlow === flow.pk;
 | 
			
		||||
        let selected = this.currentFlow === flow.pk;
 | 
			
		||||
        if (!this.currentFlow && this.defaultFlowSlug && flow.slug === this.defaultFlowSlug) {
 | 
			
		||||
            selected = true;
 | 
			
		||||
        }
 | 
			
		||||
        return selected;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    connectedCallback() {
 | 
			
		||||
 | 
			
		||||
@ -6,6 +6,7 @@ import "@goauthentik/admin/property-mappings/PropertyMappingProviderRadiusForm";
 | 
			
		||||
import "@goauthentik/admin/property-mappings/PropertyMappingProviderSAMLForm";
 | 
			
		||||
import "@goauthentik/admin/property-mappings/PropertyMappingProviderSCIMForm";
 | 
			
		||||
import "@goauthentik/admin/property-mappings/PropertyMappingProviderScopeForm";
 | 
			
		||||
import "@goauthentik/admin/property-mappings/PropertyMappingSourceKerberosForm";
 | 
			
		||||
import "@goauthentik/admin/property-mappings/PropertyMappingSourceLDAPForm";
 | 
			
		||||
import "@goauthentik/admin/property-mappings/PropertyMappingSourceOAuthForm";
 | 
			
		||||
import "@goauthentik/admin/property-mappings/PropertyMappingSourcePlexForm";
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,40 @@
 | 
			
		||||
import { BasePropertyMappingForm } from "@goauthentik/admin/property-mappings/BasePropertyMappingForm";
 | 
			
		||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
			
		||||
import "@goauthentik/elements/CodeMirror";
 | 
			
		||||
import "@goauthentik/elements/forms/HorizontalFormElement";
 | 
			
		||||
 | 
			
		||||
import { customElement } from "lit/decorators.js";
 | 
			
		||||
 | 
			
		||||
import { KerberosSourcePropertyMapping, PropertymappingsApi } from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
@customElement("ak-property-mapping-source-kerberos-form")
 | 
			
		||||
export class PropertyMappingSourceKerberosForm extends BasePropertyMappingForm<KerberosSourcePropertyMapping> {
 | 
			
		||||
    docLink(): string {
 | 
			
		||||
        return "/docs/sources/property-mappings/expressions?utm_source=authentik";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    loadInstance(pk: string): Promise<KerberosSourcePropertyMapping> {
 | 
			
		||||
        return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsSourceKerberosRetrieve({
 | 
			
		||||
            pmUuid: pk,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async send(data: KerberosSourcePropertyMapping): Promise<KerberosSourcePropertyMapping> {
 | 
			
		||||
        if (this.instance) {
 | 
			
		||||
            return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsSourceKerberosUpdate({
 | 
			
		||||
                pmUuid: this.instance.pk,
 | 
			
		||||
                kerberosSourcePropertyMappingRequest: data,
 | 
			
		||||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
            return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsSourceKerberosCreate({
 | 
			
		||||
                kerberosSourcePropertyMappingRequest: data,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
    interface HTMLElementTagNameMap {
 | 
			
		||||
        "ak-property-mapping-source-kerberos-form": PropertyMappingSourceKerberosForm;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -6,6 +6,7 @@ import "@goauthentik/admin/property-mappings/PropertyMappingProviderRadiusForm";
 | 
			
		||||
import "@goauthentik/admin/property-mappings/PropertyMappingProviderSAMLForm";
 | 
			
		||||
import "@goauthentik/admin/property-mappings/PropertyMappingProviderSCIMForm";
 | 
			
		||||
import "@goauthentik/admin/property-mappings/PropertyMappingProviderScopeForm";
 | 
			
		||||
import "@goauthentik/admin/property-mappings/PropertyMappingSourceKerberosForm";
 | 
			
		||||
import "@goauthentik/admin/property-mappings/PropertyMappingSourceLDAPForm";
 | 
			
		||||
import "@goauthentik/admin/property-mappings/PropertyMappingSourceOAuthForm";
 | 
			
		||||
import "@goauthentik/admin/property-mappings/PropertyMappingSourcePlexForm";
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,12 @@ import { msg } from "@lit/localize";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators.js";
 | 
			
		||||
 | 
			
		||||
import { GoogleWorkspaceProviderGroup, ProvidersApi, SyncObjectModelEnum } from "@goauthentik/api";
 | 
			
		||||
import {
 | 
			
		||||
    GoogleWorkspaceProviderGroup,
 | 
			
		||||
    ProvidersApi,
 | 
			
		||||
    ProvidersGoogleWorkspaceSyncObjectCreateRequest,
 | 
			
		||||
    SyncObjectModelEnum,
 | 
			
		||||
} from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
@customElement("ak-provider-google-workspace-groups-list")
 | 
			
		||||
export class GoogleWorkspaceProviderGroupList extends Table<GoogleWorkspaceProviderGroup> {
 | 
			
		||||
@ -31,8 +36,11 @@ export class GoogleWorkspaceProviderGroupList extends Table<GoogleWorkspaceProvi
 | 
			
		||||
                <ak-sync-object-form
 | 
			
		||||
                    .provider=${this.providerId}
 | 
			
		||||
                    model=${SyncObjectModelEnum.Group}
 | 
			
		||||
                    .sync=${new ProvidersApi(DEFAULT_CONFIG)
 | 
			
		||||
                        .providersGoogleWorkspaceSyncObjectCreate}
 | 
			
		||||
                    .sync=${(data: ProvidersGoogleWorkspaceSyncObjectCreateRequest) => {
 | 
			
		||||
                        return new ProvidersApi(
 | 
			
		||||
                            DEFAULT_CONFIG,
 | 
			
		||||
                        ).providersGoogleWorkspaceSyncObjectCreate(data);
 | 
			
		||||
                    }}
 | 
			
		||||
                    slot="form"
 | 
			
		||||
                >
 | 
			
		||||
                </ak-sync-object-form>
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,12 @@ import { msg } from "@lit/localize";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators.js";
 | 
			
		||||
 | 
			
		||||
import { GoogleWorkspaceProviderUser, ProvidersApi, SyncObjectModelEnum } from "@goauthentik/api";
 | 
			
		||||
import {
 | 
			
		||||
    GoogleWorkspaceProviderUser,
 | 
			
		||||
    ProvidersApi,
 | 
			
		||||
    ProvidersGoogleWorkspaceSyncObjectCreateRequest,
 | 
			
		||||
    SyncObjectModelEnum,
 | 
			
		||||
} from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
@customElement("ak-provider-google-workspace-users-list")
 | 
			
		||||
export class GoogleWorkspaceProviderUserList extends Table<GoogleWorkspaceProviderUser> {
 | 
			
		||||
@ -31,8 +36,11 @@ export class GoogleWorkspaceProviderUserList extends Table<GoogleWorkspaceProvid
 | 
			
		||||
                <ak-sync-object-form
 | 
			
		||||
                    .provider=${this.providerId}
 | 
			
		||||
                    model=${SyncObjectModelEnum.User}
 | 
			
		||||
                    .sync=${new ProvidersApi(DEFAULT_CONFIG)
 | 
			
		||||
                        .providersGoogleWorkspaceSyncObjectCreate}
 | 
			
		||||
                    .sync=${(data: ProvidersGoogleWorkspaceSyncObjectCreateRequest) => {
 | 
			
		||||
                        return new ProvidersApi(
 | 
			
		||||
                            DEFAULT_CONFIG,
 | 
			
		||||
                        ).providersGoogleWorkspaceSyncObjectCreate(data);
 | 
			
		||||
                    }}
 | 
			
		||||
                    slot="form"
 | 
			
		||||
                >
 | 
			
		||||
                </ak-sync-object-form>
 | 
			
		||||
 | 
			
		||||
@ -2,24 +2,17 @@ import "@goauthentik/admin/common/ak-crypto-certificate-search";
 | 
			
		||||
import "@goauthentik/admin/common/ak-flow-search/ak-branded-flow-search";
 | 
			
		||||
import { BaseProviderForm } from "@goauthentik/admin/providers/BaseProviderForm";
 | 
			
		||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
			
		||||
import { first } from "@goauthentik/common/utils";
 | 
			
		||||
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
 | 
			
		||||
import "@goauthentik/elements/forms/FormGroup";
 | 
			
		||||
import "@goauthentik/elements/forms/HorizontalFormElement";
 | 
			
		||||
import "@goauthentik/elements/forms/Radio";
 | 
			
		||||
import "@goauthentik/elements/forms/SearchSelect";
 | 
			
		||||
 | 
			
		||||
import { msg } from "@lit/localize";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement } from "lit/decorators.js";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined.js";
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    FlowsInstancesListDesignationEnum,
 | 
			
		||||
    LDAPAPIAccessMode,
 | 
			
		||||
    LDAPProvider,
 | 
			
		||||
    ProvidersApi,
 | 
			
		||||
} from "@goauthentik/api";
 | 
			
		||||
import { LDAPProvider, ProvidersApi } from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
import { renderForm } from "./LDAPProviderFormForm.js";
 | 
			
		||||
 | 
			
		||||
@customElement("ak-provider-ldap-form")
 | 
			
		||||
export class LDAPProviderFormPage extends WithBrandConfig(BaseProviderForm<LDAPProvider>) {
 | 
			
		||||
@ -42,210 +35,8 @@ export class LDAPProviderFormPage extends WithBrandConfig(BaseProviderForm<LDAPP
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // All Provider objects have an Authorization flow, but not all providers have an Authentication
 | 
			
		||||
    // flow. LDAP needs only one field, but it is not an Authorization field, it is an
 | 
			
		||||
    // Authentication field. So, yeah, we're using the authorization field to store the
 | 
			
		||||
    // authentication information, which is why the ak-branded-flow-search call down there looks so
 | 
			
		||||
    // weird-- we're looking up Authentication flows, but we're storing them in the Authorization
 | 
			
		||||
    // field of the target Provider.
 | 
			
		||||
    renderForm(): TemplateResult {
 | 
			
		||||
        return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
 | 
			
		||||
                <input
 | 
			
		||||
                    type="text"
 | 
			
		||||
                    value="${ifDefined(this.instance?.name)}"
 | 
			
		||||
                    class="pf-c-form-control"
 | 
			
		||||
                    required
 | 
			
		||||
                />
 | 
			
		||||
            </ak-form-element-horizontal>
 | 
			
		||||
            <ak-form-element-horizontal label=${msg("Bind mode")} name="bindMode">
 | 
			
		||||
                <ak-radio
 | 
			
		||||
                    .options=${[
 | 
			
		||||
                        {
 | 
			
		||||
                            label: msg("Cached binding"),
 | 
			
		||||
                            value: LDAPAPIAccessMode.Cached,
 | 
			
		||||
                            default: true,
 | 
			
		||||
                            description: html`${msg(
 | 
			
		||||
                                "Flow is executed and session is cached in memory. Flow is executed when session expires",
 | 
			
		||||
                            )}`,
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            label: msg("Direct binding"),
 | 
			
		||||
                            value: LDAPAPIAccessMode.Direct,
 | 
			
		||||
                            description: html`${msg(
 | 
			
		||||
                                "Always execute the configured bind flow to authenticate the user",
 | 
			
		||||
                            )}`,
 | 
			
		||||
                        },
 | 
			
		||||
                    ]}
 | 
			
		||||
                    .value=${this.instance?.bindMode}
 | 
			
		||||
                >
 | 
			
		||||
                </ak-radio>
 | 
			
		||||
                <p class="pf-c-form__helper-text">
 | 
			
		||||
                    ${msg("Configure how the outpost authenticates requests.")}
 | 
			
		||||
                </p>
 | 
			
		||||
            </ak-form-element-horizontal>
 | 
			
		||||
            <ak-form-element-horizontal label=${msg("Search mode")} name="searchMode">
 | 
			
		||||
                <ak-radio
 | 
			
		||||
                    .options=${[
 | 
			
		||||
                        {
 | 
			
		||||
                            label: msg("Cached querying"),
 | 
			
		||||
                            value: LDAPAPIAccessMode.Cached,
 | 
			
		||||
                            default: true,
 | 
			
		||||
                            description: html`${msg(
 | 
			
		||||
                                "The outpost holds all users and groups in-memory and will refresh every 5 Minutes",
 | 
			
		||||
                            )}`,
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            label: msg("Direct querying"),
 | 
			
		||||
                            value: LDAPAPIAccessMode.Direct,
 | 
			
		||||
                            description: html`${msg(
 | 
			
		||||
                                "Always returns the latest data, but slower than cached querying",
 | 
			
		||||
                            )}`,
 | 
			
		||||
                        },
 | 
			
		||||
                    ]}
 | 
			
		||||
                    .value=${this.instance?.searchMode}
 | 
			
		||||
                >
 | 
			
		||||
                </ak-radio>
 | 
			
		||||
                <p class="pf-c-form__helper-text">
 | 
			
		||||
                    ${msg("Configure how the outpost queries the core authentik server's users.")}
 | 
			
		||||
                </p>
 | 
			
		||||
            </ak-form-element-horizontal>
 | 
			
		||||
            <ak-form-element-horizontal name="mfaSupport">
 | 
			
		||||
                <label class="pf-c-switch">
 | 
			
		||||
                    <input
 | 
			
		||||
                        class="pf-c-switch__input"
 | 
			
		||||
                        type="checkbox"
 | 
			
		||||
                        ?checked=${first(this.instance?.mfaSupport, true)}
 | 
			
		||||
                    />
 | 
			
		||||
                    <span class="pf-c-switch__toggle">
 | 
			
		||||
                        <span class="pf-c-switch__toggle-icon">
 | 
			
		||||
                            <i class="fas fa-check" aria-hidden="true"></i>
 | 
			
		||||
                        </span>
 | 
			
		||||
                    </span>
 | 
			
		||||
                    <span class="pf-c-switch__label">${msg("Code-based MFA Support")}</span>
 | 
			
		||||
                </label>
 | 
			
		||||
                <p class="pf-c-form__helper-text">
 | 
			
		||||
                    ${msg(
 | 
			
		||||
                        "When enabled, code-based multi-factor authentication can be used by appending a semicolon and the TOTP code to the password. This should only be enabled if all users that will bind to this provider have a TOTP device configured, as otherwise a password may incorrectly be rejected if it contains a semicolon.",
 | 
			
		||||
                    )}
 | 
			
		||||
                </p>
 | 
			
		||||
            </ak-form-element-horizontal>
 | 
			
		||||
 | 
			
		||||
            <ak-form-group>
 | 
			
		||||
                <span slot="header"> ${msg("Flow settings")} </span>
 | 
			
		||||
                <div slot="body" class="pf-c-form">
 | 
			
		||||
                    <ak-form-element-horizontal
 | 
			
		||||
                        label=${msg("Bind flow")}
 | 
			
		||||
                        name="authorizationFlow"
 | 
			
		||||
                        required
 | 
			
		||||
                    >
 | 
			
		||||
                        <ak-branded-flow-search
 | 
			
		||||
                            flowType=${FlowsInstancesListDesignationEnum.Authentication}
 | 
			
		||||
                            .currentFlow=${this.instance?.authorizationFlow}
 | 
			
		||||
                            .brandFlow=${this.brand?.flowAuthentication}
 | 
			
		||||
                            required
 | 
			
		||||
                        ></ak-branded-flow-search>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${msg("Flow used for users to authenticate.")}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                    <ak-form-element-horizontal
 | 
			
		||||
                        label=${msg("Unbind flow")}
 | 
			
		||||
                        name="invalidationFlow"
 | 
			
		||||
                        required
 | 
			
		||||
                    >
 | 
			
		||||
                        <ak-flow-search
 | 
			
		||||
                            flowType=${FlowsInstancesListDesignationEnum.Invalidation}
 | 
			
		||||
                            .currentFlow=${this.instance?.invalidationFlow}
 | 
			
		||||
                            required
 | 
			
		||||
                        ></ak-flow-search>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${msg("Flow used for unbinding users.")}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                </div>
 | 
			
		||||
            </ak-form-group>
 | 
			
		||||
            <ak-form-group .expanded=${true}>
 | 
			
		||||
                <span slot="header"> ${msg("Protocol settings")} </span>
 | 
			
		||||
                <div slot="body" class="pf-c-form">
 | 
			
		||||
                    <ak-form-element-horizontal
 | 
			
		||||
                        label=${msg("Base DN")}
 | 
			
		||||
                        ?required=${true}
 | 
			
		||||
                        name="baseDn"
 | 
			
		||||
                    >
 | 
			
		||||
                        <input
 | 
			
		||||
                            type="text"
 | 
			
		||||
                            value="${first(this.instance?.baseDn, "DC=ldap,DC=goauthentik,DC=io")}"
 | 
			
		||||
                            class="pf-c-form-control"
 | 
			
		||||
                            required
 | 
			
		||||
                        />
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${msg(
 | 
			
		||||
                                "LDAP DN under which bind requests and search requests can be made.",
 | 
			
		||||
                            )}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                    <ak-form-element-horizontal label=${msg("Certificate")} name="certificate">
 | 
			
		||||
                        <ak-crypto-certificate-search
 | 
			
		||||
                            .certificate=${this.instance?.certificate}
 | 
			
		||||
                        ></ak-crypto-certificate-search>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${msg(
 | 
			
		||||
                                "The certificate for the above configured Base DN. As a fallback, the provider uses a self-signed certificate.",
 | 
			
		||||
                            )}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                    <ak-form-element-horizontal
 | 
			
		||||
                        label=${msg("TLS Server name")}
 | 
			
		||||
                        name="tlsServerName"
 | 
			
		||||
                    >
 | 
			
		||||
                        <input
 | 
			
		||||
                            type="text"
 | 
			
		||||
                            value="${first(this.instance?.tlsServerName, "")}"
 | 
			
		||||
                            class="pf-c-form-control"
 | 
			
		||||
                            required
 | 
			
		||||
                        />
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${msg(
 | 
			
		||||
                                "DNS name for which the above configured certificate should be used. The certificate cannot be detected based on the base DN, as the SSL/TLS negotiation happens before such data is exchanged.",
 | 
			
		||||
                            )}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                    <ak-form-element-horizontal
 | 
			
		||||
                        label=${msg("UID start number")}
 | 
			
		||||
                        ?required=${true}
 | 
			
		||||
                        name="uidStartNumber"
 | 
			
		||||
                    >
 | 
			
		||||
                        <input
 | 
			
		||||
                            type="number"
 | 
			
		||||
                            value="${first(this.instance?.uidStartNumber, 2000)}"
 | 
			
		||||
                            class="pf-c-form-control"
 | 
			
		||||
                            required
 | 
			
		||||
                        />
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${msg(
 | 
			
		||||
                                "The start for uidNumbers, this number is added to the user.Pk to make sure that the numbers aren't too low for POSIX users. Default is 2000 to ensure that we don't collide with local users uidNumber",
 | 
			
		||||
                            )}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                    <ak-form-element-horizontal
 | 
			
		||||
                        label=${msg("GID start number")}
 | 
			
		||||
                        ?required=${true}
 | 
			
		||||
                        name="gidStartNumber"
 | 
			
		||||
                    >
 | 
			
		||||
                        <input
 | 
			
		||||
                            type="number"
 | 
			
		||||
                            value="${first(this.instance?.gidStartNumber, 4000)}"
 | 
			
		||||
                            class="pf-c-form-control"
 | 
			
		||||
                            required
 | 
			
		||||
                        />
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${msg(
 | 
			
		||||
                                "The start for gidNumbers, this number is added to a number generated from the group.Pk to make sure that the numbers aren't too low for POSIX groups. Default is 4000 to ensure that we don't collide with local groups or users primary groups gidNumber",
 | 
			
		||||
                            )}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                </div>
 | 
			
		||||
            </ak-form-group>`;
 | 
			
		||||
    renderForm() {
 | 
			
		||||
        return renderForm(this.instance ?? {}, [], this.brand);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										176
									
								
								web/src/admin/providers/ldap/LDAPProviderFormForm.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								web/src/admin/providers/ldap/LDAPProviderFormForm.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,176 @@
 | 
			
		||||
import "@goauthentik/admin/common/ak-crypto-certificate-search";
 | 
			
		||||
import "@goauthentik/admin/common/ak-flow-search/ak-branded-flow-search";
 | 
			
		||||
import "@goauthentik/admin/common/ak-flow-search/ak-flow-search";
 | 
			
		||||
import "@goauthentik/components/ak-radio-input";
 | 
			
		||||
import "@goauthentik/components/ak-text-input";
 | 
			
		||||
import "@goauthentik/components/ak-textarea-input";
 | 
			
		||||
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
 | 
			
		||||
import "@goauthentik/elements/ak-dual-select/ak-dual-select-provider.js";
 | 
			
		||||
import "@goauthentik/elements/forms/FormGroup";
 | 
			
		||||
import "@goauthentik/elements/forms/HorizontalFormElement";
 | 
			
		||||
import "@goauthentik/elements/forms/Radio";
 | 
			
		||||
import "@goauthentik/elements/forms/SearchSelect";
 | 
			
		||||
import "@goauthentik/elements/utils/TimeDeltaHelp";
 | 
			
		||||
 | 
			
		||||
import { msg } from "@lit/localize";
 | 
			
		||||
import { html, nothing } from "lit";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined.js";
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    CurrentBrand,
 | 
			
		||||
    FlowsInstancesListDesignationEnum,
 | 
			
		||||
    LDAPProvider,
 | 
			
		||||
    ValidationError,
 | 
			
		||||
} from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    bindModeOptions,
 | 
			
		||||
    cryptoCertificateHelp,
 | 
			
		||||
    gidStartNumberHelp,
 | 
			
		||||
    mfaSupportHelp,
 | 
			
		||||
    searchModeOptions,
 | 
			
		||||
    tlsServerNameHelp,
 | 
			
		||||
    uidStartNumberHelp,
 | 
			
		||||
} from "./LDAPOptionsAndHelp.js";
 | 
			
		||||
 | 
			
		||||
// All Provider objects have an Authorization flow, but not all providers have an Authentication
 | 
			
		||||
// flow. LDAP needs only one field, but it is not an Authorization field, it is an Authentication
 | 
			
		||||
// field. So, yeah, we're using the authorization field to store the authentication information,
 | 
			
		||||
// which is why the ak-branded-flow-search call down there looks so weird-- we're looking up
 | 
			
		||||
// Authentication flows, but we're storing them in the Authorization field of the target Provider.
 | 
			
		||||
 | 
			
		||||
export function renderForm(
 | 
			
		||||
    provider?: Partial<LDAPProvider>,
 | 
			
		||||
    errors: ValidationError = {},
 | 
			
		||||
    brand?: CurrentBrand,
 | 
			
		||||
) {
 | 
			
		||||
    return html`
 | 
			
		||||
        <ak-text-input
 | 
			
		||||
            name="name"
 | 
			
		||||
            value=${ifDefined(provider?.name)}
 | 
			
		||||
            label=${msg("Name")}
 | 
			
		||||
            .errorMessages=${errors?.name ?? []}
 | 
			
		||||
            required
 | 
			
		||||
            help=${msg("Method's display Name.")}
 | 
			
		||||
        ></ak-text-input>
 | 
			
		||||
        <ak-radio-input
 | 
			
		||||
            label=${msg("Bind mode")}
 | 
			
		||||
            name="bindMode"
 | 
			
		||||
            .options=${bindModeOptions}
 | 
			
		||||
            .value=${provider?.bindMode}
 | 
			
		||||
            help=${msg("Configure how the outpost authenticates requests.")}
 | 
			
		||||
        >
 | 
			
		||||
        </ak-radio-input>
 | 
			
		||||
 | 
			
		||||
        <ak-radio-input
 | 
			
		||||
            label=${msg("Search mode")}
 | 
			
		||||
            name="searchMode"
 | 
			
		||||
            .options=${searchModeOptions}
 | 
			
		||||
            .value=${provider?.searchMode}
 | 
			
		||||
            help=${msg("Configure how the outpost queries the core authentik server's users.")}
 | 
			
		||||
        >
 | 
			
		||||
        </ak-radio-input>
 | 
			
		||||
 | 
			
		||||
        <ak-switch-input
 | 
			
		||||
            name="openInNewTab"
 | 
			
		||||
            label=${msg("Code-based MFA Support")}
 | 
			
		||||
            ?checked=${provider?.mfaSupport ?? true}
 | 
			
		||||
            help=${mfaSupportHelp}
 | 
			
		||||
        >
 | 
			
		||||
        </ak-switch-input>
 | 
			
		||||
 | 
			
		||||
        <ak-form-group expanded>
 | 
			
		||||
            <span slot="header"> ${msg("Flow settings")} </span>
 | 
			
		||||
 | 
			
		||||
            <div slot="body" class="pf-c-form">
 | 
			
		||||
                <ak-form-element-horizontal
 | 
			
		||||
                    label=${msg("Bind flow")}
 | 
			
		||||
                    ?required=${true}
 | 
			
		||||
                    name="authorizationFlow"
 | 
			
		||||
                    .errorMessages=${errors?.authorizationFlow ?? []}
 | 
			
		||||
                >
 | 
			
		||||
                    <ak-branded-flow-search
 | 
			
		||||
                        flowType=${FlowsInstancesListDesignationEnum.Authentication}
 | 
			
		||||
                        .currentFlow=${provider?.authorizationFlow}
 | 
			
		||||
                        .brandFlow=${brand?.flowAuthentication}
 | 
			
		||||
                        required
 | 
			
		||||
                    ></ak-branded-flow-search>
 | 
			
		||||
                    <p class="pf-c-form__helper-text">
 | 
			
		||||
                        ${msg("Flow used for users to authenticate.")}
 | 
			
		||||
                    </p>
 | 
			
		||||
                </ak-form-element-horizontal>
 | 
			
		||||
 | 
			
		||||
                <ak-form-element-horizontal
 | 
			
		||||
                    label=${msg("Unbind flow")}
 | 
			
		||||
                    name="invalidationFlow"
 | 
			
		||||
                    required
 | 
			
		||||
                >
 | 
			
		||||
                    <ak-branded-flow-search
 | 
			
		||||
                        flowType=${FlowsInstancesListDesignationEnum.Invalidation}
 | 
			
		||||
                        .currentFlow=${provider?.invalidationFlow}
 | 
			
		||||
                        .brandFlow=${brand.flowInvalidation}
 | 
			
		||||
                        .errorMessages=${errors?.invalidationFlow ?? []}
 | 
			
		||||
                        required
 | 
			
		||||
                    ></ak-branded-flow-search>
 | 
			
		||||
                    <p class="pf-c-form__helper-text">${msg("Flow used for unbinding users.")}</p>
 | 
			
		||||
                </ak-form-element-horizontal>
 | 
			
		||||
            </div>
 | 
			
		||||
        </ak-form-group>
 | 
			
		||||
 | 
			
		||||
        <ak-form-group expanded>
 | 
			
		||||
            <span slot="header"> ${msg("Protocol settings")} </span>
 | 
			
		||||
            <div slot="body" class="pf-c-form">
 | 
			
		||||
                <ak-text-input
 | 
			
		||||
                    name="baseDn"
 | 
			
		||||
                    label=${msg("Base DN")}
 | 
			
		||||
                    required
 | 
			
		||||
                    value="${provider?.baseDn ?? "DC=ldap,DC=goauthentik,DC=io"}"
 | 
			
		||||
                    .errorMessages=${errors?.baseDn ?? []}
 | 
			
		||||
                    help=${msg(
 | 
			
		||||
                        "LDAP DN under which bind requests and search requests can be made.",
 | 
			
		||||
                    )}
 | 
			
		||||
                >
 | 
			
		||||
                </ak-text-input>
 | 
			
		||||
 | 
			
		||||
                <ak-form-element-horizontal
 | 
			
		||||
                    label=${msg("Certificate")}
 | 
			
		||||
                    name="certificate"
 | 
			
		||||
                    .errorMessages=${errors?.certificate ?? []}
 | 
			
		||||
                >
 | 
			
		||||
                    <ak-crypto-certificate-search
 | 
			
		||||
                        certificate=${ifDefined(provider?.certificate ?? nothing)}
 | 
			
		||||
                        name="certificate"
 | 
			
		||||
                    >
 | 
			
		||||
                    </ak-crypto-certificate-search>
 | 
			
		||||
                    <p class="pf-c-form__helper-text">${cryptoCertificateHelp}</p>
 | 
			
		||||
                </ak-form-element-horizontal>
 | 
			
		||||
 | 
			
		||||
                <ak-text-input
 | 
			
		||||
                    label=${msg("TLS Server name")}
 | 
			
		||||
                    name="tlsServerName"
 | 
			
		||||
                    value="${provider?.tlsServerName ?? ""}"
 | 
			
		||||
                    .errorMessages=${errors?.tlsServerName ?? []}
 | 
			
		||||
                    help=${tlsServerNameHelp}
 | 
			
		||||
                ></ak-text-input>
 | 
			
		||||
 | 
			
		||||
                <ak-number-input
 | 
			
		||||
                    label=${msg("UID start number")}
 | 
			
		||||
                    required
 | 
			
		||||
                    name="uidStartNumber"
 | 
			
		||||
                    value="${provider?.uidStartNumber ?? 2000}"
 | 
			
		||||
                    .errorMessages=${errors?.uidStartNumber ?? []}
 | 
			
		||||
                    help=${uidStartNumberHelp}
 | 
			
		||||
                ></ak-number-input>
 | 
			
		||||
 | 
			
		||||
                <ak-number-input
 | 
			
		||||
                    label=${msg("GID start number")}
 | 
			
		||||
                    required
 | 
			
		||||
                    name="gidStartNumber"
 | 
			
		||||
                    value="${provider?.gidStartNumber ?? 4000}"
 | 
			
		||||
                    .errorMessages=${errors?.gidStartNumber ?? []}
 | 
			
		||||
                    help=${gidStartNumberHelp}
 | 
			
		||||
                ></ak-number-input>
 | 
			
		||||
            </div>
 | 
			
		||||
        </ak-form-group>
 | 
			
		||||
    `;
 | 
			
		||||
}
 | 
			
		||||
@ -8,7 +8,12 @@ import { msg } from "@lit/localize";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators.js";
 | 
			
		||||
 | 
			
		||||
import { MicrosoftEntraProviderGroup, ProvidersApi, SyncObjectModelEnum } from "@goauthentik/api";
 | 
			
		||||
import {
 | 
			
		||||
    MicrosoftEntraProviderGroup,
 | 
			
		||||
    ProvidersApi,
 | 
			
		||||
    ProvidersMicrosoftEntraSyncObjectCreateRequest,
 | 
			
		||||
    SyncObjectModelEnum,
 | 
			
		||||
} from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
@customElement("ak-provider-microsoft-entra-groups-list")
 | 
			
		||||
export class MicrosoftEntraProviderGroupList extends Table<MicrosoftEntraProviderGroup> {
 | 
			
		||||
@ -28,8 +33,11 @@ export class MicrosoftEntraProviderGroupList extends Table<MicrosoftEntraProvide
 | 
			
		||||
                <ak-sync-object-form
 | 
			
		||||
                    .provider=${this.providerId}
 | 
			
		||||
                    model=${SyncObjectModelEnum.Group}
 | 
			
		||||
                    .sync=${new ProvidersApi(DEFAULT_CONFIG)
 | 
			
		||||
                        .providersMicrosoftEntraSyncObjectCreate}
 | 
			
		||||
                    .sync=${(data: ProvidersMicrosoftEntraSyncObjectCreateRequest) => {
 | 
			
		||||
                        return new ProvidersApi(
 | 
			
		||||
                            DEFAULT_CONFIG,
 | 
			
		||||
                        ).providersMicrosoftEntraSyncObjectCreate(data);
 | 
			
		||||
                    }}
 | 
			
		||||
                    slot="form"
 | 
			
		||||
                >
 | 
			
		||||
                </ak-sync-object-form>
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,12 @@ import { msg } from "@lit/localize";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators.js";
 | 
			
		||||
 | 
			
		||||
import { MicrosoftEntraProviderUser, ProvidersApi, SyncObjectModelEnum } from "@goauthentik/api";
 | 
			
		||||
import {
 | 
			
		||||
    MicrosoftEntraProviderUser,
 | 
			
		||||
    ProvidersApi,
 | 
			
		||||
    ProvidersMicrosoftEntraSyncObjectCreateRequest,
 | 
			
		||||
    SyncObjectModelEnum,
 | 
			
		||||
} from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
@customElement("ak-provider-microsoft-entra-users-list")
 | 
			
		||||
export class MicrosoftEntraProviderUserList extends Table<MicrosoftEntraProviderUser> {
 | 
			
		||||
@ -31,8 +36,11 @@ export class MicrosoftEntraProviderUserList extends Table<MicrosoftEntraProvider
 | 
			
		||||
                <ak-sync-object-form
 | 
			
		||||
                    .provider=${this.providerId}
 | 
			
		||||
                    model=${SyncObjectModelEnum.User}
 | 
			
		||||
                    .sync=${new ProvidersApi(DEFAULT_CONFIG)
 | 
			
		||||
                        .providersMicrosoftEntraSyncObjectCreate}
 | 
			
		||||
                    .sync=${(data: ProvidersMicrosoftEntraSyncObjectCreateRequest) => {
 | 
			
		||||
                        return new ProvidersApi(
 | 
			
		||||
                            DEFAULT_CONFIG,
 | 
			
		||||
                        ).providersMicrosoftEntraSyncObjectCreate(data);
 | 
			
		||||
                    }}
 | 
			
		||||
                    slot="form"
 | 
			
		||||
                >
 | 
			
		||||
                </ak-sync-object-form>
 | 
			
		||||
 | 
			
		||||
@ -122,10 +122,23 @@ export function renderForm(
 | 
			
		||||
            name="name"
 | 
			
		||||
            label=${msg("Name")}
 | 
			
		||||
            value=${ifDefined(provider?.name)}
 | 
			
		||||
            .errorMessages=${errors?.name ?? []}
 | 
			
		||||
            required
 | 
			
		||||
        ></ak-text-input>
 | 
			
		||||
 | 
			
		||||
        <ak-form-element-horizontal
 | 
			
		||||
            name="authorizationFlow"
 | 
			
		||||
            label=${msg("Authorization flow")}
 | 
			
		||||
            ?required=${true}
 | 
			
		||||
        >
 | 
			
		||||
            <ak-flow-search
 | 
			
		||||
                flowType=${FlowsInstancesListDesignationEnum.Authorization}
 | 
			
		||||
                .currentFlow=${provider?.authorizationFlow}
 | 
			
		||||
                required
 | 
			
		||||
            ></ak-flow-search>
 | 
			
		||||
            <p class="pf-c-form__helper-text">
 | 
			
		||||
                ${msg("Flow used when authorizing this provider.")}
 | 
			
		||||
            </p>
 | 
			
		||||
        </ak-form-element-horizontal>
 | 
			
		||||
        <ak-form-group expanded>
 | 
			
		||||
            <span slot="header"> ${msg("Protocol settings")} </span>
 | 
			
		||||
            <div slot="body" class="pf-c-form">
 | 
			
		||||
@ -144,7 +157,6 @@ export function renderForm(
 | 
			
		||||
                    name="clientId"
 | 
			
		||||
                    label=${msg("Client ID")}
 | 
			
		||||
                    value="${first(provider?.clientId, randomString(40, ascii_letters + digits))}"
 | 
			
		||||
                    .errorMessages=${errors?.clientId ?? []}
 | 
			
		||||
                    required
 | 
			
		||||
                >
 | 
			
		||||
                </ak-text-input>
 | 
			
		||||
@ -156,26 +168,20 @@ export function renderForm(
 | 
			
		||||
                        randomString(128, ascii_letters + digits),
 | 
			
		||||
                    )}"
 | 
			
		||||
                    ?hidden=${!showClientSecret}
 | 
			
		||||
                    .errorMessages=${errors?.clientSecret ?? []}
 | 
			
		||||
                >
 | 
			
		||||
                </ak-text-input>
 | 
			
		||||
                <ak-textarea-input
 | 
			
		||||
                    name="redirectUris"
 | 
			
		||||
                    label=${msg("Redirect URIs/Origins (RegEx)")}
 | 
			
		||||
                    .value=${provider?.redirectUris}
 | 
			
		||||
                    .errorMessages=${errors?.redirectUriHelp ?? []}
 | 
			
		||||
                    .bighelp=${redirectUriHelp}
 | 
			
		||||
                >
 | 
			
		||||
                </ak-textarea-input>
 | 
			
		||||
 | 
			
		||||
                <ak-form-element-horizontal
 | 
			
		||||
                    label=${msg("Signing Key")}
 | 
			
		||||
                    name="signingKey"
 | 
			
		||||
                    .errorMessages=${errors?.signingKey ?? []}
 | 
			
		||||
                >
 | 
			
		||||
                <ak-form-element-horizontal label=${msg("Signing Key")} name="signingKey">
 | 
			
		||||
                    <!-- NOTE: 'null' cast to 'undefined' on signingKey to satisfy Lit requirements -->
 | 
			
		||||
                    <ak-crypto-certificate-search
 | 
			
		||||
                        certificate=${ifDefined(provider.signingKey ?? undefined)}
 | 
			
		||||
                        certificate=${ifDefined(provider?.signingKey ?? undefined)}
 | 
			
		||||
                        singleton
 | 
			
		||||
                    ></ak-crypto-certificate-search>
 | 
			
		||||
                    <p class="pf-c-form__helper-text">${msg("Key used to sign the tokens.")}</p>
 | 
			
		||||
@ -183,7 +189,7 @@ export function renderForm(
 | 
			
		||||
                <ak-form-element-horizontal label=${msg("Encryption Key")} name="encryptionKey">
 | 
			
		||||
                    <!-- NOTE: 'null' cast to 'undefined' on encryptionKey to satisfy Lit requirements -->
 | 
			
		||||
                    <ak-crypto-certificate-search
 | 
			
		||||
                        certificate=${ifDefined(provider.encryptionKey ?? undefined)}
 | 
			
		||||
                        certificate=${ifDefined(provider?.encryptionKey ?? undefined)}
 | 
			
		||||
                    ></ak-crypto-certificate-search>
 | 
			
		||||
                    <p class="pf-c-form__helper-text">${msg("Key used to encrypt the tokens.")}</p>
 | 
			
		||||
                </ak-form-element-horizontal>
 | 
			
		||||
@ -191,7 +197,7 @@ export function renderForm(
 | 
			
		||||
        </ak-form-group>
 | 
			
		||||
 | 
			
		||||
        <ak-form-group>
 | 
			
		||||
            <span slot="header"> ${msg("Flow settings")} </span>
 | 
			
		||||
            <span slot="header"> ${msg("Advanced flow settings")} </span>
 | 
			
		||||
            <div slot="body" class="pf-c-form">
 | 
			
		||||
                <ak-form-element-horizontal
 | 
			
		||||
                    name="authenticationFlow"
 | 
			
		||||
@ -207,30 +213,15 @@ export function renderForm(
 | 
			
		||||
                        )}
 | 
			
		||||
                    </p>
 | 
			
		||||
                </ak-form-element-horizontal>
 | 
			
		||||
                <ak-form-element-horizontal
 | 
			
		||||
                    name="authorizationFlow"
 | 
			
		||||
                    label=${msg("Authorization flow")}
 | 
			
		||||
                    .errorMessages=${errors?.authorizationFlow ?? []}
 | 
			
		||||
                    ?required=${true}
 | 
			
		||||
                >
 | 
			
		||||
                    <ak-flow-search
 | 
			
		||||
                        flowType=${FlowsInstancesListDesignationEnum.Authorization}
 | 
			
		||||
                        .currentFlow=${provider?.authorizationFlow}
 | 
			
		||||
                        required
 | 
			
		||||
                    ></ak-flow-search>
 | 
			
		||||
                    <p class="pf-c-form__helper-text">
 | 
			
		||||
                        ${msg("Flow used when authorizing this provider.")}
 | 
			
		||||
                    </p>
 | 
			
		||||
                </ak-form-element-horizontal>
 | 
			
		||||
                <ak-form-element-horizontal
 | 
			
		||||
                    label=${msg("Invalidation flow")}
 | 
			
		||||
                    name="invalidationFlow"
 | 
			
		||||
                    .errorMessages=${errors?.invalidationFlow ?? []}
 | 
			
		||||
                    required
 | 
			
		||||
                >
 | 
			
		||||
                    <ak-flow-search
 | 
			
		||||
                        flowType=${FlowsInstancesListDesignationEnum.Invalidation}
 | 
			
		||||
                        .currentFlow=${provider?.invalidationFlow}
 | 
			
		||||
                        defaultFlowSlug="default-provider-invalidation-flow"
 | 
			
		||||
                        required
 | 
			
		||||
                    ></ak-flow-search>
 | 
			
		||||
                    <p class="pf-c-form__helper-text">
 | 
			
		||||
@ -248,7 +239,6 @@ export function renderForm(
 | 
			
		||||
                    label=${msg("Access code validity")}
 | 
			
		||||
                    required
 | 
			
		||||
                    value="${first(provider?.accessCodeValidity, "minutes=1")}"
 | 
			
		||||
                    .errorMessages=${errors?.accessCodeValidity ?? []}
 | 
			
		||||
                    .bighelp=${html`<p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${msg("Configure how long access codes are valid for.")}
 | 
			
		||||
                        </p>
 | 
			
		||||
@ -260,7 +250,6 @@ export function renderForm(
 | 
			
		||||
                    label=${msg("Access Token validity")}
 | 
			
		||||
                    value="${first(provider?.accessTokenValidity, "minutes=5")}"
 | 
			
		||||
                    required
 | 
			
		||||
                    .errorMessages=${errors?.accessTokenValidity ?? []}
 | 
			
		||||
                    .bighelp=${html` <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${msg("Configure how long access tokens are valid for.")}
 | 
			
		||||
                        </p>
 | 
			
		||||
@ -272,19 +261,14 @@ export function renderForm(
 | 
			
		||||
                    name="refreshTokenValidity"
 | 
			
		||||
                    label=${msg("Refresh Token validity")}
 | 
			
		||||
                    value="${first(provider?.refreshTokenValidity, "days=30")}"
 | 
			
		||||
                    required
 | 
			
		||||
                    .errorMessages=${errors?.refreshTokenValidity ?? []}
 | 
			
		||||
                    ?required=${true}
 | 
			
		||||
                    .bighelp=${html` <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${msg("Configure how long refresh tokens are valid for.")}
 | 
			
		||||
                        </p>
 | 
			
		||||
                        <ak-utils-time-delta-help></ak-utils-time-delta-help>`}
 | 
			
		||||
                >
 | 
			
		||||
                </ak-text-input>
 | 
			
		||||
                <ak-form-element-horizontal
 | 
			
		||||
                    label=${msg("Scopes")}
 | 
			
		||||
                    name="propertyMappings"
 | 
			
		||||
                    .errorMessages=${errors?.propertyMappings ?? []}
 | 
			
		||||
                >
 | 
			
		||||
                <ak-form-element-horizontal label=${msg("Scopes")} name="propertyMappings">
 | 
			
		||||
                    <ak-dual-select-dynamic-selected
 | 
			
		||||
                        .provider=${oauth2PropertyMappingsProvider}
 | 
			
		||||
                        .selector=${makeOAuth2PropertyMappingsSelector(provider?.propertyMappings)}
 | 
			
		||||
@ -332,11 +316,7 @@ export function renderForm(
 | 
			
		||||
        <ak-form-group>
 | 
			
		||||
            <span slot="header">${msg("Machine-to-Machine authentication settings")}</span>
 | 
			
		||||
            <div slot="body" class="pf-c-form">
 | 
			
		||||
                <ak-form-element-horizontal
 | 
			
		||||
                    label=${msg("Trusted OIDC Sources")}
 | 
			
		||||
                    name="jwksSources"
 | 
			
		||||
                    .errorMessages=${errors?.jwksSources ?? []}
 | 
			
		||||
                >
 | 
			
		||||
                <ak-form-element-horizontal label=${msg("Trusted OIDC Sources")} name="jwksSources">
 | 
			
		||||
                    <ak-dual-select-dynamic-selected
 | 
			
		||||
                        .provider=${oauth2SourcesProvider}
 | 
			
		||||
                        .selector=${makeSourceSelector(provider?.jwksSources)}
 | 
			
		||||
 | 
			
		||||
@ -258,6 +258,20 @@ export class ProxyProviderFormPage extends BaseProviderForm<ProxyProvider> {
 | 
			
		||||
                    required
 | 
			
		||||
                />
 | 
			
		||||
            </ak-form-element-horizontal>
 | 
			
		||||
            <ak-form-element-horizontal
 | 
			
		||||
                label=${msg("Authorization flow")}
 | 
			
		||||
                required
 | 
			
		||||
                name="authorizationFlow"
 | 
			
		||||
            >
 | 
			
		||||
                <ak-flow-search
 | 
			
		||||
                    flowType=${FlowsInstancesListDesignationEnum.Authorization}
 | 
			
		||||
                    .currentFlow=${this.instance?.authorizationFlow}
 | 
			
		||||
                    required
 | 
			
		||||
                ></ak-flow-search>
 | 
			
		||||
                <p class="pf-c-form__helper-text">
 | 
			
		||||
                    ${msg("Flow used when authorizing this provider.")}
 | 
			
		||||
                </p>
 | 
			
		||||
            </ak-form-element-horizontal>
 | 
			
		||||
 | 
			
		||||
            <div class="pf-c-card pf-m-selectable pf-m-selected">
 | 
			
		||||
                <div class="pf-c-card__body">${this.renderModeSelector()}</div>
 | 
			
		||||
@ -394,7 +408,7 @@ ${this.instance?.skipPathRegex}</textarea
 | 
			
		||||
            </ak-form-group>
 | 
			
		||||
 | 
			
		||||
            <ak-form-group>
 | 
			
		||||
                <span slot="header"> ${msg("Flow settings")} </span>
 | 
			
		||||
                <span slot="header"> ${msg("Advanced flow settings")} </span>
 | 
			
		||||
                <div slot="body" class="pf-c-form">
 | 
			
		||||
                    <ak-form-element-horizontal
 | 
			
		||||
                        label=${msg("Authentication flow")}
 | 
			
		||||
@ -411,20 +425,6 @@ ${this.instance?.skipPathRegex}</textarea
 | 
			
		||||
                            )}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                    <ak-form-element-horizontal
 | 
			
		||||
                        label=${msg("Authorization flow")}
 | 
			
		||||
                        required
 | 
			
		||||
                        name="authorizationFlow"
 | 
			
		||||
                    >
 | 
			
		||||
                        <ak-flow-search
 | 
			
		||||
                            flowType=${FlowsInstancesListDesignationEnum.Authorization}
 | 
			
		||||
                            .currentFlow=${this.instance?.authorizationFlow}
 | 
			
		||||
                            required
 | 
			
		||||
                        ></ak-flow-search>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${msg("Flow used when authorizing this provider.")}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                    <ak-form-element-horizontal
 | 
			
		||||
                        label=${msg("Invalidation flow")}
 | 
			
		||||
                        name="invalidationFlow"
 | 
			
		||||
@ -433,6 +433,7 @@ ${this.instance?.skipPathRegex}</textarea
 | 
			
		||||
                        <ak-flow-search
 | 
			
		||||
                            flowType=${FlowsInstancesListDesignationEnum.Invalidation}
 | 
			
		||||
                            .currentFlow=${this.instance?.invalidationFlow}
 | 
			
		||||
                            defaultFlowSlug="default-provider-invalidation-flow"
 | 
			
		||||
                            required
 | 
			
		||||
                        ></ak-flow-search>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
 | 
			
		||||
@ -89,6 +89,20 @@ export class SAMLProviderFormPage extends BaseProviderForm<SAMLProvider> {
 | 
			
		||||
                    required
 | 
			
		||||
                />
 | 
			
		||||
            </ak-form-element-horizontal>
 | 
			
		||||
            <ak-form-element-horizontal
 | 
			
		||||
                label=${msg("Authorization flow")}
 | 
			
		||||
                required
 | 
			
		||||
                name="authorizationFlow"
 | 
			
		||||
            >
 | 
			
		||||
                <ak-flow-search
 | 
			
		||||
                    flowType=${FlowsInstancesListDesignationEnum.Authorization}
 | 
			
		||||
                    .currentFlow=${this.instance?.authorizationFlow}
 | 
			
		||||
                    required
 | 
			
		||||
                ></ak-flow-search>
 | 
			
		||||
                <p class="pf-c-form__helper-text">
 | 
			
		||||
                    ${msg("Flow used when authorizing this provider.")}
 | 
			
		||||
                </p>
 | 
			
		||||
            </ak-form-element-horizontal>
 | 
			
		||||
 | 
			
		||||
            <ak-form-group .expanded=${true}>
 | 
			
		||||
                <span slot="header"> ${msg("Protocol settings")} </span>
 | 
			
		||||
@ -155,7 +169,7 @@ export class SAMLProviderFormPage extends BaseProviderForm<SAMLProvider> {
 | 
			
		||||
            </ak-form-group>
 | 
			
		||||
 | 
			
		||||
            <ak-form-group>
 | 
			
		||||
                <span slot="header"> ${msg("Flow settings")} </span>
 | 
			
		||||
                <span slot="header"> ${msg("Advanced flow settings")} </span>
 | 
			
		||||
                <div slot="body" class="pf-c-form">
 | 
			
		||||
                    <ak-form-element-horizontal
 | 
			
		||||
                        label=${msg("Authentication flow")}
 | 
			
		||||
@ -172,20 +186,6 @@ export class SAMLProviderFormPage extends BaseProviderForm<SAMLProvider> {
 | 
			
		||||
                            )}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                    <ak-form-element-horizontal
 | 
			
		||||
                        label=${msg("Authorization flow")}
 | 
			
		||||
                        required
 | 
			
		||||
                        name="authorizationFlow"
 | 
			
		||||
                    >
 | 
			
		||||
                        <ak-flow-search
 | 
			
		||||
                            flowType=${FlowsInstancesListDesignationEnum.Authorization}
 | 
			
		||||
                            .currentFlow=${this.instance?.authorizationFlow}
 | 
			
		||||
                            required
 | 
			
		||||
                        ></ak-flow-search>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${msg("Flow used when authorizing this provider.")}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                    <ak-form-element-horizontal
 | 
			
		||||
                        label=${msg("Invalidation flow")}
 | 
			
		||||
                        name="invalidationFlow"
 | 
			
		||||
@ -194,6 +194,7 @@ export class SAMLProviderFormPage extends BaseProviderForm<SAMLProvider> {
 | 
			
		||||
                        <ak-flow-search
 | 
			
		||||
                            flowType=${FlowsInstancesListDesignationEnum.Invalidation}
 | 
			
		||||
                            .currentFlow=${this.instance?.invalidationFlow}
 | 
			
		||||
                            defaultFlowSlug="default-provider-invalidation-flow"
 | 
			
		||||
                            required
 | 
			
		||||
                        ></ak-flow-search>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
 | 
			
		||||
@ -54,6 +54,7 @@ export class SAMLProviderImportForm extends Form<SAMLProvider> {
 | 
			
		||||
            >
 | 
			
		||||
                <ak-flow-search-no-default
 | 
			
		||||
                    flowType=${FlowsInstancesListDesignationEnum.Invalidation}
 | 
			
		||||
                    defaultFlowSlug="default-provider-invalidation-flow"
 | 
			
		||||
                    required
 | 
			
		||||
                ></ak-flow-search-no-default>
 | 
			
		||||
                <p class="pf-c-form__helper-text">
 | 
			
		||||
 | 
			
		||||
@ -38,12 +38,15 @@ export async function scimPropertyMappingsProvider(page = 1, search = "") {
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function makeSCIMPropertyMappingsSelector(instanceMappings: string[] | undefined) {
 | 
			
		||||
export function makeSCIMPropertyMappingsSelector(
 | 
			
		||||
    instanceMappings: string[] | undefined,
 | 
			
		||||
    defaultSelected: string,
 | 
			
		||||
) {
 | 
			
		||||
    const localMappings = instanceMappings ? new Set(instanceMappings) : undefined;
 | 
			
		||||
    return localMappings
 | 
			
		||||
        ? ([pk, _]: DualSelectPair) => localMappings.has(pk)
 | 
			
		||||
        : ([_0, _1, _2, mapping]: DualSelectPair<SCIMMapping>) =>
 | 
			
		||||
              mapping?.managed === "goauthentik.io/providers/scim/user";
 | 
			
		||||
              mapping?.managed === defaultSelected;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@customElement("ak-provider-scim-form")
 | 
			
		||||
@ -189,6 +192,7 @@ export class SCIMProviderFormPage extends BaseProviderForm<SCIMProvider> {
 | 
			
		||||
                            .provider=${scimPropertyMappingsProvider}
 | 
			
		||||
                            .selector=${makeSCIMPropertyMappingsSelector(
 | 
			
		||||
                                this.instance?.propertyMappings,
 | 
			
		||||
                                "goauthentik.io/providers/scim/user",
 | 
			
		||||
                            )}
 | 
			
		||||
                            available-label=${msg("Available User Property Mappings")}
 | 
			
		||||
                            selected-label=${msg("Selected User Property Mappings")}
 | 
			
		||||
@ -205,6 +209,7 @@ export class SCIMProviderFormPage extends BaseProviderForm<SCIMProvider> {
 | 
			
		||||
                            .provider=${scimPropertyMappingsProvider}
 | 
			
		||||
                            .selector=${makeSCIMPropertyMappingsSelector(
 | 
			
		||||
                                this.instance?.propertyMappingsGroup,
 | 
			
		||||
                                "goauthentik.io/providers/scim/group",
 | 
			
		||||
                            )}
 | 
			
		||||
                            available-label=${msg("Available Group Property Mappings")}
 | 
			
		||||
                            selected-label=${msg("Selected Group Property Mappings")}
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,12 @@ import { msg } from "@lit/localize";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators.js";
 | 
			
		||||
 | 
			
		||||
import { ProvidersApi, SCIMProviderGroup, SyncObjectModelEnum } from "@goauthentik/api";
 | 
			
		||||
import {
 | 
			
		||||
    ProvidersApi,
 | 
			
		||||
    ProvidersScimSyncObjectCreateRequest,
 | 
			
		||||
    SCIMProviderGroup,
 | 
			
		||||
    SyncObjectModelEnum,
 | 
			
		||||
} from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
@customElement("ak-provider-scim-groups-list")
 | 
			
		||||
export class SCIMProviderGroupList extends Table<SCIMProviderGroup> {
 | 
			
		||||
@ -29,7 +34,9 @@ export class SCIMProviderGroupList extends Table<SCIMProviderGroup> {
 | 
			
		||||
                <ak-sync-object-form
 | 
			
		||||
                    .provider=${this.providerId}
 | 
			
		||||
                    model=${SyncObjectModelEnum.Group}
 | 
			
		||||
                    .sync=${new ProvidersApi(DEFAULT_CONFIG).providersScimSyncObjectCreate}
 | 
			
		||||
                    .sync=${(data: ProvidersScimSyncObjectCreateRequest) => {
 | 
			
		||||
                        return new ProvidersApi(DEFAULT_CONFIG).providersScimSyncObjectCreate(data);
 | 
			
		||||
                    }}
 | 
			
		||||
                    slot="form"
 | 
			
		||||
                >
 | 
			
		||||
                </ak-sync-object-form>
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,12 @@ import { msg } from "@lit/localize";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators.js";
 | 
			
		||||
 | 
			
		||||
import { ProvidersApi, SCIMProviderUser, SyncObjectModelEnum } from "@goauthentik/api";
 | 
			
		||||
import {
 | 
			
		||||
    ProvidersApi,
 | 
			
		||||
    ProvidersScimSyncObjectCreateRequest,
 | 
			
		||||
    SCIMProviderUser,
 | 
			
		||||
    SyncObjectModelEnum,
 | 
			
		||||
} from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
@customElement("ak-provider-scim-users-list")
 | 
			
		||||
export class SCIMProviderUserList extends Table<SCIMProviderUser> {
 | 
			
		||||
@ -29,7 +34,9 @@ export class SCIMProviderUserList extends Table<SCIMProviderUser> {
 | 
			
		||||
                <ak-sync-object-form
 | 
			
		||||
                    .provider=${this.providerId}
 | 
			
		||||
                    model=${SyncObjectModelEnum.User}
 | 
			
		||||
                    .sync=${new ProvidersApi(DEFAULT_CONFIG).providersScimSyncObjectCreate}
 | 
			
		||||
                    .sync=${(data: ProvidersScimSyncObjectCreateRequest) => {
 | 
			
		||||
                        return new ProvidersApi(DEFAULT_CONFIG).providersScimSyncObjectCreate(data);
 | 
			
		||||
                    }}
 | 
			
		||||
                    slot="form"
 | 
			
		||||
                >
 | 
			
		||||
                </ak-sync-object-form>
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
import "@goauthentik/admin/sources/SourceWizard";
 | 
			
		||||
import "@goauthentik/admin/sources/kerberos/KerberosSourceForm";
 | 
			
		||||
import "@goauthentik/admin/sources/ldap/LDAPSourceForm";
 | 
			
		||||
import "@goauthentik/admin/sources/oauth/OAuthSourceForm";
 | 
			
		||||
import "@goauthentik/admin/sources/plex/PlexSourceForm";
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,4 @@
 | 
			
		||||
import "@goauthentik/admin/sources/kerberos/KerberosSourceViewPage";
 | 
			
		||||
import "@goauthentik/admin/sources/ldap/LDAPSourceViewPage";
 | 
			
		||||
import "@goauthentik/admin/sources/oauth/OAuthSourceViewPage";
 | 
			
		||||
import "@goauthentik/admin/sources/plex/PlexSourceViewPage";
 | 
			
		||||
@ -36,6 +37,10 @@ export class SourceViewPage extends AKElement {
 | 
			
		||||
            return html`<ak-empty-state ?loading=${true} ?fullHeight=${true}></ak-empty-state>`;
 | 
			
		||||
        }
 | 
			
		||||
        switch (this.source?.component) {
 | 
			
		||||
            case "ak-source-kerberos-form":
 | 
			
		||||
                return html`<ak-source-kerberos-view
 | 
			
		||||
                    sourceSlug=${this.source.slug}
 | 
			
		||||
                ></ak-source-kerberos-view>`;
 | 
			
		||||
            case "ak-source-ldap-form":
 | 
			
		||||
                return html`<ak-source-ldap-view
 | 
			
		||||
                    sourceSlug=${this.source.slug}
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,4 @@
 | 
			
		||||
import "@goauthentik/admin/sources/kerberos/KerberosSourceForm";
 | 
			
		||||
import "@goauthentik/admin/sources/ldap/LDAPSourceForm";
 | 
			
		||||
import "@goauthentik/admin/sources/oauth/OAuthSourceForm";
 | 
			
		||||
import "@goauthentik/admin/sources/plex/PlexSourceForm";
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										39
									
								
								web/src/admin/sources/kerberos/KerberosSourceConnectivity.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								web/src/admin/sources/kerberos/KerberosSourceConnectivity.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,39 @@
 | 
			
		||||
import { AKElement } from "@goauthentik/elements/Base";
 | 
			
		||||
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
 | 
			
		||||
 | 
			
		||||
import { CSSResult, TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators.js";
 | 
			
		||||
 | 
			
		||||
import PFList from "@patternfly/patternfly/components/List/list.css";
 | 
			
		||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
 | 
			
		||||
 | 
			
		||||
@customElement("ak-source-kerberos-connectivity")
 | 
			
		||||
export class KerberosSourceConnectivity extends AKElement {
 | 
			
		||||
    @property()
 | 
			
		||||
    connectivity?: {
 | 
			
		||||
        [key: string]: {
 | 
			
		||||
            [key: string]: string;
 | 
			
		||||
        };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    static get styles(): CSSResult[] {
 | 
			
		||||
        return [PFBase, PFList];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    render(): TemplateResult {
 | 
			
		||||
        if (!this.connectivity) {
 | 
			
		||||
            return html``;
 | 
			
		||||
        }
 | 
			
		||||
        return html`<ul class="pf-c-list">
 | 
			
		||||
            ${Object.keys(this.connectivity).map((serverKey) => {
 | 
			
		||||
                return html`<li>${serverKey}: ${this.connectivity![serverKey]}</li>`;
 | 
			
		||||
            })}
 | 
			
		||||
        </ul>`;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
    interface HTMLElementTagNameMap {
 | 
			
		||||
        "ak-source-kerberos-connectivity": KerberosSourceConnectivity;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										456
									
								
								web/src/admin/sources/kerberos/KerberosSourceForm.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										456
									
								
								web/src/admin/sources/kerberos/KerberosSourceForm.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,456 @@
 | 
			
		||||
import "@goauthentik/admin/common/ak-flow-search/ak-source-flow-search";
 | 
			
		||||
import { iconHelperText, placeholderHelperText } from "@goauthentik/admin/helperText";
 | 
			
		||||
import { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm";
 | 
			
		||||
import {
 | 
			
		||||
    GroupMatchingModeToLabel,
 | 
			
		||||
    UserMatchingModeToLabel,
 | 
			
		||||
} from "@goauthentik/admin/sources/oauth/utils";
 | 
			
		||||
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
 | 
			
		||||
import { first } from "@goauthentik/common/utils";
 | 
			
		||||
import "@goauthentik/components/ak-switch-input";
 | 
			
		||||
import "@goauthentik/components/ak-text-input";
 | 
			
		||||
import "@goauthentik/components/ak-textarea-input";
 | 
			
		||||
import {
 | 
			
		||||
    CapabilitiesEnum,
 | 
			
		||||
    WithCapabilitiesConfig,
 | 
			
		||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
 | 
			
		||||
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
 | 
			
		||||
import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types.js";
 | 
			
		||||
import "@goauthentik/elements/forms/FormGroup";
 | 
			
		||||
import "@goauthentik/elements/forms/HorizontalFormElement";
 | 
			
		||||
import "@goauthentik/elements/forms/SearchSelect";
 | 
			
		||||
 | 
			
		||||
import { msg } from "@lit/localize";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, state } from "lit/decorators.js";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined.js";
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    FlowsInstancesListDesignationEnum,
 | 
			
		||||
    GroupMatchingModeEnum,
 | 
			
		||||
    KerberosSource,
 | 
			
		||||
    KerberosSourcePropertyMapping,
 | 
			
		||||
    KerberosSourceRequest,
 | 
			
		||||
    PropertymappingsApi,
 | 
			
		||||
    SourcesApi,
 | 
			
		||||
    UserMatchingModeEnum,
 | 
			
		||||
} from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
async function propertyMappingsProvider(page = 1, search = "") {
 | 
			
		||||
    const propertyMappings = await new PropertymappingsApi(
 | 
			
		||||
        DEFAULT_CONFIG,
 | 
			
		||||
    ).propertymappingsSourceKerberosList({
 | 
			
		||||
        ordering: "managed",
 | 
			
		||||
        pageSize: 20,
 | 
			
		||||
        search: search.trim(),
 | 
			
		||||
        page,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        pagination: propertyMappings.pagination,
 | 
			
		||||
        options: propertyMappings.results.map((m) => [m.pk, m.name, m.name, m]),
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function makePropertyMappingsSelector(object: string, instanceMappings?: string[]) {
 | 
			
		||||
    const localMappings = instanceMappings ? new Set(instanceMappings) : undefined;
 | 
			
		||||
    return localMappings
 | 
			
		||||
        ? ([pk, _]: DualSelectPair) => localMappings.has(pk)
 | 
			
		||||
        : ([_0, _1, _2, mapping]: DualSelectPair<KerberosSourcePropertyMapping>) =>
 | 
			
		||||
              object == "user" &&
 | 
			
		||||
              mapping?.managed?.startsWith("goauthentik.io/sources/kerberos/user/default/");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@customElement("ak-source-kerberos-form")
 | 
			
		||||
export class KerberosSourceForm extends WithCapabilitiesConfig(BaseSourceForm<KerberosSource>) {
 | 
			
		||||
    async loadInstance(pk: string): Promise<KerberosSource> {
 | 
			
		||||
        const source = await new SourcesApi(DEFAULT_CONFIG).sourcesKerberosRetrieve({
 | 
			
		||||
            slug: pk,
 | 
			
		||||
        });
 | 
			
		||||
        this.clearIcon = false;
 | 
			
		||||
        return source;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @state()
 | 
			
		||||
    clearIcon = false;
 | 
			
		||||
 | 
			
		||||
    async send(data: KerberosSource): Promise<KerberosSource> {
 | 
			
		||||
        let source: KerberosSource;
 | 
			
		||||
        if (this.instance) {
 | 
			
		||||
            source = await new SourcesApi(DEFAULT_CONFIG).sourcesKerberosPartialUpdate({
 | 
			
		||||
                slug: this.instance.slug,
 | 
			
		||||
                patchedKerberosSourceRequest: data,
 | 
			
		||||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
            source = await new SourcesApi(DEFAULT_CONFIG).sourcesKerberosCreate({
 | 
			
		||||
                kerberosSourceRequest: data as unknown as KerberosSourceRequest,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        const c = await config();
 | 
			
		||||
        if (c.capabilities.includes(CapabilitiesEnum.CanSaveMedia)) {
 | 
			
		||||
            const icon = this.getFormFiles()["icon"];
 | 
			
		||||
            if (icon || this.clearIcon) {
 | 
			
		||||
                await new SourcesApi(DEFAULT_CONFIG).sourcesAllSetIconCreate({
 | 
			
		||||
                    slug: source.slug,
 | 
			
		||||
                    file: icon,
 | 
			
		||||
                    clear: this.clearIcon,
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            await new SourcesApi(DEFAULT_CONFIG).sourcesAllSetIconUrlCreate({
 | 
			
		||||
                slug: source.slug,
 | 
			
		||||
                filePathRequest: {
 | 
			
		||||
                    url: data.icon || "",
 | 
			
		||||
                },
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        return source;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    renderForm(): TemplateResult {
 | 
			
		||||
        return html` <ak-text-input
 | 
			
		||||
                name="name"
 | 
			
		||||
                label=${msg("Name")}
 | 
			
		||||
                value=${ifDefined(this.instance?.name)}
 | 
			
		||||
                required
 | 
			
		||||
            ></ak-text-input>
 | 
			
		||||
            <ak-text-input
 | 
			
		||||
                name="slug"
 | 
			
		||||
                label=${msg("Slug")}
 | 
			
		||||
                value=${ifDefined(this.instance?.slug)}
 | 
			
		||||
                required
 | 
			
		||||
            ></ak-text-input>
 | 
			
		||||
            <ak-switch-input
 | 
			
		||||
                name="enabled"
 | 
			
		||||
                ?checked=${first(this.instance?.enabled, true)}
 | 
			
		||||
                label=${msg("Enabled")}
 | 
			
		||||
            ></ak-switch-input>
 | 
			
		||||
            <ak-switch-input
 | 
			
		||||
                name="passwordLoginUpdateInternalPassword"
 | 
			
		||||
                ?checked=${first(this.instance?.passwordLoginUpdateInternalPassword, false)}
 | 
			
		||||
                label=${msg("Update internal password on login")}
 | 
			
		||||
                help=${msg(
 | 
			
		||||
                    "When the user logs in to authentik using this source password backend, update their credentials in authentik.",
 | 
			
		||||
                )}
 | 
			
		||||
            ></ak-switch-input>
 | 
			
		||||
            <ak-switch-input
 | 
			
		||||
                name="syncUsers"
 | 
			
		||||
                ?checked=${first(this.instance?.syncUsers, true)}
 | 
			
		||||
                label=${msg("Sync users")}
 | 
			
		||||
            ></ak-switch-input>
 | 
			
		||||
            <ak-switch-input
 | 
			
		||||
                name="syncUsersPassword"
 | 
			
		||||
                ?checked=${first(this.instance?.syncUsersPassword, true)}
 | 
			
		||||
                label=${msg("User password writeback")}
 | 
			
		||||
                help=${msg(
 | 
			
		||||
                    "Enable this option to write password changes made in authentik back to Kerberos. Ignored if sync is disabled.",
 | 
			
		||||
                )}
 | 
			
		||||
            ></ak-switch-input>
 | 
			
		||||
            <ak-form-group .expanded=${true}>
 | 
			
		||||
                <span slot="header"> ${msg("Realm settings")} </span>
 | 
			
		||||
                <div slot="body" class="pf-c-form">
 | 
			
		||||
                    <ak-text-input
 | 
			
		||||
                        name="realm"
 | 
			
		||||
                        label=${msg("Realm")}
 | 
			
		||||
                        value=${ifDefined(this.instance?.realm)}
 | 
			
		||||
                        placeholder="AUTHENTIK.COMPANY"
 | 
			
		||||
                        required
 | 
			
		||||
                    ></ak-text-input>
 | 
			
		||||
                    <ak-textarea-input
 | 
			
		||||
                        name="krb5Conf"
 | 
			
		||||
                        label=${msg("Kerberos 5 configuration")}
 | 
			
		||||
                        value=${ifDefined(this.instance?.krb5Conf)}
 | 
			
		||||
                        help=${msg(
 | 
			
		||||
                            "Kerberos 5 configuration. See man krb5.conf(5) for configuration format. If left empty, a default krb5.conf will be used.",
 | 
			
		||||
                        )}
 | 
			
		||||
                    ></ak-textarea-input>
 | 
			
		||||
                    <ak-form-element-horizontal
 | 
			
		||||
                        label=${msg("User matching mode")}
 | 
			
		||||
                        ?required=${true}
 | 
			
		||||
                        name="userMatchingMode"
 | 
			
		||||
                    >
 | 
			
		||||
                        <select class="pf-c-form-control">
 | 
			
		||||
                            <option
 | 
			
		||||
                                value=${UserMatchingModeEnum.Identifier}
 | 
			
		||||
                                ?selected=${this.instance?.userMatchingMode ===
 | 
			
		||||
                                UserMatchingModeEnum.Identifier}
 | 
			
		||||
                            >
 | 
			
		||||
                                ${UserMatchingModeToLabel(UserMatchingModeEnum.Identifier)}
 | 
			
		||||
                            </option>
 | 
			
		||||
                            <option
 | 
			
		||||
                                value=${UserMatchingModeEnum.EmailLink}
 | 
			
		||||
                                ?selected=${this.instance?.userMatchingMode ===
 | 
			
		||||
                                UserMatchingModeEnum.EmailLink}
 | 
			
		||||
                            >
 | 
			
		||||
                                ${UserMatchingModeToLabel(UserMatchingModeEnum.EmailLink)}
 | 
			
		||||
                            </option>
 | 
			
		||||
                            <option
 | 
			
		||||
                                value=${UserMatchingModeEnum.EmailDeny}
 | 
			
		||||
                                ?selected=${this.instance?.userMatchingMode ===
 | 
			
		||||
                                UserMatchingModeEnum.EmailDeny}
 | 
			
		||||
                            >
 | 
			
		||||
                                ${UserMatchingModeToLabel(UserMatchingModeEnum.EmailDeny)}
 | 
			
		||||
                            </option>
 | 
			
		||||
                            <option
 | 
			
		||||
                                value=${UserMatchingModeEnum.UsernameLink}
 | 
			
		||||
                                ?selected=${this.instance?.userMatchingMode ===
 | 
			
		||||
                                UserMatchingModeEnum.UsernameLink}
 | 
			
		||||
                            >
 | 
			
		||||
                                ${UserMatchingModeToLabel(UserMatchingModeEnum.UsernameLink)}
 | 
			
		||||
                            </option>
 | 
			
		||||
                            <option
 | 
			
		||||
                                value=${UserMatchingModeEnum.UsernameDeny}
 | 
			
		||||
                                ?selected=${this.instance?.userMatchingMode ===
 | 
			
		||||
                                UserMatchingModeEnum.UsernameDeny}
 | 
			
		||||
                            >
 | 
			
		||||
                                ${UserMatchingModeToLabel(UserMatchingModeEnum.UsernameDeny)}
 | 
			
		||||
                            </option>
 | 
			
		||||
                        </select>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                    <ak-form-element-horizontal
 | 
			
		||||
                        label=${msg("Group matching mode")}
 | 
			
		||||
                        ?required=${true}
 | 
			
		||||
                        name="groupMatchingMode"
 | 
			
		||||
                    >
 | 
			
		||||
                        <select class="pf-c-form-control">
 | 
			
		||||
                            <option
 | 
			
		||||
                                value=${GroupMatchingModeEnum.Identifier}
 | 
			
		||||
                                ?selected=${this.instance?.groupMatchingMode ===
 | 
			
		||||
                                GroupMatchingModeEnum.Identifier}
 | 
			
		||||
                            >
 | 
			
		||||
                                ${UserMatchingModeToLabel(UserMatchingModeEnum.Identifier)}
 | 
			
		||||
                            </option>
 | 
			
		||||
                            <option
 | 
			
		||||
                                value=${GroupMatchingModeEnum.NameLink}
 | 
			
		||||
                                ?selected=${this.instance?.groupMatchingMode ===
 | 
			
		||||
                                GroupMatchingModeEnum.NameLink}
 | 
			
		||||
                            >
 | 
			
		||||
                                ${GroupMatchingModeToLabel(GroupMatchingModeEnum.NameLink)}
 | 
			
		||||
                            </option>
 | 
			
		||||
                            <option
 | 
			
		||||
                                value=${GroupMatchingModeEnum.NameDeny}
 | 
			
		||||
                                ?selected=${this.instance?.groupMatchingMode ===
 | 
			
		||||
                                GroupMatchingModeEnum.NameDeny}
 | 
			
		||||
                            >
 | 
			
		||||
                                ${GroupMatchingModeToLabel(GroupMatchingModeEnum.NameDeny)}
 | 
			
		||||
                            </option>
 | 
			
		||||
                        </select>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                </div>
 | 
			
		||||
            </ak-form-group>
 | 
			
		||||
            <ak-form-group .expanded=${false}>
 | 
			
		||||
                <span slot="header"> ${msg("Sync connection settings")} </span>
 | 
			
		||||
                <div slot="body" class="pf-c-form">
 | 
			
		||||
                    <ak-text-input
 | 
			
		||||
                        name="syncPrincipal"
 | 
			
		||||
                        label=${msg("Sync principal")}
 | 
			
		||||
                        value=${ifDefined(this.instance?.syncPrincipal)}
 | 
			
		||||
                        help=${msg("Principal used to authenticate to the KDC for syncing.")}
 | 
			
		||||
                    ></ak-text-input>
 | 
			
		||||
                    <ak-form-element-horizontal
 | 
			
		||||
                        name="syncPassword"
 | 
			
		||||
                        label=${msg("Sync password")}
 | 
			
		||||
                        ?writeOnly=${this.instance !== undefined}
 | 
			
		||||
                    >
 | 
			
		||||
                        <input type="text" value="" class="pf-c-form-control" />
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${msg(
 | 
			
		||||
                                "Password used to authenticate to the KDC for syncing. Optional if Sync keytab or Sync credentials cache is provided.",
 | 
			
		||||
                            )}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                    <ak-form-element-horizontal
 | 
			
		||||
                        name="syncKeytab"
 | 
			
		||||
                        label=${msg("Sync keytab")}
 | 
			
		||||
                        ?writeOnly=${this.instance !== undefined}
 | 
			
		||||
                    >
 | 
			
		||||
                        <textarea class="pf-c-form-control"></textarea>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${msg(
 | 
			
		||||
                                "Keytab used to authenticate to the KDC for syncing. Optional if Sync password or Sync credentials cache is provided. Must be base64 encoded or in the form TYPE:residual.",
 | 
			
		||||
                            )}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                    <ak-text-input
 | 
			
		||||
                        name="syncCcache"
 | 
			
		||||
                        label=${msg("Sync credentials cache")}
 | 
			
		||||
                        value=${ifDefined(this.instance?.syncCcache)}
 | 
			
		||||
                        help=${msg(
 | 
			
		||||
                            "Credentials cache used to authenticate to the KDC for syncing. Optional if Sync password or Sync keytab is provided. Must be in the form TYPE:residual.",
 | 
			
		||||
                        )}
 | 
			
		||||
                    ></ak-text-input>
 | 
			
		||||
                </div>
 | 
			
		||||
            </ak-form-group>
 | 
			
		||||
            <ak-form-group .expanded=${false}>
 | 
			
		||||
                <span slot="header"> ${msg("SPNEGO settings")} </span>
 | 
			
		||||
                <div slot="body" class="pf-c-form">
 | 
			
		||||
                    <ak-text-input
 | 
			
		||||
                        name="spnegoServerName"
 | 
			
		||||
                        label=${msg("SPNEGO server name")}
 | 
			
		||||
                        value=${ifDefined(this.instance?.spnegoServerName)}
 | 
			
		||||
                        help=${msg(
 | 
			
		||||
                            "Force the use of a specific server name for SPNEGO. Must be in the form HTTP@domain",
 | 
			
		||||
                        )}
 | 
			
		||||
                    ></ak-text-input>
 | 
			
		||||
                    <ak-form-element-horizontal
 | 
			
		||||
                        name="spnegoKeytab"
 | 
			
		||||
                        label=${msg("SPNEGO keytab")}
 | 
			
		||||
                        ?writeOnly=${this.instance !== undefined}
 | 
			
		||||
                    >
 | 
			
		||||
                        <textarea class="pf-c-form-control"></textarea>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${msg(
 | 
			
		||||
                                "Keytab used for SPNEGO. Optional if SPNEGO credentials cache is provided. Must be base64 encoded or in the form TYPE:residual.",
 | 
			
		||||
                            )}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                    <ak-text-input
 | 
			
		||||
                        name="spnegoCcache"
 | 
			
		||||
                        label=${msg("SPNEGO credentials cache")}
 | 
			
		||||
                        value=${ifDefined(this.instance?.spnegoCcache)}
 | 
			
		||||
                        help=${msg(
 | 
			
		||||
                            "Credentials cache used for SPNEGO. Optional if SPNEGO keytab is provided. Must be in the form TYPE:residual.",
 | 
			
		||||
                        )}
 | 
			
		||||
                    ></ak-text-input>
 | 
			
		||||
                </div>
 | 
			
		||||
            </ak-form-group>
 | 
			
		||||
            <ak-form-group ?expanded=${false}>
 | 
			
		||||
                <span slot="header"> ${msg("Kerberos Attribute mapping")} </span>
 | 
			
		||||
                <div slot="body" class="pf-c-form">
 | 
			
		||||
                    <ak-form-element-horizontal
 | 
			
		||||
                        label=${msg("User Property Mappings")}
 | 
			
		||||
                        name="userPropertyMappings"
 | 
			
		||||
                    >
 | 
			
		||||
                        <ak-dual-select-dynamic-selected
 | 
			
		||||
                            .provider=${propertyMappingsProvider}
 | 
			
		||||
                            .selector=${makePropertyMappingsSelector(
 | 
			
		||||
                                "user",
 | 
			
		||||
                                this.instance?.userPropertyMappings,
 | 
			
		||||
                            )}
 | 
			
		||||
                            available-label="${msg("Available User Property Mappings")}"
 | 
			
		||||
                            selected-label="${msg("Selected User Property Mappings")}"
 | 
			
		||||
                        ></ak-dual-select-dynamic-selected>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${msg("Property mappings for user creation.")}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                    <ak-form-element-horizontal
 | 
			
		||||
                        label=${msg("Group Property Mappings")}
 | 
			
		||||
                        name="groupPropertyMappings"
 | 
			
		||||
                    >
 | 
			
		||||
                        <ak-dual-select-dynamic-selected
 | 
			
		||||
                            .provider=${propertyMappingsProvider}
 | 
			
		||||
                            .selector=${makePropertyMappingsSelector(
 | 
			
		||||
                                "group",
 | 
			
		||||
                                this.instance?.groupPropertyMappings,
 | 
			
		||||
                            )}
 | 
			
		||||
                            available-label="${msg("Available Group Property Mappings")}"
 | 
			
		||||
                            selected-label="${msg("Selected Group Property Mappings")}"
 | 
			
		||||
                        ></ak-dual-select-dynamic-selected>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${msg("Property mappings for group creation.")}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                </div>
 | 
			
		||||
            </ak-form-group>
 | 
			
		||||
            <ak-form-group>
 | 
			
		||||
                <span slot="header"> ${msg("Flow settings")} </span>
 | 
			
		||||
                <div slot="body" class="pf-c-form">
 | 
			
		||||
                    <ak-form-element-horizontal
 | 
			
		||||
                        label=${msg("Authentication flow")}
 | 
			
		||||
                        name="authenticationFlow"
 | 
			
		||||
                    >
 | 
			
		||||
                        <ak-source-flow-search
 | 
			
		||||
                            flowType=${FlowsInstancesListDesignationEnum.Authentication}
 | 
			
		||||
                            .currentFlow=${this.instance?.authenticationFlow}
 | 
			
		||||
                            .instanceId=${this.instance?.pk}
 | 
			
		||||
                            fallback="default-source-authentication"
 | 
			
		||||
                        ></ak-source-flow-search>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${msg("Flow to use when authenticating existing users.")}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                    <ak-form-element-horizontal
 | 
			
		||||
                        label=${msg("Enrollment flow")}
 | 
			
		||||
                        name="enrollmentFlow"
 | 
			
		||||
                    >
 | 
			
		||||
                        <ak-source-flow-search
 | 
			
		||||
                            flowType=${FlowsInstancesListDesignationEnum.Enrollment}
 | 
			
		||||
                            .currentFlow=${this.instance?.enrollmentFlow}
 | 
			
		||||
                            .instanceId=${this.instance?.pk}
 | 
			
		||||
                            fallback="default-source-enrollment"
 | 
			
		||||
                        ></ak-source-flow-search>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${msg("Flow to use when enrolling new users.")}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                </div>
 | 
			
		||||
            </ak-form-group>
 | 
			
		||||
            <ak-form-group>
 | 
			
		||||
                <span slot="header"> ${msg("Additional settings")} </span>
 | 
			
		||||
                <div slot="body" class="pf-c-form">
 | 
			
		||||
                    <ak-text-input
 | 
			
		||||
                        name="userPathTemplate"
 | 
			
		||||
                        label=${msg("User path")}
 | 
			
		||||
                        value=${first(
 | 
			
		||||
                            this.instance?.userPathTemplate,
 | 
			
		||||
                            "goauthentik.io/sources/%(slug)s",
 | 
			
		||||
                        )}
 | 
			
		||||
                        help=${placeholderHelperText}
 | 
			
		||||
                    ></ak-text-input>
 | 
			
		||||
                </div>
 | 
			
		||||
                ${this.can(CapabilitiesEnum.CanSaveMedia)
 | 
			
		||||
                    ? html`<ak-form-element-horizontal label=${msg("Icon")} name="icon">
 | 
			
		||||
                              <input type="file" value="" class="pf-c-form-control" />
 | 
			
		||||
                              ${this.instance?.icon
 | 
			
		||||
                                  ? html`
 | 
			
		||||
                                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                                            ${msg("Currently set to:")} ${this.instance?.icon}
 | 
			
		||||
                                        </p>
 | 
			
		||||
                                    `
 | 
			
		||||
                                  : html``}
 | 
			
		||||
                          </ak-form-element-horizontal>
 | 
			
		||||
                          ${this.instance?.icon
 | 
			
		||||
                              ? html`
 | 
			
		||||
                                    <ak-form-element-horizontal>
 | 
			
		||||
                                        <label class="pf-c-switch">
 | 
			
		||||
                                            <input
 | 
			
		||||
                                                class="pf-c-switch__input"
 | 
			
		||||
                                                type="checkbox"
 | 
			
		||||
                                                @change=${(ev: Event) => {
 | 
			
		||||
                                                    const target = ev.target as HTMLInputElement;
 | 
			
		||||
                                                    this.clearIcon = target.checked;
 | 
			
		||||
                                                }}
 | 
			
		||||
                                            />
 | 
			
		||||
                                            <span class="pf-c-switch__toggle">
 | 
			
		||||
                                                <span class="pf-c-switch__toggle-icon">
 | 
			
		||||
                                                    <i class="fas fa-check" aria-hidden="true"></i>
 | 
			
		||||
                                                </span>
 | 
			
		||||
                                            </span>
 | 
			
		||||
                                            <span class="pf-c-switch__label">
 | 
			
		||||
                                                ${msg("Clear icon")}
 | 
			
		||||
                                            </span>
 | 
			
		||||
                                        </label>
 | 
			
		||||
                                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                                            ${msg("Delete currently set icon.")}
 | 
			
		||||
                                        </p>
 | 
			
		||||
                                    </ak-form-element-horizontal>
 | 
			
		||||
                                `
 | 
			
		||||
                              : html``}`
 | 
			
		||||
                    : html`<ak-form-element-horizontal label=${msg("Icon")} name="icon">
 | 
			
		||||
                          <input
 | 
			
		||||
                              type="text"
 | 
			
		||||
                              value="${first(this.instance?.icon, "")}"
 | 
			
		||||
                              class="pf-c-form-control"
 | 
			
		||||
                          />
 | 
			
		||||
                          <p class="pf-c-form__helper-text">${iconHelperText}</p>
 | 
			
		||||
                      </ak-form-element-horizontal>`}
 | 
			
		||||
            </ak-form-group>`;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
    interface HTMLElementTagNameMap {
 | 
			
		||||
        "ak-source-kerberos-form": KerberosSourceForm;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										213
									
								
								web/src/admin/sources/kerberos/KerberosSourceViewPage.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										213
									
								
								web/src/admin/sources/kerberos/KerberosSourceViewPage.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,213 @@
 | 
			
		||||
import "@goauthentik/admin/rbac/ObjectPermissionsPage";
 | 
			
		||||
import "@goauthentik/admin/sources/kerberos/KerberosSourceConnectivity";
 | 
			
		||||
import "@goauthentik/admin/sources/kerberos/KerberosSourceForm";
 | 
			
		||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
			
		||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
 | 
			
		||||
import "@goauthentik/components/events/ObjectChangelog";
 | 
			
		||||
import MDSourceKerberosBrowser from "@goauthentik/docs/users-sources/sources/protocols/kerberos/browser.md";
 | 
			
		||||
import { AKElement } from "@goauthentik/elements/Base";
 | 
			
		||||
import "@goauthentik/elements/CodeMirror";
 | 
			
		||||
import "@goauthentik/elements/Markdown";
 | 
			
		||||
import "@goauthentik/elements/SyncStatusCard";
 | 
			
		||||
import "@goauthentik/elements/Tabs";
 | 
			
		||||
import "@goauthentik/elements/buttons/ActionButton";
 | 
			
		||||
import "@goauthentik/elements/buttons/SpinnerButton";
 | 
			
		||||
import "@goauthentik/elements/forms/ModalForm";
 | 
			
		||||
 | 
			
		||||
import { msg } from "@lit/localize";
 | 
			
		||||
import { CSSResult, TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators.js";
 | 
			
		||||
 | 
			
		||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
 | 
			
		||||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
 | 
			
		||||
import PFContent from "@patternfly/patternfly/components/Content/content.css";
 | 
			
		||||
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
 | 
			
		||||
import PFList from "@patternfly/patternfly/components/List/list.css";
 | 
			
		||||
import PFPage from "@patternfly/patternfly/components/Page/page.css";
 | 
			
		||||
import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
 | 
			
		||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    KerberosSource,
 | 
			
		||||
    RbacPermissionsAssignedByUsersListModelEnum,
 | 
			
		||||
    SourcesApi,
 | 
			
		||||
    SyncStatus,
 | 
			
		||||
} from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
@customElement("ak-source-kerberos-view")
 | 
			
		||||
export class KerberosSourceViewPage extends AKElement {
 | 
			
		||||
    @property({ type: String })
 | 
			
		||||
    set sourceSlug(slug: string) {
 | 
			
		||||
        new SourcesApi(DEFAULT_CONFIG)
 | 
			
		||||
            .sourcesKerberosRetrieve({
 | 
			
		||||
                slug: slug,
 | 
			
		||||
            })
 | 
			
		||||
            .then((source) => {
 | 
			
		||||
                this.source = source;
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @property({ attribute: false })
 | 
			
		||||
    source!: KerberosSource;
 | 
			
		||||
 | 
			
		||||
    @state()
 | 
			
		||||
    syncState?: SyncStatus;
 | 
			
		||||
 | 
			
		||||
    static get styles(): CSSResult[] {
 | 
			
		||||
        return [PFBase, PFPage, PFButton, PFGrid, PFContent, PFCard, PFDescriptionList, PFList];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super();
 | 
			
		||||
        this.addEventListener(EVENT_REFRESH, () => {
 | 
			
		||||
            if (!this.source?.slug) return;
 | 
			
		||||
            this.sourceSlug = this.source?.slug;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    load(): void {
 | 
			
		||||
        new SourcesApi(DEFAULT_CONFIG)
 | 
			
		||||
            .sourcesKerberosSyncStatusRetrieve({
 | 
			
		||||
                slug: this.source.slug,
 | 
			
		||||
            })
 | 
			
		||||
            .then((state) => {
 | 
			
		||||
                this.syncState = state;
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    renderSyncCards(): TemplateResult {
 | 
			
		||||
        if (!this.source.syncUsers) {
 | 
			
		||||
            return html``;
 | 
			
		||||
        }
 | 
			
		||||
        return html`
 | 
			
		||||
            <div class="pf-c-card pf-l-grid__item pf-m-2-col">
 | 
			
		||||
                <div class="pf-c-card__title">
 | 
			
		||||
                    <p>${msg("Connectivity")}</p>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="pf-c-card__body">
 | 
			
		||||
                    <ak-source-kerberos-connectivity
 | 
			
		||||
                        .connectivity=${this.source.connectivity}
 | 
			
		||||
                    ></ak-source-kerberos-connectivity>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="pf-l-grid__item pf-m-10-col">
 | 
			
		||||
                <ak-sync-status-card
 | 
			
		||||
                    .fetch=${() => {
 | 
			
		||||
                        return new SourcesApi(DEFAULT_CONFIG).sourcesKerberosSyncStatusRetrieve({
 | 
			
		||||
                            slug: this.source?.slug,
 | 
			
		||||
                        });
 | 
			
		||||
                    }}
 | 
			
		||||
                    .triggerSync=${() => {
 | 
			
		||||
                        return new SourcesApi(DEFAULT_CONFIG).sourcesKerberosPartialUpdate({
 | 
			
		||||
                            slug: this.source?.slug || "",
 | 
			
		||||
                            patchedKerberosSourceRequest: {},
 | 
			
		||||
                        });
 | 
			
		||||
                    }}
 | 
			
		||||
                ></ak-sync-status-card>
 | 
			
		||||
            </div>
 | 
			
		||||
        `;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    render(): TemplateResult {
 | 
			
		||||
        if (!this.source) {
 | 
			
		||||
            return html``;
 | 
			
		||||
        }
 | 
			
		||||
        return html`<ak-tabs>
 | 
			
		||||
            <section
 | 
			
		||||
                slot="page-overview"
 | 
			
		||||
                data-tab-title="${msg("Overview")}"
 | 
			
		||||
                class="pf-c-page__main-section pf-m-no-padding-mobile"
 | 
			
		||||
                @activate=${() => {
 | 
			
		||||
                    this.load();
 | 
			
		||||
                }}
 | 
			
		||||
            >
 | 
			
		||||
                <div class="pf-l-grid pf-m-gutter">
 | 
			
		||||
                    <div class="pf-c-card pf-l-grid__item pf-m-12-col">
 | 
			
		||||
                        <div class="pf-c-card__body">
 | 
			
		||||
                            <dl class="pf-c-description-list pf-m-2-col-on-lg">
 | 
			
		||||
                                <div class="pf-c-description-list__group">
 | 
			
		||||
                                    <dt class="pf-c-description-list__term">
 | 
			
		||||
                                        <span class="pf-c-description-list__text"
 | 
			
		||||
                                            >${msg("Name")}</span
 | 
			
		||||
                                        >
 | 
			
		||||
                                    </dt>
 | 
			
		||||
                                    <dd class="pf-c-description-list__description">
 | 
			
		||||
                                        <div class="pf-c-description-list__text">
 | 
			
		||||
                                            ${this.source.name}
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                    </dd>
 | 
			
		||||
                                </div>
 | 
			
		||||
                                <div class="pf-c-description-list__group">
 | 
			
		||||
                                    <dt class="pf-c-description-list__term">
 | 
			
		||||
                                        <span class="pf-c-description-list__text"
 | 
			
		||||
                                            >${msg("Realm")}</span
 | 
			
		||||
                                        >
 | 
			
		||||
                                    </dt>
 | 
			
		||||
                                    <dd class="pf-c-description-list__description">
 | 
			
		||||
                                        <div class="pf-c-description-list__text">
 | 
			
		||||
                                            ${this.source.realm}
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                    </dd>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </dl>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="pf-c-card__footer">
 | 
			
		||||
                            <ak-forms-modal>
 | 
			
		||||
                                <span slot="submit"> ${msg("Update")} </span>
 | 
			
		||||
                                <span slot="header"> ${msg("Update Kerberos Source")} </span>
 | 
			
		||||
                                <ak-source-kerberos-form
 | 
			
		||||
                                    slot="form"
 | 
			
		||||
                                    .instancePk=${this.source.slug}
 | 
			
		||||
                                >
 | 
			
		||||
                                </ak-source-kerberos-form>
 | 
			
		||||
                                <button slot="trigger" class="pf-c-button pf-m-primary">
 | 
			
		||||
                                    ${msg("Edit")}
 | 
			
		||||
                                </button>
 | 
			
		||||
                            </ak-forms-modal>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    ${this.renderSyncCards()}
 | 
			
		||||
                    <div class="pf-c-card pf-l-grid__item pf-m-12-col">
 | 
			
		||||
                        <div class="pf-c-card__body">
 | 
			
		||||
                            <ak-markdown
 | 
			
		||||
                                .md=${MDSourceKerberosBrowser}
 | 
			
		||||
                                meta="users-sources/protocols/kerberos/browser.md"
 | 
			
		||||
                                ;
 | 
			
		||||
                            ></ak-markdown>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </section>
 | 
			
		||||
            <section
 | 
			
		||||
                slot="page-changelog"
 | 
			
		||||
                data-tab-title="${msg("Changelog")}"
 | 
			
		||||
                class="pf-c-page__main-section pf-m-no-padding-mobile"
 | 
			
		||||
            >
 | 
			
		||||
                <div class="pf-l-grid pf-m-gutter">
 | 
			
		||||
                    <div class="pf-c-card pf-l-grid__item pf-m-12-col">
 | 
			
		||||
                        <div class="pf-c-card__body">
 | 
			
		||||
                            <ak-object-changelog
 | 
			
		||||
                                targetModelPk=${this.source.pk || ""}
 | 
			
		||||
                                targetModelApp="authentik_sources_kerberos"
 | 
			
		||||
                                targetModelName="kerberossource"
 | 
			
		||||
                            >
 | 
			
		||||
                            </ak-object-changelog>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </section>
 | 
			
		||||
            <ak-rbac-object-permission-page
 | 
			
		||||
                slot="page-permissions"
 | 
			
		||||
                data-tab-title="${msg("Permissions")}"
 | 
			
		||||
                model=${RbacPermissionsAssignedByUsersListModelEnum.SourcesKerberosKerberossource}
 | 
			
		||||
                objectPk=${this.source.pk}
 | 
			
		||||
            ></ak-rbac-object-permission-page>
 | 
			
		||||
        </ak-tabs>`;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
    interface HTMLElementTagNameMap {
 | 
			
		||||
        "ak-source-kerberos-view": KerberosSourceViewPage;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -2,6 +2,7 @@ import "@goauthentik/admin/rbac/ObjectPermissionModal";
 | 
			
		||||
import "@goauthentik/admin/stages/StageWizard";
 | 
			
		||||
import "@goauthentik/admin/stages/authenticator_duo/AuthenticatorDuoStageForm";
 | 
			
		||||
import "@goauthentik/admin/stages/authenticator_duo/DuoDeviceImportForm";
 | 
			
		||||
import "@goauthentik/admin/stages/authenticator_endpoint_gdtc/AuthenticatorEndpointGDTCStageForm";
 | 
			
		||||
import "@goauthentik/admin/stages/authenticator_sms/AuthenticatorSMSStageForm";
 | 
			
		||||
import "@goauthentik/admin/stages/authenticator_static/AuthenticatorStaticStageForm";
 | 
			
		||||
import "@goauthentik/admin/stages/authenticator_totp/AuthenticatorTOTPStageForm";
 | 
			
		||||
@ -25,8 +26,7 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
			
		||||
import "@goauthentik/elements/forms/DeleteBulkForm";
 | 
			
		||||
import "@goauthentik/elements/forms/ModalForm";
 | 
			
		||||
import "@goauthentik/elements/forms/ProxyForm";
 | 
			
		||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
 | 
			
		||||
import { TableColumn } from "@goauthentik/elements/table/Table";
 | 
			
		||||
import { PaginatedResponse, TableColumn } from "@goauthentik/elements/table/Table";
 | 
			
		||||
import { TablePage } from "@goauthentik/elements/table/TablePage";
 | 
			
		||||
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,75 @@
 | 
			
		||||
import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
 | 
			
		||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
			
		||||
import { first } from "@goauthentik/common/utils";
 | 
			
		||||
import "@goauthentik/elements/CodeMirror";
 | 
			
		||||
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
 | 
			
		||||
import "@goauthentik/elements/forms/FormGroup";
 | 
			
		||||
import "@goauthentik/elements/forms/HorizontalFormElement";
 | 
			
		||||
 | 
			
		||||
import { msg } from "@lit/localize";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement } from "lit/decorators.js";
 | 
			
		||||
 | 
			
		||||
import { AuthenticatorEndpointGDTCStage, StagesApi } from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
@customElement("ak-stage-authenticator-endpoint-gdtc-form")
 | 
			
		||||
export class AuthenticatorEndpointGDTCStageForm extends BaseStageForm<AuthenticatorEndpointGDTCStage> {
 | 
			
		||||
    loadInstance(pk: string): Promise<AuthenticatorEndpointGDTCStage> {
 | 
			
		||||
        return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorEndpointGdtcRetrieve({
 | 
			
		||||
            stageUuid: pk,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async send(data: AuthenticatorEndpointGDTCStage): Promise<AuthenticatorEndpointGDTCStage> {
 | 
			
		||||
        if (this.instance) {
 | 
			
		||||
            return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorEndpointGdtcPartialUpdate({
 | 
			
		||||
                stageUuid: this.instance.pk || "",
 | 
			
		||||
                patchedAuthenticatorEndpointGDTCStageRequest: data,
 | 
			
		||||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
            return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorEndpointGdtcCreate({
 | 
			
		||||
                authenticatorEndpointGDTCStageRequest: data,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    renderForm(): TemplateResult {
 | 
			
		||||
        return html` <span>
 | 
			
		||||
                ${msg(
 | 
			
		||||
                    "Stage used to verify users' browsers using Google Chrome Device Trust. This stage can be used in authentication/authorization flows.",
 | 
			
		||||
                )}
 | 
			
		||||
            </span>
 | 
			
		||||
            <ak-form-element-horizontal label=${msg("Name")} required name="name">
 | 
			
		||||
                <input
 | 
			
		||||
                    type="text"
 | 
			
		||||
                    value="${first(this.instance?.name, "")}"
 | 
			
		||||
                    class="pf-c-form-control"
 | 
			
		||||
                    required
 | 
			
		||||
                />
 | 
			
		||||
            </ak-form-element-horizontal>
 | 
			
		||||
            <ak-form-group expanded>
 | 
			
		||||
                <span slot="header"> ${msg("Google Verified Access API")} </span>
 | 
			
		||||
                <div slot="body" class="pf-c-form">
 | 
			
		||||
                    <ak-form-element-horizontal
 | 
			
		||||
                        label=${msg("Credentials")}
 | 
			
		||||
                        required
 | 
			
		||||
                        name="credentials"
 | 
			
		||||
                    >
 | 
			
		||||
                        <ak-codemirror
 | 
			
		||||
                            mode=${CodeMirrorMode.JavaScript}
 | 
			
		||||
                            .value="${first(this.instance?.credentials, {})}"
 | 
			
		||||
                        ></ak-codemirror>
 | 
			
		||||
                        <p class="pf-c-form__helper-text">
 | 
			
		||||
                            ${msg("Google Cloud credentials file.")}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ak-form-element-horizontal>
 | 
			
		||||
                </div>
 | 
			
		||||
            </ak-form-group>`;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
    interface HTMLElementTagNameMap {
 | 
			
		||||
        "ak-stage-authenticator-endpoint-gdtc-form": AuthenticatorEndpointGDTCStageForm;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -66,6 +66,10 @@ export class PasswordStageForm extends BaseStageForm<PasswordStage> {
 | 
			
		||||
                name: BackendsEnum.SourcesLdapAuthLdapBackend,
 | 
			
		||||
                label: msg("User database + LDAP password"),
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                name: BackendsEnum.SourcesKerberosAuthKerberosBackend,
 | 
			
		||||
                label: msg("User database + Kerberos password"),
 | 
			
		||||
            },
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        return html` <span>
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,12 @@
 | 
			
		||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
			
		||||
import { SentryIgnoredError } from "@goauthentik/common/errors";
 | 
			
		||||
import { deviceTypeName } from "@goauthentik/common/labels";
 | 
			
		||||
import { getRelativeTime } from "@goauthentik/common/utils";
 | 
			
		||||
import "@goauthentik/elements/forms/DeleteBulkForm";
 | 
			
		||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
 | 
			
		||||
import { Table, TableColumn } from "@goauthentik/elements/table/Table";
 | 
			
		||||
 | 
			
		||||
import { msg } from "@lit/localize";
 | 
			
		||||
import { msg, str } from "@lit/localize";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators.js";
 | 
			
		||||
 | 
			
		||||
@ -54,20 +55,21 @@ export class UserDeviceTable extends Table<Device> {
 | 
			
		||||
 | 
			
		||||
    async deleteWrapper(device: Device) {
 | 
			
		||||
        const api = new AuthenticatorsApi(DEFAULT_CONFIG);
 | 
			
		||||
        const id = { id: device.pk };
 | 
			
		||||
        switch (device.type) {
 | 
			
		||||
            case "authentik_stages_authenticator_duo.DuoDevice":
 | 
			
		||||
                return api.authenticatorsAdminDuoDestroy(id);
 | 
			
		||||
                return api.authenticatorsAdminDuoDestroy({ id: parseInt(device.pk, 10) });
 | 
			
		||||
            case "authentik_stages_authenticator_sms.SMSDevice":
 | 
			
		||||
                return api.authenticatorsAdminSmsDestroy(id);
 | 
			
		||||
                return api.authenticatorsAdminSmsDestroy({ id: parseInt(device.pk, 10) });
 | 
			
		||||
            case "authentik_stages_authenticator_totp.TOTPDevice":
 | 
			
		||||
                return api.authenticatorsAdminTotpDestroy(id);
 | 
			
		||||
                return api.authenticatorsAdminTotpDestroy({ id: parseInt(device.pk, 10) });
 | 
			
		||||
            case "authentik_stages_authenticator_static.StaticDevice":
 | 
			
		||||
                return api.authenticatorsAdminStaticDestroy(id);
 | 
			
		||||
                return api.authenticatorsAdminStaticDestroy({ id: parseInt(device.pk, 10) });
 | 
			
		||||
            case "authentik_stages_authenticator_webauthn.WebAuthnDevice":
 | 
			
		||||
                return api.authenticatorsAdminWebauthnDestroy(id);
 | 
			
		||||
                return api.authenticatorsAdminWebauthnDestroy({ id: parseInt(device.pk, 10) });
 | 
			
		||||
            default:
 | 
			
		||||
                break;
 | 
			
		||||
                throw new SentryIgnoredError(
 | 
			
		||||
                    msg(str`Device type ${device.verboseName} cannot be deleted`),
 | 
			
		||||
                );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -103,10 +105,14 @@ export class UserDeviceTable extends Table<Device> {
 | 
			
		||||
            html`${deviceTypeName(item)}
 | 
			
		||||
            ${item.extraDescription ? ` - ${item.extraDescription}` : ""}`,
 | 
			
		||||
            html`${item.confirmed ? msg("Yes") : msg("No")}`,
 | 
			
		||||
            html`<div>${getRelativeTime(item.created)}</div>
 | 
			
		||||
                <small>${item.created.toLocaleString()}</small>`,
 | 
			
		||||
            html`<div>${getRelativeTime(item.lastUpdated)}</div>
 | 
			
		||||
                <small>${item.lastUpdated.toLocaleString()}</small>`,
 | 
			
		||||
            html`${item.created.getTime() > 0
 | 
			
		||||
                ? html`<div>${getRelativeTime(item.created)}</div>
 | 
			
		||||
                      <small>${item.created.toLocaleString()}</small>`
 | 
			
		||||
                : html`-`}`,
 | 
			
		||||
            html`${item.lastUpdated
 | 
			
		||||
                ? html`<div>${getRelativeTime(item.lastUpdated)}</div>
 | 
			
		||||
                      <small>${item.lastUpdated.toLocaleString()}</small>`
 | 
			
		||||
                : html`-`}`,
 | 
			
		||||
            html`${item.lastUsed
 | 
			
		||||
                ? html`<div>${getRelativeTime(item.lastUsed)}</div>
 | 
			
		||||
                      <small>${item.lastUsed.toLocaleString()}</small>`
 | 
			
		||||
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 882 KiB After Width: | Height: | Size: 671 KiB  | 
							
								
								
									
										21
									
								
								web/src/common/purify.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								web/src/common/purify.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
			
		||||
import DOMPurify from "dompurify";
 | 
			
		||||
 | 
			
		||||
import { render } from "@lit-labs/ssr";
 | 
			
		||||
import { collectResult } from "@lit-labs/ssr/lib/render-result.js";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { unsafeHTML } from "lit/directives/unsafe-html.js";
 | 
			
		||||
import { until } from "lit/directives/until.js";
 | 
			
		||||
 | 
			
		||||
export const DOM_PURIFY_STRICT: DOMPurify.Config = {
 | 
			
		||||
    ALLOWED_TAGS: ["#text"],
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function purify(input: TemplateResult): TemplateResult {
 | 
			
		||||
    return html`${until(
 | 
			
		||||
        (async () => {
 | 
			
		||||
            const rendered = await collectResult(render(input));
 | 
			
		||||
            const purified = DOMPurify.sanitize(rendered);
 | 
			
		||||
            return html`${unsafeHTML(purified)}`;
 | 
			
		||||
        })(),
 | 
			
		||||
    )}`;
 | 
			
		||||
}
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
import { EVENT_REFRESH, EVENT_THEME_CHANGE } from "@goauthentik/common/constants";
 | 
			
		||||
import { DOM_PURIFY_STRICT } from "@goauthentik/common/purify";
 | 
			
		||||
import { AKElement } from "@goauthentik/elements/Base";
 | 
			
		||||
import "@goauthentik/elements/EmptyState";
 | 
			
		||||
import mermaid, { MermaidConfig } from "mermaid";
 | 
			
		||||
@ -47,6 +48,8 @@ export class Diagram extends AKElement {
 | 
			
		||||
                curve: "linear",
 | 
			
		||||
            },
 | 
			
		||||
            htmlLabels: false,
 | 
			
		||||
            securityLevel: "strict",
 | 
			
		||||
            dompurifyConfig: DOM_PURIFY_STRICT,
 | 
			
		||||
        };
 | 
			
		||||
        mermaid.initialize(this.config);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -86,8 +86,8 @@ export class RacInterface extends Interface {
 | 
			
		||||
    static domSize(): { width: number; height: number } {
 | 
			
		||||
        const size = document.body.getBoundingClientRect();
 | 
			
		||||
        return {
 | 
			
		||||
            width: size.width * window.devicePixelRatio,
 | 
			
		||||
            height: size.height * window.devicePixelRatio,
 | 
			
		||||
            width: size.width,
 | 
			
		||||
            height: size.height,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -175,7 +175,6 @@ export class RacInterface extends Interface {
 | 
			
		||||
        const params = new URLSearchParams();
 | 
			
		||||
        params.set("screen_width", Math.floor(RacInterface.domSize().width).toString());
 | 
			
		||||
        params.set("screen_height", Math.floor(RacInterface.domSize().height).toString());
 | 
			
		||||
        params.set("screen_dpi", (window.devicePixelRatio * 96).toString());
 | 
			
		||||
        this.client.connect(params.toString());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@ import {
 | 
			
		||||
    TITLE_DEFAULT,
 | 
			
		||||
} from "@goauthentik/common/constants";
 | 
			
		||||
import { globalAK } from "@goauthentik/common/global";
 | 
			
		||||
import { purify } from "@goauthentik/common/purify";
 | 
			
		||||
import { configureSentry } from "@goauthentik/common/sentry";
 | 
			
		||||
import { first } from "@goauthentik/common/utils";
 | 
			
		||||
import { WebsocketClient } from "@goauthentik/common/ws";
 | 
			
		||||
@ -16,6 +17,7 @@ import { themeImage } from "@goauthentik/elements/utils/images";
 | 
			
		||||
import "@goauthentik/flow/sources/apple/AppleLoginInit";
 | 
			
		||||
import "@goauthentik/flow/sources/plex/PlexLoginInit";
 | 
			
		||||
import "@goauthentik/flow/stages/FlowErrorStage";
 | 
			
		||||
import "@goauthentik/flow/stages/FlowFrameStage";
 | 
			
		||||
import "@goauthentik/flow/stages/RedirectStage";
 | 
			
		||||
import { StageHost, SubmitOptions } from "@goauthentik/flow/stages/base";
 | 
			
		||||
 | 
			
		||||
@ -170,6 +172,19 @@ export class FlowExecutor extends Interface implements StageHost {
 | 
			
		||||
        this.addEventListener(EVENT_FLOW_INSPECTOR_TOGGLE, () => {
 | 
			
		||||
            this.inspectorOpen = !this.inspectorOpen;
 | 
			
		||||
        });
 | 
			
		||||
        window.addEventListener("message", (event) => {
 | 
			
		||||
            const msg: {
 | 
			
		||||
                source?: string;
 | 
			
		||||
                context?: string;
 | 
			
		||||
                message: string;
 | 
			
		||||
            } = event.data;
 | 
			
		||||
            if (msg.source !== "goauthentik.io" || msg.context !== "flow-executor") {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if (msg.message === "submit") {
 | 
			
		||||
                this.submit({} as FlowChallengeResponseRequest);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async getTheme(): Promise<UiThemeEnum> {
 | 
			
		||||
@ -429,6 +444,11 @@ export class FlowExecutor extends Interface implements StageHost {
 | 
			
		||||
                </ak-stage-redirect>`;
 | 
			
		||||
            case "xak-flow-shell":
 | 
			
		||||
                return html`${unsafeHTML((this.challenge as ShellChallenge).body)}`;
 | 
			
		||||
            case "xak-flow-frame":
 | 
			
		||||
                return html`<xak-flow-frame
 | 
			
		||||
                    .host=${this as StageHost}
 | 
			
		||||
                    .challenge=${this.challenge}
 | 
			
		||||
                ></xak-flow-frame>`;
 | 
			
		||||
            default:
 | 
			
		||||
                return html`Invalid native challenge element`;
 | 
			
		||||
        }
 | 
			
		||||
@ -499,9 +519,13 @@ export class FlowExecutor extends Interface implements StageHost {
 | 
			
		||||
                                            <ul class="pf-c-list pf-m-inline">
 | 
			
		||||
                                                ${this.brand?.uiFooterLinks?.map((link) => {
 | 
			
		||||
                                                    if (link.href) {
 | 
			
		||||
                                                        return html`<li>
 | 
			
		||||
                                                            <a href="${link.href}">${link.name}</a>
 | 
			
		||||
                                                        </li>`;
 | 
			
		||||
                                                        return html`${purify(
 | 
			
		||||
                                                            html`<li>
 | 
			
		||||
                                                                <a href="${link.href}"
 | 
			
		||||
                                                                    >${link.name}</a
 | 
			
		||||
                                                                >
 | 
			
		||||
                                                            </li>`,
 | 
			
		||||
                                                        )}`;
 | 
			
		||||
                                                    }
 | 
			
		||||
                                                    return html`<li>
 | 
			
		||||
                                                        <span>${link.name}</span>
 | 
			
		||||
 | 
			
		||||
@ -114,7 +114,7 @@ export class InputPassword extends AKElement {
 | 
			
		||||
        this.input.type = "password";
 | 
			
		||||
        this.input.name = this.name;
 | 
			
		||||
        this.input.placeholder = this.placeholder;
 | 
			
		||||
        this.input.autofocus = true;
 | 
			
		||||
        this.input.autofocus = this.grabFocus;
 | 
			
		||||
        this.input.autocomplete = "current-password";
 | 
			
		||||
        this.input.classList.add("pf-c-form-control");
 | 
			
		||||
        this.input.required = true;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										54
									
								
								web/src/flow/stages/FlowFrameStage.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								web/src/flow/stages/FlowFrameStage.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,54 @@
 | 
			
		||||
import "@goauthentik/elements/EmptyState";
 | 
			
		||||
import "@goauthentik/flow/FormStatic";
 | 
			
		||||
import { BaseStage } from "@goauthentik/flow/stages/base";
 | 
			
		||||
 | 
			
		||||
import { CSSResult, TemplateResult, css, html, nothing } from "lit";
 | 
			
		||||
import { customElement } from "lit/decorators.js";
 | 
			
		||||
 | 
			
		||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
 | 
			
		||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
 | 
			
		||||
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
 | 
			
		||||
import PFTitle from "@patternfly/patternfly/components/Title/title.css";
 | 
			
		||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
 | 
			
		||||
 | 
			
		||||
import { FrameChallenge, FrameChallengeResponseRequest } from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
@customElement("xak-flow-frame")
 | 
			
		||||
export class FlowFrameStage extends BaseStage<FrameChallenge, FrameChallengeResponseRequest> {
 | 
			
		||||
    static get styles(): CSSResult[] {
 | 
			
		||||
        return [PFBase, PFLogin, PFForm, PFFormControl, PFTitle, css``];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    render(): TemplateResult {
 | 
			
		||||
        if (!this.challenge) {
 | 
			
		||||
            return html`<ak-empty-state loading> </ak-empty-state>`;
 | 
			
		||||
        }
 | 
			
		||||
        return html` <header class="pf-c-login__main-header">
 | 
			
		||||
                <h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
 | 
			
		||||
            </header>
 | 
			
		||||
            <div class="pf-c-login__main-body">
 | 
			
		||||
                ${this.challenge.loadingOverlay
 | 
			
		||||
                    ? html`<ak-empty-state
 | 
			
		||||
                          loading
 | 
			
		||||
                          header=${this.challenge.loadingText ?? undefined}
 | 
			
		||||
                      >
 | 
			
		||||
                      </ak-empty-state>`
 | 
			
		||||
                    : nothing}
 | 
			
		||||
                <iframe
 | 
			
		||||
                    style=${this.challenge.loadingOverlay
 | 
			
		||||
                        ? "width:0;height:0;position:absolute;"
 | 
			
		||||
                        : ""}
 | 
			
		||||
                    src=${this.challenge.url}
 | 
			
		||||
                ></iframe>
 | 
			
		||||
            </div>
 | 
			
		||||
            <footer class="pf-c-login__main-footer">
 | 
			
		||||
                <ul class="pf-c-login__main-footer-links"></ul>
 | 
			
		||||
            </footer>`;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
    interface HTMLElementTagNameMap {
 | 
			
		||||
        "xak-flow-frame": FlowFrameStage;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -43,8 +43,17 @@ export class RedirectStage extends BaseStage<RedirectChallenge, FlowChallengeRes
 | 
			
		||||
 | 
			
		||||
    firstUpdated(): void {
 | 
			
		||||
        if (this.promptUser) {
 | 
			
		||||
            document.addEventListener("keydown", (ev) => {
 | 
			
		||||
                if (ev.key === "Enter") {
 | 
			
		||||
                    this.redirect();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        this.redirect();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    redirect() {
 | 
			
		||||
        console.debug(
 | 
			
		||||
            "authentik/stages/redirect: redirecting to url from server",
 | 
			
		||||
            this.challenge.to,
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,9 @@
 | 
			
		||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
			
		||||
import { SentryIgnoredError } from "@goauthentik/common/errors";
 | 
			
		||||
import "@goauthentik/elements/forms/HorizontalFormElement";
 | 
			
		||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
 | 
			
		||||
 | 
			
		||||
import { msg } from "@lit/localize";
 | 
			
		||||
import { msg, str } from "@lit/localize";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators.js";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined.js";
 | 
			
		||||
@ -10,11 +11,11 @@ import { ifDefined } from "lit/directives/if-defined.js";
 | 
			
		||||
import { AuthenticatorsApi, Device } from "@goauthentik/api";
 | 
			
		||||
 | 
			
		||||
@customElement("ak-user-mfa-form")
 | 
			
		||||
export class MFADeviceForm extends ModelForm<Device, number> {
 | 
			
		||||
export class MFADeviceForm extends ModelForm<Device, string> {
 | 
			
		||||
    @property()
 | 
			
		||||
    deviceType!: string;
 | 
			
		||||
 | 
			
		||||
    async loadInstance(pk: number): Promise<Device> {
 | 
			
		||||
    async loadInstance(pk: string): Promise<Device> {
 | 
			
		||||
        const devices = await new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsAllList();
 | 
			
		||||
        return devices.filter((device) => {
 | 
			
		||||
            return device.pk === pk && device.type === this.deviceType;
 | 
			
		||||
@ -29,36 +30,38 @@ export class MFADeviceForm extends ModelForm<Device, number> {
 | 
			
		||||
        switch (this.instance?.type) {
 | 
			
		||||
            case "authentik_stages_authenticator_duo.DuoDevice":
 | 
			
		||||
                await new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsDuoUpdate({
 | 
			
		||||
                    id: this.instance?.pk,
 | 
			
		||||
                    id: parseInt(this.instance?.pk, 10),
 | 
			
		||||
                    duoDeviceRequest: device,
 | 
			
		||||
                });
 | 
			
		||||
                break;
 | 
			
		||||
            case "authentik_stages_authenticator_sms.SMSDevice":
 | 
			
		||||
                await new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsSmsUpdate({
 | 
			
		||||
                    id: this.instance?.pk,
 | 
			
		||||
                    id: parseInt(this.instance?.pk, 10),
 | 
			
		||||
                    sMSDeviceRequest: device,
 | 
			
		||||
                });
 | 
			
		||||
                break;
 | 
			
		||||
            case "authentik_stages_authenticator_totp.TOTPDevice":
 | 
			
		||||
                await new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsTotpUpdate({
 | 
			
		||||
                    id: this.instance?.pk,
 | 
			
		||||
                    id: parseInt(this.instance?.pk, 10),
 | 
			
		||||
                    tOTPDeviceRequest: device,
 | 
			
		||||
                });
 | 
			
		||||
                break;
 | 
			
		||||
            case "authentik_stages_authenticator_static.StaticDevice":
 | 
			
		||||
                await new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsStaticUpdate({
 | 
			
		||||
                    id: this.instance?.pk,
 | 
			
		||||
                    id: parseInt(this.instance?.pk, 10),
 | 
			
		||||
                    staticDeviceRequest: device,
 | 
			
		||||
                });
 | 
			
		||||
                break;
 | 
			
		||||
            case "authentik_stages_authenticator_webauthn.WebAuthnDevice":
 | 
			
		||||
                await new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsWebauthnUpdate({
 | 
			
		||||
                    id: this.instance?.pk,
 | 
			
		||||
                    id: parseInt(this.instance?.pk, 10),
 | 
			
		||||
                    webAuthnDeviceRequest: device,
 | 
			
		||||
                });
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                break;
 | 
			
		||||
                throw new SentryIgnoredError(
 | 
			
		||||
                    msg(str`Device type ${device.verboseName} cannot be edited`),
 | 
			
		||||
                );
 | 
			
		||||
        }
 | 
			
		||||
        return device;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
import { AndNext, DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
			
		||||
import { SentryIgnoredError } from "@goauthentik/common/errors";
 | 
			
		||||
import { deviceTypeName } from "@goauthentik/common/labels";
 | 
			
		||||
import { getRelativeTime } from "@goauthentik/common/utils";
 | 
			
		||||
import "@goauthentik/elements/buttons/Dropdown";
 | 
			
		||||
@ -10,7 +11,7 @@ import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/tab
 | 
			
		||||
import "@goauthentik/user/user-settings/mfa/MFADeviceForm";
 | 
			
		||||
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
 | 
			
		||||
 | 
			
		||||
import { msg } from "@lit/localize";
 | 
			
		||||
import { msg, str } from "@lit/localize";
 | 
			
		||||
import { TemplateResult, html } from "lit";
 | 
			
		||||
import { customElement, property } from "lit/decorators.js";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined.js";
 | 
			
		||||
@ -89,7 +90,7 @@ export class MFADevicesPage extends Table<Device> {
 | 
			
		||||
 | 
			
		||||
    async deleteWrapper(device: Device) {
 | 
			
		||||
        const api = new AuthenticatorsApi(DEFAULT_CONFIG);
 | 
			
		||||
        const id = { id: device.pk };
 | 
			
		||||
        const id = { id: parseInt(device.pk, 10) };
 | 
			
		||||
        switch (device.type) {
 | 
			
		||||
            case "authentik_stages_authenticator_duo.DuoDevice":
 | 
			
		||||
                return api.authenticatorsDuoDestroy(id);
 | 
			
		||||
@ -102,7 +103,9 @@ export class MFADevicesPage extends Table<Device> {
 | 
			
		||||
            case "authentik_stages_authenticator_webauthn.WebAuthnDevice":
 | 
			
		||||
                return api.authenticatorsWebauthnDestroy(id);
 | 
			
		||||
            default:
 | 
			
		||||
                break;
 | 
			
		||||
                throw new SentryIgnoredError(
 | 
			
		||||
                    msg(str`Device type ${device.verboseName} cannot be deleted`),
 | 
			
		||||
                );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -44,10 +44,7 @@ describe("Configure Oauth2 Providers", () => {
 | 
			
		||||
            [setTypeCreate, "selectProviderType", "OAuth2/OpenID Provider"],
 | 
			
		||||
            [clickButton, "Next"],
 | 
			
		||||
            [setTextInput, "name", newProviderName],
 | 
			
		||||
            [setFormGroup, /Flow settings/, "open"],
 | 
			
		||||
            [setSearchSelect, "authenticationFlow", "default-authentication-flow"],
 | 
			
		||||
            [setSearchSelect, "authorizationFlow", "default-provider-authorization-explicit-consent"],
 | 
			
		||||
            [setSearchSelect, "invalidationFlow", "default-invalidation-flow"],
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        await ProviderWizardView.pause();
 | 
			
		||||
@ -77,3 +74,23 @@ describe("Configure LDAP Providers", () => {
 | 
			
		||||
        await ProviderWizardView.nextButton.click();
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe("Configure Radius Providers", () => {
 | 
			
		||||
    it("Should configure a simple Radius Provider", async () => {
 | 
			
		||||
        const newProviderName = `New Radius Provider - ${randomId()}`;
 | 
			
		||||
 | 
			
		||||
        await reachTheProvider();
 | 
			
		||||
        await $("ak-wizard-page-type-create").waitForDisplayed();
 | 
			
		||||
 | 
			
		||||
        // prettier-ignore
 | 
			
		||||
        await fillOutFields([
 | 
			
		||||
            [setTypeCreate, "selectProviderType", "Radius Provider"],
 | 
			
		||||
            [clickButton, "Next"],
 | 
			
		||||
            [setTextInput, "name", newProviderName],
 | 
			
		||||
            [setSearchSelect, "authorizationFlow", "default-authentication-flow"],
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        await ProviderWizardView.pause();
 | 
			
		||||
        await ProviderWizardView.nextButton.click();
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
@ -6922,6 +6922,24 @@ Bindings to groups/users are checked against the user of the event.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s521681ed1d5ff814">
 | 
			
		||||
  <source>Log back into <x id="0" equiv-text="${this.challenge.applicationName}"/></source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sc991a35f5e88d1d3">
 | 
			
		||||
  <source>Encryption Key</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s8a598f7aef81c3bc">
 | 
			
		||||
  <source>Key used to encrypt the tokens.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sbfee780fa0a2c83e">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be deleted</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s336936629cdeb3e5">
 | 
			
		||||
  <source>Stage used to verify users' browsers using Google Chrome Device Trust. This stage can be used in authentication/authorization flows.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s85fe794c71b4ace8">
 | 
			
		||||
  <source>Google Verified Access API</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s013620384af7c8b4">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be edited</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
    </body>
 | 
			
		||||
  </file>
 | 
			
		||||
 | 
			
		||||
@ -7187,6 +7187,24 @@ Bindings to groups/users are checked against the user of the event.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s521681ed1d5ff814">
 | 
			
		||||
  <source>Log back into <x id="0" equiv-text="${this.challenge.applicationName}"/></source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sc991a35f5e88d1d3">
 | 
			
		||||
  <source>Encryption Key</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s8a598f7aef81c3bc">
 | 
			
		||||
  <source>Key used to encrypt the tokens.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sbfee780fa0a2c83e">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be deleted</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s336936629cdeb3e5">
 | 
			
		||||
  <source>Stage used to verify users' browsers using Google Chrome Device Trust. This stage can be used in authentication/authorization flows.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s85fe794c71b4ace8">
 | 
			
		||||
  <source>Google Verified Access API</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s013620384af7c8b4">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be edited</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
    </body>
 | 
			
		||||
  </file>
 | 
			
		||||
 | 
			
		||||
@ -6839,6 +6839,24 @@ Bindings to groups/users are checked against the user of the event.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s521681ed1d5ff814">
 | 
			
		||||
  <source>Log back into <x id="0" equiv-text="${this.challenge.applicationName}"/></source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sc991a35f5e88d1d3">
 | 
			
		||||
  <source>Encryption Key</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s8a598f7aef81c3bc">
 | 
			
		||||
  <source>Key used to encrypt the tokens.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sbfee780fa0a2c83e">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be deleted</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s336936629cdeb3e5">
 | 
			
		||||
  <source>Stage used to verify users' browsers using Google Chrome Device Trust. This stage can be used in authentication/authorization flows.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s85fe794c71b4ace8">
 | 
			
		||||
  <source>Google Verified Access API</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s013620384af7c8b4">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be edited</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
    </body>
 | 
			
		||||
  </file>
 | 
			
		||||
 | 
			
		||||
@ -9120,6 +9120,26 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
 | 
			
		||||
<trans-unit id="s521681ed1d5ff814">
 | 
			
		||||
  <source>Log back into <x id="0" equiv-text="${this.challenge.applicationName}"/></source>
 | 
			
		||||
  <target>Se reconnecter à <x id="0" equiv-text="${this.challenge.applicationName}"/></target>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sc991a35f5e88d1d3">
 | 
			
		||||
  <source>Encryption Key</source>
 | 
			
		||||
  <target>Clé de chiffrement</target>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s8a598f7aef81c3bc">
 | 
			
		||||
  <source>Key used to encrypt the tokens.</source>
 | 
			
		||||
  <target>Clé utilisée pour chiffrer les jetons.</target>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sbfee780fa0a2c83e">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be deleted</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s336936629cdeb3e5">
 | 
			
		||||
  <source>Stage used to verify users' browsers using Google Chrome Device Trust. This stage can be used in authentication/authorization flows.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s85fe794c71b4ace8">
 | 
			
		||||
  <source>Google Verified Access API</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s013620384af7c8b4">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be edited</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
    </body>
 | 
			
		||||
  </file>
 | 
			
		||||
 | 
			
		||||
@ -8756,6 +8756,24 @@ Bindings to groups/users are checked against the user of the event.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s521681ed1d5ff814">
 | 
			
		||||
  <source>Log back into <x id="0" equiv-text="${this.challenge.applicationName}"/></source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sc991a35f5e88d1d3">
 | 
			
		||||
  <source>Encryption Key</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s8a598f7aef81c3bc">
 | 
			
		||||
  <source>Key used to encrypt the tokens.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sbfee780fa0a2c83e">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be deleted</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s336936629cdeb3e5">
 | 
			
		||||
  <source>Stage used to verify users' browsers using Google Chrome Device Trust. This stage can be used in authentication/authorization flows.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s85fe794c71b4ace8">
 | 
			
		||||
  <source>Google Verified Access API</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s013620384af7c8b4">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be edited</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
    </body>
 | 
			
		||||
  </file>
 | 
			
		||||
 | 
			
		||||
@ -8602,6 +8602,24 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s521681ed1d5ff814">
 | 
			
		||||
  <source>Log back into <x id="0" equiv-text="${this.challenge.applicationName}"/></source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sc991a35f5e88d1d3">
 | 
			
		||||
  <source>Encryption Key</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s8a598f7aef81c3bc">
 | 
			
		||||
  <source>Key used to encrypt the tokens.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sbfee780fa0a2c83e">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be deleted</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s336936629cdeb3e5">
 | 
			
		||||
  <source>Stage used to verify users' browsers using Google Chrome Device Trust. This stage can be used in authentication/authorization flows.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s85fe794c71b4ace8">
 | 
			
		||||
  <source>Google Verified Access API</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s013620384af7c8b4">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be edited</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
    </body>
 | 
			
		||||
  </file>
 | 
			
		||||
 | 
			
		||||
@ -9021,6 +9021,24 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s521681ed1d5ff814">
 | 
			
		||||
  <source>Log back into <x id="0" equiv-text="${this.challenge.applicationName}"/></source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sc991a35f5e88d1d3">
 | 
			
		||||
  <source>Encryption Key</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s8a598f7aef81c3bc">
 | 
			
		||||
  <source>Key used to encrypt the tokens.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sbfee780fa0a2c83e">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be deleted</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s336936629cdeb3e5">
 | 
			
		||||
  <source>Stage used to verify users' browsers using Google Chrome Device Trust. This stage can be used in authentication/authorization flows.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s85fe794c71b4ace8">
 | 
			
		||||
  <source>Google Verified Access API</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s013620384af7c8b4">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be edited</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
    </body>
 | 
			
		||||
  </file>
 | 
			
		||||
 | 
			
		||||
@ -9061,4 +9061,22 @@ Bindings to groups/users are checked against the user of the event.</source>
 | 
			
		||||
<trans-unit id="s521681ed1d5ff814">
 | 
			
		||||
  <source>Log back into <x id="0" equiv-text="${this.challenge.applicationName}"/></source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sc991a35f5e88d1d3">
 | 
			
		||||
  <source>Encryption Key</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s8a598f7aef81c3bc">
 | 
			
		||||
  <source>Key used to encrypt the tokens.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sbfee780fa0a2c83e">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be deleted</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s336936629cdeb3e5">
 | 
			
		||||
  <source>Stage used to verify users' browsers using Google Chrome Device Trust. This stage can be used in authentication/authorization flows.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s85fe794c71b4ace8">
 | 
			
		||||
  <source>Google Verified Access API</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s013620384af7c8b4">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be edited</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
</body></file></xliff>
 | 
			
		||||
 | 
			
		||||
@ -9084,6 +9084,24 @@ Bindings to groups/users are checked against the user of the event.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s521681ed1d5ff814">
 | 
			
		||||
  <source>Log back into <x id="0" equiv-text="${this.challenge.applicationName}"/></source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sc991a35f5e88d1d3">
 | 
			
		||||
  <source>Encryption Key</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s8a598f7aef81c3bc">
 | 
			
		||||
  <source>Key used to encrypt the tokens.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sbfee780fa0a2c83e">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be deleted</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s336936629cdeb3e5">
 | 
			
		||||
  <source>Stage used to verify users' browsers using Google Chrome Device Trust. This stage can be used in authentication/authorization flows.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s85fe794c71b4ace8">
 | 
			
		||||
  <source>Google Verified Access API</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s013620384af7c8b4">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be edited</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
    </body>
 | 
			
		||||
  </file>
 | 
			
		||||
 | 
			
		||||
@ -6832,6 +6832,24 @@ Bindings to groups/users are checked against the user of the event.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s521681ed1d5ff814">
 | 
			
		||||
  <source>Log back into <x id="0" equiv-text="${this.challenge.applicationName}"/></source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sc991a35f5e88d1d3">
 | 
			
		||||
  <source>Encryption Key</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s8a598f7aef81c3bc">
 | 
			
		||||
  <source>Key used to encrypt the tokens.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sbfee780fa0a2c83e">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be deleted</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s336936629cdeb3e5">
 | 
			
		||||
  <source>Stage used to verify users' browsers using Google Chrome Device Trust. This stage can be used in authentication/authorization flows.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s85fe794c71b4ace8">
 | 
			
		||||
  <source>Google Verified Access API</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s013620384af7c8b4">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be edited</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
    </body>
 | 
			
		||||
  </file>
 | 
			
		||||
 | 
			
		||||
@ -5770,6 +5770,24 @@ Bindings to groups/users are checked against the user of the event.</source>
 | 
			
		||||
<trans-unit id="s521681ed1d5ff814">
 | 
			
		||||
  <source>Log back into <x id="0" equiv-text="${this.challenge.applicationName}"/></source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sc991a35f5e88d1d3">
 | 
			
		||||
  <source>Encryption Key</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s8a598f7aef81c3bc">
 | 
			
		||||
  <source>Key used to encrypt the tokens.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sbfee780fa0a2c83e">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be deleted</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s336936629cdeb3e5">
 | 
			
		||||
  <source>Stage used to verify users' browsers using Google Chrome Device Trust. This stage can be used in authentication/authorization flows.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s85fe794c71b4ace8">
 | 
			
		||||
  <source>Google Verified Access API</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s013620384af7c8b4">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be edited</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
</body>
 | 
			
		||||
</file>
 | 
			
		||||
</xliff>
 | 
			
		||||
 | 
			
		||||
@ -9093,27 +9093,55 @@ Bindings to groups/users are checked against the user of the event.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s0159c800f16ee1a5">
 | 
			
		||||
  <source>Flow used when logging out of this provider.</source>
 | 
			
		||||
  <target>登出此提供程序时使用的流程。</target>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s821d70ef9496e6f7">
 | 
			
		||||
  <source>Unbind flow</source>
 | 
			
		||||
  <target>取消绑定流程</target>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s6773ec2a233bbf41">
 | 
			
		||||
  <source>Flow used for unbinding users.</source>
 | 
			
		||||
  <target>用于取消绑定用户的流程。</target>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s99060120f626df5b">
 | 
			
		||||
  <source>Verify SCIM server's certificates</source>
 | 
			
		||||
  <target>验证 SCIM 服务器证书</target>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sac88482c48453fc8">
 | 
			
		||||
  <source>You've logged out of <x id="0" equiv-text="${this.challenge.applicationName}"/>. You can go back to the overview to launch another application, or log out of your authentik account.</source>
 | 
			
		||||
  <target>您已成功登出 <x id="0" equiv-text="${this.challenge.applicationName}"/>。现在您可以返回总览页来启动其他应用,或者登出您的 authentik 账户。</target>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s3108167b562674e2">
 | 
			
		||||
  <source>Go back to overview</source>
 | 
			
		||||
  <target>返回总览</target>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sdb749e793de55478">
 | 
			
		||||
  <source>Log out of <x id="0" equiv-text="${this.challenge.brandName}"/></source>
 | 
			
		||||
  <target>登出 <x id="0" equiv-text="${this.challenge.brandName}"/></target>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s521681ed1d5ff814">
 | 
			
		||||
  <source>Log back into <x id="0" equiv-text="${this.challenge.applicationName}"/></source>
 | 
			
		||||
  <target>重新登录 <x id="0" equiv-text="${this.challenge.applicationName}"/></target>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sc991a35f5e88d1d3">
 | 
			
		||||
  <source>Encryption Key</source>
 | 
			
		||||
  <target>加密密钥</target>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s8a598f7aef81c3bc">
 | 
			
		||||
  <source>Key used to encrypt the tokens.</source>
 | 
			
		||||
  <target>用于加密令牌的密钥。</target>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sbfee780fa0a2c83e">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be deleted</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s336936629cdeb3e5">
 | 
			
		||||
  <source>Stage used to verify users' browsers using Google Chrome Device Trust. This stage can be used in authentication/authorization flows.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s85fe794c71b4ace8">
 | 
			
		||||
  <source>Google Verified Access API</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s013620384af7c8b4">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be edited</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
    </body>
 | 
			
		||||
  </file>
 | 
			
		||||
 | 
			
		||||
@ -6880,6 +6880,24 @@ Bindings to groups/users are checked against the user of the event.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s521681ed1d5ff814">
 | 
			
		||||
  <source>Log back into <x id="0" equiv-text="${this.challenge.applicationName}"/></source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sc991a35f5e88d1d3">
 | 
			
		||||
  <source>Encryption Key</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s8a598f7aef81c3bc">
 | 
			
		||||
  <source>Key used to encrypt the tokens.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sbfee780fa0a2c83e">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be deleted</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s336936629cdeb3e5">
 | 
			
		||||
  <source>Stage used to verify users' browsers using Google Chrome Device Trust. This stage can be used in authentication/authorization flows.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s85fe794c71b4ace8">
 | 
			
		||||
  <source>Google Verified Access API</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s013620384af7c8b4">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be edited</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
    </body>
 | 
			
		||||
  </file>
 | 
			
		||||
 | 
			
		||||
@ -836,7 +836,7 @@
 | 
			
		||||
      </trans-unit>
 | 
			
		||||
      <trans-unit id="sff69c1a637f899a6">
 | 
			
		||||
        <source>Bind flow</source>
 | 
			
		||||
        <target>Bind 流程</target>
 | 
			
		||||
        <target>绑定流程</target>
 | 
			
		||||
        
 | 
			
		||||
      </trans-unit>
 | 
			
		||||
      <trans-unit id="s319040353f479853">
 | 
			
		||||
@ -1918,12 +1918,12 @@
 | 
			
		||||
      </trans-unit>
 | 
			
		||||
      <trans-unit id="sb7794c2910b1a9ec">
 | 
			
		||||
        <source>Bind DN</source>
 | 
			
		||||
        <target>Bind DN</target>
 | 
			
		||||
        <target>绑定 DN</target>
 | 
			
		||||
        
 | 
			
		||||
      </trans-unit>
 | 
			
		||||
      <trans-unit id="s5694f9421c428227">
 | 
			
		||||
        <source>Bind Password</source>
 | 
			
		||||
        <target>Bind 密码</target>
 | 
			
		||||
        <target>绑定密码</target>
 | 
			
		||||
        
 | 
			
		||||
      </trans-unit>
 | 
			
		||||
      <trans-unit id="s086e1bbe7c97ea16">
 | 
			
		||||
@ -2847,7 +2847,7 @@ doesn't pass when either or both of the selected options are equal or above the
 | 
			
		||||
      </trans-unit>
 | 
			
		||||
      <trans-unit id="sb7684e2910a33a1f">
 | 
			
		||||
        <source>Bind CN</source>
 | 
			
		||||
        <target>Bind CN</target>
 | 
			
		||||
        <target>绑定 CN</target>
 | 
			
		||||
        
 | 
			
		||||
      </trans-unit>
 | 
			
		||||
      <trans-unit id="s3de6db803012016a">
 | 
			
		||||
@ -8731,10 +8731,6 @@ Bindings to groups/users are checked against the user of the event.</source>
 | 
			
		||||
  <source>Search returned no results.</source>
 | 
			
		||||
  <target>搜索未返回结果。</target>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s11ec812e25ceef8a">
 | 
			
		||||
  <source>No messages found</source>
 | 
			
		||||
  <target>未找到消息</target>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s23446284b56ca0cc">
 | 
			
		||||
  <source>Reputation score(s)</source>
 | 
			
		||||
  <target>信誉分数</target>
 | 
			
		||||
@ -9094,6 +9090,46 @@ Bindings to groups/users are checked against the user of the event.</source>
 | 
			
		||||
<trans-unit id="s25bacc19d98b444e">
 | 
			
		||||
  <source>Parent Group</source>
 | 
			
		||||
  <target>父组</target>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s0159c800f16ee1a5">
 | 
			
		||||
  <source>Flow used when logging out of this provider.</source>
 | 
			
		||||
  <target>登出此提供程序时使用的流程。</target>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s821d70ef9496e6f7">
 | 
			
		||||
  <source>Unbind flow</source>
 | 
			
		||||
  <target>取消绑定流程</target>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s6773ec2a233bbf41">
 | 
			
		||||
  <source>Flow used for unbinding users.</source>
 | 
			
		||||
  <target>用于取消绑定用户的流程。</target>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s99060120f626df5b">
 | 
			
		||||
  <source>Verify SCIM server's certificates</source>
 | 
			
		||||
  <target>验证 SCIM 服务器证书</target>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sac88482c48453fc8">
 | 
			
		||||
  <source>You've logged out of <x id="0" equiv-text="${this.challenge.applicationName}"/>. You can go back to the overview to launch another application, or log out of your authentik account.</source>
 | 
			
		||||
  <target>您已成功登出 <x id="0" equiv-text="${this.challenge.applicationName}"/>。现在您可以返回总览页来启动其他应用,或者登出您的 authentik 账户。</target>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s3108167b562674e2">
 | 
			
		||||
  <source>Go back to overview</source>
 | 
			
		||||
  <target>返回总览</target>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sdb749e793de55478">
 | 
			
		||||
  <source>Log out of <x id="0" equiv-text="${this.challenge.brandName}"/></source>
 | 
			
		||||
  <target>登出 <x id="0" equiv-text="${this.challenge.brandName}"/></target>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s521681ed1d5ff814">
 | 
			
		||||
  <source>Log back into <x id="0" equiv-text="${this.challenge.applicationName}"/></source>
 | 
			
		||||
  <target>重新登录 <x id="0" equiv-text="${this.challenge.applicationName}"/></target>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sc991a35f5e88d1d3">
 | 
			
		||||
  <source>Encryption Key</source>
 | 
			
		||||
  <target>加密密钥</target>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s8a598f7aef81c3bc">
 | 
			
		||||
  <source>Key used to encrypt the tokens.</source>
 | 
			
		||||
  <target>用于加密令牌的密钥。</target>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
    </body>
 | 
			
		||||
  </file>
 | 
			
		||||
 | 
			
		||||
@ -8717,6 +8717,24 @@ Bindings to groups/users are checked against the user of the event.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s521681ed1d5ff814">
 | 
			
		||||
  <source>Log back into <x id="0" equiv-text="${this.challenge.applicationName}"/></source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sc991a35f5e88d1d3">
 | 
			
		||||
  <source>Encryption Key</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s8a598f7aef81c3bc">
 | 
			
		||||
  <source>Key used to encrypt the tokens.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="sbfee780fa0a2c83e">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be deleted</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s336936629cdeb3e5">
 | 
			
		||||
  <source>Stage used to verify users' browsers using Google Chrome Device Trust. This stage can be used in authentication/authorization flows.</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s85fe794c71b4ace8">
 | 
			
		||||
  <source>Google Verified Access API</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
<trans-unit id="s013620384af7c8b4">
 | 
			
		||||
  <source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be edited</source>
 | 
			
		||||
</trans-unit>
 | 
			
		||||
    </body>
 | 
			
		||||
  </file>
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user