diff --git a/authentik/admin/templates/administration/user/disable.html b/authentik/admin/templates/administration/user/disable.html
deleted file mode 100644
index 3069d8809..000000000
--- a/authentik/admin/templates/administration/user/disable.html
+++ /dev/null
@@ -1,42 +0,0 @@
-{% extends "administration/base.html" %}
-
-{% load i18n %}
-{% load authentik_utils %}
-
-{% block content %}
-
-
- {% block above_form %}
-
- {% blocktrans with object_type=object|verbose_name %}
- Disable {{ object_type }}
- {% endblocktrans %}
-
- {% endblock %}
-
-
-
-{% endblock %}
diff --git a/authentik/admin/templates/generic/form.html b/authentik/admin/templates/generic/form.html
index 20b78908f..2ef3e09e9 100644
--- a/authentik/admin/templates/generic/form.html
+++ b/authentik/admin/templates/generic/form.html
@@ -30,7 +30,7 @@
{% block action %}{% endblock %}
- {% trans "Cancel" %}
+ {% trans "Cancel" %}
{% endblock %}
diff --git a/authentik/api/templates/api/swagger.html b/authentik/api/templates/api/swagger.html
index f1538480e..f79c37309 100644
--- a/authentik/api/templates/api/swagger.html
+++ b/authentik/api/templates/api/swagger.html
@@ -2,6 +2,10 @@
{% load static %}
+{% block title %}
+authentik API Browser
+{% endblock %}
+
{% block head %}
{% endblock %}
diff --git a/authentik/api/v2/urls.py b/authentik/api/v2/urls.py
index fc25c2158..4c45e5697 100644
--- a/authentik/api/v2/urls.py
+++ b/authentik/api/v2/urls.py
@@ -107,7 +107,6 @@ router.register("core/applications", ApplicationViewSet)
router.register("core/groups", GroupViewSet)
router.register("core/users", UserViewSet)
router.register("core/user_consent", UserConsentViewSet)
-router.register("core/source_user_connections_oauth", UserOAuthSourceConnectionViewSet)
router.register("core/tokens", TokenViewSet)
router.register("outposts/outposts", OutpostViewSet)
@@ -129,6 +128,7 @@ router.register("events/transports", NotificationTransportViewSet)
router.register("events/rules", NotificationRuleViewSet)
router.register("sources/all", SourceViewSet)
+router.register("sources/oauth_user_connections", UserOAuthSourceConnectionViewSet)
router.register("sources/ldap", LDAPSourceViewSet)
router.register("sources/saml", SAMLSourceViewSet)
router.register("sources/oauth", OAuthSourceViewSet)
diff --git a/authentik/core/api/sources.py b/authentik/core/api/sources.py
index 2ce9809f7..8aee40e97 100644
--- a/authentik/core/api/sources.py
+++ b/authentik/core/api/sources.py
@@ -94,7 +94,7 @@ class SourceViewSet(
if not policy_engine.passing:
continue
source_settings = source.ui_user_settings
- source_settings.initial_data["object_uid"] = str(source.pk)
+ source_settings.initial_data["object_uid"] = source.slug
if not source_settings.is_valid():
LOGGER.warning(source_settings.errors)
matching_sources.append(source_settings.validated_data)
diff --git a/authentik/core/templates/error/generic.html b/authentik/core/templates/error/generic.html
index ca1d53de5..5ef6538bd 100644
--- a/authentik/core/templates/error/generic.html
+++ b/authentik/core/templates/error/generic.html
@@ -25,9 +25,6 @@
{% trans message %}
{% endif %}
- {% if 'back' in request.GET %}
- {% trans 'Back' %}
- {% endif %}
{% trans 'Go to home' %}
diff --git a/authentik/core/templates/generic/delete.html b/authentik/core/templates/generic/delete.html
deleted file mode 100644
index 8f8a58c9c..000000000
--- a/authentik/core/templates/generic/delete.html
+++ /dev/null
@@ -1,37 +0,0 @@
-{% load i18n %}
-{% load authentik_utils %}
-
-
-
- {% block above_form %}
-
- {% blocktrans with object_type=object|verbose_name %}
- Delete {{ object_type }}
- {% endblocktrans %}
-
- {% endblock %}
-
-
-
-
diff --git a/authentik/core/templates/login/base.html b/authentik/core/templates/login/base.html
deleted file mode 100644
index f1145ad7b..000000000
--- a/authentik/core/templates/login/base.html
+++ /dev/null
@@ -1,26 +0,0 @@
-{% load static %}
-{% load i18n %}
-
-
-
-
-
- {% block card_title %}
- {% trans title %}
- {% endblock %}
-
-
-
- {% block card %}
-
- {% endblock %}
-
-
diff --git a/authentik/core/templates/partials/form.html b/authentik/core/templates/partials/form.html
deleted file mode 100644
index 4a3b81c4c..000000000
--- a/authentik/core/templates/partials/form.html
+++ /dev/null
@@ -1,73 +0,0 @@
-{% load authentik_utils %}
-{% load i18n %}
-
-{% csrf_token %}
-{% if form.non_field_errors %}
-
-{% endif %}
-{% for field in form %}
-{% if field.field.widget|fieldtype == 'HiddenInput' %}
- {{ field }}
-{% else %}
-
-{% endif %}
-{% endfor %}
diff --git a/authentik/flows/api/stages.py b/authentik/flows/api/stages.py
index 4837abd6d..8073e2564 100644
--- a/authentik/flows/api/stages.py
+++ b/authentik/flows/api/stages.py
@@ -87,7 +87,7 @@ class StageViewSet(
user_settings = stage.ui_user_settings
if not user_settings:
continue
- user_settings.initial_data["object_uid"] = stage.pk
+ user_settings.initial_data["object_uid"] = str(stage.pk)
if not user_settings.is_valid():
LOGGER.warning(user_settings.errors)
matching_stages.append(user_settings.initial_data)
diff --git a/authentik/lib/templatetags/authentik_utils.py b/authentik/lib/templatetags/authentik_utils.py
index 40fc8ef31..a23e59dbc 100644
--- a/authentik/lib/templatetags/authentik_utils.py
+++ b/authentik/lib/templatetags/authentik_utils.py
@@ -2,32 +2,12 @@
from django import template
from django.db.models import Model
-from django.template import Context
from structlog.stdlib import get_logger
-from authentik.lib.utils.urls import is_url_absolute
-
register = template.Library()
LOGGER = get_logger()
-@register.simple_tag(takes_context=True)
-def back(context: Context) -> str:
- """Return a link back (either from GET parameter or referer."""
- if "request" not in context:
- return ""
- request = context.get("request")
- url = ""
- if "HTTP_REFERER" in request.META:
- url = request.META.get("HTTP_REFERER")
- if "back" in request.GET:
- url = request.GET.get("back")
-
- if not is_url_absolute(url):
- return url
- return ""
-
-
@register.filter("fieldtype")
def fieldtype(field):
"""Return classname"""
diff --git a/authentik/policies/templates/policies/denied.html b/authentik/policies/templates/policies/denied.html
index 118869de7..eb942e7c6 100644
--- a/authentik/policies/templates/policies/denied.html
+++ b/authentik/policies/templates/policies/denied.html
@@ -15,7 +15,6 @@
{% block card %}
{% endblock %}
diff --git a/authentik/sources/oauth/api/source_connection.py b/authentik/sources/oauth/api/source_connection.py
index 4133b897a..bab90cf86 100644
--- a/authentik/sources/oauth/api/source_connection.py
+++ b/authentik/sources/oauth/api/source_connection.py
@@ -11,10 +11,10 @@ class UserOAuthSourceConnectionSerializer(SourceSerializer):
class Meta:
model = UserOAuthSourceConnection
fields = [
+ "pk",
"user",
"source",
"identifier",
- "access_token",
]
@@ -23,6 +23,7 @@ class UserOAuthSourceConnectionViewSet(ModelViewSet):
queryset = UserOAuthSourceConnection.objects.all()
serializer_class = UserOAuthSourceConnectionSerializer
+ filterset_fields = ["source"]
def get_queryset(self):
if not self.request:
diff --git a/authentik/sources/oauth/models.py b/authentik/sources/oauth/models.py
index 9067aae79..cb9024b84 100644
--- a/authentik/sources/oauth/models.py
+++ b/authentik/sources/oauth/models.py
@@ -9,8 +9,7 @@ from django.utils.translation import gettext_lazy as _
from rest_framework.serializers import Serializer
from authentik.core.models import Source, UserSourceConnection
-from authentik.core.types import UILoginButton
-from authentik.flows.challenge import Challenge, ChallengeTypes
+from authentik.core.types import UILoginButton, UserSettingSerializer
class OAuthSource(Source):
@@ -67,13 +66,11 @@ class OAuthSource(Source):
)
@property
- def ui_user_settings(self) -> Optional[Challenge]:
- view_name = "authentik_sources_oauth:oauth-client-user"
- return Challenge(
+ def ui_user_settings(self) -> Optional[UserSettingSerializer]:
+ return UserSettingSerializer(
data={
- "type": ChallengeTypes.shell.value,
- "title": self.name,
- "component": reverse(view_name, kwargs={"source_slug": self.slug}),
+ "title": f"OAuth {self.name}",
+ "component": "ak-user-settings-source-oauth",
}
)
diff --git a/authentik/sources/oauth/templates/oauth_client/user.html b/authentik/sources/oauth/templates/oauth_client/user.html
deleted file mode 100644
index ea30ace65..000000000
--- a/authentik/sources/oauth/templates/oauth_client/user.html
+++ /dev/null
@@ -1,24 +0,0 @@
-{% load i18n %}
-
-
-
- {% blocktrans with source_name=source.name %}
- Source {{ source_name }}
- {% endblocktrans %}
-
-
- {% if connections.exists %}
-
{% trans 'Connected.' %}
-
- {% trans 'Disconnect' %}
-
- {% else %}
-
Not connected.
-
- {% trans 'Connect' %}
-
- {% endif %}
-
-
diff --git a/authentik/sources/oauth/urls.py b/authentik/sources/oauth/urls.py
index 085546d6d..fabe02acf 100644
--- a/authentik/sources/oauth/urls.py
+++ b/authentik/sources/oauth/urls.py
@@ -4,7 +4,6 @@ from django.urls import path
from authentik.sources.oauth.types.manager import RequestKind
from authentik.sources.oauth.views.dispatcher import DispatcherView
-from authentik.sources.oauth.views.user import DisconnectView, UserSettingsView
urlpatterns = [
path(
@@ -17,14 +16,4 @@ urlpatterns = [
DispatcherView.as_view(kind=RequestKind.callback),
name="oauth-client-callback",
),
- path(
- "user//",
- UserSettingsView.as_view(),
- name="oauth-client-user",
- ),
- path(
- "user//disconnect/",
- DisconnectView.as_view(),
- name="oauth-client-disconnect",
- ),
]
diff --git a/authentik/sources/oauth/views/user.py b/authentik/sources/oauth/views/user.py
deleted file mode 100644
index c6d7dbe5b..000000000
--- a/authentik/sources/oauth/views/user.py
+++ /dev/null
@@ -1,70 +0,0 @@
-"""authentik oauth_client user views"""
-from typing import Optional
-
-from django.contrib import messages
-from django.contrib.auth.mixins import LoginRequiredMixin
-from django.http import HttpRequest, HttpResponse
-from django.shortcuts import get_object_or_404, redirect, render
-from django.urls import reverse
-from django.utils.translation import gettext as _
-from django.views.generic import TemplateView, View
-
-from authentik.sources.oauth.models import OAuthSource, UserOAuthSourceConnection
-
-
-class UserSettingsView(LoginRequiredMixin, TemplateView):
- """Show user current connection state"""
-
- template_name = "oauth_client/user.html"
-
- def get_context_data(self, **kwargs):
- source = get_object_or_404(OAuthSource, slug=self.kwargs.get("source_slug"))
- connections = UserOAuthSourceConnection.objects.filter(
- user=self.request.user, source=source
- )
- kwargs["source"] = source
- kwargs["connections"] = connections
- return super().get_context_data(**kwargs)
-
-
-class DisconnectView(LoginRequiredMixin, View):
- """Delete connection with source"""
-
- source: Optional[OAuthSource] = None
- aas: Optional[UserOAuthSourceConnection] = None
-
- def dispatch(self, request: HttpRequest, source_slug: str) -> HttpResponse:
- self.source = get_object_or_404(OAuthSource, slug=source_slug)
- self.aas = get_object_or_404(
- UserOAuthSourceConnection, source=self.source, user=request.user
- )
- return super().dispatch(request, source_slug)
-
- def post(self, request: HttpRequest, source_slug: str) -> HttpResponse:
- """Delete connection object"""
- if "confirmdelete" in request.POST:
- # User confirmed deletion
- self.aas.delete()
- messages.success(request, _("Connection successfully deleted"))
- return redirect(
- reverse(
- "authentik_sources_oauth:oauth-client-user",
- kwargs={"source_slug": self.source.slug},
- )
- )
- return self.get(request, source_slug)
-
- # pylint: disable=unused-argument
- def get(self, request: HttpRequest, source_slug: str) -> HttpResponse:
- """Show delete form"""
- return render(
- request,
- "generic/delete.html",
- {
- "object": self.source,
- "delete_url": reverse(
- "authentik_sources_oauth:oauth-client-disconnect",
- kwargs={"source_slug": self.source.slug},
- ),
- },
- )
diff --git a/authentik/stages/authenticator_static/signals.py b/authentik/stages/authenticator_static/signals.py
index f8aefba12..0b81c3663 100644
--- a/authentik/stages/authenticator_static/signals.py
+++ b/authentik/stages/authenticator_static/signals.py
@@ -9,6 +9,7 @@ from authentik.events.models import Event
@receiver(pre_delete, sender=StaticDevice)
# pylint: disable=unused-argument
def pre_delete_event(sender, instance: StaticDevice, **_):
+ """Create event before deleting Static Devices"""
# Create event with email notification
event = Event.new(
"static_authenticator_disable", message="User disabled Static OTP Tokens."
diff --git a/authentik/stages/authenticator_totp/signals.py b/authentik/stages/authenticator_totp/signals.py
index 971de1adc..530233a8a 100644
--- a/authentik/stages/authenticator_totp/signals.py
+++ b/authentik/stages/authenticator_totp/signals.py
@@ -9,6 +9,7 @@ from authentik.events.models import Event
@receiver(pre_delete, sender=TOTPDevice)
# pylint: disable=unused-argument
def pre_delete_event(sender, instance: TOTPDevice, **_):
+ """Create event before deleting TOTP Devices"""
# Create event with email notification
event = Event.new("totp_disable", message="User disabled Time-based OTP.")
event.set_user(instance.user)
diff --git a/swagger.yaml b/swagger.yaml
index b370c7640..7272898d7 100755
--- a/swagger.yaml
+++ b/swagger.yaml
@@ -1188,147 +1188,6 @@ paths:
required: true
type: string
format: uuid
- /core/source_user_connections_oauth/:
- get:
- operationId: core_source_user_connections_oauth_list
- description: Source Viewset
- parameters:
- - name: ordering
- in: query
- description: Which field to use when ordering the results.
- required: false
- type: string
- - name: search
- in: query
- description: A search term.
- required: false
- type: string
- - name: page
- in: query
- description: Page Index
- required: false
- type: integer
- - name: page_size
- in: query
- description: Page Size
- required: false
- type: integer
- responses:
- '200':
- description: ''
- schema:
- required:
- - results
- - pagination
- type: object
- properties:
- pagination:
- required:
- - next
- - previous
- - count
- - current
- - total_pages
- - start_index
- - end_index
- type: object
- properties:
- next:
- type: number
- previous:
- type: number
- count:
- type: number
- current:
- type: number
- total_pages:
- type: number
- start_index:
- type: number
- end_index:
- type: number
- results:
- type: array
- items:
- $ref: '#/definitions/UserOAuthSourceConnection'
- tags:
- - core
- post:
- operationId: core_source_user_connections_oauth_create
- description: Source Viewset
- parameters:
- - name: data
- in: body
- required: true
- schema:
- $ref: '#/definitions/UserOAuthSourceConnection'
- responses:
- '201':
- description: ''
- schema:
- $ref: '#/definitions/UserOAuthSourceConnection'
- tags:
- - core
- parameters: []
- /core/source_user_connections_oauth/{id}/:
- get:
- operationId: core_source_user_connections_oauth_read
- description: Source Viewset
- parameters: []
- responses:
- '200':
- description: ''
- schema:
- $ref: '#/definitions/UserOAuthSourceConnection'
- tags:
- - core
- put:
- operationId: core_source_user_connections_oauth_update
- description: Source Viewset
- parameters:
- - name: data
- in: body
- required: true
- schema:
- $ref: '#/definitions/UserOAuthSourceConnection'
- responses:
- '200':
- description: ''
- schema:
- $ref: '#/definitions/UserOAuthSourceConnection'
- tags:
- - core
- patch:
- operationId: core_source_user_connections_oauth_partial_update
- description: Source Viewset
- parameters:
- - name: data
- in: body
- required: true
- schema:
- $ref: '#/definitions/UserOAuthSourceConnection'
- responses:
- '200':
- description: ''
- schema:
- $ref: '#/definitions/UserOAuthSourceConnection'
- tags:
- - core
- delete:
- operationId: core_source_user_connections_oauth_delete
- description: Source Viewset
- parameters: []
- responses:
- '204':
- description: ''
- tags:
- - core
- parameters:
- - name: id
- in: path
- description: A unique integer value identifying this User OAuth Source Connection.
- required: true
- type: integer
/core/tokens/:
get:
operationId: core_tokens_list
@@ -7551,6 +7410,152 @@ paths:
type: string
format: slug
pattern: ^[-a-zA-Z0-9_]+$
+ /sources/oauth_user_connections/:
+ get:
+ operationId: sources_oauth_user_connections_list
+ description: Source Viewset
+ parameters:
+ - name: source
+ in: query
+ description: ''
+ required: false
+ type: string
+ - name: ordering
+ in: query
+ description: Which field to use when ordering the results.
+ required: false
+ type: string
+ - name: search
+ in: query
+ description: A search term.
+ required: false
+ type: string
+ - name: page
+ in: query
+ description: Page Index
+ required: false
+ type: integer
+ - name: page_size
+ in: query
+ description: Page Size
+ required: false
+ type: integer
+ responses:
+ '200':
+ description: ''
+ schema:
+ required:
+ - results
+ - pagination
+ type: object
+ properties:
+ pagination:
+ required:
+ - next
+ - previous
+ - count
+ - current
+ - total_pages
+ - start_index
+ - end_index
+ type: object
+ properties:
+ next:
+ type: number
+ previous:
+ type: number
+ count:
+ type: number
+ current:
+ type: number
+ total_pages:
+ type: number
+ start_index:
+ type: number
+ end_index:
+ type: number
+ results:
+ type: array
+ items:
+ $ref: '#/definitions/UserOAuthSourceConnection'
+ tags:
+ - sources
+ post:
+ operationId: sources_oauth_user_connections_create
+ description: Source Viewset
+ parameters:
+ - name: data
+ in: body
+ required: true
+ schema:
+ $ref: '#/definitions/UserOAuthSourceConnection'
+ responses:
+ '201':
+ description: ''
+ schema:
+ $ref: '#/definitions/UserOAuthSourceConnection'
+ tags:
+ - sources
+ parameters: []
+ /sources/oauth_user_connections/{id}/:
+ get:
+ operationId: sources_oauth_user_connections_read
+ description: Source Viewset
+ parameters: []
+ responses:
+ '200':
+ description: ''
+ schema:
+ $ref: '#/definitions/UserOAuthSourceConnection'
+ tags:
+ - sources
+ put:
+ operationId: sources_oauth_user_connections_update
+ description: Source Viewset
+ parameters:
+ - name: data
+ in: body
+ required: true
+ schema:
+ $ref: '#/definitions/UserOAuthSourceConnection'
+ responses:
+ '200':
+ description: ''
+ schema:
+ $ref: '#/definitions/UserOAuthSourceConnection'
+ tags:
+ - sources
+ patch:
+ operationId: sources_oauth_user_connections_partial_update
+ description: Source Viewset
+ parameters:
+ - name: data
+ in: body
+ required: true
+ schema:
+ $ref: '#/definitions/UserOAuthSourceConnection'
+ responses:
+ '200':
+ description: ''
+ schema:
+ $ref: '#/definitions/UserOAuthSourceConnection'
+ tags:
+ - sources
+ delete:
+ operationId: sources_oauth_user_connections_delete
+ description: Source Viewset
+ parameters: []
+ responses:
+ '204':
+ description: ''
+ tags:
+ - sources
+ parameters:
+ - name: id
+ in: path
+ description: A unique integer value identifying this User OAuth Source Connection.
+ required: true
+ type: integer
/sources/saml/:
get:
operationId: sources_saml_list
@@ -10963,29 +10968,6 @@ definitions:
attributes:
title: Attributes
type: object
- UserOAuthSourceConnection:
- description: OAuth Source Serializer
- required:
- - user
- - source
- - identifier
- type: object
- properties:
- user:
- title: User
- type: integer
- source:
- title: Source
- type: string
- identifier:
- title: Identifier
- type: string
- maxLength: 255
- minLength: 1
- access_token:
- title: Access token
- type: string
- x-nullable: true
User:
title: User
description: User Serializer
@@ -13742,6 +13724,29 @@ definitions:
title: Callback url
type: string
readOnly: true
+ UserOAuthSourceConnection:
+ description: OAuth Source Serializer
+ required:
+ - user
+ - source
+ - identifier
+ type: object
+ properties:
+ pk:
+ title: ID
+ type: integer
+ readOnly: true
+ user:
+ title: User
+ type: integer
+ source:
+ title: Source
+ type: string
+ identifier:
+ title: Identifier
+ type: string
+ maxLength: 255
+ minLength: 1
SAMLSource:
description: SAMLSource Serializer
required:
diff --git a/tests/e2e/test_flows_enroll.py b/tests/e2e/test_flows_enroll.py
index 6f5400293..abd4c976d 100644
--- a/tests/e2e/test_flows_enroll.py
+++ b/tests/e2e/test_flows_enroll.py
@@ -1,5 +1,6 @@
"""Test Enroll flow"""
from sys import platform
+from time import sleep
from typing import Any, Optional
from unittest.case import skipUnless
@@ -190,6 +191,7 @@ class TestFlowsEnroll(SeleniumTestCase):
self.driver.close()
self.driver.switch_to.window(self.driver.window_handles[0])
+ sleep(2)
# We're now logged in
wait = WebDriverWait(
self.get_shadow_root("ak-interface-admin"), self.wait_timeout
diff --git a/web/src/api/legacy.ts b/web/src/api/legacy.ts
index 7062ab82a..c8a520d71 100644
--- a/web/src/api/legacy.ts
+++ b/web/src/api/legacy.ts
@@ -94,6 +94,9 @@ export class AppURLManager {
static sourceSAML(slug: string, rest: string): string {
return `/source/saml/${slug}/${rest}`;
}
+ static sourceOAuth(slug: string, action: string): string {
+ return `/source/oauth/${action}/${slug}/`;
+ }
static providerSAML(rest: string): string {
return `/application/saml/${rest}`;
}
diff --git a/web/src/pages/users/UserSettingsPage.ts b/web/src/pages/users/UserSettingsPage.ts
index e38c98ab0..eefdae207 100644
--- a/web/src/pages/users/UserSettingsPage.ts
+++ b/web/src/pages/users/UserSettingsPage.ts
@@ -24,6 +24,7 @@ import "./settings/UserSettingsAuthenticatorTOTP";
import "./settings/UserSettingsAuthenticatorStatic";
import "./settings/UserSettingsAuthenticatorWebAuthnDevices";
import "./settings/UserSettingsPassword";
+import "./settings/SourceSettingsOAuth";
@customElement("ak-user-settings")
export class UserSettingsPage extends LitElement {
@@ -35,16 +36,16 @@ export class UserSettingsPage extends LitElement {
renderStageSettings(stage: UserSetting): TemplateResult {
switch (stage.component) {
case "ak-user-settings-authenticator-webauthn":
- return html`
+ return html`
`;
case "ak-user-settings-password":
- return html`
+ return html`
`;
case "ak-user-settings-authenticator-totp":
- return html`
+ return html`
`;
case "ak-user-settings-authenticator-static":
- return html`
+ return html`
`;
default:
return html`
@@ -57,6 +58,22 @@ export class UserSettingsPage extends LitElement {
}
}
+ renderSourceSettings(source: UserSetting): TemplateResult {
+ switch (source.component) {
+ case "ak-user-settings-source-oauth":
+ return html`
+ `;
+ default:
+ return html`
`;
+ }
+ }
+
render(): TemplateResult {
return html`
@@ -89,18 +106,11 @@ export class UserSettingsPage extends LitElement {
`;
});
}))}
- ${until(new SourcesApi(DEFAULT_CONFIG).sourcesAllUserSettings({}).then((sources) => {
- return sources.map((source) => {
- // TODO: Check for non-shell sources
- return html``;
+ ${until(new SourcesApi(DEFAULT_CONFIG).sourcesAllUserSettings({}).then((source) => {
+ return source.map((stage) => {
+ return html`
+ ${this.renderSourceSettings(stage)}
+ `;
});
}))}
diff --git a/web/src/pages/users/settings/SourceSettingsOAuth.ts b/web/src/pages/users/settings/SourceSettingsOAuth.ts
new file mode 100644
index 000000000..383c1a7f5
--- /dev/null
+++ b/web/src/pages/users/settings/SourceSettingsOAuth.ts
@@ -0,0 +1,50 @@
+import { customElement, html, TemplateResult } from "lit-element";
+import { BaseUserSettings } from "./BaseUserSettings";
+import { OAuthSource, SourcesApi } from "authentik-api";
+import { until } from "lit-html/directives/until";
+import { DEFAULT_CONFIG } from "../../../api/Config";
+import { gettext } from "django";
+import { AppURLManager } from "../../../api/legacy";
+
+@customElement("ak-user-settings-source-oauth")
+export class SourceSettingsOAuth extends BaseUserSettings {
+
+ render(): TemplateResult {
+ return html`${until(new SourcesApi(DEFAULT_CONFIG).sourcesOauthRead({
+ slug: this.objectId
+ }).then((source) => {
+ return html`
+
+ ${gettext(`Source ${source.name}`)}
+
+
+ ${this.renderInner(source)}
+
+
`;
+ }))}`;
+ }
+
+ renderInner(source: OAuthSource): TemplateResult {
+ return html`${until(new SourcesApi(DEFAULT_CONFIG).sourcesOauthUserConnectionsList({
+ source: this.objectId
+ }).then((connection) => {
+ if (connection.results.length > 0) {
+ return html`${gettext("Connected.")}
+ `;
+ }
+ return html`${gettext("Not connected.")}
+
+ ${gettext("Connect")}
+ `;
+ }))}`;
+ }
+
+}