diff --git a/.github/workflows/ci-main.yml b/.github/workflows/ci-main.yml index 64c4a6163..574729b4e 100644 --- a/.github/workflows/ci-main.yml +++ b/.github/workflows/ci-main.yml @@ -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 diff --git a/authentik/core/templates/base/skeleton.html b/authentik/core/templates/base/skeleton.html index 66ca0c04f..77b4cf788 100644 --- a/authentik/core/templates/base/skeleton.html +++ b/authentik/core/templates/base/skeleton.html @@ -17,7 +17,6 @@ {% endblock %} - {% block head %} {% endblock %} diff --git a/authentik/core/templates/if/flow.html b/authentik/core/templates/if/flow.html index 09c4c5246..3b6260b39 100644 --- a/authentik/core/templates/if/flow.html +++ b/authentik/core/templates/if/flow.html @@ -4,6 +4,7 @@ {% load i18n %} {% block head_before %} +{{ block.super }} {% if flow.compatibility_mode %} {% endif %} diff --git a/authentik/core/templates/if/user.html b/authentik/core/templates/if/user.html new file mode 100644 index 000000000..2fbd2a3e9 --- /dev/null +++ b/authentik/core/templates/if/user.html @@ -0,0 +1,28 @@ +{% extends "base/skeleton.html" %} + +{% load static %} +{% load i18n %} + +{% block head %} + +{% endblock %} + +{% block body %} + + +
+
+
+ + + + + +

+ {% trans "Loading..." %} +

+
+
+
+
+{% endblock %} diff --git a/authentik/core/urls.py b/authentik/core/urls.py index 772fc0126..8c10b43c3 100644 --- a/authentik/core/urls.py +++ b/authentik/core/urls.py @@ -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//", ensure_csrf_cookie(FlowInterfaceView.as_view()), diff --git a/authentik/tenants/utils.py b/authentik/tenants/utils.py index b13861215..c96c3cad2 100644 --- a/authentik/tenants/utils.py +++ b/authentik/tenants/utils.py @@ -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"), } diff --git a/tests/e2e/test_flows_authenticators.py b/tests/e2e/test_flows_authenticators.py index fc6119233..8a53714da 100644 --- a/tests/e2e/test_flows_authenticators.py +++ b/tests/e2e/test_flows_authenticators.py @@ -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( diff --git a/tests/e2e/test_flows_enroll.py b/tests/e2e/test_flows_enroll.py index 31f27dac7..772ee0dfb 100644 --- a/tests/e2e/test_flows_enroll.py +++ b/tests/e2e/test_flows_enroll.py @@ -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")) diff --git a/tests/e2e/test_flows_login.py b/tests/e2e/test_flows_login.py index 5659e40b2..5aef5c05e 100644 --- a/tests/e2e/test_flows_login.py +++ b/tests/e2e/test_flows_login.py @@ -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()) diff --git a/tests/e2e/test_flows_stage_setup.py b/tests/e2e/test_flows_stage_setup.py index 109266879..ada738a81 100644 --- a/tests/e2e/test_flows_stage_setup.py +++ b/tests/e2e/test_flows_stage_setup.py @@ -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)) diff --git a/tests/e2e/test_source_oauth.py b/tests/e2e/test_source_oauth.py index 8d665fcb7..8045aeb03 100644 --- a/tests/e2e/test_source_oauth.py +++ b/tests/e2e/test_source_oauth.py @@ -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")) diff --git a/tests/e2e/test_source_saml.py b/tests/e2e/test_source_saml.py index c561ca090..d717b3309 100644 --- a/tests/e2e/test_source_saml.py +++ b/tests/e2e/test_source_saml.py @@ -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") diff --git a/tests/e2e/utils.py b/tests/e2e/utils.py index fa99ac5c8..45418ba73 100644 --- a/tests/e2e/utils.py +++ b/tests/e2e/utils.py @@ -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""" diff --git a/web/poly.ts b/web/poly.ts index 510032fcc..9fdc4a56d 100644 --- a/web/poly.ts +++ b/web/poly.ts @@ -1,2 +1,4 @@ +// @ts-ignore +window["polymerSkipLoadingFontRoboto"] = true; import "construct-style-sheets-polyfill"; import "@webcomponents/webcomponentsjs"; diff --git a/web/rollup.config.js b/web/rollup.config.js index a06e836c9..f7f5d02e3 100644 --- a/web/rollup.config.js +++ b/web/rollup.config.js @@ -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, + }, + }, ]; diff --git a/web/src/authentik.css b/web/src/authentik.css index 118c82c5c..043f574f4 100644 --- a/web/src/authentik.css +++ b/web/src/authentik.css @@ -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); diff --git a/web/src/elements/router/RouterOutlet.ts b/web/src/elements/router/RouterOutlet.ts index 981d0d28a..f66d6c7fb 100644 --- a/web/src/elements/router/RouterOutlet.ts +++ b/web/src/elements/router/RouterOutlet.ts @@ -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); diff --git a/web/src/interfaces/AdminInterface.ts b/web/src/interfaces/AdminInterface.ts index 750c73796..4c2cb6c06 100644 --- a/web/src/interfaces/AdminInterface.ts +++ b/web/src/interfaces/AdminInterface.ts @@ -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} > @@ -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``; }), )} - - ${t`Library`} + + ${t`Go to user interface`} - - ${t`Monitor`} - - ${t`Overview`} - - - ${t`System Tasks`} - + + ${t`Overview`} - + + ${t`System Tasks`} + + ${t`Resources`} ${t`Tenants`} - + ${t`Outposts`} ${t`Outposts`} @@ -212,7 +213,7 @@ export class AdminInterface extends LitElement { ${t`Integrations`} - + ${t`Events`} ${t`Notification Transports`} - + ${t`Customisation`} ${t`Policies`} @@ -242,7 +243,7 @@ export class AdminInterface extends LitElement { ${t`Property Mappings`} - + ${t`Flows`} ${t`Invitations`} - + ${t`Identity & Cryptography`} ; + + 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`
+ + ${this.renderSidebarItems()} + +
+
+
+
+
+
+ + +
+
+
+ + +
+
+
+
`; + } + + renderSidebarItems(): TemplateResult { + return html` + ${until( + this.version.then((version) => { + if (version.versionCurrent !== VERSION) { + return html` + ${t`A newer version of the frontend is available.`} + `; + } + return html``; + }), + )} + ${until( + me().then((u) => { + if (u.original) { + return html` + ${t`You're currently impersonating ${u.user.username}. Click to stop.`} + `; + } + return html``; + }), + )} + + ${t`Go to admin interface`} + + + ${t`Library`} + + `; + } +} diff --git a/web/src/interfaces/admin/index.html b/web/src/interfaces/admin/index.html deleted file mode 100644 index 1c83a2714..000000000 --- a/web/src/interfaces/admin/index.html +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - authentik - - - - -
-
-
- - - - - -

Loading...

-
-
-
-
- - diff --git a/web/src/interfaces/flow/index.html b/web/src/interfaces/flow/index.html deleted file mode 100644 index d70880073..000000000 --- a/web/src/interfaces/flow/index.html +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - authentik - - - - -
-
-
- - - - - -

Loading...

-
-
-
-
- - diff --git a/web/src/locales/en.po b/web/src/locales/en.po index 8c1dd3189..a08de843f 100644 --- a/web/src/locales/en.po +++ b/web/src/locales/en.po @@ -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." diff --git a/web/src/locales/pseudo-LOCALE.po b/web/src/locales/pseudo-LOCALE.po index c147c8ad2..9d20942df 100644 --- a/web/src/locales/pseudo-LOCALE.po +++ b/web/src/locales/pseudo-LOCALE.po @@ -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 "" diff --git a/web/src/routes.ts b/web/src/routesAdmin.ts similarity index 96% rename from web/src/routes.ts rename to web/src/routesAdmin.ts index e520049d3..58462a1e4 100644 --- a/web/src/routes.ts +++ b/web/src/routesAdmin.ts @@ -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``), new Route( new RegExp("^/administration/overview$"), diff --git a/web/src/routesUser.ts b/web/src/routesUser.ts new file mode 100644 index 000000000..fc69c25e6 --- /dev/null +++ b/web/src/routesUser.ts @@ -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``), + new Route(new RegExp("^/user$"), html``), +]; diff --git a/web/src/user/LibraryPage.ts b/web/src/user/LibraryPage.ts new file mode 100644 index 000000000..a87b8ddc6 --- /dev/null +++ b/web/src/user/LibraryPage.ts @@ -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``; + } + return html`
+
+ ${this.application.metaIcon + ? html`Application Icon` + : html``} + ${until( + me().then((u) => { + if (!u.user.isSuperuser) return html``; + return html` + + + + `; + }), + )} +
+
+

+ ${this.application.name} +

+
+ ${this.application.metaPublisher} +
+
+
${truncate(this.application.metaDescription, 35)}
+
`; + } +} + +@customElement("ak-library") +export class LibraryPage extends LitElement { + @property({ attribute: false }) + apps?: AKResponse; + + 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`
+
+ +

${t`No Applications available.`}

+
+ ${t`Either no applications are defined, or you don't have access to any.`} +
+
+
`; + } + + renderApps(): TemplateResult { + return html``; + } + + render(): TemplateResult { + return html`
+ + +
+ ${loading( + this.apps, + html`${(this.apps?.results.length || 0) > 0 + ? this.renderApps() + : this.renderEmptyState()}`, + )} +
+
`; + } +} diff --git a/web/src/user/user-settings/UserSelfForm.ts b/web/src/user/user-settings/UserSelfForm.ts new file mode 100644 index 000000000..ffacc10f6 --- /dev/null +++ b/web/src/user/user-settings/UserSelfForm.ts @@ -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 { + viewportCheck = false; + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + loadInstance(pk: number): Promise { + return new CoreApi(DEFAULT_CONFIG).coreUsersMeRetrieve().then((su) => { + return su.user; + }); + } + + getSuccessMessage(): string { + return t`Successfully updated details.`; + } + + send = (data: UserSelf): Promise => { + return new CoreApi(DEFAULT_CONFIG) + .coreUsersUpdateSelfUpdate({ + userSelfRequest: data, + }) + .then((su) => { + return su.user; + }); + }; + + renderForm(): TemplateResult { + if (!this.instance) { + return html` `; + } + return html`
+ + +

+ ${t`Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.`} +

+
+ + +

${t`User's display name.`}

+
+ + + + +
+
+
+ + ${until( + tenant().then((tenant) => { + if (tenant.flowUnenrollment) { + return html` + ${t`Delete account`} + `; + } + return html``; + }), + )} +
+
+
+
`; + } +} diff --git a/web/src/user/user-settings/UserSettingsPage.ts b/web/src/user/user-settings/UserSettingsPage.ts new file mode 100644 index 000000000..e7b627857 --- /dev/null +++ b/web/src/user/user-settings/UserSettingsPage.ts @@ -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; + + @property({ attribute: false }) + sourceSettings?: Promise; + + 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` + `; + case "ak-user-settings-password": + return html` + `; + case "ak-user-settings-authenticator-totp": + return html` + `; + case "ak-user-settings-authenticator-static": + return html` + `; + case "ak-user-settings-authenticator-duo": + return html` + `; + default: + return html`

${t`Error: unsupported stage settings: ${stage.component}`}

`; + } + } + + renderSourceSettings(source: UserSetting): TemplateResult { + switch (source.component) { + case "ak-user-settings-source-oauth": + return html` + `; + case "ak-user-settings-source-plex": + return html` + `; + default: + return html`

${t`Error: unsupported source settings: ${source.component}`}

`; + } + } + + render(): TemplateResult { + return html`
+
+ + + +
+
+
${t`Update details`}
+
+ +
+
+
+
+ +
+ ${until( + this.userSettings?.then((stages) => { + return stages.map((stage) => { + return html`
+ ${this.renderStageSettings(stage)} +
`; + }); + }), + )} + ${until( + this.sourceSettings?.then((source) => { + return source.map((stage) => { + return html`
+ ${this.renderSourceSettings(stage)} +
`; + }); + }), + )} +
+
+
`; + } +} diff --git a/web/src/user/user-settings/settings/BaseUserSettings.ts b/web/src/user/user-settings/settings/BaseUserSettings.ts new file mode 100644 index 000000000..7b7863d9d --- /dev/null +++ b/web/src/user/user-settings/settings/BaseUserSettings.ts @@ -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]; + } +} diff --git a/web/src/user/user-settings/settings/SourceSettingsOAuth.ts b/web/src/user/user-settings/settings/SourceSettingsOAuth.ts new file mode 100644 index 000000000..42ada8723 --- /dev/null +++ b/web/src/user/user-settings/settings/SourceSettingsOAuth.ts @@ -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`
+
${t`Source ${this.title}`}
+
${this.renderInner()}
+
`; + } + + renderInner(): TemplateResult { + return html`${until( + new SourcesApi(DEFAULT_CONFIG) + .sourcesUserConnectionsOauthList({ + sourceSlug: this.objectId, + }) + .then((connection) => { + if (connection.results.length > 0) { + return html`

${t`Connected.`}

+ `; + } + return html`

${t`Not connected.`}

+ + ${t`Connect`} + `; + }), + )}`; + } +} diff --git a/web/src/user/user-settings/settings/SourceSettingsPlex.ts b/web/src/user/user-settings/settings/SourceSettingsPlex.ts new file mode 100644 index 000000000..cd015b6a2 --- /dev/null +++ b/web/src/user/user-settings/settings/SourceSettingsPlex.ts @@ -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`
+
${t`Source ${this.title}`}
+
${this.renderInner()}
+
`; + } + + renderInner(): TemplateResult { + return html`${until( + new SourcesApi(DEFAULT_CONFIG) + .sourcesUserConnectionsPlexList({ + sourceSlug: this.objectId, + }) + .then((connection) => { + if (connection.results.length > 0) { + return html`

${t`Connected.`}

+ `; + } + return html`

${t`Not connected.`}

`; + }), + )}`; + } +} diff --git a/web/src/user/user-settings/settings/UserSettingsAuthenticatorDuo.ts b/web/src/user/user-settings/settings/UserSettingsAuthenticatorDuo.ts new file mode 100644 index 000000000..86f34c66b --- /dev/null +++ b/web/src/user/user-settings/settings/UserSettingsAuthenticatorDuo.ts @@ -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`
+

+ ${t`Status: Enabled`} + +

+
+ `; + } + + renderDisabled(): TemplateResult { + return html`
+

+ ${t`Status: Disabled`} + +

+
+ `; + } + + render(): TemplateResult { + return html`
+
${t`Duo`}
+ ${until( + new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsDuoList({}).then((devices) => { + return devices.results.length > 0 + ? this.renderEnabled() + : this.renderDisabled(); + }), + )} +
`; + } +} diff --git a/web/src/user/user-settings/settings/UserSettingsAuthenticatorStatic.ts b/web/src/user/user-settings/settings/UserSettingsAuthenticatorStatic.ts new file mode 100644 index 000000000..077980a92 --- /dev/null +++ b/web/src/user/user-settings/settings/UserSettingsAuthenticatorStatic.ts @@ -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`
+

+ ${t`Status: Enabled`} + +

+
    + ${until( + new AuthenticatorsApi(DEFAULT_CONFIG) + .authenticatorsStaticList({}) + .then((devices) => { + if (devices.results.length < 1) { + return; + } + return devices.results[0].tokenSet?.map((token) => { + return html`
  • ${token.token}
  • `; + }); + }), + )} +
+
+ `; + } + + renderDisabled(): TemplateResult { + return html`
+

+ ${t`Status: Disabled`} + +

+
+ `; + } + + render(): TemplateResult { + return html`
+
${t`Static tokens`}
+ ${until( + new AuthenticatorsApi(DEFAULT_CONFIG) + .authenticatorsStaticList({}) + .then((devices) => { + return devices.results.length > 0 + ? this.renderEnabled() + : this.renderDisabled(); + }), + )} +
`; + } +} diff --git a/web/src/user/user-settings/settings/UserSettingsAuthenticatorTOTP.ts b/web/src/user/user-settings/settings/UserSettingsAuthenticatorTOTP.ts new file mode 100644 index 000000000..827973df1 --- /dev/null +++ b/web/src/user/user-settings/settings/UserSettingsAuthenticatorTOTP.ts @@ -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`
+

+ ${t`Status: Enabled`} + +

+
+ `; + } + + renderDisabled(): TemplateResult { + return html`
+

+ ${t`Status: Disabled`} + +

+
+ `; + } + + render(): TemplateResult { + return html`
+
${t`Time-based One-Time Passwords`}
+ ${until( + new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsTotpList({}).then((devices) => { + return devices.results.length > 0 + ? this.renderEnabled() + : this.renderDisabled(); + }), + )} +
`; + } +} diff --git a/web/src/user/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts b/web/src/user/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts new file mode 100644 index 000000000..8436125fe --- /dev/null +++ b/web/src/user/user-settings/settings/UserSettingsAuthenticatorWebAuthn.ts @@ -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` { + return new AuthenticatorsApi(DEFAULT_CONFIG) + .authenticatorsWebauthnDestroy({ + id: device.pk || 0, + }) + .then(() => { + this.dispatchEvent( + new CustomEvent(EVENT_REFRESH, { + bubbles: true, + composed: true, + }), + ); + }); + }} + > + + `; + } + + renderUpdate(device: WebAuthnDevice): TemplateResult { + return html` + ${t`Update`} + ${t`Update`} + { + return new AuthenticatorsApi(DEFAULT_CONFIG) + .authenticatorsWebauthnUpdate({ + id: device.pk || 0, + webAuthnDeviceRequest: data as WebAuthnDevice, + }) + .then(() => { + this.requestUpdate(); + }); + }} + > +
+ + + +
+
+ +
`; + } + + render(): TemplateResult { + return html`
+
${t`WebAuthn Devices`}
+
+
    + ${until( + new AuthenticatorsApi(DEFAULT_CONFIG) + .authenticatorsWebauthnList({}) + .then((devices) => { + return devices.results.map((device) => { + return html`
  • +
    +
    +
    + ${device.name || "-"} +
    +
    + ${t`Created ${device.createdOn?.toLocaleString()}`} +
    +
    + ${this.renderUpdate(device)} + ${this.renderDelete(device)} +
    +
    +
    +
  • `; + }); + }), + )} +
+
+ +
`; + } +} diff --git a/web/src/user/user-settings/settings/UserSettingsPassword.ts b/web/src/user/user-settings/settings/UserSettingsPassword.ts new file mode 100644 index 000000000..f92f4eecc --- /dev/null +++ b/web/src/user/user-settings/settings/UserSettingsPassword.ts @@ -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`
+
${t`Change your password`}
+ +
`; + } +} diff --git a/web/src/user/user-settings/tokens/UserTokenForm.ts b/web/src/user/user-settings/tokens/UserTokenForm.ts new file mode 100644 index 000000000..3c4ffd4c2 --- /dev/null +++ b/web/src/user/user-settings/tokens/UserTokenForm.ts @@ -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 { + @property() + intent: IntentEnum = IntentEnum.Api; + + loadInstance(pk: string): Promise { + 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 => { + 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`
+ + + + + + +
`; + } +} diff --git a/web/src/user/user-settings/tokens/UserTokenList.ts b/web/src/user/user-settings/tokens/UserTokenList.ts new file mode 100644 index 000000000..26ae7e605 --- /dev/null +++ b/web/src/user/user-settings/tokens/UserTokenList.ts @@ -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 { + searchEnabled(): boolean { + return true; + } + + expandable = true; + checkbox = true; + + @property() + order = "expires"; + + apiEndpoint(page: number): Promise> { + 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` + + ${t`Create`} + ${t`Create Token`} + + + + + ${t`Create`} + ${t`Create App password`} + + + + + ${super.renderToolbar()} + `; + } + + renderExpanded(item: Token): TemplateResult { + return html` +
+
+
+
+ ${t`User`} +
+
+
+ ${item.user?.username} +
+
+
+
+
+ ${t`Expiring`} +
+
+
+ ${item.expiring ? t`Yes` : t`No`} +
+
+
+
+
+ ${t`Expiring`} +
+
+
+ ${item.expiring ? item.expires?.toLocaleString() : "-"} +
+
+
+
+
+ ${t`Intent`} +
+
+
+ ${IntentToLabel(item.intent || IntentEnum.Api)} +
+
+
+
+
+ + `; + } + + renderToolbarSelected(): TemplateResult { + const disabled = this.selectedElements.length < 1; + return html` { + return new CoreApi(DEFAULT_CONFIG).coreTokensDestroy({ + identifier: item.identifier, + }); + }} + > + + `; + } + + row(item: Token): TemplateResult[] { + return [ + html`${item.identifier}`, + html` + + ${t`Update`} + ${t`Update Token`} + + + + + + ${t`Copy Key`} + + `, + ]; + } +}