interface split (#943)
This commit is contained in:
parent
d7ab2a362a
commit
9441be1ee2
126
.github/workflows/ci-main.yml
vendored
126
.github/workflows/ci-main.yml
vendored
|
@ -25,14 +25,14 @@ jobs:
|
|||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.9'
|
||||
- id: cache-pipenv
|
||||
uses: actions/cache@v2.1.6
|
||||
with:
|
||||
path: ~/.local/share/virtualenvs
|
||||
key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
|
||||
# - id: cache-pipenv
|
||||
# uses: actions/cache@v2.1.6
|
||||
# with:
|
||||
# path: ~/.local/share/virtualenvs
|
||||
# key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
|
||||
- name: prepare
|
||||
env:
|
||||
INSTALL: ${{ steps.cache-pipenv.outputs.cache-hit }}
|
||||
# env:
|
||||
# INSTALL: ${{ steps.cache-pipenv.outputs.cache-hit }}
|
||||
run: scripts/ci_prepare.sh
|
||||
- name: run pylint
|
||||
run: pipenv run pylint authentik tests lifecycle
|
||||
|
@ -43,14 +43,14 @@ jobs:
|
|||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.9'
|
||||
- id: cache-pipenv
|
||||
uses: actions/cache@v2.1.6
|
||||
with:
|
||||
path: ~/.local/share/virtualenvs
|
||||
key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
|
||||
# - id: cache-pipenv
|
||||
# uses: actions/cache@v2.1.6
|
||||
# with:
|
||||
# path: ~/.local/share/virtualenvs
|
||||
# key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
|
||||
- name: prepare
|
||||
env:
|
||||
INSTALL: ${{ steps.cache-pipenv.outputs.cache-hit }}
|
||||
# env:
|
||||
# INSTALL: ${{ steps.cache-pipenv.outputs.cache-hit }}
|
||||
run: scripts/ci_prepare.sh
|
||||
- name: run black
|
||||
run: pipenv run black --check authentik tests lifecycle
|
||||
|
@ -61,14 +61,14 @@ jobs:
|
|||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.9'
|
||||
- id: cache-pipenv
|
||||
uses: actions/cache@v2.1.6
|
||||
with:
|
||||
path: ~/.local/share/virtualenvs
|
||||
key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
|
||||
# - id: cache-pipenv
|
||||
# uses: actions/cache@v2.1.6
|
||||
# with:
|
||||
# path: ~/.local/share/virtualenvs
|
||||
# key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
|
||||
- name: prepare
|
||||
env:
|
||||
INSTALL: ${{ steps.cache-pipenv.outputs.cache-hit }}
|
||||
# env:
|
||||
# INSTALL: ${{ steps.cache-pipenv.outputs.cache-hit }}
|
||||
run: scripts/ci_prepare.sh
|
||||
- name: run isort
|
||||
run: pipenv run isort --check authentik tests lifecycle
|
||||
|
@ -79,14 +79,14 @@ jobs:
|
|||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.9'
|
||||
- id: cache-pipenv
|
||||
uses: actions/cache@v2.1.6
|
||||
with:
|
||||
path: ~/.local/share/virtualenvs
|
||||
key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
|
||||
# - id: cache-pipenv
|
||||
# uses: actions/cache@v2.1.6
|
||||
# with:
|
||||
# path: ~/.local/share/virtualenvs
|
||||
# key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
|
||||
- name: prepare
|
||||
env:
|
||||
INSTALL: ${{ steps.cache-pipenv.outputs.cache-hit }}
|
||||
# env:
|
||||
# INSTALL: ${{ steps.cache-pipenv.outputs.cache-hit }}
|
||||
run: scripts/ci_prepare.sh
|
||||
- name: run bandit
|
||||
run: pipenv run bandit -r authentik tests lifecycle
|
||||
|
@ -113,14 +113,14 @@ jobs:
|
|||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.9'
|
||||
- id: cache-pipenv
|
||||
uses: actions/cache@v2.1.6
|
||||
with:
|
||||
path: ~/.local/share/virtualenvs
|
||||
key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
|
||||
# - id: cache-pipenv
|
||||
# uses: actions/cache@v2.1.6
|
||||
# with:
|
||||
# path: ~/.local/share/virtualenvs
|
||||
# key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
|
||||
- name: prepare
|
||||
env:
|
||||
INSTALL: ${{ steps.cache-pipenv.outputs.cache-hit }}
|
||||
# env:
|
||||
# INSTALL: ${{ steps.cache-pipenv.outputs.cache-hit }}
|
||||
run: scripts/ci_prepare.sh
|
||||
- name: run migrations
|
||||
run: pipenv run python -m lifecycle.migrate
|
||||
|
@ -138,14 +138,14 @@ jobs:
|
|||
# Copy current, latest config to local
|
||||
cp authentik/lib/default.yml local.env.yml
|
||||
git checkout $(git describe --abbrev=0 --match 'version/*')
|
||||
- id: cache-pipenv
|
||||
uses: actions/cache@v2.1.6
|
||||
with:
|
||||
path: ~/.local/share/virtualenvs
|
||||
key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
|
||||
# - id: cache-pipenv
|
||||
# uses: actions/cache@v2.1.6
|
||||
# with:
|
||||
# path: ~/.local/share/virtualenvs
|
||||
# key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
|
||||
- name: prepare
|
||||
env:
|
||||
INSTALL: ${{ steps.cache-pipenv.outputs.cache-hit }}
|
||||
# env:
|
||||
# INSTALL: ${{ steps.cache-pipenv.outputs.cache-hit }}
|
||||
run: scripts/ci_prepare.sh
|
||||
- name: run migrations to stable
|
||||
run: pipenv run python -m lifecycle.migrate
|
||||
|
@ -168,14 +168,14 @@ jobs:
|
|||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.9'
|
||||
- id: cache-pipenv
|
||||
uses: actions/cache@v2.1.6
|
||||
with:
|
||||
path: ~/.local/share/virtualenvs
|
||||
key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
|
||||
# - id: cache-pipenv
|
||||
# uses: actions/cache@v2.1.6
|
||||
# with:
|
||||
# path: ~/.local/share/virtualenvs
|
||||
# key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
|
||||
- name: prepare
|
||||
env:
|
||||
INSTALL: ${{ steps.cache-pipenv.outputs.cache-hit }}
|
||||
# env:
|
||||
# INSTALL: ${{ steps.cache-pipenv.outputs.cache-hit }}
|
||||
run: scripts/ci_prepare.sh
|
||||
- uses: testspace-com/setup-testspace@v1
|
||||
with:
|
||||
|
@ -197,14 +197,14 @@ jobs:
|
|||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.9'
|
||||
- id: cache-pipenv
|
||||
uses: actions/cache@v2.1.6
|
||||
with:
|
||||
path: ~/.local/share/virtualenvs
|
||||
key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
|
||||
# - id: cache-pipenv
|
||||
# uses: actions/cache@v2.1.6
|
||||
# with:
|
||||
# path: ~/.local/share/virtualenvs
|
||||
# key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
|
||||
- name: prepare
|
||||
env:
|
||||
INSTALL: ${{ steps.cache-pipenv.outputs.cache-hit }}
|
||||
# env:
|
||||
# INSTALL: ${{ steps.cache-pipenv.outputs.cache-hit }}
|
||||
run: scripts/ci_prepare.sh
|
||||
- uses: testspace-com/setup-testspace@v1
|
||||
with:
|
||||
|
@ -236,14 +236,14 @@ jobs:
|
|||
- uses: testspace-com/setup-testspace@v1
|
||||
with:
|
||||
domain: ${{github.repository_owner}}
|
||||
- id: cache-pipenv
|
||||
uses: actions/cache@v2.1.6
|
||||
with:
|
||||
path: ~/.local/share/virtualenvs
|
||||
key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
|
||||
# - id: cache-pipenv
|
||||
# uses: actions/cache@v2.1.6
|
||||
# with:
|
||||
# path: ~/.local/share/virtualenvs
|
||||
# key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
|
||||
- name: prepare
|
||||
env:
|
||||
INSTALL: ${{ steps.cache-pipenv.outputs.cache-hit }}
|
||||
# env:
|
||||
# INSTALL: ${{ steps.cache-pipenv.outputs.cache-hit }}
|
||||
run: |
|
||||
scripts/ci_prepare.sh
|
||||
docker-compose -f tests/e2e/ci.docker-compose.yml up -d
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
{% endblock %}
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'dist/authentik.css' %}">
|
||||
<script src="{% static 'dist/poly.js' %}" type="module"></script>
|
||||
<script>window["polymerSkipLoadingFontRoboto"] = true;</script>
|
||||
{% block head %}
|
||||
{% endblock %}
|
||||
</head>
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
{% load i18n %}
|
||||
|
||||
{% block head_before %}
|
||||
{{ block.super }}
|
||||
{% if flow.compatibility_mode %}
|
||||
<script>ShadyDOM = { force: !navigator.webdriver };</script>
|
||||
{% endif %}
|
||||
|
|
28
authentik/core/templates/if/user.html
Normal file
28
authentik/core/templates/if/user.html
Normal file
|
@ -0,0 +1,28 @@
|
|||
{% extends "base/skeleton.html" %}
|
||||
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block head %}
|
||||
<script src="{% static 'dist/UserInterface.js' %}" type="module"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<ak-message-container></ak-message-container>
|
||||
<ak-interface-user>
|
||||
<section class="ak-static-page pf-c-page__main-section pf-m-no-padding-mobile pf-m-xl">
|
||||
<div class="pf-c-empty-state" style="height: 100vh;">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<span class="pf-c-spinner pf-m-xl pf-c-empty-state__icon" role="progressbar" aria-valuetext="{% trans 'Loading...' %}">
|
||||
<span class="pf-c-spinner__clipper"></span>
|
||||
<span class="pf-c-spinner__lead-ball"></span>
|
||||
<span class="pf-c-spinner__tail-ball"></span>
|
||||
</span>
|
||||
<h1 class="pf-c-title pf-m-lg">
|
||||
{% trans "Loading..." %}
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</ak-interface-user>
|
||||
{% endblock %}
|
|
@ -12,7 +12,7 @@ from authentik.core.views.session import EndSessionView
|
|||
urlpatterns = [
|
||||
path(
|
||||
"",
|
||||
login_required(RedirectView.as_view(pattern_name="authentik_core:if-admin")),
|
||||
login_required(RedirectView.as_view(pattern_name="authentik_core:if-user")),
|
||||
name="root-redirect",
|
||||
),
|
||||
# Impersonation
|
||||
|
@ -32,6 +32,11 @@ urlpatterns = [
|
|||
ensure_csrf_cookie(TemplateView.as_view(template_name="if/admin.html")),
|
||||
name="if-admin",
|
||||
),
|
||||
path(
|
||||
"if/user/",
|
||||
ensure_csrf_cookie(TemplateView.as_view(template_name="if/user.html")),
|
||||
name="if-user",
|
||||
),
|
||||
path(
|
||||
"if/flow/<slug:flow_slug>/",
|
||||
ensure_csrf_cookie(FlowInterfaceView.as_view()),
|
||||
|
|
|
@ -5,7 +5,6 @@ from django.db.models import F, Q
|
|||
from django.db.models import Value as V
|
||||
from django.http.request import HttpRequest
|
||||
|
||||
from authentik import __version__
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.tenants.models import Tenant
|
||||
|
||||
|
@ -31,6 +30,5 @@ def context_processor(request: HttpRequest) -> dict[str, Any]:
|
|||
tenant = getattr(request, "tenant", DEFAULT_TENANT)
|
||||
return {
|
||||
"tenant": tenant,
|
||||
"ak_version": __version__,
|
||||
"footer_links": CONFIG.y("footer_links"),
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ class TestFlowsAuthenticator(SeleniumTestCase):
|
|||
|
||||
code_stage.find_element(By.CSS_SELECTOR, "input[name=code]").send_keys(totp.token())
|
||||
code_stage.find_element(By.CSS_SELECTOR, "input[name=code]").send_keys(Keys.ENTER)
|
||||
self.wait_for_url(self.if_admin_url("/library"))
|
||||
self.wait_for_url(self.if_user_url("/library"))
|
||||
self.assert_user(USER())
|
||||
|
||||
@retry()
|
||||
|
@ -67,7 +67,7 @@ class TestFlowsAuthenticator(SeleniumTestCase):
|
|||
self.driver.get(self.url("authentik_core:if-flow", flow_slug=flow.slug))
|
||||
self.login()
|
||||
|
||||
self.wait_for_url(self.if_admin_url("/library"))
|
||||
self.wait_for_url(self.if_user_url("/library"))
|
||||
self.assert_user(USER())
|
||||
|
||||
self.driver.get(
|
||||
|
@ -112,7 +112,7 @@ class TestFlowsAuthenticator(SeleniumTestCase):
|
|||
self.driver.get(self.url("authentik_core:if-flow", flow_slug=flow.slug))
|
||||
self.login()
|
||||
|
||||
self.wait_for_url(self.if_admin_url("/library"))
|
||||
self.wait_for_url(self.if_user_url("/library"))
|
||||
self.assert_user(USER())
|
||||
|
||||
self.driver.get(
|
||||
|
|
|
@ -96,11 +96,11 @@ class TestFlowsEnroll(SeleniumTestCase):
|
|||
|
||||
self.initial_stages()
|
||||
|
||||
interface_admin = self.get_shadow_root("ak-interface-admin")
|
||||
wait = WebDriverWait(interface_admin, self.wait_timeout)
|
||||
interface_user = self.get_shadow_root("ak-interface-user")
|
||||
wait = WebDriverWait(interface_user, self.wait_timeout)
|
||||
|
||||
wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "ak-sidebar")))
|
||||
self.driver.get(self.if_admin_url("/user"))
|
||||
self.driver.get(self.if_user_url("/user"))
|
||||
|
||||
user = User.objects.get(username="foo")
|
||||
self.assertEqual(user.username, "foo")
|
||||
|
@ -195,10 +195,10 @@ class TestFlowsEnroll(SeleniumTestCase):
|
|||
|
||||
sleep(2)
|
||||
# We're now logged in
|
||||
wait = WebDriverWait(self.get_shadow_root("ak-interface-admin"), self.wait_timeout)
|
||||
wait = WebDriverWait(self.get_shadow_root("ak-interface-user"), self.wait_timeout)
|
||||
|
||||
wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "ak-sidebar")))
|
||||
self.driver.get(self.if_admin_url("/user"))
|
||||
self.driver.get(self.if_user_url("/user"))
|
||||
|
||||
self.assert_user(User.objects.get(username="foo"))
|
||||
|
||||
|
|
|
@ -22,5 +22,5 @@ class TestFlowsLogin(SeleniumTestCase):
|
|||
)
|
||||
)
|
||||
self.login()
|
||||
self.wait_for_url(self.if_admin_url("/library"))
|
||||
self.wait_for_url(self.if_user_url("/library"))
|
||||
self.assert_user(USER())
|
||||
|
|
|
@ -42,7 +42,7 @@ class TestFlowsStageSetup(SeleniumTestCase):
|
|||
)
|
||||
)
|
||||
self.login()
|
||||
self.wait_for_url(self.if_admin_url("/library"))
|
||||
self.wait_for_url(self.if_user_url("/library"))
|
||||
|
||||
self.driver.get(
|
||||
self.url(
|
||||
|
@ -62,7 +62,7 @@ class TestFlowsStageSetup(SeleniumTestCase):
|
|||
Keys.ENTER
|
||||
)
|
||||
|
||||
self.wait_for_url(self.if_admin_url("/library"))
|
||||
self.wait_for_url(self.if_user_url("/library"))
|
||||
# Because USER() is cached, we need to get the user manually here
|
||||
user = User.objects.get(username=USER().username)
|
||||
self.assertTrue(user.check_password(new_password))
|
||||
|
|
|
@ -174,8 +174,8 @@ class TestSourceOAuth2(SeleniumTestCase):
|
|||
prompt_stage.find_element(By.CSS_SELECTOR, "input[name=username]").send_keys(Keys.ENTER)
|
||||
|
||||
# Wait until we've logged in
|
||||
self.wait_for_url(self.if_admin_url("/library"))
|
||||
self.driver.get(self.if_admin_url("/user"))
|
||||
self.wait_for_url(self.if_user_url("/library"))
|
||||
self.driver.get(self.if_user_url("/user"))
|
||||
|
||||
self.assert_user(User(username="foo", name="admin", email="admin@example.com"))
|
||||
|
||||
|
@ -253,8 +253,8 @@ class TestSourceOAuth2(SeleniumTestCase):
|
|||
self.driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click()
|
||||
|
||||
# Wait until we've logged in
|
||||
self.wait_for_url(self.if_admin_url("/library"))
|
||||
self.driver.get(self.if_admin_url("/user"))
|
||||
self.wait_for_url(self.if_user_url("/library"))
|
||||
self.driver.get(self.if_user_url("/user"))
|
||||
|
||||
self.assert_user(User(username="foo", name="admin", email="admin@example.com"))
|
||||
|
||||
|
@ -348,7 +348,7 @@ class TestSourceOAuth1(SeleniumTestCase):
|
|||
# Wait until we've loaded the user info page
|
||||
sleep(2)
|
||||
# Wait until we've logged in
|
||||
self.wait_for_url(self.if_admin_url("/library"))
|
||||
self.driver.get(self.if_admin_url("/user"))
|
||||
self.wait_for_url(self.if_user_url("/library"))
|
||||
self.driver.get(self.if_user_url("/user"))
|
||||
|
||||
self.assert_user(User(username="example-user", name="test name", email="foo@example.com"))
|
||||
|
|
|
@ -153,8 +153,8 @@ class TestSourceSAML(SeleniumTestCase):
|
|||
self.driver.find_element(By.ID, "password").send_keys(Keys.ENTER)
|
||||
|
||||
# Wait until we're logged in
|
||||
self.wait_for_url(self.if_admin_url("/library"))
|
||||
self.driver.get(self.if_admin_url("/user"))
|
||||
self.wait_for_url(self.if_user_url("/library"))
|
||||
self.driver.get(self.if_user_url("/user"))
|
||||
|
||||
self.assert_user(
|
||||
User.objects.exclude(username="akadmin")
|
||||
|
@ -233,8 +233,8 @@ class TestSourceSAML(SeleniumTestCase):
|
|||
self.driver.find_element(By.ID, "password").send_keys(Keys.ENTER)
|
||||
|
||||
# Wait until we're logged in
|
||||
self.wait_for_url(self.if_admin_url("/library"))
|
||||
self.driver.get(self.if_admin_url("/user"))
|
||||
self.wait_for_url(self.if_user_url("/library"))
|
||||
self.driver.get(self.if_user_url("/user"))
|
||||
|
||||
self.assert_user(
|
||||
User.objects.exclude(username="akadmin")
|
||||
|
@ -300,8 +300,8 @@ class TestSourceSAML(SeleniumTestCase):
|
|||
self.driver.find_element(By.ID, "password").send_keys(Keys.ENTER)
|
||||
|
||||
# Wait until we're logged in
|
||||
self.wait_for_url(self.if_admin_url("/library"))
|
||||
self.driver.get(self.if_admin_url("/user"))
|
||||
self.wait_for_url(self.if_user_url("/library"))
|
||||
self.driver.get(self.if_user_url("/user"))
|
||||
|
||||
self.assert_user(
|
||||
User.objects.exclude(username="akadmin")
|
||||
|
|
|
@ -126,9 +126,9 @@ class SeleniumTestCase(StaticLiveServerTestCase):
|
|||
"""reverse `view` with `**kwargs` into full URL using live_server_url"""
|
||||
return self.live_server_url + reverse(view, kwargs=kwargs)
|
||||
|
||||
def if_admin_url(self, view) -> str:
|
||||
def if_user_url(self, view) -> str:
|
||||
"""same as self.url() but show URL in shell"""
|
||||
return f"{self.live_server_url}/if/admin/#{view}"
|
||||
return f"{self.live_server_url}/if/user/#{view}"
|
||||
|
||||
def get_shadow_root(self, selector: str, container: Optional[WebElement] = None) -> WebElement:
|
||||
"""Get shadow root element's inner shadowRoot"""
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
// @ts-ignore
|
||||
window["polymerSkipLoadingFontRoboto"] = true;
|
||||
import "construct-style-sheets-polyfill";
|
||||
import "@webcomponents/webcomponentsjs";
|
||||
|
|
|
@ -87,40 +87,7 @@ export default [
|
|||
clearScreen: false,
|
||||
},
|
||||
},
|
||||
// Main Application
|
||||
{
|
||||
input: "./src/interfaces/AdminInterface.ts",
|
||||
context: "window",
|
||||
output: [
|
||||
{
|
||||
format: "es",
|
||||
dir: "dist",
|
||||
sourcemap: true,
|
||||
manualChunks: manualChunks,
|
||||
chunkFileNames: "admin-[name].js",
|
||||
},
|
||||
],
|
||||
plugins: [
|
||||
cssimport(),
|
||||
resolve({ extensions, browser: true }),
|
||||
commonjs(),
|
||||
babel({
|
||||
extensions,
|
||||
babelHelpers: "runtime",
|
||||
include: ["src/**/*"],
|
||||
}),
|
||||
replace({
|
||||
"process.env.NODE_ENV": JSON.stringify(isProdBuild ? "production" : "development"),
|
||||
"preventAssignment": true,
|
||||
}),
|
||||
sourcemaps(),
|
||||
isProdBuild && terser(),
|
||||
].filter((p) => p),
|
||||
watch: {
|
||||
clearScreen: false,
|
||||
},
|
||||
},
|
||||
// Flow executor
|
||||
// Flow interface
|
||||
{
|
||||
input: "./src/interfaces/FlowInterface.ts",
|
||||
context: "window",
|
||||
|
@ -153,4 +120,70 @@ export default [
|
|||
clearScreen: false,
|
||||
},
|
||||
},
|
||||
// Admin interface
|
||||
{
|
||||
input: "./src/interfaces/AdminInterface.ts",
|
||||
context: "window",
|
||||
output: [
|
||||
{
|
||||
format: "es",
|
||||
dir: "dist",
|
||||
sourcemap: true,
|
||||
manualChunks: manualChunks,
|
||||
chunkFileNames: "admin-[name].js",
|
||||
},
|
||||
],
|
||||
plugins: [
|
||||
cssimport(),
|
||||
resolve({ extensions, browser: true }),
|
||||
commonjs(),
|
||||
babel({
|
||||
extensions,
|
||||
babelHelpers: "runtime",
|
||||
include: ["src/**/*"],
|
||||
}),
|
||||
replace({
|
||||
"process.env.NODE_ENV": JSON.stringify(isProdBuild ? "production" : "development"),
|
||||
"preventAssignment": true,
|
||||
}),
|
||||
sourcemaps(),
|
||||
isProdBuild && terser(),
|
||||
].filter((p) => p),
|
||||
watch: {
|
||||
clearScreen: false,
|
||||
},
|
||||
},
|
||||
// User interface
|
||||
{
|
||||
input: "./src/interfaces/UserInterface.ts",
|
||||
context: "window",
|
||||
output: [
|
||||
{
|
||||
format: "es",
|
||||
dir: "dist",
|
||||
sourcemap: true,
|
||||
manualChunks: manualChunks,
|
||||
chunkFileNames: "user-[name].js",
|
||||
},
|
||||
],
|
||||
plugins: [
|
||||
cssimport(),
|
||||
resolve({ extensions, browser: true }),
|
||||
commonjs(),
|
||||
babel({
|
||||
extensions,
|
||||
babelHelpers: "runtime",
|
||||
include: ["src/**/*"],
|
||||
}),
|
||||
replace({
|
||||
"process.env.NODE_ENV": JSON.stringify(isProdBuild ? "production" : "development"),
|
||||
"preventAssignment": true,
|
||||
}),
|
||||
sourcemaps(),
|
||||
isProdBuild && terser(),
|
||||
].filter((p) => p),
|
||||
watch: {
|
||||
clearScreen: false,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
|
|
@ -83,9 +83,6 @@ html > form > input {
|
|||
color: var(--pf-global--danger-color--100);
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--ak-dark-background) !important;
|
||||
}
|
||||
.ak-static-page h1 {
|
||||
color: var(--ak-dark-foreground);
|
||||
}
|
||||
|
@ -99,6 +96,9 @@ body {
|
|||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: var(--ak-dark-background) !important;
|
||||
}
|
||||
:root {
|
||||
--pf-global--Color--100: var(--ak-dark-foreground);
|
||||
--pf-c-page__main-section--m-light--BackgroundColor: var(--ak-dark-background-darker);
|
||||
|
|
|
@ -8,7 +8,6 @@ import {
|
|||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { Route } from "./Route";
|
||||
import { ROUTES } from "../../routes";
|
||||
import { RouteMatch } from "./RouteMatch";
|
||||
import AKGlobal from "../../authentik.css";
|
||||
|
||||
|
@ -45,6 +44,9 @@ export class RouterOutlet extends LitElement {
|
|||
@property()
|
||||
defaultUrl?: string;
|
||||
|
||||
@property({ attribute: false })
|
||||
routes: Route[] = [];
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
AKGlobal,
|
||||
|
@ -59,8 +61,6 @@ export class RouterOutlet extends LitElement {
|
|||
}
|
||||
}
|
||||
*:first-child {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
`,
|
||||
|
@ -90,7 +90,7 @@ export class RouterOutlet extends LitElement {
|
|||
return;
|
||||
}
|
||||
let matchedRoute: RouteMatch | null = null;
|
||||
ROUTES.some((route) => {
|
||||
this.routes.some((route) => {
|
||||
const match = route.url.exec(activeUrl);
|
||||
if (match != null) {
|
||||
matchedRoute = new RouteMatch(route);
|
||||
|
|
|
@ -33,6 +33,7 @@ import {
|
|||
import { AdminApi, Version } from "@goauthentik/api";
|
||||
import { DEFAULT_CONFIG } from "../api/Config";
|
||||
import { WebsocketClient } from "../common/ws";
|
||||
import { ROUTES } from "../routesAdmin";
|
||||
|
||||
@customElement("ak-interface-admin")
|
||||
export class AdminInterface extends LitElement {
|
||||
|
@ -110,7 +111,8 @@ export class AdminInterface extends LitElement {
|
|||
class="pf-c-page__main"
|
||||
tabindex="-1"
|
||||
id="main-content"
|
||||
defaultUrl="/library"
|
||||
defaultUrl="/administration/overview"
|
||||
.routes=${ROUTES}
|
||||
>
|
||||
</ak-router-outlet>
|
||||
</main>
|
||||
|
@ -135,9 +137,11 @@ export class AdminInterface extends LitElement {
|
|||
}
|
||||
|
||||
renderSidebarItems(): TemplateResult {
|
||||
const superUserCondition = () => {
|
||||
return me().then((u) => u.user.isSuperuser || false);
|
||||
};
|
||||
me().then((u) => {
|
||||
if (!u.user.isSuperuser) {
|
||||
window.location.assign("/if/user");
|
||||
}
|
||||
});
|
||||
return html`
|
||||
${until(
|
||||
this.version.then((version) => {
|
||||
|
@ -167,19 +171,16 @@ export class AdminInterface extends LitElement {
|
|||
return html``;
|
||||
}),
|
||||
)}
|
||||
<ak-sidebar-item path="/library">
|
||||
<span slot="label">${t`Library`}</span>
|
||||
<ak-sidebar-item path="/if/user/" ?isAbsoluteLink=${true} ?highlight=${true}>
|
||||
<span slot="label">${t`Go to user interface`}</span>
|
||||
</ak-sidebar-item>
|
||||
<ak-sidebar-item .condition=${superUserCondition}>
|
||||
<span slot="label">${t`Monitor`}</span>
|
||||
<ak-sidebar-item path="/administration/overview">
|
||||
<span slot="label">${t`Overview`}</span>
|
||||
</ak-sidebar-item>
|
||||
<ak-sidebar-item path="/administration/system-tasks">
|
||||
<span slot="label">${t`System Tasks`}</span>
|
||||
</ak-sidebar-item>
|
||||
</ak-sidebar-item>
|
||||
<ak-sidebar-item .condition=${superUserCondition}>
|
||||
<ak-sidebar-item>
|
||||
<span slot="label">${t`Resources`}</span>
|
||||
<ak-sidebar-item
|
||||
path="/core/applications"
|
||||
|
@ -203,7 +204,7 @@ export class AdminInterface extends LitElement {
|
|||
<span slot="label">${t`Tenants`}</span>
|
||||
</ak-sidebar-item>
|
||||
</ak-sidebar-item>
|
||||
<ak-sidebar-item .condition=${superUserCondition}>
|
||||
<ak-sidebar-item>
|
||||
<span slot="label">${t`Outposts`}</span>
|
||||
<ak-sidebar-item path="/outpost/outposts">
|
||||
<span slot="label">${t`Outposts`}</span>
|
||||
|
@ -212,7 +213,7 @@ export class AdminInterface extends LitElement {
|
|||
<span slot="label">${t`Integrations`}</span>
|
||||
</ak-sidebar-item>
|
||||
</ak-sidebar-item>
|
||||
<ak-sidebar-item .condition=${superUserCondition}>
|
||||
<ak-sidebar-item>
|
||||
<span slot="label">${t`Events`}</span>
|
||||
<ak-sidebar-item
|
||||
path="/events/log"
|
||||
|
@ -227,7 +228,7 @@ export class AdminInterface extends LitElement {
|
|||
<span slot="label">${t`Notification Transports`}</span>
|
||||
</ak-sidebar-item>
|
||||
</ak-sidebar-item>
|
||||
<ak-sidebar-item .condition=${superUserCondition}>
|
||||
<ak-sidebar-item>
|
||||
<span slot="label">${t`Customisation`}</span>
|
||||
<ak-sidebar-item path="/policy/policies">
|
||||
<span slot="label">${t`Policies`}</span>
|
||||
|
@ -242,7 +243,7 @@ export class AdminInterface extends LitElement {
|
|||
<span slot="label">${t`Property Mappings`}</span>
|
||||
</ak-sidebar-item>
|
||||
</ak-sidebar-item>
|
||||
<ak-sidebar-item .condition=${superUserCondition}>
|
||||
<ak-sidebar-item>
|
||||
<span slot="label">${t`Flows`}</span>
|
||||
<ak-sidebar-item
|
||||
path="/flow/flows"
|
||||
|
@ -260,7 +261,7 @@ export class AdminInterface extends LitElement {
|
|||
<span slot="label">${t`Invitations`}</span>
|
||||
</ak-sidebar-item>
|
||||
</ak-sidebar-item>
|
||||
<ak-sidebar-item .condition=${superUserCondition}>
|
||||
<ak-sidebar-item>
|
||||
<span slot="label">${t`Identity & Cryptography`}</span>
|
||||
<ak-sidebar-item
|
||||
path="/identity/users"
|
||||
|
|
176
web/src/interfaces/UserInterface.ts
Normal file
176
web/src/interfaces/UserInterface.ts
Normal file
|
@ -0,0 +1,176 @@
|
|||
import "../elements/messages/MessageContainer";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { me } from "../api/Users";
|
||||
import "./locale";
|
||||
import "../elements/sidebar/SidebarItem";
|
||||
import { t } from "@lingui/macro";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css";
|
||||
import AKGlobal from "../authentik.css";
|
||||
|
||||
import "../elements/router/RouterOutlet";
|
||||
import "../elements/messages/MessageContainer";
|
||||
import "../elements/notifications/NotificationDrawer";
|
||||
import "../elements/sidebar/Sidebar";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import {
|
||||
EVENT_API_DRAWER_TOGGLE,
|
||||
EVENT_NOTIFICATION_DRAWER_TOGGLE,
|
||||
EVENT_SIDEBAR_TOGGLE,
|
||||
VERSION,
|
||||
} from "../constants";
|
||||
import { AdminApi, Version } from "@goauthentik/api";
|
||||
import { DEFAULT_CONFIG } from "../api/Config";
|
||||
import { WebsocketClient } from "../common/ws";
|
||||
import { ROUTES } from "../routesUser";
|
||||
|
||||
@customElement("ak-interface-user")
|
||||
export class UserInterface extends LitElement {
|
||||
@property({ type: Boolean })
|
||||
sidebarOpen = true;
|
||||
|
||||
@property({ type: Boolean })
|
||||
notificationOpen = false;
|
||||
|
||||
@property({ type: Boolean })
|
||||
apiDrawerOpen = false;
|
||||
|
||||
ws: WebsocketClient;
|
||||
|
||||
private version: Promise<Version>;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
PFBase,
|
||||
PFPage,
|
||||
PFButton,
|
||||
PFDrawer,
|
||||
AKGlobal,
|
||||
css`
|
||||
.pf-c-page__main,
|
||||
.pf-c-drawer__content,
|
||||
.pf-c-page__drawer {
|
||||
z-index: auto !important;
|
||||
}
|
||||
.display-none {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.ws = new WebsocketClient();
|
||||
this.sidebarOpen = window.innerWidth >= 1280;
|
||||
window.addEventListener("resize", () => {
|
||||
this.sidebarOpen = window.innerWidth >= 1280;
|
||||
});
|
||||
window.addEventListener(EVENT_SIDEBAR_TOGGLE, () => {
|
||||
this.sidebarOpen = !this.sidebarOpen;
|
||||
});
|
||||
window.addEventListener(EVENT_NOTIFICATION_DRAWER_TOGGLE, () => {
|
||||
this.notificationOpen = !this.notificationOpen;
|
||||
});
|
||||
window.addEventListener(EVENT_API_DRAWER_TOGGLE, () => {
|
||||
this.apiDrawerOpen = !this.apiDrawerOpen;
|
||||
});
|
||||
this.version = new AdminApi(DEFAULT_CONFIG).adminVersionRetrieve();
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html` <div class="pf-c-page">
|
||||
<ak-sidebar
|
||||
class="pf-c-page__sidebar ${this.sidebarOpen ? "pf-m-expanded" : "pf-m-collapsed"}"
|
||||
>
|
||||
${this.renderSidebarItems()}
|
||||
</ak-sidebar>
|
||||
<div class="pf-c-page__drawer">
|
||||
<div
|
||||
class="pf-c-drawer ${this.notificationOpen || this.apiDrawerOpen
|
||||
? "pf-m-expanded"
|
||||
: "pf-m-collapsed"}"
|
||||
>
|
||||
<div class="pf-c-drawer__main">
|
||||
<div class="pf-c-drawer__content">
|
||||
<div class="pf-c-drawer__body">
|
||||
<main class="pf-c-page__main">
|
||||
<ak-router-outlet
|
||||
role="main"
|
||||
class="pf-c-page__main"
|
||||
tabindex="-1"
|
||||
id="main-content"
|
||||
defaultUrl="/library"
|
||||
.routes=${ROUTES}
|
||||
>
|
||||
</ak-router-outlet>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
<ak-notification-drawer
|
||||
class="pf-c-drawer__panel pf-m-width-33 ${this.notificationOpen
|
||||
? ""
|
||||
: "display-none"}"
|
||||
?hidden=${!this.notificationOpen}
|
||||
></ak-notification-drawer>
|
||||
<ak-api-drawer
|
||||
class="pf-c-drawer__panel pf-m-width-33 ${this.apiDrawerOpen
|
||||
? ""
|
||||
: "display-none"}"
|
||||
?hidden=${!this.apiDrawerOpen}
|
||||
></ak-api-drawer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
renderSidebarItems(): TemplateResult {
|
||||
return html`
|
||||
${until(
|
||||
this.version.then((version) => {
|
||||
if (version.versionCurrent !== VERSION) {
|
||||
return html`<ak-sidebar-item ?highlight=${true}>
|
||||
<span slot="label"
|
||||
>${t`A newer version of the frontend is available.`}</span
|
||||
>
|
||||
</ak-sidebar-item>`;
|
||||
}
|
||||
return html``;
|
||||
}),
|
||||
)}
|
||||
${until(
|
||||
me().then((u) => {
|
||||
if (u.original) {
|
||||
return html`<ak-sidebar-item
|
||||
?highlight=${true}
|
||||
?isAbsoluteLink=${true}
|
||||
path=${`/-/impersonation/end/?back=${window.location.pathname}%23${window.location.hash}`}
|
||||
>
|
||||
<span slot="label"
|
||||
>${t`You're currently impersonating ${u.user.username}. Click to stop.`}</span
|
||||
>
|
||||
</ak-sidebar-item>`;
|
||||
}
|
||||
return html``;
|
||||
}),
|
||||
)}
|
||||
<ak-sidebar-item path="/if/admin" ?isAbsoluteLink=${true} ?highlight=${true}>
|
||||
<span slot="label">${t`Go to admin interface`}</span>
|
||||
</ak-sidebar-item>
|
||||
<ak-sidebar-item path="/library">
|
||||
<span slot="label">${t`Library`}</span>
|
||||
</ak-sidebar-item>
|
||||
`;
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
||||
<link rel="stylesheet" type="text/css" href="/static/dist/patternfly-base.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/static/dist/page.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/static/dist/empty-state.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/static/dist/spinner.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/static/dist/authentik.css" />
|
||||
<script src="/static/dist/poly.js" type="module"></script>
|
||||
<script>
|
||||
window["polymerSkipLoadingFontRoboto"] = true;
|
||||
</script>
|
||||
<script src="/static/dist/AdminInterface.js" type="module"></script>
|
||||
<title>authentik</title>
|
||||
</head>
|
||||
<body>
|
||||
<ak-message-container></ak-message-container>
|
||||
<ak-interface-admin>
|
||||
<section class="ak-static-page pf-c-page__main-section pf-m-no-padding-mobile pf-m-xl">
|
||||
<div class="pf-c-empty-state" style="height: 100vh">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<span
|
||||
class="pf-c-spinner pf-m-xl pf-c-empty-state__icon"
|
||||
role="progressbar"
|
||||
aria-valuetext="Loading..."
|
||||
>
|
||||
<span class="pf-c-spinner__clipper"></span>
|
||||
<span class="pf-c-spinner__lead-ball"></span>
|
||||
<span class="pf-c-spinner__tail-ball"></span>
|
||||
</span>
|
||||
<h1 class="pf-c-title pf-m-lg">Loading...</h1>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</ak-interface-admin>
|
||||
</body>
|
||||
</html>
|
|
@ -1,40 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
||||
<link rel="stylesheet" type="text/css" href="/static/dist/patternfly-base.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/static/dist/page.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/static/dist/empty-state.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/static/dist/spinner.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/static/dist/authentik.css" />
|
||||
<script>
|
||||
ShadyDOM = { force: !navigator.webdriver };
|
||||
window["polymerSkipLoadingFontRoboto"] = true;
|
||||
</script>
|
||||
<script src="/static/dist/poly.js" type="module"></script>
|
||||
<script src="/static/dist/FlowInterface.js" type="module"></script>
|
||||
<title>authentik</title>
|
||||
</head>
|
||||
<body>
|
||||
<ak-message-container></ak-message-container>
|
||||
<ak-flow-executor>
|
||||
<section class="ak-static-page pf-c-page__main-section pf-m-no-padding-mobile pf-m-xl">
|
||||
<div class="pf-c-empty-state" style="height: 100vh">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<span
|
||||
class="pf-c-spinner pf-m-xl pf-c-empty-state__icon"
|
||||
role="progressbar"
|
||||
aria-valuetext="Loading..."
|
||||
>
|
||||
<span class="pf-c-spinner__clipper"></span>
|
||||
<span class="pf-c-spinner__lead-ball"></span>
|
||||
<span class="pf-c-spinner__tail-ball"></span>
|
||||
</span>
|
||||
<h1 class="pf-c-title pf-m-lg">Loading...</h1>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</ak-flow-executor>
|
||||
</body>
|
||||
</html>
|
|
@ -34,6 +34,7 @@ msgid "8 digits, not compatible with apps like Google Authenticator"
|
|||
msgstr "8 digits, not compatible with apps like Google Authenticator"
|
||||
|
||||
#: src/interfaces/AdminInterface.ts
|
||||
#: src/interfaces/UserInterface.ts
|
||||
msgid "A newer version of the frontend is available."
|
||||
msgstr "A newer version of the frontend is available."
|
||||
|
||||
|
@ -167,6 +168,10 @@ msgstr "Additional group DN, prepended to the Base DN."
|
|||
msgid "Additional user DN, prepended to the Base DN."
|
||||
msgstr "Additional user DN, prepended to the Base DN."
|
||||
|
||||
#:
|
||||
#~ msgid "Admin"
|
||||
#~ msgstr "Admin"
|
||||
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||
#: src/pages/providers/proxy/ProxyProviderForm.ts
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
|
@ -282,6 +287,7 @@ msgstr "Application(s)"
|
|||
#: src/pages/LibraryPage.ts
|
||||
#: src/pages/applications/ApplicationListPage.ts
|
||||
#: src/pages/outposts/OutpostForm.ts
|
||||
#: src/user/LibraryPage.ts
|
||||
msgid "Applications"
|
||||
msgstr "Applications"
|
||||
|
||||
|
@ -381,6 +387,7 @@ msgstr "Authentication flow"
|
|||
|
||||
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStage.ts
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
msgid "Authenticator"
|
||||
msgstr "Authenticator"
|
||||
|
||||
|
@ -602,6 +609,7 @@ msgid "Certificates"
|
|||
msgstr "Certificates"
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsPassword.ts
|
||||
#: src/user/user-settings/settings/UserSettingsPassword.ts
|
||||
msgid "Change password"
|
||||
msgstr "Change password"
|
||||
|
||||
|
@ -610,6 +618,7 @@ msgid "Change status"
|
|||
msgstr "Change status"
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsPassword.ts
|
||||
#: src/user/user-settings/settings/UserSettingsPassword.ts
|
||||
msgid "Change your password"
|
||||
msgstr "Change your password"
|
||||
|
||||
|
@ -788,6 +797,7 @@ msgid "Configuration stage"
|
|||
msgstr "Configuration stage"
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
msgid "Configure WebAuthn"
|
||||
msgstr "Configure WebAuthn"
|
||||
|
||||
|
@ -820,6 +830,7 @@ msgid "Configure how the issuer field of the ID Token should be filled."
|
|||
msgstr "Configure how the issuer field of the ID Token should be filled."
|
||||
|
||||
#: src/pages/user-settings/UserSettingsPage.ts
|
||||
#: src/user/user-settings/UserSettingsPage.ts
|
||||
msgid "Configure settings relevant to your user profile."
|
||||
msgstr "Configure settings relevant to your user profile."
|
||||
|
||||
|
@ -836,11 +847,14 @@ msgid "Configure what data should be used as unique User Identifier. For most ca
|
|||
msgstr "Configure what data should be used as unique User Identifier. For most cases, the default should be fine."
|
||||
|
||||
#: src/pages/user-settings/settings/SourceSettingsOAuth.ts
|
||||
#: src/user/user-settings/settings/SourceSettingsOAuth.ts
|
||||
msgid "Connect"
|
||||
msgstr "Connect"
|
||||
|
||||
#: src/pages/user-settings/settings/SourceSettingsOAuth.ts
|
||||
#: src/pages/user-settings/settings/SourceSettingsPlex.ts
|
||||
#: src/user/user-settings/settings/SourceSettingsOAuth.ts
|
||||
#: src/user/user-settings/settings/SourceSettingsPlex.ts
|
||||
msgid "Connected."
|
||||
msgstr "Connected."
|
||||
|
||||
|
@ -927,6 +941,7 @@ msgid "Copy"
|
|||
msgstr "Copy"
|
||||
|
||||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "Copy Key"
|
||||
msgstr "Copy Key"
|
||||
|
||||
|
@ -988,11 +1003,15 @@ msgstr "Copy recovery link"
|
|||
#: src/pages/users/UserListPage.ts
|
||||
#: src/pages/users/UserListPage.ts
|
||||
#: src/pages/users/UserListPage.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "Create"
|
||||
msgstr "Create"
|
||||
|
||||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "Create App password"
|
||||
msgstr "Create App password"
|
||||
|
||||
|
@ -1070,6 +1089,8 @@ msgstr "Create Tenant"
|
|||
#: src/pages/tokens/TokenListPage.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "Create Token"
|
||||
msgstr "Create Token"
|
||||
|
||||
|
@ -1106,6 +1127,7 @@ msgid "Created by"
|
|||
msgstr "Created by"
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
msgid "Created {0}"
|
||||
msgstr "Created {0}"
|
||||
|
||||
|
@ -1191,6 +1213,8 @@ msgstr "Define how notifications are sent to users, like Email or Webhook."
|
|||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/pages/users/UserListPage.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "Delete"
|
||||
msgstr "Delete"
|
||||
|
||||
|
@ -1216,6 +1240,7 @@ msgstr "Delete"
|
|||
#~ msgstr "Delete Session"
|
||||
|
||||
#: src/pages/user-settings/UserSelfForm.ts
|
||||
#: src/user/user-settings/UserSelfForm.ts
|
||||
msgid "Delete account"
|
||||
msgstr "Delete account"
|
||||
|
||||
|
@ -1253,6 +1278,7 @@ msgstr "Deny the user access"
|
|||
#: src/pages/system-tasks/SystemTaskListPage.ts
|
||||
#: src/pages/tokens/TokenForm.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenForm.ts
|
||||
#: src/user/user-settings/tokens/UserTokenForm.ts
|
||||
msgid "Description"
|
||||
msgstr "Description"
|
||||
|
||||
|
@ -1294,6 +1320,7 @@ msgid "Device classes which can be used to authenticate."
|
|||
msgstr "Device classes which can be used to authenticate."
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
msgid "Device name"
|
||||
msgstr "Device name"
|
||||
|
||||
|
@ -1312,14 +1339,17 @@ msgstr "Digits"
|
|||
#~ msgstr "Disable"
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorDuo.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorDuo.ts
|
||||
msgid "Disable Duo authenticator"
|
||||
msgstr "Disable Duo authenticator"
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorStatic.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorStatic.ts
|
||||
msgid "Disable Static Tokens"
|
||||
msgstr "Disable Static Tokens"
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
|
||||
msgid "Disable Time-based OTP"
|
||||
msgstr "Disable Time-based OTP"
|
||||
|
||||
|
@ -1329,6 +1359,8 @@ msgstr "Disabled"
|
|||
|
||||
#: src/pages/user-settings/settings/SourceSettingsOAuth.ts
|
||||
#: src/pages/user-settings/settings/SourceSettingsPlex.ts
|
||||
#: src/user/user-settings/settings/SourceSettingsOAuth.ts
|
||||
#: src/user/user-settings/settings/SourceSettingsPlex.ts
|
||||
msgid "Disconnect"
|
||||
msgstr "Disconnect"
|
||||
|
||||
|
@ -1364,6 +1396,7 @@ msgid "Dummy stage used for testing. Shows a simple continue button and always p
|
|||
msgstr "Dummy stage used for testing. Shows a simple continue button and always passes."
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorDuo.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorDuo.ts
|
||||
msgid "Duo"
|
||||
msgstr "Duo"
|
||||
|
||||
|
@ -1423,6 +1456,7 @@ msgid "Edit User"
|
|||
msgstr "Edit User"
|
||||
|
||||
#: src/pages/LibraryPage.ts
|
||||
#: src/user/LibraryPage.ts
|
||||
msgid "Either no applications are defined, or you don't have access to any."
|
||||
msgstr "Either no applications are defined, or you don't have access to any."
|
||||
|
||||
|
@ -1432,6 +1466,7 @@ msgstr "Either no applications are defined, or you don't have access to any."
|
|||
#: src/pages/user-settings/UserSelfForm.ts
|
||||
#: src/pages/users/UserForm.ts
|
||||
#: src/pages/users/UserViewPage.ts
|
||||
#: src/user/user-settings/UserSelfForm.ts
|
||||
msgid "Email"
|
||||
msgstr "Email"
|
||||
|
||||
|
@ -1469,6 +1504,7 @@ msgstr "Embedded outpost is not configured correctly."
|
|||
#~ msgstr "Enable"
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorDuo.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorDuo.ts
|
||||
msgid "Enable Duo authenticator"
|
||||
msgstr "Enable Duo authenticator"
|
||||
|
||||
|
@ -1477,10 +1513,12 @@ msgid "Enable StartTLS"
|
|||
msgstr "Enable StartTLS"
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorStatic.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorStatic.ts
|
||||
msgid "Enable Static Tokens"
|
||||
msgstr "Enable Static Tokens"
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
|
||||
msgid "Enable TOTP"
|
||||
msgstr "Enable TOTP"
|
||||
|
||||
|
@ -1533,10 +1571,12 @@ msgid "Error when validating assertion on server: {err}"
|
|||
msgstr "Error when validating assertion on server: {err}"
|
||||
|
||||
#: src/pages/user-settings/UserSettingsPage.ts
|
||||
#: src/user/user-settings/UserSettingsPage.ts
|
||||
msgid "Error: unsupported source settings: {0}"
|
||||
msgstr "Error: unsupported source settings: {0}"
|
||||
|
||||
#: src/pages/user-settings/UserSettingsPage.ts
|
||||
#: src/user/user-settings/UserSettingsPage.ts
|
||||
msgid "Error: unsupported stage settings: {0}"
|
||||
msgstr "Error: unsupported stage settings: {0}"
|
||||
|
||||
|
@ -1622,6 +1662,8 @@ msgstr "Expires?"
|
|||
|
||||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "Expiring"
|
||||
msgstr "Expiring"
|
||||
|
||||
|
@ -1884,6 +1926,10 @@ msgstr "Generate"
|
|||
msgid "Generate Certificate-Key Pair"
|
||||
msgstr "Generate Certificate-Key Pair"
|
||||
|
||||
#: src/interfaces/UserInterface.ts
|
||||
msgid "Go to admin interface"
|
||||
msgstr "Go to admin interface"
|
||||
|
||||
#: src/elements/table/TablePagination.ts
|
||||
msgid "Go to next page"
|
||||
msgstr "Go to next page"
|
||||
|
@ -1892,6 +1938,10 @@ msgstr "Go to next page"
|
|||
msgid "Go to previous page"
|
||||
msgstr "Go to previous page"
|
||||
|
||||
#: src/interfaces/AdminInterface.ts
|
||||
msgid "Go to user interface"
|
||||
msgstr "Go to user interface"
|
||||
|
||||
#: src/pages/events/RuleForm.ts
|
||||
#: src/pages/policies/PolicyBindingForm.ts
|
||||
#: src/pages/policies/PolicyBindingForm.ts
|
||||
|
@ -2029,6 +2079,8 @@ msgstr "Icon shown in the browser tab."
|
|||
#: src/pages/tokens/TokenListPage.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenForm.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/user/user-settings/tokens/UserTokenForm.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "Identifier"
|
||||
msgstr "Identifier"
|
||||
|
||||
|
@ -2134,6 +2186,7 @@ msgstr "Integrations"
|
|||
#: src/pages/tokens/TokenForm.ts
|
||||
#: src/pages/tokens/TokenListPage.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "Intent"
|
||||
msgstr "Intent"
|
||||
|
||||
|
@ -2274,7 +2327,7 @@ msgstr "Launch URL"
|
|||
msgid "Let the user identify themselves with their username or Email address."
|
||||
msgstr "Let the user identify themselves with their username or Email address."
|
||||
|
||||
#: src/interfaces/AdminInterface.ts
|
||||
#: src/interfaces/UserInterface.ts
|
||||
msgid "Library"
|
||||
msgstr "Library"
|
||||
|
||||
|
@ -2322,6 +2375,7 @@ msgstr "Load servers"
|
|||
#: src/pages/applications/ApplicationViewPage.ts
|
||||
#: src/pages/applications/ApplicationViewPage.ts
|
||||
#: src/pages/user-settings/UserSelfForm.ts
|
||||
#: src/user/user-settings/UserSelfForm.ts
|
||||
#: src/utils.ts
|
||||
msgid "Loading"
|
||||
msgstr "Loading"
|
||||
|
@ -2541,11 +2595,12 @@ msgstr "Model deleted"
|
|||
msgid "Model updated"
|
||||
msgstr "Model updated"
|
||||
|
||||
#: src/interfaces/AdminInterface.ts
|
||||
msgid "Monitor"
|
||||
msgstr "Monitor"
|
||||
#:
|
||||
#~ msgid "Monitor"
|
||||
#~ msgstr "Monitor"
|
||||
|
||||
#: src/pages/LibraryPage.ts
|
||||
#: src/user/LibraryPage.ts
|
||||
msgid "My Applications"
|
||||
msgstr "My Applications"
|
||||
|
||||
|
@ -2627,6 +2682,7 @@ msgstr "My Applications"
|
|||
#: src/pages/users/UserForm.ts
|
||||
#: src/pages/users/UserListPage.ts
|
||||
#: src/pages/users/UserViewPage.ts
|
||||
#: src/user/user-settings/UserSelfForm.ts
|
||||
msgid "Name"
|
||||
msgstr "Name"
|
||||
|
||||
|
@ -2676,10 +2732,12 @@ msgstr "Newly created users are added to this group, if a group is selected."
|
|||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/pages/users/GroupSelectModal.ts
|
||||
#: src/pages/users/UserListPage.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "No"
|
||||
msgstr "No"
|
||||
|
||||
#: src/pages/LibraryPage.ts
|
||||
#: src/user/LibraryPage.ts
|
||||
msgid "No Applications available."
|
||||
msgstr "No Applications available."
|
||||
|
||||
|
@ -2747,6 +2805,8 @@ msgstr "Not configured action"
|
|||
|
||||
#: src/pages/user-settings/settings/SourceSettingsOAuth.ts
|
||||
#: src/pages/user-settings/settings/SourceSettingsPlex.ts
|
||||
#: src/user/user-settings/settings/SourceSettingsOAuth.ts
|
||||
#: src/user/user-settings/settings/SourceSettingsPlex.ts
|
||||
msgid "Not connected."
|
||||
msgstr "Not connected."
|
||||
|
||||
|
@ -3414,6 +3474,7 @@ msgstr "Required."
|
|||
#: src/pages/user-settings/UserSelfForm.ts
|
||||
#: src/pages/users/ServiceAccountForm.ts
|
||||
#: src/pages/users/UserForm.ts
|
||||
#: src/user/user-settings/UserSelfForm.ts
|
||||
msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only."
|
||||
msgstr "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only."
|
||||
|
||||
|
@ -3790,6 +3851,8 @@ msgstr "Source linked"
|
|||
|
||||
#: src/pages/user-settings/settings/SourceSettingsOAuth.ts
|
||||
#: src/pages/user-settings/settings/SourceSettingsPlex.ts
|
||||
#: src/user/user-settings/settings/SourceSettingsOAuth.ts
|
||||
#: src/user/user-settings/settings/SourceSettingsPlex.ts
|
||||
msgid "Source {0}"
|
||||
msgstr "Source {0}"
|
||||
|
||||
|
@ -3900,6 +3963,7 @@ msgid "Static Tokens"
|
|||
msgstr "Static Tokens"
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorStatic.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorStatic.ts
|
||||
msgid "Static tokens"
|
||||
msgstr "Static tokens"
|
||||
|
||||
|
@ -3918,12 +3982,18 @@ msgstr "Status"
|
|||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorDuo.ts
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorStatic.ts
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorDuo.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorStatic.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
|
||||
msgid "Status: Disabled"
|
||||
msgstr "Status: Disabled"
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorDuo.ts
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorStatic.ts
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorDuo.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorStatic.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
|
||||
msgid "Status: Enabled"
|
||||
msgstr "Status: Enabled"
|
||||
|
||||
|
@ -4065,6 +4135,7 @@ msgstr "Successfully created tenant."
|
|||
|
||||
#: src/pages/tokens/TokenForm.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenForm.ts
|
||||
#: src/user/user-settings/tokens/UserTokenForm.ts
|
||||
msgid "Successfully created token."
|
||||
msgstr "Successfully created token."
|
||||
|
||||
|
@ -4123,10 +4194,12 @@ msgid "Successfully updated certificate-key pair."
|
|||
msgstr "Successfully updated certificate-key pair."
|
||||
|
||||
#: src/pages/user-settings/UserSelfForm.ts
|
||||
#: src/user/user-settings/UserSelfForm.ts
|
||||
msgid "Successfully updated details."
|
||||
msgstr "Successfully updated details."
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
msgid "Successfully updated device."
|
||||
msgstr "Successfully updated device."
|
||||
|
||||
|
@ -4222,6 +4295,7 @@ msgstr "Successfully updated tenant."
|
|||
|
||||
#: src/pages/tokens/TokenForm.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenForm.ts
|
||||
#: src/user/user-settings/tokens/UserTokenForm.ts
|
||||
msgid "Successfully updated token."
|
||||
msgstr "Successfully updated token."
|
||||
|
||||
|
@ -4465,6 +4539,7 @@ msgid "Time offset when temporary users should be deleted. This only applies if
|
|||
msgstr "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3)."
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
|
||||
msgid "Time-based One-Time Passwords"
|
||||
msgstr "Time-based One-Time Passwords"
|
||||
|
||||
|
@ -4512,6 +4587,7 @@ msgstr "Token validity"
|
|||
|
||||
#: src/pages/tokens/TokenListPage.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "Token(s)"
|
||||
msgstr "Token(s)"
|
||||
|
||||
|
@ -4525,6 +4601,7 @@ msgid "Tokens & App passwords"
|
|||
msgstr "Tokens & App passwords"
|
||||
|
||||
#: src/pages/user-settings/UserSettingsPage.ts
|
||||
#: src/user/user-settings/UserSettingsPage.ts
|
||||
msgid "Tokens and App passwords"
|
||||
msgstr "Tokens and App passwords"
|
||||
|
||||
|
@ -4692,6 +4769,11 @@ msgstr "Up-to-date!"
|
|||
#: src/pages/users/UserActiveForm.ts
|
||||
#: src/pages/users/UserListPage.ts
|
||||
#: src/pages/users/UserViewPage.ts
|
||||
#: src/user/user-settings/UserSelfForm.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "Update"
|
||||
msgstr "Update"
|
||||
|
||||
|
@ -4775,6 +4857,7 @@ msgstr "Update Tenant"
|
|||
|
||||
#: src/pages/tokens/TokenListPage.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "Update Token"
|
||||
msgstr "Update Token"
|
||||
|
||||
|
@ -4789,6 +4872,7 @@ msgid "Update available"
|
|||
msgstr "Update available"
|
||||
|
||||
#: src/pages/user-settings/UserSettingsPage.ts
|
||||
#: src/user/user-settings/UserSettingsPage.ts
|
||||
msgid "Update details"
|
||||
msgstr "Update details"
|
||||
|
||||
|
@ -4867,6 +4951,7 @@ msgstr "Use this tenant for each domain that doesn't have a dedicated tenant."
|
|||
#: src/pages/tokens/TokenListPage.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/pages/users/UserListPage.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "User"
|
||||
msgstr "User"
|
||||
|
||||
|
@ -4884,6 +4969,7 @@ msgid "User Reputation"
|
|||
msgstr "User Reputation"
|
||||
|
||||
#: src/pages/user-settings/UserSettingsPage.ts
|
||||
#: src/user/user-settings/UserSettingsPage.ts
|
||||
msgid "User Settings"
|
||||
msgstr "User Settings"
|
||||
|
||||
|
@ -4900,6 +4986,7 @@ msgid "User database + standard password"
|
|||
msgstr "User database + standard password"
|
||||
|
||||
#: src/pages/user-settings/UserSettingsPage.ts
|
||||
#: src/user/user-settings/UserSettingsPage.ts
|
||||
msgid "User details"
|
||||
msgstr "User details"
|
||||
|
||||
|
@ -4943,6 +5030,7 @@ msgstr "User's avatar"
|
|||
|
||||
#: src/pages/user-settings/UserSelfForm.ts
|
||||
#: src/pages/users/UserForm.ts
|
||||
#: src/user/user-settings/UserSelfForm.ts
|
||||
msgid "User's display name."
|
||||
msgstr "User's display name."
|
||||
|
||||
|
@ -4971,6 +5059,7 @@ msgstr "Userinfo URL"
|
|||
#: src/pages/users/UserForm.ts
|
||||
#: src/pages/users/UserListPage.ts
|
||||
#: src/pages/users/UserViewPage.ts
|
||||
#: src/user/user-settings/UserSelfForm.ts
|
||||
msgid "Username"
|
||||
msgstr "Username"
|
||||
|
||||
|
@ -5000,6 +5089,10 @@ msgstr "Using flow"
|
|||
msgid "Using source"
|
||||
msgstr "Using source"
|
||||
|
||||
#: src/pages/users/ServiceAccountForm.ts
|
||||
msgid "Valid for 360 days, after which the password will automatically rotate. You can copy the password from the Token List."
|
||||
msgstr "Valid for 360 days, after which the password will automatically rotate. You can copy the password from the Token List."
|
||||
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||
msgid "Valid redirect URLs after a successful authorization flow. Also specify any origins here for Implicit flows."
|
||||
msgstr "Valid redirect URLs after a successful authorization flow. Also specify any origins here for Implicit flows."
|
||||
|
@ -5085,6 +5178,7 @@ msgid "WebAuthn Authenticators"
|
|||
msgstr "WebAuthn Authenticators"
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
msgid "WebAuthn Devices"
|
||||
msgstr "WebAuthn Devices"
|
||||
|
||||
|
@ -5180,6 +5274,7 @@ msgstr "X509 Subject"
|
|||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/pages/users/GroupSelectModal.ts
|
||||
#: src/pages/users/UserListPage.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "Yes"
|
||||
msgstr "Yes"
|
||||
|
||||
|
@ -5188,6 +5283,7 @@ msgid "You can only select providers that match the type of the outpost."
|
|||
msgstr "You can only select providers that match the type of the outpost."
|
||||
|
||||
#: src/interfaces/AdminInterface.ts
|
||||
#: src/interfaces/UserInterface.ts
|
||||
msgid "You're currently impersonating {0}. Click to stop."
|
||||
msgstr "You're currently impersonating {0}. Click to stop."
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ msgid "8 digits, not compatible with apps like Google Authenticator"
|
|||
msgstr ""
|
||||
|
||||
#: src/interfaces/AdminInterface.ts
|
||||
#: src/interfaces/UserInterface.ts
|
||||
msgid "A newer version of the frontend is available."
|
||||
msgstr ""
|
||||
|
||||
|
@ -167,6 +168,10 @@ msgstr ""
|
|||
msgid "Additional user DN, prepended to the Base DN."
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
#~ msgid "Admin"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||
#: src/pages/providers/proxy/ProxyProviderForm.ts
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
|
@ -282,6 +287,7 @@ msgstr ""
|
|||
#: src/pages/LibraryPage.ts
|
||||
#: src/pages/applications/ApplicationListPage.ts
|
||||
#: src/pages/outposts/OutpostForm.ts
|
||||
#: src/user/LibraryPage.ts
|
||||
msgid "Applications"
|
||||
msgstr ""
|
||||
|
||||
|
@ -377,6 +383,7 @@ msgstr ""
|
|||
|
||||
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStage.ts
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
msgid "Authenticator"
|
||||
msgstr ""
|
||||
|
||||
|
@ -598,6 +605,7 @@ msgid "Certificates"
|
|||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsPassword.ts
|
||||
#: src/user/user-settings/settings/UserSettingsPassword.ts
|
||||
msgid "Change password"
|
||||
msgstr ""
|
||||
|
||||
|
@ -606,6 +614,7 @@ msgid "Change status"
|
|||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsPassword.ts
|
||||
#: src/user/user-settings/settings/UserSettingsPassword.ts
|
||||
msgid "Change your password"
|
||||
msgstr ""
|
||||
|
||||
|
@ -782,6 +791,7 @@ msgid "Configuration stage"
|
|||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
msgid "Configure WebAuthn"
|
||||
msgstr ""
|
||||
|
||||
|
@ -814,6 +824,7 @@ msgid "Configure how the issuer field of the ID Token should be filled."
|
|||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/UserSettingsPage.ts
|
||||
#: src/user/user-settings/UserSettingsPage.ts
|
||||
msgid "Configure settings relevant to your user profile."
|
||||
msgstr ""
|
||||
|
||||
|
@ -830,11 +841,14 @@ msgid "Configure what data should be used as unique User Identifier. For most ca
|
|||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/settings/SourceSettingsOAuth.ts
|
||||
#: src/user/user-settings/settings/SourceSettingsOAuth.ts
|
||||
msgid "Connect"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/settings/SourceSettingsOAuth.ts
|
||||
#: src/pages/user-settings/settings/SourceSettingsPlex.ts
|
||||
#: src/user/user-settings/settings/SourceSettingsOAuth.ts
|
||||
#: src/user/user-settings/settings/SourceSettingsPlex.ts
|
||||
msgid "Connected."
|
||||
msgstr ""
|
||||
|
||||
|
@ -921,6 +935,7 @@ msgid "Copy"
|
|||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "Copy Key"
|
||||
msgstr ""
|
||||
|
||||
|
@ -982,11 +997,15 @@ msgstr ""
|
|||
#: src/pages/users/UserListPage.ts
|
||||
#: src/pages/users/UserListPage.ts
|
||||
#: src/pages/users/UserListPage.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "Create"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "Create App password"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1064,6 +1083,8 @@ msgstr ""
|
|||
#: src/pages/tokens/TokenListPage.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "Create Token"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1100,6 +1121,7 @@ msgid "Created by"
|
|||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
msgid "Created {0}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1185,6 +1207,8 @@ msgstr ""
|
|||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/pages/users/UserListPage.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "Delete"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1210,6 +1234,7 @@ msgstr ""
|
|||
#~ msgstr ""
|
||||
|
||||
#: src/pages/user-settings/UserSelfForm.ts
|
||||
#: src/user/user-settings/UserSelfForm.ts
|
||||
msgid "Delete account"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1245,6 +1270,7 @@ msgstr ""
|
|||
#: src/pages/system-tasks/SystemTaskListPage.ts
|
||||
#: src/pages/tokens/TokenForm.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenForm.ts
|
||||
#: src/user/user-settings/tokens/UserTokenForm.ts
|
||||
msgid "Description"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1286,6 +1312,7 @@ msgid "Device classes which can be used to authenticate."
|
|||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
msgid "Device name"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1304,14 +1331,17 @@ msgstr ""
|
|||
#~ msgstr ""
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorDuo.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorDuo.ts
|
||||
msgid "Disable Duo authenticator"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorStatic.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorStatic.ts
|
||||
msgid "Disable Static Tokens"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
|
||||
msgid "Disable Time-based OTP"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1321,6 +1351,8 @@ msgstr ""
|
|||
|
||||
#: src/pages/user-settings/settings/SourceSettingsOAuth.ts
|
||||
#: src/pages/user-settings/settings/SourceSettingsPlex.ts
|
||||
#: src/user/user-settings/settings/SourceSettingsOAuth.ts
|
||||
#: src/user/user-settings/settings/SourceSettingsPlex.ts
|
||||
msgid "Disconnect"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1356,6 +1388,7 @@ msgid "Dummy stage used for testing. Shows a simple continue button and always p
|
|||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorDuo.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorDuo.ts
|
||||
msgid "Duo"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1415,6 +1448,7 @@ msgid "Edit User"
|
|||
msgstr ""
|
||||
|
||||
#: src/pages/LibraryPage.ts
|
||||
#: src/user/LibraryPage.ts
|
||||
msgid "Either no applications are defined, or you don't have access to any."
|
||||
msgstr ""
|
||||
|
||||
|
@ -1424,6 +1458,7 @@ msgstr ""
|
|||
#: src/pages/user-settings/UserSelfForm.ts
|
||||
#: src/pages/users/UserForm.ts
|
||||
#: src/pages/users/UserViewPage.ts
|
||||
#: src/user/user-settings/UserSelfForm.ts
|
||||
msgid "Email"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1461,6 +1496,7 @@ msgstr ""
|
|||
#~ msgstr ""
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorDuo.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorDuo.ts
|
||||
msgid "Enable Duo authenticator"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1469,10 +1505,12 @@ msgid "Enable StartTLS"
|
|||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorStatic.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorStatic.ts
|
||||
msgid "Enable Static Tokens"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
|
||||
msgid "Enable TOTP"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1525,10 +1563,12 @@ msgid "Error when validating assertion on server: {err}"
|
|||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/UserSettingsPage.ts
|
||||
#: src/user/user-settings/UserSettingsPage.ts
|
||||
msgid "Error: unsupported source settings: {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/UserSettingsPage.ts
|
||||
#: src/user/user-settings/UserSettingsPage.ts
|
||||
msgid "Error: unsupported stage settings: {0}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1614,6 +1654,8 @@ msgstr ""
|
|||
|
||||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "Expiring"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1876,6 +1918,10 @@ msgstr ""
|
|||
msgid "Generate Certificate-Key Pair"
|
||||
msgstr ""
|
||||
|
||||
#: src/interfaces/UserInterface.ts
|
||||
msgid "Go to admin interface"
|
||||
msgstr ""
|
||||
|
||||
#: src/elements/table/TablePagination.ts
|
||||
msgid "Go to next page"
|
||||
msgstr ""
|
||||
|
@ -1884,6 +1930,10 @@ msgstr ""
|
|||
msgid "Go to previous page"
|
||||
msgstr ""
|
||||
|
||||
#: src/interfaces/AdminInterface.ts
|
||||
msgid "Go to user interface"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/RuleForm.ts
|
||||
#: src/pages/policies/PolicyBindingForm.ts
|
||||
#: src/pages/policies/PolicyBindingForm.ts
|
||||
|
@ -2021,6 +2071,8 @@ msgstr ""
|
|||
#: src/pages/tokens/TokenListPage.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenForm.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/user/user-settings/tokens/UserTokenForm.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "Identifier"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2126,6 +2178,7 @@ msgstr ""
|
|||
#: src/pages/tokens/TokenForm.ts
|
||||
#: src/pages/tokens/TokenListPage.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "Intent"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2266,7 +2319,7 @@ msgstr ""
|
|||
msgid "Let the user identify themselves with their username or Email address."
|
||||
msgstr ""
|
||||
|
||||
#: src/interfaces/AdminInterface.ts
|
||||
#: src/interfaces/UserInterface.ts
|
||||
msgid "Library"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2314,6 +2367,7 @@ msgstr ""
|
|||
#: src/pages/applications/ApplicationViewPage.ts
|
||||
#: src/pages/applications/ApplicationViewPage.ts
|
||||
#: src/pages/user-settings/UserSelfForm.ts
|
||||
#: src/user/user-settings/UserSelfForm.ts
|
||||
#: src/utils.ts
|
||||
msgid "Loading"
|
||||
msgstr ""
|
||||
|
@ -2533,11 +2587,12 @@ msgstr ""
|
|||
msgid "Model updated"
|
||||
msgstr ""
|
||||
|
||||
#: src/interfaces/AdminInterface.ts
|
||||
msgid "Monitor"
|
||||
msgstr ""
|
||||
#:
|
||||
#~ msgid "Monitor"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/pages/LibraryPage.ts
|
||||
#: src/user/LibraryPage.ts
|
||||
msgid "My Applications"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2619,6 +2674,7 @@ msgstr ""
|
|||
#: src/pages/users/UserForm.ts
|
||||
#: src/pages/users/UserListPage.ts
|
||||
#: src/pages/users/UserViewPage.ts
|
||||
#: src/user/user-settings/UserSelfForm.ts
|
||||
msgid "Name"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2668,10 +2724,12 @@ msgstr ""
|
|||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/pages/users/GroupSelectModal.ts
|
||||
#: src/pages/users/UserListPage.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "No"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/LibraryPage.ts
|
||||
#: src/user/LibraryPage.ts
|
||||
msgid "No Applications available."
|
||||
msgstr ""
|
||||
|
||||
|
@ -2739,6 +2797,8 @@ msgstr ""
|
|||
|
||||
#: src/pages/user-settings/settings/SourceSettingsOAuth.ts
|
||||
#: src/pages/user-settings/settings/SourceSettingsPlex.ts
|
||||
#: src/user/user-settings/settings/SourceSettingsOAuth.ts
|
||||
#: src/user/user-settings/settings/SourceSettingsPlex.ts
|
||||
msgid "Not connected."
|
||||
msgstr ""
|
||||
|
||||
|
@ -3406,6 +3466,7 @@ msgstr ""
|
|||
#: src/pages/user-settings/UserSelfForm.ts
|
||||
#: src/pages/users/ServiceAccountForm.ts
|
||||
#: src/pages/users/UserForm.ts
|
||||
#: src/user/user-settings/UserSelfForm.ts
|
||||
msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only."
|
||||
msgstr ""
|
||||
|
||||
|
@ -3782,6 +3843,8 @@ msgstr ""
|
|||
|
||||
#: src/pages/user-settings/settings/SourceSettingsOAuth.ts
|
||||
#: src/pages/user-settings/settings/SourceSettingsPlex.ts
|
||||
#: src/user/user-settings/settings/SourceSettingsOAuth.ts
|
||||
#: src/user/user-settings/settings/SourceSettingsPlex.ts
|
||||
msgid "Source {0}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -3892,6 +3955,7 @@ msgid "Static Tokens"
|
|||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorStatic.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorStatic.ts
|
||||
msgid "Static tokens"
|
||||
msgstr ""
|
||||
|
||||
|
@ -3910,12 +3974,18 @@ msgstr ""
|
|||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorDuo.ts
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorStatic.ts
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorDuo.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorStatic.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
|
||||
msgid "Status: Disabled"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorDuo.ts
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorStatic.ts
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorDuo.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorStatic.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
|
||||
msgid "Status: Enabled"
|
||||
msgstr ""
|
||||
|
||||
|
@ -4057,6 +4127,7 @@ msgstr ""
|
|||
|
||||
#: src/pages/tokens/TokenForm.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenForm.ts
|
||||
#: src/user/user-settings/tokens/UserTokenForm.ts
|
||||
msgid "Successfully created token."
|
||||
msgstr ""
|
||||
|
||||
|
@ -4115,10 +4186,12 @@ msgid "Successfully updated certificate-key pair."
|
|||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/UserSelfForm.ts
|
||||
#: src/user/user-settings/UserSelfForm.ts
|
||||
msgid "Successfully updated details."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
msgid "Successfully updated device."
|
||||
msgstr ""
|
||||
|
||||
|
@ -4214,6 +4287,7 @@ msgstr ""
|
|||
|
||||
#: src/pages/tokens/TokenForm.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenForm.ts
|
||||
#: src/user/user-settings/tokens/UserTokenForm.ts
|
||||
msgid "Successfully updated token."
|
||||
msgstr ""
|
||||
|
||||
|
@ -4450,6 +4524,7 @@ msgid "Time offset when temporary users should be deleted. This only applies if
|
|||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
|
||||
msgid "Time-based One-Time Passwords"
|
||||
msgstr ""
|
||||
|
||||
|
@ -4497,6 +4572,7 @@ msgstr ""
|
|||
|
||||
#: src/pages/tokens/TokenListPage.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "Token(s)"
|
||||
msgstr ""
|
||||
|
||||
|
@ -4510,6 +4586,7 @@ msgid "Tokens & App passwords"
|
|||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/UserSettingsPage.ts
|
||||
#: src/user/user-settings/UserSettingsPage.ts
|
||||
msgid "Tokens and App passwords"
|
||||
msgstr ""
|
||||
|
||||
|
@ -4677,6 +4754,11 @@ msgstr ""
|
|||
#: src/pages/users/UserActiveForm.ts
|
||||
#: src/pages/users/UserListPage.ts
|
||||
#: src/pages/users/UserViewPage.ts
|
||||
#: src/user/user-settings/UserSelfForm.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "Update"
|
||||
msgstr ""
|
||||
|
||||
|
@ -4760,6 +4842,7 @@ msgstr ""
|
|||
|
||||
#: src/pages/tokens/TokenListPage.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "Update Token"
|
||||
msgstr ""
|
||||
|
||||
|
@ -4774,6 +4857,7 @@ msgid "Update available"
|
|||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/UserSettingsPage.ts
|
||||
#: src/user/user-settings/UserSettingsPage.ts
|
||||
msgid "Update details"
|
||||
msgstr ""
|
||||
|
||||
|
@ -4852,6 +4936,7 @@ msgstr ""
|
|||
#: src/pages/tokens/TokenListPage.ts
|
||||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/pages/users/UserListPage.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "User"
|
||||
msgstr ""
|
||||
|
||||
|
@ -4869,6 +4954,7 @@ msgid "User Reputation"
|
|||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/UserSettingsPage.ts
|
||||
#: src/user/user-settings/UserSettingsPage.ts
|
||||
msgid "User Settings"
|
||||
msgstr ""
|
||||
|
||||
|
@ -4885,6 +4971,7 @@ msgid "User database + standard password"
|
|||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/UserSettingsPage.ts
|
||||
#: src/user/user-settings/UserSettingsPage.ts
|
||||
msgid "User details"
|
||||
msgstr ""
|
||||
|
||||
|
@ -4928,6 +5015,7 @@ msgstr ""
|
|||
|
||||
#: src/pages/user-settings/UserSelfForm.ts
|
||||
#: src/pages/users/UserForm.ts
|
||||
#: src/user/user-settings/UserSelfForm.ts
|
||||
msgid "User's display name."
|
||||
msgstr ""
|
||||
|
||||
|
@ -4956,6 +5044,7 @@ msgstr ""
|
|||
#: src/pages/users/UserForm.ts
|
||||
#: src/pages/users/UserListPage.ts
|
||||
#: src/pages/users/UserViewPage.ts
|
||||
#: src/user/user-settings/UserSelfForm.ts
|
||||
msgid "Username"
|
||||
msgstr ""
|
||||
|
||||
|
@ -4985,6 +5074,10 @@ msgstr ""
|
|||
msgid "Using source"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/users/ServiceAccountForm.ts
|
||||
msgid "Valid for 360 days, after which the password will automatically rotate. You can copy the password from the Token List."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||
msgid "Valid redirect URLs after a successful authorization flow. Also specify any origins here for Implicit flows."
|
||||
msgstr ""
|
||||
|
@ -5070,6 +5163,7 @@ msgid "WebAuthn Authenticators"
|
|||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
#: src/user/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts
|
||||
msgid "WebAuthn Devices"
|
||||
msgstr ""
|
||||
|
||||
|
@ -5163,6 +5257,7 @@ msgstr ""
|
|||
#: src/pages/user-settings/tokens/UserTokenList.ts
|
||||
#: src/pages/users/GroupSelectModal.ts
|
||||
#: src/pages/users/UserListPage.ts
|
||||
#: src/user/user-settings/tokens/UserTokenList.ts
|
||||
msgid "Yes"
|
||||
msgstr ""
|
||||
|
||||
|
@ -5171,6 +5266,7 @@ msgid "You can only select providers that match the type of the outpost."
|
|||
msgstr ""
|
||||
|
||||
#: src/interfaces/AdminInterface.ts
|
||||
#: src/interfaces/UserInterface.ts
|
||||
msgid "You're currently impersonating {0}. Click to stop."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import "./pages/events/TransportListPage";
|
|||
import "./pages/flows/FlowListPage";
|
||||
import "./pages/flows/FlowViewPage";
|
||||
import "./pages/groups/GroupListPage";
|
||||
import "./pages/LibraryPage";
|
||||
import "./user/LibraryPage";
|
||||
import "./pages/outposts/OutpostListPage";
|
||||
import "./pages/outposts/ServiceConnectionListPage";
|
||||
import "./pages/policies/PolicyListPage";
|
||||
|
@ -35,8 +35,8 @@ import "./pages/users/UserViewPage";
|
|||
|
||||
export const ROUTES: Route[] = [
|
||||
// Prevent infinite Shell loops
|
||||
new Route(new RegExp("^/$")).redirect("/library"),
|
||||
new Route(new RegExp("^#.*")).redirect("/library"),
|
||||
new Route(new RegExp("^/$")).redirect("/administration/overview"),
|
||||
new Route(new RegExp("^#.*")).redirect("/administration/overview"),
|
||||
new Route(new RegExp("^/library$"), html`<ak-library></ak-library>`),
|
||||
new Route(
|
||||
new RegExp("^/administration/overview$"),
|
13
web/src/routesUser.ts
Normal file
13
web/src/routesUser.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { html } from "lit-html";
|
||||
import { Route } from "./elements/router/Route";
|
||||
|
||||
import "./user/LibraryPage";
|
||||
import "./user/user-settings/UserSettingsPage";
|
||||
|
||||
export const ROUTES: Route[] = [
|
||||
// Prevent infinite Shell loops
|
||||
new Route(new RegExp("^/$")).redirect("/library"),
|
||||
new Route(new RegExp("^#.*")).redirect("/library"),
|
||||
new Route(new RegExp("^/library$"), html`<ak-library></ak-library>`),
|
||||
new Route(new RegExp("^/user$"), html`<ak-user-settings></ak-user-settings>`),
|
||||
];
|
170
web/src/user/LibraryPage.ts
Normal file
170
web/src/user/LibraryPage.ts
Normal file
|
@ -0,0 +1,170 @@
|
|||
import { t } from "@lingui/macro";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { ifDefined } from "lit-html/directives/if-defined";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { Application, CoreApi } from "@goauthentik/api";
|
||||
import { AKResponse } from "../api/Client";
|
||||
import { DEFAULT_CONFIG } from "../api/Config";
|
||||
import { me } from "../api/Users";
|
||||
import { loading, truncate } from "../utils";
|
||||
import "../elements/PageHeader";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
||||
import PFTitle from "@patternfly/patternfly/components/Title/title.css";
|
||||
import PFEmptyState from "@patternfly/patternfly/components/EmptyState/empty-state.css";
|
||||
import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
||||
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
||||
import AKGlobal from "../authentik.css";
|
||||
import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";
|
||||
import PFGallery from "@patternfly/patternfly/layouts/Gallery/gallery.css";
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
|
||||
@customElement("ak-library-app")
|
||||
export class LibraryApplication extends LitElement {
|
||||
@property({ attribute: false })
|
||||
application?: Application;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
PFBase,
|
||||
PFCard,
|
||||
PFButton,
|
||||
PFAvatar,
|
||||
AKGlobal,
|
||||
css`
|
||||
.pf-c-card {
|
||||
height: 100%;
|
||||
}
|
||||
i.pf-icon {
|
||||
height: 36px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
.pf-c-avatar {
|
||||
--pf-c-avatar--BorderRadius: 0;
|
||||
}
|
||||
.pf-c-card__header {
|
||||
min-height: 60px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.pf-c-card__header a {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
if (!this.application) {
|
||||
return html`<ak-spinner></ak-spinner>`;
|
||||
}
|
||||
return html` <div class="pf-c-card pf-m-hoverable pf-m-compact">
|
||||
<div class="pf-c-card__header">
|
||||
${this.application.metaIcon
|
||||
? html`<a href="${ifDefined(this.application.launchUrl ?? "")}"
|
||||
><img
|
||||
class="app-icon pf-c-avatar"
|
||||
src="${ifDefined(this.application.metaIcon)}"
|
||||
alt="Application Icon"
|
||||
/></a>`
|
||||
: html`<i class="fas fas fa-share-square"></i>`}
|
||||
${until(
|
||||
me().then((u) => {
|
||||
if (!u.user.isSuperuser) return html``;
|
||||
return html`
|
||||
<a
|
||||
class="pf-c-button pf-m-control pf-m-small"
|
||||
href="#/core/applications/${this.application?.slug}"
|
||||
>
|
||||
<i class="fas fa-pencil-alt"></i>
|
||||
</a>
|
||||
`;
|
||||
}),
|
||||
)}
|
||||
</div>
|
||||
<div class="pf-c-card__title">
|
||||
<p id="card-1-check-label">
|
||||
<a href="${ifDefined(this.application.launchUrl ?? "")}"
|
||||
>${this.application.name}</a
|
||||
>
|
||||
</p>
|
||||
<div class="pf-c-content">
|
||||
<small>${this.application.metaPublisher}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-card__body">${truncate(this.application.metaDescription, 35)}</div>
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
@customElement("ak-library")
|
||||
export class LibraryPage extends LitElement {
|
||||
@property({ attribute: false })
|
||||
apps?: AKResponse<Application>;
|
||||
|
||||
pageTitle(): string {
|
||||
return t`My Applications`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFEmptyState, PFTitle, PFPage, PFContent, PFGallery, AKGlobal].concat(css`
|
||||
:host,
|
||||
main {
|
||||
height: 100%;
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
firstUpdated(): void {
|
||||
new CoreApi(DEFAULT_CONFIG).coreApplicationsList({}).then((apps) => {
|
||||
this.apps = apps;
|
||||
});
|
||||
}
|
||||
|
||||
renderEmptyState(): TemplateResult {
|
||||
return html` <div class="pf-c-empty-state pf-m-full-height">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i>
|
||||
<h1 class="pf-c-title pf-m-lg">${t`No Applications available.`}</h1>
|
||||
<div class="pf-c-empty-state__body">
|
||||
${t`Either no applications are defined, or you don't have access to any.`}
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
renderApps(): TemplateResult {
|
||||
return html`<div class="pf-l-gallery pf-m-gutter">
|
||||
${this.apps?.results.map(
|
||||
(app) => html`<ak-library-app .application=${app}></ak-library-app>`,
|
||||
)}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<main role="main" class="pf-c-page__main" tabindex="-1" id="main-content">
|
||||
<ak-page-header icon="pf-icon pf-icon-applications" header=${t`Applications`}>
|
||||
</ak-page-header>
|
||||
<section class="pf-c-page__main-section">
|
||||
${loading(
|
||||
this.apps,
|
||||
html`${(this.apps?.results.length || 0) > 0
|
||||
? this.renderApps()
|
||||
: this.renderEmptyState()}`,
|
||||
)}
|
||||
</section>
|
||||
</main>`;
|
||||
}
|
||||
}
|
100
web/src/user/user-settings/UserSelfForm.ts
Normal file
100
web/src/user/user-settings/UserSelfForm.ts
Normal file
|
@ -0,0 +1,100 @@
|
|||
import { t } from "@lingui/macro";
|
||||
import { customElement, html, TemplateResult } from "lit-element";
|
||||
import { CoreApi, UserSelf } from "@goauthentik/api";
|
||||
import { ifDefined } from "lit-html/directives/if-defined";
|
||||
import { DEFAULT_CONFIG, tenant } from "../../api/Config";
|
||||
import "../../elements/forms/FormElement";
|
||||
import "../../elements/EmptyState";
|
||||
import "../../elements/forms/Form";
|
||||
import "../../elements/forms/HorizontalFormElement";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { ModelForm } from "../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-user-self-form")
|
||||
export class UserSelfForm extends ModelForm<UserSelf, number> {
|
||||
viewportCheck = false;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
loadInstance(pk: number): Promise<UserSelf> {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreUsersMeRetrieve().then((su) => {
|
||||
return su.user;
|
||||
});
|
||||
}
|
||||
|
||||
getSuccessMessage(): string {
|
||||
return t`Successfully updated details.`;
|
||||
}
|
||||
|
||||
send = (data: UserSelf): Promise<UserSelf> => {
|
||||
return new CoreApi(DEFAULT_CONFIG)
|
||||
.coreUsersUpdateSelfUpdate({
|
||||
userSelfRequest: data,
|
||||
})
|
||||
.then((su) => {
|
||||
return su.user;
|
||||
});
|
||||
};
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
if (!this.instance) {
|
||||
return html`<ak-empty-state ?loading="${true}" header=${t`Loading`}> </ak-empty-state>`;
|
||||
}
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal label=${t`Username`} ?required=${true} name="username">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.username)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">${t`User's display name.`}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${t`Email`} name="email">
|
||||
<input
|
||||
type="email"
|
||||
value="${ifDefined(this.instance?.email)}"
|
||||
class="pf-c-form-control"
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<div class="pf-c-form__group pf-m-action">
|
||||
<div class="pf-c-form__horizontal-group">
|
||||
<div class="pf-c-form__actions">
|
||||
<button
|
||||
@click=${(ev: Event) => {
|
||||
return this.submit(ev);
|
||||
}}
|
||||
class="pf-c-button pf-m-primary"
|
||||
>
|
||||
${t`Update`}
|
||||
</button>
|
||||
${until(
|
||||
tenant().then((tenant) => {
|
||||
if (tenant.flowUnenrollment) {
|
||||
return html`<a
|
||||
class="pf-c-button pf-m-danger"
|
||||
href="/if/flow/${tenant.flowUnenrollment}"
|
||||
>
|
||||
${t`Delete account`}
|
||||
</a>`;
|
||||
}
|
||||
return html``;
|
||||
}),
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>`;
|
||||
}
|
||||
}
|
186
web/src/user/user-settings/UserSettingsPage.ts
Normal file
186
web/src/user/user-settings/UserSettingsPage.ts
Normal file
|
@ -0,0 +1,186 @@
|
|||
import { t } from "@lingui/macro";
|
||||
import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
|
||||
|
||||
import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
||||
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
||||
import PFGallery from "@patternfly/patternfly/layouts/Gallery/gallery.css";
|
||||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
||||
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
|
||||
import PFSizing from "@patternfly/patternfly/utilities/Sizing/sizing.css";
|
||||
import PFFlex from "@patternfly/patternfly/utilities/Flex/flex.css";
|
||||
import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css";
|
||||
import AKGlobal from "../../authentik.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
import { SourcesApi, StagesApi, UserSetting } from "@goauthentik/api";
|
||||
import { DEFAULT_CONFIG } from "../../api/Config";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { ifDefined } from "lit-html/directives/if-defined";
|
||||
import "../../elements/Tabs";
|
||||
import "../../elements/PageHeader";
|
||||
import "./tokens/UserTokenList";
|
||||
import "./UserSelfForm";
|
||||
import "./settings/UserSettingsAuthenticatorDuo";
|
||||
import "./settings/UserSettingsAuthenticatorStatic";
|
||||
import "./settings/UserSettingsAuthenticatorTOTP";
|
||||
import "./settings/UserSettingsAuthenticatorWebAuthn";
|
||||
import "./settings/UserSettingsPassword";
|
||||
import "./settings/SourceSettingsOAuth";
|
||||
import "./settings/SourceSettingsPlex";
|
||||
import { EVENT_REFRESH } from "../../constants";
|
||||
|
||||
@customElement("ak-user-settings")
|
||||
export class UserSettingsPage extends LitElement {
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
PFBase,
|
||||
PFPage,
|
||||
PFFlex,
|
||||
PFDisplay,
|
||||
PFGallery,
|
||||
PFContent,
|
||||
PFCard,
|
||||
PFDescriptionList,
|
||||
PFSizing,
|
||||
PFForm,
|
||||
PFFormControl,
|
||||
AKGlobal,
|
||||
];
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
userSettings?: Promise<UserSetting[]>;
|
||||
|
||||
@property({ attribute: false })
|
||||
sourceSettings?: Promise<UserSetting[]>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.addEventListener(EVENT_REFRESH, () => {
|
||||
this.firstUpdated();
|
||||
});
|
||||
}
|
||||
|
||||
firstUpdated(): void {
|
||||
this.userSettings = new StagesApi(DEFAULT_CONFIG).stagesAllUserSettingsList();
|
||||
this.sourceSettings = new SourcesApi(DEFAULT_CONFIG).sourcesAllUserSettingsList();
|
||||
}
|
||||
|
||||
renderStageSettings(stage: UserSetting): TemplateResult {
|
||||
switch (stage.component) {
|
||||
case "ak-user-settings-authenticator-webauthn":
|
||||
return html`<ak-user-settings-authenticator-webauthn
|
||||
objectId=${stage.objectUid}
|
||||
.configureUrl=${stage.configureUrl}
|
||||
>
|
||||
</ak-user-settings-authenticator-webauthn>`;
|
||||
case "ak-user-settings-password":
|
||||
return html`<ak-user-settings-password
|
||||
objectId=${stage.objectUid}
|
||||
.configureUrl=${stage.configureUrl}
|
||||
>
|
||||
</ak-user-settings-password>`;
|
||||
case "ak-user-settings-authenticator-totp":
|
||||
return html`<ak-user-settings-authenticator-totp
|
||||
objectId=${stage.objectUid}
|
||||
.configureUrl=${stage.configureUrl}
|
||||
>
|
||||
</ak-user-settings-authenticator-totp>`;
|
||||
case "ak-user-settings-authenticator-static":
|
||||
return html`<ak-user-settings-authenticator-static
|
||||
objectId=${stage.objectUid}
|
||||
.configureUrl=${stage.configureUrl}
|
||||
>
|
||||
</ak-user-settings-authenticator-static>`;
|
||||
case "ak-user-settings-authenticator-duo":
|
||||
return html`<ak-user-settings-authenticator-duo
|
||||
objectId=${stage.objectUid}
|
||||
.configureUrl=${stage.configureUrl}
|
||||
>
|
||||
</ak-user-settings-authenticator-duo>`;
|
||||
default:
|
||||
return html`<p>${t`Error: unsupported stage settings: ${stage.component}`}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
renderSourceSettings(source: UserSetting): TemplateResult {
|
||||
switch (source.component) {
|
||||
case "ak-user-settings-source-oauth":
|
||||
return html`<ak-user-settings-source-oauth
|
||||
objectId=${source.objectUid}
|
||||
title=${source.title}
|
||||
.configureUrl=${source.configureUrl}
|
||||
>
|
||||
</ak-user-settings-source-oauth>`;
|
||||
case "ak-user-settings-source-plex":
|
||||
return html`<ak-user-settings-source-plex
|
||||
objectId=${source.objectUid}
|
||||
title=${source.title}
|
||||
>
|
||||
</ak-user-settings-source-plex>`;
|
||||
default:
|
||||
return html`<p>${t`Error: unsupported source settings: ${source.component}`}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<div class="pf-c-page">
|
||||
<main role="main" class="pf-c-page__main" tabindex="-1">
|
||||
<ak-page-header
|
||||
icon="pf-icon pf-icon-user"
|
||||
header=${t`User Settings`}
|
||||
description=${t`Configure settings relevant to your user profile.`}
|
||||
>
|
||||
</ak-page-header>
|
||||
<ak-tabs ?vertical="${true}" style="height: 100%;">
|
||||
<section
|
||||
slot="page-details"
|
||||
data-tab-title="${t`User details`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">${t`Update details`}</div>
|
||||
<div class="pf-c-card__body">
|
||||
<ak-user-self-form .instancePk=${1}></ak-user-self-form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section
|
||||
slot="page-tokens"
|
||||
data-tab-title="${t`Tokens and App passwords`}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
<ak-user-token-list></ak-user-token-list>
|
||||
</section>
|
||||
${until(
|
||||
this.userSettings?.then((stages) => {
|
||||
return stages.map((stage) => {
|
||||
return html`<section
|
||||
slot="page-${stage.objectUid}"
|
||||
data-tab-title="${ifDefined(stage.title)}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
${this.renderStageSettings(stage)}
|
||||
</section>`;
|
||||
});
|
||||
}),
|
||||
)}
|
||||
${until(
|
||||
this.sourceSettings?.then((source) => {
|
||||
return source.map((stage) => {
|
||||
return html`<section
|
||||
slot="page-${stage.objectUid}"
|
||||
data-tab-title="${ifDefined(stage.title)}"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
||||
>
|
||||
${this.renderSourceSettings(stage)}
|
||||
</section>`;
|
||||
});
|
||||
}),
|
||||
)}
|
||||
</ak-tabs>
|
||||
</main>
|
||||
</div>`;
|
||||
}
|
||||
}
|
19
web/src/user/user-settings/settings/BaseUserSettings.ts
Normal file
19
web/src/user/user-settings/settings/BaseUserSettings.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { CSSResult, LitElement, property } from "lit-element";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import AKGlobal from "../../../authentik.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
|
||||
export abstract class BaseUserSettings extends LitElement {
|
||||
@property()
|
||||
objectId!: string;
|
||||
|
||||
@property()
|
||||
configureUrl?: string;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFCard, PFButton, PFForm, PFFormControl, AKGlobal];
|
||||
}
|
||||
}
|
50
web/src/user/user-settings/settings/SourceSettingsOAuth.ts
Normal file
50
web/src/user/user-settings/settings/SourceSettingsOAuth.ts
Normal file
|
@ -0,0 +1,50 @@
|
|||
import { customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { BaseUserSettings } from "./BaseUserSettings";
|
||||
import { SourcesApi } from "@goauthentik/api";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { DEFAULT_CONFIG } from "../../../api/Config";
|
||||
import { t } from "@lingui/macro";
|
||||
import { ifDefined } from "lit-html/directives/if-defined";
|
||||
|
||||
@customElement("ak-user-settings-source-oauth")
|
||||
export class SourceSettingsOAuth extends BaseUserSettings {
|
||||
@property()
|
||||
title!: string;
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">${t`Source ${this.title}`}</div>
|
||||
<div class="pf-c-card__body">${this.renderInner()}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
renderInner(): TemplateResult {
|
||||
return html`${until(
|
||||
new SourcesApi(DEFAULT_CONFIG)
|
||||
.sourcesUserConnectionsOauthList({
|
||||
sourceSlug: this.objectId,
|
||||
})
|
||||
.then((connection) => {
|
||||
if (connection.results.length > 0) {
|
||||
return html`<p>${t`Connected.`}</p>
|
||||
<button
|
||||
class="pf-c-button pf-m-danger"
|
||||
@click=${() => {
|
||||
return new SourcesApi(
|
||||
DEFAULT_CONFIG,
|
||||
).sourcesUserConnectionsOauthDestroy({
|
||||
id: connection.results[0].pk || 0,
|
||||
});
|
||||
}}
|
||||
>
|
||||
${t`Disconnect`}
|
||||
</button>`;
|
||||
}
|
||||
return html`<p>${t`Not connected.`}</p>
|
||||
<a class="pf-c-button pf-m-primary" href=${ifDefined(this.configureUrl)}>
|
||||
${t`Connect`}
|
||||
</a>`;
|
||||
}),
|
||||
)}`;
|
||||
}
|
||||
}
|
46
web/src/user/user-settings/settings/SourceSettingsPlex.ts
Normal file
46
web/src/user/user-settings/settings/SourceSettingsPlex.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
import { customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { BaseUserSettings } from "./BaseUserSettings";
|
||||
import { SourcesApi } from "@goauthentik/api";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { DEFAULT_CONFIG } from "../../../api/Config";
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
@customElement("ak-user-settings-source-plex")
|
||||
export class SourceSettingsPlex extends BaseUserSettings {
|
||||
@property()
|
||||
title!: string;
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">${t`Source ${this.title}`}</div>
|
||||
<div class="pf-c-card__body">${this.renderInner()}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
renderInner(): TemplateResult {
|
||||
return html`${until(
|
||||
new SourcesApi(DEFAULT_CONFIG)
|
||||
.sourcesUserConnectionsPlexList({
|
||||
sourceSlug: this.objectId,
|
||||
})
|
||||
.then((connection) => {
|
||||
if (connection.results.length > 0) {
|
||||
return html`<p>${t`Connected.`}</p>
|
||||
<button
|
||||
class="pf-c-button pf-m-danger"
|
||||
@click=${() => {
|
||||
return new SourcesApi(
|
||||
DEFAULT_CONFIG,
|
||||
).sourcesUserConnectionsPlexDestroy({
|
||||
id: connection.results[0].pk || 0,
|
||||
});
|
||||
}}
|
||||
>
|
||||
${t`Disconnect`}
|
||||
</button>`;
|
||||
}
|
||||
return html`<p>${t`Not connected.`}</p>`;
|
||||
}),
|
||||
)}`;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
import { AuthenticatorsApi } from "@goauthentik/api";
|
||||
import { t } from "@lingui/macro";
|
||||
import { customElement, html, TemplateResult } from "lit-element";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { DEFAULT_CONFIG } from "../../../api/Config";
|
||||
import { BaseUserSettings } from "./BaseUserSettings";
|
||||
import { EVENT_REFRESH } from "../../../constants";
|
||||
|
||||
@customElement("ak-user-settings-authenticator-duo")
|
||||
export class UserSettingsAuthenticatorDuo extends BaseUserSettings {
|
||||
renderEnabled(): TemplateResult {
|
||||
return html`<div class="pf-c-card__body">
|
||||
<p>
|
||||
${t`Status: Enabled`}
|
||||
<i class="pf-icon pf-icon-ok"></i>
|
||||
</p>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
<button
|
||||
class="pf-c-button pf-m-danger"
|
||||
@click=${() => {
|
||||
return new AuthenticatorsApi(DEFAULT_CONFIG)
|
||||
.authenticatorsDuoList({})
|
||||
.then((devices) => {
|
||||
if (devices.results.length < 1) {
|
||||
return;
|
||||
}
|
||||
// TODO: Handle multiple devices, currently we assume only one TOTP Device
|
||||
return new AuthenticatorsApi(DEFAULT_CONFIG)
|
||||
.authenticatorsDuoDestroy({
|
||||
id: devices.results[0].pk || 0,
|
||||
})
|
||||
.then(() => {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(EVENT_REFRESH, {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
}}
|
||||
>
|
||||
${t`Disable Duo authenticator`}
|
||||
</button>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
renderDisabled(): TemplateResult {
|
||||
return html` <div class="pf-c-card__body">
|
||||
<p>
|
||||
${t`Status: Disabled`}
|
||||
<i class="pf-icon pf-icon-error-circle-o"></i>
|
||||
</p>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
${this.configureUrl
|
||||
? html`<a
|
||||
href="${this.configureUrl}?next=/%23%2Fuser"
|
||||
class="pf-c-button pf-m-primary"
|
||||
>${t`Enable Duo authenticator`}
|
||||
</a>`
|
||||
: html``}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">${t`Duo`}</div>
|
||||
${until(
|
||||
new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsDuoList({}).then((devices) => {
|
||||
return devices.results.length > 0
|
||||
? this.renderEnabled()
|
||||
: this.renderDisabled();
|
||||
}),
|
||||
)}
|
||||
</div>`;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
import { AuthenticatorsApi } from "@goauthentik/api";
|
||||
import { t } from "@lingui/macro";
|
||||
import { CSSResult, customElement, html, TemplateResult } from "lit-element";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { DEFAULT_CONFIG } from "../../../api/Config";
|
||||
import { STATIC_TOKEN_STYLE } from "../../../flows/stages/authenticator_static/AuthenticatorStaticStage";
|
||||
import { BaseUserSettings } from "./BaseUserSettings";
|
||||
import { EVENT_REFRESH } from "../../../constants";
|
||||
|
||||
@customElement("ak-user-settings-authenticator-static")
|
||||
export class UserSettingsAuthenticatorStatic extends BaseUserSettings {
|
||||
static get styles(): CSSResult[] {
|
||||
return super.styles.concat(STATIC_TOKEN_STYLE);
|
||||
}
|
||||
|
||||
renderEnabled(): TemplateResult {
|
||||
return html`<div class="pf-c-card__body">
|
||||
<p>
|
||||
${t`Status: Enabled`}
|
||||
<i class="pf-icon pf-icon-ok"></i>
|
||||
</p>
|
||||
<ul class="ak-otp-tokens">
|
||||
${until(
|
||||
new AuthenticatorsApi(DEFAULT_CONFIG)
|
||||
.authenticatorsStaticList({})
|
||||
.then((devices) => {
|
||||
if (devices.results.length < 1) {
|
||||
return;
|
||||
}
|
||||
return devices.results[0].tokenSet?.map((token) => {
|
||||
return html`<li>${token.token}</li>`;
|
||||
});
|
||||
}),
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
<button
|
||||
class="pf-c-button pf-m-danger"
|
||||
@click=${() => {
|
||||
return new AuthenticatorsApi(DEFAULT_CONFIG)
|
||||
.authenticatorsStaticList({})
|
||||
.then((devices) => {
|
||||
if (devices.results.length < 1) {
|
||||
return;
|
||||
}
|
||||
// TODO: Handle multiple devices, currently we assume only one TOTP Device
|
||||
return new AuthenticatorsApi(DEFAULT_CONFIG)
|
||||
.authenticatorsStaticDestroy({
|
||||
id: devices.results[0].pk || 0,
|
||||
})
|
||||
.then(() => {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(EVENT_REFRESH, {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
}}
|
||||
>
|
||||
${t`Disable Static Tokens`}
|
||||
</button>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
renderDisabled(): TemplateResult {
|
||||
return html` <div class="pf-c-card__body">
|
||||
<p>
|
||||
${t`Status: Disabled`}
|
||||
<i class="pf-icon pf-icon-error-circle-o"></i>
|
||||
</p>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
${this.configureUrl
|
||||
? html`<a
|
||||
href="${this.configureUrl}?next=/%23%2Fuser"
|
||||
class="pf-c-button pf-m-primary"
|
||||
>${t`Enable Static Tokens`}
|
||||
</a>`
|
||||
: html``}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">${t`Static tokens`}</div>
|
||||
${until(
|
||||
new AuthenticatorsApi(DEFAULT_CONFIG)
|
||||
.authenticatorsStaticList({})
|
||||
.then((devices) => {
|
||||
return devices.results.length > 0
|
||||
? this.renderEnabled()
|
||||
: this.renderDisabled();
|
||||
}),
|
||||
)}
|
||||
</div>`;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
import { AuthenticatorsApi } from "@goauthentik/api";
|
||||
import { t } from "@lingui/macro";
|
||||
import { customElement, html, TemplateResult } from "lit-element";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { DEFAULT_CONFIG } from "../../../api/Config";
|
||||
import { BaseUserSettings } from "./BaseUserSettings";
|
||||
import { EVENT_REFRESH } from "../../../constants";
|
||||
|
||||
@customElement("ak-user-settings-authenticator-totp")
|
||||
export class UserSettingsAuthenticatorTOTP extends BaseUserSettings {
|
||||
renderEnabled(): TemplateResult {
|
||||
return html`<div class="pf-c-card__body">
|
||||
<p>
|
||||
${t`Status: Enabled`}
|
||||
<i class="pf-icon pf-icon-ok"></i>
|
||||
</p>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
<button
|
||||
class="pf-c-button pf-m-danger"
|
||||
@click=${() => {
|
||||
return new AuthenticatorsApi(DEFAULT_CONFIG)
|
||||
.authenticatorsTotpList({})
|
||||
.then((devices) => {
|
||||
if (devices.results.length < 1) {
|
||||
return;
|
||||
}
|
||||
// TODO: Handle multiple devices, currently we assume only one TOTP Device
|
||||
return new AuthenticatorsApi(DEFAULT_CONFIG)
|
||||
.authenticatorsTotpDestroy({
|
||||
id: devices.results[0].pk || 0,
|
||||
})
|
||||
.then(() => {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(EVENT_REFRESH, {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
}}
|
||||
>
|
||||
${t`Disable Time-based OTP`}
|
||||
</button>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
renderDisabled(): TemplateResult {
|
||||
return html` <div class="pf-c-card__body">
|
||||
<p>
|
||||
${t`Status: Disabled`}
|
||||
<i class="pf-icon pf-icon-error-circle-o"></i>
|
||||
</p>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
${this.configureUrl
|
||||
? html`<a
|
||||
href="${this.configureUrl}?next=/%23%2Fuser"
|
||||
class="pf-c-button pf-m-primary"
|
||||
>${t`Enable TOTP`}
|
||||
</a>`
|
||||
: html``}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">${t`Time-based One-Time Passwords`}</div>
|
||||
${until(
|
||||
new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsTotpList({}).then((devices) => {
|
||||
return devices.results.length > 0
|
||||
? this.renderEnabled()
|
||||
: this.renderDisabled();
|
||||
}),
|
||||
)}
|
||||
</div>`;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
import { CSSResult, customElement, html, TemplateResult } from "lit-element";
|
||||
import { t } from "@lingui/macro";
|
||||
import { AuthenticatorsApi, WebAuthnDevice } from "@goauthentik/api";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { DEFAULT_CONFIG } from "../../../api/Config";
|
||||
import { BaseUserSettings } from "./BaseUserSettings";
|
||||
import PFDataList from "@patternfly/patternfly/components/DataList/data-list.css";
|
||||
import "../../../elements/buttons/ModalButton";
|
||||
import "../../../elements/buttons/SpinnerButton";
|
||||
import "../../../elements/forms/DeleteForm";
|
||||
import "../../../elements/forms/Form";
|
||||
import "../../../elements/forms/ModalForm";
|
||||
import "../../../elements/forms/HorizontalFormElement";
|
||||
import { ifDefined } from "lit-html/directives/if-defined";
|
||||
import { EVENT_REFRESH } from "../../../constants";
|
||||
|
||||
@customElement("ak-user-settings-authenticator-webauthn")
|
||||
export class UserSettingsAuthenticatorWebAuthn extends BaseUserSettings {
|
||||
static get styles(): CSSResult[] {
|
||||
return super.styles.concat(PFDataList);
|
||||
}
|
||||
|
||||
renderDelete(device: WebAuthnDevice): TemplateResult {
|
||||
return html`<ak-forms-delete
|
||||
.obj=${device}
|
||||
objectLabel=${t`Authenticator`}
|
||||
.delete=${() => {
|
||||
return new AuthenticatorsApi(DEFAULT_CONFIG)
|
||||
.authenticatorsWebauthnDestroy({
|
||||
id: device.pk || 0,
|
||||
})
|
||||
.then(() => {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(EVENT_REFRESH, {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<button slot="trigger" class="pf-c-button pf-m-danger">${t`Delete`}</button>
|
||||
</ak-forms-delete>`;
|
||||
}
|
||||
|
||||
renderUpdate(device: WebAuthnDevice): TemplateResult {
|
||||
return html`<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update`} </span>
|
||||
<ak-form
|
||||
slot="form"
|
||||
successMessage=${t`Successfully updated device.`}
|
||||
.send=${(data: unknown) => {
|
||||
return new AuthenticatorsApi(DEFAULT_CONFIG)
|
||||
.authenticatorsWebauthnUpdate({
|
||||
id: device.pk || 0,
|
||||
webAuthnDeviceRequest: data as WebAuthnDevice,
|
||||
})
|
||||
.then(() => {
|
||||
this.requestUpdate();
|
||||
});
|
||||
}}
|
||||
>
|
||||
<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Device name`}
|
||||
?required=${true}
|
||||
name="name"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(device.name)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
</form>
|
||||
</ak-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">${t`Update`}</button>
|
||||
</ak-forms-modal>`;
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">${t`WebAuthn Devices`}</div>
|
||||
<div class="pf-c-card__body">
|
||||
<ul class="pf-c-data-list" role="list">
|
||||
${until(
|
||||
new AuthenticatorsApi(DEFAULT_CONFIG)
|
||||
.authenticatorsWebauthnList({})
|
||||
.then((devices) => {
|
||||
return devices.results.map((device) => {
|
||||
return html`<li class="pf-c-data-list__item">
|
||||
<div class="pf-c-data-list__item-row">
|
||||
<div class="pf-c-data-list__item-content">
|
||||
<div class="pf-c-data-list__cell">
|
||||
${device.name || "-"}
|
||||
</div>
|
||||
<div class="pf-c-data-list__cell">
|
||||
${t`Created ${device.createdOn?.toLocaleString()}`}
|
||||
</div>
|
||||
<div class="pf-c-data-list__cell">
|
||||
${this.renderUpdate(device)}
|
||||
${this.renderDelete(device)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>`;
|
||||
});
|
||||
}),
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
${this.configureUrl
|
||||
? html`<a
|
||||
href="${this.configureUrl}?next=/%23%2Fuser"
|
||||
class="pf-c-button pf-m-primary"
|
||||
>${t`Configure WebAuthn`}
|
||||
</a>`
|
||||
: html``}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
}
|
20
web/src/user/user-settings/settings/UserSettingsPassword.ts
Normal file
20
web/src/user/user-settings/settings/UserSettingsPassword.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { customElement, html, TemplateResult } from "lit-element";
|
||||
import { t } from "@lingui/macro";
|
||||
import { BaseUserSettings } from "./BaseUserSettings";
|
||||
import { ifDefined } from "lit-html/directives/if-defined";
|
||||
|
||||
@customElement("ak-user-settings-password")
|
||||
export class UserSettingsPassword extends BaseUserSettings {
|
||||
render(): TemplateResult {
|
||||
// For this stage we don't need to check for a configureFlow,
|
||||
// as the stage won't return any UI Elements if no configureFlow is set.
|
||||
return html`<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">${t`Change your password`}</div>
|
||||
<div class="pf-c-card__body">
|
||||
<a href="${ifDefined(this.configureUrl)}" class="pf-c-button pf-m-primary">
|
||||
${t`Change password`}
|
||||
</a>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
}
|
62
web/src/user/user-settings/tokens/UserTokenForm.ts
Normal file
62
web/src/user/user-settings/tokens/UserTokenForm.ts
Normal file
|
@ -0,0 +1,62 @@
|
|||
import { CoreApi, IntentEnum, Token } from "@goauthentik/api";
|
||||
import { t } from "@lingui/macro";
|
||||
import { customElement, property } from "lit-element";
|
||||
import { html, TemplateResult } from "lit-html";
|
||||
import { DEFAULT_CONFIG } from "../../../api/Config";
|
||||
import { ifDefined } from "lit-html/directives/if-defined";
|
||||
import "../../../elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
|
||||
@customElement("ak-user-token-form")
|
||||
export class UserTokenForm extends ModelForm<Token, string> {
|
||||
@property()
|
||||
intent: IntentEnum = IntentEnum.Api;
|
||||
|
||||
loadInstance(pk: string): Promise<Token> {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreTokensRetrieve({
|
||||
identifier: pk,
|
||||
});
|
||||
}
|
||||
|
||||
getSuccessMessage(): string {
|
||||
if (this.instance) {
|
||||
return t`Successfully updated token.`;
|
||||
} else {
|
||||
return t`Successfully created token.`;
|
||||
}
|
||||
}
|
||||
|
||||
send = (data: Token): Promise<Token> => {
|
||||
if (this.instance) {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreTokensUpdate({
|
||||
identifier: this.instance.identifier,
|
||||
tokenRequest: data,
|
||||
});
|
||||
} else {
|
||||
data.intent = this.intent;
|
||||
return new CoreApi(DEFAULT_CONFIG).coreTokensCreate({
|
||||
tokenRequest: data,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal label=${t`Identifier`} ?required=${true} name="identifier">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.identifier)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${t`Description`} name="description">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.description)}"
|
||||
class="pf-c-form-control"
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
</form>`;
|
||||
}
|
||||
}
|
156
web/src/user/user-settings/tokens/UserTokenList.ts
Normal file
156
web/src/user/user-settings/tokens/UserTokenList.ts
Normal file
|
@ -0,0 +1,156 @@
|
|||
import { t } from "@lingui/macro";
|
||||
import { CSSResult, customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { AKResponse } from "../../../api/Client";
|
||||
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
|
||||
|
||||
import "../../../elements/forms/DeleteBulkForm";
|
||||
import "../../../elements/forms/ModalForm";
|
||||
import "../../../elements/buttons/ModalButton";
|
||||
import "../../../elements/buttons/Dropdown";
|
||||
import "../../../elements/buttons/TokenCopyButton";
|
||||
import { Table, TableColumn } from "../../../elements/table/Table";
|
||||
import { PAGE_SIZE } from "../../../constants";
|
||||
import { CoreApi, IntentEnum, Token } from "@goauthentik/api";
|
||||
import { DEFAULT_CONFIG } from "../../../api/Config";
|
||||
import "./UserTokenForm";
|
||||
import { IntentToLabel } from "../../../pages/tokens/TokenListPage";
|
||||
|
||||
@customElement("ak-user-token-list")
|
||||
export class UserTokenList extends Table<Token> {
|
||||
searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
expandable = true;
|
||||
checkbox = true;
|
||||
|
||||
@property()
|
||||
order = "expires";
|
||||
|
||||
apiEndpoint(page: number): Promise<AKResponse<Token>> {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreTokensList({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
pageSize: PAGE_SIZE,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
return [new TableColumn(t`Identifier`, "identifier"), new TableColumn("")];
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return super.styles.concat(PFDescriptionList);
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
return html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Create`} </span>
|
||||
<span slot="header"> ${t`Create Token`} </span>
|
||||
<ak-user-token-form intent=${IntentEnum.Api} slot="form"> </ak-user-token-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Create Token`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Create`} </span>
|
||||
<span slot="header"> ${t`Create App password`} </span>
|
||||
<ak-user-token-form intent=${IntentEnum.AppPassword} slot="form">
|
||||
</ak-user-token-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${t`Create App password`}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
${super.renderToolbar()}
|
||||
`;
|
||||
}
|
||||
|
||||
renderExpanded(item: Token): TemplateResult {
|
||||
return html` <td role="cell" colspan="3">
|
||||
<div class="pf-c-table__expandable-row-content">
|
||||
<dl class="pf-c-description-list pf-m-horizontal">
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`User`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${item.user?.username}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Expiring`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${item.expiring ? t`Yes` : t`No`}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Expiring`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${item.expiring ? item.expires?.toLocaleString() : "-"}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Intent`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${IntentToLabel(item.intent || IntentEnum.Api)}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</td>
|
||||
<td></td>`;
|
||||
}
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
objectLabel=${t`Token(s)`}
|
||||
.objects=${this.selectedElements}
|
||||
.delete=${(item: Token) => {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreTokensDestroy({
|
||||
identifier: item.identifier,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<button ?disabled=${disabled} slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${t`Delete`}
|
||||
</button>
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
row(item: Token): TemplateResult[] {
|
||||
return [
|
||||
html`${item.identifier}`,
|
||||
html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${t`Update`} </span>
|
||||
<span slot="header"> ${t`Update Token`} </span>
|
||||
<ak-user-token-form slot="form" .instancePk=${item.identifier}>
|
||||
</ak-user-token-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-plain">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-token-copy-button identifier="${item.identifier}">
|
||||
${t`Copy Key`}
|
||||
</ak-token-copy-button>
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
Reference in a new issue